Merge branch 'master' into gitignore-review

This commit is contained in:
Andrew Murray 2018-09-29 07:26:46 +10:00 committed by GitHub
commit 33f1a2563e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 1096 additions and 497 deletions

View File

@ -4,7 +4,11 @@
### What actually happened?
### What versions of Pillow and Python are you using?
### What are your OS, Python and Pillow versions?
* OS:
* Python:
* Pillow:
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

View File

@ -27,9 +27,11 @@ matrix:
- python: '3.6'
- python: '3.6'
dist: trusty
env: PYTHONOPTIMIZE=1
- python: '3.5'
- python: '3.5'
dist: trusty
env: PYTHONOPTIMIZE=2
- python: '3.4'
dist: trusty
- env: DOCKER="alpine" DOCKER_TAG="pytest"

View File

@ -7,7 +7,7 @@ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\
python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick\
libharfbuzz-dev libfribidi-dev
pip install cffi
PYTHONOPTIMIZE=0 pip install cffi
pip install check-manifest
pip install coverage
pip install olefile

View File

@ -5,6 +5,84 @@ Changelog (Pillow)
5.3.0 (unreleased)
------------------
- Fixed decompression bomb check in _crop #3313
[dinkolubina, hugovk]
- Added support to ImageDraw.floodfill for non-RGB colors #3377
[radarhere]
- Tests: Avoid catching unexpected exceptions in tests #2203
[jdufresne]
- Use TextIOWrapper.detach() instead of NoCloseStream #2214
[jdufresne]
- Added transparency to matrix conversion #3205
[radarhere]
- Added ImageOps pad method #3364
[radarhere]
- Give correct extrema for I;16 format images #3359
[bz2]
- Added PySide2 #3279
[radarhere]
- Corrected TIFF tags #3369
[radarhere]
- CI: Install CFFI and pycparser without any PYTHONOPTIMIZE #3374
[hugovk]
- Read/Save RGB webp as RGB (instead of RGBX) #3298
[kkopachev]
- ImageDraw: Add line joints #3250
[radarhere]
- Improved performance of ImageDraw floodfill method #3294
[yo1995]
- Fix builds with --parallel #3272
[hsoft]
- Add more raw Tiff modes (RGBaX, RGBaXX, RGBAX, RGBAXX) #3335
[homm]
- Close existing WebP fp before setting new fp #3341
[radarhere]
- Add orientation, compression and id_section as TGA save keyword arguments #3327
[radarhere]
- Convert int values of RATIONAL TIFF tags to floats #3338
[radarhere, wiredfool]
- Fix code for PYTHONOPTIMIZE #3233
[hugovk]
- Changed ImageFilter.Kernel to subclass ImageFilter.BuiltinFilter, instead of the other way around #3273
[radarhere]
- Remove unused draw.draw_line, draw.draw_point and font.getabc methods #3232
[hugovk]
- Tests: Added ImageFilter tests #3295
[radarhere]
- Tests: Added ImageChops tests #3230
[hugovk, radarhere]
- AppVeyor: Download lib if not present in pillow-depends #3316
[radarhere]
- Travis CI: Add Python 3.7 and Xenial #3234
[hugovk]
- Docs: Added documentation for NumPy conversion #3301
[radarhere]
- Depends: Update libimagequant to 2.12.1 #3281
[radarhere]
@ -17,7 +95,7 @@ Changelog (Pillow)
- Skip outline if the draw operation fills with the same colour #2922
[radarhere]
- Flake8 fixes #3173
- Flake8 fixes #3173, #3380
[radarhere]
- Avoid deprecated 'U' mode when opening files #2187

View File

@ -89,7 +89,7 @@ Released as needed privately to individual vendors for critical security-related
$ git clone https://github.com/python-pillow/pillow-wheels
$ cd pillow-wheels
$ git submodule init
$ git submodule update
$ git submodule update Pillow
$ cd Pillow
$ git fetch --all
$ git checkout [[release tag]]

View File

@ -25,9 +25,8 @@ class TestImagingLeaks(PillowTestCase):
if i < min_iterations:
mem_limit = mem + 1
continue
self.assertLessEqual(mem, mem_limit,
msg='memory usage limit exceeded after %d iterations'
% (i + 1))
msg = 'memory usage limit exceeded after %d iterations' % (i + 1)
self.assertLessEqual(mem, mem_limit, msg)
def test_leak_putdata(self):
im = Image.new('RGB', (25, 25))

View File

