diff --git a/.travis.yml b/.travis.yml index d0ba64c50..ba6ba5e74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,15 +16,14 @@ python: - 3.4 install: - - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick lcov" - - "pip install cffi" - - "pip install coveralls nose coveralls-merge" - - "gem install coveralls-lcov" + - "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick" + - "travis_retry pip install cffi" + - "travis_retry pip install coverage nose" # Pyroma installation is slow on Py3, so just do it for Py2. - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then travis_retry pip install pyroma; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi # webp - pushd depends && ./install_webp.sh && popd @@ -42,18 +41,20 @@ script: after_success: # gather the coverage data + - travis_retry sudo apt-get -qq install lcov - lcov --capture --directory . -b . --output-file coverage.info # filter to remove system headers - lcov --remove coverage.info '/usr/*' -o coverage.filtered.info # convert to json + - travis_retry gem install coveralls-lcov - coveralls-lcov -v -n coverage.filtered.info > coverage.c.json - coverage report + - travis_retry pip install coveralls-merge - coveralls-merge coverage.c.json - - pip install pep8 pyflakes - - pep8 --statistics --count *.py + - travis_retry pip install pep8 pyflakes - pep8 --statistics --count PIL/*.py - pep8 --statistics --count Tests/*.py - pyflakes *.py | tee >(wc -l) @@ -65,3 +66,5 @@ after_success: # (Installation is very slow on Py3, so just do it for Py2.) - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-install.sh; fi - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-run.sh; fi +matrix: + fast_finish: true diff --git a/CHANGES.rst b/CHANGES.rst index 97bbd5d86..cc92eba8d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,27 @@ Changelog (Pillow) 2.6.0 (unreleased) ------------------ +- Jpeg2k Decode/Encode Memory Leak Fix #898 + [joshware, wiredfool] + +- EpsFilePlugin Speed improvements #886 + [wiredfool, karstenw] + +- Don't resize if already the right size. + [radarhere] + +- Fix for reading multipage TIFFs #885 + [kostrom, wiredfool] + +- Correctly handle saving gray and CMYK JPEGs with quality=keep #857 + [etienned] + +- Correct duplicate Tiff Metadata and Exif tag values + [hugovk] + +- Windows fixes #871 + [wiredfool] + - Fix TGA files with image ID field #856 [megabuz] @@ -55,7 +76,7 @@ Changelog (Pillow) - Added docs for ExifTags [Wintermute3] -- More tests for CurImagePlugin, DcxImagePlugin, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util +- More tests for CurImagePlugin, DcxImagePlugin, Effects.c, GimpGradientFile, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util [hugovk] - Fix return value of FreeTypeFont.textsize() does not include font offsets diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 7b82fc3e8..05be24f46 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -91,26 +91,32 @@ def Ghostscript(tile, size, fp, scale=1): out_fd, outfile = tempfile.mkstemp() os.close(out_fd) - in_fd, infile = tempfile.mkstemp() - os.close(in_fd) - # ignore length and offset! - # ghostscript can read it - # copy whole file to read in ghostscript - with open(infile, 'wb') as f: - # fetch length of fp - fp.seek(0, 2) - fsize = fp.tell() - # ensure start position - # go back - fp.seek(0) - lengthfile = fsize - while lengthfile > 0: - s = fp.read(min(lengthfile, 100*1024)) - if not s: - break - length -= len(s) - f.write(s) + infile_temp = None + if hasattr(fp, 'name') and os.path.exists(fp.name): + infile = fp.name + else: + in_fd, infile_temp = tempfile.mkstemp() + os.close(in_fd) + infile = infile_temp + + # ignore length and offset! + # ghostscript can read it + # copy whole file to read in ghostscript + with open(infile_temp, 'wb') as f: + # fetch length of fp + fp.seek(0, 2) + fsize = fp.tell() + # ensure start position + # go back + fp.seek(0) + lengthfile = fsize + while lengthfile > 0: + s = fp.read(min(lengthfile, 100*1024)) + if not s: + break + lengthfile -= len(s) + f.write(s) # Build ghostscript command command = ["gs", @@ -143,56 +149,36 @@ def Ghostscript(tile, size, fp, scale=1): finally: try: os.unlink(outfile) - os.unlink(infile) - except: - pass - + if infile_temo: + os.unlink(infile_temp) + except: pass + return im class PSFile: - """Wrapper that treats either CR or LF as end of line.""" + """Wrapper for bytesio object that treats either CR or LF as end of line.""" def __init__(self, fp): self.fp = fp self.char = None - - def __getattr__(self, id): - v = getattr(self.fp, id) - setattr(self, id, v) - return v - def seek(self, offset, whence=0): self.char = None self.fp.seek(offset, whence) - - def read(self, count): - return self.fp.read(count).decode('latin-1') - - def readbinary(self, count): - return self.fp.read(count) - - def tell(self): - pos = self.fp.tell() - if self.char: - pos -= 1 - return pos - def readline(self): - s = b"" - if self.char: - c = self.char - self.char = None - else: - c = self.fp.read(1) + s = self.char or b"" + self.char = None + + c = self.fp.read(1) while c not in b"\r\n": s = s + c c = self.fp.read(1) - if c == b"\r": - self.char = self.fp.read(1) - if self.char == b"\n": - self.char = None - return s.decode('latin-1') + "\n" + self.char = self.fp.read(1) + # line endings can be 1 or 2 of \r \n, in either order + if self.char in b"\r\n": + self.char = None + + return s.decode('latin-1') def _accept(prefix): return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 @@ -208,33 +194,23 @@ class EpsImageFile(ImageFile.ImageFile): format = "EPS" format_description = "Encapsulated Postscript" + mode_map = { 1:"L", 2:"LAB", 3:"RGB" } + def _open(self): + (length, offset) = self._find_offset(self.fp) - fp = PSFile(self.fp) - - # FIX for: Some EPS file not handled correctly / issue #302 - # EPS can contain binary data - # or start directly with latin coding - # read header in both ways to handle both - # file types - # more info see: - # http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf - - # for HEAD without binary preview - s = fp.read(4) - # for HEAD with binary preview - fp.seek(0) - sb = fp.readbinary(160) - - if s[:4] == "%!PS": - fp.seek(0, 2) - length = fp.tell() - offset = 0 - elif i32(sb[0:4]) == 0xC6D3D0C5: - offset = i32(sb[4:8]) - length = i32(sb[8:12]) - else: - raise SyntaxError("not an EPS file") + # Rewrap the open file pointer in something that will + # convert line endings and decode to latin-1. + try: + if bytes is str: + # Python2, no encoding conversion necessary + fp = open(self.fp.name, "Ur") + else: + # Python3, can use bare open command. + fp = open(self.fp.name, "Ur", encoding='latin-1') + except Exception as msg: + # Expect this for bytesio/stringio + fp = PSFile(self.fp) # go to offset - start of "%!PS" fp.seek(offset) @@ -247,18 +223,12 @@ class EpsImageFile(ImageFile.ImageFile): # # Load EPS header - s = fp.readline() - + s = fp.readline().strip('\r\n') + while s: - if len(s) > 255: raise SyntaxError("not an EPS file") - if s[-2:] == '\r\n': - s = s[:-2] - elif s[-1:] == '\n': - s = s[:-1] - try: m = split.match(s) except re.error as v: @@ -280,9 +250,7 @@ class EpsImageFile(ImageFile.ImageFile): pass else: - m = field.match(s) - if m: k = m.group(1) @@ -292,16 +260,16 @@ class EpsImageFile(ImageFile.ImageFile): self.info[k[:8]] = k[9:] else: self.info[k] = "" - elif s[0:1] == '%': + elif s[0] == '%': # handle non-DSC Postscript comments that some # tools mistakenly put in the Comments section pass else: raise IOError("bad EPS header") - s = fp.readline() + s = fp.readline().strip('\r\n') - if s[:1] != "%": + if s[0] != "%": break # @@ -312,64 +280,48 @@ class EpsImageFile(ImageFile.ImageFile): if len(s) > 255: raise SyntaxError("not an EPS file") - if s[-2:] == '\r\n': - s = s[:-2] - elif s[-1:] == '\n': - s = s[:-1] - if s[:11] == "%ImageData:": + # Encoded bitmapped image. + [x, y, bi, mo, z3, z4, en, id] = s[11:].split(None, 7) - [x, y, bi, mo, z3, z4, en, id] =\ - s[11:].split(None, 7) - - x = int(x) - y = int(y) - - bi = int(bi) - mo = int(mo) - - en = int(en) - - if en == 1: - decoder = "eps_binary" - elif en == 2: - decoder = "eps_hex" - else: + if int(bi) != 8: break - if bi != 8: + try: + self.mode = self.mode_map[int(mo)] + except: break - if mo == 1: - self.mode = "L" - elif mo == 2: - self.mode = "LAB" - elif mo == 3: - self.mode = "RGB" - else: - break - - if id[:1] == id[-1:] == '"': - id = id[1:-1] - - # Scan forward to the actual image data - while True: - s = fp.readline() - if not s: - break - if s[:len(id)] == id: - self.size = x, y - self.tile2 = [(decoder, - (0, 0, x, y), - fp.tell(), - 0)] - return - - s = fp.readline() + + self.size = int(x), int(y) + return + + s = fp.readline().strip('\r\n') if not s: break if not box: raise IOError("cannot determine EPS bounding box") + def _find_offset(self, fp): + + s = fp.read(160) + + if s[:4] == b"%!PS": + # for HEAD without binary preview + fp.seek(0, 2) + length = fp.tell() + offset = 0 + elif i32(s[0:4]) == 0xC6D3D0C5: + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # more info see http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + offset = i32(s[4:8]) + length = i32(s[8:12]) + else: + raise SyntaxError("not an EPS file") + + return (length, offset) + def load(self, scale=1): # Load EPS via Ghostscript if not self.tile: diff --git a/PIL/ExifTags.py b/PIL/ExifTags.py index 25cd08068..52e145f62 100644 --- a/PIL/ExifTags.py +++ b/PIL/ExifTags.py @@ -67,8 +67,8 @@ TAGS = { 0x0213: "YCbCrPositioning", 0x0214: "ReferenceBlackWhite", 0x1000: "RelatedImageFileFormat", - 0x1001: "RelatedImageLength", # FIXME / Dictionary contains duplicate keys - 0x1001: "RelatedImageWidth", # FIXME \ Dictionary contains duplicate keys + 0x1001: "RelatedImageWidth", + 0x1002: "RelatedImageLength", 0x828d: "CFARepeatPatternDim", 0x828e: "CFAPattern", 0x828f: "BatteryLevel", diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 7c88addae..696f425f1 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -24,6 +24,7 @@ from PIL._binary import o8 EPSILON = 1e-10 + def linear(middle, pos): if pos <= middle: if middle < EPSILON: @@ -38,25 +39,30 @@ def linear(middle, pos): else: return 0.5 + 0.5 * pos / middle + def curved(middle, pos): return pos ** (log(0.5) / log(max(middle, EPSILON))) + def sine(middle, pos): return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + def sphere_increasing(middle, pos): return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + def sphere_decreasing(middle, pos): return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) -SEGMENTS = [ linear, curved, sine, sphere_increasing, sphere_decreasing ] +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] + class GradientFile: gradient = None - def getpalette(self, entries = 256): + def getpalette(self, entries=256): palette = [] @@ -89,6 +95,7 @@ class GradientFile: return b"".join(palette), "RGBA" + ## # File handler for GIMP's gradient format. @@ -99,7 +106,13 @@ class GimpGradientFile(GradientFile): if fp.readline()[:13] != b"GIMP Gradient": raise SyntaxError("not a GIMP gradient file") - count = int(fp.readline()) + line = fp.readline() + + # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do + if line.startswith(b"Name: "): + line = fp.readline().strip() + + count = int(line) gradient = [] @@ -108,13 +121,13 @@ class GimpGradientFile(GradientFile): s = fp.readline().split() w = [float(x) for x in s[:11]] - x0, x1 = w[0], w[2] - xm = w[1] - rgb0 = w[3:7] - rgb1 = w[7:11] + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] segment = SEGMENTS[int(s[11])] - cspace = int(s[12]) + cspace = int(s[12]) if cspace != 0: raise IOError("cannot handle HSV colour space") diff --git a/PIL/Image.py b/PIL/Image.py index 628300d6a..76f3ab0a5 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1516,6 +1516,9 @@ class Image: self.load() + if self.size == size: + return self._new(self.im) + if self.mode in ("1", "P"): resample = NEAREST @@ -1912,6 +1915,16 @@ class Image: im = self.im.transpose(method) return self._new(im) + def effect_spread(self, distance): + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + im = self.im.effect_spread(distance) + return self._new(im) + # -------------------------------------------------------------------- # Lazy operations @@ -2421,3 +2434,32 @@ def _show(image, **options): def _showxv(image, title=None, **options): from PIL import ImageShow ImageShow.show(image, title, **options) + + +# -------------------------------------------------------------------- +# Effects + +def effect_mandelbrot(size, extent, quality): + """ + Generate a Mandelbrot set covering the given extent. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param extent: The extent to cover, as a 4-tuple: + (x0, y0, x1, y2). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size, sigma): + """ + Generate Gaussian noise centered around 128. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param sigma: Standard deviation of noise. + """ + return Image()._new(core.effect_noise(size, sigma)) + +# End of file diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 0778c6f66..82691af92 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -234,6 +234,8 @@ class ImageFile(Image.Image): break b = b[n:] t = t + n + # Need to cleanup here to prevent leaks in PyPy + d.cleanup() self.tile = [] self.readonly = readonly @@ -479,6 +481,7 @@ def _save(im, fp, tile, bufsize=0): break if s < 0: raise IOError("encoder error %d when writing image file" % s) + e.cleanup() else: # slight speedup: compress to real file object for e, b, o, a in tile: @@ -489,6 +492,7 @@ def _save(im, fp, tile, bufsize=0): s = e.encode_to_file(fh, bufsize) if s < 0: raise IOError("encoder error %d when writing image file" % s) + e.cleanup() try: fp.flush() except: diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index bb812eb3f..a3b603458 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -550,6 +550,15 @@ def convert_dict_qtables(qtables): def get_sampling(im): + # There's no subsampling when image have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to default value. + # + # NOTE: currently Pillow can't encode JPEG to YCCK format. + # If YCCK support is added in the future, subsampling code will have + # to be updated (here and in JpegEncode.c) to deal with 4 layers. + if not hasattr(im, 'layers') or im.layers in (1, 4): + return -1 sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] return samplings.get(sampling, -1) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 9bef30ebe..50648288e 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -281,6 +281,7 @@ class ImageFileDirectory(collections.MutableMapping): self.tagdata = {} self.tagtype = {} # added 2008-06-05 by Florian Hoech self.next = None + self.offset = None def __str__(self): return str(self.as_dict()) @@ -415,6 +416,7 @@ class ImageFileDirectory(collections.MutableMapping): # load tag dictionary self.reset() + self.offset = fp.tell() i16 = self.i16 i32 = self.i32 @@ -446,7 +448,11 @@ class ImageFileDirectory(collections.MutableMapping): # Get and expand tag value if size > 4: here = fp.tell() + if Image.DEBUG: + print ("Tag Location: %s" %here) fp.seek(i32(ifd, 8)) + if Image.DEBUG: + print ("Data Location: %s" %fp.tell()) data = ImageFile._safe_read(fp, size) fp.seek(here) else: @@ -630,18 +636,20 @@ class TiffImageFile(ImageFile.ImageFile): def seek(self, frame): "Select a given frame as current image" - if frame < 0: frame = 0 self._seek(frame) + # Create a new core image object on second and + # subsequent frames in the image. Image may be + # different size/mode. + Image._decompression_bomb_check(self.size) + self.im = Image.core.new(self.mode, self.size) def tell(self): "Return the current frame number" - return self._tell() def _seek(self, frame): - self.fp = self.__fp if frame < self.__frame: # rewind file @@ -650,14 +658,21 @@ class TiffImageFile(ImageFile.ImageFile): while self.__frame < frame: if not self.__next: raise EOFError("no more images in TIFF file") + if Image.DEBUG: + print("Seeking to frame %s, on frame %s, __next %s, location: %s"% + (frame, self.__frame, self.__next, self.fp.tell())) + # reset python3 buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() self.fp.seek(self.__next) + if Image.DEBUG: + print("Loading tags, location: %s"%self.fp.tell()) self.tag.load(self.fp) self.__next = self.tag.next self.__frame += 1 self._setup() - + def _tell(self): - return self.__frame def _decoder(self, rawmode, layer, tile=None): @@ -706,6 +721,7 @@ class TiffImageFile(ImageFile.ImageFile): # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) ignored, extents, ignored_2, args = self.tile[0] + args = args + (self.ifd.offset,) decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) try: @@ -744,7 +760,8 @@ class TiffImageFile(ImageFile.ImageFile): 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() + if not self.__next: + self.fp.close() self.fp = None # might be shared if err < 0: diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 966779ce9..d15aa7ebe 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -46,8 +46,8 @@ TAGS = { (262, 5): "CMYK", (262, 6): "YCbCr", (262, 8): "CieLAB", - (262, 32803): "CFA", # TIFF/EP, Adobe DNG - (262, 32892): "LinearRaw", # Adobe DNG + (262, 32803): "CFA", # TIFF/EP, Adobe DNG + (262, 32892): "LinearRaw", # Adobe DNG 263: "Thresholding", 264: "CellWidth", @@ -240,7 +240,7 @@ TAGS = { 45579: "YawAngle", 45580: "PitchAngle", 45581: "RollAngle", - + # Adobe DNG 50706: "DNGVersion", 50707: "DNGBackwardVersion", @@ -255,7 +255,6 @@ TAGS = { 50716: "BlackLevelDeltaV", 50717: "WhiteLevel", 50718: "DefaultScale", - 50741: "BestQualityScale", # FIXME! Dictionary contains duplicate keys 50741 50719: "DefaultCropOrigin", 50720: "DefaultCropSize", 50778: "CalibrationIlluminant1", @@ -279,11 +278,12 @@ TAGS = { 50737: "ChromaBlurRadius", 50738: "AntiAliasStrength", 50740: "DNGPrivateData", - 50741: "MakerNoteSafety", # FIXME! Dictionary contains duplicate keys 50741 + 50741: "MakerNoteSafety", + 50780: "BestQualityScale", - #ImageJ - 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe - 50839: "ImageJMetaData", # private tag registered with Adobe + # ImageJ + 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe + 50839: "ImageJMetaData", # private tag registered with Adobe } ## diff --git a/README.rst b/README.rst index 482a1e2ec..d58666bc6 100644 --- a/README.rst +++ b/README.rst @@ -20,3 +20,6 @@ Pillow is the "friendly" PIL fork by `Alex Clark and Contributors cleanup) + decoder->cleanup(&decoder->state); free(decoder->state.buffer); free(decoder->state.context); Py_XDECREF(decoder->lock); @@ -442,8 +444,9 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) char* rawmode; char* compname; int fp; + int ifdoffset; - if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp)) + if (! PyArg_ParseTuple(args, "sssii", &mode, &rawmode, &compname, &fp, &ifdoffset)) return NULL; TRACE(("new tiff decoder %s\n", compname)); @@ -455,7 +458,7 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) if (get_unpacker(decoder, mode, rawmode) < 0) return NULL; - if (! ImagingLibTiffInit(&decoder->state, fp)) { + if (! ImagingLibTiffInit(&decoder->state, fp, ifdoffset)) { Py_DECREF(decoder); PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed"); return NULL; diff --git a/depends/install_openjpeg.sh b/depends/install_openjpeg.sh index b3a6ccc4d..83620f4b8 100755 --- a/depends/install_openjpeg.sh +++ b/depends/install_openjpeg.sh @@ -11,9 +11,9 @@ rm -r openjpeg-2.1.0 tar -xvzf openjpeg-2.1.0.tar.gz -pushd openjpeg-2.1.0 +pushd openjpeg-2.1.0 -cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install +cmake -DCMAKE_INSTALL_PREFIX=/usr . && make -j4 && sudo make -j4 install popd diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 5f5963712..7810159cc 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,18 +1,15 @@ #!/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' +if [ ! -f libwebp-0.4.1.tar.gz ]; then + wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.1.tar.gz' fi -rm -r libwebp-0.4.0 -tar -xvzf libwebp-0.4.0.tar.gz +rm -r libwebp-0.4.1 +tar -xvzf libwebp-0.4.1.tar.gz +pushd libwebp-0.4.1 -pushd libwebp-0.4.0 - -./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make && sudo make install +./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make -j4 && sudo make -j4 install popd - diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 97caea722..cd187a4a2 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -670,6 +670,7 @@ files, using either JPEG or HEX encoding depending on the image mode (and whether JPEG support is available or not). PIXAR (read only) +^^^^ PIL provides limited support for PIXAR raster files. The library can identify and read “dumped” RGB files. diff --git a/encode.c b/encode.c index b14942376..34e3b8933 100644 --- a/encode.c +++ b/encode.c @@ -99,6 +99,18 @@ _dealloc(ImagingEncoderObject* encoder) PyObject_Del(encoder); } +static PyObject* +_encode_cleanup(ImagingEncoderObject* encoder, PyObject* args) +{ + int status = 0; + + if (encoder->cleanup){ + status = encoder->cleanup(&encoder->state); + } + + return Py_BuildValue("i", status); +} + static PyObject* _encode(ImagingEncoderObject* encoder, PyObject* args) { @@ -239,6 +251,7 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args) static struct PyMethodDef methods[] = { {"encode", (PyCFunction)_encode, 1}, + {"cleanup", (PyCFunction)_encode_cleanup, 1}, {"encode_to_file", (PyCFunction)_encode_to_file, 1}, {"setimage", (PyCFunction)_setimage, 1}, {NULL, NULL} /* sentinel */ @@ -540,8 +553,8 @@ static unsigned int** get_qtables_arrays(PyObject* qtables) { tables = PySequence_Fast(qtables, "expected a sequence"); num_tables = PySequence_Size(qtables); - if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) { - PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4."); + if (num_tables < 1 || num_tables > NUM_QUANT_TBLS) { + PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 1 and 4."); return NULL; } qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*)); @@ -760,7 +773,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) (ttag_t) PyInt_AsLong(key), intav); free(intav); - } + } } else { TRACE((" %d elements, setting as floats \n", len)); floatav = malloc(sizeof(float)*len); @@ -903,7 +916,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) j2k_decode_coord_tuple(tile_offset, &context->tile_offset_x, &context->tile_offset_y); - j2k_decode_coord_tuple(tile_size, + j2k_decode_coord_tuple(tile_size, &context->tile_size_x, &context->tile_size_y); @@ -918,7 +931,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) if (context->tile_offset_x > context->offset_x || context->tile_offset_y > context->offset_y) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "JPEG 2000 tile offset too large to cover image area"); Py_DECREF(encoder); return NULL; diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 46a6cfb72..3d9119c7f 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -49,6 +49,12 @@ #define L(rgb)\ ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114) +#ifndef round +double round(double x) { + return floor(x+0.5); +} +#endif + /* ------------------- */ /* 1 (bit) conversions */ /* ------------------- */ diff --git a/libImaging/Effects.c b/libImaging/Effects.c index db6e72989..eb598d968 100644 --- a/libImaging/Effects.c +++ b/libImaging/Effects.c @@ -48,25 +48,25 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) for (y = 0; y < ysize; y++) { UINT8* buf = im->image8[y]; - for (x = 0; x < xsize; x++) { - x1 = y1 = xi2 = yi2 = 0.0; - cr = x*dr + extent[0]; - ci = y*di + extent[1]; - for (k = 1;; k++) { - y1 = 2*x1*y1 + ci; - x1 = xi2 - yi2 + cr; - xi2 = x1*x1; - yi2 = y1*y1; - if ((xi2 + yi2) > radius) { - buf[x] = k*255/quality; - break; - } - if (k > quality) { - buf[x] = 0; - break; - } - } - } + for (x = 0; x < xsize; x++) { + x1 = y1 = xi2 = yi2 = 0.0; + cr = x*dr + extent[0]; + ci = y*di + extent[1]; + for (k = 1;; k++) { + y1 = 2*x1*y1 + ci; + x1 = xi2 - yi2 + cr; + xi2 = x1*x1; + yi2 = y1*y1; + if ((xi2 + yi2) > radius) { + buf[x] = k*255/quality; + break; + } + if (k > quality) { + buf[x] = 0; + break; + } + } + } } return im; } @@ -74,7 +74,7 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) Imaging ImagingEffectNoise(int xsize, int ysize, float sigma) { - /* Generate gaussian noise centered around 128 */ + /* Generate Gaussian noise centered around 128 */ Imaging imOut; int x, y; @@ -83,19 +83,19 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) imOut = ImagingNew("L", xsize, ysize); if (!imOut) - return NULL; + return NULL; next = 0.0; nextok = 0; for (y = 0; y < imOut->ysize; y++) { UINT8* out = imOut->image8[y]; - for (x = 0; x < imOut->xsize; x++) { + for (x = 0; x < imOut->xsize; x++) { if (nextok) { this = next; nextok = 0; } else { - /* after numerical recepies */ + /* after numerical recipes */ double v1, v2, radius, factor; do { v1 = rand()*(2.0/32767.0) - 1.0; @@ -113,14 +113,6 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) return imOut; } -Imaging -ImagingEffectPerlinTurbulence(int xsize, int ysize) -{ - /* Perlin turbulence (In progress) */ - - return NULL; -} - Imaging ImagingEffectSpread(Imaging imIn, int distance) { @@ -132,11 +124,11 @@ ImagingEffectSpread(Imaging imIn, int distance) imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) - return NULL; + return NULL; -#define SPREAD(type, image)\ +#define SPREAD(type, image)\ for (y = 0; y < imIn->ysize; y++)\ - for (x = 0; x < imIn->xsize; x++) {\ + for (x = 0; x < imIn->xsize; x++) {\ int xx = x + (rand() % distance) - distance/2;\ int yy = y + (rand() % distance) - distance/2;\ if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\ @@ -147,9 +139,9 @@ ImagingEffectSpread(Imaging imIn, int distance) } if (imIn->image8) { - SPREAD(UINT8, image8); + SPREAD(UINT8, image8); } else { - SPREAD(INT32, image32); + SPREAD(INT32, image32); } ImagingCopyInfo(imOut, imIn); @@ -157,217 +149,4 @@ ImagingEffectSpread(Imaging imIn, int distance) return imOut; } -/* -------------------------------------------------------------------- */ -/* Taken from the "C" code in the W3C SVG specification. Translated - to C89 by Fredrik Lundh */ - -#if 0 - -/* Produces results in the range [1, 2**31 - 2]. -Algorithm is: r = (a * r) mod m -where a = 16807 and m = 2**31 - 1 = 2147483647 -See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 -To test: the algorithm should produce the result 1043618065 -as the 10,000th generated number if the original seed is 1. -*/ -#define RAND_m 2147483647 /* 2**31 - 1 */ -#define RAND_a 16807 /* 7**5; primitive root of m */ -#define RAND_q 127773 /* m / a */ -#define RAND_r 2836 /* m % a */ - -static long -perlin_setup_seed(long lSeed) -{ - if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1; - if (lSeed > RAND_m - 1) lSeed = RAND_m - 1; - return lSeed; -} - -static long -perlin_random(long lSeed) -{ - long result; - result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q); - if (result <= 0) result += RAND_m; - return result; -} - -#define BSize 0x100 -#define BM 0xff -#define PerlinN 0x1000 -#define NP 12 /* 2^PerlinN */ -#define NM 0xfff -static int perlin_uLatticeSelector[BSize + BSize + 2]; -static double perlin_fGradient[4][BSize + BSize + 2][2]; -typedef struct -{ - int nWidth; /* How much to subtract to wrap for stitching. */ - int nHeight; - int nWrapX; /* Minimum value to wrap. */ - int nWrapY; -} StitchInfo; - -static void -perlin_init(long lSeed) -{ - double s; - int i, j, k; - lSeed = perlin_setup_seed(lSeed); - for(k = 0; k < 4; k++) - { - for(i = 0; i < BSize; i++) - { - perlin_uLatticeSelector[i] = i; - for (j = 0; j < 2; j++) - perlin_fGradient[k][i][j] = (double)(((lSeed = perlin_random(lSeed)) % (BSize + BSize)) - BSize) / BSize; - s = (double) (sqrt(perlin_fGradient[k][i][0] * perlin_fGradient[k][i][0] + perlin_fGradient[k][i][1] * perlin_fGradient[k][i][1])); - perlin_fGradient[k][i][0] /= s; - perlin_fGradient[k][i][1] /= s; - } - } - while(--i) - { - k = perlin_uLatticeSelector[i]; - perlin_uLatticeSelector[i] = perlin_uLatticeSelector[j = (lSeed = perlin_random(lSeed)) % BSize]; - perlin_uLatticeSelector[j] = k; - } - for(i = 0; i < BSize + 2; i++) - { - perlin_uLatticeSelector[BSize + i] = perlin_uLatticeSelector[i]; - for(k = 0; k < 4; k++) - for(j = 0; j < 2; j++) - perlin_fGradient[k][BSize + i][j] = perlin_fGradient[k][i][j]; - } -} - -#define s_curve(t) ( t * t * (3. - 2. * t) ) -#define lerp(t, a, b) ( a + t * (b - a) ) -static double -perlin_noise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo) -{ - int bx0, bx1, by0, by1, b00, b10, b01, b11; - double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; - register int i, j; - - t = vec[0] + (double) PerlinN; - bx0 = (int)t; - bx1 = bx0+1; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - t = vec[1] + (double) PerlinN; - by0 = (int)t; - by1 = by0+1; - ry0 = t - (int)t; - ry1 = ry0 - 1.0f; - - /* If stitching, adjust lattice points accordingly. */ - if(pStitchInfo != NULL) - { - if(bx0 >= pStitchInfo->nWrapX) - bx0 -= pStitchInfo->nWidth; - if(bx1 >= pStitchInfo->nWrapX) - bx1 -= pStitchInfo->nWidth; - if(by0 >= pStitchInfo->nWrapY) - by0 -= pStitchInfo->nHeight; - if(by1 >= pStitchInfo->nWrapY) - by1 -= pStitchInfo->nHeight; - } - - bx0 &= BM; - bx1 &= BM; - by0 &= BM; - by1 &= BM; - - i = perlin_uLatticeSelector[bx0]; - j = perlin_uLatticeSelector[bx1]; - b00 = perlin_uLatticeSelector[i + by0]; - b10 = perlin_uLatticeSelector[j + by0]; - b01 = perlin_uLatticeSelector[i + by1]; - b11 = perlin_uLatticeSelector[j + by1]; - sx = (double) (s_curve(rx0)); - sy = (double) (s_curve(ry0)); - q = perlin_fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1]; - q = perlin_fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1]; - a = lerp(sx, u, v); - q = perlin_fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1]; - q = perlin_fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1]; - b = lerp(sx, u, v); - return lerp(sy, a, b); -} - -double -perlin_turbulence( - int nColorChannel, double *point, double fBaseFreqX, double fBaseFreqY, - int nNumOctaves, int bFractalSum, int bDoStitching, - double fTileX, double fTileY, double fTileWidth, double fTileHeight) -{ - StitchInfo stitch; - StitchInfo *pStitchInfo = NULL; /* Not stitching when NULL. */ - - double fSum = 0.0f; - double vec[2]; - double ratio = 1; - - int nOctave; - - vec[0] = point[0] * fBaseFreqX; - vec[1] = point[1] * fBaseFreqY; - - /* Adjust the base frequencies if necessary for stitching. */ - if(bDoStitching) - { - /* When stitching tiled turbulence, the frequencies must be adjusted */ - /* so that the tile borders will be continuous. */ - if(fBaseFreqX != 0.0) - { - double fLoFreq = (double) (floor(fTileWidth * fBaseFreqX)) / fTileWidth; - double fHiFreq = (double) (ceil(fTileWidth * fBaseFreqX)) / fTileWidth; - if(fBaseFreqX / fLoFreq < fHiFreq / fBaseFreqX) - fBaseFreqX = fLoFreq; - else - fBaseFreqX = fHiFreq; - } - - if(fBaseFreqY != 0.0) - { - double fLoFreq = (double) (floor(fTileHeight * fBaseFreqY)) / fTileHeight; - double fHiFreq = (double) (ceil(fTileHeight * fBaseFreqY)) / fTileHeight; - if(fBaseFreqY / fLoFreq < fHiFreq / fBaseFreqY) - fBaseFreqY = fLoFreq; - else - fBaseFreqY = fHiFreq; - } - - /* Set up initial stitch values. */ - pStitchInfo = &stitch; - stitch.nWidth = (int) (fTileWidth * fBaseFreqX + 0.5f); - stitch.nWrapX = (int) (fTileX * fBaseFreqX + PerlinN + stitch.nWidth); - stitch.nHeight = (int) (fTileHeight * fBaseFreqY + 0.5f); - stitch.nWrapY = (int) (fTileY * fBaseFreqY + PerlinN + stitch.nHeight); - } - - for(nOctave = 0; nOctave < nNumOctaves; nOctave++) - { - if(bFractalSum) - fSum += (double) (perlin_noise2(nColorChannel, vec, pStitchInfo) / ratio); - else - fSum += (double) (fabs(perlin_noise2(nColorChannel, vec, pStitchInfo)) / ratio); - - vec[0] *= 2; - vec[1] *= 2; - ratio *= 2; - - if(pStitchInfo != NULL) - { - /* Update stitch values. Subtracting PerlinN before the multiplication and */ - /* adding it afterward simplifies to subtracting it once. */ - stitch.nWidth *= 2; - stitch.nWrapX = 2 * stitch.nWrapX - PerlinN; - stitch.nHeight *= 2; - stitch.nWrapY = 2 * stitch.nWrapY - PerlinN; - } - } - return fSum; -} - -#endif +// End of file diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 97ec81003..abf8cebbe 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -800,6 +800,11 @@ ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { if (context->decoder) ImagingIncrementalCodecDestroy(context->decoder); + context->error_msg = NULL; + + /* Prevent multiple calls to ImagingIncrementalCodecDestroy */ + context->decoder = NULL; + return -1; } diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c old mode 100644 new mode 100755 index e8eef08c1..ea4bca2f2 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -576,15 +576,20 @@ int ImagingJpeg2KEncodeCleanup(ImagingCodecState state) { JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; - if (context->quality_layers) + if (context->quality_layers && context->encoder) Py_DECREF(context->quality_layers); if (context->error_msg) free ((void *)context->error_msg); + context->error_msg = NULL; + if (context->encoder) ImagingIncrementalCodecDestroy(context->encoder); + /* Prevent multiple calls to ImagingIncrementalCodecDestroy */ + context->encoder = NULL; + return -1; } diff --git a/libImaging/TiffDecode.c b/libImaging/TiffDecode.c index 787cd4506..1d320e9bd 100644 --- a/libImaging/TiffDecode.c +++ b/libImaging/TiffDecode.c @@ -21,8 +21,8 @@ #include "TiffDecode.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)); + TRACE(("State: Location %u size %d eof %d data: %p ifd: %d\n", (uint)state->loc, + (int)state->size, (uint)state->eof, state->data, state->ifd)); } /* @@ -142,7 +142,7 @@ void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) { (void) hdata; (void) base; (void) size; } -int ImagingLibTiffInit(ImagingCodecState state, int fp) { +int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset) { TIFFSTATE *clientstate = (TIFFSTATE *)state->context; TRACE(("initing libtiff\n")); @@ -158,6 +158,7 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp) { clientstate->size = 0; clientstate->data = 0; clientstate->fp = fp; + clientstate->ifd = offset; clientstate->eof = 0; return 1; @@ -195,7 +196,6 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int clientstate->loc = 0; clientstate->data = (tdata_t)buffer; clientstate->flrealloc = 0; - dump_state(clientstate); TIFFSetWarningHandler(NULL); @@ -220,6 +220,16 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int return -1; } + if (clientstate->ifd){ + unsigned int ifdoffset = clientstate->ifd; + TRACE(("reading tiff ifd %d\n", ifdoffset)); + int rv = TIFFSetSubDirectory(tiff, ifdoffset); + if (!rv){ + TRACE(("error in TIFFSetSubDirectory")); + return -1; + } + } + size = TIFFScanlineSize(tiff); TRACE(("ScanlineSize: %d \n", size)); if (size > state->bytes) { diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 46c940d1b..9875309e3 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -26,6 +26,7 @@ typedef struct { toff_t loc; /* toff_t == uint32 */ tsize_t size; /* tsize_t == int32 */ int fp; + int ifd; /* offset of the ifd, used for multipage */ TIFF *tiff; /* Used in write */ toff_t eof; int flrealloc; /* may we realloc */ @@ -33,7 +34,7 @@ typedef struct { -extern int ImagingLibTiffInit(ImagingCodecState state, int fp); +extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset); extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp); extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); @@ -50,5 +51,4 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); #define TRACE(args) - #endif diff --git a/mp_compile.py b/mp_compile.py index 794028c4b..c8a437400 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -3,7 +3,7 @@ from multiprocessing import Pool, cpu_count from distutils.ccompiler import CCompiler -import os +import os, sys try: MAX_PROCS = int(os.environ.get('MAX_CONCURRENCY', cpu_count())) @@ -50,7 +50,7 @@ def _mp_compile(self, sources, output_dir=None, macros=None, return objects # explicitly don't enable if environment says 1 processor -if MAX_PROCS != 1: +if MAX_PROCS != 1 and not sys.platform.startswith('win'): try: # bug, only enable if we can make a Pool. see issue #790 and # http://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing diff --git a/selftest.py b/selftest.py index 29af34ad2..b13dfec28 100644 --- a/selftest.py +++ b/selftest.py @@ -49,13 +49,13 @@ def testimage(): Or open existing files: - >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.gif")) + >>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.gif")) >>> _info(im) ('GIF', 'P', (128, 128)) - >>> _info(Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))) + >>> _info(Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm"))) ('PPM', 'RGB', (128, 128)) >>> try: - ... _info(Image.open(os.path.join(ROOT, "Tests/images/lena.jpg"))) + ... _info(Image.open(os.path.join(ROOT, "Tests/images/hopper.jpg"))) ... except IOError as v: ... print(v) ('JPEG', 'RGB', (128, 128)) @@ -63,7 +63,7 @@ def testimage(): PIL doesn't actually load the image data until it's needed, or you call the "load" method: - >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")) + >>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm")) >>> print(im.im) # internal image attribute None >>> a = im.load() @@ -73,7 +73,7 @@ def testimage(): You can apply many different operations on images. Most operations return a new image: - >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")) + >>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm")) >>> _info(im.convert("L")) (None, 'L', (128, 128)) >>> _info(im.copy()) @@ -89,9 +89,9 @@ def testimage(): >>> len(im.getdata()) 16384 >>> im.getextrema() - ((61, 255), (26, 234), (44, 223)) + ((0, 255), (0, 255), (0, 255)) >>> im.getpixel((0, 0)) - (223, 162, 133) + (20, 20, 70) >>> len(im.getprojection()) 2 >>> len(im.histogram()) diff --git a/setup.py b/setup.py index cc8117944..46293fbc8 100644 --- a/setup.py +++ b/setup.py @@ -487,6 +487,8 @@ class pil_build_ext(build_ext): # In Google's precompiled zip it is call "libwebp": if _find_library_file(self, "webp"): feature.webp = "webp" + elif _find_library_file(self, "libwebp"): + feature.webp = "libwebp" if feature.want('webpmux'): if (_find_include_file(self, "webp/mux.h") and @@ -494,6 +496,9 @@ class pil_build_ext(build_ext): if (_find_library_file(self, "webpmux") and _find_library_file(self, "webpdemux")): feature.webpmux = "webpmux" + if (_find_library_file(self, "libwebpmux") and + _find_library_file(self, "libwebpdemux")): + feature.webpmux = "libwebpmux" for f in feature: if not getattr(feature, f) and feature.require(f): @@ -559,13 +564,13 @@ class pil_build_ext(build_ext): libraries=["lcms2"] + extra)) if os.path.isfile("_webp.c") and feature.webp: - libs = ["webp"] + libs = [feature.webp] defs = [] if feature.webpmux: defs.append(("HAVE_WEBPMUX", None)) - libs.append("webpmux") - libs.append("webpdemux") + libs.append(feature.webpmux) + libs.append(feature.webpmux.replace('pmux','pdemux')) exts.append(Extension( "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs))