mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-17 22:17:00 +00:00
refactor scale_surface, and add old version as "legacy linear" option
This commit is contained in:
parent
22bd42e0c7
commit
5f96795080
@ -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
|
||||
|
@ -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 &) {
|
||||
@ -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 &) {
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
(NEAREST_NEIGHBOR, "nn")
|
||||
(XBRZ_LIN, "xbrzlin")
|
||||
(XBRZ_NN, "xbrznn")
|
||||
(LEGACY_LINEAR, "legacy_lin")
|
||||
)
|
||||
|
||||
private:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
@ -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++) {
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user