@ -9,8 +9,7 @@ iterations = 5000
When run on a system without the jpeg leak fixes,
the valgrind runs look like this.
NOSE_PROCESSES=0 NOSE_TIMEOUT=600 valgrind --tool=massif \
python test-installed.py -s -v Tests/check_jpeg_leaks.py
valgrind --tool=massif python test-installed.py -s -v Tests/check_jpeg_leaks.py
"""

View File

@ -272,8 +272,8 @@ class PillowLeakTestCase(PillowTestCase):
for cycle in range(self.iterations):
core()
mem = (self._get_mem_usage() - start_mem)
self.assertLess(mem, self.mem_limit,
msg='memory usage limit exceeded in iteration %d' % cycle)
msg = 'memory usage limit exceeded in iteration %d' % cycle
self.assertLess(mem, self.mem_limit, msg)
# helpers

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

View File

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -52,10 +52,5 @@ for i0 in range(65556):
print()
# print(check(min_size, min_start))
print("#define ACCESS_TABLE_SIZE", min_size)
print("#define ACCESS_TABLE_HASH", min_start)
# for m in modes:
# print(m, "=>", hash(m, min_start) % min_size)

View File

@ -22,7 +22,6 @@ class TestBmpReference(PillowTestCase):
im.load()
except Exception: # as msg:
pass
# print("Bad Image %s: %s" %(f,msg))
def test_questionable(self):
""" These shouldn't crash/dos, but it's not well defined that these
@ -43,11 +42,11 @@ class TestBmpReference(PillowTestCase):
im = Image.open(f)
im.load()
if os.path.basename(f) not in supported:
print("Please add %s to the partially supported bmp specs." % f)
print("Please add %s to the partially supported"
" bmp specs." % f)
except Exception: # as msg:
if os.path.basename(f) in supported:
raise
# print("Bad Image %s: %s" %(f,msg))
def test_good(self):
""" These should all work. There's a set of target files in the

View File

@ -335,35 +335,35 @@ class TestColorLut3DFilter(PillowTestCase):
g.transpose(Image.ROTATE_180)])
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1]
with self.assertRaisesRegex(ValueError, "should have table_channels"):
im.filter(lut)
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = (numpy.array(lut.table, dtype=numpy.float32)
.reshape((7 * 9 * 11), 3))
with self.assertRaisesRegex(ValueError, "should have table_channels"):
im.filter(lut)
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float16)
self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float32)
self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float64)
self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.int32)
im.filter(lut)
lut.table = numpy.array(lut.table, dtype=numpy.int8)

View File

@ -61,6 +61,29 @@ class TestDecompressionCrop(PillowTestCase):
self.assert_warning(Image.DecompressionBombWarning,
self.src.crop, box)
def test_crop_decompression_checks(self):
im = Image.new("RGB", (100, 100))
good_values = ((-9999, -9999, -9990, -9990),
(-999, -999, -990, -990))
warning_values = ((-160, -160, 99, 99),
(160, 160, -99, -99))
error_values = ((-99909, -99990, 99999, 99999),
(99909, 99990, -99999, -99999))
for value in good_values:
self.assertEqual(im.crop(value).size, (9, 9))
for value in warning_values:
self.assert_warning(Image.DecompressionBombWarning, im.crop, value)
for value in error_values:
with self.assertRaises(Image.DecompressionBombError):
im.crop(value)
if __name__ == '__main__':
unittest.main()

View File

@ -2,8 +2,6 @@ from helper import unittest, PillowTestCase
from PIL import GdImageFile
import io
TEST_GD_FILE = "Tests/images/hopper.gd"

View File

@ -141,11 +141,9 @@ class TestFileJpeg(PillowTestCase):
im = Image.open('Tests/images/icc_profile_big.jpg')
f = self.tempfile("temp.jpg")
icc_profile = im.info["icc_profile"]
try:
im.save(f, format='JPEG', progressive=True, quality=95,
icc_profile=icc_profile, optimize=True)
except IOError:
self.fail("Failed saving image with icc larger than image size")
# Should not raise IOError for image with icc larger than image size.
im.save(f, format='JPEG', progressive=True, quality=95,
icc_profile=icc_profile, optimize=True)
def test_optimize(self):
im1 = self.roundtrip(hopper())

View File

@ -231,6 +231,16 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self):
# issue #1765
im = hopper('RGB')
out = self.tempfile('temp.tif')
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, dpi=(72, 72))
TiffImagePlugin.WRITE_LIBTIFF = False
reloaded = Image.open(out)
self.assertEqual(reloaded.info['dpi'], (72.0, 72.0))
def test_g3_compression(self):
i = Image.open('Tests/images/hopper_g4_500.tif')
out = self.tempfile("temp.tif")
@ -529,10 +539,8 @@ class TestFileLibTiff(LibTiffTestCase):
im = Image.open(tmpfile)
im.n_frames
im.close()
try:
os.remove(tmpfile) # Windows PermissionError here!
except:
self.fail("Should not get permission error here")
# Should not raise PermissionError.
os.remove(tmpfile)
def test_read_icc(self):
with Image.open("Tests/images/hopper.iccprofile.tif") as img:

View File

@ -53,10 +53,10 @@ class TestFileTga(PillowTestCase):
# Generate a new test name every time so the
# test will not fail with permission error
# on Windows.
test_file = self.tempfile("temp.tga")
out = self.tempfile("temp.tga")
original_im.save(test_file, rle=rle)
saved_im = Image.open(test_file)
original_im.save(out, rle=rle)
saved_im = Image.open(out)
if rle:
self.assertEqual(
saved_im.info["compression"],
@ -95,34 +95,93 @@ class TestFileTga(PillowTestCase):
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
test_file = self.tempfile("temp.tga")
out = self.tempfile("temp.tga")
# Save
im.save(test_file)
test_im = Image.open(test_file)
im.save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (100, 100))
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
# RGBA save
im.convert("RGBA").save(test_file)
test_im = Image.open(test_file)
im.convert("RGBA").save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (100, 100))
def test_save_id_section(self):
test_file = "Tests/images/rgb32rle.tga"
im = Image.open(test_file)
out = self.tempfile("temp.tga")
# Check there is no id section
im.save(out)
test_im = Image.open(out)
self.assertNotIn("id_section", test_im.info)
# Save with custom id section
im.save(out, id_section=b"Test content")
test_im = Image.open(out)
self.assertEqual(test_im.info["id_section"], b"Test content")
# Save with custom id section greater than 255 characters
id_section = b"Test content" * 25
self.assert_warning(UserWarning,
lambda: im.save(out, id_section=id_section))
test_im = Image.open(out)
self.assertEqual(test_im.info["id_section"], id_section[:255])
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
# Save with no id section
im.save(out, id_section="")
test_im = Image.open(out)
self.assertNotIn("id_section", test_im.info)
def test_save_orientation(self):
test_file = "Tests/images/rgb32rle.tga"
im = Image.open(test_file)
self.assertEqual(im.info["orientation"], -1)
out = self.tempfile("temp.tga")
im.save(out, orientation=1)
test_im = Image.open(out)
self.assertEqual(test_im.info["orientation"], 1)
def test_save_rle(self):
test_file = "Tests/images/rgb32rle.tga"
im = Image.open(test_file)
self.assertEqual(im.info["compression"], "tga_rle")
test_file = self.tempfile("temp.tga")
out = self.tempfile("temp.tga")
# Save
im.save(test_file)
test_im = Image.open(test_file)
im.save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (199, 199))
self.assertEqual(test_im.info["compression"], "tga_rle")
# Save without compression
im.save(out, compression=None)
test_im = Image.open(out)
self.assertNotIn("compression", test_im.info)
# RGBA save
im.convert("RGBA").save(test_file)
test_im = Image.open(test_file)
im.convert("RGBA").save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (199, 199))
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
self.assertNotIn("compression", im.info)
# Save with compression
im.save(out, compression="tga_rle")
test_im = Image.open(out)
self.assertEqual(test_im.info["compression"], "tga_rle")
def test_save_l_transparency(self):
# There are 559 transparent pixels in la.tga.
num_transparent = 559
@ -133,10 +192,10 @@ class TestFileTga(PillowTestCase):
self.assertEqual(
im.getchannel("A").getcolors()[0][0], num_transparent)
test_file = self.tempfile("temp.tga")
im.save(test_file)
out = self.tempfile("temp.tga")
im.save(out)
test_im = Image.open(test_file)
test_im = Image.open(out)
self.assertEqual(test_im.mode, "LA")
self.assertEqual(
test_im.getchannel("A").getcolors()[0][0], num_transparent)

View File

@ -1,6 +1,5 @@
import logging
from io import BytesIO
import struct
import sys
from helper import unittest, PillowTestCase, hopper
@ -59,7 +58,8 @@ class TestFileTiff(PillowTestCase):
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (52, 53))
self.assertEqual(im.tile, [('raw', (0, 0, 52, 53), 160, ('RGBA', 0, 1))])
self.assertEqual(im.tile,
[('raw', (0, 0, 52, 53), 160, ('RGBA', 0, 1))])
im.load()
def test_set_legacy_api(self):
@ -133,11 +133,8 @@ class TestFileTiff(PillowTestCase):
def test_bad_exif(self):
i = Image.open('Tests/images/hopper_bad_exif.jpg')
try:
self.assert_warning(UserWarning, i._getexif)
except struct.error:
self.fail(
"Bad EXIF data passed incorrect values to _binary unpack")
# Should not raise struct.error.
self.assert_warning(UserWarning, i._getexif)
def test_save_rgba(self):
im = hopper("RGBA")

View File

@ -56,7 +56,8 @@ class TestFileTiffMetadata(PillowTestCase):
loaded = Image.open(f)
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts],
(len(bindata),))
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
@ -75,8 +76,10 @@ class TestFileTiffMetadata(PillowTestCase):
img.save(f, tiffinfo=info)
loaded = Image.open(f)
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts],
(8, len(bindata) - 8))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts],
(8, len(bindata) - 8))
def test_read_metadata(self):
img = Image.open('Tests/images/hopper_g4.tif')
@ -133,8 +136,8 @@ class TestFileTiffMetadata(PillowTestCase):
if isinstance(v, IFDRational):
original[k] = IFDRational(*_limit_rational(v, 2**31))
if isinstance(v, tuple) and isinstance(v[0], IFDRational):
original[k] = tuple([IFDRational(
*_limit_rational(elt, 2**31)) for elt in v])
original[k] = tuple([IFDRational(*_limit_rational(elt, 2**31))
for elt in v])
ignored = ['StripByteCounts', 'RowsPerStrip',
'PageNumber', 'StripOffsets']
@ -169,10 +172,8 @@ class TestFileTiffMetadata(PillowTestCase):
f = io.BytesIO(b'II*\x00\x08\x00\x00\x00')
head = f.read(8)
info = TiffImagePlugin.ImageFileDirectory(head)
try:
self.assert_warning(UserWarning, info.load, f)
except struct.error:
self.fail("Should not be struct errors there.")
# Should not raise struct.error.
self.assert_warning(UserWarning, info.load, f)
def test_iccprofile(self):
# https://github.com/python-pillow/Pillow/issues/1462
@ -186,7 +187,8 @@ class TestFileTiffMetadata(PillowTestCase):
def test_iccprofile_binary(self):
# https://github.com/python-pillow/Pillow/issues/1526
# We should be able to load this, but probably won't be able to save it.
# We should be able to load this,
# but probably won't be able to save it.
im = Image.open('Tests/images/hopper.iccprofile_binary.tif')
self.assertEqual(im.tag_v2.tagtype[34675], 1)
@ -223,10 +225,8 @@ class TestFileTiffMetadata(PillowTestCase):
head = data.read(8)
info = TiffImagePlugin.ImageFileDirectory_v2(head)
info.load(data)
try:
info = dict(info)
except ValueError:
self.fail("Should not be struct value error there.")
# Should not raise ValueError.
info = dict(info)
self.assertIn(33432, info)
def test_PhotoshopInfo(self):
@ -245,10 +245,8 @@ class TestFileTiffMetadata(PillowTestCase):
ifd._tagdata[277] = struct.pack('hh', 4, 4)
ifd.tagtype[277] = TiffTags.SHORT
try:
self.assert_warning(UserWarning, lambda: ifd[277])
except ValueError:
self.fail("Invalid Metadata count should not cause a Value Error.")
# Should not raise ValueError.
self.assert_warning(UserWarning, lambda: ifd[277])
if __name__ == '__main__':

View File

@ -16,8 +16,7 @@ class TestFileWebp(PillowTestCase):
self.skipTest('WebP support not installed')
return
# WebPAnimDecoder only returns RGBA or RGBX, never RGB
self.rgb_mode = "RGBX" if _webp.HAVE_WEBPANIM else "RGB"
self.rgb_mode = "RGB"
def test_version(self):
_webp.WebPDecoderVersion()
@ -29,8 +28,7 @@ class TestFileWebp(PillowTestCase):
Does it have the bits we expect?
"""
file_path = "Tests/images/hopper.webp"
image = Image.open(file_path)
image = Image.open("Tests/images/hopper.webp")
self.assertEqual(image.mode, self.rgb_mode)
self.assertEqual(image.size, (128, 128))
@ -40,9 +38,8 @@ class TestFileWebp(PillowTestCase):
# generated with:
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
target = Image.open('Tests/images/hopper_webp_bits.ppm')
target = target.convert(self.rgb_mode)
self.assert_image_similar(image, target, 20.0)
self.assert_image_similar_tofile(
image, 'Tests/images/hopper_webp_bits.ppm', 1.0)
def test_write_rgb(self):
"""
@ -61,13 +58,9 @@ class TestFileWebp(PillowTestCase):
image.load()
image.getdata()
# If we're using the exact same version of WebP, this test should pass.
# but it doesn't if the WebP is generated on Ubuntu and tested on
# Fedora.
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
# target = Image.open('Tests/images/hopper_webp_write.ppm')
# self.assert_image_equal(image, target)
self.assert_image_similar_tofile(
image, 'Tests/images/hopper_webp_write.ppm', 12.0)
# This test asserts that the images are similar. If the average pixel
# difference between the two images is less than the epsilon value,
@ -135,6 +128,13 @@ class TestFileWebp(PillowTestCase):
self.assertRaises(TypeError, _webp.WebPAnimDecoder)
self.assertRaises(TypeError, _webp.WebPDecode)
def test_no_resource_warning(self):
file_path = "Tests/images/hopper.webp"
image = Image.open(file_path)
temp_file = self.tempfile("temp.webp")
self.assert_warning(None, image.save, temp_file)
if __name__ == '__main__':
unittest.main()

View File

@ -19,8 +19,7 @@ class TestFileWebpLossless(PillowTestCase):
if (_webp.WebPDecoderVersion() < 0x0200):
self.skipTest('lossless not included')
# WebPAnimDecoder only returns RGBA or RGBX, never RGB
self.rgb_mode = "RGBX" if _webp.HAVE_WEBPANIM else "RGB"
self.rgb_mode = "RGB"
def test_write_lossless_rgb(self):
temp_file = self.tempfile("temp.webp")

View File

@ -47,10 +47,6 @@ class TestFormatHSV(PillowTestCase):
img = Image.merge('RGB', (r, g, b))
# print(("%d, %d -> "% (int(1.75*px),int(.25*px))) + \
# "(%s, %s, %s)"%img.getpixel((1.75*px, .25*px)))
# print(("%d, %d -> "% (int(.75*px),int(.25*px))) + \
# "(%s, %s, %s)"%img.getpixel((.75*px, .25*px)))
return img
def to_xxx_colorsys(self, im, func, mode):
@ -95,15 +91,6 @@ class TestFormatHSV(PillowTestCase):
im = src.convert('HSV')
comparable = self.to_hsv_colorsys(src)
# print(im.getpixel((448, 64)))
# print(comparable.getpixel((448, 64)))
# print(im.split()[0].histogram())
# print(comparable.split()[0].histogram())
# im.split()[0].show()
# comparable.split()[0].show()
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
1, "Hue conversion is wrong")
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
@ -111,16 +98,9 @@ class TestFormatHSV(PillowTestCase):
self.assert_image_similar(im.getchannel(2), comparable.getchannel(2),
1, "Value conversion is wrong")
# print(im.getpixel((192, 64)))
comparable = src
im = im.convert('RGB')
# im.split()[0].show()
# comparable.split()[0].show()
# print(im.getpixel((192, 64)))
# print(comparable.getpixel((192, 64)))
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
3, "R conversion is wrong")
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
@ -132,12 +112,6 @@ class TestFormatHSV(PillowTestCase):
im = hopper('RGB').convert('HSV')
comparable = self.to_hsv_colorsys(hopper('RGB'))
# print([ord(x) for x in im.split()[0].tobytes()[:80]])
# print([ord(x) for x in comparable.split()[0].tobytes()[:80]])
# print(im.split()[0].histogram())
# print(comparable.split()[0].histogram())
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
1, "Hue conversion is wrong")
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
@ -150,12 +124,6 @@ class TestFormatHSV(PillowTestCase):
converted = comparable.convert('RGB')
comparable = self.to_rgb_colorsys(comparable)
# print(converted.split()[1].histogram())
# print(target.split()[1].histogram())
# print([ord(x) for x in target.split()[1].tobytes()[:80]])
# print([ord(x) for x in converted.split()[1].tobytes()[:80]])
self.assert_image_similar(converted.getchannel(0),
comparable.getchannel(0),
3, "R conversion is wrong")

View File

@ -1,15 +1,20 @@
from helper import unittest, PillowTestCase, hopper, on_appveyor
try:
from PIL import PyAccess
except ImportError:
# Skip in setUp()
pass
from PIL import Image
import sys
import os
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
if os.environ.get("PYTHONOPTIMIZE") == "2":
cffi = None
else:
try:
from PIL import PyAccess
import cffi
except ImportError:
cffi = None
class AccessTest(PillowTestCase):
# initial value
@ -113,38 +118,20 @@ class TestImageGetPixel(AccessTest):
self.check(mode, 2**16-1)
@unittest.skipIf(cffi is None, "No cffi")
class TestCffiPutPixel(TestImagePutPixel):
_need_cffi_access = True
def setUp(self):
try:
import cffi
assert cffi # silence warning
except ImportError:
self.skipTest("No cffi")
@unittest.skipIf(cffi is None, "No cffi")
class TestCffiGetPixel(TestImageGetPixel):
_need_cffi_access = True
def setUp(self):
try:
import cffi
assert cffi # silence warning
except ImportError:
self.skipTest("No cffi")
@unittest.skipIf(cffi is None, "No cffi")
class TestCffi(AccessTest):
_need_cffi_access = True
def setUp(self):
try:
import cffi
assert cffi # silence warning
except ImportError:
self.skipTest("No cffi")
def _test_get_access(self, im):
"""Do we get the same thing as the old pixel access

