anim_decode: fix alpha blending with big-endian

images are decoded in RGBA/BGRA, but represented as uint32_t during the
blend process; this fixes the channel extraction

Bug: webp:548
Change-Id: Ie74aa43d8f87d3552d5afc0abba466335f5d1617
This commit is contained in:
James Zern 2021-12-22 13:46:21 -08:00
parent a510fedb9a
commit e4886716d3

View File

@ -23,6 +23,14 @@
#define NUM_CHANNELS 4 #define NUM_CHANNELS 4
// Channel extraction from a uint32_t representation of a uint8_t RGBA/BGRA
// buffer.
#ifdef WORDS_BIGENDIAN
#define CHANNEL_SHIFT(i) (24 - (i) * 8)
#else
#define CHANNEL_SHIFT(i) ((i) * 8)
#endif
typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int); typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
static void BlendPixelRowNonPremult(uint32_t* const src, static void BlendPixelRowNonPremult(uint32_t* const src,
const uint32_t* const dst, int num_pixels); const uint32_t* const dst, int num_pixels);
@ -209,35 +217,35 @@ static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
const uint8_t dst_channel = (dst >> shift) & 0xff; const uint8_t dst_channel = (dst >> shift) & 0xff;
const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a; const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
assert(blend_unscaled < (1ULL << 32) / scale); assert(blend_unscaled < (1ULL << 32) / scale);
return (blend_unscaled * scale) >> 24; return (blend_unscaled * scale) >> CHANNEL_SHIFT(3);
} }
// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha. // Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) { static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
const uint8_t src_a = (src >> 24) & 0xff; const uint8_t src_a = (src >> CHANNEL_SHIFT(3)) & 0xff;
if (src_a == 0) { if (src_a == 0) {
return dst; return dst;
} else { } else {
const uint8_t dst_a = (dst >> 24) & 0xff; const uint8_t dst_a = (dst >> CHANNEL_SHIFT(3)) & 0xff;
// This is the approximate integer arithmetic for the actual formula: // This is the approximate integer arithmetic for the actual formula:
// dst_factor_a = (dst_a * (255 - src_a)) / 255. // dst_factor_a = (dst_a * (255 - src_a)) / 255.
const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8; const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
const uint8_t blend_a = src_a + dst_factor_a; const uint8_t blend_a = src_a + dst_factor_a;
const uint32_t scale = (1UL << 24) / blend_a; const uint32_t scale = (1UL << 24) / blend_a;
const uint8_t blend_r = const uint8_t blend_r = BlendChannelNonPremult(
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0); src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(0));
const uint8_t blend_g = const uint8_t blend_g = BlendChannelNonPremult(
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8); src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(1));
const uint8_t blend_b = const uint8_t blend_b = BlendChannelNonPremult(
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16); src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(2));
assert(src_a + dst_factor_a < 256); assert(src_a + dst_factor_a < 256);
return (blend_r << 0) | return ((uint32_t)blend_r << CHANNEL_SHIFT(0)) |
(blend_g << 8) | ((uint32_t)blend_g << CHANNEL_SHIFT(1)) |
(blend_b << 16) | ((uint32_t)blend_b << CHANNEL_SHIFT(2)) |
((uint32_t)blend_a << 24); ((uint32_t)blend_a << CHANNEL_SHIFT(3));
} }
} }
@ -247,7 +255,7 @@ static void BlendPixelRowNonPremult(uint32_t* const src,
const uint32_t* const dst, int num_pixels) { const uint32_t* const dst, int num_pixels) {
int i; int i;
for (i = 0; i < num_pixels; ++i) { for (i = 0; i < num_pixels; ++i) {
const uint8_t src_alpha = (src[i] >> 24) & 0xff; const uint8_t src_alpha = (src[i] >> CHANNEL_SHIFT(3)) & 0xff;
if (src_alpha != 0xff) { if (src_alpha != 0xff) {
src[i] = BlendPixelNonPremult(src[i], dst[i]); src[i] = BlendPixelNonPremult(src[i], dst[i]);
} }
@ -264,7 +272,7 @@ static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha. // Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) { static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
const uint8_t src_a = (src >> 24) & 0xff; const uint8_t src_a = (src >> CHANNEL_SHIFT(3)) & 0xff;
return src + ChannelwiseMultiply(dst, 256 - src_a); return src + ChannelwiseMultiply(dst, 256 - src_a);
} }
@ -274,7 +282,7 @@ static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
int num_pixels) { int num_pixels) {
int i; int i;
for (i = 0; i < num_pixels; ++i) { for (i = 0; i < num_pixels; ++i) {
const uint8_t src_alpha = (src[i] >> 24) & 0xff; const uint8_t src_alpha = (src[i] >> CHANNEL_SHIFT(3)) & 0xff;
if (src_alpha != 0xff) { if (src_alpha != 0xff) {
src[i] = BlendPixelPremult(src[i], dst[i]); src[i] = BlendPixelPremult(src[i], dst[i]);
} }