diff --git a/.travis.yml b/.travis.yml index 0b6263480..382f205d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,6 @@ after_success: - travis_retry pip install coveralls-merge - coveralls-merge coverage.c.json - - travis_retry pip install pep8 pyflakes - pep8 --statistics --count PIL/*.py - pep8 --statistics --count Tests/*.py @@ -62,10 +61,53 @@ after_success: - pyflakes PIL/*.py | tee >(wc -l) - pyflakes Tests/*.py | tee >(wc -l) - # Coverage and quality reports on just the latest diff. # (Installation is very slow on Py3, so just do it for Py2.) - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-install.sh; fi - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-run.sh; fi + + # after_all + - | + if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py + python travis_after_all.py + export $(cat .to_export_back) + if [ "$BUILD_LEADER" = "YES" ]; then + if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then + echo "All jobs succeded! Triggering OS X build..." + # Trigger an OS X build at the pillow-wheels repo + ./build_children.sh + else + echo "Some jobs failed" + fi + fi + fi + +after_failure: + - | + if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py + python travis_after_all.py + export $(cat .to_export_back) + if [ "$BUILD_LEADER" = "YES" ]; then + if [ "$BUILD_AGGREGATE_STATUS" = "others_failed" ]; then + echo "All jobs failed" + else + echo "Some jobs failed" + fi + fi + fi + +after_script: + - | + if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS + fi + matrix: fast_finish: true + +env: + global: + # travis encrypt AUTH_TOKEN= + secure: "Vzm7aG1Qv0SDQcqiPzZMedNLn5ZmpL7IzF0DYnqcD+/l+zmKU22SnJBcX0uVXumo+r7eZfpsShpqfcdsZvMlvmQnwz+Y6AGKQru9tCKZbTMnuRjWKKXekC+tr8Xt9CKvRVtte5PyXW31paxUI3/e+fQGBwoFjEEC+6EpEOjeRfE=" diff --git a/CHANGES.rst b/CHANGES.rst index 9db9a6645..dea818960 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,13 +1,55 @@ Changelog (Pillow) ================== +2.8.0 (unreleased) +------------------ + +- Improve reference docs for PIL.ImageDraw.Draw.pieslice() #1145 + [audreyr] + +- Added copy method font_variant() and accessible properties to truetype() #1123 + [radarhere] + +- Fix ImagingEffectNoise #1128 + [hugovk] + +- Remove unreachable code + [hugovk] + +- Let Python do the endian stuff + tests #1121 + [amoibos, radarhere] + +- Fix webp decode memory leak #1114 + [benoit-pierre] + +- Fast path for opaque pixels in RGBa unpacker #1088 + [bgilbert] + +- Enable basic support for 'RGBa' raw encoding/decoding #1096 + [immerrr] + +- Fix pickling L mode images with no palette, #1095 + [hugovk] + +- iPython display hook #1091 + [wiredfool] + +- Adjust buffer size when quality=keep, fixes #148 (again) + [wiredfool] + +- Fix for corrupted bitmaps embedded in truetype fonts. #1072 + [jackyyf, wiredfool] + 2.7.0 (2015-01-01) ------------------ +- Split Sane into a separate repo: https://github.com/python-pillow/Sane + [hugovk] + - Look for OSX and Linux fonts in common places. #1054 [charleslaw] -- Fix potential PNG decompression DOS #1060 +- Fix CVE-2014-9601, potential PNG decompression DOS #1060 [wiredfool] - Use underscores, not spaces, in TIFF tag kwargs. #1044, #1058 @@ -22,13 +64,13 @@ Changelog (Pillow) - Fix MSVC compiler error: Use Py_ssize_t instead of ssize_t #1051 [cgohlke] -- Fix compiler error: MSVC needs varaibles defined at the start of the block #1048 +- Fix compiler error: MSVC needs variables defined at the start of the block #1048 [cgohlke] - The GIF Palette optimization algorithm is only applicable to mode='P' or 'L' #993 [moriyoshi] -- Use PySide as an alernative to PyQt4/5. +- Use PySide as an alternative to PyQt4/5. [holg] - Replace affine-based im.resize implementation with convolution-based im.stretch #997 @@ -82,7 +124,7 @@ Changelog (Pillow) 2.6.2 (2015-01-01) ------------------ -- Fix potential PNG decompression DOS #1060 +- Fix CVE-2014-9601, potential PNG decompression DOS #1060 [wiredfool] - Fix Regression in PyPy 2.4 in streamio #958 @@ -1757,7 +1799,7 @@ Changelog (Pillow) (1.1.2c1 and 1.1.2 final released) + Adapted to Python 2.1. Among other things, all uses of the - "regex" module has been repleased with "re". + "regex" module have been replaced with "re". + Fixed attribute error when reading large PNG files (this bug was introduced in maintenance code released after the 1.1.1 @@ -2381,7 +2423,7 @@ Changelog (Pillow) the default value is 75. JPEG smooth smooth dithered images. value - is strengh (1-100). default is + is strength (1-100). default is off (0). PNG optimize minimize output file at the diff --git a/MANIFEST.in b/MANIFEST.in index 8a8694ad1..b808ad17b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include *.c include *.h include *.md include *.py +include *.sh include *.rst include *.txt include *.yaml diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index db8cec0ca..b4817db27 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -273,7 +273,7 @@ class IcoImageFile(ImageFile.ImageFile): self.size = im.size def load_seek(self): - # Flage the ImageFile.Parser so that it + # Flag the ImageFile.Parser so that it # just does all the decode at the end. pass # diff --git a/PIL/Image.py b/PIL/Image.py index 98e3644c0..2f304a9a8 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -598,6 +598,16 @@ class Image: id(self) ) + def _repr_png_(self): + """ iPython display hook support + + :returns: png version of the image as bytes + """ + from io import BytesIO + b = BytesIO() + self.save(b, 'PNG') + return b.getvalue() + def __getattr__(self, name): if name == "__array_interface__": # numpy array interface support @@ -625,7 +635,7 @@ class Image: self.mode = mode self.size = size self.im = core.new(mode, size) - if mode in ("L", "P"): + if mode in ("L", "P") and palette: self.putpalette(palette) self.frombytes(data) @@ -2024,7 +2034,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args): You can also use any pixel decoder supported by PIL. For more information on available decoders, see the section - **Writing Your Own File Decoder**. + :ref:`Writing Your Own File Decoder `. Note that this function decodes pixel data only, not entire images. If you have an entire image in a string, wrap it in a diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index afbae372f..a9c50e560 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -133,6 +133,11 @@ class FreeTypeFont: DeprecationWarning) font = file + self.path = font + self.size = size + self.index = index + self.encoding = encoding + if isPath(font): self.font = core.getfont(font, size, index, encoding) else: @@ -162,6 +167,22 @@ class FreeTypeFont: self.font.render(text, im.id, mode == "1") return im, offset + def font_variant(self, font=None, size=None, index=None, encoding=None): + """ + Create a copy of this FreeTypeFont object, + using any specified arguments to override the settings. + + Parameters are identical to the parameters used to initialize this + object, minus the deprecated 'file' argument. + + :return: A FreeTypeFont object. + """ + return FreeTypeFont(font=self.path if font is None else font, + size=self.size if size is None else size, + index=self.index if index is None else index, + encoding=self.encoding if encoding is None else + encoding) + ## # Wrapper that creates a transposed font from any existing font # object. @@ -214,7 +235,7 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): This function requires the _imagingft service. - :param filename: A truetype font file. Under Windows, if the file + :param font: A truetype font file. Under Windows, if the file is not found in this filename, the loader also looks in Windows :file:`fonts/` directory. :param size: The requested size, in points. @@ -224,6 +245,7 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). See the FreeType documentation for more information. + :param filename: Deprecated. Please use font instead. :return: A font object. :exception IOError: If the file could not be read. """ @@ -254,8 +276,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): elif sys.platform in ('linux', 'linux2'): lindirs = os.environ.get("XDG_DATA_DIRS", "") if not lindirs: - #According to the freedesktop spec, XDG_DATA_DIRS should - #default to /usr/share + # According to the freedesktop spec, XDG_DATA_DIRS should + # default to /usr/share lindirs = '/usr/share' lindirs = lindirs.split(":") for lindir in lindirs: @@ -265,7 +287,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): filepath = os.path.join(walkroot, ttf_filename) return FreeTypeFont(filepath, size, index, encoding) elif sys.platform == 'darwin': - macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] + macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', + os.path.expanduser('~/Library/Fonts/')] for macdir in macdirs: filepath = os.path.join(macdir, ttf_filename) if os.path.exists(filepath): diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 9dd79d5e6..8c20f5863 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -684,7 +684,8 @@ def _save(im, fp, filename): # https://github.com/jdriscoll/django-imagekit/issues/50 bufsize = 0 if "optimize" in info or "progressive" in info or "progression" in info: - if quality >= 95: + # keep sets quality to 0, but the actual value may be high. + if quality >= 95 or quality == 0: bufsize = 2 * im.size[0] * im.size[1] else: bufsize = im.size[0] * im.size[1] diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 7a9becd3b..d8593f90d 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -175,7 +175,7 @@ class iTXt(str): :param lang: language code :param tkey: UTF-8 version of the key name """ - + self = str.__new__(cls, text) self.lang = lang self.tkey = tkey @@ -198,7 +198,7 @@ class PngInfo: :param data: a byte string of the encoded data """ - + self.chunks.append((cid, data)) def add_itxt(self, key, value, lang="", tkey="", zip=False): @@ -211,7 +211,7 @@ class PngInfo: :param zip: compression flag """ - + if not isinstance(key, bytes): key = key.encode("latin-1", "strict") if not isinstance(value, bytes): @@ -235,7 +235,7 @@ class PngInfo: :param value: value for this key, text or an :py:class:`PIL.PngImagePlugin.iTXt` instance :param zip: compression flag - + """ if isinstance(value, iTXt): return self.add_itxt(key, value, value.lang, value.tkey, bool(zip)) @@ -462,7 +462,7 @@ class PngStream(ChunkStream): self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) self.check_text_memory(len(v)) - + return s @@ -729,10 +729,6 @@ def _save(im, fp, filename, chunk=putchunk, check=0): alpha_bytes = 2**bits chunk(fp, b"tRNS", alpha[:alpha_bytes]) - if 0: - # FIXME: to be supported some day - chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) - dpi = im.encoderinfo.get("dpi") if dpi: chunk(fp, b"pHYs", diff --git a/PIL/_binary.py b/PIL/_binary.py index 51ce45a79..2f5e8ffd4 100644 --- a/PIL/_binary.py +++ b/PIL/_binary.py @@ -11,6 +11,8 @@ # See the README file for information on usage and redistribution. # +from struct import unpack, pack + if bytes is str: def i8(c): return ord(c) @@ -34,7 +36,7 @@ def i16le(c, o=0): c: string containing bytes to convert o: offset of bytes to convert in string """ - return i8(c[o]) | (i8(c[o+1]) << 8) + return unpack("H", c[o:o+2])[0] def i32be(c, o=0): - return ((i8(c[o]) << 24) | (i8(c[o+1]) << 16) | - (i8(c[o+2]) << 8) | i8(c[o+3])) + return unpack(">I", c[o:o+4])[0] # Output, le = little endian, be = big endian def o16le(i): - return o8(i) + o8(i >> 8) + return pack("> 8) + o8(i >> 16) + o8(i >> 24) + return pack("> 8) + o8(i) + return pack(">H", i) def o32be(i): - return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i) + return pack(">I", i) # End of file diff --git a/README.rst b/README.rst index dfd3e2c31..ce1c707c3 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,19 @@ Pillow ====== -*Python Imaging Library (Fork)* +Python Imaging Library (Fork) +----------------------------- -Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation `_, `check the changelog `_ and `find out how to contribute `_. +Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow - :alt: Travis CI build status + :alt: Travis CI build status (Linux) +.. image:: https://travis-ci.org/python-pillow/pillow-wheels.svg?branch=latest + :target: https://travis-ci.org/python-pillow/pillow-wheels + :alt: Travis CI build status (OS X) + .. image:: https://pypip.in/v/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Latest PyPI version @@ -24,3 +29,15 @@ Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_ +- `Contribute `_ +- `Documentation `_ + + - `About `_ + - `Guides `_ + - `Installation `_ + - `Reference `_ diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 9fa5e71a4..bd4de99c1 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -78,7 +78,7 @@ def makedelta(fp, sequence): if not previous: # global header - for s in getheader(im) + getdata(im): + for s in getheader(im)[0] + getdata(im): fp.write(s) else: diff --git a/Tests/README.rst b/Tests/README.rst index 6fd770f55..e3f160b09 100644 --- a/Tests/README.rst +++ b/Tests/README.rst @@ -3,7 +3,7 @@ Pillow Tests Test scripts are named ``test_xxx.py`` and use the ``unittest`` module. A base class and helper functions can be found in ``helper.py``. -Depedencies +Dependencies ----------- Install:: diff --git a/Tests/check_webp_leaks.py b/Tests/check_webp_leaks.py new file mode 100644 index 000000000..b5875c8ab --- /dev/null +++ b/Tests/check_webp_leaks.py @@ -0,0 +1,37 @@ +from helper import unittest, PillowTestCase +import sys +from PIL import Image +from io import BytesIO + +# Limits for testing the leak +mem_limit = 16 # max increase in MB +iterations = 5000 +test_file = "Tests/images/hopper.webp" + + +@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") +class TestWebPLeaks(PillowTestCase): + + def setUp(self): + try: + from PIL import _webp + except ImportError: + self.skipTest('WebP support not installed') + + def _get_mem_usage(self): + from resource import getpagesize, getrusage, RUSAGE_SELF + mem = getrusage(RUSAGE_SELF).ru_maxrss + return mem * getpagesize() / 1024 / 1024 + + def test_leak_load(self): + with open(test_file, 'rb') as f: + im_data = f.read() + start_mem = self._get_mem_usage() + for count in range(iterations): + with Image.open(BytesIO(im_data)) as im: + im.load() + mem = (self._get_mem_usage() - start_mem) + self.assertLess(mem, mem_limit, msg='memory usage limit exceeded') + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/fonts/DejaVuSans-bitmap.ttf b/Tests/fonts/DejaVuSans-bitmap.ttf new file mode 100644 index 000000000..702cce37d Binary files /dev/null and b/Tests/fonts/DejaVuSans-bitmap.ttf differ diff --git a/Tests/fonts/DejaVuSans.ttf b/Tests/fonts/DejaVuSans.ttf new file mode 100644 index 000000000..683bbbd41 Binary files /dev/null and b/Tests/fonts/DejaVuSans.ttf differ diff --git a/Tests/test_binary.py b/Tests/test_binary.py new file mode 100644 index 000000000..4d3fb5914 --- /dev/null +++ b/Tests/test_binary.py @@ -0,0 +1,28 @@ +from helper import unittest, PillowTestCase + +from PIL import _binary + +class TestBinary(PillowTestCase): + + def test_standard(self): + self.assertEqual(_binary.i8(b'*'), 42) + self.assertEqual(_binary.o8(42), b'*') + + def test_little_endian(self): + self.assertEqual(_binary.i16le(b'\xff\xff\x00\x00'), 65535) + self.assertEqual(_binary.i32le(b'\xff\xff\x00\x00'), 65535) + + self.assertEqual(_binary.o16le(65535), b'\xff\xff') + self.assertEqual(_binary.o32le(65535), b'\xff\xff\x00\x00') + + def test_big_endian(self): + self.assertEqual(_binary.i16be(b'\x00\x00\xff\xff'), 0) + self.assertEqual(_binary.i32be(b'\x00\x00\xff\xff'), 65535) + + self.assertEqual(_binary.o16be(65535), b'\xff\xff') + self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff') + +if __name__ == '__main__': + unittest.main() + +# End of file \ No newline at end of file diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index b0f179d5f..64e8b74af 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -3,6 +3,7 @@ from helper import djpeg_available, cjpeg_available import random from io import BytesIO +import os from PIL import Image from PIL import ImageFile @@ -334,6 +335,24 @@ class TestFileJpeg(PillowTestCase): self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001) self.assertEqual(tag_ids['RelatedImageLength'], 0x1002) + def test_MAXBLOCK_scaling(self): + def gen_random_image(size): + """ Generates a very hard to compress file + :param size: tuple + """ + return Image.frombytes('RGB',size, os.urandom(size[0]*size[1] *3)) + + im = gen_random_image((512,512)) + f = self.tempfile("temp.jpeg") + im.save(f, quality=100, optimize=True) + + reloaded = Image.open(f) + + # none of these should crash + reloaded.save(f, quality='keep') + reloaded.save(f, quality='keep', progressive=True) + reloaded.save(f, quality='keep', optimize=True) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index b556199f5..4cd5dc703 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -374,6 +374,14 @@ class TestFilePng(PillowTestCase): im = roundtrip(im) self.assertEqual(im.info['icc_profile'], expected_icc) + def test_repr_png(self): + im = hopper() + + repr_png = Image.open(BytesIO(im._repr_png_())) + self.assertEqual(repr_png.format, 'PNG') + self.assert_image_equal(im, repr_png) + + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index ee3e678eb..32aef075e 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -176,12 +176,23 @@ class TestFileTiff(PillowTestCase): # Assert self.assertIsInstance(ret, str) + + def test_as_dict(self): + # Arrange + filename = "Tests/images/pil136.tiff" + im = Image.open(filename) + + # Act + ret = im.ifd.as_dict() + + # Assert + self.assertIsInstance(ret, dict) + self.assertEqual( - ret, - '{256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), ' - '262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), ' - '279: (9460,), 282: ((720000, 10000),), ' - '283: ((720000, 10000),), 284: (1,)}') + ret, {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), + 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), + 279: (9460,), 282: ((720000, 10000),), + 283: ((720000, 10000),), 284: (1,)}) def test__delitem__(self): # Arrange diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 22c5c0922..f316b71e1 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -83,7 +83,11 @@ class TestFileWebpAlpha(PillowTestCase): image.load() image.getdata() - self.assert_image_similar(image, pil_image, 1.0) + # early versions of webp are known to produce higher deviations: deal with it + if _webp.WebPDecoderVersion(self) <= 0x201: + self.assert_image_similar(image, pil_image, 3.0) + else: + self.assert_image_similar(image, pil_image, 1.0) if __name__ == '__main__': diff --git a/Tests/test_image.py b/Tests/test_image.py index 156164f0c..89d7b635f 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -168,8 +168,6 @@ class TestImage(PillowTestCase): ValueError, lambda: Image.effect_mandelbrot(size, extent, quality)) - @unittest.skipUnless(sys.platform.startswith('win32'), - "Stalls on Travis CI, passes on Windows") def test_effect_noise(self): # Arrange size = (100, 100) @@ -180,8 +178,8 @@ class TestImage(PillowTestCase): # Assert self.assertEqual(im.size, (100, 100)) - self.assertEqual(im.getpixel((0, 0)), 60) - self.assertEqual(im.getpixel((0, 1)), 28) + self.assertEqual(im.mode, "L") + self.assertNotEqual(im.getpixel((0, 0)), im.getpixel((0, 1))) def test_effect_spread(self): # Arrange diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 1f935088c..1b03ed13b 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -44,6 +44,22 @@ try: self.assertRegexpMatches( ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + def test_font_properties(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + self.assertEqual(ttf.path, FONT_PATH) + self.assertEqual(ttf.size, FONT_SIZE) + + ttf_copy = ttf.font_variant() + self.assertEqual(ttf_copy.path, FONT_PATH) + self.assertEqual(ttf_copy.size, FONT_SIZE) + + ttf_copy = ttf.font_variant(size=FONT_SIZE+1) + self.assertEqual(ttf_copy.size, FONT_SIZE+1) + + second_font_path = "Tests/fonts/DejaVuSans.ttf" + ttf_copy = ttf.font_variant(font=second_font_path) + self.assertEqual(ttf_copy.path, second_font_path) + def test_font_with_name(self): ImageFont.truetype(FONT_PATH, FONT_SIZE) self._render(FONT_PATH) diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py new file mode 100644 index 000000000..9da3d1776 --- /dev/null +++ b/Tests/test_imagefont_bitmap.py @@ -0,0 +1,24 @@ +from helper import unittest, PillowTestCase +from PIL import Image, ImageFont, ImageDraw + +class TestImageFontBitmap(PillowTestCase): + def test_similar(self): + text = 'EmbeddedBitmap' + font_outline = ImageFont.truetype(font='Tests/fonts/DejaVuSans.ttf', size=24) + font_bitmap = ImageFont.truetype(font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) + size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text) + size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) + im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) + im_outline = im_bitmap.copy() + draw_bitmap, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline) + + # Metrics are different on the bitmap and ttf fonts, more so on some platforms + # and versions of freetype than others. Mac has a 1px difference, linux doesn't. + draw_bitmap.text((0, size_final[1] - size_bitmap[1]), + text, fill=(0, 0, 0), font=font_bitmap) + draw_outline.text((0, size_final[1] - size_outline[1]), + text, fill=(0, 0, 0), font=font_outline) + self.assert_image_similar(im_bitmap, im_outline, 20) + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 102835b58..4d40b4100 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -48,6 +48,10 @@ class TestLibPack(PillowTestCase): self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + self.assertEqual(pack("RGBa", "RGBa"), [1, 2, 3, 4]) + self.assertEqual(pack("RGBa", "BGRa"), [3, 2, 1, 4]) + self.assertEqual(pack("RGBa", "aBGR"), [4, 3, 2, 1]) + self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4]) self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3]) @@ -125,6 +129,11 @@ class TestLibPack(PillowTestCase): self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) + self.assertEqual(unpack("RGBa", "RGBa", 4), (1, 2, 3, 4)) + self.assertEqual(unpack("RGBa", "BGRa", 4), (3, 2, 1, 4)) + self.assertEqual(unpack("RGBa", "aRGB", 4), (2, 3, 4, 1)) + self.assertEqual(unpack("RGBa", "aBGR", 4), (4, 3, 2, 1)) + self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 59dfd5948..f1c594be9 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -5,10 +5,12 @@ from PIL import Image class TestPickle(PillowTestCase): - def helper_pickle_file(self, pickle, protocol=0): + def helper_pickle_file(self, pickle, protocol=0, mode=None): # Arrange im = Image.open('Tests/images/hopper.jpg') filename = self.tempfile('temp.pkl') + if mode: + im = im.convert(mode) # Act with open(filename, 'wb') as f: @@ -19,9 +21,11 @@ class TestPickle(PillowTestCase): # Assert self.assertEqual(im, loaded_im) - def helper_pickle_string( - self, pickle, protocol=0, file='Tests/images/hopper.jpg'): + def helper_pickle_string(self, pickle, protocol=0, + file='Tests/images/hopper.jpg', mode=None): im = Image.open(file) + if mode: + im = im.convert(mode) # Act dumped_string = pickle.dumps(im, protocol) @@ -67,6 +71,26 @@ class TestPickle(PillowTestCase): ]: self.helper_pickle_string(pickle, file=file) + def test_pickle_l_mode(self): + # Arrange + import pickle + + # Act / Assert + for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): + self.helper_pickle_string(pickle, protocol, mode="L") + self.helper_pickle_file(pickle, protocol, mode="L") + + def test_cpickle_l_mode(self): + # Arrange + try: + import cPickle + except ImportError: + return + + # Act / Assert + for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1): + self.helper_pickle_string(cPickle, protocol, mode="L") + self.helper_pickle_file(cPickle, protocol, mode="L") if __name__ == '__main__': unittest.main() diff --git a/_imagingft.c b/_imagingft.c index eb6313704..ad40ee425 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -243,7 +243,11 @@ font_getsize(FontObject* self, PyObject* args) &delta); x += delta.x; } - error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + + /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 + * Yifu Yu, 2014-10-15 + */ + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); if (error) return geterror(error); if (i == 0) @@ -316,7 +320,8 @@ font_getabc(FontObject* self, PyObject* args) int index, error; face = self->face; index = FT_Get_Char_Index(face, ch); - error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */ + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); if (error) return geterror(error); a = face->glyph->metrics.horiBearingX / 64.0; @@ -363,8 +368,8 @@ font_render(FontObject* self, PyObject* args) } im = (Imaging) id; - - load_flags = FT_LOAD_RENDER; + /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */ + load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP; if (mask) load_flags |= FT_LOAD_TARGET_MONO; diff --git a/_webp.c b/_webp.c index c201813d7..a8c6d40af 100644 --- a/_webp.c +++ b/_webp.c @@ -136,9 +136,9 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; - uint8_t *webp; + const uint8_t *webp; Py_ssize_t size; - PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None; + PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL, *icc_profile = NULL, *exif = NULL; WebPDecoderConfig config; VP8StatusCode vp8_status_code = VP8_STATUS_OK; char* mode = "RGB"; @@ -173,31 +173,34 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) WebPData exif_data = {0}; WebPMux* mux = WebPMuxCreate(&data, copy_data); - WebPMuxGetFrame(mux, 1, &image); - webp = (uint8_t*)image.bitstream.bytes; + if (NULL == mux) + goto end; + + if (WEBP_MUX_OK != WebPMuxGetFrame(mux, 1, &image)) + { + WebPMuxDelete(mux); + goto end; + } + + webp = image.bitstream.bytes; size = image.bitstream.size; vp8_status_code = WebPDecode(webp, size, &config); - WebPMuxGetChunk(mux, "ICCP", &icc_profile_data); - if (icc_profile_data.size > 0) { + if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "ICCP", &icc_profile_data)) icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size); - } - WebPMuxGetChunk(mux, "EXIF", &exif_data); - if (exif_data.size > 0) { + if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "EXIF", &exif_data)) exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size); - } + WebPDataClear(&image.bitstream); WebPMuxDelete(mux); } #endif } - if (vp8_status_code != VP8_STATUS_OK) { - WebPFreeDecBuffer(&config.output); - Py_RETURN_NONE; - } + if (vp8_status_code != VP8_STATUS_OK) + goto end; if (config.output.colorspace < MODE_YUV) { bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, @@ -215,8 +218,21 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) pymode = PyString_FromString(mode); #endif ret = Py_BuildValue("SiiSSS", bytes, config.output.width, - config.output.height, pymode, icc_profile, exif); + config.output.height, pymode, + NULL == icc_profile ? Py_None : icc_profile, + NULL == exif ? Py_None : exif); + +end: WebPFreeDecBuffer(&config.output); + + Py_XDECREF(bytes); + Py_XDECREF(pymode); + Py_XDECREF(icc_profile); + Py_XDECREF(exif); + + if (Py_None == ret) + Py_RETURN_NONE; + return ret; } diff --git a/build_children.sh b/build_children.sh new file mode 100755 index 000000000..c4ed4ebfa --- /dev/null +++ b/build_children.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Get last child project build number from branch named "latest" +BUILD_NUM=$(curl -s 'https://api.travis-ci.org/repos/python-pillow/pillow-wheels/branches/latest' | grep -o '^{"branch":{"id":[0-9]*,' | grep -o '[0-9]' | tr -d '\n') + +# Restart last child project build +curl -X POST https://api.travis-ci.org/builds/$BUILD_NUM/restart --header "Authorization: token "$AUTH_TOKEN diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index b440f7ac9..a85a917b8 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -125,7 +125,7 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following not present. **jfif_version** - A tuple representing the jfif version, (major version, minor version). + A tuple representing the jfif version, (major version, minor version). **jfif_density** A tuple representing the pixel density of the image, in units specified @@ -139,8 +139,8 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following * 2 - Pixels per Centimeter **dpi** - A tuple representing the reported pixel density in pixels per inch, if - the file is a jfif file and the units are in inches. + A tuple representing the reported pixel density in pixels per inch, if + the file is a jfif file and the units are in inches. **adobe** Adobe application marker found. If the file is not an Adobe JPEG file, this @@ -153,10 +153,10 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following Indicates that this is a progressive JPEG file. **icc-profile** - The ICC color profile for the image. + The ICC color profile for the image. **exif** - Raw EXIF data from the image. + Raw EXIF data from the image. The :py:meth:`~PIL.Image.Image.save` method supports the following options: @@ -178,7 +178,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: **dpi** A tuple of integers representing the pixel density, ``(x,y)``. -**icc-profile** +**icc-profile** If present, the image is stored with the provided ICC profile. If this parameter is not provided, the image will be saved with no profile attached. To preserve the existing profile:: @@ -186,11 +186,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: im.save(filename, 'jpeg', icc_profile=im.info.get('icc_profile')) **exif** - If present, the image will be stored with the provided raw EXIF data. + If present, the image will be stored with the provided raw EXIF data. **subsampling** - If present, sets the subsampling for the encoder. - + If present, sets the subsampling for the encoder. + * ``keep``: Only valid for JPEG files, will retain the original image setting. * ``4:4:4``, ``4:2:2``, ``4:1:1``: Specific sampling values * ``-1``: equivalent to ``keep`` @@ -206,7 +206,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: * a string, naming a preset, e.g. ``keep``, ``web_low``, or ``web_high`` * a list, tuple, or dictionary (with integer keys = range(len(keys))) of lists of 64 integers. There must be - between 2 and 4 tables. + between 2 and 4 tables. .. versionadded:: 2.5.0 @@ -347,16 +347,22 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: possible. This includes extra processing in order to find optimal encoder settings. -**transparency** +**transparency** For ``P``, ``L``, and ``RGB`` images, this option controls what color image to mark as transparent. **dpi** - A tuple of two numbers corresponding to the desired dpi in each direction. + A tuple of two numbers corresponding to the desired dpi in each direction. **pnginfo** A :py:class:`PIL.PngImagePlugin.PngInfo` instance containing text tags. +**compress_level** + ZLIB compression level, a number between 0 and 9: 1 gives best speed, + 9 gives best compression, 0 gives no compression at all. Default is 6. + When ``optimize`` option is True ``compress_level`` has no effect + (it is set to 9 regardless of a value passed). + **bits (experimental)** For ``P`` images, this option controls how many bits to store. If omitted, the PNG writer uses 8 bits (256 colors). @@ -450,7 +456,7 @@ Saving Tiff Images The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments: -**tiffinfo** +**tiffinfo** A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict object containing tiff tags and values. The TIFF field type is autodetected for Numeric and string values, any other types @@ -459,7 +465,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with the appropriate numerical value from ``TiffTags.TYPES``. - + .. versionadded:: 2.3.0 **compression** @@ -471,7 +477,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo. -**description** +**description** **software** @@ -483,7 +489,7 @@ These arguments to set the tiff header fields are an alternative to using the ge Strings **resolution_unit** - A string of "inch", "centimeter" or "cm" + A string of "inch", "centimeter" or "cm" **resolution** diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 10833a53e..0af4007be 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -1,4 +1,6 @@ -Writing your own file decoder +.. _file-decoders: + +Writing Your Own File Decoder ============================= The Python Imaging Library uses a plug-in model which allows you to @@ -7,7 +9,7 @@ library itself. Such plug-ins usually have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name (usually an abbreviation). -.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually. +.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually. A decoder plug-in should contain a decoder class, based on the :py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an @@ -68,7 +70,7 @@ true color. ] Image.register_open("SPAM", SpamImageFile) - + Image.register_extension("SPAM", ".spam") Image.register_extension("SPAM", ".spa") # dos version @@ -116,7 +118,8 @@ The fields are used as follows: Note that the :py:attr:`tile` attribute contains a list of tile descriptors, not just a single descriptor. -The ``raw`` decoder +The raw decoder +--------------- The ``raw`` decoder is used to read uncompressed data from an image file. It can be used with most uncompressed file formats, such as PPM, BMP, uncompressed @@ -124,7 +127,7 @@ TIFF, and many others. To use the raw decoder with the :py:func:`PIL.Image.fromstring` function, use the following syntax:: image = Image.fromstring( - mode, size, data, "raw", + mode, size, data, "raw", raw mode, stride, orientation ) diff --git a/docs/installation.rst b/docs/installation.rst index cca94a135..d9027bda6 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -12,7 +12,7 @@ Installation .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. -Simple installation +Simple Installation ------------------- .. note:: @@ -121,7 +121,7 @@ Sample Usage:: $ MAX_CONCURRENCY=1 python setup.py build-ext --enable-[feature] install -Linux installation +Linux Installation ------------------ .. note:: @@ -160,10 +160,10 @@ Prerequisites are installed on **Fedora 20** with:: lcms2-devel libwebp-devel tcl-devel tk-devel -Mac OS X installation ---------------------- +OS X Installation +----------------- -We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow with with XCode. +We provide binaries for OS X in the form of `Python Wheels `_. Alternatively you can compile Pillow from soure with XCode. The easiest way to install external libraries is via `Homebrew `_. After you install Homebrew, run:: @@ -173,7 +173,7 @@ Install Pillow with:: $ pip install Pillow -Windows installation +Windows Installation -------------------- We provide binaries for Windows in the form of Python Eggs and `Python Wheels @@ -194,11 +194,11 @@ Python Eggs Python Wheels ^^^^^^^^^^^^^ -.. Note:: Experimental. Requires setuptools >=0.8 and pip >=1.4.1 +.. Note:: Requires setuptools >=0.8 and pip >=1.4.1. Some older versions of pip required the ``--use-wheel`` flag. :: - $ pip install --use-wheel Pillow + $ pip install Pillow If the above does not work, it's likely because we haven't uploaded a wheel for the latest version of Pillow. In that case, try pinning it @@ -206,10 +206,10 @@ to a specific version: :: - $ pip install --use-wheel Pillow==2.6.1 + $ pip install Pillow==2.6.1 -FreeBSD installation ---------------------- +FreeBSD Installation +-------------------- .. Note:: Only FreeBSD 10 tested diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index c24a9dd99..e6d5c36ee 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -184,8 +184,11 @@ Methods :param xy: Four points to define the bounding box. Sequence of ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. - :param outline: Color to use for the outline. + :param start: Starting angle, in degrees. Angles are measured from + 3 o'clock, increasing clockwise. + :param end: Ending angle, in degrees. :param fill: Color to use for the fill. + :param outline: Color to use for the outline. .. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None) diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index e3f9ed8d6..00249a601 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -13,7 +13,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module .. code-block:: python - import Image, ImageMath + from PIL import Image, ImageMath im1 = Image.open("image1.jpg") im2 = Image.open("image2.jpg") diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst index 700464144..03aa39811 100644 --- a/docs/reference/ImagePath.rst +++ b/docs/reference/ImagePath.rst @@ -53,7 +53,7 @@ vector data. Path objects can be passed to the methods on the Converts the path to a Python list [(x, y), …]. :param flat: By default, this function returns a list of 2-tuples - [(x, y), ...]. If this argument is :keyword:`True`, it + [(x, y), ...]. If this argument is `True`, it returns a flat list [x, y, ...] instead. :return: A list of coordinates. See **flat**. diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index abb717587..8359bd7aa 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -1,6 +1,13 @@ Pillow 2.7.0 ============ +Sane Plugin +----------- + +The Sane plugin has now been split into its own repo: +https://github.com/python-pillow/Sane . + + Png text chunk size limits -------------------------- @@ -157,7 +164,7 @@ so the quality was worse compared to other Gaussian blur software. The new implementation does not have this drawback. -TFF Parameter Changes +TIFF Parameter Changes ---------------------- Several kwarg parameters for saving TIFF images were previously diff --git a/libImaging/Dib.c b/libImaging/Dib.c index 8e138bd6b..55de7d9a5 100644 --- a/libImaging/Dib.c +++ b/libImaging/Dib.c @@ -204,19 +204,6 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) #endif -#if 0 - { - /* DEBUG: dump palette to file */ - FILE *err = fopen("dib.pal", "w"); - for (i = 0; i < 256; i++) - fprintf(err, "%d: %d/%d/%d\n", i, - pal->palPalEntry[i].peRed, - pal->palPalEntry[i].peGreen, - pal->palPalEntry[i].peBlue); - fclose(err); - } -#endif - dib->palette = CreatePalette(pal); } diff --git a/libImaging/Effects.c b/libImaging/Effects.c index eb598d968..a3f5e7cd9 100644 --- a/libImaging/Effects.c +++ b/libImaging/Effects.c @@ -98,8 +98,8 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) /* after numerical recipes */ double v1, v2, radius, factor; do { - v1 = rand()*(2.0/32767.0) - 1.0; - v2 = rand()*(2.0/32767.0) - 1.0; + v1 = rand()*(2.0/RAND_MAX) - 1.0; + v2 = rand()*(2.0/RAND_MAX) - 1.0; radius= v1*v1 + v2*v2; } while (radius >= 1.0); factor = sqrt(-2.0*log(radius)/radius); diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index f586974c3..97cbcf2ed 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -304,23 +304,6 @@ perspective_transform(double* xin, double* yin, int x, int y, void* data) return 1; } -#if 0 -static int -quadratic_transform(double* xin, double* yin, int x, int y, void* data) -{ - double* a = (double*) data; - - double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; - double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7]; - double a8 = a[8]; double a9 = a[9]; double a10 = a[10]; double a11 = a[11]; - - xin[0] = a0 + a1*x + a2*y + a3*x*x + a4*x*y + a5*y*y; - yin[0] = a6 + a7*x + a8*y + a9*x*x + a10*x*y + a11*y*y; - - return 1; -} -#endif - static int quad_transform(double* xin, double* yin, int x, int y, void* data) { diff --git a/libImaging/Pack.c b/libImaging/Pack.c index fecafbde4..d174d7d0d 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -530,6 +530,11 @@ static struct { {"RGBA", "B", 8, band2}, {"RGBA", "A", 8, band3}, + /* true colour w. alpha premultiplied */ + {"RGBa", "RGBa", 32, copy4}, + {"RGBa", "BGRa", 32, ImagingPackBGRA}, + {"RGBa", "aBGR", 32, ImagingPackABGR}, + /* true colour w. padding */ {"RGBX", "RGBX", 32, copy4}, {"RGBX", "RGBX;L", 32, packRGBXL}, diff --git a/libImaging/QuantHash.c b/libImaging/QuantHash.c index a1f99ed3a..48b7a973e 100644 --- a/libImaging/QuantHash.c +++ b/libImaging/QuantHash.c @@ -44,9 +44,6 @@ struct _HashTable { #define RESIZE_FACTOR 3 static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc); -#if 0 -static int _hashtable_test(HashTable *); -#endif HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) { HashTable *h; @@ -132,22 +129,6 @@ static void _hashtable_resize(HashTable *h) { } } -#if 0 -static int _hashtable_test(HashTable *h) { - uint32_t i; - int j; - HashNode *n; - for (i=0;ilength;i++) { - for (n=h->table[i];n&&n->next;n=n->next) { - j=h->cmpFunc(h,n->key,n->next->key); - printf ("%c",j?(j<0?'-':'+'):'='); - } - printf ("\n"); - } - return 0; -} -#endif - static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int update,CollisionFunc cf) { uint32_t hash=h->hashFunc(h,node->key)%h->length; HashNode **n,*nv; diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 7c453dbfd..9db43147f 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -638,7 +638,12 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels) int a = in[3]; if (!a) out[R] = out[G] = out[B] = out[A] = 0; - else { + else if (a == 255) { + out[R] = in[0]; + out[G] = in[1]; + out[B] = in[2]; + out[A] = a; + } else { out[R] = CLIP(in[0] * 255 / a); out[G] = CLIP(in[1] * 255 / a); out[B] = CLIP(in[2] * 255 / a); @@ -1114,6 +1119,12 @@ static struct { {"RGBA", "B", 8, band2}, {"RGBA", "A", 8, band3}, + /* true colour w. alpha premultiplied */ + {"RGBa", "RGBa", 32, copy4}, + {"RGBa", "BGRa", 32, unpackBGRA}, + {"RGBa", "aRGB", 32, unpackARGB}, + {"RGBa", "aBGR", 32, unpackABGR}, + /* true colour w. padding */ {"RGBX", "RGB", 24, ImagingUnpackRGB}, {"RGBX", "RGB;L", 24, unpackRGBL}, diff --git a/setup.py b/setup.py index 90687d51f..7262db39e 100644 --- a/setup.py +++ b/setup.py @@ -722,6 +722,9 @@ class pil_build_ext(build_ext): os.unlink(tmpfile) +def debug_build(): + return hasattr(sys, 'gettotalrefcount') + setup( name=NAME, version=PILLOW_VERSION, @@ -754,6 +757,6 @@ setup( test_suite='PIL.tests', keywords=["Imaging", ], license='Standard PIL License', - zip_safe=True, + zip_safe= not debug_build(), ) # End of file