Inline g/setPixel in imageCleanTransparent (#14323)

This commit is contained in:
DS 2024-02-15 19:38:23 +01:00 committed by GitHub
parent c81e0b7433
commit 4843890c56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 43 additions and 17 deletions

View File

@ -65,20 +65,27 @@ public:
}
};
/* Fill in RGB values for transparent pixels, to correct for odd colors
* appearing at borders when blending. This is because many PNG optimizers
* like to discard RGB values of transparent pixels, but when blending then
* with non-transparent neighbors, their RGB values will show up nonetheless.
*
* This function modifies the original image in-place.
*
* Parameter "threshold" is the alpha level below which pixels are considered
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
* 0 when alpha blending is used.
*/
void imageCleanTransparent(video::IImage *src, u32 threshold)
template <bool IS_A8R8G8B8>
static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold)
{
core::dimension2d<u32> dim = src->getDimension();
void *const src_data = src->getData();
const core::dimension2d<u32> dim = src->getDimension();
auto get_pixel = [src, src_data, dim](u32 x, u32 y) -> video::SColor {
if constexpr (IS_A8R8G8B8) {
return reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
} else {
return src->getPixel(x, y);
}
};
auto set_pixel = [src, src_data, dim](u32 x, u32 y, video::SColor color) {
if constexpr (IS_A8R8G8B8) {
u32 *dest = &reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
*dest = color.color;
} else {
src->setPixel(x, y, color);
}
};
Bitmap bitmap(dim.Width, dim.Height);
@ -86,7 +93,7 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
// Note: loop y around x for better cache locality.
for (u32 ctry = 0; ctry < dim.Height; ctry++)
for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) {
if (src->getPixel(ctrx, ctry).getAlpha() > threshold)
if (get_pixel(ctrx, ctry).getAlpha() > threshold)
bitmap.set(ctrx, ctry);
}
@ -125,7 +132,7 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
// Add RGB values weighted by alpha IF the pixel is opaque, otherwise
// use full weight since we want to propagate colors.
video::SColor d = src->getPixel(sx, sy);
video::SColor d = get_pixel(sx, sy);
u32 a = d.getAlpha() <= threshold ? 255 : d.getAlpha();
ss += a;
sr += a * d.getRed();
@ -135,11 +142,11 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
// Set pixel to average weighted by alpha
if (ss > 0) {
video::SColor c = src->getPixel(ctrx, ctry);
video::SColor c = get_pixel(ctrx, ctry);
c.setRed(sr / ss);
c.setGreen(sg / ss);
c.setBlue(sb / ss);
src->setPixel(ctrx, ctry, c);
set_pixel(ctrx, ctry, c);
newmap.set(ctrx, ctry);
}
}
@ -154,6 +161,25 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
}
}
/* Fill in RGB values for transparent pixels, to correct for odd colors
* appearing at borders when blending. This is because many PNG optimizers
* like to discard RGB values of transparent pixels, but when blending then
* with non-transparent neighbors, their RGB values will show up nonetheless.
*
* This function modifies the original image in-place.
*
* Parameter "threshold" is the alpha level below which pixels are considered
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
* 0 when alpha blending is used.
*/
void imageCleanTransparent(video::IImage *src, u32 threshold)
{
if (src->getColorFormat() == video::ECF_A8R8G8B8)
imageCleanTransparentWithInlining<true>(src, threshold);
else
imageCleanTransparentWithInlining<false>(src, threshold);
}
/* Scale a region of an image into another image, using nearest-neighbor with
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
* to prevent non-integer scaling ratio artifacts. Note that this may cause