Merge pull request #98 from wiredfool/g4_support

G4 Tiff support
This commit is contained in:
Alex Clark ☺ 2013-03-13 15:41:33 -07:00
commit 62bb9aaaa2
15 changed files with 998 additions and 21 deletions

View File

@ -50,6 +50,7 @@ from PIL import _binary
import array, sys
import collections
import itertools
import os
II = b"II" # little-endian (intel-style)
MM = b"MM" # big-endian (motorola-style)
@ -121,6 +122,8 @@ COMPRESSION_INFO = {
32773: "packbits"
}
COMPRESSION_INFO_REV = dict([(v,k) for (k,v) in COMPRESSION_INFO.items()])
OPEN_INFO = {
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
# ExtraSamples) => mode, rawmode
@ -532,7 +535,12 @@ class TiffImageFile(ImageFile.ImageFile):
self.__frame = -1
self.__fp = self.fp
# and load the first frame
if Image.DEBUG:
print ("*** TiffImageFile._open ***")
print ("- __first:", self.__first)
print ("- ifh: ", ifh)
# and load the first frame
self._seek(0)
def seek(self, frame):
@ -567,7 +575,7 @@ class TiffImageFile(ImageFile.ImageFile):
return self.__frame
def _decoder(self, rawmode, layer):
def _decoder(self, rawmode, layer, tile=None):
"Setup decoder contexts"
args = None
@ -594,6 +602,63 @@ class TiffImageFile(ImageFile.ImageFile):
return args
def _load_libtiff(self):
""" Overload method triggered when we detect a g3/g4 tiff
Calls out to lib tiff """
pixel = Image.Image.load(self)
if self.tile is None:
raise IOError("cannot load this image")
if not self.tile:
return pixel
self.load_prepare()
if not len(self.tile) == 1:
raise IOError("Not exactly one tile")
d, e, o, a = self.tile[0]
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
try:
d.setimage(self.im, e)
except ValueError:
raise IOError("Couldn't set the image")
if hasattr(self.fp, "fileno"):
# we've got a actual file on disk, pass in the fp.
if Image.DEBUG:
print ("have fileno, calling fileno version of the decoder.")
self.fp.seek(0)
n,e = d.decode("fpfp") # 4 bytes, otherwise the trace might error out
elif hasattr(self.fp, "getvalue"):
# We've got a stringio like thing passed in. Yay for all in memory.
# The decoder needs the entire file in one shot, so there's not
# a lot we can do here other than give it the entire file.
# unless we could do something like get the address of the underlying
# string for stringio.
if Image.DEBUG:
print ("have getvalue. just sending in a string from getvalue")
n,e = d.decode(self.fp.getvalue())
else:
# we have something else.
if Image.DEBUG:
print ("don't have fileno or getvalue. just reading")
# UNDONE -- so much for that buffer size thing.
n, e = d.decode(self.fp.read())
self.tile = []
self.readonly = 0
self.fp = None # might be shared
if e < 0:
raise IOError(e)
self.load_end()
return Image.Image.load(self)
def _setup(self):
"Setup this image object based on current tags"
@ -669,20 +734,54 @@ class TiffImageFile(ImageFile.ImageFile):
self.tile = []
if STRIPOFFSETS in self.tag:
# striped image
offsets = self.tag[STRIPOFFSETS]
h = getscalar(ROWSPERSTRIP, ysize)
w = self.size[0]
a = None
for o in self.tag[STRIPOFFSETS]:
if not a:
a = self._decoder(rawmode, l)
if self._compression in ["tiff_ccitt", "group3",
"group4", "tiff_raw_16"]:
## if Image.DEBUG:
## print "Activating g4 compression for whole file"
# Decoder expects entire file as one tile.
# There's a buffer size limit in load (64k)
# so large g4 images will fail if we use that
# function.
#
# Setup the one tile for the whole image, then
# replace the existing load function with our
# _load_libtiff function.
self.load = self._load_libtiff
# To be nice on memory footprint, if there's a
# file descriptor, use that instead of reading
# into a string in python.
# libtiff closes the file descriptor, so pass in a dup.
fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
# Offset in the tile tuple is 0, we go from 0,0 to
# w,h, and we only do this once -- eds
a = (rawmode, self._compression, fp )
self.tile.append(
(self._compression,
(0, min(y, ysize), w, min(y+h, ysize)),
o, a))
y = y + h
if y >= self.size[1]:
x = y = 0
l = l + 1
(0, 0, w, ysize),
0, a))
a = None
else:
for i in range(len(offsets)):
a = self._decoder(rawmode, l, i)
self.tile.append(
(self._compression,
(0, min(y, ysize), w, min(y+h, ysize)),
offsets[i], a))
if Image.DEBUG:
print ("tiles: ", self.tile)
y = y + h
if y >= self.size[1]:
x = y = 0
l = l + 1
a = None
elif TILEOFFSETS in self.tag:
# tiled image
@ -764,8 +863,12 @@ def _save(im, fp, filename):
ifd = ImageFileDirectory(prefix)
compression = im.info.get('compression','raw')
libtiff = compression in ["tiff_ccitt", "group3",
"group4", "tiff_raw_16"]
# -- multi-page -- skip TIFF header on subsequent pages
if fp.tell() == 0:
if not libtiff and fp.tell() == 0:
# tiff header (write via IFD to get everything right)
# PIL always starts the first IFD at offset 8
fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))
@ -842,13 +945,65 @@ def _save(im, fp, filename):
ifd[ROWSPERSTRIP] = im.size[1]
ifd[STRIPBYTECOUNTS] = stride * im.size[1]
ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
ifd[COMPRESSION] = 1 # no compression
ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default
offset = ifd.save(fp)
if libtiff:
if Image.DEBUG:
print ("Saving using libtiff encoder")
print (ifd.items())
_fp = 0
if hasattr(fp, "fileno"):
fp.seek(0)
_fp = os.dup(fp.fileno())
ImageFile._save(im, fp, [
("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
])
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
atts = dict([(k,v) for (k,(v,)) in ifd.items() if k not in blocklist])
try:
# pull in more bits from the original file, e.g x,y resolution
# so that we can save(load('')) == original file.
for k,v in im.ifd.items():
if k not in atts and k not in blocklist:
if type(v[0]) == tuple and len(v) > 1:
# A tuple of more than one rational tuples
# flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
atts[k] = [float(elt[0])/float(elt[1]) for elt in v]
continue
if type(v[0]) == tuple and len(v) == 1:
# A tuple of one rational tuples
# flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL
atts[k] = float(v[0][0])/float(v[0][1])
continue
if type(v) == tuple and len(v) == 1:
# int or similar
atts[k] = v[0]
continue
if type(v) == str:
atts[k] = v
continue
except:
# if we don't have an ifd here, just punt.
pass
if Image.DEBUG:
print (atts)
a = (rawmode, compression, _fp, filename, atts)
e = Image._getencoder(im.mode, compression, a, im.encoderconfig)
e.setimage(im.im, (0,0)+im.size)
while 1:
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
if not _fp:
fp.write(d)
if s:
break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
else:
offset = ifd.save(fp)
ImageFile._save(im, fp, [
("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
])
# -- helper for multi-page save --

BIN
Tests/images/lena_bw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
Tests/images/lena_g4.tif Normal file

Binary file not shown.

Binary file not shown.

BIN
Tests/images/pport_g4.tif Normal file

Binary file not shown.

View File

@ -2,6 +2,9 @@ from tester import *
from PIL import Image
import StringIO
import random
def test_sanity():
file = tempfile("temp.tif")
@ -55,3 +58,98 @@ def test_gimp_tiff():
('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')),
])
assert_no_exception(lambda: im.load())
def _assert_noerr(im):
"""Helper tests that assert basic sanity about the g4 tiff reading"""
#1 bit
assert_equal(im.mode, "1")
# Does the data actually load
assert_no_exception(lambda: im.load())
assert_no_exception(lambda: im.getdata())
try:
assert_equal(im._compression, 'group4')
except:
print "No _compression"
print (dir(im))
# can we write it back out, in a different form.
out = tempfile("temp.png")
assert_no_exception(lambda: im.save(out))
def test_g4_tiff():
"""Test the ordinary file path load path"""
file = "Tests/images/lena_g4_500.tif"
im = Image.open(file)
assert_equal(im.size, (500,500))
_assert_noerr(im)
def test_g4_large():
file = "Tests/images/pport_g4.tif"
im = Image.open(file)
_assert_noerr(im)
def test_g4_tiff_file():
"""Testing the string load path"""
file = "Tests/images/lena_g4_500.tif"
with open(file,'rb') as f:
im = Image.open(f)
assert_equal(im.size, (500,500))
_assert_noerr(im)
def test_g4_tiff_stringio():
"""Testing the stringio loading code path"""
file = "Tests/images/lena_g4_500.tif"
s = StringIO.StringIO()
with open(file,'rb') as f:
s.write(f.read())
s.seek(0)
im = Image.open(s)
assert_equal(im.size, (500,500))
_assert_noerr(im)
def test_g4_tiff_fail(): # UNDONE fails badly, unknown reason
"""The 128x128 lena image fails for some reason. Investigating"""
Image.DEBUG = True
file = "Tests/images/lena_g4.tif"
im = Image.open(file)
assert_equal(im.size, (128,128))
_assert_noerr(im)
Image.DEBUG = False
def test_g4_eq_png():
""" Checking that we're actually getting the data that we expect"""
png = Image.open('Tests/images/lena_bw_500.png')
g4 = Image.open('Tests/images/lena_g4_500.tif')
assert_image_equal(g4, png)
def test_g4_write():
"""Checking to see that the saved image is the same as what we wrote"""
Image.DEBUG = True
file = "Tests/images/lena_g4_500.tif"
orig = Image.open(file)
out = "temp.tif"
rot = orig.transpose(Image.ROTATE_90)
assert_equal(rot.size,(500,500))
rot.save(out)
reread = Image.open(out)
assert_equal(reread.size,(500,500))
_assert_noerr(reread)
assert_image_equal(reread, rot)
assert_false(orig.tobytes() == reread.tobytes())
Image.DEBUG = False

