add blending to webp load

This commit is contained in:
John Cupitt 2018-11-04 18:28:36 +00:00
parent 5e071a783f
commit e81e7f96eb
1 changed files with 63 additions and 7 deletions

View File

@ -100,9 +100,9 @@ typedef struct {
int frame_width;
int frame_height;
/* BG colour as a 32-bit int
/* RGBA background colour.
*/
int background;
uint32_t background;
/* TRUE for RGBA.
*/
@ -450,7 +450,8 @@ vips_image_paint_pel( VipsImage *image, const VipsRect *r, const VipsPel *ink )
int x, y, z;
/* We plot the first line pointwise, then memcpy() it for the
* subsequent lines.
* subsequent lines. We need to work for RGB and RGBA, so we
* can't just write uint32s.
*/
to = VIPS_IMAGE_ADDR( image, ovl.left, ovl.top );
@ -472,17 +473,64 @@ vips_image_paint_pel( VipsImage *image, const VipsRect *r, const VipsPel *ink )
}
}
/* Blend two uint8s.
*/
#define BLEND( X, aX, Y, aY, scale ) \
((X * aX + Y * aY) * scale >> 24)
/* Extract R, G, B, A, assuming little-endian.
*/
#define getR( V ) (V & 0xff)
#define getG( V ) ((V >> 8) & 0xff)
#define getB( V ) ((V >> 16) & 0xff)
#define getA( V ) ((V >> 24) & 0xff)
/* Rebuild RGBA, assuming little-endian.
*/
#define setRGBA( R, G, B, A ) (R | (G << 8) | (B << 16) | (A << 24))
/* OVER blend of two unpremultiplied RGBA uint32_t
*
* We assume little-endian (x86), add a byteswap before this if necessary.
*/
static uint32_t
blend_pixel( uint32_t A, uint32_t B )
{
uint8_t aA = getA( A );
if( aA == 0 )
return( B );
uint8_t aB = getA( B );
uint8_t fac = (aB * (256 - aA)) >> 8;
uint8_t aR = aA + fac;
int scale = (1 << 24) / aR;
uint8_t rR = BLEND( getR( A ), aA, getR( B ), fac, scale );
uint8_t gR = BLEND( getG( A ), aA, getG( B ), fac, scale );
uint8_t bR = BLEND( getB( A ), aA, getB( B ), fac, scale );
return( setRGBA( rR, gR, bR, aR ) );
}
static void
vips_image_paint_image( VipsImage *image,
VipsImage *ink, int x, int y, gboolean blend )
{
VipsRect valid = { 0, 0, image->Xsize, image->Ysize };
VipsRect sub = { x, y, ink->Xsize, ink->Ysize };
int ps = VIPS_IMAGE_SIZEOF_PEL( image );
VipsRect ovl;
g_assert( VIPS_IMAGE_SIZEOF_PEL( image ) ==
VIPS_IMAGE_SIZEOF_PEL( ink ) );
g_assert( VIPS_IMAGE_SIZEOF_PEL( ink ) == ps );
/* Disable blend if we are not RGBA.
*/
if( image->Bands != 4 )
blend = FALSE;
vips_rect_intersectrect( &valid, &sub, &ovl );
if( !vips_rect_isempty( &ovl ) ) {
@ -493,8 +541,16 @@ vips_image_paint_image( VipsImage *image,
q = VIPS_IMAGE_ADDR( image, ovl.left, ovl.top );
for( i = 0; i < ovl.height; i++ ) {
memcpy( (char *) q, (char *) p,
ovl.width * VIPS_IMAGE_SIZEOF_PEL( image ) );
if( blend ) {
uint32_t *A = (uint32_t *) p;
uint32_t *B = (uint32_t *) q;
for( x = 0; x < ovl.width; x++ )
B[x] = blend_pixel( A[x], B[x] );
}
else
memcpy( (char *) q, (char *) p,
ovl.width * ps );
p += VIPS_IMAGE_SIZEOF_LINE( ink );
q += VIPS_IMAGE_SIZEOF_LINE( image );