merge from master

This commit is contained in:
wiredfool 2014-03-31 15:47:52 -07:00
commit caf27c6908
44 changed files with 3209 additions and 68 deletions

9
.gitignore vendored
View File

@ -6,3 +6,12 @@ dist
*.so *.so
docs/_build docs/_build
*~ *~
# Vim cruft
.*.swp
#emacs
*~
\#*#
.#*

View File

@ -4,19 +4,32 @@ language: python
virtualenv: virtualenv:
system_site_packages: true system_site_packages: true
notifications:
irc: "chat.freenode.net#pil"
python: python:
- 2.6 - 2.6
- 2.7 - 2.7
- 3.2 - 3.2
- 3.3 - 3.3
- "pypy"
install: install:
- "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev" - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake"
- "pip install cffi" - "pip install cffi"
# webp
- pushd depends && ./install_webp.sh && popd
# openjpeg
- pushd depends && ./install_openjpeg.sh && popd
script: script:
- python setup.py clean - python setup.py clean
- python setup.py build_ext --inplace - python setup.py build_ext --inplace
- python selftest.py - python selftest.py
- python Tests/run.py - python Tests/run.py
matrix:
allow_failures:
- python: "pypy"

View File

@ -4,6 +4,21 @@ Changelog (Pillow)
2.4.0 (unreleased) 2.4.0 (unreleased)
------------------ ------------------
- Improved icns support
[al45tair]
- Fix libtiff leaking open files, fixes #580
[wiredfool]
- Fixes for Jpeg encoding in Python 3, fixes #577
[wiredfool]
- Added support for JPEG 2000
[al45tair]
- Add more detailed error messages to Image.py
[larsmans]
- Avoid conflicting _expand functions in PIL & MINGW, fixes #538 - Avoid conflicting _expand functions in PIL & MINGW, fixes #538
[aclark] [aclark]

BIN
Images/pillow.icns Normal file

Binary file not shown.

BIN
Images/pillow.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -10,12 +10,17 @@
# Copyright (c) 2004 by Bob Ippolito. # Copyright (c) 2004 by Bob Ippolito.
# Copyright (c) 2004 by Secret Labs. # Copyright (c) 2004 by Secret Labs.
# Copyright (c) 2004 by Fredrik Lundh. # Copyright (c) 2004 by Fredrik Lundh.
# Copyright (c) 2014 by Alastair Houghton.
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from PIL import Image, ImageFile, _binary from PIL import Image, ImageFile, PngImagePlugin, _binary
import struct import struct, io
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k:
from PIL import Jpeg2KImagePlugin
i8 = _binary.i8 i8 = _binary.i8
@ -40,14 +45,15 @@ def read_32(fobj, start_length, size):
""" """
(start, length) = start_length (start, length) = start_length
fobj.seek(start) fobj.seek(start)
sizesq = size[0] * size[1] pixel_size = (size[0] * size[2], size[1] * size[2])
sizesq = pixel_size[0] * pixel_size[1]
if length == sizesq * 3: if length == sizesq * 3:
# uncompressed ("RGBRGBGB") # uncompressed ("RGBRGBGB")
indata = fobj.read(length) indata = fobj.read(length)
im = Image.frombuffer("RGB", size, indata, "raw", "RGB", 0, 1) im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1)
else: else:
# decode image # decode image
im = Image.new("RGB", size, None) im = Image.new("RGB", pixel_size, None)
for band_ix in range(3): for band_ix in range(3):
data = [] data = []
bytesleft = sizesq bytesleft = sizesq
@ -72,7 +78,7 @@ def read_32(fobj, start_length, size):
"Error reading channel [%r left]" % bytesleft "Error reading channel [%r left]" % bytesleft
) )
band = Image.frombuffer( band = Image.frombuffer(
"L", size, b"".join(data), "raw", "L", 0, 1 "L", pixel_size, b"".join(data), "raw", "L", 0, 1
) )
im.im.putband(band.im, band_ix) im.im.putband(band.im, band_ix)
return {"RGB": im} return {"RGB": im}
@ -81,27 +87,80 @@ def read_mk(fobj, start_length, size):
# Alpha masks seem to be uncompressed # Alpha masks seem to be uncompressed
(start, length) = start_length (start, length) = start_length
fobj.seek(start) fobj.seek(start)
pixel_size = (size[0] * size[2], size[1] * size[2])
sizesq = pixel_size[0] * pixel_size[1]
band = Image.frombuffer( band = Image.frombuffer(
"L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 "L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1
) )
return {"A": band} return {"A": band}
def read_png_or_jpeg2000(fobj, start_length, size):
(start, length) = start_length
fobj.seek(start)
sig = fobj.read(12)
if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a':
fobj.seek(start)
im = PngImagePlugin.PngImageFile(fobj)
return {"RGBA": im}
elif sig[:4] == b'\xff\x4f\xff\x51' \
or sig[:4] == b'\x0d\x0a\x87\x0a' \
or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
if not enable_jpeg2k:
raise ValueError('Unsupported icon subimage format (rebuild PIL with JPEG 2000 support to fix this)')
# j2k, jpc or j2c
fobj.seek(start)
jp2kstream = fobj.read(length)
f = io.BytesIO(jp2kstream)
im = Jpeg2KImagePlugin.Jpeg2KImageFile(f)
if im.mode != 'RGBA':
im = im.convert('RGBA')
return {"RGBA": im}
else:
raise ValueError('Unsupported icon subimage format')
class IcnsFile: class IcnsFile:
SIZES = { SIZES = {
(128, 128): [ (512, 512, 2): [
(b'ic10', read_png_or_jpeg2000),
],
(512, 512, 1): [
(b'ic09', read_png_or_jpeg2000),
],
(256, 256, 2): [
(b'ic14', read_png_or_jpeg2000),
],
(256, 256, 1): [
(b'ic08', read_png_or_jpeg2000),
],
(128, 128, 2): [
(b'ic13', read_png_or_jpeg2000),
],
(128, 128, 1): [
(b'ic07', read_png_or_jpeg2000),
(b'it32', read_32t), (b'it32', read_32t),
(b't8mk', read_mk), (b't8mk', read_mk),
], ],
(48, 48): [ (64, 64, 1): [
(b'icp6', read_png_or_jpeg2000),
],
(32, 32, 2): [
(b'ic12', read_png_or_jpeg2000),
],
(48, 48, 1): [
(b'ih32', read_32), (b'ih32', read_32),
(b'h8mk', read_mk), (b'h8mk', read_mk),
], ],
(32, 32): [ (32, 32, 1): [
(b'icp5', read_png_or_jpeg2000),
(b'il32', read_32), (b'il32', read_32),
(b'l8mk', read_mk), (b'l8mk', read_mk),
], ],
(16, 16): [ (16, 16, 2): [
(b'ic11', read_png_or_jpeg2000),
],
(16, 16, 1): [
(b'icp4', read_png_or_jpeg2000),
(b'is32', read_32), (b'is32', read_32),
(b's8mk', read_mk), (b's8mk', read_mk),
], ],
@ -115,7 +174,7 @@ class IcnsFile:
self.dct = dct = {} self.dct = dct = {}
self.fobj = fobj self.fobj = fobj
sig, filesize = nextheader(fobj) sig, filesize = nextheader(fobj)
if sig != 'icns': if sig != b'icns':
raise SyntaxError('not an icns file') raise SyntaxError('not an icns file')
i = HEADERSIZE i = HEADERSIZE
while i < filesize: while i < filesize:
@ -157,7 +216,14 @@ class IcnsFile:
def getimage(self, size=None): def getimage(self, size=None):
if size is None: if size is None:
size = self.bestsize() size = self.bestsize()
if len(size) == 2:
size = (size[0], size[1], 1)
channels = self.dataforsize(size) channels = self.dataforsize(size)
im = channels.get('RGBA', None)
if im:
return im
im = channels.get("RGB").copy() im = channels.get("RGB").copy()
try: try:
im.putalpha(channels["A"]) im.putalpha(channels["A"])
@ -185,18 +251,29 @@ class IcnsImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
self.icns = IcnsFile(self.fp) self.icns = IcnsFile(self.fp)
self.mode = 'RGBA' self.mode = 'RGBA'
self.size = self.icns.bestsize() self.best_size = self.icns.bestsize()
self.size = (self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2])
self.info['sizes'] = self.icns.itersizes() self.info['sizes'] = self.icns.itersizes()
# Just use this to see if it's loaded or not yet. # Just use this to see if it's loaded or not yet.
self.tile = ('',) self.tile = ('',)
def load(self): def load(self):
if len(self.size) == 3:
self.best_size = self.size
self.size = (self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2])
Image.Image.load(self) Image.Image.load(self)
if not self.tile: if not self.tile:
return return
self.load_prepare() self.load_prepare()
# This is likely NOT the best way to do it, but whatever. # This is likely NOT the best way to do it, but whatever.
im = self.icns.getimage(self.size) im = self.icns.getimage(self.best_size)
# If this is a PNG or JPEG 2000, it won't be loaded yet
im.load()
self.im = im.im self.im = im.im
self.mode = im.mode self.mode = im.mode
self.size = im.size self.size = im.size
@ -205,12 +282,18 @@ class IcnsImageFile(ImageFile.ImageFile):
self.tile = () self.tile = ()
self.load_end() self.load_end()
Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns')
Image.register_extension("ICNS", '.icns') Image.register_extension("ICNS", '.icns')
if __name__ == '__main__': if __name__ == '__main__':
import os, sys import os, sys
imf = IcnsImageFile(open(sys.argv[1], 'rb'))
for size in imf.info['sizes']:
imf.size = size
imf.load()
im = imf.im
im.save('out-%s-%s-%s.png' % size)
im = Image.open(open(sys.argv[1], "rb")) im = Image.open(open(sys.argv[1], "rb"))
im.save("out.png") im.save("out.png")
if sys.platform == 'windows':
os.startfile("out.png") os.startfile("out.png")

View File

@ -1965,7 +1965,7 @@ def fromarray(obj, mode=None):
else: else:
ndmax = 4 ndmax = 4
if ndim > ndmax: if ndim > ndmax:
raise ValueError("Too many dimensions.") raise ValueError("Too many dimensions: %d > %d." % (ndim, ndmax))
size = shape[1], shape[0] size = shape[1], shape[0]
if strides is not None: if strides is not None:
@ -2018,7 +2018,7 @@ def open(fp, mode="r"):
""" """
if mode != "r": if mode != "r":
raise ValueError("bad mode") raise ValueError("bad mode %r" % mode)
if isPath(fp): if isPath(fp):
filename = fp filename = fp
@ -2054,7 +2054,8 @@ def open(fp, mode="r"):
#traceback.print_exc() #traceback.print_exc()
pass pass
raise IOError("cannot identify image file") raise IOError("cannot identify image file %r"
% (filename if filename else fp))
# #
# Image processing. # Image processing.

View File

@ -20,15 +20,6 @@
from PIL import Image from PIL import Image
import re import re
##
# Convert color string to RGB tuple.
#
# @param color A CSS3-style colour string.
# @return An RGB-tuple.
# @exception ValueError If the color string could not be interpreted
# as an RGB value.
def getrgb(color): def getrgb(color):
""" """
Convert a color string to an RGB tuple. If the string cannot be parsed, Convert a color string to an RGB tuple. If the string cannot be parsed,
@ -37,7 +28,7 @@ def getrgb(color):
.. versionadded:: 1.1.4 .. versionadded:: 1.1.4
:param color: A color string :param color: A color string
:return: ``(red, green, blue)`` :return: ``(red, green, blue[, alpha])``
""" """
try: try:
rgb = colormap[color] rgb = colormap[color]
@ -114,7 +105,7 @@ def getcolor(color, mode):
.. versionadded:: 1.1.4 .. versionadded:: 1.1.4
:param color: A color string :param color: A color string
:return: ``(red, green, blue)`` :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])``
""" """
# same as getrgb, but converts the result to the given mode # same as getrgb, but converts the result to the given mode
color, alpha = getrgb(color), 255 color, alpha = getrgb(color), 255

View File

@ -205,7 +205,7 @@ class ImageFile(Image.Image):
else: else:
raise IndexError(ie) raise IndexError(ie)
if not s: # truncated jpeg if not s and not d.handles_eof: # truncated jpeg
self.tile = [] self.tile = []
# JpegDecode needs to clean things up here either way # JpegDecode needs to clean things up here either way

249
PIL/Jpeg2KImagePlugin.py Normal file
View File