View File

@ -3236,6 +3236,7 @@ extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
@ -3254,6 +3255,7 @@ extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args);
/* Display support etc (in display.c) */
#ifdef WIN32
@ -3303,6 +3305,17 @@ static PyMethodDef functions[] = {
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
#endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
#ifdef HAVE_LIBTIFF
{"tiff_ccitt_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"group3_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"group4_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_raw_16_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_ccitt_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"group3_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"group4_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_raw_16_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
#endif
{"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
{"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},

View File

@ -388,6 +388,75 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args)
return (PyObject*) decoder;
}
/* -------------------------------------------------------------------- */
/* LibTiff */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBTIFF
#include "Tiff.h"
#include <string.h>
#ifdef __WIN32__
#define strcasecmp(s1, s2) stricmp(s1, s2)
#endif
PyObject*
PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
{
ImagingDecoderObject* decoder;
char* mode;
char* rawmode;
char* compname;
int compression;
int fp;
if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp))
return NULL;
TRACE(("new tiff decoder %s\n", compname));
/* UNDONE -- we can probably do almost any arbitrary compression here,
* since we're effective passing in the whole file in one shot and
* getting back the data row by row. V2 maybe
*/
if (strcasecmp(compname, "tiff_ccitt") == 0) {
compression = COMPRESSION_CCITTRLE;
} else if (strcasecmp(compname, "group3") == 0) {
compression = COMPRESSION_CCITTFAX3;
} else if (strcasecmp(compname, "group4") == 0) {
compression = COMPRESSION_CCITTFAX4;
} else if (strcasecmp(compname, "tiff_raw_16") == 0) {
compression = COMPRESSION_CCITTRLEW;
} else {
PyErr_SetString(PyExc_ValueError, "unknown compession");
return NULL;
}
decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE));
if (decoder == NULL)
return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffInit(&decoder->state, compression, fp)) {
Py_DECREF(decoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;
}
decoder->decode = ImagingLibTiffDecode;
return (PyObject*) decoder;
}
#endif
/* -------------------------------------------------------------------- */
/* MSP */

151
encode.c
View File

@ -650,3 +650,154 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
}
#endif
/* -------------------------------------------------------------------- */
/* LibTiff */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBTIFF
#include "Tiff.h"
#include <string.h>
#ifdef __WIN32__
#define strcasecmp(s1, s2) stricmp(s1, s2)
#endif
PyObject*
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
{
ImagingEncoderObject* encoder;
char* mode;
char* rawmode;
char* compname;
char* filename;
int compression;
int fp;
PyObject *dir;
PyObject *key, *value;
Py_ssize_t pos = 0;
int status;
Py_ssize_t d_size;
PyObject *keys, *values;
if (! PyArg_ParseTuple(args, "sssisO", &mode, &rawmode, &compname, &fp, &filename, &dir)) {
return NULL;
}
if (!PyDict_Check(dir)) {
PyErr_SetString(PyExc_ValueError, "Invalid Dictionary");
return NULL;
} else {
d_size = PyDict_Size(dir);
TRACE(("dict size: %d\n", (int)d_size));
keys = PyDict_Keys(dir);
values = PyDict_Values(dir);
for (pos=0;pos<d_size;pos++){
TRACE((" key: %d\n", (int)PyInt_AsLong(PyList_GetItem(keys,pos))));
}
pos = 0;
}
TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename));
/* UNDONE -- we can probably do almost any arbitrary compression here,
* so long as we're doing row/stripe based actions and not tiles.
*/
if (strcasecmp(compname, "tiff_ccitt") == 0) {
compression = COMPRESSION_CCITTRLE;
} else if (strcasecmp(compname, "group3") == 0) {
compression = COMPRESSION_CCITTFAX3;
} else if (strcasecmp(compname, "group4") == 0) {
compression = COMPRESSION_CCITTFAX4;
} else if (strcasecmp(compname, "tiff_raw_16") == 0) {
compression = COMPRESSION_CCITTRLEW;
} else {
PyErr_SetString(PyExc_ValueError, "unknown compession");
return NULL;
}
TRACE(("Found compression: %d\n", compression));
encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE));
if (encoder == NULL)
return NULL;
if (get_packer(encoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffEncodeInit(&encoder->state, filename, fp)) {
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;
}
// While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t
// while (PyDict_Next(dir, &pos, &key, &value)) {
for (pos=0;pos<d_size;pos++){
key = PyList_GetItem(keys,pos);
value = PyList_GetItem(values,pos);
status = 0;
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key)));
if (PyInt_Check(value)) {
TRACE(("Setting from Int: %d %ld \n", (int)PyInt_AsLong(key),PyInt_AsLong(value)));
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
PyInt_AsLong(value));
} else if(PyBytes_Check(value)) {
TRACE(("Setting from String: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value)));
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
PyBytes_AsString(value));
} else if(PyList_Check(value)) {
int len,i;
float *floatav;
TRACE(("Setting from List: %d \n", (int)PyInt_AsLong(key)));
len = (int)PyList_Size(value);
TRACE((" %d elements, setting as floats \n", len));
floatav = malloc(sizeof(float)*len);
if (floatav) {
for (i=0;i<len;i++) {
floatav[i] = (float)PyFloat_AsDouble(PyList_GetItem(value,i));
}
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
floatav);
free(floatav);
}
} else if (PyFloat_Check(value)) {
TRACE(("Setting from String: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value)));
status = ImagingLibTiffSetField(&encoder->state,
(ttag_t) PyInt_AsLong(key),
(float)PyFloat_AsDouble(value));
} else {
TRACE(("Unhandled type for key %d : %s ",
(int)PyInt_AsLong(key),
PyBytes_AsString(PyObject_Str(value))));
}
if (!status) {
TRACE(("Error setting Field\n"));
Py_DECREF(encoder);
PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary");
return NULL;
}
}
encoder->encode = ImagingLibTiffEncode;
return (PyObject*) encoder;
}
#endif

