mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-18 11:14:46 +03:00
Merge branch 'master' into cleanup-test_image_toqimage
This commit is contained in:
commit
c97229466c
|
@ -9,22 +9,21 @@ notifications:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures:
|
|
||||||
- python: nightly
|
|
||||||
include:
|
include:
|
||||||
- python: "pypy"
|
- python: "pypy"
|
||||||
- python: "pypy3"
|
- python: "pypy3"
|
||||||
- python: '3.6'
|
- python: '3.6'
|
||||||
- python: '2.7'
|
- python: '2.7'
|
||||||
- env: DOCKER="alpine"
|
- env: DOCKER="alpine"
|
||||||
|
- env: DOCKER="arch" # contains PyQt5
|
||||||
- env: DOCKER="ubuntu-trusty-x86"
|
- env: DOCKER="ubuntu-trusty-x86"
|
||||||
- env: DOCKER="ubuntu-xenial-amd64"
|
- env: DOCKER="ubuntu-xenial-amd64"
|
||||||
- env: DOCKER="ubuntu-precise-amd64"
|
- env: DOCKER="ubuntu-precise-amd64"
|
||||||
|
- env: DOCKER="debian-stretch-x86"
|
||||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||||
- python: '3.5'
|
- python: '3.5'
|
||||||
- python: '3.4'
|
- python: '3.4'
|
||||||
- python: '3.3'
|
- python: '3.3'
|
||||||
- python: 'nightly'
|
|
||||||
|
|
||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
|
|
27
CHANGES.rst
27
CHANGES.rst
|
@ -4,6 +4,33 @@ Changelog (Pillow)
|
||||||
4.1.0 (unreleased)
|
4.1.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Removed use of spaces in TIFF kwargs names, deprecated in 2.7 #1390
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Removed deprecated ImageDraw setink, setfill, setfont methods #2220
|
||||||
|
[jdufresne]
|
||||||
|
|
||||||
|
- Send unwanted subprocess output to /dev/null #2253
|
||||||
|
[jdufresne]
|
||||||
|
|
||||||
|
- Fix division by zero when creating 0x0 image from numpy array #2419
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Test: Added matrix convert tests #2381
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Replaced broken URL to partners.adobe.com #2413
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Removed unused private functions in setup.py and build_dep.py #2414
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Test: Fixed Qt tests for QT5, Arch, and saving 1 bit PNG #2394
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Test: docker builds for Arch and Debian Stretch #2394
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
- Updated libwebp to 0.6.0 on appveyor #2395
|
- Updated libwebp to 0.6.0 on appveyor #2395
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from . import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from ._binary import i32le as i32, o32le as o32
|
from ._binary import i32le as i32, o32le as o32
|
||||||
|
@ -57,8 +58,8 @@ def has_ghostscript():
|
||||||
if not sys.platform.startswith('win'):
|
if not sys.platform.startswith('win'):
|
||||||
import subprocess
|
import subprocess
|
||||||
try:
|
try:
|
||||||
gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE)
|
with open(os.devnull, 'wb') as devnull:
|
||||||
gs.stdout.read()
|
subprocess.check_call(['gs', '--version'], stdout=devnull)
|
||||||
return True
|
return True
|
||||||
except OSError:
|
except OSError:
|
||||||
# no ghostscript
|
# no ghostscript
|
||||||
|
@ -137,12 +138,8 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
|
|
||||||
# push data through ghostscript
|
# push data through ghostscript
|
||||||
try:
|
try:
|
||||||
gs = subprocess.Popen(command, stdin=subprocess.PIPE,
|
with open(os.devnull, 'w+b') as devnull:
|
||||||
stdout=subprocess.PIPE)
|
subprocess.check_call(command, stdin=devnull, stdout=devnull)
|
||||||
gs.stdin.close()
|
|
||||||
status = gs.wait()
|
|
||||||
if status:
|
|
||||||
raise IOError("gs failed (status %d)" % status)
|
|
||||||
im = Image.open(outfile)
|
im = Image.open(outfile)
|
||||||
im.load()
|
im.load()
|
||||||
finally:
|
finally:
|
||||||
|
@ -321,7 +318,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
# EPS can contain binary data
|
# EPS can contain binary data
|
||||||
# or start directly with latin coding
|
# or start directly with latin coding
|
||||||
# more info see:
|
# more info see:
|
||||||
# http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
|
# https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
|
||||||
offset = i32(s[4:8])
|
offset = i32(s[4:8])
|
||||||
length = i32(s[8:12])
|
length = i32(s[8:12])
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -519,18 +519,17 @@ def _save_netpbm(im, fp, filename):
|
||||||
|
|
||||||
with open(filename, 'wb') as f:
|
with open(filename, 'wb') as f:
|
||||||
if im.mode != "RGB":
|
if im.mode != "RGB":
|
||||||
with tempfile.TemporaryFile() as stderr:
|
with open(os.devnull, 'wb') as devnull:
|
||||||
check_call(["ppmtogif", file], stdout=f, stderr=stderr)
|
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
|
||||||
else:
|
else:
|
||||||
# Pipe ppmquant output into ppmtogif
|
# Pipe ppmquant output into ppmtogif
|
||||||
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
||||||
quant_cmd = ["ppmquant", "256", file]
|
quant_cmd = ["ppmquant", "256", file]
|
||||||
togif_cmd = ["ppmtogif"]
|
togif_cmd = ["ppmtogif"]
|
||||||
with tempfile.TemporaryFile() as stderr:
|
with open(os.devnull, 'wb') as devnull:
|
||||||
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
|
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
|
||||||
with tempfile.TemporaryFile() as stderr:
|
|
||||||
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
|
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
|
||||||
stdout=f, stderr=stderr)
|
stdout=f, stderr=devnull)
|
||||||
|
|
||||||
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
|
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
|
||||||
quant_proc.stdout.close()
|
quant_proc.stdout.close()
|
||||||
|
|
|
@ -329,8 +329,8 @@ def _save(im, fp, filename):
|
||||||
from subprocess import Popen, PIPE, CalledProcessError
|
from subprocess import Popen, PIPE, CalledProcessError
|
||||||
|
|
||||||
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
|
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
|
||||||
with tempfile.TemporaryFile() as stderr:
|
with open(os.devnull, 'wb') as devnull:
|
||||||
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr)
|
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
|
||||||
|
|
||||||
convert_proc.stdout.close()
|
convert_proc.stdout.close()
|
||||||
|
|
||||||
|
|
|
@ -2036,7 +2036,7 @@ def _check_size(size):
|
||||||
if len(size) != 2:
|
if len(size) != 2:
|
||||||
raise ValueError("Size must be a tuple of length 2")
|
raise ValueError("Size must be a tuple of length 2")
|
||||||
if size[0] < 0 or size[1] < 0:
|
if size[0] < 0 or size[1] < 0:
|
||||||
raise ValueError("Width and Height must be => 0")
|
raise ValueError("Width and height must be >= 0")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import numbers
|
import numbers
|
||||||
import warnings
|
|
||||||
|
|
||||||
from . import Image, ImageColor
|
from . import Image, ImageColor
|
||||||
from ._util import isStringType
|
from ._util import isStringType
|
||||||
|
@ -87,20 +86,6 @@ class ImageDraw(object):
|
||||||
self.fill = 0
|
self.fill = 0
|
||||||
self.font = None
|
self.font = None
|
||||||
|
|
||||||
def setink(self, ink):
|
|
||||||
raise NotImplementedError("setink() has been removed. " +
|
|
||||||
"Please use keyword arguments instead.")
|
|
||||||
|
|
||||||
def setfill(self, onoff):
|
|
||||||
raise NotImplementedError("setfill() has been removed. " +
|
|
||||||
"Please use keyword arguments instead.")
|
|
||||||
|
|
||||||
def setfont(self, font):
|
|
||||||
warnings.warn("setfont() is deprecated. " +
|
|
||||||
"Please set the attribute directly instead.")
|
|
||||||
# compatibility
|
|
||||||
self.font = font
|
|
||||||
|
|
||||||
def getfont(self):
|
def getfont(self):
|
||||||
"""Get the current default font."""
|
"""Get the current default font."""
|
||||||
if not self.font:
|
if not self.font:
|
||||||
|
|
|
@ -62,7 +62,7 @@ The tables format between im.quantization and quantization in presets differ in
|
||||||
You can convert the dict format to the preset format with the
|
You can convert the dict format to the preset format with the
|
||||||
`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
|
`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
|
||||||
|
|
||||||
Libjpeg ref.: http://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
|
Libjpeg ref.: https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -1377,11 +1377,6 @@ def _save(im, fp, filename):
|
||||||
(DATE_TIME, "date_time"),
|
(DATE_TIME, "date_time"),
|
||||||
(ARTIST, "artist"),
|
(ARTIST, "artist"),
|
||||||
(COPYRIGHT, "copyright")]:
|
(COPYRIGHT, "copyright")]:
|
||||||
name_with_spaces = name.replace("_", " ")
|
|
||||||
if "_" in name and name_with_spaces in im.encoderinfo:
|
|
||||||
warnings.warn("%r is deprecated; use %r instead" %
|
|
||||||
(name_with_spaces, name), DeprecationWarning)
|
|
||||||
ifd[key] = im.encoderinfo[name.replace("_", " ")]
|
|
||||||
if name in im.encoderinfo:
|
if name in im.encoderinfo:
|
||||||
ifd[key] = im.encoderinfo[name]
|
ifd[key] = im.encoderinfo[name]
|
||||||
|
|
||||||
|
|
|
@ -218,25 +218,25 @@ def command_succeeds(cmd):
|
||||||
command succeeds, or False if an OSError was raised by subprocess.Popen.
|
command succeeds, or False if an OSError was raised by subprocess.Popen.
|
||||||
"""
|
"""
|
||||||
import subprocess
|
import subprocess
|
||||||
with open(os.devnull, 'w') as f:
|
with open(os.devnull, 'wb') as f:
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(cmd, stdout=f, stderr=subprocess.STDOUT).wait()
|
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def djpeg_available():
|
def djpeg_available():
|
||||||
return command_succeeds(['djpeg', '--help'])
|
return command_succeeds(['djpeg', '-version'])
|
||||||
|
|
||||||
|
|
||||||
def cjpeg_available():
|
def cjpeg_available():
|
||||||
return command_succeeds(['cjpeg', '--help'])
|
return command_succeeds(['cjpeg', '-version'])
|
||||||
|
|
||||||
|
|
||||||
def netpbm_available():
|
def netpbm_available():
|
||||||
return (command_succeeds(["ppmquant", "--help"]) and
|
return (command_succeeds(["ppmquant", "--version"]) and
|
||||||
command_succeeds(["ppmtogif", "--help"]))
|
command_succeeds(["ppmtogif", "--version"]))
|
||||||
|
|
||||||
|
|
||||||
def imagemagick_available():
|
def imagemagick_available():
|
||||||
|
@ -253,6 +253,12 @@ if sys.platform == 'win32':
|
||||||
else:
|
else:
|
||||||
IMCONVERT = 'convert'
|
IMCONVERT = 'convert'
|
||||||
|
|
||||||
|
def distro():
|
||||||
|
if os.path.exists('/etc/os-release'):
|
||||||
|
with open('/etc/os-release', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if 'ID=' in line:
|
||||||
|
return line.strip().split('=')[1]
|
||||||
|
|
||||||
class cached_property(object):
|
class cached_property(object):
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
|
|
BIN
Tests/images/hopper-XYZ.png
Normal file
BIN
Tests/images/hopper-XYZ.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
|
@ -417,25 +417,6 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
||||||
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
||||||
|
|
||||||
def test_deprecation_warning_with_spaces(self):
|
|
||||||
kwargs = {'resolution unit': 'inch',
|
|
||||||
'x resolution': 36,
|
|
||||||
'y resolution': 72}
|
|
||||||
filename = self.tempfile("temp.tif")
|
|
||||||
self.assert_warning(DeprecationWarning,
|
|
||||||
lambda: hopper("RGB").save(filename, **kwargs))
|
|
||||||
from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
|
|
||||||
|
|
||||||
im = Image.open(filename)
|
|
||||||
|
|
||||||
# legacy interface
|
|
||||||
self.assertEqual(im.tag[X_RESOLUTION][0][0], 36)
|
|
||||||
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 72)
|
|
||||||
|
|
||||||
# v2 interface
|
|
||||||
self.assertEqual(im.tag_v2[X_RESOLUTION], 36)
|
|
||||||
self.assertEqual(im.tag_v2[Y_RESOLUTION], 72)
|
|
||||||
|
|
||||||
def test_lzw(self):
|
def test_lzw(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open("Tests/images/hopper_lzw.tif")
|
im = Image.open("Tests/images/hopper_lzw.tif")
|
||||||
|
@ -480,12 +461,12 @@ class TestFileTiff(PillowTestCase):
|
||||||
# however does.
|
# however does.
|
||||||
im = Image.new('RGB', (1, 1))
|
im = Image.new('RGB', (1, 1))
|
||||||
im.info['icc_profile'] = 'Dummy value'
|
im.info['icc_profile'] = 'Dummy value'
|
||||||
|
|
||||||
# Try save-load round trip to make sure both handle icc_profile.
|
# Try save-load round trip to make sure both handle icc_profile.
|
||||||
tmpfile = self.tempfile('temp.tif')
|
tmpfile = self.tempfile('temp.tif')
|
||||||
im.save(tmpfile, 'TIFF', compression='raw')
|
im.save(tmpfile, 'TIFF', compression='raw')
|
||||||
reloaded = Image.open(tmpfile)
|
reloaded = Image.open(tmpfile)
|
||||||
|
|
||||||
self.assertEqual(b'Dummy value', reloaded.info['icc_profile'])
|
self.assertEqual(b'Dummy value', reloaded.info['icc_profile'])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ class TestImageConvert(PillowTestCase):
|
||||||
convert(im, mode)
|
convert(im, mode)
|
||||||
|
|
||||||
# Check 0
|
# Check 0
|
||||||
im = Image.new(mode, (0,0))
|
im = Image.new(mode, (0, 0))
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
convert(im, mode)
|
convert(im, mode)
|
||||||
|
|
||||||
def test_default(self):
|
def test_default(self):
|
||||||
|
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
|
@ -137,6 +137,77 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
self.assert_image_similar(alpha, comparable, 5)
|
self.assert_image_similar(alpha, comparable, 5)
|
||||||
|
|
||||||
|
def test_matrix_illegal_conversion(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper('CMYK')
|
||||||
|
matrix = (
|
||||||
|
0.412453, 0.357580, 0.180423, 0,
|
||||||
|
0.212671, 0.715160, 0.072169, 0,
|
||||||
|
0.019334, 0.119193, 0.950227, 0)
|
||||||
|
self.assertNotEqual(im.mode, 'RGB')
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
lambda: im.convert(mode='CMYK', matrix=matrix))
|
||||||
|
|
||||||
|
def test_matrix_wrong_mode(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper('L')
|
||||||
|
matrix = (
|
||||||
|
0.412453, 0.357580, 0.180423, 0,
|
||||||
|
0.212671, 0.715160, 0.072169, 0,
|
||||||
|
0.019334, 0.119193, 0.950227, 0)
|
||||||
|
self.assertEqual(im.mode, 'L')
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
lambda: im.convert(mode='L', matrix=matrix))
|
||||||
|
|
||||||
|
def test_matrix_xyz(self):
|
||||||
|
|
||||||
|
def matrix_convert(mode):
|
||||||
|
# Arrange
|
||||||
|
im = hopper('RGB')
|
||||||
|
matrix = (
|
||||||
|
0.412453, 0.357580, 0.180423, 0,
|
||||||
|
0.212671, 0.715160, 0.072169, 0,
|
||||||
|
0.019334, 0.119193, 0.950227, 0)
|
||||||
|
self.assertEqual(im.mode, 'RGB')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
# Convert an RGB image to the CIE XYZ colour space
|
||||||
|
converted_im = im.convert(mode=mode, matrix=matrix)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(converted_im.mode, mode)
|
||||||
|
self.assertEqual(converted_im.size, im.size)
|
||||||
|
target = Image.open('Tests/images/hopper-XYZ.png')
|
||||||
|
if converted_im.mode == 'RGB':
|
||||||
|
self.assert_image_similar(converted_im, target, 3)
|
||||||
|
else:
|
||||||
|
self.assert_image_similar(converted_im, target.split()[0], 1)
|
||||||
|
|
||||||
|
|
||||||
|
matrix_convert('RGB')
|
||||||
|
matrix_convert('L')
|
||||||
|
|
||||||
|
def test_matrix_identity(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper('RGB')
|
||||||
|
identity_matrix = (
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0)
|
||||||
|
self.assertEqual(im.mode, 'RGB')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
# Convert with an identity matrix
|
||||||
|
converted_im = im.convert(mode='RGB', matrix=identity_matrix)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
# No change
|
||||||
|
self.assert_image_equal(converted_im, im)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper, distro
|
||||||
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
||||||
|
|
||||||
from PIL import ImageQt
|
from PIL import ImageQt
|
||||||
|
|
||||||
|
@unittest.skipIf(ImageQt.qt_version == '5' and distro() == 'arch',
|
||||||
|
"Topixmap fails on Arch + QT5")
|
||||||
class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
||||||
|
|
||||||
def roundtrip(self, expected):
|
def roundtrip(self, expected):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
from test_imageqt import PillowQtTestCase
|
from test_imageqt import PillowQtTestCase
|
||||||
|
|
||||||
from PIL import ImageQt
|
from PIL import ImageQt, Image
|
||||||
|
|
||||||
|
|
||||||
if ImageQt.qt_is_installed:
|
if ImageQt.qt_is_installed:
|
||||||
|
@ -9,38 +9,66 @@ if ImageQt.qt_is_installed:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui
|
||||||
|
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QApplication
|
||||||
|
QT_VERSION = 5
|
||||||
except (ImportError, RuntimeError):
|
except (ImportError, RuntimeError):
|
||||||
try:
|
try:
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
|
||||||
|
QT_VERSION = 4
|
||||||
except (ImportError, RuntimeError):
|
except (ImportError, RuntimeError):
|
||||||
from PySide import QtGui
|
from PySide import QtGui
|
||||||
|
from PySide.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
|
||||||
|
QT_VERSION = 4
|
||||||
|
|
||||||
class TestToQImage(PillowQtTestCase, PillowTestCase):
|
class TestToQImage(PillowQtTestCase, PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
PillowQtTestCase.setUp(self)
|
PillowQtTestCase.setUp(self)
|
||||||
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
for mode in ('RGB', 'RGBA', 'L', 'P', '1'):
|
||||||
data = ImageQt.toqimage(hopper(mode))
|
src = hopper(mode)
|
||||||
|
data = ImageQt.toqimage(src)
|
||||||
|
|
||||||
self.assertIsInstance(data, QImage)
|
self.assertIsInstance(data, QImage)
|
||||||
self.assertFalse(data.isNull())
|
self.assertFalse(data.isNull())
|
||||||
|
|
||||||
|
# reload directly from the qimage
|
||||||
|
rt = ImageQt.fromqimage(data)
|
||||||
|
if mode in ('L', 'P', '1'):
|
||||||
|
self.assert_image_equal(rt, src.convert('RGB'))
|
||||||
|
else:
|
||||||
|
self.assert_image_equal(rt, src)
|
||||||
|
|
||||||
|
if mode == '1':
|
||||||
|
# BW appears to not save correctly on QT4 and QT5
|
||||||
|
# kicks out errors on console:
|
||||||
|
# libpng warning: Invalid color type/bit depth combination in IHDR
|
||||||
|
# libpng error: Invalid IHDR data
|
||||||
|
continue
|
||||||
|
|
||||||
# Test saving the file
|
# Test saving the file
|
||||||
tempfile = self.tempfile('temp_{}.png'.format(mode))
|
tempfile = self.tempfile('temp_{}.png'.format(mode))
|
||||||
data.save(tempfile)
|
data.save(tempfile)
|
||||||
|
|
||||||
|
# Check that it actually worked.
|
||||||
|
reloaded = Image.open(tempfile)
|
||||||
|
# Gray images appear to come back in palette mode.
|
||||||
|
# They're roughly equivalent
|
||||||
|
if QT_VERSION == 4 and mode == 'L':
|
||||||
|
src = src.convert('P')
|
||||||
|
self.assert_image_equal(reloaded, src)
|
||||||
|
|
||||||
def test_segfault(self):
|
def test_segfault(self):
|
||||||
PillowQtTestCase.setUp(self)
|
PillowQtTestCase.setUp(self)
|
||||||
|
|
||||||
app = QtGui.QApplication([])
|
app = QApplication([])
|
||||||
ex = Example()
|
ex = Example()
|
||||||
assert(app) # Silence warning
|
assert(app) # Silence warning
|
||||||
assert(ex) # Silence warning
|
assert(ex) # Silence warning
|
||||||
|
|
||||||
|
|
||||||
if ImageQt.qt_is_installed:
|
if ImageQt.qt_is_installed:
|
||||||
class Example(QtGui.QWidget):
|
class Example(QWidget):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Example, self).__init__()
|
super(Example, self).__init__()
|
||||||
|
@ -51,9 +79,9 @@ if ImageQt.qt_is_installed:
|
||||||
|
|
||||||
pixmap1 = QtGui.QPixmap.fromImage(qimage)
|
pixmap1 = QtGui.QPixmap.fromImage(qimage)
|
||||||
|
|
||||||
hbox = QtGui.QHBoxLayout(self)
|
hbox = QHBoxLayout(self)
|
||||||
|
|
||||||
lbl = QtGui.QLabel(self)
|
lbl = QLabel(self)
|
||||||
# Segfault in the problem
|
# Segfault in the problem
|
||||||
lbl.setPixmap(pixmap1.copy())
|
lbl.setPixmap(pixmap1.copy())
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper, distro
|
||||||
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
||||||
|
|
||||||
from PIL import ImageQt
|
from PIL import ImageQt
|
||||||
|
@ -9,6 +9,8 @@ if ImageQt.qt_is_installed:
|
||||||
|
|
||||||
class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
||||||
|
|
||||||
|
@unittest.skipIf(ImageQt.qt_version == '5' and distro() == 'arch',
|
||||||
|
"Topixmap fails on Arch + QT5")
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
PillowQtTestCase.setUp(self)
|
PillowQtTestCase.setUp(self)
|
||||||
|
|
||||||
|
|
|
@ -44,14 +44,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
draw.polygon(list(range(100)))
|
draw.polygon(list(range(100)))
|
||||||
draw.rectangle(list(range(4)))
|
draw.rectangle(list(range(4)))
|
||||||
|
|
||||||
def test_removed_methods(self):
|
|
||||||
im = hopper()
|
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
|
||||||
|
|
||||||
self.assertRaises(Exception, lambda: draw.setink(0))
|
|
||||||
self.assertRaises(Exception, lambda: draw.setfill(0))
|
|
||||||
|
|
||||||
def test_valueerror(self):
|
def test_valueerror(self):
|
||||||
im = Image.open("Tests/images/chi.gif")
|
im = Image.open("Tests/images/chi.gif")
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,14 @@ class TestNumpy(PillowTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(im.getdata()), len(arr))
|
self.assertEqual(len(im.getdata()), len(arr))
|
||||||
|
|
||||||
|
def test_zero_size(self):
|
||||||
|
# Shouldn't cause floating point exception
|
||||||
|
# See https://github.com/python-pillow/Pillow/issues/2259
|
||||||
|
|
||||||
|
im = Image.fromarray(numpy.empty((0, 0), dtype=numpy.uint8))
|
||||||
|
|
||||||
|
self.assertEqual(im.size, (0, 0))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
4
map.c
4
map.c
|
@ -342,7 +342,7 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
|
||||||
stride = xsize * 4;
|
stride = xsize * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ysize > INT_MAX / stride) {
|
if (stride > 0 && ysize > INT_MAX / stride) {
|
||||||
PyErr_SetString(PyExc_MemoryError, "Integer overflow in ysize");
|
PyErr_SetString(PyExc_MemoryError, "Integer overflow in ysize");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
|
||||||
if (offset > PY_SSIZE_T_MAX - size) {
|
if (offset > PY_SSIZE_T_MAX - size) {
|
||||||
PyErr_SetString(PyExc_MemoryError, "Integer overflow in offset");
|
PyErr_SetString(PyExc_MemoryError, "Integer overflow in offset");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check buffer size */
|
/* check buffer size */
|
||||||
if (PyImaging_GetBuffer(target, &view) < 0)
|
if (PyImaging_GetBuffer(target, &view) < 0)
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -84,10 +84,6 @@ def _find_library_file(self, library):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _lib_include(root):
|
|
||||||
# map root to (root/lib, root/include)
|
|
||||||
return os.path.join(root, "lib"), os.path.join(root, "include")
|
|
||||||
|
|
||||||
def _cmd_exists(cmd):
|
def _cmd_exists(cmd):
|
||||||
return any(
|
return any(
|
||||||
os.access(os.path.join(path, cmd), os.X_OK)
|
os.access(os.path.join(path, cmd), os.X_OK)
|
||||||
|
|
|
@ -10,10 +10,6 @@ def _relpath(*args):
|
||||||
return os.path.join(os.getcwd(), *args)
|
return os.path.join(os.getcwd(), *args)
|
||||||
|
|
||||||
|
|
||||||
def _relbuild(*args):
|
|
||||||
return _relpath('build', *args)
|
|
||||||
|
|
||||||
|
|
||||||
build_dir = _relpath('build')
|
build_dir = _relpath('build')
|
||||||
inc_dir = _relpath('depends')
|
inc_dir = _relpath('depends')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user