Merge branch 'master' of https://github.com/python-pillow/Pillow into jpegdpi

This commit is contained in:
Arjen Nienhuis 2016-08-27 18:50:46 +02:00
commit d5a99bf685
44 changed files with 662 additions and 140 deletions

View File

@ -4,12 +4,63 @@ Changelog (Pillow)
3.4.0 (unreleased) 3.4.0 (unreleased)
------------------ ------------------
- Resampling lookups, trailing empty coefficients, precision #2008
[homm]
- Add (un)packing between RGBA and BGRa #2057
[arjennienhuis]
- Added return for J2k (and fpx) Load to return a pixel access object #2061
[wiredfool]
- Skip failing numpy tests on Pypy <= 5.3.1 #2090
[arjennienhuis]
- Show warning when trying to save RGBA image as JPEG #2010
[homm]
- Respect pixel centers during transform #2022
[homm]
- TOC for supported file formats #2056
[polarize]
- Fix conversion of bit images to numpy arrays Fixes #350, #2058
[matthew-brett]
- Add ImageOps.scale to expand or contract a PIL image by a factor #2011
[vlmath]
- Flake8 fixes #2050
[hugovk]
- Updated freetype to 2.6.5 on Appveyor builds #2035
[radarhere]
- PCX encoder fixes #2023, pr #2041
[homm]
- Docs: Windows console prompts are > #2031
[techtonik]
- Expose Pillow package version as PIL.__version__ #2027
[techtonik]
- Add Box and Hamming filters for resampling #1959 - Add Box and Hamming filters for resampling #1959
[homm] [homm]
- Retain a reference to core image object in PyAccess #2009 - Retain a reference to core image object in PyAccess #2009
[homm] [homm]
3.3.1 (2016-08-18)
------------------
- Fix C90 compilation error for Tcl / Tk rewrite #2033
[matthew-brett]
- Fix image loading when rotating by 0 deg #2052
[homm]
3.3.0 (2016-07-01) 3.3.0 (2016-07-01)
------------------ ------------------

View File

