mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-14 09:14:45 +03:00
Cherry picked 13f2c5ae14
This commit is contained in:
parent
17e624e522
commit
812b46901e
BIN
Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif
Normal file
BIN
Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif
Normal file
Binary file not shown.
|
@ -2,7 +2,9 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
import pytest
|
||||||
|
|
||||||
|
from PIL import Image, ImageFile, TiffImagePlugin, UnidentifiedImageError
|
||||||
from PIL._util import py3
|
from PIL._util import py3
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||||
|
|
||||||
|
@ -13,7 +15,6 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class TestFileTiff(PillowTestCase):
|
class TestFileTiff(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
filename = self.tempfile("temp.tif")
|
filename = self.tempfile("temp.tif")
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
|
@ -64,16 +65,14 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (52, 53))
|
self.assertEqual(im.size, (52, 53))
|
||||||
self.assertEqual(
|
self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))])
|
||||||
im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))])
|
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_set_legacy_api(self):
|
def test_set_legacy_api(self):
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
with self.assertRaises(Exception) as e:
|
with self.assertRaises(Exception) as e:
|
||||||
ifd.legacy_api = None
|
ifd.legacy_api = None
|
||||||
self.assertEqual(str(e.exception),
|
self.assertEqual(str(e.exception), "Not allowing setting of legacy api")
|
||||||
"Not allowing setting of legacy api")
|
|
||||||
|
|
||||||
def test_size(self):
|
def test_size(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
|
@ -93,10 +92,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(
|
|
||||||
im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
|
||||||
|
|
||||||
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
|
@ -105,10 +102,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
im = Image.open(filename)
|
im = Image.open(filename)
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(
|
|
||||||
im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
|
||||||
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
||||||
|
|
||||||
# Legacy.
|
# Legacy.
|
||||||
|
@ -163,12 +158,10 @@ class TestFileTiff(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file)
|
||||||
SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file)
|
|
||||||
|
|
||||||
TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0")
|
TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0")
|
||||||
self.assertRaises(
|
self.assertRaises(SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file)
|
||||||
SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file)
|
|
||||||
TiffImagePlugin.PREFIXES.pop()
|
TiffImagePlugin.PREFIXES.pop()
|
||||||
|
|
||||||
def test_bad_exif(self):
|
def test_bad_exif(self):
|
||||||
|
@ -223,8 +216,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assertEqual(im.getpixel((0, 1)), 0)
|
self.assertEqual(im.getpixel((0, 1)), 0)
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
"""Are we generating the same interpretation
|
||||||
of the image as Imagemagick is? """
|
of the image as Imagemagick is?"""
|
||||||
|
|
||||||
im = Image.open("Tests/images/12bit.cropped.tif")
|
im = Image.open("Tests/images/12bit.cropped.tif")
|
||||||
|
|
||||||
|
@ -243,8 +236,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343)
|
self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343)
|
||||||
self.assertEqual(
|
self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
||||||
im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
|
||||||
|
|
||||||
def test_unknown_pixel_mode(self):
|
def test_unknown_pixel_mode(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
|
@ -454,8 +446,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
def test_with_underscores(self):
|
def test_with_underscores(self):
|
||||||
kwargs = {"resolution_unit": "inch",
|
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
||||||
"x_resolution": 72, "y_resolution": 36}
|
|
||||||
filename = self.tempfile("temp.tif")
|
filename = self.tempfile("temp.tif")
|
||||||
hopper("RGB").save(filename, **kwargs)
|
hopper("RGB").save(filename, **kwargs)
|
||||||
im = Image.open(filename)
|
im = Image.open(filename)
|
||||||
|
@ -486,8 +477,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
infile = "Tests/images/tiff_strip_raw.tif"
|
infile = "Tests/images/tiff_strip_raw.tif"
|
||||||
im = Image.open(infile)
|
im = Image.open(infile)
|
||||||
|
|
||||||
self.assert_image_equal_tofile(
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_strip_planar_raw(self):
|
def test_strip_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co INTERLEAVE=BAND \
|
# gdal_translate -of GTiff -co INTERLEAVE=BAND \
|
||||||
|
@ -495,16 +485,14 @@ class TestFileTiff(PillowTestCase):
|
||||||
infile = "Tests/images/tiff_strip_planar_raw.tif"
|
infile = "Tests/images/tiff_strip_planar_raw.tif"
|
||||||
im = Image.open(infile)
|
im = Image.open(infile)
|
||||||
|
|
||||||
self.assert_image_equal_tofile(
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_strip_planar_raw_with_overviews(self):
|
def test_strip_planar_raw_with_overviews(self):
|
||||||
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
||||||
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
||||||
im = Image.open(infile)
|
im = Image.open(infile)
|
||||||
|
|
||||||
self.assert_image_equal_tofile(
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_tiled_planar_raw(self):
|
def test_tiled_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
||||||
|
@ -513,8 +501,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
infile = "Tests/images/tiff_tiled_planar_raw.tif"
|
infile = "Tests/images/tiff_tiled_planar_raw.tif"
|
||||||
im = Image.open(infile)
|
im = Image.open(infile)
|
||||||
|
|
||||||
self.assert_image_equal_tofile(
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_palette(self):
|
def test_palette(self):
|
||||||
for mode in ["P", "PA"]:
|
for mode in ["P", "PA"]:
|
||||||
|
@ -541,8 +528,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
# Test appending images
|
# Test appending images
|
||||||
mp = io.BytesIO()
|
mp = io.BytesIO()
|
||||||
im = Image.new("RGB", (100, 100), "#f00")
|
im = Image.new("RGB", (100, 100), "#f00")
|
||||||
ims = [Image.new("RGB", (100, 100), color)
|
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||||
for color in ["#0f0", "#00f"]]
|
|
||||||
im.copy().save(mp, format="TIFF", save_all=True, append_images=ims)
|
im.copy().save(mp, format="TIFF", save_all=True, append_images=ims)
|
||||||
|
|
||||||
mp.seek(0, os.SEEK_SET)
|
mp.seek(0, os.SEEK_SET)
|
||||||
|
@ -555,8 +541,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
yield im
|
yield im
|
||||||
|
|
||||||
mp = io.BytesIO()
|
mp = io.BytesIO()
|
||||||
im.save(mp, format="TIFF", save_all=True,
|
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))
|
||||||
append_images=imGenerator(ims))
|
|
||||||
|
|
||||||
mp.seek(0, os.SEEK_SET)
|
mp.seek(0, os.SEEK_SET)
|
||||||
reread = Image.open(mp)
|
reread = Image.open(mp)
|
||||||
|
@ -616,6 +601,18 @@ class TestFileTiffW32(PillowTestCase):
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"test_file",
|
||||||
|
[
|
||||||
|
"Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.timeout(2)
|
||||||
|
def test_oom(self, test_file):
|
||||||
|
with pytest.raises(UnidentifiedImageError):
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
# this is an mmaped file.
|
# this is an mmaped file.
|
||||||
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
||||||
im.save(tmpfile)
|
im.save(tmpfile)
|
||||||
|
|
|
@ -261,6 +261,8 @@ OPEN_INFO = {
|
||||||
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MAX_SAMPLESPERPIXEL = max(len(key_tp[4]) for key_tp in OPEN_INFO.keys())
|
||||||
|
|
||||||
PREFIXES = [
|
PREFIXES = [
|
||||||
b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
|
b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
|
||||||
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
|
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
|
||||||
|
@ -1264,10 +1266,25 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
else:
|
else:
|
||||||
bps_count = 1
|
bps_count = 1
|
||||||
bps_count += len(extra_tuple)
|
bps_count += len(extra_tuple)
|
||||||
# Some files have only one value in bps_tuple,
|
bps_actual_count = len(bps_tuple)
|
||||||
# while should have more. Fix it
|
samples_per_pixel = self.tag_v2.get(
|
||||||
if bps_count > len(bps_tuple) and len(bps_tuple) == 1:
|
SAMPLESPERPIXEL,
|
||||||
bps_tuple = bps_tuple * bps_count
|
3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if samples_per_pixel > MAX_SAMPLESPERPIXEL:
|
||||||
|
# DOS check, samples_per_pixel can be a Long, and we extend the tuple below
|
||||||
|
logger.error("More samples per pixel than can be decoded: %s", samples_per_pixel)
|
||||||
|
raise SyntaxError("Invalid value for samples per pixel")
|
||||||
|
|
||||||
|
if samples_per_pixel < bps_actual_count:
|
||||||
|
# If a file has more values in bps_tuple than expected,
|
||||||
|
# remove the excess.
|
||||||
|
bps_tuple = bps_tuple[:samples_per_pixel]
|
||||||
|
elif samples_per_pixel > bps_actual_count and bps_actual_count == 1:
|
||||||
|
# If a file has only one value in bps_tuple, when it should have more,
|
||||||
|
# presume it is the same number of bits for all of the samples.
|
||||||
|
bps_tuple = bps_tuple * samples_per_pixel
|
||||||
|
|
||||||
# mode: check photometric interpretation and bits per pixel
|
# mode: check photometric interpretation and bits per pixel
|
||||||
key = (
|
key = (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user