View File

@ -187,6 +187,7 @@ class TestImageConvert(PillowTestCase):
def matrix_convert(mode):
# Arrange
im = hopper('RGB')
im.info['transparency'] = (255, 0, 0)
matrix = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
@ -203,9 +204,12 @@ class TestImageConvert(PillowTestCase):
target = Image.open('Tests/images/hopper-XYZ.png')
if converted_im.mode == 'RGB':
self.assert_image_similar(converted_im, target, 3)
self.assertEqual(converted_im.info['transparency'],
(105, 54, 4))
else:
self.assert_image_similar(converted_im,
target.getchannel(0), 1)
self.assertEqual(converted_im.info['transparency'], 105)
matrix_convert('RGB')
matrix_convert('L')

View File

@ -94,6 +94,15 @@ class TestImageFilter(PillowTestCase):
self.assertEqual(rankfilter.size, 1)
self.assertEqual(rankfilter.rank, 2)
def test_builtinfilter_p(self):
builtinFilter = ImageFilter.BuiltinFilter()
self.assertRaises(ValueError, builtinFilter.filter, hopper("P"))
def test_kernel_not_enough_coefficients(self):
self.assertRaises(ValueError,
lambda: ImageFilter.Kernel((3, 3), (0, 0)))
def test_consistency_3x3(self):
source = Image.open("Tests/images/hopper.bmp")
reference = Image.open("Tests/images/hopper_emboss.bmp")

View File

@ -1,3 +1,4 @@
from PIL import Image
from helper import unittest, PillowTestCase, hopper
@ -19,6 +20,13 @@ class TestImageGetExtrema(PillowTestCase):
extrema("RGBA"), ((0, 255), (0, 255), (0, 255), (255, 255)))
self.assertEqual(
extrema("CMYK"), (((0, 255), (0, 255), (0, 255), (0, 0))))
self.assertEqual(extrema("I;16"), (0, 255))
def test_true_16(self):
im = Image.open("Tests/images/16_bit_noise.tif")
self.assertEqual(im.mode, 'I;16')
extrema = im.getextrema()
self.assertEqual(extrema, (106, 285))
if __name__ == '__main__':

View File

@ -352,10 +352,8 @@ class CoreResamplePassesTest(PillowTestCase):
class CoreResampleCoefficientsTest(PillowTestCase):
def test_reduce(self):
test_color = 254
# print()
for size in range(400000, 400010, 2):
# print(size)
i = Image.new('L', (size, 1), 0)
draw = ImageDraw.Draw(i)
draw.rectangle((0, 0, i.size[0] // 2 - 1, 0), test_color)
@ -363,7 +361,6 @@ class CoreResampleCoefficientsTest(PillowTestCase):
px = i.resize((5, i.size[1]), Image.BICUBIC).load()
if px[2, 0] != test_color // 2:
self.assertEqual(test_color // 2, px[2, 0])
# print('>', size, test_color // 2, px[2, 0])
def test_nonzero_coefficients(self):
# regression test for the wrong coefficients calculation

View File

@ -114,15 +114,16 @@ class TestImageRotate(PillowTestCase):
# Alpha images are handled differently internally
im = Image.new('RGBA', (10, 10), 'green')
im = im.rotate(45, expand=1)
corner = im.getpixel((0,0))
corner = im.getpixel((0, 0))
self.assertEqual(corner, (0, 0, 0, 0))
def test_alpha_rotate_with_fill(self):
# Alpha images are handled differently internally
im = Image.new('RGBA', (10, 10), 'green')
im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255))
corner = im.getpixel((0,0))
corner = im.getpixel((0, 0))
self.assertEqual(corner, (255, 0, 0, 255))
if __name__ == '__main__':
unittest.main()

View File

@ -3,6 +3,16 @@ from helper import unittest, PillowTestCase, hopper
from PIL import Image
from PIL import ImageChops
BLACK = (0, 0, 0)
BROWN = (127, 64, 0)
CYAN = (0, 255, 255)
DARK_GREEN = (0, 128, 0)
GREEN = (0, 255, 0)
ORANGE = (255, 128, 0)
WHITE = (255, 255, 255)
GREY = 128
class TestImageChops(PillowTestCase):
@ -35,6 +45,303 @@ class TestImageChops(PillowTestCase):
ImageChops.offset(im, 10)
ImageChops.offset(im, 10, 20)
def test_add(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.add(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
self.assertEqual(new.getpixel((50, 50)), ORANGE)
def test_add_scale_offset(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.add(im1, im2, scale=2.5, offset=100)
# Assert
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
self.assertEqual(new.getpixel((50, 50)), (202, 151, 100))
def test_add_clip(self):
# Arrange
im = hopper()
# Act
new = ImageChops.add(im, im)
# Assert
self.assertEqual(new.getpixel((50, 50)), (255, 255, 254))
def test_add_modulo(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.add_modulo(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
self.assertEqual(new.getpixel((50, 50)), ORANGE)
def test_add_modulo_no_clip(self):
# Arrange
im = hopper()
# Act
new = ImageChops.add_modulo(im, im)
# Assert
self.assertEqual(new.getpixel((50, 50)), (224, 76, 254))
def test_blend(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.blend(im1, im2, 0.5)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
self.assertEqual(new.getpixel((50, 50)), BROWN)
def test_constant(self):
# Arrange
im = Image.new("RGB", (20, 10))
# Act
new = ImageChops.constant(im, GREY)
# Assert
self.assertEqual(new.size, im.size)
self.assertEqual(new.getpixel((0, 0)), GREY)
self.assertEqual(new.getpixel((19, 9)), GREY)
def test_darker_image(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
# Act
new = ImageChops.darker(im1, im2)
# Assert
self.assert_image_equal(new, im2)
def test_darker_pixel(self):
# Arrange
im1 = hopper()
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
# Act
new = ImageChops.darker(im1, im2)
# Assert
self.assertEqual(new.getpixel((50, 50)), (240, 166, 0))
def test_difference(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_arc_end_le_start.png")
im2 = Image.open("Tests/images/imagedraw_arc_no_loops.png")
# Act
new = ImageChops.difference(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
def test_difference_pixel(self):
# Arrange
im1 = hopper()
im2 = Image.open("Tests/images/imagedraw_polygon_kite_RGB.png")
# Act
new = ImageChops.difference(im1, im2)
# Assert
self.assertEqual(new.getpixel((50, 50)), (240, 166, 128))
def test_duplicate(self):
# Arrange
im = hopper()
# Act
new = ImageChops.duplicate(im)
# Assert
self.assert_image_equal(new, im)
def test_invert(self):
# Arrange
im = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.invert(im)
# Assert
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
self.assertEqual(new.getpixel((0, 0)), WHITE)
self.assertEqual(new.getpixel((50, 50)), CYAN)
def test_lighter_image(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
# Act
new = ImageChops.lighter(im1, im2)
# Assert
self.assert_image_equal(new, im1)
def test_lighter_pixel(self):
# Arrange
im1 = hopper()
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
# Act
new = ImageChops.lighter(im1, im2)
# Assert
self.assertEqual(new.getpixel((50, 50)), (255, 255, 127))
def test_multiply_black(self):
"""If you multiply an image with a solid black image,
the result is black."""
# Arrange
im1 = hopper()
black = Image.new("RGB", im1.size, "black")
# Act
new = ImageChops.multiply(im1, black)
# Assert
self.assert_image_equal(new, black)
def test_multiply_green(self):
# Arrange
im = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
green = Image.new("RGB", im.size, "green")
# Act
new = ImageChops.multiply(im, green)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
self.assertEqual(new.getpixel((25, 25)), DARK_GREEN)
self.assertEqual(new.getpixel((50, 50)), BLACK)
def test_multiply_white(self):
"""If you multiply with a solid white image,
the image is unaffected."""
# Arrange
im1 = hopper()
white = Image.new("RGB", im1.size, "white")
# Act
new = ImageChops.multiply(im1, white)
# Assert
self.assert_image_equal(new, im1)
def test_offset(self):
# Arrange
im = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
xoffset = 45
yoffset = 20
# Act
new = ImageChops.offset(im, xoffset, yoffset)
# Assert
self.assertEqual(new.getbbox(), (0, 45, 100, 96))
self.assertEqual(new.getpixel((50, 50)), BLACK)
self.assertEqual(new.getpixel((50+xoffset, 50+yoffset)), DARK_GREEN)
# Test no yoffset
self.assertEqual(ImageChops.offset(im, xoffset),
ImageChops.offset(im, xoffset, xoffset))
def test_screen(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
im2 = Image.open("Tests/images/imagedraw_floodfill_RGB.png")
# Act
new = ImageChops.screen(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
self.assertEqual(new.getpixel((50, 50)), ORANGE)
def test_subtract(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
# Act
new = ImageChops.subtract(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 50, 76, 76))
self.assertEqual(new.getpixel((50, 50)), GREEN)
self.assertEqual(new.getpixel((50, 51)), BLACK)
def test_subtract_scale_offset(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
# Act
new = ImageChops.subtract(im1, im2, scale=2.5, offset=100)
# Assert
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
self.assertEqual(new.getpixel((50, 50)), (100, 202, 100))
def test_subtract_clip(self):
# Arrange
im1 = hopper()
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
# Act
new = ImageChops.subtract(im1, im2)
# Assert
self.assertEqual(new.getpixel((50, 50)), (0, 0, 127))
def test_subtract_modulo(self):
# Arrange
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
# Act
new = ImageChops.subtract_modulo(im1, im2)
# Assert
self.assertEqual(new.getbbox(), (25, 50, 76, 76))
self.assertEqual(new.getpixel((50, 50)), GREEN)
self.assertEqual(new.getpixel((50, 51)), BLACK)
def test_subtract_modulo_no_clip(self):
# Arrange
im1 = hopper()
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
# Act
new = ImageChops.subtract_modulo(im1, im2)
# Assert
self.assertEqual(new.getpixel((50, 50)), (241, 167, 127))
def test_logical(self):
def table(op, a, b):

View File

@ -175,11 +175,11 @@ class TestImageCms(PillowTestCase):
def test_unsupported_color_space(self):
self.assertRaises(ImageCms.PyCMSError,
ImageCms.createProfile, "unsupported")
ImageCms.createProfile, "unsupported")
def test_invalid_color_temperature(self):
self.assertRaises(ImageCms.PyCMSError,
ImageCms.createProfile, "LAB", "invalid")
ImageCms.createProfile, "LAB", "invalid")
def test_simple_lab(self):
i = Image.new('RGB', (10, 10), (128, 128, 128))
@ -446,20 +446,20 @@ class TestImageCms(PillowTestCase):
self.assert_image_equal(source_image_aux, result_image_aux)
def test_preserve_auxiliary_channels_rgba(self):
self.assert_aux_channel_preserved(mode='RGBA',
transform_in_place=False, preserved_channel='A')
self.assert_aux_channel_preserved(
mode='RGBA', transform_in_place=False, preserved_channel='A')
def test_preserve_auxiliary_channels_rgba_in_place(self):
self.assert_aux_channel_preserved(mode='RGBA',
transform_in_place=True, preserved_channel='A')
self.assert_aux_channel_preserved(
mode='RGBA', transform_in_place=True, preserved_channel='A')
def test_preserve_auxiliary_channels_rgbx(self):
self.assert_aux_channel_preserved(mode='RGBX',
transform_in_place=False, preserved_channel='X')
self.assert_aux_channel_preserved(
mode='RGBX', transform_in_place=False, preserved_channel='X')
def test_preserve_auxiliary_channels_rgbx_in_place(self):
self.assert_aux_channel_preserved(mode='RGBX',
transform_in_place=True, preserved_channel='X')
self.assert_aux_channel_preserved(
mode='RGBX', transform_in_place=True, preserved_channel='X')
def test_auxiliary_channels_isolated(self):
# test data in aux channels does not affect non-aux channels

View File

@ -344,19 +344,26 @@ class TestImageDraw(PillowTestCase):
self.assert_image_similar(im, Image.open(expected), 1)
def test_floodfill(self):
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(BBOX2, outline="yellow", fill="green")
centre_point = (int(W/2), int(H/2))
red = ImageColor.getrgb("red")
im_floodfill = Image.open("Tests/images/imagedraw_floodfill.png")
# Act
ImageDraw.floodfill(im, centre_point, red)
for mode, value in [
("L", 1),
("RGBA", (255, 0, 0, 0)),
("RGB", red)
]:
# Arrange
im = Image.new(mode, (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(BBOX2, outline="yellow", fill="green")
centre_point = (int(W/2), int(H/2))
# Assert
self.assert_image_equal(im, im_floodfill)
# Act
ImageDraw.floodfill(im, centre_point, value)
# Assert
expected = "Tests/images/imagedraw_floodfill_"+mode+".png"
im_floodfill = Image.open(expected)
self.assert_image_equal(im, im_floodfill)
# Test that using the same colour does not change the image
ImageDraw.floodfill(im, centre_point, red)
@ -366,6 +373,11 @@ class TestImageDraw(PillowTestCase):
ImageDraw.floodfill(im, (W, H), red)
self.assert_image_equal(im, im_floodfill)
# Test filling at the edge of an image
im = Image.new("RGB", (1, 1))
ImageDraw.floodfill(im, (0, 0), red)
self.assert_image_equal(im, Image.new("RGB", (1, 1), red))
def test_floodfill_border(self):
# floodfill() is experimental
@ -563,6 +575,20 @@ class TestImageDraw(PillowTestCase):
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_line_joint(self):
im = Image.new("RGB", (500, 325))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_line_joint_curve.png"
# Act
xy = [(400, 280), (380, 280), (450, 280), (440, 120), (350, 200),
(310, 280), (300, 280), (250, 280), (250, 200), (150, 200),
(150, 260), (50, 200), (150, 50), (250, 100)]
draw.line(xy, GRAY, 50, "curve")
# Assert
self.assert_image_similar(im, Image.open(expected), 3)
def test_textsize_empty_string(self):
# https://github.com/python-pillow/Pillow/issues/2783
# Arrange
@ -596,12 +622,12 @@ class TestImageDraw(PillowTestCase):
["red", "#f00"]
]:
for operation, args in {
'chord':[BBOX1, 0, 180],
'ellipse':[BBOX1],
'shape':[s],
'pieslice':[BBOX1, -90, 45],
'polygon':[[(18, 30), (85, 30), (60, 72)]],
'rectangle':[BBOX1]
'chord': [BBOX1, 0, 180],
'ellipse': [BBOX1],
'shape': [s],
'pieslice': [BBOX1, -90, 45],
'polygon': [[(18, 30), (85, 30), (60, 72)]],
'rectangle': [BBOX1]
}.items():
# Arrange
im = Image.new(mode, (W, H))

View File

@ -215,7 +215,7 @@ class TestImageFont(PillowTestCase):
# Act/Assert
self.assertRaises(
AssertionError,
ValueError,
draw.multiline_text, (0, 0), TEST_TEXT, font=ttf, align="unknown")
def test_draw_align(self):

View File

@ -24,6 +24,9 @@ class TestImageOps(PillowTestCase):
ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255))
ImageOps.colorize(hopper("L"), "black", "white")
ImageOps.pad(hopper("L"), (128, 128))
ImageOps.pad(hopper("RGB"), (128, 128))
ImageOps.crop(hopper("L"), 1)
ImageOps.crop(hopper("RGB"), 1)
@ -70,6 +73,26 @@ class TestImageOps(PillowTestCase):
newimg = ImageOps.fit(hopper("RGB").resize((100, 1)), (35, 35))
self.assertEqual(newimg.size, (35, 35))
def test_pad(self):
# Same ratio
im = hopper()
new_size = (im.width * 2, im.height * 2)
new_im = ImageOps.pad(im, new_size)
self.assertEqual(new_im.size, new_size)
for label, color, new_size in [
("h", None, (im.width * 4, im.height * 2)),
("v", "#f00", (im.width * 2, im.height * 4))
]:
for i, centering in enumerate([(0, 0), (0.5, 0.5), (1, 1)]):
new_im = ImageOps.pad(im, new_size,
color=color, centering=centering)
self.assertEqual(new_im.size, new_size)
target = Image.open(
"Tests/images/imageops_pad_"+label+"_"+str(i)+".jpg")
self.assert_image_similar(new_im, target, 6)
def test_pil163(self):
# Division by zero in equalize if < 255 pixels in image (@PIL163)

View File

@ -33,6 +33,8 @@ class PillowQPixmapTestCase(PillowQtTestCase):
from PyQt4.QtGui import QGuiApplication
elif ImageQt.qt_version == 'side':
from PySide.QtGui import QGuiApplication
elif ImageQt.qt_version == 'side2':
from PySide2.QtGui import QGuiApplication
except ImportError:
self.skipTest('QGuiApplication not installed')
@ -56,6 +58,8 @@ class TestImageQt(PillowQtTestCase, PillowTestCase):
from PyQt4.QtGui import qRgb
elif ImageQt.qt_version == 'side':
from PySide.QtGui import qRgb
elif ImageQt.qt_version == 'side2':
from PySide2.QtGui import qRgb
self.assertEqual(qRgb(0, 0, 0), qRgba(0, 0, 0, 255))

View File

@ -25,7 +25,7 @@ class TestImageTk(PillowTestCase):
self.skipTest("Tk not installed")
try:
# setup tk
app = tk.Frame()
tk.Frame()
# root = tk.Tk()
except (tk.TclError) as v:
self.skipTest("TCL Error: %s" % v)

View File

@ -96,7 +96,6 @@ if sys.platform.startswith('win32'):
hdr.biClrImportant = 0
hdc = CreateCompatibleDC(None)
# print('hdc:',hex(hdc))
pixels = ctypes.c_void_p()
dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS,
ctypes.byref(pixels), None, 0)

View File

@ -221,7 +221,8 @@ class TestLibUnpack(PillowTestCase):
data_len = data * len(pixels)
data = bytes(bytearray(range(1, data_len + 1)))
im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1)
im = Image.frombytes(mode, (len(pixels), 1), data,
"raw", rawmode, 0, 1)
for x, pixel in enumerate(pixels):
self.assertEqual(pixel, im.getpixel((x, 0)))
@ -265,9 +266,11 @@ class TestLibUnpack(PillowTestCase):
def test_P(self):
self.assert_unpack("P", "P;1", b'\xe4', 1, 1, 1, 0, 0, 1, 0, 0)
self.assert_unpack("P", "P;2", b'\xe4', 3, 2, 1, 0)
# self.assert_unpack("P", "P;2L", b'\xe4', 1, 1, 1, 0) # erroneous?
# erroneous?
# self.assert_unpack("P", "P;2L", b'\xe4', 1, 1, 1, 0)
self.assert_unpack("P", "P;4", b'\x02\xef', 0, 2, 14, 15)
# self.assert_unpack("P", "P;4L", b'\x02\xef', 2, 10, 10, 0) # erroneous?
# erroneous?
# self.assert_unpack("P", "P;4L", b'\x02\xef', 2, 10, 10, 0)
self.assert_unpack("P", "P", 1, 1, 2, 3, 4)
self.assert_unpack("P", "P;R", 1, 128, 64, 192, 32)
@ -309,13 +312,25 @@ class TestLibUnpack(PillowTestCase):
"RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11))
self.assert_unpack(
"RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
self.assert_unpack(
"RGBA", "RGBAX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14))
self.assert_unpack(
"RGBA", "RGBAXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
self.assert_unpack(
"RGBA", "RGBa", 4,
(63, 127, 191, 4), (159, 191, 223, 8), (191, 212, 233, 12))
self.assert_unpack(
"RGBA", "RGBa",
b'\x01\x02\x03\x00\x10\x20\x30\xff',
(0, 0, 0, 0), (16, 32, 48, 255))
b'\x01\x02\x03\x00\x10\x20\x30\x7f\x10\x20\x30\xff',
(0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255))
self.assert_unpack(
"RGBA", "RGBaX",
b'\x01\x02\x03\x00-\x10\x20\x30\x7f-\x10\x20\x30\xff-',
(0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255))
self.assert_unpack(
"RGBA", "RGBaXX",
b'\x01\x02\x03\x00==\x10\x20\x30\x7f!!\x10\x20\x30\xff??',
(0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255))
self.assert_unpack(
"RGBA", "RGBa;16L", 8,
(63, 127, 191, 8), (159, 191, 223, 16), (191, 212, 233, 24))
@ -361,7 +376,8 @@ class TestLibUnpack(PillowTestCase):
self.assert_unpack(
"RGBA", "YCCA;P",
b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data
(0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17))
(0, 161, 0, 4), (255, 255, 255, 237),
(27, 158, 0, 206), (0, 118, 0, 17))
self.assert_unpack(
"RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
self.assert_unpack(
@ -413,7 +429,8 @@ class TestLibUnpack(PillowTestCase):
self.assert_unpack(
"RGBX", "YCC;P",
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
(127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X))
(127, 102, 0, X), (192, 227, 0, X),
(213, 255, 170, X), (98, 255, 133, X))
self.assert_unpack("RGBX", "R", 1,
(1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
self.assert_unpack("RGBX", "G", 1,

View File

@ -34,7 +34,6 @@ class TestNumpy(PillowTestCase):
i = Image.fromarray(a)
if list(i.getchannel(0).getdata()) != list(range(100)):
print("data mismatch for", dtype)
# print(dtype, list(i.getdata()))
return i
# Check supported 1-bit integer formats

View File

@ -26,7 +26,9 @@ class TestPdfParser(PillowTestCase):
def test_parsing(self):
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"),
b"Name#Hash")
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash", as_text=True), "Name#Hash")
self.assertEqual(PdfParser.interpret_name(
b"Name#23Hash", as_text=True
), "Name#Hash")
self.assertEqual(PdfParser.get_value(b"1 2 R ", 0),
(IndirectReference(1, 2), 5))
self.assertEqual(PdfParser.get_value(b"true[", 0), (True, 4))
@ -72,7 +74,9 @@ class TestPdfParser(PillowTestCase):
self.assertIsInstance(a, list)
self.assertEqual(len(a), 4)
self.assertEqual(a[0], PdfName("Name"))
s = PdfParser.get_value(b"<</Name (value) /Length 5>>\nstream\nabcde\nendstream<<...", 0)[0]
s = PdfParser.get_value(
b"<</Name (value) /Length 5>>\nstream\nabcde\nendstream<<...", 0
)[0]
self.assertIsInstance(s, PdfStream)
self.assertEqual(s.dictionary.Name, "value")
self.assertEqual(s.decode(), b"abcde")

View File

@ -1,5 +1,5 @@
from helper import unittest, PillowTestCase, hopper
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
from test_imageqt import PillowQPixmapTestCase
from PIL import ImageQt
@ -7,7 +7,6 @@ from PIL import ImageQt
class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
def roundtrip(self, expected):
PillowQtTestCase.setUp(self)
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb
self.assert_image_equal(result, expected.convert('RGB'))

View File

@ -13,19 +13,26 @@ if ImageQt.qt_is_installed:
QT_VERSION = 5
except (ImportError, RuntimeError):
try:
from PyQt4 import QtGui
from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
QT_VERSION = 4
from PySide2 import QtGui
from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, \
QApplication
QT_VERSION = 5
except (ImportError, RuntimeError):
from PySide import QtGui
from PySide.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
QT_VERSION = 4
try:
from PyQt4 import QtGui
from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, \
QApplication
QT_VERSION = 4
except (ImportError, RuntimeError):
from PySide import QtGui
from PySide.QtGui import QWidget, QHBoxLayout, QLabel, \
QApplication
QT_VERSION = 4
class TestToQImage(PillowQtTestCase, PillowTestCase):
def test_sanity(self):
PillowQtTestCase.setUp(self)
for mode in ('RGB', 'RGBA', 'L', 'P', '1'):
src = hopper(mode)
data = ImageQt.toqimage(src)
@ -61,8 +68,6 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
self.assert_image_equal(reloaded, src)
def test_segfault(self):
PillowQtTestCase.setUp(self)
app = QApplication([])
ex = Example()
assert(app) # Silence warning

View File

@ -1,5 +1,5 @@
from helper import unittest, PillowTestCase, hopper
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
from test_imageqt import PillowQPixmapTestCase
from PIL import ImageQt
@ -10,8 +10,6 @@ if ImageQt.qt_is_installed:
class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
def test_sanity(self):
PillowQtTestCase.setUp(self)
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
data = ImageQt.toqpixmap(hopper(mode))

View File

@ -1,10 +1,20 @@
#!/bin/bash
# install extra test images
rm -r test_images
rm -rf test_images
# Use SVN to just fetch a single git subdirectory
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images
# Use SVN to just fetch a single Git subdirectory
svn_checkout()
{
if [ ! -z $1 ]; then
echo ""
echo "Retrying svn checkout..."
echo ""
fi
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images
}
svn_checkout || svn_checkout retry || svn_checkout retry || svn_checkout retry
cp -r test_images/* ../Tests/images

View File

@ -171,7 +171,6 @@ The fields are used as follows:
stride defaults to 0.
**orientation**
Whether the first line in the image is the top line on the screen (1), or
the bottom line (-1). If omitted, the orientation defaults to 1.
@ -204,7 +203,7 @@ table describes some commonly used **raw modes**:
+-----------+-----------------------------------------------------------------+
| ``RGBX`` | 24-bit true colour, stored as (red, green, blue, pad). |
+-----------+-----------------------------------------------------------------+
| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, the |
| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, then|
| | all green pixels, finally all blue pixels). |
+-----------+-----------------------------------------------------------------+

View File

@ -171,19 +171,22 @@ Methods
:param outline: Color to use for the outline.
:param fill: Color to use for the fill.
.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0)
.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0, joint=None)
Draws a line between the coordinates in the **xy** list.
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
numeric values like ``[x, y, x, y, ...]``.
:param fill: Color to use for the line.
:param width: The line width, in pixels. Note that line
joins are not handled well, so wide polylines will not look good.
:param width: The line width, in pixels.
.. versionadded:: 1.1.5
.. note:: This option was broken until version 1.1.6.
:param joint: Joint type between a sequence of lines. It can be "curve",
for rounded edges, or None.
.. versionadded:: 5.3.0
.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None)
@ -250,9 +253,8 @@ Methods
:param align: If the text is passed on to multiline_text(),
"left", "center" or "right".
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right), 'ttb' (top to
bottom) or 'btt' (bottom to top). Requires
libraqm.
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
@ -280,9 +282,8 @@ Methods
:param spacing: The number of pixels between lines.
:param align: "left", "center" or "right".
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right), 'ttb' (top to
bottom) or 'btt' (bottom to top). Requires
libraqm.
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
@ -309,9 +310,8 @@ Methods
:param spacing: If the text is passed on to multiline_textsize(),
the number of pixels between lines.
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right), 'ttb' (top to
bottom) or 'btt' (bottom to top). Requires
libraqm.
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0
@ -336,9 +336,8 @@ Methods
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
:param spacing: The number of pixels between lines.
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right), 'ttb' (top to
bottom) or 'btt' (bottom to top). Requires
libraqm.
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0

View File

@ -67,9 +67,8 @@ Methods
.. versionadded:: 1.1.5
:param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right), 'ttb' (top to
bottom) or 'btt' (bottom to top). Requires
libraqm.
left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm.
.. versionadded:: 4.2.0

View File

@ -4,8 +4,8 @@
:py:mod:`ImageQt` Module
========================
The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5 or
PySide QImage objects from PIL images.
The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5, PySide or
PySide2 QImage objects from PIL images.
.. versionadded:: 1.1.6

View File

@ -1,5 +1,7 @@
# A monkey patch of the base distutils.ccompiler to use parallel builds
# Tested on 2.7, looks to be identical to 3.3.
# Only applied on Python < 3.5 because otherwise, it conflicts with Python's
# own newly-added support for parallel builds.
from __future__ import print_function
from multiprocessing import Pool, cpu_count
@ -77,4 +79,6 @@ def install():
"%s processes" % MAX_PROCS)
install()
# We monkeypatch only versions earlier than 3.5
if sys.version_info < (3, 5):
install()

View File

@ -205,6 +205,12 @@ class pil_build_ext(build_ext):
if self.debug:
global DEBUG
DEBUG = True
if sys.version_info >= (3, 5) and not self.parallel:
# For Python < 3.5, we monkeypatch distutils to have parallel
# builds. If --parallel (or -j) wasn't specified, we want to
# reproduce the same behavior as before, that is, auto-detect the
# number of jobs.
self.parallel = mp_compile.MAX_PROCS
for x in self.feature:
if getattr(self, 'disable_%s' % x):
setattr(self.feature, x, False)
@ -518,10 +524,7 @@ class pil_build_ext(build_ext):
if _find_include_file(self, 'tiff.h'):
if _find_library_file(self, "tiff"):
feature.tiff = "tiff"
if (sys.platform == "win32" and
_find_library_file(self, "libtiff")):
feature.tiff = "libtiff"
if (sys.platform == "darwin" and
if (sys.platform in ["win32", "darwin"] and
_find_library_file(self, "libtiff")):
feature.tiff = "libtiff"
@ -547,7 +550,6 @@ class pil_build_ext(build_ext):
break
if freetype_version:
feature.freetype = "freetype"
feature.freetype_version = freetype_version
if subdir:
_add_directory(self.compiler.include_dirs, subdir, 0)

View File

@ -110,20 +110,6 @@ class BdfFontFile(FontFile.FontFile):
if s.find(b"LogicalFontDescription") < 0:
comments.append(s[i+1:-1].decode('ascii'))
# font = props["FONT"].split("-")
# font[4] = bdf_slant[font[4].upper()]
# font[11] = bdf_spacing[font[11].upper()]
# ascent = int(props["FONT_ASCENT"])
# descent = int(props["FONT_DESCENT"])
# fontname = ";".join(font[1:])
# print("#", fontname)
# for i in comments:
# print("#", i)
while True:
c = bdf_char(fp)
if not c:

View File

@ -56,14 +56,6 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
m = s
# print("width", i8(s[0]))
# print("height", i8(s[1]))
# print("colors", i8(s[2]))
# print("reserved", i8(s[3]))
# print("hotspot x", i16(s[4:]))
# print("hotspot y", i16(s[6:]))
# print("bytes", i32(s[8:]))
# print("offset", i32(s[12:]))
if not m:
raise TypeError("No cursors were found")

View File

@ -82,7 +82,6 @@ def Ghostscript(tile, size, fp, scale=1):
# resolution is dependent on bbox and size
res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
# print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
import subprocess
import tempfile
@ -357,54 +356,49 @@ def _save(im, fp, filename, eps=1):
else:
raise ValueError("image mode is not supported")
class NoCloseStream(object):
def __init__(self, fp):
self.fp = fp
def __getattr__(self, name):
return getattr(self.fp, name)
def close(self):
pass
base_fp = fp
wrapped_fp = False
if fp != sys.stdout:
fp = NoCloseStream(fp)
if sys.version_info.major > 2:
fp = io.TextIOWrapper(fp, encoding='latin-1')
wrapped_fp = True
try:
if eps:
#
# write EPS header
fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
fp.write("%%Creator: PIL 0.1 EpsEncode\n")
# fp.write("%%CreationDate: %s"...)
fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
fp.write("%%Pages: 1\n")
fp.write("%%EndComments\n")
fp.write("%%Page: 1 1\n")
fp.write("%%ImageData: %d %d " % im.size)
fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
if eps:
#
# write EPS header
fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
fp.write("%%Creator: PIL 0.1 EpsEncode\n")
# fp.write("%%CreationDate: %s"...)
fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
fp.write("%%Pages: 1\n")
fp.write("%%EndComments\n")
fp.write("%%Page: 1 1\n")
fp.write("%%ImageData: %d %d " % im.size)
fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
# image header
fp.write("gsave\n")
fp.write("10 dict begin\n")
fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
fp.write("%d %d scale\n" % im.size)
fp.write("%d %d 8\n" % im.size) # <= bits
fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
fp.write("{ currentfile buf readhexstring pop } bind\n")
fp.write(operator[2] + "\n")
if hasattr(fp, "flush"):
fp.flush()
#
# image header
fp.write("gsave\n")
fp.write("10 dict begin\n")
fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
fp.write("%d %d scale\n" % im.size)
fp.write("%d %d 8\n" % im.size) # <= bits
fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
fp.write("{ currentfile buf readhexstring pop } bind\n")
fp.write(operator[2] + "\n")
if hasattr(fp, "flush"):
fp.flush()
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
fp.write("\n%%%%EndBinary\n")
fp.write("grestore end\n")
if hasattr(fp, "flush"):
fp.flush()
fp.write("\n%%%%EndBinary\n")
fp.write("grestore end\n")
if hasattr(fp, "flush"):
fp.flush()
finally:
if wrapped_fp:
fp.detach()
#
# --------------------------------------------------------------------

View File

@ -90,7 +90,6 @@ class FontFile(object):
x = xx
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
self.bitmap.paste(im.crop(src), s)
# print(chr(i), dst, s)
self.metrics[i] = d, dst, s
def save(self, filename):

View File

@ -114,8 +114,6 @@ class FpxImageFile(ImageFile.ImageFile):
if id in prop:
self.jpeg[i] = prop[id]
# print(len(self.jpeg), "tables loaded")
self._open_subimage(1, self.maxid)
def _open_subimage(self, index=1, subimage=0):
@ -143,8 +141,6 @@ class FpxImageFile(ImageFile.ImageFile):
offset = i32(s, 28)
length = i32(s, 32)
# print(size, self.mode, self.rawmode)
if size != self.size:
raise IOError("subimage mismatch")

View File

@ -61,7 +61,8 @@ class GdImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4, ("L", 0, 1))]
self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4,
("L", 0, 1))]
def open(fp, mode="r"):

View File

@ -443,7 +443,6 @@ def _getdecoder(mode, decoder_name, args, extra=()):
try:
# get decoder
decoder = getattr(core, decoder_name + "_decoder")
# print(decoder, mode, args + extra)
return decoder(mode, *args + extra)
except AttributeError:
raise IOError("decoder %s not available" % decoder_name)
@ -465,7 +464,6 @@ def _getencoder(mode, encoder_name, args, extra=()):
try:
# get encoder
encoder = getattr(core, encoder_name + "_encoder")
# print(encoder, mode, args + extra)
return encoder(mode, *args + extra)
except AttributeError:
raise IOError("encoder %s not available" % encoder_name)
@ -900,12 +898,28 @@ class Image(object):
if not mode or (mode == self.mode and not matrix):
return self.copy()
has_transparency = self.info.get('transparency') is not None
if matrix:
# matrix conversion
if mode not in ("L", "RGB"):
raise ValueError("illegal conversion")
im = self.im.convert_matrix(mode, matrix)
return self._new(im)
new = self._new(im)
if has_transparency and self.im.bands == 3:
transparency = new.info['transparency']
def convert_transparency(m, v):
v = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]*0.5
return max(0, min(255, int(v)))
if mode == "L":
transparency = convert_transparency(matrix, transparency)
elif len(mode) == 3:
transparency = tuple([
convert_transparency(matrix[i*4:i*4+4], transparency)
for i in range(0, len(transparency))
])
new.info['transparency'] = transparency
return new
if mode == "P" and self.mode == "RGBA":
return self.quantize(colors)
@ -913,8 +927,7 @@ class Image(object):
trns = None
delete_trns = False
# transparency handling
if "transparency" in self.info and \
self.info['transparency'] is not None:
if has_transparency:
if self.mode in ('L', 'RGB') and mode == 'RGBA':
# Use transparent conversion to promote from transparent
# color to an alpha channel.
@ -1104,12 +1117,9 @@ class Image(object):
x0, y0, x1, y1 = map(int, map(round, box))
if x1 < x0:
x1 = x0
if y1 < y0:
y1 = y0
absolute_values = (abs(x1 - x0), abs(y1 - y0))
_decompression_bomb_check((x1, y1))
_decompression_bomb_check(absolute_values)
return im.crop((x0, y0, x1, y1))
@ -1894,7 +1904,7 @@ class Image(object):
parameter should always be used.
:param params: Extra parameters to the image writer.
:returns: None
:exception KeyError: If the output format could not be determined
:exception ValueError: If the output format could not be determined
from the file name. Use the format option to solve this.
:exception IOError: If the file could not be written. The file
may have been created, and may contain partial data.
@ -2448,7 +2458,7 @@ def fromarray(obj, mode=None):
from PIL import Image
import numpy as np
im = Image.open('hopper.jpg')
a = numpy.asarray(im)
a = np.asarray(im)
Then this can be used to convert it to a Pillow image::
@ -2470,7 +2480,6 @@ def fromarray(obj, mode=None):
typekey = (1, 1) + shape[2:], arr['typestr']
mode, rawmode = _fromarray_typemap[typekey]
except KeyError:
# print(typekey)
raise TypeError("Cannot handle this data type")
else:
rawmode = mode

View File

@ -30,6 +30,7 @@
# See the README file for information on usage and redistribution.
#
import math
import numbers
from . import Image, ImageColor
@ -149,11 +150,64 @@ class ImageDraw(object):
if ink is not None and ink != fill:
self.draw.draw_ellipse(xy, ink, 0)
def line(self, xy, fill=None, width=0):
def line(self, xy, fill=None, width=0, joint=None):
"""Draw a line, or a connected sequence of line segments."""
ink, fill = self._getink(fill)
ink = self._getink(fill)[0]
if ink is not None:
self.draw.draw_lines(xy, ink, width)
if joint == "curve" and width > 4:
for i in range(1, len(xy)-1):
point = xy[i]
angles = [
math.degrees(math.atan2(
end[0] - start[0], start[1] - end[1]
)) % 360
for start, end in ((xy[i-1], point), (point, xy[i+1]))
]
if angles[0] == angles[1]:
# This is a straight line, so no joint is required
continue
def coord_at_angle(coord, angle):
x, y = coord
angle -= 90
distance = width/2 - 1
return tuple([
p +
(math.floor(p_d) if p_d > 0 else math.ceil(p_d))
for p, p_d in
((x, distance * math.cos(math.radians(angle))),
(y, distance * math.sin(math.radians(angle))))
])
flipped = ((angles[1] > angles[0] and
angles[1] - 180 > angles[0]) or
(angles[1] < angles[0] and
angles[1] + 180 > angles[0]))
coords = [
(point[0] - width/2 + 1, point[1] - width/2 + 1),
(point[0] + width/2 - 1, point[1] + width/2 - 1)
]
if flipped:
start, end = (angles[1] + 90, angles[0] + 90)
else:
start, end = (angles[0] - 90, angles[1] - 90)
self.pieslice(coords, start - 90, end - 90, fill)
if width > 8:
# Cover potential gaps between the line and the joint
if flipped:
gapCoords = [
coord_at_angle(point, angles[0]+90),
point,
coord_at_angle(point, angles[1]+90)
]
else:
gapCoords = [
coord_at_angle(point, angles[0]-90),
point,
coord_at_angle(point, angles[1]-90)
]
self.line(gapCoords, fill, width=3)
def shape(self, shape, fill=None, outline=None):
"""(Experimental) Draw a shape."""
@ -246,7 +300,7 @@ class ImageDraw(object):
elif align == "right":
left += (max_width - widths[idx])
else:
assert False, 'align must be "left", "center" or "right"'
raise ValueError('align must be "left", "center" or "right"')
self.text((left, top), line, fill, font, anchor,
direction=direction, features=features)
top += line_spacing
@ -341,6 +395,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
homogeneous, but similar, colors.
"""
# based on an implementation by Eric S. Raymond
# amended by yo1995 @20180806
pixel = image.load()
x, y = xy
try:
@ -350,39 +405,36 @@ def floodfill(image, xy, value, border=None, thresh=0):
pixel[x, y] = value
except (ValueError, IndexError):
return # seed point outside image
edge = [(x, y)]
if border is None:
while edge:
newedge = []
for (x, y) in edge:
for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
try:
p = pixel[s, t]
except IndexError:
pass
edge = {(x, y)}
full_edge = set() # use a set to keep record of current and previous edge pixels to reduce memory consumption
while edge:
new_edge = set()
for (x, y) in edge: # 4 adjacent method
for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
if (s, t) in full_edge:
continue # if already processed, skip
try:
p = pixel[s, t]
except (ValueError, IndexError):
pass
else:
full_edge.add((s, t))
if border is None:
fill = _color_diff(p, background) <= thresh
else:
if _color_diff(p, background) <= thresh:
pixel[s, t] = value
newedge.append((s, t))
edge = newedge
fill = p != value and p != border
if fill:
pixel[s, t] = value
new_edge.add((s, t))
full_edge = edge # discard pixels processed
edge = new_edge
def _color_diff(color1, color2):
"""
Uses 1-norm distance to calculate difference between two values.
"""
if isinstance(color2, tuple):
return sum([abs(color1[i]-color2[i]) for i in range(0, len(color2))])
else:
while edge:
newedge = []
for (x, y) in edge:
for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
try:
p = pixel[s, t]
except IndexError:
pass
else:
if p != value and p != border:
pixel[s, t] = value
newedge.append((s, t))
edge = newedge
def _color_diff(rgb1, rgb2):
"""
Uses 1-norm distance to calculate difference between two rgb values.
"""
return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2])
return abs(color1-color2)

View File

@ -30,7 +30,6 @@
from . import Image
from ._util import isPath
import io
import os
import sys
import struct

View File

@ -33,7 +33,14 @@ class MultibandFilter(Filter):
pass
class Kernel(MultibandFilter):
class BuiltinFilter(MultibandFilter):
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
return image.filter(*self.filterargs)
class Kernel(BuiltinFilter):
"""
Create a convolution kernel. The current version only
supports 3x3 and 5x5 integer and floating point kernels.
@ -60,16 +67,6 @@ class Kernel(MultibandFilter):
raise ValueError("not enough coefficients in kernel")
self.filterargs = size, scale, offset, kernel
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
return image.filter(*self.filterargs)
class BuiltinFilter(Kernel):
def __init__(self):
pass
class RankFilter(Filter):
"""

View File

@ -162,7 +162,8 @@ class FreeTypeFont(object):
size, offset = self.font.getsize(text, direction, features)
return (size[0] + offset[0], size[1] + offset[1])
def getsize_multiline(self, text, direction=None, spacing=4, features=None):
def getsize_multiline(self, text, direction=None,
spacing=4, features=None):
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.getsize('A')[1] + spacing

View File

@ -151,11 +151,6 @@ class LutBuilder(object):
patterns += self._pattern_permute(pattern, options, result)
# # Debugging
# for p, r in patterns:
# print(p, r)
# print('--')
# compile the patterns into regular expressions for speed
for i, pattern in enumerate(patterns):
p = pattern[0].replace('.', 'X').replace('X', '[01]')

View File

@ -221,6 +221,50 @@ def colorize(image, black, white, mid=None, blackpoint=0,
return _lut(image, red + green + blue)
def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
"""
Returns a sized and padded version of the image, expanded to fill the
requested aspect ratio and size.
:param image: The image to size and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:param color: The background color of the padded image.
:param centering: Control the position of the original image within the
padded version.
(0.5, 0.5) will keep the image centered
(0, 0) will keep the image aligned to the top left
(1, 1) will keep the image aligned to the bottom
right
:return: An image.
"""
im_ratio = image.width / image.height
dest_ratio = float(size[0]) / size[1]
if im_ratio == dest_ratio:
out = image.resize(size, resample=method)
else:
out = Image.new(image.mode, size, color)
if im_ratio > dest_ratio:
new_height = int(image.height / image.width * size[0])
if new_height != size[1]:
image = image.resize((size[0], new_height), resample=method)
y = int((size[1] - new_height) * max(0, min(centering[1], 1)))
out.paste(image, (0, y))
else:
new_width = int(image.width / image.height * size[1])
if new_width != size[0]:
image = image.resize((new_width, size[1]), resample=method)
x = int((size[0] - new_width) * max(0, min(centering[0], 1)))
out.paste(image, (x, 0))
return out
def crop(image, border=0):
"""
Remove border from image. The same amount of pixels are removed

View File

@ -23,16 +23,21 @@ import sys
qt_versions = [
['5', 'PyQt5'],
['side2', 'PySide2'],
['4', 'PyQt4'],
['side', 'PySide']
]
# If a version has already been imported, attempt it first
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules,
reverse=True)
for qt_version, qt_module in qt_versions:
try:
if qt_module == 'PyQt5':
from PyQt5.QtGui import QImage, qRgba, QPixmap
from PyQt5.QtCore import QBuffer, QIODevice
elif qt_module == 'PySide2':
from PySide2.QtGui import QImage, qRgba, QPixmap
from PySide2.QtCore import QBuffer, QIODevice
elif qt_module == 'PyQt4':
from PyQt4.QtGui import QImage, qRgba, QPixmap
from PyQt4.QtCore import QBuffer, QIODevice

View File

@ -32,13 +32,6 @@ if sys.version_info.major > 2:
else:
import Tkinter as tkinter
# required for pypy, which always has cffi installed
try:
from cffi import FFI
ffi = FFI()
except ImportError:
pass
from . import Image
from io import BytesIO
@ -192,7 +185,11 @@ class PhotoImage(object):
from . import _imagingtk
try:
if hasattr(tk, 'interp'):
# Pypy is using a ffi cdata element
# Required for PyPy, which always has CFFI installed
from cffi import FFI
ffi = FFI()
# PyPy is using an FFI CDATA element
# (Pdb) self.tk.interp
# <cdata 'Tcl_Interp *' 0x3061b50>
_imagingtk.tkinit(

View File

@ -103,8 +103,6 @@ class IptcImageFile(ImageFile.ImageFile):
else:
self.info[tag] = tagdata
# print(tag, self.info[tag])
# mode
layers = i8(self.info[(3, 60)][0])
component = i8(self.info[(3, 60)][1])

View File

@ -334,7 +334,6 @@ class JpegImageFile(ImageFile.ImageFile):
if i in MARKER:
name, description, handler = MARKER[i]
# print(hex(i), name, description)
if handler is not None:
handler(self, i)
if i == 0xFFDA: # start of scan

View File

@ -450,12 +450,12 @@ class PdfParser:
self.pages_ref = self.next_object_id(0)
self.rewrite_pages()
self.write_obj(self.root_ref,
Type=PdfName(b"Catalog"),
Pages=self.pages_ref)
Type=PdfName(b"Catalog"),
Pages=self.pages_ref)
self.write_obj(self.pages_ref,
Type=PdfName(b"Pages"),
Count=len(self.pages),
Kids=self.pages)
Type=PdfName(b"Pages"),
Count=len(self.pages),
Kids=self.pages)
return self.root_ref
def rewrite_pages(self):

View File

@ -74,7 +74,6 @@ def isSpiderHeader(t):
labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes
# print("labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt))
if labbyt != (labrec * lenbyt):
return 0
# looks like a valid header

View File

@ -20,6 +20,8 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, o8, o16le as o16
import warnings
__version__ = "0.3"
@ -53,7 +55,7 @@ class TgaImageFile(ImageFile.ImageFile):
# process header
s = self.fp.read(18)
idlen = i8(s[0])
id_len = i8(s[0])
colormaptype = i8(s[1])
imagetype = i8(s[2])
@ -100,8 +102,8 @@ class TgaImageFile(ImageFile.ImageFile):
if imagetype & 8:
self.info["compression"] = "tga_rle"
if idlen:
self.info["id_section"] = self.fp.read(idlen)
if id_len:
self.info["id_section"] = self.fp.read(id_len)
if colormaptype:
# read palette
@ -151,11 +153,23 @@ def _save(im, fp, filename):
except KeyError:
raise IOError("cannot write mode %s as TGA" % im.mode)
rle = im.encoderinfo.get("rle", False)
if "rle" in im.encoderinfo:
rle = im.encoderinfo["rle"]
else:
compression = im.encoderinfo.get("compression",
im.info.get("compression"))
rle = compression == "tga_rle"
if rle:
imagetype += 8
id_section = im.encoderinfo.get("id_section",
im.info.get("id_section", ""))
id_len = len(id_section)
if id_len > 255:
id_len = 255
id_section = id_section[:255]
warnings.warn("id_section has been trimmed to 255 characters")
if colormaptype:
colormapfirst, colormaplength, colormapentry = 0, 256, 24
else:
@ -166,11 +180,12 @@ def _save(im, fp, filename):
else:
flags = 0
orientation = im.info.get("orientation", -1)
orientation = im.encoderinfo.get("orientation",
im.info.get("orientation", -1))
if orientation > 0:
flags = flags | 0x20
fp.write(b"\000" +
fp.write(o8(id_len) +
o8(colormaptype) +
o8(imagetype) +
o16(colormapfirst) +
@ -183,6 +198,9 @@ def _save(im, fp, filename):
o8(bits) +
o8(flags))
if id_section:
fp.write(id_section)
if colormaptype:
fp.write(im.im.getpalette("RGB", "BGR"))

View File

@ -207,8 +207,16 @@ OPEN_INFO = {
(MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"),
(II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
(MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
(II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"),
(MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"),
(II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"),
(MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"),
(II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
(MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
(II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"),
(MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"),
(II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"),
(MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"),
(II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
(MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
@ -253,8 +261,8 @@ OPEN_INFO = {
(MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
(II, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
(MM, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
(II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
(MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
(II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXX"),
(MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXX"),
(II, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
(MM, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
@ -567,6 +575,9 @@ class ImageFileDirectory_v2(MutableMapping):
if self.tagtype[tag] == 7 and py3:
values = [value.encode("ascii", 'replace') if isinstance(
value, str) else value]
elif self.tagtype[tag] == 5:
values = [float(v) if isinstance(v, int) else v
for v in values]
values = tuple(info.cvt_enum(value) for value in values)
@ -1254,9 +1265,6 @@ class TiffImageFile(ImageFile.ImageFile):
h = self.tag_v2.get(ROWSPERSTRIP, ysize)
w = self.size[0]
if READ_LIBTIFF or self._compression != 'raw':
# if DEBUG:
# print("Activating g4 compression for whole file")
# Decoder expects entire file as one tile.
# There's a buffer size limit in load (64k)
# so large g4 images will fail if we use that
@ -1529,7 +1537,6 @@ def _save(im, fp, filename):
rawmode = 'I;16N'
a = (rawmode, compression, _fp, filename, atts)
# print(im.mode, compression, a, im.encoderconfig)
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
e.setimage(im.im, (0, 0)+im.size)
while True:

View File

@ -122,7 +122,7 @@ TAGS_V2 = {
316: ("HostComputer", ASCII, 1),
317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}),
318: ("WhitePoint", RATIONAL, 2),
319: ("PrimaryChromaticities", SHORT, 6),
319: ("PrimaryChromaticities", RATIONAL, 6),
320: ("ColorMap", SHORT, 0),
321: ("HalftoneHints", SHORT, 2),
@ -159,7 +159,7 @@ TAGS_V2 = {
529: ("YCbCrCoefficients", RATIONAL, 3),
530: ("YCbCrSubSampling", SHORT, 2),
531: ("YCbCrPositioning", SHORT, 1),
532: ("ReferenceBlackWhite", LONG, 0),
532: ("ReferenceBlackWhite", RATIONAL, 6),
700: ('XMP', BYTE, 1),

View File

@ -5,6 +5,7 @@ from io import BytesIO
_VALID_WEBP_MODES = {
"RGBX": True,
"RGBA": True,
"RGB": True,
}
_VALID_WEBP_LEGACY_MODES = {
@ -63,7 +64,8 @@ class WebPImageFile(ImageFile.ImageFile):
bgcolor & 0xFF
self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
self._n_frames = frame_count
self.mode = mode
self.mode = 'RGB' if mode == 'RGBX' else mode
self.rawmode = mode
self.tile = []
# Attempt to read ICC / EXIF / XMP chunks from file
@ -153,8 +155,10 @@ class WebPImageFile(ImageFile.ImageFile):
self.__loaded = self.__logical_frame
# Set tile
if self.fp:
self.fp.close()
self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
return super(WebPImageFile, self).load()
@ -240,16 +244,23 @@ def _save_all(im, fp, filename):
# Make sure image mode is supported
frame = ims
rawmode = ims.mode
if ims.mode not in _VALID_WEBP_MODES:
alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()
frame = ims.convert('RGBA' if alpha else 'RGBX')
alpha = 'A' in ims.mode or 'a' in ims.mode \
or (ims.mode == 'P' and 'A' in ims.im.getpalettemode())
rawmode = 'RGBA' if alpha else 'RGB'
frame = ims.convert(rawmode)
if rawmode == 'RGB':
# For faster conversion, use RGBX
rawmode = 'RGBX'
# Append the frame to the animation encoder
enc.add(
frame.tobytes(),
frame.tobytes('raw', rawmode),
timestamp,
frame.size[0], frame.size[1],
frame.mode,
rawmode,
lossless,
quality,
method
@ -288,7 +299,8 @@ def _save(im, fp, filename):
xmp = im.encoderinfo.get("xmp", "")
if im.mode not in _VALID_WEBP_LEGACY_MODES:
alpha = im.mode == 'P' and 'A' in im.im.getpalettemode()
alpha = 'A' in im.mode or 'a' in im.mode \
or (im.mode == 'P' and 'A' in im.im.getpalettemode())
im = im.convert('RGBA' if alpha else 'RGB')
data = _webp.WebPEncode(

View File

@ -109,8 +109,6 @@ class WmfStubImageFile(ImageFile.StubImageFile):
self.info["dpi"] = 72
# print(self.mode, self.size, self.info)
# sanity check (standard metafile header)
if s[22:26] != b"\x01\x00\t\x00":
raise SyntaxError("Unsupported WMF file format")

View File

@ -1,4 +1,4 @@
"""Pillow {} (Fork of the Python Imaging Library)
"""Pillow (Fork of the Python Imaging Library)
Pillow is the friendly PIL fork by Alex Clark and Contributors.
https://github.com/python-pillow/Pillow/
@ -24,8 +24,6 @@ PILLOW_VERSION = __version__ = _version.__version__
del _version
__doc__ = __doc__.format(__version__) # include version in docstring
_plugins = ['BlpImagePlugin',
'BmpImagePlugin',

View File

@ -1998,6 +1998,7 @@ _getextrema(ImagingObject* self, PyObject* args)
UINT8 u[2];
INT32 i[2];
FLOAT32 f[2];
UINT16 s[2];
} extrema;
int status;
@ -2013,6 +2014,10 @@ _getextrema(ImagingObject* self, PyObject* args)
return Py_BuildValue("ii", extrema.i[0], extrema.i[1]);
case IMAGING_TYPE_FLOAT32:
return Py_BuildValue("dd", extrema.f[0], extrema.f[1]);
case IMAGING_TYPE_SPECIAL:
if (strcmp(self->image->mode, "I;16") == 0) {
return Py_BuildValue("HH", extrema.s[0], extrema.s[1]);
}
}
Py_INCREF(Py_None);
@ -2697,22 +2702,6 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
_draw_line(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
int ink;
if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink))
return NULL;
if (ImagingDrawLine(self->image->image, x0, y0, x1, y1,
&ink, self->blend) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
_draw_lines(ImagingDrawObject* self, PyObject* args)
{
@ -2766,21 +2755,6 @@ _draw_lines(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
static PyObject*
_draw_point(ImagingDrawObject* self, PyObject* args)
{
int x, y;
int ink;
if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink))
return NULL;
if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
_draw_points(ImagingDrawObject* self, PyObject* args)
{
@ -2961,14 +2935,12 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
static struct PyMethodDef _draw_methods[] = {
#ifdef WITH_IMAGEDRAW
/* Graphics (ImageDraw) */
{"draw_line", (PyCFunction)_draw_line, 1},
{"draw_lines", (PyCFunction)_draw_lines, 1},
#ifdef WITH_ARROW
{"draw_outline", (PyCFunction)_draw_outline, 1},
#endif
{"draw_polygon", (PyCFunction)_draw_polygon, 1},
{"draw_rectangle", (PyCFunction)_draw_rectangle, 1},
{"draw_point", (PyCFunction)_draw_point, 1},
{"draw_points", (PyCFunction)_draw_points, 1},
{"draw_arc", (PyCFunction)_draw_arc, 1},
{"draw_bitmap", (PyCFunction)_draw_bitmap, 1},

View File

@ -674,47 +674,6 @@ font_getsize(FontObject* self, PyObject* args)
);
}
static PyObject*
font_getabc(FontObject* self, PyObject* args)
{
FT_ULong ch;
FT_Face face;
double a, b, c;
/* calculate ABC values for a given string */
PyObject* string;
if (!PyArg_ParseTuple(args, "O:getabc", &string))
return NULL;
#if PY_VERSION_HEX >= 0x03000000
if (!PyUnicode_Check(string)) {
#else
if (!PyUnicode_Check(string) && !PyString_Check(string)) {
#endif
PyErr_SetString(PyExc_TypeError, "expected string");
return NULL;
}
if (font_getchar(string, 0, &ch)) {
int index, error;
face = self->face;
index = FT_Get_Char_Index(face, ch);
/* 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;
b = face->glyph->metrics.width / 64.0;
c = (face->glyph->metrics.horiAdvance -
face->glyph->metrics.horiBearingX -
face->glyph->metrics.width) / 64.0;
} else
a = b = c = 0.0;
return Py_BuildValue("ddd", a, b, c);
}
static PyObject*
font_render(FontObject* self, PyObject* args)
{
@ -854,7 +813,6 @@ font_dealloc(FontObject* self)
static PyMethodDef font_methods[] = {
{"render", (PyCFunction) font_render, METH_VARARGS},
{"getsize", (PyCFunction) font_getsize, METH_VARARGS},
{"getabc", (PyCFunction) font_getabc, METH_VARARGS},
{NULL, NULL}
};

View File

@ -216,6 +216,8 @@ PyObject* _anim_encoder_add(PyObject* self, PyObject* args)
WebPPictureImportRGBA(frame, rgb, 4 * width);
} else if (strcmp(mode, "RGBX")==0) {
WebPPictureImportRGBX(frame, rgb, 4 * width);
} else {
WebPPictureImportRGB(frame, rgb, 3 * width);
}
// Add the frame to the encoder

View File

@ -797,6 +797,48 @@ unpackRGBa(UINT8* _out, const UINT8* in, int pixels)
}
}
static void
unpackRGBaskip1(UINT8* _out, const UINT8* in, int pixels)
{
int i;
UINT32* out = (UINT32*) _out;
/* premultiplied RGBA */
for (i = 0; i < pixels; i++) {
int a = in[3];
if ( ! a) {
out[i] = 0;
} else if (a == 255) {
out[i] = MAKE_UINT32(in[0], in[1], in[2], a);
} else {
out[i] = MAKE_UINT32(CLIP8(in[0] * 255 / a),
CLIP8(in[1] * 255 / a),
CLIP8(in[2] * 255 / a), a);
}
in += 5;
}
}
static void
unpackRGBaskip2(UINT8* _out, const UINT8* in, int pixels)
{
int i;
UINT32* out = (UINT32*) _out;
/* premultiplied RGBA */
for (i = 0; i < pixels; i++) {
int a = in[3];
if ( ! a) {
out[i] = 0;
} else if (a == 255) {
out[i] = MAKE_UINT32(in[0], in[1], in[2], a);
} else {
out[i] = MAKE_UINT32(CLIP8(in[0] * 255 / a),
CLIP8(in[1] * 255 / a),
CLIP8(in[2] * 255 / a), a);
}
in += 6;
}
}
static void
unpackBGRa(UINT8* _out, const UINT8* in, int pixels)
{
@ -1301,7 +1343,11 @@ static struct {
{"RGBA", "LA", 16, unpackRGBALA},
{"RGBA", "LA;16B", 32, unpackRGBALA16B},
{"RGBA", "RGBA", 32, copy4},
{"RGBA", "RGBAX", 40, copy4skip1},
{"RGBA", "RGBAXX", 48, copy4skip2},
{"RGBA", "RGBa", 32, unpackRGBa},
{"RGBA", "RGBaX", 40, unpackRGBaskip1},
{"RGBA", "RGBaXX", 48, unpackRGBaskip2},
{"RGBA", "RGBa;16L", 64, unpackRGBa16L},
{"RGBA", "RGBa;16B", 64, unpackRGBa16B},
{"RGBA", "BGRa", 32, unpackBGRa},

View File

@ -1,5 +1,6 @@
#!/bin/sh
mkdir /var/cache/pacman/pkg
pacman -S --noconfirm mingw32/mingw-w64-i686-python3-pip \
mingw32/mingw-w64-i686-python3-setuptools \
mingw32/mingw-w64-i686-python2-pip \