mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-24 14:14:47 +03:00
Merge branch 'master' into tiff-tags
This commit is contained in:
commit
f39bf365af
30
CHANGES.rst
30
CHANGES.rst
|
@ -5,6 +5,36 @@ Changelog (Pillow)
|
||||||
6.1.0 (unreleased)
|
6.1.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Update Py_UNICODE to Py_UCS4 #3780
|
||||||
|
[nulano]
|
||||||
|
|
||||||
|
- Consider I;16 pixel size when drawing #3899
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add TIFFTAG_SAMPLEFORMAT to blocklist #3926
|
||||||
|
[cgohlke, radarhere]
|
||||||
|
|
||||||
|
- Create GIF deltas from background colour of GIF frames if disposal mode is 2 #3708
|
||||||
|
[sircinnamon, radarhere]
|
||||||
|
|
||||||
|
- Added ImageSequence all_frames #3778
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Use unsigned int to store TIFF IFD offsets #3923
|
||||||
|
[cgohlke]
|
||||||
|
|
||||||
|
- Include CPPFLAGS when searching for libraries #3819
|
||||||
|
[jefferyto]
|
||||||
|
|
||||||
|
- Updated TIFF tile descriptors to match current decoding functionality #3795
|
||||||
|
[dmnisson]
|
||||||
|
|
||||||
|
- Added an `image.entropy()` method (second revision) #3608
|
||||||
|
[fish2000]
|
||||||
|
|
||||||
|
- Pass the correct types to PyArg_ParseTuple #3880
|
||||||
|
[QuLogic]
|
||||||
|
|
||||||
- Fixed crash when loading non-font bytes #3912
|
- Fixed crash when loading non-font bytes #3912
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
NotoNastaliqUrdu-Regular.ttf, from https://github.com/googlei18n/noto-fonts
|
NotoNastaliqUrdu-Regular.ttf and NotoSansSymbols-Regular.ttf, from https://github.com/googlei18n/noto-fonts
|
||||||
NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/
|
NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/
|
||||||
AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype
|
AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype
|
||||||
TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny
|
TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny
|
||||||
|
|
BIN
Tests/fonts/NotoSansSymbols-Regular.ttf
Normal file
BIN
Tests/fonts/NotoSansSymbols-Regular.ttf
Normal file
Binary file not shown.
BIN
Tests/images/imagedraw_rectangle_I.png
Normal file
BIN
Tests/images/imagedraw_rectangle_I.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 181 B |
BIN
Tests/images/unicode_extended.png
Normal file
BIN
Tests/images/unicode_extended.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 999 B |
|
@ -1,6 +1,6 @@
|
||||||
from .helper import unittest, PillowTestCase, hopper, netpbm_available
|
from .helper import unittest, PillowTestCase, hopper, netpbm_available
|
||||||
|
|
||||||
from PIL import Image, ImagePalette, GifImagePlugin
|
from PIL import Image, ImagePalette, GifImagePlugin, ImageDraw
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class TestFileGif(PillowTestCase):
|
||||||
return len(test_file.getvalue())
|
return len(test_file.getvalue())
|
||||||
|
|
||||||
self.assertEqual(test_grayscale(0), 800)
|
self.assertEqual(test_grayscale(0), 800)
|
||||||
self.assertEqual(test_grayscale(1), 38)
|
self.assertEqual(test_grayscale(1), 44)
|
||||||
self.assertEqual(test_bilevel(0), 800)
|
self.assertEqual(test_bilevel(0), 800)
|
||||||
self.assertEqual(test_bilevel(1), 800)
|
self.assertEqual(test_bilevel(1), 800)
|
||||||
|
|
||||||
|
@ -318,6 +318,103 @@ class TestFileGif(PillowTestCase):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, i + 1)
|
self.assertEqual(img.disposal_method, i + 1)
|
||||||
|
|
||||||
|
def test_dispose2_palette(self):
|
||||||
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
|
# 4 backgrounds: White, Grey, Black, Red
|
||||||
|
circles = [(255, 255, 255), (153, 153, 153), (0, 0, 0), (255, 0, 0)]
|
||||||
|
|
||||||
|
im_list = []
|
||||||
|
for circle in circles:
|
||||||
|
img = Image.new("RGB", (100, 100), (255, 0, 0))
|
||||||
|
|
||||||
|
# Red circle in center of each frame
|
||||||
|
d = ImageDraw.Draw(img)
|
||||||
|
d.ellipse([(40, 40), (60, 60)], fill=circle)
|
||||||
|
|
||||||
|
im_list.append(img)
|
||||||
|
|
||||||
|
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
||||||
|
|
||||||
|
img = Image.open(out)
|
||||||
|
|
||||||
|
for i, circle in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGB")
|
||||||
|
|
||||||
|
# Check top left pixel matches background
|
||||||
|
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
||||||
|
|
||||||
|
# Center remains red every frame
|
||||||
|
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
|
||||||
|
|
||||||
|
def test_dispose2_diff(self):
|
||||||
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
|
# 4 frames: red/blue, red/red, blue/blue, red/blue
|
||||||
|
circles = [
|
||||||
|
((255, 0, 0, 255), (0, 0, 255, 255)),
|
||||||
|
((255, 0, 0, 255), (255, 0, 0, 255)),
|
||||||
|
((0, 0, 255, 255), (0, 0, 255, 255)),
|
||||||
|
((255, 0, 0, 255), (0, 0, 255, 255)),
|
||||||
|
]
|
||||||
|
|
||||||
|
im_list = []
|
||||||
|
for i in range(len(circles)):
|
||||||
|
# Transparent BG
|
||||||
|
img = Image.new("RGBA", (100, 100), (255, 255, 255, 0))
|
||||||
|
|
||||||
|
# Two circles per frame
|
||||||
|
d = ImageDraw.Draw(img)
|
||||||
|
d.ellipse([(0, 30), (40, 70)], fill=circles[i][0])
|
||||||
|
d.ellipse([(60, 30), (100, 70)], fill=circles[i][1])
|
||||||
|
|
||||||
|
im_list.append(img)
|
||||||
|
|
||||||
|
im_list[0].save(
|
||||||
|
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
|
||||||
|
)
|
||||||
|
|
||||||
|
img = Image.open(out)
|
||||||
|
|
||||||
|
for i, colours in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGBA")
|
||||||
|
|
||||||
|
# Check left circle is correct colour
|
||||||
|
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
||||||
|
|
||||||
|
# Check right circle is correct colour
|
||||||
|
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
||||||
|
|
||||||
|
# Check BG is correct colour
|
||||||
|
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
|
||||||
|
|
||||||
|
def test_dispose2_background(self):
|
||||||
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
|
im_list = []
|
||||||
|
|
||||||
|
im = Image.new("P", (100, 100))
|
||||||
|
d = ImageDraw.Draw(im)
|
||||||
|
d.rectangle([(50, 0), (100, 100)], fill="#f00")
|
||||||
|
d.rectangle([(0, 0), (50, 100)], fill="#0f0")
|
||||||
|
im_list.append(im)
|
||||||
|
|
||||||
|
im = Image.new("P", (100, 100))
|
||||||
|
d = ImageDraw.Draw(im)
|
||||||
|
d.rectangle([(0, 0), (100, 50)], fill="#f00")
|
||||||
|
d.rectangle([(0, 50), (100, 100)], fill="#0f0")
|
||||||
|
im_list.append(im)
|
||||||
|
|
||||||
|
im_list[0].save(
|
||||||
|
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
||||||
|
)
|
||||||
|
|
||||||
|
im = Image.open(out)
|
||||||
|
im.seek(1)
|
||||||
|
self.assertEqual(im.getpixel((0, 0)), 0)
|
||||||
|
|
||||||
def test_iss634(self):
|
def test_iss634(self):
|
||||||
img = Image.open("Tests/images/iss634.gif")
|
img = Image.open("Tests/images/iss634.gif")
|
||||||
# seek to the second frame
|
# seek to the second frame
|
||||||
|
|
|
@ -122,7 +122,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (278, 374))
|
self.assertEqual(im.size, (278, 374))
|
||||||
self.assertEqual(im.tile[0][:3], ("tiff_adobe_deflate", (0, 0, 278, 374), 0))
|
self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
|
@ -684,10 +684,10 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
im.tile,
|
im.tile,
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"tiff_adobe_deflate",
|
"libtiff",
|
||||||
(0, 0, 100, 40),
|
(0, 0, 100, 40),
|
||||||
0,
|
0,
|
||||||
("RGB;16N", "tiff_adobe_deflate", False),
|
("RGB;16N", "tiff_adobe_deflate", False, 8),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -701,7 +701,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (100, 40))
|
self.assertEqual(im.size, (100, 40))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.tile, [("tiff_lzw", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False))]
|
im.tile,
|
||||||
|
[("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236))],
|
||||||
)
|
)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
@ -720,7 +721,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (256, 256))
|
self.assertEqual(im.size, (256, 256))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.tile, [("jpeg", (0, 0, 256, 256), 0, ("RGB", "jpeg", False))]
|
im.tile, [("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))]
|
||||||
)
|
)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import sys
|
||||||
|
|
||||||
from .helper import unittest, PillowTestCase, hopper
|
from .helper import unittest, PillowTestCase, hopper
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin, features
|
||||||
from PIL._util import py3
|
from PIL._util import py3
|
||||||
from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION, RESOLUTION_UNIT
|
from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION, RESOLUTION_UNIT
|
||||||
|
|
||||||
|
@ -587,6 +587,30 @@ class TestFileTiff(PillowTestCase):
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
|
|
||||||
|
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
||||||
|
def test_sampleformat_not_corrupted(self):
|
||||||
|
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
||||||
|
# when saving to a new file.
|
||||||
|
# Pillow 6.0 fails with "OSError: cannot identify image file".
|
||||||
|
import base64
|
||||||
|
|
||||||
|
tiff = BytesIO(
|
||||||
|
base64.b64decode(
|
||||||
|
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
|
||||||
|
b"AAIBAwADAAAAwgAAAAMBAwABAAAACAAAAAYBAwABAAAAAgAAABEBBAABAAAA"
|
||||||
|
b"4AAAABUBAwABAAAAAwAAABYBBAABAAAAAQAAABcBBAABAAAACwAAABoBBQAB"
|
||||||
|
b"AAAAyAAAABsBBQABAAAA0AAAABwBAwABAAAAAQAAACgBAwABAAAAAQAAAFMB"
|
||||||
|
b"AwADAAAA2AAAAAAAAAAIAAgACAABAAAAAQAAAAEAAAABAAAAAQABAAEAAAB4"
|
||||||
|
b"nGNgYAAAAAMAAQ=="
|
||||||
|
)
|
||||||
|
)
|
||||||
|
out = BytesIO()
|
||||||
|
with Image.open(tiff) as im:
|
||||||
|
im.save(out, format="tiff")
|
||||||
|
out.seek(0)
|
||||||
|
with Image.open(out) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only")
|
@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only")
|
||||||
class TestFileTiffW32(PillowTestCase):
|
class TestFileTiffW32(PillowTestCase):
|
||||||
|
|
|
@ -479,6 +479,19 @@ class TestImageDraw(PillowTestCase):
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(im, Image.open(expected))
|
self.assert_image_equal(im, Image.open(expected))
|
||||||
|
|
||||||
|
def test_rectangle_I16(self):
|
||||||
|
# Arrange
|
||||||
|
im = Image.new("I;16", (W, H))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
draw.rectangle(BBOX1, fill="black", outline="green")
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(
|
||||||
|
im.convert("I"), Image.open("Tests/images/imagedraw_rectangle_I.png")
|
||||||
|
)
|
||||||
|
|
||||||
def test_floodfill(self):
|
def test_floodfill(self):
|
||||||
red = ImageColor.getrgb("red")
|
red = ImageColor.getrgb("red")
|
||||||
|
|
||||||
|
|
|
@ -463,6 +463,26 @@ class TestImageFont(PillowTestCase):
|
||||||
with self.assertRaises(UnicodeEncodeError):
|
with self.assertRaises(UnicodeEncodeError):
|
||||||
font.getsize(u"’")
|
font.getsize(u"’")
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
sys.platform.startswith("win32") and sys.version.startswith("2"),
|
||||||
|
"requires Python 3.x on Windows",
|
||||||
|
)
|
||||||
|
def test_unicode_extended(self):
|
||||||
|
# issue #3777
|
||||||
|
text = u"A\u278A\U0001F12B"
|
||||||
|
target = "Tests/images/unicode_extended.png"
|
||||||
|
|
||||||
|
ttf = ImageFont.truetype(
|
||||||
|
"Tests/fonts/NotoSansSymbols-Regular.ttf",
|
||||||
|
FONT_SIZE,
|
||||||
|
layout_engine=self.LAYOUT_ENGINE,
|
||||||
|
)
|
||||||
|
img = Image.new("RGB", (100, 60))
|
||||||
|
d = ImageDraw.Draw(img)
|
||||||
|
d.text((10, 10), text, font=ttf)
|
||||||
|
|
||||||
|
self.assert_image_similar_tofile(img, target, self.metrics["multiline"])
|
||||||
|
|
||||||
def _test_fake_loading_font(self, path_to_fake, fontname):
|
def _test_fake_loading_font(self, path_to_fake, fontname):
|
||||||
# Make a copy of FreeTypeFont so we can patch the original
|
# Make a copy of FreeTypeFont so we can patch the original
|
||||||
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
||||||
|
|
|
@ -74,3 +74,25 @@ class TestImageSequence(PillowTestCase):
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
color2 = im.getpalette()[0:3]
|
color2 = im.getpalette()[0:3]
|
||||||
self.assertEqual(color1, color2)
|
self.assertEqual(color1, color2)
|
||||||
|
|
||||||
|
def test_all_frames(self):
|
||||||
|
# Test a single image
|
||||||
|
im = Image.open("Tests/images/iss634.gif")
|
||||||
|
ims = ImageSequence.all_frames(im)
|
||||||
|
|
||||||
|
self.assertEqual(len(ims), 42)
|
||||||
|
for i, im_frame in enumerate(ims):
|
||||||
|
self.assertFalse(im_frame is im)
|
||||||
|
|
||||||
|
im.seek(i)
|
||||||
|
self.assert_image_equal(im, im_frame)
|
||||||
|
|
||||||
|
# Test a series of images
|
||||||
|
ims = ImageSequence.all_frames([im, hopper(), im])
|
||||||
|
self.assertEqual(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)
|
||||||
|
self.assert_image_equal(im.rotate(90), im_frame)
|
||||||
|
|
|
@ -11,6 +11,14 @@ An optional ``include_layered_windows`` parameter has been added to ``ImageGrab.
|
||||||
defaulting to ``False``. If true, layered windows will be included in the resulting
|
defaulting to ``False``. If true, layered windows will be included in the resulting
|
||||||
image on Windows.
|
image on Windows.
|
||||||
|
|
||||||
|
ImageSequence.all_frames
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A new method to facilitate applying a given function to all frames in an image, or to
|
||||||
|
all frames in a list of images. The frames are returned as a list of separate images.
|
||||||
|
For example, ``ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))``
|
||||||
|
could be used to return all frames from an image, each rotated 90 degrees.
|
||||||
|
|
||||||
Variation fonts
|
Variation fonts
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -386,8 +386,8 @@ class pil_build_ext(build_ext):
|
||||||
_add_directory(library_dirs, lib_root)
|
_add_directory(library_dirs, lib_root)
|
||||||
_add_directory(include_dirs, include_root)
|
_add_directory(include_dirs, include_root)
|
||||||
|
|
||||||
# respect CFLAGS/LDFLAGS
|
# respect CFLAGS/CPPFLAGS/LDFLAGS
|
||||||
for k in ("CFLAGS", "LDFLAGS"):
|
for k in ("CFLAGS", "CPPFLAGS", "LDFLAGS"):
|
||||||
if k in os.environ:
|
if k in os.environ:
|
||||||
for match in re.finditer(r"-I([^\s]+)", os.environ[k]):
|
for match in re.finditer(r"-I([^\s]+)", os.environ[k]):
|
||||||
_add_directory(include_dirs, match.group(1))
|
_add_directory(include_dirs, match.group(1))
|
||||||
|
|
|
@ -426,6 +426,7 @@ def _write_multiple_frames(im, fp, palette):
|
||||||
|
|
||||||
im_frames = []
|
im_frames = []
|
||||||
frame_count = 0
|
frame_count = 0
|
||||||
|
background_im = None
|
||||||
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
|
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
|
||||||
for im_frame in ImageSequence.Iterator(imSequence):
|
for im_frame in ImageSequence.Iterator(imSequence):
|
||||||
# a copy is required here since seek can still mutate the image
|
# a copy is required here since seek can still mutate the image
|
||||||
|
@ -445,11 +446,22 @@ def _write_multiple_frames(im, fp, palette):
|
||||||
if im_frames:
|
if im_frames:
|
||||||
# delta frame
|
# delta frame
|
||||||
previous = im_frames[-1]
|
previous = im_frames[-1]
|
||||||
if _get_palette_bytes(im_frame) == _get_palette_bytes(previous["im"]):
|
if encoderinfo.get("disposal") == 2:
|
||||||
delta = ImageChops.subtract_modulo(im_frame, previous["im"])
|
if background_im is None:
|
||||||
|
background = _get_background(
|
||||||
|
im,
|
||||||
|
im.encoderinfo.get("background", im.info.get("background")),
|
||||||
|
)
|
||||||
|
background_im = Image.new("P", im_frame.size, background)
|
||||||
|
background_im.putpalette(im_frames[0]["im"].palette)
|
||||||
|
base_im = background_im
|
||||||
|
else:
|
||||||
|
base_im = previous["im"]
|
||||||
|
if _get_palette_bytes(im_frame) == _get_palette_bytes(base_im):
|
||||||
|
delta = ImageChops.subtract_modulo(im_frame, base_im)
|
||||||
else:
|
else:
|
||||||
delta = ImageChops.subtract_modulo(
|
delta = ImageChops.subtract_modulo(
|
||||||
im_frame.convert("RGB"), previous["im"].convert("RGB")
|
im_frame.convert("RGB"), base_im.convert("RGB")
|
||||||
)
|
)
|
||||||
bbox = delta.getbbox()
|
bbox = delta.getbbox()
|
||||||
if not bbox:
|
if not bbox:
|
||||||
|
@ -683,10 +695,12 @@ def _get_color_table_size(palette_bytes):
|
||||||
# calculate the palette size for the header
|
# calculate the palette size for the header
|
||||||
import math
|
import math
|
||||||
|
|
||||||
color_table_size = int(math.ceil(math.log(len(palette_bytes) // 3, 2))) - 1
|
if not palette_bytes:
|
||||||
if color_table_size < 0:
|
return 0
|
||||||
color_table_size = 0
|
elif len(palette_bytes) < 9:
|
||||||
return color_table_size
|
return 1
|
||||||
|
else:
|
||||||
|
return int(math.ceil(math.log(len(palette_bytes) // 3, 2))) - 1
|
||||||
|
|
||||||
|
|
||||||
def _get_header_palette(palette_bytes):
|
def _get_header_palette(palette_bytes):
|
||||||
|
@ -717,6 +731,18 @@ def _get_palette_bytes(im):
|
||||||
return im.palette.palette
|
return im.palette.palette
|
||||||
|
|
||||||
|
|
||||||
|
def _get_background(im, infoBackground):
|
||||||
|
background = 0
|
||||||
|
if infoBackground:
|
||||||
|
background = infoBackground
|
||||||
|
if isinstance(background, tuple):
|
||||||
|
# WebPImagePlugin stores an RGBA value in info["background"]
|
||||||
|
# So it must be converted to the same format as GifImagePlugin's
|
||||||
|
# info["background"] - a global color table index
|
||||||
|
background = im.palette.getcolor(background)
|
||||||
|
return background
|
||||||
|
|
||||||
|
|
||||||
def _get_global_header(im, info):
|
def _get_global_header(im, info):
|
||||||
"""Return a list of strings representing a GIF header"""
|
"""Return a list of strings representing a GIF header"""
|
||||||
|
|
||||||
|
@ -736,14 +762,7 @@ def _get_global_header(im, info):
|
||||||
if im.info.get("version") == b"89a":
|
if im.info.get("version") == b"89a":
|
||||||
version = b"89a"
|
version = b"89a"
|
||||||
|
|
||||||
background = 0
|
background = _get_background(im, info.get("background"))
|
||||||
if "background" in info:
|
|
||||||
background = info["background"]
|
|
||||||
if isinstance(background, tuple):
|
|
||||||
# WebPImagePlugin stores an RGBA value in info["background"]
|
|
||||||
# So it must be converted to the same format as GifImagePlugin's
|
|
||||||
# info["background"] - a global color table index
|
|
||||||
background = im.palette.getcolor(background)
|
|
||||||
|
|
||||||
palette_bytes = _get_palette_bytes(im)
|
palette_bytes = _get_palette_bytes(im)
|
||||||
color_table_size = _get_color_table_size(palette_bytes)
|
color_table_size = _get_color_table_size(palette_bytes)
|
||||||
|
|
|
@ -54,3 +54,25 @@ class Iterator(object):
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
return self.__next__()
|
return self.__next__()
|
||||||
|
|
||||||
|
|
||||||
|
def all_frames(im, func=None):
|
||||||
|
"""
|
||||||
|
Applies a given function to all frames in an image or a list of images.
|
||||||
|
The frames are returned as a list of separate images.
|
||||||
|
|
||||||
|
:param im: An image, or a list of images.
|
||||||
|
:param func: The function to apply to all of the image frames.
|
||||||
|
:returns: A list of images.
|
||||||
|
"""
|
||||||
|
if not isinstance(im, list):
|
||||||
|
im = [im]
|
||||||
|
|
||||||
|
ims = []
|
||||||
|
for imSequence in im:
|
||||||
|
current = imSequence.tell()
|
||||||
|
|
||||||
|
ims += [im_frame.copy() for im_frame in Iterator(imSequence)]
|
||||||
|
|
||||||
|
imSequence.seek(current)
|
||||||
|
return [func(im) for im in ims] if func else ims
|
||||||
|
|
|
@ -1123,7 +1123,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# (self._compression, (extents tuple),
|
# (self._compression, (extents tuple),
|
||||||
# 0, (rawmode, self._compression, fp))
|
# 0, (rawmode, self._compression, fp))
|
||||||
extents = self.tile[0][1]
|
extents = self.tile[0][1]
|
||||||
args = list(self.tile[0][3]) + [self.tag_v2.offset]
|
args = list(self.tile[0][3])
|
||||||
|
|
||||||
# To be nice on memory footprint, if there's a
|
# To be nice on memory footprint, if there's a
|
||||||
# file descriptor, use that instead of reading
|
# file descriptor, use that instead of reading
|
||||||
|
@ -1330,8 +1330,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# Offset in the tile tuple is 0, we go from 0,0 to
|
# Offset in the tile tuple is 0, we go from 0,0 to
|
||||||
# w,h, and we only do this once -- eds
|
# w,h, and we only do this once -- eds
|
||||||
a = (rawmode, self._compression, False)
|
a = (rawmode, self._compression, False, self.tag_v2.offset)
|
||||||
self.tile.append((self._compression, (0, 0, xsize, ysize), 0, a))
|
self.tile.append(("libtiff", (0, 0, xsize, ysize), 0, a))
|
||||||
|
|
||||||
elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2:
|
elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2:
|
||||||
# striped image
|
# striped image
|
||||||
|
@ -1542,6 +1542,8 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
# optional types for non core tags
|
# optional types for non core tags
|
||||||
types = {}
|
types = {}
|
||||||
|
# SAMPLEFORMAT is determined by the image format and should not be copied
|
||||||
|
# from legacy_ifd.
|
||||||
# STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
|
# STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
|
||||||
# based on the data in the strip.
|
# based on the data in the strip.
|
||||||
# The other tags expect arrays with a certain length (fixed or depending on
|
# The other tags expect arrays with a certain length (fixed or depending on
|
||||||
|
@ -1550,6 +1552,7 @@ def _save(im, fp, filename):
|
||||||
blocklist = [
|
blocklist = [
|
||||||
COLORMAP,
|
COLORMAP,
|
||||||
REFERENCEBLACKWHITE,
|
REFERENCEBLACKWHITE,
|
||||||
|
SAMPLEFORMAT,
|
||||||
STRIPBYTECOUNTS,
|
STRIPBYTECOUNTS,
|
||||||
STRIPOFFSETS,
|
STRIPOFFSETS,
|
||||||
TRANSFERFUNCTION,
|
TRANSFERFUNCTION,
|
||||||
|
|
|
@ -327,6 +327,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
|
||||||
static int
|
static int
|
||||||
font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
||||||
{
|
{
|
||||||
|
#if PY_VERSION_HEX < 0x03000000
|
||||||
if (PyUnicode_Check(string)) {
|
if (PyUnicode_Check(string)) {
|
||||||
Py_UNICODE* p = PyUnicode_AS_UNICODE(string);
|
Py_UNICODE* p = PyUnicode_AS_UNICODE(string);
|
||||||
int size = PyUnicode_GET_SIZE(string);
|
int size = PyUnicode_GET_SIZE(string);
|
||||||
|
@ -336,7 +337,6 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PY_VERSION_HEX < 0x03000000
|
|
||||||
if (PyString_Check(string)) {
|
if (PyString_Check(string)) {
|
||||||
unsigned char* p = (unsigned char*) PyString_AS_STRING(string);
|
unsigned char* p = (unsigned char*) PyString_AS_STRING(string);
|
||||||
int size = PyString_GET_SIZE(string);
|
int size = PyString_GET_SIZE(string);
|
||||||
|
@ -345,6 +345,13 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
||||||
*char_out = (unsigned char) p[index];
|
*char_out = (unsigned char) p[index];
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (PyUnicode_Check(string)) {
|
||||||
|
if (index >= PyUnicode_GET_LENGTH(string))
|
||||||
|
return 0;
|
||||||
|
*char_out = PyUnicode_READ_CHAR(string, index);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -366,6 +373,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject *
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX < 0x03000000
|
||||||
if (PyUnicode_Check(string)) {
|
if (PyUnicode_Check(string)) {
|
||||||
Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
|
Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
|
||||||
Py_ssize_t size = PyUnicode_GET_SIZE(string);
|
Py_ssize_t size = PyUnicode_GET_SIZE(string);
|
||||||
|
@ -385,9 +393,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} else if (PyString_Check(string)) {
|
||||||
#if PY_VERSION_HEX < 0x03000000
|
|
||||||
else if (PyString_Check(string)) {
|
|
||||||
char *text = PyString_AS_STRING(string);
|
char *text = PyString_AS_STRING(string);
|
||||||
int size = PyString_GET_SIZE(string);
|
int size = PyString_GET_SIZE(string);
|
||||||
if (! size) {
|
if (! size) {
|
||||||
|
@ -404,6 +410,28 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (PyUnicode_Check(string)) {
|
||||||
|
Py_UCS4 *text = PyUnicode_AsUCS4Copy(string);
|
||||||
|
Py_ssize_t size = PyUnicode_GET_LENGTH(string);
|
||||||
|
if (!text || !size) {
|
||||||
|
/* return 0 and clean up, no glyphs==no size,
|
||||||
|
and raqm fails with empty strings */
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
int set_text = (*p_raqm.set_text)(rq, (const uint32_t *)(text), size);
|
||||||
|
PyMem_Free(text);
|
||||||
|
if (!set_text) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
if (lang) {
|
||||||
|
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
PyErr_SetString(PyExc_TypeError, "expected string");
|
PyErr_SetString(PyExc_TypeError, "expected string");
|
||||||
|
|
|
@ -503,9 +503,9 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
|
||||||
char* rawmode;
|
char* rawmode;
|
||||||
char* compname;
|
char* compname;
|
||||||
int fp;
|
int fp;
|
||||||
int ifdoffset;
|
uint32 ifdoffset;
|
||||||
|
|
||||||
if (! PyArg_ParseTuple(args, "sssii", &mode, &rawmode, &compname, &fp, &ifdoffset))
|
if (! PyArg_ParseTuple(args, "sssiI", &mode, &rawmode, &compname, &fp, &ifdoffset))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
TRACE(("new tiff decoder %s\n", compname));
|
TRACE(("new tiff decoder %s\n", compname));
|
||||||
|
|
|
@ -68,7 +68,12 @@ static inline void
|
||||||
point8(Imaging im, int x, int y, int ink)
|
point8(Imaging im, int x, int y, int ink)
|
||||||
{
|
{
|
||||||
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize)
|
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize)
|
||||||
im->image8[y][x] = (UINT8) ink;
|
if (strncmp(im->mode, "I;16", 4) == 0) {
|
||||||
|
im->image8[y][x*2] = (UINT8) ink;
|
||||||
|
im->image8[y][x*2+1] = (UINT8) ink;
|
||||||
|
} else {
|
||||||
|
im->image8[y][x] = (UINT8) ink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -95,7 +100,7 @@ point32rgba(Imaging im, int x, int y, int ink)
|
||||||
static inline void
|
static inline void
|
||||||
hline8(Imaging im, int x0, int y0, int x1, int ink)
|
hline8(Imaging im, int x0, int y0, int x1, int ink)
|
||||||
{
|
{
|
||||||
int tmp;
|
int tmp, pixelwidth;
|
||||||
|
|
||||||
if (y0 >= 0 && y0 < im->ysize) {
|
if (y0 >= 0 && y0 < im->ysize) {
|
||||||
if (x0 > x1)
|
if (x0 > x1)
|
||||||
|
@ -108,8 +113,11 @@ hline8(Imaging im, int x0, int y0, int x1, int ink)
|
||||||
return;
|
return;
|
||||||
else if (x1 >= im->xsize)
|
else if (x1 >= im->xsize)
|
||||||
x1 = im->xsize-1;
|
x1 = im->xsize-1;
|
||||||
if (x0 <= x1)
|
if (x0 <= x1) {
|
||||||
memset(im->image8[y0] + x0, (UINT8) ink, x1 - x0 + 1);
|
pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;
|
||||||
|
memset(im->image8[y0] + x0 * pixelwidth, (UINT8) ink,
|
||||||
|
(x1 - x0 + 1) * pixelwidth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
|
||||||
(void) hdata; (void) base; (void) size;
|
(void) hdata; (void) base; (void) size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset) {
|
int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
|
||||||
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
||||||
|
|
||||||
TRACE(("initing libtiff\n"));
|
TRACE(("initing libtiff\n"));
|
||||||
|
|
|
@ -43,7 +43,7 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset);
|
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset);
|
||||||
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
|
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
|
||||||
extern int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length);
|
extern int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length);
|
||||||
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
|
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user