mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 01:16:16 +03:00
Merge commit 'e0b94d65bedb4a2124228aa579686a3ba2974d11' into replace-resize
This commit is contained in:
commit
1a097d2e02
18
CHANGES.rst
18
CHANGES.rst
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -18,12 +18,15 @@
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL._util import isPath
|
from PIL._util import isPath
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if 'PyQt4.QtGui' not in sys.modules:
|
||||||
try:
|
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.
|
||||||
|
|
|
@ -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"):
|
||||||
|
try:
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
_fp = os.dup(fp.fileno())
|
_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]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"""
|
||||||
|
@ -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__':
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
10
_imaging.c
10
_imaging.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
|
|
@ -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::
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user