From 08edf7032197f40f026084a06a7a7ec3bea3c236 Mon Sep 17 00:00:00 2001 From: homm Date: Sun, 12 Jun 2016 22:00:34 +0300 Subject: [PATCH 1/6] paste.c formatting --- libImaging/Paste.c | 92 +++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/libImaging/Paste.c b/libImaging/Paste.c index d27b10fdb..e80a7ba64 100644 --- a/libImaging/Paste.c +++ b/libImaging/Paste.c @@ -5,15 +5,15 @@ * paste image on another image * * history: - * 96-03-27 fl Created - * 96-07-16 fl Support "1", "L" and "RGBA" masks - * 96-08-16 fl Merged with opaque paste - * 97-01-17 fl Faster blending, added support for RGBa images - * 97-08-27 fl Faster masking for 32-bit images - * 98-02-02 fl Fixed MULDIV255 macro for gcc - * 99-02-02 fl Added "RGBa" mask support - * 99-02-06 fl Rewritten. Added support for masked fill operations. - * 99-12-08 fl Fixed matte fill. + * 96-03-27 fl Created + * 96-07-16 fl Support "1", "L" and "RGBA" masks + * 96-08-16 fl Merged with opaque paste + * 97-01-17 fl Faster blending, added support for RGBa images + * 97-08-27 fl Faster masking for 32-bit images + * 98-02-02 fl Fixed MULDIV255 macro for gcc + * 99-02-02 fl Added "RGBa" mask support + * 99-02-06 fl Rewritten. Added support for masked fill operations. + * 99-12-08 fl Fixed matte fill. * * Copyright (c) Fredrik Lundh 1996-97. * Copyright (c) Secret Labs AB 1997-99. @@ -24,19 +24,19 @@ #include "Imaging.h" /* like (a * b + 127) / 255), but much faster on most platforms */ -#define MULDIV255NEW(a, b, tmp)\ - (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) +#define MULDIV255NEW(a, b, tmp)\ + (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) -#define MULDIV255OLD(a, b, tmp)\ +#define MULDIV255OLD(a, b, tmp)\ (((a) * (b) + 127) / 255) #define MULDIV255 MULDIV255NEW -#define BLEND(mask, in1, in2, tmp1, tmp2)\ - (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) +#define BLEND(mask, in1, in2, tmp1, tmp2)\ + (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) -#define PREBLEND(mask, in1, in2, tmp1)\ - (MULDIV255(in1, 255 - mask, tmp1) + in2) +#define PREBLEND(mask, in1, in2, tmp1)\ + (MULDIV255(in1, 255 - mask, tmp1) + in2) static inline void paste(Imaging imOut, Imaging imIn, int dx, int dy, int sx, int sy, @@ -212,7 +212,7 @@ paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask, int ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, - int dx0, int dy0, int dx1, int dy1) + int dx0, int dy0, int dx1, int dy1) { int xsize, ysize; int pixelsize; @@ -220,8 +220,8 @@ ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, ImagingSectionCookie cookie; if (!imOut || !imIn) { - (void) ImagingError_ModeError(); - return -1; + (void) ImagingError_ModeError(); + return -1; } pixelsize = imOut->pixelsize; @@ -231,28 +231,28 @@ ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, if (xsize != imIn->xsize || ysize != imIn->ysize || pixelsize != imIn->pixelsize) { - (void) ImagingError_Mismatch(); - return -1; + (void) ImagingError_Mismatch(); + return -1; } if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) { - (void) ImagingError_Mismatch(); - return -1; + (void) ImagingError_Mismatch(); + return -1; } /* Determine which region to copy */ sx0 = sy0 = 0; if (dx0 < 0) - xsize += dx0, sx0 = -dx0, dx0 = 0; + xsize += dx0, sx0 = -dx0, dx0 = 0; if (dx0 + xsize > imOut->xsize) - xsize = imOut->xsize - dx0; + xsize = imOut->xsize - dx0; if (dy0 < 0) - ysize += dy0, sy0 = -dy0, dy0 = 0; + ysize += dy0, sy0 = -dy0, dy0 = 0; if (dy0 + ysize > imOut->ysize) - ysize = imOut->ysize - dy0; + ysize = imOut->ysize - dy0; if (xsize <= 0 || ysize <= 0) - return 0; + return 0; if (!imMask) { ImagingSectionEnter(&cookie); @@ -284,8 +284,8 @@ ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, ImagingSectionLeave(&cookie); } else { - (void) ImagingError_ValueError("bad transparency mask"); - return -1; + (void) ImagingError_ValueError("bad transparency mask"); + return -1; } return 0; @@ -308,15 +308,15 @@ fill(Imaging imOut, const void* ink_, int dx, int dy, dx *= pixelsize; xsize *= pixelsize; - for (y = 0; y < ysize; y++) - memset(imOut->image[y+dy]+dx, ink8, xsize); + for (y = 0; y < ysize; y++) + memset(imOut->image[y+dy]+dx, ink8, xsize); } else { - for (y = 0; y < ysize; y++) { + for (y = 0; y < ysize; y++) { INT32* out = imOut->image32[y+dy]+dx; - for (x = 0; x < xsize; x++) - out[x] = ink32; + for (x = 0; x < xsize; x++) + out[x] = ink32; } } @@ -489,8 +489,8 @@ ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, int sx0, sy0; if (!imOut || !ink) { - (void) ImagingError_ModeError(); - return -1; + (void) ImagingError_ModeError(); + return -1; } pixelsize = imOut->pixelsize; @@ -499,23 +499,23 @@ ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, ysize = dy1 - dy0; if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) { - (void) ImagingError_Mismatch(); - return -1; + (void) ImagingError_Mismatch(); + return -1; } /* Determine which region to fill */ sx0 = sy0 = 0; if (dx0 < 0) - xsize += dx0, sx0 = -dx0, dx0 = 0; + xsize += dx0, sx0 = -dx0, dx0 = 0; if (dx0 + xsize > imOut->xsize) - xsize = imOut->xsize - dx0; + xsize = imOut->xsize - dx0; if (dy0 < 0) - ysize += dy0, sy0 = -dy0, dy0 = 0; + ysize += dy0, sy0 = -dy0, dy0 = 0; if (dy0 + ysize > imOut->ysize) - ysize = imOut->ysize - dy0; + ysize = imOut->ysize - dy0; if (xsize <= 0 || ysize <= 0) - return 0; + return 0; if (!imMask) { ImagingSectionEnter(&cookie); @@ -547,8 +547,8 @@ ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, ImagingSectionLeave(&cookie); } else { - (void) ImagingError_ValueError("bad transparency mask"); - return -1; + (void) ImagingError_ValueError("bad transparency mask"); + return -1; } return 0; From 003a7523dfb8dd6757004888e973ebe0b96725bf Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 5 Jul 2016 03:25:23 +0300 Subject: [PATCH 2/6] speedup paste with masks --- libImaging/Paste.c | 57 ++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/libImaging/Paste.c b/libImaging/Paste.c index e80a7ba64..aadb9004a 100644 --- a/libImaging/Paste.c +++ b/libImaging/Paste.c @@ -99,7 +99,7 @@ paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, { /* paste with mode "L" matte */ - int x, y, i; + int x, y; unsigned int tmp1, tmp2; if (imOut->image8) { @@ -117,15 +117,16 @@ paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, } else { for (y = 0; y < ysize; y++) { - UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; - UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; - UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + UINT8* out = (UINT8*) (imOut->image32[y + dy] + dx); + UINT8* in = (UINT8*) (imIn->image32[y + sy] + sx); + UINT8* mask = (UINT8*) (imMask->image8[y+sy] + sx); for (x = 0; x < xsize; x++) { - for (i = 0; i < pixelsize; i++) { - *out = BLEND(*mask, *out, *in, tmp1, tmp2); - out++, in++; - } - mask++; + UINT8 a = mask[0]; + out[0] = BLEND(a, out[0], in[0], tmp1, tmp2); + out[1] = BLEND(a, out[1], in[1], tmp1, tmp2); + out[2] = BLEND(a, out[2], in[2], tmp1, tmp2); + out[3] = BLEND(a, out[3], in[3], tmp1, tmp2); + out += 4; in += 4; mask ++; } } } @@ -138,7 +139,7 @@ paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, { /* paste with mode "RGBA" matte */ - int x, y, i; + int x, y; unsigned int tmp1, tmp2; if (imOut->image8) { @@ -156,15 +157,16 @@ paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, } else { for (y = 0; y < ysize; y++) { - UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; - UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; - UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + UINT8* out = (UINT8*) (imOut->image32[y + dy] + dx); + UINT8* in = (UINT8*) (imIn->image32[y + sy] + sx); + UINT8* mask = (UINT8*) (imMask->image32[y+sy] + sx); for (x = 0; x < xsize; x++) { - for (i = 0; i < pixelsize; i++) { - *out = BLEND(*mask, *out, *in, tmp1, tmp2); - out++, in++; - } - mask += 4; + UINT8 a = mask[3]; + out[0] = BLEND(a, out[0], in[0], tmp1, tmp2); + out[1] = BLEND(a, out[1], in[1], tmp1, tmp2); + out[2] = BLEND(a, out[2], in[2], tmp1, tmp2); + out[3] = BLEND(a, out[3], in[3], tmp1, tmp2); + out += 4; in += 4; mask += 4; } } } @@ -178,7 +180,7 @@ paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask, { /* paste with mode "RGBa" matte */ - int x, y, i; + int x, y; unsigned int tmp1; if (imOut->image8) { @@ -196,15 +198,16 @@ paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask, } else { for (y = 0; y < ysize; y++) { - UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; - UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; - UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + UINT8* out = (UINT8*) (imOut->image32[y + dy] + dx); + UINT8* in = (UINT8*) (imIn->image32[y + sy] + sx); + UINT8* mask = (UINT8*) (imMask->image32[y+sy] + sx); for (x = 0; x < xsize; x++) { - for (i = 0; i < pixelsize; i++) { - *out = PREBLEND(*mask, *out, *in, tmp1); - out++, in++; - } - mask += 4; + UINT8 a = mask[3]; + out[0] = PREBLEND(a, out[0], in[0], tmp1); + out[1] = PREBLEND(a, out[1], in[1], tmp1); + out[2] = PREBLEND(a, out[2], in[2], tmp1); + out[3] = PREBLEND(a, out[3], in[3], tmp1); + out += 4; in += 4; mask += 4; } } } From 30a5f1a0d0db06e654de633b432047fa980327d4 Mon Sep 17 00:00:00 2001 From: homm Date: Tue, 5 Jul 2016 03:52:48 +0300 Subject: [PATCH 3/6] fix blend macros --- libImaging/Paste.c | 50 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/libImaging/Paste.c b/libImaging/Paste.c index aadb9004a..a18e5d6f2 100644 --- a/libImaging/Paste.c +++ b/libImaging/Paste.c @@ -24,19 +24,19 @@ #include "Imaging.h" /* like (a * b + 127) / 255), but much faster on most platforms */ -#define MULDIV255NEW(a, b, tmp)\ - (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) +#define MULDIV255NEW(a, tmp)\ + (tmp = (a) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) -#define MULDIV255OLD(a, b, tmp)\ - (((a) * (b) + 127) / 255) +#define MULDIV255OLD(a, tmp)\ + (((a) + 127) / 255) #define MULDIV255 MULDIV255NEW -#define BLEND(mask, in1, in2, tmp1, tmp2)\ - (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) +#define BLEND(mask, in1, in2, tmp1)\ + MULDIV255(in1 * (255 - mask) + in2 * mask, tmp1) #define PREBLEND(mask, in1, in2, tmp1)\ - (MULDIV255(in1, 255 - mask, tmp1) + in2) + (MULDIV255(in1 * (255 - mask), tmp1) + in2) static inline void paste(Imaging imOut, Imaging imIn, int dx, int dy, int sx, int sy, @@ -100,7 +100,7 @@ paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, /* paste with mode "L" matte */ int x, y; - unsigned int tmp1, tmp2; + unsigned int tmp1; if (imOut->image8) { @@ -109,7 +109,7 @@ paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, UINT8* in = imIn->image8[y+sy]+sx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - *out = BLEND(*mask, *out, *in, tmp1, tmp2); + *out = BLEND(*mask, *out, *in, tmp1); out++, in++, mask++; } } @@ -122,10 +122,10 @@ paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, UINT8* mask = (UINT8*) (imMask->image8[y+sy] + sx); for (x = 0; x < xsize; x++) { UINT8 a = mask[0]; - out[0] = BLEND(a, out[0], in[0], tmp1, tmp2); - out[1] = BLEND(a, out[1], in[1], tmp1, tmp2); - out[2] = BLEND(a, out[2], in[2], tmp1, tmp2); - out[3] = BLEND(a, out[3], in[3], tmp1, tmp2); + out[0] = BLEND(a, out[0], in[0], tmp1); + out[1] = BLEND(a, out[1], in[1], tmp1); + out[2] = BLEND(a, out[2], in[2], tmp1); + out[3] = BLEND(a, out[3], in[3], tmp1); out += 4; in += 4; mask ++; } } @@ -140,7 +140,7 @@ paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, /* paste with mode "RGBA" matte */ int x, y; - unsigned int tmp1, tmp2; + unsigned int tmp1; if (imOut->image8) { @@ -149,7 +149,7 @@ paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, UINT8* in = imIn->image8[y+sy]+sx; UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; for (x = 0; x < xsize; x++) { - *out = BLEND(*mask, *out, *in, tmp1, tmp2); + *out = BLEND(*mask, *out, *in, tmp1); out++, in++, mask += 4; } } @@ -162,10 +162,10 @@ paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, UINT8* mask = (UINT8*) (imMask->image32[y+sy] + sx); for (x = 0; x < xsize; x++) { UINT8 a = mask[3]; - out[0] = BLEND(a, out[0], in[0], tmp1, tmp2); - out[1] = BLEND(a, out[1], in[1], tmp1, tmp2); - out[2] = BLEND(a, out[2], in[2], tmp1, tmp2); - out[3] = BLEND(a, out[3], in[3], tmp1, tmp2); + out[0] = BLEND(a, out[0], in[0], tmp1); + out[1] = BLEND(a, out[1], in[1], tmp1); + out[2] = BLEND(a, out[2], in[2], tmp1); + out[3] = BLEND(a, out[3], in[3], tmp1); out += 4; in += 4; mask += 4; } } @@ -373,7 +373,7 @@ fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, /* fill with mode "L" matte */ int x, y, i; - unsigned int tmp1, tmp2; + unsigned int tmp1; if (imOut->image8) { @@ -381,7 +381,7 @@ fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, UINT8* out = imOut->image8[y+dy]+dx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - *out = BLEND(*mask, *out, ink[0], tmp1, tmp2); + *out = BLEND(*mask, *out, ink[0], tmp1); out++, mask++; } } @@ -393,7 +393,7 @@ fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; for (x = 0; x < xsize; x++) { for (i = 0; i < pixelsize; i++) { - *out = BLEND(*mask, *out, ink[i], tmp1, tmp2); + *out = BLEND(*mask, *out, ink[i], tmp1); out++; } mask++; @@ -410,7 +410,7 @@ fill_mask_RGBA(Imaging imOut, const UINT8* ink, Imaging imMask, /* fill with mode "RGBA" matte */ int x, y, i; - unsigned int tmp1, tmp2; + unsigned int tmp1; if (imOut->image8) { @@ -419,7 +419,7 @@ fill_mask_RGBA(Imaging imOut, const UINT8* ink, Imaging imMask, UINT8* out = imOut->image8[y+dy]+dx; UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; for (x = 0; x < xsize; x++) { - *out = BLEND(*mask, *out, ink[0], tmp1, tmp2); + *out = BLEND(*mask, *out, ink[0], tmp1); out++, mask += 4; } } @@ -433,7 +433,7 @@ fill_mask_RGBA(Imaging imOut, const UINT8* ink, Imaging imMask, UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; for (x = 0; x < xsize; x++) { for (i = 0; i < pixelsize; i++) { - *out = BLEND(*mask, *out, ink[i], tmp1, tmp2); + *out = BLEND(*mask, *out, ink[i], tmp1); out++; } mask += 4; From c90eecd8f3d86fd3760adec88f14b365bbbf99d8 Mon Sep 17 00:00:00 2001 From: homm Date: Sat, 3 Sep 2016 17:42:35 +0300 Subject: [PATCH 4/6] paste test for different mask formats --- Tests/test_image_paste.py | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Tests/test_image_paste.py diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py new file mode 100644 index 000000000..ddcf7a655 --- /dev/null +++ b/Tests/test_image_paste.py @@ -0,0 +1,112 @@ +from helper import unittest, PillowTestCase, hopper + +from PIL import Image + + +class TestImagingPaste(PillowTestCase): + masks = {} + + def mask_1(self, size): + mask = Image.new('1', size) + px = mask.load() + for y in range(mask.height): + for x in range(mask.width): + px[y, x] = (x + y) % 2 + return mask + + def mask_L(self, size): + mask = Image.new('L', size) + px = mask.load() + for y in range(mask.height): + for x in range(mask.width): + px[y, x] = (x + y) % 255 + return mask + + def mask_RGBA(self, size): + mask = Image.new('RGB', size, 'red').split() + return Image.merge('RGBA', mask + (self.mask_L(size),)) + + def mask_RGBa(self, size): + mask = Image.new('RGB', size, 'red').split() + return Image.merge('RGBa', mask + (self.mask_L(size),)) + + def test_image_solid(self): + for mode in ('L', 'RGB'): + im = Image.new(mode, (200, 200), 'red') + im2 = hopper(mode) + + im.paste(im2, (0, 0)) + + im = im.crop((0, 0) + im2.size) + self.assert_image_equal(im, im2) + + def test_image_mask_1(self): + for mode in ('L', 'RGB'): + im = Image.new(mode, (200, 200), 'red') + im2 = hopper(mode) + mask = self.mask_1(im2.size) + + im.paste(im2, (0, 0), mask) + + def test_image_mask_L(self): + for mode in ('L', 'RGB'): + im = Image.new(mode, (200, 200), 'red') + im2 = hopper(mode) + mask = self.mask_L(im2.size) + + im.paste(im2, (0, 0), mask) + + def test_image_mask_RGBA(self): + for mode in ('L', 'RGB'): + im = Image.new(mode, (200, 200), 'red') + im2 = hopper(mode) + mask = self.mask_RGBA(im2.size) + + im.paste(im2, (0, 0), mask) + + def test_image_mask_RGBa(self): + for mode in ('L', 'RGB'): + im = Image.new(mode, (200, 200), 'red') + im2 = hopper(mode) + mask = self.mask_RGBa(im2.size) + + im.paste(im2, (0, 0), mask) + + def test_color_solid(self): + for mode in ('L', 'RGB'): + im = hopper(mode) + im2 = 'red' + + im.paste(im2) + + def test_color_mask_1(self): + for mode in ('L', 'RGB'): + im = hopper(mode) + im2 = 'red' + mask = self.mask_1(im.size) + + im.paste(im2, (0, 0), mask) + + def test_color_mask_L(self): + for mode in ('L', 'RGB'): + im = hopper(mode) + im2 = 'red' + mask = self.mask_L(im.size) + + im.paste(im2, (0, 0), mask) + + def test_color_mask_RGBA(self): + for mode in ('L', 'RGB'): + im = hopper(mode) + im2 = 'red' + mask = self.mask_RGBA(im.size) + + im.paste(im2, (0, 0), mask) + + def test_color_mask_RGBa(self): + for mode in ('L', 'RGB'): + im = hopper(mode) + im2 = 'red' + mask = self.mask_RGBa(im.size) + + im.paste(im2, (0, 0), mask) From 7faf18ccde2843a0abb6cfe2c7779000822ce7c2 Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 19 Sep 2016 02:49:41 +0300 Subject: [PATCH 5/6] better paste tests --- Tests/test_image_paste.py | 256 +++++++++++++++++++++++++++++--------- 1 file changed, 200 insertions(+), 56 deletions(-) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index ddcf7a655..f0f2b8773 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -1,112 +1,256 @@ -from helper import unittest, PillowTestCase, hopper +from helper import PillowTestCase from PIL import Image class TestImagingPaste(PillowTestCase): masks = {} + size = 128 - def mask_1(self, size): - mask = Image.new('1', size) + def assert_9points_image(self, im, expected): + expected = [ + point[0] + if im.mode == 'L' else + point[:len(im.mode)] + for point in expected + ] + px = im.load() + actual = [ + px[0, 0], + px[self.size // 2, 0], + px[self.size - 1, 0], + px[0, self.size // 2], + px[self.size // 2, self.size // 2], + px[self.size - 1, self.size // 2], + px[0, self.size - 1], + px[self.size // 2, self.size - 1], + px[self.size - 1, self.size - 1], + ] + self.assertEqual(actual, expected) + + def mask_1(self): + mask = Image.new('1', (self.size, self.size)) px = mask.load() for y in range(mask.height): for x in range(mask.width): px[y, x] = (x + y) % 2 return mask - def mask_L(self, size): - mask = Image.new('L', size) - px = mask.load() - for y in range(mask.height): - for x in range(mask.width): + def mask_L(self): + return self.gradient_L().transpose(Image.ROTATE_270) + + def gradient_L(self): + gradient = Image.new('L', (self.size, self.size)) + px = gradient.load() + for y in range(gradient.height): + for x in range(gradient.width): px[y, x] = (x + y) % 255 - return mask + return gradient - def mask_RGBA(self, size): - mask = Image.new('RGB', size, 'red').split() - return Image.merge('RGBA', mask + (self.mask_L(size),)) + def gradient_RGB(self): + return Image.merge('RGB', [ + self.gradient_L(), + self.gradient_L().transpose(Image.ROTATE_90), + self.gradient_L().transpose(Image.ROTATE_180), + ]) - def mask_RGBa(self, size): - mask = Image.new('RGB', size, 'red').split() - return Image.merge('RGBa', mask + (self.mask_L(size),)) + def gradient_RGBA(self): + return Image.merge('RGBA', [ + self.gradient_L(), + self.gradient_L().transpose(Image.ROTATE_90), + self.gradient_L().transpose(Image.ROTATE_180), + self.gradient_L().transpose(Image.ROTATE_270), + ]) + + def gradient_RGBa(self): + return Image.merge('RGBa', [ + self.gradient_L(), + self.gradient_L().transpose(Image.ROTATE_90), + self.gradient_L().transpose(Image.ROTATE_180), + self.gradient_L().transpose(Image.ROTATE_270), + ]) def test_image_solid(self): - for mode in ('L', 'RGB'): + for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'red') - im2 = hopper(mode) + im2 = getattr(self, 'gradient_' + mode)() - im.paste(im2, (0, 0)) + im.paste(im2, (12, 23)) - im = im.crop((0, 0) + im2.size) + im = im.crop((12, 23, im2.width + 12, im2.height + 23)) self.assert_image_equal(im, im2) def test_image_mask_1(self): - for mode in ('L', 'RGB'): - im = Image.new(mode, (200, 200), 'red') - im2 = hopper(mode) - mask = self.mask_1(im2.size) + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), 'white') + im2 = getattr(self, 'gradient_' + mode)() + mask = self.mask_1() im.paste(im2, (0, 0), mask) + self.assert_9points_image(im, [ + (255, 255, 255, 255), + (255, 255, 255, 255), + (127, 254, 127, 0), + (255, 255, 255, 255), + (255, 255, 255, 255), + (191, 190, 63, 64), + (127, 0, 127, 254), + (191, 64, 63, 190), + (255, 255, 255, 255), + ]) + def test_image_mask_L(self): - for mode in ('L', 'RGB'): - im = Image.new(mode, (200, 200), 'red') - im2 = hopper(mode) - mask = self.mask_L(im2.size) + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), 'white') + im2 = getattr(self, 'gradient_' + mode)() + mask = self.mask_L() im.paste(im2, (0, 0), mask) + im.save('_test_image_mask_L {}.png'.format(mode)) + + self.assert_9points_image(im, [ + (128, 191, 255, 191), + (208, 239, 239, 208), + (255, 255, 255, 255), + (112, 111, 206, 207), + (192, 191, 191, 191), + (239, 239, 207, 207), + (128, 1, 128, 254), + (207, 113, 112, 207), + (255, 191, 128, 191), + ]) def test_image_mask_RGBA(self): - for mode in ('L', 'RGB'): - im = Image.new(mode, (200, 200), 'red') - im2 = hopper(mode) - mask = self.mask_RGBA(im2.size) + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), 'white') + im2 = getattr(self, 'gradient_' + mode)() + mask = self.gradient_RGBA() im.paste(im2, (0, 0), mask) + im.save('_test_image_mask_RGBA {}.png'.format(mode)) + + self.assert_9points_image(im, [ + (128, 191, 255, 191), + (208, 239, 239, 208), + (255, 255, 255, 255), + (112, 111, 206, 207), + (192, 191, 191, 191), + (239, 239, 207, 207), + (128, 1, 128, 254), + (207, 113, 112, 207), + (255, 191, 128, 191), + ]) def test_image_mask_RGBa(self): - for mode in ('L', 'RGB'): - im = Image.new(mode, (200, 200), 'red') - im2 = hopper(mode) - mask = self.mask_RGBa(im2.size) + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), 'white') + im2 = getattr(self, 'gradient_' + mode)() + mask = self.gradient_RGBa() im.paste(im2, (0, 0), mask) + im.save('_test_image_mask_RGBA {}.png'.format(mode)) + + self.assert_9points_image(im, [ + (128, 255, 126, 255), + (0, 127, 126, 255), + (126, 253, 126, 255), + (128, 127, 254, 255), + (0, 255, 254, 255), + (126, 125, 254, 255), + (128, 1, 128, 255), + (0, 129, 128, 255), + (126, 255, 128, 255), + ]) def test_color_solid(self): - for mode in ('L', 'RGB'): - im = hopper(mode) - im2 = 'red' + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), 'black') - im.paste(im2) + rect = (12, 23, 128 + 12, 128 + 23) + im.paste('white', rect) + + hist = im.crop(rect).histogram() + while hist: + head, hist = hist[:256], hist[256:] + self.assertEqual(head[255], 128 * 128) + self.assertEqual(sum(head[:255]), 0) def test_color_mask_1(self): - for mode in ('L', 'RGB'): - im = hopper(mode) - im2 = 'red' - mask = self.mask_1(im.size) + for mode in ('RGBA', 'RGB', 'L'): + im = Image.new(mode, (200, 200), (50, 60, 70, 80)[:len(mode)]) + mask = self.mask_1() - im.paste(im2, (0, 0), mask) + im.paste((10, 20, 30, 40)[:len(mode)], (0, 0), mask) + + self.assert_9points_image(im, [ + (50, 60, 70, 80), + (50, 60, 70, 80), + (10, 20, 30, 40), + (50, 60, 70, 80), + (50, 60, 70, 80), + (10, 20, 30, 40), + (10, 20, 30, 40), + (10, 20, 30, 40), + (50, 60, 70, 80), + ]) def test_color_mask_L(self): - for mode in ('L', 'RGB'): - im = hopper(mode) - im2 = 'red' - mask = self.mask_L(im.size) + for mode in ('RGBA', 'RGB', 'L'): + im = getattr(self, 'gradient_' + mode)() + im2 = 'white' + mask = self.mask_L() im.paste(im2, (0, 0), mask) + self.assert_9points_image(im, [ + (127, 191, 254, 191), + (111, 207, 206, 110), + (127, 254, 127, 0), + (207, 207, 239, 239), + (191, 191, 190, 191), + (207, 206, 111, 112), + (254, 254, 254, 255), + (239, 206, 206, 238), + (254, 191, 127, 191), + ]) + def test_color_mask_RGBA(self): - for mode in ('L', 'RGB'): - im = hopper(mode) - im2 = 'red' - mask = self.mask_RGBA(im.size) + for mode in ('RGBA', 'RGB', 'L'): + im = getattr(self, 'gradient_' + mode)() + im2 = 'white' + mask = self.gradient_RGBA() im.paste(im2, (0, 0), mask) + self.assert_9points_image(im, [ + (127, 191, 254, 191), + (111, 207, 206, 110), + (127, 254, 127, 0), + (207, 207, 239, 239), + (191, 191, 190, 191), + (207, 206, 111, 112), + (254, 254, 254, 255), + (239, 206, 206, 238), + (254, 191, 127, 191), + ]) + def test_color_mask_RGBa(self): - for mode in ('L', 'RGB'): - im = hopper(mode) - im2 = 'red' - mask = self.mask_RGBa(im.size) + for mode in ('RGBA', 'RGB', 'L'): + im = getattr(self, 'gradient_' + mode)() + im2 = 'white' + mask = self.gradient_RGBa() im.paste(im2, (0, 0), mask) + + self.assert_9points_image(im, [ + (255, 63, 126, 63), + (47, 143, 142, 46), + (126, 253, 126, 255), + (15, 15, 47, 47), + (63, 63, 62, 63), + (142, 141, 46, 47), + (255, 255, 255, 0), + (48, 15, 15, 47), + (126, 63, 255, 63) + ]) From cdd06249453f02176facb0f07f7126f74c48f628 Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 19 Sep 2016 02:59:34 +0300 Subject: [PATCH 6/6] improve tests --- Tests/helper.py | 9 +++++ Tests/test_image_paste.py | 82 +++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 0dc39e91f..9f1501249 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -238,3 +238,12 @@ if sys.platform == 'win32': IMCONVERT = os.path.join(IMCONVERT, 'convert.exe') else: IMCONVERT = 'convert' + + +class cached_property(object): + def __init__(self, func): + self.func = func + + def __get__(self, instance, cls=None): + result = instance.__dict__[self.func.__name__] = self.func(instance) + return result diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index f0f2b8773..f3fe9a2af 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -1,4 +1,4 @@ -from helper import PillowTestCase +from helper import PillowTestCase, cached_property from PIL import Image @@ -28,6 +28,7 @@ class TestImagingPaste(PillowTestCase): ] self.assertEqual(actual, expected) + @cached_property def mask_1(self): mask = Image.new('1', (self.size, self.size)) px = mask.load() @@ -36,9 +37,11 @@ class TestImagingPaste(PillowTestCase): px[y, x] = (x + y) % 2 return mask + @cached_property def mask_L(self): - return self.gradient_L().transpose(Image.ROTATE_270) + return self.gradient_L.transpose(Image.ROTATE_270) + @cached_property def gradient_L(self): gradient = Image.new('L', (self.size, self.size)) px = gradient.load() @@ -47,33 +50,36 @@ class TestImagingPaste(PillowTestCase): px[y, x] = (x + y) % 255 return gradient + @cached_property def gradient_RGB(self): return Image.merge('RGB', [ - self.gradient_L(), - self.gradient_L().transpose(Image.ROTATE_90), - self.gradient_L().transpose(Image.ROTATE_180), + self.gradient_L, + self.gradient_L.transpose(Image.ROTATE_90), + self.gradient_L.transpose(Image.ROTATE_180), ]) + @cached_property def gradient_RGBA(self): return Image.merge('RGBA', [ - self.gradient_L(), - self.gradient_L().transpose(Image.ROTATE_90), - self.gradient_L().transpose(Image.ROTATE_180), - self.gradient_L().transpose(Image.ROTATE_270), + self.gradient_L, + self.gradient_L.transpose(Image.ROTATE_90), + self.gradient_L.transpose(Image.ROTATE_180), + self.gradient_L.transpose(Image.ROTATE_270), ]) + @cached_property def gradient_RGBa(self): return Image.merge('RGBa', [ - self.gradient_L(), - self.gradient_L().transpose(Image.ROTATE_90), - self.gradient_L().transpose(Image.ROTATE_180), - self.gradient_L().transpose(Image.ROTATE_270), + self.gradient_L, + self.gradient_L.transpose(Image.ROTATE_90), + self.gradient_L.transpose(Image.ROTATE_180), + self.gradient_L.transpose(Image.ROTATE_270), ]) def test_image_solid(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'red') - im2 = getattr(self, 'gradient_' + mode)() + im2 = getattr(self, 'gradient_' + mode) im.paste(im2, (12, 23)) @@ -83,10 +89,9 @@ class TestImagingPaste(PillowTestCase): def test_image_mask_1(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'white') - im2 = getattr(self, 'gradient_' + mode)() - mask = self.mask_1() + im2 = getattr(self, 'gradient_' + mode) - im.paste(im2, (0, 0), mask) + im.paste(im2, (0, 0), self.mask_1) self.assert_9points_image(im, [ (255, 255, 255, 255), @@ -103,11 +108,9 @@ class TestImagingPaste(PillowTestCase): def test_image_mask_L(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'white') - im2 = getattr(self, 'gradient_' + mode)() - mask = self.mask_L() + im2 = getattr(self, 'gradient_' + mode) - im.paste(im2, (0, 0), mask) - im.save('_test_image_mask_L {}.png'.format(mode)) + im.paste(im2, (0, 0), self.mask_L) self.assert_9points_image(im, [ (128, 191, 255, 191), @@ -124,11 +127,9 @@ class TestImagingPaste(PillowTestCase): def test_image_mask_RGBA(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'white') - im2 = getattr(self, 'gradient_' + mode)() - mask = self.gradient_RGBA() + im2 = getattr(self, 'gradient_' + mode) - im.paste(im2, (0, 0), mask) - im.save('_test_image_mask_RGBA {}.png'.format(mode)) + im.paste(im2, (0, 0), self.gradient_RGBA) self.assert_9points_image(im, [ (128, 191, 255, 191), @@ -145,11 +146,9 @@ class TestImagingPaste(PillowTestCase): def test_image_mask_RGBa(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), 'white') - im2 = getattr(self, 'gradient_' + mode)() - mask = self.gradient_RGBa() + im2 = getattr(self, 'gradient_' + mode) - im.paste(im2, (0, 0), mask) - im.save('_test_image_mask_RGBA {}.png'.format(mode)) + im.paste(im2, (0, 0), self.gradient_RGBa) self.assert_9points_image(im, [ (128, 255, 126, 255), @@ -179,9 +178,9 @@ class TestImagingPaste(PillowTestCase): def test_color_mask_1(self): for mode in ('RGBA', 'RGB', 'L'): im = Image.new(mode, (200, 200), (50, 60, 70, 80)[:len(mode)]) - mask = self.mask_1() + color = (10, 20, 30, 40)[:len(mode)] - im.paste((10, 20, 30, 40)[:len(mode)], (0, 0), mask) + im.paste(color, (0, 0), self.mask_1) self.assert_9points_image(im, [ (50, 60, 70, 80), @@ -197,11 +196,10 @@ class TestImagingPaste(PillowTestCase): def test_color_mask_L(self): for mode in ('RGBA', 'RGB', 'L'): - im = getattr(self, 'gradient_' + mode)() - im2 = 'white' - mask = self.mask_L() + im = getattr(self, 'gradient_' + mode).copy() + color = 'white' - im.paste(im2, (0, 0), mask) + im.paste(color, (0, 0), self.mask_L) self.assert_9points_image(im, [ (127, 191, 254, 191), @@ -217,11 +215,10 @@ class TestImagingPaste(PillowTestCase): def test_color_mask_RGBA(self): for mode in ('RGBA', 'RGB', 'L'): - im = getattr(self, 'gradient_' + mode)() - im2 = 'white' - mask = self.gradient_RGBA() + im = getattr(self, 'gradient_' + mode).copy() + color = 'white' - im.paste(im2, (0, 0), mask) + im.paste(color, (0, 0), self.gradient_RGBA) self.assert_9points_image(im, [ (127, 191, 254, 191), @@ -237,11 +234,10 @@ class TestImagingPaste(PillowTestCase): def test_color_mask_RGBa(self): for mode in ('RGBA', 'RGB', 'L'): - im = getattr(self, 'gradient_' + mode)() - im2 = 'white' - mask = self.gradient_RGBa() + im = getattr(self, 'gradient_' + mode).copy() + color = 'white' - im.paste(im2, (0, 0), mask) + im.paste(color, (0, 0), self.gradient_RGBa) self.assert_9points_image(im, [ (255, 63, 126, 63),