From 69bea50810ebc0b4234de4bf461ce5c76f2af72b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 29 Nov 2016 19:25:49 +0000 Subject: [PATCH 1/4] Allow 0 size images, Fixes #2259 --- PIL/Image.py | 4 ++-- Tests/test_image.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index c086dfcd7cb..03f3973ee44 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1994,8 +1994,8 @@ def _check_size(size): raise ValueError("Size must be a tuple") if len(size) != 2: raise ValueError("Size must be a tuple of length 2") - if size[0] <= 0 or size[1] <= 0: - raise ValueError("Width and Height must be > 0") + if size[0] < 0 or size[1] < 0: + raise ValueError("Width and Height must be => 0") return True diff --git a/Tests/test_image.py b/Tests/test_image.py index ef9aa16af76..f1457a85bb6 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -256,7 +256,11 @@ def test_check_size(self): with self.assertRaises(ValueError): Image.new('RGB', (0,)) # Tuple too short with self.assertRaises(ValueError): - Image.new('RGB', (0,0)) # w,h <= 0 + Image.new('RGB', (-1,-1)) # w,h < 0 + + # this should pass with 0 sized images, #2259 + im = Image.new('L', (0, 0)) + self.assertEqual(im.size, (0, 0)) self.assertTrue(Image.new('RGB', (1,1))) # Should pass lists too From 0a922b962fdc92115a0a9f7deb909acf48afa063 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 27 Dec 2016 04:53:23 -0800 Subject: [PATCH 2/4] tests for basic operations on 0x0 images --- Tests/test_image_access.py | 13 +++++++++++++ Tests/test_image_convert.py | 5 +++++ Tests/test_image_copy.py | 9 +++++++++ Tests/test_image_crop.py | 17 +++++++++++++++++ Tests/test_image_resize.py | 8 ++++++++ 5 files changed, 52 insertions(+) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 0f8d2a65461..900f39eb488 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -78,12 +78,25 @@ def check(self, mode, c=None): im.getpixel((0, 0)), c, "put/getpixel roundtrip failed for mode %s, color %s" % (mode, c)) + # Check 0 + im = Image.new(mode, (0, 0), None) + with self.assertRaises(IndexError): + im.putpixel((0, 0), c) + with self.assertRaises(IndexError): + im.getpixel((0, 0)) + # check initial color im = Image.new(mode, (1, 1), c) self.assertEqual( im.getpixel((0, 0)), c, "initial color failed for mode %s, color %s " % (mode, c)) + # Check 0 + im = Image.new(mode, (0, 0), c) + with self.assertRaises(IndexError): + im.getpixel((0, 0)) + + def test_basic(self): for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", "P", "PA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr"): diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 0c98211e7a6..54ffde10b33 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -19,6 +19,11 @@ def convert(im, mode): for mode in modes: convert(im, mode) + # Check 0 + im = Image.new(mode, (0,0)) + for mode in modes: + convert(im, mode) + def test_default(self): im = hopper("P") diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py index ba53758d5a7..c50205c9ce9 100644 --- a/Tests/test_image_copy.py +++ b/Tests/test_image_copy.py @@ -1,5 +1,7 @@ from helper import unittest, PillowTestCase, hopper +from PIL import Image + import copy @@ -33,5 +35,12 @@ def test_copy(self): self.assertEqual(out.mode, im.mode) self.assertEqual(out.size, croppedSize) + def test_copy_zero(self): + im = Image.new('RGB', (0,0)) + out = im.copy() + self.assertEqual(out.mode, im.mode) + self.assertEqual(out.size, im.size) + + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index c12e29be410..c887ab0c1e0 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -83,6 +83,23 @@ def test_crop_crash(self): img = img.crop(extents) img.load() + def test_crop_zero(self): + + im = Image.new('RGB', (0, 0), 'white') + + cropped = im.crop((0, 0, 0, 0)) + self.assertEqual(cropped.size, (0, 0)) + + cropped = im.crop((10, 10, 20, 20)) + self.assertEqual(cropped.size, (10, 10)) + self.assertEqual(cropped.getdata()[0], (0, 0, 0)) + + im = Image.new('RGB', (0, 0)) + + cropped = im.crop((10, 10, 20, 20)) + self.assertEqual(cropped.size, (10, 10)) + self.assertEqual(cropped.getdata()[2], (0, 0, 0)) + if __name__ == '__main__': diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 7db40965979..38a60564c5f 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -89,6 +89,14 @@ def test_endianness(self): # as separately resized channel self.assert_image_equal(ch, references[channels[i]]) + def test_enlarge_zero(self): + for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS]: + r = self.resize(Image.new('RGB', (0,0), "white"), (212, 195), f) + self.assertEqual(r.mode, "RGB") + self.assertEqual(r.size, (212, 195)) + self.assertEqual(r.getdata()[0], (0,0,0)) + class TestImageResize(PillowTestCase): From 7d59183c1d61bbbad8fa5739711eac43c6725a63 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 31 Dec 2016 13:08:00 +0000 Subject: [PATCH 3/4] Zero image size test --- Tests/test_image_rotate.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index e90b9a592d9..70523a698b0 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -15,13 +15,19 @@ def rotate(im, mode, angle): self.assertEqual(out.size, im.size) else: self.assertNotEqual(out.size, im.size) - for mode in "1", "P", "L", "RGB", "I", "F": + + for mode in ("1", "P", "L", "RGB", "I", "F"): im = hopper(mode) rotate(im, mode, 45) - for angle in 0, 90, 180, 270: + + for angle in (0, 90, 180, 270): im = Image.open('Tests/images/test-card.png') rotate(im, im.mode, angle) + for angle in (0, 45, 90, 180, 270): + im = Image.new('RGB',(0,0)) + rotate(im, im.mode, angle) + if __name__ == '__main__': unittest.main() From 20abc9cdfee882a0e90eea05929a3af78d767e8d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 31 Dec 2016 13:31:51 +0000 Subject: [PATCH 4/4] Fix size check on expan for image_rotate --- Tests/test_image_rotate.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 70523a698b0..dfe5a073145 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -13,8 +13,11 @@ def rotate(im, mode, angle): self.assertEqual(out.mode, mode) if angle % 180 == 0: self.assertEqual(out.size, im.size) + elif im.size == (0, 0): + self.assertEqual(out.size, im.size) else: self.assertNotEqual(out.size, im.size) + for mode in ("1", "P", "L", "RGB", "I", "F"): im = hopper(mode)