View File

@ -420,6 +420,12 @@ extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
#endif
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
#ifdef HAVE_LIBTIFF
extern int ImagingLibTiffDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
extern int ImagingLibTiffEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
#endif
#ifdef HAVE_LIBMPEG
extern int ImagingMpegDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);

57
libImaging/Tiff.h Normal file
View File

@ -0,0 +1,57 @@
/*
* The Python Imaging Library.
* $Id: //modules/pil/libImaging/Tiff.h#1 $
*
* declarations for the LibTiff-based Group3 and Group4 decoder
*
*/
#ifndef _TIFFIO_
#include <tiffio.h>
#endif
#ifndef _TIFF_
#include <tiff.h>
#endif
#ifndef min
#define min(x,y) (( x > y ) ? y : x )
#define max(x,y) (( x < y ) ? y : x )
#endif
#ifndef _PIL_LIBTIFF_
#define _PIL_LIBTIFF_
typedef struct {
tdata_t data; /* tdata_t == void* */
toff_t loc; /* toff_t == uint32 */
tsize_t size; /* tsize_t == int32 */
int fp;
TIFF *tiff; /* Used in write */
toff_t eof;
int flrealloc; /* may we realloc */
} TIFFSTATE;
extern int ImagingLibTiffInit(ImagingCodecState state, int compression, int fp);
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
#if defined(_MSC_VER) && (_MSC_VER == 1310)
/* VS2003/py2.4 can't use varargs. Skipping trace for now.*/
#define TRACE(args)
#else
#define VA_ARGS(...) __VA_ARGS__
#define TRACE(args) fprintf(stderr, VA_ARGS args)
/*
#define TRACE(args)
*/
#endif
#endif

