Merge commit 'e0b94d65bedb4a2124228aa579686a3ba2974d11' into replace-resize

This commit is contained in:
homm 2014-11-19 13:45:04 +03:00
commit 1a097d2e02
14 changed files with 181 additions and 23 deletions

View File

@ -3,6 +3,24 @@ Changelog (Pillow)
2.7.0 (unreleased) 2.7.0 (unreleased)
------------------ ------------------
- Fix for saving TIFF image into an io.BytesIO buffer #1011
[mfergie]
- Fix antialias compilation on debug versions of Python #1010
[wiredfool]
- Fix for Image.putdata segfault #1009
[wiredfool]
- Ico save, additional tests #1007
[exherb]
- Use PyQt4 if it has already been imported, otherwise prefer PyQt5. #1003
[AurelienBallier]
- Speedup stretch implementation up to 2.5 times. #977
[homm]
- Speed up rotation by using cache aware loops, added transpose to rotations. #994 - Speed up rotation by using cache aware loops, added transpose to rotations. #994
[homm] [homm]

View File

@ -24,6 +24,9 @@
__version__ = "0.1" __version__ = "0.1"
import struct
from io import BytesIO
from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary
from math import log, ceil from math import log, ceil
@ -37,6 +40,42 @@ i32 = _binary.i32le
_MAGIC = b"\0\0\1\0" _MAGIC = b"\0\0\1\0"
def _save(im, fp, filename):
fp.write(_MAGIC) # (2+2)
sizes = im.encoderinfo.get("sizes",
[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)])
width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes)
sizes = sorted(sizes, key=lambda x: x[0])
fp.write(struct.pack("H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16
for size in sizes:
width, height = size
fp.write(struct.pack("B", width)) # bWidth(1)
fp.write(struct.pack("B", height)) # bHeight(1)
fp.write(b"\0") # bColorCount(1)
fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2)
fp.write(struct.pack("H", 32)) # wBitCount(2)
image_io = BytesIO()
tmp = im.copy()
tmp.thumbnail(size, Image.ANTIALIAS)
tmp.save(image_io, "png")
image_io.seek(0)
image_bytes = image_io.read()
bytes_len = len(image_bytes)
fp.write(struct.pack("I", bytes_len)) # dwBytesInRes(4)
fp.write(struct.pack("I", offset)) # dwImageOffset(4)
current = fp.tell()
fp.seek(offset)
fp.write(image_bytes)
offset = offset + bytes_len
fp.seek(current)
def _accept(prefix): def _accept(prefix):
return prefix[:4] == _MAGIC return prefix[:4] == _MAGIC
@ -241,4 +280,5 @@ class IcoImageFile(ImageFile.ImageFile):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
Image.register_open("ICO", IcoImageFile, _accept) Image.register_open("ICO", IcoImageFile, _accept)
Image.register_save("ICO", _save)
Image.register_extension("ICO", ".ico") Image.register_extension("ICO", ".ico")

View File

@ -18,12 +18,15 @@
from PIL import Image from PIL import Image
from PIL._util import isPath from PIL._util import isPath
import sys
try: if 'PyQt4.QtGui' not in sys.modules:
try:
from PyQt5.QtGui import QImage, qRgba from PyQt5.QtGui import QImage, qRgba
except: except:
from PyQt4.QtGui import QImage, qRgba from PyQt4.QtGui import QImage, qRgba
else: #PyQt4 is used
from PyQt4.QtGui import QImage, qRgba
## ##
# (Internal) Turns an RGB color into a Qt compatible color integer. # (Internal) Turns an RGB color into a Qt compatible color integer.

View File

@ -54,6 +54,7 @@ import sys
import collections import collections
import itertools import itertools
import os import os
import io
# Set these to true to force use of libtiff for reading or writing. # Set these to true to force use of libtiff for reading or writing.
READ_LIBTIFF = False READ_LIBTIFF = False
@ -1128,8 +1129,11 @@ def _save(im, fp, filename):
print (ifd.items()) print (ifd.items())
_fp = 0 _fp = 0
if hasattr(fp, "fileno"): if hasattr(fp, "fileno"):
fp.seek(0) try:
_fp = os.dup(fp.fileno()) fp.seek(0)
_fp = os.dup(fp.fileno())
except io.UnsupportedOperation:
pass
# ICC Profile crashes. # ICC Profile crashes.
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE]

View File

@ -1,5 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase, hopper
import io
from PIL import Image from PIL import Image
# sample ppm stream # sample ppm stream
@ -16,6 +17,32 @@ class TestFileIco(PillowTestCase):
self.assertEqual(im.size, (16, 16)) self.assertEqual(im.size, (16, 16))
self.assertEqual(im.format, "ICO") self.assertEqual(im.format, "ICO")
def test_save_to_bytes(self):
output = io.BytesIO()
im = hopper()
im.save(output, "ico", sizes=[(32, 32), (64, 64)])
# the default image
output.seek(0)
reloaded = Image.open(output)
self.assertEqual(reloaded.info['sizes'],set([(32, 32), (64, 64)]))
self.assertEqual(im.mode, reloaded.mode)
self.assertEqual((64, 64), reloaded.size)
self.assertEqual(reloaded.format, "ICO")
self.assert_image_equal(reloaded, hopper().resize((64,64), Image.ANTIALIAS))
# the other one
output.seek(0)
reloaded = Image.open(output)
reloaded.size = (32,32)
self.assertEqual(im.mode, reloaded.mode)
self.assertEqual((32, 32), reloaded.size)
self.assertEqual(reloaded.format, "ICO")
self.assert_image_equal(reloaded, hopper().resize((32,32), Image.ANTIALIAS))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,9 +1,11 @@
from helper import unittest, PillowTestCase, hopper, py3 from helper import unittest, PillowTestCase, hopper, py3
import os import os
import io
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
class LibTiffTestCase(PillowTestCase): class LibTiffTestCase(PillowTestCase):
def setUp(self): def setUp(self):
@ -31,6 +33,7 @@ class LibTiffTestCase(PillowTestCase):
out = self.tempfile("temp.png") out = self.tempfile("temp.png")
im.save(out) im.save(out)
class TestFileLibTiff(LibTiffTestCase): class TestFileLibTiff(LibTiffTestCase):
def test_g4_tiff(self): def test_g4_tiff(self):
@ -59,9 +62,8 @@ class TestFileLibTiff(LibTiffTestCase):
def test_g4_tiff_bytesio(self): def test_g4_tiff_bytesio(self):
"""Testing the stringio loading code path""" """Testing the stringio loading code path"""
from io import BytesIO
file = "Tests/images/hopper_g4_500.tif" file = "Tests/images/hopper_g4_500.tif"
s = BytesIO() s = io.BytesIO()
with open(file, 'rb') as f: with open(file, 'rb') as f:
s.write(f.read()) s.write(f.read())
s.seek(0) s.seek(0)
@ -288,7 +290,7 @@ class TestFileLibTiff(LibTiffTestCase):
im2 = Image.open(out) im2 = Image.open(out)
self.assert_image_equal(im, im2) self.assert_image_equal(im, im2)
def xtest_bw_compression_wRGB(self): def xtest_bw_compression_w_rgb(self):
""" This test passes, but when running all tests causes a failure due """ This test passes, but when running all tests causes a failure due
to output on stderr from the error thrown by libtiff. We need to to output on stderr from the error thrown by libtiff. We need to
capture that but not now""" capture that but not now"""
@ -319,19 +321,19 @@ class TestFileLibTiff(LibTiffTestCase):
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue # file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
im.seek(0) im.seek(0)
self.assertEqual(im.size, (10,10)) self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,128,0)) self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 128, 0))
self.assertTrue(im.tag.next) self.assertTrue(im.tag.next)
im.seek(1) im.seek(1)
self.assertEqual(im.size, (10,10)) self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (255,0,0)) self.assertEqual(im.convert('RGB').getpixel((0, 0)), (255, 0, 0))
self.assertTrue(im.tag.next) self.assertTrue(im.tag.next)
im.seek(2) im.seek(2)
self.assertFalse(im.tag.next) self.assertFalse(im.tag.next)
self.assertEqual(im.size, (20,20)) self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,0,255)) self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255))
TiffImagePlugin.READ_LIBTIFF = False TiffImagePlugin.READ_LIBTIFF = False
@ -357,7 +359,32 @@ class TestFileLibTiff(LibTiffTestCase):
self.assertEqual(im.mode, "L") self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, 7.3) self.assert_image_similar(im, original, 7.3)
def test_save_bytesio(self):
# PR 1011
# Test TIFF saving to io.BytesIO() object.
TiffImagePlugin.WRITE_LIBTIFF = True
TiffImagePlugin.READ_LIBTIFF = True
# Generate test image
pilim = hopper()
def save_bytesio(compression=None):
buffer_io = io.BytesIO()
pilim.save(buffer_io, format="tiff", compression=compression)
buffer_io.seek(0)
pilim_load = Image.open(buffer_io)
self.assert_image_similar(pilim, pilim_load, 0)
# save_bytesio()
save_bytesio('raw')
save_bytesio("packbits")
save_bytesio("tiff_lzw")
TiffImagePlugin.WRITE_LIBTIFF = False
TiffImagePlugin.READ_LIBTIFF = False
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,4 +1,5 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from array import array
import sys import sys
@ -63,6 +64,25 @@ class TestImagePutData(PillowTestCase):
target = [2.0 * float(elt) + 256.0 for elt in data] target = [2.0 * float(elt) + 256.0 for elt in data]
self.assertEqual(list(im.getdata()), target) self.assertEqual(list(im.getdata()), target)
def test_array_B(self):
# shouldn't segfault
# see https://github.com/python-pillow/Pillow/issues/1008
arr = array('B', [0])*15000
im = Image.new('L', (150, 100))
im.putdata(arr)
self.assertEqual(len(im.getdata()),len(arr))
def test_array_F(self):
# shouldn't segfault
# see https://github.com/python-pillow/Pillow/issues/1008
im = Image.new('F', (150, 100))
arr = array('f', [0.0])*15000
im.putdata(arr)
self.assertEqual(len(im.getdata()),len(arr))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -121,6 +121,16 @@ class TestNumpy(PillowTestCase):
im.point(lut) im.point(lut)
def test_putdata(self):
# shouldn't segfault
# see https://github.com/python-pillow/Pillow/issues/1008
im = Image.new('F', (150, 100))
arr = numpy.zeros((15000,), numpy.float32)
im.putdata(arr)
self.assertEqual(len(im.getdata()),len(arr))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1269,7 +1269,7 @@ _putdata(ImagingObject* self, PyObject* args)
if (scale == 1.0 && offset == 0.0) { if (scale == 1.0 && offset == 0.0) {
/* Clipped data */ /* Clipped data */
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(data, i); op = PySequence_Fast_GET_ITEM(seq, i);
image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
if (++x >= (int) image->xsize){ if (++x >= (int) image->xsize){
x = 0, y++; x = 0, y++;
@ -1279,7 +1279,7 @@ _putdata(ImagingObject* self, PyObject* args)
} else { } else {
/* Scaled and clipped data */ /* Scaled and clipped data */
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
PyObject *op = PySequence_Fast_GET_ITEM(data, i); PyObject *op = PySequence_Fast_GET_ITEM(seq, i);
image->image8[y][x] = CLIP( image->image8[y][x] = CLIP(
(int) (PyFloat_AsDouble(op) * scale + offset)); (int) (PyFloat_AsDouble(op) * scale + offset));
if (++x >= (int) image->xsize){ if (++x >= (int) image->xsize){
@ -1299,7 +1299,7 @@ _putdata(ImagingObject* self, PyObject* args)
switch (image->type) { switch (image->type) {
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(data, i); op = PySequence_Fast_GET_ITEM(seq, i);
IMAGING_PIXEL_INT32(image, x, y) = IMAGING_PIXEL_INT32(image, x, y) =
(INT32) (PyFloat_AsDouble(op) * scale + offset); (INT32) (PyFloat_AsDouble(op) * scale + offset);
if (++x >= (int) image->xsize){ if (++x >= (int) image->xsize){
@ -1310,7 +1310,7 @@ _putdata(ImagingObject* self, PyObject* args)
break; break;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(data, i); op = PySequence_Fast_GET_ITEM(seq, i);
IMAGING_PIXEL_FLOAT32(image, x, y) = IMAGING_PIXEL_FLOAT32(image, x, y) =
(FLOAT32) (PyFloat_AsDouble(op) * scale + offset); (FLOAT32) (PyFloat_AsDouble(op) * scale + offset);
if (++x >= (int) image->xsize){ if (++x >= (int) image->xsize){
@ -1326,7 +1326,7 @@ _putdata(ImagingObject* self, PyObject* args)
INT32 inkint; INT32 inkint;
} u; } u;
op = PySequence_Fast_GET_ITEM(data, i); op = PySequence_Fast_GET_ITEM(seq, i);
if (!op || !getink(op, image, u.ink)) { if (!op || !getink(op, image, u.ink)) {
return NULL; return NULL;
} }

View File

@ -589,6 +589,14 @@ ICO
ICO is used to store icons on Windows. The largest available icon is read. ICO is used to store icons on Windows. The largest available icon is read.
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**sizes**
A list of sizes including in this ico file; these are a 2-tuple,
``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)]``. Any size is bigger then the original
size or 255 will be ignored.
ICNS ICNS
^^^^ ^^^^

View File

@ -129,7 +129,7 @@ Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy
Prerequisites are installed on **Ubuntu 14.04 LTS** with:: Prerequisites are installed on **Ubuntu 14.04 LTS** with::
$ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
Prerequisites are installed on **Fedora 20** with:: Prerequisites are installed on **Fedora 20** with::

View File

@ -116,6 +116,7 @@ ITU-R 709, using the D65 luminant) to the CIE XYZ color space:
.. automethod:: PIL.Image.Image.getcolors .. automethod:: PIL.Image.Image.getcolors
.. automethod:: PIL.Image.Image.getdata .. automethod:: PIL.Image.Image.getdata
.. automethod:: PIL.Image.Image.getextrema .. automethod:: PIL.Image.Image.getextrema
.. automethod:: PIL.Image.Image.getpalette
.. automethod:: PIL.Image.Image.getpixel .. automethod:: PIL.Image.Image.getpixel
.. automethod:: PIL.Image.Image.histogram .. automethod:: PIL.Image.Image.histogram
.. automethod:: PIL.Image.Image.offset .. automethod:: PIL.Image.Image.offset

View File

@ -93,7 +93,7 @@ static inline UINT8 clip8(float in)
/* This is work around bug in GCC prior 4.9 in 64 bit 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. GCC generates code with partial dependency which 3 times slower.
See: http://stackoverflow.com/a/26588074/253146 */ See: http://stackoverflow.com/a/26588074/253146 */
#if defined(__x86_64__) && defined(__SSE__) && \ #if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \
! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900) ! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900)
static float __attribute__((always_inline)) i2f(int v) { static float __attribute__((always_inline)) i2f(int v) {
float x; float x;