From 8180b0f4c39e1fef552aa08701e552af2597081b Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 06:52:19 +0300 Subject: [PATCH 01/10] ResampleVertical first implementation --- libImaging/Resample.c | 69 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 8ad1c9d97..48f8b8f20 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -255,6 +255,75 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) } +Imaging +ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) +{ + ImagingSectionCookie cookie; + Imaging imOut; + int ss0, ss1, ss2, ss3; + int xx, yy, y, kmax, ymin, ymax; + int *xbounds; + int *k, *kk; + double *prekk; + + + kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &prekk); + if ( ! kmax) { + return (Imaging) ImagingError_MemoryError(); + } + + kk = malloc(ysize * kmax * sizeof(int)); + if ( ! kk) { + free(xbounds); + free(prekk); + return (Imaging) ImagingError_MemoryError(); + } + + for (y = 0; y < ysize * kmax; y++) { + kk[y] = (int) (0.5 + prekk[y] * (1 << PRECISION_BITS)); + } + + free(prekk); + + imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); + if ( ! imOut) { + free(kk); + free(xbounds); + return NULL; + } + + ImagingSectionEnter(&cookie); + if (imIn->image8) { + } else if (imIn->type == IMAGING_TYPE_UINT8) { + if (imIn->bands == 2) { + } else if (imIn->bands == 3) { + for (yy = 0; yy < ysize; yy++) { + k = &kk[yy * kmax]; + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1); + for (y = 0; y < ymax; y++) { + ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; + ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y]; + ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; + } + imOut->image[yy][xx*4 + 0] = clip8(ss0); + imOut->image[yy][xx*4 + 1] = clip8(ss1); + imOut->image[yy][xx*4 + 2] = clip8(ss2); + } + } + } else { + } + } + + ImagingSectionLeave(&cookie); + free(kk); + free(xbounds); + return imOut; +} + + Imaging ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) { From a1b12f792af64a142b777a744f515fcc856501ba Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 07:09:49 +0300 Subject: [PATCH 02/10] improve CoreResampleAccuracy test. Use all bands configurations --- Tests/test_image_resample.py | 121 ++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 7c5b3a770..353e0d603 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -35,7 +35,7 @@ class TestImagingResampleVulnerability(PillowTestCase): class TestImagingCoreResampleAccuracy(PillowTestCase): - def make_case(self, size, color): + def make_case(self, mode, size, color): """Makes a sample image with two dark and two bright squares. For example: e0 e0 1f 1f @@ -50,7 +50,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): rectangle = ImageDraw.Draw(i).rectangle rectangle((0, 0, size[0] // 2 - 1, size[1] // 2 - 1), bright) rectangle((size[0] // 2, size[1] // 2, size[0], size[1]), bright) - return i + return i.convert(mode) def make_sample(self, data, size): """Restores a sample image from given data string which contains @@ -70,17 +70,16 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): return sample def check_case(self, case, sample): - for channel in case.split(): - s_px = sample.load() - c_px = channel.load() - for y in range(case.size[1]): - for x in range(case.size[0]): - if c_px[x, y] != s_px[x, y]: - message = '\nHave: \n{}\n\nExpected: \n{}'.format( - self.serialize_image(channel), - self.serialize_image(sample), - ) - self.assertEqual(s_px[x, y], c_px[x, y], message) + s_px = sample.load() + c_px = case.load() + for y in range(case.size[1]): + for x in range(case.size[0]): + if c_px[x, y] != s_px[x, y]: + message = '\nHave: \n{}\n\nExpected: \n{}'.format( + self.serialize_image(case), + self.serialize_image(sample), + ) + self.assertEqual(s_px[x, y], c_px[x, y], message) def serialize_image(self, image): s_px = image.load() @@ -93,61 +92,67 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): ) def test_reduce_bilinear(self): - case = self.make_case((8, 8), 0xe1) - data = ('e1 c9' - 'c9 b7') - self.check_case( - case.resize((4, 4), Image.BILINEAR), - self.make_sample(data, (4, 4))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (8, 8), 0xe1) + case = case.resize((4, 4), Image.BILINEAR) + data = ('e1 c9' + 'c9 b7') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) def test_reduce_bicubic(self): - case = self.make_case((12, 12), 0xe1) - data = ('e1 e3 d4' - 'e3 e5 d6' - 'd4 d6 c9') - self.check_case( - case.resize((6, 6), Image.BICUBIC), - self.make_sample(data, (6, 6))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (12, 12), 0xe1) + case = case.resize((6, 6), Image.BICUBIC) + data = ('e1 e3 d4' + 'e3 e5 d6' + 'd4 d6 c9') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (6, 6))) def test_reduce_lanczos(self): - case = self.make_case((16, 16), 0xe1) - data = ('e1 e0 e4 d7' - 'e0 df e3 d6' - 'e4 e3 e7 da' - 'd7 d6 d9 ce') - self.check_case( - case.resize((8, 8), Image.LANCZOS), - self.make_sample(data, (8, 8))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (16, 16), 0xe1) + case = case.resize((8, 8), Image.LANCZOS) + data = ('e1 e0 e4 d7' + 'e0 df e3 d6' + 'e4 e3 e7 da' + 'd7 d6 d9 ce') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_bilinear(self): - case = self.make_case((2, 2), 0xe1) - data = ('e1 b0' - 'b0 98') - self.check_case( - case.resize((4, 4), Image.BILINEAR), - self.make_sample(data, (4, 4))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (2, 2), 0xe1) + case = case.resize((4, 4), Image.BILINEAR) + data = ('e1 b0' + 'b0 98') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) def test_enlarge_bicubic(self): - case = self.make_case((4, 4), 0xe1) - data = ('e1 e5 ee b9' - 'e5 e9 f3 bc' - 'ee f3 fd c1' - 'b9 bc c1 a2') - self.check_case( - case.resize((8, 8), Image.BICUBIC), - self.make_sample(data, (8, 8))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (4, 4), 0xe1) + case = case.resize((8, 8), Image.BICUBIC) + data = ('e1 e5 ee b9' + 'e5 e9 f3 bc' + 'ee f3 fd c1' + 'b9 bc c1 a2') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_lanczos(self): - case = self.make_case((6, 6), 0xe1) - data = ('e1 e0 db ed f5 b8' - 'e0 df da ec f3 b7' - 'db db d6 e7 ee b5' - 'ed ec e6 fb ff bf' - 'f5 f4 ee ff ff c4' - 'b8 b7 b4 bf c4 a0') - self.check_case( - case.resize((12, 12), Image.LANCZOS), - self.make_sample(data, (12, 12))) + for mode in ['RGBX', 'RGB', 'L']: + case = self.make_case(mode, (6, 6), 0xe1) + case = case.resize((12, 12), Image.LANCZOS) + data = ('e1 e0 db ed f5 b8' + 'e0 df da ec f3 b7' + 'db db d6 e7 ee b5' + 'ed ec e6 fb ff bf' + 'f5 f4 ee ff ff c4' + 'b8 b7 b4 bf c4 a0') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (12, 12))) class CoreResampleConsistencyTest(PillowTestCase): From 5cd8c35c820c7beaf0ddd6eb1213e4b412d9d19c Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 07:12:01 +0300 Subject: [PATCH 03/10] complete ImagingResampleVertical_8bpc implementation --- libImaging/Resample.c | 49 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 48f8b8f20..8048e5955 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -200,13 +200,13 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; - ss0 = ss1 = 1 << (PRECISION_BITS -1); + ss0 = ss3 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; - ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; + ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; } imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 3] = clip8(ss1); + imOut->image[yy][xx*4 + 3] = clip8(ss3); } } } else if (imIn->bands == 3) { @@ -294,8 +294,33 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) ImagingSectionEnter(&cookie); if (imIn->image8) { + for (yy = 0; yy < ysize; yy++) { + k = &kk[yy * kmax]; + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss0 = 1 << (PRECISION_BITS -1); + for (y = 0; y < ymax; y++) + ss0 += ((UINT8) imIn->image8[y + ymin][xx]) * k[y]; + imOut->image8[yy][xx] = clip8(ss0); + } + } } else if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == 2) { + for (yy = 0; yy < ysize; yy++) { + k = &kk[yy * kmax]; + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss0 = ss3 = 1 << (PRECISION_BITS -1); + for (y = 0; y < ymax; y++) { + ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; + ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; + } + imOut->image[yy][xx*4 + 0] = clip8(ss0); + imOut->image[yy][xx*4 + 3] = clip8(ss3); + } + } } else if (imIn->bands == 3) { for (yy = 0; yy < ysize; yy++) { k = &kk[yy * kmax]; @@ -314,6 +339,24 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) } } } else { + for (yy = 0; yy < ysize; yy++) { + k = &kk[yy * kmax]; + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1); + for (y = 0; y < ymax; y++) { + ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; + ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y]; + ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; + ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; + } + imOut->image[yy][xx*4 + 0] = clip8(ss0); + imOut->image[yy][xx*4 + 1] = clip8(ss1); + imOut->image[yy][xx*4 + 2] = clip8(ss2); + imOut->image[yy][xx*4 + 3] = clip8(ss3); + } + } } } From c051f1d79d088a76eeaf44a0a876697c7ae88a1e Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 07:20:38 +0300 Subject: [PATCH 04/10] ImagingResampleVertical_32bpc implementation --- libImaging/Resample.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 8048e5955..b5a0709cd 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -427,6 +427,66 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) } +Imaging +ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) +{ + ImagingSectionCookie cookie; + Imaging imOut; + double ss; + int xx, yy, y, kmax, ymin, ymax; + int *xbounds; + double *k, *kk; + + kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &kk); + if ( ! kmax) { + return (Imaging) ImagingError_MemoryError(); + } + + imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); + if ( ! imOut) { + free(kk); + free(xbounds); + return NULL; + } + + ImagingSectionEnter(&cookie); + switch(imIn->type) { + case IMAGING_TYPE_INT32: + for (yy = 0; yy < ysize; yy++) { + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * kmax]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss = 0.0; + for (y = 0; y < ymax; y++) + ss += IMAGING_PIXEL_I(imIn, xx, y + ymin) * k[y]; + IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); + } + } + break; + + case IMAGING_TYPE_FLOAT32: + for (yy = 0; yy < ysize; yy++) { + ymin = xbounds[yy * 2 + 0]; + ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * kmax]; + for (xx = 0; xx < imOut->xsize; xx++) { + ss = 0.0; + for (y = 0; y < ymax; y++) + ss += IMAGING_PIXEL_F(imIn, xx, y + ymin) * k[y]; + IMAGING_PIXEL_F(imOut, xx, yy) = ss; + } + } + break; + } + + ImagingSectionLeave(&cookie); + free(kk); + free(xbounds); + return imOut; +} + + Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter) { From 4ea0c834503ac3b2eada1d05c958a6f79055ad00 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 07:24:22 +0300 Subject: [PATCH 05/10] do not rotate image between passes --- libImaging/Resample.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index b5a0709cd..ccc3a22bd 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -490,10 +490,11 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter) { - Imaging imTemp1, imTemp2, imTemp3; + Imaging imTemp; Imaging imOut; struct filter *filterp; Imaging (*ResampleHorizontal)(Imaging imIn, int xsize, struct filter *filterp); + Imaging (*ResampleVertical)(Imaging imIn, int xsize, struct filter *filterp); if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) return (Imaging) ImagingError_ModeError(); @@ -502,14 +503,17 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) return (Imaging) ImagingError_ModeError(); } else if (imIn->image8) { ResampleHorizontal = ImagingResampleHorizontal_8bpc; + ResampleVertical = ImagingResampleVertical_8bpc; } else { switch(imIn->type) { case IMAGING_TYPE_UINT8: ResampleHorizontal = ImagingResampleHorizontal_8bpc; + ResampleVertical = ImagingResampleVertical_8bpc; break; case IMAGING_TYPE_INT32: case IMAGING_TYPE_FLOAT32: ResampleHorizontal = ImagingResampleHorizontal_32bpc; + ResampleVertical = ImagingResampleVertical_32bpc; break; default: return (Imaging) ImagingError_ModeError(); @@ -534,25 +538,13 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) } /* two-pass resize, first pass */ - imTemp1 = ResampleHorizontal(imIn, xsize, filterp); - if ( ! imTemp1) - return NULL; - - /* transpose image once */ - imTemp2 = ImagingTransposeToNew(imTemp1); - ImagingDelete(imTemp1); - if ( ! imTemp2) + imTemp = ResampleHorizontal(imIn, xsize, filterp); + if ( ! imTemp) return NULL; /* second pass */ - imTemp3 = ResampleHorizontal(imTemp2, ysize, filterp); - ImagingDelete(imTemp2); - if ( ! imTemp3) - return NULL; - - /* transpose result */ - imOut = ImagingTransposeToNew(imTemp3); - ImagingDelete(imTemp3); + imOut = ResampleVertical(imTemp, ysize, filterp); + ImagingDelete(imTemp); if ( ! imOut) return NULL; From 34afabbf18c3c9ec60309791cced6c3709c35e1b Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 07:24:53 +0300 Subject: [PATCH 06/10] return malloc where clearing is not required --- libImaging/Resample.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index ccc3a22bd..11fc2969e 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -107,7 +107,7 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp, if ( ! kk) return 0; - xbounds = calloc(outSize * 2, sizeof(int)); + xbounds = malloc(outSize * 2 * sizeof(int)); if ( ! xbounds) { free(kk); return 0; @@ -160,7 +160,7 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) return (Imaging) ImagingError_MemoryError(); } - kk = calloc(xsize * kmax, sizeof(int)); + kk = malloc(xsize * kmax * sizeof(int)); if ( ! kk) { free(xbounds); free(prekk); From f09067e45eeecdd85e8d379ddb7f2ea581e08708 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 27 May 2016 10:27:10 +0300 Subject: [PATCH 07/10] add La mode (2 bands) to the test --- PIL/ImageMode.py | 1 + Tests/test_image_resample.py | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 957ef9c78..3587cec24 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -40,6 +40,7 @@ def getmode(mode): _modes[m] = ModeDescriptor(m, bands, basemode, basetype) # extra experimental modes _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") + _modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") # mapping modes _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 353e0d603..9ee9ac48f 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -1,5 +1,5 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image, ImageDraw +from PIL import Image, ImageDraw, ImageMode class TestImagingResampleVulnerability(PillowTestCase): @@ -43,14 +43,12 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): 1f 1f e0 e0 1f 1f e0 e0 """ - dark = (255 - color, 255 - color, 255 - color, 255 - color) - bright = (color, color, color, color) + case = Image.new('L', size, 255 - color) + rectangle = ImageDraw.Draw(case).rectangle + rectangle((0, 0, size[0] // 2 - 1, size[1] // 2 - 1), color) + rectangle((size[0] // 2, size[1] // 2, size[0], size[1]), color) - i = Image.new('RGBX', size, dark) - rectangle = ImageDraw.Draw(i).rectangle - rectangle((0, 0, size[0] // 2 - 1, size[1] // 2 - 1), bright) - rectangle((size[0] // 2, size[1] // 2, size[0], size[1]), bright) - return i.convert(mode) + return Image.merge(mode, [case] * len(mode)) def make_sample(self, data, size): """Restores a sample image from given data string which contains @@ -92,7 +90,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): ) def test_reduce_bilinear(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (8, 8), 0xe1) case = case.resize((4, 4), Image.BILINEAR) data = ('e1 c9' @@ -101,7 +99,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): self.check_case(channel, self.make_sample(data, (4, 4))) def test_reduce_bicubic(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (12, 12), 0xe1) case = case.resize((6, 6), Image.BICUBIC) data = ('e1 e3 d4' @@ -111,7 +109,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): self.check_case(channel, self.make_sample(data, (6, 6))) def test_reduce_lanczos(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (16, 16), 0xe1) case = case.resize((8, 8), Image.LANCZOS) data = ('e1 e0 e4 d7' @@ -122,7 +120,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_bilinear(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (2, 2), 0xe1) case = case.resize((4, 4), Image.BILINEAR) data = ('e1 b0' @@ -131,7 +129,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): self.check_case(channel, self.make_sample(data, (4, 4))) def test_enlarge_bicubic(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (4, 4), 0xe1) case = case.resize((8, 8), Image.BICUBIC) data = ('e1 e5 ee b9' @@ -142,7 +140,7 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_lanczos(self): - for mode in ['RGBX', 'RGB', 'L']: + for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (6, 6), 0xe1) case = case.resize((12, 12), Image.LANCZOS) data = ('e1 e0 db ed f5 b8' From 3b7923c09f97b86f8c916548e237a9f5189addc4 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 3 Jun 2016 12:51:58 +0300 Subject: [PATCH 08/10] add calloc explanation --- libImaging/Resample.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 11fc2969e..d32fe63ae 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -91,7 +91,7 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp, /* determine support size (length of resampling filter) */ support = filterp->support * filterscale; - /* maximum number of coofs */ + /* maximum number of coeffs */ kmax = (int) ceil(support) * 2 + 1; // check for overflow @@ -102,7 +102,9 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp, if (outSize > INT_MAX / (2 * sizeof(double))) return 0; - /* coefficient buffer */ + /* coefficient buffer. kmax elements for each of outSize pixels. + Only xmax number of coefficients are initialized (xmax <= kmax), + other coefficients should be 0, so we are using calloc. */ kk = calloc(outSize * kmax, sizeof(double)); if ( ! kk) return 0; From c8262660f09d10577ef688e6ac4cac41cad434fb Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 3 Jun 2016 13:01:24 +0300 Subject: [PATCH 09/10] add RGBa to ImageMode.getmode() as a temporary --- PIL/ImageMode.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 3587cec24..583fd7eda 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -39,6 +39,7 @@ def getmode(mode): for m, (basemode, basetype, bands) in Image._MODEINFO.items(): _modes[m] = ModeDescriptor(m, bands, basemode, basetype) # extra experimental modes + _modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") _modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") From 12c8cf94f244b3fc1039e71c7a664b97a2045554 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 8 Jun 2016 03:45:08 +0300 Subject: [PATCH 10/10] replace calloc with remaining values emptying --- libImaging/Resample.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index d32fe63ae..23e39a2e4 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -102,10 +102,8 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp, if (outSize > INT_MAX / (2 * sizeof(double))) return 0; - /* coefficient buffer. kmax elements for each of outSize pixels. - Only xmax number of coefficients are initialized (xmax <= kmax), - other coefficients should be 0, so we are using calloc. */ - kk = calloc(outSize * kmax, sizeof(double)); + /* coefficient buffer */ + kk = malloc(outSize * kmax * sizeof(double)); if ( ! kk) return 0; @@ -136,6 +134,10 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp, if (ww != 0.0) k[x] /= ww; } + // Remaining values should stay empty if they are used despite of xmax. + for (; x < kmax; x++) { + k[x] = 0; + } xbounds[xx * 2 + 0] = xmin; xbounds[xx * 2 + 1] = xmax; }