417
libImaging/TiffDecode.c Normal file
View File

@ -0,0 +1,417 @@
/*
* The Python Imaging Library.
* $Id: //modules/pil/libImaging/TiffDecode.c#1 $
*
* LibTiff-based Group3 and Group4 decoder
*
*
* started modding to use non-private tiff functions to port to libtiff 4.x
* eds 3/12/12
*
*/
#include "Imaging.h"
#ifdef HAVE_LIBTIFF
#ifndef uint
#define uint uint32
#endif
#include "Tiff.h"
void dump_state(const TIFFSTATE *state){
TRACE(("State: Location %u size %d eof %d data: %p \n", (uint)state->loc,
(int)state->size, (uint)state->eof, state->data));
}
/*
procs for TIFFOpenClient
*/
tsize_t _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
tsize_t to_read;
TRACE(("_tiffReadProc: %d \n", (int)size));
dump_state(state);
to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc);
TRACE(("to_read: %d\n", (int)to_read));
_TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read);
state->loc += (toff_t)to_read;
TRACE( ("location: %u\n", (uint)state->loc));
return to_read;
}
tsize_t _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
tsize_t to_write;
TRACE(("_tiffWriteProc: %d \n", (int)size));
dump_state(state);
to_write = min(size, state->size - (tsize_t)state->loc);
if (state->flrealloc && size>to_write) {
tdata_t new;
tsize_t newsize=state->size;
while (newsize < (size + state->size)) {
newsize += 64*1024;
// newsize*=2; // UNDONE, by 64k chunks?
}
TRACE(("Reallocing in write to %d bytes\n", (int)newsize));
new = realloc(state->data, newsize);
if (!new) {
// fail out
return 0;
}
state->data = new;
state->size = newsize;
to_write = size;
}
TRACE(("to_write: %d\n", (int)to_write));
_TIFFmemcpy((UINT8 *)state->data + state->loc, buf, to_write);
state->loc += (toff_t)to_write;
state->eof = max(state->loc, state->eof);
dump_state(state);
return to_write;
}
toff_t _tiffSeekProc(thandle_t hdata, toff_t off, int whence) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
TRACE(("_tiffSeekProc: off: %u whence: %d \n", (uint)off, whence));
dump_state(state);
switch (whence) {
case 0:
state->loc = off;
break;
case 1:
state->loc += off;
break;
case 2:
state->loc = state->eof + off;
break;
}
dump_state(state);
return state->loc;
}
int _tiffCloseProc(thandle_t hdata) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
TRACE(("_tiffCloseProc \n"));
dump_state(state);
return 0;
}
toff_t _tiffSizeProc(thandle_t hdata) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
TRACE(("_tiffSizeProc \n"));
dump_state(state);
return (toff_t)state->size;
}
int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
TIFFSTATE *state = (TIFFSTATE *)hdata;
TRACE(("_tiffMapProc input size: %u, data: %p\n", (uint)*psize, *pbase));
dump_state(state);
*pbase = state->data;
*psize = state->size;
TRACE(("_tiffMapProc returning size: %u, data: %p\n", (uint)*psize, *pbase));
return (1);
}
int _tiffNullMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
(void) hdata; (void) pbase; (void) psize;
return (0);
}
void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
TRACE(("_tiffUnMapProc\n"));
(void) hdata; (void) base; (void) size;
}
int ImagingLibTiffInit(ImagingCodecState state, int compression, int fp) {
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
TRACE(("initing libtiff\n"));
TRACE(("Compression: %d, filepointer: %d \n", compression, fp));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("State: context %p \n", state->context));
clientstate->loc = 0;
clientstate->size = 0;
clientstate->data = 0;
clientstate->fp = fp;
clientstate->eof = 0;
return 1;
}
int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
char *filename = "tempfile.tif";
char *mode = "r";
TIFF *tiff;
int size;
/* buffer is the encoded file, bytes is the length of the encoded file */
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */
TRACE(("in decoder: bytes %d\n", bytes));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3]));
TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
im->mode, im->type, im->bands, im->xsize, im->ysize));
TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n",
im->image8, im->image32, im->image, im->block));
TRACE(("Image: pixelsize: %d, linesize %d \n",
im->pixelsize, im->linesize));
dump_state(clientstate);
clientstate->size = bytes;
clientstate->eof = clientstate->size;
clientstate->loc = 0;
clientstate->data = (tdata_t)buffer;
clientstate->flrealloc = 0;
dump_state(clientstate);
if (clientstate->fp) {
TRACE(("Opening using fd: %d\n",clientstate->fp));
tiff = TIFFFdOpen(clientstate->fp, filename, mode);
} else {
TRACE(("Opening from string\n"));
tiff = TIFFClientOpen(filename, mode,
(thandle_t) clientstate,
_tiffReadProc, _tiffWriteProc,
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
_tiffMapProc, _tiffUnmapProc);
}
if (!tiff){
TRACE(("Error, didn't get the tiff\n"));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
size = TIFFScanlineSize(tiff);
TRACE(("ScanlineSize: %d \n", size));
if (size > state->bytes) {
TRACE(("Error, scanline size > buffer size\n"));
state->errcode = IMAGING_CODEC_BROKEN;
TIFFClose(tiff);
return -1;
}
// Have to do this row by row and shove stuff into the buffer that way,
// with shuffle. (or, just alloc a buffer myself, then figure out how to get it
// back in. Can't use read encoded stripe.
// This thing pretty much requires that I have the whole image in one shot.
// Prehaps a stub version would work better???
while(state->y < state->ysize){
if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) {
TRACE(("Decode Error, row %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN;
TIFFClose(tiff);
return -1;
}
/* TRACE(("Decoded row %d \n", state->y)); */
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->buffer,
state->xsize);
state->y++;
}
TIFFClose(tiff);
TRACE(("Done Decoding, Returning \n"));
// Returning -1 here to force ImageFile.load to break, rather than
// even think about looping back around.
return -1;
}
int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
// Open the FD or the pointer as a tiff file, for writing.
// We may have to do some monkeying around to make this really work.
// If we have a fp, then we're good.
// If we have a memory string, we're probably going to have to malloc, then
// shuffle bytes into the writescanline process.
// Going to have to deal with the directory as well.
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
int bufsize = 64*1024;
char *mode = "w";
TRACE(("initing libtiff\n"));
TRACE(("Filename %s, filepointer: %d \n", filename, fp));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("State: context %p \n", state->context));
clientstate->loc = 0;
clientstate->size = 0;
clientstate->eof =0;
clientstate->data = 0;
clientstate->flrealloc = 0;
clientstate->fp = fp;
state->state = 0;
if (fp) {
TRACE(("Opening using fd: %d for writing \n",clientstate->fp));
clientstate->tiff = TIFFFdOpen(clientstate->fp, filename, mode);
} else {
// malloc a buffer to write the tif, we're going to need to realloc or something if we need bigger.
TRACE(("Opening a buffer for writing \n"));
clientstate->data = malloc(bufsize);
clientstate->size = bufsize;
clientstate->flrealloc=1;
if (!clientstate->data) {
TRACE(("Error, couldn't allocate a buffer of size %d\n", bufsize));
return 0;
}
clientstate->tiff = TIFFClientOpen(filename, mode,
(thandle_t) clientstate,
_tiffReadProc, _tiffWriteProc,
_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
_tiffNullMapProc, _tiffUnmapProc); /*force no mmap*/
}
if (!clientstate->tiff) {
TRACE(("Error, couldn't open tiff file\n"));
return 0;
}
return 1;
}
int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){
// after tif_dir.c->TIFFSetField.
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
va_list ap;
int status;
va_start(ap, tag);
status = TIFFVSetField(clientstate->tiff, tag, ap);
va_end(ap);
return status;
}
int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
/* One shot encoder. Encode everything to the tiff in the clientstate.
If we're running off of a FD, then run once, we're good, everything
ends up in the file, we close and we're done.
If we're going to memory, then we need to write the whole file into memory, then
parcel it back out to the pystring buffer bytes at a time.
*/
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
TIFF *tiff = clientstate->tiff;
TRACE(("in encoder: bytes %d\n", bytes));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
state->xoff, state->yoff));
TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3]));
TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
im->mode, im->type, im->bands, im->xsize, im->ysize));
TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n",
im->image8, im->image32, im->image, im->block));
TRACE(("Image: pixelsize: %d, linesize %d \n",
im->pixelsize, im->linesize));
dump_state(clientstate);
if (state->state == 0) {
TRACE(("Encoding line bt line"));
while(state->y < state->ysize){
state->shuffle(state->buffer,
(UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->xsize);
if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y, 0) == -1) {
TRACE(("Encode Error, row %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN;
TIFFClose(tiff);
if (!clientstate->fp){
free(clientstate->data);
}
return -1;
}
state->y++;
}
if (state->y == state->ysize) {
state->state=1;
TRACE(("Flushing \n"));
if (!TIFFFlush(tiff)) {
TRACE(("Error flushing the tiff"));
// likely reason is memory.
state->errcode = IMAGING_CODEC_MEMORY;
TIFFClose(tiff);
if (!clientstate->fp){
free(clientstate->data);
}
return -1;
}
TRACE(("Closing \n"));
TIFFClose(tiff);
// reset the clientstate metadata to use it to read out the buffer.
clientstate->loc = 0;
clientstate->size = clientstate->eof; // redundant?
}
}
if (state->state == 1 && !clientstate->fp) {
int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes);
TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
if (clientstate->loc == clientstate->eof) {
TRACE(("Hit EOF, calling an end, freeing data"));
state->errcode = IMAGING_CODEC_END;
free(clientstate->data);
}
return read;
}
state->errcode = IMAGING_CODEC_END;
return 0;
}
#endif