@ -216,7 +216,7 @@ class FpxImageFile(ImageFile.ImageFile):
self.fp = self.ole.openstream(self.stream[:2] + self.fp = self.ole.openstream(self.stream[:2] +
["Subimage 0000 Data"]) ["Subimage 0000 Data"])
ImageFile.ImageFile.load(self) return ImageFile.ImageFile.load(self)
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -246,7 +246,7 @@ else:
_MODE_CONV = { _MODE_CONV = {
# official modes # official modes
"1": ('|b1', None), # broken "1": ('|b1', None), # Bits need to be extended to bytes
"L": ('|u1', None), "L": ('|u1', None),
"LA": ('|u1', 2), "LA": ('|u1', 2),
"I": (_ENDIAN + 'i4', None), "I": (_ENDIAN + 'i4', None),
@ -615,17 +615,21 @@ class Image(object):
self.save(b, 'PNG') self.save(b, 'PNG')
return b.getvalue() return b.getvalue()
def __getattr__(self, name): @property
if name == "__array_interface__": def __array_interface__(self):
# numpy array interface support # numpy array interface support
new = {} new = {}
shape, typestr = _conv_type_shape(self) shape, typestr = _conv_type_shape(self)
new['shape'] = shape new['shape'] = shape
new['typestr'] = typestr new['typestr'] = typestr
new['data'] = self.tobytes()
new['version'] = 3 new['version'] = 3
if self.mode == '1':
# Binary images need to be extended from bits to bytes
# See: https://github.com/python-pillow/Pillow/issues/350
new['data'] = self.tobytes('raw', 'L')
else:
new['data'] = self.tobytes()
return new return new
raise AttributeError(name)
def __getstate__(self): def __getstate__(self):
return [ return [
@ -1877,7 +1881,7 @@ class Image(object):
xs = float(x1 - x0) / w xs = float(x1 - x0) / w
ys = float(y1 - y0) / h ys = float(y1 - y0) / h
method = AFFINE method = AFFINE
data = (xs, 0, x0 + xs/2, 0, ys, y0 + ys/2) data = (xs, 0, x0, 0, ys, y0)
elif method == PERSPECTIVE: elif method == PERSPECTIVE:
data = data[0:8] data = data[0:8]

View File

@ -178,6 +178,27 @@ def crop(image, border=0):
) )
def scale(image, factor, resample=Image.NEAREST):
"""
Returns a rescaled image by a specific factor given in parameter.
A factor greater than 1 expands the image, between 0 and 1 contracts the
image.
:param factor: The expansion factor, as a float.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.resize function.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if factor == 1:
return image.copy()
elif factor <= 0:
raise ValueError("the factor must be greater than 0")
else:
size = (int(round(factor * image.width)),
int(round(factor * image.height)))
return image.resize(size, resample)
def deform(image, deformer, resample=Image.BILINEAR): def deform(image, deformer, resample=Image.BILINEAR):
""" """
Deform the image. Deform the image.

View File

@ -207,7 +207,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4]) t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
ImageFile.ImageFile.load(self) return ImageFile.ImageFile.load(self)
def _accept(prefix): def _accept(prefix):

View File

@ -583,6 +583,14 @@ def _save(im, fp, filename):
except KeyError: except KeyError:
raise IOError("cannot write mode %s as JPEG" % im.mode) raise IOError("cannot write mode %s as JPEG" % im.mode)
if im.mode == 'RGBA':
warnings.warn(
'You are saving RGBA image as JPEG. The alpha channel will be '
'discarded. This conversion is deprecated and will be disabled '
'in Pillow 3.7. Please, convert the image to RGB explicitly.',
DeprecationWarning
)
info = im.encoderinfo info = im.encoderinfo
dpi = info.get("dpi", (0, 0)) dpi = info.get("dpi", (0, 0))

View File

@ -14,6 +14,8 @@
VERSION = '1.1.7' # PIL version VERSION = '1.1.7' # PIL version
PILLOW_VERSION = '3.4.0.dev0' # Pillow PILLOW_VERSION = '3.4.0.dev0' # Pillow
__version__ = PILLOW_VERSION
_plugins = ['BmpImagePlugin', _plugins = ['BmpImagePlugin',
'BufrStubImagePlugin', 'BufrStubImagePlugin',
'CurImagePlugin', 'CurImagePlugin',

View File

@ -1,6 +1,7 @@
from PIL import Image from PIL import Image
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
class TestJ2kEncodeOverflow(PillowTestCase): class TestJ2kEncodeOverflow(PillowTestCase):
def test_j2k_overflow(self): def test_j2k_overflow(self):

View File

@ -99,7 +99,7 @@ class PillowTestCase(unittest.TestCase):
" average pixel value difference %.4f > epsilon %.4f" % ( " average pixel value difference %.4f > epsilon %.4f" % (
ave_diff, epsilon)) ave_diff, epsilon))
def assert_warning(self, warn_class, func): def assert_warning(self, warn_class, func, *args, **kwargs):
import warnings import warnings
result = None result = None
@ -108,7 +108,7 @@ class PillowTestCase(unittest.TestCase):
warnings.simplefilter("always") warnings.simplefilter("always")
# Hopefully trigger a warning. # Hopefully trigger a warning.
result = func() result = func(*args, **kwargs)
# Verify some things. # Verify some things.
self.assertGreaterEqual(len(w), 1) self.assertGreaterEqual(len(w), 1)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -77,6 +77,5 @@ class TestFileBmp(PillowTestCase):
self.assert_image_equal(im, target) self.assert_image_equal(im, target)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -450,6 +450,25 @@ class TestFileJpeg(PillowTestCase):
# Assert # Assert
self.assertEqual(im.format, "JPEG") self.assertEqual(im.format, "JPEG")
def test_save_correct_modes(self):
out = BytesIO()
for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']:
img = Image.new(mode, (20, 20))
img.save(out, "JPEG")
def test_save_wrong_modes(self):
out = BytesIO()
for mode in ['LA', 'La', 'RGBa', 'P']:
img = Image.new(mode, (20, 20))
self.assertRaises(IOError, img.save, out, "JPEG")
def test_save_modes_with_warnings(self):
# ref https://github.com/python-pillow/Pillow/issues/2005
out = BytesIO()
for mode in ['RGBA']:
img = Image.new(mode, (20, 20))
self.assert_warning(DeprecationWarning, img.save, out, "JPEG")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -34,7 +34,8 @@ class TestFileJpeg2k(PillowTestCase):
self.assertRegexpMatches(Image.core.jp2klib_version, '\d+\.\d+\.\d+$') self.assertRegexpMatches(Image.core.jp2klib_version, '\d+\.\d+\.\d+$')
im = Image.open('Tests/images/test-card-lossless.jp2') im = Image.open('Tests/images/test-card-lossless.jp2')
im.load() px = im.load()
self.assertEqual(px[0,0], (0, 0, 0))
self.assertEqual(im.mode, 'RGB') self.assertEqual(im.mode, 'RGB')
self.assertEqual(im.size, (640, 480)) self.assertEqual(im.size, (640, 480))
self.assertEqual(im.format, 'JPEG2000') self.assertEqual(im.format, 'JPEG2000')

View File

@ -170,7 +170,8 @@ class TestFileLibTiff(LibTiffTestCase):
'RowsPerStrip', 'RowsPerStrip',
'StripOffsets'] 'StripOffsets']
for field in requested_fields: for field in requested_fields:
self.assertTrue(field in reloaded, "%s not in metadata" % field) self.assertTrue(field in reloaded,
"%s not in metadata" % field)
def test_additional_metadata(self): def test_additional_metadata(self):
# these should not crash. Seriously dummy data, most of it doesn't make # these should not crash. Seriously dummy data, most of it doesn't make
@ -183,7 +184,8 @@ class TestFileLibTiff(LibTiffTestCase):
in TiffTags.LIBTIFF_CORE] in TiffTags.LIBTIFF_CORE]
if info.type is not None) if info.type is not None)
# Exclude ones that have special meaning that we're already testing them # Exclude ones that have special meaning
# that we're already testing them
im = Image.open('Tests/images/hopper_g4.tif') im = Image.open('Tests/images/hopper_g4.tif')
for tag in im.tag_v2.keys(): for tag in im.tag_v2.keys():
try: try:
@ -504,6 +506,5 @@ class TestFileLibTiff(LibTiffTestCase):
im.save(outfile) im.save(outfile)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image, PcxImagePlugin from PIL import Image, ImageFile, PcxImagePlugin
class TestFilePcx(PillowTestCase): class TestFilePcx(PillowTestCase):
@ -46,6 +46,84 @@ class TestFilePcx(PillowTestCase):
# Make sure all pixels are either 0 or 255. # Make sure all pixels are either 0 or 255.
self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144) self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144)
def test_1px_width(self):
im = Image.new('L', (1, 256))
px = im.load()
for y in range(256):
px[0, y] = y
self._roundtrip(im)
def test_large_count(self):
im = Image.new('L', (256, 1))
px = im.load()
for x in range(256):
px[x, 0] = x // 67 * 67
self._roundtrip(im)
def _test_buffer_overflow(self, im, size=1024):
_last = ImageFile.MAXBLOCK
ImageFile.MAXBLOCK = size
try:
self._roundtrip(im)
finally:
ImageFile.MAXBLOCK = _last
def test_break_in_count_overflow(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_one_in_loop(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
self._test_buffer_overflow(im)
def test_break_many_in_loop(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(4):
for x in range(256):
px[x, y] = x % 128
for x in range(8):
px[x, 4] = 16
self._test_buffer_overflow(im)
def test_break_one_at_end(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
px[0, 3] = 128 + 64
self._test_buffer_overflow(im)
def test_break_many_at_end(self):
im = Image.new('L', (256, 5))
px = im.load()
for y in range(5):
for x in range(256):
px[x, y] = x % 128
for x in range(4):
px[x * 2, 3] = 128 + 64
px[x + 256 - 4, 3] = 0
self._test_buffer_overflow(im)
def test_break_padding(self):
im = Image.new('L', (257, 5))
px = im.load()
for y in range(5):
for x in range(257):
px[x, y] = x % 128
for x in range(5):
px[x, 3] = 0
self._test_buffer_overflow(im)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -347,7 +347,6 @@ class TestFilePng(PillowTestCase):
finally: finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False ImageFile.LOAD_TRUNCATED_IMAGES = False
def test_roundtrip_dpi(self): def test_roundtrip_dpi(self):
# Check dpi roundtripping # Check dpi roundtripping

View File

@ -105,6 +105,7 @@ class TestCffiPutPixel(TestImagePutPixel):
def setUp(self): def setUp(self):
try: try:
import cffi import cffi
assert cffi # silence warning
except ImportError: except ImportError:
self.skipTest("No cffi") self.skipTest("No cffi")
@ -115,6 +116,7 @@ class TestCffiGetPixel(TestImageGetPixel):
def setUp(self): def setUp(self):
try: try:
import cffi import cffi
assert cffi # silence warning
except ImportError: except ImportError:
self.skipTest("No cffi") self.skipTest("No cffi")
@ -125,6 +127,7 @@ class TestCffi(AccessTest):
def setUp(self): def setUp(self):
try: try:
import cffi import cffi
assert cffi # silence warning
except ImportError: except ImportError:
self.skipTest("No cffi") self.skipTest("No cffi")
@ -229,7 +232,7 @@ class TestCffi(AccessTest):
# Do not save references to the image, only to the access object # Do not save references to the image, only to the access object
px = Image.new('L', (size, 1), 0).load() px = Image.new('L', (size, 1), 0).load()
for i in range(size): for i in range(size):
# pixels can contain garbarge if image is released # pixels can contain garbage if image is released
self.assertEqual(px[i, 0], 0) self.assertEqual(px[i, 0], 0)

View File

@ -25,13 +25,26 @@ class TestImageArray(PillowTestCase):
self.assertEqual(test("RGBX"), (3, (100, 128, 4), '|u1', 51200)) self.assertEqual(test("RGBX"), (3, (100, 128, 4), '|u1', 51200))
def test_fromarray(self): def test_fromarray(self):
class Wrapper(object):
""" Class with API matching Image.fromarray """
def __init__(self, img, arr_params):
self.img = img
self.__array_interface__ = arr_params
def tobytes(self):
return self.img.tobytes()
def test(mode): def test(mode):
i = im.convert(mode) i = im.convert(mode)
a = i.__array_interface__ a = i.__array_interface__
a["strides"] = 1 # pretend it's non-contigous a["strides"] = 1 # pretend it's non-contigous
i.__array_interface__ = a # patch in new version of attribute # Make wrapper instance for image, new array interface
out = Image.fromarray(i) wrapped = Wrapper(i, a)
out = Image.fromarray(wrapped)
return out.mode, out.size, list(i.getdata()) == list(out.getdata()) return out.mode, out.size, list(i.getdata()) == list(out.getdata())
# self.assertEqual(test("1"), ("1", (128, 100), True)) # self.assertEqual(test("1"), ("1", (128, 100), True))
self.assertEqual(test("L"), ("L", (128, 100), True)) self.assertEqual(test("L"), ("L", (128, 100), True))
self.assertEqual(test("I"), ("I", (128, 100), True)) self.assertEqual(test("I"), ("I", (128, 100), True))

View File

@ -20,13 +20,13 @@ class TestImageGetData(PillowTestCase):
return data[0], len(data), len(list(data)) return data[0], len(data), len(list(data))
self.assertEqual(getdata("1"), (0, 960, 960)) self.assertEqual(getdata("1"), (0, 960, 960))
self.assertEqual(getdata("L"), (25, 960, 960)) self.assertEqual(getdata("L"), (16, 960, 960))
self.assertEqual(getdata("I"), (25, 960, 960)) self.assertEqual(getdata("I"), (16, 960, 960))
self.assertEqual(getdata("F"), (25.0, 960, 960)) self.assertEqual(getdata("F"), (16.0, 960, 960))
self.assertEqual(getdata("RGB"), (((20, 20, 70), 960, 960))) self.assertEqual(getdata("RGB"), (((11, 13, 52), 960, 960)))
self.assertEqual(getdata("RGBA"), ((20, 20, 70, 255), 960, 960)) self.assertEqual(getdata("RGBA"), ((11, 13, 52, 255), 960, 960))
self.assertEqual(getdata("CMYK"), ((235, 235, 185, 0), 960, 960)) self.assertEqual(getdata("CMYK"), ((244, 242, 203, 0), 960, 960))
self.assertEqual(getdata("YCbCr"), ((25, 153, 123), 960, 960)) self.assertEqual(getdata("YCbCr"), ((16, 147, 123), 960, 960))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -320,5 +320,22 @@ class CoreResamplePassesTest(PillowTestCase):
self.assertEqual(Image.core.getcount(), count + 2) self.assertEqual(Image.core.getcount(), count + 2)
class CoreResampleCoefficientsTest(PillowTestCase):
def test_reduce(self):
test_color = 254
# print ''
for size in range(400000, 400010, 2):
# print '\r', size,
i = Image.new('L', (size, 1), 0)
draw = ImageDraw.Draw(i)
draw.rectangle((0, 0, i.size[0] // 2 - 1, 0), test_color)
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 '\r>', size, test_color // 2, px[2, 0]
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,3 +1,5 @@
import math
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image
@ -98,7 +100,7 @@ class TestImageTransform(PillowTestCase):
def test_alpha_premult_resize(self): def test_alpha_premult_resize(self):
def op(im, sz): def op(im, sz):
return im.resize(sz, Image.LINEAR) return im.resize(sz, Image.BILINEAR)
self._test_alpha_premult(op) self._test_alpha_premult(op)
@ -139,5 +141,117 @@ class TestImageTransform(PillowTestCase):
self.test_mesh() self.test_mesh()
class TestImageTransformAffine(PillowTestCase):
transform = Image.AFFINE
def _test_image(self):
im = hopper('RGB')
return im.crop((10, 20, im.width - 10, im.height - 20))
def _test_rotate(self, deg, transpose):
im = self._test_image()
angle = - math.radians(deg)
matrix = [
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0,
0, 0]
matrix[2] = (1 - matrix[0] - matrix[1]) * im.width / 2
matrix[5] = (1 - matrix[3] - matrix[4]) * im.height / 2
if transpose is not None:
transposed = im.transpose(transpose)
else:
transposed = im
for resample in [Image.NEAREST, Image.BILINEAR, Image.BICUBIC]:
transformed = im.transform(transposed.size, self.transform,
matrix, resample)
self.assert_image_equal(transposed, transformed)
def test_rotate_0_deg(self):
self._test_rotate(0, None)
def test_rotate_90_deg(self):
self._test_rotate(90, Image.ROTATE_90)
def test_rotate_180_deg(self):
self._test_rotate(180, Image.ROTATE_180)
def test_rotate_270_deg(self):
self._test_rotate(270, Image.ROTATE_270)
def _test_resize(self, scale, epsilonscale):
im = self._test_image()
size_up = int(round(im.width * scale)), int(round(im.height * scale))
matrix_up = [
1 / scale, 0, 0,
0, 1 / scale, 0,
0, 0]
matrix_down = [
scale, 0, 0,
0, scale, 0,
0, 0]
for resample, epsilon in [(Image.NEAREST, 0),
(Image.BILINEAR, 2), (Image.BICUBIC, 1)]:
transformed = im.transform(
size_up, self.transform, matrix_up, resample)
transformed = transformed.transform(
im.size, self.transform, matrix_down, resample)
self.assert_image_similar(transformed, im, epsilon * epsilonscale)
def test_resize_1_1x(self):
self._test_resize(1.1, 6.9)
def test_resize_1_5x(self):
self._test_resize(1.5, 5.5)
def test_resize_2_0x(self):
self._test_resize(2.0, 5.5)
def test_resize_2_3x(self):
self._test_resize(2.3, 3.7)
def test_resize_2_5x(self):
self._test_resize(2.5, 3.7)
def _test_translate(self, x, y, epsilonscale):
im = self._test_image()
size_up = int(round(im.width + x)), int(round(im.height + y))
matrix_up = [
1, 0, -x,
0, 1, -y,
0, 0]
matrix_down = [
1, 0, x,
0, 1, y,
0, 0]
for resample, epsilon in [(Image.NEAREST, 0),
(Image.BILINEAR, 1.5), (Image.BICUBIC, 1)]:
transformed = im.transform(
size_up, self.transform, matrix_up, resample)
transformed = transformed.transform(
im.size, self.transform, matrix_down, resample)
self.assert_image_similar(transformed, im, epsilon * epsilonscale)
def test_translate_0_1(self):
self._test_translate(.1, 0, 3.7)
def test_translate_0_6(self):
self._test_translate(.6, 0, 9.1)
def test_translate_50(self):
self._test_translate(50, 50, 0)
class TestImageTransformPerspective(TestImageTransformAffine):
# Repeat all tests for AFFINE transormations with PERSPECTIVE
transform = Image.PERSPECTIVE
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -78,6 +78,22 @@ class TestImageOps(PillowTestCase):
ImageOps.equalize(i.convert("P")) ImageOps.equalize(i.convert("P"))
ImageOps.equalize(i.convert("RGB")) ImageOps.equalize(i.convert("RGB"))
def test_scale(self):
# Test the scaling function
i = hopper("L").resize((50, 50))
with self.assertRaises(ValueError):
ImageOps.scale(i, -1)
newimg = ImageOps.scale(i, 1)
self.assertEqual(newimg.size, (50, 50))
newimg = ImageOps.scale(i, 2)
self.assertEqual(newimg.size, (100, 100))
newimg = ImageOps.scale(i, 0.5)
self.assertEqual(newimg.size, (25, 25))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -5,6 +5,7 @@ from PIL import ImagePath, Image
import array import array
import struct import struct
class TestImagePath(PillowTestCase): class TestImagePath(PillowTestCase):
def test_path(self): def test_path(self):
@ -61,7 +62,6 @@ class TestImagePath(PillowTestCase):
p = ImagePath.Path(arr.tostring()) p = ImagePath.Path(arr.tostring())
self.assertEqual(list(p), [(0.0, 1.0)]) self.assertEqual(list(p), [(0.0, 1.0)])
def test_overflow_segfault(self): def test_overflow_segfault(self):
try: try:
# post patch, this fails with a memory error # post patch, this fails with a memory error

View File

@ -56,7 +56,7 @@ class TestImageSequence(PillowTestCase):
im = Image.open('Tests/images/multipage.tiff') im = Image.open('Tests/images/multipage.tiff')
firstFrame = None firstFrame = None
for frame in ImageSequence.Iterator(im): for frame in ImageSequence.Iterator(im):
if firstFrame == None: if firstFrame is None:
firstFrame = frame.copy() firstFrame = frame.copy()
pass pass
for frame in ImageSequence.Iterator(im): for frame in ImageSequence.Iterator(im):

View File

@ -8,6 +8,7 @@ except (OSError, ImportError) as v:
# Skipped via setUp() # Skipped via setUp()
pass pass
class TestImageTk(PillowTestCase): class TestImageTk(PillowTestCase):
def setUp(self): def setUp(self):

View File

@ -57,7 +57,10 @@ if sys.platform.startswith('win32'):
DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ] DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ]
CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection
CreateDIBSection.argtypes = [ctypes.wintypes.HDC, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD] CreateDIBSection.argtypes = [ctypes.wintypes.HDC, ctypes.c_void_p,
ctypes.c_uint,
ctypes.POINTER(ctypes.c_void_p),
ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD]
CreateDIBSection.restype = ctypes.wintypes.HBITMAP CreateDIBSection.restype = ctypes.wintypes.HBITMAP
def serialize_dib(bi, pixels): def serialize_dib(bi, pixels):
@ -99,7 +102,8 @@ if sys.platform.startswith('win32'):
hdc = CreateCompatibleDC(None) hdc = CreateCompatibleDC(None)
# print('hdc:',hex(hdc)) # print('hdc:',hex(hdc))
pixels = ctypes.c_void_p() pixels = ctypes.c_void_p()
dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0) dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS,
ctypes.byref(pixels), None, 0)
SelectObject(hdc, dib) SelectObject(hdc, dib)
imdib.expose(hdc) imdib.expose(hdc)

View File

@ -10,11 +10,11 @@ class TestLibPack(PillowTestCase):
def test_pack(self): def test_pack(self):
def pack(mode, rawmode): def pack(mode, rawmode, in_data=(1, 2, 3, 4)):
if len(mode) == 1: if len(mode) == 1:
im = Image.new(mode, (1, 1), 1) im = Image.new(mode, (1, 1), in_data[0])
else: else:
im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) im = Image.new(mode, (1, 1), in_data[:len(mode)])
if py3: if py3:
return list(im.tobytes("raw", rawmode)) return list(im.tobytes("raw", rawmode))
@ -47,6 +47,8 @@ class TestLibPack(PillowTestCase):
self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255?
self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4])
self.assertEqual(pack("RGBA", "BGRa"), [0, 0, 0, 4])
self.assertEqual(pack("RGBA", "BGRa", in_data=(20, 30, 40, 50)), [8, 6, 4, 50])
self.assertEqual(pack("RGBa", "RGBa"), [1, 2, 3, 4]) self.assertEqual(pack("RGBa", "RGBa"), [1, 2, 3, 4])
self.assertEqual(pack("RGBa", "BGRa"), [3, 2, 1, 4]) self.assertEqual(pack("RGBa", "BGRa"), [3, 2, 1, 4])
@ -122,7 +124,9 @@ class TestLibPack(PillowTestCase):
self.assertEqual(unpack("RGB", "XBGR", 4), (4, 3, 2)) self.assertEqual(unpack("RGB", "XBGR", 4), (4, 3, 2))
self.assertEqual(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) self.assertEqual(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4))
self.assertEqual(unpack("RGBA", "RGBa", 4), (63, 127, 191, 4))
self.assertEqual(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) self.assertEqual(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4))
self.assertEqual(unpack("RGBA", "BGRa", 4), (191, 127, 63, 4))
self.assertEqual(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) self.assertEqual(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1))
self.assertEqual(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) self.assertEqual(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1))
self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0))

View File

@ -1,4 +1,5 @@
from __future__ import print_function from __future__ import print_function
import sys
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image
@ -6,18 +7,29 @@ from PIL import Image
try: try:
import site import site
import numpy import numpy
assert site # silence warning
assert numpy # silence warning
except ImportError: except ImportError:
# Skip via setUp() # Skip via setUp()
pass pass
TEST_IMAGE_SIZE = (10, 10) TEST_IMAGE_SIZE = (10, 10)
# Numpy on pypy as of pypy 5.3.1 is corrupting the numpy.array(Image)
# call such that it's returning a object of type numpy.ndarray, but
# the repr is that of a PIL.Image. Size and shape are 1 and (), not the
# size and shape of the array. This causes failures in several tests.
SKIP_NUMPY_ON_PYPY = hasattr(sys, 'pypy_version_info') and (
sys.pypy_version_info <= (5,3,1,'final',0))
class TestNumpy(PillowTestCase): class TestNumpy(PillowTestCase):
def setUp(self): def setUp(self):
try: try:
import site import site
import numpy import numpy
assert site # silence warning
assert numpy # silence warning
except ImportError: except ImportError:
self.skipTest("ImportError") self.skipTest("ImportError")
@ -100,6 +112,7 @@ class TestNumpy(PillowTestCase):
self.assert_image(Image.fromarray(a[:, :, 1]), "L", TEST_IMAGE_SIZE) self.assert_image(Image.fromarray(a[:, :, 1]), "L", TEST_IMAGE_SIZE)
def _test_img_equals_nparray(self, img, np): def _test_img_equals_nparray(self, img, np):
self.assertGreaterEqual(len(np.shape), 2)
np_size = np.shape[1], np.shape[0] np_size = np.shape[1], np.shape[0]
self.assertEqual(img.size, np_size) self.assertEqual(img.size, np_size)
px = img.load() px = img.load()
@ -107,12 +120,22 @@ class TestNumpy(PillowTestCase):
for y in range(0, img.size[1], int(img.size[1]/10)): for y in range(0, img.size[1], int(img.size[1]/10)):
self.assert_deep_equal(px[x, y], np[y, x]) self.assert_deep_equal(px[x, y], np[y, x])
@unittest.skipIf(SKIP_NUMPY_ON_PYPY, "numpy.array(Image) is flaky on PyPy")
def test_16bit(self): def test_16bit(self):
img = Image.open('Tests/images/16bit.cropped.tif') img = Image.open('Tests/images/16bit.cropped.tif')
np_img = numpy.array(img) np_img = numpy.array(img)
self._test_img_equals_nparray(img, np_img) self._test_img_equals_nparray(img, np_img)
self.assertEqual(np_img.dtype, numpy.dtype('<u2')) self.assertEqual(np_img.dtype, numpy.dtype('<u2'))
def test_1bit(self):
# Test that 1-bit arrays convert to numpy and back
# See: https://github.com/python-pillow/Pillow/issues/350
arr = numpy.array([[1, 0, 0, 1, 0], [0, 1, 0, 0, 0]], 'u1')
img = Image.fromarray(arr * 255).convert('1')
self.assertEqual(img.mode, '1')
arr_back = numpy.array(img)
numpy.testing.assert_array_equal(arr, arr_back)
def test_save_tiff_uint16(self): def test_save_tiff_uint16(self):
''' '''
Open a single-channel uint16 greyscale image and verify that it can be saved without Open a single-channel uint16 greyscale image and verify that it can be saved without
@ -128,6 +151,7 @@ class TestNumpy(PillowTestCase):
im_good = Image.open(filename) im_good = Image.open(filename)
self.assert_image_equal(im_good, im_test) self.assert_image_equal(im_good, im_test)
@unittest.skipIf(SKIP_NUMPY_ON_PYPY, "numpy.array(Image) is flaky on PyPy")
def test_to_array(self): def test_to_array(self):
def _to_array(mode, dtype): def _to_array(mode, dtype):

View File

@ -371,7 +371,8 @@ int load_tkinter_funcs(void)
#if PY_VERSION_HEX >= 0x03000000 #if PY_VERSION_HEX >= 0x03000000
char *fname2char(PyObject *fname) char *fname2char(PyObject *fname)
{ {
PyObject *bytes = PyUnicode_EncodeFSDefault(fname); PyObject* bytes;
bytes = PyUnicode_EncodeFSDefault(fname);
if (bytes == NULL) { if (bytes == NULL) {
return NULL; return NULL;
} }
@ -391,9 +392,10 @@ void *_dfunc(void *lib_handle, const char *func_name)
* Returns function pointer or NULL if not present. * Returns function pointer or NULL if not present.
*/ */
void* func;
/* Reset errors. */ /* Reset errors. */
dlerror(); dlerror();
void *func = dlsym(lib_handle, func_name); func = dlsym(lib_handle, func_name);
if (func == NULL) { if (func == NULL) {
const char *error = dlerror(); const char *error = dlerror();
PyErr_SetString(PyExc_RuntimeError, error); PyErr_SetString(PyExc_RuntimeError, error);

View File

@ -729,7 +729,7 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE*
/* http://littlecms2.blogspot.com/2009/07/less-is-more.html */ /* http://littlecms2.blogspot.com/2009/07/less-is-more.html */
// double array of RGB values with max on each identitiy // double array of RGB values with max on each identity
hXYZ = cmsCreateXYZProfile(); hXYZ = cmsCreateXYZProfile();
if (hXYZ == NULL) if (hXYZ == NULL)
return 0; return 0;

View File

@ -204,7 +204,7 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
} }
state->bytes = (state->bits * state->xsize+7)/8; state->bytes = (state->bits * state->xsize+7)/8;
} }
/* malloc check ok, oveflow checked above */ /* malloc check ok, overflow checked above */
state->buffer = (UINT8*) malloc(state->bytes); state->buffer = (UINT8*) malloc(state->bytes);
if (!state->buffer) if (!state->buffer)
return PyErr_NoMemory(); return PyErr_NoMemory();

View File

@ -16,6 +16,8 @@ explicitly.
Fully supported formats Fully supported formats
----------------------- -----------------------
.. contents::
BMP BMP
^^^ ^^^

View File

@ -51,11 +51,11 @@ supported Pythons in both 32 and 64-bit versions in wheel, egg, and
executable installers. These binaries have all of the optional executable installers. These binaries have all of the optional
libraries included:: libraries included::
$ pip install Pillow > pip install Pillow
or:: or::
$ easy_install Pillow > easy_install Pillow
OS X Installation OS X Installation
@ -282,7 +282,9 @@ Building on Linux
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
If you didn't build Python from source, make sure you have Python's If you didn't build Python from source, make sure you have Python's
development libraries installed. In Debian or Ubuntu:: development libraries installed.
In Debian or Ubuntu::
$ sudo apt-get install python-dev python-setuptools $ sudo apt-get install python-dev python-setuptools
@ -294,6 +296,10 @@ In Fedora, the command is::
$ sudo dnf install python-devel redhat-rpm-config $ sudo dnf install python-devel redhat-rpm-config
Or for Python 3::
$ sudo dnf install python3-devel redhat-rpm-config
.. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions.
Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy

View File

@ -0,0 +1,15 @@
3.4.0
-----
New resizing filters
====================
Two new filters available for ``Image.resize()`` and ``Image.thumbnail()``
functions: ``BOX`` and ``HAMMING``. ``BOX`` is the high-performance filter with
two times shorter window than ``BILINEAR``. It can be used for image reduction
3 and more times and produces a more sharp result than ``BILINEAR``.
``HAMMING`` filter has the same performance as ``BILINEAR`` filter while
providing the image downscaling quality comparable to ``BICUBIC``.
Both new filters don't show good quality for the image upscaling.

View File

@ -6,6 +6,7 @@ Release Notes
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
3.4.0
3.3.0 3.3.0
3.2.0 3.2.0
3.1.2 3.1.2

View File

@ -365,7 +365,7 @@ get_packer(ImagingEncoderObject* encoder, const char* mode,
pack = ImagingFindPacker(mode, rawmode, &bits); pack = ImagingFindPacker(mode, rawmode, &bits);
if (!pack) { if (!pack) {
Py_DECREF(encoder); Py_DECREF(encoder);
PyErr_SetString(PyExc_SystemError, "unknown raw mode"); PyErr_Format(PyExc_ValueError, "No packer found from %s to %s", mode, rawmode);
return -1; return -1;
} }

View File

@ -240,7 +240,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
/* transform primitives (ImagingTransformMap) */ /* transform primitives (ImagingTransformMap) */
static int static int
affine_transform(double* xin, double* yin, int x, int y, void* data) affine_transform(double* xout, double* yout, int x, int y, void* data)
{ {
/* full moon tonight. your compiler will generate bogus code /* full moon tonight. your compiler will generate bogus code
for simple expressions, unless you reorganize the code, or for simple expressions, unless you reorganize the code, or
@ -250,28 +250,34 @@ affine_transform(double* xin, double* yin, int x, int y, void* data)
double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
xin[0] = a0*x + a1*y + a2; double xin = x + 0.5;
yin[0] = a3*x + a4*y + a5; double yin = y + 0.5;
xout[0] = a0*xin + a1*yin + a2;
yout[0] = a3*xin + a4*yin + a5;
return 1; return 1;
} }
static int static int
perspective_transform(double* xin, double* yin, int x, int y, void* data) perspective_transform(double* xout, double* yout, int x, int y, void* data)
{ {
double* a = (double*) data; double* a = (double*) data;
double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
double a6 = a[6]; double a7 = a[7]; double a6 = a[6]; double a7 = a[7];
xin[0] = (a0*x + a1*y + a2) / (a6*x + a7*y + 1); double xin = x + 0.5;
yin[0] = (a3*x + a4*y + a5) / (a6*x + a7*y + 1); double yin = y + 0.5;
xout[0] = (a0*xin + a1*yin + a2) / (a6*xin + a7*yin + 1);
yout[0] = (a3*xin + a4*yin + a5) / (a6*xin + a7*yin + 1);
return 1; return 1;
} }
static int static int
quad_transform(double* xin, double* yin, int x, int y, void* data) quad_transform(double* xout, double* yout, int x, int y, void* data)
{ {
/* quad warp: map quadrilateral to rectangle */ /* quad warp: map quadrilateral to rectangle */
@ -279,8 +285,11 @@ quad_transform(double* xin, double* yin, int x, int y, void* data)
double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3];
double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7]; double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7];
xin[0] = a0 + a1*x + a2*y + a3*x*y; double xin = x + 0.5;
yin[0] = a4 + a5*x + a6*y + a7*x*y; double yin = y + 0.5;
xout[0] = a0 + a1*xin + a2*yin + a3*xin*yin;
yout[0] = a4 + a5*xin + a6*yin + a7*xin*yin;
return 1; return 1;
} }
@ -691,8 +700,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
xo = a[2]; xo = a[2] + a[0] * 0.5;
yo = a[5]; yo = a[5] + a[4] * 0.5;
xmin = x1; xmin = x1;
xmax = x0; xmax = x0;
@ -771,8 +780,10 @@ affine_fixed(Imaging imOut, Imaging imIn,
/* use 16.16 fixed point arithmetics */ /* use 16.16 fixed point arithmetics */
#define FIX(v) FLOOR((v)*65536.0 + 0.5) #define FIX(v) FLOOR((v)*65536.0 + 0.5)
a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]); a0 = FIX(a[0]); a1 = FIX(a[1]);
a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]); a3 = FIX(a[3]); a4 = FIX(a[4]);
a2 = FIX(a[2] + a[0] * 0.5 + a[1] * 0.5);
a5 = FIX(a[5] + a[3] * 0.5 + a[4] * 0.5);
#undef FIX #undef FIX
@ -835,9 +846,10 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
filterid, fill); filterid, fill);
} }
if (a[1] == 0 && a[3] == 0) if (a[1] == 0 && a[3] == 0) {
/* Scaling */ /* Scaling */
return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
}
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
return (Imaging) ImagingError_ModeError(); return (Imaging) ImagingError_ModeError();
@ -867,8 +879,8 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
xsize = (int) imIn->xsize; xsize = (int) imIn->xsize;
ysize = (int) imIn->ysize; ysize = (int) imIn->ysize;
xo = a[2]; xo = a[2] + a[1] * 0.5 + a[0] * 0.5;
yo = a[5]; yo = a[5] + a[4] * 0.5 + a[3] * 0.5;
#define AFFINE_TRANSFORM(pixel, image)\ #define AFFINE_TRANSFORM(pixel, image)\
for (y = y0; y < y1; y++) {\ for (y = y0; y < y1; y++) {\

View File

@ -72,6 +72,10 @@
#define C64L C64N #define C64L C64N
#endif #endif
/* like (a * b + 127) / 255), but much faster on most platforms */
#define MULDIV255(a, b, tmp)\
(tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
static void static void
pack1(UINT8* out, const UINT8* in, int pixels) pack1(UINT8* out, const UINT8* in, int pixels)
{ {
@ -315,6 +319,21 @@ ImagingPackABGR(UINT8* out, const UINT8* in, int pixels)
} }
} }
void
ImagingPackBGRa(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* BGRa, reversed bytes with premultiplied alpha */
for (i = 0; i < pixels; i++) {
int alpha = out[3] = in[A];
int tmp;
out[0] = MULDIV255(in[B], alpha, tmp);
out[1] = MULDIV255(in[G], alpha, tmp);
out[2] = MULDIV255(in[R], alpha, tmp);
out += 4; in += 4;
}
}
static void static void
packRGBL(UINT8* out, const UINT8* in, int pixels) packRGBL(UINT8* out, const UINT8* in, int pixels)
{ {
@ -525,6 +544,7 @@ static struct {
{"RGBA", "BGR", 24, ImagingPackBGR}, {"RGBA", "BGR", 24, ImagingPackBGR},
{"RGBA", "BGRA", 32, ImagingPackBGRA}, {"RGBA", "BGRA", 32, ImagingPackBGRA},
{"RGBA", "ABGR", 32, ImagingPackABGR}, {"RGBA", "ABGR", 32, ImagingPackABGR},
{"RGBA", "BGRa", 32, ImagingPackBGRa},
{"RGBA", "R", 8, band0}, {"RGBA", "R", 8, band0},
{"RGBA", "G", 8, band1}, {"RGBA", "G", 8, band1},
{"RGBA", "B", 8, band2}, {"RGBA", "B", 8, band2},

View File

@ -75,7 +75,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
(UINT8*) im->image[state->y + state->yoff] + (UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, state->xsize); state->xoff * im->pixelsize, state->xsize);
state->y++; state->y += 1;
state->count = 1; state->count = 1;
state->LAST = state->buffer[0]; state->LAST = state->buffer[0];
@ -91,7 +91,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* when we arrive here, "count" contains the number of /* when we arrive here, "count" contains the number of
bytes having the value of "LAST" that we've already bytes having the value of "LAST" that we've already
seen */ seen */
while (state->x < planes * bytes_per_line) { do {
/* If we're encoding an odd width file, and we've /* If we're encoding an odd width file, and we've
got more than one plane, we need to pad each got more than one plane, we need to pad each
color row with padding bytes at the end. Since color row with padding bytes at the end. Since
@ -103,22 +103,23 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (state->count == 63) { if (state->count == 63) {
/* this run is full; flush it */ /* this run is full; flush it */
if (bytes < 2) if (bytes < 2) {
return ptr - buf; return ptr - buf;
*ptr++ = 0xff; }
*ptr++ = state->LAST; ptr[0] = 0xff;
ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
state->count = 0; state->count = 0;
} }
this = state->buffer[state->x]; this = state->buffer[state->x];
if (this == state->LAST) { if (this == state->LAST) {
/* extend the current run */ /* extend the current run */
state->x++; state->x += 1;
state->count++; state->count += 1;
} else { } else {
/* start a new run */ /* start a new run */
@ -126,15 +127,17 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (bytes < 1) { if (bytes < 1) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = state->LAST; ptr[0] = state->LAST;
bytes--; ptr += 1;
bytes -= 1;
} else { } else {
if (state->count > 0) { if (state->count > 0) {
if (bytes < 2) { if (bytes < 2) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = 0xc0 | state->count; ptr[0] = 0xc0 | state->count;
*ptr++ = state->LAST; ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
} }
} }
@ -142,8 +145,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->LAST = this; state->LAST = this;
state->count = 1; state->count = 1;
state->x++; state->x += 1;
} }
} }
@ -152,33 +154,34 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (bytes < 1 + padding) { if (bytes < 1 + padding) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = state->LAST; ptr[0] = state->LAST;
bytes--; ptr += 1;
bytes -= 1;
} else { } else {
if (state->count > 0) { if (state->count > 0) {
if (bytes < 2 + padding) { if (bytes < 2 + padding) {
return ptr - buf; return ptr - buf;
} }
*ptr++ = 0xc0 | state->count; ptr[0] = 0xc0 | state->count;
*ptr++ = state->LAST; ptr[1] = state->LAST;
ptr += 2;
bytes -= 2; bytes -= 2;
} }
} }
if (bytes < padding) {
return ptr - buf;
}
/* add the padding */ /* add the padding */
for (i = 0; i < padding; i++) { for (i = 0; i < padding; i++) {
*ptr++=0; ptr[0] = 0;
bytes--; ptr += 1;
bytes -= 1;
} }
/* reset for the next color plane. */ /* reset for the next color plane. */
if (state->x < planes * bytes_per_line) { if (state->x < planes * bytes_per_line) {
state->count = 1; state->count = 1;
state->LAST = state->buffer[state->x]; state->LAST = state->buffer[state->x];
state->x++; state->x += 1;
}
} }
} while (state->x < planes * bytes_per_line);
/* read next line */ /* read next line */
state->state = FETCH; state->state = FETCH;
break; break;

View File

@ -407,7 +407,7 @@ int quantize_octree(Pixel *pixelData,
/* remove the used fine colors from the coarse cube */ /* remove the used fine colors from the coarse cube */
subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors); subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors);
/* did the substraction cleared one or more coarse bucket? */ /* did the subtraction cleared one or more coarse bucket? */
while (nCoarseColors > count_used_color_buckets(coarseCube)) { while (nCoarseColors > count_used_color_buckets(coarseCube)) {
/* then we can use the free buckets for fine colors */ /* then we can use the free buckets for fine colors */
nAlreadySubtracted = nFineColors; nAlreadySubtracted = nFineColors;

View File

@ -80,18 +80,52 @@ static struct filter LANCZOS = { lanczos_filter, 3.0 };
#define PRECISION_BITS (32 - 8 - 2) #define PRECISION_BITS (32 - 8 - 2)
UINT8 _lookups[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
UINT8 *lookups = &_lookups[128];
static inline UINT8 clip8(int in) static inline UINT8 clip8(int in)
{ {
if (in >= (1 << PRECISION_BITS << 8)) return lookups[in >> PRECISION_BITS];
return 255;
if (in <= 0)
return 0;
return (UINT8) (in >> PRECISION_BITS);
} }
int int
ImagingPrecompute(int inSize, int outSize, struct filter *filterp, precompute_coeffs(int inSize, int outSize, struct filter *filterp,
int **xboundsp, double **kkp) { int **xboundsp, double **kkp) {
double support, scale, filterscale; double support, scale, filterscale;
double center, ww, ss; double center, ww, ss;
@ -142,6 +176,16 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp,
k = &kk[xx * kmax]; k = &kk[xx * kmax];
for (x = 0; x < xmax; x++) { for (x = 0; x < xmax; x++) {
double w = filterp->filter((x + xmin - center + 0.5) * ss); double w = filterp->filter((x + xmin - center + 0.5) * ss);
if (w == 0) {
if (x == 0) {
x -= 1;
xmin += 1;
xmax -= 1;
} else if (x == xmax - 1) {
xmax -= 1;
}
continue;
}
k[x] = w; k[x] = w;
ww += w; ww += w;
} }
@ -162,6 +206,32 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp,
} }
int
normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp)
{
int x;
INT32 *kk;
/* malloc check ok, overflow checked in precompute_coeffs */
kk = malloc(outSize * kmax * sizeof(INT32));
if ( ! kk) {
return 0;
}
for (x = 0; x < outSize * kmax; x++) {
if (prekk[x] < 0) {
kk[x] = (int) (-0.5 + prekk[x] * (1 << PRECISION_BITS));
} else {
kk[x] = (int) (0.5 + prekk[x] * (1 << PRECISION_BITS));
}
}
*kkp = kk;
return kmax;
}
Imaging Imaging
ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp)
{ {
@ -170,28 +240,21 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp)
int ss0, ss1, ss2, ss3; int ss0, ss1, ss2, ss3;
int xx, yy, x, kmax, xmin, xmax; int xx, yy, x, kmax, xmin, xmax;
int *xbounds; int *xbounds;
int *k, *kk; INT32 *k, *kk;
double *prekk; double *prekk;
kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &prekk);
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &prekk);
if ( ! kmax) { if ( ! kmax) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
kk = malloc(xsize * kmax * sizeof(int)); kmax = normalize_coeffs_8bpc(xsize, kmax, prekk, &kk);
if ( ! kk) {
free(xbounds);
free(prekk); free(prekk);
if ( ! kmax) {
free(xbounds);
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
for (x = 0; x < xsize * kmax; x++) {
kk[x] = (int) (0.5 + prekk[x] * (1 << PRECISION_BITS));
}
free(prekk);
imOut = ImagingNew(imIn->mode, xsize, imIn->ysize); imOut = ImagingNew(imIn->mode, xsize, imIn->ysize);
if ( ! imOut) { if ( ! imOut) {
free(kk); free(kk);
@ -282,28 +345,21 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
int ss0, ss1, ss2, ss3; int ss0, ss1, ss2, ss3;
int xx, yy, y, kmax, ymin, ymax; int xx, yy, y, kmax, ymin, ymax;
int *xbounds; int *xbounds;
int *k, *kk; INT32 *k, *kk;
double *prekk; double *prekk;
kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &prekk);
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &prekk);
if ( ! kmax) { if ( ! kmax) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
kk = malloc(ysize * kmax * sizeof(int)); kmax = normalize_coeffs_8bpc(ysize, kmax, prekk, &kk);
if ( ! kk) {
free(xbounds);
free(prekk); free(prekk);
if ( ! kmax) {
free(xbounds);
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
for (y = 0; y < ysize * kmax; y++) {
kk[y] = (int) (0.5 + prekk[y] * (1 << PRECISION_BITS));
}
free(prekk);
imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); imOut = ImagingNew(imIn->mode, imIn->xsize, ysize);
if ( ! imOut) { if ( ! imOut) {
free(kk); free(kk);
@ -396,7 +452,7 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp)
int *xbounds; int *xbounds;
double *k, *kk; double *k, *kk;
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &kk); kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &kk);
if ( ! kmax) { if ( ! kmax) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }
@ -456,7 +512,7 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
int *xbounds; int *xbounds;
double *k, *kk; double *k, *kk;
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &kk); kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &kk);
if ( ! kmax) { if ( ! kmax) {
return (Imaging) ImagingError_MemoryError(); return (Imaging) ImagingError_MemoryError();
} }

View File

@ -747,6 +747,30 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels)
} }
} }
static void
unpackBGRa(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* premultiplied BGRA */
for (i = 0; i < pixels; i++) {
int a = in[3];
if (!a)
out[R] = out[G] = out[B] = out[A] = 0;
else if (a == 255) {
out[R] = in[2];
out[G] = in[1];
out[B] = in[0];
out[A] = a;
} else {
out[R] = CLIP(in[2] * 255 / a);
out[G] = CLIP(in[1] * 255 / a);
out[B] = CLIP(in[0] * 255 / a);
out[A] = a;
}
out += 4; in += 4;
}
}
static void static void
unpackRGBAI(UINT8* out, const UINT8* in, int pixels) unpackRGBAI(UINT8* out, const UINT8* in, int pixels)
{ {
@ -1206,6 +1230,7 @@ static struct {
{"RGBA", "LA;16B", 32, unpackRGBALA16B}, {"RGBA", "LA;16B", 32, unpackRGBALA16B},
{"RGBA", "RGBA", 32, copy4}, {"RGBA", "RGBA", 32, copy4},
{"RGBA", "RGBa", 32, unpackRGBa}, {"RGBA", "RGBa", 32, unpackRGBa},
{"RGBA", "BGRa", 32, unpackBGRa},
{"RGBA", "RGBA;I", 32, unpackRGBAI}, {"RGBA", "RGBA;I", 32, unpackRGBAI},
{"RGBA", "RGBA;L", 32, unpackRGBAL}, {"RGBA", "RGBA;L", 32, unpackRGBAL},
{"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15},

View File

@ -28,9 +28,9 @@ libs = {
'dir': 'tiff-4.0.6', 'dir': 'tiff-4.0.6',
}, },
'freetype': { 'freetype': {
'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.3.tar.gz', 'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.5.tar.gz',
'hash': 'md5:8b0c80b64042b7c39b9fa9debe6305c3', 'hash': 'md5:31b2276515d9ee1c7f37d9c9f4f3145a',
'dir': 'freetype-2.6.3', 'dir': 'freetype-2.6.5',
}, },
'lcms': { 'lcms': {
'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',