From affade75955be9e95b38e07e2d90af66f33c9454 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 12 Feb 2020 18:29:19 +0200 Subject: [PATCH] Replace unittest with pytest --- Tests/test_box_blur.py | 437 ++++---- Tests/test_decompression_bomb.py | 16 +- Tests/test_file_dcx.py | 136 +-- Tests/test_file_dds.py | 262 ++--- Tests/test_file_fli.py | 164 +-- Tests/test_file_fpx.py | 34 +- Tests/test_file_gbr.py | 20 +- Tests/test_file_gimpgradient.py | 180 ++-- Tests/test_file_mcidas.py | 42 +- Tests/test_file_mic.py | 84 +- Tests/test_file_mpo.py | 363 +++---- Tests/test_file_pixar.py | 30 +- Tests/test_file_psd.py | 173 ++-- Tests/test_file_sun.py | 70 +- Tests/test_file_tar.py | 69 +- Tests/test_file_xpm.py | 45 +- Tests/test_file_xvthumb.py | 48 +- Tests/test_image_crop.py | 156 +-- Tests/test_image_draft.py | 132 +-- Tests/test_image_entropy.py | 27 +- Tests/test_image_filter.py | 250 ++--- Tests/test_image_frombytes.py | 18 +- Tests/test_image_point.py | 69 +- Tests/test_image_putdata.py | 122 +-- Tests/test_image_quantize.py | 111 +- Tests/test_image_rotate.py | 217 ++-- Tests/test_image_thumbnail.py | 157 +-- Tests/test_image_tobitmap.py | 18 +- Tests/test_image_transpose.py | 245 ++--- Tests/test_imagechops.py | 539 +++++----- Tests/test_imagecms.py | 1064 ++++++++++--------- Tests/test_imagedraw.py | 1637 +++++++++++++++--------------- Tests/test_imagedraw2.py | 308 +++--- Tests/test_imageenhance.py | 81 +- Tests/test_imageops.py | 525 +++++----- Tests/test_imagetk.py | 116 ++- Tests/test_map.py | 47 +- Tests/test_numpy.py | 366 +++---- Tests/test_pdfparser.py | 217 ++-- 39 files changed, 4415 insertions(+), 4180 deletions(-) diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index 378d1f045..44910b9ed 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -1,7 +1,6 @@ +import pytest from PIL import Image, ImageFilter -from .helper import PillowTestCase - sample = Image.new("L", (7, 5)) # fmt: off sample.putdata(sum([ @@ -21,226 +20,244 @@ def test_imageops_box_blur(): assert isinstance(i, Image.Image) -class TestBoxBlur(PillowTestCase): - def box_blur(self, image, radius=1, n=1): - return image._new(image.im.box_blur(radius, n)) +def box_blur(image, radius=1, n=1): + return image._new(image.im.box_blur(radius, n)) - def assertImage(self, im, data, delta=0): - it = iter(im.getdata()) - for data_row in data: - im_row = [next(it) for _ in range(im.size[0])] - if any( - abs(data_v - im_v) > delta for data_v, im_v in zip(data_row, im_row) - ): - self.assertEqual(im_row, data_row) - self.assertRaises(StopIteration, next, it) - def assertBlur(self, im, radius, data, passes=1, delta=0): - # check grayscale image - self.assertImage(self.box_blur(im, radius, passes), data, delta) - rgba = Image.merge("RGBA", (im, im, im, im)) - for band in self.box_blur(rgba, radius, passes).split(): - self.assertImage(band, data, delta) +def assertImage(im, data, delta=0): + it = iter(im.getdata()) + for data_row in data: + im_row = [next(it) for _ in range(im.size[0])] + if any(abs(data_v - im_v) > delta for data_v, im_v in zip(data_row, im_row)): + assert im_row == data_row + with pytest.raises(StopIteration): + next(it) - def test_color_modes(self): - self.assertRaises(ValueError, self.box_blur, sample.convert("1")) - self.assertRaises(ValueError, self.box_blur, sample.convert("P")) - self.box_blur(sample.convert("L")) - self.box_blur(sample.convert("LA")) - self.box_blur(sample.convert("LA").convert("La")) - self.assertRaises(ValueError, self.box_blur, sample.convert("I")) - self.assertRaises(ValueError, self.box_blur, sample.convert("F")) - self.box_blur(sample.convert("RGB")) - self.box_blur(sample.convert("RGBA")) - self.box_blur(sample.convert("RGBA").convert("RGBa")) - self.box_blur(sample.convert("CMYK")) - self.assertRaises(ValueError, self.box_blur, sample.convert("YCbCr")) - def test_radius_0(self): - self.assertBlur( - sample, - 0, - [ - # fmt: off - [210, 50, 20, 10, 220, 230, 80], - [190, 210, 20, 180, 170, 40, 110], - [120, 210, 250, 60, 220, 0, 220], - [220, 40, 230, 80, 130, 250, 40], - [250, 0, 80, 30, 60, 20, 110], - # fmt: on - ], - ) +def assertBlur(im, radius, data, passes=1, delta=0): + # check grayscale image + assertImage(box_blur(im, radius, passes), data, delta) + rgba = Image.merge("RGBA", (im, im, im, im)) + for band in box_blur(rgba, radius, passes).split(): + assertImage(band, data, delta) - def test_radius_0_02(self): - self.assertBlur( - sample, - 0.02, - [ - # fmt: off - [206, 55, 20, 17, 215, 223, 83], - [189, 203, 31, 171, 169, 46, 110], - [125, 206, 241, 69, 210, 13, 210], - [215, 49, 221, 82, 131, 235, 48], - [244, 7, 80, 32, 60, 27, 107], - # fmt: on - ], - delta=2, - ) - def test_radius_0_05(self): - self.assertBlur( - sample, - 0.05, - [ - # fmt: off - [202, 62, 22, 27, 209, 215, 88], - [188, 194, 44, 161, 168, 56, 111], - [131, 201, 229, 81, 198, 31, 198], - [209, 62, 209, 86, 133, 216, 59], - [237, 17, 80, 36, 60, 35, 103], - # fmt: on - ], - delta=2, - ) +def test_color_modes(): + with pytest.raises(ValueError): + box_blur(sample.convert("1")) + with pytest.raises(ValueError): + box_blur(sample.convert("P")) + box_blur(sample.convert("L")) + box_blur(sample.convert("LA")) + box_blur(sample.convert("LA").convert("La")) + with pytest.raises(ValueError): + box_blur(sample.convert("I")) + with pytest.raises(ValueError): + box_blur(sample.convert("F")) + box_blur(sample.convert("RGB")) + box_blur(sample.convert("RGBA")) + box_blur(sample.convert("RGBA").convert("RGBa")) + box_blur(sample.convert("CMYK")) + with pytest.raises(ValueError): + box_blur(sample.convert("YCbCr")) - def test_radius_0_1(self): - self.assertBlur( - sample, - 0.1, - [ - # fmt: off - [196, 72, 24, 40, 200, 203, 93], - [187, 183, 62, 148, 166, 68, 111], - [139, 193, 213, 96, 182, 54, 182], - [201, 78, 193, 91, 133, 191, 73], - [227, 31, 80, 42, 61, 47, 99], - # fmt: on - ], - delta=1, - ) - def test_radius_0_5(self): - self.assertBlur( - sample, - 0.5, - [ - # fmt: off - [176, 101, 46, 83, 163, 165, 111], - [176, 149, 108, 122, 144, 120, 117], - [164, 171, 159, 141, 134, 119, 129], - [170, 136, 133, 114, 116, 124, 109], - [184, 95, 72, 70, 69, 81, 89], - # fmt: on - ], - delta=1, - ) +def test_radius_0(): + assertBlur( + sample, + 0, + [ + # fmt: off + [210, 50, 20, 10, 220, 230, 80], + [190, 210, 20, 180, 170, 40, 110], + [120, 210, 250, 60, 220, 0, 220], + [220, 40, 230, 80, 130, 250, 40], + [250, 0, 80, 30, 60, 20, 110], + # fmt: on + ], + ) - def test_radius_1(self): - self.assertBlur( - sample, - 1, - [ - # fmt: off - [170, 109, 63, 97, 146, 153, 116], - [168, 142, 112, 128, 126, 143, 121], - [169, 166, 142, 149, 126, 131, 114], - [159, 156, 109, 127, 94, 117, 112], - [164, 128, 63, 87, 76, 89, 90], - # fmt: on - ], - delta=1, - ) - def test_radius_1_5(self): - self.assertBlur( - sample, - 1.5, - [ - # fmt: off - [155, 120, 105, 112, 124, 137, 130], - [160, 136, 124, 125, 127, 134, 130], - [166, 147, 130, 125, 120, 121, 119], - [168, 145, 119, 109, 103, 105, 110], - [168, 134, 96, 85, 85, 89, 97], - # fmt: on - ], - delta=1, - ) +def test_radius_0_02(): + assertBlur( + sample, + 0.02, + [ + # fmt: off + [206, 55, 20, 17, 215, 223, 83], + [189, 203, 31, 171, 169, 46, 110], + [125, 206, 241, 69, 210, 13, 210], + [215, 49, 221, 82, 131, 235, 48], + [244, 7, 80, 32, 60, 27, 107], + # fmt: on + ], + delta=2, + ) - def test_radius_bigger_then_half(self): - self.assertBlur( - sample, - 3, - [ - # fmt: off - [144, 145, 142, 128, 114, 115, 117], - [148, 145, 137, 122, 109, 111, 112], - [152, 145, 131, 117, 103, 107, 108], - [156, 144, 126, 111, 97, 102, 103], - [160, 144, 121, 106, 92, 98, 99], - # fmt: on - ], - delta=1, - ) - def test_radius_bigger_then_width(self): - self.assertBlur( - sample, - 10, - [ - [158, 153, 147, 141, 135, 129, 123], - [159, 153, 147, 141, 136, 130, 124], - [159, 154, 148, 142, 136, 130, 124], - [160, 154, 148, 142, 137, 131, 125], - [160, 155, 149, 143, 137, 131, 125], - ], - delta=0, - ) +def test_radius_0_05(): + assertBlur( + sample, + 0.05, + [ + # fmt: off + [202, 62, 22, 27, 209, 215, 88], + [188, 194, 44, 161, 168, 56, 111], + [131, 201, 229, 81, 198, 31, 198], + [209, 62, 209, 86, 133, 216, 59], + [237, 17, 80, 36, 60, 35, 103], + # fmt: on + ], + delta=2, + ) - def test_extreme_large_radius(self): - self.assertBlur( - sample, - 600, - [ - [162, 162, 162, 162, 162, 162, 162], - [162, 162, 162, 162, 162, 162, 162], - [162, 162, 162, 162, 162, 162, 162], - [162, 162, 162, 162, 162, 162, 162], - [162, 162, 162, 162, 162, 162, 162], - ], - delta=1, - ) - def test_two_passes(self): - self.assertBlur( - sample, - 1, - [ - # fmt: off - [153, 123, 102, 109, 132, 135, 129], - [159, 138, 123, 121, 133, 131, 126], - [162, 147, 136, 124, 127, 121, 121], - [159, 140, 125, 108, 111, 106, 108], - [154, 126, 105, 87, 94, 93, 97], - # fmt: on - ], - passes=2, - delta=1, - ) +def test_radius_0_1(): + assertBlur( + sample, + 0.1, + [ + # fmt: off + [196, 72, 24, 40, 200, 203, 93], + [187, 183, 62, 148, 166, 68, 111], + [139, 193, 213, 96, 182, 54, 182], + [201, 78, 193, 91, 133, 191, 73], + [227, 31, 80, 42, 61, 47, 99], + # fmt: on + ], + delta=1, + ) - def test_three_passes(self): - self.assertBlur( - sample, - 1, - [ - # fmt: off - [146, 131, 116, 118, 126, 131, 130], - [151, 138, 125, 123, 126, 128, 127], - [154, 143, 129, 123, 120, 120, 119], - [152, 139, 122, 113, 108, 108, 108], - [148, 132, 112, 102, 97, 99, 100], - # fmt: on - ], - passes=3, - delta=1, - ) + +def test_radius_0_5(): + assertBlur( + sample, + 0.5, + [ + # fmt: off + [176, 101, 46, 83, 163, 165, 111], + [176, 149, 108, 122, 144, 120, 117], + [164, 171, 159, 141, 134, 119, 129], + [170, 136, 133, 114, 116, 124, 109], + [184, 95, 72, 70, 69, 81, 89], + # fmt: on + ], + delta=1, + ) + + +def test_radius_1(): + assertBlur( + sample, + 1, + [ + # fmt: off + [170, 109, 63, 97, 146, 153, 116], + [168, 142, 112, 128, 126, 143, 121], + [169, 166, 142, 149, 126, 131, 114], + [159, 156, 109, 127, 94, 117, 112], + [164, 128, 63, 87, 76, 89, 90], + # fmt: on + ], + delta=1, + ) + + +def test_radius_1_5(): + assertBlur( + sample, + 1.5, + [ + # fmt: off + [155, 120, 105, 112, 124, 137, 130], + [160, 136, 124, 125, 127, 134, 130], + [166, 147, 130, 125, 120, 121, 119], + [168, 145, 119, 109, 103, 105, 110], + [168, 134, 96, 85, 85, 89, 97], + # fmt: on + ], + delta=1, + ) + + +def test_radius_bigger_then_half(): + assertBlur( + sample, + 3, + [ + # fmt: off + [144, 145, 142, 128, 114, 115, 117], + [148, 145, 137, 122, 109, 111, 112], + [152, 145, 131, 117, 103, 107, 108], + [156, 144, 126, 111, 97, 102, 103], + [160, 144, 121, 106, 92, 98, 99], + # fmt: on + ], + delta=1, + ) + + +def test_radius_bigger_then_width(): + assertBlur( + sample, + 10, + [ + [158, 153, 147, 141, 135, 129, 123], + [159, 153, 147, 141, 136, 130, 124], + [159, 154, 148, 142, 136, 130, 124], + [160, 154, 148, 142, 137, 131, 125], + [160, 155, 149, 143, 137, 131, 125], + ], + delta=0, + ) + + +def test_extreme_large_radius(): + assertBlur( + sample, + 600, + [ + [162, 162, 162, 162, 162, 162, 162], + [162, 162, 162, 162, 162, 162, 162], + [162, 162, 162, 162, 162, 162, 162], + [162, 162, 162, 162, 162, 162, 162], + [162, 162, 162, 162, 162, 162, 162], + ], + delta=1, + ) + + +def test_two_passes(): + assertBlur( + sample, + 1, + [ + # fmt: off + [153, 123, 102, 109, 132, 135, 129], + [159, 138, 123, 121, 133, 131, 126], + [162, 147, 136, 124, 127, 121, 121], + [159, 140, 125, 108, 111, 106, 108], + [154, 126, 105, 87, 94, 93, 97], + # fmt: on + ], + passes=2, + delta=1, + ) + + +def test_three_passes(): + assertBlur( + sample, + 1, + [ + # fmt: off + [146, 131, 116, 118, 126, 131, 130], + [151, 138, 125, 123, 126, 128, 127], + [154, 143, 129, 123, 120, 120, 119], + [152, 139, 122, 113, 108, 108, 108], + [148, 132, 112, 102, 97, 99, 100], + # fmt: on + ], + passes=3, + delta=1, + ) diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index e55cf23dc..f3981b3ec 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -22,7 +22,7 @@ class TestDecompressionBomb(PillowTestCase): # Arrange # Turn limit off Image.MAX_IMAGE_PIXELS = None - self.assertIsNone(Image.MAX_IMAGE_PIXELS) + assert Image.MAX_IMAGE_PIXELS is None # Act / Assert # Implicit assert: no warning. @@ -33,7 +33,7 @@ class TestDecompressionBomb(PillowTestCase): def test_warning(self): # Set limit to trigger warning on the test file Image.MAX_IMAGE_PIXELS = 128 * 128 - 1 - self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1) + assert Image.MAX_IMAGE_PIXELS == 128 * 128 - 1 def open(): with Image.open(TEST_FILE): @@ -44,18 +44,18 @@ class TestDecompressionBomb(PillowTestCase): def test_exception(self): # Set limit to trigger exception on the test file Image.MAX_IMAGE_PIXELS = 64 * 128 - 1 - self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1) + assert Image.MAX_IMAGE_PIXELS == 64 * 128 - 1 - with self.assertRaises(Image.DecompressionBombError): + with pytest.raises(Image.DecompressionBombError): with Image.open(TEST_FILE): pass def test_exception_ico(self): - with self.assertRaises(Image.DecompressionBombError): + with pytest.raises(Image.DecompressionBombError): Image.open("Tests/images/decompression_bomb.ico") def test_exception_gif(self): - with self.assertRaises(Image.DecompressionBombError): + with pytest.raises(Image.DecompressionBombError): Image.open("Tests/images/decompression_bomb.gif") @@ -85,11 +85,11 @@ class TestDecompressionCrop(PillowTestCase): error_values = ((-99909, -99990, 99999, 99999), (99909, 99990, -99999, -99999)) for value in good_values: - self.assertEqual(im.crop(value).size, (9, 9)) + assert im.crop(value).size == (9, 9) for value in warning_values: pytest.warns(Image.DecompressionBombWarning, im.crop, value) for value in error_values: - with self.assertRaises(Image.DecompressionBombError): + with pytest.raises(Image.DecompressionBombError): im.crop(value) diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 36f46299c..bc76b4591 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -1,84 +1,92 @@ -import unittest - import pytest from PIL import DcxImagePlugin, Image -from .helper import PillowTestCase, assert_image_equal, hopper, is_pypy +from .helper import assert_image_equal, hopper, is_pypy # Created with ImageMagick: convert hopper.ppm hopper.dcx TEST_FILE = "Tests/images/hopper.dcx" -class TestFileDcx(PillowTestCase): - def test_sanity(self): - # Arrange +def test_sanity(): + # Arrange + + # Act + with Image.open(TEST_FILE) as im: + + # Assert + assert im.size == (128, 128) + assert isinstance(im, DcxImagePlugin.DcxImageFile) + orig = hopper() + assert_image_equal(im, orig) + + +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + im = Image.open(TEST_FILE) + im.load() + + pytest.warns(ResourceWarning, open) + + +def test_closed_file(): + def open(): + im = Image.open(TEST_FILE) + im.load() + im.close() + + pytest.warns(None, open) + + +def test_context_manager(): + def open(): + with Image.open(TEST_FILE) as im: + im.load() + + pytest.warns(None, open) + + +def test_invalid_file(): + with open("Tests/images/flower.jpg", "rb") as fp: + with pytest.raises(SyntaxError): + DcxImagePlugin.DcxImageFile(fp) + + +def test_tell(): + # Arrange + with Image.open(TEST_FILE) as im: # Act - with Image.open(TEST_FILE) as im: + frame = im.tell() - # Assert - self.assertEqual(im.size, (128, 128)) - self.assertIsInstance(im, DcxImagePlugin.DcxImageFile) - orig = hopper() - assert_image_equal(im, orig) + # Assert + assert frame == 0 - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - im = Image.open(TEST_FILE) - im.load() - pytest.warns(ResourceWarning, open) +def test_n_frames(): + with Image.open(TEST_FILE) as im: + assert im.n_frames == 1 + assert not im.is_animated - def test_closed_file(self): - def open(): - im = Image.open(TEST_FILE) - im.load() - im.close() - pytest.warns(None, open) +def test_eoferror(): + with Image.open(TEST_FILE) as im: + n_frames = im.n_frames - def test_context_manager(self): - def open(): - with Image.open(TEST_FILE) as im: - im.load() + # Test seeking past the last frame + with pytest.raises(EOFError): + im.seek(n_frames) + assert im.tell() < n_frames - pytest.warns(None, open) + # Test that seeking to the last frame does not raise an error + im.seek(n_frames - 1) - def test_invalid_file(self): - with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, DcxImagePlugin.DcxImageFile, fp) - def test_tell(self): - # Arrange - with Image.open(TEST_FILE) as im: +def test_seek_too_far(): + # Arrange + with Image.open(TEST_FILE) as im: + frame = 999 # too big on purpose - # Act - frame = im.tell() - - # Assert - self.assertEqual(frame, 0) - - def test_n_frames(self): - with Image.open(TEST_FILE) as im: - self.assertEqual(im.n_frames, 1) - self.assertFalse(im.is_animated) - - def test_eoferror(self): - with Image.open(TEST_FILE) as im: - n_frames = im.n_frames - - # Test seeking past the last frame - self.assertRaises(EOFError, im.seek, n_frames) - self.assertLess(im.tell(), n_frames) - - # Test that seeking to the last frame does not raise an error - im.seek(n_frames - 1) - - def test_seek_too_far(self): - # Arrange - with Image.open(TEST_FILE) as im: - frame = 999 # too big on purpose - - # Act / Assert - self.assertRaises(EOFError, im.seek, frame) + # Act / Assert + with pytest.raises(EOFError): + im.seek(frame) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 4a967ce80..66db429a1 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -1,8 +1,10 @@ +"""Test DdsImagePlugin""" from io import BytesIO +import pytest from PIL import DdsImagePlugin, Image -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds" @@ -12,146 +14,148 @@ TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds" TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/uncompressed_rgb.dds" -class TestFileDds(PillowTestCase): - """Test DdsImagePlugin""" +def test_sanity_dxt1(): + """Check DXT1 images can be opened""" + with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target: + target = target.convert("RGBA") + with Image.open(TEST_FILE_DXT1) as im: + im.load() - def test_sanity_dxt1(self): - """Check DXT1 images can be opened""" - with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target: - target = target.convert("RGBA") - with Image.open(TEST_FILE_DXT1) as im: + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (256, 256) + + assert_image_equal(im, target) + + +def test_sanity_dxt5(): + """Check DXT5 images can be opened""" + + with Image.open(TEST_FILE_DXT5) as im: + im.load() + + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (256, 256) + + with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target: + assert_image_equal(target, im) + + +def test_sanity_dxt3(): + """Check DXT3 images can be opened""" + + with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target: + with Image.open(TEST_FILE_DXT3) as im: im.load() - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (256, 256) - assert_image_equal(im, target) - - def test_sanity_dxt5(self): - """Check DXT5 images can be opened""" - - with Image.open(TEST_FILE_DXT5) as im: - im.load() - - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) - - with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target: assert_image_equal(target, im) - def test_sanity_dxt3(self): - """Check DXT3 images can be opened""" - with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target: - with Image.open(TEST_FILE_DXT3) as im: - im.load() +def test_dx10_bc7(): + """Check DX10 images can be opened""" - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) + with Image.open(TEST_FILE_DX10_BC7) as im: + im.load() - assert_image_equal(target, im) + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (256, 256) - def test_dx10_bc7(self): - """Check DX10 images can be opened""" + with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target: + assert_image_equal(target, im) - with Image.open(TEST_FILE_DX10_BC7) as im: + +def test_dx10_bc7_unorm_srgb(): + """Check DX10 unsigned normalized integer images can be opened""" + + with Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) as im: + im.load() + + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (16, 16) + assert im.info["gamma"] == 1 / 2.2 + + with Image.open( + TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png") + ) as target: + assert_image_equal(target, im) + + +def test_unimplemented_dxgi_format(): + with pytest.raises(NotImplementedError): + Image.open("Tests/images/unimplemented_dxgi_format.dds",) + + +def test_uncompressed_rgb(): + """Check uncompressed RGB images can be opened""" + + with Image.open(TEST_FILE_UNCOMPRESSED_RGB) as im: + im.load() + + assert im.format == "DDS" + assert im.mode == "RGBA" + assert im.size == (800, 600) + + with Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")) as target: + assert_image_equal(target, im) + + +def test__validate_true(): + """Check valid prefix""" + # Arrange + prefix = b"DDS etc" + + # Act + output = DdsImagePlugin._validate(prefix) + + # Assert + assert output + + +def test__validate_false(): + """Check invalid prefix""" + # Arrange + prefix = b"something invalid" + + # Act + output = DdsImagePlugin._validate(prefix) + + # Assert + assert not output + + +def test_short_header(): + """ Check a short header""" + with open(TEST_FILE_DXT5, "rb") as f: + img_file = f.read() + + def short_header(): + Image.open(BytesIO(img_file[:119])) + + with pytest.raises(IOError): + short_header() + + +def test_short_file(): + """ Check that the appropriate error is thrown for a short file""" + + with open(TEST_FILE_DXT5, "rb") as f: + img_file = f.read() + + def short_file(): + with Image.open(BytesIO(img_file[:-100])) as im: im.load() - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (256, 256)) + with pytest.raises(IOError): + short_file() - with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target: - assert_image_equal(target, im) - def test_dx10_bc7_unorm_srgb(self): - """Check DX10 unsigned normalized integer images can be opened""" - - with Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) as im: - im.load() - - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (16, 16)) - self.assertEqual(im.info["gamma"], 1 / 2.2) - - with Image.open( - TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png") - ) as target: - assert_image_equal(target, im) - - def test_unimplemented_dxgi_format(self): - self.assertRaises( - NotImplementedError, - Image.open, - "Tests/images/unimplemented_dxgi_format.dds", - ) - - def test_uncompressed_rgb(self): - """Check uncompressed RGB images can be opened""" - - with Image.open(TEST_FILE_UNCOMPRESSED_RGB) as im: - im.load() - - self.assertEqual(im.format, "DDS") - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (800, 600)) - - with Image.open( - TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png") - ) as target: - assert_image_equal(target, im) - - def test__validate_true(self): - """Check valid prefix""" - # Arrange - prefix = b"DDS etc" - - # Act - output = DdsImagePlugin._validate(prefix) - - # Assert - self.assertTrue(output) - - def test__validate_false(self): - """Check invalid prefix""" - # Arrange - prefix = b"something invalid" - - # Act - output = DdsImagePlugin._validate(prefix) - - # Assert - self.assertFalse(output) - - def test_short_header(self): - """ Check a short header""" - with open(TEST_FILE_DXT5, "rb") as f: - img_file = f.read() - - def short_header(): - Image.open(BytesIO(img_file[:119])) - - self.assertRaises(IOError, short_header) - - def test_short_file(self): - """ Check that the appropriate error is thrown for a short file""" - - with open(TEST_FILE_DXT5, "rb") as f: - img_file = f.read() - - def short_file(): - with Image.open(BytesIO(img_file[:-100])) as im: - im.load() - - self.assertRaises(IOError, short_file) - - def test_unimplemented_pixel_format(self): - self.assertRaises( - NotImplementedError, - Image.open, - "Tests/images/unimplemented_pixel_format.dds", - ) +def test_unimplemented_pixel_format(): + with pytest.raises(NotImplementedError): + Image.open("Tests/images/unimplemented_pixel_format.dds",) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 45c0fd1a1..726e16c1a 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -1,9 +1,7 @@ -import unittest - import pytest from PIL import FliImagePlugin, Image -from .helper import PillowTestCase, assert_image_equal, is_pypy +from .helper import assert_image_equal, is_pypy # created as an export of a palette image from Gimp2.6 # save as...-> hopper.fli, default options. @@ -13,105 +11,115 @@ static_test_file = "Tests/images/hopper.fli" animated_test_file = "Tests/images/a.fli" -class TestFileFli(PillowTestCase): - def test_sanity(self): +def test_sanity(): + with Image.open(static_test_file) as im: + im.load() + assert im.mode == "P" + assert im.size == (128, 128) + assert im.format == "FLI" + assert not im.is_animated + + with Image.open(animated_test_file) as im: + assert im.mode == "P" + assert im.size == (320, 200) + assert im.format == "FLI" + assert im.info["duration"] == 71 + assert im.is_animated + + +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + im = Image.open(static_test_file) + im.load() + + pytest.warns(ResourceWarning, open) + + +def test_closed_file(): + def open(): + im = Image.open(static_test_file) + im.load() + im.close() + + pytest.warns(None, open) + + +def test_context_manager(): + def open(): with Image.open(static_test_file) as im: im.load() - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "FLI") - self.assertFalse(im.is_animated) - with Image.open(animated_test_file) as im: - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (320, 200)) - self.assertEqual(im.format, "FLI") - self.assertEqual(im.info["duration"], 71) - self.assertTrue(im.is_animated) + pytest.warns(None, open) - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - im = Image.open(static_test_file) - im.load() - pytest.warns(ResourceWarning, open) +def test_tell(): + # Arrange + with Image.open(static_test_file) as im: - def test_closed_file(self): - def open(): - im = Image.open(static_test_file) - im.load() - im.close() + # Act + frame = im.tell() - pytest.warns(None, open) + # Assert + assert frame == 0 - def test_context_manager(self): - def open(): - with Image.open(static_test_file) as im: - im.load() - pytest.warns(None, open) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - def test_tell(self): - # Arrange - with Image.open(static_test_file) as im: + with pytest.raises(SyntaxError): + FliImagePlugin.FliImageFile(invalid_file) - # Act - frame = im.tell() - # Assert - self.assertEqual(frame, 0) +def test_n_frames(): + with Image.open(static_test_file) as im: + assert im.n_frames == 1 + assert not im.is_animated - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" + with Image.open(animated_test_file) as im: + assert im.n_frames == 384 + assert im.is_animated - self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file) - def test_n_frames(self): - with Image.open(static_test_file) as im: - self.assertEqual(im.n_frames, 1) - self.assertFalse(im.is_animated) +def test_eoferror(): + with Image.open(animated_test_file) as im: + n_frames = im.n_frames - with Image.open(animated_test_file) as im: - self.assertEqual(im.n_frames, 384) - self.assertTrue(im.is_animated) + # Test seeking past the last frame + with pytest.raises(EOFError): + im.seek(n_frames) + assert im.tell() < n_frames - def test_eoferror(self): - with Image.open(animated_test_file) as im: - n_frames = im.n_frames + # Test that seeking to the last frame does not raise an error + im.seek(n_frames - 1) - # Test seeking past the last frame - self.assertRaises(EOFError, im.seek, n_frames) - self.assertLess(im.tell(), n_frames) - # Test that seeking to the last frame does not raise an error - im.seek(n_frames - 1) +def test_seek_tell(): + with Image.open(animated_test_file) as im: - def test_seek_tell(self): - with Image.open(animated_test_file) as im: + layer_number = im.tell() + assert layer_number == 0 - layer_number = im.tell() - self.assertEqual(layer_number, 0) + im.seek(0) + layer_number = im.tell() + assert layer_number == 0 - im.seek(0) - layer_number = im.tell() - self.assertEqual(layer_number, 0) + im.seek(1) + layer_number = im.tell() + assert layer_number == 1 - im.seek(1) - layer_number = im.tell() - self.assertEqual(layer_number, 1) + im.seek(2) + layer_number = im.tell() + assert layer_number == 2 - im.seek(2) - layer_number = im.tell() - self.assertEqual(layer_number, 2) + im.seek(1) + layer_number = im.tell() + assert layer_number == 1 - im.seek(1) - layer_number = im.tell() - self.assertEqual(layer_number, 1) - def test_seek(self): - with Image.open(animated_test_file) as im: - im.seek(50) +def test_seek(): + with Image.open(animated_test_file) as im: + im.seek(50) - with Image.open("Tests/images/a_fli.png") as expected: - assert_image_equal(im, expected) + with Image.open("Tests/images/a_fli.png") as expected: + assert_image_equal(im, expected) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 25a7ff24b..76758ad50 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -1,9 +1,6 @@ -import unittest - +import pytest from PIL import Image -from .helper import PillowTestCase - try: from PIL import FpxImagePlugin except ImportError: @@ -11,18 +8,23 @@ except ImportError: else: olefile_installed = True +pytestmark = pytest.mark.skipif( + not olefile_installed, reason="olefile package not installed" +) -@unittest.skipUnless(olefile_installed, "olefile package not installed") -class TestFileFpx(PillowTestCase): - def test_invalid_file(self): - # Test an invalid OLE file - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, invalid_file) - # Test a valid OLE file, but not an FPX file - ole_file = "Tests/images/test-ole-file.doc" - self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, ole_file) +def test_invalid_file(): + # Test an invalid OLE file + invalid_file = "Tests/images/flower.jpg" + with pytest.raises(SyntaxError): + FpxImagePlugin.FpxImageFile(invalid_file) - def test_fpx_invalid_number_of_bands(self): - with self.assertRaisesRegex(IOError, "Invalid number of bands"): - Image.open("Tests/images/input_bw_five_bands.fpx") + # Test a valid OLE file, but not an FPX file + ole_file = "Tests/images/test-ole-file.doc" + with pytest.raises(SyntaxError): + FpxImagePlugin.FpxImageFile(ole_file) + + +def test_fpx_invalid_number_of_bands(): + with pytest.raises(IOError, match="Invalid number of bands"): + Image.open("Tests/images/input_bw_five_bands.fpx") diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 4304d8ae8..f183390bc 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -1,15 +1,17 @@ +import pytest from PIL import GbrImagePlugin, Image -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal -class TestFileGbr(PillowTestCase): - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file) + with pytest.raises(SyntaxError): + GbrImagePlugin.GbrImageFile(invalid_file) - def test_gbr_file(self): - with Image.open("Tests/images/gbr.gbr") as im: - with Image.open("Tests/images/gbr.png") as target: - assert_image_equal(target, im) + +def test_gbr_file(): + with Image.open("Tests/images/gbr.gbr") as im: + with Image.open("Tests/images/gbr.png") as target: + assert_image_equal(target, im) diff --git a/Tests/test_file_gimpgradient.py b/Tests/test_file_gimpgradient.py index bafee79a3..c5f4e4c2d 100644 --- a/Tests/test_file_gimpgradient.py +++ b/Tests/test_file_gimpgradient.py @@ -1,122 +1,128 @@ from PIL import GimpGradientFile -from .helper import PillowTestCase + +def test_linear_pos_le_middle(): + # Arrange + middle = 0.5 + pos = 0.25 + + # Act + ret = GimpGradientFile.linear(middle, pos) + + # Assert + assert ret == 0.25 -class TestImage(PillowTestCase): - def test_linear_pos_le_middle(self): - # Arrange - middle = 0.5 - pos = 0.25 +def test_linear_pos_le_small_middle(): + # Arrange + middle = 1e-11 + pos = 1e-12 - # Act - ret = GimpGradientFile.linear(middle, pos) + # Act + ret = GimpGradientFile.linear(middle, pos) - # Assert - self.assertEqual(ret, 0.25) + # Assert + assert ret == 0.0 - def test_linear_pos_le_small_middle(self): - # Arrange - middle = 1e-11 - pos = 1e-12 - # Act - ret = GimpGradientFile.linear(middle, pos) +def test_linear_pos_gt_middle(): + # Arrange + middle = 0.5 + pos = 0.75 - # Assert - self.assertEqual(ret, 0.0) + # Act + ret = GimpGradientFile.linear(middle, pos) - def test_linear_pos_gt_middle(self): - # Arrange - middle = 0.5 - pos = 0.75 + # Assert + assert ret == 0.75 - # Act - ret = GimpGradientFile.linear(middle, pos) - # Assert - self.assertEqual(ret, 0.75) +def test_linear_pos_gt_small_middle(): + # Arrange + middle = 1 - 1e-11 + pos = 1 - 1e-12 - def test_linear_pos_gt_small_middle(self): - # Arrange - middle = 1 - 1e-11 - pos = 1 - 1e-12 + # Act + ret = GimpGradientFile.linear(middle, pos) - # Act - ret = GimpGradientFile.linear(middle, pos) + # Assert + assert ret == 1.0 - # Assert - self.assertEqual(ret, 1.0) - def test_curved(self): - # Arrange - middle = 0.5 - pos = 0.75 +def test_curved(): + # Arrange + middle = 0.5 + pos = 0.75 - # Act - ret = GimpGradientFile.curved(middle, pos) + # Act + ret = GimpGradientFile.curved(middle, pos) - # Assert - self.assertEqual(ret, 0.75) + # Assert + assert ret == 0.75 - def test_sine(self): - # Arrange - middle = 0.5 - pos = 0.75 - # Act - ret = GimpGradientFile.sine(middle, pos) +def test_sine(): + # Arrange + middle = 0.5 + pos = 0.75 - # Assert - self.assertEqual(ret, 0.8535533905932737) + # Act + ret = GimpGradientFile.sine(middle, pos) - def test_sphere_increasing(self): - # Arrange - middle = 0.5 - pos = 0.75 + # Assert + assert ret == 0.8535533905932737 - # Act - ret = GimpGradientFile.sphere_increasing(middle, pos) - # Assert - self.assertAlmostEqual(ret, 0.9682458365518543) +def test_sphere_increasing(): + # Arrange + middle = 0.5 + pos = 0.75 - def test_sphere_decreasing(self): - # Arrange - middle = 0.5 - pos = 0.75 + # Act + ret = GimpGradientFile.sphere_increasing(middle, pos) - # Act - ret = GimpGradientFile.sphere_decreasing(middle, pos) + # Assert + assert round(abs(ret - 0.9682458365518543), 7) == 0 - # Assert - self.assertEqual(ret, 0.3385621722338523) - def test_load_via_imagepalette(self): - # Arrange - from PIL import ImagePalette +def test_sphere_decreasing(): + # Arrange + middle = 0.5 + pos = 0.75 - test_file = "Tests/images/gimp_gradient.ggr" + # Act + ret = GimpGradientFile.sphere_decreasing(middle, pos) - # Act - palette = ImagePalette.load(test_file) + # Assert + assert ret == 0.3385621722338523 - # Assert - # load returns raw palette information - self.assertEqual(len(palette[0]), 1024) - self.assertEqual(palette[1], "RGBA") - def test_load_1_3_via_imagepalette(self): - # Arrange - from PIL import ImagePalette +def test_load_via_imagepalette(): + # Arrange + from PIL import ImagePalette - # GIMP 1.3 gradient files contain a name field - test_file = "Tests/images/gimp_gradient_with_name.ggr" + test_file = "Tests/images/gimp_gradient.ggr" - # Act - palette = ImagePalette.load(test_file) + # Act + palette = ImagePalette.load(test_file) - # Assert - # load returns raw palette information - self.assertEqual(len(palette[0]), 1024) - self.assertEqual(palette[1], "RGBA") + # Assert + # load returns raw palette information + assert len(palette[0]) == 1024 + assert palette[1] == "RGBA" + + +def test_load_1_3_via_imagepalette(): + # Arrange + from PIL import ImagePalette + + # GIMP 1.3 gradient files contain a name field + test_file = "Tests/images/gimp_gradient_with_name.ggr" + + # Act + palette = ImagePalette.load(test_file) + + # Assert + # load returns raw palette information + assert len(palette[0]) == 1024 + assert palette[1] == "RGBA" diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index 4380f91e1..516dbb208 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -1,28 +1,30 @@ +import pytest from PIL import Image, McIdasImagePlugin -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal -class TestFileMcIdas(PillowTestCase): - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, McIdasImagePlugin.McIdasImageFile, invalid_file) + with pytest.raises(SyntaxError): + McIdasImagePlugin.McIdasImageFile(invalid_file) - def test_valid_file(self): - # Arrange - # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 - # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ - test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" - saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png" - # Act - with Image.open(test_file) as im: - im.load() +def test_valid_file(): + # Arrange + # https://ghrc.nsstc.nasa.gov/hydro/details/cmx3g8 + # https://ghrc.nsstc.nasa.gov/pub/fieldCampaigns/camex3/cmx3g8/browse/ + test_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.ara" + saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png" - # Assert - self.assertEqual(im.format, "MCIDAS") - self.assertEqual(im.mode, "I") - self.assertEqual(im.size, (1800, 400)) - with Image.open(saved_file) as im2: - assert_image_equal(im, im2) + # Act + with Image.open(test_file) as im: + im.load() + + # Assert + assert im.format == "MCIDAS" + assert im.mode == "I" + assert im.size == (1800, 400) + with Image.open(saved_file) as im2: + assert_image_equal(im, im2) diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index 75f709725..f7ab774dd 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -1,8 +1,7 @@ -import unittest - +import pytest from PIL import Image, ImagePalette, features -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import assert_image_similar, hopper try: from PIL import MicImagePlugin @@ -14,52 +13,59 @@ else: TEST_FILE = "Tests/images/hopper.mic" -@unittest.skipUnless(olefile_installed, "olefile package not installed") -@unittest.skipUnless(features.check("libtiff"), "libtiff not installed") -class TestFileMic(PillowTestCase): - def test_sanity(self): - with Image.open(TEST_FILE) as im: - im.load() - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "MIC") +pytestmark = [ + pytest.mark.skipif(not olefile_installed, reason="olefile package not installed"), + pytest.mark.skipif(not features.check("libtiff"), reason="libtiff not installed"), +] - # Adjust for the gamma of 2.2 encoded into the file - lut = ImagePalette.make_gamma_lut(1 / 2.2) - im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()]) - im2 = hopper("RGBA") - assert_image_similar(im, im2, 10) +def test_sanity(): + with Image.open(TEST_FILE) as im: + im.load() + assert im.mode == "RGBA" + assert im.size == (128, 128) + assert im.format == "MIC" - def test_n_frames(self): - with Image.open(TEST_FILE) as im: + # Adjust for the gamma of 2.2 encoded into the file + lut = ImagePalette.make_gamma_lut(1 / 2.2) + im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()]) - self.assertEqual(im.n_frames, 1) + im2 = hopper("RGBA") + assert_image_similar(im, im2, 10) - def test_is_animated(self): - with Image.open(TEST_FILE) as im: - self.assertFalse(im.is_animated) +def test_n_frames(): + with Image.open(TEST_FILE) as im: + assert im.n_frames == 1 - def test_tell(self): - with Image.open(TEST_FILE) as im: - self.assertEqual(im.tell(), 0) +def test_is_animated(): + with Image.open(TEST_FILE) as im: + assert not im.is_animated - def test_seek(self): - with Image.open(TEST_FILE) as im: - im.seek(0) - self.assertEqual(im.tell(), 0) +def test_tell(): + with Image.open(TEST_FILE) as im: + assert im.tell() == 0 - self.assertRaises(EOFError, im.seek, 99) - self.assertEqual(im.tell(), 0) - def test_invalid_file(self): - # Test an invalid OLE file - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, MicImagePlugin.MicImageFile, invalid_file) +def test_seek(): + with Image.open(TEST_FILE) as im: + im.seek(0) + assert im.tell() == 0 - # Test a valid OLE file, but not a MIC file - ole_file = "Tests/images/test-ole-file.doc" - self.assertRaises(SyntaxError, MicImagePlugin.MicImageFile, ole_file) + with pytest.raises(EOFError): + im.seek(99) + assert im.tell() == 0 + + +def test_invalid_file(): + # Test an invalid OLE file + invalid_file = "Tests/images/flower.jpg" + with pytest.raises(SyntaxError): + MicImagePlugin.MicImageFile(invalid_file) + + # Test a valid OLE file, but not a MIC file + ole_file = "Tests/images/test-ole-file.doc" + with pytest.raises(SyntaxError): + MicImagePlugin.MicImageFile(ole_file) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 5ed1bb75e..fd951eff0 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -1,202 +1,221 @@ -import unittest from io import BytesIO import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_similar, is_pypy +from .helper import assert_image_similar, is_pypy test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"] -class TestFileMpo(PillowTestCase): - def setUp(self): - codecs = dir(Image.core) - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - self.skipTest("jpeg support not available") +def setup_module(): + codecs = dir(Image.core) + if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + pytest.skip("jpeg support not available") - def frame_roundtrip(self, im, **options): - # Note that for now, there is no MPO saving functionality - out = BytesIO() - im.save(out, "MPO", **options) - test_bytes = out.tell() - out.seek(0) - im = Image.open(out) - im.bytes = test_bytes # for testing only - return im - def test_sanity(self): - for test_file in test_files: - with Image.open(test_file) as im: - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (640, 480)) - self.assertEqual(im.format, "MPO") +def frame_roundtrip(im, **options): + # Note that for now, there is no MPO saving functionality + out = BytesIO() + im.save(out, "MPO", **options) + test_bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = test_bytes # for testing only + return im - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - im = Image.open(test_files[0]) + +def test_sanity(): + for test_file in test_files: + with Image.open(test_file) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (640, 480) + assert im.format == "MPO" + + +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + im = Image.open(test_files[0]) + im.load() + + pytest.warns(ResourceWarning, open) + + +def test_closed_file(): + def open(): + im = Image.open(test_files[0]) + im.load() + im.close() + + pytest.warns(None, open) + + +def test_context_manager(): + def open(): + with Image.open(test_files[0]) as im: im.load() - pytest.warns(ResourceWarning, open) + pytest.warns(None, open) - def test_closed_file(self): - def open(): - im = Image.open(test_files[0]) - im.load() - im.close() - pytest.warns(None, open) - - def test_context_manager(self): - def open(): - with Image.open(test_files[0]) as im: - im.load() - - pytest.warns(None, open) - - def test_app(self): - for test_file in test_files: - # Test APP/COM reader (@PIL135) - with Image.open(test_file) as im: - self.assertEqual(im.applist[0][0], "APP1") - self.assertEqual(im.applist[1][0], "APP2") - self.assertEqual( - im.applist[1][1][:16], - b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00", - ) - self.assertEqual(len(im.applist), 2) - - def test_exif(self): - for test_file in test_files: - with Image.open(test_file) as im: - info = im._getexif() - self.assertEqual(info[272], "Nintendo 3DS") - self.assertEqual(info[296], 2) - self.assertEqual(info[34665], 188) - - def test_frame_size(self): - # This image has been hexedited to contain a different size - # in the EXIF data of the second frame - with Image.open("Tests/images/sugarshack_frame_size.mpo") as im: - self.assertEqual(im.size, (640, 480)) - - im.seek(1) - self.assertEqual(im.size, (680, 480)) - - def test_parallax(self): - # Nintendo - with Image.open("Tests/images/sugarshack.mpo") as im: - exif = im.getexif() - self.assertEqual( - exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375 +def test_app(): + for test_file in test_files: + # Test APP/COM reader (@PIL135) + with Image.open(test_file) as im: + assert im.applist[0][0] == "APP1" + assert im.applist[1][0] == "APP2" + assert ( + im.applist[1][1][:16] + == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" ) + assert len(im.applist) == 2 - # Fujifilm - with Image.open("Tests/images/fujifilm.mpo") as im: - im.seek(1) - exif = im.getexif() - self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125) - def test_mp(self): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - self.assertEqual(mpinfo[45056], b"0100") - self.assertEqual(mpinfo[45057], 2) +def test_exif(): + for test_file in test_files: + with Image.open(test_file) as im: + info = im._getexif() + assert info[272] == "Nintendo 3DS" + assert info[296] == 2 + assert info[34665] == 188 - def test_mp_offset(self): - # This image has been manually hexedited to have an IFD offset of 10 - # in APP2 data, in contrast to normal 8 - with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im: + +def test_frame_size(): + # This image has been hexedited to contain a different size + # in the EXIF data of the second frame + with Image.open("Tests/images/sugarshack_frame_size.mpo") as im: + assert im.size == (640, 480) + + im.seek(1) + assert im.size == (680, 480) + + +def test_parallax(): + # Nintendo + with Image.open("Tests/images/sugarshack.mpo") as im: + exif = im.getexif() + assert exif.get_ifd(0x927C)[0x1101]["Parallax"] == -44.798187255859375 + + # Fujifilm + with Image.open("Tests/images/fujifilm.mpo") as im: + im.seek(1) + exif = im.getexif() + assert exif.get_ifd(0x927C)[0xB211] == -3.125 + + +def test_mp(): + for test_file in test_files: + with Image.open(test_file) as im: mpinfo = im._getmp() - self.assertEqual(mpinfo[45056], b"0100") - self.assertEqual(mpinfo[45057], 2) + assert mpinfo[45056] == b"0100" + assert mpinfo[45057] == 2 - def test_mp_no_data(self): - # This image has been manually hexedited to have the second frame - # beyond the end of the file - with Image.open("Tests/images/sugarshack_no_data.mpo") as im: - with self.assertRaises(ValueError): - im.seek(1) - def test_mp_attribute(self): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - frameNumber = 0 - for mpentry in mpinfo[45058]: - mpattr = mpentry["Attribute"] - if frameNumber: - self.assertFalse(mpattr["RepresentativeImageFlag"]) - else: - self.assertTrue(mpattr["RepresentativeImageFlag"]) - self.assertFalse(mpattr["DependentParentImageFlag"]) - self.assertFalse(mpattr["DependentChildImageFlag"]) - self.assertEqual(mpattr["ImageDataFormat"], "JPEG") - self.assertEqual(mpattr["MPType"], "Multi-Frame Image: (Disparity)") - self.assertEqual(mpattr["Reserved"], 0) - frameNumber += 1 +def test_mp_offset(): + # This image has been manually hexedited to have an IFD offset of 10 + # in APP2 data, in contrast to normal 8 + with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im: + mpinfo = im._getmp() + assert mpinfo[45056] == b"0100" + assert mpinfo[45057] == 2 - def test_seek(self): - for test_file in test_files: - with Image.open(test_file) as im: - self.assertEqual(im.tell(), 0) - # prior to first image raises an error, both blatant and borderline - self.assertRaises(EOFError, im.seek, -1) - self.assertRaises(EOFError, im.seek, -523) - # after the final image raises an error, - # both blatant and borderline - self.assertRaises(EOFError, im.seek, 2) - self.assertRaises(EOFError, im.seek, 523) - # bad calls shouldn't change the frame - self.assertEqual(im.tell(), 0) - # this one will work - im.seek(1) - self.assertEqual(im.tell(), 1) - # and this one, too - im.seek(0) - self.assertEqual(im.tell(), 0) - def test_n_frames(self): - with Image.open("Tests/images/sugarshack.mpo") as im: - self.assertEqual(im.n_frames, 2) - self.assertTrue(im.is_animated) +def test_mp_no_data(): + # This image has been manually hexedited to have the second frame + # beyond the end of the file + with Image.open("Tests/images/sugarshack_no_data.mpo") as im: + with pytest.raises(ValueError): + im.seek(1) - def test_eoferror(self): - with Image.open("Tests/images/sugarshack.mpo") as im: - n_frames = im.n_frames - # Test seeking past the last frame - self.assertRaises(EOFError, im.seek, n_frames) - self.assertLess(im.tell(), n_frames) +def test_mp_attribute(): + for test_file in test_files: + with Image.open(test_file) as im: + mpinfo = im._getmp() + frameNumber = 0 + for mpentry in mpinfo[45058]: + mpattr = mpentry["Attribute"] + if frameNumber: + assert not mpattr["RepresentativeImageFlag"] + else: + assert mpattr["RepresentativeImageFlag"] + assert not mpattr["DependentParentImageFlag"] + assert not mpattr["DependentChildImageFlag"] + assert mpattr["ImageDataFormat"] == "JPEG" + assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" + assert mpattr["Reserved"] == 0 + frameNumber += 1 - # Test that seeking to the last frame does not raise an error - im.seek(n_frames - 1) - def test_image_grab(self): - for test_file in test_files: - with Image.open(test_file) as im: - self.assertEqual(im.tell(), 0) - im0 = im.tobytes() - im.seek(1) - self.assertEqual(im.tell(), 1) - im1 = im.tobytes() - im.seek(0) - self.assertEqual(im.tell(), 0) - im02 = im.tobytes() - self.assertEqual(im0, im02) - self.assertNotEqual(im0, im1) +def test_seek(): + for test_file in test_files: + with Image.open(test_file) as im: + assert im.tell() == 0 + # prior to first image raises an error, both blatant and borderline + with pytest.raises(EOFError): + im.seek(-1) + with pytest.raises(EOFError): + im.seek(-523) + # after the final image raises an error, + # both blatant and borderline + with pytest.raises(EOFError): + im.seek(2) + with pytest.raises(EOFError): + im.seek(523) + # bad calls shouldn't change the frame + assert im.tell() == 0 + # this one will work + im.seek(1) + assert im.tell() == 1 + # and this one, too + im.seek(0) + assert im.tell() == 0 - def test_save(self): - # Note that only individual frames can be saved at present - for test_file in test_files: - with Image.open(test_file) as im: - self.assertEqual(im.tell(), 0) - jpg0 = self.frame_roundtrip(im) - assert_image_similar(im, jpg0, 30) - im.seek(1) - self.assertEqual(im.tell(), 1) - jpg1 = self.frame_roundtrip(im) - assert_image_similar(im, jpg1, 30) + +def test_n_frames(): + with Image.open("Tests/images/sugarshack.mpo") as im: + assert im.n_frames == 2 + assert im.is_animated + + +def test_eoferror(): + with Image.open("Tests/images/sugarshack.mpo") as im: + n_frames = im.n_frames + + # Test seeking past the last frame + with pytest.raises(EOFError): + im.seek(n_frames) + assert im.tell() < n_frames + + # Test that seeking to the last frame does not raise an error + im.seek(n_frames - 1) + + +def test_image_grab(): + for test_file in test_files: + with Image.open(test_file) as im: + assert im.tell() == 0 + im0 = im.tobytes() + im.seek(1) + assert im.tell() == 1 + im1 = im.tobytes() + im.seek(0) + assert im.tell() == 0 + im02 = im.tobytes() + assert im0 == im02 + assert im0 != im1 + + +def test_save(): + # Note that only individual frames can be saved at present + for test_file in test_files: + with Image.open(test_file) as im: + assert im.tell() == 0 + jpg0 = frame_roundtrip(im) + assert_image_similar(im, jpg0, 30) + im.seek(1) + assert im.tell() == 1 + jpg1 = frame_roundtrip(im) + assert_image_similar(im, jpg1, 30) diff --git a/Tests/test_file_pixar.py b/Tests/test_file_pixar.py index 201f38416..5e83c6104 100644 --- a/Tests/test_file_pixar.py +++ b/Tests/test_file_pixar.py @@ -1,23 +1,25 @@ +import pytest from PIL import Image, PixarImagePlugin -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import assert_image_similar, hopper TEST_FILE = "Tests/images/hopper.pxr" -class TestFilePixar(PillowTestCase): - def test_sanity(self): - with Image.open(TEST_FILE) as im: - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "PIXAR") - self.assertIsNone(im.get_format_mimetype()) +def test_sanity(): + with Image.open(TEST_FILE) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format == "PIXAR" + assert im.get_format_mimetype() is None - im2 = hopper() - assert_image_similar(im, im2, 4.8) + im2 = hopper() + assert_image_similar(im, im2, 4.8) - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, PixarImagePlugin.PixarImageFile, invalid_file) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" + + with pytest.raises(SyntaxError): + PixarImagePlugin.PixarImageFile(invalid_file) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index e77964d32..4d8c6eba4 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -1,116 +1,129 @@ -import unittest - import pytest from PIL import Image, PsdImagePlugin -from .helper import PillowTestCase, assert_image_similar, hopper, is_pypy +from .helper import assert_image_similar, hopper, is_pypy test_file = "Tests/images/hopper.psd" -class TestImagePsd(PillowTestCase): - def test_sanity(self): +def test_sanity(): + with Image.open(test_file) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format == "PSD" + + im2 = hopper() + assert_image_similar(im, im2, 4.8) + + +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + im = Image.open(test_file) + im.load() + + pytest.warns(ResourceWarning, open) + + +def test_closed_file(): + def open(): + im = Image.open(test_file) + im.load() + im.close() + + pytest.warns(None, open) + + +def test_context_manager(): + def open(): with Image.open(test_file) as im: im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "PSD") - im2 = hopper() - assert_image_similar(im, im2, 4.8) + pytest.warns(None, open) - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - im = Image.open(test_file) - im.load() - pytest.warns(ResourceWarning, open) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - def test_closed_file(self): - def open(): - im = Image.open(test_file) - im.load() - im.close() + with pytest.raises(SyntaxError): + PsdImagePlugin.PsdImageFile(invalid_file) - pytest.warns(None, open) - def test_context_manager(self): - def open(): - with Image.open(test_file) as im: - im.load() +def test_n_frames(): + with Image.open("Tests/images/hopper_merged.psd") as im: + assert im.n_frames == 1 + assert not im.is_animated - pytest.warns(None, open) + with Image.open(test_file) as im: + assert im.n_frames == 2 + assert im.is_animated - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file) +def test_eoferror(): + with Image.open(test_file) as im: + # PSD seek index starts at 1 rather than 0 + n_frames = im.n_frames + 1 - def test_n_frames(self): - with Image.open("Tests/images/hopper_merged.psd") as im: - self.assertEqual(im.n_frames, 1) - self.assertFalse(im.is_animated) + # Test seeking past the last frame + with pytest.raises(EOFError): + im.seek(n_frames) + assert im.tell() < n_frames - with Image.open(test_file) as im: - self.assertEqual(im.n_frames, 2) - self.assertTrue(im.is_animated) + # Test that seeking to the last frame does not raise an error + im.seek(n_frames - 1) - def test_eoferror(self): - with Image.open(test_file) as im: - # PSD seek index starts at 1 rather than 0 - n_frames = im.n_frames + 1 - # Test seeking past the last frame - self.assertRaises(EOFError, im.seek, n_frames) - self.assertLess(im.tell(), n_frames) +def test_seek_tell(): + with Image.open(test_file) as im: - # Test that seeking to the last frame does not raise an error - im.seek(n_frames - 1) + layer_number = im.tell() + assert layer_number == 1 - def test_seek_tell(self): - with Image.open(test_file) as im: + with pytest.raises(EOFError): + im.seek(0) - layer_number = im.tell() - self.assertEqual(layer_number, 1) + im.seek(1) + layer_number = im.tell() + assert layer_number == 1 - self.assertRaises(EOFError, im.seek, 0) + im.seek(2) + layer_number = im.tell() + assert layer_number == 2 - im.seek(1) - layer_number = im.tell() - self.assertEqual(layer_number, 1) - im.seek(2) - layer_number = im.tell() - self.assertEqual(layer_number, 2) +def test_seek_eoferror(): + with Image.open(test_file) as im: - def test_seek_eoferror(self): - with Image.open(test_file) as im: + with pytest.raises(EOFError): + im.seek(-1) - self.assertRaises(EOFError, im.seek, -1) - def test_open_after_exclusive_load(self): - with Image.open(test_file) as im: - im.load() - im.seek(im.tell() + 1) - im.load() +def test_open_after_exclusive_load(): + with Image.open(test_file) as im: + im.load() + im.seek(im.tell() + 1) + im.load() - def test_icc_profile(self): - with Image.open(test_file) as im: - self.assertIn("icc_profile", im.info) - icc_profile = im.info["icc_profile"] - self.assertEqual(len(icc_profile), 3144) +def test_icc_profile(): + with Image.open(test_file) as im: + assert "icc_profile" in im.info - def test_no_icc_profile(self): - with Image.open("Tests/images/hopper_merged.psd") as im: - self.assertNotIn("icc_profile", im.info) + icc_profile = im.info["icc_profile"] + assert len(icc_profile) == 3144 - def test_combined_larger_than_size(self): - # The 'combined' sizes of the individual parts is larger than the - # declared 'size' of the extra data field, resulting in a backwards seek. - # If we instead take the 'size' of the extra data field as the source of truth, - # then the seek can't be negative - with self.assertRaises(IOError): - Image.open("Tests/images/combined_larger_than_size.psd") +def test_no_icc_profile(): + with Image.open("Tests/images/hopper_merged.psd") as im: + assert "icc_profile" not in im.info + + +def test_combined_larger_than_size(): + # The 'combined' sizes of the individual parts is larger than the + # declared 'size' of the extra data field, resulting in a backwards seek. + + # If we instead take the 'size' of the extra data field as the source of truth, + # then the seek can't be negative + with pytest.raises(IOError): + Image.open("Tests/images/combined_larger_than_size.psd") diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index adf97e1bf..03e26ef8b 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -1,47 +1,51 @@ import os -import unittest +import pytest from PIL import Image, SunImagePlugin -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper EXTRA_DIR = "Tests/images/sunraster" -class TestFileSun(PillowTestCase): - def test_sanity(self): - # Arrange - # Created with ImageMagick: convert hopper.jpg hopper.ras - test_file = "Tests/images/hopper.ras" +def test_sanity(): + # Arrange + # Created with ImageMagick: convert hopper.jpg hopper.ras + test_file = "Tests/images/hopper.ras" - # Act - with Image.open(test_file) as im: + # Act + with Image.open(test_file) as im: - # Assert - self.assertEqual(im.size, (128, 128)) + # Assert + assert im.size == (128, 128) - assert_image_similar(im, hopper(), 5) # visually verified + assert_image_similar(im, hopper(), 5) # visually verified - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, SunImagePlugin.SunImageFile, invalid_file) + invalid_file = "Tests/images/flower.jpg" + with pytest.raises(SyntaxError): + SunImagePlugin.SunImageFile(invalid_file) - def test_im1(self): - with Image.open("Tests/images/sunraster.im1") as im: - with Image.open("Tests/images/sunraster.im1.png") as target: + +def test_im1(): + with Image.open("Tests/images/sunraster.im1") as im: + with Image.open("Tests/images/sunraster.im1.png") as target: + assert_image_equal(im, target) + + +@pytest.mark.skipif( + not os.path.exists(EXTRA_DIR), reason="Extra image files not installed" +) +def test_others(): + files = ( + os.path.join(EXTRA_DIR, f) + for f in os.listdir(EXTRA_DIR) + if os.path.splitext(f)[1] in (".sun", ".SUN", ".ras") + ) + for path in files: + with Image.open(path) as im: + im.load() + assert isinstance(im, SunImagePlugin.SunImageFile) + target_path = "%s.png" % os.path.splitext(path)[0] + # im.save(target_file) + with Image.open(target_path) as target: assert_image_equal(im, target) - - @unittest.skipUnless(os.path.exists(EXTRA_DIR), "Extra image files not installed") - def test_others(self): - files = ( - os.path.join(EXTRA_DIR, f) - for f in os.listdir(EXTRA_DIR) - if os.path.splitext(f)[1] in (".sun", ".SUN", ".ras") - ) - for path in files: - with Image.open(path) as im: - im.load() - self.assertIsInstance(im, SunImagePlugin.SunImageFile) - target_path = "%s.png" % os.path.splitext(path)[0] - # im.save(target_file) - with Image.open(target_path) as target: - assert_image_equal(im, target) diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index 240f7acba..b0e4926b7 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -1,9 +1,7 @@ -import unittest - import pytest from PIL import Image, TarIO -from .helper import PillowTestCase, is_pypy +from .helper import is_pypy codecs = dir(Image.core) @@ -11,41 +9,44 @@ codecs = dir(Image.core) TEST_TAR_FILE = "Tests/images/hopper.tar" -class TestFileTar(PillowTestCase): - def setUp(self): - if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: - self.skipTest("neither jpeg nor zip support available") +def setup_module(): + if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: + pytest.skip("neither jpeg nor zip support available") - def test_sanity(self): - for codec, test_path, format in [ - ["zip_decoder", "hopper.png", "PNG"], - ["jpeg_decoder", "hopper.jpg", "JPEG"], - ]: - if codec in codecs: - with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar: - with Image.open(tar) as im: - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, format) - @unittest.skipIf(is_pypy(), "Requires CPython") - def test_unclosed_file(self): - def open(): - TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") +def test_sanity(): + for codec, test_path, format in [ + ["zip_decoder", "hopper.png", "PNG"], + ["jpeg_decoder", "hopper.jpg", "JPEG"], + ]: + if codec in codecs: + with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar: + with Image.open(tar) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format == format - pytest.warns(ResourceWarning, open) - def test_close(self): - def open(): - tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") - tar.close() +@pytest.mark.skipif(is_pypy(), reason="Requires CPython") +def test_unclosed_file(): + def open(): + TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") - pytest.warns(None, open) + pytest.warns(ResourceWarning, open) - def test_contextmanager(self): - def open(): - with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"): - pass - pytest.warns(None, open) +def test_close(): + def open(): + tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") + tar.close() + + pytest.warns(None, open) + + +def test_contextmanager(): + def open(): + with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"): + pass + + pytest.warns(None, open) diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 70e701d4c..187440d4e 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -1,33 +1,36 @@ +import pytest from PIL import Image, XpmImagePlugin -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import assert_image_similar, hopper TEST_FILE = "Tests/images/hopper.xpm" -class TestFileXpm(PillowTestCase): - def test_sanity(self): - with Image.open(TEST_FILE) as im: - im.load() - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "XPM") +def test_sanity(): + with Image.open(TEST_FILE) as im: + im.load() + assert im.mode == "P" + assert im.size == (128, 128) + assert im.format == "XPM" - # large error due to quantization->44 colors. - assert_image_similar(im.convert("RGB"), hopper("RGB"), 60) + # large error due to quantization->44 colors. + assert_image_similar(im.convert("RGB"), hopper("RGB"), 60) - def test_invalid_file(self): - invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, XpmImagePlugin.XpmImageFile, invalid_file) +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" - def test_load_read(self): - # Arrange - with Image.open(TEST_FILE) as im: - dummy_bytes = 1 + with pytest.raises(SyntaxError): + XpmImagePlugin.XpmImageFile(invalid_file) - # Act - data = im.load_read(dummy_bytes) - # Assert - self.assertEqual(len(data), 16384) +def test_load_read(): + # Arrange + with Image.open(TEST_FILE) as im: + dummy_bytes = 1 + + # Act + data = im.load_read(dummy_bytes) + + # Assert + assert len(data) == 16384 diff --git a/Tests/test_file_xvthumb.py b/Tests/test_file_xvthumb.py index 2a04b7bf1..7c8c45113 100644 --- a/Tests/test_file_xvthumb.py +++ b/Tests/test_file_xvthumb.py @@ -1,35 +1,37 @@ +import pytest from PIL import Image, XVThumbImagePlugin -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import assert_image_similar, hopper TEST_FILE = "Tests/images/hopper.p7" -class TestFileXVThumb(PillowTestCase): - def test_open(self): - # Act - with Image.open(TEST_FILE) as im: +def test_open(): + # Act + with Image.open(TEST_FILE) as im: - # Assert - self.assertEqual(im.format, "XVThumb") + # Assert + assert im.format == "XVThumb" - # Create a Hopper image with a similar XV palette - im_hopper = hopper().quantize(palette=im) - assert_image_similar(im, im_hopper, 9) + # Create a Hopper image with a similar XV palette + im_hopper = hopper().quantize(palette=im) + assert_image_similar(im, im_hopper, 9) - def test_unexpected_eof(self): - # Test unexpected EOF reading XV thumbnail file - # Arrange - bad_file = "Tests/images/hopper_bad.p7" - # Act / Assert - self.assertRaises(SyntaxError, XVThumbImagePlugin.XVThumbImageFile, bad_file) +def test_unexpected_eof(): + # Test unexpected EOF reading XV thumbnail file + # Arrange + bad_file = "Tests/images/hopper_bad.p7" - def test_invalid_file(self): - # Arrange - invalid_file = "Tests/images/flower.jpg" + # Act / Assert + with pytest.raises(SyntaxError): + XVThumbImagePlugin.XVThumbImageFile(bad_file) - # Act / Assert - self.assertRaises( - SyntaxError, XVThumbImagePlugin.XVThumbImageFile, invalid_file - ) + +def test_invalid_file(): + # Arrange + invalid_file = "Tests/images/flower.jpg" + + # Act / Assert + with pytest.raises(SyntaxError): + XVThumbImagePlugin.XVThumbImageFile(invalid_file) diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index 70ceb1c0f..3a2ce150d 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -1,103 +1,109 @@ +import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImageCrop(PillowTestCase): - def test_crop(self): - def crop(mode): - im = hopper(mode) - assert_image_equal(im.crop(), im) +def test_crop(): + def crop(mode): + im = hopper(mode) + assert_image_equal(im.crop(), im) - cropped = im.crop((50, 50, 100, 100)) - self.assertEqual(cropped.mode, mode) - self.assertEqual(cropped.size, (50, 50)) + cropped = im.crop((50, 50, 100, 100)) + assert cropped.mode == mode + assert cropped.size == (50, 50) - for mode in "1", "P", "L", "RGB", "I", "F": - crop(mode) + for mode in "1", "P", "L", "RGB", "I", "F": + crop(mode) - def test_wide_crop(self): - def crop(*bbox): - i = im.crop(bbox) - h = i.histogram() - while h and not h[-1]: - del h[-1] - return tuple(h) - im = Image.new("L", (100, 100), 1) +def test_wide_crop(): + def crop(*bbox): + i = im.crop(bbox) + h = i.histogram() + while h and not h[-1]: + del h[-1] + return tuple(h) - self.assertEqual(crop(0, 0, 100, 100), (0, 10000)) - self.assertEqual(crop(25, 25, 75, 75), (0, 2500)) + im = Image.new("L", (100, 100), 1) - # sides - self.assertEqual(crop(-25, 0, 25, 50), (1250, 1250)) - self.assertEqual(crop(0, -25, 50, 25), (1250, 1250)) - self.assertEqual(crop(75, 0, 125, 50), (1250, 1250)) - self.assertEqual(crop(0, 75, 50, 125), (1250, 1250)) + assert crop(0, 0, 100, 100) == (0, 10000) + assert crop(25, 25, 75, 75) == (0, 2500) - self.assertEqual(crop(-25, 25, 125, 75), (2500, 5000)) - self.assertEqual(crop(25, -25, 75, 125), (2500, 5000)) + # sides + assert crop(-25, 0, 25, 50) == (1250, 1250) + assert crop(0, -25, 50, 25) == (1250, 1250) + assert crop(75, 0, 125, 50) == (1250, 1250) + assert crop(0, 75, 50, 125) == (1250, 1250) - # corners - self.assertEqual(crop(-25, -25, 25, 25), (1875, 625)) - self.assertEqual(crop(75, -25, 125, 25), (1875, 625)) - self.assertEqual(crop(75, 75, 125, 125), (1875, 625)) - self.assertEqual(crop(-25, 75, 25, 125), (1875, 625)) + assert crop(-25, 25, 125, 75) == (2500, 5000) + assert crop(25, -25, 75, 125) == (2500, 5000) - def test_negative_crop(self): - # Check negative crop size (@PIL171) + # corners + assert crop(-25, -25, 25, 25) == (1875, 625) + assert crop(75, -25, 125, 25) == (1875, 625) + assert crop(75, 75, 125, 125) == (1875, 625) + assert crop(-25, 75, 25, 125) == (1875, 625) - im = Image.new("L", (512, 512)) - im = im.crop((400, 400, 200, 200)) - self.assertEqual(im.size, (0, 0)) - self.assertEqual(len(im.getdata()), 0) - self.assertRaises(IndexError, lambda: im.getdata()[0]) +def test_negative_crop(): + # Check negative crop size (@PIL171) - def test_crop_float(self): - # Check cropping floats are rounded to nearest integer - # https://github.com/python-pillow/Pillow/issues/1744 + im = Image.new("L", (512, 512)) + im = im.crop((400, 400, 200, 200)) - # Arrange - im = Image.new("RGB", (10, 10)) - self.assertEqual(im.size, (10, 10)) + assert im.size == (0, 0) + assert len(im.getdata()) == 0 + with pytest.raises(IndexError): + im.getdata()[0] - # Act - cropped = im.crop((0.9, 1.1, 4.2, 5.8)) - # Assert - self.assertEqual(cropped.size, (3, 5)) +def test_crop_float(): + # Check cropping floats are rounded to nearest integer + # https://github.com/python-pillow/Pillow/issues/1744 - def test_crop_crash(self): - # Image.crop crashes prepatch with an access violation - # apparently a use after free on windows, see - # https://github.com/python-pillow/Pillow/issues/1077 + # Arrange + im = Image.new("RGB", (10, 10)) + assert im.size == (10, 10) - test_img = "Tests/images/bmp/g/pal8-0.bmp" - extents = (1, 1, 10, 10) - # works prepatch - with Image.open(test_img) as img: - img2 = img.crop(extents) - img2.load() + # Act + cropped = im.crop((0.9, 1.1, 4.2, 5.8)) - # fail prepatch - with Image.open(test_img) as img: - img = img.crop(extents) - img.load() + # Assert + assert cropped.size == (3, 5) - def test_crop_zero(self): - im = Image.new("RGB", (0, 0), "white") +def test_crop_crash(): + # Image.crop crashes prepatch with an access violation + # apparently a use after free on Windows, see + # https://github.com/python-pillow/Pillow/issues/1077 - cropped = im.crop((0, 0, 0, 0)) - self.assertEqual(cropped.size, (0, 0)) + test_img = "Tests/images/bmp/g/pal8-0.bmp" + extents = (1, 1, 10, 10) + # works prepatch + with Image.open(test_img) as img: + img2 = img.crop(extents) + img2.load() - cropped = im.crop((10, 10, 20, 20)) - self.assertEqual(cropped.size, (10, 10)) - self.assertEqual(cropped.getdata()[0], (0, 0, 0)) + # fail prepatch + with Image.open(test_img) as img: + img = img.crop(extents) + img.load() - 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)) +def test_crop_zero(): + + im = Image.new("RGB", (0, 0), "white") + + cropped = im.crop((0, 0, 0, 0)) + assert cropped.size == (0, 0) + + cropped = im.crop((10, 10, 20, 20)) + assert cropped.size == (10, 10) + assert cropped.getdata()[0] == (0, 0, 0) + + im = Image.new("RGB", (0, 0)) + + cropped = im.crop((10, 10, 20, 20)) + assert cropped.size == (10, 10) + assert cropped.getdata()[2] == (0, 0, 0) diff --git a/Tests/test_image_draft.py b/Tests/test_image_draft.py index a185b3a63..0090ce378 100644 --- a/Tests/test_image_draft.py +++ b/Tests/test_image_draft.py @@ -1,73 +1,77 @@ +import pytest from PIL import Image -from .helper import PillowTestCase, fromstring, tostring +from .helper import fromstring, tostring -class TestImageDraft(PillowTestCase): - def setUp(self): - codecs = dir(Image.core) - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - self.skipTest("jpeg support not available") +def setup_module(): + codecs = dir(Image.core) + if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + pytest.skip("jpeg support not available") - def draft_roundtrip(self, in_mode, in_size, req_mode, req_size): - im = Image.new(in_mode, in_size) - data = tostring(im, "JPEG") - im = fromstring(data) - mode, box = im.draft(req_mode, req_size) - scale, _ = im.decoderconfig - self.assertEqual(box[:2], (0, 0)) - self.assertTrue((im.width - scale) < box[2] <= im.width) - self.assertTrue((im.height - scale) < box[3] <= im.height) - return im - def test_size(self): - for in_size, req_size, out_size in [ - ((435, 361), (2048, 2048), (435, 361)), # bigger - ((435, 361), (435, 361), (435, 361)), # same - ((128, 128), (64, 64), (64, 64)), - ((128, 128), (32, 32), (32, 32)), - ((128, 128), (16, 16), (16, 16)), - # large requested width - ((435, 361), (218, 128), (435, 361)), # almost 2x - ((435, 361), (217, 128), (218, 181)), # more than 2x - ((435, 361), (109, 64), (218, 181)), # almost 4x - ((435, 361), (108, 64), (109, 91)), # more than 4x - ((435, 361), (55, 32), (109, 91)), # almost 8x - ((435, 361), (54, 32), (55, 46)), # more than 8x - ((435, 361), (27, 16), (55, 46)), # more than 16x - # and vice versa - ((435, 361), (128, 181), (435, 361)), # almost 2x - ((435, 361), (128, 180), (218, 181)), # more than 2x - ((435, 361), (64, 91), (218, 181)), # almost 4x - ((435, 361), (64, 90), (109, 91)), # more than 4x - ((435, 361), (32, 46), (109, 91)), # almost 8x - ((435, 361), (32, 45), (55, 46)), # more than 8x - ((435, 361), (16, 22), (55, 46)), # more than 16x - ]: - im = self.draft_roundtrip("L", in_size, None, req_size) - im.load() - self.assertEqual(im.size, out_size) +def draft_roundtrip(in_mode, in_size, req_mode, req_size): + im = Image.new(in_mode, in_size) + data = tostring(im, "JPEG") + im = fromstring(data) + mode, box = im.draft(req_mode, req_size) + scale, _ = im.decoderconfig + assert box[:2] == (0, 0) + assert (im.width - scale) < box[2] <= im.width + assert (im.height - scale) < box[3] <= im.height + return im - def test_mode(self): - for in_mode, req_mode, out_mode in [ - ("RGB", "1", "RGB"), - ("RGB", "L", "L"), - ("RGB", "RGB", "RGB"), - ("RGB", "YCbCr", "YCbCr"), - ("L", "1", "L"), - ("L", "L", "L"), - ("L", "RGB", "L"), - ("L", "YCbCr", "L"), - ("CMYK", "1", "CMYK"), - ("CMYK", "L", "CMYK"), - ("CMYK", "RGB", "CMYK"), - ("CMYK", "YCbCr", "CMYK"), - ]: - im = self.draft_roundtrip(in_mode, (64, 64), req_mode, None) - im.load() - self.assertEqual(im.mode, out_mode) - def test_several_drafts(self): - im = self.draft_roundtrip("L", (128, 128), None, (64, 64)) - im.draft(None, (64, 64)) +def test_size(): + for in_size, req_size, out_size in [ + ((435, 361), (2048, 2048), (435, 361)), # bigger + ((435, 361), (435, 361), (435, 361)), # same + ((128, 128), (64, 64), (64, 64)), + ((128, 128), (32, 32), (32, 32)), + ((128, 128), (16, 16), (16, 16)), + # large requested width + ((435, 361), (218, 128), (435, 361)), # almost 2x + ((435, 361), (217, 128), (218, 181)), # more than 2x + ((435, 361), (109, 64), (218, 181)), # almost 4x + ((435, 361), (108, 64), (109, 91)), # more than 4x + ((435, 361), (55, 32), (109, 91)), # almost 8x + ((435, 361), (54, 32), (55, 46)), # more than 8x + ((435, 361), (27, 16), (55, 46)), # more than 16x + # and vice versa + ((435, 361), (128, 181), (435, 361)), # almost 2x + ((435, 361), (128, 180), (218, 181)), # more than 2x + ((435, 361), (64, 91), (218, 181)), # almost 4x + ((435, 361), (64, 90), (109, 91)), # more than 4x + ((435, 361), (32, 46), (109, 91)), # almost 8x + ((435, 361), (32, 45), (55, 46)), # more than 8x + ((435, 361), (16, 22), (55, 46)), # more than 16x + ]: + im = draft_roundtrip("L", in_size, None, req_size) im.load() + assert im.size == out_size + + +def test_mode(): + for in_mode, req_mode, out_mode in [ + ("RGB", "1", "RGB"), + ("RGB", "L", "L"), + ("RGB", "RGB", "RGB"), + ("RGB", "YCbCr", "YCbCr"), + ("L", "1", "L"), + ("L", "L", "L"), + ("L", "RGB", "L"), + ("L", "YCbCr", "L"), + ("CMYK", "1", "CMYK"), + ("CMYK", "L", "CMYK"), + ("CMYK", "RGB", "CMYK"), + ("CMYK", "YCbCr", "CMYK"), + ]: + im = draft_roundtrip(in_mode, (64, 64), req_mode, None) + im.load() + assert im.mode == out_mode + + +def test_several_drafts(): + im = draft_roundtrip("L", (128, 128), None, (64, 64)) + im.draft(None, (64, 64)) + im.load() diff --git a/Tests/test_image_entropy.py b/Tests/test_image_entropy.py index 941f7665d..876d676fe 100644 --- a/Tests/test_image_entropy.py +++ b/Tests/test_image_entropy.py @@ -1,17 +1,16 @@ -from .helper import PillowTestCase, hopper +from .helper import hopper -class TestImageEntropy(PillowTestCase): - def test_entropy(self): - def entropy(mode): - return hopper(mode).entropy() +def test_entropy(): + def entropy(mode): + return hopper(mode).entropy() - self.assertAlmostEqual(entropy("1"), 0.9138803254693582) - self.assertAlmostEqual(entropy("L"), 7.063008716585465) - self.assertAlmostEqual(entropy("I"), 7.063008716585465) - self.assertAlmostEqual(entropy("F"), 7.063008716585465) - self.assertAlmostEqual(entropy("P"), 5.0530452472519745) - self.assertAlmostEqual(entropy("RGB"), 8.821286587714319) - self.assertAlmostEqual(entropy("RGBA"), 7.42724306524488) - self.assertAlmostEqual(entropy("CMYK"), 7.4272430652448795) - self.assertAlmostEqual(entropy("YCbCr"), 7.698360534903628) + assert round(abs(entropy("1") - 0.9138803254693582), 7) == 0 + assert round(abs(entropy("L") - 7.063008716585465), 7) == 0 + assert round(abs(entropy("I") - 7.063008716585465), 7) == 0 + assert round(abs(entropy("F") - 7.063008716585465), 7) == 0 + assert round(abs(entropy("P") - 5.0530452472519745), 7) == 0 + assert round(abs(entropy("RGB") - 8.821286587714319), 7) == 0 + assert round(abs(entropy("RGBA") - 7.42724306524488), 7) == 0 + assert round(abs(entropy("CMYK") - 7.4272430652448795), 7) == 0 + assert round(abs(entropy("YCbCr") - 7.698360534903628), 7) == 0 diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 6b36267b3..42f4f448d 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -1,143 +1,155 @@ +import pytest from PIL import Image, ImageFilter -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImageFilter(PillowTestCase): - def test_sanity(self): - def filter(filter): - for mode in ["L", "RGB", "CMYK"]: - im = hopper(mode) - out = im.filter(filter) - self.assertEqual(out.mode, im.mode) - self.assertEqual(out.size, im.size) +def test_sanity(): + def apply_filter(filter_to_apply): + for mode in ["L", "RGB", "CMYK"]: + im = hopper(mode) + out = im.filter(filter_to_apply) + assert out.mode == im.mode + assert out.size == im.size - filter(ImageFilter.BLUR) - filter(ImageFilter.CONTOUR) - filter(ImageFilter.DETAIL) - filter(ImageFilter.EDGE_ENHANCE) - filter(ImageFilter.EDGE_ENHANCE_MORE) - filter(ImageFilter.EMBOSS) - filter(ImageFilter.FIND_EDGES) - filter(ImageFilter.SMOOTH) - filter(ImageFilter.SMOOTH_MORE) - filter(ImageFilter.SHARPEN) - filter(ImageFilter.MaxFilter) - filter(ImageFilter.MedianFilter) - filter(ImageFilter.MinFilter) - filter(ImageFilter.ModeFilter) - filter(ImageFilter.GaussianBlur) - filter(ImageFilter.GaussianBlur(5)) - filter(ImageFilter.BoxBlur(5)) - filter(ImageFilter.UnsharpMask) - filter(ImageFilter.UnsharpMask(10)) + apply_filter(ImageFilter.BLUR) + apply_filter(ImageFilter.CONTOUR) + apply_filter(ImageFilter.DETAIL) + apply_filter(ImageFilter.EDGE_ENHANCE) + apply_filter(ImageFilter.EDGE_ENHANCE_MORE) + apply_filter(ImageFilter.EMBOSS) + apply_filter(ImageFilter.FIND_EDGES) + apply_filter(ImageFilter.SMOOTH) + apply_filter(ImageFilter.SMOOTH_MORE) + apply_filter(ImageFilter.SHARPEN) + apply_filter(ImageFilter.MaxFilter) + apply_filter(ImageFilter.MedianFilter) + apply_filter(ImageFilter.MinFilter) + apply_filter(ImageFilter.ModeFilter) + apply_filter(ImageFilter.GaussianBlur) + apply_filter(ImageFilter.GaussianBlur(5)) + apply_filter(ImageFilter.BoxBlur(5)) + apply_filter(ImageFilter.UnsharpMask) + apply_filter(ImageFilter.UnsharpMask(10)) - self.assertRaises(TypeError, filter, "hello") + with pytest.raises(TypeError): + apply_filter("hello") - def test_crash(self): - # crashes on small images - im = Image.new("RGB", (1, 1)) - im.filter(ImageFilter.SMOOTH) +def test_crash(): - im = Image.new("RGB", (2, 2)) - im.filter(ImageFilter.SMOOTH) + # crashes on small images + im = Image.new("RGB", (1, 1)) + im.filter(ImageFilter.SMOOTH) - im = Image.new("RGB", (3, 3)) - im.filter(ImageFilter.SMOOTH) + im = Image.new("RGB", (2, 2)) + im.filter(ImageFilter.SMOOTH) - def test_modefilter(self): - def modefilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 - mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - return mod, mod2 + im = Image.new("RGB", (3, 3)) + im.filter(ImageFilter.SMOOTH) - self.assertEqual(modefilter("1"), (4, 0)) - self.assertEqual(modefilter("L"), (4, 0)) - self.assertEqual(modefilter("P"), (4, 0)) - self.assertEqual(modefilter("RGB"), ((4, 0, 0), (0, 0, 0))) - def test_rankfilter(self): - def rankfilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - minimum = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) - med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) - maximum = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) - return minimum, med, maximum +def test_modefilter(): + def modefilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 + mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + return mod, mod2 - self.assertEqual(rankfilter("1"), (0, 4, 8)) - self.assertEqual(rankfilter("L"), (0, 4, 8)) - self.assertRaises(ValueError, rankfilter, "P") - self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) - self.assertEqual(rankfilter("I"), (0, 4, 8)) - self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) + assert modefilter("1") == (4, 0) + assert modefilter("L") == (4, 0) + assert modefilter("P") == (4, 0) + assert modefilter("RGB") == ((4, 0, 0), (0, 0, 0)) - def test_rankfilter_properties(self): - rankfilter = ImageFilter.RankFilter(1, 2) - self.assertEqual(rankfilter.size, 1) - self.assertEqual(rankfilter.rank, 2) +def test_rankfilter(): + def rankfilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + minimum = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) + med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) + maximum = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) + return minimum, med, maximum - def test_builtinfilter_p(self): - builtinFilter = ImageFilter.BuiltinFilter() + assert rankfilter("1") == (0, 4, 8) + assert rankfilter("L") == (0, 4, 8) + with pytest.raises(ValueError): + rankfilter("P") + assert rankfilter("RGB") == ((0, 0, 0), (4, 0, 0), (8, 0, 0)) + assert rankfilter("I") == (0, 4, 8) + assert rankfilter("F") == (0.0, 4.0, 8.0) - self.assertRaises(ValueError, builtinFilter.filter, hopper("P")) - def test_kernel_not_enough_coefficients(self): - self.assertRaises(ValueError, lambda: ImageFilter.Kernel((3, 3), (0, 0))) +def test_rankfilter_properties(): + rankfilter = ImageFilter.RankFilter(1, 2) - def test_consistency_3x3(self): - with Image.open("Tests/images/hopper.bmp") as source: - with Image.open("Tests/images/hopper_emboss.bmp") as reference: - kernel = ImageFilter.Kernel( # noqa: E127 - (3, 3), - # fmt: off - (-1, -1, 0, - -1, 0, 1, - 0, 1, 1), - # fmt: on - 0.3, + assert rankfilter.size == 1 + assert rankfilter.rank == 2 + + +def test_builtinfilter_p(): + builtinFilter = ImageFilter.BuiltinFilter() + + with pytest.raises(ValueError): + builtinFilter.filter(hopper("P")) + + +def test_kernel_not_enough_coefficients(): + with pytest.raises(ValueError): + ImageFilter.Kernel((3, 3), (0, 0)) + + +def test_consistency_3x3(): + with Image.open("Tests/images/hopper.bmp") as source: + with Image.open("Tests/images/hopper_emboss.bmp") as reference: + kernel = ImageFilter.Kernel( # noqa: E127 + (3, 3), + # fmt: off + (-1, -1, 0, + -1, 0, 1, + 0, 1, 1), + # fmt: on + 0.3, + ) + source = source.split() * 2 + reference = reference.split() * 2 + + for mode in ["L", "LA", "RGB", "CMYK"]: + assert_image_equal( + Image.merge(mode, source[: len(mode)]).filter(kernel), + Image.merge(mode, reference[: len(mode)]), ) - source = source.split() * 2 - reference = reference.split() * 2 - for mode in ["L", "LA", "RGB", "CMYK"]: - assert_image_equal( - Image.merge(mode, source[: len(mode)]).filter(kernel), - Image.merge(mode, reference[: len(mode)]), - ) - def test_consistency_5x5(self): - with Image.open("Tests/images/hopper.bmp") as source: - with Image.open("Tests/images/hopper_emboss_more.bmp") as reference: - kernel = ImageFilter.Kernel( # noqa: E127 - (5, 5), - # fmt: off - (-1, -1, -1, -1, 0, - -1, -1, -1, 0, 1, - -1, -1, 0, 1, 1, - -1, 0, 1, 1, 1, - 0, 1, 1, 1, 1), - # fmt: on - 0.3, +def test_consistency_5x5(): + with Image.open("Tests/images/hopper.bmp") as source: + with Image.open("Tests/images/hopper_emboss_more.bmp") as reference: + kernel = ImageFilter.Kernel( # noqa: E127 + (5, 5), + # fmt: off + (-1, -1, -1, -1, 0, + -1, -1, -1, 0, 1, + -1, -1, 0, 1, 1, + -1, 0, 1, 1, 1, + 0, 1, 1, 1, 1), + # fmt: on + 0.3, + ) + source = source.split() * 2 + reference = reference.split() * 2 + + for mode in ["L", "LA", "RGB", "CMYK"]: + assert_image_equal( + Image.merge(mode, source[: len(mode)]).filter(kernel), + Image.merge(mode, reference[: len(mode)]), ) - source = source.split() * 2 - reference = reference.split() * 2 - - for mode in ["L", "LA", "RGB", "CMYK"]: - assert_image_equal( - Image.merge(mode, source[: len(mode)]).filter(kernel), - Image.merge(mode, reference[: len(mode)]), - ) diff --git a/Tests/test_image_frombytes.py b/Tests/test_image_frombytes.py index 88c196432..faf94ac77 100644 --- a/Tests/test_image_frombytes.py +++ b/Tests/test_image_frombytes.py @@ -1,14 +1,16 @@ +import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImageFromBytes(PillowTestCase): - def test_sanity(self): - im1 = hopper() - im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) +def test_sanity(): + im1 = hopper() + im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) - assert_image_equal(im1, im2) + assert_image_equal(im1, im2) - def test_not_implemented(self): - self.assertRaises(NotImplementedError, Image.fromstring) + +def test_not_implemented(): + with pytest.raises(NotImplementedError): + Image.fromstring() diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 36772d72c..fe868b7c2 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -1,39 +1,48 @@ -from .helper import PillowTestCase, assert_image_equal, hopper +import pytest + +from .helper import assert_image_equal, hopper -class TestImagePoint(PillowTestCase): - def test_sanity(self): - im = hopper() +def test_sanity(): + im = hopper() - self.assertRaises(ValueError, im.point, list(range(256))) - im.point(list(range(256)) * 3) - im.point(lambda x: x) + with pytest.raises(ValueError): + im.point(list(range(256))) + im.point(list(range(256)) * 3) + im.point(lambda x: x) - im = im.convert("I") - self.assertRaises(ValueError, im.point, list(range(256))) - im.point(lambda x: x * 1) - im.point(lambda x: x + 1) - im.point(lambda x: x * 1 + 1) - self.assertRaises(TypeError, im.point, lambda x: x - 1) - self.assertRaises(TypeError, im.point, lambda x: x / 1) + im = im.convert("I") + with pytest.raises(ValueError): + im.point(list(range(256))) + im.point(lambda x: x * 1) + im.point(lambda x: x + 1) + im.point(lambda x: x * 1 + 1) + with pytest.raises(TypeError): + im.point(lambda x: x - 1) + with pytest.raises(TypeError): + im.point(lambda x: x / 1) - def test_16bit_lut(self): - """ Tests for 16 bit -> 8 bit lut for converting I->L images - see https://github.com/python-pillow/Pillow/issues/440 - """ - im = hopper("I") - im.point(list(range(256)) * 256, "L") - def test_f_lut(self): - """ Tests for floating point lut of 8bit gray image """ - im = hopper("L") - lut = [0.5 * float(x) for x in range(256)] +def test_16bit_lut(): + """ Tests for 16 bit -> 8 bit lut for converting I->L images + see https://github.com/python-pillow/Pillow/issues/440 + """ + im = hopper("I") + im.point(list(range(256)) * 256, "L") - out = im.point(lut, "F") - int_lut = [x // 2 for x in range(256)] - assert_image_equal(out.convert("L"), im.point(int_lut, "L")) +def test_f_lut(): + """ Tests for floating point lut of 8bit gray image """ + im = hopper("L") + lut = [0.5 * float(x) for x in range(256)] - def test_f_mode(self): - im = hopper("F") - self.assertRaises(ValueError, im.point, None) + out = im.point(lut, "F") + + int_lut = [x // 2 for x in range(256)] + assert_image_equal(out.convert("L"), im.point(int_lut, "L")) + + +def test_f_mode(): + im = hopper("F") + with pytest.raises(ValueError): + im.point(None) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 2c6881179..54712fd6c 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -3,83 +3,87 @@ from array import array from PIL import Image -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImagePutData(PillowTestCase): - def test_sanity(self): +def test_sanity(): + im1 = hopper() - im1 = hopper() + data = list(im1.getdata()) - data = list(im1.getdata()) + im2 = Image.new(im1.mode, im1.size, 0) + im2.putdata(data) - im2 = Image.new(im1.mode, im1.size, 0) - im2.putdata(data) + assert_image_equal(im1, im2) - assert_image_equal(im1, im2) + # readonly + im2 = Image.new(im1.mode, im2.size, 0) + im2.readonly = 1 + im2.putdata(data) - # readonly - im2 = Image.new(im1.mode, im2.size, 0) - im2.readonly = 1 - im2.putdata(data) + assert not im2.readonly + assert_image_equal(im1, im2) - self.assertFalse(im2.readonly) - assert_image_equal(im1, im2) - def test_long_integers(self): - # see bug-200802-systemerror - def put(value): - im = Image.new("RGBA", (1, 1)) - im.putdata([value]) - return im.getpixel((0, 0)) +def test_long_integers(): + # see bug-200802-systemerror + def put(value): + im = Image.new("RGBA", (1, 1)) + im.putdata([value]) + return im.getpixel((0, 0)) - self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) - self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) - self.assertEqual(put(-1), (255, 255, 255, 255)) - self.assertEqual(put(-1), (255, 255, 255, 255)) - if sys.maxsize > 2 ** 32: - self.assertEqual(put(sys.maxsize), (255, 255, 255, 255)) - else: - self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) + assert put(0xFFFFFFFF) == (255, 255, 255, 255) + assert put(0xFFFFFFFF) == (255, 255, 255, 255) + assert put(-1) == (255, 255, 255, 255) + assert put(-1) == (255, 255, 255, 255) + if sys.maxsize > 2 ** 32: + assert put(sys.maxsize) == (255, 255, 255, 255) + else: + assert put(sys.maxsize) == (255, 255, 255, 127) - def test_pypy_performance(self): - im = Image.new("L", (256, 256)) - im.putdata(list(range(256)) * 256) - def test_mode_i(self): - src = hopper("L") - data = list(src.getdata()) - im = Image.new("I", src.size, 0) - im.putdata(data, 2, 256) +def test_pypy_performance(): + im = Image.new("L", (256, 256)) + im.putdata(list(range(256)) * 256) - target = [2 * elt + 256 for elt in data] - self.assertEqual(list(im.getdata()), target) - def test_mode_F(self): - src = hopper("L") - data = list(src.getdata()) - im = Image.new("F", src.size, 0) - im.putdata(data, 2.0, 256.0) +def test_mode_i(): + src = hopper("L") + data = list(src.getdata()) + im = Image.new("I", src.size, 0) + im.putdata(data, 2, 256) - target = [2.0 * float(elt) + 256.0 for elt in data] - self.assertEqual(list(im.getdata()), target) + target = [2 * elt + 256 for elt in data] + assert list(im.getdata()) == target - def test_array_B(self): - # shouldn't segfault - # see https://github.com/python-pillow/Pillow/issues/1008 - arr = array("B", [0]) * 15000 - im = Image.new("L", (150, 100)) - im.putdata(arr) +def test_mode_F(): + src = hopper("L") + data = list(src.getdata()) + im = Image.new("F", src.size, 0) + im.putdata(data, 2.0, 256.0) - self.assertEqual(len(im.getdata()), len(arr)) + target = [2.0 * float(elt) + 256.0 for elt in data] + assert list(im.getdata()) == target - def test_array_F(self): - # shouldn't segfault - # see https://github.com/python-pillow/Pillow/issues/1008 - im = Image.new("F", (150, 100)) - arr = array("f", [0.0]) * 15000 - im.putdata(arr) +def test_array_B(): + # shouldn't segfault + # see https://github.com/python-pillow/Pillow/issues/1008 - self.assertEqual(len(im.getdata()), len(arr)) + arr = array("B", [0]) * 15000 + im = Image.new("L", (150, 100)) + im.putdata(arr) + + assert len(im.getdata()) == len(arr) + + +def test_array_F(): + # shouldn't segfault + # see https://github.com/python-pillow/Pillow/issues/1008 + + im = Image.new("F", (150, 100)) + arr = array("f", [0.0]) * 15000 + im.putdata(arr) + + assert len(im.getdata()) == len(arr) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 70bc3e488..96fa143a9 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -1,67 +1,74 @@ +import pytest from PIL import Image -from .helper import PillowTestCase, assert_image, assert_image_similar, hopper +from .helper import assert_image, assert_image_similar, hopper -class TestImageQuantize(PillowTestCase): - def test_sanity(self): - image = hopper() - converted = image.quantize() - assert_image(converted, "P", converted.size) - assert_image_similar(converted.convert("RGB"), image, 10) +def test_sanity(): + image = hopper() + converted = image.quantize() + assert_image(converted, "P", converted.size) + assert_image_similar(converted.convert("RGB"), image, 10) - image = hopper() - converted = image.quantize(palette=hopper("P")) - assert_image(converted, "P", converted.size) - assert_image_similar(converted.convert("RGB"), image, 60) + image = hopper() + converted = image.quantize(palette=hopper("P")) + assert_image(converted, "P", converted.size) + assert_image_similar(converted.convert("RGB"), image, 60) - def test_libimagequant_quantize(self): - image = hopper() - try: - converted = image.quantize(100, Image.LIBIMAGEQUANT) - except ValueError as ex: - if "dependency" in str(ex).lower(): - self.skipTest("libimagequant support not available") - else: - raise - assert_image(converted, "P", converted.size) - assert_image_similar(converted.convert("RGB"), image, 15) - self.assertEqual(len(converted.getcolors()), 100) - def test_octree_quantize(self): - image = hopper() - converted = image.quantize(100, Image.FASTOCTREE) - assert_image(converted, "P", converted.size) - assert_image_similar(converted.convert("RGB"), image, 20) - self.assertEqual(len(converted.getcolors()), 100) +def test_libimagequant_quantize(): + image = hopper() + try: + converted = image.quantize(100, Image.LIBIMAGEQUANT) + except ValueError as ex: + if "dependency" in str(ex).lower(): + pytest.skip("libimagequant support not available") + else: + raise + assert_image(converted, "P", converted.size) + assert_image_similar(converted.convert("RGB"), image, 15) + assert len(converted.getcolors()) == 100 - def test_rgba_quantize(self): - image = hopper("RGBA") - self.assertRaises(ValueError, image.quantize, method=0) - self.assertEqual(image.quantize().convert().mode, "RGBA") +def test_octree_quantize(): + image = hopper() + converted = image.quantize(100, Image.FASTOCTREE) + assert_image(converted, "P", converted.size) + assert_image_similar(converted.convert("RGB"), image, 20) + assert len(converted.getcolors()) == 100 - def test_quantize(self): - with Image.open("Tests/images/caption_6_33_22.png") as image: - image = image.convert("RGB") - converted = image.quantize() - assert_image(converted, "P", converted.size) - assert_image_similar(converted.convert("RGB"), image, 1) - def test_quantize_no_dither(self): - image = hopper() - with Image.open("Tests/images/caption_6_33_22.png") as palette: - palette = palette.convert("P") +def test_rgba_quantize(): + image = hopper("RGBA") + with pytest.raises(ValueError): + image.quantize(method=0) - converted = image.quantize(dither=0, palette=palette) - assert_image(converted, "P", converted.size) + assert image.quantize().convert().mode == "RGBA" - def test_quantize_dither_diff(self): - image = hopper() - with Image.open("Tests/images/caption_6_33_22.png") as palette: - palette = palette.convert("P") - dither = image.quantize(dither=1, palette=palette) - nodither = image.quantize(dither=0, palette=palette) +def test_quantize(): + with Image.open("Tests/images/caption_6_33_22.png") as image: + image = image.convert("RGB") + converted = image.quantize() + assert_image(converted, "P", converted.size) + assert_image_similar(converted.convert("RGB"), image, 1) - self.assertNotEqual(dither.tobytes(), nodither.tobytes()) + +def test_quantize_no_dither(): + image = hopper() + with Image.open("Tests/images/caption_6_33_22.png") as palette: + palette = palette.convert("P") + + converted = image.quantize(dither=0, palette=palette) + assert_image(converted, "P", converted.size) + + +def test_quantize_dither_diff(): + image = hopper() + with Image.open("Tests/images/caption_6_33_22.png") as palette: + palette = palette.convert("P") + + dither = image.quantize(dither=1, palette=palette) + nodither = image.quantize(dither=0, palette=palette) + + assert dither.tobytes() != nodither.tobytes() diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 11da27aef..a41d850bb 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,127 +1,140 @@ from PIL import Image -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper -class TestImageRotate(PillowTestCase): - def rotate(self, im, mode, angle, center=None, translate=None): - out = im.rotate(angle, center=center, translate=translate) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) # default rotate clips output - out = im.rotate(angle, center=center, translate=translate, expand=1) - 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) +def rotate(im, mode, angle, center=None, translate=None): + out = im.rotate(angle, center=center, translate=translate) + assert out.mode == mode + assert out.size == im.size # default rotate clips output + out = im.rotate(angle, center=center, translate=translate, expand=1) + assert out.mode == mode + if angle % 180 == 0: + assert out.size == im.size + elif im.size == (0, 0): + assert out.size == im.size + else: + assert out.size != im.size - def test_mode(self): - for mode in ("1", "P", "L", "RGB", "I", "F"): - im = hopper(mode) - self.rotate(im, mode, 45) - def test_angle(self): - for angle in (0, 90, 180, 270): - with Image.open("Tests/images/test-card.png") as im: - self.rotate(im, im.mode, angle) +def test_mode(): + for mode in ("1", "P", "L", "RGB", "I", "F"): + im = hopper(mode) + rotate(im, mode, 45) - def test_zero(self): - for angle in (0, 45, 90, 180, 270): - im = Image.new("RGB", (0, 0)) - self.rotate(im, im.mode, angle) - def test_resample(self): - # Target image creation, inspected by eye. - # >>> im = Image.open('Tests/images/hopper.ppm') - # >>> im = im.rotate(45, resample=Image.BICUBIC, expand=True) - # >>> im.save('Tests/images/hopper_45.png') +def test_angle(): + for angle in (0, 90, 180, 270): + with Image.open("Tests/images/test-card.png") as im: + rotate(im, im.mode, angle) - with Image.open("Tests/images/hopper_45.png") as target: - for (resample, epsilon) in ( - (Image.NEAREST, 10), - (Image.BILINEAR, 5), - (Image.BICUBIC, 0), - ): - im = hopper() - im = im.rotate(45, resample=resample, expand=True) - assert_image_similar(im, target, epsilon) - def test_center_0(self): - im = hopper() - im = im.rotate(45, center=(0, 0), resample=Image.BICUBIC) +def test_zero(): + for angle in (0, 45, 90, 180, 270): + im = Image.new("RGB", (0, 0)) + rotate(im, im.mode, angle) - with Image.open("Tests/images/hopper_45.png") as target: - target_origin = target.size[1] / 2 - target = target.crop((0, target_origin, 128, target_origin + 128)) - assert_image_similar(im, target, 15) +def test_resample(): + # Target image creation, inspected by eye. + # >>> im = Image.open('Tests/images/hopper.ppm') + # >>> im = im.rotate(45, resample=Image.BICUBIC, expand=True) + # >>> im.save('Tests/images/hopper_45.png') - def test_center_14(self): - im = hopper() - im = im.rotate(45, center=(14, 14), resample=Image.BICUBIC) + with Image.open("Tests/images/hopper_45.png") as target: + for (resample, epsilon) in ( + (Image.NEAREST, 10), + (Image.BILINEAR, 5), + (Image.BICUBIC, 0), + ): + im = hopper() + im = im.rotate(45, resample=resample, expand=True) + assert_image_similar(im, target, epsilon) - with Image.open("Tests/images/hopper_45.png") as target: - target_origin = target.size[1] / 2 - 14 - target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) - assert_image_similar(im, target, 10) +def test_center_0(): + im = hopper() + im = im.rotate(45, center=(0, 0), resample=Image.BICUBIC) - def test_translate(self): - im = hopper() - with Image.open("Tests/images/hopper_45.png") as target: - target_origin = (target.size[1] / 2 - 64) - 5 - target = target.crop( - (target_origin, target_origin, target_origin + 128, target_origin + 128) - ) + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = target.size[1] / 2 + target = target.crop((0, target_origin, 128, target_origin + 128)) - im = im.rotate(45, translate=(5, 5), resample=Image.BICUBIC) + assert_image_similar(im, target, 15) - assert_image_similar(im, target, 1) - def test_fastpath_center(self): - # if the center is -1,-1 and we rotate by 90<=x<=270 the - # resulting image should be black - for angle in (90, 180, 270): - im = hopper().rotate(angle, center=(-1, -1)) - assert_image_equal(im, Image.new("RGB", im.size, "black")) +def test_center_14(): + im = hopper() + im = im.rotate(45, center=(14, 14), resample=Image.BICUBIC) - def test_fastpath_translate(self): - # if we post-translate by -128 - # resulting image should be black - for angle in (0, 90, 180, 270): - im = hopper().rotate(angle, translate=(-128, -128)) - assert_image_equal(im, Image.new("RGB", im.size, "black")) + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = target.size[1] / 2 - 14 + target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) - def test_center(self): - im = hopper() - self.rotate(im, im.mode, 45, center=(0, 0)) - self.rotate(im, im.mode, 45, translate=(im.size[0] / 2, 0)) - self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] / 2, 0)) + assert_image_similar(im, target, 10) - def test_rotate_no_fill(self): - im = Image.new("RGB", (100, 100), "green") - im = im.rotate(45) - with Image.open("Tests/images/rotate_45_no_fill.png") as target: - assert_image_equal(im, target) - def test_rotate_with_fill(self): - im = Image.new("RGB", (100, 100), "green") - im = im.rotate(45, fillcolor="white") - with Image.open("Tests/images/rotate_45_with_fill.png") as target: - assert_image_equal(im, target) +def test_translate(): + im = hopper() + with Image.open("Tests/images/hopper_45.png") as target: + target_origin = (target.size[1] / 2 - 64) - 5 + target = target.crop( + (target_origin, target_origin, target_origin + 128, target_origin + 128) + ) - def test_alpha_rotate_no_fill(self): - # Alpha images are handled differently internally - im = Image.new("RGBA", (10, 10), "green") - im = im.rotate(45, expand=1) - corner = im.getpixel((0, 0)) - self.assertEqual(corner, (0, 0, 0, 0)) + im = im.rotate(45, translate=(5, 5), resample=Image.BICUBIC) - def test_alpha_rotate_with_fill(self): - # Alpha images are handled differently internally - im = Image.new("RGBA", (10, 10), "green") - im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255)) - corner = im.getpixel((0, 0)) - self.assertEqual(corner, (255, 0, 0, 255)) + assert_image_similar(im, target, 1) + + +def test_fastpath_center(): + # if the center is -1,-1 and we rotate by 90<=x<=270 the + # resulting image should be black + for angle in (90, 180, 270): + im = hopper().rotate(angle, center=(-1, -1)) + assert_image_equal(im, Image.new("RGB", im.size, "black")) + + +def test_fastpath_translate(): + # if we post-translate by -128 + # resulting image should be black + for angle in (0, 90, 180, 270): + im = hopper().rotate(angle, translate=(-128, -128)) + assert_image_equal(im, Image.new("RGB", im.size, "black")) + + +def test_center(): + im = hopper() + rotate(im, im.mode, 45, center=(0, 0)) + rotate(im, im.mode, 45, translate=(im.size[0] / 2, 0)) + rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] / 2, 0)) + + +def test_rotate_no_fill(): + im = Image.new("RGB", (100, 100), "green") + im = im.rotate(45) + with Image.open("Tests/images/rotate_45_no_fill.png") as target: + assert_image_equal(im, target) + + +def test_rotate_with_fill(): + im = Image.new("RGB", (100, 100), "green") + im = im.rotate(45, fillcolor="white") + with Image.open("Tests/images/rotate_45_with_fill.png") as target: + assert_image_equal(im, target) + + +def test_alpha_rotate_no_fill(): + # Alpha images are handled differently internally + im = Image.new("RGBA", (10, 10), "green") + im = im.rotate(45, expand=1) + corner = im.getpixel((0, 0)) + assert corner == (0, 0, 0, 0) + + +def test_alpha_rotate_with_fill(): + # Alpha images are handled differently internally + im = Image.new("RGBA", (10, 10), "green") + im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255)) + corner = im.getpixel((0, 0)) + assert corner == (255, 0, 0, 255) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 216793673..de15f5e2e 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -1,7 +1,7 @@ +import pytest from PIL import Image from .helper import ( - PillowTestCase, assert_image_equal, assert_image_similar, fromstring, @@ -10,98 +10,103 @@ from .helper import ( ) -class TestImageThumbnail(PillowTestCase): - def test_sanity(self): - im = hopper() - self.assertIsNone(im.thumbnail((100, 100))) +def test_sanity(): + im = hopper() + assert im.thumbnail((100, 100)) is None - self.assertEqual(im.size, (100, 100)) + assert im.size == (100, 100) - def test_aspect(self): - im = Image.new("L", (128, 128)) - im.thumbnail((100, 100)) - self.assertEqual(im.size, (100, 100)) - im = Image.new("L", (128, 256)) - im.thumbnail((100, 100)) - self.assertEqual(im.size, (50, 100)) +def test_aspect(): + im = Image.new("L", (128, 128)) + im.thumbnail((100, 100)) + assert im.size == (100, 100) - im = Image.new("L", (128, 256)) - im.thumbnail((50, 100)) - self.assertEqual(im.size, (50, 100)) + im = Image.new("L", (128, 256)) + im.thumbnail((100, 100)) + assert im.size == (50, 100) - im = Image.new("L", (256, 128)) - im.thumbnail((100, 100)) - self.assertEqual(im.size, (100, 50)) + im = Image.new("L", (128, 256)) + im.thumbnail((50, 100)) + assert im.size == (50, 100) - im = Image.new("L", (256, 128)) - im.thumbnail((100, 50)) - self.assertEqual(im.size, (100, 50)) + im = Image.new("L", (256, 128)) + im.thumbnail((100, 100)) + assert im.size == (100, 50) - im = Image.new("L", (128, 128)) - im.thumbnail((100, 100)) - self.assertEqual(im.size, (100, 100)) + im = Image.new("L", (256, 128)) + im.thumbnail((100, 50)) + assert im.size == (100, 50) - im = Image.new("L", (256, 162)) # ratio is 1.5802469136 - im.thumbnail((33, 33)) - self.assertEqual(im.size, (33, 21)) # ratio is 1.5714285714 + im = Image.new("L", (128, 128)) + im.thumbnail((100, 100)) + assert im.size == (100, 100) - im = Image.new("L", (162, 256)) # ratio is 0.6328125 - im.thumbnail((33, 33)) - self.assertEqual(im.size, (21, 33)) # ratio is 0.6363636364 + im = Image.new("L", (256, 162)) # ratio is 1.5802469136 + im.thumbnail((33, 33)) + assert im.size == (33, 21) # ratio is 1.5714285714 - def test_float(self): - im = Image.new("L", (128, 128)) - im.thumbnail((99.9, 99.9)) - self.assertEqual(im.size, (100, 100)) + im = Image.new("L", (162, 256)) # ratio is 0.6328125 + im.thumbnail((33, 33)) + assert im.size == (21, 33) # ratio is 0.6363636364 - def test_no_resize(self): - # Check that draft() can resize the image to the destination size - with Image.open("Tests/images/hopper.jpg") as im: - im.draft(None, (64, 64)) - self.assertEqual(im.size, (64, 64)) - # Test thumbnail(), where only draft() is necessary to resize the image - with Image.open("Tests/images/hopper.jpg") as im: - im.thumbnail((64, 64)) - self.assertEqual(im.size, (64, 64)) +def test_float(): + im = Image.new("L", (128, 128)) + im.thumbnail((99.9, 99.9)) + assert im.size == (100, 100) - def test_DCT_scaling_edges(self): - # Make an image with red borders and size (N * 8) + 1 to cross DCT grid - im = Image.new("RGB", (257, 257), "red") - im.paste(Image.new("RGB", (235, 235)), (11, 11)) - thumb = fromstring(tostring(im, "JPEG", quality=99, subsampling=0)) - # small reducing_gap to amplify the effect - thumb.thumbnail((32, 32), Image.BICUBIC, reducing_gap=1.0) +def test_no_resize(): + # Check that draft() can resize the image to the destination size + with Image.open("Tests/images/hopper.jpg") as im: + im.draft(None, (64, 64)) + assert im.size == (64, 64) - ref = im.resize((32, 32), Image.BICUBIC) - # This is still JPEG, some error is present. Without the fix it is 11.5 - assert_image_similar(thumb, ref, 1.5) + # Test thumbnail(), where only draft() is necessary to resize the image + with Image.open("Tests/images/hopper.jpg") as im: + im.thumbnail((64, 64)) + assert im.size == (64, 64) - def test_reducing_gap_values(self): - im = hopper() - im.thumbnail((18, 18), Image.BICUBIC) - ref = hopper() - ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=2.0) - # reducing_gap=2.0 should be the default +def test_DCT_scaling_edges(): + # Make an image with red borders and size (N * 8) + 1 to cross DCT grid + im = Image.new("RGB", (257, 257), "red") + im.paste(Image.new("RGB", (235, 235)), (11, 11)) + + thumb = fromstring(tostring(im, "JPEG", quality=99, subsampling=0)) + # small reducing_gap to amplify the effect + thumb.thumbnail((32, 32), Image.BICUBIC, reducing_gap=1.0) + + ref = im.resize((32, 32), Image.BICUBIC) + # This is still JPEG, some error is present. Without the fix it is 11.5 + assert_image_similar(thumb, ref, 1.5) + + +def test_reducing_gap_values(): + im = hopper() + im.thumbnail((18, 18), Image.BICUBIC) + + ref = hopper() + ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=2.0) + # reducing_gap=2.0 should be the default + assert_image_equal(ref, im) + + ref = hopper() + ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=None) + with pytest.raises(AssertionError): assert_image_equal(ref, im) - ref = hopper() - ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=None) - with self.assertRaises(AssertionError): + assert_image_similar(ref, im, 3.5) + + +def test_reducing_gap_for_DCT_scaling(): + with Image.open("Tests/images/hopper.jpg") as ref: + # thumbnail should call draft with reducing_gap scale + ref.draft(None, (18 * 3, 18 * 3)) + ref = ref.resize((18, 18), Image.BICUBIC) + + with Image.open("Tests/images/hopper.jpg") as im: + im.thumbnail((18, 18), Image.BICUBIC, reducing_gap=3.0) + assert_image_equal(ref, im) - - assert_image_similar(ref, im, 3.5) - - def test_reducing_gap_for_DCT_scaling(self): - with Image.open("Tests/images/hopper.jpg") as ref: - # thumbnail should call draft with reducing_gap scale - ref.draft(None, (18 * 3, 18 * 3)) - ref = ref.resize((18, 18), Image.BICUBIC) - - with Image.open("Tests/images/hopper.jpg") as im: - im.thumbnail((18, 18), Image.BICUBIC, reducing_gap=3.0) - - assert_image_equal(ref, im) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py index ab5b666cb..178cfcef3 100644 --- a/Tests/test_image_tobitmap.py +++ b/Tests/test_image_tobitmap.py @@ -1,14 +1,16 @@ -from .helper import PillowTestCase, assert_image_equal, fromstring, hopper +import pytest + +from .helper import assert_image_equal, fromstring, hopper -class TestImageToBitmap(PillowTestCase): - def test_sanity(self): +def test_sanity(): - self.assertRaises(ValueError, lambda: hopper().tobitmap()) + with pytest.raises(ValueError): + hopper().tobitmap() - im1 = hopper().convert("1") + im1 = hopper().convert("1") - bitmap = im1.tobitmap() + bitmap = im1.tobitmap() - self.assertIsInstance(bitmap, bytes) - assert_image_equal(im1, fromstring(bitmap)) + assert isinstance(bitmap, bytes) + assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 940a48a53..a004434da 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -9,151 +9,154 @@ from PIL.Image import ( ) from . import helper -from .helper import PillowTestCase, assert_image_equal +from .helper import assert_image_equal + +HOPPER = { + mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() + for mode in ["L", "RGB", "I;16", "I;16L", "I;16B"] +} -class TestImageTranspose(PillowTestCase): +def test_flip_left_right(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(FLIP_LEFT_RIGHT) + assert out.mode == mode + assert out.size == im.size - hopper = { - mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() - for mode in ["L", "RGB", "I;16", "I;16L", "I;16B"] - } + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, y - 2)) - def test_flip_left_right(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(FLIP_LEFT_RIGHT) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) + for mode in HOPPER: + transpose(mode) - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((x - 2, 1))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((x - 2, y - 2))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, y - 2))) - for mode in self.hopper: - transpose(mode) +def test_flip_top_bottom(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(FLIP_TOP_BOTTOM) + assert out.mode == mode + assert out.size == im.size - def test_flip_top_bottom(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(FLIP_TOP_BOTTOM) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((x - 2, 1)) - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y - 2))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((x - 2, y - 2))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((x - 2, 1))) + for mode in HOPPER: + transpose(mode) - for mode in self.hopper: - transpose(mode) - def test_rotate_90(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(ROTATE_90) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size[::-1]) +def test_rotate_90(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(ROTATE_90) + assert out.mode == mode + assert out.size == im.size[::-1] - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, x - 2))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((y - 2, x - 2))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((y - 2, 1))) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, 1)) - for mode in self.hopper: - transpose(mode) + for mode in HOPPER: + transpose(mode) - def test_rotate_180(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(ROTATE_180) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((x - 2, y - 2))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, y - 2))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((x - 2, 1))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, 1))) +def test_rotate_180(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(ROTATE_180) + assert out.mode == mode + assert out.size == im.size - for mode in self.hopper: - transpose(mode) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) - def test_rotate_270(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(ROTATE_270) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size[::-1]) + for mode in HOPPER: + transpose(mode) - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((y - 2, 1))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((y - 2, x - 2))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, x - 2))) - for mode in self.hopper: - transpose(mode) +def test_rotate_270(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(ROTATE_270) + assert out.mode == mode + assert out.size == im.size[::-1] - def test_transpose(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(TRANSPOSE) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size[::-1]) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, x - 2)) - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, x - 2))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((y - 2, 1))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((y - 2, x - 2))) + for mode in HOPPER: + transpose(mode) - for mode in self.hopper: - transpose(mode) - def test_tranverse(self): - def transpose(mode): - im = self.hopper[mode] - out = im.transpose(TRANSVERSE) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size[::-1]) +def test_transpose(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(TRANSPOSE) + assert out.mode == mode + assert out.size == im.size[::-1] - x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((y - 2, x - 2))) - self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((y - 2, 1))) - self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, x - 2))) - self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, 1))) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, x - 2)) - for mode in self.hopper: - transpose(mode) + for mode in HOPPER: + transpose(mode) - def test_roundtrip(self): - for mode in self.hopper: - im = self.hopper[mode] - def transpose(first, second): - return im.transpose(first).transpose(second) +def test_tranverse(): + def transpose(mode): + im = HOPPER[mode] + out = im.transpose(TRANSVERSE) + assert out.mode == mode + assert out.size == im.size[::-1] - assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) - assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) - assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) - assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) - assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM) - ) - assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT) - ) - assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT) - ) - assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM) - ) - assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE) - ) + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) + + for mode in HOPPER: + transpose(mode) + + +def test_roundtrip(): + for mode in HOPPER: + im = HOPPER[mode] + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) + assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM) + ) + assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT) + ) + assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT) + ) + assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM) + ) + assert_image_equal(im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 6eb61ec59..8dec6a1d5 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -1,6 +1,6 @@ from PIL import Image, ImageChops -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper BLACK = (0, 0, 0) BROWN = (127, 64, 0) @@ -13,352 +13,373 @@ WHITE = (255, 255, 255) GREY = 128 -class TestImageChops(PillowTestCase): - def test_sanity(self): +def test_sanity(): + im = hopper("L") - im = hopper("L") + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) - ImageChops.constant(im, 128) - ImageChops.duplicate(im) - ImageChops.invert(im) - ImageChops.lighter(im, im) - ImageChops.darker(im, im) - ImageChops.difference(im, im) - ImageChops.multiply(im, im) - ImageChops.screen(im, im) + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) - ImageChops.add(im, im) - ImageChops.add(im, im, 2.0) - ImageChops.add(im, im, 2.0, 128) - ImageChops.subtract(im, im) - ImageChops.subtract(im, im, 2.0) - ImageChops.subtract(im, im, 2.0, 128) + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) - ImageChops.add_modulo(im, im) - ImageChops.subtract_modulo(im, im) + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) - ImageChops.blend(im, im, 0.5) - ImageChops.composite(im, im, im) + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) - ImageChops.offset(im, 10) - ImageChops.offset(im, 10, 20) - def test_add(self): - # Arrange - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: +def test_add(): + # Arrange + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add(im1, im2) + # Act + new = ImageChops.add(im1, im2) - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), ORANGE) + # Assert + assert new.getbbox() == (25, 25, 76, 76) + assert new.getpixel((50, 50)) == ORANGE - def test_add_scale_offset(self): - # Arrange - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add(im1, im2, scale=2.5, offset=100) +def test_add_scale_offset(): + # Arrange + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Assert - self.assertEqual(new.getbbox(), (0, 0, 100, 100)) - self.assertEqual(new.getpixel((50, 50)), (202, 151, 100)) + # Act + new = ImageChops.add(im1, im2, scale=2.5, offset=100) - def test_add_clip(self): - # Arrange - im = hopper() + # Assert + assert new.getbbox() == (0, 0, 100, 100) + assert new.getpixel((50, 50)) == (202, 151, 100) - # Act - new = ImageChops.add(im, im) - # Assert - self.assertEqual(new.getpixel((50, 50)), (255, 255, 254)) +def test_add_clip(): + # Arrange + im = hopper() - def test_add_modulo(self): - # Arrange - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: + # Act + new = ImageChops.add(im, im) - # Act - new = ImageChops.add_modulo(im1, im2) + # Assert + assert new.getpixel((50, 50)) == (255, 255, 254) - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), ORANGE) - def test_add_modulo_no_clip(self): - # Arrange - im = hopper() +def test_add_modulo(): + # Arrange + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.add_modulo(im, im) + # Act + new = ImageChops.add_modulo(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (224, 76, 254)) + # Assert + assert new.getbbox() == (25, 25, 76, 76) + assert new.getpixel((50, 50)) == ORANGE - def test_blend(self): - # Arrange - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - # Act - new = ImageChops.blend(im1, im2, 0.5) +def test_add_modulo_no_clip(): + # Arrange + im = hopper() - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), BROWN) + # Act + new = ImageChops.add_modulo(im, im) - def test_constant(self): - # Arrange - im = Image.new("RGB", (20, 10)) + # Assert + assert new.getpixel((50, 50)) == (224, 76, 254) - # Act - new = ImageChops.constant(im, GREY) - # Assert - self.assertEqual(new.size, im.size) - self.assertEqual(new.getpixel((0, 0)), GREY) - self.assertEqual(new.getpixel((19, 9)), GREY) +def test_blend(): + # Arrange + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - def test_darker_image(self): - # Arrange - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: + # Act + new = ImageChops.blend(im1, im2, 0.5) - # Act - new = ImageChops.darker(im1, im2) + # Assert + assert new.getbbox() == (25, 25, 76, 76) + assert new.getpixel((50, 50)) == BROWN - # Assert - assert_image_equal(new, im2) - def test_darker_pixel(self): - # Arrange - im1 = hopper() - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: +def test_constant(): + # Arrange + im = Image.new("RGB", (20, 10)) + + # Act + new = ImageChops.constant(im, GREY) + + # Assert + assert new.size == im.size + assert new.getpixel((0, 0)) == GREY + assert new.getpixel((19, 9)) == GREY + + +def test_darker_image(): + # Arrange + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: # Act new = ImageChops.darker(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (240, 166, 0)) + # Assert + assert_image_equal(new, im2) - def test_difference(self): - # Arrange - with Image.open("Tests/images/imagedraw_arc_end_le_start.png") as im1: - with Image.open("Tests/images/imagedraw_arc_no_loops.png") as im2: - # Act - new = ImageChops.difference(im1, im2) +def test_darker_pixel(): + # Arrange + im1 = hopper() + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + # Act + new = ImageChops.darker(im1, im2) - def test_difference_pixel(self): - # Arrange - im1 = hopper() - with Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") as im2: + # Assert + assert new.getpixel((50, 50)) == (240, 166, 0) + + +def test_difference(): + # Arrange + with Image.open("Tests/images/imagedraw_arc_end_le_start.png") as im1: + with Image.open("Tests/images/imagedraw_arc_no_loops.png") as im2: # Act new = ImageChops.difference(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (240, 166, 128)) + # Assert + assert new.getbbox() == (25, 25, 76, 76) - def test_duplicate(self): - # Arrange - im = hopper() + +def test_difference_pixel(): + # Arrange + im1 = hopper() + with Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") as im2: # Act - new = ImageChops.duplicate(im) + new = ImageChops.difference(im1, im2) - # Assert - assert_image_equal(new, im) + # Assert + assert new.getpixel((50, 50)) == (240, 166, 128) - def test_invert(self): - # Arrange - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: - # Act - new = ImageChops.invert(im) +def test_duplicate(): + # Arrange + im = hopper() - # Assert - self.assertEqual(new.getbbox(), (0, 0, 100, 100)) - self.assertEqual(new.getpixel((0, 0)), WHITE) - self.assertEqual(new.getpixel((50, 50)), CYAN) + # Act + new = ImageChops.duplicate(im) - def test_lighter_image(self): - # Arrange - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: + # Assert + assert_image_equal(new, im) - # Act - new = ImageChops.lighter(im1, im2) - # Assert - assert_image_equal(new, im1) +def test_invert(): + # Arrange + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: - def test_lighter_pixel(self): - # Arrange - im1 = hopper() - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: + # Act + new = ImageChops.invert(im) + + # Assert + assert new.getbbox() == (0, 0, 100, 100) + assert new.getpixel((0, 0)) == WHITE + assert new.getpixel((50, 50)) == CYAN + + +def test_lighter_image(): + # Arrange + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: # Act new = ImageChops.lighter(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (255, 255, 127)) + # Assert + assert_image_equal(new, im1) - def test_multiply_black(self): - """If you multiply an image with a solid black image, - the result is black.""" - # Arrange - im1 = hopper() - black = Image.new("RGB", im1.size, "black") + +def test_lighter_pixel(): + # Arrange + im1 = hopper() + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: # Act - new = ImageChops.multiply(im1, black) + new = ImageChops.lighter(im1, im2) - # Assert - assert_image_equal(new, black) + # Assert + assert new.getpixel((50, 50)) == (255, 255, 127) - def test_multiply_green(self): - # Arrange - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: - green = Image.new("RGB", im.size, "green") - # Act - new = ImageChops.multiply(im, green) +def test_multiply_black(): + """If you multiply an image with a solid black image, + the result is black.""" + # Arrange + im1 = hopper() + black = Image.new("RGB", im1.size, "black") - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) - self.assertEqual(new.getpixel((25, 25)), DARK_GREEN) - self.assertEqual(new.getpixel((50, 50)), BLACK) + # Act + new = ImageChops.multiply(im1, black) - def test_multiply_white(self): - """If you multiply with a solid white image, - the image is unaffected.""" - # Arrange - im1 = hopper() - white = Image.new("RGB", im1.size, "white") + # Assert + assert_image_equal(new, black) + + +def test_multiply_green(): + # Arrange + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: + green = Image.new("RGB", im.size, "green") # Act - new = ImageChops.multiply(im1, white) + new = ImageChops.multiply(im, green) - # Assert - assert_image_equal(new, im1) + # Assert + assert new.getbbox() == (25, 25, 76, 76) + assert new.getpixel((25, 25)) == DARK_GREEN + assert new.getpixel((50, 50)) == BLACK - def test_offset(self): - # Arrange - xoffset = 45 - yoffset = 20 - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im: + +def test_multiply_white(): + """If you multiply with a solid white image, the image is unaffected.""" + # Arrange + im1 = hopper() + white = Image.new("RGB", im1.size, "white") + + # Act + new = ImageChops.multiply(im1, white) + + # Assert + assert_image_equal(new, im1) + + +def test_offset(): + # Arrange + xoffset = 45 + yoffset = 20 + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im: + + # Act + new = ImageChops.offset(im, xoffset, yoffset) + + # Assert + assert new.getbbox() == (0, 45, 100, 96) + assert new.getpixel((50, 50)) == BLACK + assert new.getpixel((50 + xoffset, 50 + yoffset)) == DARK_GREEN + + # Test no yoffset + assert ImageChops.offset(im, xoffset) == ImageChops.offset(im, xoffset, xoffset) + + +def test_screen(): + # Arrange + with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: # Act - new = ImageChops.offset(im, xoffset, yoffset) + new = ImageChops.screen(im1, im2) - # Assert - self.assertEqual(new.getbbox(), (0, 45, 100, 96)) - self.assertEqual(new.getpixel((50, 50)), BLACK) - self.assertEqual(new.getpixel((50 + xoffset, 50 + yoffset)), DARK_GREEN) + # Assert + assert new.getbbox() == (25, 25, 76, 76) + assert new.getpixel((50, 50)) == ORANGE - # Test no yoffset - self.assertEqual( - ImageChops.offset(im, xoffset), ImageChops.offset(im, xoffset, xoffset) - ) - def test_screen(self): - # Arrange - with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: - - # Act - new = ImageChops.screen(im1, im2) - - # Assert - self.assertEqual(new.getbbox(), (25, 25, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), ORANGE) - - def test_subtract(self): - # Arrange - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - - # Act - new = ImageChops.subtract(im1, im2) - - # Assert - self.assertEqual(new.getbbox(), (25, 50, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), GREEN) - self.assertEqual(new.getpixel((50, 51)), BLACK) - - def test_subtract_scale_offset(self): - # Arrange - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - - # Act - new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) - - # Assert - self.assertEqual(new.getbbox(), (0, 0, 100, 100)) - self.assertEqual(new.getpixel((50, 50)), (100, 202, 100)) - - def test_subtract_clip(self): - # Arrange - im1 = hopper() - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: +def test_subtract(): + # Arrange + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: # Act new = ImageChops.subtract(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (0, 0, 127)) + # Assert + assert new.getbbox() == (25, 50, 76, 76) + assert new.getpixel((50, 50)) == GREEN + assert new.getpixel((50, 51)) == BLACK - def test_subtract_modulo(self): - # Arrange - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: - with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Act - new = ImageChops.subtract_modulo(im1, im2) +def test_subtract_scale_offset(): + # Arrange + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: - # Assert - self.assertEqual(new.getbbox(), (25, 50, 76, 76)) - self.assertEqual(new.getpixel((50, 50)), GREEN) - self.assertEqual(new.getpixel((50, 51)), BLACK) + # Act + new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) - def test_subtract_modulo_no_clip(self): - # Arrange - im1 = hopper() - with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: + # Assert + assert new.getbbox() == (0, 0, 100, 100) + assert new.getpixel((50, 50)) == (100, 202, 100) + + +def test_subtract_clip(): + # Arrange + im1 = hopper() + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: + + # Act + new = ImageChops.subtract(im1, im2) + + # Assert + assert new.getpixel((50, 50)) == (0, 0, 127) + + +def test_subtract_modulo(): + # Arrange + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: + with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: # Act new = ImageChops.subtract_modulo(im1, im2) - # Assert - self.assertEqual(new.getpixel((50, 50)), (241, 167, 127)) + # Assert + assert new.getbbox() == (25, 50, 76, 76) + assert new.getpixel((50, 50)) == GREEN + assert new.getpixel((50, 51)) == BLACK - def test_logical(self): - def table(op, a, b): - out = [] - for x in (a, b): - imx = Image.new("1", (1, 1), x) - for y in (a, b): - imy = Image.new("1", (1, 1), y) - out.append(op(imx, imy).getpixel((0, 0))) - return tuple(out) - self.assertEqual(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) - self.assertEqual(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) - self.assertEqual(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) +def test_subtract_modulo_no_clip(): + # Arrange + im1 = hopper() + with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: - self.assertEqual(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) - self.assertEqual(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) - self.assertEqual(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + # Act + new = ImageChops.subtract_modulo(im1, im2) - self.assertEqual(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) - self.assertEqual(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) - self.assertEqual(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) + # Assert + assert new.getpixel((50, 50)) == (241, 167, 127) + + +def test_logical(): + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + assert table(ImageChops.logical_and, 0, 1) == (0, 0, 0, 255) + assert table(ImageChops.logical_or, 0, 1) == (0, 255, 255, 255) + assert table(ImageChops.logical_xor, 0, 1) == (0, 255, 255, 0) + + assert table(ImageChops.logical_and, 0, 128) == (0, 0, 0, 255) + assert table(ImageChops.logical_or, 0, 128) == (0, 255, 255, 255) + assert table(ImageChops.logical_xor, 0, 128) == (0, 255, 255, 0) + + assert table(ImageChops.logical_and, 0, 255) == (0, 0, 0, 255) + assert table(ImageChops.logical_or, 0, 255) == (0, 255, 255, 255) + assert table(ImageChops.logical_xor, 0, 255) == (0, 255, 255, 0) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index ba704f4c8..ac34a8106 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -1,17 +1,12 @@ import datetime import os +import re from io import BytesIO import pytest from PIL import Image, ImageMode -from .helper import ( - PillowTestCase, - assert_image, - assert_image_equal, - assert_image_similar, - hopper, -) +from .helper import assert_image, assert_image_equal, assert_image_similar, hopper try: from PIL import ImageCms @@ -19,7 +14,7 @@ try: ImageCms.core.profile_open except ImportError: - # Skipped via setUp() + # Skipped via setup_module() pass @@ -27,588 +22,587 @@ SRGB = "Tests/icc/sRGB_IEC61966-2-1_black_scaled.icc" HAVE_PROFILE = os.path.exists(SRGB) -class TestImageCms(PillowTestCase): - def setUp(self): - try: - from PIL import ImageCms +def setup_module(): + try: + from PIL import ImageCms - # need to hit getattr to trigger the delayed import error - ImageCms.core.profile_open - except ImportError as v: - self.skipTest(v) + # need to hit getattr to trigger the delayed import error + ImageCms.core.profile_open + except ImportError as v: + pytest.skip(str(v)) - def skip_missing(self): - if not HAVE_PROFILE: - self.skipTest("SRGB profile not available") - def test_sanity(self): +def skip_missing(): + if not HAVE_PROFILE: + pytest.skip("SRGB profile not available") - # basic smoke test. - # this mostly follows the cms_test outline. - v = ImageCms.versions() # should return four strings - self.assertEqual(v[0], "1.0.0 pil") - self.assertEqual(list(map(type, v)), [str, str, str, str]) +def test_sanity(): + # basic smoke test. + # this mostly follows the cms_test outline. - # internal version number - self.assertRegex(ImageCms.core.littlecms_version, r"\d+\.\d+$") + v = ImageCms.versions() # should return four strings + assert v[0] == "1.0.0 pil" + assert list(map(type, v)) == [str, str, str, str] - self.skip_missing() - i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) - assert_image(i, "RGB", (128, 128)) + # internal version number + assert re.search(r"\d+\.\d+$", ImageCms.core.littlecms_version) - i = hopper() - ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) - assert_image(i, "RGB", (128, 128)) + skip_missing() + i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) + assert_image(i, "RGB", (128, 128)) + i = hopper() + ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(hopper(), t) + assert_image(i, "RGB", (128, 128)) + + with hopper() as i: t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - i = ImageCms.applyTransform(hopper(), t) + ImageCms.applyTransform(hopper(), t, inPlace=True) assert_image(i, "RGB", (128, 128)) - with hopper() as i: - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - ImageCms.applyTransform(hopper(), t, inPlace=True) - assert_image(i, "RGB", (128, 128)) + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(hopper(), t) + assert_image(i, "RGB", (128, 128)) - p = ImageCms.createProfile("sRGB") - o = ImageCms.getOpenProfile(SRGB) - t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") - i = ImageCms.applyTransform(hopper(), t) - assert_image(i, "RGB", (128, 128)) + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + assert t.inputMode == "RGB" + assert t.outputMode == "RGB" + i = ImageCms.applyTransform(hopper(), t) + assert_image(i, "RGB", (128, 128)) - t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") - self.assertEqual(t.inputMode, "RGB") - self.assertEqual(t.outputMode, "RGB") - i = ImageCms.applyTransform(hopper(), t) - assert_image(i, "RGB", (128, 128)) + # test PointTransform convenience API + hopper().point(t) - # test PointTransform convenience API - hopper().point(t) - def test_name(self): - self.skip_missing() - # get profile information for file - self.assertEqual( - ImageCms.getProfileName(SRGB).strip(), - "IEC 61966-2-1 Default RGB Colour Space - sRGB", - ) +def test_name(): + skip_missing() + # get profile information for file + assert ( + ImageCms.getProfileName(SRGB).strip() + == "IEC 61966-2-1 Default RGB Colour Space - sRGB" + ) - def test_info(self): - self.skip_missing() - self.assertEqual( - ImageCms.getProfileInfo(SRGB).splitlines(), - [ - "sRGB IEC61966-2-1 black scaled", - "", - "Copyright International Color Consortium, 2009", - "", - ], - ) - def test_copyright(self): - self.skip_missing() - self.assertEqual( - ImageCms.getProfileCopyright(SRGB).strip(), - "Copyright International Color Consortium, 2009", - ) +def test_info(): + skip_missing() + assert ImageCms.getProfileInfo(SRGB).splitlines() == [ + "sRGB IEC61966-2-1 black scaled", + "", + "Copyright International Color Consortium, 2009", + "", + ] - def test_manufacturer(self): - self.skip_missing() - self.assertEqual(ImageCms.getProfileManufacturer(SRGB).strip(), "") - def test_model(self): - self.skip_missing() - self.assertEqual( - ImageCms.getProfileModel(SRGB).strip(), - "IEC 61966-2-1 Default RGB Colour Space - sRGB", - ) +def test_copyright(): + skip_missing() + assert ( + ImageCms.getProfileCopyright(SRGB).strip() + == "Copyright International Color Consortium, 2009" + ) - def test_description(self): - self.skip_missing() - self.assertEqual( - ImageCms.getProfileDescription(SRGB).strip(), - "sRGB IEC61966-2-1 black scaled", - ) - def test_intent(self): - self.skip_missing() - self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0) - self.assertEqual( - ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT - ), - 1, - ) +def test_manufacturer(): + skip_missing() + assert ImageCms.getProfileManufacturer(SRGB).strip() == "" - def test_profile_object(self): - # same, using profile object - p = ImageCms.createProfile("sRGB") - # self.assertEqual(ImageCms.getProfileName(p).strip(), - # 'sRGB built-in - (lcms internal)') - # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), - # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) - self.assertEqual(ImageCms.getDefaultIntent(p), 0) - self.assertEqual( - ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT - ), - 1, - ) - def test_extensions(self): - # extensions +def test_model(): + skip_missing() + assert ( + ImageCms.getProfileModel(SRGB).strip() + == "IEC 61966-2-1 Default RGB Colour Space - sRGB" + ) - with Image.open("Tests/images/rgb.jpg") as i: - p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) - self.assertEqual( - ImageCms.getProfileName(p).strip(), - "IEC 61966-2.1 Default RGB colour space - sRGB", - ) - def test_exceptions(self): - # Test mode mismatch - psRGB = ImageCms.createProfile("sRGB") - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA")) +def test_description(): + skip_missing() + assert ( + ImageCms.getProfileDescription(SRGB).strip() == "sRGB IEC61966-2-1 black scaled" + ) - # the procedural pyCMS API uses PyCMSError for all sorts of errors - with hopper() as im: - self.assertRaises( - ImageCms.PyCMSError, ImageCms.profileToProfile, im, "foo", "bar" + +def test_intent(): + skip_missing() + assert ImageCms.getDefaultIntent(SRGB) == 0 + support = ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + ) + assert support == 1 + + +def test_profile_object(): + # same, using profile object + p = ImageCms.createProfile("sRGB") + # assert ImageCms.getProfileName(p).strip() == "sRGB built-in - (lcms internal)" + # assert ImageCms.getProfileInfo(p).splitlines() == + # ["sRGB built-in", "", "WhitePoint : D65 (daylight)", "", ""] + assert ImageCms.getDefaultIntent(p) == 0 + support = ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + ) + assert support == 1 + + +def test_extensions(): + # extensions + + with Image.open("Tests/images/rgb.jpg") as i: + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + assert ( + ImageCms.getProfileName(p).strip() + == "IEC 61966-2.1 Default RGB colour space - sRGB" + ) + + +def test_exceptions(): + # Test mode mismatch + psRGB = ImageCms.createProfile("sRGB") + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") + with pytest.raises(ValueError): + t.apply_in_place(hopper("RGBA")) + + # the procedural pyCMS API uses PyCMSError for all sorts of errors + with hopper() as im: + with pytest.raises(ImageCms.PyCMSError): + ImageCms.profileToProfile(im, "foo", "bar") + with pytest.raises(ImageCms.PyCMSError): + ImageCms.buildTransform("foo", "bar", "RGB", "RGB") + with pytest.raises(ImageCms.PyCMSError): + ImageCms.getProfileName(None) + skip_missing() + with pytest.raises(ImageCms.PyCMSError): + ImageCms.isIntentSupported(SRGB, None, None) + + +def test_display_profile(): + # try fetching the profile for the current display device + ImageCms.get_display_profile() + + +def test_lab_color_profile(): + ImageCms.createProfile("LAB", 5000) + ImageCms.createProfile("LAB", 6500) + + +def test_unsupported_color_space(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.createProfile("unsupported") + + +def test_invalid_color_temperature(): + with pytest.raises(ImageCms.PyCMSError): + ImageCms.createProfile("LAB", "invalid") + + +def test_simple_lab(): + i = Image.new("RGB", (10, 10), (128, 128, 128)) + + psRGB = ImageCms.createProfile("sRGB") + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + assert i_lab.mode == "LAB" + + k = i_lab.getpixel((0, 0)) + # not a linear luminance map. so L != 128: + assert k == (137, 128, 128) + + l_data = i_lab.getdata(0) + a_data = i_lab.getdata(1) + b_data = i_lab.getdata(2) + + assert list(l_data) == [137] * 100 + assert list(a_data) == [128] * 100 + assert list(b_data) == [128] * 100 + + +def test_lab_color(): + psRGB = ImageCms.createProfile("sRGB") + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") + + # Need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, and + # have that mapping work back to a PIL mode (likely RGB). + i = ImageCms.applyTransform(hopper(), t) + assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + with Image.open("Tests/images/hopper.Lab.tif") as target: + assert_image_similar(i, target, 3.5) + + +def test_lab_srgb(): + psRGB = ImageCms.createProfile("sRGB") + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") + + with Image.open("Tests/images/hopper.Lab.tif") as img: + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + assert_image_similar(hopper(), img_srgb, 30) + assert img_srgb.info["icc_profile"] + + profile = ImageCmsProfile(BytesIO(img_srgb.info["icc_profile"])) + assert "sRGB" in ImageCms.getProfileDescription(profile) + + +def test_lab_roundtrip(): + # check to see if we're at least internally consistent. + psRGB = ImageCms.createProfile("sRGB") + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(hopper(), t) + + assert i.info["icc_profile"] == ImageCmsProfile(pLab).tobytes() + + out = ImageCms.applyTransform(i, t2) + + assert_image_similar(hopper(), out, 2) + + +def test_profile_tobytes(): + with Image.open("Tests/images/rgb.jpg") as i: + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + + p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes())) + + # not the same bytes as the original icc_profile, but it does roundtrip + assert p.tobytes() == p2.tobytes() + assert ImageCms.getProfileName(p) == ImageCms.getProfileName(p2) + assert ImageCms.getProfileDescription(p) == ImageCms.getProfileDescription(p2) + + +def test_extended_information(): + skip_missing() + o = ImageCms.getOpenProfile(SRGB) + p = o.profile + + def assert_truncated_tuple_equal(tup1, tup2, digits=10): + # Helper function to reduce precision of tuples of floats + # recursively and then check equality. + power = 10 ** digits + + def truncate_tuple(tuple_or_float): + return tuple( + truncate_tuple(val) + if isinstance(val, tuple) + else int(val * power) / power + for val in tuple_or_float ) - self.assertRaises( - ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB" - ) - self.assertRaises(ImageCms.PyCMSError, ImageCms.getProfileName, None) - self.skip_missing() - self.assertRaises( - ImageCms.PyCMSError, ImageCms.isIntentSupported, SRGB, None, None - ) - def test_display_profile(self): - # try fetching the profile for the current display device - ImageCms.get_display_profile() + assert truncate_tuple(tup1) == truncate_tuple(tup2) - def test_lab_color_profile(self): - ImageCms.createProfile("LAB", 5000) - ImageCms.createProfile("LAB", 6500) - - def test_unsupported_color_space(self): - self.assertRaises(ImageCms.PyCMSError, ImageCms.createProfile, "unsupported") - - def test_invalid_color_temperature(self): - self.assertRaises(ImageCms.PyCMSError, ImageCms.createProfile, "LAB", "invalid") - - def test_simple_lab(self): - i = Image.new("RGB", (10, 10), (128, 128, 128)) - - psRGB = ImageCms.createProfile("sRGB") - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") - - i_lab = ImageCms.applyTransform(i, t) - - self.assertEqual(i_lab.mode, "LAB") - - k = i_lab.getpixel((0, 0)) - # not a linear luminance map. so L != 128: - self.assertEqual(k, (137, 128, 128)) - - l_data = i_lab.getdata(0) - a_data = i_lab.getdata(1) - b_data = i_lab.getdata(2) - - self.assertEqual(list(l_data), [137] * 100) - self.assertEqual(list(a_data), [128] * 100) - self.assertEqual(list(b_data), [128] * 100) - - def test_lab_color(self): - psRGB = ImageCms.createProfile("sRGB") - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") - - # Need to add a type mapping for some PIL type to TYPE_Lab_8 in - # findLCMSType, and have that mapping work back to a PIL mode - # (likely RGB). - i = ImageCms.applyTransform(hopper(), t) - assert_image(i, "LAB", (128, 128)) - - # i.save('temp.lab.tif') # visually verified vs PS. - - with Image.open("Tests/images/hopper.Lab.tif") as target: - assert_image_similar(i, target, 3.5) - - def test_lab_srgb(self): - psRGB = ImageCms.createProfile("sRGB") - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - - with Image.open("Tests/images/hopper.Lab.tif") as img: - img_srgb = ImageCms.applyTransform(img, t) - - # img_srgb.save('temp.srgb.tif') # visually verified vs ps. - - assert_image_similar(hopper(), img_srgb, 30) - self.assertTrue(img_srgb.info["icc_profile"]) - - profile = ImageCmsProfile(BytesIO(img_srgb.info["icc_profile"])) - self.assertIn("sRGB", ImageCms.getProfileDescription(profile)) - - def test_lab_roundtrip(self): - # check to see if we're at least internally consistent. - psRGB = ImageCms.createProfile("sRGB") - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") - - t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - - i = ImageCms.applyTransform(hopper(), t) - - self.assertEqual(i.info["icc_profile"], ImageCmsProfile(pLab).tobytes()) - - out = ImageCms.applyTransform(i, t2) - - assert_image_similar(hopper(), out, 2) - - def test_profile_tobytes(self): - with Image.open("Tests/images/rgb.jpg") as i: - p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) - - p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes())) - - # not the same bytes as the original icc_profile, - # but it does roundtrip - self.assertEqual(p.tobytes(), p2.tobytes()) - self.assertEqual(ImageCms.getProfileName(p), ImageCms.getProfileName(p2)) - self.assertEqual( - ImageCms.getProfileDescription(p), ImageCms.getProfileDescription(p2) - ) - - def test_extended_information(self): - self.skip_missing() - o = ImageCms.getOpenProfile(SRGB) - p = o.profile - - def assert_truncated_tuple_equal(tup1, tup2, digits=10): - # Helper function to reduce precision of tuples of floats - # recursively and then check equality. - power = 10 ** digits - - def truncate_tuple(tuple_or_float): - return tuple( - truncate_tuple(val) - if isinstance(val, tuple) - else int(val * power) / power - for val in tuple_or_float - ) - - self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2)) - - self.assertEqual(p.attributes, 4294967296) - assert_truncated_tuple_equal( - p.blue_colorant, + assert p.attributes == 4294967296 + assert_truncated_tuple_equal( + p.blue_colorant, + ( + (0.14306640625, 0.06060791015625, 0.7140960693359375), + (0.1558847490315394, 0.06603820639433387, 0.06060791015625), + ), + ) + assert_truncated_tuple_equal( + p.blue_primary, + ( + (0.14306641366715667, 0.06060790921083026, 0.7140960805782015), + (0.15588475410450106, 0.06603820408959558, 0.06060790921083026), + ), + ) + assert_truncated_tuple_equal( + p.chromatic_adaptation, + ( ( - (0.14306640625, 0.06060791015625, 0.7140960693359375), - (0.1558847490315394, 0.06603820639433387, 0.06060791015625), + (1.04791259765625, 0.0229339599609375, -0.050201416015625), + (0.02960205078125, 0.9904632568359375, -0.0170745849609375), + (-0.009246826171875, 0.0150604248046875, 0.7517852783203125), ), - ) - assert_truncated_tuple_equal( - p.blue_primary, ( - (0.14306641366715667, 0.06060790921083026, 0.7140960805782015), - (0.15588475410450106, 0.06603820408959558, 0.06060790921083026), + (1.0267159024652783, 0.022470062342089134, 0.0229339599609375), + (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), + (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875), ), - ) - assert_truncated_tuple_equal( - p.chromatic_adaptation, - ( - ( - (1.04791259765625, 0.0229339599609375, -0.050201416015625), - (0.02960205078125, 0.9904632568359375, -0.0170745849609375), - (-0.009246826171875, 0.0150604248046875, 0.7517852783203125), - ), - ( - (1.0267159024652783, 0.022470062342089134, 0.0229339599609375), - (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), - (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875), - ), - ), - ) - self.assertIsNone(p.chromaticity) - self.assertEqual( - p.clut, - { - 0: (False, False, True), - 1: (False, False, True), - 2: (False, False, True), - 3: (False, False, True), - }, - ) + ), + ) + assert p.chromaticity is None + assert p.clut == { + 0: (False, False, True), + 1: (False, False, True), + 2: (False, False, True), + 3: (False, False, True), + } - self.assertIsNone(p.colorant_table) - self.assertIsNone(p.colorant_table_out) - self.assertIsNone(p.colorimetric_intent) - self.assertEqual(p.connection_space, "XYZ ") - self.assertEqual(p.copyright, "Copyright International Color Consortium, 2009") - self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31)) - self.assertEqual(p.device_class, "mntr") - assert_truncated_tuple_equal( - p.green_colorant, - ( - (0.3851470947265625, 0.7168731689453125, 0.097076416015625), - (0.32119769927720654, 0.5978443449048152, 0.7168731689453125), - ), - ) - assert_truncated_tuple_equal( - p.green_primary, - ( - (0.3851470888162112, 0.7168731974161346, 0.09707641738998518), - (0.32119768793686687, 0.5978443567149709, 0.7168731974161346), - ), - ) - self.assertEqual(p.header_flags, 0) - self.assertEqual(p.header_manufacturer, "\x00\x00\x00\x00") - self.assertEqual(p.header_model, "\x00\x00\x00\x00") - self.assertEqual( - p.icc_measurement_condition, - { - "backing": (0.0, 0.0, 0.0), - "flare": 0.0, - "geo": "unknown", - "observer": 1, - "illuminant_type": "D65", - }, - ) - self.assertEqual(p.icc_version, 33554432) - self.assertIsNone(p.icc_viewing_condition) - self.assertEqual( - p.intent_supported, - { - 0: (True, True, True), - 1: (True, True, True), - 2: (True, True, True), - 3: (True, True, True), - }, - ) - self.assertTrue(p.is_matrix_shaper) - self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0))) - self.assertIsNone(p.manufacturer) - assert_truncated_tuple_equal( - p.media_black_point, - ( - (0.012054443359375, 0.0124969482421875, 0.01031494140625), - (0.34573304157549234, 0.35842450765864337, 0.0124969482421875), - ), - ) - assert_truncated_tuple_equal( - p.media_white_point, - ( - (0.964202880859375, 1.0, 0.8249053955078125), - (0.3457029219802284, 0.3585375327567059, 1.0), - ), - ) - assert_truncated_tuple_equal( - (p.media_white_point_temperature,), (5000.722328847392,) - ) - self.assertEqual(p.model, "IEC 61966-2-1 Default RGB Colour Space - sRGB") + assert p.colorant_table is None + assert p.colorant_table_out is None + assert p.colorimetric_intent is None + assert p.connection_space == "XYZ " + assert p.copyright == "Copyright International Color Consortium, 2009" + assert p.creation_date == datetime.datetime(2009, 2, 27, 21, 36, 31) + assert p.device_class == "mntr" + assert_truncated_tuple_equal( + p.green_colorant, + ( + (0.3851470947265625, 0.7168731689453125, 0.097076416015625), + (0.32119769927720654, 0.5978443449048152, 0.7168731689453125), + ), + ) + assert_truncated_tuple_equal( + p.green_primary, + ( + (0.3851470888162112, 0.7168731974161346, 0.09707641738998518), + (0.32119768793686687, 0.5978443567149709, 0.7168731974161346), + ), + ) + assert p.header_flags == 0 + assert p.header_manufacturer == "\x00\x00\x00\x00" + assert p.header_model == "\x00\x00\x00\x00" + assert p.icc_measurement_condition == { + "backing": (0.0, 0.0, 0.0), + "flare": 0.0, + "geo": "unknown", + "observer": 1, + "illuminant_type": "D65", + } + assert p.icc_version == 33554432 + assert p.icc_viewing_condition is None + assert p.intent_supported == { + 0: (True, True, True), + 1: (True, True, True), + 2: (True, True, True), + 3: (True, True, True), + } + assert p.is_matrix_shaper + assert p.luminance == ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0)) + assert p.manufacturer is None + assert_truncated_tuple_equal( + p.media_black_point, + ( + (0.012054443359375, 0.0124969482421875, 0.01031494140625), + (0.34573304157549234, 0.35842450765864337, 0.0124969482421875), + ), + ) + assert_truncated_tuple_equal( + p.media_white_point, + ( + (0.964202880859375, 1.0, 0.8249053955078125), + (0.3457029219802284, 0.3585375327567059, 1.0), + ), + ) + assert_truncated_tuple_equal( + (p.media_white_point_temperature,), (5000.722328847392,) + ) + assert p.model == "IEC 61966-2-1 Default RGB Colour Space - sRGB" - self.assertIsNone(p.perceptual_rendering_intent_gamut) + assert p.perceptual_rendering_intent_gamut is None - self.assertEqual(p.profile_description, "sRGB IEC61966-2-1 black scaled") - self.assertEqual(p.profile_id, b")\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r") - assert_truncated_tuple_equal( - p.red_colorant, - ( - (0.436065673828125, 0.2224884033203125, 0.013916015625), - (0.6484536316398539, 0.3308524880306778, 0.2224884033203125), - ), - ) - assert_truncated_tuple_equal( - p.red_primary, - ( - (0.43606566581047446, 0.22248840582960838, 0.013916015621759925), - (0.6484536250319214, 0.3308524944738204, 0.22248840582960838), - ), - ) - self.assertEqual(p.rendering_intent, 0) - self.assertIsNone(p.saturation_rendering_intent_gamut) - self.assertIsNone(p.screening_description) - self.assertIsNone(p.target) - self.assertEqual(p.technology, "CRT ") - self.assertEqual(p.version, 2.0) - self.assertEqual( - p.viewing_condition, "Reference Viewing Condition in IEC 61966-2-1" - ) - self.assertEqual(p.xcolor_space, "RGB ") + assert p.profile_description == "sRGB IEC61966-2-1 black scaled" + assert p.profile_id == b")\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r" + assert_truncated_tuple_equal( + p.red_colorant, + ( + (0.436065673828125, 0.2224884033203125, 0.013916015625), + (0.6484536316398539, 0.3308524880306778, 0.2224884033203125), + ), + ) + assert_truncated_tuple_equal( + p.red_primary, + ( + (0.43606566581047446, 0.22248840582960838, 0.013916015621759925), + (0.6484536250319214, 0.3308524944738204, 0.22248840582960838), + ), + ) + assert p.rendering_intent == 0 + assert p.saturation_rendering_intent_gamut is None + assert p.screening_description is None + assert p.target is None + assert p.technology == "CRT " + assert p.version == 2.0 + assert p.viewing_condition == "Reference Viewing Condition in IEC 61966-2-1" + assert p.xcolor_space == "RGB " - def test_deprecations(self): - self.skip_missing() - o = ImageCms.getOpenProfile(SRGB) - p = o.profile - def helper_deprecated(attr, expected): - result = pytest.warns(DeprecationWarning, getattr, p, attr) - self.assertEqual(result, expected) +def test_deprecations(): + skip_missing() + o = ImageCms.getOpenProfile(SRGB) + p = o.profile - # p.color_space - helper_deprecated("color_space", "RGB") + def helper_deprecated(attr, expected): + result = pytest.warns(DeprecationWarning, getattr, p, attr) + assert result == expected - # p.pcs - helper_deprecated("pcs", "XYZ") + # p.color_space + helper_deprecated("color_space", "RGB") - # p.product_copyright - helper_deprecated( - "product_copyright", "Copyright International Color Consortium, 2009" - ) + # p.pcs + helper_deprecated("pcs", "XYZ") - # p.product_desc - helper_deprecated("product_desc", "sRGB IEC61966-2-1 black scaled") + # p.product_copyright + helper_deprecated( + "product_copyright", "Copyright International Color Consortium, 2009" + ) - # p.product_description - helper_deprecated("product_description", "sRGB IEC61966-2-1 black scaled") + # p.product_desc + helper_deprecated("product_desc", "sRGB IEC61966-2-1 black scaled") - # p.product_manufacturer - helper_deprecated("product_manufacturer", "") + # p.product_description + helper_deprecated("product_description", "sRGB IEC61966-2-1 black scaled") - # p.product_model - helper_deprecated( - "product_model", "IEC 61966-2-1 Default RGB Colour Space - sRGB" - ) + # p.product_manufacturer + helper_deprecated("product_manufacturer", "") - def test_profile_typesafety(self): - """ Profile init type safety + # p.product_model + helper_deprecated("product_model", "IEC 61966-2-1 Default RGB Colour Space - sRGB") - prepatch, these would segfault, postpatch they should emit a typeerror - """ - with self.assertRaises(TypeError): - ImageCms.ImageCmsProfile(0).tobytes() - with self.assertRaises(TypeError): - ImageCms.ImageCmsProfile(1).tobytes() +def test_profile_typesafety(): + """ Profile init type safety - def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): - def create_test_image(): - # set up test image with something interesting in the tested aux channel. - # fmt: off - nine_grid_deltas = [ # noqa: E131 - (-1, -1), (-1, 0), (-1, 1), - (0, -1), (0, 0), (0, 1), - (1, -1), (1, 0), (1, 1), - ] - # fmt: on - chans = [] - bands = ImageMode.getmode(mode).bands - for band_ndx in range(len(bands)): - channel_type = "L" # 8-bit unorm - channel_pattern = hopper(channel_type) + prepatch, these would segfault, postpatch they should emit a typeerror + """ - # paste pattern with varying offsets to avoid correlation - # potentially hiding some bugs (like channels getting mixed). - paste_offset = ( - int(band_ndx / len(bands) * channel_pattern.size[0]), - int(band_ndx / (len(bands) * 2) * channel_pattern.size[1]), - ) - channel_data = Image.new(channel_type, channel_pattern.size) - for delta in nine_grid_deltas: - channel_data.paste( - channel_pattern, - tuple( - paste_offset[c] + delta[c] * channel_pattern.size[c] - for c in range(2) - ), - ) - chans.append(channel_data) - return Image.merge(mode, chans) + with pytest.raises(TypeError): + ImageCms.ImageCmsProfile(0).tobytes() + with pytest.raises(TypeError): + ImageCms.ImageCmsProfile(1).tobytes() - source_image = create_test_image() - source_image_aux = source_image.getchannel(preserved_channel) - # create some transform, it doesn't matter which one - source_profile = ImageCms.createProfile("sRGB") - destination_profile = ImageCms.createProfile("sRGB") - t = ImageCms.buildTransform( - source_profile, destination_profile, inMode=mode, outMode=mode - ) - - # apply transform - if transform_in_place: - ImageCms.applyTransform(source_image, t, inPlace=True) - result_image = source_image - else: - result_image = ImageCms.applyTransform(source_image, t, inPlace=False) - result_image_aux = result_image.getchannel(preserved_channel) - - assert_image_equal(source_image_aux, result_image_aux) - - def test_preserve_auxiliary_channels_rgba(self): - self.assert_aux_channel_preserved( - mode="RGBA", transform_in_place=False, preserved_channel="A" - ) - - def test_preserve_auxiliary_channels_rgba_in_place(self): - self.assert_aux_channel_preserved( - mode="RGBA", transform_in_place=True, preserved_channel="A" - ) - - def test_preserve_auxiliary_channels_rgbx(self): - self.assert_aux_channel_preserved( - mode="RGBX", transform_in_place=False, preserved_channel="X" - ) - - def test_preserve_auxiliary_channels_rgbx_in_place(self): - self.assert_aux_channel_preserved( - mode="RGBX", transform_in_place=True, preserved_channel="X" - ) - - def test_auxiliary_channels_isolated(self): - # test data in aux channels does not affect non-aux channels - aux_channel_formats = [ - # format, profile, color-only format, source test image - ("RGBA", "sRGB", "RGB", hopper("RGBA")), - ("RGBX", "sRGB", "RGB", hopper("RGBX")), - ("LAB", "LAB", "LAB", Image.open("Tests/images/hopper.Lab.tif")), +def assert_aux_channel_preserved(mode, transform_in_place, preserved_channel): + def create_test_image(): + # set up test image with something interesting in the tested aux channel. + # fmt: off + nine_grid_deltas = [ # noqa: E131 + (-1, -1), (-1, 0), (-1, 1), + (0, -1), (0, 0), (0, 1), + (1, -1), (1, 0), (1, 1), ] - for src_format in aux_channel_formats: - for dst_format in aux_channel_formats: - for transform_in_place in [True, False]: - # inplace only if format doesn't change - if transform_in_place and src_format[0] != dst_format[0]: - continue + # fmt: on + chans = [] + bands = ImageMode.getmode(mode).bands + for band_ndx in range(len(bands)): + channel_type = "L" # 8-bit unorm + channel_pattern = hopper(channel_type) - # convert with and without AUX data, test colors are equal - source_profile = ImageCms.createProfile(src_format[1]) - destination_profile = ImageCms.createProfile(dst_format[1]) - source_image = src_format[3] - test_transform = ImageCms.buildTransform( - source_profile, - destination_profile, - inMode=src_format[0], - outMode=dst_format[0], + # paste pattern with varying offsets to avoid correlation + # potentially hiding some bugs (like channels getting mixed). + paste_offset = ( + int(band_ndx / len(bands) * channel_pattern.size[0]), + int(band_ndx / (len(bands) * 2) * channel_pattern.size[1]), + ) + channel_data = Image.new(channel_type, channel_pattern.size) + for delta in nine_grid_deltas: + channel_data.paste( + channel_pattern, + tuple( + paste_offset[c] + delta[c] * channel_pattern.size[c] + for c in range(2) + ), + ) + chans.append(channel_data) + return Image.merge(mode, chans) + + source_image = create_test_image() + source_image_aux = source_image.getchannel(preserved_channel) + + # create some transform, it doesn't matter which one + source_profile = ImageCms.createProfile("sRGB") + destination_profile = ImageCms.createProfile("sRGB") + t = ImageCms.buildTransform( + source_profile, destination_profile, inMode=mode, outMode=mode + ) + + # apply transform + if transform_in_place: + ImageCms.applyTransform(source_image, t, inPlace=True) + result_image = source_image + else: + result_image = ImageCms.applyTransform(source_image, t, inPlace=False) + result_image_aux = result_image.getchannel(preserved_channel) + + assert_image_equal(source_image_aux, result_image_aux) + + +def test_preserve_auxiliary_channels_rgba(): + assert_aux_channel_preserved( + mode="RGBA", transform_in_place=False, preserved_channel="A" + ) + + +def test_preserve_auxiliary_channels_rgba_in_place(): + assert_aux_channel_preserved( + mode="RGBA", transform_in_place=True, preserved_channel="A" + ) + + +def test_preserve_auxiliary_channels_rgbx(): + assert_aux_channel_preserved( + mode="RGBX", transform_in_place=False, preserved_channel="X" + ) + + +def test_preserve_auxiliary_channels_rgbx_in_place(): + assert_aux_channel_preserved( + mode="RGBX", transform_in_place=True, preserved_channel="X" + ) + + +def test_auxiliary_channels_isolated(): + # test data in aux channels does not affect non-aux channels + aux_channel_formats = [ + # format, profile, color-only format, source test image + ("RGBA", "sRGB", "RGB", hopper("RGBA")), + ("RGBX", "sRGB", "RGB", hopper("RGBX")), + ("LAB", "LAB", "LAB", Image.open("Tests/images/hopper.Lab.tif")), + ] + for src_format in aux_channel_formats: + for dst_format in aux_channel_formats: + for transform_in_place in [True, False]: + # inplace only if format doesn't change + if transform_in_place and src_format[0] != dst_format[0]: + continue + + # convert with and without AUX data, test colors are equal + source_profile = ImageCms.createProfile(src_format[1]) + destination_profile = ImageCms.createProfile(dst_format[1]) + source_image = src_format[3] + test_transform = ImageCms.buildTransform( + source_profile, + destination_profile, + inMode=src_format[0], + outMode=dst_format[0], + ) + + # test conversion from aux-ful source + if transform_in_place: + test_image = source_image.copy() + ImageCms.applyTransform(test_image, test_transform, inPlace=True) + else: + test_image = ImageCms.applyTransform( + source_image, test_transform, inPlace=False ) - # test conversion from aux-ful source - if transform_in_place: - test_image = source_image.copy() - ImageCms.applyTransform( - test_image, test_transform, inPlace=True - ) - else: - test_image = ImageCms.applyTransform( - source_image, test_transform, inPlace=False - ) + # reference conversion from aux-less source + reference_transform = ImageCms.buildTransform( + source_profile, + destination_profile, + inMode=src_format[2], + outMode=dst_format[2], + ) + reference_image = ImageCms.applyTransform( + source_image.convert(src_format[2]), reference_transform + ) - # reference conversion from aux-less source - reference_transform = ImageCms.buildTransform( - source_profile, - destination_profile, - inMode=src_format[2], - outMode=dst_format[2], - ) - reference_image = ImageCms.applyTransform( - source_image.convert(src_format[2]), reference_transform - ) - - assert_image_equal( - test_image.convert(dst_format[2]), reference_image - ) + assert_image_equal(test_image.convert(dst_format[2]), reference_image) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 7dc51562c..e5951fb02 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,9 +1,9 @@ import os.path -import unittest +import pytest from PIL import Image, ImageColor, ImageDraw, ImageFont, features -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper BLACK = (0, 0, 0) WHITE = (255, 255, 255) @@ -33,868 +33,901 @@ KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)] HAS_FREETYPE = features.check("freetype2") -class TestImageDraw(PillowTestCase): - def test_sanity(self): - im = hopper("RGB").copy() +def test_sanity(): + im = hopper("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + +def test_valueerror(): + with Image.open("Tests/images/chi.gif") as im: - draw = ImageDraw.ImageDraw(im) draw = ImageDraw.Draw(im) + draw.line((0, 0), fill=(0, 0, 0)) - draw.ellipse(list(range(4))) - draw.line(list(range(10))) - draw.polygon(list(range(100))) - draw.rectangle(list(range(4))) - def test_valueerror(self): - with Image.open("Tests/images/chi.gif") as im: +def test_mode_mismatch(): + im = hopper("RGB").copy() - draw = ImageDraw.Draw(im) - draw.line((0, 0), fill=(0, 0, 0)) + with pytest.raises(ValueError): + ImageDraw.ImageDraw(im, mode="L") - def test_mode_mismatch(self): - im = hopper("RGB").copy() - self.assertRaises(ValueError, ImageDraw.ImageDraw, im, mode="L") +def helper_arc(bbox, start, end): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) - def helper_arc(self, bbox, start, end): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) + # Act + draw.arc(bbox, start, end) + + # Assert + assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1) + + +def test_arc1(): + helper_arc(BBOX1, 0, 180) + helper_arc(BBOX1, 0.5, 180.4) + + +def test_arc2(): + helper_arc(BBOX2, 0, 180) + helper_arc(BBOX2, 0.5, 180.4) + + +def test_arc_end_le_start(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + start = 270.5 + end = 0 + + # Act + draw.arc(BBOX1, start=start, end=end) + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_arc_end_le_start.png")) + + +def test_arc_no_loops(): + # No need to go in loops + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + start = 5 + end = 370 + + # Act + draw.arc(BBOX1, start=start, end=end) + + # Assert + assert_image_similar(im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1) + + +def test_arc_width(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_arc_width.png" + + # Act + draw.arc(BBOX1, 10, 260, width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_arc_width_pieslice_large(): + # Tests an arc with a large enough width that it is a pieslice + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_arc_width_pieslice.png" + + # Act + draw.arc(BBOX1, 10, 260, fill="yellow", width=100) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_arc_width_fill(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_arc_width_fill.png" + + # Act + draw.arc(BBOX1, 10, 260, fill="yellow", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_arc_width_non_whole_angle(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_arc_width_non_whole_angle.png" + + # Act + draw.arc(BBOX1, 10, 259.5, width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_bitmap(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + with Image.open("Tests/images/pil123rgba.png") as small: + small = small.resize((50, 50), Image.NEAREST) # Act - draw.arc(bbox, start, end) + draw.bitmap((10, 10), small) - # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1) + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) - def test_arc1(self): - self.helper_arc(BBOX1, 0, 180) - self.helper_arc(BBOX1, 0.5, 180.4) - def test_arc2(self): - self.helper_arc(BBOX2, 0, 180) - self.helper_arc(BBOX2, 0.5, 180.4) +def helper_chord(mode, bbox, start, end): + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_chord_{}.png".format(mode) - def test_arc_end_le_start(self): - # Arrange - im = Image.new("RGB", (W, H)) + # Act + draw.chord(bbox, start, end, fill="red", outline="yellow") + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_chord1(): + for mode in ["RGB", "L"]: + helper_chord(mode, BBOX1, 0, 180) + helper_chord(mode, BBOX1, 0.5, 180.4) + + +def test_chord2(): + for mode in ["RGB", "L"]: + helper_chord(mode, BBOX2, 0, 180) + helper_chord(mode, BBOX2, 0.5, 180.4) + + +def test_chord_width(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_chord_width.png" + + # Act + draw.chord(BBOX1, 10, 260, outline="yellow", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_chord_width_fill(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_chord_width_fill.png" + + # Act + draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def helper_ellipse(mode, bbox): + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode) + + # Act + draw.ellipse(bbox, fill="green", outline="blue") + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_ellipse1(): + for mode in ["RGB", "L"]: + helper_ellipse(mode, BBOX1) + + +def test_ellipse2(): + for mode in ["RGB", "L"]: + helper_ellipse(mode, BBOX2) + + +def test_ellipse_edge(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.ellipse(((0, 0), (W - 1, H)), fill="white") + + # Assert + assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) + + +def test_ellipse_symmetric(): + for bbox in [(25, 25, 76, 76), (25, 25, 75, 75)]: + im = Image.new("RGB", (101, 101)) draw = ImageDraw.Draw(im) - start = 270.5 - end = 0 - - # Act - draw.arc(BBOX1, start=start, end=end) - - # Assert - assert_image_equal( - im, Image.open("Tests/images/imagedraw_arc_end_le_start.png") - ) - - def test_arc_no_loops(self): - # No need to go in loops - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - start = 5 - end = 370 - - # Act - draw.arc(BBOX1, start=start, end=end) - - # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1 - ) - - def test_arc_width(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width.png" - - # Act - draw.arc(BBOX1, 10, 260, width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_arc_width_pieslice_large(self): - # Tests an arc with a large enough width that it is a pieslice - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width_pieslice.png" - - # Act - draw.arc(BBOX1, 10, 260, fill="yellow", width=100) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_arc_width_fill(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width_fill.png" - - # Act - draw.arc(BBOX1, 10, 260, fill="yellow", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_arc_width_non_whole_angle(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width_non_whole_angle.png" - - # Act - draw.arc(BBOX1, 10, 259.5, width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_bitmap(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - with Image.open("Tests/images/pil123rgba.png") as small: - small = small.resize((50, 50), Image.NEAREST) - - # Act - draw.bitmap((10, 10), small) - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) - - def helper_chord(self, mode, bbox, start, end): - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_chord_{}.png".format(mode) - - # Act - draw.chord(bbox, start, end, fill="red", outline="yellow") - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_chord1(self): - for mode in ["RGB", "L"]: - self.helper_chord(mode, BBOX1, 0, 180) - self.helper_chord(mode, BBOX1, 0.5, 180.4) - - def test_chord2(self): - for mode in ["RGB", "L"]: - self.helper_chord(mode, BBOX2, 0, 180) - self.helper_chord(mode, BBOX2, 0.5, 180.4) - - def test_chord_width(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_chord_width.png" - - # Act - draw.chord(BBOX1, 10, 260, outline="yellow", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_chord_width_fill(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_chord_width_fill.png" - - # Act - draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def helper_ellipse(self, mode, bbox): - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode) - - # Act draw.ellipse(bbox, fill="green", outline="blue") + assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT)) - # Assert - assert_image_similar(im, Image.open(expected), 1) - def test_ellipse1(self): - for mode in ["RGB", "L"]: - self.helper_ellipse(mode, BBOX1) +def test_ellipse_width(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_ellipse_width.png" - def test_ellipse2(self): - for mode in ["RGB", "L"]: - self.helper_ellipse(mode, BBOX2) + # Act + draw.ellipse(BBOX1, outline="blue", width=5) - def test_ellipse_edge(self): + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_ellipse_width_large(): + # Arrange + im = Image.new("RGB", (500, 500)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_ellipse_width_large.png" + + # Act + draw.ellipse((25, 25, 475, 475), outline="blue", width=75) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_ellipse_width_fill(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_ellipse_width_fill.png" + + # Act + draw.ellipse(BBOX1, fill="green", outline="blue", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def helper_line(points): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.line(points, fill="yellow", width=2) + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) + + +def test_line1(): + helper_line(POINTS1) + + +def test_line2(): + helper_line(POINTS2) + + +def test_shape1(): + # Arrange + im = Image.new("RGB", (100, 100), "white") + draw = ImageDraw.Draw(im) + x0, y0 = 5, 5 + x1, y1 = 5, 50 + x2, y2 = 95, 50 + x3, y3 = 95, 5 + + # Act + s = ImageDraw.Outline() + s.move(x0, y0) + s.curve(x1, y1, x2, y2, x3, y3) + s.line(x0, y0) + + draw.shape(s, fill=1) + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_shape1.png")) + + +def test_shape2(): + # Arrange + im = Image.new("RGB", (100, 100), "white") + draw = ImageDraw.Draw(im) + x0, y0 = 95, 95 + x1, y1 = 95, 50 + x2, y2 = 5, 50 + x3, y3 = 5, 95 + + # Act + s = ImageDraw.Outline() + s.move(x0, y0) + s.curve(x1, y1, x2, y2, x3, y3) + s.line(x0, y0) + + draw.shape(s, outline="blue") + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_shape2.png")) + + +def helper_pieslice(bbox, start, end): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.pieslice(bbox, start, end, fill="white", outline="blue") + + # Assert + assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) + + +def test_pieslice1(): + helper_pieslice(BBOX1, -90, 45) + helper_pieslice(BBOX1, -90.5, 45.4) + + +def test_pieslice2(): + helper_pieslice(BBOX2, -90, 45) + helper_pieslice(BBOX2, -90.5, 45.4) + + +def test_pieslice_width(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_pieslice_width.png" + + # Act + draw.pieslice(BBOX1, 10, 260, outline="blue", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_pieslice_width_fill(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_pieslice_width_fill.png" + + # Act + draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def helper_point(points): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.point(points, fill="yellow") + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) + + +def test_point1(): + helper_point(POINTS1) + + +def test_point2(): + helper_point(POINTS2) + + +def helper_polygon(points): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.polygon(points, fill="red", outline="blue") + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) + + +def test_polygon1(): + helper_polygon(POINTS1) + + +def test_polygon2(): + helper_polygon(POINTS2) + + +def test_polygon_kite(): + # Test drawing lines of different gradients (dx>dy, dy>dx) and + # vertical (dx==0) and horizontal (dy==0) lines + for mode in ["RGB", "L"]: # Arrange - im = Image.new("RGB", (W, H)) + im = Image.new(mode, (W, H)) draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_polygon_kite_{}.png".format(mode) # Act - draw.ellipse(((0, 0), (W - 1, H)), fill="white") - - # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1 - ) - - def test_ellipse_symmetric(self): - for bbox in [(25, 25, 76, 76), (25, 25, 75, 75)]: - im = Image.new("RGB", (101, 101)) - draw = ImageDraw.Draw(im) - draw.ellipse(bbox, fill="green", outline="blue") - assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT)) - - def test_ellipse_width(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width.png" - - # Act - draw.ellipse(BBOX1, outline="blue", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_ellipse_width_large(self): - # Arrange - im = Image.new("RGB", (500, 500)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width_large.png" - - # Act - draw.ellipse((25, 25, 475, 475), outline="blue", width=75) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_ellipse_width_fill(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width_fill.png" - - # Act - draw.ellipse(BBOX1, fill="green", outline="blue", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def helper_line(self, points): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.line(points, fill="yellow", width=2) - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) - - def test_line1(self): - self.helper_line(POINTS1) - - def test_line2(self): - self.helper_line(POINTS2) - - def test_shape1(self): - # Arrange - im = Image.new("RGB", (100, 100), "white") - draw = ImageDraw.Draw(im) - x0, y0 = 5, 5 - x1, y1 = 5, 50 - x2, y2 = 95, 50 - x3, y3 = 95, 5 - - # Act - s = ImageDraw.Outline() - s.move(x0, y0) - s.curve(x1, y1, x2, y2, x3, y3) - s.line(x0, y0) - - draw.shape(s, fill=1) - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_shape1.png")) - - def test_shape2(self): - # Arrange - im = Image.new("RGB", (100, 100), "white") - draw = ImageDraw.Draw(im) - x0, y0 = 95, 95 - x1, y1 = 95, 50 - x2, y2 = 5, 50 - x3, y3 = 5, 95 - - # Act - s = ImageDraw.Outline() - s.move(x0, y0) - s.curve(x1, y1, x2, y2, x3, y3) - s.line(x0, y0) - - draw.shape(s, outline="blue") - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_shape2.png")) - - def helper_pieslice(self, bbox, start, end): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.pieslice(bbox, start, end, fill="white", outline="blue") - - # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) - - def test_pieslice1(self): - self.helper_pieslice(BBOX1, -90, 45) - self.helper_pieslice(BBOX1, -90.5, 45.4) - - def test_pieslice2(self): - self.helper_pieslice(BBOX2, -90, 45) - self.helper_pieslice(BBOX2, -90.5, 45.4) - - def test_pieslice_width(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_pieslice_width.png" - - # Act - draw.pieslice(BBOX1, 10, 260, outline="blue", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_pieslice_width_fill(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_pieslice_width_fill.png" - - # Act - draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def helper_point(self, points): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.point(points, fill="yellow") - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) - - def test_point1(self): - self.helper_point(POINTS1) - - def test_point2(self): - self.helper_point(POINTS2) - - def helper_polygon(self, points): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.polygon(points, fill="red", outline="blue") - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) - - def test_polygon1(self): - self.helper_polygon(POINTS1) - - def test_polygon2(self): - self.helper_polygon(POINTS2) - - def test_polygon_kite(self): - # Test drawing lines of different gradients (dx>dy, dy>dx) and - # vertical (dx==0) and horizontal (dy==0) lines - for mode in ["RGB", "L"]: - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_polygon_kite_{}.png".format(mode) - - # Act - draw.polygon(KITE_POINTS, fill="blue", outline="yellow") - - # Assert - assert_image_equal(im, Image.open(expected)) - - def helper_rectangle(self, bbox): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.rectangle(bbox, fill="black", outline="green") - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) - - def test_rectangle1(self): - self.helper_rectangle(BBOX1) - - def test_rectangle2(self): - self.helper_rectangle(BBOX2) - - def test_big_rectangle(self): - # Test drawing a rectangle bigger than the image - # Arrange - im = Image.new("RGB", (W, H)) - bbox = [(-1, -1), (W + 1, H + 1)] - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_big_rectangle.png" - - # Act - draw.rectangle(bbox, fill="orange") - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_rectangle_width(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_rectangle_width.png" - - # Act - draw.rectangle(BBOX1, outline="green", width=5) + draw.polygon(KITE_POINTS, fill="blue", outline="yellow") # Assert assert_image_equal(im, Image.open(expected)) - def test_rectangle_width_fill(self): + +def helper_rectangle(bbox): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.rectangle(bbox, fill="black", outline="green") + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) + + +def test_rectangle1(): + helper_rectangle(BBOX1) + + +def test_rectangle2(): + helper_rectangle(BBOX2) + + +def test_big_rectangle(): + # Test drawing a rectangle bigger than the image + # Arrange + im = Image.new("RGB", (W, H)) + bbox = [(-1, -1), (W + 1, H + 1)] + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_big_rectangle.png" + + # Act + draw.rectangle(bbox, fill="orange") + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_rectangle_width(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_rectangle_width.png" + + # Act + draw.rectangle(BBOX1, outline="green", width=5) + + # Assert + assert_image_equal(im, Image.open(expected)) + + +def test_rectangle_width_fill(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_rectangle_width_fill.png" + + # Act + draw.rectangle(BBOX1, fill="blue", outline="green", width=5) + + # Assert + assert_image_equal(im, Image.open(expected)) + + +def test_rectangle_I16(): + # Arrange + im = Image.new("I;16", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.rectangle(BBOX1, fill="black", outline="green") + + # Assert + assert_image_equal( + im.convert("I"), Image.open("Tests/images/imagedraw_rectangle_I.png") + ) + + +def test_floodfill(): + red = ImageColor.getrgb("red") + + for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]: # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_rectangle_width_fill.png" - - # Act - draw.rectangle(BBOX1, fill="blue", outline="green", width=5) - - # Assert - assert_image_equal(im, Image.open(expected)) - - def test_rectangle_I16(self): - # Arrange - im = Image.new("I;16", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - draw.rectangle(BBOX1, fill="black", outline="green") - - # Assert - assert_image_equal( - im.convert("I"), Image.open("Tests/images/imagedraw_rectangle_I.png") - ) - - def test_floodfill(self): - red = ImageColor.getrgb("red") - - for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]: - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - draw.rectangle(BBOX2, outline="yellow", fill="green") - centre_point = (int(W / 2), int(H / 2)) - - # Act - ImageDraw.floodfill(im, centre_point, value) - - # Assert - expected = "Tests/images/imagedraw_floodfill_" + mode + ".png" - with Image.open(expected) as im_floodfill: - assert_image_equal(im, im_floodfill) - - # Test that using the same colour does not change the image - ImageDraw.floodfill(im, centre_point, red) - assert_image_equal(im, im_floodfill) - - # Test that filling outside the image does not change the image - ImageDraw.floodfill(im, (W, H), red) - assert_image_equal(im, im_floodfill) - - # Test filling at the edge of an image - im = Image.new("RGB", (1, 1)) - ImageDraw.floodfill(im, (0, 0), red) - assert_image_equal(im, Image.new("RGB", (1, 1), red)) - - def test_floodfill_border(self): - # floodfill() is experimental - - # Arrange - im = Image.new("RGB", (W, H)) + im = Image.new(mode, (W, H)) draw = ImageDraw.Draw(im) draw.rectangle(BBOX2, outline="yellow", fill="green") centre_point = (int(W / 2), int(H / 2)) # Act - ImageDraw.floodfill( - im, - centre_point, - ImageColor.getrgb("red"), - border=ImageColor.getrgb("black"), - ) + ImageDraw.floodfill(im, centre_point, value) # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) + expected = "Tests/images/imagedraw_floodfill_" + mode + ".png" + with Image.open(expected) as im_floodfill: + assert_image_equal(im, im_floodfill) - def test_floodfill_thresh(self): - # floodfill() is experimental + # Test that using the same colour does not change the image + ImageDraw.floodfill(im, centre_point, red) + assert_image_equal(im, im_floodfill) - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - draw.rectangle(BBOX2, outline="darkgreen", fill="green") - centre_point = (int(W / 2), int(H / 2)) + # Test that filling outside the image does not change the image + ImageDraw.floodfill(im, (W, H), red) + assert_image_equal(im, im_floodfill) - # Act - ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"), thresh=30) + # Test filling at the edge of an image + im = Image.new("RGB", (1, 1)) + ImageDraw.floodfill(im, (0, 0), red) + assert_image_equal(im, Image.new("RGB", (1, 1), red)) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) - def test_floodfill_not_negative(self): - # floodfill() is experimental - # Test that floodfill does not extend into negative coordinates +def test_floodfill_border(): + # floodfill() is experimental - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - draw.line((W / 2, 0, W / 2, H / 2), fill="green") - draw.line((0, H / 2, W / 2, H / 2), fill="green") + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + draw.rectangle(BBOX2, outline="yellow", fill="green") + centre_point = (int(W / 2), int(H / 2)) - # Act - ImageDraw.floodfill(im, (int(W / 4), int(H / 4)), ImageColor.getrgb("red")) + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), border=ImageColor.getrgb("black"), + ) - # Assert + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + +def test_floodfill_thresh(): + # floodfill() is experimental + + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + draw.rectangle(BBOX2, outline="darkgreen", fill="green") + centre_point = (int(W / 2), int(H / 2)) + + # Act + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"), thresh=30) + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + +def test_floodfill_not_negative(): + # floodfill() is experimental + # Test that floodfill does not extend into negative coordinates + + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + draw.line((W / 2, 0, W / 2, H / 2), fill="green") + draw.line((0, H / 2, W / 2, H / 2), fill="green") + + # Act + ImageDraw.floodfill(im, (int(W / 4), int(H / 4)), ImageColor.getrgb("red")) + + # Assert + assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill_not_negative.png") + ) + + +def create_base_image_draw( + size, mode=DEFAULT_MODE, background1=WHITE, background2=GRAY +): + img = Image.new(mode, size, background1) + for x in range(0, size[0]): + for y in range(0, size[1]): + if (x + y) % 2 == 0: + img.putpixel((x, y), background2) + return img, ImageDraw.Draw(img) + + +def test_square(): + with Image.open(os.path.join(IMAGES_PATH, "square.png")) as expected: + expected.load() + img, draw = create_base_image_draw((10, 10)) + draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK) + assert_image_equal(img, expected, "square as normal polygon failed") + img, draw = create_base_image_draw((10, 10)) + draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK) + assert_image_equal(img, expected, "square as inverted polygon failed") + img, draw = create_base_image_draw((10, 10)) + draw.rectangle((2, 2, 7, 7), BLACK) + assert_image_equal(img, expected, "square as normal rectangle failed") + img, draw = create_base_image_draw((10, 10)) + draw.rectangle((7, 7, 2, 2), BLACK) + assert_image_equal(img, expected, "square as inverted rectangle failed") + + +def test_triangle_right(): + with Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK) + assert_image_equal(img, expected, "triangle right failed") + + +def test_line_horizontal(): + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w2px_normal.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 5), BLACK, 2) assert_image_equal( - im, Image.open("Tests/images/imagedraw_floodfill_not_negative.png") + img, expected, "line straight horizontal normal 2px wide failed" ) - - def create_base_image_draw( - self, size, mode=DEFAULT_MODE, background1=WHITE, background2=GRAY - ): - img = Image.new(mode, size, background1) - for x in range(0, size[0]): - for y in range(0, size[1]): - if (x + y) % 2 == 0: - img.putpixel((x, y), background2) - return img, ImageDraw.Draw(img) - - def test_square(self): - with Image.open(os.path.join(IMAGES_PATH, "square.png")) as expected: - expected.load() - img, draw = self.create_base_image_draw((10, 10)) - draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK) - assert_image_equal(img, expected, "square as normal polygon failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK) - assert_image_equal(img, expected, "square as inverted polygon failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.rectangle((2, 2, 7, 7), BLACK) - assert_image_equal(img, expected, "square as normal rectangle failed") - img, draw = self.create_base_image_draw((10, 10)) - draw.rectangle((7, 7, 2, 2), BLACK) - assert_image_equal(img, expected, "square as inverted rectangle failed") - - def test_triangle_right(self): - with Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK) - assert_image_equal(img, expected, "triangle right failed") - - def test_line_horizontal(self): - with Image.open( - os.path.join(IMAGES_PATH, "line_horizontal_w2px_normal.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 5), BLACK, 2) - assert_image_equal( - img, expected, "line straight horizontal normal 2px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_horizontal_w2px_inverted.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 5), BLACK, 2) - assert_image_equal( - img, expected, "line straight horizontal inverted 2px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_horizontal_w3px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 5), BLACK, 3) - assert_image_equal( - img, expected, "line straight horizontal normal 3px wide failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 5), BLACK, 3) - assert_image_equal( - img, expected, "line straight horizontal inverted 3px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_horizontal_w101px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((200, 110)) - draw.line((5, 55, 195, 55), BLACK, 101) - assert_image_equal( - img, expected, "line straight horizontal 101px wide failed" - ) - - def test_line_h_s1_w2(self): - self.skipTest("failing") - with Image.open( - os.path.join(IMAGES_PATH, "line_horizontal_slope1px_w2px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 6), BLACK, 2) - assert_image_equal( - img, expected, "line horizontal 1px slope 2px wide failed" - ) - - def test_line_vertical(self): - with Image.open( - os.path.join(IMAGES_PATH, "line_vertical_w2px_normal.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 5, 14), BLACK, 2) - assert_image_equal( - img, expected, "line straight vertical normal 2px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_vertical_w2px_inverted.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 5, 5), BLACK, 2) - assert_image_equal( - img, expected, "line straight vertical inverted 2px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_vertical_w3px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 5, 14), BLACK, 3) - assert_image_equal( - img, expected, "line straight vertical normal 3px wide failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 5, 5), BLACK, 3) - assert_image_equal( - img, expected, "line straight vertical inverted 3px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_vertical_w101px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((110, 200)) - draw.line((55, 5, 55, 195), BLACK, 101) - assert_image_equal( - img, expected, "line straight vertical 101px wide failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_vertical_slope1px_w2px.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 6, 14), BLACK, 2) - assert_image_equal(img, expected, "line vertical 1px slope 2px wide failed") - - def test_line_oblique_45(self): - with Image.open( - os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 5, 14, 14), BLACK, 3) - assert_image_equal( - img, expected, "line oblique 45 normal 3px wide A failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 14, 5, 5), BLACK, 3) - assert_image_equal( - img, expected, "line oblique 45 inverted 3px wide A failed" - ) - with Image.open( - os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png") - ) as expected: - expected.load() - img, draw = self.create_base_image_draw((20, 20)) - draw.line((14, 5, 5, 14), BLACK, 3) - assert_image_equal( - img, expected, "line oblique 45 normal 3px wide B failed" - ) - img, draw = self.create_base_image_draw((20, 20)) - draw.line((5, 14, 14, 5), BLACK, 3) - assert_image_equal( - img, expected, "line oblique 45 inverted 3px wide B failed" - ) - - def test_wide_line_dot(self): - # Test drawing a wide "line" from one point to another just draws - # a single point - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_wide_line_dot.png" - - # Act - draw.line([(50, 50), (50, 50)], width=3) - - # Assert - assert_image_similar(im, Image.open(expected), 1) - - def test_line_joint(self): - im = Image.new("RGB", (500, 325)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_line_joint_curve.png" - - # Act - xy = [ - (400, 280), - (380, 280), - (450, 280), - (440, 120), - (350, 200), - (310, 280), - (300, 280), - (250, 280), - (250, 200), - (150, 200), - (150, 260), - (50, 200), - (150, 50), - (250, 100), - ] - draw.line(xy, GRAY, 50, "curve") - - # Assert - assert_image_similar(im, Image.open(expected), 3) - - def test_textsize_empty_string(self): - # https://github.com/python-pillow/Pillow/issues/2783 - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - - # Act - # Should not cause 'SystemError: returned NULL without setting an error' - draw.textsize("") - draw.textsize("\n") - draw.textsize("test\n") - - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_textsize_stroke(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw.Draw(im) - font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20) - - # Act / Assert - self.assertEqual(draw.textsize("A", font, stroke_width=2), (16, 20)) - self.assertEqual( - draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2), (52, 44) + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w2px_inverted.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 5), BLACK, 2) + assert_image_equal( + img, expected, "line straight horizontal inverted 2px wide failed" ) + with Image.open(os.path.join(IMAGES_PATH, "line_horizontal_w3px.png")) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 5), BLACK, 3) + assert_image_equal( + img, expected, "line straight horizontal normal 3px wide failed" + ) + img, draw = create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 5), BLACK, 3) + assert_image_equal( + img, expected, "line straight horizontal inverted 3px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w101px.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((200, 110)) + draw.line((5, 55, 195, 55), BLACK, 101) + assert_image_equal(img, expected, "line straight horizontal 101px wide failed") - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_stroke(self): - for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items(): - # Arrange - im = Image.new("RGB", (120, 130)) - draw = ImageDraw.Draw(im) - font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) - # Act - draw.text( - (10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill - ) +def test_line_h_s1_w2(): + pytest.skip("failing") + with Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_slope1px_w2px.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 6), BLACK, 2) + assert_image_equal(img, expected, "line horizontal 1px slope 2px wide failed") - # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1 - ) - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_stroke_multiline(self): +def test_line_vertical(): + with Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w2px_normal.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 5, 14), BLACK, 2) + assert_image_equal( + img, expected, "line straight vertical normal 2px wide failed" + ) + with Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w2px_inverted.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 14, 5, 5), BLACK, 2) + assert_image_equal( + img, expected, "line straight vertical inverted 2px wide failed" + ) + with Image.open(os.path.join(IMAGES_PATH, "line_vertical_w3px.png")) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 5, 14), BLACK, 3) + assert_image_equal( + img, expected, "line straight vertical normal 3px wide failed" + ) + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 14, 5, 5), BLACK, 3) + assert_image_equal( + img, expected, "line straight vertical inverted 3px wide failed" + ) + with Image.open(os.path.join(IMAGES_PATH, "line_vertical_w101px.png")) as expected: + expected.load() + img, draw = create_base_image_draw((110, 200)) + draw.line((55, 5, 55, 195), BLACK, 101) + assert_image_equal(img, expected, "line straight vertical 101px wide failed") + with Image.open( + os.path.join(IMAGES_PATH, "line_vertical_slope1px_w2px.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 6, 14), BLACK, 2) + assert_image_equal(img, expected, "line vertical 1px slope 2px wide failed") + + +def test_line_oblique_45(): + with Image.open( + os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 5, 14, 14), BLACK, 3) + assert_image_equal(img, expected, "line oblique 45 normal 3px wide A failed") + img, draw = create_base_image_draw((20, 20)) + draw.line((14, 14, 5, 5), BLACK, 3) + assert_image_equal(img, expected, "line oblique 45 inverted 3px wide A failed") + with Image.open( + os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png") + ) as expected: + expected.load() + img, draw = create_base_image_draw((20, 20)) + draw.line((14, 5, 5, 14), BLACK, 3) + assert_image_equal(img, expected, "line oblique 45 normal 3px wide B failed") + img, draw = create_base_image_draw((20, 20)) + draw.line((5, 14, 14, 5), BLACK, 3) + assert_image_equal(img, expected, "line oblique 45 inverted 3px wide B failed") + + +def test_wide_line_dot(): + # Test drawing a wide "line" from one point to another just draws a single point + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_wide_line_dot.png" + + # Act + draw.line([(50, 50), (50, 50)], width=3) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +def test_line_joint(): + im = Image.new("RGB", (500, 325)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_line_joint_curve.png" + + # Act + xy = [ + (400, 280), + (380, 280), + (450, 280), + (440, 120), + (350, 200), + (310, 280), + (300, 280), + (250, 280), + (250, 200), + (150, 200), + (150, 260), + (50, 200), + (150, 50), + (250, 100), + ] + draw.line(xy, GRAY, 50, "curve") + + # Assert + assert_image_similar(im, Image.open(expected), 3) + + +def test_textsize_empty_string(): + # https://github.com/python-pillow/Pillow/issues/2783 + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + # Should not cause 'SystemError: returned NULL without setting an error' + draw.textsize("") + draw.textsize("\n") + draw.textsize("test\n") + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_textsize_stroke(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20) + + # Act / Assert + assert draw.textsize("A", font, stroke_width=2) == (16, 20) + assert draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2) == (52, 44) + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_stroke(): + for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items(): # Arrange - im = Image.new("RGB", (100, 250)) + im = Image.new("RGB", (120, 130)) draw = ImageDraw.Draw(im) font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) # Act - draw.multiline_text( - (10, 10), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0" - ) + draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill) # Assert assert_image_similar( - im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3 + im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1 ) - def test_same_color_outline(self): - # Prepare shape - x0, y0 = 5, 5 - x1, y1 = 5, 50 - x2, y2 = 95, 50 - x3, y3 = 95, 5 - s = ImageDraw.Outline() - s.move(x0, y0) - s.curve(x1, y1, x2, y2, x3, y3) - s.line(x0, y0) +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_stroke_multiline(): + # Arrange + im = Image.new("RGB", (100, 250)) + draw = ImageDraw.Draw(im) + font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) - # Begin - for mode in ["RGB", "L"]: - for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]: - for operation, args in { - "chord": [BBOX1, 0, 180], - "ellipse": [BBOX1], - "shape": [s], - "pieslice": [BBOX1, -90, 45], - "polygon": [[(18, 30), (85, 30), (60, 72)]], - "rectangle": [BBOX1], - }.items(): - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) + # Act + draw.multiline_text( + (10, 10), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0" + ) - # Act - draw_method = getattr(draw, operation) - args += [fill, outline] - draw_method(*args) + # Assert + assert_image_similar( + im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3 + ) - # Assert - expected = "Tests/images/imagedraw_outline_{}_{}.png".format( - operation, mode - ) - assert_image_similar(im, Image.open(expected), 1) + +def test_same_color_outline(): + # Prepare shape + x0, y0 = 5, 5 + x1, y1 = 5, 50 + x2, y2 = 95, 50 + x3, y3 = 95, 5 + + s = ImageDraw.Outline() + s.move(x0, y0) + s.curve(x1, y1, x2, y2, x3, y3) + s.line(x0, y0) + + # Begin + for mode in ["RGB", "L"]: + for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]: + for operation, args in { + "chord": [BBOX1, 0, 180], + "ellipse": [BBOX1], + "shape": [s], + "pieslice": [BBOX1, -90, 45], + "polygon": [[(18, 30), (85, 30), (60, 72)]], + "rectangle": [BBOX1], + }.items(): + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw_method = getattr(draw, operation) + args += [fill, outline] + draw_method(*args) + + # Assert + expected = "Tests/images/imagedraw_outline_{}_{}.png".format( + operation, mode + ) + assert_image_similar(im, Image.open(expected), 1) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 80bafcb96..406a3a85b 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -1,9 +1,9 @@ import os.path -import unittest +import pytest from PIL import Image, ImageDraw2, features -from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper +from .helper import assert_image_equal, assert_image_similar, hopper BLACK = (0, 0, 0) WHITE = (255, 255, 255) @@ -34,190 +34,206 @@ HAS_FREETYPE = features.check("freetype2") FONT_PATH = "Tests/fonts/FreeMono.ttf" -class TestImageDraw(PillowTestCase): - def test_sanity(self): - im = hopper("RGB").copy() +def test_sanity(): + im = hopper("RGB").copy() - draw = ImageDraw2.Draw(im) - pen = ImageDraw2.Pen("blue", width=7) - draw.line(list(range(10)), pen) + draw = ImageDraw2.Draw(im) + pen = ImageDraw2.Pen("blue", width=7) + draw.line(list(range(10)), pen) - from PIL import ImageDraw + from PIL import ImageDraw - draw, handler = ImageDraw.getdraw(im) - pen = ImageDraw2.Pen("blue", width=7) - draw.line(list(range(10)), pen) + draw, handler = ImageDraw.getdraw(im) + pen = ImageDraw2.Pen("blue", width=7) + draw.line(list(range(10)), pen) - def helper_ellipse(self, mode, bbox): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - pen = ImageDraw2.Pen("blue", width=2) - brush = ImageDraw2.Brush("green") - expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode) - # Act - draw.ellipse(bbox, pen, brush) +def helper_ellipse(mode, bbox): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + pen = ImageDraw2.Pen("blue", width=2) + brush = ImageDraw2.Brush("green") + expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode) - # Assert - assert_image_similar(im, Image.open(expected), 1) + # Act + draw.ellipse(bbox, pen, brush) - def test_ellipse1(self): - self.helper_ellipse("RGB", BBOX1) + # Assert + assert_image_similar(im, Image.open(expected), 1) - def test_ellipse2(self): - self.helper_ellipse("RGB", BBOX2) - def test_ellipse_edge(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - brush = ImageDraw2.Brush("white") +def test_ellipse1(): + helper_ellipse("RGB", BBOX1) - # Act - draw.ellipse(((0, 0), (W - 1, H)), brush) - # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1 - ) +def test_ellipse2(): + helper_ellipse("RGB", BBOX2) - def helper_line(self, points): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - pen = ImageDraw2.Pen("yellow", width=2) - # Act - draw.line(points, pen) +def test_ellipse_edge(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + brush = ImageDraw2.Brush("white") - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) + # Act + draw.ellipse(((0, 0), (W - 1, H)), brush) - def test_line1_pen(self): - self.helper_line(POINTS1) + # Assert + assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) - def test_line2_pen(self): - self.helper_line(POINTS2) - def test_line_pen_as_brush(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - pen = None - brush = ImageDraw2.Pen("yellow", width=2) +def helper_line(points): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + pen = ImageDraw2.Pen("yellow", width=2) - # Act - # Pass in the pen as the brush parameter - draw.line(POINTS1, pen, brush) + # Act + draw.line(points, pen) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) - def helper_polygon(self, points): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - pen = ImageDraw2.Pen("blue", width=2) - brush = ImageDraw2.Brush("red") - # Act - draw.polygon(points, pen, brush) +def test_line1_pen(): + helper_line(POINTS1) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) - def test_polygon1(self): - self.helper_polygon(POINTS1) +def test_line2_pen(): + helper_line(POINTS2) - def test_polygon2(self): - self.helper_polygon(POINTS2) - def helper_rectangle(self, bbox): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - pen = ImageDraw2.Pen("green", width=2) - brush = ImageDraw2.Brush("black") +def test_line_pen_as_brush(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + pen = None + brush = ImageDraw2.Pen("yellow", width=2) - # Act - draw.rectangle(bbox, pen, brush) + # Act + # Pass in the pen as the brush parameter + draw.line(POINTS1, pen, brush) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) - def test_rectangle1(self): - self.helper_rectangle(BBOX1) - def test_rectangle2(self): - self.helper_rectangle(BBOX2) +def helper_polygon(points): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + pen = ImageDraw2.Pen("blue", width=2) + brush = ImageDraw2.Brush("red") - def test_big_rectangle(self): - # Test drawing a rectangle bigger than the image - # Arrange - im = Image.new("RGB", (W, H)) - bbox = [(-1, -1), (W + 1, H + 1)] - brush = ImageDraw2.Brush("orange") - draw = ImageDraw2.Draw(im) - expected = "Tests/images/imagedraw_big_rectangle.png" + # Act + draw.polygon(points, pen, brush) - # Act - draw.rectangle(bbox, brush) + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) - # Assert - assert_image_similar(im, Image.open(expected), 1) - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_text(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - font = ImageDraw2.Font("white", FONT_PATH) - expected = "Tests/images/imagedraw2_text.png" +def test_polygon1(): + helper_polygon(POINTS1) - # Act - draw.text((5, 5), "ImageDraw2", font) - # Assert - assert_image_similar(im, Image.open(expected), 13) +def test_polygon2(): + helper_polygon(POINTS2) - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_textsize(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - font = ImageDraw2.Font("white", FONT_PATH) - # Act - size = draw.textsize("ImageDraw2", font) +def helper_rectangle(bbox): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + pen = ImageDraw2.Pen("green", width=2) + brush = ImageDraw2.Brush("black") - # Assert - self.assertEqual(size[1], 12) + # Act + draw.rectangle(bbox, pen, brush) - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_textsize_empty_string(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - font = ImageDraw2.Font("white", FONT_PATH) + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) - # Act - # Should not cause 'SystemError: returned NULL without setting an error' - draw.textsize("", font) - draw.textsize("\n", font) - draw.textsize("test\n", font) - @unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") - def test_flush(self): - # Arrange - im = Image.new("RGB", (W, H)) - draw = ImageDraw2.Draw(im) - font = ImageDraw2.Font("white", FONT_PATH) +def test_rectangle1(): + helper_rectangle(BBOX1) - # Act - draw.text((5, 5), "ImageDraw2", font) - im2 = draw.flush() - # Assert - assert_image_equal(im, im2) +def test_rectangle2(): + helper_rectangle(BBOX2) + + +def test_big_rectangle(): + # Test drawing a rectangle bigger than the image + # Arrange + im = Image.new("RGB", (W, H)) + bbox = [(-1, -1), (W + 1, H + 1)] + brush = ImageDraw2.Brush("orange") + draw = ImageDraw2.Draw(im) + expected = "Tests/images/imagedraw_big_rectangle.png" + + # Act + draw.rectangle(bbox, brush) + + # Assert + assert_image_similar(im, Image.open(expected), 1) + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_text(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + font = ImageDraw2.Font("white", FONT_PATH) + expected = "Tests/images/imagedraw2_text.png" + + # Act + draw.text((5, 5), "ImageDraw2", font) + + # Assert + assert_image_similar(im, Image.open(expected), 13) + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_textsize(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + font = ImageDraw2.Font("white", FONT_PATH) + + # Act + size = draw.textsize("ImageDraw2", font) + + # Assert + assert size[1] == 12 + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_textsize_empty_string(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + font = ImageDraw2.Font("white", FONT_PATH) + + # Act + # Should not cause 'SystemError: returned NULL without setting an error' + draw.textsize("", font) + draw.textsize("\n", font) + draw.textsize("test\n", font) + + +@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +def test_flush(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + font = ImageDraw2.Font("white", FONT_PATH) + + # Act + draw.text((5, 5), "ImageDraw2", font) + im2 = draw.flush() + + # Assert + assert_image_equal(im, im2) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 9524d2fcc..32ab05f17 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -1,54 +1,55 @@ from PIL import Image, ImageEnhance -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper -class TestImageEnhance(PillowTestCase): - def test_sanity(self): +def test_sanity(): + # FIXME: assert_image + # Implicit asserts no exception: + ImageEnhance.Color(hopper()).enhance(0.5) + ImageEnhance.Contrast(hopper()).enhance(0.5) + ImageEnhance.Brightness(hopper()).enhance(0.5) + ImageEnhance.Sharpness(hopper()).enhance(0.5) - # FIXME: assert_image - # Implicit asserts no exception: - ImageEnhance.Color(hopper()).enhance(0.5) - ImageEnhance.Contrast(hopper()).enhance(0.5) - ImageEnhance.Brightness(hopper()).enhance(0.5) - ImageEnhance.Sharpness(hopper()).enhance(0.5) - def test_crash(self): +def test_crash(): + # crashes on small images + im = Image.new("RGB", (1, 1)) + ImageEnhance.Sharpness(im).enhance(0.5) - # crashes on small images - im = Image.new("RGB", (1, 1)) - ImageEnhance.Sharpness(im).enhance(0.5) - def _half_transparent_image(self): - # returns an image, half transparent, half solid - im = hopper("RGB") +def _half_transparent_image(): + # returns an image, half transparent, half solid + im = hopper("RGB") - transparent = Image.new("L", im.size, 0) - solid = Image.new("L", (im.size[0] // 2, im.size[1]), 255) - transparent.paste(solid, (0, 0)) - im.putalpha(transparent) + transparent = Image.new("L", im.size, 0) + solid = Image.new("L", (im.size[0] // 2, im.size[1]), 255) + transparent.paste(solid, (0, 0)) + im.putalpha(transparent) - return im + return im - def _check_alpha(self, im, original, op, amount): - self.assertEqual(im.getbands(), original.getbands()) - assert_image_equal( - im.getchannel("A"), - original.getchannel("A"), - "Diff on {}: {}".format(op, amount), - ) - def test_alpha(self): - # Issue https://github.com/python-pillow/Pillow/issues/899 - # Is alpha preserved through image enhancement? +def _check_alpha(im, original, op, amount): + assert im.getbands() == original.getbands() + assert_image_equal( + im.getchannel("A"), + original.getchannel("A"), + "Diff on {}: {}".format(op, amount), + ) - original = self._half_transparent_image() - for op in ["Color", "Brightness", "Contrast", "Sharpness"]: - for amount in [0, 0.5, 1.0]: - self._check_alpha( - getattr(ImageEnhance, op)(original).enhance(amount), - original, - op, - amount, - ) +def test_alpha(): + # Issue https://github.com/python-pillow/Pillow/issues/899 + # Is alpha preserved through image enhancement? + + original = _half_transparent_image() + + for op in ["Color", "Brightness", "Contrast", "Sharpness"]: + for amount in [0, 0.5, 1.0]: + _check_alpha( + getattr(ImageEnhance, op)(original).enhance(amount), + original, + op, + amount, + ) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 166a2605d..af78c1197 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,7 +1,7 @@ +import pytest from PIL import Image, ImageOps from .helper import ( - PillowTestCase, assert_image_equal, assert_image_similar, assert_tuple_approx_equal, @@ -16,287 +16,294 @@ except ImportError: HAVE_WEBP = False -class TestImageOps(PillowTestCase): - class Deformer: - def getmesh(self, im): - x, y = im.size - return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] +class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] - deformer = Deformer() - def test_sanity(self): +deformer = Deformer() - ImageOps.autocontrast(hopper("L")) - ImageOps.autocontrast(hopper("RGB")) - ImageOps.autocontrast(hopper("L"), cutoff=10) - ImageOps.autocontrast(hopper("L"), ignore=[0, 255]) +def test_sanity(): - ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255)) - ImageOps.colorize(hopper("L"), "black", "white") + ImageOps.autocontrast(hopper("L")) + ImageOps.autocontrast(hopper("RGB")) - ImageOps.pad(hopper("L"), (128, 128)) - ImageOps.pad(hopper("RGB"), (128, 128)) + ImageOps.autocontrast(hopper("L"), cutoff=10) + ImageOps.autocontrast(hopper("L"), ignore=[0, 255]) - ImageOps.crop(hopper("L"), 1) - ImageOps.crop(hopper("RGB"), 1) + ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(hopper("L"), "black", "white") - ImageOps.deform(hopper("L"), self.deformer) - ImageOps.deform(hopper("RGB"), self.deformer) + ImageOps.pad(hopper("L"), (128, 128)) + ImageOps.pad(hopper("RGB"), (128, 128)) - ImageOps.equalize(hopper("L")) - ImageOps.equalize(hopper("RGB")) + ImageOps.crop(hopper("L"), 1) + ImageOps.crop(hopper("RGB"), 1) - ImageOps.expand(hopper("L"), 1) - ImageOps.expand(hopper("RGB"), 1) - ImageOps.expand(hopper("L"), 2, "blue") - ImageOps.expand(hopper("RGB"), 2, "blue") + ImageOps.deform(hopper("L"), deformer) + ImageOps.deform(hopper("RGB"), deformer) - ImageOps.fit(hopper("L"), (128, 128)) - ImageOps.fit(hopper("RGB"), (128, 128)) + ImageOps.equalize(hopper("L")) + ImageOps.equalize(hopper("RGB")) - ImageOps.flip(hopper("L")) - ImageOps.flip(hopper("RGB")) + ImageOps.expand(hopper("L"), 1) + ImageOps.expand(hopper("RGB"), 1) + ImageOps.expand(hopper("L"), 2, "blue") + ImageOps.expand(hopper("RGB"), 2, "blue") - ImageOps.grayscale(hopper("L")) - ImageOps.grayscale(hopper("RGB")) + ImageOps.fit(hopper("L"), (128, 128)) + ImageOps.fit(hopper("RGB"), (128, 128)) - ImageOps.invert(hopper("L")) - ImageOps.invert(hopper("RGB")) + ImageOps.flip(hopper("L")) + ImageOps.flip(hopper("RGB")) - ImageOps.mirror(hopper("L")) - ImageOps.mirror(hopper("RGB")) + ImageOps.grayscale(hopper("L")) + ImageOps.grayscale(hopper("RGB")) - ImageOps.posterize(hopper("L"), 4) - ImageOps.posterize(hopper("RGB"), 4) + ImageOps.invert(hopper("L")) + ImageOps.invert(hopper("RGB")) - ImageOps.solarize(hopper("L")) - ImageOps.solarize(hopper("RGB")) + ImageOps.mirror(hopper("L")) + ImageOps.mirror(hopper("RGB")) - ImageOps.exif_transpose(hopper("L")) - ImageOps.exif_transpose(hopper("RGB")) + ImageOps.posterize(hopper("L"), 4) + ImageOps.posterize(hopper("RGB"), 4) - def test_1pxfit(self): - # Division by zero in equalize if image is 1 pixel high - newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) + ImageOps.solarize(hopper("L")) + ImageOps.solarize(hopper("RGB")) - newimg = ImageOps.fit(hopper("RGB").resize((1, 100)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) + ImageOps.exif_transpose(hopper("L")) + ImageOps.exif_transpose(hopper("RGB")) - newimg = ImageOps.fit(hopper("RGB").resize((100, 1)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) - def test_fit_same_ratio(self): - # The ratio for this image is 1000.0 / 755 = 1.3245033112582782 - # If the ratios are not acknowledged to be the same, - # and Pillow attempts to adjust the width to - # 1.3245033112582782 * 755 = 1000.0000000000001 - # then centering this greater width causes a negative x offset when cropping - with Image.new("RGB", (1000, 755)) as im: - new_im = ImageOps.fit(im, (1000, 755)) - self.assertEqual(new_im.size, (1000, 755)) +def test_1pxfit(): + # Division by zero in equalize if image is 1 pixel high + newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35)) + assert newimg.size == (35, 35) - def test_pad(self): - # Same ratio - im = hopper() - new_size = (im.width * 2, im.height * 2) - new_im = ImageOps.pad(im, new_size) - self.assertEqual(new_im.size, new_size) + newimg = ImageOps.fit(hopper("RGB").resize((1, 100)), (35, 35)) + assert newimg.size == (35, 35) - for label, color, new_size in [ - ("h", None, (im.width * 4, im.height * 2)), - ("v", "#f00", (im.width * 2, im.height * 4)), - ]: - for i, centering in enumerate([(0, 0), (0.5, 0.5), (1, 1)]): - new_im = ImageOps.pad(im, new_size, color=color, centering=centering) - self.assertEqual(new_im.size, new_size) + newimg = ImageOps.fit(hopper("RGB").resize((100, 1)), (35, 35)) + assert newimg.size == (35, 35) + +def test_fit_same_ratio(): + # The ratio for this image is 1000.0 / 755 = 1.3245033112582782 + # If the ratios are not acknowledged to be the same, + # and Pillow attempts to adjust the width to + # 1.3245033112582782 * 755 = 1000.0000000000001 + # then centering this greater width causes a negative x offset when cropping + with Image.new("RGB", (1000, 755)) as im: + new_im = ImageOps.fit(im, (1000, 755)) + assert new_im.size == (1000, 755) + + +def test_pad(): + # Same ratio + im = hopper() + new_size = (im.width * 2, im.height * 2) + new_im = ImageOps.pad(im, new_size) + assert new_im.size == new_size + + for label, color, new_size in [ + ("h", None, (im.width * 4, im.height * 2)), + ("v", "#f00", (im.width * 2, im.height * 4)), + ]: + for i, centering in enumerate([(0, 0), (0.5, 0.5), (1, 1)]): + new_im = ImageOps.pad(im, new_size, color=color, centering=centering) + assert new_im.size == new_size + + with Image.open( + "Tests/images/imageops_pad_" + label + "_" + str(i) + ".jpg" + ) as target: + assert_image_similar(new_im, target, 6) + + +def test_pil163(): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = hopper("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + +def test_scale(): + # Test the scaling function + i = hopper("L").resize((50, 50)) + + with pytest.raises(ValueError): + ImageOps.scale(i, -1) + + newimg = ImageOps.scale(i, 1) + assert newimg.size == (50, 50) + + newimg = ImageOps.scale(i, 2) + assert newimg.size == (100, 100) + + newimg = ImageOps.scale(i, 0.5) + assert newimg.size == (25, 25) + + +def test_colorize_2color(): + # Test the colorizing function with 2-color functionality + + # Open test image (256px by 10px, black to white) + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") + + # Create image with original 2-color functionality + im_test = ImageOps.colorize(im, "red", "green") + + # Test output image (2-color) + left = (0, 1) + middle = (127, 1) + right = (255, 1) + assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg="mid test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) + + +def test_colorize_2color_offset(): + # Test the colorizing function with 2-color functionality and offset + + # Open test image (256px by 10px, black to white) + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") + + # Create image with original 2-color functionality with offsets + im_test = ImageOps.colorize( + im, black="red", white="green", blackpoint=50, whitepoint=100 + ) + + # Test output image (2-color) with offsets + left = (25, 1) + middle = (75, 1) + right = (125, 1) + assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg="mid test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) + + +def test_colorize_3color_offset(): + # Test the colorizing function with 3-color functionality and offset + + # Open test image (256px by 10px, black to white) + with Image.open("Tests/images/bw_gradient.png") as im: + im = im.convert("L") + + # Create image with new three color functionality with offsets + im_test = ImageOps.colorize( + im, + black="red", + white="green", + mid="blue", + blackpoint=50, + whitepoint=200, + midpoint=100, + ) + + # Test output image (3-color) with offsets + left = (25, 1) + left_middle = (75, 1) + middle = (100, 1) + right_middle = (150, 1) + right = (225, 1) + assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(left_middle), + (127, 0, 127), + threshold=1, + msg="low-mid test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(middle), (0, 0, 255), threshold=1, msg="mid incorrect" + ) + assert_tuple_approx_equal( + im_test.getpixel(right_middle), + (0, 63, 127), + threshold=1, + msg="high-mid test pixel incorrect", + ) + assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) + + +def test_exif_transpose(): + exts = [".jpg"] + if HAVE_WEBP and _webp.HAVE_WEBPANIM: + exts.append(".webp") + for ext in exts: + with Image.open("Tests/images/hopper" + ext) as base_im: + + def check(orientation_im): + for im in [ + orientation_im, + orientation_im.copy(), + ]: # ImageFile # Image + if orientation_im is base_im: + assert "exif" not in im.info + else: + original_exif = im.info["exif"] + transposed_im = ImageOps.exif_transpose(im) + assert_image_similar(base_im, transposed_im, 17) + if orientation_im is base_im: + assert "exif" not in im.info + else: + assert transposed_im.info["exif"] != original_exif + + assert 0x0112 not in transposed_im.getexif() + + # Repeat the operation to test that it does not keep transposing + transposed_im2 = ImageOps.exif_transpose(transposed_im) + assert_image_equal(transposed_im2, transposed_im) + + check(base_im) + for i in range(2, 9): with Image.open( - "Tests/images/imageops_pad_" + label + "_" + str(i) + ".jpg" - ) as target: - assert_image_similar(new_im, target, 6) - - def test_pil163(self): - # Division by zero in equalize if < 255 pixels in image (@PIL163) - - i = hopper("RGB").resize((15, 16)) - - ImageOps.equalize(i.convert("L")) - ImageOps.equalize(i.convert("P")) - ImageOps.equalize(i.convert("RGB")) - - def test_scale(self): - # Test the scaling function - i = hopper("L").resize((50, 50)) - - with self.assertRaises(ValueError): - ImageOps.scale(i, -1) - - newimg = ImageOps.scale(i, 1) - self.assertEqual(newimg.size, (50, 50)) - - newimg = ImageOps.scale(i, 2) - self.assertEqual(newimg.size, (100, 100)) - - newimg = ImageOps.scale(i, 0.5) - self.assertEqual(newimg.size, (25, 25)) - - def test_colorize_2color(self): - # Test the colorizing function with 2-color functionality - - # Open test image (256px by 10px, black to white) - with Image.open("Tests/images/bw_gradient.png") as im: - im = im.convert("L") - - # Create image with original 2-color functionality - im_test = ImageOps.colorize(im, "red", "green") - - # Test output image (2-color) - left = (0, 1) - middle = (127, 1) - right = (255, 1) - assert_tuple_approx_equal( - im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg="black test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(middle), - (127, 63, 0), - threshold=1, - msg="mid test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg="white test pixel incorrect", - ) - - def test_colorize_2color_offset(self): - # Test the colorizing function with 2-color functionality and offset - - # Open test image (256px by 10px, black to white) - with Image.open("Tests/images/bw_gradient.png") as im: - im = im.convert("L") - - # Create image with original 2-color functionality with offsets - im_test = ImageOps.colorize( - im, black="red", white="green", blackpoint=50, whitepoint=100 - ) - - # Test output image (2-color) with offsets - left = (25, 1) - middle = (75, 1) - right = (125, 1) - assert_tuple_approx_equal( - im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg="black test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(middle), - (127, 63, 0), - threshold=1, - msg="mid test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg="white test pixel incorrect", - ) - - def test_colorize_3color_offset(self): - # Test the colorizing function with 3-color functionality and offset - - # Open test image (256px by 10px, black to white) - with Image.open("Tests/images/bw_gradient.png") as im: - im = im.convert("L") - - # Create image with new three color functionality with offsets - im_test = ImageOps.colorize( - im, - black="red", - white="green", - mid="blue", - blackpoint=50, - whitepoint=200, - midpoint=100, - ) - - # Test output image (3-color) with offsets - left = (25, 1) - left_middle = (75, 1) - middle = (100, 1) - right_middle = (150, 1) - right = (225, 1) - assert_tuple_approx_equal( - im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg="black test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(left_middle), - (127, 0, 127), - threshold=1, - msg="low-mid test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(middle), (0, 0, 255), threshold=1, msg="mid incorrect" - ) - assert_tuple_approx_equal( - im_test.getpixel(right_middle), - (0, 63, 127), - threshold=1, - msg="high-mid test pixel incorrect", - ) - assert_tuple_approx_equal( - im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg="white test pixel incorrect", - ) - - def test_exif_transpose(self): - exts = [".jpg"] - if HAVE_WEBP and _webp.HAVE_WEBPANIM: - exts.append(".webp") - for ext in exts: - with Image.open("Tests/images/hopper" + ext) as base_im: - - def check(orientation_im): - for im in [ - orientation_im, - orientation_im.copy(), - ]: # ImageFile # Image - if orientation_im is base_im: - self.assertNotIn("exif", im.info) - else: - original_exif = im.info["exif"] - transposed_im = ImageOps.exif_transpose(im) - assert_image_similar(base_im, transposed_im, 17) - if orientation_im is base_im: - self.assertNotIn("exif", im.info) - else: - self.assertNotEqual( - transposed_im.info["exif"], original_exif - ) - - self.assertNotIn(0x0112, transposed_im.getexif()) - - # Repeat the operation - # to test that it does not keep transposing - transposed_im2 = ImageOps.exif_transpose(transposed_im) - assert_image_equal(transposed_im2, transposed_im) - - check(base_im) - for i in range(2, 9): - with Image.open( - "Tests/images/hopper_orientation_" + str(i) + ext - ) as orientation_im: - check(orientation_im) + "Tests/images/hopper_orientation_" + str(i) + ext + ) as orientation_im: + check(orientation_im) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index aba378714..d13920c16 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,8 +1,7 @@ -import unittest - +import pytest from PIL import Image -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import assert_image_equal, hopper try: from PIL import ImageTk @@ -12,76 +11,81 @@ try: dir(ImageTk) HAS_TK = True except (OSError, ImportError): - # Skipped via setUp() + # Skipped via pytestmark HAS_TK = False TK_MODES = ("1", "L", "P", "RGB", "RGBA") -@unittest.skipUnless(HAS_TK, "Tk not installed") -class TestImageTk(PillowTestCase): - def setUp(self): - try: - # setup tk - tk.Frame() - # root = tk.Tk() - except tk.TclError as v: - self.skipTest("TCL Error: %s" % v) +pytestmark = pytest.mark.skipif(not HAS_TK, reason="Tk not installed") - def test_kw(self): - TEST_JPG = "Tests/images/hopper.jpg" - TEST_PNG = "Tests/images/hopper.png" - with Image.open(TEST_JPG) as im1: - with Image.open(TEST_PNG) as im2: - with open(TEST_PNG, "rb") as fp: - data = fp.read() - kw = {"file": TEST_JPG, "data": data} - # Test "file" - im = ImageTk._get_image_from_kw(kw) - assert_image_equal(im, im1) +def setup_module(): + try: + # setup tk + tk.Frame() + # root = tk.Tk() + except tk.TclError as v: + pytest.skip("TCL Error: %s" % v) - # Test "data" - im = ImageTk._get_image_from_kw(kw) - assert_image_equal(im, im2) - # Test no relevant entry - im = ImageTk._get_image_from_kw(kw) - self.assertIsNone(im) +def test_kw(): + TEST_JPG = "Tests/images/hopper.jpg" + TEST_PNG = "Tests/images/hopper.png" + with Image.open(TEST_JPG) as im1: + with Image.open(TEST_PNG) as im2: + with open(TEST_PNG, "rb") as fp: + data = fp.read() + kw = {"file": TEST_JPG, "data": data} - def test_photoimage(self): - for mode in TK_MODES: - # test as image: - im = hopper(mode) + # Test "file" + im = ImageTk._get_image_from_kw(kw) + assert_image_equal(im, im1) - # this should not crash - im_tk = ImageTk.PhotoImage(im) + # Test "data" + im = ImageTk._get_image_from_kw(kw) + assert_image_equal(im, im2) - self.assertEqual(im_tk.width(), im.width) - self.assertEqual(im_tk.height(), im.height) + # Test no relevant entry + im = ImageTk._get_image_from_kw(kw) + assert im is None - reloaded = ImageTk.getimage(im_tk) - assert_image_equal(reloaded, im.convert("RGBA")) - def test_photoimage_blank(self): - # test a image using mode/size: - for mode in TK_MODES: - im_tk = ImageTk.PhotoImage(mode, (100, 100)) - - self.assertEqual(im_tk.width(), 100) - self.assertEqual(im_tk.height(), 100) - - # reloaded = ImageTk.getimage(im_tk) - # assert_image_equal(reloaded, im) - - def test_bitmapimage(self): - im = hopper("1") +def test_photoimage(): + for mode in TK_MODES: + # test as image: + im = hopper(mode) # this should not crash - im_tk = ImageTk.BitmapImage(im) + im_tk = ImageTk.PhotoImage(im) - self.assertEqual(im_tk.width(), im.width) - self.assertEqual(im_tk.height(), im.height) + assert im_tk.width() == im.width + assert im_tk.height() == im.height + + reloaded = ImageTk.getimage(im_tk) + assert_image_equal(reloaded, im.convert("RGBA")) + + +def test_photoimage_blank(): + # test a image using mode/size: + for mode in TK_MODES: + im_tk = ImageTk.PhotoImage(mode, (100, 100)) + + assert im_tk.width() == 100 + assert im_tk.height() == 100 # reloaded = ImageTk.getimage(im_tk) # assert_image_equal(reloaded, im) + + +def test_bitmapimage(): + im = hopper("1") + + # this should not crash + im_tk = ImageTk.BitmapImage(im) + + assert im_tk.width() == im.width + assert im_tk.height() == im.height + + # reloaded = ImageTk.getimage(im_tk) + # assert_image_equal(reloaded, im) diff --git a/Tests/test_map.py b/Tests/test_map.py index bdc3a7e2c..dffd18843 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -1,38 +1,39 @@ import sys -import unittest +import pytest from PIL import Image -from .helper import PillowTestCase, is_win32 +from .helper import is_win32 try: import numpy except ImportError: numpy = None +pytestmark = pytest.mark.skipif(is_win32(), reason="Win32 does not call map_buffer") -@unittest.skipIf(is_win32(), "Win32 does not call map_buffer") -class TestMap(PillowTestCase): - def test_overflow(self): - # There is the potential to overflow comparisons in map.c - # if there are > SIZE_MAX bytes in the image or if - # the file encodes an offset that makes - # (offset + size(bytes)) > SIZE_MAX - # Note that this image triggers the decompression bomb warning: - max_pixels = Image.MAX_IMAGE_PIXELS - Image.MAX_IMAGE_PIXELS = None +def test_overflow(): + # There is the potential to overflow comparisons in map.c + # if there are > SIZE_MAX bytes in the image or if + # the file encodes an offset that makes + # (offset + size(bytes)) > SIZE_MAX - # This image hits the offset test. - with Image.open("Tests/images/l2rgb_read.bmp") as im: - with self.assertRaises((ValueError, MemoryError, IOError)): - im.load() + # Note that this image triggers the decompression bomb warning: + max_pixels = Image.MAX_IMAGE_PIXELS + Image.MAX_IMAGE_PIXELS = None - Image.MAX_IMAGE_PIXELS = max_pixels + # This image hits the offset test. + with Image.open("Tests/images/l2rgb_read.bmp") as im: + with pytest.raises((ValueError, MemoryError, IOError)): + im.load() - @unittest.skipIf(sys.maxsize <= 2 ** 32, "requires 64-bit system") - @unittest.skipIf(numpy is None, "Numpy is not installed") - def test_ysize(self): - # Should not raise 'Integer overflow in ysize' - arr = numpy.zeros((46341, 46341), dtype=numpy.uint8) - Image.fromarray(arr) + Image.MAX_IMAGE_PIXELS = max_pixels + + +@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system") +@pytest.mark.skipif(numpy is None, reason="NumPy is not installed") +def test_ysize(): + # Should not raise 'Integer overflow in ysize' + arr = numpy.zeros((46341, 46341), dtype=numpy.uint8) + Image.fromarray(arr) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 45fabee78..f758ad91a 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,9 +1,7 @@ -import unittest - import pytest from PIL import Image -from .helper import PillowTestCase, assert_deep_equal, assert_image, hopper +from .helper import assert_deep_equal, assert_image, hopper try: import numpy @@ -14,211 +12,227 @@ except ImportError: TEST_IMAGE_SIZE = (10, 10) -@unittest.skipIf(numpy is None, "Numpy is not installed") -class TestNumpy(PillowTestCase): - def test_numpy_to_image(self): - def to_image(dtype, bands=1, boolean=0): - if bands == 1: - if boolean: - data = [0, 255] * 50 - else: - data = list(range(100)) - a = numpy.array(data, dtype=dtype) - a.shape = TEST_IMAGE_SIZE - i = Image.fromarray(a) - if list(i.getdata()) != data: - print("data mismatch for", dtype) +pytestmark = pytest.mark.skipif(numpy is None, reason="NumPy is not installed") + + +def test_numpy_to_image(): + def to_image(dtype, bands=1, boolean=0): + if bands == 1: + if boolean: + data = [0, 255] * 50 else: data = list(range(100)) - a = numpy.array([[x] * bands for x in data], dtype=dtype) - a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands - i = Image.fromarray(a) - if list(i.getchannel(0).getdata()) != list(range(100)): - print("data mismatch for", dtype) - return i - - # Check supported 1-bit integer formats - assert_image(to_image(numpy.bool, 1, 1), "1", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.bool8, 1, 1), "1", TEST_IMAGE_SIZE) - - # Check supported 8-bit integer formats - assert_image(to_image(numpy.uint8), "L", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.uint8, 3), "RGB", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.uint8, 4), "RGBA", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.int8), "I", TEST_IMAGE_SIZE) - - # Check non-fixed-size integer types - # These may fail, depending on the platform, since we have no native - # 64 bit int image types. - # assert_image(to_image(numpy.uint), "I", TEST_IMAGE_SIZE) - # assert_image(to_image(numpy.int), "I", TEST_IMAGE_SIZE) - - # Check 16-bit integer formats - if Image._ENDIAN == "<": - assert_image(to_image(numpy.uint16), "I;16", TEST_IMAGE_SIZE) + a = numpy.array(data, dtype=dtype) + a.shape = TEST_IMAGE_SIZE + i = Image.fromarray(a) + if list(i.getdata()) != data: + print("data mismatch for", dtype) else: - assert_image(to_image(numpy.uint16), "I;16B", TEST_IMAGE_SIZE) + data = list(range(100)) + a = numpy.array([[x] * bands for x in data], dtype=dtype) + a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands + i = Image.fromarray(a) + if list(i.getchannel(0).getdata()) != list(range(100)): + print("data mismatch for", dtype) + return i - assert_image(to_image(numpy.int16), "I", TEST_IMAGE_SIZE) + # Check supported 1-bit integer formats + assert_image(to_image(numpy.bool, 1, 1), "1", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.bool8, 1, 1), "1", TEST_IMAGE_SIZE) - # Check 32-bit integer formats - assert_image(to_image(numpy.uint32), "I", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE) + # Check supported 8-bit integer formats + assert_image(to_image(numpy.uint8), "L", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.uint8, 3), "RGB", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.uint8, 4), "RGBA", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.int8), "I", TEST_IMAGE_SIZE) - # Check 64-bit integer formats - self.assertRaises(TypeError, to_image, numpy.uint64) - self.assertRaises(TypeError, to_image, numpy.int64) + # Check non-fixed-size integer types + # These may fail, depending on the platform, since we have no native + # 64-bit int image types. + # assert_image(to_image(numpy.uint), "I", TEST_IMAGE_SIZE) + # assert_image(to_image(numpy.int), "I", TEST_IMAGE_SIZE) - # Check floating-point formats - assert_image(to_image(numpy.float), "F", TEST_IMAGE_SIZE) - self.assertRaises(TypeError, to_image, numpy.float16) - assert_image(to_image(numpy.float32), "F", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.float64), "F", TEST_IMAGE_SIZE) + # Check 16-bit integer formats + if Image._ENDIAN == "<": + assert_image(to_image(numpy.uint16), "I;16", TEST_IMAGE_SIZE) + else: + assert_image(to_image(numpy.uint16), "I;16B", TEST_IMAGE_SIZE) - assert_image(to_image(numpy.uint8, 2), "LA", (10, 10)) - assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) - assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) + assert_image(to_image(numpy.int16), "I", TEST_IMAGE_SIZE) - # based on an erring example at - # https://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function - def test_3d_array(self): - size = (5, TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1]) - a = numpy.ones(size, dtype=numpy.uint8) - assert_image(Image.fromarray(a[1, :, :]), "L", TEST_IMAGE_SIZE) - size = (TEST_IMAGE_SIZE[0], 5, TEST_IMAGE_SIZE[1]) - a = numpy.ones(size, dtype=numpy.uint8) - assert_image(Image.fromarray(a[:, 1, :]), "L", TEST_IMAGE_SIZE) - size = (TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], 5) - a = numpy.ones(size, dtype=numpy.uint8) - assert_image(Image.fromarray(a[:, :, 1]), "L", TEST_IMAGE_SIZE) + # Check 32-bit integer formats + assert_image(to_image(numpy.uint32), "I", TEST_IMAGE_SIZE) + assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE) - def _test_img_equals_nparray(self, img, np): - self.assertGreaterEqual(len(np.shape), 2) - np_size = np.shape[1], np.shape[0] - self.assertEqual(img.size, np_size) - px = img.load() - for x in range(0, img.size[0], int(img.size[0] / 10)): - for y in range(0, img.size[1], int(img.size[1] / 10)): - assert_deep_equal(px[x, y], np[y, x]) + # Check 64-bit integer formats + with pytest.raises(TypeError): + to_image(numpy.uint64) + with pytest.raises(TypeError): + to_image(numpy.int64) - def test_16bit(self): - with Image.open("Tests/images/16bit.cropped.tif") as img: - np_img = numpy.array(img) - self._test_img_equals_nparray(img, np_img) - self.assertEqual(np_img.dtype, numpy.dtype("= 2 + np_size = np.shape[1], np.shape[0] + assert img.size == np_size + px = img.load() + for x in range(0, img.size[0], int(img.size[0] / 10)): + for y in range(0, img.size[1], int(img.size[1] / 10)): + assert_deep_equal(px[x, y], np[y, x]) - np_img = numpy.array(img) - self._test_img_equals_nparray(img, np_img) - self.assertEqual(np_img.dtype, dtype) - modes = [ - ("L", numpy.uint8), - ("I", numpy.int32), - ("F", numpy.float32), - ("LA", numpy.uint8), - ("RGB", numpy.uint8), - ("RGBA", numpy.uint8), - ("RGBX", numpy.uint8), - ("CMYK", numpy.uint8), - ("YCbCr", numpy.uint8), - ("I;16", "u2"), - ("I;16L", "u2"), + ("I;16L", "", 0), (b"\x90\x1F\xA3", 8)) - self.assertEqual( - PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), (b"\x90\x1F\xA0", 17) - ) - self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5)) - self.assertEqual( - PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), (b"asd(qwe)zxc", 13) - ) - self.assertEqual( - PdfParser.get_value(b"(Two \\\nwords.)", 0), (b"Two words.", 14) - ) - self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), (b"Two\nlines.", 12)) - self.assertEqual( - PdfParser.get_value(b"(Two\r\nlines.)", 0), (b"Two\nlines.", 13) - ) - self.assertEqual( - PdfParser.get_value(b"(Two\\nlines.)", 0), (b"Two\nlines.", 13) - ) - self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), (b"One(paren", 12)) - self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), (b"One)paren", 12)) - self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7)) - self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6)) - self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5)) - self.assertEqual(PdfParser.get_value(b"(\\53a)", 0), (b"\x2Ba", 6)) - self.assertEqual(PdfParser.get_value(b"(\\1111)", 0), (b"\x491", 7)) - self.assertEqual(PdfParser.get_value(b" 123 (", 0), (123, 4)) - self.assertAlmostEqual(PdfParser.get_value(b" 123.4 %", 0)[0], 123.4) - self.assertEqual(PdfParser.get_value(b" 123.4 %", 0)[1], 6) - self.assertRaises(PdfFormatError, PdfParser.get_value, b"]", 0) - d = PdfParser.get_value(b"<>", 0)[0] - self.assertIsInstance(d, PdfDict) - self.assertEqual(len(d), 2) - self.assertEqual(d.Name, "value") - self.assertEqual(d[b"Name"], b"value") - self.assertEqual(d.N, PdfName("V")) - a = PdfParser.get_value(b"[/Name (value) /N /V]", 0)[0] - self.assertIsInstance(a, list) - self.assertEqual(len(a), 4) - self.assertEqual(a[0], PdfName("Name")) - s = PdfParser.get_value( - b"<>\nstream\nabcde\nendstream<<...", 0 - )[0] - self.assertIsInstance(s, PdfStream) - self.assertEqual(s.dictionary.Name, "value") - self.assertEqual(s.decode(), b"abcde") - for name in ["CreationDate", "ModDate"]: - for date, value in { - b"20180729214124": "20180729214124", - b"D:20180729214124": "20180729214124", - b"D:2018072921": "20180729210000", - b"D:20180729214124Z": "20180729214124", - b"D:20180729214124+08'00'": "20180729134124", - b"D:20180729214124-05'00'": "20180730024124", - }.items(): - d = PdfParser.get_value( - b"<>", 0 - )[0] - self.assertEqual(time.strftime("%Y%m%d%H%M%S", getattr(d, name)), value) +def test_parsing(): + assert PdfParser.interpret_name(b"Name#23Hash") == b"Name#Hash" + assert PdfParser.interpret_name(b"Name#23Hash", as_text=True) == "Name#Hash" + assert PdfParser.get_value(b"1 2 R ", 0) == (IndirectReference(1, 2), 5) + assert PdfParser.get_value(b"true[", 0) == (True, 4) + assert PdfParser.get_value(b"false%", 0) == (False, 5) + assert PdfParser.get_value(b"null<", 0) == (None, 4) + assert PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0) == (123, 15) + assert PdfParser.get_value(b"<901FA3>", 0) == (b"\x90\x1F\xA3", 8) + assert PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3) == (b"\x90\x1F\xA0", 17) + assert PdfParser.get_value(b"(asd)", 0) == (b"asd", 5) + assert PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0) == (b"asd(qwe)zxc", 13) + assert PdfParser.get_value(b"(Two \\\nwords.)", 0) == (b"Two words.", 14) + assert PdfParser.get_value(b"(Two\nlines.)", 0) == (b"Two\nlines.", 12) + assert PdfParser.get_value(b"(Two\r\nlines.)", 0) == (b"Two\nlines.", 13) + assert PdfParser.get_value(b"(Two\\nlines.)", 0) == (b"Two\nlines.", 13) + assert PdfParser.get_value(b"(One\\(paren).", 0) == (b"One(paren", 12) + assert PdfParser.get_value(b"(One\\)paren).", 0) == (b"One)paren", 12) + assert PdfParser.get_value(b"(\\0053)", 0) == (b"\x053", 7) + assert PdfParser.get_value(b"(\\053)", 0) == (b"\x2B", 6) + assert PdfParser.get_value(b"(\\53)", 0) == (b"\x2B", 5) + assert PdfParser.get_value(b"(\\53a)", 0) == (b"\x2Ba", 6) + assert PdfParser.get_value(b"(\\1111)", 0) == (b"\x491", 7) + assert PdfParser.get_value(b" 123 (", 0) == (123, 4) + assert round(abs(PdfParser.get_value(b" 123.4 %", 0)[0] - 123.4), 7) == 0 + assert PdfParser.get_value(b" 123.4 %", 0)[1] == 6 + with pytest.raises(PdfFormatError): + PdfParser.get_value(b"]", 0) + d = PdfParser.get_value(b"<>", 0)[0] + assert isinstance(d, PdfDict) + assert len(d) == 2 + assert d.Name == "value" + assert d[b"Name"] == b"value" + assert d.N == PdfName("V") + a = PdfParser.get_value(b"[/Name (value) /N /V]", 0)[0] + assert isinstance(a, list) + assert len(a) == 4 + assert a[0] == PdfName("Name") + s = PdfParser.get_value( + b"<>\nstream\nabcde\nendstream<<...", 0 + )[0] + assert isinstance(s, PdfStream) + assert s.dictionary.Name == "value" + assert s.decode() == b"abcde" + for name in ["CreationDate", "ModDate"]: + for date, value in { + b"20180729214124": "20180729214124", + b"D:20180729214124": "20180729214124", + b"D:2018072921": "20180729210000", + b"D:20180729214124Z": "20180729214124", + b"D:20180729214124+08'00'": "20180729134124", + b"D:20180729214124-05'00'": "20180730024124", + }.items(): + d = PdfParser.get_value(b"<>", 0)[ + 0 + ] + assert time.strftime("%Y%m%d%H%M%S", getattr(d, name)) == value - def test_pdf_repr(self): - self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") - self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash") - self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual( - bytes(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" - ) - self.assertEqual( - bytes(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" - ) - self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual( - pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj" - ) - self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash") - self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual( - pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), - b"<<\n/Name 1 2 R\n>>", - ) - self.assertEqual( - pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" - ) - self.assertEqual(pdf_repr(123), b"123") - self.assertEqual(pdf_repr(True), b"true") - self.assertEqual(pdf_repr(False), b"false") - self.assertEqual(pdf_repr(None), b"null") - self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)") - self.assertEqual( - pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]" - ) - self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>") + +def test_pdf_repr(): + assert bytes(IndirectReference(1, 2)) == b"1 2 R" + assert bytes(IndirectObjectDef(*IndirectReference(1, 2))) == b"1 2 obj" + assert bytes(PdfName(b"Name#Hash")) == b"/Name#23Hash" + assert bytes(PdfName("Name#Hash")) == b"/Name#23Hash" + assert bytes(PdfDict({b"Name": IndirectReference(1, 2)})) == b"<<\n/Name 1 2 R\n>>" + assert bytes(PdfDict({"Name": IndirectReference(1, 2)})) == b"<<\n/Name 1 2 R\n>>" + assert pdf_repr(IndirectReference(1, 2)) == b"1 2 R" + assert pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))) == b"1 2 obj" + assert pdf_repr(PdfName(b"Name#Hash")) == b"/Name#23Hash" + assert pdf_repr(PdfName("Name#Hash")) == b"/Name#23Hash" + assert ( + pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})) == b"<<\n/Name 1 2 R\n>>" + ) + assert ( + pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})) == b"<<\n/Name 1 2 R\n>>" + ) + assert pdf_repr(123) == b"123" + assert pdf_repr(True) == b"true" + assert pdf_repr(False) == b"false" + assert pdf_repr(None) == b"null" + assert pdf_repr(b"a)/b\\(c") == br"(a\)/b\\\(c)" + assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]" + assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"