From 8b6ad4a4714f5bdd5128a2564b1589712c993567 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 26 Nov 2019 03:36:58 +0300 Subject: [PATCH] tests for supported modes --- Tests/test_image_reduce.py | 61 ++++++++++++++++++++++++++++++++++++++ src/PIL/Image.py | 8 +++++ src/libImaging/Reduce.c | 6 ++-- 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 Tests/test_image_reduce.py diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py new file mode 100644 index 000000000..73bfa6498 --- /dev/null +++ b/Tests/test_image_reduce.py @@ -0,0 +1,61 @@ +from PIL import Image + +from .helper import PillowTestCase, hopper + + +class TestImageReduce(PillowTestCase): + # There are several internal implementations + remarkable_factors = [ + 1, 2, 3, 4, 5, 6, # special implementations + (1, 2), (1, 3), (1, 4), # 1xN implementation + (2, 1), (3, 1), (4, 1), # Nx1 implementation + # general implementation with different paths + (4, 6), (5, 6), (4, 7), (5, 7), + ] + + def test_args(self): + im = Image.new('L', (10, 10)) + + self.assertEqual((4, 4), im.reduce(3).size) + self.assertEqual((4, 10), im.reduce((3, 1)).size) + self.assertEqual((10, 4), im.reduce((1, 3)).size) + + with self.assertRaises(ValueError): + im.reduce(0) + with self.assertRaises(TypeError): + im.reduce(2.0) + with self.assertRaises(ValueError): + im.reduce((0, 10)) + + def check_correctness(self, im, factor): + if not isinstance(factor, (list, tuple)): + factor = (factor, factor) + + # Image.reduce() should works very similar to Image.resize(BOX) + # when image size is dividable by the factor. + desired_size = (im.width // factor[0], im.height // factor[1]) + im = im.crop((0, 0, desired_size[0] * factor[0], desired_size[1] * factor[1])) + + reduced = im.reduce(factor) + resized = im.resize(desired_size, Image.BOX) + + epsilon = 0.5 * len(reduced.getbands()) + self.assert_image_similar(reduced, resized, epsilon) + + def test_mode_RGB(self): + im = hopper('RGB') + for factor in self.remarkable_factors: + self.check_correctness(im, factor) + + def test_mode_LA(self): + im = Image.open("Tests/images/transparent.png").convert('LA') + for factor in self.remarkable_factors: + print(factor) + self.check_correctness(im, factor) + + def test_mode_RGBA(self): + im = Image.open("Tests/images/transparent.png").convert('RGBA') + for factor in self.remarkable_factors: + print(factor) + self.check_correctness(im, factor) + diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ae683de3a..9df67ff39 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1899,6 +1899,14 @@ class Image(object): if not isinstance(factor, (list, tuple)): factor = (factor, factor) + if factor == (1, 1): + return self.copy() + + if self.mode in ["LA", "RGBA"]: + im = self.convert(self.mode[:-1] + "a") + im = im.reduce(factor) + return im.convert(self.mode) + self.load() return self._new(self.im.reduce(factor)) diff --git a/src/libImaging/Reduce.c b/src/libImaging/Reduce.c index 26e695a94..37390ac22 100644 --- a/src/libImaging/Reduce.c +++ b/src/libImaging/Reduce.c @@ -365,7 +365,7 @@ ImagingReduceNx1(Imaging imOut, Imaging imIn, int xscale) void ImagingReduce2x2(Imaging imOut, Imaging imIn) { - /* Fast special case for xscale = 2 and yscale = 2. + /* Optimized implementation for xscale = 2 and yscale = 2. */ int xscale = 2, yscale = 2; int x, y; @@ -435,7 +435,7 @@ ImagingReduce2x2(Imaging imOut, Imaging imIn) void ImagingReduce3x3(Imaging imOut, Imaging imIn) { - /* Fast special case for xscale = 3 and yscale = 3. + /* Optimized implementation for xscale = 3 and yscale = 3. */ int xscale = 3, yscale = 3; int x, y; @@ -519,7 +519,7 @@ ImagingReduce3x3(Imaging imOut, Imaging imIn) void ImagingReduce4x4(Imaging imOut, Imaging imIn) { - /* Fast special case for xscale = 4 and yscale = 4. + /* Optimized implementation for xscale = 4 and yscale = 4. */ int xscale = 4, yscale = 4; int x, y;