Merge branch 'master' into tiff-tags

This commit is contained in:
Hugo 2019-06-30 20:58:42 +03:00 committed by GitHub
commit f39bf365af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 336 additions and 41 deletions

View File

@ -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]

View File

@ -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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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");

View File

@ -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));

View File

@ -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);
}
} }
} }

View File

@ -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"));

View File

@ -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, ...);