diff --git a/12bit.MM.cropped.tif b/12bit.MM.cropped.tif new file mode 100644 index 0000000..c4c24e2 Binary files /dev/null and b/12bit.MM.cropped.tif differ diff --git a/12bit.MM.deflate.tif b/12bit.MM.deflate.tif new file mode 100644 index 0000000..90a62e9 Binary files /dev/null and b/12bit.MM.deflate.tif differ diff --git a/12bit.deflate.tif b/12bit.deflate.tif new file mode 100644 index 0000000..4f9f4ec Binary files /dev/null and b/12bit.deflate.tif differ diff --git a/python-pillow.spec b/python-pillow.spec index 01aa568..669a45e 100644 --- a/python-pillow.spec +++ b/python-pillow.spec @@ -25,7 +25,7 @@ Name: python-pillow Version: 2.2.1 -Release: 1%{?snap}%{?dist} +Release: 2%{?snap}%{?dist} Summary: Python image processing library # License: see http://www.pythonware.com/products/pil/license.htm @@ -35,9 +35,15 @@ URL: http://python-imaging.github.com/Pillow/ # Obtain the tarball for a certain commit via: # wget --content-disposition https://github.com/python-imaging/Pillow/tarball/$commit Source0: https://github.com/python-imaging/Pillow/tarball/%{commit}/python-imaging-Pillow-%{version}-%{ahead}-g%{shortcommit}.tar.gz +Source1: 12bit.MM.cropped.tif +Source2: 12bit.MM.deflate.tif +Source3: 12bit.deflate.tif + # Add s390* and ppc* archs Patch0: python-pillow-archs.patch +# Fix tiff byteorder issues, see https://github.com/python-imaging/Pillow/pull/388 +Patch1: python-pillow_tiff-byteorder.patch BuildRequires: python2-devel BuildRequires: python-setuptools @@ -205,6 +211,10 @@ PIL image wrapper for Qt. %prep %setup -q -n python-imaging-Pillow-%{shortcommit} %patch0 -p1 -b .archs +%patch1 -p1 +cp -a %{SOURCE1} Tests/images +cp -a %{SOURCE2} Tests/images +cp -a %{SOURCE3} Tests/images %if %{with_python3} # Create Python 3 source tree @@ -356,6 +366,9 @@ popd %endif %changelog +* Wed Oct 23 2013 Sandro Mani - 2.2.1-2 +- Backport fix for decoding tiffs with correct byteorder, fixes rhbz#1019656 + * Wed Oct 02 2013 Sandro Mani - 2.2.1-1 - Update to 2.2.1 - Really enable webp on ppc, but leave disabled on s390 diff --git a/python-pillow_tiff-byteorder.patch b/python-pillow_tiff-byteorder.patch new file mode 100644 index 0000000..a28ba1c --- /dev/null +++ b/python-pillow_tiff-byteorder.patch @@ -0,0 +1,397 @@ +diff -rupN python-imaging-Pillow-3c2496e/encode.c python-imaging-Pillow-3c2496e-new/encode.c +--- python-imaging-Pillow-3c2496e/encode.c 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/encode.c 2013-10-22 23:17:17.175834726 +0200 +@@ -773,11 +773,10 @@ PyImaging_LibTiffEncoderNew(PyObject* se + (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))); ++ TRACE(("Setting from Bytes: %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; +@@ -795,12 +794,12 @@ PyImaging_LibTiffEncoderNew(PyObject* se + free(floatav); + } + } else if (PyFloat_Check(value)) { +- TRACE(("Setting from String: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); ++ TRACE(("Setting from Float: %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 ", ++ TRACE(("Unhandled type for key %d : %s \n", + (int)PyInt_AsLong(key), + PyBytes_AsString(PyObject_Str(value)))); + } +diff -rupN python-imaging-Pillow-3c2496e/libImaging/Pack.c python-imaging-Pillow-3c2496e-new/libImaging/Pack.c +--- python-imaging-Pillow-3c2496e/libImaging/Pack.c 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/libImaging/Pack.c 2013-10-22 23:17:17.176834743 +0200 +@@ -362,6 +362,27 @@ packI16B(UINT8* out, const UINT8* in_, i + } + + static void ++packI16N_I16B(UINT8* out, const UINT8* in, int pixels){ ++ int i; ++ UINT8* tmp = (UINT8*) in; ++ for (i = 0; i < pixels; i++) { ++ C16B; ++ out += 2; tmp += 2; ++ } ++ ++} ++static void ++packI16N_I16(UINT8* out, const UINT8* in, int pixels){ ++ int i; ++ UINT8* tmp = (UINT8*) in; ++ for (i = 0; i < pixels; i++) { ++ C16L; ++ out += 2; tmp += 2; ++ } ++} ++ ++ ++static void + packI32S(UINT8* out, const UINT8* in, int pixels) + { + int i; +@@ -541,6 +562,9 @@ static struct { + {"I;16", "I;16", 16, copy2}, + {"I;16B", "I;16B", 16, copy2}, + {"I;16L", "I;16L", 16, copy2}, ++ {"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian. ++ {"I;16L", "I;16N", 16, packI16N_I16}, ++ {"I;16B", "I;16N", 16, packI16N_I16B}, + {"BGR;15", "BGR;15", 16, copy2}, + {"BGR;16", "BGR;16", 16, copy2}, + {"BGR;24", "BGR;24", 24, copy3}, +diff -rupN python-imaging-Pillow-3c2496e/libImaging/Storage.c python-imaging-Pillow-3c2496e-new/libImaging/Storage.c +--- python-imaging-Pillow-3c2496e/libImaging/Storage.c 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/libImaging/Storage.c 2013-10-22 23:17:17.176834743 +0200 +@@ -105,7 +105,8 @@ ImagingNewPrologueSubtype(const char *mo + im->linesize = xsize * 4; + im->type = IMAGING_TYPE_INT32; + +- } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 || strcmp(mode, "I;16B") == 0) { ++ } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \ ++ || strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) { + /* EXPERIMENTAL */ + /* 16-bit raw integer images */ + im->bands = 1; +diff -rupN python-imaging-Pillow-3c2496e/libImaging/Unpack.c python-imaging-Pillow-3c2496e-new/libImaging/Unpack.c +--- python-imaging-Pillow-3c2496e/libImaging/Unpack.c 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/libImaging/Unpack.c 2013-10-22 23:17:17.176834743 +0200 +@@ -661,6 +661,26 @@ unpackCMYKI(UINT8* out, const UINT8* in, + } + + static void ++unpackI16N_I16B(UINT8* out, const UINT8* in, int pixels){ ++ int i; ++ UINT8* tmp = (UINT8*) out; ++ for (i = 0; i < pixels; i++) { ++ C16B; ++ in += 2; tmp += 2; ++ } ++ ++} ++static void ++unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ ++ int i; ++ UINT8* tmp = (UINT8*) out; ++ for (i = 0; i < pixels; i++) { ++ C16L; ++ in += 2; tmp += 2; ++ } ++} ++ ++static void + copy1(UINT8* out, const UINT8* in, int pixels) + { + /* L, P */ +@@ -1004,6 +1024,10 @@ static struct { + {"I;16B", "I;16B", 16, copy2}, + {"I;16L", "I;16L", 16, copy2}, + ++ {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. ++ {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. ++ {"I;16B", "I;16N", 16, unpackI16N_I16B}, ++ + {NULL} /* sentinel */ + }; + +diff -rupN python-imaging-Pillow-3c2496e/PIL/Image.py python-imaging-Pillow-3c2496e-new/PIL/Image.py +--- python-imaging-Pillow-3c2496e/PIL/Image.py 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/PIL/Image.py 2013-10-22 23:19:51.334508104 +0200 +@@ -224,16 +224,17 @@ _MODE_CONV = { + "RGBA": ('|u1', 4), + "CMYK": ('|u1', 4), + "YCbCr": ('|u1', 3), +- "I;16": ('=u2', None), ++ # I;16 == I;16L, and I;32 == I;32L ++ "I;16": ('u2', None), + "I;16L": ('i2', None), + "I;16LS": ('u4', None), + "I;32L": ('i4', None), + "I;32LS": (' 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 ++ atts={} ++ # Merge the ones that we have with (optional) more bits from ++ # the original file, e.g x,y resolution so that we can ++ # save(load('')) == original file. ++ for k,v in itertools.chain(ifd.items(), getattr(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) > 2: ++ # List of ints? ++ # BitsPerSample is one example, I get (8,8,8) ++ # UNDONE ++ continue ++ if type(v) == tuple and len(v) == 2: ++ # one rational tuple ++ # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL ++ atts[k] = float(v[0])/float(v[1]) ++ continue ++ if type(v) == tuple and len(v) == 1: ++ v = v[0] ++ # drop through ++ if isStringType(v): ++ atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" ++ continue ++ else: ++ # int or similar ++ atts[k] = v ++ + if Image.DEBUG: + print (atts) ++ ++ # libtiff always returns the bytes in native order. ++ # we're expecting image byte order. So, if the rawmode ++ # contains I;16, we need to convert from native to image ++ # byte order. ++ if im.mode in ('I;16B', 'I;16'): ++ rawmode = 'I;16N' ++ + a = (rawmode, compression, _fp, filename, atts) ++ # print (im.mode, compression, a, im.encoderconfig) + e = Image._getencoder(im.mode, compression, a, im.encoderconfig) + e.setimage(im.im, (0,0)+im.size) + while 1: +diff -rupN python-imaging-Pillow-3c2496e/Tests/test_file_libtiff.py python-imaging-Pillow-3c2496e-new/Tests/test_file_libtiff.py +--- python-imaging-Pillow-3c2496e/Tests/test_file_libtiff.py 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/Tests/test_file_libtiff.py 2013-10-22 23:17:17.175834726 +0200 +@@ -93,6 +93,8 @@ def test_g4_write(): + _assert_noerr(reread) + assert_image_equal(reread, rot) + ++ assert_equal(reread.info['compression'], orig.info['compression']) ++ + assert_false(orig.tobytes() == reread.tobytes()) + + def test_adobe_deflate_tiff(): +@@ -105,3 +107,65 @@ def test_adobe_deflate_tiff(): + assert_no_exception(lambda: im.load()) + + ++def test_little_endian(): ++ im = Image.open('Tests/images/12bit.deflate.tif') ++ assert_equal(im.getpixel((0,0)), 480) ++ assert_equal(im.mode, 'I;16') ++ ++ b = im.tobytes() ++ # Bytes are in image native order (little endian) ++ if py3: ++ assert_equal(b[0], ord(b'\xe0')) ++ assert_equal(b[1], ord(b'\x01')) ++ else: ++ assert_equal(b[0], b'\xe0') ++ assert_equal(b[1], b'\x01') ++ ++ ++ out = tempfile("temp.tif") ++ out = "temp.le.tif" ++ im.save(out) ++ reread = Image.open(out) ++ ++ assert_equal(reread.info['compression'], im.info['compression']) ++ assert_equal(reread.getpixel((0,0)), 480) ++ # UNDONE - libtiff defaults to writing in native endian, so ++ # on big endian, we'll get back mode = 'I;16B' here. ++ ++def test_big_endian(): ++ im = Image.open('Tests/images/12bit.MM.deflate.tif') ++ ++ assert_equal(im.getpixel((0,0)), 480) ++ assert_equal(im.mode, 'I;16B') ++ ++ b = im.tobytes() ++ ++ # Bytes are in image native order (big endian) ++ if py3: ++ assert_equal(b[0], ord(b'\x01')) ++ assert_equal(b[1], ord(b'\xe0')) ++ else: ++ assert_equal(b[0], b'\x01') ++ assert_equal(b[1], b'\xe0') ++ ++ out = tempfile("temp.tif") ++ im.save(out) ++ reread = Image.open(out) ++ ++ assert_equal(reread.info['compression'], im.info['compression']) ++ assert_equal(reread.getpixel((0,0)), 480) ++ ++def test_g4_string_info(): ++ """Tests String data in info directory""" ++ file = "Tests/images/lena_g4_500.tif" ++ orig = Image.open(file) ++ ++ out = tempfile("temp.tif") ++ ++ orig.tag[269] = 'temp.tif' ++ orig.save(out) ++ ++ reread = Image.open(out) ++ assert_equal('temp.tif', reread.tag[269]) ++ ++ +diff -rupN python-imaging-Pillow-3c2496e/Tests/test_file_tiff.py python-imaging-Pillow-3c2496e-new/Tests/test_file_tiff.py +--- python-imaging-Pillow-3c2496e/Tests/test_file_tiff.py 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/Tests/test_file_tiff.py 2013-10-22 23:17:17.175834726 +0200 +@@ -71,3 +71,34 @@ def test_xyres_tiff(): + im.tag.tags[Y_RESOLUTION] = (72,) + im._setup() + assert_equal(im.info['dpi'], (72., 72.)) ++ ++ ++def test_little_endian(): ++ im = Image.open('Tests/images/12bit.cropped.tif') ++ assert_equal(im.getpixel((0,0)), 480) ++ assert_equal(im.mode, 'I;16') ++ ++ b = im.tobytes() ++ # Bytes are in image native order (little endian) ++ if py3: ++ assert_equal(b[0], ord(b'\xe0')) ++ assert_equal(b[1], ord(b'\x01')) ++ else: ++ assert_equal(b[0], b'\xe0') ++ assert_equal(b[1], b'\x01') ++ ++ ++def test_big_endian(): ++ im = Image.open('Tests/images/12bit.MM.cropped.tif') ++ assert_equal(im.getpixel((0,0)), 480) ++ assert_equal(im.mode, 'I;16B') ++ ++ b = im.tobytes() ++ ++ # Bytes are in image native order (big endian) ++ if py3: ++ assert_equal(b[0], ord(b'\x01')) ++ assert_equal(b[1], ord(b'\xe0')) ++ else: ++ assert_equal(b[0], b'\x01') ++ assert_equal(b[1], b'\xe0') +diff -rupN python-imaging-Pillow-3c2496e/Tests/test_numpy.py python-imaging-Pillow-3c2496e-new/Tests/test_numpy.py +--- python-imaging-Pillow-3c2496e/Tests/test_numpy.py 2013-10-02 19:07:32.000000000 +0200 ++++ python-imaging-Pillow-3c2496e-new/Tests/test_numpy.py 2013-10-22 23:17:17.175834726 +0200 +@@ -78,7 +78,7 @@ def test_16bit(): + img = Image.open('Tests/images/12bit.cropped.tif') + np_img = numpy.array(img) + _test_img_equals_nparray(img, np_img) +- assert_equal(np_img.dtype, numpy.dtype('uint16')) ++ assert_equal(np_img.dtype, numpy.dtype('u2'), +- ("I;16L", 'uint16'), ++ ("I;16L", '