diff --git a/CHANGES.rst b/CHANGES.rst index f9586184e..e26794af6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,30 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- Fix ValueError in Exif/Tiff IFD #2719 + [wiredfool] + +- Use pathlib2 for Path objects on Python < 3.4 #2291 + [asergi] + +- Export only required properties in unsafe_ptrs #2740 + [homm] + +- Alpha composite fixes #2709 + [homm] + +- Faster Transpose operations, added 'Transverse' option #2730 + [homm] + +- Deprecate ImageOps undocumented functions gaussian_blur, gblur, unsharp_mask, usm and box_blur in favor of ImageFilter implementations #2735 + [homm] + +- Dependencies: Updated freetype to 2.8.1 #2741 + [radarhere] + +- Bug: Player skipped first image #2742 + [radarhere] + - Faster filter operations for Kernel, Gaussian, and Unsharp Mask filters #2679 [homm] diff --git a/PIL/Image.py b/PIL/Image.py index e49b1555d..2904e4e07 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -123,6 +123,16 @@ try: except ImportError: HAS_CFFI = False +try: + from pathlib import Path + HAS_PATHLIB = True +except ImportError: + try: + from pathlib2 import Path + HAS_PATHLIB = True + except ImportError: + HAS_PATHLIB = False + def isImageType(t): """ @@ -150,6 +160,7 @@ ROTATE_90 = 2 ROTATE_180 = 3 ROTATE_270 = 4 TRANSPOSE = 5 +TRANSVERSE = 6 # transforms AFFINE = 0 @@ -1403,9 +1414,9 @@ class Image(object): Performance Note: Not currently implemented in-place in the core layer. """ - if not isinstance(source, tuple): + if not isinstance(source, (list, tuple)): raise ValueError("Source must be a tuple") - if not isinstance(dest, tuple): + if not isinstance(dest, (list, tuple)): raise ValueError("Destination must be a tuple") if not len(source) in (2, 4): raise ValueError("Source must be a 2 or 4-tuple") @@ -1429,7 +1440,7 @@ class Image(object): box = dest + (dest[0] + overlay.width, dest[1] + overlay.height) # destination image. don't copy if we're using the whole image. - if dest == (0,0) + self.size: + if box == (0,0) + self.size: background = self else: background = self.crop(box) @@ -1874,11 +1885,9 @@ class Image(object): if isPath(fp): filename = fp open_fp = True - elif sys.version_info >= (3, 4): - from pathlib import Path - if isinstance(fp, Path): - filename = str(fp) - open_fp = True + elif HAS_PATHLIB and isinstance(fp, Path): + filename = str(fp) + open_fp = True if not filename and hasattr(fp, "name") and isPath(fp.name): # only set the name for metadata purposes filename = fp.name @@ -2173,8 +2182,8 @@ class Image(object): :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, - :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or - :py:attr:`PIL.Image.TRANSPOSE`. + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270`, + :py:attr:`PIL.Image.TRANSPOSE` or :py:attr:`PIL.Image.TRANSVERSE`. :returns: Returns a flipped or rotated copy of this image. """ @@ -2515,13 +2524,8 @@ def open(fp, mode="r"): filename = "" if isPath(fp): filename = fp - else: - try: - from pathlib import Path - if isinstance(fp, Path): - filename = str(fp.resolve()) - except ImportError: - pass + elif HAS_PATHLIB and isinstance(fp, Path): + filename = str(fp.resolve()) if filename: fp = builtins.open(filename, "rb") diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index c89225484..100fea8bd 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -160,6 +160,26 @@ class GaussianBlur(MultibandFilter): return image.gaussian_blur(self.radius) +class BoxBlur(MultibandFilter): + """Blurs the image by setting each pixel to the average value of the pixels + in a square box extending radius pixels in each direction. + Supports float radius of arbitrary size. Uses an optimized implementation + which runs in linear time relative to the size of the image + for any radius value. + + :param radius: Size of the box in one direction. Radius 0 does not blur, + returns an identical image. Radius 1 takes 1 pixel + in each direction, i.e. 9 pixels in total. + """ + name = "BoxBlur" + + def __init__(self, radius): + self.radius = radius + + def filter(self, image): + return image.box_blur(self.radius) + + class UnsharpMask(MultibandFilter): """Unsharp mask filter. diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 3681109c1..447d48aae 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -21,6 +21,7 @@ from . import Image from ._util import isStringType import operator import functools +import warnings # @@ -437,6 +438,13 @@ def solarize(image, threshold=128): def gaussian_blur(im, radius=None): """ PIL_usm.gblur(im, [radius])""" + warnings.warn( + 'PIL.ImageOps.gaussian_blur is deprecated. ' + 'Use PIL.ImageFilter.GaussianBlur instead. ' + 'This function will be removed in a future version.', + DeprecationWarning + ) + if radius is None: radius = 5.0 @@ -444,12 +452,30 @@ def gaussian_blur(im, radius=None): return im.im.gaussian_blur(radius) -gblur = gaussian_blur + +def gblur(im, radius=None): + """ PIL_usm.gblur(im, [radius])""" + + warnings.warn( + 'PIL.ImageOps.gblur is deprecated. ' + 'Use PIL.ImageFilter.GaussianBlur instead. ' + 'This function will be removed in a future version.', + DeprecationWarning + ) + + return gaussian_blur(im, radius) def unsharp_mask(im, radius=None, percent=None, threshold=None): """ PIL_usm.usm(im, [radius, percent, threshold])""" + warnings.warn( + 'PIL.ImageOps.unsharp_mask is deprecated. ' + 'Use PIL.ImageFilter.UnsharpMask instead. ' + 'This function will be removed in a future version.', + DeprecationWarning + ) + if radius is None: radius = 5.0 if percent is None: @@ -461,7 +487,18 @@ def unsharp_mask(im, radius=None, percent=None, threshold=None): return im.im.unsharp_mask(radius, percent, threshold) -usm = unsharp_mask + +def usm(im, radius=None, percent=None, threshold=None): + """ PIL_usm.usm(im, [radius, percent, threshold])""" + + warnings.warn( + 'PIL.ImageOps.usm is deprecated. ' + 'Use PIL.ImageFilter.UnsharpMask instead. ' + 'This function will be removed in a future version.', + DeprecationWarning + ) + + return unsharp_mask(im, radius, percent, threshold) def box_blur(image, radius): @@ -478,6 +515,13 @@ def box_blur(image, radius): in each direction, i.e. 9 pixels in total. :return: An image. """ + warnings.warn( + 'PIL.ImageOps.box_blur is deprecated. ' + 'Use PIL.ImageFilter.BoxBlur instead. ' + 'This function will be removed in a future version.', + DeprecationWarning + ) + image.load() return image._new(image.im.box_blur(radius)) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 09ce86af4..620efc4ff 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -49,8 +49,7 @@ class PyAccess(object): self.image8 = ffi.cast('unsigned char **', vals['image8']) self.image32 = ffi.cast('int **', vals['image32']) self.image = ffi.cast('unsigned char **', vals['image']) - self.xsize = vals['xsize'] - self.ysize = vals['ysize'] + self.xsize, self.ysize = img.im.size # Keep pointer to im object to prevent dereferencing. self._im = img.im diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index bd66f4a7a..e6ecabc1d 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -550,11 +550,28 @@ class ImageFileDirectory_v2(collections.MutableMapping): dest = self._tags_v1 if legacy_api else self._tags_v2 - if info.length == 1: - if legacy_api and self.tagtype[tag] in [5, 10]: + # Three branches: + # Spec'd length == 1, Actual length 1, store as element + # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. + # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. + # Don't mess with the legacy api, since it's frozen. + if ((info.length == 1) or + (info.length is None and len(values) == 1 and not legacy_api)): + # Don't mess with the legacy api, since it's frozen. + if legacy_api and self.tagtype[tag] in [5, 10]: # rationals values = values, - dest[tag], = values + try: + dest[tag], = values + except ValueError: + # We've got a builtin tag with 1 expected entry + warnings.warn( + "Metadata Warning, tag %s had too many entries: %s, expected 1" % ( + tag, len(values))) + dest[tag] = values[0] + else: + # Spec'd length > 1 or undefined + # Unspec'd, and length > 1 dest[tag] = values def __delitem__(self, tag): @@ -1011,8 +1028,10 @@ class TiffImageFile(ImageFile.ImageFile): args = rawmode, "" if JPEGTABLES in self.tag_v2: # Hack to handle abbreviated JPEG headers - # FIXME This will fail with more than one value - self.tile_prefix, = self.tag_v2[JPEGTABLES] + # Definition of JPEGTABLES is that the count + # is the number of bytes in the tables datastream + # so, it should always be 1 in our tag info + self.tile_prefix = self.tag_v2[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 731d0ec8b..eba88ef8d 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -23,7 +23,7 @@ from collections import namedtuple class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] - def __new__(cls, value=None, name="unknown", type=None, length=0, enum=None): + def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): return super(TagInfo, cls).__new__( cls, value, name, type, length, enum or {}) @@ -142,6 +142,8 @@ TAGS_V2 = { 341: ("SMaxSampleValue", DOUBLE, 0), 342: ("TransferRange", SHORT, 6), + 347: ("JPEGTables", UNDEFINED, 1), + # obsolete JPEG tags 512: ("JPEGProc", SHORT, 1), 513: ("JPEGInterchangeFormat", LONG, 1), @@ -158,7 +160,10 @@ TAGS_V2 = { 531: ("YCbCrPositioning", SHORT, 1), 532: ("ReferenceBlackWhite", LONG, 0), + 700: ('XMP', BYTE, 1), + 33432: ("Copyright", ASCII, 1), + 34377: ('PhotoshopInfo', BYTE, 1), # FIXME add more tags here 34665: ("ExifIFD", SHORT, 1), @@ -188,8 +193,8 @@ TAGS_V2 = { 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), 50780: ("BestQualityScale", RATIONAL, 1), - 50838: ("ImageJMetaDataByteCounts", LONG, 1), - 50839: ("ImageJMetaData", UNDEFINED, 1) + 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one + 50839: ("ImageJMetaData", UNDEFINED, 1) # see Issue #2006 } # Legacy Tags structure diff --git a/Scripts/player.py b/Scripts/player.py index dcf7d9307..6e6e21d20 100755 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -22,13 +22,10 @@ from PIL import Image, ImageTk class UI(tkinter.Label): def __init__(self, master, im): - if isinstance(im, list): + self.im = im + if isinstance(self.im, list): # list of images - self.im = im[1:] - im = self.im[0] - else: - # sequence - self.im = im + im = self.im.pop(0) if im.mode == "1": self.image = ImageTk.BitmapImage(im, foreground="white") diff --git a/Tests/images/issue_2278.tif b/Tests/images/issue_2278.tif new file mode 100644 index 000000000..adac046db Binary files /dev/null and b/Tests/images/issue_2278.tif differ diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index d99847740..622b842d0 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -13,20 +13,6 @@ sample.putdata(sum([ ], [])) -class ImageMock(object): - def __init__(self): - self.im = self - - def load(self): - pass - - def _new(self, im): - return im - - def box_blur(self, radius, n): - return radius, n - - class TestBoxBlurApi(PillowTestCase): def test_imageops_box_blur(self): diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9d11e32ef..bb5768046 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -56,7 +56,7 @@ class TestFileTiffMetadata(PillowTestCase): loaded = Image.open(f) self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),)) - self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], len(bindata)) + self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),)) self.assertEqual(loaded.tag[ImageJMetaData], bindata) self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata) @@ -69,6 +69,16 @@ class TestFileTiffMetadata(PillowTestCase): loaded_double = loaded.tag[tag_ids['YawAngle']][0] self.assertAlmostEqual(loaded_double, doubledata) + # check with 2 element ImageJMetaDataByteCounts, issue #2006 + + info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8) + img.save(f, tiffinfo=info) + loaded = Image.open(f) + + self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)) + self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)) + + def test_read_metadata(self): img = Image.open('Tests/images/hopper_g4.tif') @@ -202,8 +212,8 @@ class TestFileTiffMetadata(PillowTestCase): im.save(out, tiffinfo=info, compression='raw') reloaded = Image.open(out) - self.assertEqual(0, reloaded.tag_v2[41988][0].numerator) - self.assertEqual(0, reloaded.tag_v2[41988][0].denominator) + self.assertEqual(0, reloaded.tag_v2[41988].numerator) + self.assertEqual(0, reloaded.tag_v2[41988].denominator) def test_expty_values(self): data = io.BytesIO( @@ -220,6 +230,27 @@ class TestFileTiffMetadata(PillowTestCase): self.fail("Should not be struct value error there.") self.assertIn(33432, info) + def test_PhotoshopInfo(self): + im = Image.open('Tests/images/issue_2278.tif') + + self.assertIsInstance(im.tag_v2[34377], bytes) + out = self.tempfile('temp.tiff') + im.save(out) + reloaded = Image.open(out) + self.assertIsInstance(reloaded.tag_v2[34377], bytes) + + 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 + + try: + self.assert_warning(UserWarning, lambda: ifd[277]) + except ValueError: + self.fail("Invalid Metadata count should not cause a Value Error.") + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_image.py b/Tests/test_image.py index 0c104f0aa..e65e0e4c7 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -2,7 +2,6 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image import os -import sys class TestImage(PillowTestCase): @@ -72,10 +71,9 @@ class TestImage(PillowTestCase): def test_bad_mode(self): self.assertRaises(ValueError, Image.open, "filename", "bad mode") - @unittest.skipIf(sys.version_info < (3, 4), - "pathlib only available in Python 3.4 or later") + @unittest.skipUnless(Image.HAS_PATHLIB, "requires pathlib/pathlib2") def test_pathlib(self): - from pathlib import Path + from PIL.Image import Path im = Image.open(Path("Tests/images/hopper.jpg")) self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 8a38b2979..3636a73f7 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -1,7 +1,6 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image -from PIL import ImageFilter +from PIL import Image, ImageFilter class TestImageFilter(PillowTestCase): @@ -9,10 +8,11 @@ class TestImageFilter(PillowTestCase): def test_sanity(self): def filter(filter): - im = hopper("L") - out = im.filter(filter) - self.assertEqual(out.mode, im.mode) - self.assertEqual(out.size, im.size) + for mode in ["L", "RGB", "CMYK"]: + im = hopper(mode) + out = im.filter(filter) + self.assertEqual(out.mode, im.mode) + self.assertEqual(out.size, im.size) filter(ImageFilter.BLUR) filter(ImageFilter.CONTOUR) @@ -28,9 +28,9 @@ class TestImageFilter(PillowTestCase): filter(ImageFilter.MedianFilter) filter(ImageFilter.MinFilter) filter(ImageFilter.ModeFilter) - filter(ImageFilter.Kernel((3, 3), list(range(9)))) filter(ImageFilter.GaussianBlur) filter(ImageFilter.GaussianBlur(5)) + filter(ImageFilter.BoxBlur(5)) filter(ImageFilter.UnsharpMask) filter(ImageFilter.UnsharpMask(10)) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index e13fc8605..a6b1191db 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -2,7 +2,7 @@ import helper from helper import unittest, PillowTestCase from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, - ROTATE_270, TRANSPOSE) + ROTATE_270, TRANSPOSE, TRANSVERSE) class TestImageTranspose(PillowTestCase): @@ -108,6 +108,22 @@ class TestImageTranspose(PillowTestCase): for mode in ("L", "RGB"): transpose(mode) + def test_tranverse(self): + def transpose(mode): + im = self.hopper[mode] + out = im.transpose(TRANSVERSE) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, x-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + def test_roundtrip(self): im = self.hopper['L'] @@ -124,6 +140,12 @@ class TestImageTranspose(PillowTestCase): im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM)) self.assert_image_equal( im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM)) + self.assert_image_equal( + im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) if __name__ == '__main__': diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index eed8ff754..cd7dcae5f 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -12,15 +12,25 @@ class TestImageOpsUsm(PillowTestCase): def test_ops_api(self): - i = ImageOps.gaussian_blur(im, 2.0) + i = self.assert_warning(DeprecationWarning, + ImageOps.gaussian_blur, im, 2.0) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) - # i.save("blur.bmp") - i = ImageOps.unsharp_mask(im, 2.0, 125, 8) + i = self.assert_warning(DeprecationWarning, + ImageOps.gblur, im, 2.0) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + + i = self.assert_warning(DeprecationWarning, + ImageOps.unsharp_mask, im, 2.0, 125, 8) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + + i = self.assert_warning(DeprecationWarning, + ImageOps.usm, im, 2.0, 125, 8) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) - # i.save("usm.bmp") def test_filter_api(self): @@ -36,38 +46,38 @@ class TestImageOpsUsm(PillowTestCase): def test_usm_formats(self): - usm = ImageOps.unsharp_mask - self.assertRaises(ValueError, usm, im.convert("1")) - usm(im.convert("L")) - self.assertRaises(ValueError, usm, im.convert("I")) - self.assertRaises(ValueError, usm, im.convert("F")) - usm(im.convert("RGB")) - usm(im.convert("RGBA")) - usm(im.convert("CMYK")) - self.assertRaises(ValueError, usm, im.convert("YCbCr")) + usm = ImageFilter.UnsharpMask + self.assertRaises(ValueError, im.convert("1").filter, usm) + im.convert("L").filter(usm) + self.assertRaises(ValueError, im.convert("I").filter, usm) + self.assertRaises(ValueError, im.convert("F").filter, usm) + im.convert("RGB").filter(usm) + im.convert("RGBA").filter(usm) + im.convert("CMYK").filter(usm) + self.assertRaises(ValueError, im.convert("YCbCr").filter, usm) def test_blur_formats(self): - blur = ImageOps.gaussian_blur - self.assertRaises(ValueError, blur, im.convert("1")) + blur = ImageFilter.GaussianBlur + self.assertRaises(ValueError, im.convert("1").filter, blur) blur(im.convert("L")) - self.assertRaises(ValueError, blur, im.convert("I")) - self.assertRaises(ValueError, blur, im.convert("F")) - blur(im.convert("RGB")) - blur(im.convert("RGBA")) - blur(im.convert("CMYK")) - self.assertRaises(ValueError, blur, im.convert("YCbCr")) + self.assertRaises(ValueError, im.convert("I").filter, blur) + self.assertRaises(ValueError, im.convert("F").filter, blur) + im.convert("RGB").filter(blur) + im.convert("RGBA").filter(blur) + im.convert("CMYK").filter(blur) + self.assertRaises(ValueError, im.convert("YCbCr").filter, blur) def test_usm_accuracy(self): src = snakes.convert('RGB') - i = src._new(ImageOps.unsharp_mask(src, 5, 1024, 0)) + i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0)) # Image should not be changed because it have only 0 and 255 levels. self.assertEqual(i.tobytes(), src.tobytes()) def test_blur_accuracy(self): - i = snakes._new(ImageOps.gaussian_blur(snakes, .4)) + i = snakes.filter(ImageFilter.GaussianBlur(.4)) # These pixels surrounded with pixels with 255 intensity. # They must be very close to 255. for x, y, c in [(1, 0, 1), (2, 0, 1), (7, 8, 1), (8, 8, 1), (2, 9, 1), diff --git a/_imaging.c b/_imaging.c index d0777c73a..7b70bef19 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1678,6 +1678,7 @@ _transpose(ImagingObject* self, PyObject* args) case 2: /* rotate 90 */ case 4: /* rotate 270 */ case 5: /* transpose */ + case 6: /* transverse */ imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize); break; default: @@ -1705,6 +1706,9 @@ _transpose(ImagingObject* self, PyObject* args) case 5: (void) ImagingTranspose(imOut, imIn); break; + case 6: + (void) ImagingTransverse(imOut, imIn); + break; } return PyImagingNew(imOut); @@ -3138,21 +3142,10 @@ _getattr_ptr(ImagingObject* self, void* closure) static PyObject* _getattr_unsafe_ptrs(ImagingObject* self, void* closure) { - return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)", - "mode", self->image->mode, - "type", self->image->type, - "depth", self->image->depth, - "bands", self->image->bands, - "xsize", self->image->xsize, - "ysize", self->image->ysize, - "palette", self->image->palette, + return Py_BuildValue("(sn)(sn)(sn)", "image8", self->image->image8, "image32", self->image->image32, - "image", self->image->image, - "block", self->image->block, - "pixelsize", self->image->pixelsize, - "linesize", self->image->linesize, - "destroy", self->image->destroy + "image", self->image->image ); }; diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst index e89fafbcf..bc1868667 100644 --- a/docs/reference/ImageFilter.rst +++ b/docs/reference/ImageFilter.rst @@ -38,6 +38,7 @@ image enhancement filters: * **SHARPEN** .. autoclass:: PIL.ImageFilter.GaussianBlur +.. autoclass:: PIL.ImageFilter.BoxBlur .. autoclass:: PIL.ImageFilter.UnsharpMask .. autoclass:: PIL.ImageFilter.Kernel .. autoclass:: PIL.ImageFilter.RankFilter diff --git a/docs/releasenotes/4.3.0.rst b/docs/releasenotes/4.3.0.rst index 4708eeb29..e9ac8d936 100644 --- a/docs/releasenotes/4.3.0.rst +++ b/docs/releasenotes/4.3.0.rst @@ -4,13 +4,19 @@ Get One Channel From Image ========================== -New method :py:meth:`PIL.Image.Image.getchannel` added. +New method :py:meth:`PIL.Image.Image.getchannel` is added. It returns single channel by index or name. For example, ``image.getchannel("A")`` will return alpha channel as separate image. ``getchannel`` should work up to 6 times faster than ``image.split()[0]`` in previous Pillow versions. +Box Blur +======== + +New filter :py:class:`PIL.ImageFilter.BoxBlur` is added. + + Partial Resampling ================== @@ -40,6 +46,22 @@ This release contains several performance improvements: using a recent version of libjpeg-turbo. +TIFF Metadata Changes +===================== + +* TIFF tags with unknown type/quantity now default to being bare + values if they are 1 element, where previously they would be a + single element tuple. This is only with the new api, not the legacy + api. This normalizes the handling of fields, so that the metadata + with inferred or image specified counts are handled the same as + metadata with count specified in the TIFF spec. +* The ``PhotoshopInfo``, ``XMP``, and ``JPEGTables`` tags now have a + defined type (bytes) and a count of 1. +* The ``ImageJMetaDataByteCounts`` tag now has an arbitrary number of + items, as there can be multiple items, one for UTF-8, and one for + UTF-16. + + Core Image API Changes ====================== diff --git a/libImaging/AlphaComposite.c b/libImaging/AlphaComposite.c index 4fee3cecd..a074334aa 100644 --- a/libImaging/AlphaComposite.c +++ b/libImaging/AlphaComposite.c @@ -11,6 +11,7 @@ #include "Imaging.h" +#define PRECISION_BITS 7 typedef struct { @@ -47,13 +48,11 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) return NULL; for (y = 0; y < imDst->ysize; y++) { - rgba8* dst = (rgba8*) imDst->image[y]; rgba8* src = (rgba8*) imSrc->image[y]; rgba8* out = (rgba8*) imOut->image[y]; for (x = 0; x < imDst->xsize; x ++) { - if (src->a == 0) { // Copy 4 bytes at once. *out = *dst; @@ -62,25 +61,20 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) // Each variable has extra meaningful bits. // Divisions are rounded. - // This code uses trick from Paste.c: - // (a + (2 << (n-1)) - 1) / ((2 << n)-1) - // almost equivalent to: - // tmp = a + (2 << (n-1)), ((tmp >> n) + tmp) >> n - UINT32 tmpr, tmpg, tmpb; - UINT16 blend = dst->a * (255 - src->a); - UINT16 outa255 = src->a * 255 + blend; + UINT32 blend = dst->a * (255 - src->a); + UINT32 outa255 = src->a * 255 + blend; // There we use 7 bits for precision. // We could use more, but we go beyond 32 bits. - UINT16 coef1 = src->a * 255 * 255 * 128 / outa255; - UINT16 coef2 = 255 * 128 - coef1; + UINT32 coef1 = src->a * 255 * 255 * (1<r * coef1 + dst->r * coef2 + (0x80 << 7); - out->r = SHIFTFORDIV255(tmpr) >> 7; - tmpg = src->g * coef1 + dst->g * coef2 + (0x80 << 7); - out->g = SHIFTFORDIV255(tmpg) >> 7; - tmpb = src->b * coef1 + dst->b * coef2 + (0x80 << 7); - out->b = SHIFTFORDIV255(tmpb) >> 7; + tmpr = src->r * coef1 + dst->r * coef2; + tmpg = src->g * coef1 + dst->g * coef2; + tmpb = src->b * coef1 + dst->b * coef2; + out->r = SHIFTFORDIV255(tmpr + (0x80<> PRECISION_BITS; + out->g = SHIFTFORDIV255(tmpg + (0x80<> PRECISION_BITS; + out->b = SHIFTFORDIV255(tmpb + (0x80<> PRECISION_BITS; out->a = SHIFTFORDIV255(outa255 + 0x80); } diff --git a/libImaging/Bands.c b/libImaging/Bands.c index 89cfd33db..b03a30f71 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -84,7 +84,7 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) } for (i = 0; i < imIn->bands; i++) { - bands[i] = ImagingNew("L", imIn->xsize, imIn->ysize); + bands[i] = ImagingNewDirty("L", imIn->xsize, imIn->ysize); if ( ! bands[i]) { for (j = 0; j < i; ++j) { ImagingDelete(bands[j]); diff --git a/libImaging/Chops.c b/libImaging/Chops.c index 73d961231..8059b6ffb 100644 --- a/libImaging/Chops.c +++ b/libImaging/Chops.c @@ -19,41 +19,41 @@ #include "Imaging.h" -#define CHOP(operation, mode)\ +#define CHOP(operation, mode)\ int x, y;\ Imaging imOut;\ imOut = create(imIn1, imIn2, mode);\ if (!imOut)\ - return NULL;\ + return NULL;\ for (y = 0; y < imOut->ysize; y++) {\ - UINT8* out = (UINT8*) imOut->image[y];\ - UINT8* in1 = (UINT8*) imIn1->image[y];\ - UINT8* in2 = (UINT8*) imIn2->image[y];\ - for (x = 0; x < imOut->linesize; x++) {\ - int temp = operation;\ - if (temp <= 0)\ - out[x] = 0;\ - else if (temp >= 255)\ - out[x] = 255;\ - else\ - out[x] = temp;\ - }\ + UINT8* out = (UINT8*) imOut->image[y];\ + UINT8* in1 = (UINT8*) imIn1->image[y];\ + UINT8* in2 = (UINT8*) imIn2->image[y];\ + for (x = 0; x < imOut->linesize; x++) {\ + int temp = operation;\ + if (temp <= 0)\ + out[x] = 0;\ + else if (temp >= 255)\ + out[x] = 255;\ + else\ + out[x] = temp;\ + }\ }\ return imOut; -#define CHOP2(operation, mode)\ +#define CHOP2(operation, mode)\ int x, y;\ Imaging imOut;\ imOut = create(imIn1, imIn2, mode);\ if (!imOut)\ - return NULL;\ + return NULL;\ for (y = 0; y < imOut->ysize; y++) {\ - UINT8* out = (UINT8*) imOut->image[y];\ - UINT8* in1 = (UINT8*) imIn1->image[y];\ - UINT8* in2 = (UINT8*) imIn2->image[y];\ - for (x = 0; x < imOut->linesize; x++) {\ - out[x] = operation;\ - }\ + UINT8* out = (UINT8*) imOut->image[y];\ + UINT8* in1 = (UINT8*) imIn1->image[y];\ + UINT8* in2 = (UINT8*) imIn2->image[y];\ + for (x = 0; x < imOut->linesize; x++) {\ + out[x] = operation;\ + }\ }\ return imOut; @@ -64,15 +64,15 @@ create(Imaging im1, Imaging im2, char* mode) if (!im1 || !im2 || im1->type != IMAGING_TYPE_UINT8 || (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1")))) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (im1->type != im2->type || im1->bands != im2->bands) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); xsize = (im1->xsize < im2->xsize) ? im1->xsize : im2->xsize; ysize = (im1->ysize < im2->ysize) ? im1->ysize : im2->ysize; - return ImagingNew(im1->mode, xsize, ysize); + return ImagingNewDirty(im1->mode, xsize, ysize); } Imaging diff --git a/libImaging/Effects.c b/libImaging/Effects.c index 5b58fddbb..26b063a11 100644 --- a/libImaging/Effects.c +++ b/libImaging/Effects.c @@ -37,7 +37,7 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) if (width < 0.0 || height < 0.0 || quality < 2) return (Imaging) ImagingError_ValueError(NULL); - im = ImagingNew("L", xsize, ysize); + im = ImagingNewDirty("L", xsize, ysize); if (!im) return NULL; @@ -81,7 +81,7 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) int nextok; double this, next; - imOut = ImagingNew("L", xsize, ysize); + imOut = ImagingNewDirty("L", xsize, ysize); if (!imOut) return NULL; @@ -90,7 +90,7 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) for (y = 0; y < imOut->ysize; y++) { UINT8* out = imOut->image8[y]; - for (x = 0; x < imOut->xsize; x++) { + for (x = 0; x < imOut->xsize; x++) { if (nextok) { this = next; nextok = 0; @@ -121,14 +121,14 @@ ImagingEffectSpread(Imaging imIn, int distance) Imaging imOut; int x, y; - imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) - return NULL; + return NULL; #define SPREAD(type, image)\ - for (y = 0; y < imIn->ysize; y++)\ - for (x = 0; x < imIn->xsize; x++) {\ + for (y = 0; y < imOut->ysize; y++)\ + for (x = 0; x < imOut->xsize; x++) {\ int xx = x + (rand() % distance) - distance/2;\ int yy = y + (rand() % distance) - distance/2;\ if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\ diff --git a/libImaging/Filter.c b/libImaging/Filter.c index e6b1a4966..6e4a00501 100644 --- a/libImaging/Filter.c +++ b/libImaging/Filter.c @@ -27,13 +27,6 @@ #include "Imaging.h" -#ifdef WORDS_BIGENDIAN - #define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24)) -#else - #define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24)) -#endif - - static inline UINT8 clip8(float in) { if (in <= 0.0) @@ -91,29 +84,14 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) } -/* This is work around bug in GCC prior 4.9 in 64 bit mode. - GCC generates code with partial dependency which 3 times slower. - See: http://stackoverflow.com/a/26588074/253146 */ -#if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \ - ! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900) -static float __attribute__((always_inline)) inline i2f(int v) { - float x; - __asm__("xorps %0, %0; cvtsi2ss %1, %0" : "=X"(x) : "r"(v) ); - return x; -} -#else -static float inline i2f(int v) { return (float) v; } -#endif - - void ImagingFilter3x3(Imaging imOut, Imaging im, const float* kernel, float offset) { #define KERNEL1x3(in0, x, kernel, d) ( \ - i2f((UINT8) in0[x-d]) * (kernel)[0] + \ - i2f((UINT8) in0[x]) * (kernel)[1] + \ - i2f((UINT8) in0[x+d]) * (kernel)[2]) + _i2f((UINT8) in0[x-d]) * (kernel)[0] + \ + _i2f((UINT8) in0[x]) * (kernel)[1] + \ + _i2f((UINT8) in0[x+d]) * (kernel)[2]) int x = 0, y = 0; @@ -210,11 +188,11 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float* kernel, float offset) { #define KERNEL1x5(in0, x, kernel, d) ( \ - i2f((UINT8) in0[x-d-d]) * (kernel)[0] + \ - i2f((UINT8) in0[x-d]) * (kernel)[1] + \ - i2f((UINT8) in0[x]) * (kernel)[2] + \ - i2f((UINT8) in0[x+d]) * (kernel)[3] + \ - i2f((UINT8) in0[x+d+d]) * (kernel)[4]) + _i2f((UINT8) in0[x-d-d]) * (kernel)[0] + \ + _i2f((UINT8) in0[x-d]) * (kernel)[1] + \ + _i2f((UINT8) in0[x]) * (kernel)[2] + \ + _i2f((UINT8) in0[x+d]) * (kernel)[3] + \ + _i2f((UINT8) in0[x+d+d]) * (kernel)[4]) int x = 0, y = 0; diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index dee2b899c..bc63ac413 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -5,7 +5,8 @@ Rotating in chunks that fit in the cache can speed up rotation 8x on a modern CPU. A chunk size of 128 requires only 65k and is large enough that the overhead from the extra loops are not apparent. */ -#define ROTATE_CHUNK 128 +#define ROTATE_CHUNK 512 +#define ROTATE_SMALL_CHUNK 8 #define COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) @@ -26,30 +27,27 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) ImagingCopyPalette(imOut, imIn); +#define FLIP_LEFT_RIGHT(INT, image) \ + for (y = 0; y < imIn->ysize; y++) { \ + INT* in = imIn->image[y]; \ + INT* out = imOut->image[y]; \ + xr = imIn->xsize-1; \ + for (x = 0; x < imIn->xsize; x++, xr--) \ + out[xr] = in[x]; \ + } + ImagingSectionEnter(&cookie); if (imIn->image8) { - for (y = 0; y < imIn->ysize; y++) { - UINT8* in = (UINT8*) imIn->image8[y]; - UINT8* out = (UINT8*) imOut->image8[y]; - x = 0; - xr = imIn->xsize-1; - for (; x < imIn->xsize; x++, xr--) - out[xr] = in[x]; - } + FLIP_LEFT_RIGHT(UINT8, image8) } else { - for (y = 0; y < imIn->ysize; y++) { - UINT32* in = (UINT32*) imIn->image32[y]; - UINT32* out = (UINT32*) imOut->image32[y]; - x = 0; - xr = imIn->xsize-1; - for (; x < imIn->xsize; x++, xr--) - out[xr] = in[x]; - } + FLIP_LEFT_RIGHT(INT32, image32) } ImagingSectionLeave(&cookie); +#undef FLIP_LEFT_RIGHT + return imOut; } @@ -84,6 +82,7 @@ ImagingRotate90(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xr, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -92,15 +91,22 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingCopyPalette(imOut, imIn); -#define ROTATE_90(image) \ +#define ROTATE_90(INT, image) \ for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ - for (yy = y; yy < yysize; yy++) { \ - xr = imIn->xsize - 1 - x; \ - for (xx = x; xx < xxsize; xx++, xr--) { \ - imOut->image[xr][yy] = imIn->image[yy][xx]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + for (yyy = yy; yyy < yyysize; yyy++) { \ + INT* in = imIn->image[yyy]; \ + xr = imIn->xsize - 1 - xx; \ + for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \ + imOut->image[xr][yyy] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -109,9 +115,9 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_90(image8) + ROTATE_90(UINT8, image8) else - ROTATE_90(image32) + ROTATE_90(INT32, image32) ImagingSectionLeave(&cookie); @@ -126,6 +132,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -134,14 +141,21 @@ ImagingTranspose(Imaging imOut, Imaging imIn) ImagingCopyPalette(imOut, imIn); -#define TRANSPOSE(image) \ +#define TRANSPOSE(INT, image) \ for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ - for (yy = y; yy < yysize; yy++) { \ - for (xx = x; xx < xxsize; xx++) { \ - imOut->image[xx][yy] = imIn->image[yy][xx]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + for (yyy = yy; yyy < yyysize; yyy++) { \ + INT* in = imIn->image[yyy]; \ + for (xxx = xx; xxx < xxxsize; xxx++) { \ + imOut->image[xxx][yyy] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -150,9 +164,9 @@ ImagingTranspose(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - TRANSPOSE(image8) + TRANSPOSE(UINT8, image8) else - TRANSPOSE(image32) + TRANSPOSE(INT32, image32) ImagingSectionLeave(&cookie); @@ -162,6 +176,57 @@ ImagingTranspose(Imaging imOut, Imaging imIn) } +Imaging +ImagingTransverse(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xr, yr, xx, yy, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + +#define TRANSVERSE(INT, image) \ + for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ + for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ + yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ + xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + yr = imIn->ysize - 1 - yy; \ + for (yyy = yy; yyy < yyysize; yyy++, yr--) { \ + INT* in = imIn->image[yyy]; \ + xr = imIn->xsize - 1 - xx; \ + for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \ + imOut->image[xr][yr] = in[xxx]; \ + } \ + } \ + } \ + } \ + } \ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + TRANSVERSE(UINT8, image8) + else + TRANSVERSE(INT32, image32) + + ImagingSectionLeave(&cookie); + +#undef TRANSVERSE + + return imOut; +} + + Imaging ImagingRotate180(Imaging imOut, Imaging imIn) { @@ -175,20 +240,23 @@ ImagingRotate180(Imaging imOut, Imaging imIn) ImagingCopyPalette(imOut, imIn); -#define ROTATE_180(image)\ - for (y = 0; y < imIn->ysize; y++, yr--) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[y][x] = imIn->image[yr][xr];\ +#define ROTATE_180(INT, image) \ + for (y = 0; y < imIn->ysize; y++, yr--) { \ + INT* in = imIn->image[y]; \ + INT* out = imOut->image[yr]; \ + xr = imIn->xsize-1; \ + for (x = 0; x < imIn->xsize; x++, xr--) \ + out[xr] = in[x]; \ } ImagingSectionEnter(&cookie); yr = imIn->ysize-1; - if (imIn->image8) - ROTATE_180(image8) - else - ROTATE_180(image32) + if (imIn->image8) { + ROTATE_180(UINT8, image8) + } else { + ROTATE_180(INT32, image32) + } ImagingSectionLeave(&cookie); @@ -203,6 +271,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, yr, xxsize, yysize; + int xxx, yyy, xxxsize, yyysize; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -211,15 +280,22 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingCopyPalette(imOut, imIn); -#define ROTATE_270(image) \ +#define ROTATE_270(INT, image) \ for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \ for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \ yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \ xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \ - yr = imIn->ysize - 1 - y; \ - for (yy = y; yy < yysize; yy++, yr--) { \ - for (xx = x; xx < xxsize; xx++) { \ - imOut->image[xx][yr] = imIn->image[yy][xx]; \ + for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \ + for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \ + yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \ + xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \ + yr = imIn->ysize - 1 - yy; \ + for (yyy = yy; yyy < yyysize; yyy++, yr--) { \ + INT* in = imIn->image[yyy]; \ + for (xxx = xx; xxx < xxxsize; xxx++) { \ + imOut->image[xxx][yr] = in[xxx]; \ + } \ + } \ } \ } \ } \ @@ -228,9 +304,9 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_270(image8) + ROTATE_270(UINT8, image8) else - ROTATE_270(image32) + ROTATE_270(INT32, image32) ImagingSectionLeave(&cookie); diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 145cc62fe..e55e6a6e8 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -20,7 +20,7 @@ extern "C" { #ifndef M_PI -#define M_PI 3.1415926535897932384626433832795 +#define M_PI 3.1415926535897932384626433832795 #endif @@ -29,18 +29,18 @@ extern "C" { /* * Image data organization: * - * mode bytes byte order + * mode bytes byte order * ------------------------------- - * 1 1 1 - * L 1 L - * P 1 P + * 1 1 1 + * L 1 L + * P 1 P * I 4 I (32-bit integer, native byte order) * F 4 F (32-bit IEEE float, native byte order) - * RGB 4 R, G, B, - - * RGBA 4 R, G, B, A - * CMYK 4 C, M, Y, K - * YCbCr 4 Y, Cb, Cr, - - * Lab 4 L, a, b, - + * RGB 4 R, G, B, - + * RGBA 4 R, G, B, A + * CMYK 4 C, M, Y, K + * YCbCr 4 Y, Cb, Cr, - + * Lab 4 L, a, b, - * * experimental modes (incomplete): * LA 4 L, -, -, A @@ -78,26 +78,26 @@ typedef struct ImagingPaletteInstance* ImagingPalette; struct ImagingMemoryInstance { /* Format */ - char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ - int type; /* Data type (IMAGING_TYPE_*) */ - int depth; /* Depth (ignored in this version) */ - int bands; /* Number of bands (1, 2, 3, or 4) */ - int xsize; /* Image dimension. */ + char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ + int type; /* Data type (IMAGING_TYPE_*) */ + int depth; /* Depth (ignored in this version) */ + int bands; /* Number of bands (1, 2, 3, or 4) */ + int xsize; /* Image dimension. */ int ysize; /* Colour palette (for "P" images only) */ ImagingPalette palette; /* Data pointers */ - UINT8 **image8; /* Set for 8-bit images (pixelsize=1). */ - INT32 **image32; /* Set for 32-bit images (pixelsize=4). */ + UINT8 **image8; /* Set for 8-bit images (pixelsize=1). */ + INT32 **image32; /* Set for 32-bit images (pixelsize=4). */ /* Internals */ - char **image; /* Actual raster data. */ - char *block; /* Set if data is allocated in a single block. */ + char **image; /* Actual raster data. */ + char *block; /* Set if data is allocated in a single block. */ - int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ - int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ + int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ + int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ /* Virtual methods */ void (*destroy)(Imaging im); @@ -130,11 +130,11 @@ struct ImagingAccessInstance { struct ImagingHistogramInstance { /* Format */ - char mode[IMAGING_MODE_LENGTH]; /* Band names (of corresponding source image) */ - int bands; /* Number of bands (1, 3, or 4) */ + char mode[IMAGING_MODE_LENGTH]; /* Band names (of corresponding source image) */ + int bands; /* Number of bands (1, 3, or 4) */ /* Data */ - long *histogram; /* Histogram (bands*256 longs) */ + long *histogram; /* Histogram (bands*256 longs) */ }; @@ -142,13 +142,13 @@ struct ImagingHistogramInstance { struct ImagingPaletteInstance { /* Format */ - char mode[IMAGING_MODE_LENGTH]; /* Band names */ + char mode[IMAGING_MODE_LENGTH]; /* Band names */ /* Data */ UINT8 palette[1024];/* Palette data (same format as image data) */ - INT16* cache; /* Palette cache (used for predefined palettes) */ - int keep_cache; /* This palette will be reused; keep cache */ + INT16* cache; /* Palette cache (used for predefined palettes) */ + int keep_cache; /* This palette will be reused; keep cache */ }; @@ -170,8 +170,8 @@ extern Imaging ImagingNewMap(const char* filename, int readonly, extern Imaging ImagingNewPrologue(const char *mode, int xsize, int ysize); extern Imaging ImagingNewPrologueSubtype(const char *mode, - int xsize, int ysize, - int structure_size); + int xsize, int ysize, + int structure_size); extern void ImagingCopyPalette(Imaging destination, Imaging source); @@ -181,8 +181,6 @@ extern void ImagingAccessInit(void); extern ImagingAccess ImagingAccessNew(Imaging im); extern void _ImagingAccessDelete(Imaging im, ImagingAccess access); #define ImagingAccessDelete(im, access) /* nop, for now */ -/*#define ImagingAccessDelete(im, access) \ - ((access)->dynamic ? _ImagingAccessDelete((im), (access)), 0 : 0)) */ extern ImagingPalette ImagingPaletteNew(const char *mode); extern ImagingPalette ImagingPaletteNewBrowser(void); @@ -191,10 +189,10 @@ extern void ImagingPaletteDelete(ImagingPalette palette); extern int ImagingPaletteCachePrepare(ImagingPalette palette); extern void ImagingPaletteCacheUpdate(ImagingPalette palette, - int r, int g, int b); + int r, int g, int b); extern void ImagingPaletteCacheDelete(ImagingPalette palette); -#define ImagingPaletteCache(p, r, g, b)\ +#define ImagingPaletteCache(p, r, g, b)\ p->cache[(r>>2) + (g>>2)*64 + (b>>2)*64*64] extern Imaging ImagingQuantize(Imaging im, int colours, int mode, int kmeans); @@ -264,14 +262,14 @@ extern Imaging ImagingFilter( extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn); extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, - int passes); + int passes); extern Imaging ImagingGetBand(Imaging im, int band); extern Imaging ImagingMerge(const char* mode, Imaging bands[4]); extern int ImagingSplit(Imaging im, Imaging bands[4]); extern int ImagingGetBBox(Imaging im, int bbox[4]); typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem; extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors, - int *colors); + int *colors); extern int ImagingGetExtrema(Imaging im, void *extrema); extern int ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj); extern ImagingHistogram ImagingGetHistogram( @@ -291,8 +289,9 @@ extern Imaging ImagingRankFilter(Imaging im, int size, int rank); extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); -extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); +extern Imaging ImagingTransverse(Imaging imOut, Imaging imIn); +extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingTransform( Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1, double *a, int filter, int fill); @@ -336,7 +335,7 @@ extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int op); extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, - const void* ink, int op); + const void* ink, int op); extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int width, int op); extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, @@ -344,9 +343,9 @@ extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, int op); extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op); extern int ImagingDrawPolygon(Imaging im, int points, int *xy, - const void* ink, int fill, int op); + const void* ink, int fill, int op); extern int ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, - const void* ink, int fill, int op); + const void* ink, int fill, int op); /* Level 2 graphics (WORK IN PROGRESS) */ extern ImagingOutline ImagingOutlineNew(void); @@ -358,7 +357,7 @@ extern int ImagingDrawOutline(Imaging im, ImagingOutline outline, extern int ImagingOutlineMove(ImagingOutline outline, float x, float y); extern int ImagingOutlineLine(ImagingOutline outline, float x, float y); extern int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, - float x2, float y2, float x3, float y3); + float x2, float y2, float x3, float y3); extern int ImagingOutlineTransform(ImagingOutline outline, double a[6]); extern int ImagingOutlineClose(ImagingOutline outline); @@ -387,30 +386,30 @@ extern UINT32 ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes); /* Codecs */ typedef struct ImagingCodecStateInstance *ImagingCodecState; typedef int (*ImagingCodec)(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingBcnDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingBitDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingEpsEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingFliDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingGifDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingGifEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingHexDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); -#ifdef HAVE_LIBJPEG + UINT8* buffer, int bytes); +#ifdef HAVE_LIBJPEG extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingJpegDecodeCleanup(ImagingCodecState state); extern int ImagingJpegUseJCSExtensions(void); extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); #endif #ifdef HAVE_OPENJPEG extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, @@ -421,45 +420,45 @@ extern int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, extern int ImagingJpeg2KEncodeCleanup(ImagingCodecState state); #endif extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); -#ifdef HAVE_LIBTIFF + UINT8* buffer, int bytes); +#ifdef HAVE_LIBTIFF extern int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); #endif -#ifdef HAVE_LIBMPEG +#ifdef HAVE_LIBMPEG extern int ImagingMpegDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); #endif extern int ImagingMspDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingPackbitsDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingPcdDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingPcxDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingPcxEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingRawDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingRawEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingSunRleDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingTgaRleDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingXbmDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingXbmEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); -#ifdef HAVE_LIBZ + UINT8* buffer, int bytes); +#ifdef HAVE_LIBZ extern int ImagingZipDecode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingZipDecodeCleanup(ImagingCodecState state); extern int ImagingZipEncode(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); extern int ImagingZipEncodeCleanup(ImagingCodecState state); #endif @@ -503,12 +502,12 @@ extern Py_ssize_t _imaging_tell_pyFd(PyObject *fd); /* Errcodes */ -#define IMAGING_CODEC_END 1 -#define IMAGING_CODEC_OVERRUN -1 -#define IMAGING_CODEC_BROKEN -2 -#define IMAGING_CODEC_UNKNOWN -3 -#define IMAGING_CODEC_CONFIG -8 -#define IMAGING_CODEC_MEMORY -9 +#define IMAGING_CODEC_END 1 +#define IMAGING_CODEC_OVERRUN -1 +#define IMAGING_CODEC_BROKEN -2 +#define IMAGING_CODEC_UNKNOWN -3 +#define IMAGING_CODEC_CONFIG -8 +#define IMAGING_CODEC_MEMORY -9 diff --git a/libImaging/ImagingUtils.h b/libImaging/ImagingUtils.h index 5541fec30..b040fc303 100644 --- a/libImaging/ImagingUtils.h +++ b/libImaging/ImagingUtils.h @@ -28,3 +28,18 @@ #define PREBLEND(mask, in1, in2, tmp1)\ (MULDIV255(in1, (255 - mask), tmp1) + in2) + + +/* This is to work around a bug in GCC prior 4.9 in 64 bit mode. + GCC generates code with partial dependency which is 3 times slower. + See: http://stackoverflow.com/a/26588074/253146 */ +#if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \ + ! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900) +static float __attribute__((always_inline)) inline _i2f(int v) { + float x; + __asm__("xorps %0, %0; cvtsi2ss %1, %0" : "=X"(x) : "r"(v) ); + return x; +} +#else +static float inline _i2f(int v) { return (float) v; } +#endif diff --git a/libImaging/ModeFilter.c b/libImaging/ModeFilter.c index 1cdb6300d..5237d0732 100644 --- a/libImaging/ModeFilter.c +++ b/libImaging/ModeFilter.c @@ -26,11 +26,11 @@ ImagingModeFilter(Imaging im, int size) int histogram[256]; if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); - imOut = ImagingNew(im->mode, im->xsize, im->ysize); + imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) - return NULL; + return NULL; size = size / 2; diff --git a/libImaging/Quant.c b/libImaging/Quant.c index 25aa7f079..df3138162 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -1642,11 +1642,11 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) ImagingSectionLeave(&cookie); if (result > 0) { - imOut = ImagingNew("P", im->xsize, im->ysize); + imOut = ImagingNewDirty("P", im->xsize, im->ysize); ImagingSectionEnter(&cookie); for (i = y = 0; y < im->ysize; y++) - for (x=0; x < im->xsize; x++) + for (x = 0; x < im->xsize; x++) imOut->image8[y][x] = (unsigned char) newData[i++]; free(newData); diff --git a/winbuild/config.py b/winbuild/config.py index 87d485568..310a58eb8 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -34,9 +34,9 @@ libs = { 'dir': 'tiff-4.0.8', }, 'freetype': { - 'url': 'https://download.savannah.gnu.org/releases/freetype/freetype-2.8.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'freetype-2.8.tar.gz', - 'dir': 'freetype-2.8', + 'url': 'https://download.savannah.gnu.org/releases/freetype/freetype-2.8.1.tar.gz', + 'filename': PILLOW_DEPENDS_DIR + 'freetype-2.8.1.tar.gz', + 'dir': 'freetype-2.8.1', }, 'lcms': { 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',