Skip to content

Commit

Permalink
Inline g/setPixel in imageCleanTransparent (#14323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Desour committed Feb 15, 2024
1 parent c81e0b7 commit 4843890
Showing 1 changed file with 43 additions and 17 deletions.
60 changes: 43 additions & 17 deletions src/client/imagefilters.cpp
Expand Up @@ -65,28 +65,35 @@ class Bitmap {
}
};

/* 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);

// First pass: Mark all opaque pixels
// 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);
}

Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
}
Expand All @@ -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
Expand Down

0 comments on commit 4843890

Please sign in to comment.