Merge branch 'master' into apng

This commit is contained in:
Andrew Murray 2020-03-09 23:29:40 +11:00 committed by GitHub
commit 9f61be4c72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1981 additions and 1810 deletions

View File

@ -5,6 +5,21 @@ Changelog (Pillow)
7.1.0 (unreleased)
------------------
- Add JPEG comment to info dictionary #4455
[radarhere]
- Fix size calculation of Image.thumbnail() #4404
[orlnub123]
- Fixed stroke on FreeType < 2.9 #4401
[radarhere]
- If present, only use alpha channel for bounding box #4454
[radarhere]
- Warn if an unknown feature is passed to features.check() #4438
[jdufresne]
- Fix Name field length when saving IM images #4424
[hugovk, radarhere]

View File

@ -5,7 +5,6 @@ Helper functions.
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
@ -192,21 +191,9 @@ class PillowTestCase(unittest.TestCase):
self.addCleanup(self.delete_tempfile, path)
return path
def open_withImagemagick(self, f):
if not imagemagick_available():
raise OSError()
outfile = self.tempfile("temp.png")
rc = subprocess.call(
[IMCONVERT, f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
if rc:
raise OSError
return Image.open(outfile)
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
class PillowLeakTestCase(PillowTestCase):
@pytest.mark.skipif(sys.platform.startswith("win32"), reason="Requires Unix or macOS")
class PillowLeakTestCase:
# requires unix/macOS
iterations = 100 # count
mem_limit = 512 # k

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -3,109 +3,108 @@ import os
import pytest
from PIL import Image
from .helper import PillowTestCase, assert_image_similar
from .helper import assert_image_similar
base = os.path.join("Tests", "images", "bmp")
class TestBmpReference(PillowTestCase):
def get_files(self, d, ext=".bmp"):
return [
os.path.join(base, d, f)
for f in os.listdir(os.path.join(base, d))
if ext in f
]
def get_files(d, ext=".bmp"):
return [
os.path.join(base, d, f) for f in os.listdir(os.path.join(base, d)) if ext in f
]
def test_bad(self):
""" These shouldn't crash/dos, but they shouldn't return anything
either """
for f in self.get_files("b"):
def open(f):
try:
with Image.open(f) as im:
im.load()
except Exception: # as msg:
pass
def test_bad():
""" These shouldn't crash/dos, but they shouldn't return anything
either """
for f in get_files("b"):
# Assert that there is no unclosed file warning
pytest.warns(None, open, f)
def test_questionable(self):
""" These shouldn't crash/dos, but it's not well defined that these
are in spec """
supported = [
"pal8os2v2.bmp",
"rgb24prof.bmp",
"pal1p1.bmp",
"pal8offs.bmp",
"rgb24lprof.bmp",
"rgb32fakealpha.bmp",
"rgb24largepal.bmp",
"pal8os2sp.bmp",
"rgb32bf-xbgr.bmp",
]
for f in self.get_files("q"):
def open(f):
try:
with Image.open(f) as im:
im.load()
if os.path.basename(f) not in supported:
print("Please add %s to the partially supported bmp specs." % f)
except Exception: # as msg:
if os.path.basename(f) in supported:
raise
pass
def test_good(self):
""" These should all work. There's a set of target files in the
html directory that we can compare against. """
# Assert that there is no unclosed file warning
pytest.warns(None, open, f)
# Target files, if they're not just replacing the extension
file_map = {
"pal1wb.bmp": "pal1.png",
"pal4rle.bmp": "pal4.png",
"pal8-0.bmp": "pal8.png",
"pal8rle.bmp": "pal8.png",
"pal8topdown.bmp": "pal8.png",
"pal8nonsquare.bmp": "pal8nonsquare-v.png",
"pal8os2.bmp": "pal8.png",
"pal8os2sp.bmp": "pal8.png",
"pal8os2v2.bmp": "pal8.png",
"pal8os2v2-16.bmp": "pal8.png",
"pal8v4.bmp": "pal8.png",
"pal8v5.bmp": "pal8.png",
"rgb16-565pal.bmp": "rgb16-565.png",
"rgb24pal.bmp": "rgb24.png",
"rgb32.bmp": "rgb24.png",
"rgb32bf.bmp": "rgb24.png",
}
def get_compare(f):
name = os.path.split(f)[1]
if name in file_map:
return os.path.join(base, "html", file_map[name])
name = os.path.splitext(name)[0]
return os.path.join(base, "html", "%s.png" % name)
def test_questionable():
""" These shouldn't crash/dos, but it's not well defined that these
are in spec """
supported = [
"pal8os2v2.bmp",
"rgb24prof.bmp",
"pal1p1.bmp",
"pal8offs.bmp",
"rgb24lprof.bmp",
"rgb32fakealpha.bmp",
"rgb24largepal.bmp",
"pal8os2sp.bmp",
"rgb32bf-xbgr.bmp",
]
for f in get_files("q"):
try:
with Image.open(f) as im:
im.load()
if os.path.basename(f) not in supported:
print("Please add %s to the partially supported bmp specs." % f)
except Exception: # as msg:
if os.path.basename(f) in supported:
raise
for f in self.get_files("g"):
try:
with Image.open(f) as im:
im.load()
with Image.open(get_compare(f)) as compare:
compare.load()
if im.mode == "P":
# assert image similar doesn't really work
# with paletized image, since the palette might
# be differently ordered for an equivalent image.
im = im.convert("RGBA")
compare = im.convert("RGBA")
assert_image_similar(im, compare, 5)
except Exception as msg:
# there are three here that are unsupported:
unsupported = (
os.path.join(base, "g", "rgb32bf.bmp"),
os.path.join(base, "g", "pal8rle.bmp"),
os.path.join(base, "g", "pal4rle.bmp"),
)
if f not in unsupported:
self.fail("Unsupported Image {}: {}".format(f, msg))
def test_good():
""" These should all work. There's a set of target files in the
html directory that we can compare against. """
# Target files, if they're not just replacing the extension
file_map = {
"pal1wb.bmp": "pal1.png",
"pal4rle.bmp": "pal4.png",
"pal8-0.bmp": "pal8.png",
"pal8rle.bmp": "pal8.png",
"pal8topdown.bmp": "pal8.png",
"pal8nonsquare.bmp": "pal8nonsquare-v.png",
"pal8os2.bmp": "pal8.png",
"pal8os2sp.bmp": "pal8.png",
"pal8os2v2.bmp": "pal8.png",
"pal8os2v2-16.bmp": "pal8.png",
"pal8v4.bmp": "pal8.png",
"pal8v5.bmp": "pal8.png",
"rgb16-565pal.bmp": "rgb16-565.png",
"rgb24pal.bmp": "rgb24.png",
"rgb32.bmp": "rgb24.png",
"rgb32bf.bmp": "rgb24.png",
}
def get_compare(f):
name = os.path.split(f)[1]
if name in file_map:
return os.path.join(base, "html", file_map[name])
name = os.path.splitext(name)[0]
return os.path.join(base, "html", "%s.png" % name)
for f in get_files("g"):
try:
with Image.open(f) as im:
im.load()
with Image.open(get_compare(f)) as compare:
compare.load()
if im.mode == "P":
# assert image similar doesn't really work
# with paletized image, since the palette might
# be differently ordered for an equivalent image.
im = im.convert("RGBA")
compare = im.convert("RGBA")
assert_image_similar(im, compare, 5)
except Exception as msg:
# there are three here that are unsupported:
unsupported = (
os.path.join(base, "g", "rgb32bf.bmp"),
os.path.join(base, "g", "pal8rle.bmp"),
os.path.join(base, "g", "pal4rle.bmp"),
)
assert f in unsupported, "Unsupported Image {}: {}".format(f, msg)

View File

@ -1,10 +1,9 @@
import sys
import unittest
import pytest
from PIL import Image
from .helper import PillowTestCase, is_pypy
from .helper import is_pypy
def test_get_stats():
@ -32,8 +31,8 @@ def test_reset_stats():
assert stats["blocks_cached"] == 0
class TestCoreMemory(PillowTestCase):
def tearDown(self):
class TestCoreMemory:
def teardown_method(self):
# Restore default values
Image.core.set_alignment(1)
Image.core.set_block_size(1024 * 1024)
@ -114,7 +113,7 @@ class TestCoreMemory(PillowTestCase):
with pytest.raises(ValueError):
Image.core.set_blocks_max(2 ** 29)
@unittest.skipIf(is_pypy(), "images are not collected")
@pytest.mark.skipif(is_pypy(), reason="Images not collected")
def test_set_blocks_max_stats(self):
Image.core.reset_stats()
Image.core.set_blocks_max(128)
@ -129,7 +128,7 @@ class TestCoreMemory(PillowTestCase):
assert stats["freed_blocks"] == 0
assert stats["blocks_cached"] == 64
@unittest.skipIf(is_pypy(), "images are not collected")
@pytest.mark.skipif(is_pypy(), reason="Images not collected")
def test_clear_cache_stats(self):
Image.core.reset_stats()
Image.core.clear_cache()
@ -163,8 +162,8 @@ class TestCoreMemory(PillowTestCase):
assert stats["freed_blocks"] >= 16
class TestEnvVars(PillowTestCase):
def tearDown(self):
class TestEnvVars:
def teardown_method(self):
# Restore default values
Image.core.set_alignment(1)
Image.core.set_block_size(1024 * 1024)

View File

@ -44,6 +44,13 @@ def test_check_modules():
assert features.check_codec(feature) in [True, False]
def test_check_warns_on_nonexistent():
with pytest.warns(UserWarning) as cm:
has_feature = features.check("typo")
assert has_feature is False
assert str(cm[-1].message) == "Unknown feature 'typo'."
def test_supported_modules():
assert isinstance(features.get_supported_modules(), list)
assert isinstance(features.get_supported_codecs(), list)

View File

@ -1,20 +1,21 @@
from PIL import Image
from .helper import PillowTestCase, assert_image_equal
from .helper import assert_image_equal
class TestFileBlp(PillowTestCase):
def test_load_blp2_raw(self):
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
with Image.open("Tests/images/blp/blp2_raw.png") as target:
assert_image_equal(im, target)
def test_load_blp2_raw():
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
with Image.open("Tests/images/blp/blp2_raw.png") as target:
assert_image_equal(im, target)
def test_load_blp2_dxt1(self):
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
assert_image_equal(im, target)
def test_load_blp2_dxt1a(self):
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
with Image.open("Tests/images/blp/blp2_dxt1a.png") as target:
assert_image_equal(im, target)
def test_load_blp2_dxt1():
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
assert_image_equal(im, target)
def test_load_blp2_dxt1a():
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
with Image.open("Tests/images/blp/blp2_dxt1a.png") as target:
assert_image_equal(im, target)

View File

@ -1,15 +1,15 @@
from PIL import Image
from .helper import PillowTestCase, assert_image_equal, assert_image_similar
from .helper import assert_image_equal, assert_image_similar
class TestFileFtex(PillowTestCase):
def test_load_raw(self):
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
with Image.open("Tests/images/ftex_uncompressed.png") as target:
assert_image_equal(im, target)
def test_load_raw():
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
with Image.open("Tests/images/ftex_uncompressed.png") as target:
assert_image_equal(im, target)
def test_load_dxt1(self):
with Image.open("Tests/images/ftex_dxt1.ftc") as im:
with Image.open("Tests/images/ftex_dxt1.png") as target:
assert_image_similar(im, target.convert("RGBA"), 15)
def test_load_dxt1():
with Image.open("Tests/images/ftex_dxt1.ftc") as im:
with Image.open("Tests/images/ftex_dxt1.png") as target:
assert_image_similar(im, target.convert("RGBA"), 15)

View File

@ -6,7 +6,6 @@ import pytest
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin
from .helper import (
PillowTestCase,
assert_image,
assert_image_equal,
assert_image_similar,
@ -15,14 +14,13 @@ from .helper import (
hopper,
is_win32,
skip_unless_feature,
unittest,
)
TEST_FILE = "Tests/images/hopper.jpg"
@skip_unless_feature("jpg")
class TestFileJpeg(PillowTestCase):
class TestFileJpeg:
def roundtrip(self, im, **options):
out = BytesIO()
im.save(out, "JPEG", **options)
@ -62,6 +60,8 @@ class TestFileJpeg(PillowTestCase):
)
assert len(im.applist) == 2
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0\x00"
def test_cmyk(self):
# Test CMYK handling. Thanks to Tim and Charlie for test data,
# Michael for getting me to look one more time.
@ -101,13 +101,13 @@ class TestFileJpeg(PillowTestCase):
assert test(100, 200) == (100, 200)
assert test(0) is None # square pixels
def test_icc(self):
def test_icc(self, tmp_path):
# Test ICC support
with Image.open("Tests/images/rgb.jpg") as im1:
icc_profile = im1.info["icc_profile"]
assert len(icc_profile) == 3144
# Roundtrip via physical file.
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
im1.save(f, icc_profile=icc_profile)
with Image.open(f) as im2:
assert im2.info.get("icc_profile") == icc_profile
@ -140,12 +140,12 @@ class TestFileJpeg(PillowTestCase):
test(ImageFile.MAXBLOCK + 1) # full buffer block plus one byte
test(ImageFile.MAXBLOCK * 4 + 3) # large block
def test_large_icc_meta(self):
def test_large_icc_meta(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148
# Sometimes the meta data on the icc_profile block is bigger than
# Image.MAXBLOCK or the image size.
with Image.open("Tests/images/icc_profile_big.jpg") as im:
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
icc_profile = im.info["icc_profile"]
# Should not raise IOError for image with icc larger than image size.
im.save(
@ -166,9 +166,9 @@ class TestFileJpeg(PillowTestCase):
assert im1.bytes >= im2.bytes
assert im1.bytes >= im3.bytes
def test_optimize_large_buffer(self):
def test_optimize_large_buffer(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xFF3333)
im.save(f, format="JPEG", optimize=True)
@ -184,14 +184,14 @@ class TestFileJpeg(PillowTestCase):
assert_image_equal(im1, im3)
assert im1.bytes >= im3.bytes
def test_progressive_large_buffer(self):
f = self.tempfile("temp.jpg")
def test_progressive_large_buffer(self, tmp_path):
f = str(tmp_path / "temp.jpg")
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096, 4096), 0xFF3333)
im.save(f, format="JPEG", progressive=True)
def test_progressive_large_buffer_highest_quality(self):
f = self.tempfile("temp.jpg")
def test_progressive_large_buffer_highest_quality(self, tmp_path):
f = str(tmp_path / "temp.jpg")
im = self.gen_random_image((255, 255))
# this requires more bytes than pixels in the image
im.save(f, format="JPEG", progressive=True, quality=100)
@ -202,9 +202,9 @@ class TestFileJpeg(PillowTestCase):
im = self.gen_random_image((256, 256), "CMYK")
im.save(f, format="JPEG", progressive=True, quality=94)
def test_large_exif(self):
def test_large_exif(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/148
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
im = hopper()
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
@ -301,7 +301,7 @@ class TestFileJpeg(PillowTestCase):
im3 = self.roundtrip(hopper(), quality=0)
assert_image(im1, im3.mode, im3.size)
self.assertGreater(im2.bytes, im3.bytes)
assert im2.bytes > im3.bytes
def test_smooth(self):
im1 = self.roundtrip(hopper())
@ -346,18 +346,18 @@ class TestFileJpeg(PillowTestCase):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
assert im._getmp() is None
def test_quality_keep(self):
def test_quality_keep(self, tmp_path):
# RGB
with Image.open("Tests/images/hopper.jpg") as im:
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep")
# Grayscale
with Image.open("Tests/images/hopper_gray.jpg") as im:
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep")
# CMYK
with Image.open("Tests/images/pil_sample_cmyk.jpg") as im:
f = self.tempfile("temp.jpg")
f = str(tmp_path / "temp.jpg")
im.save(f, quality="keep")
def test_junk_jpeg_header(self):
@ -389,16 +389,16 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(IOError):
im.load()
def _n_qtables_helper(self, n, test_file):
with Image.open(test_file) as im:
f = self.tempfile("temp.jpg")
im.save(f, qtables=[[n] * 64] * n)
with Image.open(f) as im:
assert len(im.quantization) == n
reloaded = self.roundtrip(im, qtables="keep")
assert im.quantization == reloaded.quantization
def test_qtables(self, tmp_path):
def _n_qtables_helper(n, test_file):
with Image.open(test_file) as im:
f = str(tmp_path / "temp.jpg")
im.save(f, qtables=[[n] * 64] * n)
with Image.open(f) as im:
assert len(im.quantization) == n
reloaded = self.roundtrip(im, qtables="keep")
assert im.quantization == reloaded.quantization
def test_qtables(self):
with Image.open("Tests/images/hopper.jpg") as im:
qtables = im.quantization
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
@ -470,14 +470,14 @@ class TestFileJpeg(PillowTestCase):
30,
)
self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
_n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
_n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
_n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
_n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
_n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
_n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
_n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
_n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
# not a sequence
with pytest.raises(ValueError):
@ -496,16 +496,16 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(ValueError):
self.roundtrip(im, qtables=[[1, 2, 3, 4]])
@unittest.skipUnless(djpeg_available(), "djpeg not available")
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self):
with Image.open(TEST_FILE) as img:
img.load_djpeg()
assert_image_similar(img, Image.open(TEST_FILE), 0)
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg(self):
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
def test_save_cjpeg(self, tmp_path):
with Image.open(TEST_FILE) as img:
tempfile = self.tempfile("temp.jpg")
tempfile = str(tmp_path / "temp.jpg")
JpegImagePlugin._save_cjpeg(img, 0, tempfile)
# Default save quality is 75%, so a tiny bit of difference is alright
assert_image_similar(img, Image.open(tempfile), 17)
@ -518,9 +518,9 @@ class TestFileJpeg(PillowTestCase):
assert tag_ids["RelatedImageWidth"] == 0x1001
assert tag_ids["RelatedImageLength"] == 0x1002
def test_MAXBLOCK_scaling(self):
def test_MAXBLOCK_scaling(self, tmp_path):
im = self.gen_random_image((512, 512))
f = self.tempfile("temp.jpeg")
f = str(tmp_path / "temp.jpeg")
im.save(f, quality=100, optimize=True)
with Image.open(f) as reloaded:
@ -555,9 +555,9 @@ class TestFileJpeg(PillowTestCase):
with pytest.raises(IOError):
img.save(out, "JPEG")
def test_save_tiff_with_dpi(self):
def test_save_tiff_with_dpi(self, tmp_path):
# Arrange
outfile = self.tempfile("temp.tif")
outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/hopper.tif") as im:
# Act
@ -577,8 +577,8 @@ class TestFileJpeg(PillowTestCase):
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
assert im.info["dpi"] == (2, 2)
def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.jpg")
def test_save_dpi_rounding(self, tmp_path):
outfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im:
im.save(outfile, dpi=(72.2, 72.2))
@ -690,11 +690,11 @@ class TestFileJpeg(PillowTestCase):
assert [65504, 24] == apps_13_lengths
@unittest.skipUnless(is_win32(), "Windows only")
@pytest.mark.skipif(not is_win32(), reason="Windows only")
@skip_unless_feature("jpg")
class TestFileCloseW32(PillowTestCase):
def test_fd_leak(self):
tmpfile = self.tempfile("temp.jpg")
class TestFileCloseW32:
def test_fd_leak(self, tmp_path):
tmpfile = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/hopper.jpg") as im:
im.save(tmpfile)

View File

@ -5,7 +5,6 @@ import pytest
from PIL import Image, ImageFile, Jpeg2KImagePlugin
from .helper import (
PillowTestCase,
assert_image_equal,
assert_image_similar,
is_big_endian,
@ -13,6 +12,8 @@ from .helper import (
skip_unless_feature,
)
pytestmark = skip_unless_feature("jpg_2000")
test_card = Image.open("Tests/images/test-card.png")
test_card.load()
@ -21,190 +22,207 @@ test_card.load()
# 'Not enough memory to handle tile data'
@skip_unless_feature("jpg_2000")
class TestFileJpeg2k(PillowTestCase):
def roundtrip(self, im, **options):
out = BytesIO()
im.save(out, "JPEG2000", **options)
test_bytes = out.tell()
out.seek(0)
im = Image.open(out)
im.bytes = test_bytes # for testing only
def roundtrip(im, **options):
out = BytesIO()
im.save(out, "JPEG2000", **options)
test_bytes = out.tell()
out.seek(0)
im = Image.open(out)
im.bytes = test_bytes # for testing only
im.load()
return im
def test_sanity():
# Internal version number
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
with Image.open("Tests/images/test-card-lossless.jp2") as im:
px = im.load()
assert px[0, 0] == (0, 0, 0)
assert im.mode == "RGB"
assert im.size == (640, 480)
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jp2"
def test_jpf():
with Image.open("Tests/images/balloon.jpf") as im:
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jpx"
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)
def test_bytesio():
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = BytesIO(f.read())
with Image.open(data) as im:
im.load()
return im
def test_sanity(self):
# Internal version number
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
with Image.open("Tests/images/test-card-lossless.jp2") as im:
px = im.load()
assert px[0, 0] == (0, 0, 0)
assert im.mode == "RGB"
assert im.size == (640, 480)
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jp2"
def test_jpf(self):
with Image.open("Tests/images/balloon.jpf") as im:
assert im.format == "JPEG2000"
assert im.get_format_mimetype() == "image/jpx"
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file)
def test_bytesio(self):
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = BytesIO(f.read())
with Image.open(data) as im:
im.load()
assert_image_similar(im, test_card, 1.0e-3)
# These two test pre-written JPEG 2000 files that were not written with
# PIL (they were made using Adobe Photoshop)
def test_lossless(self):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.load()
outfile = self.tempfile("temp_test-card.png")
im.save(outfile)
assert_image_similar(im, test_card, 1.0e-3)
def test_lossy_tiled(self):
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
im.load()
assert_image_similar(im, test_card, 2.0)
def test_lossless_rt(self):
im = self.roundtrip(test_card)
assert_image_equal(im, test_card)
# These two test pre-written JPEG 2000 files that were not written with
# PIL (they were made using Adobe Photoshop)
def test_lossy_rt(self):
im = self.roundtrip(test_card, quality_layers=[20])
def test_lossless(tmp_path):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.load()
outfile = str(tmp_path / "temp_test-card.png")
im.save(outfile)
assert_image_similar(im, test_card, 1.0e-3)
def test_lossy_tiled():
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
im.load()
assert_image_similar(im, test_card, 2.0)
def test_tiled_rt(self):
im = self.roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_offset_rt(self):
im = self.roundtrip(
test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32)
)
assert_image_equal(im, test_card)
def test_lossless_rt():
im = roundtrip(test_card)
assert_image_equal(im, test_card)
def test_tiled_offset_too_small(self):
def test_lossy_rt():
im = roundtrip(test_card, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_tiled_rt():
im = roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_offset_rt():
im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32))
assert_image_equal(im, test_card)
def test_tiled_offset_too_small():
with pytest.raises(ValueError):
roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32))
def test_irreversible_rt():
im = roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt():
im = roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, test_card, 2.0)
def test_prog_res_rt():
im = roundtrip(test_card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, test_card)
def test_reduce():
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.reduce = 2
im.load()
assert im.size == (160, 120)
def test_layers_type(tmp_path):
outfile = str(tmp_path / "temp_layers.jp2")
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers)
for quality_layers in ["quality_layers", ("100", "50", "10")]:
with pytest.raises(ValueError):
self.roundtrip(
test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(128, 32)
)
def test_irreversible_rt(self):
im = self.roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt(self):
im = self.roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP")
assert_image_similar(im, test_card, 2.0)
def test_prog_res_rt(self):
im = self.roundtrip(test_card, num_resolutions=8, progression="RLCP")
assert_image_equal(im, test_card)
def test_reduce(self):
with Image.open("Tests/images/test-card-lossless.jp2") as im:
im.reduce = 2
im.load()
assert im.size == (160, 120)
def test_layers_type(self):
outfile = self.tempfile("temp_layers.jp2")
for quality_layers in [[100, 50, 10], (100, 50, 10), None]:
test_card.save(outfile, quality_layers=quality_layers)
for quality_layers in ["quality_layers", ("100", "50", "10")]:
with pytest.raises(ValueError):
test_card.save(outfile, quality_layers=quality_layers)
def test_layers(self):
out = BytesIO()
test_card.save(
out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP"
)
out.seek(0)
def test_layers():
out = BytesIO()
test_card.save(out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP")
out.seek(0)
with Image.open(out) as im:
im.layers = 1
im.load()
assert_image_similar(im, test_card, 13)
with Image.open(out) as im:
im.layers = 1
im.load()
assert_image_similar(im, test_card, 13)
out.seek(0)
with Image.open(out) as im:
im.layers = 3
im.load()
assert_image_similar(im, test_card, 0.4)
out.seek(0)
with Image.open(out) as im:
im.layers = 3
im.load()
assert_image_similar(im, test_card, 0.4)
def test_rgba(self):
# Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
# Act
j2k.load()
jp2.load()
def test_rgba():
# Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
# Assert
assert j2k.mode == "RGBA"
assert jp2.mode == "RGBA"
def test_16bit_monochrome_has_correct_mode(self):
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
# Act
j2k.load()
assert j2k.mode == "I;16"
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
jp2.load()
assert jp2.mode == "I;16"
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_jp2_like_tiff(self):
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
assert_image_similar(jp2, tiff_16bit, 1e-3)
# Assert
assert j2k.mode == "RGBA"
assert jp2.mode == "RGBA"
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_j2k_like_tiff(self):
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
assert_image_similar(j2k, tiff_16bit, 1e-3)
def test_16bit_j2k_roundtrips(self):
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
im = self.roundtrip(j2k)
assert_image_equal(im, j2k)
def test_16bit_monochrome_has_correct_mode():
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
j2k.load()
assert j2k.mode == "I;16"
def test_16bit_jp2_roundtrips(self):
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
jp2.load()
assert jp2.mode == "I;16"
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_jp2_like_tiff():
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
im = self.roundtrip(jp2)
assert_image_equal(im, jp2)
assert_image_similar(jp2, tiff_16bit, 1e-3)
def test_unbound_local(self):
# prepatch, a malformed jp2 file could cause an UnboundLocalError
# exception.
with pytest.raises(IOError):
Image.open("Tests/images/unbound_variable.jp2")
def test_parser_feed(self):
# Arrange
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = f.read()
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_16bit_monochrome_j2k_like_tiff():
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
assert_image_similar(j2k, tiff_16bit, 1e-3)
# Act
p = ImageFile.Parser()
p.feed(data)
# Assert
assert p.image.size == (640, 480)
def test_16bit_j2k_roundtrips():
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
im = roundtrip(j2k)
assert_image_equal(im, j2k)
def test_16bit_jp2_roundtrips():
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
im = roundtrip(jp2)
assert_image_equal(im, jp2)
def test_unbound_local():
# prepatch, a malformed jp2 file could cause an UnboundLocalError exception.
with pytest.raises(IOError):
Image.open("Tests/images/unbound_variable.jp2")
def test_parser_feed():
# Arrange
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
data = f.read()
# Act
p = ImageFile.Parser()
p.feed(data)
# Assert
assert p.image.size == (640, 480)

View File

@ -10,7 +10,6 @@ import pytest
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags
from .helper import (
PillowTestCase,
assert_image_equal,
assert_image_equal_tofile,
assert_image_similar,
@ -23,8 +22,8 @@ logger = logging.getLogger(__name__)
@skip_unless_feature("libtiff")
class LibTiffTestCase(PillowTestCase):
def _assert_noerr(self, im):
class LibTiffTestCase:
def _assert_noerr(self, tmp_path, im):
"""Helper tests that assert basic sanity about the g4 tiff reading"""
# 1 bit
assert im.mode == "1"
@ -40,7 +39,7 @@ class LibTiffTestCase(PillowTestCase):
print(dir(im))
# can we write it back out, in a different form.
out = self.tempfile("temp.png")
out = str(tmp_path / "temp.png")
im.save(out)
out_bytes = io.BytesIO()
@ -48,29 +47,29 @@ class LibTiffTestCase(PillowTestCase):
class TestFileLibTiff(LibTiffTestCase):
def test_g4_tiff(self):
def test_g4_tiff(self, tmp_path):
"""Test the ordinary file path load path"""
test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as im:
assert im.size == (500, 500)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_large(self):
def test_g4_large(self, tmp_path):
test_file = "Tests/images/pport_g4.tif"
with Image.open(test_file) as im:
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_tiff_file(self):
def test_g4_tiff_file(self, tmp_path):
"""Testing the string load path"""
test_file = "Tests/images/hopper_g4_500.tif"
with open(test_file, "rb") as f:
with Image.open(f) as im:
assert im.size == (500, 500)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_tiff_bytesio(self):
def test_g4_tiff_bytesio(self, tmp_path):
"""Testing the stringio loading code path"""
test_file = "Tests/images/hopper_g4_500.tif"
s = io.BytesIO()
@ -79,9 +78,9 @@ class TestFileLibTiff(LibTiffTestCase):
s.seek(0)
with Image.open(s) as im:
assert im.size == (500, 500)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_non_disk_file_object(self):
def test_g4_non_disk_file_object(self, tmp_path):
"""Testing loading from non-disk non-BytesIO file object"""
test_file = "Tests/images/hopper_g4_500.tif"
s = io.BytesIO()
@ -91,7 +90,7 @@ class TestFileLibTiff(LibTiffTestCase):
r = io.BufferedReader(s)
with Image.open(r) as im:
assert im.size == (500, 500)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_eq_png(self):
""" Checking that we're actually getting the data that we expect"""
@ -106,18 +105,18 @@ class TestFileLibTiff(LibTiffTestCase):
with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
assert_image_equal(g4, png)
def test_g4_write(self):
def test_g4_write(self, tmp_path):
"""Checking to see that the saved image is the same as what we wrote"""
test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as orig:
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
rot = orig.transpose(Image.ROTATE_90)
assert rot.size == (500, 500)
rot.save(out)
with Image.open(out) as reread:
assert reread.size == (500, 500)
self._assert_noerr(reread)
self._assert_noerr(tmp_path, reread)
assert_image_equal(reread, rot)
assert reread.info["compression"] == "group4"
@ -135,10 +134,10 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_write_metadata(self):
def test_write_metadata(self, tmp_path):
""" Test metadata writing through libtiff """
for legacy_api in [False, True]:
f = self.tempfile("temp.tiff")
f = str(tmp_path / "temp.tiff")
with Image.open("Tests/images/hopper_g4.tif") as img:
img.save(f, tiffinfo=img.tag)
@ -183,7 +182,7 @@ class TestFileLibTiff(LibTiffTestCase):
for field in requested_fields:
assert field in reloaded, "%s not in metadata" % field
def test_additional_metadata(self):
def test_additional_metadata(self, tmp_path):
# these should not crash. Seriously dummy data, most of it doesn't make
# any sense, so we're running up against limits where we're asking
# libtiff to do stupid things.
@ -232,14 +231,14 @@ class TestFileLibTiff(LibTiffTestCase):
# Extra samples really doesn't make sense in this application.
del new_ifd[338]
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, tiffinfo=new_ifd)
TiffImagePlugin.WRITE_LIBTIFF = False
def test_custom_metadata(self):
def test_custom_metadata(self, tmp_path):
tc = namedtuple("test_case", "value,type,supported_by_default")
custom = {
37000 + k: v
@ -284,7 +283,7 @@ class TestFileLibTiff(LibTiffTestCase):
def check_tags(tiffinfo):
im = hopper()
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
im.save(out, tiffinfo=tiffinfo)
with Image.open(out) as reloaded:
@ -323,26 +322,26 @@ class TestFileLibTiff(LibTiffTestCase):
)
TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self):
def test_int_dpi(self, tmp_path):
# issue #1765
im = hopper("RGB")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, dpi=(72, 72))
TiffImagePlugin.WRITE_LIBTIFF = False
with Image.open(out) as reloaded:
assert reloaded.info["dpi"] == (72.0, 72.0)
def test_g3_compression(self):
def test_g3_compression(self, tmp_path):
with Image.open("Tests/images/hopper_g4_500.tif") as i:
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
i.save(out, compression="group3")
with Image.open(out) as reread:
assert reread.info["compression"] == "group3"
assert_image_equal(reread, i)
def test_little_endian(self):
def test_little_endian(self, tmp_path):
with Image.open("Tests/images/16bit.deflate.tif") as im:
assert im.getpixel((0, 0)) == 480
assert im.mode == "I;16"
@ -352,7 +351,7 @@ class TestFileLibTiff(LibTiffTestCase):
assert b[0] == ord(b"\xe0")
assert b[1] == ord(b"\x01")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
# out = "temp.le.tif"
im.save(out)
with Image.open(out) as reread:
@ -361,7 +360,7 @@ class TestFileLibTiff(LibTiffTestCase):
# UNDONE - libtiff defaults to writing in native endian, so
# on big endian, we'll get back mode = 'I;16B' here.
def test_big_endian(self):
def test_big_endian(self, tmp_path):
with Image.open("Tests/images/16bit.MM.deflate.tif") as im:
assert im.getpixel((0, 0)) == 480
assert im.mode == "I;16B"
@ -372,17 +371,17 @@ class TestFileLibTiff(LibTiffTestCase):
assert b[0] == ord(b"\x01")
assert b[1] == ord(b"\xe0")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
im.save(out)
with Image.open(out) as reread:
assert reread.info["compression"] == im.info["compression"]
assert reread.getpixel((0, 0)) == 480
def test_g4_string_info(self):
def test_g4_string_info(self, tmp_path):
"""Tests String data in info directory"""
test_file = "Tests/images/hopper_g4_500.tif"
with Image.open(test_file) as orig:
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
orig.tag[269] = "temp.tif"
orig.save(out)
@ -406,10 +405,10 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
def test_blur(self):
def test_blur(self, tmp_path):
# test case from irc, how to do blur on b/w image
# and save to compressed tif.
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
with Image.open("Tests/images/pport_g4.tif") as im:
im = im.convert("L")
@ -421,11 +420,11 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_equal(im, im2)
def test_compressions(self):
def test_compressions(self, tmp_path):
# Test various tiff compressions and assert similar image content but reduced
# file sizes.
im = hopper("RGB")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
im.save(out)
size_raw = os.path.getsize(out)
@ -449,9 +448,9 @@ class TestFileLibTiff(LibTiffTestCase):
assert size_compressed > size_jpeg
assert size_jpeg > size_jpeg_30
def test_quality(self):
def test_quality(self, tmp_path):
im = hopper("RGB")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
with pytest.raises(ValueError):
im.save(out, compression="tiff_lzw", quality=50)
@ -464,21 +463,21 @@ class TestFileLibTiff(LibTiffTestCase):
im.save(out, compression="jpeg", quality=0)
im.save(out, compression="jpeg", quality=100)
def test_cmyk_save(self):
def test_cmyk_save(self, tmp_path):
im = hopper("CMYK")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
im.save(out, compression="tiff_adobe_deflate")
with Image.open(out) as im2:
assert_image_equal(im, im2)
def xtest_bw_compression_w_rgb(self):
def xtest_bw_compression_w_rgb(self, tmp_path):
""" This test passes, but when running all tests causes a failure due
to output on stderr from the error thrown by libtiff. We need to
capture that but not now"""
im = hopper("RGB")
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
with pytest.raises(IOError):
im.save(out, compression="tiff_ccitt")
@ -619,22 +618,22 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False
TiffImagePlugin.READ_LIBTIFF = False
def test_crashing_metadata(self):
def test_crashing_metadata(self, tmp_path):
# issue 1597
with Image.open("Tests/images/rdf.tif") as im:
out = self.tempfile("temp.tif")
out = str(tmp_path / "temp.tif")
TiffImagePlugin.WRITE_LIBTIFF = True
# this shouldn't crash
im.save(out, format="TIFF")
TiffImagePlugin.WRITE_LIBTIFF = False
def test_page_number_x_0(self):
def test_page_number_x_0(self, tmp_path):
# Issue 973
# Test TIFF with tag 297 (Page Number) having value of 0 0.
# The first number is the current page number.
# The second is the total number of pages, zero means not available.
outfile = self.tempfile("temp.tif")
outfile = str(tmp_path / "temp.tif")
# Created by printing a page in Chrome to PDF, then:
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
# -dNOPAUSE /tmp/test.pdf -c quit
@ -643,10 +642,10 @@ class TestFileLibTiff(LibTiffTestCase):
# Should not divide by zero
im.save(outfile)
def test_fd_duplication(self):
def test_fd_duplication(self, tmp_path):
# https://github.com/python-pillow/Pillow/issues/1651
tmpfile = self.tempfile("temp.tif")
tmpfile = str(tmp_path / "temp.tif")
with open(tmpfile, "wb") as f:
with open("Tests/images/g4-multi.tiff", "rb") as src:
f.write(src.read())
@ -685,9 +684,9 @@ class TestFileLibTiff(LibTiffTestCase):
assert im.size == (10, 10)
im.load()
def test_save_tiff_with_jpegtables(self):
def test_save_tiff_with_jpegtables(self, tmp_path):
# Arrange
outfile = self.tempfile("temp.tif")
outfile = str(tmp_path / "temp.tif")
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
# Contains JPEGTables (347) tag

