Merge branch 'master' into jpeg-loading-without-convertion

This commit is contained in:
Alexander 2017-08-28 19:45:19 +03:00
commit d8af3fc23a
59 changed files with 1594 additions and 618 deletions

View File

@ -26,6 +26,8 @@ matrix:
- env: DOCKER="debian-stretch-x86"
- env: DOCKER="centos-6-amd64"
- env: DOCKER="amazon-amd64"
- env: DOCKER="fedora-24-amd64"
- env: DOCKER="fedora-26-amd64"
dist: trusty

View File

@ -4,6 +4,78 @@ Changelog (Pillow)
4.3.0 (unreleased)
------------------
- Region of interest (box) for resampling #2254
[homm]
- Basic support for Termux (android) in setup.py #2684
[wiredfool]
- Bug: Fix Image.fromarray for numpy.bool type. #2683
[wiredfool]
- CI: Add Fedora 24 and 26 to Docker tests
[wiredfool]
- JPEG: Fix ZeroDivisionError when EXIF contains invalid DPI (0/0). #2667
[vytisb]
- Depends: Updated openjpeg to 2.2.0 #2669
[radarhere]
- Depends: Updated Tk Tcl to 8.6.7 #2668
[radarhere]
- Depends: Updated libimagequant to 2.10.2 #2660
[radarhere]
- Test: Added test for ImImagePlugin tell() #2675
[radarhere]
- Test: Additional tests for SGIImagePlugin #2659
[radarhere]
- New Image.getchannel method #2661
[homm]
- Remove unused im.copy2 and core.copy methods #2657
[homm]
- Fast Image.merge() #2677
[homm]
- Fast Image.split() #2676
[homm]
- Fast image allocation #2655
[homm]
- Storage cleanup #2654
[homm]
- FLI: Use frame count from FLI header #2674
[radarhere]
- Test: Test animated FLI file #2650
[hugovk]
- Bug: Fixed uninitialized memory in bc5 decoding #2648
[ifeherva]
- Moved SgiImagePlugin save error to before the start of write operations #2646
[radarhere]
- Move createfontdatachunk.py so isn't installed globally #2645
[hugovk]
- Bug: Fix unexpected keyword argument 'align' #2641
[hugovk]
- Add newlines to error message for clarity #2640
[hugovk]
- Docs: Updated redirected URL #2637
[radarhere]
- Bug: Fix JPEG DPI when EXIF is invalid #2632
[wiredfool]

View File

@ -49,6 +49,9 @@ class FliImageFile(ImageFile.ImageFile):
s[20:22] == b"\x00\x00"): # reserved
raise SyntaxError("not an FLI/FLC file")
# frames
self.__framecount = i16(s[6:8])
# image characteristics
self.mode = "P"
self.size = i16(s[8:10]), i16(s[10:12])
@ -56,7 +59,7 @@ class FliImageFile(ImageFile.ImageFile):
# animation speed
duration = i32(s[16:20])
if magic == 0xAF11:
duration = (duration * 1000) / 70
duration = (duration * 1000) // 70
self.info["duration"] = duration
# look for palette
@ -86,8 +89,6 @@ class FliImageFile(ImageFile.ImageFile):
self.__frame = -1
self.__fp = self.fp
self.__rewind = self.fp.tell()
self._n_frames = None
self._is_animated = None
self.seek(0)
def _palette(self, palette, shift):
@ -110,44 +111,23 @@ class FliImageFile(ImageFile.ImageFile):
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self.seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
return self.__framecount
@property
def is_animated(self):
if self._is_animated is None:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
return self.__framecount > 1
def seek(self, frame):
if frame == self.__frame:
return
if frame < self.__frame:
self._seek(0)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1):
try:
self._seek(f)
except EOFError:
self.seek(last_frame)
if frame >= self.__framecount:
raise EOFError("no more images in FLI file")
for f in range(self.__frame + 1, frame + 1):
self._seek(f)
def _seek(self, frame):
if frame == 0:
self.__frame = -1
@ -176,6 +156,7 @@ class FliImageFile(ImageFile.ImageFile):
def tell(self):
return self.__frame
#
# registry

View File

