From 598d97daffe27d1fdc20a79dbac7ff3fa353cfc1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 19 Dec 2013 21:38:31 -0800 Subject: [PATCH 01/29] Reorg+add test for #452 --- Tests/test_image_getpixel.py | 72 +++++++++++++++++------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py index 6c5e8b084..104b67097 100644 --- a/Tests/test_image_getpixel.py +++ b/Tests/test_image_getpixel.py @@ -9,49 +9,43 @@ def color(mode): else: return tuple(range(1, bands+1)) -def test_pixel(): - def pixel(mode): + +def check(mode, c=None): + if not c: c = color(mode) - im = Image.new(mode, (1, 1), None) - im.putpixel((0, 0), c) - return im.getpixel((0, 0)) + + #check putpixel + im = Image.new(mode, (1, 1), None) + im.putpixel((0, 0), c) + assert_equal(im.getpixel((0, 0)), c, + "put/getpixel roundtrip failed for mode %s, color %s" % + (mode, c)) + + # check inital color + im = Image.new(mode, (1, 1), c) + assert_equal(im.getpixel((0, 0)), c, + "initial color failed for mode %s, color %s " % + (mode, color)) - assert_equal(pixel("1"), 1) - assert_equal(pixel("L"), 1) - assert_equal(pixel("LA"), (1, 2)) - assert_equal(pixel("I"), 1) - assert_equal(pixel("I;16"), 1) - assert_equal(pixel("I;16B"), 1) - assert_equal(pixel("F"), 1.0) - assert_equal(pixel("P"), 1) - assert_equal(pixel("PA"), (1, 2)) - assert_equal(pixel("RGB"), (1, 2, 3)) - assert_equal(pixel("RGBA"), (1, 2, 3, 4)) - assert_equal(pixel("RGBX"), (1, 2, 3, 4)) - assert_equal(pixel("CMYK"), (1, 2, 3, 4)) - assert_equal(pixel("YCbCr"), (1, 2, 3)) +def test_basic(): + for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", + "P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"): + check(mode) -def test_image(): - - def pixel(mode): - im = Image.new(mode, (1, 1), color(mode)) - return im.getpixel((0, 0)) - - assert_equal(pixel("1"), 1) - assert_equal(pixel("L"), 1) - assert_equal(pixel("LA"), (1, 2)) - assert_equal(pixel("I"), 1) - assert_equal(pixel("I;16"), 1) - assert_equal(pixel("I;16B"), 1) - assert_equal(pixel("F"), 1.0) - assert_equal(pixel("P"), 1) - assert_equal(pixel("PA"), (1, 2)) - assert_equal(pixel("RGB"), (1, 2, 3)) - assert_equal(pixel("RGBA"), (1, 2, 3, 4)) - assert_equal(pixel("RGBX"), (1, 2, 3, 4)) - assert_equal(pixel("CMYK"), (1, 2, 3, 4)) - assert_equal(pixel("YCbCr"), (1, 2, 3)) +def test_signedness(): + # see https://github.com/python-imaging/Pillow/issues/452 + # pixelaccess is using signed int* instead of uint* + for mode in ("I;16", "I;16B"): + check(mode, 2**15-1) + check(mode, 2**15) + check(mode, 2**15+1) + check(mode, 2**16-1) + check("I", 2**31-1) + check("I", 2**31) + check("I", 2**31+1) + check("I", 2**32-1) + From 77c36d6edcf9c24280e978fffb899a29e04f0dd0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 19 Dec 2013 21:39:18 -0800 Subject: [PATCH 02/29] Using uint* for pixel access in mode I;16 and I;32, fixes #452 --- _imaging.c | 8 ++++---- libImaging/Access.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/_imaging.c b/_imaging.c index dcb063081..0cce74ce0 100644 --- a/_imaging.c +++ b/_imaging.c @@ -463,14 +463,14 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) { union { UINT8 b[4]; - INT16 h; - INT32 i; + UINT16 h; + UINT32 i; FLOAT32 f; } pixel; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return NULL; + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; } access->get_pixel(im, x, y, &pixel); diff --git a/libImaging/Access.c b/libImaging/Access.c index 70eb1af4c..82a4d5297 100644 --- a/libImaging/Access.c +++ b/libImaging/Access.c @@ -94,11 +94,11 @@ static void get_pixel_16L(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x+x]; - INT16* out = color; + UINT16* out = color; #ifdef WORDS_BIGENDIAN out[0] = in[0] + (in[1]<<8); #else - out[0] = *(INT16*) in; + out[0] = *(UINT16*) in; #endif } @@ -106,9 +106,9 @@ static void get_pixel_16B(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x+x]; - INT16* out = color; + UINT16* out = color; #ifdef WORDS_BIGENDIAN - out[0] = *(INT16*) in; + out[0] = *(UINT16*) in; #else out[0] = in[1] + (in[0]<<8); #endif @@ -125,11 +125,11 @@ static void get_pixel_32L(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x*4]; - INT32* out = color; + UINT32* out = color; #ifdef WORDS_BIGENDIAN out[0] = in[0] + (in[1]<<8) + (in[2]<<16) + (in[3]<<24); #else - out[0] = *(INT32*) in; + out[0] = *(UINT32*) in; #endif } @@ -137,9 +137,9 @@ static void get_pixel_32B(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x*4]; - INT32* out = color; + UINT32* out = color; #ifdef WORDS_BIGENDIAN - out[0] = *(INT32*) in; + out[0] = *(UINT32*) in; #else out[0] = in[3] + (in[2]<<8) + (in[1]<<16) + (in[0]<<24); #endif From 1dd80b26250206b8d5f00e91b3e24bda2570b745 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 30 Dec 2013 21:00:32 -0800 Subject: [PATCH 03/29] reverted int32 changes --- Tests/test_image_getpixel.py | 4 ---- _imaging.c | 2 +- libImaging/Access.c | 8 ++++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py index 104b67097..0acfd6698 100644 --- a/Tests/test_image_getpixel.py +++ b/Tests/test_image_getpixel.py @@ -42,10 +42,6 @@ def test_signedness(): check(mode, 2**15+1) check(mode, 2**16-1) - check("I", 2**31-1) - check("I", 2**31) - check("I", 2**31+1) - check("I", 2**32-1) diff --git a/_imaging.c b/_imaging.c index 0cce74ce0..3511de4cf 100644 --- a/_imaging.c +++ b/_imaging.c @@ -464,7 +464,7 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) union { UINT8 b[4]; UINT16 h; - UINT32 i; + INT32 i; FLOAT32 f; } pixel; diff --git a/libImaging/Access.c b/libImaging/Access.c index 82a4d5297..62c97f3a3 100644 --- a/libImaging/Access.c +++ b/libImaging/Access.c @@ -125,11 +125,11 @@ static void get_pixel_32L(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x*4]; - UINT32* out = color; + INT32* out = color; #ifdef WORDS_BIGENDIAN out[0] = in[0] + (in[1]<<8) + (in[2]<<16) + (in[3]<<24); #else - out[0] = *(UINT32*) in; + out[0] = *(INT32*) in; #endif } @@ -137,9 +137,9 @@ static void get_pixel_32B(Imaging im, int x, int y, void* color) { UINT8* in = (UINT8*) &im->image[y][x*4]; - UINT32* out = color; + INT32* out = color; #ifdef WORDS_BIGENDIAN - out[0] = *(UINT32*) in; + out[0] = *(INT32*) in; #else out[0] = in[3] + (in[2]<<8) + (in[1]<<16) + (in[0]<<24); #endif From 5efe737f6f8a10a40cef4bb0a251c100101c9762 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 10:41:25 -0800 Subject: [PATCH 04/29] Cffi pixel access object, #248 --- PIL/Image.py | 25 +++++- PIL/PyAccess.py | 191 +++++++++++++++++++++++++++++++++++++++++++++ Tests/test_cffi.py | 48 ++++++++++++ _imaging.c | 23 ++++++ 4 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 PIL/PyAccess.py create mode 100644 Tests/test_cffi.py diff --git a/PIL/Image.py b/PIL/Image.py index 87631d6f7..8c5844c64 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -100,6 +100,17 @@ import os, sys import collections import numbers +USE_CFFI_ACCESS = True +try: + import cffi + HAS_CFFI=True +except: + HAS_CFFI=False + +#if HAS_CFFI: +# from PIL import PyAccess + + def isImageType(t): """ @@ -468,6 +479,7 @@ class Image: self.info = {} self.category = NORMAL self.readonly = 0 + self.pyaccess = None def _new(self, im): new = Image() @@ -645,6 +657,13 @@ class Image: self.palette.mode = "RGBA" if self.im: + if HAS_CFFI and USE_CFFI_ACCESS: + if self.pyaccess: + return self.pyaccess + from PIL import PyAccess + self.pyaccess = PyAccess.new(self, self.readonly) + if self.pyaccess: + return self.pyaccess return self.im.pixel_access(self.readonly) def verify(self): @@ -974,6 +993,8 @@ class Image: """ self.load() + if self.pyaccess: + return self.pyaccess.getpixel(xy) return self.im.getpixel(xy) def getprojection(self): @@ -1290,7 +1311,9 @@ class Image: self.load() if self.readonly: self._copy() - + + if self.pyaccess: # undone , what about the readonly? Fix premerge + return self.pyaccess.putpixel(xy,value) return self.im.putpixel(xy, value) def resize(self, size, resample=NEAREST): diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py new file mode 100644 index 000000000..95abbf238 --- /dev/null +++ b/PIL/PyAccess.py @@ -0,0 +1,191 @@ +# +# The Python Imaging Library +# Pillow fork +# +# Python implementation of the PixelAccess Object +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# Copyright (c) 2013 Eric Soroos +# +# See the README file for information on usage and redistribution +# + +# Notes: +# +# * Implements the pixel access object following Access. +# * Does not implement the line functions, as they don't appear to be used +# * Taking only the tuple form, which is used from python. +# * Fill.c uses the integer form, but it's still going to use the old Access.c implementation. +# + +from __future__ import print_function + +from cffi import FFI + +DEBUG = 0 + +defs = """ +struct ImagingMemoryInstance { + + /* Format */ + char mode[7]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ + int type; /* Data type (IMAGING_TYPE_*) */ + int depth; /* Depth (ignored in this version) */ + int bands; /* Number of bands (1, 2, 3, or 4) */ + int xsize; /* Image dimension. */ + int ysize; + + /* Colour palette (for "P" images only) */ + void *palette; + + /* Data pointers */ + unsigned char **image8; /* Set for 8-bit images (pixelsize=1). */ + int **image32; /* Set for 32-bit images (pixelsize=4). */ + + /* Internals */ + char **image; /* Actual raster data. */ + char *block; /* Set if data is allocated in a single block. */ + + int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ + int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ + + /* Virtual methods */ + void (*destroy)(int im); /*keeping this for compatibility */ +}; + +struct Pixel_RGBA { + unsigned char r,g,b,a; +}; + +""" +ffi = FFI() +ffi.cdef(defs) + + +class PyAccess(object): + + def __init__(self, img, readonly = False): + vals = dict(img.im.unsafe_ptrs) + self.readonly = readonly + for att in ['palette', 'image8', 'image32','image', 'block', 'destroy']: + vals[att] = ffi.cast("void *", vals[att]) + if DEBUG: + print (vals) + self.im = ffi.new("struct ImagingMemoryInstance *", vals) + + def __setitem__(self, xy, color): + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ + if self.readonly: raise Exception('ValueError') # undone, ImagingError_ValueError + (x,y) = self.check_xy(xy) + return self.set_pixel(x,y,color) + + def __getitem__(self, xy): + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multiple band + images + + :param xy: The pixel coordinate, given as (x, y). + """ + + (x,y) = self.check_xy(xy) + return self.get_pixel(x,y) + + putpixel = __setitem__ + getpixel = __getitem__ + + def check_xy(self, xy): + (x,y) = xy + if not (0 <= x < self.im.xsize and 0 <= y < self.im.ysize): + raise Exception('ValueError- pixel location out of range') #undone + return (x,y) + + +class _PyAccess32_3(PyAccess): + def __init__(self, *args, **kwargs): + PyAccess.__init__(self, *args, **kwargs) + self.pixels = ffi.cast("struct Pixel_RGBA **", self.im.image32) + + def get_pixel(self, x,y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b) + + def set_pixel(self, x,y, color): + pixel = self.pixels[y][x] + try: + # tuple + pixel.r, pixel.g, pixel.b = color + except: + # int, as a char[4] + pixel.r = color >> 24 + pixel.g = (color >> 16) & 0xFF + pixel.b = (color >> 8) & 0xFF + +class _PyAccess32_4(PyAccess): + def __init__(self, *args, **kwargs): + PyAccess.__init__(self, *args, **kwargs) + self.pixels = ffi.cast("struct Pixel_RGBA **", self.im.image32) + + def get_pixel(self, x,y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b, pixel.a) + + def set_pixel(self, x,y, color): + pixel = self.pixels[y][x] + try: + # tuple + pixel.r, pixel.g, pixel.b, pixel.a = color + except: + # int, as a char[4] + pixel.r = color >> 24 + pixel.g = (color >> 16) & 0xFF + pixel.b = (color >> 8) & 0xFF + pixel.a = color & 0xFF + +class _PyAccess8(PyAccess): + def __init__(self, *args, **kwargs): + PyAccess.__init__(self, *args, **kwargs) + self.pixels = self.im.image8 + + def get_pixel(self, x,y): + return self.pixels[y][x] + + def set_pixel(self, x,y, color): + try: + # integer + self.pixels[y][x] = color & 0xFF + except: + # tuple + self.pixels[y][x] = color[0] & 0xFF + + +mode_map = {'1': _PyAccess8, + 'L': _PyAccess8, + 'P': _PyAccess8, + 'RGB': _PyAccess32_3, + 'LAB': _PyAccess32_3, + 'YCbCr': _PyAccess32_3, + 'RGBA': _PyAccess32_4, + 'RGBa': _PyAccess32_4, + 'RGBX': _PyAccess32_4, + 'CMYK': _PyAccess32_4, + } + +def new(img, readonly=False): + + access_type = mode_map.get(img.mode, None) + if not access_type: + if DEBUG: print ("PyAccess Not Implemented: %s" % img.mode) + return None + if DEBUG: print ("New PyAccess: %s" % img.mode) + return access_type(img, readonly) + + diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py new file mode 100644 index 000000000..c3883d22f --- /dev/null +++ b/Tests/test_cffi.py @@ -0,0 +1,48 @@ +from tester import * + +from PIL import Image, PyAccess + +import test_image_putpixel as put +import test_image_getpixel as get + + + +try: + import cffi +except: + skip() + +Image.USE_CFFI_ACCESS = True + +def test_put(): + put.test_sanity() + +def test_get(): + get.test_pixel() + get.test_image() + + +def xtest_direct(): + im = Image.open('lena.png') + caccess = im.im.pixel_access(false) + access = PyAccess.new(im) + + print (caccess[(0,0)]) + print (access[(0,0)]) + + print (access.im.depth) + print (access.im.image32[0][0]) + print (im.getpixel((0,0))) + print (access.get_pixel(0,0)) + access.set_pixel(0,0,(1,2,3)) + print (im.getpixel((0,0))) + print (access.get_pixel(0,0)) + + access.set_pixel(0,0,(1,2,3)) + print (im.getpixel((0,0))) + print (access.get_pixel(0,0)) + + p_int = (5 << 24) + (4<<16) + (3 <<8) + access.set_pixel(0,0,p_int) + print (im.getpixel((0,0))) + print (access.get_pixel(0,0)) diff --git a/_imaging.c b/_imaging.c index c62f0257d..13466f00a 100644 --- a/_imaging.c +++ b/_imaging.c @@ -3072,12 +3072,35 @@ _getattr_ptr(ImagingObject* self, void* closure) #endif } +static PyObject* +_getattr_unsafe_ptrs(ImagingObject* self, void* closure) +{ + return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)", + "mode", self->image->mode, + "type", self->image->type, + "depth", self->image->depth, + "bands", self->image->bands, + "xsize", self->image->xsize, + "ysize", self->image->ysize, + "palette", self->image->palette, + "image8", self->image->image8, + "image32", self->image->image32, + "image", self->image->image, + "block", self->image->block, + "pixelsize", self->image->pixelsize, + "linesize", self->image->linesize, + "destroy", self->image->destroy + ); +}; + + static struct PyGetSetDef getsetters[] = { { "mode", (getter) _getattr_mode }, { "size", (getter) _getattr_size }, { "bands", (getter) _getattr_bands }, { "id", (getter) _getattr_id }, { "ptr", (getter) _getattr_ptr }, + { "unsafe_ptrs", (getter) _getattr_unsafe_ptrs }, { NULL } }; From bb20f6ca831bfaf41d00f1c864c282f4acd649f4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 16:45:01 -0800 Subject: [PATCH 05/29] basic benchmark shows ~order of magnitude speedup --- Tests/bench_cffi_access.py | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Tests/bench_cffi_access.py diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py new file mode 100644 index 000000000..21d491818 --- /dev/null +++ b/Tests/bench_cffi_access.py @@ -0,0 +1,43 @@ +from tester import * + +# not running this test by default. No DOS against travis. + +from PIL import PyAccess + +import time + +def iterate_get(size, access): + (w,h) = size + for x in range(w): + for y in range(h): + access[(x,y)] + +def iterate_set(size, access): + (w,h) = size + for x in range(w): + for y in range(h): + access[(x,y)] = access[(x,y)] + +def timer(func, label, *args): + starttime = time.time() + func(*args) + endtime = time.time() + print ("%s: %.4f s" %(label, endtime-starttime)) + +def test_direct(): + im = lena() + im.load() + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + assert_equal(caccess[(0,0)], access[(0,0)]) + + print ("Size: %sx%s" % im.size) + timer(iterate_get, 'PyAccess - get', im.size, access) + timer(iterate_set, 'PyAccess - set', im.size, access) + timer(iterate_get, 'C-api - get', im.size, caccess) + timer(iterate_set, 'C-api - set', im.size, caccess) + + + + From de538897115e0e3948caf4c42ac41e1f1876a173 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 20:50:05 -0800 Subject: [PATCH 06/29] removed initial testing --- Tests/test_cffi.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index c3883d22f..f4fc8760f 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -22,27 +22,3 @@ def test_get(): get.test_image() -def xtest_direct(): - im = Image.open('lena.png') - caccess = im.im.pixel_access(false) - access = PyAccess.new(im) - - print (caccess[(0,0)]) - print (access[(0,0)]) - - print (access.im.depth) - print (access.im.image32[0][0]) - print (im.getpixel((0,0))) - print (access.get_pixel(0,0)) - access.set_pixel(0,0,(1,2,3)) - print (im.getpixel((0,0))) - print (access.get_pixel(0,0)) - - access.set_pixel(0,0,(1,2,3)) - print (im.getpixel((0,0))) - print (access.get_pixel(0,0)) - - p_int = (5 << 24) + (4<<16) + (3 <<8) - access.set_pixel(0,0,p_int) - print (im.getpixel((0,0))) - print (access.get_pixel(0,0)) From 663f881b212d290604e4c259256616b26c1be09e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 21:06:13 -0800 Subject: [PATCH 07/29] all original tests pass --- PIL/Image.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 8c5844c64..2e353dd47 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1311,8 +1311,10 @@ class Image: self.load() if self.readonly: self._copy() + self.pyaccess = None + self.load() - if self.pyaccess: # undone , what about the readonly? Fix premerge + if self.pyaccess: return self.pyaccess.putpixel(xy,value) return self.im.putpixel(xy, value) From 273a5014d2b3782819d7eaae144bfe80496f5125 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 21:18:53 -0800 Subject: [PATCH 08/29] more extensive testing, matches original c code --- Tests/test_cffi.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index f4fc8760f..1cb1e694c 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -21,4 +21,44 @@ def test_get(): get.test_pixel() get.test_image() +def _test_get_access(im): + """ Do we get the same thing as the old pixel access """ + """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w,h = im.size + for x in range(0,w,10): + for y in range(0,h,10): + assert_equal(caccess[(x,y)], access[(x,y)]) + +def test_get_vs_c(): + _test_get_access(lena('RGB')) + _test_get_access(lena('RGBA')) + _test_get_access(lena('L')) + _test_get_access(lena('1')) + _test_get_access(lena('P')) + + +def _test_set_access(im, color): + """ Are we writing the correct bits into the image? """ + + """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w,h = im.size + for x in range(0,w,10): + for y in range(0,h,10): + access[(x,y)] = color + assert_equal(caccess[(x,y)], color) + +def test_set_vs_c(): + _test_set_access(lena('RGB'), (255, 128,0) ) + _test_set_access(lena('RGBA'), (255, 192, 128, 0)) + _test_set_access(lena('L'), 128) + _test_set_access(lena('1'), 255) + _test_set_access(lena('P') , 128) + + From c98f731f7e720a609d8f30d5a50888ec05869721 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 21:36:13 -0800 Subject: [PATCH 09/29] Ensuring c-api access --- Tests/test_image_getpixel.py | 2 ++ Tests/test_image_putpixel.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py index 6c5e8b084..ffa6a9c52 100644 --- a/Tests/test_image_getpixel.py +++ b/Tests/test_image_getpixel.py @@ -2,6 +2,8 @@ from tester import * from PIL import Image +Image.USE_CFFI_ACCESS=False + def color(mode): bands = Image.getmodebands(mode) if bands == 1: diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py index 2b60bbd97..5f19237cb 100644 --- a/Tests/test_image_putpixel.py +++ b/Tests/test_image_putpixel.py @@ -2,6 +2,8 @@ from tester import * from PIL import Image +Image.USE_CFFI_ACCESS=False + def test_sanity(): im1 = lena() From 2921b5c5e5145a26320faf8e3bd6058f3f9ec1b6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 21:36:29 -0800 Subject: [PATCH 10/29] Getting travis into the testing loop --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1eecc9c13..d7c3dcd27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,9 @@ python: - 3.2 - 3.3 -install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript"" +install: + - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev" + - "pip install cffi" script: - python setup.py clean From d8a88a53902c85418d50751ee5cda84ba1705090 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 22:00:09 -0800 Subject: [PATCH 11/29] Passes tests on python 3.2 Reorganized to remove ImagingMemoryInstance struct, only saving the three pointers that we need (image, image8, image32) and the x/ysize ints. --- PIL/PyAccess.py | 79 +++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 58 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 95abbf238..8ccff11ed 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -26,34 +26,6 @@ from cffi import FFI DEBUG = 0 defs = """ -struct ImagingMemoryInstance { - - /* Format */ - char mode[7]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ - int type; /* Data type (IMAGING_TYPE_*) */ - int depth; /* Depth (ignored in this version) */ - int bands; /* Number of bands (1, 2, 3, or 4) */ - int xsize; /* Image dimension. */ - int ysize; - - /* Colour palette (for "P" images only) */ - void *palette; - - /* Data pointers */ - unsigned char **image8; /* Set for 8-bit images (pixelsize=1). */ - int **image32; /* Set for 32-bit images (pixelsize=4). */ - - /* Internals */ - char **image; /* Actual raster data. */ - char *block; /* Set if data is allocated in a single block. */ - - int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ - int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ - - /* Virtual methods */ - void (*destroy)(int im); /*keeping this for compatibility */ -}; - struct Pixel_RGBA { unsigned char r,g,b,a; }; @@ -68,12 +40,18 @@ class PyAccess(object): def __init__(self, img, readonly = False): vals = dict(img.im.unsafe_ptrs) self.readonly = readonly - for att in ['palette', 'image8', 'image32','image', 'block', 'destroy']: - vals[att] = ffi.cast("void *", vals[att]) + self.image8 = ffi.cast('unsigned char **', vals['image8']) + self.image32 = ffi.cast('int **', vals['image32']) + self.image = ffi.cast('unsigned char **', vals['image']) + self.xsize = vals['xsize'] + self.ysize = vals['ysize'] + if DEBUG: print (vals) - self.im = ffi.new("struct ImagingMemoryInstance *", vals) + self._post_init() + def _post_init(): pass + def __setitem__(self, xy, color): """ Modifies the pixel at x,y. The color is given as a single @@ -104,15 +82,14 @@ class PyAccess(object): def check_xy(self, xy): (x,y) = xy - if not (0 <= x < self.im.xsize and 0 <= y < self.im.ysize): + if not (0 <= x < self.xsize and 0 <= y < self.ysize): raise Exception('ValueError- pixel location out of range') #undone return (x,y) class _PyAccess32_3(PyAccess): - def __init__(self, *args, **kwargs): - PyAccess.__init__(self, *args, **kwargs) - self.pixels = ffi.cast("struct Pixel_RGBA **", self.im.image32) + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) def get_pixel(self, x,y): pixel = self.pixels[y][x] @@ -120,19 +97,13 @@ class _PyAccess32_3(PyAccess): def set_pixel(self, x,y, color): pixel = self.pixels[y][x] - try: - # tuple - pixel.r, pixel.g, pixel.b = color - except: - # int, as a char[4] - pixel.r = color >> 24 - pixel.g = (color >> 16) & 0xFF - pixel.b = (color >> 8) & 0xFF + # tuple + pixel.r, pixel.g, pixel.b = color + class _PyAccess32_4(PyAccess): - def __init__(self, *args, **kwargs): - PyAccess.__init__(self, *args, **kwargs) - self.pixels = ffi.cast("struct Pixel_RGBA **", self.im.image32) + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) def get_pixel(self, x,y): pixel = self.pixels[y][x] @@ -140,20 +111,12 @@ class _PyAccess32_4(PyAccess): def set_pixel(self, x,y, color): pixel = self.pixels[y][x] - try: - # tuple - pixel.r, pixel.g, pixel.b, pixel.a = color - except: - # int, as a char[4] - pixel.r = color >> 24 - pixel.g = (color >> 16) & 0xFF - pixel.b = (color >> 8) & 0xFF - pixel.a = color & 0xFF + # tuple + pixel.r, pixel.g, pixel.b, pixel.a = color class _PyAccess8(PyAccess): - def __init__(self, *args, **kwargs): - PyAccess.__init__(self, *args, **kwargs) - self.pixels = self.im.image8 + def _post_init(self, *args, **kwargs): + self.pixels = self.image8 def get_pixel(self, x,y): return self.pixels[y][x] From 32f4097d1e132eb02689a553c89a2bc893dda256 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 22:18:42 -0800 Subject: [PATCH 12/29] Fixes putalpha tests Make sure we clear the pyaccess object each time we reset the self.im object --- PIL/Image.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index 2e353dd47..254b5b9c4 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -504,6 +504,7 @@ class Image: def _copy(self): self.load() self.im = self.im.copy() + self.pyaccess = None self.readonly = 0 def _dump(self, file=None, format=None): @@ -1205,12 +1206,14 @@ class Image: mode = getmodebase(self.mode) + "A" try: self.im.setmode(mode) + self.pyaccess = None except (AttributeError, ValueError): # do things the hard way im = self.im.convert(mode) if im.mode not in ("LA", "RGBA"): raise ValueError # sanity check self.im = im + self.pyaccess = None self.mode = self.im.mode except (KeyError, ValueError): raise ValueError("illegal image mode") @@ -1616,6 +1619,7 @@ class Image: self.size = size self.readonly = 0 + self.pyaccess = None # FIXME: the different tranform methods need further explanation # instead of bloating the method docs, add a separate chapter. From 594276c45e7ec4e78f29d6ff61a958bb079aa42a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 5 Jan 2014 22:20:07 -0800 Subject: [PATCH 13/29] existing behaviour is to clamp to max, not bitmask --- PIL/PyAccess.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 8ccff11ed..5f56e2bca 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -112,6 +112,7 @@ class _PyAccess32_4(PyAccess): def set_pixel(self, x,y, color): pixel = self.pixels[y][x] # tuple + #undone clamp? pixel.r, pixel.g, pixel.b, pixel.a = color class _PyAccess8(PyAccess): @@ -124,10 +125,10 @@ class _PyAccess8(PyAccess): def set_pixel(self, x,y, color): try: # integer - self.pixels[y][x] = color & 0xFF + self.pixels[y][x] = min(color,255) except: # tuple - self.pixels[y][x] = color[0] & 0xFF + self.pixels[y][x] = min(color[0],255) mode_map = {'1': _PyAccess8, From 4cacfe6b118fdcf2b0bfb5e185c40dc9c62fe25f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 08:55:06 -0800 Subject: [PATCH 14/29] longer benchmark --- Tests/bench_cffi_access.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 21d491818..140cedd61 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -19,10 +19,16 @@ def iterate_set(size, access): access[(x,y)] = access[(x,y)] def timer(func, label, *args): + iterations = 1000 starttime = time.time() - func(*args) - endtime = time.time() - print ("%s: %.4f s" %(label, endtime-starttime)) + for x in range(iterations): + func(*args) + if time.time()-starttime > 10: + print ("%s: breaking at %s iterations, %.6f per iteration"%(label, x+1, (time.time()-starttime)/(x+1.0))) + break + if x == iterations-1: + endtime = time.time() + print ("%s: %.4f s %.6f per iteration" %(label, endtime-starttime, (endtime-starttime)/(x+1.0))) def test_direct(): im = lena() From 53ba63fcd5fb58899084703ea2297191647eca7b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 21:19:58 -0800 Subject: [PATCH 15/29] Raising proper ValueErrors --- PIL/PyAccess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 5f56e2bca..1c1294203 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -61,7 +61,7 @@ class PyAccess(object): :param xy: The pixel coordinate, given as (x, y). :param value: The pixel value. """ - if self.readonly: raise Exception('ValueError') # undone, ImagingError_ValueError + if self.readonly: raise ValueError('Attempt to putpixel a read only image') (x,y) = self.check_xy(xy) return self.set_pixel(x,y,color) @@ -83,8 +83,8 @@ class PyAccess(object): def check_xy(self, xy): (x,y) = xy if not (0 <= x < self.xsize and 0 <= y < self.ysize): - raise Exception('ValueError- pixel location out of range') #undone - return (x,y) + raise ValueError('pixel location out of range') + return xy class _PyAccess32_3(PyAccess): From c3812f46b24f0162c1417979e68431213517a1fd Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 21:20:19 -0800 Subject: [PATCH 16/29] clamping channels to 255 --- PIL/PyAccess.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 1c1294203..e3fdc9ec7 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -98,8 +98,9 @@ class _PyAccess32_3(PyAccess): def set_pixel(self, x,y, color): pixel = self.pixels[y][x] # tuple - pixel.r, pixel.g, pixel.b = color - + pixel.r = min(color[0],255) + pixel.g = min(color[1],255) + pixel.b = min(color[2],255) class _PyAccess32_4(PyAccess): def _post_init(self, *args, **kwargs): @@ -112,8 +113,11 @@ class _PyAccess32_4(PyAccess): def set_pixel(self, x,y, color): pixel = self.pixels[y][x] # tuple - #undone clamp? - pixel.r, pixel.g, pixel.b, pixel.a = color + pixel.r = min(color[0],255) + pixel.g = min(color[1],255) + pixel.b = min(color[2],255) + pixel.a = min(color[3],255) + class _PyAccess8(PyAccess): def _post_init(self, *args, **kwargs): From 8a69f6caa0ae564812a6c1b23c8fa4457f7ad5c5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 22:08:14 -0800 Subject: [PATCH 17/29] use cffi by default in pypy, not in c-python --- PIL/Image.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 254b5b9c4..25673c4f0 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -100,18 +100,14 @@ import os, sys import collections import numbers -USE_CFFI_ACCESS = True +# works everywhere, win for pypy, not cpython +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi HAS_CFFI=True except: HAS_CFFI=False -#if HAS_CFFI: -# from PIL import PyAccess - - - def isImageType(t): """ Checks if an object is an image object. From 121b51aebc6da52fb1473a4ca379beea0e51265c Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 22:09:00 -0800 Subject: [PATCH 18/29] trying to bust up the JIT a bit --- Tests/bench_cffi_access.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 140cedd61..8f8ef937a 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -3,6 +3,7 @@ from tester import * # not running this test by default. No DOS against travis. from PIL import PyAccess +from PIL import Image import time @@ -16,10 +17,10 @@ def iterate_set(size, access): (w,h) = size for x in range(w): for y in range(h): - access[(x,y)] = access[(x,y)] + access[(x,y)] = (x %256,y%256,0) def timer(func, label, *args): - iterations = 1000 + iterations = 5000 starttime = time.time() for x in range(iterations): func(*args) @@ -33,6 +34,7 @@ def timer(func, label, *args): def test_direct(): im = lena() im.load() + #im = Image.new( "RGB", (2000,2000), (1,3,2)) caccess = im.im.pixel_access(False) access = PyAccess.new(im, False) From 5dfadf623bc90dc43cffacdafc291a955228a869 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 22:51:31 -0800 Subject: [PATCH 19/29] LA, PA image modes --- PIL/PyAccess.py | 20 ++++++++++++++++++++ Tests/test_cffi.py | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index e3fdc9ec7..40b717c4a 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -86,8 +86,24 @@ class PyAccess(object): raise ValueError('pixel location out of range') return xy +class _PyAccess32_2(PyAccess): + """ PA, LA, stored in first and last bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x,y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.a) + def set_pixel(self, x,y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0],255) + pixel.a = min(color[1],255) + class _PyAccess32_3(PyAccess): + """ RGB and friends, stored in the first three bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) @@ -103,6 +119,7 @@ class _PyAccess32_3(PyAccess): pixel.b = min(color[2],255) class _PyAccess32_4(PyAccess): + """ RGBA etc, all 4 bytes of a 32 bit word """ def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) @@ -120,6 +137,7 @@ class _PyAccess32_4(PyAccess): class _PyAccess8(PyAccess): + """ 1, L, P, 8 bit images stored as uint8 """ def _post_init(self, *args, **kwargs): self.pixels = self.image8 @@ -138,6 +156,8 @@ class _PyAccess8(PyAccess): mode_map = {'1': _PyAccess8, 'L': _PyAccess8, 'P': _PyAccess8, + 'LA': _PyAccess32_2, + 'PA': _PyAccess32_2, 'RGB': _PyAccess32_3, 'LAB': _PyAccess32_3, 'YCbCr': _PyAccess32_3, diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 1cb1e694c..4af0456ca 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -37,8 +37,10 @@ def test_get_vs_c(): _test_get_access(lena('RGB')) _test_get_access(lena('RGBA')) _test_get_access(lena('L')) + _test_get_access(lena('LA')) _test_get_access(lena('1')) _test_get_access(lena('P')) + #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? def _test_set_access(im, color): @@ -58,7 +60,9 @@ def test_set_vs_c(): _test_set_access(lena('RGB'), (255, 128,0) ) _test_set_access(lena('RGBA'), (255, 192, 128, 0)) _test_set_access(lena('L'), 128) + _test_set_access(lena('LA'), (128,128)) _test_set_access(lena('1'), 255) _test_set_access(lena('P') , 128) + ##_test_set_access(i, (128,128)) #PA -- undone how to make From 84e2cf6eda35ea5cad5e6f56cfd77281d03989ae Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 23:15:00 -0800 Subject: [PATCH 20/29] I;16 modes --- PIL/PyAccess.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 40b717c4a..f75fb312d 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -22,6 +22,7 @@ from __future__ import print_function from cffi import FFI +import sys DEBUG = 0 @@ -29,7 +30,9 @@ defs = """ struct Pixel_RGBA { unsigned char r,g,b,a; }; - +struct Pixel_I16 { + unsigned char l,r; +}; """ ffi = FFI() ffi.cdef(defs) @@ -152,6 +155,59 @@ class _PyAccess8(PyAccess): # tuple self.pixels[y][x] = min(color[0],255) +class _PyAccessI16_N(PyAccess): + """ I;16 access, native bitendian without conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('unsigned short **', self.image) + + def get_pixel(self, x,y): + return self.pixels[y][x] + + def set_pixel(self, x,y, color): + try: + # integer + self.pixels[y][x] = min(color, 65535) + except: + # tuple + self.pixels[y][x] = min(color[0], 65535) + +class _PyAccessI16_L(PyAccess): + """ I;16L access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x,y): + pixel = self.pixels[y][x] + return pixel.l + pixel.r * 256 + + def set_pixel(self, x,y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color & 0xFF + pixel.r = color >> 8 + +class _PyAccessI16_B(PyAccess): + """ I;16B access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x,y): + pixel = self.pixels[y][x] + return pixel.l *256 + pixel.r + + def set_pixel(self, x,y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color >> 8 + pixel.r = color & 0xFF mode_map = {'1': _PyAccess8, 'L': _PyAccess8, @@ -167,6 +223,16 @@ mode_map = {'1': _PyAccess8, 'CMYK': _PyAccess32_4, } +if sys.byteorder == 'little': + mode_map['I;16'] = _PyAccessI16_N + mode_map['I;16L'] = _PyAccessI16_N + mode_map['I;16B'] = _PyAccessI16_B +else: + mode_map['I;16'] = _PyAccessI16_L + mode_map['I;16L'] = _PyAccessI16_L + mode_map['I;16B'] = _PyAccessI16_N + + def new(img, readonly=False): access_type = mode_map.get(img.mode, None) From b6ad79d9f902088a3bdb2f5d799d98f87bea135e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 6 Jan 2014 23:25:41 -0800 Subject: [PATCH 21/29] detailed tests for I;16 modes, merged from i16-pixelaccess branch --- Tests/test_cffi.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 4af0456ca..87214475b 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -18,8 +18,8 @@ def test_put(): put.test_sanity() def test_get(): - get.test_pixel() - get.test_image() + get.test_basic() + get.test_signedness() def _test_get_access(im): """ Do we get the same thing as the old pixel access """ @@ -31,7 +31,7 @@ def _test_get_access(im): w,h = im.size for x in range(0,w,10): for y in range(0,h,10): - assert_equal(caccess[(x,y)], access[(x,y)]) + assert_equal(access[(x,y)], caccess[(x,y)]) def test_get_vs_c(): _test_get_access(lena('RGB')) @@ -42,6 +42,12 @@ def test_get_vs_c(): _test_get_access(lena('P')) #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? + im = Image.new('I;16', (10,10), 40000) + _test_get_access(im) + im = Image.new('I;16L', (10,10), 40000) + _test_get_access(im) + im = Image.new('I;16B', (10,10), 40000) + _test_get_access(im) def _test_set_access(im, color): """ Are we writing the correct bits into the image? """ @@ -54,7 +60,7 @@ def _test_set_access(im, color): for x in range(0,w,10): for y in range(0,h,10): access[(x,y)] = color - assert_equal(caccess[(x,y)], color) + assert_equal(color, caccess[(x,y)]) def test_set_vs_c(): _test_set_access(lena('RGB'), (255, 128,0) ) @@ -65,4 +71,10 @@ def test_set_vs_c(): _test_set_access(lena('P') , 128) ##_test_set_access(i, (128,128)) #PA -- undone how to make + im = Image.new('I;16', (10,10), 40000) + _test_set_access(im, 45000) + im = Image.new('I;16L', (10,10), 40000) + _test_set_access(im, 45000) + im = Image.new('I;16B', (10,10), 40000) + _test_set_access(im, 45000) From bfdc599c289c046e11f708ce286b68560ecc17c0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 8 Jan 2014 20:23:20 -0800 Subject: [PATCH 22/29] F mode support --- PIL/PyAccess.py | 19 +++++++++++++++++++ Tests/test_cffi.py | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index f75fb312d..cd862d4f0 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -209,6 +209,24 @@ class _PyAccessI16_B(PyAccess): pixel.l = color >> 8 pixel.r = color & 0xFF + +class _PyAccessF(PyAccess): + """ 32 bit float access """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('float **', self.image32) + + def get_pixel(self, x,y): + return self.pixels[y][x] + + def set_pixel(self, x,y, color): + try: + # not a tuple + self.pixels[y][x] = color + except: + # tuple + self.pixels[y][x] = color[0] + + mode_map = {'1': _PyAccess8, 'L': _PyAccess8, 'P': _PyAccess8, @@ -221,6 +239,7 @@ mode_map = {'1': _PyAccess8, 'RGBa': _PyAccess32_4, 'RGBX': _PyAccess32_4, 'CMYK': _PyAccess32_4, + 'F': _PyAccessF, } if sys.byteorder == 'little': diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 87214475b..1cf991701 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -41,13 +41,16 @@ def test_get_vs_c(): _test_get_access(lena('1')) _test_get_access(lena('P')) #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? - + _test_get_access(lena('F')) + im = Image.new('I;16', (10,10), 40000) _test_get_access(im) im = Image.new('I;16L', (10,10), 40000) _test_get_access(im) im = Image.new('I;16B', (10,10), 40000) _test_get_access(im) + + def _test_set_access(im, color): """ Are we writing the correct bits into the image? """ @@ -70,7 +73,8 @@ def test_set_vs_c(): _test_set_access(lena('1'), 255) _test_set_access(lena('P') , 128) ##_test_set_access(i, (128,128)) #PA -- undone how to make - + _test_set_access(lena('F'), 1024.0) + im = Image.new('I;16', (10,10), 40000) _test_set_access(im, 45000) im = Image.new('I;16L', (10,10), 40000) From 79a7fdc1002cde5383996b147fd1fa2993f962ca Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 8 Jan 2014 21:18:38 -0800 Subject: [PATCH 23/29] I32 modes, not that I can actually use the I;32L/B ones --- PIL/PyAccess.py | 36 +++++++++++++++++++++++++++++++++++- Tests/test_cffi.py | 16 ++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index cd862d4f0..792a62f76 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -209,6 +209,33 @@ class _PyAccessI16_B(PyAccess): pixel.l = color >> 8 pixel.r = color & 0xFF +class _PyAccessI32_N(PyAccess): + """ Signed Int32 access, native endian """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def get_pixel(self, x,y): + return self.pixels[y][x] + + def set_pixel(self, x,y, color): + self.pixels[y][x] = color + +class _PyAccessI32_Swap(PyAccess): + """ I;32L/B access, with byteswapping conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def reverse(self, i): + orig = ffi.new('int *', i) + chars = ffi.cast('unsigned char *', orig) + chars[0],chars[1],chars[2],chars[3] = chars[3], chars[2],chars[1],chars[0] + return ffi.cast('int *', chars)[0] + + def get_pixel(self, x,y): + return self.reverse(self.pixels[y][x]) + + def set_pixel(self, x,y, color): + self.pixels[y][x] = self.reverse(color) class _PyAccessF(PyAccess): """ 32 bit float access """ @@ -246,11 +273,18 @@ if sys.byteorder == 'little': mode_map['I;16'] = _PyAccessI16_N mode_map['I;16L'] = _PyAccessI16_N mode_map['I;16B'] = _PyAccessI16_B + + mode_map['I'] = _PyAccessI32_N + mode_map['I;32L'] = _PyAccessI32_N + mode_map['I;32B'] = _PyAccessI32_Swap else: mode_map['I;16'] = _PyAccessI16_L mode_map['I;16L'] = _PyAccessI16_L mode_map['I;16B'] = _PyAccessI16_N - + + mode_map['I'] = _PyAccessI32_Swap + mode_map['I;32L'] = _PyAccessI32_Swap + mode_map['I;32B'] = _PyAccessI32_N def new(img, readonly=False): diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 1cf991701..4065a9e53 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -50,6 +50,15 @@ def test_get_vs_c(): im = Image.new('I;16B', (10,10), 40000) _test_get_access(im) + im = Image.new('I', (10,10), 40000) + _test_get_access(im) + # These don't actually appear to be modes that I can actually make, + # as unpack sets them directly into the I mode. + #im = Image.new('I;32L', (10,10), -2**10) + #_test_get_access(im) + #im = Image.new('I;32B', (10,10), 2**10) + #_test_get_access(im) + def _test_set_access(im, color): @@ -82,3 +91,10 @@ def test_set_vs_c(): im = Image.new('I;16B', (10,10), 40000) _test_set_access(im, 45000) + + im = Image.new('I', (10,10), 40000) + _test_set_access(im, 45000) +# im = Image.new('I;32L', (10,10), -(2**10)) +# _test_set_access(im, -(2**13)+1) + #im = Image.new('I;32B', (10,10), 2**10) + #_test_set_access(im, 2**13-1) From 9e069bfa542b505d019ddb6c38a32a2fc1435eb2 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Thu, 9 Jan 2014 05:44:18 +0000 Subject: [PATCH 24/29] I mode is native endian --- PIL/PyAccess.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PIL/PyAccess.py b/PIL/PyAccess.py index 792a62f76..f76beb820 100644 --- a/PIL/PyAccess.py +++ b/PIL/PyAccess.py @@ -267,6 +267,7 @@ mode_map = {'1': _PyAccess8, 'RGBX': _PyAccess32_4, 'CMYK': _PyAccess32_4, 'F': _PyAccessF, + 'I': _PyAccessI32_N, } if sys.byteorder == 'little': @@ -274,15 +275,13 @@ if sys.byteorder == 'little': mode_map['I;16L'] = _PyAccessI16_N mode_map['I;16B'] = _PyAccessI16_B - mode_map['I'] = _PyAccessI32_N mode_map['I;32L'] = _PyAccessI32_N mode_map['I;32B'] = _PyAccessI32_Swap else: mode_map['I;16'] = _PyAccessI16_L mode_map['I;16L'] = _PyAccessI16_L mode_map['I;16B'] = _PyAccessI16_N - - mode_map['I'] = _PyAccessI32_Swap + mode_map['I;32L'] = _PyAccessI32_Swap mode_map['I;32B'] = _PyAccessI32_N From c245c1102d7ba784f0749d3e6b68f5a3d3f56124 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 12 Jan 2014 10:33:00 -0800 Subject: [PATCH 25/29] platform test matrix updates --- docs/installation.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 6c6fe3196..1eae2208c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -188,19 +188,25 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Redhat Linux 6 |Yes | 2.6 | |x86 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | +| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | +| Fedora 20 |Yes | 2.7,3.3 | 2.3.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | +| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.3.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.3.0 |x86,x86-64 | +| | | | | | +| | | 2.7,3.2 | 2.3.0 |ppc | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 13.10 |Yes | 2.7,3.2,3.3 | 2.3.0 |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Raspian Wheezy |Yes | 2.7,3.2 | 2.3.0 |arm | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From 0348fcac51046bc369da3003e70f2790586315d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Reni=C3=A9?= Date: Sun, 19 Jan 2014 16:40:39 +0100 Subject: [PATCH 26/29] Fix crash on save when icc_profile is None --- PIL/PngImagePlugin.py | 2 +- Tests/images/icc_profile_none.png | Bin 0 -> 4128 bytes Tests/test_file_png.py | 7 +++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Tests/images/icc_profile_none.png diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 7028083d7..f3738bd24 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -618,7 +618,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] except ImportError: name = b"ICC Profile" - data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + data = name or b'ICC Profile' + b"\0\0" + zlib.compress(im.info["icc_profile"]) chunk(fp, b"iCCP", data) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) diff --git a/Tests/images/icc_profile_none.png b/Tests/images/icc_profile_none.png new file mode 100644 index 0000000000000000000000000000000000000000..9ecff3a97b8eb78026c4ccf27b72039b52311f1e GIT binary patch literal 4128 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 Date: Sun, 19 Jan 2014 19:09:40 +0100 Subject: [PATCH 27/29] Cleanup handling of ICC profile, more extensive testing --- PIL/PngImagePlugin.py | 11 +++-------- Tests/images/trollface.png | Bin 0 -> 13871 bytes Tests/test_file_png.py | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 Tests/images/trollface.png diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index f3738bd24..a6038d9f2 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -605,20 +605,15 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, cid, data) # ICC profile writing support -- 2008-06-06 Florian Hoech - if "icc_profile" in im.info: + if im.info.get("icc_profile"): # ICC profile # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) - try: - import ICCProfile - p = ICCProfile.ICCProfile(im.info["icc_profile"]) - name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] - except ImportError: - name = b"ICC Profile" - data = name or b'ICC Profile' + b"\0\0" + zlib.compress(im.info["icc_profile"]) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) chunk(fp, b"iCCP", data) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) diff --git a/Tests/images/trollface.png b/Tests/images/trollface.png new file mode 100644 index 0000000000000000000000000000000000000000..f90ebe545a482e36c6965e9c3ea3a213ea2600ab GIT binary patch literal 13871 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 99b00f718..73a3fff15 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -252,8 +252,25 @@ def test_trns_rgb(): assert_equal(im.info["transparency"], (0, 1, 2)) def test_save_icc_profile_none(): + # check saving files with an ICC profile set to None (omit profile) in_file = "Tests/images/icc_profile_none.png" im = Image.open(in_file) + assert im.info['icc_profile'] is None file = tempfile("temp.png") assert_no_exception(lambda: im.save(file)) + im = Image.open(file) + assert 'icc_profile' not in im.info + +def test_save_icc_profile_exists(): + # check icc profile is kept during save() + in_file = "Tests/images/trollface.png" + im = Image.open(in_file) + orig_profile = im.info['icc_profile'] + assert len(orig_profile) == 6636 + + file = tempfile("temp.png") + assert_no_exception(lambda: im.save(file)) + + im = Image.open(file) + assert im.info['icc_profile'] == orig_profile From 0492409def4f309a33d580f01922e3e92bbaeeb9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 20 Jan 2014 13:34:48 -0800 Subject: [PATCH 28/29] don't use bare asserts, don't mask file builtin with local --- Tests/test_file_png.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 73a3fff15..07e7e5385 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -255,22 +255,22 @@ def test_save_icc_profile_none(): # check saving files with an ICC profile set to None (omit profile) in_file = "Tests/images/icc_profile_none.png" im = Image.open(in_file) - assert im.info['icc_profile'] is None + assert_equal(im.info['icc_profile'], None) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) - im = Image.open(file) - assert 'icc_profile' not in im.info + f = tempfile("temp.png") + assert_no_exception(lambda: im.save(f)) + im = Image.open(f) + assert_false('icc_profile' in im.info) def test_save_icc_profile_exists(): # check icc profile is kept during save() in_file = "Tests/images/trollface.png" im = Image.open(in_file) orig_profile = im.info['icc_profile'] - assert len(orig_profile) == 6636 + assert_equal(len(orig_profile), 6636) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) + f = tempfile("temp.png") + assert_no_exception(lambda: im.save(f)) - im = Image.open(file) - assert im.info['icc_profile'] == orig_profile + im = Image.open(f) + assert_equal(im.info['icc_profile'], orig_profile) From 598bd0265097aac1504b61c4ec405bd39ccae00b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 21 Jan 2014 20:50:54 -0800 Subject: [PATCH 29/29] Tightened up the tests --- Tests/images/trollface.png | Bin 13871 -> 0 bytes Tests/test_file_png.py | 23 ++++++++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) delete mode 100644 Tests/images/trollface.png diff --git a/Tests/images/trollface.png b/Tests/images/trollface.png deleted file mode 100644 index f90ebe545a482e36c6965e9c3ea3a213ea2600ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13871 zcmV+~Hqgn5P)4Tx0C)k_S!Yxg&yw%C3AkKx&N=6tbIv&0Rtk4NK`=qMG$r{{NLO2cE9bH?Q^E*H&xZuHB;3+Q#AlEtm8vN0}ZkWy1X;F0je+I|tCf%v#z7yp-CP+%1NynZ!GxW@$tnjZOv z7Cx3e@!Cf4nHfF~LIZ#K43>cyy@GA5VA>HD4t6&+`-4@z!*wlT43CA(LnAehF#OCQ z@6bqd>p%39@xEH7Fvh|3Lazv&zj`kE;|-5|Mheqg!lNv0V9X8U-iT-`9T>wA1-*~= zwYK{sv*qcf{fCY~c>C!Z{FXsb`b8QX%|#9SoHHQA=*R|mE`%&F0)fB_M1gR)4+bK@ z7-#|QKfMU>26&hg4f7&E0QdmoAb3OwJSGIj?0@96{)3|XYn%@}?!Rkf2Oh9&)IW3n zo{b%z^RG@n;0fcubUZxA5%0*l2v@(9f6e(<;z-{wYs9aKuZZvewF$+Gl0?a%G*K!j zRg?l?N714ffe1<(C6CfTsiBl$o&wMX?_te8e{DR{AJpOwhf(Akr%qUIzeZg@ne1`}z1rvTMLA*Grh)AlO4xm|ap_LgI-1 z|E3-7S-*|_YWZs~f@q)oi{`ZqfHEA9MC1RW`JV%zVE_QStbfsXo#1sZ48YB&9#P@Z zzxAPxPH2DyWN?Hs09L>S_~GX)31op1PzO4|5SRgL-~e3U`0<5fFdW2y;~*KNgKUro zia;5t0++y5a09e}4$uwmgNNV|7zdN!HJAg7U=3`59k2&M5CKF2Q9%q48^i+%L6VR> zqzdUk#*iiC0J%XvP%sn)9fwk(Y^VS#gQ}r==q7XD0Y+x9H;sy8^7D6kc4bb*zA9M^l9bJsBMYo{`(39vz^bQ7tp~diF z$feL{c0iGa|Af2F;;5xxQf~N$F1m6jX z3E2oG33Uk_34;k!2#X0D2zv;h5-t($5s?$|5GfLw5qT2D5#k!aCvhZ+Bsoh`Px63dmgGAr6{#qx0jVcxB565kJLyx>Ph>=7JY*VV zE@bg!#bme0o{+7R6Or?hYmwv0Pm-6D-zJ|X-=?6Vkf1Q52%^ZLXrLITSf(VP<8dV_|NMw-T!CYGk0 z<}S?~Es~a()_^vUHkY=Ac8c}~9SfZXoflmuT_fF7x^MJM^y>6p^jY*x^po^I7}yzf z7y=mb89Er=FrpX*87&xN7%wmmFs?IEGbu57Fl8~dFuh_%FbgtUGAA%!VjgAQW?^N~ zV+my`WqH7|&PvOw&g#!v$lAlY!bZiW%I3#b$aarym7RuNgFTSFguS1AgM*nvpCgK+ znq!P(kCTtnhBJk;iSrE?mP?+?o2!tkk86XQjoXC#IQKQ~S3E>K@;tsg=XnNszVY($ z+VP&|?ciPJqvtc=i|4z>H^WcDuf`w7U&H@g03#qT5Fk(~FfND?lo9k5EEjwtgbi$gldF)MmZz3Cm(P(OQa~$cDkLg&E9@%DDn=@{D1KHFQ3_CMP+C^zQ}$B6 zth}JYse)IjR+&>}S9Mdps5+;{p@vthQG2J(rS7R-tG=Wmpy97^UE`CcxMsL!o8~ty zMXeKBz1m1^J?$**F&#=B8=W$pSzS(DAKh!Z8+tN&33|QyXniC7eEn$y76T81dV>u^ zIl~i%{YE$=E2A=_cgBLo;l^Di2opn-b0#yUJf^{>9cIAH!0epa8*@JMF!MVW7z=ZY za*Gd^5|+m;2d${AT&)_ccC0n6bFE+7@Y_V$^x2ZxI@{LUezViIE3lih7qdTM|Hy&S z!Ox-7k-*W`vDR_hNyn+kY0+86Io)~c7~iqjW5X_tE`ct0UCCVWuD9IKZgy@BZu@vM zd<}lv-N3!VeZxb`T}vlT z4^N*yt#rC71C`;IF_tNnS(XDN4SUg>_BZ`owI zW_eQub46yww@S~-rx(;OG*+=xWmo;U=y&l&wO)054R1~HCCsJhOCK)VULL$6f2F>b zu{OK*XI*gJ+*Qk~{q=J7^$pAoxz`ZaqOPr6cfLM;L;FT&qex?Q6Kzv=GiZ)#UcKpd zbE?ItrSF#ft;SZK)`~W&wybujJ+6JD!?$Cu)1h^&H-dzqj2R+Pi+==l(*UYv1b!_79%-Tl9|&7!3?P)P4A1P-F1kkjhZ^u;TFT z5&4mhN3xIFM`cFa9?Lv#8A4r4FP2{hzTA8j{c7)Z(hPbg>kavv;#ua|nzsUPo91Na?#^q^kG`{d_jJWU>O+OLOKWyq&$iqjl~cW zh!Z*xrDE^lFeG@=4st9-Ams?P3{3?cpbuaeW0GagV_9QUXV2xB;KFh%@Hp@W@CETZ z3up?m2yO~J6fPAB5LFQ)5qm4%B#|U(A;lr}QTn#bDOn3SX1PWAMuk{K4JEA7Gvx{u zPgP;nFKQj?$2BxHahh+ndbAsKDs}VqPU_P&JBb!^+^o$I(;FaDN$tw*V6mRFp&kB_ylrk}LGU;sxTLl9Xo zGI%TGUFdk&o$$*MXCo7%(xcmAQE^`Jy~n``-jj-n21!oIQ7LCri_#KKNuOFs&pmCP z!IMdx1+o!26leI)s^*&J+2uPHIG(dFv@NnOwm5HEVpOVErdh69p-?GxL8MCXqIk7Y zP3on&D+aaK>(=Ti8hEZrTvxiG-DuKe*X(-Jw-C!wJN^#% z{{_xcuzOZXM}W#(J&>K{yWNZm2eDH<>g$+zf0TYZvkkUANF( zr5^Qra=je)5%=Hq4L)e=uNx?Tm^XN8D1JD6B=S+_X!jWHlM~}_o-#gDdu~1HJ(V{V{12r)+`jhDl|TL$h1L+2NEeg@d`GlNjK%Sg zXplORC6iyLn4mnQ=AqG{^`|>UU&YYMIKVW-{E+1iYc*ReyC(Y&j?0`zoNHVe+Lg~W%!UG~6qCm7#Oj+!Ocoh6~(toW?nTUXm0vt_V- zZkKDXZ-3~}<{0S2?exaE_?YRj9hX8^9@kbk4YybLSa(kM7w|sf>!s%{=cC|j;D`4Q z3cv@d1(65M1YZdW3Y7`n4|@=v6=5Dp6Ztx-D%vxKH|9g^g*ctKnfSm2>V#*<@0{p4 z*`N3z>2`8`N^xpxTF5D>Gdx+ zJ~r+)<8HFHNZvAUO=xRv|K6!^JG86xPIGtX-8((Q_qOlr_1*5b8rU8z9+nz;K6+-% zZQS6g#&gZ7z?Y;mYwyNZx3_kW{QtYQbo5TZ$O3RK836lq0BqC%$cO7nypwR=i3Px% z1ORIVfZ+882;3-u6zBhWe?R~U*WZW%1ze9|1AIUnD8jW5GvElE*g0od4SSGokP7tOQAE+ zZ!wCPQp_HlVT};V5!Mkg5#?ivv6;kJ;yfH9u8u^Sq?=TQw2Mrh><+m;`D=~7Lx@+HRCrJ1ljwV~N%1y`1W5_WS*Z+Z8R^e5ow5aTaq^xD z#){%fbjqYEqN?TULK+~5jB0>}iVpdQ?Y>j-ba284zfA@N_{kAn)KW@rrh zh#*5qAsi8@h!(^mk`3vKyo_8&$)eIwlV|~SGI|E~eK`Rj@Fkce)FJF75+iEB3Sh4j zOB3J2nd25nVo8Zei^w?0n&JHQHAM_1EoBFlE!9Wr6Es+w5?T@35xNk1I{ID)KSpZC zUZxOcZsr-5QdWC5dbXGBl^mX&;#`31IrlZ5G+t*uX*ic#6&Mw~1;52|MN&nNi$#ii zNT^HVB*&z(q>W_gWHx1A$_>i5E7U0FD8(yBt7NKns(sUt()85I)~?lQ*S({6NB^$D zu;IKh!bH&2*6fsdhsC-Tx3#NHrR}Uer-Q#^yEDNtJC`!o0k;W!pL@MWhNqvGrMJG1 zgKvsoZvaW4dr)@>V`zNXn+VOw`e?G4(AY=u90}3K$4|;6UQT98DNMtjN=RSNz-PY8 zjz1%wi^zLZaI-MFSo-{z(wpT`m5LXBT&%5;yi|F4sg|$K_iA$k-L;AvDvckTOIs9f zjkV$1_d093EV{wn`g_K`n|)RN?hh4*I7U#99zTwLf*H?xihEWw={jxlGVpcXo25Cn zcjoVtKlH57t>t~<-FW=9a3^QiU{7-Y!q4{yn}7U|0z_~Pl^*uK5L|200_JdqDF~c^ zSAI2U1w&vKUiUbN7gC3gK?zVfbQhXO5W!x@BZ?5?NE)OU@-B)Qm4sSFJD?w9v@mz! zddhu5W5PE?5m+*8GqEiWiR&NO z$>Ppx&1TAO!ePQ`%w@uD$>Yi!!FPtgL10L5MVLfHPSjhhOngd`QOZ-gNoG&ZT>i0Q zrm}-7S}jj~U(-k{74~3~b&Oz1sPnP`*rBc(dcHl6MCM&{k@ zoHO3Jdie|mj|#nucFw1kQk2zH$W}hC@~(zzDlW@knXHSdXKEO_p4h0@EY_min%G{` zncL;iO?P+tUip282L%0B9!d}18P*w@9*rFn8%IuTJX@Zen9hI6_p0*s!5f{~thbNm z$==y4TztRsL1HOj`QpmZ>gqcBBl9Qm&)OSyoA@uDU(L5Tx3;&>>@e>%d{g*V_HFmO z-S_rg+THNo_T9Z7>Oazc4DXTdS?!(Q8{a3~SKp7_zq!BiljEnu&w`&LKYt!b9C#j_ zKX`O-a42!;by#vZdgT8IZz;*6w+Dc5nsE7X_VDm4F8~Au0Q}59Jlv}}Jp6eHPB!3w zAiaUV|5qFdW8fNR#l{gDs%TC+I{$n7FLqLPeTXy_Up&$tYiNvd_C6DbN|Ni^$e_o$g2EKa+{{C(W`0?Y% zr=R?v53e8Jt!}5|{qb4f2w$P0PZ#$!Fyk5ibc663fbrt4Xnxn>`80#@8i19y-f#0& z2jx=?!tNpX=bwN6^VeU0{rK;H|NHCr1>dW6{LDVCt9q+;^1nL|;&S!#RR-g!2Vtb} zDg+JfMWu0FrhO%kdMjtg&i2USI>QTF_+{sjykUQ0{fxu$w1dzqW;FmO3fsa|Blftq z?5jJywXS@8KzjRAF*yHcAC9LSgjQo+Z4fWc%-);n@4r9()p>O=9Tz?sK6hI2g>ALE z&DDL6(=7h84aU%r-(#AK$FfXCJv#SPV>cj~rai*(#k@Z8Dn? z*|QA9Qw>6u=+T0XBz{yUYqm$djYc`UUdG?=SNBydt`5qh2jXc4p&Eian=cmAeUCgf zSDWh&JNUE8XSMI$jqn9~b|4=$5Kl1(j~;?pEFXZ8qDLK?0J)z>_Sk)e)#Ft>G1pm) zj~IrB55l8{Km~H&YW`TXtVo$G+Km)D!*SWwVRWp!_Aw8Y-c=3jVsNhT8`r3vRaY{-i74(bz5Am}s8<${mSE&jTH z#D~1$+t0q|e)sD6aqL~g@bE#n(ny{V64k>S(L=}aX-N>M~{J?n!<}3&4SALPh zvxiL_J-yZYipP#&xNQ)!w*k$yniwxs3rl+VuZrb5;?u%g<(t`2+=^Fbb5%y(goFPc zCj8viZ5-!h?%a?2mrp;>?jJe`BzKlH`Xr@=gN|&n0zGN~h{650;z0lE>iGame!an~ zJ^Iz^u}J4cJkbYSKE)vLG={X+tSSAz+Iu2m0iz8?&kEu=vRma7eXgG2dYXs)WcgU- zUD*+j?oY(z_;8RP_Ot8%%^K{|U-_H7H!sDka7Yw-kvGGsd;L^6(eBilBA(`fpHp#w ztDNFXxuzc6Kim3{L8zLummHdx1ZVmh7Lp$IxZ{46N9Omci~)e#RlLd`Kin%P?02v7 zr5uyzow3E*Ugds#5NO`3M6p*=V==kYioZ;cJ0)>z?jy=92K3|9ABi`-sdjey}6SKz)AK62rXZsBI@ZZJd=pbYyPdp8HvZ>J{J_GWZ z-UKm*ERR=ZPiaqsOvBhS0j*bmNmgwD2AAj0sjZVUM`=uDW*WFLK9=}JtAs^wh zmj~V7`a6OOy>pU1IP+VIUVTaFF%Zow@1xder@{7?sFc)~E3%!K~9%-v;rw{pesw@2=0TrkvrMHSml*y1Tg_`6m|E zQ1Jf!x8&b~C)lVc8Q0U)X5Lb#xM)zHfydSOc=LnvG;f5gvF?1zk&ux!<2qt@RR_P1 zZp7VB)vI~gVXxe-48zu6mML%y=i7Jeeu~0V@waM;-mY~vPcwd7Ii)8nyrn_dowzH>$qETxcBe%6{ml3H zDv4k+R4u)D*D4-7qs`89%YWrZyc8?<@qt(69sQGW_2Q`pVtNophJZl5F?_Ft^v)zL zu2rD;bp1?Mi5Q8&aaQ*krwl9ODZSHgh01Tnai`4VatX^uUl^;KA zKQ#!g1|kTNYw1M-*}2s~9wa_949Q%#(z!$4ibwuiIqTWB_*ae6^4G%Kbw^B9eN@+( z9?JTl6{GZHEW?9P5raewieRf^K%{q%9xM9MpYHLAZl=TCBUW;i`K}5VW6EqZS&Usc zt=wf8CuFT0;1BLIx>cUQ0-IIX?6nRJLTd>84sgCRx}y_&3M*{iyRRw{uEXA&MKZ@^ zpRJw)uv#lc153IUPjp*&ekaUXK5l<%KO4s=50AGRoGQ2Ut9*3_p-L)&$w@^Z3&xAj zo>ltQKEraE2b=Nnd+Zd0`$Jx>qq>=|3R4^^eR5pcR`Lq3(qENl`HMM<=T%=lJ7TVK z#=YYV4?=4Q6d?DNb9c%6ZTWypOGnsOisHO`e(*(pT5+}RvVm^tb4NZnuda&I zb5`wF@psyJ%vpW>-u*HR#g;pW%|(_k5G*o?h>M z*7q*IJziJt6;qXek2mW09sZ@G{nS6Y!w3*1t@rzY1}Y-}#>^VAG-NaOE4mf;Ufk|W zd5XF7d*4fMXEToa-tE_8I;?Nu}b6xi6aXz&w zz?sGz;ZeOxW!sB;1V78o4&0va==TnvJx(k4**A@>4-~J*7~_|3*?o8;WJ3T^IR_}4 zu1pVETxBnU)yiG8%g%A~Go4WxF;;o-q>f@{dY(UWkm-GAnQBqR&x6nVB&(TnOV0|+ z^X}Lm-Ut~O#RrhH1Nk03m$C2JqUUQ1&-K_VSQ3S-w<_sb>~6Qh-R+-YA9MK(F-1J{%p>17|+$?hdXhc)VzZ-l5=0Z8vH8NZ(j*tTrYVV%*%rnSbTiDTxYlDDwG z(w!P~_U-TSXF2uzw*mOj8|dJ>hl4(KWw1DNuXUxS@BV58SOv~~T(dVyh7s*Ji#UNH z)5?>@Wvlxt(UpAF&u6rweyYO?|Cw4x{MG!+XZ02#p6E;GD#mmWGUZjm8JQ^SVQWbA zlktjJuX8&CRr4snR~|f9hQS%n)H>^%Dn5K9r{^r zT7GBsTUgbdYTIjCHRH;Mr*sb=?5O8{S8JX1uKa;r<#;&a%0VX2WCZW4NJ$rxepl^X zR|7ZdtHL`af8+U~=@fIL$iKcz-w~I5GkJre>7T zsJ#l|jqUg%?dNQd{iweTBwwezvBRc4=u4>rGF=Ace!9YaJO1`UxEgbo- z?$b9yM)nQh!*BnYJladgN%Rt|0!g1jRl`D(!D~%Cx_7Of7wzGAm;X^eNdVtv1Z;7ZhOc6t?7TeB31;mF^|Lw z_RiIZBH~iRDBpFCITUUtYx(n`&Ggi(73QyhYm^l#ONty-a(C=c56Jm_Z}0X|SAk@F zqn^$!oJ`+kGk)BTJ$Y*3NJsZ04-;@tuOs!klGhgh=zGQww&Wc@o`2zGy(4-0)d&z@ z1^Dwk^wmhtA|c1>?OTnZJFp49!Xs$(E4@q(2W)9rR@r2Gmq`a4?fHPIb9Rs3Bi5=} zoXZK$%G*yL@A_+`ezI%n!BshD@3FA|_0>qhBVc_a#<2vcz_HJ;0TsFIUJZ^ty4hOU zM_u9ZGlGkK*03ILws#pgajiHYXO&&GkC=C!dk>)9Tq=K(GXz;5MIIRrcI7|A_9hUZ z3WC#kr~Bw6(-8)Z;`?YTyv$zm>f`(>9hcAIyY0+A<5l_2@{sw$-gB7gt;j|>2wP1= zJy(3WP0ueeyUj@a;EF5#>lo96fU$tH1mF-otr>a0_x)A-9+uz7J@JerHrkGyV{BPo zqRt4ebTX__Ti445*6)2UpUzk3tGRPL_r-oTPLC(@1(PArx%{h9i&%UL$SL3}>s9;c zQ-OPs@jjmQq)@!s6C>*GIPZ+NRi_!Qv8LE&c@%%;nC2vRT6gim|LiJm@R4oCKf^4) z8UYd$Bqt;w0%l35bfc}#SM5hJGrdfvB*yqNy_QUcsRqpPv)Eg>em9e2xAT4GKjRmD zDL&5;I5eQ&xj*);{Gh|?e30QEzsQ|u<^z7{|9&IH3n^3uj7|Z&vWjeF-@2=8cE}aK zV&!LrN2?L4{EEBkp&@vD(B$PNgcbNl23K@yvT&>YA>Ip}_;SukvbHsJt@vg$ij$F`4BJoIL8cYL?$k9ta!mY;`O*j z{p{@VWcRIFR`JI;#IrR7nP17W_};w{Fw98G*a?!v6(acYRY7KcUB~zGD;wwJ_w3=x zd4zAY!FF^dR~0|-N4SjojC%M$?)i#$=x9C32|q3W=qQJ=AIK{@t+nbWVPtmi-UujU z${8u?D<}e!b1-ICo*#bkf*-kYj8$C6nBBMdfHj(X^k-J} zhB##T$Z)a;+)TYQ`^*or^o^j)%xU(th?6HB$!3W_Nb6{_+(t#rd}dgcuH#kv46o9S zavCk;8FjCoXL)9|y~+Xm9vAuU>3}uLGs7eQVEJ19TO%38VGwvEqM}O1K)ez7^>0R^ zt~+k+Gv6(J-NALl=aohjH|&YI$Blb9a&|0ayVjW;j^u2GSJsRXhu--qfA{Zq8j1ieJPU;T&<~S9Y0yWX(a4QY3<*Ru97EyY^K;dLn0cvwlzTpM6X8I6KGH z!Eig@_k7RZuhfM2M)QV#i(AIy@*v1zYZ!FTl%7a(E2thXaqE3X65GS@`_Y2B&B~sf z%hvN=?N@wYlVN6cLBCp+>&%WCWOoq@-6-dr<-a!svhBVRa4_0*J)+=^u2pi}PZs^4*^^DJwsc?&b_dD{zl@77+VKHB&%T{5D1Kr z72fCvT-V2^%e2pQqfT~?&B`ytnQlv#`LDPNr)rLtkI!;f^+C*$>&3emtLw%)oiqE9 zK}i2|gv^0t6(9p5l0Bjoc1xc5#b>mWZl)jg;#mn4rms8^+J<6@07c=&h{Tqzgw$Z+$AemQB2yi7Wc+@r%ByGhbUG zex-kxT)Bx4rCz>b46fgML-z8SVUOfu>Wu)Ph_m-KCdBWq?kk^HaYyWVQ^X`k+mlZ_ z9xz!RB-0O1;$E{v-XrIT&lz5hk;%Bva^(8H$7XcRut#l|-UxtKjjtqK2?+db&6=_0 zFVoL(XJzs!8;>*d)#5hm6aDC?)%a=-=U8D};TieJM+-0NJAd)5`bxO2t8@HBZ0Z#C z;(6(f07&Je6$rLF*OtGGV3g#}HEH77hWwzq#e0`tkkH~gl zWPZwjeVQphlk6=aAO4K1#_Kh7s|jQ)Z0YQLRcAx0hewM`mhVg-zL|a{%ksB^1HNMM z7?sB)hac*N{pgy-KWf|3+xA9~lkBaqvza^)C8TP2`FtyM`n{7o$MZeO7Je3!>t5Mc zZw%*G?JK{Pev}jA3Xbv{e|!jp$$f?YZ1PY0Mj(;#NrXHW2D#Q=6GUC!5-m)(S=k3{ ze{xp)-){5>$E>2=#A5QTIlY? zTPeeQ4adj&JiFw`AY_ej$6Gfy-m2y&h^ zzM3L?f{v1%(Tm>|-M01SaJ|ue^wo+p>W=z&>HRMQz1qe6jy{Nvyb(k>BajWpsRC#I z@fDMWeAn6<+wddjsJx9zg@-w9lfEh^2y$Etv3R`S<&^uXmQ5wE3eAM)x6H; zXY7yfv#1??HJ^%26Li!sUHhA%YWh_^72XxXgkqE{OJUH zb46dXZ-?sZPZe+Ucjc@&?(lPkW6!qYKP&I~8NEODSEERvYW!AnMvPtO-V;{E;K^M1 z%rH*GqJfLKbAGyqYsRVK|0p>*%Hk3y#TMh096Rk>pO}Mcup-9sHv$ev388;-RZ?4g zREU_+5kA#hV#K!6p~?N;WxiKj#1{98mpjMWy?4JY?5x?v(c@{^@4mb3jR5g10lIrs z5_%1v@yecNYJgLp$T8zI<2&NXw!-u`j`X5!Wf%3LkL+GK%;c31Va89imu=L^&bPf0 zGUqO<2E=tv?3Jiiva+|som{iG%gRE_UQ&egtLMXNR_Nu=t>V9ibvw4j|`oe6`UEHOgHk#;#%dIact!&`i3ty z)jfJME0{AjnQn~nQxAdyYz>3%<5Nj8KAw!&Sm?0EV)pd0t5)}2>Fkg@PX?n}3l``&K!>9C+7J}3<80V3`DER zBJ{m)T2Q>c`chvUzjAC zzraL4uXCLN<&;`JY7id&Z;k5o^l#LBG+?Ez1bdT5JsMX90IOva^(mZeqMZsH{k_f~ zGoZXlO8!4;{E@V3iPi`Xx~!ji5C}qn_kvdfdr4H2tFdWrx5=8?ha$71z?m)FS69cp zbHB>BvZE%-ANj0wN98LxpZcp&mKd5ZyF*5!bIw+pE8?x%j@V^9TqaJDkKf<5-yO>+ z9`TRpGhpPT_n`c8gFxV;4Wk5&K0!RWBzDBs?Zj`!mUzC(s%BQs$jL}kd+f3su>l`j za?UxfU*fA#3`%DO-V0uO8W<%lCQf(pqXJL_*JSUxYc+}&xe#-e ztEc&Is8s|{$vrWi=*k~DqlEwmeH!MiKp(!Qp@$tS1BgAV?z25I)~F}{#Db5vHFEN^ z@1^c%-~akuX!L7k@Sh&xN*+?B4Z?5r)u@**lHSwPJ%i*=)e*a>qlV4=MtpDVh%s}H zIK6X)9qlT;6}hlICo4lR`$l*TLQyF%M4|FaTs_HKHrXAG6`LUH?(!FLpXKZgyfOg9 z;W(1^4*aEkuVllGWYAcy8A32coW}_VF!yGyas_xTwF&7 zLPeyo7hR_2j;(#gE92p|0`Hwu0tbZNv^$0%%gbvJHW#jHyyzxt#He%POmlm4&G?}a z!I3TdxcZ&@7}1lC=hEM7jsq|JEqv=h#sV-h0}}xdtXH(lKIB z0(|w)%XfwG`m53AOwYU_anm|85EUgAS#OW;{I($WYzUOM*+KZW|N69QveR-3bY&pSw)y`FaXOYq;#&?r z&>+VjE*gT@8=;Y$JFZ#OtAdtmRu!$Px#p}6ok!iaDJAx z_XZHps{=r+uR$OapM2dp7<`rTC-Uj9R`?oeeoD1#2!!_<1hV=C*PX-RZw;EZO4-c) zOylEk=Kz5D8U!-&#n+uf@u*at7yx2<4FZ|?hU;e-j+4#rC4UWqXXBgipJ_1e8URZE x8U!-&TGI^NH5_+0ehl_C2r(nC^S8{v{{vUS>+lcoXwCot002ovPDHLkV1kk@z;6Hm diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 07e7e5385..e8d2a1208 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -257,20 +257,17 @@ def test_save_icc_profile_none(): im = Image.open(in_file) assert_equal(im.info['icc_profile'], None) - f = tempfile("temp.png") - assert_no_exception(lambda: im.save(f)) - im = Image.open(f) + im = roundtrip(im) assert_false('icc_profile' in im.info) -def test_save_icc_profile_exists(): - # check icc profile is kept during save() - in_file = "Tests/images/trollface.png" - im = Image.open(in_file) - orig_profile = im.info['icc_profile'] - assert_equal(len(orig_profile), 6636) +def test_roundtrip_icc_profile(): + # check that we can roundtrip the icc profile + im = lena('RGB') - f = tempfile("temp.png") - assert_no_exception(lambda: im.save(f)) + jpeg_image = Image.open('Tests/images/flower2.jpg') + expected_icc = jpeg_image.info['icc_profile'] + + im.info['icc_profile'] = expected_icc + im = roundtrip(im) + assert_equal(im.info['icc_profile'], expected_icc) - im = Image.open(f) - assert_equal(im.info['icc_profile'], orig_profile)