mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge branch 'master' into imaging-copy-palette-info
This commit is contained in:
commit
bca70a743d
24
CHANGES.rst
24
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]
|
||||
|
||||
|
|
32
PIL/Image.py
32
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,9 +1885,7 @@ 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):
|
||||
elif HAS_PATHLIB and isinstance(fp, Path):
|
||||
filename = str(fp)
|
||||
open_fp = True
|
||||
if not filename and hasattr(fp, "name") and isPath(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):
|
||||
elif HAS_PATHLIB and isinstance(fp, Path):
|
||||
filename = str(fp.resolve())
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if filename:
|
||||
fp = builtins.open(filename, "rb")
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
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":
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -22,13 +22,10 @@ from PIL import Image, ImageTk
|
|||
class UI(tkinter.Label):
|
||||
|
||||
def __init__(self, master, im):
|
||||
if isinstance(im, list):
|
||||
# list of images
|
||||
self.im = im[1:]
|
||||
im = self.im[0]
|
||||
else:
|
||||
# sequence
|
||||
self.im = im
|
||||
if isinstance(self.im, list):
|
||||
# list of images
|
||||
im = self.im.pop(0)
|
||||
|
||||
if im.mode == "1":
|
||||
self.image = ImageTk.BitmapImage(im, foreground="white")
|
||||
|
|
BIN
Tests/images/issue_2278.tif
Normal file
BIN
Tests/images/issue_2278.tif
Normal file
Binary file not shown.
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,7 +8,8 @@ class TestImageFilter(PillowTestCase):
|
|||
def test_sanity(self):
|
||||
|
||||
def filter(filter):
|
||||
im = hopper("L")
|
||||
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)
|
||||
|
@ -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))
|
||||
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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),
|
||||
|
|
19
_imaging.c
19
_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
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
======================
|
||||
|
||||
|
|
|
@ -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<<PRECISION_BITS) / outa255;
|
||||
UINT32 coef2 = 255 * (1<<PRECISION_BITS) - coef1;
|
||||
|
||||
tmpr = src->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)) >> PRECISION_BITS;
|
||||
out->g = SHIFTFORDIV255(tmpg + (0x80<<PRECISION_BITS)) >> PRECISION_BITS;
|
||||
out->b = SHIFTFORDIV255(tmpb + (0x80<<PRECISION_BITS)) >> PRECISION_BITS;
|
||||
out->a = SHIFTFORDIV255(outa255 + 0x80);
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -72,7 +72,7 @@ create(Imaging im1, Imaging im2, char* mode)
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
#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) {\
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,7 @@ ImagingModeFilter(Imaging im, int size)
|
|||
if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
|
||||
imOut = ImagingNew(im->mode, im->xsize, im->ysize);
|
||||
imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize);
|
||||
if (!imOut)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue
Block a user