Merge branch 'master' into tests-for-open-bugs

This commit is contained in:
wiredfool 2016-04-29 21:09:52 +01:00
commit 1284f1682f
44 changed files with 718 additions and 168 deletions

View File

@ -24,7 +24,6 @@ install:
- "travis_retry pip install cffi"
- "travis_retry pip install nose"
- "travis_retry pip install check-manifest"
- "travis_retry pip install Sphinx"
# Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those
- if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi
@ -33,6 +32,9 @@ install:
# Coverage 4.0 doesn't support Python 3.2
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi
# docs only on python 2.7
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi
# clean checkout for manifest
- mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest
@ -51,9 +53,9 @@ script:
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi
- pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
# Sphinx
- make install
- pushd docs; make html; make linkcheck; popd
# Docs
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi
after_success:
# gather the coverage data

View File

@ -4,6 +4,51 @@ Changelog (Pillow)
3.3.0 (unreleased)
------------------
- Jpeg qtables are unsigned chars #1814
[thebostik]
- Added additional EXIF tags #1841, TIFF Tags #1821
[radarhere]
- Changed documentation to refer to ImageSequence Iterator #1833
[radarhere]
- Fix Fedora prerequisites in installation docs, depends script #1842
[living180]
- Added _accept hook for PixarImagePlugin #1843
[radarhere]
- Removed outdated scanner classifier #1823
[radarhere]
- Combined identical error messages in _imaging #1825
[radarhere]
- Added debug option for setup.py to trace header and library finding #1790
[wiredfool]
- Fix doc building on travis #1820, #1844
[wiredfool]
- Fix for DIB/BMP images #1813
[wiredfool]
- Add PixarImagePlugin file extension #1809
[radarhere]
- Catch struct.errors when verifying png files #1805
[wiredfool]
- SpiderImagePlugin: raise an error when seeking in a non-stack file #1794
[radarhere, jmichalon]
- Added Support for 2/4 bpp Tiff Grayscale Images #1789
[zwhfly]
- Removed unused variable from selftest #1788
[radarhere]
- Added warning for as_dict method (deprecated in 3.0.0) #1799
[radarhere]
@ -34,7 +79,7 @@ Changelog (Pillow)
- Fixed documented name of JPEG property #1783
[radarhere]
- Fixed UnboundLocalErrorwhen loading a corrupt jpeg2k file #1780
- Fixed UnboundLocalError when loading a corrupt jpeg2k file #1780
[wiredfool]
- Fixed integer overflow in path.c #1773

View File

@ -26,6 +26,12 @@ coverage:
doc:
$(MAKE) -C docs html
doccheck:
$(MAKE) -C docs html
# Don't make our test rely on the links in the docs being up every single build.
# We don't control them. But do check, and update them to the target of their redirectes.
$(MAKE) -C docs linkcheck || true
docserve:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&

View File

