Skip to content

Commit

Permalink
refactor scale_surface, and add old version as "legacy linear" option
Browse files Browse the repository at this point in the history
  • Loading branch information
cbeck88 committed Nov 14, 2014
1 parent 22bd42e commit 5f96795
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 3 deletions.
3 changes: 2 additions & 1 deletion data/gui/default/window/advanced_graphics_options.cfg
Expand Up @@ -39,10 +39,11 @@


#define _GUI_SCALE_ALGO_OPTIONS CASE
{_GUI_SCALE_OPTION ({CASE}+"_linear") _"Linear" _"Bilinear intepolation scaling (legacy wesnoth option)"}
{_GUI_SCALE_OPTION ({CASE}+"_legacy_lin") _"Linear (legacy)" _"Bilinear intepolation scaling (legacy wesnoth option)"}
{_GUI_SCALE_OPTION ({CASE}+"_nn") _"Nearest Neighbor" _"Nearest Neighbor scaling (fastest)"}
{_GUI_SCALE_OPTION ({CASE}+"_xbrzlin") _"xBRZ + linear" _"xBRZ followed by Bilinear interpolation"}
{_GUI_SCALE_OPTION ({CASE}+"_xbrznn") _"xBRZ + NN" _"xBRZ followed by Nearest Neighbor (recommended)"}
{_GUI_SCALE_OPTION ({CASE}+"_linear") _"Linear" _"Bilinear intepolation scaling (new implementation)"}
#enddef

#define _GUI_SCALE_CHOICE CASE LABEL TOOLTIP
Expand Down
4 changes: 2 additions & 2 deletions src/gui/dialogs/advanced_graphics_options.cpp
Expand Up @@ -72,7 +72,7 @@ void tadvanced_graphics_options::setup_scale_button(const std::string & case_id,
{
std::string pref_id = "scale_" + case_id;

tadvanced_graphics_options::SCALING_ALGORITHM algo = tadvanced_graphics_options::LINEAR;
tadvanced_graphics_options::SCALING_ALGORITHM algo = tadvanced_graphics_options::LEGACY_LINEAR;
try {
algo = string_to_SCALING_ALGORITHM(preferences::get(pref_id));
} catch (bad_enum_cast &) {
Expand All @@ -89,7 +89,7 @@ void tadvanced_graphics_options::setup_scale_button(const std::string & case_id,

void tadvanced_graphics_options::scale_button_callback(std::string pref_id, SCALING_ALGORITHM me, twindow & window)
{
tadvanced_graphics_options::SCALING_ALGORITHM algo = tadvanced_graphics_options::LINEAR;
tadvanced_graphics_options::SCALING_ALGORITHM algo = tadvanced_graphics_options::LEGACY_LINEAR;
try {
algo = string_to_SCALING_ALGORITHM(preferences::get(pref_id));
} catch (bad_enum_cast &) {
Expand Down
1 change: 1 addition & 0 deletions src/gui/dialogs/advanced_graphics_options.hpp
Expand Up @@ -48,6 +48,7 @@ class tadvanced_graphics_options : public tdialog
(NEAREST_NEIGHBOR, "nn")
(XBRZ_LIN, "xbrzlin")
(XBRZ_NN, "xbrznn")
(LEGACY_LINEAR, "legacy_lin")
)

private:
Expand Down
4 changes: 4 additions & 0 deletions src/image.cpp
Expand Up @@ -775,6 +775,10 @@ static surface scale_surface_algorithm(const surface & res, int w, int h, gui2::
surface xbrz_temp(scale_surface_xbrz(res, std::max(std::min(z_factor,5),1)));
return scale_surface_nn(xbrz_temp, w, h);
}
case gui2::tadvanced_graphics_options::LEGACY_LINEAR:
{
return scale_surface_legacy(res, w, h);
}
default:
assert(false && "I don't know how to implement this scaling algorithm");
throw 42;
Expand Down
145 changes: 145 additions & 0 deletions src/sdl/utils.cpp
Expand Up @@ -480,6 +480,137 @@ surface scale_surface(const surface &surf, int w, int h, bool optimize)
return NULL;
}

{
const_surface_lock src_lock(src);
surface_lock dst_lock(dst);

const Uint32* const src_pixels = src_lock.pixels();
Uint32* const dst_pixels = dst_lock.pixels();

fixed_t xratio = fxpdiv(surf->w,w);
fixed_t yratio = fxpdiv(surf->h,h);

fixed_t ysrc = ftofxp(0.0);
for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
fixed_t xsrc = ftofxp(0.0);
for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
const int xsrcint = fxptoi(xsrc);
const int ysrcint = fxptoi(ysrc);

const Uint32* const src_word = src_pixels + ysrcint*src->w + xsrcint;
Uint32* const dst_word = dst_pixels + ydst*dst->w + xdst;
const int dx = (xsrcint + 1 < src->w) ? 1 : 0;
const int dy = (ysrcint + 1 < src->h) ? src->w : 0;

Uint8 r,g,b,a;
Uint32 rr,gg,bb,aa, temp;

Uint32 pix[4], bilin[4];

// This next part is the fixed point
// equivalent of "take everything to
// the right of the decimal point."
// These fundamental weights decide
// the contributions from various
// input pixels. The labels assume
// that the upper left corner of the
// screen ("northeast") is 0,0 but the
// code should still be consistent if
// the graphics origin is actually
// somewhere else.
//
// That is, the bilin array holds the
// "geometric" weights. I.E. If I'm scaling
// a 2 x 2 block a 10 x 10 block, then for
// pixel (2,2) of ouptut, the upper left
// pixel should be 10:1 more influential than
// the upper right, and also 10:1 more influential
// than lower left, and 100:1 more influential
// than lower right.

const fixed_t e = 0x000000FF & xsrc;
const fixed_t s = 0x000000FF & ysrc;
const fixed_t n = 0xFF - s;
const fixed_t w = 0xFF - e;

pix[0] = *src_word; // northwest
pix[1] = *(src_word + dx); // northeast
pix[2] = *(src_word + dy); // southwest
pix[3] = *(src_word + dx + dy); // southeast

bilin[0] = n*w;
bilin[1] = n*e;
bilin[2] = s*w;
bilin[3] = s*e;

int loc;
rr = bb = gg = aa = 0;
for (loc=0; loc<4; loc++) {
a = pix[loc] >> 24;
r = pix[loc] >> 16;
g = pix[loc] >> 8;
b = pix[loc] >> 0;

//We also have to implement weighting by alpha for the RGB components
//If a unit has some parts solid and some parts translucent,
//i.e. a red cloak but a dark shadow, then when we scale in
//the shadow shouldn't appear to become red at the edges.
//This part also smoothly interpolates between alpha=0 being
//transparent and having no contribution, vs being opaque.
temp = (a * bilin[loc]);
rr += r * temp;
gg += g * temp;
bb += b * temp;
aa += temp;
}

a = aa >> (16); // we average the alphas, they don't get weighted by any other factor besides bilin
if (a != 0) {
rr /= a; // finish alpha weighting: divide by sum of alphas
gg /= a;
bb /= a;
}
r = rr >> (16); // now shift over by 16 for the bilin part, 8
g = gg >> (16);
b = bb >> (16);
*dst_word = (a << 24) + (r << 16) + (g << 8) + b;
}
}
}

return optimize ? create_optimized_surface(dst) : dst;
}

// NOTE: Don't pass this function 0 scaling arguments.
surface scale_surface_legacy(const surface &surf, int w, int h, bool optimize)
{
// Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
assert(SDL_ALPHA_TRANSPARENT==0);

if(surf == NULL)
return NULL;

if(w == surf->w && h == surf->h) {
return surf;
}
assert(w >= 0);
assert(h >= 0);

surface dst(create_neutral_surface(w,h));

if (w == 0 || h ==0) {
std::cerr << "Create an empty image\n";
return create_optimized_surface(dst);
}

surface src(make_neutral_surface(surf));
// Now both surfaces are always in the "neutral" pixel format

if(src == NULL || dst == NULL) {
std::cerr << "Could not create surface to scale onto\n";
return NULL;
}

{
const_surface_lock src_lock(src);
surface_lock dst_lock(dst);
Expand Down Expand Up @@ -568,6 +699,19 @@ surface scale_surface(const surface &surf, int w, int h, bool optimize)
// Some of the input images are hex tiles,
// created using a hexagon shaped alpha channel
// that is either set to full-on or full-off.
//
// If intermediate alpha values are introduced
// along a hex edge, it produces a gametime artifact.
// Moving the mouse around will leave behind
// "hexagon halos" from the temporary highlighting.
// In other words, the Wesnoth rendering engine
// freaks out.
//
// The alpha thresholding step attempts
// to accommodates this limitation.
// There is a small loss of quality.
// For example, skeleton bowstrings
// are not as good as they could be.

rr = gg = bb = aa = 0;
for (loc=0; loc<4; loc++) {
Expand All @@ -589,6 +733,7 @@ surface scale_surface(const surface &surf, int w, int h, bool optimize)
g = gg >> 16;
b = bb >> 16;
a = aa >> 16;
a = (a < avg_a/2) ? 0 : avg_a;
*dst_word = (a << 24) + (r << 16) + (g << 8) + b;
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/sdl/utils.hpp
Expand Up @@ -193,6 +193,19 @@ surface scale_surface_nn(const surface & surf, int w, int h);
*/
surface scale_surface(const surface &surf, int w, int h, bool optimize=true);

/** Scale a surface (legacy (1.10, 1.12) version)
* @param surf The source surface.
* @param w The width of the resulting surface.
* @param h The height of the resulting surface.
* @param optimize Should the return surface be RLE optimized.
* @return A surface containing the scaled version of the source.
* @retval 0 Returned upon error.
* @retval surf Returned if w == surf->w and h == surf->h
* note this ignores the optimize flag.
*/
surface scale_surface_legacy(const surface &surf, int w, int h, bool optimize=true);


/** Scale a surface using modified nearest neighbour algorithm. Use only if
* preserving sharp edges is a priority (e.g. minimap).
* @param surf The source surface.
Expand Down

0 comments on commit 5f96795

Please sign in to comment.