View File

@ -15,16 +15,16 @@ class TestFileLibTiffSmall(LibTiffTestCase):
file just before reading in libtiff. These tests remain
to ensure that it stays fixed. """
def test_g4_hopper_file(self):
def test_g4_hopper_file(self, tmp_path):
"""Testing the open file load path"""
test_file = "Tests/images/hopper_g4.tif"
with open(test_file, "rb") as f:
with Image.open(f) as im:
assert im.size == (128, 128)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_hopper_bytesio(self):
def test_g4_hopper_bytesio(self, tmp_path):
"""Testing the bytesio loading code path"""
test_file = "Tests/images/hopper_g4.tif"
s = BytesIO()
@ -33,12 +33,12 @@ class TestFileLibTiffSmall(LibTiffTestCase):
s.seek(0)
with Image.open(s) as im:
assert im.size == (128, 128)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)
def test_g4_hopper(self):
def test_g4_hopper(self, tmp_path):
"""The 128x128 lena image failed for some reason."""
test_file = "Tests/images/hopper_g4.tif"
with Image.open(test_file) as im:
assert im.size == (128, 128)
self._assert_noerr(im)
self._assert_noerr(tmp_path, im)

View File

@ -1,71 +1,90 @@
import os.path
import subprocess
import pytest
from PIL import Image
from .helper import (
PillowTestCase,
IMCONVERT,
assert_image_equal,
hopper,
imagemagick_available,
skip_known_bad_test,
)
_roundtrip = imagemagick_available()
class TestFilePalm(PillowTestCase):
_roundtrip = imagemagick_available()
def helper_save_as_palm(self, mode):
# Arrange
im = hopper(mode)
outfile = self.tempfile("temp_" + mode + ".palm")
def helper_save_as_palm(tmp_path, mode):
# Arrange
im = hopper(mode)
outfile = str(tmp_path / ("temp_" + mode + ".palm"))
# Act
im.save(outfile)
# Act
im.save(outfile)
# Assert
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
# Assert
assert os.path.isfile(outfile)
assert os.path.getsize(outfile) > 0
def roundtrip(self, mode):
if not self._roundtrip:
return
im = hopper(mode)
outfile = self.tempfile("temp.palm")
def open_with_imagemagick(tmp_path, f):
if not imagemagick_available():
raise OSError()
im.save(outfile)
converted = self.open_withImagemagick(outfile)
assert_image_equal(converted, im)
outfile = str(tmp_path / "temp.png")
rc = subprocess.call(
[IMCONVERT, f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
if rc:
raise OSError
return Image.open(outfile)
def test_monochrome(self):
# Arrange
mode = "1"
# Act / Assert
self.helper_save_as_palm(mode)
self.roundtrip(mode)
def roundtrip(tmp_path, mode):
if not _roundtrip:
return
def test_p_mode(self):
# Arrange
mode = "P"
im = hopper(mode)
outfile = str(tmp_path / "temp.palm")
# Act / Assert
self.helper_save_as_palm(mode)
skip_known_bad_test("Palm P image is wrong")
self.roundtrip(mode)
im.save(outfile)
converted = open_with_imagemagick(tmp_path, outfile)
assert_image_equal(converted, im)
def test_l_ioerror(self):
# Arrange
mode = "L"
# Act / Assert
with pytest.raises(IOError):
self.helper_save_as_palm(mode)
def test_monochrome(tmp_path):
# Arrange
mode = "1"
def test_rgb_ioerror(self):
# Arrange
mode = "RGB"
# Act / Assert
helper_save_as_palm(tmp_path, mode)
roundtrip(tmp_path, mode)
# Act / Assert
with pytest.raises(IOError):
self.helper_save_as_palm(mode)
def test_p_mode(tmp_path):
# Arrange
mode = "P"
# Act / Assert
helper_save_as_palm(tmp_path, mode)
skip_known_bad_test("Palm P image is wrong")
roundtrip(tmp_path, mode)
def test_l_ioerror(tmp_path):
# Arrange
mode = "L"
# Act / Assert
with pytest.raises(IOError):
helper_save_as_palm(tmp_path, mode)
def test_rgb_ioerror(tmp_path):
# Arrange
mode = "RGB"
# Act / Assert
with pytest.raises(IOError):
helper_save_as_palm(tmp_path, mode)

View File

@ -1,18 +1,15 @@
from PIL import Image
from .helper import PillowTestCase
def test_load_raw():
with Image.open("Tests/images/hopper.pcd") as im:
im.load() # should not segfault.
class TestFilePcd(PillowTestCase):
def test_load_raw(self):
with Image.open("Tests/images/hopper.pcd") as im:
im.load() # should not segfault.
# Note that this image was created with a resized hopper
# image, which was then converted to pcd with imagemagick
# and the colors are wonky in Pillow. It's unclear if this
# is a pillow or a convert issue, as other images not generated
# from convert look find on pillow and not imagemagick.
# Note that this image was created with a resized hopper
# image, which was then converted to pcd with imagemagick
# and the colors are wonky in Pillow. It's unclear if this
# is a pillow or a convert issue, as other images not generated
# from convert look find on pillow and not imagemagick.
# target = hopper().resize((768,512))
# assert_image_similar(im, target, 10)
# target = hopper().resize((768,512))
# assert_image_similar(im, target, 10)

View File

@ -1,130 +1,142 @@
import pytest
from PIL import Image, ImageFile, PcxImagePlugin
from .helper import PillowTestCase, assert_image_equal, hopper
from .helper import assert_image_equal, hopper
class TestFilePcx(PillowTestCase):
def _roundtrip(self, im):
f = self.tempfile("temp.pcx")
def _roundtrip(tmp_path, im):
f = str(tmp_path / "temp.pcx")
im.save(f)
with Image.open(f) as im2:
assert im2.mode == im.mode
assert im2.size == im.size
assert im2.format == "PCX"
assert im2.get_format_mimetype() == "image/x-pcx"
assert_image_equal(im2, im)
def test_sanity(tmp_path):
for mode in ("1", "L", "P", "RGB"):
_roundtrip(tmp_path, hopper(mode))
# Test an unsupported mode
f = str(tmp_path / "temp.pcx")
im = hopper("RGBA")
with pytest.raises(ValueError):
im.save(f)
with Image.open(f) as im2:
assert im2.mode == im.mode
assert im2.size == im.size
assert im2.format == "PCX"
assert im2.get_format_mimetype() == "image/x-pcx"
assert_image_equal(im2, im)
def test_sanity(self):
for mode in ("1", "L", "P", "RGB"):
self._roundtrip(hopper(mode))
# Test an unsupported mode
f = self.tempfile("temp.pcx")
im = hopper("RGBA")
with pytest.raises(ValueError):
im.save(f)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
PcxImagePlugin.PcxImageFile(invalid_file)
with pytest.raises(SyntaxError):
PcxImagePlugin.PcxImageFile(invalid_file)
def test_odd(self):
# see issue #523, odd sized images should have a stride that's even.
# not that imagemagick or gimp write pcx that way.
# we were not handling properly.
for mode in ("1", "L", "P", "RGB"):
# larger, odd sized images are better here to ensure that
# we handle interrupted scan lines properly.
self._roundtrip(hopper(mode).resize((511, 511)))
def test_odd(tmp_path):
# See issue #523, odd sized images should have a stride that's even.
# Not that ImageMagick or GIMP write PCX that way.
# We were not handling properly.
for mode in ("1", "L", "P", "RGB"):
# larger, odd sized images are better here to ensure that
# we handle interrupted scan lines properly.
_roundtrip(tmp_path, hopper(mode).resize((511, 511)))
def test_pil184(self):
# Check reading of files where xmin/xmax is not zero.
test_file = "Tests/images/pil184.pcx"
with Image.open(test_file) as im:
assert im.size == (447, 144)
assert im.tile[0][1] == (0, 0, 447, 144)
def test_pil184():
# Check reading of files where xmin/xmax is not zero.
# Make sure all pixels are either 0 or 255.
assert im.histogram()[0] + im.histogram()[255] == 447 * 144
test_file = "Tests/images/pil184.pcx"
with Image.open(test_file) as im:
assert im.size == (447, 144)
assert im.tile[0][1] == (0, 0, 447, 144)
def test_1px_width(self):
im = Image.new("L", (1, 256))
px = im.load()
for y in range(256):
px[0, y] = y
self._roundtrip(im)
# Make sure all pixels are either 0 or 255.
assert im.histogram()[0] + im.histogram()[255] == 447 * 144
def test_large_count(self):
im = Image.new("L", (256, 1))
px = im.load()
def test_1px_width(tmp_path):
im = Image.new("L", (1, 256))
px = im.load()
for y in range(256):
px[0, y] = y
_roundtrip(tmp_path, im)
def test_large_count(tmp_path):
im = Image.new("L", (256, 1))
px = im.load()
for x in range(256):
px[x, 0] = x // 67 * 67
_roundtrip(tmp_path, im)
def _test_buffer_overflow(tmp_path, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
_roundtrip(tmp_path, im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, 0] = x // 67 * 67
self._roundtrip(im)
px[x, y] = x % 128
_test_buffer_overflow(tmp_path, im)
def _test_buffer_overflow(self, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
self._roundtrip(im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_one_in_loop(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
_test_buffer_overflow(tmp_path, im)
def test_break_one_in_loop(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_many_in_loop(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
for x in range(8):
px[x, 4] = 16
self._test_buffer_overflow(im)
def test_break_many_in_loop(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
for x in range(8):
px[x, 4] = 16
_test_buffer_overflow(tmp_path, im)
def test_break_one_at_end(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
self._test_buffer_overflow(im)
def test_break_many_at_end(self):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
for x in range(4):
px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
self._test_buffer_overflow(im)
def test_break_one_at_end(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
_test_buffer_overflow(tmp_path, im)
def test_break_padding(self):
im = Image.new("L", (257, 5))
px = im.load()
for y in range(5):
for x in range(257):
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
self._test_buffer_overflow(im)
def test_break_many_at_end(tmp_path):
im = Image.new("L", (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
for x in range(4):
px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
_test_buffer_overflow(tmp_path, im)
def test_break_padding(tmp_path):
im = Image.new("L", (257, 5))
px = im.load()
for y in range(5):
for x in range(257):
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
_test_buffer_overflow(tmp_path, im)

View File

@ -1,5 +1,4 @@
import re
import unittest
import zlib
from io import BytesIO
@ -8,7 +7,6 @@ from PIL import Image, ImageFile, PngImagePlugin
from .helper import (
PillowLeakTestCase,
PillowTestCase,
assert_image,
assert_image_equal,
hopper,
@ -55,7 +53,7 @@ def roundtrip(im, **options):
@skip_unless_feature("zlib")
class TestFilePng(PillowTestCase):
class TestFilePng:
def get_chunks(self, filename):
chunks = []
with open(filename, "rb") as fp:
@ -72,12 +70,12 @@ class TestFilePng(PillowTestCase):
return chunks
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
def test_sanity(self):
def test_sanity(self, tmp_path):
# internal version number
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version)
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
hopper("RGB").save(test_file)
@ -232,14 +230,14 @@ class TestFilePng(PillowTestCase):
# image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876
def test_save_p_transparent_palette(self):
def test_save_p_transparent_palette(self, tmp_path):
in_file = "Tests/images/pil123p.png"
with Image.open(in_file) as im:
# 'transparency' contains a byte string with the opacity for
# each palette entry
assert len(im.info["transparency"]) == 256
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
# check if saved image contains same transparency
@ -253,14 +251,14 @@ class TestFilePng(PillowTestCase):
# image has 124 unique alpha values
assert len(im.getchannel("A").getcolors()) == 124
def test_save_p_single_transparency(self):
def test_save_p_single_transparency(self, tmp_path):
in_file = "Tests/images/p_trns_single.png"
with Image.open(in_file) as im:
# pixel value 164 is full transparent
assert im.info["transparency"] == 164
assert im.getpixel((31, 31)) == 164
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
# check if saved image contains same transparency
@ -276,14 +274,14 @@ class TestFilePng(PillowTestCase):
# image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876
def test_save_p_transparent_black(self):
def test_save_p_transparent_black(self, tmp_path):
# check if solid black image with full transparency
# is supported (check for #1838)
im = Image.new("RGBA", (10, 10), (0, 0, 0, 0))
assert im.getcolors() == [(100, (0, 0, 0, 0))]
im = im.convert("P")
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
# check if saved image contains same transparency
@ -294,7 +292,7 @@ class TestFilePng(PillowTestCase):
assert_image(im, "RGBA", (10, 10))
assert im.getcolors() == [(100, (0, 0, 0, 0))]
def test_save_greyscale_transparency(self):
def test_save_greyscale_transparency(self, tmp_path):
for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items():
in_file = "Tests/images/" + mode.lower() + "_trns.png"
with Image.open(in_file) as im:
@ -304,7 +302,7 @@ class TestFilePng(PillowTestCase):
im_rgba = im.convert("RGBA")
assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
with Image.open(test_file) as test_im:
@ -315,10 +313,10 @@ class TestFilePng(PillowTestCase):
test_im_rgba = test_im.convert("RGBA")
assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
def test_save_rgb_single_transparency(self):
def test_save_rgb_single_transparency(self, tmp_path):
in_file = "Tests/images/caption_6_33_22.png"
with Image.open(in_file) as im:
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
def test_load_verify(self):
@ -483,12 +481,12 @@ class TestFilePng(PillowTestCase):
im = roundtrip(im, transparency=(0, 1, 2))
assert im.info["transparency"] == (0, 1, 2)
def test_trns_p(self):
def test_trns_p(self, tmp_path):
# Check writing a transparency of 0, issue #528
im = hopper("P")
im.info["transparency"] = 0
f = self.tempfile("temp.png")
f = str(tmp_path / "temp.png")
im.save(f)
with Image.open(f) as im2:
@ -539,9 +537,9 @@ class TestFilePng(PillowTestCase):
assert repr_png.format == "PNG"
assert_image_equal(im, repr_png)
def test_chunk_order(self):
def test_chunk_order(self, tmp_path):
with Image.open("Tests/images/icc_profile.png") as im:
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.convert("P").save(test_file, dpi=(100, 100))
chunks = self.get_chunks(test_file)
@ -598,34 +596,34 @@ class TestFilePng(PillowTestCase):
exif = im._getexif()
assert exif[274] == 1
def test_exif_save(self):
def test_exif_save(self, tmp_path):
with Image.open("Tests/images/exif.png") as im:
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
with Image.open(test_file) as reloaded:
exif = reloaded._getexif()
assert exif[274] == 1
def test_exif_from_jpg(self):
def test_exif_from_jpg(self, tmp_path):
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file)
with Image.open(test_file) as reloaded:
exif = reloaded._getexif()
assert exif[305] == "Adobe Photoshop CS Macintosh"
def test_exif_argument(self):
def test_exif_argument(self, tmp_path):
with Image.open(TEST_PNG_FILE) as im:
test_file = self.tempfile("temp.png")
test_file = str(tmp_path / "temp.png")
im.save(test_file, exif=b"exifstring")
with Image.open(test_file) as reloaded:
assert reloaded.info["exif"] == b"Exif\x00\x00exifstring"
@unittest.skipIf(is_win32(), "requires Unix or macOS")
@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
@skip_unless_feature("zlib")
class TestTruncatedPngPLeaks(PillowLeakTestCase):
mem_limit = 2 * 1024 # max increase in K

View File

@ -1,77 +1,82 @@
import pytest
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
# sample ppm stream
test_file = "Tests/images/hopper.ppm"
TEST_FILE = "Tests/images/hopper.ppm"
class TestFilePpm(PillowTestCase):
def test_sanity(self):
with Image.open(test_file) as im:
im.load()
assert im.mode == "RGB"
assert im.size == (128, 128)
assert im.format, "PPM"
assert im.get_format_mimetype() == "image/x-portable-pixmap"
def test_sanity():
with Image.open(TEST_FILE) as im:
im.load()
assert im.mode == "RGB"
assert im.size == (128, 128)
assert im.format, "PPM"
assert im.get_format_mimetype() == "image/x-portable-pixmap"
def test_16bit_pgm(self):
with Image.open("Tests/images/16_bit_binary.pgm") as im:
im.load()
assert im.mode == "I"
assert im.size == (20, 100)
assert im.get_format_mimetype() == "image/x-portable-graymap"
with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt:
assert_image_equal(im, tgt)
def test_16bit_pgm():
with Image.open("Tests/images/16_bit_binary.pgm") as im:
im.load()
assert im.mode == "I"
assert im.size == (20, 100)
assert im.get_format_mimetype() == "image/x-portable-graymap"
def test_16bit_pgm_write(self):
with Image.open("Tests/images/16_bit_binary.pgm") as im:
im.load()
with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt:
assert_image_equal(im, tgt)
f = self.tempfile("temp.pgm")
im.save(f, "PPM")
with Image.open(f) as reloaded:
assert_image_equal(im, reloaded)
def test_16bit_pgm_write(tmp_path):
with Image.open("Tests/images/16_bit_binary.pgm") as im:
im.load()
def test_pnm(self):
with Image.open("Tests/images/hopper.pnm") as im:
assert_image_similar(im, hopper(), 0.0001)
f = str(tmp_path / "temp.pgm")
im.save(f, "PPM")
f = self.tempfile("temp.pnm")
im.save(f)
with Image.open(f) as reloaded:
assert_image_equal(im, reloaded)
with Image.open(f) as reloaded:
assert_image_equal(im, reloaded)
def test_truncated_file(self):
path = self.tempfile("temp.pgm")
with open(path, "w") as f:
f.write("P6")
def test_pnm(tmp_path):
with Image.open("Tests/images/hopper.pnm") as im:
assert_image_similar(im, hopper(), 0.0001)
with pytest.raises(ValueError):
Image.open(path)
f = str(tmp_path / "temp.pnm")
im.save(f)
def test_neg_ppm(self):
# Storage.c accepted negative values for xsize, ysize. the
# internal open_ppm function didn't check for sanity but it
# has been removed. The default opener doesn't accept negative
# sizes.
with Image.open(f) as reloaded:
assert_image_equal(im, reloaded)
with pytest.raises(IOError):
Image.open("Tests/images/negative_size.ppm")
def test_mimetypes(self):
path = self.tempfile("temp.pgm")
def test_truncated_file(tmp_path):
path = str(tmp_path / "temp.pgm")
with open(path, "w") as f:
f.write("P6")
with open(path, "w") as f:
f.write("P4\n128 128\n255")
with Image.open(path) as im:
assert im.get_format_mimetype() == "image/x-portable-bitmap"
with pytest.raises(ValueError):
Image.open(path)
with open(path, "w") as f:
f.write("PyCMYK\n128 128\n255")
with Image.open(path) as im:
assert im.get_format_mimetype() == "image/x-portable-anymap"
def test_neg_ppm():
# Storage.c accepted negative values for xsize, ysize. the
# internal open_ppm function didn't check for sanity but it
# has been removed. The default opener doesn't accept negative
# sizes.
with pytest.raises(IOError):
Image.open("Tests/images/negative_size.ppm")
def test_mimetypes(tmp_path):
path = str(tmp_path / "temp.pgm")
with open(path, "w") as f:
f.write("P4\n128 128\n255")
with Image.open(path) as im:
assert im.get_format_mimetype() == "image/x-portable-bitmap"
with open(path, "w") as f:
f.write("PyCMYK\n128 128\n255")
with Image.open(path) as im:
assert im.get_format_mimetype() == "image/x-portable-anymap"

View File

@ -1,92 +1,100 @@
import pytest
from PIL import Image, SgiImagePlugin
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper
from .helper import assert_image_equal, assert_image_similar, hopper
class TestFileSgi(PillowTestCase):
def test_rgb(self):
# Created with ImageMagick then renamed:
# convert hopper.ppm -compress None sgi:hopper.rgb
test_file = "Tests/images/hopper.rgb"
def test_rgb():
# Created with ImageMagick then renamed:
# convert hopper.ppm -compress None sgi:hopper.rgb
test_file = "Tests/images/hopper.rgb"
with Image.open(test_file) as im:
assert_image_equal(im, hopper())
assert im.get_format_mimetype() == "image/rgb"
with Image.open(test_file) as im:
assert_image_equal(im, hopper())
assert im.get_format_mimetype() == "image/rgb"
def test_rgb16(self):
test_file = "Tests/images/hopper16.rgb"
with Image.open(test_file) as im:
assert_image_equal(im, hopper())
def test_rgb16():
test_file = "Tests/images/hopper16.rgb"
def test_l(self):
# Created with ImageMagick
# convert hopper.ppm -monochrome -compress None sgi:hopper.bw
test_file = "Tests/images/hopper.bw"
with Image.open(test_file) as im:
assert_image_equal(im, hopper())
with Image.open(test_file) as im:
assert_image_similar(im, hopper("L"), 2)
assert im.get_format_mimetype() == "image/sgi"
def test_rgba(self):
# Created with ImageMagick:
# convert transparent.png -compress None transparent.sgi
test_file = "Tests/images/transparent.sgi"
def test_l():
# Created with ImageMagick
# convert hopper.ppm -monochrome -compress None sgi:hopper.bw
test_file = "Tests/images/hopper.bw"
with Image.open(test_file) as im:
with Image.open("Tests/images/transparent.png") as target:
assert_image_equal(im, target)
assert im.get_format_mimetype() == "image/sgi"
with Image.open(test_file) as im:
assert_image_similar(im, hopper("L"), 2)
assert im.get_format_mimetype() == "image/sgi"
def test_rle(self):
# Created with ImageMagick:
# convert hopper.ppm hopper.sgi
test_file = "Tests/images/hopper.sgi"
with Image.open(test_file) as im:
with Image.open("Tests/images/hopper.rgb") as target:
assert_image_equal(im, target)
def test_rgba():
# Created with ImageMagick:
# convert transparent.png -compress None transparent.sgi
test_file = "Tests/images/transparent.sgi"
def test_rle16(self):
test_file = "Tests/images/tv16.sgi"
with Image.open(test_file) as im:
with Image.open("Tests/images/transparent.png") as target:
assert_image_equal(im, target)
assert im.get_format_mimetype() == "image/sgi"
with Image.open(test_file) as im:
with Image.open("Tests/images/tv.rgb") as target:
assert_image_equal(im, target)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
def test_rle():
# Created with ImageMagick:
# convert hopper.ppm hopper.sgi
test_file = "Tests/images/hopper.sgi"
with pytest.raises(ValueError):
SgiImagePlugin.SgiImageFile(invalid_file)
with Image.open(test_file) as im:
with Image.open("Tests/images/hopper.rgb") as target:
assert_image_equal(im, target)
def test_write(self):
def roundtrip(img):
out = self.tempfile("temp.sgi")
img.save(out, format="sgi")
with Image.open(out) as reloaded:
assert_image_equal(img, reloaded)
for mode in ("L", "RGB", "RGBA"):
roundtrip(hopper(mode))
def test_rle16():
test_file = "Tests/images/tv16.sgi"
# Test 1 dimension for an L mode image
roundtrip(Image.new("L", (10, 1)))
with Image.open(test_file) as im:
with Image.open("Tests/images/tv.rgb") as target:
assert_image_equal(im, target)
def test_write16(self):
test_file = "Tests/images/hopper16.rgb"
with Image.open(test_file) as im:
out = self.tempfile("temp.sgi")
im.save(out, format="sgi", bpc=2)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with Image.open(out) as reloaded:
assert_image_equal(im, reloaded)
with pytest.raises(ValueError):
SgiImagePlugin.SgiImageFile(invalid_file)
def test_unsupported_mode(self):
im = hopper("LA")
out = self.tempfile("temp.sgi")
with pytest.raises(ValueError):
im.save(out, format="sgi")
def test_write(tmp_path):
def roundtrip(img):
out = str(tmp_path / "temp.sgi")
img.save(out, format="sgi")
with Image.open(out) as reloaded:
assert_image_equal(img, reloaded)
for mode in ("L", "RGB", "RGBA"):
roundtrip(hopper(mode))
# Test 1 dimension for an L mode image
roundtrip(Image.new("L", (10, 1)))
def test_write16(tmp_path):
test_file = "Tests/images/hopper16.rgb"
with Image.open(test_file) as im:
out = str(tmp_path / "temp.sgi")
im.save(out, format="sgi", bpc=2)
with Image.open(out) as reloaded:
assert_image_equal(im, reloaded)
def test_unsupported_mode(tmp_path):
im = hopper("LA")
out = str(tmp_path / "temp.sgi")
with pytest.raises(ValueError):
im.save(out, format="sgi")

View File

@ -1,6 +1,5 @@
import logging
import os
import unittest
from io import BytesIO
import pytest
@ -8,7 +7,6 @@ from PIL import Image, TiffImagePlugin
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
from .helper import (
PillowTestCase,
assert_image_equal,
assert_image_equal_tofile,
assert_image_similar,
@ -21,10 +19,10 @@ from .helper import (
logger = logging.getLogger(__name__)
class TestFileTiff(PillowTestCase):
def test_sanity(self):
class TestFileTiff:
def test_sanity(self, tmp_path):
filename = self.tempfile("temp.tif")
filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename)
@ -54,7 +52,7 @@ class TestFileTiff(PillowTestCase):
with Image.open(filename):
pass
@unittest.skipIf(is_pypy(), "Requires CPython")
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open("Tests/images/multipage.tiff")
@ -155,8 +153,8 @@ class TestFileTiff(PillowTestCase):
assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit
assert im.info["dpi"] == (dpi[1], dpi[1])
def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.tif")
def test_save_dpi_rounding(self, tmp_path):
outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/hopper.tif") as im:
for dpi in (72.2, 72.8):
im.save(outfile, dpi=(dpi, dpi))
@ -190,14 +188,14 @@ class TestFileTiff(PillowTestCase):
# Should not raise struct.error.
pytest.warns(UserWarning, i._getexif)
def test_save_rgba(self):
def test_save_rgba(self, tmp_path):
im = hopper("RGBA")
outfile = self.tempfile("temp.tif")
outfile = str(tmp_path / "temp.tif")
im.save(outfile)
def test_save_unsupported_mode(self):
def test_save_unsupported_mode(self, tmp_path):
im = hopper("HSV")
outfile = self.tempfile("temp.tif")
outfile = str(tmp_path / "temp.tif")
with pytest.raises(IOError):
im.save(outfile)
@ -459,9 +457,9 @@ class TestFileTiff(PillowTestCase):
assert im2.mode == "L"
assert_image_equal(im, im2)
def test_with_underscores(self):
def test_with_underscores(self, tmp_path):
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
filename = self.tempfile("temp.tif")
filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename, **kwargs)
with Image.open(filename) as im:
@ -473,14 +471,14 @@ class TestFileTiff(PillowTestCase):
assert im.tag_v2[X_RESOLUTION] == 72
assert im.tag_v2[Y_RESOLUTION] == 36
def test_roundtrip_tiff_uint16(self):
def test_roundtrip_tiff_uint16(self, tmp_path):
# Test an image of all '0' values
pixel_value = 0x1234
infile = "Tests/images/uint16_1_4660.tif"
with Image.open(infile) as im:
assert im.getpixel((0, 0)) == pixel_value
tmpfile = self.tempfile("temp.tif")
tmpfile = str(tmp_path / "temp.tif")
im.save(tmpfile)
with Image.open(tmpfile) as reloaded:
@ -512,9 +510,9 @@ class TestFileTiff(PillowTestCase):
with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_palette(self):
for mode in ["P", "PA"]:
outfile = self.tempfile("temp.tif")
def test_palette(self, tmp_path):
def roundtrip(mode):
outfile = str(tmp_path / "temp.tif")
im = hopper(mode)
im.save(outfile)
@ -522,6 +520,9 @@ class TestFileTiff(PillowTestCase):
with Image.open(outfile) as reloaded:
assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
for mode in ["P", "PA"]:
roundtrip(mode)
def test_tiff_save_all(self):
mp = BytesIO()
with Image.open("Tests/images/multipage.tiff") as im:
@ -552,7 +553,7 @@ class TestFileTiff(PillowTestCase):
with Image.open(mp) as reread:
assert reread.n_frames == 3
def test_saving_icc_profile(self):
def test_saving_icc_profile(self, tmp_path):
# Tests saving TIFF with icc_profile set.
# At the time of writing this will only work for non-compressed tiffs
# as libtiff does not support embedded ICC profiles,
@ -561,14 +562,14 @@ class TestFileTiff(PillowTestCase):
im.info["icc_profile"] = "Dummy value"
# Try save-load round trip to make sure both handle icc_profile.
tmpfile = self.tempfile("temp.tif")
tmpfile = str(tmp_path / "temp.tif")
im.save(tmpfile, "TIFF", compression="raw")
with Image.open(tmpfile) as reloaded:
assert b"Dummy value" == reloaded.info["icc_profile"]
def test_close_on_load_exclusive(self):
def test_close_on_load_exclusive(self, tmp_path):
# similar to test_fd_leak, but runs on unixlike os
tmpfile = self.tempfile("temp.tif")
tmpfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile)
@ -579,8 +580,8 @@ class TestFileTiff(PillowTestCase):
im.load()
assert fp.closed
def test_close_on_load_nonexclusive(self):
tmpfile = self.tempfile("temp.tif")
def test_close_on_load_nonexclusive(self, tmp_path):
tmpfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile)
@ -601,10 +602,10 @@ class TestFileTiff(PillowTestCase):
Image.open("Tests/images/string_dimension.tiff")
@unittest.skipUnless(is_win32(), "Windows only")
class TestFileTiffW32(PillowTestCase):
def test_fd_leak(self):
tmpfile = self.tempfile("temp.tif")
@pytest.mark.skipif(not is_win32(), reason="Windows only")
class TestFileTiffW32:
def test_fd_leak(self, tmp_path):
tmpfile = str(tmp_path / "temp.tif")
# this is an mmaped file.
with Image.open("Tests/images/uint16_1_4660.tif") as im:

View File

@ -5,321 +5,335 @@ import pytest
from PIL import Image, TiffImagePlugin, TiffTags
from PIL.TiffImagePlugin import IFDRational
from .helper import PillowTestCase, assert_deep_equal, hopper
tag_ids = {info.name: info.value for info in TiffTags.TAGS_V2.values()}
class TestFileTiffMetadata(PillowTestCase):
def test_rt_metadata(self):
""" Test writing arbitrary metadata into the tiff image directory
Use case is ImageJ private tags, one numeric, one arbitrary
data. https://github.com/python-pillow/Pillow/issues/291
"""
img = hopper()
# Behaviour change: re #1416
# Pre ifd rewrite, ImageJMetaData was being written as a string(2),
# Post ifd rewrite, it's defined as arbitrary bytes(7). It should
# roundtrip with the actual bytes, rather than stripped text
# of the premerge tests.
#
# For text items, we still have to decode('ascii','replace') because
# the tiff file format can't take 8 bit bytes in that field.
basetextdata = "This is some arbitrary metadata for a text field"
bindata = basetextdata.encode("ascii") + b" \xff"
textdata = basetextdata + " " + chr(255)
reloaded_textdata = basetextdata + " ?"
floatdata = 12.345
doubledata = 67.89
info = TiffImagePlugin.ImageFileDirectory()
ImageJMetaData = tag_ids["ImageJMetaData"]
ImageJMetaDataByteCounts = tag_ids["ImageJMetaDataByteCounts"]
ImageDescription = tag_ids["ImageDescription"]
info[ImageJMetaDataByteCounts] = len(bindata)
info[ImageJMetaData] = bindata
info[tag_ids["RollAngle"]] = floatdata
info.tagtype[tag_ids["RollAngle"]] = 11
info[tag_ids["YawAngle"]] = doubledata
info.tagtype[tag_ids["YawAngle"]] = 12
info[ImageDescription] = textdata
f = self.tempfile("temp.tif")
img.save(f, tiffinfo=info)
with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata),)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata),)
assert loaded.tag[ImageJMetaData] == bindata
assert loaded.tag_v2[ImageJMetaData] == bindata
assert loaded.tag[ImageDescription] == (reloaded_textdata,)
assert loaded.tag_v2[ImageDescription] == reloaded_textdata
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
assert round(abs(loaded_float - floatdata), 5) == 0
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
assert round(abs(loaded_double - doubledata), 7) == 0
# check with 2 element ImageJMetaDataByteCounts, issue #2006
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
img.save(f, tiffinfo=info)
with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8)
def test_read_metadata(self):
with Image.open("Tests/images/hopper_g4.tif") as img:
assert {
"YResolution": IFDRational(4294967295, 113653537),
"PlanarConfiguration": 1,
"BitsPerSample": (1,),
"ImageLength": 128,
"Compression": 4,
"FillOrder": 1,
"RowsPerStrip": 128,
"ResolutionUnit": 3,
"PhotometricInterpretation": 0,
"PageNumber": (0, 1),
"XResolution": IFDRational(4294967295, 113653537),
"ImageWidth": 128,
"Orientation": 1,
"StripByteCounts": (1968,),
"SamplesPerPixel": 1,
"StripOffsets": (8,),
} == img.tag_v2.named()
assert {
"YResolution": ((4294967295, 113653537),),
"PlanarConfiguration": (1,),
"BitsPerSample": (1,),
"ImageLength": (128,),
"Compression": (4,),
"FillOrder": (1,),
"RowsPerStrip": (128,),
"ResolutionUnit": (3,),
"PhotometricInterpretation": (0,),
"PageNumber": (0, 1),
"XResolution": ((4294967295, 113653537),),
"ImageWidth": (128,),
"Orientation": (1,),
"StripByteCounts": (1968,),
"SamplesPerPixel": (1,),
"StripOffsets": (8,),
} == img.tag.named()
def test_write_metadata(self):
""" Test metadata writing through the python code """
with Image.open("Tests/images/hopper.tif") as img:
f = self.tempfile("temp.tiff")
img.save(f, tiffinfo=img.tag)
original = img.tag_v2.named()
with Image.open(f) as loaded:
reloaded = loaded.tag_v2.named()
ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"]
for tag, value in reloaded.items():
if tag in ignored:
continue
if isinstance(original[tag], tuple) and isinstance(
original[tag][0], IFDRational
):
# Need to compare element by element in the tuple,
# not comparing tuples of object references
assert_deep_equal(
original[tag],
value,
"{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
)
else:
assert original[tag] == value, "{} didn't roundtrip, {}, {}".format(
tag, original[tag], value
)
for tag, value in original.items():
if tag not in ignored:
assert value == reloaded[tag], "%s didn't roundtrip" % tag
def test_no_duplicate_50741_tag(self):
assert tag_ids["MakerNoteSafety"] == 50741
assert tag_ids["BestQualityScale"] == 50780
def test_empty_metadata(self):
f = io.BytesIO(b"II*\x00\x08\x00\x00\x00")
head = f.read(8)
info = TiffImagePlugin.ImageFileDirectory(head)
# Should not raise struct.error.
pytest.warns(UserWarning, info.load, f)
def test_iccprofile(self):
# https://github.com/python-pillow/Pillow/issues/1462
out = self.tempfile("temp.tiff")
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
im.save(out)
with Image.open(out) as reloaded:
assert not isinstance(im.info["icc_profile"], tuple)
assert im.info["icc_profile"] == reloaded.info["icc_profile"]
def test_iccprofile_binary(self):
# https://github.com/python-pillow/Pillow/issues/1526
# We should be able to load this,
# but probably won't be able to save it.
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
assert im.tag_v2.tagtype[34675] == 1
assert im.info["icc_profile"]
def test_iccprofile_save_png(self):
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
outfile = self.tempfile("temp.png")
im.save(outfile)
def test_iccprofile_binary_save_png(self):
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
outfile = self.tempfile("temp.png")
im.save(outfile)
def test_exif_div_zero(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
info[41988] = TiffImagePlugin.IFDRational(0, 0)
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert 0 == reloaded.tag_v2[41988].numerator
assert 0 == reloaded.tag_v2[41988].denominator
def test_ifd_unsigned_rational(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
max_long = 2 ** 32 - 1
# 4 bytes unsigned long
numerator = max_long
info[41493] = TiffImagePlugin.IFDRational(numerator, 1)
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert max_long == reloaded.tag_v2[41493].numerator
assert 1 == reloaded.tag_v2[41493].denominator
# out of bounds of 4 byte unsigned long
numerator = max_long + 1
info[41493] = TiffImagePlugin.IFDRational(numerator, 1)
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert max_long == reloaded.tag_v2[41493].numerator
assert 1 == reloaded.tag_v2[41493].denominator
def test_ifd_signed_rational(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
# pair of 4 byte signed longs
numerator = 2 ** 31 - 1
denominator = -(2 ** 31)
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
from .helper import assert_deep_equal, hopper
TAG_IDS = {info.name: info.value for info in TiffTags.TAGS_V2.values()}
def test_rt_metadata(tmp_path):
""" Test writing arbitrary metadata into the tiff image directory
Use case is ImageJ private tags, one numeric, one arbitrary
data. https://github.com/python-pillow/Pillow/issues/291
"""
img = hopper()
# Behaviour change: re #1416
# Pre ifd rewrite, ImageJMetaData was being written as a string(2),
# Post ifd rewrite, it's defined as arbitrary bytes(7). It should
# roundtrip with the actual bytes, rather than stripped text
# of the premerge tests.
#
# For text items, we still have to decode('ascii','replace') because
# the tiff file format can't take 8 bit bytes in that field.
basetextdata = "This is some arbitrary metadata for a text field"
bindata = basetextdata.encode("ascii") + b" \xff"
textdata = basetextdata + " " + chr(255)
reloaded_textdata = basetextdata + " ?"
floatdata = 12.345
doubledata = 67.89
info = TiffImagePlugin.ImageFileDirectory()
ImageJMetaData = TAG_IDS["ImageJMetaData"]
ImageJMetaDataByteCounts = TAG_IDS["ImageJMetaDataByteCounts"]
ImageDescription = TAG_IDS["ImageDescription"]
info[ImageJMetaDataByteCounts] = len(bindata)
info[ImageJMetaData] = bindata
info[TAG_IDS["RollAngle"]] = floatdata
info.tagtype[TAG_IDS["RollAngle"]] = 11
info[TAG_IDS["YawAngle"]] = doubledata
info.tagtype[TAG_IDS["YawAngle"]] = 12
info[ImageDescription] = textdata
f = str(tmp_path / "temp.tif")
img.save(f, tiffinfo=info)
with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata),)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata),)
assert loaded.tag[ImageJMetaData] == bindata
assert loaded.tag_v2[ImageJMetaData] == bindata
assert loaded.tag[ImageDescription] == (reloaded_textdata,)
assert loaded.tag_v2[ImageDescription] == reloaded_textdata
loaded_float = loaded.tag[TAG_IDS["RollAngle"]][0]
assert round(abs(loaded_float - floatdata), 5) == 0
loaded_double = loaded.tag[TAG_IDS["YawAngle"]][0]
assert round(abs(loaded_double - doubledata), 7) == 0
# check with 2 element ImageJMetaDataByteCounts, issue #2006
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
img.save(f, tiffinfo=info)
with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8)
def test_read_metadata():
with Image.open("Tests/images/hopper_g4.tif") as img:
assert {
"YResolution": IFDRational(4294967295, 113653537),
"PlanarConfiguration": 1,
"BitsPerSample": (1,),
"ImageLength": 128,
"Compression": 4,
"FillOrder": 1,
"RowsPerStrip": 128,
"ResolutionUnit": 3,
"PhotometricInterpretation": 0,
"PageNumber": (0, 1),
"XResolution": IFDRational(4294967295, 113653537),
"ImageWidth": 128,
"Orientation": 1,
"StripByteCounts": (1968,),
"SamplesPerPixel": 1,
"StripOffsets": (8,),
} == img.tag_v2.named()
assert {
"YResolution": ((4294967295, 113653537),),
"PlanarConfiguration": (1,),
"BitsPerSample": (1,),
"ImageLength": (128,),
"Compression": (4,),
"FillOrder": (1,),
"RowsPerStrip": (128,),
"ResolutionUnit": (3,),
"PhotometricInterpretation": (0,),
"PageNumber": (0, 1),
"XResolution": ((4294967295, 113653537),),
"ImageWidth": (128,),
"Orientation": (1,),
"StripByteCounts": (1968,),
"SamplesPerPixel": (1,),
"StripOffsets": (8,),
} == img.tag.named()
def test_write_metadata(tmp_path):
""" Test metadata writing through the python code """
with Image.open("Tests/images/hopper.tif") as img:
f = str(tmp_path / "temp.tiff")
img.save(f, tiffinfo=img.tag)
original = img.tag_v2.named()
with Image.open(f) as loaded:
reloaded = loaded.tag_v2.named()
ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"]
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
for tag, value in reloaded.items():
if tag in ignored:
continue
if isinstance(original[tag], tuple) and isinstance(
original[tag][0], IFDRational
):
# Need to compare element by element in the tuple,
# not comparing tuples of object references
assert_deep_equal(
original[tag],
value,
"{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
)
else:
assert original[tag] == value, "{} didn't roundtrip, {}, {}".format(
tag, original[tag], value
)
with Image.open(out) as reloaded:
assert numerator == reloaded.tag_v2[37380].numerator
assert denominator == reloaded.tag_v2[37380].denominator
for tag, value in original.items():
if tag not in ignored:
assert value == reloaded[tag], "%s didn't roundtrip" % tag
numerator = -(2 ** 31)
denominator = 2 ** 31 - 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
def test_no_duplicate_50741_tag():
assert TAG_IDS["MakerNoteSafety"] == 50741
assert TAG_IDS["BestQualityScale"] == 50780
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert numerator == reloaded.tag_v2[37380].numerator
assert denominator == reloaded.tag_v2[37380].denominator
def test_empty_metadata():
f = io.BytesIO(b"II*\x00\x08\x00\x00\x00")
head = f.read(8)
info = TiffImagePlugin.ImageFileDirectory(head)
# Should not raise struct.error.
pytest.warns(UserWarning, info.load, f)
# out of bounds of 4 byte signed long
numerator = -(2 ** 31) - 1
denominator = 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
def test_iccprofile(tmp_path):
# https://github.com/python-pillow/Pillow/issues/1462
out = str(tmp_path / "temp.tiff")
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
im.save(out)
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert not isinstance(im.info["icc_profile"], tuple)
assert im.info["icc_profile"] == reloaded.info["icc_profile"]
with Image.open(out) as reloaded:
assert 2 ** 31 - 1 == reloaded.tag_v2[37380].numerator
assert -1 == reloaded.tag_v2[37380].denominator
def test_ifd_signed_long(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
def test_iccprofile_binary():
# https://github.com/python-pillow/Pillow/issues/1526
# We should be able to load this,
# but probably won't be able to save it.
info[37000] = -60000
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
assert im.tag_v2.tagtype[34675] == 1
assert im.info["icc_profile"]
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert reloaded.tag_v2[37000] == -60000
def test_iccprofile_save_png(tmp_path):
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
outfile = str(tmp_path / "temp.png")
im.save(outfile)
def test_empty_values(self):
data = io.BytesIO(
b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00"
b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a "
b"text\x00\x00"
)
head = data.read(8)
info = TiffImagePlugin.ImageFileDirectory_v2(head)
info.load(data)
# Should not raise ValueError.
info = dict(info)
assert 33432 in info
def test_PhotoshopInfo(self):
with Image.open("Tests/images/issue_2278.tif") as im:
assert len(im.tag_v2[34377]) == 1
assert isinstance(im.tag_v2[34377][0], bytes)
out = self.tempfile("temp.tiff")
im.save(out)
with Image.open(out) as reloaded:
assert len(reloaded.tag_v2[34377]) == 1
assert isinstance(reloaded.tag_v2[34377][0], bytes)
def test_iccprofile_binary_save_png(tmp_path):
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
outfile = str(tmp_path / "temp.png")
im.save(outfile)
def test_too_many_entries(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2()
# 277: ("SamplesPerPixel", SHORT, 1),
ifd._tagdata[277] = struct.pack("hh", 4, 4)
ifd.tagtype[277] = TiffTags.SHORT
def test_exif_div_zero(tmp_path):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
info[41988] = TiffImagePlugin.IFDRational(0, 0)
# Should not raise ValueError.
pytest.warns(UserWarning, lambda: ifd[277])
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert 0 == reloaded.tag_v2[41988].numerator
assert 0 == reloaded.tag_v2[41988].denominator
def test_ifd_unsigned_rational(tmp_path):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
max_long = 2 ** 32 - 1
# 4 bytes unsigned long
numerator = max_long
info[41493] = TiffImagePlugin.IFDRational(numerator, 1)
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert max_long == reloaded.tag_v2[41493].numerator
assert 1 == reloaded.tag_v2[41493].denominator
# out of bounds of 4 byte unsigned long
numerator = max_long + 1
info[41493] = TiffImagePlugin.IFDRational(numerator, 1)
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert max_long == reloaded.tag_v2[41493].numerator
assert 1 == reloaded.tag_v2[41493].denominator
def test_ifd_signed_rational(tmp_path):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
# pair of 4 byte signed longs
numerator = 2 ** 31 - 1
denominator = -(2 ** 31)
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert numerator == reloaded.tag_v2[37380].numerator
assert denominator == reloaded.tag_v2[37380].denominator
numerator = -(2 ** 31)
denominator = 2 ** 31 - 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert numerator == reloaded.tag_v2[37380].numerator
assert denominator == reloaded.tag_v2[37380].denominator
# out of bounds of 4 byte signed long
numerator = -(2 ** 31) - 1
denominator = 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert 2 ** 31 - 1 == reloaded.tag_v2[37380].numerator
assert -1 == reloaded.tag_v2[37380].denominator
def test_ifd_signed_long(tmp_path):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
info[37000] = -60000
out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded:
assert reloaded.tag_v2[37000] == -60000
def test_empty_values():
data = io.BytesIO(
b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00"
b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a "
b"text\x00\x00"
)
head = data.read(8)
info = TiffImagePlugin.ImageFileDirectory_v2(head)
info.load(data)
# Should not raise ValueError.
info = dict(info)
assert 33432 in info
def test_PhotoshopInfo(tmp_path):
with Image.open("Tests/images/issue_2278.tif") as im:
assert len(im.tag_v2[34377]) == 1
assert isinstance(im.tag_v2[34377][0], bytes)
out = str(tmp_path / "temp.tiff")
im.save(out)
with Image.open(out) as reloaded:
assert len(reloaded.tag_v2[34377]) == 1
assert isinstance(reloaded.tag_v2[34377][0], bytes)
def test_too_many_entries():
ifd = TiffImagePlugin.ImageFileDirectory_v2()
# 277: ("SamplesPerPixel", SHORT, 1),
ifd._tagdata[277] = struct.pack("hh", 4, 4)
ifd.tagtype[277] = TiffTags.SHORT
# Should not raise ValueError.
pytest.warns(UserWarning, lambda: ifd[277])

View File

@ -3,142 +3,134 @@ import itertools
from PIL import Image
from .helper import PillowTestCase, assert_image_similar, hopper
from .helper import assert_image_similar, hopper
class TestFormatHSV(PillowTestCase):
def int_to_float(self, i):
return i / 255
def int_to_float(i):
return i / 255
def str_to_float(self, i):
return ord(i) / 255
def tuple_to_ints(self, tp):
x, y, z = tp
return int(x * 255.0), int(y * 255.0), int(z * 255.0)
def str_to_float(i):
return ord(i) / 255
def test_sanity(self):
Image.new("HSV", (100, 100))
def wedge(self):
w = Image._wedge()
w90 = w.rotate(90)
def tuple_to_ints(tp):
x, y, z = tp
return int(x * 255.0), int(y * 255.0), int(z * 255.0)
(px, h) = w.size
r = Image.new("L", (px * 3, h))
g = r.copy()
b = r.copy()
def test_sanity():
Image.new("HSV", (100, 100))
r.paste(w, (0, 0))
r.paste(w90, (px, 0))
g.paste(w90, (0, 0))
g.paste(w, (2 * px, 0))
def wedge():
w = Image._wedge()
w90 = w.rotate(90)
b.paste(w, (px, 0))
b.paste(w90, (2 * px, 0))
(px, h) = w.size
img = Image.merge("RGB", (r, g, b))
r = Image.new("L", (px * 3, h))
g = r.copy()
b = r.copy()
return img
r.paste(w, (0, 0))
r.paste(w90, (px, 0))
def to_xxx_colorsys(self, im, func, mode):
# convert the hard way using the library colorsys routines.
g.paste(w90, (0, 0))
g.paste(w, (2 * px, 0))
(r, g, b) = im.split()
b.paste(w, (px, 0))
b.paste(w90, (2 * px, 0))
conv_func = self.int_to_float
img = Image.merge("RGB", (r, g, b))
converted = [
self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b)))
for (_r, _g, _b) in itertools.zip_longest(
r.tobytes(), g.tobytes(), b.tobytes()
)
]
return img
new_bytes = b"".join(
bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted
)
hsv = Image.frombytes(mode, r.size, new_bytes)
def to_xxx_colorsys(im, func, mode):
# convert the hard way using the library colorsys routines.
return hsv
(r, g, b) = im.split()
def to_hsv_colorsys(self, im):
return self.to_xxx_colorsys(im, colorsys.rgb_to_hsv, "HSV")
conv_func = int_to_float
def to_rgb_colorsys(self, im):
return self.to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB")
converted = [
tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b)))
for (_r, _g, _b) in itertools.zip_longest(r.tobytes(), g.tobytes(), b.tobytes())
]
def test_wedge(self):
src = self.wedge().resize((3 * 32, 32), Image.BILINEAR)
im = src.convert("HSV")
comparable = self.to_hsv_colorsys(src)
new_bytes = b"".join(
bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted
)
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
)
assert_image_similar(
im.getchannel(1),
comparable.getchannel(1),
1,
"Saturation conversion is wrong",
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
)
hsv = Image.frombytes(mode, r.size, new_bytes)
comparable = src
im = im.convert("RGB")
return hsv
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong"
)
assert_image_similar(
im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong"
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong"
)
def test_convert(self):
im = hopper("RGB").convert("HSV")
comparable = self.to_hsv_colorsys(hopper("RGB"))
def to_hsv_colorsys(im):
return to_xxx_colorsys(im, colorsys.rgb_to_hsv, "HSV")
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
)
assert_image_similar(
im.getchannel(1),
comparable.getchannel(1),
1,
"Saturation conversion is wrong",
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
)
def test_hsv_to_rgb(self):
comparable = self.to_hsv_colorsys(hopper("RGB"))
converted = comparable.convert("RGB")
comparable = self.to_rgb_colorsys(comparable)
def to_rgb_colorsys(im):
return to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB")
assert_image_similar(
converted.getchannel(0),
comparable.getchannel(0),
3,
"R conversion is wrong",
)
assert_image_similar(
converted.getchannel(1),
comparable.getchannel(1),
3,
"G conversion is wrong",
)
assert_image_similar(
converted.getchannel(2),
comparable.getchannel(2),
3,
"B conversion is wrong",
)
def test_wedge():
src = wedge().resize((3 * 32, 32), Image.BILINEAR)
im = src.convert("HSV")
comparable = to_hsv_colorsys(src)
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
)
assert_image_similar(
im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong",
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
)
comparable = src
im = im.convert("RGB")
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong"
)
assert_image_similar(
im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong"
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong"
)
def test_convert():
im = hopper("RGB").convert("HSV")
comparable = to_hsv_colorsys(hopper("RGB"))
assert_image_similar(
im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong"
)
assert_image_similar(
im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong",
)
assert_image_similar(
im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong"
)
def test_hsv_to_rgb():
comparable = to_hsv_colorsys(hopper("RGB"))
converted = comparable.convert("RGB")
comparable = to_rgb_colorsys(comparable)
assert_image_similar(
converted.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong",
)
assert_image_similar(
converted.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong",
)
assert_image_similar(
converted.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong",
)

View File

@ -2,13 +2,12 @@ import ctypes
import os
import subprocess
import sys
import unittest
from distutils import ccompiler, sysconfig
import pytest
from PIL import Image
from .helper import PillowTestCase, assert_image_equal, hopper, is_win32, on_ci
from .helper import assert_image_equal, hopper, is_win32, on_ci
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
@ -22,17 +21,17 @@ else:
cffi = None
class AccessTest(PillowTestCase):
class AccessTest:
# initial value
_init_cffi_access = Image.USE_CFFI_ACCESS
_need_cffi_access = False
@classmethod
def setUpClass(cls):
def setup_class(cls):
Image.USE_CFFI_ACCESS = cls._need_cffi_access
@classmethod
def tearDownClass(cls):
def teardown_class(cls):
Image.USE_CFFI_ACCESS = cls._init_cffi_access
@ -200,17 +199,17 @@ class TestImageGetPixel(AccessTest):
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
@unittest.skipIf(cffi is None, "No cffi")
@pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiPutPixel(TestImagePutPixel):
_need_cffi_access = True
@unittest.skipIf(cffi is None, "No cffi")
@pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiGetPixel(TestImageGetPixel):
_need_cffi_access = True
@unittest.skipIf(cffi is None, "No cffi")
@pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffi(AccessTest):
_need_cffi_access = True
@ -326,10 +325,11 @@ class TestCffi(AccessTest):
assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0)
class TestEmbeddable(unittest.TestCase):
@unittest.skipIf(
class TestEmbeddable:
@pytest.mark.skipif(
not is_win32() or on_ci(),
"Failing on AppVeyor / GitHub Actions when run from subprocess, not from shell",
reason="Failing on AppVeyor / GitHub Actions when run from subprocess, "
"not from shell",
)
def test_embeddable(self):
with open("embed_pil.c", "w") as fh:

View File

@ -1,250 +1,261 @@
import pytest
from PIL import Image
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
class TestImageConvert(PillowTestCase):
def test_sanity(self):
def convert(im, mode):
out = im.convert(mode)
assert out.mode == mode
assert out.size == im.size
def test_sanity():
def convert(im, mode):
out = im.convert(mode)
assert out.mode == mode
assert out.size == im.size
modes = (
"1",
"L",
"LA",
"P",
"PA",
"I",
"F",
"RGB",
"RGBA",
"RGBX",
"CMYK",
"YCbCr",
"HSV",
)
modes = (
"1",
"L",
"LA",
"P",
"PA",
"I",
"F",
"RGB",
"RGBA",
"RGBX",
"CMYK",
"YCbCr",
"HSV",
)
for mode in modes:
im = hopper(mode)
for mode in modes:
im = hopper(mode)
for mode in modes:
convert(im, mode)
convert(im, mode)
# Check 0
im = Image.new(mode, (0, 0))
for mode in modes:
convert(im, mode)
# Check 0
im = Image.new(mode, (0, 0))
for mode in modes:
convert(im, mode)
def test_default(self):
im = hopper("P")
assert_image(im, "P", im.size)
im = im.convert()
assert_image(im, "RGB", im.size)
im = im.convert()
assert_image(im, "RGB", im.size)
def test_default():
# ref https://github.com/python-pillow/Pillow/issues/274
im = hopper("P")
assert_image(im, "P", im.size)
im = im.convert()
assert_image(im, "RGB", im.size)
im = im.convert()
assert_image(im, "RGB", im.size)
def _test_float_conversion(self, im):
orig = im.getpixel((5, 5))
converted = im.convert("F").getpixel((5, 5))
assert orig == converted
def test_8bit(self):
with Image.open("Tests/images/hopper.jpg") as im:
self._test_float_conversion(im.convert("L"))
# ref https://github.com/python-pillow/Pillow/issues/274
def test_16bit(self):
with Image.open("Tests/images/16bit.cropped.tif") as im:
self._test_float_conversion(im)
def test_16bit_workaround(self):
with Image.open("Tests/images/16bit.cropped.tif") as im:
self._test_float_conversion(im.convert("I"))
def _test_float_conversion(im):
orig = im.getpixel((5, 5))
converted = im.convert("F").getpixel((5, 5))
assert orig == converted
def test_rgba_p(self):
im = hopper("RGBA")
im.putalpha(hopper("L"))
converted = im.convert("P")
comparable = converted.convert("RGBA")
def test_8bit():
with Image.open("Tests/images/hopper.jpg") as im:
_test_float_conversion(im.convert("L"))
assert_image_similar(im, comparable, 20)
def test_trns_p(self):
im = hopper("P")
im.info["transparency"] = 0
def test_16bit():
with Image.open("Tests/images/16bit.cropped.tif") as im:
_test_float_conversion(im)
f = self.tempfile("temp.png")
im_l = im.convert("L")
assert im_l.info["transparency"] == 0 # undone
im_l.save(f)
def test_16bit_workaround():
with Image.open("Tests/images/16bit.cropped.tif") as im:
_test_float_conversion(im.convert("I"))
im_rgb = im.convert("RGB")
assert im_rgb.info["transparency"] == (0, 0, 0) # undone
im_rgb.save(f)
# ref https://github.com/python-pillow/Pillow/issues/664
def test_rgba_p():
im = hopper("RGBA")
im.putalpha(hopper("L"))
def test_trns_p_rgba(self):
# Arrange
im = hopper("P")
im.info["transparency"] = 128
converted = im.convert("P")
comparable = converted.convert("RGBA")
# Act
im_rgba = im.convert("RGBA")
assert_image_similar(im, comparable, 20)
# Assert
assert "transparency" not in im_rgba.info
# https://github.com/python-pillow/Pillow/issues/2702
assert im_rgba.palette is None
def test_trns_l(self):
im = hopper("L")
im.info["transparency"] = 128
def test_trns_p(tmp_path):
im = hopper("P")
im.info["transparency"] = 0
f = self.tempfile("temp.png")
f = str(tmp_path / "temp.png")
im_rgb = im.convert("RGB")
assert im_rgb.info["transparency"] == (128, 128, 128) # undone
im_rgb.save(f)
im_l = im.convert("L")
assert im_l.info["transparency"] == 0 # undone
im_l.save(f)
im_rgb = im.convert("RGB")
assert im_rgb.info["transparency"] == (0, 0, 0) # undone
im_rgb.save(f)
# ref https://github.com/python-pillow/Pillow/issues/664
def test_trns_p_rgba():
# Arrange
im = hopper("P")
im.info["transparency"] = 128
# Act
im_rgba = im.convert("RGBA")
# Assert
assert "transparency" not in im_rgba.info
# https://github.com/python-pillow/Pillow/issues/2702
assert im_rgba.palette is None
def test_trns_l(tmp_path):
im = hopper("L")
im.info["transparency"] = 128
f = str(tmp_path / "temp.png")
im_rgb = im.convert("RGB")
assert im_rgb.info["transparency"] == (128, 128, 128) # undone
im_rgb.save(f)
im_p = im.convert("P")
assert "transparency" in im_p.info
im_p.save(f)
im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE)
assert "transparency" not in im_p.info
im_p.save(f)
def test_trns_RGB(tmp_path):
im = hopper("RGB")
im.info["transparency"] = im.getpixel((0, 0))
f = str(tmp_path / "temp.png")
im_l = im.convert("L")
assert im_l.info["transparency"] == im_l.getpixel((0, 0)) # undone
im_l.save(f)
im_p = im.convert("P")
assert "transparency" in im_p.info
im_p.save(f)
im_rgba = im.convert("RGBA")
assert "transparency" not in im_rgba.info
im_rgba.save(f)
im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE)
assert "transparency" not in im_p.info
im_p.save(f)
def test_gif_with_rgba_palette_to_p():
# See https://github.com/python-pillow/Pillow/issues/2433
with Image.open("Tests/images/hopper.gif") as im:
im.info["transparency"] = 255
im.load()
assert im.palette.mode == "RGBA"
im_p = im.convert("P")
assert "transparency" in im_p.info
im_p.save(f)
im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE)
assert "transparency" not in im_p.info
im_p.save(f)
# Should not raise ValueError: unrecognized raw mode
im_p.load()
def test_trns_RGB(self):
def test_p_la():
im = hopper("RGBA")
alpha = hopper("L")
im.putalpha(alpha)
comparable = im.convert("P").convert("LA").getchannel("A")
assert_image_similar(alpha, comparable, 5)
def test_matrix_illegal_conversion():
# Arrange
im = hopper("CMYK")
# fmt: off
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
# fmt: on
assert im.mode != "RGB"
# Act / Assert
with pytest.raises(ValueError):
im.convert(mode="CMYK", matrix=matrix)
def test_matrix_wrong_mode():
# Arrange
im = hopper("L")
# fmt: off
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
# fmt: on
assert im.mode == "L"
# Act / Assert
with pytest.raises(ValueError):
im.convert(mode="L", matrix=matrix)
def test_matrix_xyz():
def matrix_convert(mode):
# Arrange
im = hopper("RGB")
im.info["transparency"] = im.getpixel((0, 0))
f = self.tempfile("temp.png")
im_l = im.convert("L")
assert im_l.info["transparency"] == im_l.getpixel((0, 0)) # undone
im_l.save(f)
im_p = im.convert("P")
assert "transparency" in im_p.info
im_p.save(f)
im_rgba = im.convert("RGBA")
assert "transparency" not in im_rgba.info
im_rgba.save(f)
im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE)
assert "transparency" not in im_p.info
im_p.save(f)
def test_gif_with_rgba_palette_to_p(self):
# See https://github.com/python-pillow/Pillow/issues/2433
with Image.open("Tests/images/hopper.gif") as im:
im.info["transparency"] = 255
im.load()
assert im.palette.mode == "RGBA"
im_p = im.convert("P")
# Should not raise ValueError: unrecognized raw mode
im_p.load()
def test_p_la(self):
im = hopper("RGBA")
alpha = hopper("L")
im.putalpha(alpha)
comparable = im.convert("P").convert("LA").getchannel("A")
assert_image_similar(alpha, comparable, 5)
def test_matrix_illegal_conversion(self):
# Arrange
im = hopper("CMYK")
im.info["transparency"] = (255, 0, 0)
# fmt: off
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
# fmt: on
assert im.mode != "RGB"
# Act / Assert
with pytest.raises(ValueError):
im.convert(mode="CMYK", matrix=matrix)
def test_matrix_wrong_mode(self):
# Arrange
im = hopper("L")
# fmt: off
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
# fmt: on
assert im.mode == "L"
# Act / Assert
with pytest.raises(ValueError):
im.convert(mode="L", matrix=matrix)
def test_matrix_xyz(self):
def matrix_convert(mode):
# Arrange
im = hopper("RGB")
im.info["transparency"] = (255, 0, 0)
# fmt: off
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
# fmt: on
assert im.mode == "RGB"
# Act
# Convert an RGB image to the CIE XYZ colour space
converted_im = im.convert(mode=mode, matrix=matrix)
# Assert
assert converted_im.mode == mode
assert converted_im.size == im.size
with Image.open("Tests/images/hopper-XYZ.png") as target:
if converted_im.mode == "RGB":
assert_image_similar(converted_im, target, 3)
assert converted_im.info["transparency"] == (105, 54, 4)
else:
assert_image_similar(converted_im, target.getchannel(0), 1)
assert converted_im.info["transparency"] == 105
matrix_convert("RGB")
matrix_convert("L")
def test_matrix_identity(self):
# Arrange
im = hopper("RGB")
# fmt: off
identity_matrix = (
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0)
# fmt: on
assert im.mode == "RGB"
# Act
# Convert with an identity matrix
converted_im = im.convert(mode="RGB", matrix=identity_matrix)
# Convert an RGB image to the CIE XYZ colour space
converted_im = im.convert(mode=mode, matrix=matrix)
# Assert
# No change
assert_image_equal(converted_im, im)
assert converted_im.mode == mode
assert converted_im.size == im.size
with Image.open("Tests/images/hopper-XYZ.png") as target:
if converted_im.mode == "RGB":
assert_image_similar(converted_im, target, 3)
assert converted_im.info["transparency"] == (105, 54, 4)
else:
assert_image_similar(converted_im, target.getchannel(0), 1)
assert converted_im.info["transparency"] == 105
matrix_convert("RGB")
matrix_convert("L")
def test_matrix_identity():
# Arrange
im = hopper("RGB")
# fmt: off
identity_matrix = (
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0)
# fmt: on
assert im.mode == "RGB"
# Act
# Convert with an identity matrix
converted_im = im.convert(mode="RGB", matrix=identity_matrix)
# Assert
# No change
assert_image_equal(converted_im, im)

View File

@ -10,29 +10,32 @@ def test_sanity():
def test_bbox():
def check(im, fill_color):
assert im.getbbox() is None
im.paste(fill_color, (10, 25, 90, 75))
assert im.getbbox() == (10, 25, 90, 75)
im.paste(fill_color, (25, 10, 75, 90))
assert im.getbbox() == (10, 10, 90, 90)
im.paste(fill_color, (-10, -10, 110, 110))
assert im.getbbox() == (0, 0, 100, 100)
# 8-bit mode
im = Image.new("L", (100, 100), 0)
assert im.getbbox() is None
im.paste(255, (10, 25, 90, 75))
assert im.getbbox() == (10, 25, 90, 75)
im.paste(255, (25, 10, 75, 90))
assert im.getbbox() == (10, 10, 90, 90)
im.paste(255, (-10, -10, 110, 110))
assert im.getbbox() == (0, 0, 100, 100)
check(im, 255)
# 32-bit mode
im = Image.new("RGB", (100, 100), 0)
assert im.getbbox() is None
check(im, 255)
im.paste(255, (10, 25, 90, 75))
assert im.getbbox() == (10, 25, 90, 75)
for mode in ("RGBA", "RGBa"):
for color in ((0, 0, 0, 0), (127, 127, 127, 0), (255, 255, 255, 0)):
im = Image.new(mode, (100, 100), color)
check(im, (255, 255, 255, 255))
im.paste(255, (25, 10, 75, 90))
assert im.getbbox() == (10, 10, 90, 90)
im.paste(255, (-10, -10, 110, 110))
assert im.getbbox() == (0, 0, 100, 100)
for mode in ("La", "LA", "PA"):
for color in ((0, 0), (127, 0), (255, 0)):
im = Image.new(mode, (100, 100), color)
check(im, (255, 255))

View File

@ -1,62 +1,63 @@
from PIL import Image, features
from .helper import PillowTestCase, assert_image_equal, hopper
from .helper import assert_image_equal, hopper
class TestImageSplit(PillowTestCase):
def test_split(self):
def split(mode):
layers = hopper(mode).split()
return [(i.mode, i.size[0], i.size[1]) for i in layers]
def test_split():
def split(mode):
layers = hopper(mode).split()
return [(i.mode, i.size[0], i.size[1]) for i in layers]
assert split("1") == [("1", 128, 128)]
assert split("L") == [("L", 128, 128)]
assert split("I") == [("I", 128, 128)]
assert split("F") == [("F", 128, 128)]
assert split("P") == [("P", 128, 128)]
assert split("RGB") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)]
assert split("RGBA") == [
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
]
assert split("CMYK") == [
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
]
assert split("YCbCr") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)]
assert split("1") == [("1", 128, 128)]
assert split("L") == [("L", 128, 128)]
assert split("I") == [("I", 128, 128)]
assert split("F") == [("F", 128, 128)]
assert split("P") == [("P", 128, 128)]
assert split("RGB") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)]
assert split("RGBA") == [
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
]
assert split("CMYK") == [
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
("L", 128, 128),
]
assert split("YCbCr") == [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)]
def test_split_merge(self):
def split_merge(mode):
return Image.merge(mode, hopper(mode).split())
assert_image_equal(hopper("1"), split_merge("1"))
assert_image_equal(hopper("L"), split_merge("L"))
assert_image_equal(hopper("I"), split_merge("I"))
assert_image_equal(hopper("F"), split_merge("F"))
assert_image_equal(hopper("P"), split_merge("P"))
assert_image_equal(hopper("RGB"), split_merge("RGB"))
assert_image_equal(hopper("RGBA"), split_merge("RGBA"))
assert_image_equal(hopper("CMYK"), split_merge("CMYK"))
assert_image_equal(hopper("YCbCr"), split_merge("YCbCr"))
def test_split_merge():
def split_merge(mode):
return Image.merge(mode, hopper(mode).split())
def test_split_open(self):
if features.check("zlib"):
test_file = self.tempfile("temp.png")
else:
test_file = self.tempfile("temp.pcx")
assert_image_equal(hopper("1"), split_merge("1"))
assert_image_equal(hopper("L"), split_merge("L"))
assert_image_equal(hopper("I"), split_merge("I"))
assert_image_equal(hopper("F"), split_merge("F"))
assert_image_equal(hopper("P"), split_merge("P"))
assert_image_equal(hopper("RGB"), split_merge("RGB"))
assert_image_equal(hopper("RGBA"), split_merge("RGBA"))
assert_image_equal(hopper("CMYK"), split_merge("CMYK"))
assert_image_equal(hopper("YCbCr"), split_merge("YCbCr"))
def split_open(mode):
hopper(mode).save(test_file)
with Image.open(test_file) as im:
return len(im.split())
assert split_open("1") == 1
assert split_open("L") == 1
assert split_open("P") == 1
assert split_open("RGB") == 3
if features.check("zlib"):
assert split_open("RGBA") == 4
def test_split_open(tmp_path):
if features.check("zlib"):
test_file = str(tmp_path / "temp.png")
else:
test_file = str(tmp_path / "temp.pcx")
def split_open(mode):
hopper(mode).save(test_file)
with Image.open(test_file) as im:
return len(im.split())
assert split_open("1") == 1
assert split_open("L") == 1
assert split_open("P") == 1
assert split_open("RGB") == 3
if features.check("zlib"):
assert split_open("RGBA") == 4

View File

@ -38,9 +38,9 @@ def test_aspect():
im.thumbnail((100, 50))
assert im.size == (100, 50)
im = Image.new("L", (128, 128))
im = Image.new("L", (64, 64))
im.thumbnail((100, 100))
assert im.size == (100, 100)
assert im.size == (64, 64)
im = Image.new("L", (256, 162)) # ratio is 1.5802469136
im.thumbnail((33, 33))
@ -50,11 +50,23 @@ def test_aspect():
im.thumbnail((33, 33))
assert im.size == (21, 33) # ratio is 0.6363636364
im = Image.new("L", (145, 100)) # ratio is 1.45
im.thumbnail((50, 50))
assert im.size == (50, 34) # ratio is 1.47058823529
im = Image.new("L", (100, 145)) # ratio is 0.689655172414
im.thumbnail((50, 50))
assert im.size == (34, 50) # ratio is 0.68
im = Image.new("L", (100, 30)) # ratio is 3.333333333333
im.thumbnail((75, 75))
assert im.size == (75, 23) # ratio is 3.260869565217
def test_float():
im = Image.new("L", (128, 128))
im.thumbnail((99.9, 99.9))
assert im.size == (100, 100)
assert im.size == (99, 99)
def test_no_resize():

View File

@ -944,6 +944,22 @@ def test_stroke():
)
@skip_unless_feature("freetype2")
def test_stroke_descender():
# Arrange
im = Image.new("RGB", (120, 130))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
# Act
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
# Assert
assert_image_similar(
im, Image.open("Tests/images/imagedraw_stroke_descender.png"), 6.76
)
@skip_unless_feature("freetype2")
def test_stroke_multiline():
# Arrange

View File

@ -1,140 +1,149 @@
import pytest
from PIL import Image, ImagePalette
from .helper import PillowTestCase, assert_image_equal
from .helper import assert_image_equal
class TestImagePalette(PillowTestCase):
def test_sanity(self):
def test_sanity():
ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
with pytest.raises(ValueError):
ImagePalette.ImagePalette("RGB", list(range(256)) * 2)
ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
with pytest.raises(ValueError):
ImagePalette.ImagePalette("RGB", list(range(256)) * 2)
def test_getcolor(self):
palette = ImagePalette.ImagePalette()
def test_getcolor():
test_map = {}
for i in range(256):
test_map[palette.getcolor((i, i, i))] = i
palette = ImagePalette.ImagePalette()
assert len(test_map) == 256
with pytest.raises(ValueError):
palette.getcolor((1, 2, 3))
test_map = {}
for i in range(256):
test_map[palette.getcolor((i, i, i))] = i
# Test unknown color specifier
with pytest.raises(ValueError):
palette.getcolor("unknown")
assert len(test_map) == 256
with pytest.raises(ValueError):
palette.getcolor((1, 2, 3))
def test_file(self):
# Test unknown color specifier
with pytest.raises(ValueError):
palette.getcolor("unknown")
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
f = self.tempfile("temp.lut")
def test_file(tmp_path):
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
f = str(tmp_path / "temp.lut")
palette.save(f)
p = ImagePalette.load(f)
# load returns raw palette information
assert len(p[0]) == 768
assert p[1] == "RGB"
p = ImagePalette.raw(p[1], p[0])
assert isinstance(p, ImagePalette.ImagePalette)
assert p.palette == palette.tobytes()
def test_make_linear_lut():
# Arrange
black = 0
white = 255
# Act
lut = ImagePalette.make_linear_lut(black, white)
# Assert
assert isinstance(lut, list)
assert len(lut) == 256
# Check values
for i in range(0, len(lut)):
assert lut[i] == i
def test_make_linear_lut_not_yet_implemented():
# Update after FIXME
# Arrange
black = 1
white = 255
# Act
with pytest.raises(NotImplementedError):
ImagePalette.make_linear_lut(black, white)
def test_make_gamma_lut():
# Arrange
exp = 5
# Act
lut = ImagePalette.make_gamma_lut(exp)
# Assert
assert isinstance(lut, list)
assert len(lut) == 256
# Check a few values
assert lut[0] == 0
assert lut[63] == 0
assert lut[127] == 8
assert lut[191] == 60
assert lut[255] == 255
def test_rawmode_valueerrors(tmp_path):
# Arrange
palette = ImagePalette.raw("RGB", list(range(256)) * 3)
# Act / Assert
with pytest.raises(ValueError):
palette.tobytes()
with pytest.raises(ValueError):
palette.getcolor((1, 2, 3))
f = str(tmp_path / "temp.lut")
with pytest.raises(ValueError):
palette.save(f)
p = ImagePalette.load(f)
# load returns raw palette information
assert len(p[0]) == 768
assert p[1] == "RGB"
def test_getdata():
# Arrange
data_in = list(range(256)) * 3
palette = ImagePalette.ImagePalette("RGB", data_in)
p = ImagePalette.raw(p[1], p[0])
assert isinstance(p, ImagePalette.ImagePalette)
assert p.palette == palette.tobytes()
# Act
mode, data_out = palette.getdata()
def test_make_linear_lut(self):
# Arrange
black = 0
white = 255
# Assert
assert mode == "RGB;L"
# Act
lut = ImagePalette.make_linear_lut(black, white)
# Assert
assert isinstance(lut, list)
assert len(lut) == 256
# Check values
for i in range(0, len(lut)):
assert lut[i] == i
def test_rawmode_getdata():
# Arrange
data_in = list(range(256)) * 3
palette = ImagePalette.raw("RGB", data_in)
def test_make_linear_lut_not_yet_implemented(self):
# Update after FIXME
# Arrange
black = 1
white = 255
# Act
rawmode, data_out = palette.getdata()
# Act
with pytest.raises(NotImplementedError):
ImagePalette.make_linear_lut(black, white)
# Assert
assert rawmode == "RGB"
assert data_in == data_out
def test_make_gamma_lut(self):
# Arrange
exp = 5
# Act
lut = ImagePalette.make_gamma_lut(exp)
def test_2bit_palette(tmp_path):
# issue #2258, 2 bit palettes are corrupted.
outfile = str(tmp_path / "temp.png")
# Assert
assert isinstance(lut, list)
assert len(lut) == 256
# Check a few values
assert lut[0] == 0
assert lut[63] == 0
assert lut[127] == 8
assert lut[191] == 60
assert lut[255] == 255
rgb = b"\x00" * 2 + b"\x01" * 2 + b"\x02" * 2
img = Image.frombytes("P", (6, 1), rgb)
img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB
img.save(outfile, format="PNG")
def test_rawmode_valueerrors(self):
# Arrange
palette = ImagePalette.raw("RGB", list(range(256)) * 3)
with Image.open(outfile) as reloaded:
assert_image_equal(img, reloaded)
# Act / Assert
with pytest.raises(ValueError):
palette.tobytes()
with pytest.raises(ValueError):
palette.getcolor((1, 2, 3))
f = self.tempfile("temp.lut")
with pytest.raises(ValueError):
palette.save(f)
def test_getdata(self):
# Arrange
data_in = list(range(256)) * 3
palette = ImagePalette.ImagePalette("RGB", data_in)
# Act
mode, data_out = palette.getdata()
# Assert
assert mode == "RGB;L"
def test_rawmode_getdata(self):
# Arrange
data_in = list(range(256)) * 3
palette = ImagePalette.raw("RGB", data_in)
# Act
rawmode, data_out = palette.getdata()
# Assert
assert rawmode == "RGB"
assert data_in == data_out
def test_2bit_palette(self):
# issue #2258, 2 bit palettes are corrupted.
outfile = self.tempfile("temp.png")
rgb = b"\x00" * 2 + b"\x01" * 2 + b"\x02" * 2
img = Image.frombytes("P", (6, 1), rgb)
img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB
img.save(outfile, format="PNG")
with Image.open(outfile) as reloaded:
assert_image_equal(img, reloaded)
def test_invalid_palette(self):
with pytest.raises(IOError):
ImagePalette.load("Tests/images/hopper.jpg")
def test_invalid_palette():
with pytest.raises(IOError):
ImagePalette.load("Tests/images/hopper.jpg")

View File

@ -1,98 +1,105 @@
import pytest
from PIL import Image, ImageSequence, TiffImagePlugin
from .helper import PillowTestCase, assert_image_equal, hopper, skip_unless_feature
from .helper import assert_image_equal, hopper, skip_unless_feature
class TestImageSequence(PillowTestCase):
def test_sanity(self):
def test_sanity(tmp_path):
test_file = self.tempfile("temp.im")
test_file = str(tmp_path / "temp.im")
im = hopper("RGB")
im.save(test_file)
im = hopper("RGB")
im.save(test_file)
seq = ImageSequence.Iterator(im)
seq = ImageSequence.Iterator(im)
index = 0
for frame in seq:
assert_image_equal(im, frame)
assert im.tell() == index
index += 1
index = 0
for frame in seq:
assert_image_equal(im, frame)
assert im.tell() == index
index += 1
assert index == 1
assert index == 1
with pytest.raises(AttributeError):
ImageSequence.Iterator(0)
with pytest.raises(AttributeError):
ImageSequence.Iterator(0)
def test_iterator(self):
with Image.open("Tests/images/multipage.tiff") as im:
i = ImageSequence.Iterator(im)
for index in range(0, im.n_frames):
assert i[index] == next(i)
with pytest.raises(IndexError):
i[index + 1]
with pytest.raises(StopIteration):
next(i)
def test_iterator_min_frame(self):
with Image.open("Tests/images/hopper.psd") as im:
i = ImageSequence.Iterator(im)
for index in range(1, im.n_frames):
assert i[index] == next(i)
def test_iterator():
with Image.open("Tests/images/multipage.tiff") as im:
i = ImageSequence.Iterator(im)
for index in range(0, im.n_frames):
assert i[index] == next(i)
with pytest.raises(IndexError):
i[index + 1]
with pytest.raises(StopIteration):
next(i)
def _test_multipage_tiff(self):
with Image.open("Tests/images/multipage.tiff") as im:
for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load()
assert index == im.tell()
frame.convert("RGB")
def test_tiff(self):
self._test_multipage_tiff()
def test_iterator_min_frame():
with Image.open("Tests/images/hopper.psd") as im:
i = ImageSequence.Iterator(im)
for index in range(1, im.n_frames):
assert i[index] == next(i)
@skip_unless_feature("libtiff")
def test_libtiff(self):
TiffImagePlugin.READ_LIBTIFF = True
self._test_multipage_tiff()
TiffImagePlugin.READ_LIBTIFF = False
def test_consecutive(self):
with Image.open("Tests/images/multipage.tiff") as im:
firstFrame = None
for frame in ImageSequence.Iterator(im):
if firstFrame is None:
firstFrame = frame.copy()
for frame in ImageSequence.Iterator(im):
assert_image_equal(frame, firstFrame)
break
def _test_multipage_tiff():
with Image.open("Tests/images/multipage.tiff") as im:
for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load()
assert index == im.tell()
frame.convert("RGB")
def test_palette_mmap(self):
# Using mmap in ImageFile can require to reload the palette.
with Image.open("Tests/images/multipage-mmap.tiff") as im:
color1 = im.getpalette()[0:3]
im.seek(0)
color2 = im.getpalette()[0:3]
assert color1 == color2
def test_all_frames(self):
# Test a single image
with Image.open("Tests/images/iss634.gif") as im:
ims = ImageSequence.all_frames(im)
def test_tiff():
_test_multipage_tiff()
assert len(ims) == 42
for i, im_frame in enumerate(ims):
assert im_frame is not im
im.seek(i)
assert_image_equal(im, im_frame)
@skip_unless_feature("libtiff")
def test_libtiff():
TiffImagePlugin.READ_LIBTIFF = True
_test_multipage_tiff()
TiffImagePlugin.READ_LIBTIFF = False
# Test a series of images
ims = ImageSequence.all_frames([im, hopper(), im])
assert len(ims) == 85
# Test an operation
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
for i, im_frame in enumerate(ims):
im.seek(i)
assert_image_equal(im.rotate(90), im_frame)
def test_consecutive():
with Image.open("Tests/images/multipage.tiff") as im:
firstFrame = None
for frame in ImageSequence.Iterator(im):
if firstFrame is None:
firstFrame = frame.copy()
for frame in ImageSequence.Iterator(im):
assert_image_equal(frame, firstFrame)
break
def test_palette_mmap():
# Using mmap in ImageFile can require to reload the palette.
with Image.open("Tests/images/multipage-mmap.tiff") as im:
color1 = im.getpalette()[0:3]
im.seek(0)
color2 = im.getpalette()[0:3]
assert color1 == color2
def test_all_frames():
# Test a single image
with Image.open("Tests/images/iss634.gif") as im:
ims = ImageSequence.all_frames(im)
assert len(ims) == 42
for i, im_frame in enumerate(ims):
assert im_frame is not im
im.seek(i)
assert_image_equal(im, im_frame)
# Test a series of images
ims = ImageSequence.all_frames([im, hopper(), im])
assert len(ims) == 85
# Test an operation
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
for i, im_frame in enumerate(ims):
im.seek(i)
assert_image_equal(im.rotate(90), im_frame)

View File

@ -1,11 +1,10 @@
import unittest
import pytest
from PIL import ImageWin
from .helper import PillowTestCase, hopper, is_win32
from .helper import hopper, is_win32
class TestImageWin(PillowTestCase):
class TestImageWin:
def test_sanity(self):
dir(ImageWin)
@ -32,8 +31,8 @@ class TestImageWin(PillowTestCase):
assert wnd2 == 50
@unittest.skipUnless(is_win32(), "Windows only")
class TestImageWinDib(PillowTestCase):
@pytest.mark.skipif(not is_win32(), reason="Windows only")
class TestImageWinDib:
def test_dib_image(self):
# Arrange
im = hopper()

View File

@ -1,15 +1,9 @@
import shutil
import pytest
from PIL import GifImagePlugin, Image, JpegImagePlugin
from .helper import (
PillowTestCase,
cjpeg_available,
djpeg_available,
is_win32,
netpbm_available,
unittest,
)
from .helper import cjpeg_available, djpeg_available, is_win32, netpbm_available
TEST_JPG = "Tests/images/hopper.jpg"
TEST_GIF = "Tests/images/hopper.gif"
@ -17,38 +11,38 @@ TEST_GIF = "Tests/images/hopper.gif"
test_filenames = ("temp_';", 'temp_";', "temp_'\"|", "temp_'\"||", "temp_'\"&&")
@unittest.skipIf(is_win32(), "requires Unix or macOS")
class TestShellInjection(PillowTestCase):
def assert_save_filename_check(self, src_img, save_func):
@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
class TestShellInjection:
def assert_save_filename_check(self, tmp_path, src_img, save_func):
for filename in test_filenames:
dest_file = self.tempfile(filename)
dest_file = str(tmp_path / filename)
save_func(src_img, 0, dest_file)
# If file can't be opened, shell injection probably occurred
with Image.open(dest_file) as im:
im.load()
@unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg_filename(self):
@pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg_filename(self, tmp_path):
for filename in test_filenames:
src_file = self.tempfile(filename)
src_file = str(tmp_path / filename)
shutil.copy(TEST_JPG, src_file)
with Image.open(src_file) as im:
im.load_djpeg()
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg_filename(self):
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
def test_save_cjpeg_filename(self, tmp_path):
with Image.open(TEST_JPG) as im:
self.assert_save_filename_check(im, JpegImagePlugin._save_cjpeg)
self.assert_save_filename_check(tmp_path, im, JpegImagePlugin._save_cjpeg)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_bmp_mode(self):
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_bmp_mode(self, tmp_path):
with Image.open(TEST_GIF) as im:
im = im.convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_l_mode(self):
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
def test_save_netpbm_filename_l_mode(self, tmp_path):
with Image.open(TEST_GIF) as im:
im = im.convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)

View File

@ -3,56 +3,58 @@ from fractions import Fraction
from PIL import Image, TiffImagePlugin, features
from PIL.TiffImagePlugin import IFDRational
from .helper import PillowTestCase, hopper
from .helper import hopper
class Test_IFDRational(PillowTestCase):
def _test_equal(self, num, denom, target):
def _test_equal(num, denom, target):
t = IFDRational(num, denom)
t = IFDRational(num, denom)
assert target == t
assert t == target
assert target == t
assert t == target
def test_sanity(self):
self._test_equal(1, 1, 1)
self._test_equal(1, 1, Fraction(1, 1))
def test_sanity():
self._test_equal(2, 2, 1)
self._test_equal(1.0, 1, Fraction(1, 1))
_test_equal(1, 1, 1)
_test_equal(1, 1, Fraction(1, 1))
self._test_equal(Fraction(1, 1), 1, Fraction(1, 1))
self._test_equal(IFDRational(1, 1), 1, 1)
_test_equal(2, 2, 1)
_test_equal(1.0, 1, Fraction(1, 1))
self._test_equal(1, 2, Fraction(1, 2))
self._test_equal(1, 2, IFDRational(1, 2))
_test_equal(Fraction(1, 1), 1, Fraction(1, 1))
_test_equal(IFDRational(1, 1), 1, 1)
def test_nonetype(self):
# Fails if the _delegate function doesn't return a valid function
_test_equal(1, 2, Fraction(1, 2))
_test_equal(1, 2, IFDRational(1, 2))
xres = IFDRational(72)
yres = IFDRational(72)
assert xres._val is not None
assert xres.numerator is not None
assert xres.denominator is not None
assert yres._val is not None
assert xres and 1
assert xres and yres
def test_nonetype():
# Fails if the _delegate function doesn't return a valid function
def test_ifd_rational_save(self):
methods = (True, False)
if not features.check("libtiff"):
methods = (False,)
xres = IFDRational(72)
yres = IFDRational(72)
assert xres._val is not None
assert xres.numerator is not None
assert xres.denominator is not None
assert yres._val is not None
for libtiff in methods:
TiffImagePlugin.WRITE_LIBTIFF = libtiff
assert xres and 1
assert xres and yres
im = hopper()
out = self.tempfile("temp.tiff")
res = IFDRational(301, 1)
im.save(out, dpi=(res, res), compression="raw")
with Image.open(out) as reloaded:
assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282])
def test_ifd_rational_save(tmp_path):
methods = (True, False)
if not features.check("libtiff"):
methods = (False,)
for libtiff in methods:
TiffImagePlugin.WRITE_LIBTIFF = libtiff
im = hopper()
out = str(tmp_path / "temp.tiff")
res = IFDRational(301, 1)
im.save(out, dpi=(res, res), compression="raw")
with Image.open(out) as reloaded:
assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282])

View File

@ -1,13 +1,13 @@
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper
from .helper import assert_image_equal, assert_image_similar, hopper
class TestUploader(PillowTestCase):
def check_upload_equal(self):
result = hopper("P").convert("RGB")
target = hopper("RGB")
assert_image_equal(result, target)
def check_upload_equal():
result = hopper("P").convert("RGB")
target = hopper("RGB")
assert_image_equal(result, target)
def check_upload_similar(self):
result = hopper("P").convert("RGB")
target = hopper("RGB")
assert_image_similar(result, target, 0)
def check_upload_similar():
result = hopper("P").convert("RGB")
target = hopper("RGB")
assert_image_similar(result, target, 0)

View File

@ -298,6 +298,11 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
**exif**
Raw EXIF data from the image.
**comment**
A comment about the image.
.. versionadded:: 7.1.0
The :py:meth:`~PIL.Image.Image.save` method supports the following options:

View File

@ -74,7 +74,8 @@ Convert files to JPEG
outfile = f + ".jpg"
if infile != outfile:
try:
Image.open(infile).save(outfile)
with Image.open(infile) as im:
im.save(outfile)
except IOError:
print("cannot convert", infile)

View File

@ -47,8 +47,8 @@ Basic Installation
Install Pillow with :command:`pip`::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
Windows Installation
@ -59,8 +59,8 @@ supported Pythons in both 32 and 64-bit versions in wheel, egg, and
executable installers. These binaries have all of the optional
libraries included except for raqm and libimagequant::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
macOS Installation
@ -71,8 +71,8 @@ versions in the wheel format. These include support for all optional
libraries except libimagequant. Raqm support requires libraqm,
fribidi, and harfbuzz to be installed separately::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
Linux Installation
^^^^^^^^^^^^^^^^^^
@ -82,8 +82,8 @@ versions in the manylinux wheel format. These include support for all
optional libraries except libimagequant. Raqm support requires
libraqm, fribidi, and harfbuzz to be installed separately::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
Most major Linux distributions, including Fedora, Debian/Ubuntu and
ArchLinux also include Pillow in packages that previously contained
@ -195,8 +195,8 @@ Many of Pillow's features require external libraries:
Once you have installed the prerequisites, run::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
If the prerequisites are installed in the standard library locations
for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no
@ -206,7 +206,7 @@ those locations by editing :file:`setup.py` or
:file:`setup.cfg`, or by adding environment variables on the command
line::
CFLAGS="-I/usr/pkg/include" python -m pip install --upgrade Pillow
CFLAGS="-I/usr/pkg/include" python3 -m pip install --upgrade Pillow
If Pillow has been previously built without the required
prerequisites, it may be necessary to manually clear the pip cache or
@ -254,7 +254,7 @@ Sample usage::
or using pip::
python -m pip install --upgrade Pillow --global-option="build_ext" --global-option="--enable-[feature]"
python3 -m pip install --upgrade Pillow --global-option="build_ext" --global-option="--enable-[feature]"
Building on macOS
@ -280,8 +280,8 @@ Then see ``depends/install_raqm_cmake.sh`` to install libraqm.
Now install Pillow with::
python -m pip install --upgrade pip
python -m pip install --upgrade Pillow
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
or from within the uncompressed source directory::

View File

@ -20,14 +20,14 @@ Example: Draw a gray cross over an image
from PIL import Image, ImageDraw
im = Image.open("hopper.jpg")
with Image.open("hopper.jpg") as im:
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, fill=128)
draw.line((0, im.size[1], im.size[0], 0), fill=128)
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, fill=128)
draw.line((0, im.size[1], im.size[0], 0), fill=128)
# write to stdout
im.save(sys.stdout, "PNG")
# write to stdout
im.save(sys.stdout, "PNG")
Concepts

View File

@ -14,12 +14,11 @@ Extracting frames from an animation
from PIL import Image, ImageSequence
im = Image.open("animation.fli")
index = 1
for frame in ImageSequence.Iterator(im):
frame.save("frame%d.png" % index)
index += 1
with Image.open("animation.fli") as im:
index = 1
for frame in ImageSequence.Iterator(im):
frame.save("frame%d.png" % index)
index += 1
The :py:class:`~PIL.ImageSequence.Iterator` class
-------------------------------------------------

View File

@ -17,8 +17,8 @@ changes it.
.. code-block:: python
from PIL import Image
im = Image.open('hopper.jpg')
px = im.load()
with Image.open('hopper.jpg') as im:
px = im.load()
print (px[4,4])
px[4,4] = (0,0,0)
print (px[4,4])

View File

@ -18,8 +18,8 @@ The following script loads an image, accesses one pixel from it, then changes it
.. code-block:: python
from PIL import Image
im = Image.open('hopper.jpg')
px = im.load()
with Image.open('hopper.jpg') as im:
px = im.load()
print (px[4,4])
px[4,4] = (0,0,0)
print (px[4,4])

View File

@ -18,6 +18,27 @@ been resolved.
im = Image.open("hopper.jpg")
im.save("out.jpg", quality=0)
API Additions
=============
Reading JPEG comments
^^^^^^^^^^^^^^^^^^^^^
When opening a JPEG image, the comment may now be read into
:py:attr:`~PIL.Image.Image.info`.
Other Changes
=============
If present, only use alpha channel for bounding box
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the :py:meth:`~PIL.Image.Image.getbbox` method calculates the bounding
box, for an RGB image it trims black pixels. Similarly, for an RGBA image it
would trim black transparent pixels. This is now changed so that if an image
has an alpha channel (RGBA, RGBa, PA, LA, La), any transparent pixels are
trimmed.
Improved APNG support
^^^^^^^^^^^^^^^^^^^^^
@ -25,4 +46,4 @@ Added support for reading and writing Animated Portable Network Graphics (APNG)
The PNG plugin now supports using the :py:meth:`~PIL.Image.Image.seek` method and the
:py:class:`~PIL.ImageSequence.Iterator` class to read APNG frame sequences.
The PNG plugin also now supports using the ``append_images`` argument to write APNG frame
sequences. See :ref:`apng-sequences` for further details.
sequences. See :ref:`apng-sequences` for further details.

View File

@ -141,8 +141,8 @@ def Ghostscript(tile, size, fp, scale=1):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.check_call(command, startupinfo=startupinfo)
im = Image.open(outfile)
im.load()
out_im = Image.open(outfile)
out_im.load()
finally:
try:
os.unlink(outfile)
@ -151,7 +151,9 @@ def Ghostscript(tile, size, fp, scale=1):
except OSError:
pass
return im.im.copy()
im = out_im.im.copy()
out_im.close()
return im
class PSFile:

View File

@ -370,7 +370,7 @@ if __name__ == "__main__":
for size in imf.info["sizes"]:
imf.size = size
imf.save("out-%s-%s-%s.png" % size)
im = Image.open(sys.argv[1])
im.save("out.png")
with Image.open(sys.argv[1]) as im:
im.save("out.png")
if sys.platform == "windows":
os.startfile("out.png")

View File

@ -2236,20 +2236,22 @@ class Image:
:returns: None
"""
# preserve aspect ratio
x, y = self.size
if x > size[0]:
y = max(round(y * size[0] / x), 1)
x = round(size[0])
if y > size[1]:
x = max(round(x * size[1] / y), 1)
y = round(size[1])
size = x, y
box = None
if size == self.size:
x, y = map(math.floor, size)
if x >= self.width and y >= self.height:
return
def round_aspect(number, key):
return max(min(math.floor(number), math.ceil(number), key=key), 1)
# preserve aspect ratio
aspect = self.width / self.height
if x / y >= aspect:
x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y))
else:
y = round_aspect(x / aspect, key=lambda n: abs(aspect - x / n))
size = (x, y)
box = None
if reducing_gap is not None:
res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap))
if res is not None:

View File

@ -71,7 +71,10 @@ class ImageFont:
def _load_pilfont(self, filename):
with open(filename, "rb") as fp:
image = None
for ext in (".png", ".gif", ".pbm"):
if image:
image.close()
try:
fullname = os.path.splitext(filename)[0] + ext
image = Image.open(fullname)
@ -81,11 +84,14 @@ class ImageFont:
if image and image.mode in ("1", "L"):
break
else:
if image:
image.close()
raise OSError("cannot find glyph data file")
self.file = fullname
return self._load_pilfont_data(fp, image)
self._load_pilfont_data(fp, image)
image.close()
def _load_pilfont_data(self, file, image):

View File

@ -35,7 +35,9 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False):
im.load()
os.unlink(filepath)
if bbox:
im = im.crop(bbox)
im_cropped = im.crop(bbox)
im.close()
return im_cropped
else:
offset, size, data = Image.core.grabscreen(include_layered_windows, all_screens)
im = Image.frombytes(

View File

@ -203,4 +203,5 @@ if __name__ == "__main__":
print("Syntax: python ImageShow.py imagefile [title]")
sys.exit()
print(show(Image.open(sys.argv[1]), *sys.argv[2:]))
with Image.open(sys.argv[1]) as im:
print(show(im, *sys.argv[2:]))

View File

@ -158,9 +158,9 @@ class IptcImageFile(ImageFile.ImageFile):
o.close()
try:
_im = Image.open(outfile)
_im.load()
self.im = _im.im
with Image.open(outfile) as _im:
_im.load()
self.im = _im.im
finally:
try:
os.unlink(outfile)

View File

@ -176,6 +176,7 @@ def COM(self, marker):
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
self.info["comment"] = s
self.app["COM"] = s # compatibility
self.applist.append(("COM", s))
@ -448,9 +449,9 @@ class JpegImageFile(ImageFile.ImageFile):
raise ValueError("Invalid Filename")
try:
_im = Image.open(path)
_im.load()
self.im = _im.im
with Image.open(path) as _im:
_im.load()
self.im = _im.im
finally:
try:
os.unlink(path)

View File

@ -304,21 +304,21 @@ if __name__ == "__main__":
print("input image must be in Spider format")
sys.exit()
im = Image.open(filename)
print("image: " + str(im))
print("format: " + str(im.format))
print("size: " + str(im.size))
print("mode: " + str(im.mode))
print("max, min: ", end=" ")
print(im.getextrema())
with Image.open(filename) as im:
print("image: " + str(im))
print("format: " + str(im.format))
print("size: " + str(im.size))
print("mode: " + str(im.mode))
print("max, min: ", end=" ")
print(im.getextrema())
if len(sys.argv) > 2:
outfile = sys.argv[2]
if len(sys.argv) > 2:
outfile = sys.argv[2]
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
print(
"saving a flipped version of %s as %s "
% (os.path.basename(filename), outfile)
)
im.save(outfile, SpiderImageFile.format)
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
print(
"saving a flipped version of %s as %s "
% (os.path.basename(filename), outfile)
)
im.save(outfile, SpiderImageFile.format)

View File

@ -1,6 +1,7 @@
import collections
import os
import sys
import warnings
import PIL
@ -76,14 +77,14 @@ def get_supported_features():
def check(feature):
return (
feature in modules
and check_module(feature)
or feature in codecs
and check_codec(feature)
or feature in features
and check_feature(feature)
)
if feature in modules:
return check_module(feature)
if feature in codecs:
return check_codec(feature)
if feature in features:
return check_feature(feature)
warnings.warn("Unknown feature '%s'." % feature, stacklevel=2)
return False
def get_supported():

View File

@ -782,9 +782,6 @@ font_render(FontObject* self, PyObject* args)
im = (Imaging) id;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
load_flags = FT_LOAD_NO_BITMAP;
if (stroker == NULL) {
load_flags |= FT_LOAD_RENDER;
}
if (mask) {
load_flags |= FT_LOAD_TARGET_MONO;
}
@ -792,7 +789,7 @@ font_render(FontObject* self, PyObject* args)
ascender = 0;
for (i = 0; i < count; i++) {
index = glyph_info[i].index;
error = FT_Load_Glyph(self->face, index, load_flags);
error = FT_Load_Glyph(self->face, index, load_flags | FT_LOAD_RENDER);
if (error) {
return geterror(error);
}
@ -806,6 +803,10 @@ font_render(FontObject* self, PyObject* args)
ascender = temp;
}
if (stroker == NULL) {
load_flags |= FT_LOAD_RENDER;
}
x = y = 0;
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
for (i = 0; i < count; i++) {
@ -908,7 +909,7 @@ font_render(FontObject* self, PyObject* args)
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
static PyObject*
font_getvarnames(FontObject* self, PyObject* args)
font_getvarnames(FontObject* self)
{
int error;
FT_UInt i, j, num_namedstyles, name_count;
@ -947,7 +948,7 @@ font_render(FontObject* self, PyObject* args)
}
static PyObject*
font_getvaraxes(FontObject* self, PyObject* args)
font_getvaraxes(FontObject* self)
{
int error;
FT_UInt i, j, num_axis, name_count;
@ -1077,8 +1078,8 @@ static PyMethodDef font_methods[] = {
#if FREETYPE_MAJOR > 2 ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
{"getvarnames", (PyCFunction) font_getvarnames, METH_VARARGS },
{"getvaraxes", (PyCFunction) font_getvaraxes, METH_VARARGS },
{"getvarnames", (PyCFunction) font_getvarnames, METH_NOARGS },
{"getvaraxes", (PyCFunction) font_getvaraxes, METH_NOARGS },
{"setvarname", (PyCFunction) font_setvarname, METH_VARARGS},
{"setvaraxes", (PyCFunction) font_setvaraxes, METH_VARARGS},
#endif

View File

@ -369,7 +369,7 @@ PyObject* _anim_decoder_dealloc(PyObject* self)
Py_RETURN_NONE;
}
PyObject* _anim_decoder_get_info(PyObject* self, PyObject* args)
PyObject* _anim_decoder_get_info(PyObject* self)
{
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
WebPAnimInfo* info = &(decp->info);
@ -406,7 +406,7 @@ PyObject* _anim_decoder_get_chunk(PyObject* self, PyObject* args)
return ret;
}
PyObject* _anim_decoder_get_next(PyObject* self, PyObject* args)
PyObject* _anim_decoder_get_next(PyObject* self)
{
uint8_t* buf;
int timestamp;
@ -428,13 +428,7 @@ PyObject* _anim_decoder_get_next(PyObject* self, PyObject* args)
return ret;
}
PyObject* _anim_decoder_has_more_frames(PyObject* self, PyObject* args)
{
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject*)self;
return Py_BuildValue("i", WebPAnimDecoderHasMoreFrames(decp->dec));
}
PyObject* _anim_decoder_reset(PyObject* self, PyObject* args)
PyObject* _anim_decoder_reset(PyObject* self)
{
WebPAnimDecoderObject* decp = (WebPAnimDecoderObject *)self;
WebPAnimDecoderReset(decp->dec);
@ -489,11 +483,10 @@ static PyTypeObject WebPAnimEncoder_Type = {
// WebPAnimDecoder methods
static struct PyMethodDef _anim_decoder_methods[] = {
{"get_info", (PyCFunction)_anim_decoder_get_info, METH_VARARGS, "get_info"},
{"get_info", (PyCFunction)_anim_decoder_get_info, METH_NOARGS, "get_info"},
{"get_chunk", (PyCFunction)_anim_decoder_get_chunk, METH_VARARGS, "get_chunk"},
{"get_next", (PyCFunction)_anim_decoder_get_next, METH_VARARGS, "get_next"},
{"has_more_frames", (PyCFunction)_anim_decoder_has_more_frames, METH_VARARGS, "has_more_frames"},
{"reset", (PyCFunction)_anim_decoder_reset, METH_VARARGS, "reset"},
{"get_next", (PyCFunction)_anim_decoder_get_next, METH_NOARGS, "get_next"},
{"reset", (PyCFunction)_anim_decoder_reset, METH_NOARGS, "reset"},
{NULL, NULL} /* sentinel */
};
@ -775,7 +768,7 @@ end:
// Return the decoder's version number, packed in hexadecimal using 8bits for
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
PyObject* WebPDecoderVersion_wrapper() {
return Py_BuildValue("i", WebPGetDecoderVersion());
}
@ -787,7 +780,7 @@ int WebPDecoderBuggyAlpha(void) {
return WebPGetDecoderVersion()==0x0103;
}
PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
PyObject* WebPDecoderBuggyAlpha_wrapper() {
return Py_BuildValue("i", WebPDecoderBuggyAlpha());
}
@ -803,8 +796,8 @@ static PyMethodDef webpMethods[] =
#endif
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
{"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
{"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_NOARGS, "WebPVersion"},
{"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_NOARGS, "WebPDecoderBuggyAlpha"},
{NULL, NULL}
};

View File

@ -55,8 +55,19 @@ ImagingGetBBox(Imaging im, int bbox[4])
GETBBOX(image8, 0xff);
} else {
INT32 mask = 0xffffffff;
if (im->bands == 3)
if (im->bands == 3) {
((UINT8*) &mask)[3] = 0;
} else if (strcmp(im->mode, "RGBa") == 0 ||
strcmp(im->mode, "RGBA") == 0 ||
strcmp(im->mode, "La") == 0 ||
strcmp(im->mode, "LA") == 0 ||
strcmp(im->mode, "PA") == 0) {
#ifdef WORDS_BIGENDIAN
mask = 0x000000ff;
#else
mask = 0xff000000;
#endif
}
GETBBOX(image32, mask);
}