@ -56,8 +56,9 @@ try:
from . import _imaging as core
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
raise ImportError("The _imaging extension was built for another "
"version of Pillow or PIL: Core Version: %s"
"Pillow Version: %s" %
"version of Pillow or PIL:\n"
"Core version: %s\n"
"Pillow version: %s" %
(getattr(core, 'PILLOW_VERSION', None),
PILLOW_VERSION))
@ -1673,7 +1674,7 @@ class Image(object):
return m_im
def resize(self, size, resample=NEAREST):
def resize(self, size, resample=NEAREST, box=None):
"""
Returns a resized copy of this image.
@ -1686,6 +1687,10 @@ class Image(object):
If omitted, or if the image has mode "1" or "P", it is
set :py:attr:`PIL.Image.NEAREST`.
See: :ref:`concept-filters`.
:param box: An optional 4-tuple of floats giving the region
of the source image which should be scaled.
The values should be within (0, 0, width, height) rectangle.
If omitted or None, the entire source is used.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
@ -1694,22 +1699,28 @@ class Image(object):
):
raise ValueError("unknown resampling filter")
self.load()
size = tuple(size)
if self.size == size:
if box is None:
box = (0, 0) + self.size
else:
box = tuple(box)
if self.size == size and box == (0, 0) + self.size:
return self.copy()
if self.mode in ("1", "P"):
resample = NEAREST
if self.mode == 'LA':
return self.convert('La').resize(size, resample).convert('LA')
return self.convert('La').resize(size, resample, box).convert('LA')
if self.mode == 'RGBA':
return self.convert('RGBa').resize(size, resample).convert('RGBA')
return self.convert('RGBa').resize(size, resample, box).convert('RGBA')
return self._new(self.im.resize(size, resample))
self.load()
return self._new(self.im.resize(size, resample, box))
def rotate(self, angle, resample=NEAREST, expand=0, center=None,
translate=None):
@ -1947,6 +1958,9 @@ class Image(object):
containing a copy of one of the original bands (red, green,
blue).
If you need only one band, :py:meth:`~PIL.Image.Image.getchannel`
method can be more convenient and faster.
:returns: A tuple containing bands.
"""
@ -1954,11 +1968,31 @@ class Image(object):
if self.im.bands == 1:
ims = [self.copy()]
else:
ims = []
for i in range(self.im.bands):
ims.append(self._new(self.im.getband(i)))
ims = map(self._new, self.im.split())
return tuple(ims)
def getchannel(self, channel):
"""
Returns an image containing a single channel of the source image.
:param channel: What channel to return. Could be index
(0 for "R" channel of "RGB") or channel name
("A" for alpha channel of "RGBA").
:returns: An image in "L" mode.
.. versionadded:: 4.3.0
"""
self.load()
if isStringType(channel):
try:
channel = self.getbands().index(channel)
except ValueError:
raise ValueError(
'The image has no channel "{}"'.format(channel))
return self._new(self.im.getband(channel))
def tell(self):
"""
Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`.
@ -2399,7 +2433,7 @@ def fromqpixmap(im):
_fromarray_typemap = {
# (shape, typestr) => mode, rawmode
# first two members of shape are set to one
# ((1, 1), "|b1"): ("1", "1"), # broken
((1, 1), "|b1"): ("1", "1;8"),
((1, 1), "|u1"): ("L", "L"),
((1, 1), "|i1"): ("I", "I;8"),
((1, 1), "<u2"): ("I", "I;16"),
@ -2612,11 +2646,9 @@ def merge(mode, bands):
raise ValueError("mode mismatch")
if im.size != bands[0].size:
raise ValueError("size mismatch")
im = core.new(mode, bands[0].size)
for i in range(getmodebands(mode)):
bands[i].load()
im.putband(bands[i].im, i)
return bands[0]._new(im)
for band in bands:
band.load()
return bands[0]._new(core.merge(mode, *[b.im for b in bands]))
# --------------------------------------------------------------------

View File

@ -67,7 +67,7 @@ class Contrast(_Enhance):
self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))
class Brightness(_Enhance):
@ -82,7 +82,7 @@ class Brightness(_Enhance):
self.degenerate = Image.new(image.mode, image.size, 0)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))
class Sharpness(_Enhance):
@ -97,4 +97,4 @@ class Sharpness(_Enhance):
self.degenerate = image.filter(ImageFilter.SMOOTH)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))

View File

@ -36,6 +36,7 @@ class _imagingft_not_installed(object):
def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed")
try:
from . import _imagingft as core
except ImportError:
@ -113,7 +114,6 @@ class ImageFont(object):
return self.font.getmask(text, mode)
##
# Wrapper for FreeType fonts. Application code should use the
# <b>truetype</b> factory function to create font objects.
@ -162,7 +162,7 @@ class FreeTypeFont(object):
def getmask(self, text, mode="", direction=None, features=None):
return self.getmask2(text, mode, direction=direction, features=features)[0]
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None):
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs):
size, offset = self.font.getsize(text, direction, features)
im = fill("L", size, 0)
self.font.render(text, im.id, mode == "1", direction, features)

View File

@ -131,9 +131,10 @@ def APP(self, marker):
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = dpi, dpi
except (KeyError, SyntaxError):
except (KeyError, SyntaxError, ZeroDivisionError):
# SyntaxError for invalid/unreadable exif
# KeyError for dpi not included
# ZeroDivisionError for invalid dpi rational value
self.info["dpi"] = 72, 72

View File

@ -133,6 +133,7 @@ class _PyAccess32_3(PyAccess):
pixel.r = min(color[0], 255)
pixel.g = min(color[1], 255)
pixel.b = min(color[2], 255)
pixel.a = 255
class _PyAccess32_4(PyAccess):

View File

@ -104,6 +104,11 @@ def _save(im, fp, filename):
z = len(im.mode)
if dim == 1 or dim == 2:
z = 1
# assert we've got the right number of bands.
if len(im.getbands()) != z:
raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
(z, len(im.getbands())))
# Minimum Byte value
pinmin = 0
# Maximum Byte value (255 = 8bits per pixel)
@ -131,11 +136,6 @@ def _save(im, fp, filename):
fp.write(struct.pack('404s', b'')) # dummy
# assert we've got the right number of bands.
if len(im.getbands()) != z:
raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
(z, len(im.getbands())))
for channel in im.split():
fp.write(channel.tobytes())

BIN
Tests/images/a.fli Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -4,17 +4,38 @@ from PIL import Image, FliImagePlugin
# created as an export of a palette image from Gimp2.6
# save as...-> hopper.fli, default options.
test_file = "Tests/images/hopper.fli"
static_test_file = "Tests/images/hopper.fli"
# From https://samples.libav.org/fli-flc/
animated_test_file = "Tests/images/a.fli"
class TestFileFli(PillowTestCase):
def test_sanity(self):
im = Image.open(test_file)
im = Image.open(static_test_file)
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "FLI")
self.assertFalse(im.is_animated)
im = Image.open(animated_test_file)
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (320, 200))
self.assertEqual(im.format, "FLI")
self.assertEqual(im.info["duration"], 71)
self.assertTrue(im.is_animated)
def test_tell(self):
# Arrange
im = Image.open(static_test_file)
# Act
frame = im.tell()
# Assert
self.assertEqual(frame, 0)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
@ -23,12 +44,16 @@ class TestFileFli(PillowTestCase):
lambda: FliImagePlugin.FliImageFile(invalid_file))
def test_n_frames(self):
im = Image.open(test_file)
im = Image.open(static_test_file)
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open(animated_test_file)
self.assertEqual(im.n_frames, 384)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open(test_file)
im = Image.open(animated_test_file)
n_frames = im.n_frames
while True:
@ -39,6 +64,33 @@ class TestFileFli(PillowTestCase):
except EOFError:
self.assertLess(im.tell(), n_frames)
def test_seek_outside(self):
# Test negative seek
im = Image.open(static_test_file)
im.seek(-1)
self.assertEqual(im.tell(), 0)
# Test seek past end of file
self.assertRaises(EOFError, lambda: im.seek(2))
def test_seek_tell(self):
im = Image.open(animated_test_file)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(2)
layer_number = im.tell()
self.assertEqual(layer_number, 2)
if __name__ == '__main__':
unittest.main()

View File

@ -15,6 +15,16 @@ class TestFileIm(PillowTestCase):
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "IM")
def test_tell(self):
# Arrange
im = Image.open(TEST_IM)
# Act
frame = im.tell()
# Assert
self.assertEqual(frame, 0)
def test_n_frames(self):
im = Image.open(TEST_IM)
self.assertEqual(im.n_frames, 1)

View File

@ -535,6 +535,16 @@ class TestFileJpeg(PillowTestCase):
# Act / Assert
self.assertEqual(im.info.get("dpi"), (508, 508))
def test_dpi_exif_zero_division(self):
# Arrange
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
im = Image.open("Tests/images/exif-dpi-zerodivision.jpg")
# Act / Assert
# This should return the default, and not raise a ZeroDivisionError
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_no_dpi_in_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with resolution removed from EXIF:

View File

@ -200,7 +200,7 @@ class TestFilePng(PillowTestCase):
self.assert_image(im, "RGBA", (162, 150))
# image has 124 unique alpha values
self.assertEqual(len(im.split()[3].getcolors()), 124)
self.assertEqual(len(im.getchannel('A').getcolors()), 124)
def test_load_transparent_rgb(self):
test_file = "Tests/images/rgb_trns.png"
@ -212,7 +212,7 @@ class TestFilePng(PillowTestCase):
self.assert_image(im, "RGBA", (64, 64))
# image has 876 transparent pixels
self.assertEqual(im.split()[3].getcolors()[0][0], 876)
self.assertEqual(im.getchannel('A').getcolors()[0][0], 876)
def test_save_p_transparent_palette(self):
in_file = "Tests/images/pil123p.png"
@ -234,7 +234,7 @@ class TestFilePng(PillowTestCase):
self.assert_image(im, "RGBA", (162, 150))
# image has 124 unique alpha values
self.assertEqual(len(im.split()[3].getcolors()), 124)
self.assertEqual(len(im.getchannel('A').getcolors()), 124)
def test_save_p_single_transparency(self):
in_file = "Tests/images/p_trns_single.png"
@ -258,7 +258,7 @@ class TestFilePng(PillowTestCase):
self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0))
# image has 876 transparent pixels
self.assertEqual(im.split()[3].getcolors()[0][0], 876)
self.assertEqual(im.getchannel('A').getcolors()[0][0], 876)
def test_save_p_transparent_black(self):
# check if solid black image with full transparency
@ -287,7 +287,7 @@ class TestFilePng(PillowTestCase):
# There are 559 transparent pixels.
im = im.convert('RGBA')
self.assertEqual(im.split()[3].getcolors()[0][0], 559)
self.assertEqual(im.getchannel('A').getcolors()[0][0], 559)
def test_save_rgb_single_transparency(self):
in_file = "Tests/images/caption_6_33_22.png"

View File

@ -55,6 +55,22 @@ class TestFileSgi(PillowTestCase):
for mode in ('L', 'RGB', 'RGBA'):
roundtrip(hopper(mode))
# Test 1 dimension for an L mode image
roundtrip(Image.new('L', (10, 1)))
def test_unsupported_mode(self):
im = hopper('LA')
out = self.tempfile('temp.sgi')
self.assertRaises(ValueError, lambda: im.save(out, format='sgi'))
def test_incorrect_number_of_bands(self):
im = hopper('YCbCr')
im.mode = 'RGB'
out = self.tempfile('temp.sgi')
self.assertRaises(ValueError, lambda: im.save(out, format='sgi'))
if __name__ == '__main__':
unittest.main()

View File

@ -103,11 +103,11 @@ class TestFormatHSV(PillowTestCase):
# im.split()[0].show()
# comparable.split()[0].show()
self.assert_image_similar(im.split()[0], comparable.split()[0],
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
1, "Hue conversion is wrong")
self.assert_image_similar(im.split()[1], comparable.split()[1],
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
1, "Saturation conversion is wrong")
self.assert_image_similar(im.split()[2], comparable.split()[2],
self.assert_image_similar(im.getchannel(2), comparable.getchannel(2),
1, "Value conversion is wrong")
# print(im.getpixel((192, 64)))
@ -120,11 +120,11 @@ class TestFormatHSV(PillowTestCase):
# print(im.getpixel((192, 64)))
# print(comparable.getpixel((192, 64)))
self.assert_image_similar(im.split()[0], comparable.split()[0],
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
3, "R conversion is wrong")
self.assert_image_similar(im.split()[1], comparable.split()[1],
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
3, "G conversion is wrong")
self.assert_image_similar(im.split()[2], comparable.split()[2],
self.assert_image_similar(im.getchannel(2), comparable.getchannel(2),
3, "B conversion is wrong")
def test_convert(self):
@ -137,11 +137,11 @@ class TestFormatHSV(PillowTestCase):
# print(im.split()[0].histogram())
# print(comparable.split()[0].histogram())
self.assert_image_similar(im.split()[0], comparable.split()[0],
self.assert_image_similar(im.getchannel(0), comparable.getchannel(0),
1, "Hue conversion is wrong")
self.assert_image_similar(im.split()[1], comparable.split()[1],
self.assert_image_similar(im.getchannel(1), comparable.getchannel(1),
1, "Saturation conversion is wrong")
self.assert_image_similar(im.split()[2], comparable.split()[2],
self.assert_image_similar(im.getchannel(2), comparable.getchannel(2),
1, "Value conversion is wrong")
def test_hsv_to_rgb(self):
@ -155,11 +155,14 @@ class TestFormatHSV(PillowTestCase):
# print([ord(x) for x in target.split()[1].tobytes()[:80]])
# print([ord(x) for x in converted.split()[1].tobytes()[:80]])
self.assert_image_similar(converted.split()[0], comparable.split()[0],
self.assert_image_similar(converted.getchannel(0),
comparable.getchannel(0),
3, "R conversion is wrong")
self.assert_image_similar(converted.split()[1], comparable.split()[1],
self.assert_image_similar(converted.getchannel(1),
comparable.getchannel(1),
3, "G conversion is wrong")
self.assert_image_similar(converted.split()[2], comparable.split()[2],
self.assert_image_similar(converted.getchannel(2),
comparable.getchannel(2),
3, "B conversion is wrong")

View File

@ -7,6 +7,25 @@ import sys
class TestImage(PillowTestCase):
def test_image_modes_success(self):
for mode in [
'1', 'P', 'PA',
'L', 'LA', 'La',
'F', 'I', 'I;16', 'I;16L', 'I;16B', 'I;16N',
'RGB', 'RGBX', 'RGBA', 'RGBa',
'CMYK', 'YCbCr', 'LAB', 'HSV',
]:
Image.new(mode, (1, 1))
def test_image_modes_fail(self):
for mode in [
'', 'bad', 'very very long',
'BGR;15', 'BGR;16', 'BGR;24', 'BGR;32'
]:
with self.assertRaises(ValueError) as e:
Image.new(mode, (1, 1));
self.assertEqual(str(e.exception), 'unrecognized image mode')
def test_sanity(self):
im = Image.new("L", (100, 100))
@ -145,14 +164,28 @@ class TestImage(PillowTestCase):
self.assertEqual(im.size[1], orig_size[1] + 2*ymargin)
def test_getbands(self):
# Arrange
# Assert
self.assertEqual(hopper('RGB').getbands(), ('R', 'G', 'B'))
self.assertEqual(hopper('YCbCr').getbands(), ('Y', 'Cb', 'Cr'))
def test_getchannel_wrong_params(self):
im = hopper()
# Act
bands = im.getbands()
self.assertRaises(ValueError, im.getchannel, -1)
self.assertRaises(ValueError, im.getchannel, 3)
self.assertRaises(ValueError, im.getchannel, 'Z')
self.assertRaises(ValueError, im.getchannel, '1')
# Assert
self.assertEqual(bands, ('R', 'G', 'B'))
def test_getchannel(self):
im = hopper('YCbCr')
Y, Cb, Cr = im.split()
self.assert_image_equal(Y, im.getchannel(0))
self.assert_image_equal(Y, im.getchannel('Y'))
self.assert_image_equal(Cb, im.getchannel(1))
self.assert_image_equal(Cb, im.getchannel('Cb'))
self.assert_image_equal(Cr, im.getchannel(2))
self.assert_image_equal(Cr, im.getchannel('Cr'))
def test_getbbox(self):
# Arrange

View File

@ -133,7 +133,7 @@ class TestImageConvert(PillowTestCase):
alpha = hopper('L')
im.putalpha(alpha)
comparable = im.convert('P').convert('LA').split()[1]
comparable = im.convert('P').convert('LA').getchannel('A')
self.assert_image_similar(alpha, comparable, 5)
@ -185,7 +185,7 @@ class TestImageConvert(PillowTestCase):
if converted_im.mode == 'RGB':
self.assert_image_similar(converted_im, target, 3)
else:
self.assert_image_similar(converted_im, target.split()[0], 1)
self.assert_image_similar(converted_im, target.getchannel(0), 1)
matrix_convert('RGB')
matrix_convert('L')

View File

@ -94,6 +94,28 @@ class TestImageFilter(PillowTestCase):
self.assertEqual(rankfilter.size, 1)
self.assertEqual(rankfilter.rank, 2)
def test_consistency_3x3(self):
im = Image.open("Tests/images/hopper.bmp")
emboss = im.filter(ImageFilter.Kernel((3, 3),
(-1, -1, 0,
-1, 0, 1,
0, 1, 1), .3))
self.assert_image_equal(
emboss, Image.open("Tests/images/hopper_emboss.bmp"))
def test_consistency_5x5(self):
im = Image.open("Tests/images/hopper.bmp")
emboss = im.filter(ImageFilter.Kernel((5, 5),
(-1, -1, -1, -1, 0,
-1, -1, -1, 0, 1,
-1, -1, 0, 1, 1,
-1, 0, 1, 1, 1,
0, 1, 1, 1, 1), 0.3))
self.assert_image_equal(
emboss, Image.open("Tests/images/hopper_emboss_more.bmp"))
if __name__ == '__main__':
unittest.main()

View File

@ -1,4 +1,7 @@
from __future__ import print_function
from __future__ import division, print_function
from contextlib import contextmanager
from helper import unittest, PillowTestCase, hopper
from PIL import Image, ImageDraw
@ -300,24 +303,46 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
class CoreResamplePassesTest(PillowTestCase):
@contextmanager
def count(self, diff):
count = Image.core.getcount()
yield
self.assertEqual(Image.core.getcount() - count, diff)
def test_horizontal(self):
im = hopper('L')
count = Image.core.getcount()
im.resize((im.size[0] + 10, im.size[1]), Image.BILINEAR)
self.assertEqual(Image.core.getcount(), count + 1)
with self.count(1):
im.resize((im.size[0] - 10, im.size[1]), Image.BILINEAR)
def test_vertical(self):
im = hopper('L')
count = Image.core.getcount()
im.resize((im.size[0], im.size[1] + 10), Image.BILINEAR)
self.assertEqual(Image.core.getcount(), count + 1)
with self.count(1):
im.resize((im.size[0], im.size[1] - 10), Image.BILINEAR)
def test_both(self):
im = hopper('L')
count = Image.core.getcount()
im.resize((im.size[0] + 10, im.size[1] + 10), Image.BILINEAR)
self.assertEqual(Image.core.getcount(), count + 2)
with self.count(2):
im.resize((im.size[0] - 10, im.size[1] - 10), Image.BILINEAR)
def test_box_horizontal(self):
im = hopper('L')
box = (20, 0, im.size[0] - 20, im.size[1])
with self.count(1):
# the same size, but different box
with_box = im.resize(im.size, Image.BILINEAR, box)
with self.count(2):
cropped = im.crop(box).resize(im.size, Image.BILINEAR)
self.assert_image_similar(with_box, cropped, 0.1)
def test_box_vertical(self):
im = hopper('L')
box = (0, 20, im.size[0], im.size[1] - 20)
with self.count(1):
# the same size, but different box
with_box = im.resize(im.size, Image.BILINEAR, box)
with self.count(2):
cropped = im.crop(box).resize(im.size, Image.BILINEAR)
self.assert_image_similar(with_box, cropped, 0.1)
class CoreResampleCoefficientsTest(PillowTestCase):
def test_reduce(self):
@ -347,5 +372,92 @@ class CoreResampleCoefficientsTest(PillowTestCase):
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel
class CoreResampleBoxTest(PillowTestCase):
def test_wrong_arguments(self):
im = hopper()
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
Image.BICUBIC, Image.LANCZOS):
im.resize((32, 32), resample, (0, 0, im.width, im.height))
im.resize((32, 32), resample, (20, 20, im.width, im.height))
im.resize((32, 32), resample, (20, 20, 20, 100))
im.resize((32, 32), resample, (20, 20, 100, 20))
with self.assertRaisesRegexp(TypeError, "must be sequence of length 4"):
im.resize((32, 32), resample, (im.width, im.height))
with self.assertRaisesRegexp(ValueError, "can't be negative"):
im.resize((32, 32), resample, (-20, 20, 100, 100))
with self.assertRaisesRegexp(ValueError, "can't be negative"):
im.resize((32, 32), resample, (20, -20, 100, 100))
with self.assertRaisesRegexp(ValueError, "can't be empty"):
im.resize((32, 32), resample, (20.1, 20, 20, 100))
with self.assertRaisesRegexp(ValueError, "can't be empty"):
im.resize((32, 32), resample, (20, 20.1, 100, 20))
with self.assertRaisesRegexp(ValueError, "can't be empty"):
im.resize((32, 32), resample, (20.1, 20.1, 20, 20))
with self.assertRaisesRegexp(ValueError, "can't exceed"):
im.resize((32, 32), resample, (0, 0, im.width + 1, im.height))
with self.assertRaisesRegexp(ValueError, "can't exceed"):
im.resize((32, 32), resample, (0, 0, im.width, im.height + 1))
def resize_tiled(self, im, dst_size, xtiles, ytiles):
def split_range(size, tiles):
scale = size / tiles
for i in range(tiles):
yield (int(round(scale * i)), int(round(scale * (i + 1))))
tiled = Image.new(im.mode, dst_size)
scale = (im.size[0] / tiled.size[0], im.size[1] / tiled.size[1])
for y0, y1 in split_range(dst_size[1], ytiles):
for x0, x1 in split_range(dst_size[0], xtiles):
box = (x0 * scale[0], y0 * scale[1],
x1 * scale[0], y1 * scale[1])
tile = im.resize((x1 - x0, y1 - y0), Image.BICUBIC, box)
tiled.paste(tile, (x0, y0))
return tiled
def test_tiles(self):
im = Image.open("Tests/images/flower.jpg")
assert im.size == (480, 360)
dst_size = (251, 188)
reference = im.resize(dst_size, Image.BICUBIC)
for tiles in [(1, 1), (3, 3), (9, 7), (100, 100)]:
tiled = self.resize_tiled(im, dst_size, *tiles)
self.assert_image_similar(reference, tiled, 0.01)
def test_subsample(self):
# This test shows advantages of the subpixel resizing
# after supersampling (e.g. during JPEG decoding).
im = Image.open("Tests/images/flower.jpg")
assert im.size == (480, 360)
dst_size = (48, 36)
# Reference is cropped image resized to destination
reference = im.crop((0, 0, 473, 353)).resize(dst_size, Image.BICUBIC)
# Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45)
supersampled = im.resize((60, 45), Image.BOX)
with_box = supersampled.resize(dst_size, Image.BICUBIC,
(0, 0, 59.125, 44.125))
without_box = supersampled.resize(dst_size, Image.BICUBIC)
# error with box should be much smaller than without
self.assert_image_similar(reference, with_box, 6)
with self.assertRaisesRegexp(AssertionError, "difference 29\."):
self.assert_image_similar(reference, without_box, 5)
def test_formats(self):
for resample in [Image.NEAREST, Image.BILINEAR]:
for mode in ['RGB', 'L', 'RGBA', 'LA', 'I', '']:
im = hopper(mode)
box = (20, 20, im.size[0] - 20, im.size[1] - 20)
with_box = im.resize((32, 32), resample, box)
cropped = im.crop(box).resize((32, 32), resample)
self.assert_image_similar(cropped, with_box, 0.4)
if __name__ == '__main__':
unittest.main()

View File

@ -360,8 +360,7 @@ class TestImageCms(PillowTestCase):
return Image.merge(mode, chans)
source_image = create_test_image()
preserved_channel_ndx = source_image.getbands().index(preserved_channel)
source_image_aux = source_image.split()[preserved_channel_ndx]
source_image_aux = source_image.getchannel(preserved_channel)
# create some transform, it doesn't matter which one
source_profile = ImageCms.createProfile("sRGB")
@ -374,7 +373,7 @@ class TestImageCms(PillowTestCase):
result_image = source_image
else:
result_image = ImageCms.applyTransform(source_image, t, inPlace=False)
result_image_aux = result_image.split()[preserved_channel_ndx]
result_image_aux = result_image.getchannel(preserved_channel)
self.assert_image_equal(source_image_aux, result_image_aux)

View File

@ -34,7 +34,7 @@ class TestImageEnhance(PillowTestCase):
def _check_alpha(self, im, original, op, amount):
self.assertEqual(im.getbands(), original.getbands())
self.assert_image_equal(im.split()[-1], original.split()[-1],
self.assert_image_equal(im.getchannel('A'), original.getchannel('A'),
"Diff on %s: %s" % (op, amount))
def test_alpha(self):

View File

@ -40,13 +40,15 @@ class SimplePatcher(object):
else:
delattr(self._parent_obj, self._attr_name)
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not Available")
class TestImageFont(PillowTestCase):
LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC
# Freetype has different metrics depending on the version.
# (and, other things, but first things first)
METRICS = { ('2', '3'): {'multiline': 30,
METRICS = {
('2', '3'): {'multiline': 30,
'textsize': 12,
'getters': (13, 16)},
('2', '7'): {'multiline': 6.2,
@ -213,6 +215,14 @@ class TestImageFont(PillowTestCase):
font=ttf,
align="unknown"))
def test_draw_align(self):
im = Image.new('RGB', (300, 100), 'white')
draw = ImageDraw.Draw(im)
ttf = self.get_font()
line = "some text"
draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left')
del draw
def test_multiline_size(self):
ttf = self.get_font()
im = Image.new(mode='RGB', size=(300, 100))
@ -494,5 +504,6 @@ class TestImageFont(PillowTestCase):
class TestImageFont_RaqmLayout(TestImageFont):
LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM
if __name__ == '__main__':
unittest.main()

View File

@ -1,157 +1,481 @@
import sys
from helper import unittest, PillowTestCase, py3
from PIL import Image
X = 255
class TestLibPack(PillowTestCase):
def assert_pack(self, mode, rawmode, data, *pixels):
"""
data - either raw bytes with data or just number of bytes in rawmode.
"""
im = Image.new(mode, (len(pixels), 1))
for x, pixel in enumerate(pixels):
im.putpixel((x, 0), pixel)
def pack(self):
pass # not yet
if isinstance(data, (int)):
data_len = data * len(pixels)
data = bytes(bytearray(range(1, data_len + 1)))
def test_pack(self):
self.assertEqual(data, im.tobytes("raw", rawmode))
def pack(mode, rawmode, in_data=(1, 2, 3, 4)):
if len(mode) == 1:
im = Image.new(mode, (1, 1), in_data[0])
def test_1(self):
self.assert_pack("1", "1", b'\x01', 0,0,0,0,0,0,0,X)
self.assert_pack("1", "1;I", b'\x01', X,X,X,X,X,X,X,0)
self.assert_pack("1", "1;R", b'\x01', X,0,0,0,0,0,0,0)
self.assert_pack("1", "1;IR", b'\x01', 0,X,X,X,X,X,X,X)
self.assert_pack("1", "1", b'\xaa', X,0,X,0,X,0,X,0)
self.assert_pack("1", "1;I", b'\xaa', 0,X,0,X,0,X,0,X)
self.assert_pack("1", "1;R", b'\xaa', 0,X,0,X,0,X,0,X)
self.assert_pack("1", "1;IR", b'\xaa', X,0,X,0,X,0,X,0)
self.assert_pack("1", "L", b'\xff\x00\x00\xff\x00\x00', X,0,0,X,0,0)
def test_L(self):
self.assert_pack("L", "L", 1, 1,2,3,4)
def test_LA(self):
self.assert_pack("LA", "LA", 2, (1,2), (3,4), (5,6))
self.assert_pack("LA", "LA;L", 2, (1,4), (2,5), (3,6))
def test_P(self):
self.assert_pack("P", "P;1", b'\xe4', 1, 1, 1, 0, 0, 255, 0, 0)
self.assert_pack("P", "P;2", b'\xe4', 3, 2, 1, 0)
self.assert_pack("P", "P;4", b'\x02\xef', 0, 2, 14, 15)
self.assert_pack("P", "P", 1, 1, 2, 3, 4)
def test_PA(self):
self.assert_pack("PA", "PA", 2, (1,2), (3,4), (5,6))
self.assert_pack("PA", "PA;L", 2, (1,4), (2,5), (3,6))
def test_RGB(self):
self.assert_pack("RGB", "RGB", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_pack("RGB", "RGBX",
b'\x01\x02\x03\xff\x05\x06\x07\xff', (1,2,3), (5,6,7))
self.assert_pack("RGB", "XRGB",
b'\x00\x02\x03\x04\x00\x06\x07\x08', (2,3,4), (6,7,8))
self.assert_pack("RGB", "BGR", 3, (3,2,1), (6,5,4), (9,8,7))
self.assert_pack("RGB", "BGRX",
b'\x01\x02\x03\x00\x05\x06\x07\x00', (3,2,1), (7,6,5))
self.assert_pack("RGB", "XBGR",
b'\x00\x02\x03\x04\x00\x06\x07\x08', (4,3,2), (8,7,6))
self.assert_pack("RGB", "RGB;L", 3, (1,4,7), (2,5,8), (3,6,9))
self.assert_pack("RGB", "R", 1, (1,9,9), (2,9,9), (3,9,9))
self.assert_pack("RGB", "G", 1, (9,1,9), (9,2,9), (9,3,9))
self.assert_pack("RGB", "B", 1, (9,9,1), (9,9,2), (9,9,3))
def test_RGBA(self):
self.assert_pack("RGBA", "RGBA", 4,
(1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_pack("RGBA", "RGBA;L", 4,
(1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_pack("RGBA", "RGB", 3, (1,2,3,14), (4,5,6,15), (7,8,9,16))
self.assert_pack("RGBA", "BGR", 3, (3,2,1,14), (6,5,4,15), (9,8,7,16))
self.assert_pack("RGBA", "BGRA", 4,
(3,2,1,4), (7,6,5,8), (11,10,9,12))
self.assert_pack("RGBA", "ABGR", 4,
(4,3,2,1), (8,7,6,5), (12,11,10,9))
self.assert_pack("RGBA", "BGRa", 4,
(191,127,63,4), (223,191,159,8), (233,212,191,12))
self.assert_pack("RGBA", "R", 1, (1,0,8,9), (2,0,8,9), (3,0,8,0))
self.assert_pack("RGBA", "G", 1, (6,1,8,9), (6,2,8,9), (6,3,8,9))
self.assert_pack("RGBA", "B", 1, (6,7,1,9), (6,7,2,0), (6,7,3,9))
self.assert_pack("RGBA", "A", 1, (6,7,0,1), (6,7,0,2), (0,7,0,3))
def test_RGBa(self):
self.assert_pack("RGBa", "RGBa", 4,
(1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_pack("RGBa", "BGRa", 4,
(3,2,1,4), (7,6,5,8), (11,10,9,12))
self.assert_pack("RGBa", "aBGR", 4,
(4,3,2,1), (8,7,6,5), (12,11,10,9))
def test_RGBX(self):
self.assert_pack("RGBX", "RGBX", 4, (1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_pack("RGBX", "RGBX;L", 4, (1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_pack("RGBX", "RGB", 3, (1,2,3,X), (4,5,6,X), (7,8,9,X))
self.assert_pack("RGBX", "BGR", 3, (3,2,1,X), (6,5,4,X), (9,8,7,X))
self.assert_pack("RGBX", "BGRX",
b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00',
(3,2,1,X), (7,6,5,X), (11,10,9,X))
self.assert_pack("RGBX", "XBGR",
b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c',
(4,3,2,X), (8,7,6,X), (12,11,10,X))
self.assert_pack("RGBX", "R", 1, (1,0,8,9), (2,0,8,9), (3,0,8,0))
self.assert_pack("RGBX", "G", 1, (6,1,8,9), (6,2,8,9), (6,3,8,9))
self.assert_pack("RGBX", "B", 1, (6,7,1,9), (6,7,2,0), (6,7,3,9))
self.assert_pack("RGBX", "X", 1, (6,7,0,1), (6,7,0,2), (0,7,0,3))
def test_CMYK(self):
self.assert_pack("CMYK", "CMYK", 4, (1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_pack("CMYK", "CMYK;I", 4,
(254,253,252,251), (250,249,248,247), (246,245,244,243))
self.assert_pack("CMYK", "CMYK;L", 4,
(1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_pack("CMYK", "K", 1, (6,7,0,1), (6,7,0,2), (0,7,0,3))
def test_YCbCr(self):
self.assert_pack("YCbCr", "YCbCr", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_pack("YCbCr", "YCbCr;L", 3, (1,4,7), (2,5,8), (3,6,9))
self.assert_pack("YCbCr", "YCbCrX",
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
(1,2,3), (5,6,7), (9,10,11))
self.assert_pack("YCbCr", "YCbCrK",
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
(1,2,3), (5,6,7), (9,10,11))
self.assert_pack("YCbCr", "Y", 1, (1,0,8,9), (2,0,8,9), (3,0,8,0))
self.assert_pack("YCbCr", "Cb", 1, (6,1,8,9), (6,2,8,9), (6,3,8,9))
self.assert_pack("YCbCr", "Cr", 1, (6,7,1,9), (6,7,2,0), (6,7,3,9))
def test_LAB(self):
self.assert_pack("LAB", "LAB", 3,
(1,130,131), (4,133,134), (7,136,137))
self.assert_pack("LAB", "L", 1, (1,9,9), (2,9,9), (3,9,9))
self.assert_pack("LAB", "A", 1, (9,1,9), (9,2,9), (9,3,9))
self.assert_pack("LAB", "B", 1, (9,9,1), (9,9,2), (9,9,3))
def test_HSV(self):
self.assert_pack("HSV", "HSV", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_pack("HSV", "H", 1, (1,9,9), (2,9,9), (3,9,9))
self.assert_pack("HSV", "S", 1, (9,1,9), (9,2,9), (9,3,9))
self.assert_pack("HSV", "V", 1, (9,9,1), (9,9,2), (9,9,3))
def test_I(self):
self.assert_pack("I", "I;16B", 2, 0x0102, 0x0304)
self.assert_pack("I", "I;32S",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
0x01000083, -2097151999)
if sys.byteorder == 'little':
self.assert_pack("I", "I", 4, 0x04030201, 0x08070605)
self.assert_pack("I", "I;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
0x01000083, -2097151999)
else:
im = Image.new(mode, (1, 1), in_data[:len(mode)])
self.assert_pack("I", "I", 4, 0x01020304, 0x05060708)
self.assert_pack("I", "I;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
-2097151999, 0x01000083)
if py3:
return list(im.tobytes("raw", rawmode))
def test_F_float(self):
self.assert_pack("F", "F;32F", 4,
1.539989614439558e-36, 4.063216068939723e-34)
if sys.byteorder == 'little':
self.assert_pack("F", "F", 4,
1.539989614439558e-36, 4.063216068939723e-34)
self.assert_pack("F", "F;32NF", 4,
1.539989614439558e-36, 4.063216068939723e-34)
else:
return [ord(c) for c in im.tobytes("raw", rawmode)]
self.assert_pack("F", "F", 4,
2.387939260590663e-38, 6.301941157072183e-36)
self.assert_pack("F", "F;32NF", 4,
2.387939260590663e-38, 6.301941157072183e-36)
order = 1 if Image._ENDIAN == '<' else -1
self.assertEqual(pack("1", "1"), [128])
self.assertEqual(pack("1", "1;I"), [0])
self.assertEqual(pack("1", "1;R"), [1])
self.assertEqual(pack("1", "1;IR"), [0])
class TestLibUnpack(PillowTestCase):
def assert_unpack(self, mode, rawmode, data, *pixels):
"""
data - either raw bytes with data or just number of bytes in rawmode.
"""
if isinstance(data, (int)):
data_len = data * len(pixels)
data = bytes(bytearray(range(1, data_len + 1)))
self.assertEqual(pack("L", "L"), [1])
im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1)
self.assertEqual(pack("I", "I"), [1, 0, 0, 0][::order])
for x, pixel in enumerate(pixels):
self.assertEqual(pixel, im.getpixel((x, 0)))
self.assertEqual(pack("F", "F"), [0, 0, 128, 63][::order])
def test_1(self):
self.assert_unpack("1", "1", b'\x01', 0, 0, 0, 0, 0, 0, 0, X)
self.assert_unpack("1", "1;I", b'\x01', X, X, X, X, X, X, X, 0)
self.assert_unpack("1", "1;R", b'\x01', X, 0, 0, 0, 0, 0, 0, 0)
self.assert_unpack("1", "1;IR", b'\x01', 0, X, X, X, X, X, X, X)
self.assertEqual(pack("LA", "LA"), [1, 2])
self.assert_unpack("1", "1", b'\xaa', X, 0, X, 0, X, 0, X, 0)
self.assert_unpack("1", "1;I", b'\xaa', 0, X, 0, X, 0, X, 0, X)
self.assert_unpack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X)
self.assert_unpack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0)
self.assertEqual(pack("RGB", "RGB"), [1, 2, 3])
self.assertEqual(pack("RGB", "RGB;L"), [1, 2, 3])
self.assertEqual(pack("RGB", "BGR"), [3, 2, 1])
self.assertEqual(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255?
self.assertEqual(pack("RGB", "BGRX"), [3, 2, 1, 0])
self.assertEqual(pack("RGB", "XRGB"), [0, 1, 2, 3])
self.assertEqual(pack("RGB", "XBGR"), [0, 3, 2, 1])
self.assert_unpack("1", "1;8", b'\x00\x01\x02\xff', 0, X, X, X)
self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255?
def test_L(self):
self.assert_unpack("L", "L;2", b'\xe4', 255, 170, 85, 0)
self.assert_unpack("L", "L;2I", b'\xe4', 0, 85, 170, 255)
self.assert_unpack("L", "L;2R", b'\xe4', 0, 170, 85, 255)
self.assert_unpack("L", "L;2IR", b'\xe4', 255, 85, 170, 0)
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.assert_unpack("L", "L;4", b'\x02\xef', 0, 34, 238, 255)
self.assert_unpack("L", "L;4I", b'\x02\xef', 255, 221, 17, 0)
self.assert_unpack("L", "L;4R", b'\x02\xef', 68, 0, 255, 119)
self.assert_unpack("L", "L;4IR", b'\x02\xef', 187, 255, 0, 136)
self.assertEqual(pack("RGBa", "RGBa"), [1, 2, 3, 4])
self.assertEqual(pack("RGBa", "BGRa"), [3, 2, 1, 4])
self.assertEqual(pack("RGBa", "aBGR"), [4, 3, 2, 1])
self.assert_unpack("L", "L", 1, 1, 2, 3, 4)
self.assert_unpack("L", "L;I", 1, 254, 253, 252, 251)
self.assert_unpack("L", "L;R", 1, 128, 64, 192, 32)
self.assert_unpack("L", "L;16", 2, 2, 4, 6, 8)
self.assert_unpack("L", "L;16B", 2, 1, 3, 5, 7)
self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4])
self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3])
def test_LA(self):
self.assert_unpack("LA", "LA", 2, (1, 2), (3, 4), (5, 6))
self.assert_unpack("LA", "LA;L", 2, (1, 4), (2, 5), (3, 6))
def test_unpack(self):
def test_P(self):
self.assert_unpack("P", "P;1", b'\xe4', 1, 1, 1, 0, 0, 1, 0, 0)
self.assert_unpack("P", "P;2", b'\xe4', 3, 2, 1, 0)
# self.assert_unpack("P", "P;2L", b'\xe4', 1, 1, 1, 0) # erroneous?
self.assert_unpack("P", "P;4", b'\x02\xef', 0, 2, 14, 15)
# self.assert_unpack("P", "P;4L", b'\x02\xef', 2, 10, 10, 0) # erroneous?
self.assert_unpack("P", "P", 1, 1, 2, 3, 4)
self.assert_unpack("P", "P;R", 1, 128, 64, 192, 32)
def unpack(mode, rawmode, bytes_):
im = None
def test_PA(self):
self.assert_unpack("PA", "PA", 2, (1, 2), (3, 4), (5, 6))
self.assert_unpack("PA", "PA;L", 2, (1, 4), (2, 5), (3, 6))
if py3:
data = bytes(range(1, bytes_+1))
def test_RGB(self):
self.assert_unpack("RGB", "RGB", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_unpack("RGB", "RGB;L", 3, (1,4,7), (2,5,8), (3,6,9))
self.assert_unpack("RGB", "RGB;R", 3, (128,64,192), (32,160,96))
self.assert_unpack("RGB", "RGB;16B", 6, (1,3,5), (7,9,11))
self.assert_unpack("RGB", "BGR", 3, (3,2,1), (6,5,4), (9,8,7))
self.assert_unpack("RGB", "RGB;15", 2, (8,131,0), (24,0,8))
self.assert_unpack("RGB", "BGR;15", 2, (0,131,8), (8,0,24))
self.assert_unpack("RGB", "RGB;16", 2, (8,64,0), (24,129,0))
self.assert_unpack("RGB", "BGR;16", 2, (0,64,8), (0,129,24))
self.assert_unpack("RGB", "RGB;4B", 2, (17,0,34), (51,0,68))
self.assert_unpack("RGB", "RGBX", 4, (1,2,3), (5,6,7), (9,10,11))
self.assert_unpack("RGB", "RGBX;L", 4, (1,4,7), (2,5,8), (3,6,9))
self.assert_unpack("RGB", "BGRX", 4, (3,2,1), (7,6,5), (11,10,9))
self.assert_unpack("RGB", "XRGB", 4, (2,3,4), (6,7,8), (10,11,12))
self.assert_unpack("RGB", "XBGR", 4, (4,3,2), (8,7,6), (12,11,10))
self.assert_unpack("RGB", "YCC;P",
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
(127,102,0), (192,227,0), (213,255,170), (98,255,133))
self.assert_unpack("RGB", "R", 1, (1,0,0), (2,0,0), (3,0,0))
self.assert_unpack("RGB", "G", 1, (0,1,0), (0,2,0), (0,3,0))
self.assert_unpack("RGB", "B", 1, (0,0,1), (0,0,2), (0,0,3))
def test_RGBA(self):
self.assert_unpack("RGBA", "LA", 2, (1,1,1,2), (3,3,3,4), (5,5,5,6))
self.assert_unpack("RGBA", "LA;16B", 4,
(1,1,1,3), (5,5,5,7), (9,9,9,11))
self.assert_unpack("RGBA", "RGBA", 4,
(1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_unpack("RGBA", "RGBa", 4,
(63,127,191,4), (159,191,223,8), (191,212,233,12))
self.assert_unpack("RGBA", "BGRa", 4,
(191,127,63,4), (223,191,159,8), (233,212,191,12))
self.assert_unpack("RGBA", "RGBA;I", 4,
(254,253,252,4), (250,249,248,8), (246,245,244,12))
self.assert_unpack("RGBA", "RGBA;L", 4,
(1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_unpack("RGBA", "RGBA;15", 2, (8,131,0,0), (24,0,8,0))
self.assert_unpack("RGBA", "BGRA;15", 2, (0,131,8,0), (8,0,24,0))
self.assert_unpack("RGBA", "RGBA;4B", 2, (17,0,34,0), (51,0,68,0))
self.assert_unpack("RGBA", "RGBA;16B", 8, (1,3,5,7), (9,11,13,15))
self.assert_unpack("RGBA", "BGRA", 4,
(3,2,1,4), (7,6,5,8), (11,10,9,12))
self.assert_unpack("RGBA", "ARGB", 4,
(2,3,4,1), (6,7,8,5), (10,11,12,9))
self.assert_unpack("RGBA", "ABGR", 4,
(4,3,2,1), (8,7,6,5), (12,11,10,9))
self.assert_unpack("RGBA", "YCCA;P",
b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data
(0,161,0,4), (255,255,255,237), (27,158,0,206), (0,118,0,17))
self.assert_unpack("RGBA", "R", 1, (1,0,0,0), (2,0,0,0), (3,0,0,0))
self.assert_unpack("RGBA", "G", 1, (0,1,0,0), (0,2,0,0), (0,3,0,0))
self.assert_unpack("RGBA", "B", 1, (0,0,1,0), (0,0,2,0), (0,0,3,0))
self.assert_unpack("RGBA", "A", 1, (0,0,0,1), (0,0,0,2), (0,0,0,3))
def test_RGBa(self):
self.assert_unpack("RGBa", "RGBa", 4,
(1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_unpack("RGBa", "BGRa", 4,
(3,2,1,4), (7,6,5,8), (11,10,9,12))
self.assert_unpack("RGBa", "aRGB", 4,
(2,3,4,1), (6,7,8,5), (10,11,12,9))
self.assert_unpack("RGBa", "aBGR", 4,
(4,3,2,1), (8,7,6,5), (12,11,10,9))
def test_RGBX(self):
self.assert_unpack("RGBX", "RGB", 3, (1,2,3,X), (4,5,6,X), (7,8,9,X))
self.assert_unpack("RGBX", "RGB;L", 3, (1,4,7,X), (2,5,8,X), (3,6,9,X))
self.assert_unpack("RGBX", "RGB;16B", 6, (1,3,5,X), (7,9,11,X))
self.assert_unpack("RGBX", "BGR", 3, (3,2,1,X), (6,5,4,X), (9,8,7,X))
self.assert_unpack("RGBX", "RGB;15", 2, (8,131,0,X), (24,0,8,X))
self.assert_unpack("RGBX", "BGR;15", 2, (0,131,8,X), (8,0,24,X))
self.assert_unpack("RGBX", "RGB;4B", 2, (17,0,34,X), (51,0,68,X))
self.assert_unpack("RGBX", "RGBX", 4,
(1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_unpack("RGBX", "RGBX;L", 4,
(1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_unpack("RGBX", "BGRX", 4, (3,2,1,X), (7,6,5,X), (11,10,9,X))
self.assert_unpack("RGBX", "XRGB", 4, (2,3,4,X), (6,7,8,X), (10,11,12,X))
self.assert_unpack("RGBX", "XBGR", 4, (4,3,2,X), (8,7,6,X), (12,11,10,X))
self.assert_unpack("RGBX", "YCC;P",
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
(127,102,0,X), (192,227,0,X), (213,255,170,X), (98,255,133,X))
self.assert_unpack("RGBX", "R", 1, (1,0,0,0), (2,0,0,0), (3,0,0,0))
self.assert_unpack("RGBX", "G", 1, (0,1,0,0), (0,2,0,0), (0,3,0,0))
self.assert_unpack("RGBX", "B", 1, (0,0,1,0), (0,0,2,0), (0,0,3,0))
self.assert_unpack("RGBX", "X", 1, (0,0,0,1), (0,0,0,2), (0,0,0,3))
def test_CMYK(self):
self.assert_unpack("CMYK", "CMYK", 4, (1,2,3,4), (5,6,7,8), (9,10,11,12))
self.assert_unpack("CMYK", "CMYK;I", 4,
(254,253,252,251), (250,249,248,247), (246,245,244,243))
self.assert_unpack("CMYK", "CMYK;L", 4,
(1,4,7,10), (2,5,8,11), (3,6,9,12))
self.assert_unpack("CMYK", "C", 1, (1,0,0,0), (2,0,0,0), (3,0,0,0))
self.assert_unpack("CMYK", "M", 1, (0,1,0,0), (0,2,0,0), (0,3,0,0))
self.assert_unpack("CMYK", "Y", 1, (0,0,1,0), (0,0,2,0), (0,0,3,0))
self.assert_unpack("CMYK", "K", 1, (0,0,0,1), (0,0,0,2), (0,0,0,3))
self.assert_unpack("CMYK", "C;I", 1,
(254,0,0,0), (253,0,0,0), (252,0,0,0))
self.assert_unpack("CMYK", "M;I", 1,
(0,254,0,0), (0,253,0,0), (0,252,0,0))
self.assert_unpack("CMYK", "Y;I", 1,
(0,0,254,0), (0,0,253,0), (0,0,252,0))
self.assert_unpack("CMYK", "K;I", 1,
(0,0,0,254), (0,0,0,253), (0,0,0,252))
def test_YCbCr(self):
self.assert_unpack("YCbCr", "YCbCr", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_unpack("YCbCr", "YCbCr;L", 3, (1,4,7), (2,5,8), (3,6,9))
self.assert_unpack("YCbCr", "YCbCrX", 4, (1,2,3), (5,6,7), (9,10,11))
self.assert_unpack("YCbCr", "YCbCrK", 4, (1,2,3), (5,6,7), (9,10,11))
def test_LAB(self):
self.assert_unpack("LAB", "LAB", 3,
(1,130,131), (4,133,134), (7,136,137))
self.assert_unpack("LAB", "L", 1, (1,0,0), (2,0,0), (3,0,0))
self.assert_unpack("LAB", "A", 1, (0,1,0), (0,2,0), (0,3,0))
self.assert_unpack("LAB", "B", 1, (0,0,1), (0,0,2), (0,0,3))
def test_HSV(self):
self.assert_unpack("HSV", "HSV", 3, (1,2,3), (4,5,6), (7,8,9))
self.assert_unpack("HSV", "H", 1, (1,0,0), (2,0,0), (3,0,0))
self.assert_unpack("HSV", "S", 1, (0,1,0), (0,2,0), (0,3,0))
self.assert_unpack("HSV", "V", 1, (0,0,1), (0,0,2), (0,0,3))
def test_I(self):
self.assert_unpack("I", "I;8", 1, 0x01, 0x02, 0x03, 0x04)
self.assert_unpack("I", "I;8S", b'\x01\x83', 1, -125)
self.assert_unpack("I", "I;16", 2, 0x0201, 0x0403)
self.assert_unpack("I", "I;16S", b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("I", "I;16B", 2, 0x0102, 0x0304)
self.assert_unpack("I", "I;16BS", b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("I", "I;32", 4, 0x04030201, 0x08070605)
self.assert_unpack("I", "I;32S",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
0x01000083, -2097151999)
self.assert_unpack("I", "I;32B", 4, 0x01020304, 0x05060708)
self.assert_unpack("I", "I;32BS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
-2097151999, 0x01000083)
if sys.byteorder == 'little':
self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605)
self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403)
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605)
self.assert_unpack("I", "I;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
0x01000083, -2097151999)
else:
data = ''.join(chr(i) for i in range(1, bytes_+1))
self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708)
self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304)
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708)
self.assert_unpack("I", "I;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
-2097151999, 0x01000083)
im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1)
def test_F_int(self):
self.assert_unpack("F", "F;8", 1, 0x01, 0x02, 0x03, 0x04)
self.assert_unpack("F", "F;8S", b'\x01\x83', 1, -125)
self.assert_unpack("F", "F;16", 2, 0x0201, 0x0403)
self.assert_unpack("F", "F;16S", b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("F", "F;16B", 2, 0x0102, 0x0304)
self.assert_unpack("F", "F;16BS", b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("F", "F;32", 4, 67305984, 134678016)
self.assert_unpack("F", "F;32S",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
16777348, -2097152000)
self.assert_unpack("F", "F;32B", 4, 0x01020304, 0x05060708)
self.assert_unpack("F", "F;32BS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
-2097152000, 16777348)
return im.getpixel((0, 0))
def unpack_1(mode, rawmode, value):
assert mode == "1"
im = None
if py3:
im = Image.frombytes(
mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1)
if sys.byteorder == 'little':
self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403)
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', 0x0183, -31999)
self.assert_unpack("F", "F;32N", 4, 67305984, 134678016)
self.assert_unpack("F", "F;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
16777348, -2097152000)
else:
im = Image.frombytes(
mode, (8, 1), chr(value), "raw", rawmode, 0, 1)
self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304)
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', -31999, 0x0183)
self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708)
self.assert_unpack("F", "F;32NS",
b'\x83\x00\x00\x01\x01\x00\x00\x83',
-2097152000, 16777348)
return tuple(im.getdata())
def test_F_float(self):
self.assert_unpack("F", "F;32F", 4,
1.539989614439558e-36, 4.063216068939723e-34)
self.assert_unpack("F", "F;32BF", 4,
2.387939260590663e-38, 6.301941157072183e-36)
self.assert_unpack("F", "F;64F",
b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', # by struct.pack
0.15000000596046448, -1234.5)
self.assert_unpack("F", "F;64BF",
b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', # by struct.pack
0.15000000596046448, -1234.5)
X = 255
if sys.byteorder == 'little':
self.assert_unpack("F", "F", 4,
1.539989614439558e-36, 4.063216068939723e-34)
self.assert_unpack("F", "F;32NF", 4,
1.539989614439558e-36, 4.063216068939723e-34)
self.assert_unpack("F", "F;64NF",
b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0',
0.15000000596046448, -1234.5)
else:
self.assert_unpack("F", "F", 4,
2.387939260590663e-38, 6.301941157072183e-36)
self.assert_unpack("F", "F;32NF", 4,
2.387939260590663e-38, 6.301941157072183e-36)
self.assert_unpack("F", "F;64NF",
b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00',
0.15000000596046448, -1234.5)
self.assertEqual(unpack_1("1", "1", 1), (0, 0, 0, 0, 0, 0, 0, X))
self.assertEqual(unpack_1("1", "1;I", 1), (X, X, X, X, X, X, X, 0))
self.assertEqual(unpack_1("1", "1;R", 1), (X, 0, 0, 0, 0, 0, 0, 0))
self.assertEqual(unpack_1("1", "1;IR", 1), (0, X, X, X, X, X, X, X))
def test_I16(self):
self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040, 0x0506)
if sys.byteorder == 'little':
self.assert_unpack("I;16", "I;16N", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16B", "I;16N", 2, 0x0201, 0x0403, 0x0605)
self.assert_unpack("I;16L", "I;16N", 2, 0x0201, 0x0403, 0x0605)
else:
self.assert_unpack("I;16", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16B", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assert_unpack("I;16L", "I;16N", 2, 0x0102, 0x0304, 0x0506)
self.assertEqual(unpack_1("1", "1", 170), (X, 0, X, 0, X, 0, X, 0))
self.assertEqual(unpack_1("1", "1;I", 170), (0, X, 0, X, 0, X, 0, X))
self.assertEqual(unpack_1("1", "1;R", 170), (0, X, 0, X, 0, X, 0, X))
self.assertEqual(unpack_1("1", "1;IR", 170), (X, 0, X, 0, X, 0, X, 0))
self.assertEqual(unpack("L", "L;2", 1), 0)
self.assertEqual(unpack("L", "L;4", 1), 0)
self.assertEqual(unpack("L", "L", 1), 1)
self.assertEqual(unpack("L", "L;I", 1), 254)
self.assertEqual(unpack("L", "L;R", 1), 128)
self.assertEqual(unpack("L", "L;16", 2), 2) # little endian
self.assertEqual(unpack("L", "L;16B", 2), 1) # big endian
self.assertEqual(unpack("LA", "LA", 2), (1, 2))
self.assertEqual(unpack("LA", "LA;L", 2), (1, 2))
self.assertEqual(unpack("RGB", "RGB", 3), (1, 2, 3))
self.assertEqual(unpack("RGB", "RGB;L", 3), (1, 2, 3))
self.assertEqual(unpack("RGB", "RGB;R", 3), (128, 64, 192))
self.assertEqual(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ?
self.assertEqual(unpack("RGB", "BGR", 3), (3, 2, 1))
self.assertEqual(unpack("RGB", "RGB;15", 2), (8, 131, 0))
self.assertEqual(unpack("RGB", "BGR;15", 2), (0, 131, 8))
self.assertEqual(unpack("RGB", "RGB;16", 2), (8, 64, 0))
self.assertEqual(unpack("RGB", "BGR;16", 2), (0, 64, 8))
self.assertEqual(unpack("RGB", "RGB;4B", 2), (17, 0, 34))
self.assertEqual(unpack("RGB", "RGBX", 4), (1, 2, 3))
self.assertEqual(unpack("RGB", "BGRX", 4), (3, 2, 1))
self.assertEqual(unpack("RGB", "XRGB", 4), (2, 3, 4))
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))
self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0))
self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0))
self.assertEqual(unpack("RGBa", "RGBa", 4), (1, 2, 3, 4))
self.assertEqual(unpack("RGBa", "BGRa", 4), (3, 2, 1, 4))
self.assertEqual(unpack("RGBa", "aRGB", 4), (2, 3, 4, 1))
self.assertEqual(unpack("RGBa", "aBGR", 4), (4, 3, 2, 1))
self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255?
self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255))
self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255))
self.assertEqual(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255))
self.assertEqual(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255))
self.assertEqual(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255))
self.assertEqual(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255))
self.assertEqual(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4))
self.assertEqual(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251))
self.assertRaises(ValueError, lambda: unpack("L", "L", 0))
self.assertRaises(ValueError, lambda: unpack("RGB", "RGB", 2))
self.assertRaises(ValueError, lambda: unpack("CMYK", "CMYK", 2))
def test_value_error(self):
self.assertRaises(ValueError, self.assert_unpack, "L", "L", 0, 0)
self.assertRaises(ValueError, self.assert_unpack, "RGB", "RGB", 2, 0)
self.assertRaises(ValueError, self.assert_unpack, "CMYK", "CMYK", 2, 0)
if __name__ == '__main__':

View File

@ -39,7 +39,7 @@ class TestNumpy(PillowTestCase):
def to_image(dtype, bands=1, boolean=0):
if bands == 1:
if boolean:
data = [0, 1] * 50
data = [0, 255] * 50
else:
data = list(range(100))
a = numpy.array(data, dtype=dtype)
@ -52,14 +52,14 @@ class TestNumpy(PillowTestCase):
a = numpy.array([[x]*bands for x in data], dtype=dtype)
a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands
i = Image.fromarray(a)
if list(i.split()[0].getdata()) != list(range(100)):
if list(i.getchannel(0).getdata()) != list(range(100)):
print("data mismatch for", dtype)
# print(dtype, list(i.getdata()))
return i
# Check supported 1-bit integer formats
self.assertRaises(TypeError, lambda: to_image(numpy.bool))
self.assertRaises(TypeError, lambda: to_image(numpy.bool8))
self.assert_image(to_image(numpy.bool, 1, 1), '1', TEST_IMAGE_SIZE)
self.assert_image(to_image(numpy.bool8, 1, 1), '1', TEST_IMAGE_SIZE)
# Check supported 8-bit integer formats
self.assert_image(to_image(numpy.uint8), "L", TEST_IMAGE_SIZE)
@ -213,5 +213,13 @@ class TestNumpy(PillowTestCase):
self.assertEqual(im.size, (0, 0))
def test_bool(self):
# https://github.com/python-pillow/Pillow/issues/2044
a = numpy.zeros((10,2), dtype=numpy.bool)
a[0][0] = True
im2 = Image.fromarray(a)
self.assertEqual(im2.getdata()[0], 255)
if __name__ == '__main__':
unittest.main()

View File

@ -599,7 +599,7 @@ _fill(PyObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color))
return NULL;
im = ImagingNew(mode, xsize, ysize);
im = ImagingNewDirty(mode, xsize, ysize);
if (!im)
return NULL;
@ -791,23 +791,6 @@ _copy(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingCopy(self->image));
}
static PyObject*
_copy2(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
ImagingObject* imagep2;
if (!PyArg_ParseTuple(args, "O!O!",
&Imaging_Type, &imagep1,
&Imaging_Type, &imagep2))
return NULL;
if (!ImagingCopy2(imagep1->image, imagep2->image))
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
_crop(ImagingObject* self, PyObject* args)
{
@ -874,7 +857,7 @@ _gaussian_blur(ImagingObject* self, PyObject* args)
return NULL;
imIn = self->image;
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
@ -1520,26 +1503,45 @@ _resize(ImagingObject* self, PyObject* args)
int xsize, ysize;
int filter = IMAGING_TRANSFORM_NEAREST;
if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
return NULL;
float box[4] = {0, 0, 0, 0};
imIn = self->image;
box[2] = imIn->xsize;
box[3] = imIn->ysize;
if (!PyArg_ParseTuple(args, "(ii)|i(ffff)", &xsize, &ysize, &filter,
&box[0], &box[1], &box[2], &box[3]))
return NULL;
if (xsize < 1 || ysize < 1) {
return ImagingError_ValueError("height and width must be > 0");
}
if (imIn->xsize == xsize && imIn->ysize == ysize) {
if (box[0] < 0 || box[1] < 0) {
return ImagingError_ValueError("box offset can't be negative");
}
if (box[2] > imIn->xsize || box[3] > imIn->ysize) {
return ImagingError_ValueError("box can't exceed original image size");
}
if (box[2] - box[0] < 0 || box[3] - box[1] < 0) {
return ImagingError_ValueError("box can't be empty");
}
if (box[0] == 0 && box[1] == 0 && box[2] == xsize && box[3] == ysize) {
imOut = ImagingCopy(imIn);
}
else if (filter == IMAGING_TRANSFORM_NEAREST) {
double a[6];
memset(a, 0, sizeof a);
a[0] = (double) imIn->xsize / xsize;
a[4] = (double) imIn->ysize / ysize;
a[0] = (double) (box[2] - box[0]) / xsize;
a[4] = (double) (box[3] - box[1]) / ysize;
a[2] = box[0];
a[5] = box[1];
imOut = ImagingNew(imIn->mode, xsize, ysize);
imOut = ImagingNewDirty(imIn->mode, xsize, ysize);
imOut = ImagingTransform(
imOut, imIn, IMAGING_TRANSFORM_AFFINE,
@ -1547,7 +1549,7 @@ _resize(ImagingObject* self, PyObject* args)
a, filter, 1);
}
else {
imOut = ImagingResample(imIn, xsize, ysize, filter);
imOut = ImagingResample(imIn, xsize, ysize, filter, box);
}
return PyImagingNew(imOut);
@ -1665,12 +1667,12 @@ _transpose(ImagingObject* self, PyObject* args)
case 0: /* flip left right */
case 1: /* flip top bottom */
case 3: /* rotate 180 */
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
break;
case 2: /* rotate 90 */
case 4: /* rotate 270 */
case 5: /* transpose */
imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);
break;
default:
PyErr_SetString(PyExc_ValueError, "No such transpose operation");
@ -1715,7 +1717,7 @@ _unsharp_mask(ImagingObject* self, PyObject* args)
return NULL;
imIn = self->image;
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
@ -1738,7 +1740,7 @@ _box_blur(ImagingObject* self, PyObject* args)
return NULL;
imIn = self->image;
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
@ -1905,6 +1907,55 @@ _putband(ImagingObject* self, PyObject* args)
return Py_None;
}
static PyObject*
_merge(PyObject* self, PyObject* args)
{
char* mode;
ImagingObject *band0 = NULL;
ImagingObject *band1 = NULL;
ImagingObject *band2 = NULL;
ImagingObject *band3 = NULL;
Imaging bands[4] = {NULL, NULL, NULL, NULL};
if (!PyArg_ParseTuple(args, "sO!|O!O!O!", &mode,
&Imaging_Type, &band0, &Imaging_Type, &band1,
&Imaging_Type, &band2, &Imaging_Type, &band3))
return NULL;
if (band0) bands[0] = band0->image;
if (band1) bands[1] = band1->image;
if (band2) bands[2] = band2->image;
if (band3) bands[3] = band3->image;
return PyImagingNew(ImagingMerge(mode, bands));
}
static PyObject*
_split(ImagingObject* self, PyObject* args)
{
int fails = 0;
Py_ssize_t i;
PyObject* list;
PyObject* imaging_object;
Imaging bands[4] = {NULL, NULL, NULL, NULL};
if ( ! ImagingSplit(self->image, bands))
return NULL;
list = PyTuple_New(self->image->bands);
for (i = 0; i < self->image->bands; i++) {
imaging_object = PyImagingNew(bands[i]);
if ( ! imaging_object)
fails += 1;
PyTuple_SET_ITEM(list, i, imaging_object);
}
if (fails) {
Py_DECREF(list);
list = NULL;
}
return list;
}
/* -------------------------------------------------------------------- */
#ifdef WITH_IMAGECHOPS
@ -2940,7 +2991,6 @@ static struct PyMethodDef methods[] = {
{"convert_matrix", (PyCFunction)_convert_matrix, 1},
{"convert_transparent", (PyCFunction)_convert_transparent, 1},
{"copy", (PyCFunction)_copy, 1},
{"copy2", (PyCFunction)_copy2, 1},
{"crop", (PyCFunction)_crop, 1},
{"expand", (PyCFunction)_expand_image, 1},
{"filter", (PyCFunction)_filter, 1},
@ -2972,6 +3022,7 @@ static struct PyMethodDef methods[] = {
{"getband", (PyCFunction)_getband, 1},
{"putband", (PyCFunction)_putband, 1},
{"split", (PyCFunction)_split, 1},
{"fillband", (PyCFunction)_fillband, 1},
{"setmode", (PyCFunction)im_setmode, 1},
@ -3316,12 +3367,12 @@ static PyMethodDef functions[] = {
{"blend", (PyCFunction)_blend, 1},
{"fill", (PyCFunction)_fill, 1},
{"new", (PyCFunction)_new, 1},
{"merge", (PyCFunction)_merge, 1},
{"getcount", (PyCFunction)_getcount, 1},
/* Functions */
{"convert", (PyCFunction)_convert2, 1},
{"copy", (PyCFunction)_copy2, 1},
/* Codecs */
{"bcn_decoder", (PyCFunction)PyImaging_BcnDecoderNew, 1},

View File

@ -247,7 +247,7 @@ PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
* Files that are valid with 0.3 are reported as being invalid.
*/
int WebPDecoderBuggyAlpha() {
int WebPDecoderBuggyAlpha(void) {
return WebPGetDecoderVersion()==0x0103;
}

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install libimagequant
archive=libimagequant-2.10.1
archive=libimagequant-2.10.2
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install openjpeg
archive=openjpeg-2.1.2
archive=openjpeg-2.2.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

5
depends/termux.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
pkg -y install python python-dev ndk-sysroot clang make \
libjpeg-turbo-dev

View File

@ -1,4 +1,4 @@
<h3>Need help?</h3>
<p>
You can get help via IRC at <a href="irc://irc.freenode.net#pil">irc://irc.freenode.net#pil</a> or Stack Overflow <a href="https://stackoverflow.com/questions/tagged/pillow">here</a> and <a href="https://stackoverflow.com/questions/tagged/pil">here</a>. Please <a href="https://github.com/python-pillow/Pillow/issues/new">report issues on GitHub</a>.
You can get help via IRC at <a href="irc://irc.freenode.net#pil">irc://irc.freenode.net#pil</a> or Stack Overflow <a href="https://stackoverflow.com/questions/tagged/pillow">here</a> and <a href="https://stackoverflow.com/questions/tagged/python-imaging-library">here</a>. Please <a href="https://github.com/python-pillow/Pillow/issues/new">report issues on GitHub</a>.
</p>

View File

@ -146,6 +146,7 @@ ITU-R 709, using the D65 luminant) to the CIE XYZ color space:
.. automethod:: PIL.Image.Image.seek
.. automethod:: PIL.Image.Image.show
.. automethod:: PIL.Image.Image.split
.. automethod:: PIL.Image.Image.getchannel
.. automethod:: PIL.Image.Image.tell
.. automethod:: PIL.Image.Image.thumbnail
.. automethod:: PIL.Image.Image.tobitmap

View File

@ -0,0 +1,9 @@
4.3.0
-----
Get One Channel From Image
==========================
New method :py:meth:`PIL.Image.Image.getchannel` added.
It returns single channel by index or name. For example,
``image.getchannel("A")`` will return alpha channel as seperate image.

View File

@ -6,6 +6,7 @@ Release Notes
.. toctree::
:maxdepth: 2
4.3.0
4.2.1
4.2.0
4.1.1
@ -21,5 +22,3 @@ Release Notes
3.0.0
2.8.0
2.7.0

View File

@ -42,7 +42,7 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc)
imDst->ysize != imSrc->ysize)
return ImagingError_Mismatch();
imOut = ImagingNew(imDst->mode, imDst->xsize, imDst->ysize);
imOut = ImagingNewDirty(imDst->mode, imDst->xsize, imDst->ysize);
if (!imOut)
return NULL;

View File

@ -22,6 +22,13 @@
#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
#ifdef WORDS_BIGENDIAN
#define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24))
#else
#define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24))
#endif
Imaging
ImagingGetBand(Imaging imIn, int band)
{
@ -43,7 +50,7 @@ ImagingGetBand(Imaging imIn, int band)
if (imIn->bands == 2 && band == 1)
band = 3;
imOut = ImagingNew("L", imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty("L", imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
@ -51,7 +58,12 @@ ImagingGetBand(Imaging imIn, int band)
for (y = 0; y < imIn->ysize; y++) {
UINT8* in = (UINT8*) imIn->image[y] + band;
UINT8* out = imOut->image8[y];
for (x = 0; x < imIn->xsize; x++) {
x = 0;
for (; x < imIn->xsize - 3; x += 4) {
*((UINT32*) (out + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]);
in += 16;
}
for (; x < imIn->xsize; x++) {
out[x] = *in;
in += 4;
}
@ -60,6 +72,101 @@ ImagingGetBand(Imaging imIn, int band)
return imOut;
}
int
ImagingSplit(Imaging imIn, Imaging bands[4])
{
int i, j, x, y;
/* Check arguments */
if (!imIn || imIn->type != IMAGING_TYPE_UINT8) {
(Imaging) ImagingError_ModeError();
return 0;
}
/* Shortcuts */
if (imIn->bands == 1) {
bands[0] = ImagingCopy(imIn);
return imIn->bands;
}
for (i = 0; i < imIn->bands; i++) {
bands[i] = ImagingNew("L", imIn->xsize, imIn->ysize);
if ( ! bands[i]) {
for (j = 0; j < i; ++j) {
ImagingDelete(bands[j]);
}
return 0;
}
}
/* Extract bands from image */
if (imIn->bands == 2) {
for (y = 0; y < imIn->ysize; y++) {
UINT8* in = (UINT8*) imIn->image[y];
UINT8* out0 = bands[0]->image8[y];
UINT8* out1 = bands[1]->image8[y];
x = 0;
for (; x < imIn->xsize - 3; x += 4) {
*((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]);
*((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+3], in[4+3], in[8+3], in[12+3]);
in += 16;
}
for (; x < imIn->xsize; x++) {
out0[x] = in[0];
out1[x] = in[3];
in += 4;
}
}
} else if (imIn->bands == 3) {
for (y = 0; y < imIn->ysize; y++) {
UINT8* in = (UINT8*) imIn->image[y];
UINT8* out0 = bands[0]->image8[y];
UINT8* out1 = bands[1]->image8[y];
UINT8* out2 = bands[2]->image8[y];
x = 0;
for (; x < imIn->xsize - 3; x += 4) {
*((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]);
*((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+1], in[4+1], in[8+1], in[12+1]);
*((UINT32*) (out2 + x)) = MAKE_UINT32(in[0+2], in[4+2], in[8+2], in[12+2]);
in += 16;
}
for (; x < imIn->xsize; x++) {
out0[x] = in[0];
out1[x] = in[1];
out2[x] = in[2];
in += 4;
}
}
} else {
for (y = 0; y < imIn->ysize; y++) {
UINT8* in = (UINT8*) imIn->image[y];
UINT8* out0 = bands[0]->image8[y];
UINT8* out1 = bands[1]->image8[y];
UINT8* out2 = bands[2]->image8[y];
UINT8* out3 = bands[3]->image8[y];
x = 0;
for (; x < imIn->xsize - 3; x += 4) {
*((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]);
*((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+1], in[4+1], in[8+1], in[12+1]);
*((UINT32*) (out2 + x)) = MAKE_UINT32(in[0+2], in[4+2], in[8+2], in[12+2]);
*((UINT32*) (out3 + x)) = MAKE_UINT32(in[0+3], in[4+3], in[8+3], in[12+3]);
in += 16;
}
for (; x < imIn->xsize; x++) {
out0[x] = in[0];
out1[x] = in[1];
out2[x] = in[2];
out3[x] = in[3];
in += 4;
}
}
}
return imIn->bands;
}
Imaging
ImagingPutBand(Imaging imOut, Imaging imIn, int band)
{
@ -127,3 +234,77 @@ ImagingFillBand(Imaging imOut, int band, int color)
return imOut;
}
Imaging
ImagingMerge(const char* mode, Imaging bands[4])
{
int i, x, y;
int bandsCount = 0;
Imaging imOut;
Imaging firstBand;
firstBand = bands[0];
if ( ! firstBand) {
return (Imaging) ImagingError_ValueError("wrong number of bands");
}
for (i = 0; i < 4; ++i) {
if ( ! bands[i]) {
break;
}
if (bands[i]->bands != 1) {
return (Imaging) ImagingError_ModeError();
}
if (bands[i]->xsize != firstBand->xsize
|| bands[i]->ysize != firstBand->ysize) {
return (Imaging) ImagingError_Mismatch();
}
}
bandsCount = i;
imOut = ImagingNewDirty(mode, firstBand->xsize, firstBand->ysize);
if ( ! imOut)
return NULL;
if (imOut->bands != bandsCount) {
ImagingDelete(imOut);
return (Imaging) ImagingError_ValueError("wrong number of bands");
}
if (imOut->bands == 1)
return ImagingCopy2(imOut, firstBand);
if (imOut->bands == 2) {
for (y = 0; y < imOut->ysize; y++) {
UINT8* in0 = bands[0]->image8[y];
UINT8* in1 = bands[1]->image8[y];
UINT32* out = (UINT32*) imOut->image32[y];
for (x = 0; x < imOut->xsize; x++) {
out[x] = MAKE_UINT32(in0[x], 0, 0, in1[x]);
}
}
} else if (imOut->bands == 3) {
for (y = 0; y < imOut->ysize; y++) {
UINT8* in0 = bands[0]->image8[y];
UINT8* in1 = bands[1]->image8[y];
UINT8* in2 = bands[2]->image8[y];
UINT32* out = (UINT32*) imOut->image32[y];
for (x = 0; x < imOut->xsize; x++) {
out[x] = MAKE_UINT32(in0[x], in1[x], in2[x], 0);
}
}
} else if (imOut->bands == 4) {
for (y = 0; y < imOut->ysize; y++) {
UINT8* in0 = bands[0]->image8[y];
UINT8* in1 = bands[1]->image8[y];
UINT8* in2 = bands[2]->image8[y];
UINT8* in3 = bands[3]->image8[y];
UINT32* out = (UINT32*) imOut->image32[y];
for (x = 0; x < imOut->xsize; x++) {
out[x] = MAKE_UINT32(in0[x], in1[x], in2[x], in3[x]);
}
}
}
return imOut;
}

View File

@ -815,6 +815,7 @@ static int decode_bcn(Imaging im, ImagingCodecState state, const UINT8* src, int
case NN: \
while (bytes >= SZ) { \
TY col[16]; \
memset(col, 0, 16 * sizeof(col[0])); \
decode_bc##NN##_block(col, ptr); \
put_block(im, state, (const char *)col, sizeof(col[0]), C); \
ptr += SZ; \

View File

@ -40,7 +40,7 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha)
else if (alpha == 1.0)
return ImagingCopy(imIn2);
imOut = ImagingNew(imIn1->mode, imIn1->xsize, imIn1->ysize);
imOut = ImagingNewDirty(imIn1->mode, imIn1->xsize, imIn1->ysize);
if (!imOut)
return NULL;

View File

@ -265,7 +265,7 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n)
strcmp(imIn->mode, "La") == 0))
return ImagingError_ModeError();
imTransposed = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
imTransposed = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);
if (!imTransposed)
return NULL;

View File

@ -1035,7 +1035,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
else
return (Imaging) ImagingError_ValueError("conversion not supported");
imOut = ImagingNew2(mode, imOut, imIn);
imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut)
return NULL;
@ -1073,7 +1073,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
if (!palette)
return (Imaging) ImagingError_ValueError("no palette");
imOut = ImagingNew2("P", imOut, imIn);
imOut = ImagingNew2Dirty("P", imOut, imIn);
if (!imOut) {
if (palette != inpalette)
ImagingPaletteDelete(palette);
@ -1211,7 +1211,7 @@ tobilevel(Imaging imOut, Imaging imIn, int dither)
if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0)
return (Imaging) ImagingError_ValueError("conversion not supported");
imOut = ImagingNew2("1", imOut, imIn);
imOut = ImagingNew2Dirty("1", imOut, imIn);
if (!imOut)
return NULL;
@ -1344,7 +1344,7 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
}
#endif
imOut = ImagingNew2(mode, imOut, imIn);
imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut)
return NULL;
@ -1407,7 +1407,7 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
g = b = r;
}
imOut = ImagingNew2(mode, imOut, imIn);
imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut){
return NULL;
}

View File

@ -28,7 +28,7 @@ _copy(Imaging imOut, Imaging imIn)
if (!imIn)
return (Imaging) ImagingError_ValueError(NULL);
imOut = ImagingNew2(imIn->mode, imOut, imIn);
imOut = ImagingNew2Dirty(imIn->mode, imOut, imIn);
if (!imOut)
return NULL;

View File

@ -37,7 +37,7 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1)
if (ysize < 0)
ysize = 0;
imOut = ImagingNew(imIn->mode, xsize, ysize);
imOut = ImagingNewDirty(imIn->mode, xsize, ysize);
if (!imOut)
return NULL;

View File

@ -68,7 +68,7 @@ ImagingFillLinearGradient(const char *mode)
return (Imaging) ImagingError_ModeError();
}
im = ImagingNew(mode, 256, 256);
im = ImagingNewDirty(mode, 256, 256);
if (!im) {
return NULL;
}
@ -91,7 +91,7 @@ ImagingFillRadialGradient(const char *mode)
return (Imaging) ImagingError_ModeError();
}
im = ImagingNew(mode, 256, 256);
im = ImagingNewDirty(mode, 256, 256);
if (!im) {
return NULL;
}

View File

@ -31,6 +31,7 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
{
Imaging imOut;
int x, y;
ImagingSectionCookie cookie;
if (xmargin < 0 && ymargin < 0)
return (Imaging) ImagingError_ValueError("bad kernel size");
@ -60,11 +61,13 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
EXPAND_LINE(type, image, imIn->ysize-1, ymargin+imIn->ysize+y);\
}
ImagingSectionEnter(&cookie);
if (imIn->image8) {
EXPAND(UINT8, image8);
} else {
EXPAND(INT32, image32);
}
ImagingSectionLeave(&cookie);
ImagingCopyInfo(imOut, imIn);
@ -78,6 +81,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
Imaging imOut;
int x, y;
FLOAT32 sum;
ImagingSectionCookie cookie;
if (!im || strcmp(im->mode, "L") != 0)
return (Imaging) ImagingError_ModeError();
@ -92,6 +96,9 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
if (!imOut)
return NULL;
// Add one time for rounding
offset += 0.5;
/* brute force kernel implementations */
#define KERNEL3x3(image, kernel, d) ( \
(int) image[y+1][x-d] * kernel[0] + \
@ -131,6 +138,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
(int) image[y-2][x+d] * kernel[23] + \
(int) image[y-2][x+d+d] * kernel[24])
ImagingSectionEnter(&cookie);
if (xsize == 3) {
/* 3x3 kernel. */
for (x = 0; x < im->xsize; x++)
@ -174,6 +182,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
for (x = 0; x < im->xsize; x++)
imOut->image8[y][x] = im->image8[y][x];
}
ImagingSectionLeave(&cookie);
return imOut;
}

View File

@ -159,11 +159,11 @@ struct ImagingPaletteInstance {
extern int ImagingNewCount;
extern Imaging ImagingNew(const char* mode, int xsize, int ysize);
extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn);
extern Imaging ImagingNewDirty(const char* mode, int xsize, int ysize);
extern Imaging ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn);
extern void ImagingDelete(Imaging im);
extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize);
extern Imaging ImagingNewArray(const char* mode, int xsize, int ysize);
extern Imaging ImagingNewMap(const char* filename, int readonly,
const char* mode, int xsize, int ysize);
@ -172,7 +172,6 @@ extern Imaging ImagingNewPrologue(const char *mode,
extern Imaging ImagingNewPrologueSubtype(const char *mode,
int xsize, int ysize,
int structure_size);
extern Imaging ImagingNewEpilogue(Imaging im);
extern void ImagingCopyInfo(Imaging destination, Imaging source);
@ -267,6 +266,8 @@ extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn);
extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius,
int passes);
extern Imaging ImagingGetBand(Imaging im, int band);
extern Imaging ImagingMerge(const char* mode, Imaging bands[4]);
extern int ImagingSplit(Imaging im, Imaging bands[4]);
extern int ImagingGetBBox(Imaging im, int bbox[4]);
typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem;
extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors,
@ -290,7 +291,7 @@ extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter);
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]);
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
extern Imaging ImagingTransform(
Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1,

View File

@ -32,7 +32,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[])
if (strcmp(mode, "L") == 0 && im->bands == 3) {
imOut = ImagingNew("L", im->xsize, im->ysize);
imOut = ImagingNewDirty("L", im->xsize, im->ysize);
if (!imOut)
return NULL;
@ -49,7 +49,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[])
} else if (strlen(mode) == 3 && im->bands == 3) {
imOut = ImagingNew(mode, im->xsize, im->ysize);
imOut = ImagingNewDirty(mode, im->xsize, im->ysize);
if (!imOut)
return NULL;

View File

@ -29,7 +29,7 @@ ImagingNegative(Imaging im)
if (!im)
return (Imaging) ImagingError_ModeError();
imOut = ImagingNew(im->mode, im->xsize, im->ysize);
imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize);
if (!imOut)
return NULL;

View File

@ -27,7 +27,7 @@ ImagingOffset(Imaging im, int xoffset, int yoffset)
if (!im)
return (Imaging) ImagingError_ModeError();
imOut = ImagingNew(im->mode, im->xsize, im->ysize);
imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize);
if (!imOut)
return NULL;

View File

@ -559,8 +559,8 @@ static struct {
/* true colour w. padding */
{"RGBX", "RGBX", 32, copy4},
{"RGBX", "RGBX;L", 32, packRGBXL},
{"RGBX", "RGB", 32, ImagingPackRGB},
{"RGBX", "BGR", 32, ImagingPackBGR},
{"RGBX", "RGB", 24, ImagingPackRGB},
{"RGBX", "BGR", 24, ImagingPackBGR},
{"RGBX", "BGRX", 32, ImagingPackBGRX},
{"RGBX", "XBGR", 32, ImagingPackXBGR},
{"RGBX", "R", 8, band0},

View File

@ -5,6 +5,13 @@
#define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F))
#ifdef WORDS_BIGENDIAN
#define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24))
#else
#define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24))
#endif
struct filter {
double (*filter)(double x);
double support;
@ -127,16 +134,16 @@ static inline UINT8 clip8(int in)
int
precompute_coeffs(int inSize, int outSize, struct filter *filterp,
int **xboundsp, double **kkp) {
precompute_coeffs(int inSize, float in0, float in1, int outSize,
struct filter *filterp, int **boundsp, double **kkp) {
double support, scale, filterscale;
double center, ww, ss;
int xx, x, kmax, xmin, xmax;
int *xbounds;
int xx, x, ksize, xmin, xmax;
int *bounds;
double *kk, *k;
/* prepare for horizontal stretch */
filterscale = scale = (double) inSize / outSize;
filterscale = scale = (double) (in1 - in0) / outSize;
if (filterscale < 1.0) {
filterscale = 1.0;
}
@ -145,27 +152,32 @@ precompute_coeffs(int inSize, int outSize, struct filter *filterp,
support = filterp->support * filterscale;
/* maximum number of coeffs */
kmax = (int) ceil(support) * 2 + 1;
ksize = (int) ceil(support) * 2 + 1;
// check for overflow
if (outSize > INT_MAX / (kmax * sizeof(double)))
if (outSize > INT_MAX / (ksize * sizeof(double))) {
ImagingError_MemoryError();
return 0;
}
/* coefficient buffer */
/* malloc check ok, overflow checked above */
kk = malloc(outSize * kmax * sizeof(double));
if ( ! kk)
kk = malloc(outSize * ksize * sizeof(double));
if ( ! kk) {
ImagingError_MemoryError();
return 0;
}
/* malloc check ok, kmax*sizeof(double) > 2*sizeof(int) */
xbounds = malloc(outSize * 2 * sizeof(int));
if ( ! xbounds) {
/* malloc check ok, ksize*sizeof(double) > 2*sizeof(int) */
bounds = malloc(outSize * 2 * sizeof(int));
if ( ! bounds) {
free(kk);
ImagingError_MemoryError();
return 0;
}
for (xx = 0; xx < outSize; xx++) {
center = (xx + 0.5) * scale;
center = in0 + (xx + 0.5) * scale;
ww = 0.0;
ss = 1.0 / filterscale;
// Round the value
@ -177,7 +189,7 @@ precompute_coeffs(int inSize, int outSize, struct filter *filterp,
if (xmax > inSize)
xmax = inSize;
xmax -= xmin;
k = &kk[xx * kmax];
k = &kk[xx * ksize];
for (x = 0; x < xmax; x++) {
double w = filterp->filter((x + xmin - center + 0.5) * ss);
k[x] = w;
@ -188,185 +200,138 @@ precompute_coeffs(int inSize, int outSize, struct filter *filterp,
k[x] /= ww;
}
// Remaining values should stay empty if they are used despite of xmax.
for (; x < kmax; x++) {
for (; x < ksize; x++) {
k[x] = 0;
}
xbounds[xx * 2 + 0] = xmin;
xbounds[xx * 2 + 1] = xmax;
bounds[xx * 2 + 0] = xmin;
bounds[xx * 2 + 1] = xmax;
}
*xboundsp = xbounds;
*boundsp = bounds;
*kkp = kk;
return kmax;
return ksize;
}
int
normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp)
void
normalize_coeffs_8bpc(int outSize, int ksize, double *prekk)
{
int x;
INT32 *kk;
/* malloc check ok, overflow checked in precompute_coeffs */
kk = malloc(outSize * kmax * sizeof(INT32));
if ( ! kk) {
return 0;
}
// use the same buffer for normalized coefficients
kk = (INT32 *) prekk;
for (x = 0; x < outSize * kmax; x++) {
for (x = 0; x < outSize * ksize; 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)
void
ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, int offset,
int ksize, int *bounds, double *prekk)
{
ImagingSectionCookie cookie;
Imaging imOut;
int ss0, ss1, ss2, ss3;
int xx, yy, x, kmax, xmin, xmax;
int *xbounds;
int xx, yy, x, xmin, xmax;
INT32 *k, *kk;
double *prekk;
kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &prekk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
kmax = normalize_coeffs_8bpc(xsize, kmax, prekk, &kk);
free(prekk);
if ( ! kmax) {
free(xbounds);
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, xsize, imIn->ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
// use the same buffer for normalized coefficients
kk = (INT32 *) prekk;
normalize_coeffs_8bpc(imOut->xsize, ksize, prekk);
ImagingSectionEnter(&cookie);
if (imIn->image8) {
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss0 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++)
ss0 += ((UINT8) imIn->image8[yy][x + xmin]) * k[x];
ss0 += ((UINT8) imIn->image8[yy + offset][x + xmin]) * k[x];
imOut->image8[yy][xx] = clip8(ss0);
}
}
} else if (imIn->type == IMAGING_TYPE_UINT8) {
if (imIn->bands == 2) {
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss0 = ss3 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x];
ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x];
ss3 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 3]) * k[x];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), 0, 0, clip8(ss3));
}
}
} else if (imIn->bands == 3) {
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x];
ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 2]) * k[x];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), 0);
}
}
} else {
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1);
for (x = 0; x < xmax; x++) {
ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x];
ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x];
ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x];
ss1 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 1]) * k[x];
ss2 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 2]) * k[x];
ss3 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 3]) * k[x];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
}
}
}
}
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
Imaging
ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
void
ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int offset,
int ksize, int *bounds, double *prekk)
{
ImagingSectionCookie cookie;
Imaging imOut;
int ss0, ss1, ss2, ss3;
int xx, yy, y, kmax, ymin, ymax;
int *xbounds;
int xx, yy, y, ymin, ymax;
INT32 *k, *kk;
double *prekk;
kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &prekk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
kmax = normalize_coeffs_8bpc(ysize, kmax, prekk, &kk);
free(prekk);
if ( ! kmax) {
free(xbounds);
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, imIn->xsize, ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
// use the same buffer for normalized coefficients
kk = (INT32 *) prekk;
normalize_coeffs_8bpc(imOut->ysize, ksize, prekk);
ImagingSectionEnter(&cookie);
if (imIn->image8) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (yy = 0; yy < imOut->ysize; yy++) {
k = &kk[yy * ksize];
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++)
@ -376,25 +341,25 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
}
} else if (imIn->type == IMAGING_TYPE_UINT8) {
if (imIn->bands == 2) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (yy = 0; yy < imOut->ysize; yy++) {
k = &kk[yy * ksize];
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss3 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y];
ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), 0, 0, clip8(ss3));
}
}
} else if (imIn->bands == 3) {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (yy = 0; yy < imOut->ysize; yy++) {
k = &kk[yy * ksize];
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
@ -402,16 +367,15 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y];
ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), 0);
}
}
} else {
for (yy = 0; yy < ysize; yy++) {
k = &kk[yy * kmax];
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
for (yy = 0; yy < imOut->ysize; yy++) {
k = &kk[yy * ksize];
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
for (xx = 0; xx < imOut->xsize; xx++) {
ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1);
for (y = 0; y < ymax; y++) {
@ -420,55 +384,36 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp)
ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y];
ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y];
}
imOut->image[yy][xx*4 + 0] = clip8(ss0);
imOut->image[yy][xx*4 + 1] = clip8(ss1);
imOut->image[yy][xx*4 + 2] = clip8(ss2);
imOut->image[yy][xx*4 + 3] = clip8(ss3);
((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
}
}
}
}
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
Imaging
ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp)
void
ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int offset,
int ksize, int *bounds, double *kk)
{
ImagingSectionCookie cookie;
Imaging imOut;
double ss;
int xx, yy, x, kmax, xmin, xmax;
int *xbounds;
double *k, *kk;
kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &kk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, xsize, imIn->ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
int xx, yy, x, xmin, xmax;
double *k;
ImagingSectionEnter(&cookie);
switch(imIn->type) {
case IMAGING_TYPE_INT32:
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss = 0.0;
for (x = 0; x < xmax; x++)
ss += IMAGING_PIXEL_I(imIn, x + xmin, yy) * k[x];
ss += IMAGING_PIXEL_I(imIn, x + xmin, yy + offset) * k[x];
IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss);
}
}
@ -476,55 +421,38 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp)
case IMAGING_TYPE_FLOAT32:
for (yy = 0; yy < imOut->ysize; yy++) {
for (xx = 0; xx < xsize; xx++) {
xmin = xbounds[xx * 2 + 0];
xmax = xbounds[xx * 2 + 1];
k = &kk[xx * kmax];
for (xx = 0; xx < imOut->xsize; xx++) {
xmin = bounds[xx * 2 + 0];
xmax = bounds[xx * 2 + 1];
k = &kk[xx * ksize];
ss = 0.0;
for (x = 0; x < xmax; x++)
ss += IMAGING_PIXEL_F(imIn, x + xmin, yy) * k[x];
ss += IMAGING_PIXEL_F(imIn, x + xmin, yy + offset) * k[x];
IMAGING_PIXEL_F(imOut, xx, yy) = ss;
}
}
break;
}
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
Imaging
ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
void
ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int offset,
int ksize, int *bounds, double *kk)
{
ImagingSectionCookie cookie;
Imaging imOut;
double ss;
int xx, yy, y, kmax, ymin, ymax;
int *xbounds;
double *k, *kk;
kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &kk);
if ( ! kmax) {
return (Imaging) ImagingError_MemoryError();
}
imOut = ImagingNew(imIn->mode, imIn->xsize, ysize);
if ( ! imOut) {
free(kk);
free(xbounds);
return NULL;
}
int xx, yy, y, ymin, ymax;
double *k;
ImagingSectionEnter(&cookie);
switch(imIn->type) {
case IMAGING_TYPE_INT32:
for (yy = 0; yy < ysize; yy++) {
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
k = &kk[yy * kmax];
for (yy = 0; yy < imOut->ysize; yy++) {
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
k = &kk[yy * ksize];
for (xx = 0; xx < imOut->xsize; xx++) {
ss = 0.0;
for (y = 0; y < ymax; y++)
@ -535,10 +463,10 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
break;
case IMAGING_TYPE_FLOAT32:
for (yy = 0; yy < ysize; yy++) {
ymin = xbounds[yy * 2 + 0];
ymax = xbounds[yy * 2 + 1];
k = &kk[yy * kmax];
for (yy = 0; yy < imOut->ysize; yy++) {
ymin = bounds[yy * 2 + 0];
ymax = bounds[yy * 2 + 1];
k = &kk[yy * ksize];
for (xx = 0; xx < imOut->xsize; xx++) {
ss = 0.0;
for (y = 0; y < ymax; y++)
@ -548,22 +476,27 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp)
}
break;
}
ImagingSectionLeave(&cookie);
free(kk);
free(xbounds);
return imOut;
}
typedef void (*ResampleFunction)(Imaging imOut, Imaging imIn, int offset,
int ksize, int *bounds, double *kk);
Imaging
ImagingResample(Imaging imIn, int xsize, int ysize, int filter)
ImagingResampleInner(Imaging imIn, int xsize, int ysize,
struct filter *filterp, float box[4],
ResampleFunction ResampleHorizontal,
ResampleFunction ResampleVertical);
Imaging
ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4])
{
Imaging imTemp = NULL;
Imaging imOut = NULL;
struct filter *filterp;
Imaging (*ResampleHorizontal)(Imaging imIn, int xsize, struct filter *filterp);
Imaging (*ResampleVertical)(Imaging imIn, int xsize, struct filter *filterp);
ResampleFunction ResampleHorizontal;
ResampleFunction ResampleVertical;
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0)
return (Imaging) ImagingError_ModeError();
@ -612,24 +545,93 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter)
);
}
/* two-pass resize, first pass */
if (imIn->xsize != xsize) {
imTemp = ResampleHorizontal(imIn, xsize, filterp);
if ( ! imTemp)
return ImagingResampleInner(imIn, xsize, ysize, filterp, box,
ResampleHorizontal, ResampleVertical);
}
Imaging
ImagingResampleInner(Imaging imIn, int xsize, int ysize,
struct filter *filterp, float box[4],
ResampleFunction ResampleHorizontal,
ResampleFunction ResampleVertical)
{
Imaging imTemp = NULL;
Imaging imOut = NULL;
int i;
int yroi_min, yroi_max;
int ksize_horiz, ksize_vert;
int *bounds_horiz, *bounds_vert;
double *kk_horiz, *kk_vert;
ksize_horiz = precompute_coeffs(imIn->xsize, box[0], box[2], xsize,
filterp, &bounds_horiz, &kk_horiz);
if ( ! ksize_horiz) {
return NULL;
}
ksize_vert = precompute_coeffs(imIn->ysize, box[1], box[3], ysize,
filterp, &bounds_vert, &kk_vert);
if ( ! ksize_vert) {
free(bounds_horiz);
free(kk_horiz);
return NULL;
}
// First used row in the source image
yroi_min = bounds_vert[0];
// Last used row in the source image
yroi_max = bounds_vert[ysize*2 - 2] + bounds_vert[ysize*2 - 1];
/* two-pass resize, first pass */
if (box[0] || box[2] != xsize) {
// Shift bounds for vertical pass
for (i = 0; i < ysize; i++) {
bounds_vert[i * 2] -= yroi_min;
}
imTemp = ImagingNewDirty(imIn->mode, xsize, yroi_max - yroi_min);
if (imTemp) {
ResampleHorizontal(imTemp, imIn, yroi_min,
ksize_horiz, bounds_horiz, kk_horiz);
}
free(bounds_horiz);
free(kk_horiz);
if ( ! imTemp) {
free(bounds_vert);
free(kk_vert);
return NULL;
}
imOut = imIn = imTemp;
} else {
// Free in any case
free(bounds_horiz);
free(kk_horiz);
}
/* second pass */
if (imIn->ysize != ysize) {
if (box[1] || box[3] != ysize) {
imOut = ImagingNewDirty(imIn->mode, imIn->xsize, ysize);
if (imOut) {
/* imIn can be the original image or horizontally resampled one */
imOut = ResampleVertical(imIn, ysize, filterp);
ResampleVertical(imOut, imIn, 0,
ksize_vert, bounds_vert, kk_vert);
}
/* it's safe to call ImagingDelete with empty value
if there was no previous step. */
if previous step was not performed. */
ImagingDelete(imTemp);
if ( ! imOut)
free(bounds_vert);
free(kk_vert);
if ( ! imOut) {
return NULL;
}
} else {
// Free in any case
free(bounds_vert);
free(kk_vert);
}
/* none of the previous steps are performed, copying */
if ( ! imOut) {

View File

@ -46,21 +46,20 @@ int ImagingNewCount = 0;
*/
Imaging
ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize,
int size)
ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size)
{
Imaging im;
ImagingSectionCookie cookie;
im = (Imaging) calloc(1, size);
if (!im)
return (Imaging) ImagingError_MemoryError();
/* linesize overflow check, roughly the current largest space req'd */
if (xsize > (INT_MAX / 4) - 1) {
return (Imaging) ImagingError_MemoryError();
}
im = (Imaging) calloc(1, size);
if (!im) {
return (Imaging) ImagingError_MemoryError();
}
/* Setup image descriptor */
im->xsize = xsize;
im->ysize = ysize;
@ -206,47 +205,21 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize,
} else {
free(im);
return (Imaging) ImagingError_ValueError("unrecognized mode");
return (Imaging) ImagingError_ValueError("unrecognized image mode");
}
/* Setup image descriptor */
strcpy(im->mode, mode);
ImagingSectionEnter(&cookie);
/* Pointer array (allocate at least one line, to avoid MemoryError
exceptions on platforms where calloc(0, x) returns NULL) */
im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *));
ImagingSectionLeave(&cookie);
if (!im->image) {
if ( ! im->image) {
free(im);
return (Imaging) ImagingError_MemoryError();
}
ImagingNewCount++;
return im;
}
Imaging
ImagingNewPrologue(const char *mode, int xsize, int ysize)
{
return ImagingNewPrologueSubtype(
mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)
);
}
Imaging
ImagingNewEpilogue(Imaging im)
{
/* If the raster data allocator didn't setup a destructor,
assume that it couldn't allocate the required amount of
memory. */
if (!im->destroy)
return (Imaging) ImagingError_MemoryError();
/* Initialize alias pointers to pixel data. */
switch (im->pixelsize) {
case 1: case 2: case 3:
@ -257,9 +230,18 @@ ImagingNewEpilogue(Imaging im)
break;
}
ImagingNewCount++;
return im;
}
Imaging
ImagingNewPrologue(const char *mode, int xsize, int ysize)
{
return ImagingNewPrologueSubtype(
mode, xsize, ysize, sizeof(struct ImagingMemoryInstance));
}
void
ImagingDelete(Imaging im)
{
@ -295,24 +277,23 @@ ImagingDestroyArray(Imaging im)
}
Imaging
ImagingNewArray(const char *mode, int xsize, int ysize)
ImagingAllocateArray(Imaging im, int dirty)
{
Imaging im;
ImagingSectionCookie cookie;
int y;
char* p;
im = ImagingNewPrologue(mode, xsize, ysize);
if (!im)
return NULL;
ImagingSectionEnter(&cookie);
/* Allocate image as an array of lines */
for (y = 0; y < im->ysize; y++) {
/* malloc check linesize checked in prologue */
if (dirty) {
p = (char *) malloc(im->linesize);
} else {
p = (char *) calloc(1, im->linesize);
}
if (!p) {
ImagingDestroyArray(im);
break;
@ -322,10 +303,13 @@ ImagingNewArray(const char *mode, int xsize, int ysize)
ImagingSectionLeave(&cookie);
if (y == im->ysize)
if (y != im->ysize) {
return (Imaging) ImagingError_MemoryError();
}
im->destroy = ImagingDestroyArray;
return ImagingNewEpilogue(im);
return im;
}
@ -341,15 +325,10 @@ ImagingDestroyBlock(Imaging im)
}
Imaging
ImagingNewBlock(const char *mode, int xsize, int ysize)
ImagingAllocateBlock(Imaging im, int dirty)
{
Imaging im;
Py_ssize_t y, i;
im = ImagingNewPrologue(mode, xsize, ysize);
if (!im)
return NULL;
/* We shouldn't overflow, since the threshold defined
below says that we're only going to allocate max 4M
here before going to the array allocator. Check anyway.
@ -357,7 +336,7 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
if (im->linesize &&
im->ysize > INT_MAX / im->linesize) {
/* punt if we're going to overflow */
return NULL;
return (Imaging) ImagingError_MemoryError();
}
if (im->ysize * im->linesize <= 0) {
@ -365,12 +344,20 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
prevents MemoryError on zero-sized images on such
platforms */
im->block = (char *) malloc(1);
} else {
if (dirty) {
/* malloc check ok, overflow check above */
im->block = (char *) malloc(im->ysize * im->linesize);
} else {
/* malloc check ok, overflow check above */
im->block = (char *) calloc(im->ysize, im->linesize);
}
}
if ( ! im->block) {
return (Imaging) ImagingError_MemoryError();
}
if (im->block) {
for (y = i = 0; y < im->ysize; y++) {
im->image[y] = im->block + i;
i += im->linesize;
@ -378,9 +365,7 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
im->destroy = ImagingDestroyBlock;
}
return ImagingNewEpilogue(im);
return im;
}
/* --------------------------------------------------------------------
@ -393,39 +378,65 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
#endif
Imaging
ImagingNew(const char* mode, int xsize, int ysize)
ImagingNewInternal(const char* mode, int xsize, int ysize, int dirty)
{
int bytes;
Imaging im;
if (strcmp(mode, "") == 0)
return (Imaging) ImagingError_ValueError("empty mode");
if (strlen(mode) == 1) {
if (mode[0] == 'F' || mode[0] == 'I')
bytes = 4;
else
bytes = 1;
} else
bytes = strlen(mode); /* close enough */
if (xsize < 0 || ysize < 0) {
return (Imaging) ImagingError_ValueError("bad image size");
}
if ((int64_t) xsize * (int64_t) ysize <= THRESHOLD / bytes) {
im = ImagingNewBlock(mode, xsize, ysize);
if (im)
im = ImagingNewPrologue(mode, xsize, ysize);
if ( ! im)
return NULL;
if (im->ysize && im->linesize <= THRESHOLD / im->ysize) {
if (ImagingAllocateBlock(im, dirty)) {
return im;
}
/* assume memory error; try allocating in array mode instead */
ImagingError_Clear();
}
return ImagingNewArray(mode, xsize, ysize);
if (ImagingAllocateArray(im, dirty)) {
return im;
}
ImagingDelete(im);
return NULL;
}
Imaging
ImagingNew2(const char* mode, Imaging imOut, Imaging imIn)
ImagingNew(const char* mode, int xsize, int ysize)
{
return ImagingNewInternal(mode, xsize, ysize, 0);
}
Imaging
ImagingNewDirty(const char* mode, int xsize, int ysize)
{
return ImagingNewInternal(mode, xsize, ysize, 1);
}
Imaging
ImagingNewBlock(const char* mode, int xsize, int ysize)
{
Imaging im;
im = ImagingNewPrologue(mode, xsize, ysize);
if ( ! im)
return NULL;
if (ImagingAllocateBlock(im, 0)) {
return im;
}
ImagingDelete(im);
return NULL;
}
Imaging
ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn)
{
/* allocate or validate output image */
@ -438,7 +449,7 @@ ImagingNew2(const char* mode, Imaging imOut, Imaging imIn)
}
} else {
/* create new image */
imOut = ImagingNew(mode, imIn->xsize, imIn->ysize);
imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
}

View File

@ -185,6 +185,19 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels)
}
}
static void
unpack18(UINT8* out, const UINT8* in, int pixels)
{
/* Unpack a '|b1' image, which is a numpy boolean.
1 == true, 0==false, in bytes */
int i;
for (i = 0; i < pixels; i++) {
out[i] = in[i] > 0 ? 255 : 0;
}
}
/* Unpack to "L" image */
@ -1163,11 +1176,14 @@ static struct {
endian byte order (default is little endian); "L" line
interleave, "S" signed, "F" floating point */
/* exception: rawmodes "I" and "F" are always native endian byte order */
/* bilevel */
{"1", "1", 1, unpack1},
{"1", "1;I", 1, unpack1I},
{"1", "1;R", 1, unpack1R},
{"1", "1;IR", 1, unpack1IR},
{"1", "1;8", 1, unpack18},
/* greyscale */
{"L", "L;2", 2, unpackL2},

9
map.c
View File

@ -229,9 +229,6 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args)
im->destroy = ImagingDestroyMap;
if (!ImagingNewEpilogue(im))
return NULL;
mapper->offset += size;
return PyImagingNew(im);
@ -368,8 +365,7 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
}
im = ImagingNewPrologueSubtype(
mode, xsize, ysize, sizeof(ImagingBufferInstance)
);
mode, xsize, ysize, sizeof(ImagingBufferInstance));
if (!im)
return NULL;
@ -387,9 +383,6 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
((ImagingBufferInstance*) im)->target = target;
((ImagingBufferInstance*) im)->view = view;
if (!ImagingNewEpilogue(im))
return NULL;
return PyImagingNew(im);
}

View File

@ -365,6 +365,14 @@ class pil_build_ext(build_ext):
# work ;-)
self.add_multiarch_paths()
# termux support for android.
# system libraries (zlib) are installed in /system/lib
# headers are at $PREFIX/include
# user libs are at $PREFIX/lib
if os.environ.get('ANDROID_ROOT', None):
_add_directory(library_dirs,
os.path.join(os.environ['ANDROID_ROOT'],'lib'))
elif sys.platform.startswith("gnu"):
self.add_multiarch_paths()

View File

@ -55,15 +55,15 @@ libs = {
'version': '8.5.19',
},
'tcl-8.6': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.6/tcl866-src.zip',
'filename': PILLOW_DEPENDS_DIR + 'tcl866-src.zip',
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.7/tcl867-src.zip',
'filename': PILLOW_DEPENDS_DIR + 'tcl867-src.zip',
'dir': '',
},
'tk-8.6': {
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.6/tk866-src.zip',
'filename': PILLOW_DEPENDS_DIR + 'tk866-src.zip',
'url': SF_MIRROR+'/project/tcl/Tcl/8.6.7/tk867-src.zip',
'filename': PILLOW_DEPENDS_DIR + 'tk867-src.zip',
'dir': '',
'version': '8.6.6',
'version': '8.6.7',
},
'webp': {
'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.6.0.tar.gz',
@ -71,9 +71,9 @@ libs = {
'dir': 'libwebp-0.6.0',
},
'openjpeg': {
'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.2/openjpeg-2.1.2.tar.gz',
'filename': PILLOW_DEPENDS_DIR + 'openjpeg-2.1.2.tar.gz',
'dir': 'openjpeg-2.1.2',
'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.2.0/openjpeg-2.2.0.tar.gz',
'filename': PILLOW_DEPENDS_DIR + 'openjpeg-2.2.0.tar.gz',
'dir': 'openjpeg-2.2.0',
},
}