From f7775ec5746d242e9c3c0f06a725bfee748f8243 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Apr 2018 17:52:20 -0400 Subject: [PATCH 1/5] Fix condition for bitmap premultiplication. --- src/image.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/image.c b/src/image.c index cd47f246c..f89cfe399 100644 --- a/src/image.c +++ b/src/image.c @@ -446,8 +446,7 @@ GdipDrawImageRect (GpGraphics *graphics, GpImage *image, REAL x, REAL y, REAL wi /* Create a surface for this bitmap if one doesn't exist */ gdip_bitmap_ensure_surface (image); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (image)) { + if (gdip_bitmap_format_needs_premultiplication (image)) { premul = gdip_bitmap_get_premultiplied_scan0 (image); if (premul) { BitmapData *data = image->active_bitmap; From 133e7b5e0c809a182a77c8ddd442c1dfbce35861 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 18 Jul 2018 12:00:29 +0200 Subject: [PATCH 2/5] Add unit test. --- tests/testgraphics.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/testgraphics.c b/tests/testgraphics.c index db076a921..3093bb82c 100644 --- a/tests/testgraphics.c +++ b/tests/testgraphics.c @@ -2050,6 +2050,34 @@ static void test_region_mask() GdipDisposeImage (bitmap); } +static void test_premultiplication () +{ + GpStatus status; + GpBitmap *bitmap; + GpBitmap *bitmapBackground; + GpGraphics *graphicsBackground; + + BYTE bpp32ArgbData[] = { 0xFF, 0xFF, 0xFF, 0x80 }; + ARGB bpp32ArgbPixels[] = { 0x80FFFFFF }; + ARGB bpp32RgbPixels[] = { 0xFF808080 }; + + status = GdipCreateBitmapFromScan0 (1, 1, 4, PixelFormat32bppARGB, bpp32ArgbData, &bitmap); + assertEqualInt (status, Ok); + verifyBitmap (bitmap, memoryBmpRawFormat, PixelFormat32bppARGB, 1, 1, ImageFlagsHasAlpha, 0, TRUE); + verifyPixels (bitmap, bpp32ArgbPixels); + status = GdipCreateBitmapFromScan0 (1, 1, 4, PixelFormat32bppRGB, NULL, &bitmapBackground); + assertEqualInt (status, Ok); + status = GdipBitmapSetPixel (bitmapBackground, 0, 0, 0); + assertEqualInt (status, Ok); + GdipGetImageGraphicsContext (bitmapBackground, &graphicsBackground); + status = GdipDrawImage (graphicsBackground, (GpImage *)bitmap, 0, 0); + assertEqualInt (status, Ok); + GdipDeleteGraphics (graphicsBackground); + verifyPixels (bitmapBackground, bpp32RgbPixels); + GdipDisposeImage ((GpImage *) bitmapBackground); + GdipDisposeImage ((GpImage *) bitmap); +} + int main (int argc, char**argv) { @@ -2092,6 +2120,7 @@ main (int argc, char**argv) test_translateClip (); test_translateClipI (); test_region_mask (); + test_premultiplication (); SHUTDOWN; return 0; From 389cb7bb49006c0a598ee3369d9c4876574740fe Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 18 Jul 2018 12:04:59 +0200 Subject: [PATCH 3/5] Fix other occurrences of the wrong premultiplication condition. --- src/image.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/image.c b/src/image.c index f89cfe399..add797f9e 100644 --- a/src/image.c +++ b/src/image.c @@ -553,8 +553,7 @@ GdipDrawImagePoints (GpGraphics *graphics, GpImage *image, GDIPCONST GpPointF *d /* Create a surface for this bitmap if one doesn't exist */ gdip_bitmap_ensure_surface (image); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (image)) { + if (gdip_bitmap_format_needs_premultiplication (image)) { premul = gdip_bitmap_get_premultiplied_scan0 (image); if (premul) { BitmapData *data = image->active_bitmap; @@ -744,8 +743,7 @@ GdipDrawImageRectRect (GpGraphics *graphics, GpImage *image, gdip_flip_x (imgflipX); gdip_bitmap_ensure_surface (imgflipX); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (imgflipX)) { + if (gdip_bitmap_format_needs_premultiplication (imgflipX)) { premulX = gdip_bitmap_get_premultiplied_scan0 (imgflipX); if (premulX) { BitmapData *data = imgflipX->active_bitmap; @@ -763,8 +761,7 @@ GdipDrawImageRectRect (GpGraphics *graphics, GpImage *image, gdip_flip_y (imgflipY); gdip_bitmap_ensure_surface (imgflipY); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (imgflipY)) { + if (gdip_bitmap_format_needs_premultiplication (imgflipY)) { premulY = gdip_bitmap_get_premultiplied_scan0 (imgflipY); if (premulY) { BitmapData *data = imgflipY->active_bitmap; @@ -783,8 +780,7 @@ GdipDrawImageRectRect (GpGraphics *graphics, GpImage *image, gdip_flip_y (imgflipXY); gdip_bitmap_ensure_surface (imgflipXY); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (imgflipXY)) { + if (gdip_bitmap_format_needs_premultiplication (imgflipXY)) { premulXY = gdip_bitmap_get_premultiplied_scan0 (imgflipXY); if (premulXY) { BitmapData *data = imgflipXY->active_bitmap; @@ -799,8 +795,7 @@ GdipDrawImageRectRect (GpGraphics *graphics, GpImage *image, gdip_bitmap_ensure_surface (image); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (image)) { + if (gdip_bitmap_format_needs_premultiplication (image)) { premul = gdip_bitmap_get_premultiplied_scan0 (image); if (premul) { BitmapData *data = image->active_bitmap; @@ -895,8 +890,7 @@ GdipDrawImageRectRect (GpGraphics *graphics, GpImage *image, gdip_bitmap_ensure_surface (image); - if (graphics->type != gtMemoryBitmap && - gdip_bitmap_format_needs_premultiplication (image)) { + if (gdip_bitmap_format_needs_premultiplication (image)) { premul = gdip_bitmap_get_premultiplied_scan0 (image); if (premul) { BitmapData *data = image->active_bitmap; From f18f214cc8cc43bcbd29ff39e4e0132233af3403 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 18 Jul 2018 14:00:01 +0200 Subject: [PATCH 4/5] Blind fix for applying color matrix on non-premultiplied surfaces. --- src/imageattributes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/imageattributes.c b/src/imageattributes.c index e0841d272..d83831637 100644 --- a/src/imageattributes.c +++ b/src/imageattributes.c @@ -221,6 +221,7 @@ gdip_process_bitmap_attributes (GpBitmap *bitmap, void **dest, GpImageAttributes ARGB *scan; ColorMatrixFlags flags = cmatrix->colormatrix_flags; ColorMatrix *cm; + BOOL bmpdest_is_premultiplied = !gdip_bitmap_format_needs_premultiplication (bmpdest); for (y = 0; y < data->height; y++) { scan = (ARGB*) v; @@ -258,7 +259,7 @@ gdip_process_bitmap_attributes (GpBitmap *bitmap, void **dest, GpImageAttributes /* remember that Cairo use pre-multiplied alpha, e.g. 50% red == 0x80800000 not 0x80ff0000 */ a = (BYTE) a_new; - if (a < 0xff) { + if (a < 0xff && bmpdest_is_premultiplied) { r = pre_multiplied_table [r][a]; g = pre_multiplied_table [g][a]; b = pre_multiplied_table [b][a]; From b5c7e6e55ef3f75c6544ae853f7b127fc3b48c59 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 18 Jul 2018 14:45:11 +0200 Subject: [PATCH 5/5] Fix applying color matrix transformation on premultiplied ARGB bitmaps. --- src/imageattributes.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/imageattributes.c b/src/imageattributes.c index d83831637..8ff1f4ff5 100644 --- a/src/imageattributes.c +++ b/src/imageattributes.c @@ -245,7 +245,7 @@ gdip_process_bitmap_attributes (GpBitmap *bitmap, void **dest, GpImageAttributes } a_new = (r * cm->m[0][3] + g * cm->m[1][3] + b * cm->m[2][3] + a * cm->m[3][3] + (255 * cm->m[4][3])); - if (a_new == 0) { + if (a_new == 0 && bmpdest_is_premultiplied) { /* 100% transparency, don't waste time computing other values (pre-mul will always be 0) */ *scan++ = 0; } else { @@ -253,16 +253,26 @@ gdip_process_bitmap_attributes (GpBitmap *bitmap, void **dest, GpImageAttributes g_new = (r * cm->m[0][1] + g * cm->m[1][1] + b * cm->m[2][1] + a * cm->m[3][1] + (255 * cm->m[4][1])); b_new = (r * cm->m[0][2] + g * cm->m[1][2] + b * cm->m[2][2] + a * cm->m[3][2] + (255 * cm->m[4][2])); + if (bmpdest_is_premultiplied && a != (BYTE) a_new && a < 0xff && a != 0) { + /* reverse previous pre-multiplication if necessary */ + r_new = r_new * 255 / a; + g_new = g_new * 255 / a; + b_new = b_new * 255 / a; + } + r = (r_new > 0xff) ? 0xff : (BYTE) r_new; g = (g_new > 0xff) ? 0xff : (BYTE) g_new; b = (b_new > 0xff) ? 0xff : (BYTE) b_new; /* remember that Cairo use pre-multiplied alpha, e.g. 50% red == 0x80800000 not 0x80ff0000 */ - a = (BYTE) a_new; - if (a < 0xff && bmpdest_is_premultiplied) { + if (bmpdest_is_premultiplied && a != (BYTE) a_new && a_new < 0xff) { + /* apply new pre-multiplication */ + a = (BYTE) a_new; r = pre_multiplied_table [r][a]; g = pre_multiplied_table [g][a]; b = pre_multiplied_table [b][a]; + } else { + a = (BYTE) a_new; } set_pixel_bgra (color_p, 0, b, g, r, a);