@ -0,0 +1,249 @@
#
# The Python Imaging Library
# $Id$
#
# JPEG2000 file handling
#
# History:
# 2014-03-12 ajh Created
#
# Copyright (c) 2014 Coriolis Systems Limited
# Copyright (c) 2014 Alastair Houghton
#
# See the README file for information on usage and redistribution.
#
__version__ = "0.1"
from PIL import Image, ImageFile, _binary
import struct
import os
import io
def _parse_codestream(fp):
"""Parse the JPEG 2000 codestream to extract the size and component
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
hdr = fp.read(2)
lsiz = struct.unpack('>H', hdr)[0]
siz = hdr + fp.read(lsiz - 2)
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
xtosiz, ytosiz, csiz \
= struct.unpack('>HHIIIIIIIIH', siz[:38])
ssiz = [None]*csiz
xrsiz = [None]*csiz
yrsiz = [None]*csiz
for i in range(csiz):
ssiz[i], xrsiz[i], yrsiz[i] \
= struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i])
size = (xsiz - xosiz, ysiz - yosiz)
if csiz == 1:
mode = 'L'
elif csiz == 2:
mode = 'LA'
elif csiz == 3:
mode = 'RGB'
elif csiz == 4:
mode == 'RGBA'
else:
mode = None
return (size, mode)
def _parse_jp2_header(fp):
"""Parse the JP2 header box to extract size, component count and
color space information, returning a PIL (size, mode) tuple."""
# Find the JP2 header box
header = None
while True:
lbox, tbox = struct.unpack('>I4s', fp.read(8))
if lbox == 1:
lbox = struct.unpack('>Q', fp.read(8))[0]
hlen = 16
else:
hlen = 8
if tbox == b'jp2h':
header = fp.read(lbox - hlen)
break
else:
fp.seek(lbox - hlen, os.SEEK_CUR)
if header is None:
raise SyntaxError('could not find JP2 header')
size = None
mode = None
hio = io.BytesIO(header)
while True:
lbox, tbox = struct.unpack('>I4s', hio.read(8))
if lbox == 1:
lbox = struct.unpack('>Q', hio.read(8))[0]
hlen = 16
else:
hlen = 8
content = hio.read(lbox - hlen)
if tbox == b'ihdr':
height, width, nc, bpc, c, unkc, ipr \
= struct.unpack('>IIHBBBB', content)
size = (width, height)
if unkc:
if nc == 1:
mode = 'L'
elif nc == 2:
mode = 'LA'
elif nc == 3:
mode = 'RGB'
elif nc == 4:
mode = 'RGBA'
break
elif tbox == b'colr':
meth, prec, approx = struct.unpack('>BBB', content[:3])
if meth == 1:
cs = struct.unpack('>I', content[3:7])[0]
if cs == 16: # sRGB
if nc == 3:
mode = 'RGB'
elif nc == 4:
mode = 'RGBA'
break
elif cs == 17: # grayscale
if nc == 1:
mode = 'L'
elif nc == 2:
mode = 'LA'
break
elif cs == 18: # sYCC
if nc == 3:
mode = 'RGB'
elif nc == 4:
mode == 'RGBA'
break
return (size, mode)
##
# Image plugin for JPEG2000 images.
class Jpeg2KImageFile(ImageFile.ImageFile):
format = "JPEG2000"
format_description = "JPEG 2000 (ISO 15444)"
def _open(self):
sig = self.fp.read(4)
if sig == b'\xff\x4f\xff\x51':
self.codec = "j2k"
self.size, self.mode = _parse_codestream(self.fp)
else:
sig = sig + self.fp.read(8)
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
self.codec = "jp2"
self.size, self.mode = _parse_jp2_header(self.fp)
else:
raise SyntaxError('not a JPEG 2000 file')
if self.size is None or self.mode is None:
raise SyntaxError('unable to determine size/mode')
self.reduce = 0
self.layers = 0
fd = -1
if hasattr(self.fp, "fileno"):
try:
fd = self.fp.fileno()
except:
fd = -1
self.tile = [('jpeg2k', (0, 0) + self.size, 0,
(self.codec, self.reduce, self.layers, fd))]
def load(self):
if self.reduce:
power = 1 << self.reduce
adjust = power >> 1
self.size = (int((self.size[0] + adjust) / power),
int((self.size[1] + adjust) / power))
if self.tile:
# Update the reduce and layers settings
t = self.tile[0]
t3 = (t[3][0], self.reduce, self.layers, t[3][3])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
ImageFile.ImageFile.load(self)
def _accept(prefix):
return (prefix[:4] == b'\xff\x4f\xff\x51'
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
# ------------------------------------------------------------
# Save support
def _save(im, fp, filename):
if filename.endswith('.j2k'):
kind = 'j2k'
else:
kind = 'jp2'
# Get the keyword arguments
info = im.encoderinfo
offset = info.get('offset', None)
tile_offset = info.get('tile_offset', None)
tile_size = info.get('tile_size', None)
quality_mode = info.get('quality_mode', 'rates')
quality_layers = info.get('quality_layers', None)
num_resolutions = info.get('num_resolutions', 0)
cblk_size = info.get('codeblock_size', None)
precinct_size = info.get('precinct_size', None)
irreversible = info.get('irreversible', False)
progression = info.get('progression', 'LRCP')
cinema_mode = info.get('cinema_mode', 'no')
fd = -1
if hasattr(fp, "fileno"):
try:
fd = fp.fileno()
except:
fd = -1
im.encoderconfig = (
offset,
tile_offset,
tile_size,
quality_mode,
quality_layers,
num_resolutions,
cblk_size,
precinct_size,
irreversible,
progression,
cinema_mode,
fd
)
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
# ------------------------------------------------------------
# Registry stuff
Image.register_open('JPEG2000', Jpeg2KImageFile, _accept)
Image.register_save('JPEG2000', _save)
Image.register_extension('JPEG2000', '.jp2')
Image.register_extension('JPEG2000', '.j2k')
Image.register_extension('JPEG2000', '.jpc')
Image.register_extension('JPEG2000', '.jpf')
Image.register_extension('JPEG2000', '.jpx')
Image.register_extension('JPEG2000', '.j2c')
Image.register_mime('JPEG2000', 'image/jp2')
Image.register_mime('JPEG2000', 'image/jpx')

View File

@ -442,7 +442,7 @@ samplings = {
} }
def convert_dict_qtables(qtables): def convert_dict_qtables(qtables):
qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]
for idx, table in enumerate(qtables): for idx, table in enumerate(qtables):
qtables[idx] = [table[i] for i in zigzag_index] qtables[idx] = [table[i] for i in zigzag_index]
return qtables return qtables
@ -504,7 +504,7 @@ def _save(im, fp, filename):
except ValueError: except ValueError:
raise ValueError("Invalid quantization table") raise ValueError("Invalid quantization table")
else: else:
qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]
if isinstance(qtables, (tuple, list, dict)): if isinstance(qtables, (tuple, list, dict)):
if isinstance(qtables, dict): if isinstance(qtables, dict):
qtables = convert_dict_qtables(qtables) qtables = convert_dict_qtables(qtables)

View File

@ -693,10 +693,11 @@ class TiffImageFile(ImageFile.ImageFile):
if not len(self.tile) == 1: if not len(self.tile) == 1:
raise IOError("Not exactly one tile") raise IOError("Not exactly one tile")
d, e, o, a = self.tile[0] # (self._compression, (extents tuple), 0, (rawmode, self._compression, fp))
d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig) ignored, extents, ignored_2, args = self.tile[0]
decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig)
try: try:
d.setimage(self.im, e) decoder.setimage(self.im, extents)
except ValueError: except ValueError:
raise IOError("Couldn't set the image") raise IOError("Couldn't set the image")
@ -712,27 +713,30 @@ class TiffImageFile(ImageFile.ImageFile):
# with here by reordering. # with here by reordering.
if Image.DEBUG: if Image.DEBUG:
print ("have getvalue. just sending in a string from getvalue") print ("have getvalue. just sending in a string from getvalue")
n,e = d.decode(self.fp.getvalue()) n,err = decoder.decode(self.fp.getvalue())
elif hasattr(self.fp, "fileno"): elif hasattr(self.fp, "fileno"):
# we've got a actual file on disk, pass in the fp. # we've got a actual file on disk, pass in the fp.
if Image.DEBUG: if Image.DEBUG:
print ("have fileno, calling fileno version of the decoder.") print ("have fileno, calling fileno version of the decoder.")
self.fp.seek(0) self.fp.seek(0)
n,e = d.decode(b"fpfp") # 4 bytes, otherwise the trace might error out n,err = decoder.decode(b"fpfp") # 4 bytes, otherwise the trace might error out
else: else:
# we have something else. # we have something else.
if Image.DEBUG: if Image.DEBUG:
print ("don't have fileno or getvalue. just reading") print ("don't have fileno or getvalue. just reading")
# UNDONE -- so much for that buffer size thing. # UNDONE -- so much for that buffer size thing.
n, e = d.decode(self.fp.read()) n,err = decoder.decode(self.fp.read())
self.tile = [] self.tile = []
self.readonly = 0 self.readonly = 0
# libtiff closed the fp in a, we need to close self.fp, if possible
if hasattr(self.fp, 'close'):
self.fp.close()
self.fp = None # might be shared self.fp = None # might be shared
if e < 0: if err < 0:
raise IOError(e) raise IOError(err)
self.load_end() self.load_end()

View File

@ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin',
'ImtImagePlugin', 'ImtImagePlugin',
'IptcImagePlugin', 'IptcImagePlugin',
'JpegImagePlugin', 'JpegImagePlugin',
'Jpeg2KImagePlugin',
'McIdasImagePlugin', 'McIdasImagePlugin',
'MicImagePlugin', 'MicImagePlugin',
'MpegImagePlugin', 'MpegImagePlugin',

BIN
Tests/images/pillow2.icns Normal file

Binary file not shown.

BIN
Tests/images/pillow3.icns Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Tests/images/test-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -2,7 +2,7 @@ from __future__ import print_function
# minimal test runner # minimal test runner
import glob, os, os.path, sys, tempfile import glob, os, os.path, sys, tempfile, re
try: try:
root = os.path.dirname(__file__) root = os.path.dirname(__file__)
@ -38,6 +38,8 @@ skipped = []
python_options = " ".join(python_options) python_options = " ".join(python_options)
tester_options = " ".join(tester_options) tester_options = " ".join(tester_options)
ignore_re = re.compile('^ignore: (.*)$', re.MULTILINE)
for file in files: for file in files:
test, ext = os.path.splitext(os.path.basename(file)) test, ext = os.path.splitext(os.path.basename(file))
if include and test not in include: if include and test not in include:
@ -48,7 +50,30 @@ for file in files:
out = os.popen("%s %s -u %s %s 2>&1" % ( out = os.popen("%s %s -u %s %s 2>&1" % (
sys.executable, python_options, file, tester_options sys.executable, python_options, file, tester_options
)) ))
result = out.read().strip() result = out.read()
# Extract any ignore patterns
ignore_pats = ignore_re.findall(result)
result = ignore_re.sub('', result)
try:
def fix_re(p):
if not p.startswith('^'):
p = '^' + p
if not p.endswith('$'):
p = p + '$'
return p
ignore_res = [re.compile(fix_re(p), re.MULTILINE) for p in ignore_pats]
except:
print('(bad ignore patterns %r)' % ignore_pats)
ignore_res = []
for r in ignore_res:
result = r.sub('', result)
result = result.strip()
if result == "ok": if result == "ok":
result = None result = None
elif result == "skip": elif result == "skip":

View File

@ -3,7 +3,7 @@ from tester import *
from PIL import Image from PIL import Image
import os import os
base = 'Tests/images/bmp/' base = os.path.join('Tests', 'images', 'bmp')
def get_files(d, ext='.bmp'): def get_files(d, ext='.bmp'):
@ -78,9 +78,9 @@ def test_good():
except Exception as msg: except Exception as msg:
# there are three here that are unsupported: # there are three here that are unsupported:
unsupported = ('Tests/images/bmp/g/rgb32bf.bmp', unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'),
'Tests/images/bmp/g/pal8rle.bmp', os.path.join(base, 'g', 'pal8rle.bmp'),
'Tests/images/bmp/g/pal4rle.bmp') os.path.join(base, 'g', 'pal4rle.bmp'))
if f not in unsupported: if f not in unsupported:
assert_true(False, "Unsupported Image %s: %s" %(f,msg)) assert_true(False, "Unsupported Image %s: %s" %(f,msg))

66
Tests/test_file_icns.py Normal file
View File

@ -0,0 +1,66 @@
from tester import *
from PIL import Image
# sample icon file
file = "Images/pillow.icns"
data = open(file, "rb").read()
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
def test_sanity():
# Loading this icon by default should result in the largest size
# (512x512@2x) being loaded
im = Image.open(file)
im.load()
assert_equal(im.mode, "RGBA")
assert_equal(im.size, (1024, 1024))
assert_equal(im.format, "ICNS")
def test_sizes():
# Check that we can load all of the sizes, and that the final pixel
# dimensions are as expected
im = Image.open(file)
for w,h,r in im.info['sizes']:
wr = w * r
hr = h * r
im2 = Image.open(file)
im2.size = (w, h, r)
im2.load()
assert_equal(im2.mode, 'RGBA')
assert_equal(im2.size, (wr, hr))
def test_older_icon():
# This icon was made with Icon Composer rather than iconutil; it still
# uses PNG rather than JP2, however (since it was made on 10.9).
im = Image.open('Tests/images/pillow2.icns')
for w,h,r in im.info['sizes']:
wr = w * r
hr = h * r
im2 = Image.open('Tests/images/pillow2.icns')
im2.size = (w, h, r)
im2.load()
assert_equal(im2.mode, 'RGBA')
assert_equal(im2.size, (wr, hr))
def test_jp2_icon():
# This icon was made by using Uli Kusterer's oldiconutil to replace
# the PNG images with JPEG 2000 ones. The advantage of doing this is
# that OS X 10.5 supports JPEG 2000 but not PNG; some commercial
# software therefore does just this.
# (oldiconutil is here: https://github.com/uliwitness/oldiconutil)
if not enable_jpeg2k:
return
im = Image.open('Tests/images/pillow3.icns')
for w,h,r in im.info['sizes']:
wr = w * r
hr = h * r
im2 = Image.open('Tests/images/pillow3.icns')
im2.size = (w, h, r)
im2.load()
assert_equal(im2.mode, 'RGBA')
assert_equal(im2.size, (wr, hr))

View File

@ -10,9 +10,7 @@ codecs = dir(Image.core)
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
skip("jpeg support not available") skip("jpeg support not available")
# sample jpeg stream test_file = "Images/lena.jpg"
file = "Images/lena.jpg"
data = open(file, "rb").read()
def roundtrip(im, **options): def roundtrip(im, **options):
out = BytesIO() out = BytesIO()
@ -30,7 +28,7 @@ def test_sanity():
# internal version number # internal version number
assert_match(Image.core.jpeglib_version, "\d+\.\d+$") assert_match(Image.core.jpeglib_version, "\d+\.\d+$")
im = Image.open(file) im = Image.open(test_file)
im.load() im.load()
assert_equal(im.mode, "RGB") assert_equal(im.mode, "RGB")
assert_equal(im.size, (128, 128)) assert_equal(im.size, (128, 128))
@ -40,7 +38,7 @@ def test_sanity():
def test_app(): def test_app():
# Test APP/COM reader (@PIL135) # Test APP/COM reader (@PIL135)
im = Image.open(file) im = Image.open(test_file)
assert_equal(im.applist[0], assert_equal(im.applist[0],
("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"))
assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) assert_equal(im.applist[1], ("COM", b"Python Imaging Library"))
@ -49,8 +47,8 @@ def test_app():
def test_cmyk(): def test_cmyk():
# Test CMYK handling. Thanks to Tim and Charlie for test data, # Test CMYK handling. Thanks to Tim and Charlie for test data,
# Michael for getting me to look one more time. # Michael for getting me to look one more time.
file = "Tests/images/pil_sample_cmyk.jpg" f = "Tests/images/pil_sample_cmyk.jpg"
im = Image.open(file) im = Image.open(f)
# the source image has red pixels in the upper left corner. # the source image has red pixels in the upper left corner.
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0)
@ -66,7 +64,7 @@ def test_cmyk():
def test_dpi(): def test_dpi():
def test(xdpi, ydpi=None): def test(xdpi, ydpi=None):
im = Image.open(file) im = Image.open(test_file)
im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) im = roundtrip(im, dpi=(xdpi, ydpi or xdpi))
return im.info.get("dpi") return im.info.get("dpi")
assert_equal(test(72), (72, 72)) assert_equal(test(72), (72, 72))
@ -80,9 +78,9 @@ def test_icc():
icc_profile = im1.info["icc_profile"] icc_profile = im1.info["icc_profile"]
assert_equal(len(icc_profile), 3144) assert_equal(len(icc_profile), 3144)
# Roundtrip via physical file. # Roundtrip via physical file.
file = tempfile("temp.jpg") f = tempfile("temp.jpg")
im1.save(file, icc_profile=icc_profile) im1.save(f, icc_profile=icc_profile)
im2 = Image.open(file) im2 = Image.open(f)
assert_equal(im2.info.get("icc_profile"), icc_profile) assert_equal(im2.info.get("icc_profile"), icc_profile)
# Roundtrip via memory buffer. # Roundtrip via memory buffer.
im1 = roundtrip(lena()) im1 = roundtrip(lena())
@ -203,3 +201,9 @@ def test_exif():
im = Image.open("Tests/images/pil_sample_rgb.jpg") im = Image.open("Tests/images/pil_sample_rgb.jpg")
info = im._getexif() info = im._getexif()
assert_equal(info[305], 'Adobe Photoshop CS Macintosh') assert_equal(info[305], 'Adobe Photoshop CS Macintosh')
def test_quality_keep():
im = Image.open("Images/lena.jpg")
f = tempfile('temp.jpg')
assert_no_exception(lambda: im.save(f, quality='keep'))

110
Tests/test_file_jpeg2k.py Normal file
View File

@ -0,0 +1,110 @@
from tester import *
from PIL import Image
from PIL import ImageFile
codecs = dir(Image.core)
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs:
skip('JPEG 2000 support not available')
# OpenJPEG 2.0.0 outputs this debugging message sometimes; we should
# ignore it---it doesn't represent a test failure.
ignore('Not enough memory to handle tile data')
test_card = Image.open('Tests/images/test-card.png')
test_card.load()
def roundtrip(im, **options):
out = BytesIO()
im.save(out, "JPEG2000", **options)
bytes = out.tell()
out.seek(0)
im = Image.open(out)
im.bytes = bytes # for testing only
im.load()
return im
# ----------------------------------------------------------------------
def test_sanity():
# Internal version number
assert_match(Image.core.jp2klib_version, '\d+\.\d+\.\d+$')
im = Image.open('Tests/images/test-card-lossless.jp2')
im.load()
assert_equal(im.mode, 'RGB')
assert_equal(im.size, (640, 480))
assert_equal(im.format, 'JPEG2000')
# ----------------------------------------------------------------------
# These two test pre-written JPEG 2000 files that were not written with
# PIL (they were made using Adobe Photoshop)
def test_lossless():
im = Image.open('Tests/images/test-card-lossless.jp2')
im.load()
im.save('/tmp/test-card.png')
assert_image_similar(im, test_card, 1.0e-3)
def test_lossy_tiled():
im = Image.open('Tests/images/test-card-lossy-tiled.jp2')
im.load()
assert_image_similar(im, test_card, 2.0)
# ----------------------------------------------------------------------
def test_lossless_rt():
im = roundtrip(test_card)
assert_image_equal(im, test_card)
def test_lossy_rt():
im = roundtrip(test_card, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_tiled_rt():
im = roundtrip(test_card, tile_size=(128, 128))
assert_image_equal(im, test_card)
def test_tiled_offset_rt():
im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0),
offset=(32, 32))
assert_image_equal(im, test_card)
def test_irreversible_rt():
im = roundtrip(test_card, irreversible=True, quality_layers=[20])
assert_image_similar(im, test_card, 2.0)
def test_prog_qual_rt():
im = roundtrip(test_card, quality_layers=[60, 40, 20], progression='LRCP')
assert_image_similar(im, test_card, 2.0)
def test_prog_res_rt():
im = roundtrip(test_card, num_resolutions=8, progression='RLCP')
assert_image_equal(im, test_card)
# ----------------------------------------------------------------------
def test_reduce():
im = Image.open('Tests/images/test-card-lossless.jp2')
im.reduce = 2
im.load()
assert_equal(im.size, (160, 120))
def test_layers():
out = BytesIO()
test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10],
progression='LRCP')
out.seek(0)
im = Image.open(out)
im.layers = 1
im.load()
assert_image_similar(im, test_card, 13)
out.seek(0)
im = Image.open(out)
im.layers = 3
im.load()
assert_image_similar(im, test_card, 0.4)

View File

@ -1,4 +1,5 @@
from tester import * from tester import *
import os
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
@ -295,3 +296,13 @@ def xtest_bw_compression_wRGB():
assert_exception(IOError, lambda: im.save(out, compression='group3')) assert_exception(IOError, lambda: im.save(out, compression='group3'))
assert_exception(IOError, lambda: im.save(out, compression='group4')) assert_exception(IOError, lambda: im.save(out, compression='group4'))
def test_fp_leak():
im = Image.open("Tests/images/lena_g4_500.tif")
fn = im.fp.fileno()
assert_no_exception(lambda: os.fstat(fn))
im.load() # this should close it.
assert_exception(OSError, lambda: os.fstat(fn))
im = None # this should force even more closed.
assert_exception(OSError, lambda: os.fstat(fn))
assert_exception(OSError, lambda: os.close(fn))

View File

@ -2,6 +2,11 @@ from tester import *
from PIL import Image from PIL import Image
if hasattr(sys, 'pypy_version_info'):
# This takes _forever_ on pypy. Open Bug,
# see https://github.com/python-imaging/Pillow/issues/484
skip()
def test_sanity(): def test_sanity():
im = lena() im = lena()

View File

@ -41,7 +41,11 @@ Image.new("L", (1, 1), "white")
assert_equal(0, ImageColor.getcolor("black", "1")) assert_equal(0, ImageColor.getcolor("black", "1"))
assert_equal(255, ImageColor.getcolor("white", "1")) assert_equal(255, ImageColor.getcolor("white", "1"))
# The following test is wrong, but is current behavior
# The correct result should be 255 due to the mode 1
assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1"))
# Correct behavior
# assert_equal(255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1"))
Image.new("1", (1, 1), "white") Image.new("1", (1, 1), "white")
assert_equal((0, 255), ImageColor.getcolor("black", "LA")) assert_equal((0, 255), ImageColor.getcolor("black", "LA"))

View File

@ -3,7 +3,7 @@ from tester import *
from PIL import Image from PIL import Image
try: try:
from PIL import ImageTk from PIL import ImageTk
except ImportError as v: except (OSError, ImportError) as v:
skip(v) skip(v)
success() success()

View File

@ -260,6 +260,11 @@ def skip(msg=None):
print("skip") print("skip")
os._exit(0) # don't run exit handlers os._exit(0) # don't run exit handlers
def ignore(pattern):
"""Tells the driver to ignore messages matching the pattern, for the
duration of the current test."""
print('ignore: %s' % pattern)
def _setup(): def _setup():
global _logfile global _logfile
def report(): def report():

View File

@ -3325,6 +3325,7 @@ extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
@ -3341,6 +3342,7 @@ extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_Jpeg2KEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args);
@ -3393,6 +3395,10 @@ static PyMethodDef functions[] = {
#ifdef HAVE_LIBJPEG #ifdef HAVE_LIBJPEG
{"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1},
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
#endif
#ifdef HAVE_OPENJPEG
{"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1},
{"jpeg2k_encoder", (PyCFunction)PyImaging_Jpeg2KEncoderNew, 1},
#endif #endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
#ifdef HAVE_LIBTIFF #ifdef HAVE_LIBTIFF
@ -3497,6 +3503,13 @@ setup_module(PyObject* m) {
} }
#endif #endif
#ifdef HAVE_OPENJPEG
{
extern const char *ImagingJpeg2KVersion(void);
PyDict_SetItemString(d, "jp2klib_version", PyUnicode_FromString(ImagingJpeg2KVersion()));
}
#endif
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
/* zip encoding strategies */ /* zip encoding strategies */
PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY);

View File

@ -52,6 +52,7 @@ typedef struct {
struct ImagingCodecStateInstance state; struct ImagingCodecStateInstance state;
Imaging im; Imaging im;
PyObject* lock; PyObject* lock;
int handles_eof;
} ImagingDecoderObject; } ImagingDecoderObject;
static PyTypeObject ImagingDecoderType; static PyTypeObject ImagingDecoderType;
@ -93,6 +94,9 @@ PyImaging_DecoderNew(int contextsize)
/* Initialize the cleanup function pointer */ /* Initialize the cleanup function pointer */
decoder->cleanup = NULL; decoder->cleanup = NULL;
/* Most decoders don't want to handle EOF themselves */
decoder->handles_eof = 0;
return decoder; return decoder;
} }
@ -194,6 +198,12 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
return Py_None; return Py_None;
} }
static PyObject *
_get_handles_eof(ImagingDecoderObject *decoder)
{
return PyBool_FromLong(decoder->handles_eof);
}
static struct PyMethodDef methods[] = { static struct PyMethodDef methods[] = {
{"decode", (PyCFunction)_decode, 1}, {"decode", (PyCFunction)_decode, 1},
{"cleanup", (PyCFunction)_decode_cleanup, 1}, {"cleanup", (PyCFunction)_decode_cleanup, 1},
@ -201,6 +211,13 @@ static struct PyMethodDef methods[] = {
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
static struct PyGetSetDef getseters[] = {
{"handles_eof", (getter)_get_handles_eof, NULL,
"True if this decoder expects to handle EOF itself.",
NULL},
{NULL, NULL, NULL, NULL, NULL} /* sentinel */
};
static PyTypeObject ImagingDecoderType = { static PyTypeObject ImagingDecoderType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"ImagingDecoder", /*tp_name*/ "ImagingDecoder", /*tp_name*/
@ -232,7 +249,7 @@ static PyTypeObject ImagingDecoderType = {
0, /*tp_iternext*/ 0, /*tp_iternext*/
methods, /*tp_methods*/ methods, /*tp_methods*/
0, /*tp_members*/ 0, /*tp_members*/
0, /*tp_getset*/ getseters, /*tp_getset*/
}; };
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
@ -762,3 +779,55 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
return (PyObject*) decoder; return (PyObject*) decoder;
} }
#endif #endif
/* -------------------------------------------------------------------- */
/* JPEG 2000 */
/* -------------------------------------------------------------------- */
#ifdef HAVE_OPENJPEG
#include "Jpeg2K.h"
PyObject*
PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
{
ImagingDecoderObject* decoder;
JPEG2KDECODESTATE *context;
char* mode;
char* format;
OPJ_CODEC_FORMAT codec_format;
int reduce = 0;
int layers = 0;
int fd = -1;
if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format,
&reduce, &layers, &fd))
return NULL;
if (strcmp(format, "j2k") == 0)
codec_format = OPJ_CODEC_J2K;
else if (strcmp(format, "jpt") == 0)
codec_format = OPJ_CODEC_JPT;
else if (strcmp(format, "jp2") == 0)
codec_format = OPJ_CODEC_JP2;
else
return NULL;
decoder = PyImaging_DecoderNew(sizeof(JPEG2KDECODESTATE));
if (decoder == NULL)
return NULL;
decoder->handles_eof = 1;
decoder->decode = ImagingJpeg2KDecode;
decoder->cleanup = ImagingJpeg2KDecodeCleanup;
context = (JPEG2KDECODESTATE *)decoder->state.context;
context->fd = fd;
context->format = codec_format;
context->reduce = reduce;
context->layers = layers;
return (PyObject*) decoder;
}
#endif /* HAVE_OPENJPEG */

18
depends/install_openjpeg.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
# install openjpeg
if [ ! -f openjpeg-2.0.0.tar.gz ]; then
wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz'
fi
rm -r openjpeg-2.0.0
tar -xvzf openjpeg-2.0.0.tar.gz
pushd openjpeg-2.0.0
cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install
popd

18
depends/install_webp.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
# install webp
if [ ! -f libwebp-0.4.0.tar.gz ]; then
wget 'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz'
fi
rm -r libwebp-0.4.0
tar -xvzf libwebp-0.4.0.tar.gz
pushd libwebp-0.4.0
./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make && sudo make install
popd

View File

@ -153,6 +153,92 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
before building the Python Imaging Library. See the distribution README for before building the Python Imaging Library. See the distribution README for
details. details.
JPEG 2000
^^^^^^^^^
PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or
``RGBA`` data. It can also read files containing ``YCbCr`` data, which it
converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is
an alpha channel. PIL supports JPEG 2000 raw codestreams (``.j2k`` files), as
well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). PIL does *not*
support files whose components have different sampling frequencies.
When loading, if you set the ``mode`` on the image prior to the
:py:meth:`~PIL.Image.Image.load` method being invoked, you can ask PIL to
convert the image to either ``RGB`` or ``RGBA`` rather than choosing for
itself. It is also possible to set ``reduce`` to the number of resolutions to
discard (each one reduces the size of the resulting image by a factor of 2),
and ``layers`` to specify the number of quality layers to load.
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**offset**
The image offset, as a tuple of integers, e.g. (16, 16)
**tile_offset**
The tile offset, again as a 2-tuple of integers.
**tile_size**
The tile size as a 2-tuple. If not specified, or if set to None, the
image will be saved without tiling.
**quality_mode**
Either `"rates"` or `"dB"` depending on the units you want to use to
specify image quality.
**quality_layers**
A sequence of numbers, each of which represents either an approximate size
reduction (if quality mode is `"rates"`) or a signal to noise ratio value
in decibels. If not specified, defaults to a single layer of full quality.
**num_resolutions**
The number of different image resolutions to be stored (which corresponds
to the number of Discrete Wavelet Transform decompositions plus one).
**codeblock_size**
The code-block size as a 2-tuple. Minimum size is 4 x 4, maximum is 1024 x
1024, with the additional restriction that no code-block may have more
than 4096 coefficients (i.e. the product of the two numbers must be no
greater than 4096).
**precinct_size**
The precinct size as a 2-tuple. Must be a power of two along both axes,
and must be greater than the code-block size.
**irreversible**
If ``True``, use the lossy Irreversible Color Transformation
followed by DWT 9-7. Defaults to ``False``, which means to use the
Reversible Color Transformation with DWT 5-3.
**progression**
Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``,
``"RPCL"``, ``"PCRL"``, ``"CPRL"``. The letters stand for Component,
Position, Resolution and Layer respectively and control the order of
encoding, the idea being that e.g. an image encoded using LRCP mode can
have its quality layers decoded as they arrive at the decoder, while one
encoded using RLCP mode will have increasing resolutions decoded as they
arrive, and so on.
**cinema_mode**
Set the encoder to produce output compliant with the digital cinema
specifications. The options here are ``"no"`` (the default),
``"cinema2k-24"`` for 24fps 2K, ``"cinema2k-48"`` for 48fps 2K, and
``"cinema4k-24"`` for 24fps 4K. Note that for compliant 2K files,
*at least one* of your image dimensions must match 2048 x 1080, while
for compliant 4K files, *at least one* of the dimensions must match
4096 x 2160.
.. note::
To enable JPEG 2000 support, you need to build and install the OpenJPEG
library, version 2.0.0 or higher, before building the Python Imaging
Library.
Windows users can install the OpenJPEG binaries available on the
OpenJPEG website, but must add them to their PATH in order to use PIL (if
you fail to do this, you will get errors about not being able to load the
``_imaging`` DLL).
MSP MSP
^^^ ^^^
@ -437,6 +523,25 @@ ICO
ICO is used to store icons on Windows. The largest available icon is read. ICO is used to store icons on Windows. The largest available icon is read.
ICNS
^^^^
PIL reads Mac OS X ``.icns`` files. By default, the largest available icon is
read, though you can override this by setting the :py:attr:`~PIL.Image.Image.size`
property before calling :py:meth:`~PIL.Image.Image.load`. The
:py:meth:`~PIL.Image.Image.open` method sets the following
:py:attr:`~PIL.Image.Image.info` property:
**sizes**
A list of supported sizes found in this icon file; these are a
3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
icon and 1 for a standard icon. You *are* permitted to use this 3-tuple
format for the :py:attr:`~PIL.Image.Image.size` property if you set it
before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size
will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you
ask for ``(512, 512, 2)``, the final value of
:py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``).
IMT IMT
^^^ ^^^

View File

@ -1,7 +1,7 @@
Pillow: a modern fork of PIL Pillow
============================ ======
Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the
Python Imaging Library by Fredrik Lundh and Contributors. Python Imaging Library by Fredrik Lundh and Contributors.
.. image:: https://travis-ci.org/python-imaging/Pillow.png .. image:: https://travis-ci.org/python-imaging/Pillow.png
@ -15,7 +15,7 @@ Python Imaging Library by Fredrik Lundh and Contributors.
:target: https://pypi.python.org/pypi/Pillow/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads :alt: Number of PyPI downloads
To start using Pillow, read the :doc:`installation To start using Pillow, please read the :doc:`installation
instructions <installation>`. instructions <installation>`.
If you can't find the information you need, try the old `PIL Handbook`_, but be If you can't find the information you need, try the old `PIL Handbook`_, but be

View File

@ -172,6 +172,15 @@ Python Wheels
$ pip install --use-wheel Pillow $ pip install --use-wheel Pillow
If the above does not work, it's likely because we haven't uploaded a
wheel for the latest version of Pillow. In that case, try pinning it
to a specific version:
::
$ pip install --use-wheel Pillow==2.3.0
Platform support Platform support
---------------- ----------------
@ -216,4 +225,6 @@ current versions of Linux, OS X, and Windows.
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | | Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.0 |x86,x86-64 |
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+

View File

@ -153,6 +153,14 @@ Plugin reference
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
:mod:`Jpeg2KImagePlugin` Module
-----------------------------
.. automodule:: PIL.Jpeg2KImagePlugin
:members:
:undoc-members:
:show-inheritance:
:mod:`McIdasImagePlugin` Module :mod:`McIdasImagePlugin` Module
------------------------------- -------------------------------

160
encode.c
View File

@ -40,6 +40,7 @@ typedef struct {
PyObject_HEAD PyObject_HEAD
int (*encode)(Imaging im, ImagingCodecState state, int (*encode)(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
int (*cleanup)(ImagingCodecState state);
struct ImagingCodecStateInstance state; struct ImagingCodecStateInstance state;
Imaging im; Imaging im;
PyObject* lock; PyObject* lock;
@ -77,6 +78,9 @@ PyImaging_EncoderNew(int contextsize)
/* Initialize encoder context */ /* Initialize encoder context */
encoder->state.context = context; encoder->state.context = context;
/* Most encoders don't need this */
encoder->cleanup = NULL;
/* Target image */ /* Target image */
encoder->lock = NULL; encoder->lock = NULL;
encoder->im = NULL; encoder->im = NULL;
@ -87,6 +91,8 @@ PyImaging_EncoderNew(int contextsize)
static void static void
_dealloc(ImagingEncoderObject* encoder) _dealloc(ImagingEncoderObject* encoder)
{ {
if (encoder->cleanup)
encoder->cleanup(&encoder->state);
free(encoder->state.buffer); free(encoder->state.buffer);
free(encoder->state.context); free(encoder->state.context);
Py_XDECREF(encoder->lock); Py_XDECREF(encoder->lock);
@ -797,4 +803,158 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
#endif #endif
/* -------------------------------------------------------------------- */
/* JPEG 2000 */
/* -------------------------------------------------------------------- */
#ifdef HAVE_OPENJPEG
#include "Jpeg2K.h"
static void
j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y)
{
*x = *y = 0;
if (tuple && PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2) {
*x = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 0));
*y = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1));
if (*x < 0)
*x = 0;
if (*y < 0)
*y = 0;
}
}
PyObject*
PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
{
ImagingEncoderObject *encoder;
JPEG2KENCODESTATE *context;
char *mode;
char *format;
OPJ_CODEC_FORMAT codec_format;
PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL;
char *quality_mode = "rates";
PyObject *quality_layers = NULL;
int num_resolutions = 0;
PyObject *cblk_size = NULL, *precinct_size = NULL;
PyObject *irreversible = NULL;
char *progression = "LRCP";
OPJ_PROG_ORDER prog_order;
char *cinema_mode = "no";
OPJ_CINEMA_MODE cine_mode;
int fd = -1;
if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format,
&offset, &tile_offset, &tile_size,
&quality_mode, &quality_layers, &num_resolutions,
&cblk_size, &precinct_size,
&irreversible, &progression, &cinema_mode,
&fd))
return NULL;
if (strcmp (format, "j2k") == 0)
codec_format = OPJ_CODEC_J2K;
else if (strcmp (format, "jpt") == 0)
codec_format = OPJ_CODEC_JPT;
else if (strcmp (format, "jp2") == 0)
codec_format = OPJ_CODEC_JP2;
else
return NULL;
if (strcmp(progression, "LRCP") == 0)
prog_order = OPJ_LRCP;
else if (strcmp(progression, "RLCP") == 0)
prog_order = OPJ_RLCP;
else if (strcmp(progression, "RPCL") == 0)
prog_order = OPJ_RPCL;
else if (strcmp(progression, "PCRL") == 0)
prog_order = OPJ_PCRL;
else if (strcmp(progression, "CPRL") == 0)
prog_order = OPJ_CPRL;
else
return NULL;
if (strcmp(cinema_mode, "no") == 0)
cine_mode = OPJ_OFF;
else if (strcmp(cinema_mode, "cinema2k-24") == 0)
cine_mode = OPJ_CINEMA2K_24;
else if (strcmp(cinema_mode, "cinema2k-48") == 0)
cine_mode = OPJ_CINEMA2K_48;
else if (strcmp(cinema_mode, "cinema4k-24") == 0)
cine_mode = OPJ_CINEMA4K_24;
else
return NULL;
encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE));
if (!encoder)
return NULL;
encoder->encode = ImagingJpeg2KEncode;
encoder->cleanup = ImagingJpeg2KEncodeCleanup;
context = (JPEG2KENCODESTATE *)encoder->state.context;
context->fd = fd;
context->format = codec_format;
context->offset_x = context->offset_y = 0;
j2k_decode_coord_tuple(offset, &context->offset_x, &context->offset_y);
j2k_decode_coord_tuple(tile_offset,
&context->tile_offset_x,
&context->tile_offset_y);
j2k_decode_coord_tuple(tile_size,
&context->tile_size_x,
&context->tile_size_y);
/* Error on illegal tile offsets */
if (context->tile_size_x && context->tile_size_y) {
if (context->tile_offset_x <= context->offset_x - context->tile_size_x
|| context->tile_offset_y <= context->offset_y - context->tile_size_y) {
PyErr_SetString(PyExc_ValueError,
"JPEG 2000 tile offset too small; top left tile must "
"intersect image area");
}
if (context->tile_offset_x > context->offset_x
|| context->tile_offset_y > context->offset_y) {
PyErr_SetString(PyExc_ValueError,
"JPEG 2000 tile offset too large to cover image area");
Py_DECREF(encoder);
return NULL;
}
}
if (quality_layers && PySequence_Check(quality_layers)) {
context->quality_is_in_db = strcmp (quality_mode, "dB") == 0;
context->quality_layers = quality_layers;
Py_INCREF(quality_layers);
}
context->num_resolutions = num_resolutions;
j2k_decode_coord_tuple(cblk_size,
&context->cblk_width,
&context->cblk_height);
j2k_decode_coord_tuple(precinct_size,
&context->precinct_width,
&context->precinct_height);
context->irreversible = PyObject_IsTrue(irreversible);
context->progression = prog_order;
context->cinema_mode = cine_mode;
return (PyObject *)encoder;
}
#endif
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

View File

@ -424,6 +424,14 @@ extern int ImagingJpegDecodeCleanup(ImagingCodecState state);
extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
#endif #endif
#ifdef HAVE_OPENJPEG
extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state);
extern int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingJpeg2KEncodeCleanup(ImagingCodecState state);
#endif
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes); UINT8* buffer, int bytes);
#ifdef HAVE_LIBTIFF #ifdef HAVE_LIBTIFF
@ -497,6 +505,32 @@ struct ImagingCodecStateInstance {
void *context; void *context;
}; };
/* Incremental encoding/decoding support */
typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec;
typedef int (*ImagingIncrementalCodecEntry)(Imaging im,
ImagingCodecState state,
ImagingIncrementalCodec codec);
enum {
INCREMENTAL_CODEC_READ = 1,
INCREMENTAL_CODEC_WRITE = 2
};
enum {
INCREMENTAL_CODEC_NOT_SEEKABLE = 0,
INCREMENTAL_CODEC_SEEKABLE = 1
};
extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state, int read_or_write, int seekable, int fd);
extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec);
extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes);
extern ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes);
extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes);
extern ssize_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes);
extern off_t ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, off_t bytes);
extern size_t ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec);
/* Errcodes */ /* Errcodes */
#define IMAGING_CODEC_END 1 #define IMAGING_CODEC_END 1
#define IMAGING_CODEC_OVERRUN -1 #define IMAGING_CODEC_OVERRUN -1

677
libImaging/Incremental.c Normal file
View File

@ -0,0 +1,677 @@
/*
* The Python Imaging Library
* $Id$
*
* incremental decoding adaptor.
*
* Copyright (c) 2014 Coriolis Systems Limited
* Copyright (c) 2014 Alastair Houghton
*
*/
#include "Imaging.h"
/* The idea behind this interface is simple: the actual decoding proceeds in
a thread, which is run in lock step with the main thread. Whenever the
ImagingIncrementalCodecRead() call runs short on data, it suspends the
decoding thread and wakes the main thread. Conversely, the
ImagingIncrementalCodecPushBuffer() call suspends the main thread and wakes
the decoding thread, providing a buffer of data.
The two threads are never running simultaneously, so there is no need for
any addition synchronisation measures outside of this file.
Note also that we start the thread suspended (on Windows), or make it
immediately wait (other platforms), so that it's possible to initialise
things before the thread starts running.
This interface is useful to allow PIL to interact efficiently with any
third-party imaging library that does not support suspendable reads;
one example is OpenJPEG (which is used for J2K support). The TIFF library
might also benefit from using this code.
Note that if using this module, you want to set handles_eof on your
decoder to true. Why? Because otherwise ImageFile.load() will abort,
thinking that the image is truncated, whereas generally you want it to
pass the EOF condition (0 bytes to read) through to your code. */
/* Additional complication: *Some* codecs need to seek; this is fine if
there is a file descriptor, but if we're buffering data it becomes
awkward. The incremental adaptor now contains code to handle these
two cases. */
#ifdef _WIN32
#include <windows.h>
#include <process.h>
#else
#include <pthread.h>
#endif
#define DEBUG_INCREMENTAL 0
#if DEBUG_INCREMENTAL
#define DEBUG(...) printf(__VA_ARGS__)
#else
#define DEBUG(...)
#endif
struct ImagingIncrementalCodecStruct {
#ifdef _WIN32
HANDLE hCodecEvent;
HANDLE hDataEvent;
HANDLE hThread;
#else
pthread_mutex_t start_mutex;
pthread_cond_t start_cond;
pthread_mutex_t codec_mutex;
pthread_cond_t codec_cond;
pthread_mutex_t data_mutex;
pthread_cond_t data_cond;
pthread_t thread;
#endif
ImagingIncrementalCodecEntry entry;
Imaging im;
ImagingCodecState state;
struct {
int fd;
UINT8 *buffer; /* Base of buffer */
UINT8 *ptr; /* Current pointer in buffer */
UINT8 *top; /* Highest point in buffer we've used */
UINT8 *end; /* End of buffer */
} stream;
int read_or_write;
int seekable;
int started;
int result;
};
static void flush_stream(ImagingIncrementalCodec codec);
#if _WIN32
static unsigned int __stdcall
codec_thread(void *ptr)
{
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
DEBUG("Entering thread\n");
codec->result = codec->entry(codec->im, codec->state, codec);
DEBUG("Leaving thread (%d)\n", codec->result);
flush_stream(codec);
SetEvent(codec->hCodecEvent);
return 0;
}
#else
static void *
codec_thread(void *ptr)
{
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
DEBUG("Entering thread\n");
codec->result = codec->entry(codec->im, codec->state, codec);
DEBUG("Leaving thread (%d)\n", codec->result);
flush_stream(codec);
pthread_mutex_lock(&codec->codec_mutex);
pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&codec->codec_mutex);
return NULL;
}
#endif
static void
flush_stream(ImagingIncrementalCodec codec)
{
UINT8 *buffer;
size_t bytes;
/* This is to flush data from the write buffer for a seekable write
codec. */
if (codec->read_or_write != INCREMENTAL_CODEC_WRITE
|| codec->state->errcode != IMAGING_CODEC_END
|| !codec->seekable
|| codec->stream.fd >= 0)
return;
DEBUG("flushing data\n");
buffer = codec->stream.buffer;
bytes = codec->stream.ptr - codec->stream.buffer;
codec->state->errcode = 0;
codec->seekable = INCREMENTAL_CODEC_NOT_SEEKABLE;
codec->stream.buffer = codec->stream.ptr = codec->stream.end
= codec->stream.top = NULL;
ImagingIncrementalCodecWrite(codec, buffer, bytes);
codec->state->errcode = IMAGING_CODEC_END;
codec->result = (int)ImagingIncrementalCodecBytesInBuffer(codec);
free(buffer);
}
/**
* Create a new incremental codec */
ImagingIncrementalCodec
ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry,
Imaging im,
ImagingCodecState state,
int read_or_write,
int seekable,
int fd)
{
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct));
codec->entry = codec_entry;
codec->im = im;
codec->state = state;
codec->result = 0;
codec->stream.fd = fd;
codec->stream.buffer = codec->stream.ptr = codec->stream.end
= codec->stream.top = NULL;
codec->started = 0;
codec->seekable = seekable;
codec->read_or_write = read_or_write;
if (fd >= 0)
lseek(fd, 0, SEEK_SET);
/* System specific set-up */
#if _WIN32
codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!codec->hCodecEvent) {
free(codec);
return NULL;
}
codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!codec->hDataEvent) {
CloseHandle(codec->hCodecEvent);
free(codec);
return NULL;
}
codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec,
CREATE_SUSPENDED, NULL);
if (!codec->hThread) {
CloseHandle(codec->hCodecEvent);
CloseHandle(codec->hDataEvent);
free(codec);
return NULL;
}
#else
if (pthread_mutex_init(&codec->start_mutex, NULL)) {
free (codec);
return NULL;
}
if (pthread_mutex_init(&codec->codec_mutex, NULL)) {
pthread_mutex_destroy(&codec->start_mutex);
free(codec);
return NULL;
}
if (pthread_mutex_init(&codec->data_mutex, NULL)) {
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
free(codec);
return NULL;
}
if (pthread_cond_init(&codec->start_cond, NULL)) {
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&codec->data_mutex);
free(codec);
return NULL;
}
if (pthread_cond_init(&codec->codec_cond, NULL)) {
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&codec->start_cond);
free(codec);
return NULL;
}
if (pthread_cond_init(&codec->data_cond, NULL)) {
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&codec->codec_cond);
free(codec);
return NULL;
}
if (pthread_create(&codec->thread, NULL, codec_thread, codec)) {
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&codec->codec_cond);
pthread_cond_destroy(&codec->data_cond);
free(codec);
return NULL;
}
#endif
return codec;
}
/**
* Destroy an incremental codec */
void
ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec)
{
DEBUG("destroying\n");
if (!codec->started) {
#ifdef _WIN32
ResumeThread(codec->hThread);
#else
pthread_cond_signal(&codec->start_cond);
#endif
codec->started = 1;
}
#ifndef _WIN32
pthread_mutex_lock(&codec->data_mutex);
#endif
if (codec->seekable && codec->stream.fd < 0)
free (codec->stream.buffer);
codec->stream.buffer = codec->stream.ptr = codec->stream.end
= codec->stream.top = NULL;
#ifdef _WIN32
SetEvent(codec->hDataEvent);
WaitForSingleObject(codec->hThread, INFINITE);
CloseHandle(codec->hThread);
CloseHandle(codec->hCodecEvent);
CloseHandle(codec->hDataEvent);
#else
pthread_cond_signal(&codec->data_cond);
pthread_mutex_unlock(&codec->data_mutex);
pthread_join(codec->thread, NULL);
pthread_mutex_destroy(&codec->start_mutex);
pthread_mutex_destroy(&codec->codec_mutex);
pthread_mutex_destroy(&codec->data_mutex);
pthread_cond_destroy(&codec->start_cond);
pthread_cond_destroy(&codec->codec_cond);
pthread_cond_destroy(&codec->data_cond);
#endif
free (codec);
}
/**
* Push a data buffer for an incremental codec */
int
ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec,
UINT8 *buf, int bytes)
{
if (!codec->started) {
DEBUG("starting\n");
#ifdef _WIN32
ResumeThread(codec->hThread);
#else
pthread_cond_signal(&codec->start_cond);
#endif
codec->started = 1;
/* Wait for the thread to ask for data */
#ifdef _WIN32
WaitForSingleObject(codec->hCodecEvent, INFINITE);
#else
pthread_mutex_lock(&codec->codec_mutex);
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
pthread_mutex_unlock(&codec->codec_mutex);
#endif
if (codec->result < 0) {
DEBUG("got result %d\n", codec->result);
return codec->result;
}
}
/* Codecs using an fd don't need data, so when we get here, we're done */
if (codec->stream.fd >= 0) {
DEBUG("got result %d\n", codec->result);
return codec->result;
}
DEBUG("providing %p, %d\n", buf, bytes);
#ifndef _WIN32
pthread_mutex_lock(&codec->data_mutex);
#endif
if (codec->read_or_write == INCREMENTAL_CODEC_READ
&& codec->seekable && codec->stream.fd < 0) {
/* In this specific case, we append to a buffer we allocate ourselves */
size_t old_size = codec->stream.end - codec->stream.buffer;
size_t new_size = codec->stream.end - codec->stream.buffer + bytes;
UINT8 *new = (UINT8 *)realloc (codec->stream.buffer, new_size);
if (!new) {
codec->state->errcode = IMAGING_CODEC_MEMORY;
#ifndef _WIN32
pthread_mutex_unlock(&codec->data_mutex);
#endif
return -1;
}
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
codec->stream.end = new + new_size;
codec->stream.buffer = new;
memcpy(new + old_size, buf, bytes);
} else {
codec->stream.buffer = codec->stream.ptr = buf;
codec->stream.end = buf + bytes;
}
#ifdef _WIN32
SetEvent(codec->hDataEvent);
WaitForSingleObject(codec->hCodecEvent, INFINITE);
#else
pthread_cond_signal(&codec->data_cond);
pthread_mutex_unlock(&codec->data_mutex);
pthread_mutex_lock(&codec->codec_mutex);
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
pthread_mutex_unlock(&codec->codec_mutex);
#endif
DEBUG("got result %d\n", codec->result);
return codec->result;
}
size_t
ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec)
{
return codec->stream.ptr - codec->stream.buffer;
}
ssize_t
ImagingIncrementalCodecRead(ImagingIncrementalCodec codec,
void *buffer, size_t bytes)
{
UINT8 *ptr = (UINT8 *)buffer;
size_t done = 0;
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
DEBUG("attempt to read from write codec\n");
return -1;
}
DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes);
if (codec->stream.fd >= 0) {
ssize_t ret = read(codec->stream.fd, buffer, bytes);
DEBUG("read %lld bytes from fd\n", (long long)ret);
return ret;
}
#ifndef _WIN32
pthread_mutex_lock(&codec->data_mutex);
#endif
while (bytes) {
size_t todo = bytes;
size_t remaining = codec->stream.end - codec->stream.ptr;
if (!remaining) {
DEBUG("waiting for data\n");
#ifndef _WIN32
pthread_mutex_lock(&codec->codec_mutex);
#endif
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
#if _WIN32
SetEvent(codec->hCodecEvent);
WaitForSingleObject(codec->hDataEvent, INFINITE);
#else
pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&codec->codec_mutex);
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
#endif
remaining = codec->stream.end - codec->stream.ptr;
codec->stream.top = codec->stream.end;
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
}
if (todo > remaining)
todo = remaining;
if (!todo)
break;
memcpy (ptr, codec->stream.ptr, todo);
codec->stream.ptr += todo;
bytes -= todo;
done += todo;
ptr += todo;
}
#ifndef _WIN32
pthread_mutex_unlock(&codec->data_mutex);
#endif
DEBUG("read total %llu bytes\n", (unsigned long long)done);
return done;
}
off_t
ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec,
off_t bytes)
{
off_t done = 0;
DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes);
/* In write mode, explicitly fill with zeroes */
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
static const UINT8 zeroes[256] = { 0 };
off_t done = 0;
while (bytes) {
size_t todo = (size_t)(bytes > 256 ? 256 : bytes);
ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo);
if (written <= 0)
break;
done += written;
bytes -= written;
}
return done;
}
if (codec->stream.fd >= 0)
return lseek(codec->stream.fd, bytes, SEEK_CUR);
#ifndef _WIN32
pthread_mutex_lock(&codec->data_mutex);
#endif
while (bytes) {
off_t todo = bytes;
off_t remaining = codec->stream.end - codec->stream.ptr;
if (!remaining) {
DEBUG("waiting for data\n");
#ifndef _WIN32
pthread_mutex_lock(&codec->codec_mutex);
#endif
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
#if _WIN32
SetEvent(codec->hCodecEvent);
WaitForSingleObject(codec->hDataEvent, INFINITE);
#else
pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&codec->codec_mutex);
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
#endif
remaining = codec->stream.end - codec->stream.ptr;
}
if (todo > remaining)
todo = remaining;
if (!todo)
break;
codec->stream.ptr += todo;
bytes -= todo;
done += todo;
}
#ifndef _WIN32
pthread_mutex_unlock(&codec->data_mutex);
#endif
DEBUG("skipped total %llu bytes\n", (unsigned long long)done);
return done;
}
ssize_t
ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec,
const void *buffer, size_t bytes)
{
const UINT8 *ptr = (const UINT8 *)buffer;
size_t done = 0;
if (codec->read_or_write == INCREMENTAL_CODEC_READ) {
DEBUG("attempt to write from read codec\n");
return -1;
}
DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes);
if (codec->stream.fd >= 0)
return write(codec->stream.fd, buffer, bytes);
#ifndef _WIN32
pthread_mutex_lock(&codec->data_mutex);
#endif
while (bytes) {
size_t todo = bytes;
size_t remaining = codec->stream.end - codec->stream.ptr;
if (!remaining) {
if (codec->seekable && codec->stream.fd < 0) {
/* In this case, we maintain the stream buffer ourselves */
size_t old_size = codec->stream.top - codec->stream.buffer;
size_t new_size = (old_size + bytes + 65535) & ~65535;
UINT8 *new = (UINT8 *)realloc(codec->stream.buffer, new_size);
if (!new) {
codec->state->errcode = IMAGING_CODEC_MEMORY;
#ifndef _WIN32
pthread_mutex_unlock(&codec->data_mutex);
#endif
return done == 0 ? -1 : done;
}
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
codec->stream.buffer = new;
codec->stream.end = new + new_size;
codec->stream.top = new + old_size;
} else {
DEBUG("waiting for space\n");
#ifndef _WIN32
pthread_mutex_lock(&codec->codec_mutex);
#endif
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
#if _WIN32
SetEvent(codec->hCodecEvent);
WaitForSingleObject(codec->hDataEvent, INFINITE);
#else
pthread_cond_signal(&codec->codec_cond);
pthread_mutex_unlock(&codec->codec_mutex);
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
#endif
}
remaining = codec->stream.end - codec->stream.ptr;
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
}
if (todo > remaining)
todo = remaining;
if (!todo)
break;
memcpy (codec->stream.ptr, ptr, todo);
codec->stream.ptr += todo;
bytes -= todo;
done += todo;
ptr += todo;
}
if (codec->stream.ptr > codec->stream.top)
codec->stream.top = codec->stream.ptr;
#ifndef _WIN32
pthread_mutex_unlock(&codec->data_mutex);
#endif
DEBUG("wrote total %llu bytes\n", (unsigned long long)done);
return done;
}
off_t
ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec,
off_t bytes)
{
off_t buffered;
DEBUG("seeking (going to %llu bytes)\n", (unsigned long long)bytes);
if (codec->stream.fd >= 0)
return lseek(codec->stream.fd, bytes, SEEK_SET);
if (!codec->seekable) {
DEBUG("attempt to seek non-seekable stream\n");
return -1;
}
if (bytes < 0) {
DEBUG("attempt to seek before stream start\n");
return -1;
}
buffered = codec->stream.top - codec->stream.buffer;
if (bytes <= buffered) {
DEBUG("seek within buffer\n");
codec->stream.ptr = codec->stream.buffer + bytes;
return bytes;
}
return buffered + ImagingIncrementalCodecSkip(codec, bytes - buffered);
}

91
libImaging/Jpeg2K.h Normal file
View File

@ -0,0 +1,91 @@
/*
* The Python Imaging Library
* $Id$
*
* declarations for the OpenJPEG codec interface.
*
* Copyright (c) 2014 by Coriolis Systems Limited
* Copyright (c) 2014 by Alastair Houghton
*/
#include <openjpeg-2.0/openjpeg.h>
/* -------------------------------------------------------------------- */
/* Decoder */
/* -------------------------------------------------------------------- */
typedef struct {
/* CONFIGURATION */
/* File descriptor, if available; otherwise, -1 */
int fd;
/* Specify the desired format */
OPJ_CODEC_FORMAT format;
/* Set to divide image resolution by 2**reduce. */
int reduce;
/* Set to limit the number of quality layers to decode (0 = all layers) */
int layers;
/* PRIVATE CONTEXT (set by decoder) */
const char *error_msg;
ImagingIncrementalCodec decoder;
} JPEG2KDECODESTATE;
/* -------------------------------------------------------------------- */
/* Encoder */
/* -------------------------------------------------------------------- */
typedef struct {
/* CONFIGURATION */
/* File descriptor, if available; otherwise, -1 */
int fd;
/* Specify the desired format */
OPJ_CODEC_FORMAT format;
/* Image offset */
int offset_x, offset_y;
/* Tile information */
int tile_offset_x, tile_offset_y;
int tile_size_x, tile_size_y;
/* Quality layers (a sequence of numbers giving *either* rates or dB) */
int quality_is_in_db;
PyObject *quality_layers;
/* Number of resolutions (DWT decompositions + 1 */
int num_resolutions;
/* Code block size */
int cblk_width, cblk_height;
/* Precinct size */
int precinct_width, precinct_height;
/* Compression style */
int irreversible;
/* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */
OPJ_PROG_ORDER progression;
/* Cinema mode */
OPJ_CINEMA_MODE cinema_mode;
/* PRIVATE CONTEXT (set by decoder) */
const char *error_msg;
ImagingIncrementalCodec encoder;
} JPEG2KENCODESTATE;
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

753
libImaging/Jpeg2KDecode.c Normal file
View File

@ -0,0 +1,753 @@
/*
* The Python Imaging Library.
* $Id$
*
* decoder for JPEG2000 image data.
*
* history:
* 2014-03-12 ajh Created
*
* Copyright (c) 2014 Coriolis Systems Limited
* Copyright (c) 2014 Alastair Houghton
*
* See the README file for details on usage and redistribution.
*/
#include "Imaging.h"
#ifdef HAVE_OPENJPEG
#include <stdlib.h>
#include "Jpeg2K.h"
typedef struct {
OPJ_UINT32 tile_index;
OPJ_UINT32 data_size;
OPJ_INT32 x0, y0, x1, y1;
OPJ_UINT32 nb_comps;
} JPEG2KTILEINFO;
/* -------------------------------------------------------------------- */
/* Error handler */
/* -------------------------------------------------------------------- */
static void
j2k_error(const char *msg, void *client_data)
{
JPEG2KDECODESTATE *state = (JPEG2KDECODESTATE *) client_data;
free((void *)state->error_msg);
state->error_msg = strdup(msg);
}
/* -------------------------------------------------------------------- */
/* Buffer input stream */
/* -------------------------------------------------------------------- */
static OPJ_SIZE_T
j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data;
size_t len = ImagingIncrementalCodecRead(decoder, p_buffer, p_nb_bytes);
return len ? len : (OPJ_SIZE_T)-1;
}
static OPJ_OFF_T
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data;
off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes);
return pos ? pos : (OPJ_OFF_T)-1;
}
/* -------------------------------------------------------------------- */
/* Unpackers */
/* -------------------------------------------------------------------- */
typedef void (*j2k_unpacker_t)(opj_image_t *in,
const JPEG2KTILEINFO *tileInfo,
const UINT8 *data,
Imaging im);
struct j2k_decode_unpacker {
const char *mode;
OPJ_COLOR_SPACE color_space;
unsigned components;
j2k_unpacker_t unpacker;
};
static inline
unsigned j2ku_shift(unsigned x, int n)
{
if (n < 0)
return x >> -n;
else
return x << n;
}
static void
j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
int csiz = (in->comps[0].prec + 7) >> 3;
unsigned x, y;
if (csiz == 3)
csiz = 4;
if (shift < 0)
offset += 1 << (-shift - 1);
switch (csiz) {
case 1:
for (y = 0; y < h; ++y) {
const UINT8 *data = &tiledata[y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x)
*row++ = j2ku_shift(offset + *data++, shift);
}
break;
case 2:
for (y = 0; y < h; ++y) {
const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x)
*row++ = j2ku_shift(offset + *data++, shift);
}
break;
case 4:
for (y = 0; y < h; ++y) {
const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x)
*row++ = j2ku_shift(offset + *data++, shift);
}
break;
}
}
static void
j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
int csiz = (in->comps[0].prec + 7) >> 3;
unsigned x, y;
if (shift < 0)
offset += 1 << (-shift - 1);
if (csiz == 3)
csiz = 4;
switch (csiz) {
case 1:
for (y = 0; y < h; ++y) {
const UINT8 *data = &tiledata[y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x) {
UINT8 byte = j2ku_shift(offset + *data++, shift);
row[0] = row[1] = row[2] = byte;
row[3] = 0xff;
row += 4;
}
}
break;
case 2:
for (y = 0; y < h; ++y) {
const UINT16 *data = (UINT16 *)&tiledata[2 * y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x) {
UINT8 byte = j2ku_shift(offset + *data++, shift);
row[0] = row[1] = row[2] = byte;
row[3] = 0xff;
row += 4;
}
}
break;
case 4:
for (y = 0; y < h; ++y) {
const UINT32 *data = (UINT32 *)&tiledata[4 * y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0;
for (x = 0; x < w; ++x) {
UINT8 byte = j2ku_shift(offset + *data++, shift);
row[0] = row[1] = row[2] = byte;
row[3] = 0xff;
row += 4;
}
}
break;
}
}
static void
j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shift = 8 - in->comps[0].prec;
int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0;
int csiz = (in->comps[0].prec + 7) >> 3;
int ashift = 8 - in->comps[1].prec;
int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0;
int acsiz = (in->comps[1].prec + 7) >> 3;
const UINT8 *atiledata;
unsigned x, y;
if (csiz == 3)
csiz = 4;
if (acsiz == 3)
acsiz = 4;
if (shift < 0)
offset += 1 << (-shift - 1);
if (ashift < 0)
aoffset += 1 << (-ashift - 1);
atiledata = tiledata + csiz * w * h;
for (y = 0; y < h; ++y) {
const UINT8 *data = &tiledata[csiz * y * w];
const UINT8 *adata = &atiledata[acsiz * y * w];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (x = 0; x < w; ++x) {
UINT32 word = 0, aword = 0, byte;
switch (csiz) {
case 1: word = *data++; break;
case 2: word = *(const UINT16 *)data; data += 2; break;
case 4: word = *(const UINT32 *)data; data += 4; break;
}
switch (acsiz) {
case 1: aword = *adata++; break;
case 2: aword = *(const UINT16 *)adata; adata += 2; break;
case 4: aword = *(const UINT32 *)adata; adata += 4; break;
}
byte = j2ku_shift(offset + word, shift);
row[0] = row[1] = row[2] = byte;
row[3] = j2ku_shift(aoffset + aword, ashift);
row += 4;
}
}
}
static void
j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shifts[3], offsets[3], csiz[3];
const UINT8 *cdata[3];
const UINT8 *cptr = tiledata;
unsigned n, x, y;
for (n = 0; n < 3; ++n) {
cdata[n] = cptr;
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
csiz[n] = (in->comps[n].prec + 7) >> 3;
if (csiz[n] == 3)
csiz[n] = 4;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
cptr += csiz[n] * w * h;
}
for (y = 0; y < h; ++y) {
const UINT8 *data[3];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (n = 0; n < 3; ++n)
data[n] = &cdata[n][csiz[n] * y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 3; ++n) {
UINT32 word = 0;
switch (csiz[n]) {
case 1: word = *data[n]++; break;
case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
}
row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
}
row[3] = 0xff;
row += 4;
}
}
}
static void
j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shifts[3], offsets[3], csiz[3];
const UINT8 *cdata[3];
const UINT8 *cptr = tiledata;
unsigned n, x, y;
for (n = 0; n < 3; ++n) {
cdata[n] = cptr;
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
csiz[n] = (in->comps[n].prec + 7) >> 3;
if (csiz[n] == 3)
csiz[n] = 4;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
cptr += csiz[n] * w * h;
}
for (y = 0; y < h; ++y) {
const UINT8 *data[3];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
UINT8 *row_start = row;
for (n = 0; n < 3; ++n)
data[n] = &cdata[n][csiz[n] * y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 3; ++n) {
UINT32 word = 0;
switch (csiz[n]) {
case 1: word = *data[n]++; break;
case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
}
row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
}
row[3] = 0xff;
row += 4;
}
ImagingConvertYCbCr2RGB(row_start, row_start, w);
}
}
static void
j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shifts[4], offsets[4], csiz[4];
const UINT8 *cdata[4];
const UINT8 *cptr = tiledata;
unsigned n, x, y;
for (n = 0; n < 4; ++n) {
cdata[n] = cptr;
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
csiz[n] = (in->comps[n].prec + 7) >> 3;
if (csiz[n] == 3)
csiz[n] = 4;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
cptr += csiz[n] * w * h;
}
for (y = 0; y < h; ++y) {
const UINT8 *data[4];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
for (n = 0; n < 4; ++n)
data[n] = &cdata[n][csiz[n] * y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 4; ++n) {
UINT32 word = 0;
switch (csiz[n]) {
case 1: word = *data[n]++; break;
case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
}
row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
}
row += 4;
}
}
}
static void
j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
const UINT8 *tiledata, Imaging im)
{
unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0;
unsigned w = tileinfo->x1 - tileinfo->x0;
unsigned h = tileinfo->y1 - tileinfo->y0;
int shifts[4], offsets[4], csiz[4];
const UINT8 *cdata[4];
const UINT8 *cptr = tiledata;
unsigned n, x, y;
for (n = 0; n < 4; ++n) {
cdata[n] = cptr;
shifts[n] = 8 - in->comps[n].prec;
offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0;
csiz[n] = (in->comps[n].prec + 7) >> 3;
if (csiz[n] == 3)
csiz[n] = 4;
if (shifts[n] < 0)
offsets[n] += 1 << (-shifts[n] - 1);
cptr += csiz[n] * w * h;
}
for (y = 0; y < h; ++y) {
const UINT8 *data[4];
UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4;
UINT8 *row_start = row;
for (n = 0; n < 4; ++n)
data[n] = &cdata[n][csiz[n] * y * w];
for (x = 0; x < w; ++x) {
for (n = 0; n < 4; ++n) {
UINT32 word = 0;
switch (csiz[n]) {
case 1: word = *data[n]++; break;
case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break;
case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break;
}
row[n] = j2ku_shift(offsets[n] + word, shifts[n]);
}
row += 4;
}
ImagingConvertYCbCr2RGB(row_start, row_start, w);
}
}
static const struct j2k_decode_unpacker j2k_unpackers[] = {
{ "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l },
{ "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
{ "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
{ "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb },
{ "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
{ "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
{ "RGB", OPJ_CLRSPC_SRGB, 4, j2ku_srgb_rgb },
{ "RGB", OPJ_CLRSPC_SYCC, 4, j2ku_sycc_rgb },
{ "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb },
{ "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la },
{ "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb },
{ "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb },
{ "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba },
{ "RGBA", OPJ_CLRSPC_SYCC, 4, j2ku_sycca_rgba },
};
/* -------------------------------------------------------------------- */
/* Decoder */
/* -------------------------------------------------------------------- */
enum {
J2K_STATE_START = 0,
J2K_STATE_DECODING = 1,
J2K_STATE_DONE = 2,
J2K_STATE_FAILED = 3,
};
static int
j2k_decode_entry(Imaging im, ImagingCodecState state,
ImagingIncrementalCodec decoder)
{
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context;
opj_stream_t *stream = NULL;
opj_image_t *image = NULL;
opj_codec_t *codec = NULL;
opj_dparameters_t params;
OPJ_COLOR_SPACE color_space;
j2k_unpacker_t unpack = NULL;
size_t buffer_size = 0;
unsigned n;
stream = opj_stream_default_create(OPJ_TRUE);
if (!stream) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_stream_set_read_function(stream, j2k_read);
opj_stream_set_skip_function(stream, j2k_skip);
opj_stream_set_user_data(stream, decoder);
/* Setup decompression context */
context->error_msg = NULL;
opj_set_default_decoder_parameters(&params);
params.cp_reduce = context->reduce;
params.cp_layer = context->layers;
codec = opj_create_decompress(context->format);
if (!codec) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_set_error_handler(codec, j2k_error, context);
opj_setup_decoder(codec, &params);
if (!opj_read_header(stream, codec, &image)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Check that this image is something we can handle */
if (image->numcomps < 1 || image->numcomps > 4
|| image->color_space == OPJ_CLRSPC_UNKNOWN) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
for (n = 1; n < image->numcomps; ++n) {
if (image->comps[n].dx != 1 || image->comps[n].dy != 1) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
}
/*
Colorspace Number of components PIL mode
------------------------------------------------------
sRGB 3 RGB
sRGB 4 RGBA
gray 1 L or I
gray 2 LA
YCC 3 YCbCr
If colorspace is unspecified, we assume:
Number of components Colorspace
-----------------------------------------
1 gray
2 gray (+ alpha)
3 sRGB
4 sRGB (+ alpha)
*/
/* Find the correct unpacker */
color_space = image->color_space;
if (color_space == OPJ_CLRSPC_UNSPECIFIED) {
switch (image->numcomps) {
case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break;
case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break;
}
}
for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) {
if (color_space == j2k_unpackers[n].color_space
&& image->numcomps == j2k_unpackers[n].components
&& strcmp (im->mode, j2k_unpackers[n].mode) == 0) {
unpack = j2k_unpackers[n].unpacker;
break;
}
}
if (!unpack) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Decode the image tile-by-tile; this means we only need use as much
memory as is required for one tile's worth of components. */
for (;;) {
JPEG2KTILEINFO tile_info;
OPJ_BOOL should_continue;
unsigned correction = (1 << params.cp_reduce) - 1;
if (!opj_read_tile_header(codec,
stream,
&tile_info.tile_index,
&tile_info.data_size,
&tile_info.x0, &tile_info.y0,
&tile_info.x1, &tile_info.y1,
&tile_info.nb_comps,
&should_continue)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
if (!should_continue)
break;
/* Adjust the tile co-ordinates based on the reduction (OpenJPEG
doesn't do this for us) */
tile_info.x0 = (tile_info.x0 + correction) >> context->reduce;
tile_info.y0 = (tile_info.y0 + correction) >> context->reduce;
tile_info.x1 = (tile_info.x1 + correction) >> context->reduce;
tile_info.y1 = (tile_info.y1 + correction) >> context->reduce;
if (buffer_size < tile_info.data_size) {
UINT8 *new = realloc (state->buffer, tile_info.data_size);
if (!new) {
state->errcode = IMAGING_CODEC_MEMORY;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
state->buffer = new;
buffer_size = tile_info.data_size;
}
if (!opj_decode_tile_data(codec,
tile_info.tile_index,
(OPJ_BYTE *)state->buffer,
tile_info.data_size,
stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Check the tile bounds; if the tile is outside the image area,
or if it has a negative width or height (i.e. the coordinates are
swapped), bail. */
if (tile_info.x0 >= tile_info.x1
|| tile_info.y0 >= tile_info.y1
|| tile_info.x0 < image->x0
|| tile_info.y0 < image->y0
|| tile_info.x1 - image->x0 > im->xsize
|| tile_info.y1 - image->y0 > im->ysize) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
unpack(image, &tile_info, state->buffer, im);
}
if (!opj_end_decompress(codec, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
state->state = J2K_STATE_DONE;
state->errcode = IMAGING_CODEC_END;
quick_exit:
if (codec)
opj_destroy_codec(codec);
if (image)
opj_image_destroy(image);
if (stream)
opj_stream_destroy(stream);
return -1;
}
int
ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context;
if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED)
return -1;
if (state->state == J2K_STATE_START) {
context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry,
im, state,
INCREMENTAL_CODEC_READ,
INCREMENTAL_CODEC_NOT_SEEKABLE,
context->fd);
if (!context->decoder) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
return -1;
}
state->state = J2K_STATE_DECODING;
}
return ImagingIncrementalCodecPushBuffer(context->decoder, buf, bytes);
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
int
ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context;
if (context->error_msg)
free ((void *)context->error_msg);
if (context->decoder)
ImagingIncrementalCodecDestroy(context->decoder);
return -1;
}
const char *
ImagingJpeg2KVersion(void)
{
return opj_version();
}
#endif /* HAVE_OPENJPEG */
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

562
libImaging/Jpeg2KEncode.c Normal file
View File

@ -0,0 +1,562 @@
/*
* The Python Imaging Library.
* $Id$
*
* decoder for JPEG2000 image data.
*
* history:
* 2014-03-12 ajh Created
*
* Copyright (c) 2014 Coriolis Systems Limited
* Copyright (c) 2014 Alastair Houghton
*
* See the README file for details on usage and redistribution.
*/
#include "Imaging.h"
#ifdef HAVE_OPENJPEG
#include "Jpeg2K.h"
#define CINEMA_24_CS_LENGTH 1302083
#define CINEMA_48_CS_LENGTH 651041
#define COMP_24_CS_MAX_LENGTH 1041666
#define COMP_48_CS_MAX_LENGTH 520833
/* -------------------------------------------------------------------- */
/* Error handler */
/* -------------------------------------------------------------------- */
static void
j2k_error(const char *msg, void *client_data)
{
JPEG2KENCODESTATE *state = (JPEG2KENCODESTATE *) client_data;
free((void *)state->error_msg);
state->error_msg = strdup(msg);
}
/* -------------------------------------------------------------------- */
/* Buffer output stream */
/* -------------------------------------------------------------------- */
static OPJ_SIZE_T
j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
size_t len = ImagingIncrementalCodecWrite(encoder, p_buffer, p_nb_bytes);
return len ? len : (OPJ_SIZE_T)-1;
}
static OPJ_OFF_T
j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
off_t pos = ImagingIncrementalCodecSkip(encoder, p_nb_bytes);
return pos ? pos : (OPJ_OFF_T)-1;
}
static OPJ_BOOL
j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data;
off_t pos = ImagingIncrementalCodecSeek(encoder, p_nb_bytes);
return pos == p_nb_bytes;
}
/* -------------------------------------------------------------------- */
/* Encoder */
/* -------------------------------------------------------------------- */
typedef void (*j2k_pack_tile_t)(Imaging im, UINT8 *buf,
unsigned x0, unsigned y0,
unsigned w, unsigned h);
static void
j2k_pack_l(Imaging im, UINT8 *buf,
unsigned x0, unsigned y0, unsigned w, unsigned h)
{
UINT8 *ptr = buf;
unsigned x,y;
for (y = 0; y < h; ++y) {
UINT8 *data = (UINT8 *)(im->image[y + y0] + x0);
for (x = 0; x < w; ++x)
*ptr++ = *data++;
}
}
static void
j2k_pack_la(Imaging im, UINT8 *buf,
unsigned x0, unsigned y0, unsigned w, unsigned h)
{
UINT8 *ptr = buf;
UINT8 *ptra = buf + w * h;
unsigned x,y;
for (y = 0; y < h; ++y) {
UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
for (x = 0; x < w; ++x) {
*ptr++ = data[0];
*ptra++ = data[3];
data += 4;
}
}
}
static void
j2k_pack_rgb(Imaging im, UINT8 *buf,
unsigned x0, unsigned y0, unsigned w, unsigned h)
{
UINT8 *pr = buf;
UINT8 *pg = pr + w * h;
UINT8 *pb = pg + w * h;
unsigned x,y;
for (y = 0; y < h; ++y) {
UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
for (x = 0; x < w; ++x) {
*pr++ = data[0];
*pg++ = data[1];
*pb++ = data[2];
data += 4;
}
}
}
static void
j2k_pack_rgba(Imaging im, UINT8 *buf,
unsigned x0, unsigned y0, unsigned w, unsigned h)
{
UINT8 *pr = buf;
UINT8 *pg = pr + w * h;
UINT8 *pb = pg + w * h;
UINT8 *pa = pb + w * h;
unsigned x,y;
for (y = 0; y < h; ++y) {
UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0);
for (x = 0; x < w; ++x) {
*pr++ = *data++;
*pg++ = *data++;
*pb++ = *data++;
*pa++ = *data++;
}
}
}
enum {
J2K_STATE_START = 0,
J2K_STATE_ENCODING = 1,
J2K_STATE_DONE = 2,
J2K_STATE_FAILED = 3,
};
static void
j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params)
{
float rate;
unsigned n;
/* These settings have been copied from opj_compress in the OpenJPEG
sources. */
params->tile_size_on = OPJ_FALSE;
params->cp_tdx = params->cp_tdy = 1;
params->tp_flag = 'C';
params->tp_on = 1;
params->cp_tx0 = params->cp_ty0 = 0;
params->image_offset_x0 = params->image_offset_y0 = 0;
params->cblockw_init = 32;
params->cblockh_init = 32;
params->csty |= 0x01;
params->prog_order = OPJ_CPRL;
params->roi_compno = -1;
params->subsampling_dx = params->subsampling_dy = 1;
params->irreversible = 1;
if (params->cp_cinema == OPJ_CINEMA4K_24) {
float max_rate = ((float)(components * im->xsize * im->ysize * 8)
/ (CINEMA_24_CS_LENGTH * 8));
params->POC[0].tile = 1;
params->POC[0].resno0 = 0;
params->POC[0].compno0 = 0;
params->POC[0].layno1 = 1;
params->POC[0].resno1 = params->numresolution - 1;
params->POC[0].compno1 = 3;
params->POC[0].prg1 = OPJ_CPRL;
params->POC[1].tile = 1;
params->POC[1].resno0 = 0;
params->POC[1].compno0 = 0;
params->POC[1].layno1 = 1;
params->POC[1].resno1 = params->numresolution - 1;
params->POC[1].compno1 = 3;
params->POC[1].prg1 = OPJ_CPRL;
params->numpocs = 2;
for (n = 0; n < params->tcp_numlayers; ++n) {
rate = 0;
if (params->tcp_rates[0] == 0) {
params->tcp_rates[n] = max_rate;
} else {
rate = ((float)(components * im->xsize * im->ysize * 8)
/ (params->tcp_rates[n] * 8));
if (rate > CINEMA_24_CS_LENGTH)
params->tcp_rates[n] = max_rate;
}
}
params->max_comp_size = COMP_24_CS_MAX_LENGTH;
} else {
float max_rate = ((float)(components * im->xsize * im->ysize * 8)
/ (CINEMA_48_CS_LENGTH * 8));
for (n = 0; n < params->tcp_numlayers; ++n) {
rate = 0;
if (params->tcp_rates[0] == 0) {
params->tcp_rates[n] = max_rate;
} else {
rate = ((float)(components * im->xsize * im->ysize * 8)
/ (params->tcp_rates[n] * 8));
if (rate > CINEMA_48_CS_LENGTH)
params->tcp_rates[n] = max_rate;
}
}
params->max_comp_size = COMP_48_CS_MAX_LENGTH;
}
}
static int
j2k_encode_entry(Imaging im, ImagingCodecState state,
ImagingIncrementalCodec encoder)
{
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
opj_stream_t *stream = NULL;
opj_image_t *image = NULL;
opj_codec_t *codec = NULL;
opj_cparameters_t params;
unsigned components;
OPJ_COLOR_SPACE color_space;
opj_image_cmptparm_t image_params[4];
unsigned xsiz, ysiz;
unsigned tile_width, tile_height;
unsigned tiles_x, tiles_y, num_tiles;
unsigned x, y, tile_ndx;
unsigned n;
j2k_pack_tile_t pack;
int ret = -1;
stream = opj_stream_default_create(OPJ_FALSE);
if (!stream) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_stream_set_write_function(stream, j2k_write);
opj_stream_set_skip_function(stream, j2k_skip);
opj_stream_set_seek_function(stream, j2k_seek);
opj_stream_set_user_data(stream, encoder);
/* Setup an opj_image */
if (strcmp (im->mode, "L") == 0) {
components = 1;
color_space = OPJ_CLRSPC_GRAY;
pack = j2k_pack_l;
} else if (strcmp (im->mode, "LA") == 0) {
components = 2;
color_space = OPJ_CLRSPC_GRAY;
pack = j2k_pack_la;
} else if (strcmp (im->mode, "RGB") == 0) {
components = 3;
color_space = OPJ_CLRSPC_SRGB;
pack = j2k_pack_rgb;
} else if (strcmp (im->mode, "YCbCr") == 0) {
components = 3;
color_space = OPJ_CLRSPC_SYCC;
pack = j2k_pack_rgb;
} else if (strcmp (im->mode, "RGBA") == 0) {
components = 4;
color_space = OPJ_CLRSPC_SRGB;
pack = j2k_pack_rgba;
} else {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
for (n = 0; n < components; ++n) {
image_params[n].dx = image_params[n].dy = 1;
image_params[n].w = im->xsize;
image_params[n].h = im->ysize;
image_params[n].x0 = image_params[n].y0 = 0;
image_params[n].prec = 8;
image_params[n].bpp = 8;
image_params[n].sgnd = 0;
}
image = opj_image_create(components, image_params, color_space);
/* Setup compression context */
context->error_msg = NULL;
opj_set_default_encoder_parameters(&params);
params.image_offset_x0 = context->offset_x;
params.image_offset_y0 = context->offset_y;
if (context->tile_size_x && context->tile_size_y) {
params.tile_size_on = OPJ_TRUE;
params.cp_tx0 = context->tile_offset_x;
params.cp_ty0 = context->tile_offset_y;
params.cp_tdx = context->tile_size_x;
params.cp_tdy = context->tile_size_y;
tile_width = params.cp_tdx;
tile_height = params.cp_tdy;
} else {
params.cp_tx0 = 0;
params.cp_ty0 = 0;
params.cp_tdx = 1;
params.cp_tdy = 1;
tile_width = im->xsize;
tile_height = im->ysize;
}
if (context->quality_layers && PySequence_Check(context->quality_layers)) {
Py_ssize_t len = PySequence_Length(context->quality_layers);
Py_ssize_t n;
float *pq;
if (len) {
if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0]))
len = sizeof(params.tcp_rates)/sizeof(params.tcp_rates[0]);
params.tcp_numlayers = (int)len;
if (context->quality_is_in_db) {
params.cp_disto_alloc = params.cp_fixed_alloc = 0;
params.cp_fixed_quality = 1;
pq = params.tcp_distoratio;
} else {
params.cp_disto_alloc = 1;
params.cp_fixed_alloc = params.cp_fixed_quality = 0;
pq = params.tcp_rates;
}
for (n = 0; n < len; ++n) {
PyObject *obj = PySequence_ITEM(context->quality_layers, n);
pq[n] = PyFloat_AsDouble(obj);
}
}
} else {
params.tcp_numlayers = 1;
params.tcp_rates[0] = 0;
params.cp_disto_alloc = 1;
}
if (context->num_resolutions)
params.numresolution = context->num_resolutions;
if (context->cblk_width >= 4 && context->cblk_width <= 1024
&& context->cblk_height >= 4 && context->cblk_height <= 1024
&& context->cblk_width * context->cblk_height <= 4096) {
params.cblockw_init = context->cblk_width;
params.cblockh_init = context->cblk_height;
}
if (context->precinct_width >= 4 && context->precinct_height >= 4
&& context->precinct_width >= context->cblk_width
&& context->precinct_height > context->cblk_height) {
params.prcw_init[0] = context->precinct_width;
params.prch_init[0] = context->precinct_height;
params.res_spec = 1;
params.csty |= 0x01;
}
params.irreversible = context->irreversible;
params.prog_order = context->progression;
params.cp_cinema = context->cinema_mode;
switch (params.cp_cinema) {
case OPJ_OFF:
params.cp_rsiz = OPJ_STD_RSIZ;
break;
case OPJ_CINEMA2K_24:
case OPJ_CINEMA2K_48:
params.cp_rsiz = OPJ_CINEMA2K;
if (params.numresolution > 6)
params.numresolution = 6;
break;
case OPJ_CINEMA4K_24:
params.cp_rsiz = OPJ_CINEMA4K;
if (params.numresolution > 7)
params.numresolution = 7;
break;
}
if (context->cinema_mode != OPJ_OFF)
j2k_set_cinema_params(im, components, &params);
/* Set up the reference grid in the image */
image->x0 = params.image_offset_x0;
image->y0 = params.image_offset_y0;
image->x1 = xsiz = im->xsize + params.image_offset_x0;
image->y1 = ysiz = im->ysize + params.image_offset_y0;
/* Create the compressor */
codec = opj_create_compress(context->format);
if (!codec) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
opj_set_error_handler(codec, j2k_error, context);
opj_setup_encoder(codec, &params, image);
/* Start encoding */
if (!opj_start_compress(codec, image, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
/* Write each tile */
tiles_x = (im->xsize + (params.image_offset_x0 - params.cp_tx0)
+ tile_width - 1) / tile_width;
tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0)
+ tile_height - 1) / tile_height;
num_tiles = tiles_x * tiles_y;
state->buffer = malloc (tile_width * tile_height * components);
tile_ndx = 0;
for (y = 0; y < tiles_y; ++y) {
unsigned ty0 = params.cp_ty0 + y * tile_height;
unsigned ty1 = ty0 + tile_height;
unsigned pixy, pixh;
if (ty0 < params.image_offset_y0)
ty0 = params.image_offset_y0;
if (ty1 > ysiz)
ty1 = ysiz;
pixy = ty0 - params.image_offset_y0;
pixh = ty1 - ty0;
for (x = 0; x < tiles_x; ++x) {
unsigned tx0 = params.cp_tx0 + x * tile_width;
unsigned tx1 = tx0 + tile_width;
unsigned pixx, pixw;
unsigned data_size;
if (tx0 < params.image_offset_x0)
tx0 = params.image_offset_x0;
if (tx1 > xsiz)
tx1 = xsiz;
pixx = tx0 - params.image_offset_x0;
pixw = tx1 - tx0;
pack(im, state->buffer, pixx, pixy, pixw, pixh);
data_size = pixw * pixh * components;
if (!opj_write_tile(codec, tile_ndx++, state->buffer,
data_size, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
}
}
if (!opj_end_compress(codec, stream)) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
goto quick_exit;
}
state->errcode = IMAGING_CODEC_END;
state->state = J2K_STATE_DONE;
ret = (int)ImagingIncrementalCodecBytesInBuffer(encoder);
quick_exit:
if (codec)
opj_destroy_codec(codec);
if (image)
opj_image_destroy(image);
if (stream)
opj_stream_destroy(stream);
return ret;
}
int
ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes)
{
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
if (state->state == J2K_STATE_FAILED)
return -1;
if (state->state == J2K_STATE_START) {
int seekable = (context->format != OPJ_CODEC_J2K
? INCREMENTAL_CODEC_SEEKABLE
: INCREMENTAL_CODEC_NOT_SEEKABLE);
context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry,
im, state,
INCREMENTAL_CODEC_WRITE,
seekable,
context->fd);
if (!context->encoder) {
state->errcode = IMAGING_CODEC_BROKEN;
state->state = J2K_STATE_FAILED;
return -1;
}
state->state = J2K_STATE_ENCODING;
}
return ImagingIncrementalCodecPushBuffer(context->encoder, buf, bytes);
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
int
ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
if (context->quality_layers)
Py_DECREF(context->quality_layers);
if (context->error_msg)
free ((void *)context->error_msg);
if (context->encoder)
ImagingIncrementalCodecDestroy(context->encoder);
return -1;
}
#endif /* HAVE_OPENJPEG */
/*
* Local Variables:
* c-basic-offset: 4
* End:
*
*/

View File

@ -192,6 +192,7 @@ if __name__ == "__main__":
check_module("PIL CORE", "PIL._imaging") check_module("PIL CORE", "PIL._imaging")
check_module("TKINTER", "PIL._imagingtk") check_module("TKINTER", "PIL._imagingtk")
check_codec("JPEG", "jpeg") check_codec("JPEG", "jpeg")
check_codec("JPEG 2000", "jpeg2k")
check_codec("ZLIB (PNG/ZIP)", "zip") check_codec("ZLIB (PNG/ZIP)", "zip")
check_codec("LIBTIFF", "libtiff") check_codec("LIBTIFF", "libtiff")
check_module("FREETYPE2", "PIL._imagingft") check_module("FREETYPE2", "PIL._imagingft")

View File

@ -33,7 +33,8 @@ _LIB_IMAGING = (
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode") "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
"Jpeg2KDecode", "Jpeg2KEncode")
def _add_directory(path, dir, where=None): def _add_directory(path, dir, where=None):
@ -80,7 +81,8 @@ def _read(file):
try: try:
import _tkinter import _tkinter
except ImportError: except (ImportError, OSError):
# pypy emits an oserror
_tkinter = None _tkinter = None
@ -88,6 +90,7 @@ NAME = 'Pillow'
VERSION = '2.3.0' VERSION = '2.3.0'
TCL_ROOT = None TCL_ROOT = None
JPEG_ROOT = None JPEG_ROOT = None
JPEG2K_ROOT = None
ZLIB_ROOT = None ZLIB_ROOT = None
TIFF_ROOT = None TIFF_ROOT = None
FREETYPE_ROOT = None FREETYPE_ROOT = None
@ -98,6 +101,7 @@ class pil_build_ext(build_ext):
class feature: class feature:
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
jpeg2000 = None
required = [] required = []
def require(self, feat): def require(self, feat):
@ -150,7 +154,7 @@ class pil_build_ext(build_ext):
# #
# add configured kits # add configured kits
for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT, for root in (TCL_ROOT, JPEG_ROOT, JPEG2K_ROOT, TIFF_ROOT, ZLIB_ROOT,
FREETYPE_ROOT, LCMS_ROOT): FREETYPE_ROOT, LCMS_ROOT):
if isinstance(root, type(())): if isinstance(root, type(())):
lib_root, include_root = root lib_root, include_root = root
@ -321,6 +325,16 @@ class pil_build_ext(build_ext):
_add_directory(library_dirs, "/usr/lib") _add_directory(library_dirs, "/usr/lib")
_add_directory(include_dirs, "/usr/include") _add_directory(include_dirs, "/usr/include")
# on Windows, look for the OpenJPEG libraries in the location that
# the official installed puts them
if sys.platform == "win32":
_add_directory(library_dirs,
os.path.join(os.environ.get("ProgramFiles", ""),
"OpenJPEG 2.0", "lib"))
_add_directory(include_dirs,
os.path.join(os.environ.get("ProgramFiles", ""),
"OpenJPEG 2.0", "include"))
# #
# insert new dirs *before* default libs, to avoid conflicts # insert new dirs *before* default libs, to avoid conflicts
# between Python PYD stub libs and real libraries # between Python PYD stub libs and real libraries
@ -349,6 +363,11 @@ class pil_build_ext(build_ext):
_find_library_file(self, "libjpeg")): _find_library_file(self, "libjpeg")):
feature.jpeg = "libjpeg" # alternative name feature.jpeg = "libjpeg" # alternative name
if feature.want('jpeg2000'):
if _find_include_file(self, "openjpeg-2.0/openjpeg.h"):
if _find_library_file(self, "openjp2"):
feature.jpeg2000 = "openjp2"
if feature.want('tiff'): if feature.want('tiff'):
if _find_library_file(self, "tiff"): if _find_library_file(self, "tiff"):
feature.tiff = "tiff" feature.tiff = "tiff"
@ -430,6 +449,11 @@ class pil_build_ext(build_ext):
if feature.jpeg: if feature.jpeg:
libs.append(feature.jpeg) libs.append(feature.jpeg)
defs.append(("HAVE_LIBJPEG", None)) defs.append(("HAVE_LIBJPEG", None))
if feature.jpeg2000:
libs.append(feature.jpeg2000)
defs.append(("HAVE_OPENJPEG", None))
if sys.platform == "win32":
defs.append(("OPJ_STATIC", None))
if feature.zlib: if feature.zlib:
libs.append(feature.zlib) libs.append(feature.zlib)
defs.append(("HAVE_LIBZ", None)) defs.append(("HAVE_LIBZ", None))
@ -537,6 +561,7 @@ class pil_build_ext(build_ext):
options = [ options = [
(feature.tcl and feature.tk, "TKINTER"), (feature.tcl and feature.tk, "TKINTER"),
(feature.jpeg, "JPEG"), (feature.jpeg, "JPEG"),
(feature.jpeg2000, "OPENJPEG (JPEG2000)"),
(feature.zlib, "ZLIB (PNG/ZIP)"), (feature.zlib, "ZLIB (PNG/ZIP)"),
(feature.tiff, "LIBTIFF"), (feature.tiff, "LIBTIFF"),
(feature.freetype, "FREETYPE2"), (feature.freetype, "FREETYPE2"),