-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #476 from wiredfool/cffi-pixelaccess
Cffi PixelAccess object, Uint16 support in PixelAccess
- Loading branch information
Showing
9 changed files
with
542 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
# | ||
# 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 | ||
import sys | ||
|
||
DEBUG = 0 | ||
|
||
defs = """ | ||
struct Pixel_RGBA { | ||
unsigned char r,g,b,a; | ||
}; | ||
struct Pixel_I16 { | ||
unsigned char l,r; | ||
}; | ||
""" | ||
ffi = FFI() | ||
ffi.cdef(defs) | ||
|
||
|
||
class PyAccess(object): | ||
|
||
def __init__(self, img, readonly = False): | ||
vals = dict(img.im.unsafe_ptrs) | ||
self.readonly = readonly | ||
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._post_init() | ||
|
||
def _post_init(): pass | ||
|
||
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 ValueError('Attempt to putpixel a read only image') | ||
(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.xsize and 0 <= y < self.ysize): | ||
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) | ||
|
||
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] | ||
# tuple | ||
pixel.r = min(color[0],255) | ||
pixel.g = min(color[1],255) | ||
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) | ||
|
||
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] | ||
# tuple | ||
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): | ||
""" 1, L, P, 8 bit images stored as uint8 """ | ||
def _post_init(self, *args, **kwargs): | ||
self.pixels = self.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] = min(color,255) | ||
except: | ||
# 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 | ||
|
||
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 """ | ||
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, | ||
'LA': _PyAccess32_2, | ||
'PA': _PyAccess32_2, | ||
'RGB': _PyAccess32_3, | ||
'LAB': _PyAccess32_3, | ||
'YCbCr': _PyAccess32_3, | ||
'RGBA': _PyAccess32_4, | ||
'RGBa': _PyAccess32_4, | ||
'RGBX': _PyAccess32_4, | ||
'CMYK': _PyAccess32_4, | ||
'F': _PyAccessF, | ||
'I': _PyAccessI32_N, | ||
} | ||
|
||
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;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;32L'] = _PyAccessI32_Swap | ||
mode_map['I;32B'] = _PyAccessI32_N | ||
|
||
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) | ||
|
||
|
Oops, something went wrong.