View File

@ -186,6 +186,7 @@ if __name__ == "__main__":
check_module("TKINTER", "_imagingtk")
check_codec("JPEG", "jpeg")
check_codec("ZLIB (PNG/ZIP)", "zip")
check_codec("G4 TIFF", "group4")
check_module("FREETYPE2", "_imagingft")
check_module("LITTLECMS", "_imagingcms")
print("-"*68)
@ -201,3 +202,4 @@ if __name__ == "__main__":
print("--- %s tests passed." % status[1])
sys.exit(exit_status)

View File

@ -26,7 +26,7 @@ _LIB_IMAGING = (
"QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
"RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
"TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
"XbmEncode", "ZipDecode", "ZipEncode")
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode")
def _add_directory(path, dir, where=None):
@ -76,7 +76,7 @@ PIL_VERSION = '1.1.7'
TCL_ROOT = None
JPEG_ROOT = None
ZLIB_ROOT = None
TIFF_ROOT = None
TIFF_ROOT = None
FREETYPE_ROOT = None
LCMS_ROOT = None
@ -156,6 +156,7 @@ class pil_build_ext(build_ext):
#
# locate tkinter libraries
if _tkinter:
TCL_VERSION = _tkinter.TCL_VERSION[:3]
@ -183,6 +184,7 @@ class pil_build_ext(build_ext):
break
else:
TCL_ROOT = None
#
# add standard directories
@ -229,6 +231,10 @@ class pil_build_ext(build_ext):
if _find_library_file(self, "tiff"):
feature.tiff = "tiff"
if sys.platform == "win32" and _find_library_file(self, "libtiff"):
feature.tiff = "libtiff"
if sys.platform == "darwin" and _find_library_file(self, "libtiff"):
feature.tiff = "libtiff"
if _find_library_file(self, "freetype"):
# look for freetype2 include files
@ -288,6 +294,9 @@ class pil_build_ext(build_ext):
if feature.zlib:
libs.append(feature.zlib)
defs.append(("HAVE_LIBZ", None))
if feature.tiff:
libs.append(feature.tiff)
defs.append(("HAVE_LIBTIFF", None))
if sys.platform == "win32":
libs.extend(["kernel32", "user32", "gdi32"])
if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1:
@ -382,7 +391,7 @@ class pil_build_ext(build_ext):
(feature.tcl and feature.tk, "TKINTER"),
(feature.jpeg, "JPEG"),
(feature.zlib, "ZLIB (PNG/ZIP)"),
# (feature.tiff, "experimental TIFF G3/G4 read"),
(feature.tiff, "experimental TIFF G3/G4 read"),
(feature.freetype, "FREETYPE2"),
(feature.lcms, "LITTLECMS"),
(feature.webp, "WEBP"),