@ -109,7 +109,12 @@ class BmpImageFile(ImageFile.ImageFile):
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
else:
for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
# 40 byte headers only have the three components in the bitfields masks,
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also https://github.com/python-pillow/Pillow/issues/1293
# Note below, None is a key in the SUPPORTED and MASK_MODES structures.
file_info['a_mask'] = None
for mask in ['r_mask', 'g_mask', 'b_mask']:
file_info[mask] = i32(read(4))
file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
@ -130,12 +135,22 @@ class BmpImageFile(ImageFile.ImageFile):
# ----------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS:
SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
32: [(0xff0000, 0xff00, 0xff, 0x0),
(0xff0000, 0xff00, 0xff, None),
(0xff0000, 0xff00, 0xff, 0xff000000),
(0x0, 0x0, 0x0, 0x0)],
24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
}
# From inspecting DIB files from Vista screenshots, the
# file format is a 32 bit color image, where the 'Alpha'
# channel for screenshots is actually a transparency mask
# (255-alpha) where 00 is solid and ff is transparent. I
# can't find documentation of this, but it matches
# imagemagick's conversion of the .dib file into a .png.
MASK_MODES = {
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
(32, (0xff0000, 0xff00, 0xff, None)): "BGRT",
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
(24, (0xff0000, 0xff00, 0xff)): "BGR",
@ -145,7 +160,7 @@ class BmpImageFile(ImageFile.ImageFile):
if file_info['bits'] in SUPPORTED:
if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
self.mode = "RGBA" if raw_mode in ("BGRA", "BGRT") else self.mode
elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
else:

View File

@ -20,6 +20,7 @@
TAGS = {
# possibly incomplete
0x000b: "ProcessingSoftware",
0x00fe: "NewSubfileType",
0x00ff: "SubfileType",
0x0100: "ImageWidth",
@ -32,7 +33,6 @@ TAGS = {
0x0109: "CellLength",
0x010a: "FillOrder",
0x010d: "DocumentName",
0x011d: "PageName",
0x010e: "ImageDescription",
0x010f: "Make",
0x0110: "Model",
@ -46,35 +46,74 @@ TAGS = {
0x011a: "XResolution",
0x011b: "YResolution",
0x011c: "PlanarConfiguration",
0x011d: "PageName",
0x0120: "FreeOffsets",
0x0121: "FreeByteCounts",
0x0122: "GrayResponseUnit",
0x0123: "GrayResponseCurve",
0x0124: "T4Options",
0x0125: "T6Options",
0x0128: "ResolutionUnit",
0x0129: "PageNumber",
0x012d: "TransferFunction",
0x0131: "Software",
0x0132: "DateTime",
0x013b: "Artist",
0x013c: "HostComputer",
0x013d: "Predictor",
0x013e: "WhitePoint",
0x013f: "PrimaryChromaticities",
0x0140: "ColorMap",
0x0141: "HalftoneHints",
0x0142: "TileWidth",
0x0143: "TileLength",
0x0144: "TileOffsets",
0x0145: "TileByteCounts",
0x014a: "SubIFDs",
0x014c: "InkSet",
0x014d: "InkNames",
0x014e: "NumberOfInks",
0x0150: "DotRange",
0x0151: "TargetPrinter",
0x0152: "ExtraSamples",
0x0153: "SampleFormat",
0x0154: "SMinSampleValue",
0x0155: "SMaxSampleValue",
0x0156: "TransferRange",
0x0157: "ClipPath",
0x0158: "XClipPathUnits",
0x0159: "YClipPathUnits",
0x015a: "Indexed",
0x015b: "JPEGTables",
0x015f: "OPIProxy",
0x0200: "JPEGProc",
0x0201: "JpegIFOffset",
0x0202: "JpegIFByteCount",
0x0203: "JpegRestartInterval",
0x0205: "JpegLosslessPredictors",
0x0206: "JpegPointTransforms",
0x0207: "JpegQTables",
0x0208: "JpegDCTables",
0x0209: "JpegACTables",
0x0211: "YCbCrCoefficients",
0x0212: "YCbCrSubSampling",
0x0213: "YCbCrPositioning",
0x0214: "ReferenceBlackWhite",
0x02bc: "XMLPacket",
0x1000: "RelatedImageFileFormat",
0x1001: "RelatedImageWidth",
0x1002: "RelatedImageLength",
0x4746: "Rating",
0x4749: "RatingPercent",
0x800d: "ImageID",
0x828d: "CFARepeatPatternDim",
0x828e: "CFAPattern",
0x828f: "BatteryLevel",
0x8298: "Copyright",
0x829a: "ExposureTime",
0x829d: "FNumber",
0x83bb: "IPTCNAA",
0x8649: "ImageResources",
0x8769: "ExifOffset",
0x8773: "InterColorProfile",
0x8822: "ExposureProgram",
@ -114,6 +153,11 @@ TAGS = {
0x9290: "SubsecTime",
0x9291: "SubsecTimeOriginal",
0x9292: "SubsecTimeDigitized",
0x9c9b: "XPTitle",
0x9c9c: "XPComment",
0x9c9d: "XPAuthor",
0x9c9e: "XPKeywords",
0x9c9f: "XPSubject",
0xa000: "FlashPixVersion",
0xa001: "ColorSpace",
0xa002: "ExifImageWidth",
@ -151,7 +195,85 @@ TAGS = {
0xa434: "LensModel",
0xa435: "LensSerialNumber",
0xa500: "Gamma",
0xc4a5: "PrintImageMatching",
0xc612: "DNGVersion",
0xc613: "DNGBackwardVersion",
0xc614: "UniqueCameraModel",
0xc615: "LocalizedCameraModel",
0xc616: "CFAPlaneColor",
0xc617: "CFALayout",
0xc618: "LinearizationTable",
0xc619: "BlackLevelRepeatDim",
0xc61a: "BlackLevel",
0xc61b: "BlackLevelDeltaH",
0xc61c: "BlackLevelDeltaV",
0xc61d: "WhiteLevel",
0xc61e: "DefaultScale",
0xc61f: "DefaultCropOrigin",
0xc620: "DefaultCropSize",
0xc621: "ColorMatrix1",
0xc622: "ColorMatrix2",
0xc623: "CameraCalibration1",
0xc624: "CameraCalibration2",
0xc625: "ReductionMatrix1",
0xc626: "ReductionMatrix2",
0xc627: "AnalogBalance",
0xc628: "AsShotNeutral",
0xc629: "AsShotWhiteXY",
0xc62a: "BaselineExposure",
0xc62b: "BaselineNoise",
0xc62c: "BaselineSharpness",
0xc62d: "BayerGreenSplit",
0xc62e: "LinearResponseLimit",
0xc62f: "CameraSerialNumber",
0xc630: "LensInfo",
0xc631: "ChromaBlurRadius",
0xc632: "AntiAliasStrength",
0xc633: "ShadowScale",
0xc634: "DNGPrivateData",
0xc635: "MakerNoteSafety",
0xc65a: "CalibrationIlluminant1",
0xc65b: "CalibrationIlluminant2",
0xc65c: "BestQualityScale",
0xc65d: "RawDataUniqueID",
0xc68b: "OriginalRawFileName",
0xc68c: "OriginalRawFileData",
0xc68d: "ActiveArea",
0xc68e: "MaskedAreas",
0xc68f: "AsShotICCProfile",
0xc690: "AsShotPreProfileMatrix",
0xc691: "CurrentICCProfile",
0xc692: "CurrentPreProfileMatrix",
0xc6bf: "ColorimetricReference",
0xc6f3: "CameraCalibrationSignature",
0xc6f4: "ProfileCalibrationSignature",
0xc6f6: "AsShotProfileName",
0xc6f7: "NoiseReductionApplied",
0xc6f8: "ProfileName",
0xc6f9: "ProfileHueSatMapDims",
0xc6fa: "ProfileHueSatMapData1",
0xc6fb: "ProfileHueSatMapData2",
0xc6fc: "ProfileToneCurve",
0xc6fd: "ProfileEmbedPolicy",
0xc6fe: "ProfileCopyright",
0xc714: "ForwardMatrix1",
0xc715: "ForwardMatrix2",
0xc716: "PreviewApplicationName",
0xc717: "PreviewApplicationVersion",
0xc718: "PreviewSettingsName",
0xc719: "PreviewSettingsDigest",
0xc71a: "PreviewColorSpace",
0xc71b: "PreviewDateTime",
0xc71c: "RawImageDigest",
0xc71d: "OriginalRawFileDigest",
0xc71e: "SubTileBlockSize",
0xc71f: "RowInterleaveFactor",
0xc725: "ProfileLookTableDims",
0xc726: "ProfileLookTableData",
0xc740: "OpcodeList1",
0xc741: "OpcodeList2",
0xc74e: "OpcodeList3",
0xc761: "NoiseProfile"
}
##

View File

@ -55,7 +55,7 @@ class GbrImageFile(ImageFile.ImageFile):
if width <= 0 or height <= 0:
raise SyntaxError("not a GIMP brush")
if color_depth not in (1, 4):
raise SyntaxError("Unsupported GMP brush color depth: %s" % color_depth)
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
if version == 1:
comment_length = header_size-20

View File

@ -1,7 +1,7 @@
# The Python Imaging Library.
# $Id$
# Optional color managment support, based on Kevin Cazabon's PyCMS
# Optional color management support, based on Kevin Cazabon's PyCMS
# library.
# History:

View File

@ -640,7 +640,7 @@ def _save(im, fp, filename):
try:
if len(table) != 64:
raise
table = array.array('b', table)
table = array.array('B', table)
except TypeError:
raise ValueError("Invalid quantization table")
else:

View File

@ -540,16 +540,16 @@ class OleMetadata(object):
OLE file.
References for SummaryInformation stream:
- http://msdn.microsoft.com/en-us/library/dd942545.aspx
- http://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx
- http://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx
- http://msdn.microsoft.com/en-us/library/aa372045.aspx
- http://sedna-soft.de/summary-information-stream/
- https://msdn.microsoft.com/en-us/library/dd942545.aspx
- https://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx
- https://msdn.microsoft.com/en-us/library/aa372045.aspx
- http://sedna-soft.de/articles/summary-information-stream/
- http://poi.apache.org/apidocs/org/apache/poi/hpsf/SummaryInformation.html
References for DocumentSummaryInformation stream:
- http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx
- http://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx
- https://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx
- http://poi.apache.org/apidocs/org/apache/poi/hpsf/DocumentSummaryInformation.html
new in version 0.25

View File

@ -29,6 +29,10 @@ __version__ = "0.1"
i16 = _binary.i16le
def _accept(prefix):
return prefix[:4] == b"\200\350\000\000"
##
# Image plugin for PIXAR raster images.
@ -39,7 +43,7 @@ class PixarImageFile(ImageFile.ImageFile):
def _open(self):
# assuming a 4-byte magic label (FIXME: add "_accept" hook)
# assuming a 4-byte magic label
s = self.fp.read(4)
if s != b"\200\350\000\000":
raise SyntaxError("not a PIXAR file")
@ -62,7 +66,6 @@ class PixarImageFile(ImageFile.ImageFile):
#
# --------------------------------------------------------------------
Image.register_open(PixarImageFile.format, PixarImageFile)
Image.register_open(PixarImageFile.format, PixarImageFile, _accept)
#
# FIXME: what's the standard extension?
Image.register_extension(PixarImageFile.format, ".pxr")

View File

@ -36,6 +36,7 @@ from __future__ import print_function
import logging
import re
import zlib
import struct
from PIL import Image, ImageFile, ImagePalette, _binary
@ -106,6 +107,7 @@ class ChunkStream(object):
def read(self):
"Fetch a new chunk. Returns header information."
cid = None
if self.queue:
cid, pos, length = self.queue[-1]
@ -116,7 +118,7 @@ class ChunkStream(object):
cid = s[4:]
pos = self.fp.tell()
length = i32(s)
if not is_cid(cid):
raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
@ -138,11 +140,15 @@ class ChunkStream(object):
def crc(self, cid, data):
"Read and verify checksum"
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
if crc1 != crc2:
raise SyntaxError("broken PNG file"
"(bad header checksum in %s)" % cid)
try:
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
if crc1 != crc2:
raise SyntaxError("broken PNG file (bad header checksum in %s)"
% cid)
except struct.error:
raise SyntaxError("broken PNG file (incomplete checksum in %s)"
% cid)
def crc_skip(self, cid, data):
"Read checksum. Used if the C module is not present"
@ -157,7 +163,11 @@ class ChunkStream(object):
cids = []
while True:
cid, pos, length = self.read()
try:
cid, pos, length = self.read()
except struct.error:
raise IOError("truncated PNG file")
if cid == endchunk:
break
self.crc(cid, ImageFile._safe_read(self.fp, length))

View File

@ -173,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile):
def seek(self, frame):
if self.istack == 0:
return
raise EOFError("attempt to seek in a non-stack file")
if frame >= self._nimages:
raise EOFError("attempt to seek past end of file")
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)

View File

@ -141,34 +141,56 @@ OPEN_INFO = {
(MM, 0, (1,), 1, (1,), ()): ("1", "1;I"),
(II, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
(MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"),
(II, 1, (1,), 1, (1,), ()): ("1", "1"),
(MM, 1, (1,), 1, (1,), ()): ("1", "1"),
(II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
(MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
(II, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
(MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
(II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
(MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
(II, 1, (1,), 1, (2,), ()): ("L", "L;2"),
(MM, 1, (1,), 1, (2,), ()): ("L", "L;2"),
(II, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
(MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
(II, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
(MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
(II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
(MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
(II, 1, (1,), 1, (4,), ()): ("L", "L;4"),
(MM, 1, (1,), 1, (4,), ()): ("L", "L;4"),
(II, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
(MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
(II, 0, (1,), 1, (8,), ()): ("L", "L;I"),
(MM, 0, (1,), 1, (8,), ()): ("L", "L;I"),
(II, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
(MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
(II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
(MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
(II, 1, (1,), 1, (1,), ()): ("1", "1"),
(MM, 1, (1,), 1, (1,), ()): ("1", "1"),
(II, 1, (1,), 1, (4,), ()): ("L", "L;4"),
# ?
(II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
(MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
(II, 1, (1,), 1, (8,), ()): ("L", "L"),
(MM, 1, (1,), 1, (8,), ()): ("L", "L"),
(II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
(MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
(II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
(MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
(II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
(II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
(MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
(II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"),
(MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"),
(II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
(MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
(II, 1, (1,), 1, (32,), ()): ("I", "I;32N"),
(II, 1, (2,), 1, (32,), ()): ("I", "I;32S"),
(MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"),
(II, 1, (3,), 1, (32,), ()): ("F", "F;32F"),
(MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"),
(II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
(MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
(II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
(MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
(II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"),
@ -183,6 +205,7 @@ OPEN_INFO = {
(MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
(II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
(MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
(II, 3, (1,), 1, (1,), ()): ("P", "P;1"),
(MM, 3, (1,), 1, (1,), ()): ("P", "P;1"),
(II, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
@ -201,10 +224,13 @@ OPEN_INFO = {
(MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
(II, 3, (1,), 2, (8,), ()): ("P", "P;R"),
(MM, 3, (1,), 2, (8,), ()): ("P", "P;R"),
(II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
(MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
(II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
(MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
(II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
}

View File

@ -188,10 +188,28 @@ TAGS = {347: 'JPEGTables',
700: 'XMP',
# Additional Exif Info
32932: 'Wang Annotation',
33434: 'ExposureTime',
33437: 'FNumber',
33445: 'MD FileTag',
33446: 'MD ScalePixel',
33447: 'MD ColorTable',
33448: 'MD LabName',
33449: 'MD SampleInfo',
33450: 'MD PrepDate',
33451: 'MD PrepTime',
33452: 'MD FileUnits',
33550: 'ModelPixelScaleTag',
33723: 'IptcNaaInfo',
33918: 'INGR Packet Data Tag',
33919: 'INGR Flag Registers',
33920: 'IrasB Transformation Matrix',
33922: 'ModelTiepointTag',
34264: 'ModelTransformationTag',
34377: 'PhotoshopInfo',
34735: 'GeoKeyDirectoryTag',
34736: 'GeoDoubleParamsTag',
34737: 'GeoAsciiParamsTag',
34850: 'ExposureProgram',
34852: 'SpectralSensitivity',
34855: 'ISOSpeedRatings',
@ -202,11 +220,15 @@ TAGS = {347: 'JPEGTables',
34867: 'ISOSpeed',
34868: 'ISOSpeedLatitudeyyy',
34869: 'ISOSpeedLatitudezzz',
34908: 'HylaFAX FaxRecvParams',
34909: 'HylaFAX FaxSubAddress',
34910: 'HylaFAX FaxRecvTime',
36864: 'ExifVersion',
36867: 'DateTimeOriginal',
36868: 'DateTImeDigitized',
37121: 'ComponentsConfiguration',
37122: 'CompressedBitsPerPixel',
37724: 'ImageSourceData',
37377: 'ShutterSpeedValue',
37378: 'ApertureValue',
37379: 'BrightnessValue',
@ -259,7 +281,13 @@ TAGS = {347: 'JPEGTables',
42035: 'LensMake',
42036: 'LensModel',
42037: 'LensSerialNumber',
42112: 'GDAL_METADATA',
42113: 'GDAL_NODATA',
42240: 'Gamma',
50215: 'Oce Scanjob Description',
50216: 'Oce Application Selector',
50217: 'Oce Identification Number',
50218: 'Oce ImageLogic Characteristics',
# Adobe DNG
50706: 'DNGVersion',
@ -298,6 +326,7 @@ TAGS = {347: 'JPEGTables',
50740: 'DNGPrivateData',
50778: 'CalibrationIlluminant1',
50779: 'CalibrationIlluminant2',
50784: 'Alias Layer Metadata'
}

View File

@ -6,41 +6,56 @@ Python Imaging Library (Fork)
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
.. image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
.. start-badges
.. image:: https://readthedocs.org/projects/pillow/badge/?version=latest
.. list-table::
:stub-columns: 1
* - docs
- |docs|
* - tests
- | |linux| |osx| |windows| |coverage| |health|
* - package
- |zenodo| |version| |downloads|
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
:target: http://pillow.readthedocs.org/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
.. |linux| image:: https://img.shields.io/travis/python-pillow/Pillow/master.svg?label=Linux%20build
:target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status (Linux)
.. image:: https://img.shields.io/travis/python-pillow/pillow-wheels/latest.svg?label=OS%20X%20build
.. |osx| image:: https://img.shields.io/travis/python-pillow/pillow-wheels/latest.svg?label=OS%20X%20build
:target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (OS X)
.. image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
.. |windows| image:: https://img.shields.io/appveyor/ci/python-pillow/Pillow/master.svg?label=Windows%20build
:target: https://ci.appveyor.com/project/python-pillow/Pillow
:alt: AppVeyor CI build status (Windows)
.. image:: https://img.shields.io/pypi/v/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Latest PyPI version
.. image:: https://img.shields.io/pypi/dm/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
.. |coverage| image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/python-pillow/Pillow?branch=master
:alt: Code coverage
.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg
.. |health| image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.svg
:target: https://landscape.io/github/python-pillow/Pillow/master
:alt: Code health
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Latest PyPI version
.. |downloads| image:: https://img.shields.io/pypi/dm/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads
.. end-badges
More Information
----------------
@ -50,7 +65,7 @@ More Information
- `Installation <https://pillow.readthedocs.org/en/latest/installation.html>`_
- `Handbook <https://pillow.readthedocs.org/en/latest/handbook/index.html>`_
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/CONTRIBUTING.md>`_
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_

BIN
Tests/images/clipboard.dib Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
Tests/images/hopper_jpg.tif Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -24,7 +24,7 @@ class TestBmpReference(PillowTestCase):
# print ("Bad Image %s: %s" %(f,msg))
def test_questionable(self):
""" These shouldn't crash/dos, but its not well defined that these
""" These shouldn't crash/dos, but it's not well defined that these
are in spec """
for f in self.get_files('q'):
try:

View File

@ -70,6 +70,13 @@ class TestFileBmp(PillowTestCase):
self.assertEqual(im.size, reloaded.size)
self.assertEqual(reloaded.format, "JPEG")
def test_load_dib(self):
# test for #1293, Imagegrab returning Unsupported Bitfields Format
im = BmpImagePlugin.DibImageFile('Tests/images/clipboard.dib')
target = Image.open('Tests/images/clipboard_target.png')
self.assert_image_equal(im.convert('RGB'), target.convert('RGB'))
self.assert_image_similar(im, target, 1)
if __name__ == '__main__':
unittest.main()

View File

@ -7,18 +7,6 @@ TEST_FILE = "Tests/images/iptc.jpg"
class TestFileIptc(PillowTestCase):
# Helpers
def dummy_IptcImagePlugin(self):
# Create an IptcImagePlugin object without initializing it
class FakeImage(object):
pass
im = FakeImage()
im.__class__ = IptcImagePlugin.IptcImageFile
return im
# Tests
def test_getiptcinfo_jpg_none(self):
# Arrange
im = hopper()

View File

@ -314,6 +314,10 @@ class TestFileJpeg(PillowTestCase):
30)
self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30)
# valid bounds for baseline qtable
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
self.roundtrip(im, qtables=[bounds_qtable])
# values from wizard.txt in jpeg9-a src package.
standard_l_qtable = [int(s) for s in """
16 11 10 16 24 40 51 61

View File

@ -419,6 +419,39 @@ class TestFileLibTiff(LibTiffTestCase):
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, 7.3)
def test_gray_semibyte_per_pixel(self):
test_files = (
(
24.8,#epsilon
(#group
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif",
)
),
(
7.3,#epsilon
(#group
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif",
)
),
)
original = hopper("L")
for epsilon, group in test_files:
im = Image.open(group[0])
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, epsilon)
for file in group[1:]:
im2 = Image.open(file)
self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.mode, "L")
self.assert_image_equal(im, im2)
def test_save_bytesio(self):
# PR 1011
# Test TIFF saving to io.BytesIO() object.

View File

@ -253,6 +253,22 @@ class TestFilePng(PillowTestCase):
im.load()
self.assertRaises(RuntimeError, im.verify)
def test_verify_struct_error(self):
# Check open/load/verify exception (#1755)
# offsets to test, -10: breaks in i32() in read. (IOError)
# -13: breaks in crc, txt chunk.
# -14: malformed chunk
for offset in (-10, -13, -14):
with open(TEST_PNG_FILE,'rb') as f:
test_file = f.read()[:offset]
im = Image.open(BytesIO(test_file))
self.assertTrue(im.fp is not None)
self.assertRaises((IOError, SyntaxError), im.verify)
def test_roundtrip_dpi(self):
# Check dpi roundtripping

View File

@ -1,6 +1,7 @@
from helper import unittest, PillowTestCase, hopper
from PIL import Image
from PIL import ImageSequence
from PIL import SpiderImagePlugin
TEST_FILE = "Tests/images/hopper.spider"
@ -85,6 +86,17 @@ class TestImageSpider(PillowTestCase):
self.assertRaises(IOError, lambda: Image.open(invalid_file))
def test_nonstack_file(self):
im = Image.open(TEST_FILE)
self.assertRaises(EOFError, lambda: im.seek(0))
def test_nonstack_dos(self):
im = Image.open(TEST_FILE)
for i, frame in enumerate(ImageSequence.Iterator(im)):
if i > 1:
self.fail("Non-stack DOS file test failed")
if __name__ == '__main__':
unittest.main()

View File

@ -337,6 +337,39 @@ class TestFileTiff(PillowTestCase):
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, 7.3)
def test_gray_semibyte_per_pixel(self):
test_files = (
(
24.8,#epsilon
(#group
"Tests/images/tiff_gray_2_4_bpp/hopper2.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif",
)
),
(
7.3,#epsilon
(#group
"Tests/images/tiff_gray_2_4_bpp/hopper4.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4I.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4R.tif",
"Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif",
)
),
)
original = hopper("L")
for epsilon, group in test_files:
im = Image.open(group[0])
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.mode, "L")
self.assert_image_similar(im, original, epsilon)
for file in group[1:]:
im2 = Image.open(file)
self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.mode, "L")
self.assert_image_equal(im, im2)
def test_page_number_x_0(self):
# Issue 973
# Test TIFF with tag 297 (Page Number) having value of 0 0.
@ -404,6 +437,19 @@ class TestFileTiff(PillowTestCase):
self.assertEqual(im.size, (10, 10))
im.load()
def test_save_tiff_with_jpegtables(self):
# Arrange
outfile = self.tempfile("temp.tif")
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
# Contains JPEGTables (347) tag
infile = "Tests/images/hopper_jpg.tif"
im = Image.open(infile)
# Act / Assert
# Should not raise UnicodeDecodeError or anything else
im.save(outfile)
if __name__ == '__main__':
unittest.main()

View File

@ -280,6 +280,8 @@ int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view)
/* error messages */
static const char* must_be_sequence = "argument must be a sequence";
static const char* must_be_two_coordinates =
"coordinate list must contain exactly 2 coordinates";
static const char* wrong_mode = "unrecognized image mode";
static const char* wrong_raw_mode = "unrecognized raw mode";
static const char* outside_image = "image index out of range";
@ -2398,9 +2400,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
return NULL;
}
@ -2472,9 +2472,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
return NULL;
}
@ -2509,9 +2507,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
return NULL;
}
@ -2692,9 +2688,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
return NULL;
}
@ -2774,9 +2768,7 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
if (n < 0)
return NULL;
if (n != 2) {
PyErr_SetString(PyExc_TypeError,
"coordinate list must contain exactly 2 coordinates"
);
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
return NULL;
}

View File

@ -21,6 +21,7 @@ install:
build_script:
- '%PYTHON%\python.exe c:\pillow\winbuild\build.py'
- cd c:\pillow
- dir dist\*.egg
- '%PYTHON%\python.exe selftest.py --installed'
test_script:
- cd c:\pillow
@ -28,3 +29,17 @@ test_script:
- '%PYTHON%\python.exe test-installed.py'
matrix:
fast_finish: true
artifacts:
- path: pillow\dist\*.egg
name: build
after_test:
- ps: Get-ChildItem .\dist\*.egg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
deploy:
provider: S3
access_key_id: AKIAIRAXC62ZNTVQJMOQ
secret_access_key:
secure: Hwb6klTqtBeMgxAjRoDltiiqpuH8xbwD4UooDzBSiCWXjuFj1lyl4kHgHwTCCGqi
bucket: pillow-nightly
folder: win/$(APPVEYOR_BUILD_NUMBER)/
artifact: /.*egg/

View File

@ -13,6 +13,6 @@ sudo dnf install redhat-rpm-config
sudo dnf install python-devel python3-devel python-virtualenv make gcc
sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \
lcms2-devel libwebp-devel openjpeg2-devel tkinter python3-tkinter \
tcl-devel tk-devel

View File

@ -24,7 +24,7 @@ To get the number and names of bands in an image, use the
Modes
-----
The :term:`mode` of an image defines the type and depth of a pixel in the
The ``mode`` of an image defines the type and depth of a pixel in the
image. The current release supports the following standard modes:
* ``1`` (1-bit pixels, black and white, stored with one pixel per byte)

View File

@ -400,26 +400,15 @@ Note that most drivers in the current version of the library only allow you to
seek to the next frame (as in the above example). To rewind the file, you may
have to reopen it.
The following iterator class lets you use the for-statement to loop over the
sequence:
The following class lets you use the for-statement to loop over the sequence:
A sequence iterator class
^^^^^^^^^^^^^^^^^^^^^^^^^
Using the ImageSequence Iterator class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
class ImageSequence:
def __init__(self, im):
self.im = im
def __getitem__(self, ix):
try:
if ix:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError # end of sequence
for frame in ImageSequence(im):
from PIL import ImageSequence
for frame in ImageSequence.Iterator(im):
# ...do something to frame...

View File

@ -202,6 +202,11 @@ Build Options
the libraries are not found. Webpmux (WebP metadata) relies on WebP
support. Tcl and Tk also must be used together.
* Build flags: ``--debug``. Adds a debugging flag to the include and
library search process to dump all paths searched for and found to
stdout.
Sample Usage::
$ MAX_CONCURRENCY=1 python setup.py build_ext --enable-[feature] install
@ -288,7 +293,7 @@ Prerequisites are installed on **Ubuntu 14.04 LTS** with::
Prerequisites are installed on **Fedora 23** with::
$ sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
$ sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \
lcms2-devel libwebp-devel tcl-devel tk-devel

View File

@ -81,13 +81,13 @@ Bitwise operators dont work on floating point images.
Logical Operators
^^^^^^^^^^^^^^^^^
Logical operators like :keyword:`and`, :keyword:`or`, and :keyword:`not` work
Logical operators like ``and``, ``or``, and ``not`` work
on entire images, rather than individual pixels.
An empty image (all pixels zero) is treated as false. All other images are
treated as true.
Note that :keyword:`and` and :keyword:`or` return the last evaluated operand,
Note that ``and`` and ``or`` return the last evaluated operand,
while not always returns a boolean value.
Built-in Functions

View File

@ -48,7 +48,7 @@ off the end of the memory buffer, causing a segfault.
This issue was found by Alyssa Besseling at Atlassian
CVE-2016-2533 -- Buffer overflow in PcdDecode.c
----------------------------------------------
-----------------------------------------------
In all versions of Pillow, dating back at least to the last PIL 1.1.7
release, ``PcdDecode.c`` has a buffer overflow error.
@ -64,7 +64,7 @@ Integer overflow in Resample.c
If a large value was passed into the new size for an image, it is
possible to overflow an int32 value passed into malloc.
kk = malloc(xsize * kmax * sizeof(float));
...
xbounds = malloc(xsize * 2 * sizeof(int));

View File

@ -191,14 +191,64 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels)
static void
unpackL2(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles */
/* nibbles (msb first, white is non-zero) */
while (pixels > 0) {
UINT8 byte = *in++;
switch (pixels) {
default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
case 1: *out++ = ((byte >> 6) & 3) * 255 / 3;
default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U;
}
pixels -= 4;
}
}
static void
unpackL2I(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (msb first, white is zero) */
while (pixels > 0) {
UINT8 byte = *in++;
switch (pixels) {
default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U);
}
pixels -= 4;
}
}
static void
unpackL2R(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (bit order reversed, white is non-zero) */
while (pixels > 0) {
UINT8 byte = *in++;
byte = BITFLIP[byte];
switch (pixels) {
default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2;
case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U;
}
pixels -= 4;
}
}
static void
unpackL2IR(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (bit order reversed, white is zero) */
while (pixels > 0) {
UINT8 byte = *in++;
byte = BITFLIP[byte];
switch (pixels) {
default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2;
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U);
}
pixels -= 4;
}
@ -207,12 +257,56 @@ unpackL2(UINT8* out, const UINT8* in, int pixels)
static void
unpackL4(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles */
/* nibbles (msb first, white is non-zero) */
while (pixels > 0) {
UINT8 byte = *in++;
switch (pixels) {
default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4;
case 1: *out++ = ((byte >> 4) & 15) * 255 / 15;
default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4;
case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U;
}
pixels -= 2;
}
}
static void
unpackL4I(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (msb first, white is zero) */
while (pixels > 0) {
UINT8 byte = *in++;
switch (pixels) {
default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4;
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U);
}
pixels -= 2;
}
}
static void
unpackL4R(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (bit order reversed, white is non-zero) */
while (pixels > 0) {
UINT8 byte = *in++;
byte = BITFLIP[byte];
switch (pixels) {
default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4;
case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U;
}
pixels -= 2;
}
}
static void
unpackL4IR(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles (bit order reversed, white is zero) */
while (pixels > 0) {
UINT8 byte = *in++;
byte = BITFLIP[byte];
switch (pixels) {
default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4;
case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U);
}
pixels -= 2;
}
@ -738,6 +832,21 @@ unpackBGRA(UINT8* out, const UINT8* in, int pixels)
}
}
static void
unpackBGRT(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* RGBA, reversed bytes */
/* Transparency map, rather than an alpha channel */
for (i = 0; i < pixels; i++) {
out[R] = in[2];
out[G] = in[1];
out[B] = in[0];
out[A] = 255 - in[3];
out += 4; in += 4;
}
}
/* Unpack to "CMYK" image */
@ -1053,7 +1162,15 @@ static struct {
/* greyscale */
{"L", "L;2", 2, unpackL2},
{"L", "L;2I", 2, unpackL2I},
{"L", "L;2R", 2, unpackL2R},
{"L", "L;2IR", 2, unpackL2IR},
{"L", "L;4", 4, unpackL4},
{"L", "L;4I", 4, unpackL4I},
{"L", "L;4R", 4, unpackL4R},
{"L", "L;4IR", 4, unpackL4IR},
{"L", "L", 8, copy1},
{"L", "L;I", 8, unpackLI},
{"L", "L;R", 8, unpackLR},
@ -1111,6 +1228,7 @@ static struct {
{"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B},
{"RGBA", "RGBA;16B", 64, unpackRGBA16B},
{"RGBA", "BGRA", 32, unpackBGRA},
{"RGBA", "BGRT", 32, unpackBGRT},
{"RGBA", "ARGB", 32, unpackARGB},
{"RGBA", "ABGR", 32, unpackABGR},
{"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},

152
setup.py
View File

@ -37,21 +37,35 @@ _LIB_IMAGING = (
"XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental",
"Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur")
DEBUG = False
def _add_directory(path, dir, where=None):
if dir is None:
def _dbg(s, tp=None):
if DEBUG:
if tp:
print(s % tp)
return
print(s)
def _add_directory(path, subdir, where=None):
if subdir is None:
return
dir = os.path.realpath(dir)
if os.path.isdir(dir) and dir not in path:
subdir = os.path.realpath(subdir)
if os.path.isdir(subdir) and subdir not in path:
if where is None:
path.append(dir)
_dbg('Appending path %s', subdir)
path.append(subdir)
else:
path.insert(where, dir)
_dbg('Inserting path %s', subdir)
path.insert(where, subdir)
def _find_include_file(self, include):
for directory in self.compiler.include_dirs:
_dbg('Checking for include file %s in %s', (include, directory))
if os.path.isfile(os.path.join(directory, include)):
_dbg('Found %s', include)
return 1
return 0
@ -61,15 +75,22 @@ def _find_library_file(self, library):
# lib extension, not the system shared lib extension: e.g. .cpython-33.so
# vs .so. See Python bug http://bugs.python.org/16754
if 'cpython' in self.compiler.shared_lib_extension:
_dbg('stripping cpython from shared library extension %s',
self.compiler.shared_lib_extension)
existing = self.compiler.shared_lib_extension
self.compiler.shared_lib_extension = "." + existing.split('.')[-1]
ret = self.compiler.find_library_file(self.compiler.library_dirs,
library)
self.compiler.shared_lib_extension = existing
return ret
else:
return self.compiler.find_library_file(self.compiler.library_dirs,
library)
ret = self.compiler.find_library_file(self.compiler.library_dirs,
library)
if ret:
_dbg('Found library %s at %s', (library, ret))
else:
_dbg("Couldn't find library %s in %s",
(library, self.compiler.library_dirs))
return ret
def _lib_include(root):
@ -100,10 +121,15 @@ LCMS_ROOT = None
class pil_build_ext(build_ext):
class feature:
zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
jpeg2000 = None
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'tcl', 'tk', 'lcms',
'webp', 'webpmux', 'jpeg2000']
required = set(['jpeg', 'zlib'])
def __init__(self):
for f in self.features:
setattr(self, f, None)
def require(self, feat):
return feat in self.required
@ -111,9 +137,8 @@ class pil_build_ext(build_ext):
return getattr(self, feat) is None
def __iter__(self):
for x in dir(self):
if x[1] != '_':
yield x
for x in self.features:
yield x
feature = feature()
@ -121,7 +146,7 @@ class pil_build_ext(build_ext):
('disable-%s' % x, None, 'Disable support for %s' % x) for x in feature
] + [
('enable-%s' % x, None, 'Enable support for %s' % x) for x in feature
]
] + [('debug', None, 'Debug logging')]
def initialize_options(self):
build_ext.initialize_options(self)
@ -131,15 +156,20 @@ class pil_build_ext(build_ext):
def finalize_options(self):
build_ext.finalize_options(self)
if self.debug:
global DEBUG
DEBUG = True
for x in self.feature:
if getattr(self, 'disable_%s' % x):
setattr(self.feature, x, False)
self.feature.required.discard(x)
_dbg('Disabling %s', x)
if getattr(self, 'enable_%s' % x):
raise ValueError(
'Conflicting options: --enable-%s and --disable-%s' %
(x, x))
if getattr(self, 'enable_%s' % x):
_dbg('Requiring %s', x)
self.feature.required.add(x)
def build_extensions(self):
@ -314,6 +344,9 @@ class pil_build_ext(build_ext):
if _tkinter:
TCL_VERSION = _tkinter.TCL_VERSION[:3]
_dbg('Tkinter found, will check for Tcl/Tk')
else:
_dbg('Tkinter not found')
if _tkinter and not TCL_ROOT:
# we have Tkinter but the TCL_ROOT variable was not set;
@ -334,6 +367,7 @@ class pil_build_ext(build_ext):
]
for TCL_ROOT in roots:
TCL_ROOT = os.path.abspath(TCL_ROOT)
_dbg('Checking %s for tk.h', TCL_ROOT)
if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")):
# FIXME: use distutils logging (?)
print("--- using Tcl/Tk libraries at", TCL_ROOT)
@ -342,6 +376,7 @@ class pil_build_ext(build_ext):
break
else:
TCL_ROOT = None
_dbg('Tcl/tk not found')
# add standard directories
@ -373,6 +408,7 @@ class pil_build_ext(build_ext):
best_path = os.path.join(program_files, name)
if best_path:
_dbg('Adding %s to search list', best_path)
_add_directory(library_dirs, os.path.join(best_path, 'lib'))
_add_directory(include_dirs,
os.path.join(best_path, 'include'))
@ -390,6 +426,7 @@ class pil_build_ext(build_ext):
feature = self.feature
if feature.want('zlib'):
_dbg('Looking for zlib')
if _find_include_file(self, "zlib.h"):
if _find_library_file(self, "z"):
feature.zlib = "z"
@ -398,6 +435,7 @@ class pil_build_ext(build_ext):
feature.zlib = "zlib" # alternative name
if feature.want('jpeg'):
_dbg('Looking for jpeg')
if _find_include_file(self, "jpeglib.h"):
if _find_library_file(self, "jpeg"):
feature.jpeg = "jpeg"
@ -407,11 +445,13 @@ class pil_build_ext(build_ext):
feature.openjpeg_version = None
if feature.want('jpeg2000'):
_dbg('Looking for jpeg2000')
best_version = None
best_path = None
# Find the best version
for directory in self.compiler.include_dirs:
_dbg('Checking for openjpeg-#.# in %s', directory)
try:
listdir = os.listdir(directory)
except Exception:
@ -421,10 +461,13 @@ class pil_build_ext(build_ext):
if name.startswith('openjpeg-') and \
os.path.isfile(os.path.join(directory, name,
'openjpeg.h')):
_dbg('Found openjpeg.h in %s/%s', (directory, name))
version = tuple([int(x) for x in name[9:].split('.')])
if best_version is None or version > best_version:
best_version = version
best_path = os.path.join(directory, name)
_dbg('Best openjpeg version %s so far in %s',
(best_version, best_path))
if best_version and _find_library_file(self, 'openjp2'):
# Add the directory to the include path so we can include
@ -436,34 +479,42 @@ class pil_build_ext(build_ext):
best_version])
if feature.want('tiff'):
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"
_dbg('Looking for tiff')
if _find_include_file(self, 'tiff.h'):
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 feature.want('freetype'):
_dbg('Looking for freetype')
if _find_library_file(self, "freetype"):
# look for freetype2 include files
freetype_version = 0
for dir in self.compiler.include_dirs:
if os.path.isfile(os.path.join(dir, "ft2build.h")):
for subdir in self.compiler.include_dirs:
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
_dbg('Found %s in %s', ("ft2build.h", subdir))
freetype_version = 21
dir = os.path.join(dir, "freetype2")
subdir = os.path.join(subdir, "freetype2")
break
dir = os.path.join(dir, "freetype2")
if os.path.isfile(os.path.join(dir, "ft2build.h")):
subdir = os.path.join(subdir, "freetype2")
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
_dbg('Found %s in %s', ("ft2build.h", subdir))
freetype_version = 21
break
if freetype_version:
feature.freetype = "freetype"
feature.freetype_version = freetype_version
if dir:
_add_directory(self.compiler.include_dirs, dir, 0)
if subdir:
_add_directory(self.compiler.include_dirs, subdir, 0)
if feature.want('lcms'):
_dbg('Looking for lcms')
if _find_include_file(self, "lcms2.h"):
if _find_library_file(self, "lcms2"):
feature.lcms = "lcms2"
@ -475,17 +526,20 @@ class pil_build_ext(build_ext):
# the library names may vary somewhat (e.g. tcl85 or tcl8.5)
version = TCL_VERSION[0] + TCL_VERSION[2]
if feature.want('tcl'):
_dbg('Looking for TCL')
if _find_library_file(self, "tcl" + version):
feature.tcl = "tcl" + version
elif _find_library_file(self, "tcl" + TCL_VERSION):
feature.tcl = "tcl" + TCL_VERSION
if feature.want('tk'):
_dbg('Looking for TK')
if _find_library_file(self, "tk" + version):
feature.tk = "tk" + version
elif _find_library_file(self, "tk" + TCL_VERSION):
feature.tk = "tk" + TCL_VERSION
if feature.want('webp'):
_dbg('Looking for webp')
if (_find_include_file(self, "webp/encode.h") and
_find_include_file(self, "webp/decode.h")):
# In Google's precompiled zip it is call "libwebp":
@ -495,6 +549,7 @@ class pil_build_ext(build_ext):
feature.webp = "libwebp"
if feature.want('webpmux'):
_dbg('Looking for webpmux')
if (_find_include_file(self, "webp/mux.h") and
_find_include_file(self, "webp/demux.h")):
if (_find_library_file(self, "webpmux") and
@ -518,10 +573,10 @@ class pil_build_ext(build_ext):
# core library
files = ["_imaging.c"]
for file in _IMAGING:
files.append(file + ".c")
for file in _LIB_IMAGING:
files.append(os.path.join("libImaging", file + ".c"))
for src_file in _IMAGING:
files.append(src_file + ".c")
for src_file in _LIB_IMAGING:
files.append(os.path.join("libImaging", src_file + ".c"))
libs = []
defs = []
@ -557,7 +612,7 @@ class pil_build_ext(build_ext):
["_imagingft.c"],
libraries=["freetype"]))
if os.path.isfile("_imagingcms.c") and feature.lcms:
if feature.lcms:
extra = []
if sys.platform == "win32":
extra.extend(["user32", "gdi32"])
@ -565,7 +620,7 @@ class pil_build_ext(build_ext):
["_imagingcms.c"],
libraries=[feature.lcms] + extra))
if os.path.isfile("_webp.c") and feature.webp:
if feature.webp:
libs = [feature.webp]
defs = []
@ -586,16 +641,17 @@ class pil_build_ext(build_ext):
framework_roots = [
"/Library/Frameworks", "/System/Library/Frameworks"
]
_dbg('Looking for TclTk Framework Build')
for root in framework_roots:
root_tcl = os.path.join(root, "Tcl.framework")
root_tk = os.path.join(root, "Tk.framework")
if (os.path.exists(root_tcl) and os.path.exists(root_tk)):
print("--- using frameworks at %s" % root)
frameworks = ["-framework", "Tcl", "-framework", "Tk"]
dir = os.path.join(root_tcl, "Headers")
_add_directory(self.compiler.include_dirs, dir, 0)
dir = os.path.join(root_tk, "Headers")
_add_directory(self.compiler.include_dirs, dir, 1)
subdir = os.path.join(root_tcl, "Headers")
_add_directory(self.compiler.include_dirs, subdir, 0)
subdir = os.path.join(root_tk, "Headers")
_add_directory(self.compiler.include_dirs, subdir, 1)
break
if frameworks:
exts.append(Extension("PIL._imagingtk",
@ -607,11 +663,8 @@ class pil_build_ext(build_ext):
["_imagingtk.c", "Tk/tkImaging.c"],
libraries=[feature.tcl, feature.tk]))
if os.path.isfile("_imagingmath.c"):
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
if os.path.isfile("_imagingmorph.c"):
exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"]))
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"]))
self.extensions[:] = exts
@ -680,8 +733,8 @@ class pil_build_ext(build_ext):
if not all:
print("To add a missing option, make sure you have the required")
print("library, and set the corresponding ROOT variable in the")
print("setup.py script.")
print("library and headers.")
print("See https://pillow.readthedocs.org/en/latest/installation.html#building-from-source")
print("")
print("To check the build, run the selftest.py script.")
@ -689,8 +742,8 @@ class pil_build_ext(build_ext):
def check_zlib_version(self, include_dirs):
# look for unsafe versions of zlib
for dir in include_dirs:
zlibfile = os.path.join(dir, "zlib.h")
for subdir in include_dirs:
zlibfile = os.path.join(subdir, "zlib.h")
if os.path.isfile(zlibfile):
break
else:
@ -702,7 +755,7 @@ class pil_build_ext(build_ext):
if m.group(1) < "1.2.3":
return m.group(1)
# http://hg.python.org/users/barry/rev/7e8deab93d5a
# https://hg.python.org/users/barry/rev/7e8deab93d5a
def add_multiarch_paths(self):
# Debian/Ubuntu multiarch support.
# https://wiki.ubuntu.com/MultiarchSpec
@ -741,7 +794,6 @@ setup(name=NAME,
"Development Status :: 6 - Mature",
"Topic :: Multimedia :: Graphics",
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
"Topic :: Multimedia :: Graphics :: Capture :: Scanners",
"Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Multimedia :: Graphics :: Viewers",