mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-16 18:24:45 +03:00
Merge branch 'master' of https://github.com/python-pillow/Pillow into jpegdpi
This commit is contained in:
commit
d5a99bf685
51
CHANGES.rst
51
CHANGES.rst
|
@ -4,12 +4,63 @@ Changelog (Pillow)
|
|||
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
|
||||
[homm]
|
||||
|
||||
- Retain a reference to core image object in PyAccess #2009
|
||||
[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)
|
||||
------------------
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
self.fp = self.ole.openstream(self.stream[:2] +
|
||||
["Subimage 0000 Data"])
|
||||
|
||||
ImageFile.ImageFile.load(self)
|
||||
return ImageFile.ImageFile.load(self)
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
16
PIL/Image.py
16
PIL/Image.py
|
@ -246,7 +246,7 @@ else:
|
|||
|
||||
_MODE_CONV = {
|
||||
# official modes
|
||||
"1": ('|b1', None), # broken
|
||||
"1": ('|b1', None), # Bits need to be extended to bytes
|
||||
"L": ('|u1', None),
|
||||
"LA": ('|u1', 2),
|
||||
"I": (_ENDIAN + 'i4', None),
|
||||
|
@ -615,17 +615,21 @@ class Image(object):
|
|||
self.save(b, 'PNG')
|
||||
return b.getvalue()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == "__array_interface__":
|
||||
@property
|
||||
def __array_interface__(self):
|
||||
# numpy array interface support
|
||||
new = {}
|
||||
shape, typestr = _conv_type_shape(self)
|
||||
new['shape'] = shape
|
||||
new['typestr'] = typestr
|
||||
new['data'] = self.tobytes()
|
||||
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
|
||||
raise AttributeError(name)
|
||||
|
||||
def __getstate__(self):
|
||||
return [
|
||||
|
@ -1877,7 +1881,7 @@ class Image(object):
|
|||
xs = float(x1 - x0) / w
|
||||
ys = float(y1 - y0) / h
|
||||
method = AFFINE
|
||||
data = (xs, 0, x0 + xs/2, 0, ys, y0 + ys/2)
|
||||
data = (xs, 0, x0, 0, ys, y0)
|
||||
|
||||
elif method == PERSPECTIVE:
|
||||
data = data[0:8]
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
Deform the image.
|
||||
|
|
|
@ -207,7 +207,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
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)]
|
||||
|
||||
ImageFile.ImageFile.load(self)
|
||||
return ImageFile.ImageFile.load(self)
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
|
|
|
@ -583,6 +583,14 @@ def _save(im, fp, filename):
|
|||
except KeyError:
|
||||
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
|
||||
|
||||
dpi = info.get("dpi", (0, 0))
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
VERSION = '1.1.7' # PIL version
|
||||
PILLOW_VERSION = '3.4.0.dev0' # Pillow
|
||||
|
||||
__version__ = PILLOW_VERSION
|
||||
|
||||
_plugins = ['BmpImagePlugin',
|
||||
'BufrStubImagePlugin',
|
||||
'CurImagePlugin',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from PIL import Image
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
|
||||
class TestJ2kEncodeOverflow(PillowTestCase):
|
||||
def test_j2k_overflow(self):
|
||||
|
||||
|
@ -12,7 +13,7 @@ class TestJ2kEncodeOverflow(PillowTestCase):
|
|||
except IOError as err:
|
||||
self.assertTrue(True, "IOError is expected")
|
||||
except Exception as err:
|
||||
self.assertTrue(False, "Expected IOError, got %s" %type(err))
|
||||
self.assertTrue(False, "Expected IOError, got %s" % type(err))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -99,7 +99,7 @@ class PillowTestCase(unittest.TestCase):
|
|||
" average pixel value difference %.4f > epsilon %.4f" % (
|
||||
ave_diff, epsilon))
|
||||
|
||||
def assert_warning(self, warn_class, func):
|
||||
def assert_warning(self, warn_class, func, *args, **kwargs):
|
||||
import warnings
|
||||
|
||||
result = None
|
||||
|
@ -108,7 +108,7 @@ class PillowTestCase(unittest.TestCase):
|
|||
warnings.simplefilter("always")
|
||||
|
||||
# Hopefully trigger a warning.
|
||||
result = func()
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
# Verify some things.
|
||||
self.assertGreaterEqual(len(w), 1)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -77,6 +77,5 @@ class TestFileBmp(PillowTestCase):
|
|||
self.assert_image_equal(im, target)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -450,6 +450,25 @@ class TestFileJpeg(PillowTestCase):
|
|||
# Assert
|
||||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -34,7 +34,8 @@ class TestFileJpeg2k(PillowTestCase):
|
|||
self.assertRegexpMatches(Image.core.jp2klib_version, '\d+\.\d+\.\d+$')
|
||||
|
||||
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.size, (640, 480))
|
||||
self.assertEqual(im.format, 'JPEG2000')
|
||||
|
|
|
@ -170,7 +170,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
'RowsPerStrip',
|
||||
'StripOffsets']
|
||||
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):
|
||||
# these should not crash. Seriously dummy data, most of it doesn't make
|
||||
|
@ -183,7 +184,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
in TiffTags.LIBTIFF_CORE]
|
||||
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')
|
||||
for tag in im.tag_v2.keys():
|
||||
try:
|
||||
|
@ -422,8 +424,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
def test_gray_semibyte_per_pixel(self):
|
||||
test_files = (
|
||||
(
|
||||
24.8,#epsilon
|
||||
(#group
|
||||
24.8, # epsilon
|
||||
( # group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
|
||||
|
@ -431,8 +433,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
)
|
||||
),
|
||||
(
|
||||
7.3,#epsilon
|
||||
(#group
|
||||
7.3, # epsilon
|
||||
( # group
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
|
||||
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
|
||||
|
@ -504,6 +506,5 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.save(outfile)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image, PcxImagePlugin
|
||||
from PIL import Image, ImageFile, PcxImagePlugin
|
||||
|
||||
|
||||
class TestFilePcx(PillowTestCase):
|
||||
|
@ -46,6 +46,84 @@ class TestFilePcx(PillowTestCase):
|
|||
# Make sure all pixels are either 0 or 255.
|
||||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -313,7 +313,7 @@ class TestFilePng(PillowTestCase):
|
|||
# -14: malformed chunk
|
||||
|
||||
for offset in (-10, -13, -14):
|
||||
with open(TEST_PNG_FILE,'rb') as f:
|
||||
with open(TEST_PNG_FILE, 'rb') as f:
|
||||
test_file = f.read()[:offset]
|
||||
|
||||
im = Image.open(BytesIO(test_file))
|
||||
|
@ -347,7 +347,6 @@ class TestFilePng(PillowTestCase):
|
|||
finally:
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
|
||||
def test_roundtrip_dpi(self):
|
||||
# Check dpi roundtripping
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ class TestCffiPutPixel(TestImagePutPixel):
|
|||
def setUp(self):
|
||||
try:
|
||||
import cffi
|
||||
assert cffi # silence warning
|
||||
except ImportError:
|
||||
self.skipTest("No cffi")
|
||||
|
||||
|
@ -115,6 +116,7 @@ class TestCffiGetPixel(TestImageGetPixel):
|
|||
def setUp(self):
|
||||
try:
|
||||
import cffi
|
||||
assert cffi # silence warning
|
||||
except ImportError:
|
||||
self.skipTest("No cffi")
|
||||
|
||||
|
@ -125,6 +127,7 @@ class TestCffi(AccessTest):
|
|||
def setUp(self):
|
||||
try:
|
||||
import cffi
|
||||
assert cffi # silence warning
|
||||
except ImportError:
|
||||
self.skipTest("No cffi")
|
||||
|
||||
|
@ -229,7 +232,7 @@ class TestCffi(AccessTest):
|
|||
# Do not save references to the image, only to the access object
|
||||
px = Image.new('L', (size, 1), 0).load()
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -25,13 +25,26 @@ class TestImageArray(PillowTestCase):
|
|||
self.assertEqual(test("RGBX"), (3, (100, 128, 4), '|u1', 51200))
|
||||
|
||||
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):
|
||||
i = im.convert(mode)
|
||||
a = i.__array_interface__
|
||||
a["strides"] = 1 # pretend it's non-contigous
|
||||
i.__array_interface__ = a # patch in new version of attribute
|
||||
out = Image.fromarray(i)
|
||||
# Make wrapper instance for image, new array interface
|
||||
wrapped = Wrapper(i, a)
|
||||
out = Image.fromarray(wrapped)
|
||||
return out.mode, out.size, list(i.getdata()) == list(out.getdata())
|
||||
|
||||
# self.assertEqual(test("1"), ("1", (128, 100), True))
|
||||
self.assertEqual(test("L"), ("L", (128, 100), True))
|
||||
self.assertEqual(test("I"), ("I", (128, 100), True))
|
||||
|
|
|
@ -20,13 +20,13 @@ class TestImageGetData(PillowTestCase):
|
|||
return data[0], len(data), len(list(data))
|
||||
|
||||
self.assertEqual(getdata("1"), (0, 960, 960))
|
||||
self.assertEqual(getdata("L"), (25, 960, 960))
|
||||
self.assertEqual(getdata("I"), (25, 960, 960))
|
||||
self.assertEqual(getdata("F"), (25.0, 960, 960))
|
||||
self.assertEqual(getdata("RGB"), (((20, 20, 70), 960, 960)))
|
||||
self.assertEqual(getdata("RGBA"), ((20, 20, 70, 255), 960, 960))
|
||||
self.assertEqual(getdata("CMYK"), ((235, 235, 185, 0), 960, 960))
|
||||
self.assertEqual(getdata("YCbCr"), ((25, 153, 123), 960, 960))
|
||||
self.assertEqual(getdata("L"), (16, 960, 960))
|
||||
self.assertEqual(getdata("I"), (16, 960, 960))
|
||||
self.assertEqual(getdata("F"), (16.0, 960, 960))
|
||||
self.assertEqual(getdata("RGB"), (((11, 13, 52), 960, 960)))
|
||||
self.assertEqual(getdata("RGBA"), ((11, 13, 52, 255), 960, 960))
|
||||
self.assertEqual(getdata("CMYK"), ((244, 242, 203, 0), 960, 960))
|
||||
self.assertEqual(getdata("YCbCr"), ((16, 147, 123), 960, 960))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -320,5 +320,22 @@ class CoreResamplePassesTest(PillowTestCase):
|
|||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import math
|
||||
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
|
@ -98,7 +100,7 @@ class TestImageTransform(PillowTestCase):
|
|||
def test_alpha_premult_resize(self):
|
||||
|
||||
def op(im, sz):
|
||||
return im.resize(sz, Image.LINEAR)
|
||||
return im.resize(sz, Image.BILINEAR)
|
||||
|
||||
self._test_alpha_premult(op)
|
||||
|
||||
|
@ -139,5 +141,117 @@ class TestImageTransform(PillowTestCase):
|
|||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -78,6 +78,22 @@ class TestImageOps(PillowTestCase):
|
|||
ImageOps.equalize(i.convert("P"))
|
||||
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__':
|
||||
unittest.main()
|
||||
|
|
|
@ -5,6 +5,7 @@ from PIL import ImagePath, Image
|
|||
import array
|
||||
import struct
|
||||
|
||||
|
||||
class TestImagePath(PillowTestCase):
|
||||
|
||||
def test_path(self):
|
||||
|
@ -61,7 +62,6 @@ class TestImagePath(PillowTestCase):
|
|||
p = ImagePath.Path(arr.tostring())
|
||||
self.assertEqual(list(p), [(0.0, 1.0)])
|
||||
|
||||
|
||||
def test_overflow_segfault(self):
|
||||
try:
|
||||
# post patch, this fails with a memory error
|
||||
|
|
|
@ -56,7 +56,7 @@ class TestImageSequence(PillowTestCase):
|
|||
im = Image.open('Tests/images/multipage.tiff')
|
||||
firstFrame = None
|
||||
for frame in ImageSequence.Iterator(im):
|
||||
if firstFrame == None:
|
||||
if firstFrame is None:
|
||||
firstFrame = frame.copy()
|
||||
pass
|
||||
for frame in ImageSequence.Iterator(im):
|
||||
|
|
|
@ -8,6 +8,7 @@ except (OSError, ImportError) as v:
|
|||
# Skipped via setUp()
|
||||
pass
|
||||
|
||||
|
||||
class TestImageTk(PillowTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -24,7 +25,7 @@ class TestImageTk(PillowTestCase):
|
|||
im2 = Image.open(TEST_PNG)
|
||||
with open(TEST_PNG, 'rb') as fp:
|
||||
data = fp.read()
|
||||
kw = {"file":TEST_JPG, "data":data}
|
||||
kw = {"file": TEST_JPG, "data": data}
|
||||
|
||||
# Test "file"
|
||||
im = ImageTk._get_image_from_kw(kw)
|
||||
|
|
|
@ -57,7 +57,10 @@ if sys.platform.startswith('win32'):
|
|||
DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ]
|
||||
|
||||
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
|
||||
|
||||
def serialize_dib(bi, pixels):
|
||||
|
@ -99,7 +102,8 @@ if sys.platform.startswith('win32'):
|
|||
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)
|
||||
dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS,
|
||||
ctypes.byref(pixels), None, 0)
|
||||
SelectObject(hdc, dib)
|
||||
|
||||
imdib.expose(hdc)
|
||||
|
|
|
@ -10,11 +10,11 @@ class TestLibPack(PillowTestCase):
|
|||
|
||||
def test_pack(self):
|
||||
|
||||
def pack(mode, rawmode):
|
||||
def pack(mode, rawmode, in_data=(1, 2, 3, 4)):
|
||||
if len(mode) == 1:
|
||||
im = Image.new(mode, (1, 1), 1)
|
||||
im = Image.new(mode, (1, 1), in_data[0])
|
||||
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:
|
||||
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("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", "BGRa"), [3, 2, 1, 4])
|
||||
|
@ -122,7 +124,9 @@ class TestLibPack(PillowTestCase):
|
|||
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), (63, 127, 191, 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", "ABGR", 4), (4, 3, 2, 1))
|
||||
self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
|
@ -6,18 +7,29 @@ from PIL import Image
|
|||
try:
|
||||
import site
|
||||
import numpy
|
||||
assert site # silence warning
|
||||
assert numpy # silence warning
|
||||
except ImportError:
|
||||
# Skip via setUp()
|
||||
pass
|
||||
|
||||
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):
|
||||
|
||||
def setUp(self):
|
||||
try:
|
||||
import site
|
||||
import numpy
|
||||
assert site # silence warning
|
||||
assert numpy # silence warning
|
||||
except ImportError:
|
||||
self.skipTest("ImportError")
|
||||
|
||||
|
@ -100,6 +112,7 @@ class TestNumpy(PillowTestCase):
|
|||
self.assert_image(Image.fromarray(a[:, :, 1]), "L", TEST_IMAGE_SIZE)
|
||||
|
||||
def _test_img_equals_nparray(self, img, np):
|
||||
self.assertGreaterEqual(len(np.shape), 2)
|
||||
np_size = np.shape[1], np.shape[0]
|
||||
self.assertEqual(img.size, np_size)
|
||||
px = img.load()
|
||||
|
@ -107,12 +120,22 @@ class TestNumpy(PillowTestCase):
|
|||
for y in range(0, img.size[1], int(img.size[1]/10)):
|
||||
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):
|
||||
img = Image.open('Tests/images/16bit.cropped.tif')
|
||||
np_img = numpy.array(img)
|
||||
self._test_img_equals_nparray(img, np_img)
|
||||
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):
|
||||
'''
|
||||
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)
|
||||
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 _to_array(mode, dtype):
|
||||
|
|
|
@ -371,7 +371,8 @@ int load_tkinter_funcs(void)
|
|||
#if PY_VERSION_HEX >= 0x03000000
|
||||
char *fname2char(PyObject *fname)
|
||||
{
|
||||
PyObject *bytes = PyUnicode_EncodeFSDefault(fname);
|
||||
PyObject* bytes;
|
||||
bytes = PyUnicode_EncodeFSDefault(fname);
|
||||
if (bytes == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -391,9 +392,10 @@ void *_dfunc(void *lib_handle, const char *func_name)
|
|||
* Returns function pointer or NULL if not present.
|
||||
*/
|
||||
|
||||
void* func;
|
||||
/* Reset errors. */
|
||||
dlerror();
|
||||
void *func = dlsym(lib_handle, func_name);
|
||||
func = dlsym(lib_handle, func_name);
|
||||
if (func == NULL) {
|
||||
const char *error = dlerror();
|
||||
PyErr_SetString(PyExc_RuntimeError, error);
|
||||
|
|
|
@ -729,7 +729,7 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE*
|
|||
|
||||
/* 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();
|
||||
if (hXYZ == NULL)
|
||||
return 0;
|
||||
|
|
2
decode.c
2
decode.c
|
@ -204,7 +204,7 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
|
|||
}
|
||||
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);
|
||||
if (!state->buffer)
|
||||
return PyErr_NoMemory();
|
||||
|
|
|
@ -16,6 +16,8 @@ explicitly.
|
|||
Fully supported formats
|
||||
-----------------------
|
||||
|
||||
.. contents::
|
||||
|
||||
BMP
|
||||
^^^
|
||||
|
||||
|
|
|
@ -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
|
||||
libraries included::
|
||||
|
||||
$ pip install Pillow
|
||||
> pip install Pillow
|
||||
|
||||
or::
|
||||
|
||||
$ easy_install Pillow
|
||||
> easy_install Pillow
|
||||
|
||||
|
||||
OS X Installation
|
||||
|
@ -282,7 +282,9 @@ Building on Linux
|
|||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
|
||||
|
@ -294,6 +296,10 @@ In Fedora, the command is::
|
|||
|
||||
$ 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.
|
||||
|
||||
Prerequisites are installed on **Ubuntu 12.04 LTS** or **Raspian Wheezy
|
||||
|
|
15
docs/releasenotes/3.4.0.rst
Normal file
15
docs/releasenotes/3.4.0.rst
Normal 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.
|
|
@ -6,6 +6,7 @@ Release Notes
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
3.4.0
|
||||
3.3.0
|
||||
3.2.0
|
||||
3.1.2
|
||||
|
|
2
encode.c
2
encode.c
|
@ -365,7 +365,7 @@ get_packer(ImagingEncoderObject* encoder, const char* mode,
|
|||
pack = ImagingFindPacker(mode, rawmode, &bits);
|
||||
if (!pack) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
|||
/* transform primitives (ImagingTransformMap) */
|
||||
|
||||
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
|
||||
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 a3 = a[3]; double a4 = a[4]; double a5 = a[5];
|
||||
|
||||
xin[0] = a0*x + a1*y + a2;
|
||||
yin[0] = a3*x + a4*y + a5;
|
||||
double xin = x + 0.5;
|
||||
double yin = y + 0.5;
|
||||
|
||||
xout[0] = a0*xin + a1*yin + a2;
|
||||
yout[0] = a3*xin + a4*yin + a5;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 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];
|
||||
|
||||
xin[0] = (a0*x + a1*y + a2) / (a6*x + a7*y + 1);
|
||||
yin[0] = (a3*x + a4*y + a5) / (a6*x + a7*y + 1);
|
||||
double xin = x + 0.5;
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
|
@ -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 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;
|
||||
yin[0] = a4 + a5*x + a6*y + a7*x*y;
|
||||
double xin = x + 0.5;
|
||||
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;
|
||||
}
|
||||
|
@ -691,8 +700,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn,
|
|||
return (Imaging) ImagingError_MemoryError();
|
||||
}
|
||||
|
||||
xo = a[2];
|
||||
yo = a[5];
|
||||
xo = a[2] + a[0] * 0.5;
|
||||
yo = a[5] + a[4] * 0.5;
|
||||
|
||||
xmin = x1;
|
||||
xmax = x0;
|
||||
|
@ -771,8 +780,10 @@ affine_fixed(Imaging imOut, Imaging imIn,
|
|||
/* use 16.16 fixed point arithmetics */
|
||||
#define FIX(v) FLOOR((v)*65536.0 + 0.5)
|
||||
|
||||
a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]);
|
||||
a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]);
|
||||
a0 = FIX(a[0]); a1 = FIX(a[1]);
|
||||
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
|
||||
|
||||
|
@ -835,9 +846,10 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
|
|||
filterid, fill);
|
||||
}
|
||||
|
||||
if (a[1] == 0 && a[3] == 0)
|
||||
if (a[1] == 0 && a[3] == 0) {
|
||||
/* Scaling */
|
||||
return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
|
||||
}
|
||||
|
||||
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
|
@ -867,8 +879,8 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn,
|
|||
xsize = (int) imIn->xsize;
|
||||
ysize = (int) imIn->ysize;
|
||||
|
||||
xo = a[2];
|
||||
yo = a[5];
|
||||
xo = a[2] + a[1] * 0.5 + a[0] * 0.5;
|
||||
yo = a[5] + a[4] * 0.5 + a[3] * 0.5;
|
||||
|
||||
#define AFFINE_TRANSFORM(pixel, image)\
|
||||
for (y = y0; y < y1; y++) {\
|
||||
|
|
|
@ -72,6 +72,10 @@
|
|||
#define C64L C64N
|
||||
#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
|
||||
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
|
||||
packRGBL(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
|
@ -525,6 +544,7 @@ static struct {
|
|||
{"RGBA", "BGR", 24, ImagingPackBGR},
|
||||
{"RGBA", "BGRA", 32, ImagingPackBGRA},
|
||||
{"RGBA", "ABGR", 32, ImagingPackABGR},
|
||||
{"RGBA", "BGRa", 32, ImagingPackBGRa},
|
||||
{"RGBA", "R", 8, band0},
|
||||
{"RGBA", "G", 8, band1},
|
||||
{"RGBA", "B", 8, band2},
|
||||
|
|
|
@ -75,7 +75,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
(UINT8*) im->image[state->y + state->yoff] +
|
||||
state->xoff * im->pixelsize, state->xsize);
|
||||
|
||||
state->y++;
|
||||
state->y += 1;
|
||||
|
||||
state->count = 1;
|
||||
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
|
||||
bytes having the value of "LAST" that we've already
|
||||
seen */
|
||||
while (state->x < planes * bytes_per_line) {
|
||||
do {
|
||||
/* If we're encoding an odd width file, and we've
|
||||
got more than one plane, we need to pad each
|
||||
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) {
|
||||
/* this run is full; flush it */
|
||||
if (bytes < 2)
|
||||
if (bytes < 2) {
|
||||
return ptr - buf;
|
||||
*ptr++ = 0xff;
|
||||
*ptr++ = state->LAST;
|
||||
}
|
||||
ptr[0] = 0xff;
|
||||
ptr[1] = state->LAST;
|
||||
ptr += 2;
|
||||
bytes -= 2;
|
||||
|
||||
state->count = 0;
|
||||
|
||||
}
|
||||
|
||||
this = state->buffer[state->x];
|
||||
|
||||
if (this == state->LAST) {
|
||||
/* extend the current run */
|
||||
state->x++;
|
||||
state->count++;
|
||||
state->x += 1;
|
||||
state->count += 1;
|
||||
|
||||
} else {
|
||||
/* start a new run */
|
||||
|
@ -126,15 +127,17 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
if (bytes < 1) {
|
||||
return ptr - buf;
|
||||
}
|
||||
*ptr++ = state->LAST;
|
||||
bytes--;
|
||||
ptr[0] = state->LAST;
|
||||
ptr += 1;
|
||||
bytes -= 1;
|
||||
} else {
|
||||
if (state->count > 0) {
|
||||
if (bytes < 2) {
|
||||
return ptr - buf;
|
||||
}
|
||||
*ptr++ = 0xc0 | state->count;
|
||||
*ptr++ = state->LAST;
|
||||
ptr[0] = 0xc0 | state->count;
|
||||
ptr[1] = state->LAST;
|
||||
ptr += 2;
|
||||
bytes -= 2;
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +145,7 @@ ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
state->LAST = this;
|
||||
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) {
|
||||
return ptr - buf;
|
||||
}
|
||||
*ptr++ = state->LAST;
|
||||
bytes--;
|
||||
ptr[0] = state->LAST;
|
||||
ptr += 1;
|
||||
bytes -= 1;
|
||||
} else {
|
||||
if (state->count > 0) {
|
||||
if (bytes < 2 + padding) {
|
||||
return ptr - buf;
|
||||
}
|
||||
*ptr++ = 0xc0 | state->count;
|
||||
*ptr++ = state->LAST;
|
||||
ptr[0] = 0xc0 | state->count;
|
||||
ptr[1] = state->LAST;
|
||||
ptr += 2;
|
||||
bytes -= 2;
|
||||
}
|
||||
}
|
||||
if (bytes < padding) {
|
||||
return ptr - buf;
|
||||
}
|
||||
/* add the padding */
|
||||
for (i=0;i<padding;i++){
|
||||
*ptr++=0;
|
||||
bytes--;
|
||||
for (i = 0; i < padding; i++) {
|
||||
ptr[0] = 0;
|
||||
ptr += 1;
|
||||
bytes -= 1;
|
||||
}
|
||||
/* reset for the next color plane. */
|
||||
if (state->x < planes * bytes_per_line) {
|
||||
state->count = 1;
|
||||
state->LAST = state->buffer[state->x];
|
||||
state->x++;
|
||||
}
|
||||
state->x += 1;
|
||||
}
|
||||
} while (state->x < planes * bytes_per_line);
|
||||
|
||||
/* read next line */
|
||||
state->state = FETCH;
|
||||
break;
|
||||
|
|
|
@ -407,7 +407,7 @@ int quantize_octree(Pixel *pixelData,
|
|||
/* remove the used fine colors from the coarse cube */
|
||||
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)) {
|
||||
/* then we can use the free buckets for fine colors */
|
||||
nAlreadySubtracted = nFineColors;
|
||||
|
|
|
@ -80,18 +80,52 @@ static struct filter LANCZOS = { lanczos_filter, 3.0 };
|
|||
#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)
|
||||
{
|
||||
if (in >= (1 << PRECISION_BITS << 8))
|
||||
return 255;
|
||||
if (in <= 0)
|
||||
return 0;
|
||||
return (UINT8) (in >> PRECISION_BITS);
|
||||
return lookups[in >> PRECISION_BITS];
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ImagingPrecompute(int inSize, int outSize, struct filter *filterp,
|
||||
precompute_coeffs(int inSize, int outSize, struct filter *filterp,
|
||||
int **xboundsp, double **kkp) {
|
||||
double support, scale, filterscale;
|
||||
double center, ww, ss;
|
||||
|
@ -142,6 +176,16 @@ ImagingPrecompute(int inSize, int outSize, struct filter *filterp,
|
|||
k = &kk[xx * kmax];
|
||||
for (x = 0; x < xmax; x++) {
|
||||
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;
|
||||
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
|
||||
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 xx, yy, x, kmax, xmin, xmax;
|
||||
int *xbounds;
|
||||
int *k, *kk;
|
||||
INT32 *k, *kk;
|
||||
double *prekk;
|
||||
|
||||
|
||||
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &prekk);
|
||||
kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &prekk);
|
||||
if ( ! kmax) {
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
}
|
||||
|
||||
kk = malloc(xsize * kmax * sizeof(int));
|
||||
if ( ! kk) {
|
||||
free(xbounds);
|
||||
kmax = normalize_coeffs_8bpc(xsize, kmax, prekk, &kk);
|
||||
free(prekk);
|
||||
if ( ! kmax) {
|
||||
free(xbounds);
|
||||
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);
|
||||
if ( ! imOut) {
|
||||
free(kk);
|
||||
|
@ -282,28 +345,21 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
|
|||
int ss0, ss1, ss2, ss3;
|
||||
int xx, yy, y, kmax, ymin, ymax;
|
||||
int *xbounds;
|
||||
int *k, *kk;
|
||||
INT32 *k, *kk;
|
||||
double *prekk;
|
||||
|
||||
|
||||
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &prekk);
|
||||
kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &prekk);
|
||||
if ( ! kmax) {
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
}
|
||||
|
||||
kk = malloc(ysize * kmax * sizeof(int));
|
||||
if ( ! kk) {
|
||||
free(xbounds);
|
||||
kmax = normalize_coeffs_8bpc(ysize, kmax, prekk, &kk);
|
||||
free(prekk);
|
||||
if ( ! kmax) {
|
||||
free(xbounds);
|
||||
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);
|
||||
if ( ! imOut) {
|
||||
free(kk);
|
||||
|
@ -396,7 +452,7 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp)
|
|||
int *xbounds;
|
||||
double *k, *kk;
|
||||
|
||||
kmax = ImagingPrecompute(imIn->xsize, xsize, filterp, &xbounds, &kk);
|
||||
kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &kk);
|
||||
if ( ! kmax) {
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
}
|
||||
|
@ -456,7 +512,7 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
|
|||
int *xbounds;
|
||||
double *k, *kk;
|
||||
|
||||
kmax = ImagingPrecompute(imIn->ysize, ysize, filterp, &xbounds, &kk);
|
||||
kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &kk);
|
||||
if ( ! kmax) {
|
||||
return (Imaging) ImagingError_MemoryError();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
unpackRGBAI(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
|
@ -1206,6 +1230,7 @@ static struct {
|
|||
{"RGBA", "LA;16B", 32, unpackRGBALA16B},
|
||||
{"RGBA", "RGBA", 32, copy4},
|
||||
{"RGBA", "RGBa", 32, unpackRGBa},
|
||||
{"RGBA", "BGRa", 32, unpackBGRa},
|
||||
{"RGBA", "RGBA;I", 32, unpackRGBAI},
|
||||
{"RGBA", "RGBA;L", 32, unpackRGBAL},
|
||||
{"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15},
|
||||
|
|
|
@ -28,9 +28,9 @@ libs = {
|
|||
'dir': 'tiff-4.0.6',
|
||||
},
|
||||
'freetype': {
|
||||
'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.3.tar.gz',
|
||||
'hash': 'md5:8b0c80b64042b7c39b9fa9debe6305c3',
|
||||
'dir': 'freetype-2.6.3',
|
||||
'url': 'http://download.savannah.gnu.org/releases/freetype/freetype-2.6.5.tar.gz',
|
||||
'hash': 'md5:31b2276515d9ee1c7f37d9c9f4f3145a',
|
||||
'dir': 'freetype-2.6.5',
|
||||
},
|
||||
'lcms': {
|
||||
'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',
|
||||
|
|
Loading…
Reference in New Issue
Block a user