Merge remote-tracking branch 'upstream/master' into MspImagePlugin

Conflicts:
	Tests/helper.py
This commit is contained in:
Hugo 2014-09-30 13:05:58 +03:00
commit 8fbd18a706
197 changed files with 2156 additions and 1306 deletions

View File

@ -5,15 +5,18 @@ notifications:
env: MAX_CONCURRENCY=4
# Run slow PyPy* first, to give them a headstart and reduce waiting time.
# Run latest 3.x and 2.x next, to get quick compatibility results.
# Then run the remainder.
python:
- "pypy"
- "pypy3"
- 2.6
- 3.4
- 2.7
- 2.6
- "2.7_with_system_site_packages" # For PyQt4
- 3.2
- 3.3
- 3.4
install:
- "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick"
@ -57,6 +60,7 @@ after_success:
- travis_retry pip install pep8 pyflakes
- pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py
- pyflakes *.py | tee >(wc -l)
- pyflakes PIL/*.py | tee >(wc -l)
- pyflakes Tests/*.py | tee >(wc -l)

View File

@ -4,13 +4,40 @@ Changelog (Pillow)
2.6.0 (unreleased)
------------------
- Jpeg2k Decode/Encode Memory Leak Fix #898
- Use redistributable image for testing #884
[hugovk]
- Use redistributable ICC profiles for testing, skip if not available #923
[wiredfool]
- Additional documentation for JPEG info and save options #890
[wiredfool]
- Fix JPEG Encoding memory leak when exif or qtables were specified
[wiredfool]
- Image.tobytes() and Image.tostring() documentation update #916 #917
[mgedmin]
- On Windows, do not execute convert.exe without specifying path #912
[cgohlke]
- Fix msvc build error #911
[cgohlke]
- Fix for handling P + transparency -> RGBA conversions #904
[wiredfool]
- Retain alpha in ImageEnhance operations #909
[wiredfool]
- Jpeg2k Decode/encode memory leak fix #898
[joshware, wiredfool]
- EpsFilePlugin Speed improvements #886
[wiredfool, karstenw]
- Don't resize if already the right size.
- Don't resize if already the right size #892
[radarhere]
- Fix for reading multipage TIFFs #885

View File

@ -7,7 +7,7 @@ Send a pull request. We'll generally want documentation and [tests](Tests/README
- Fork the repo
- Make a branch
- Add your changes + Tests
- Run the test suite. Try to run on both Python 2.x and 3.x, or you'll get tripped up. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request.
- Run the test suite. Try to run on both Python 2.x and 3.x, or you'll get tripped up. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests.
- Push to your fork, and make a pull request.
A few guidelines:

View File

@ -26,12 +26,12 @@ from PIL import FontFile
# --------------------------------------------------------------------
bdf_slant = {
"R": "Roman",
"I": "Italic",
"O": "Oblique",
"RI": "Reverse Italic",
"RO": "Reverse Oblique",
"OT": "Other"
"R": "Roman",
"I": "Italic",
"O": "Oblique",
"RI": "Reverse Italic",
"RO": "Reverse Oblique",
"OT": "Other"
}
bdf_spacing = {
@ -40,8 +40,8 @@ bdf_spacing = {
"C": "Cell"
}
def bdf_char(f):
def bdf_char(f):
# skip to STARTCHAR
while True:
s = f.readline()
@ -82,6 +82,7 @@ def bdf_char(f):
return id, int(props["ENCODING"]), bbox, im
##
# Font file plugin for the X11 BDF format.
@ -113,10 +114,10 @@ class BdfFontFile(FontFile.FontFile):
font[4] = bdf_slant[font[4].upper()]
font[11] = bdf_spacing[font[11].upper()]
ascent = int(props["FONT_ASCENT"])
descent = int(props["FONT_DESCENT"])
# ascent = int(props["FONT_ASCENT"])
# descent = int(props["FONT_DESCENT"])
fontname = ";".join(font[1:])
# fontname = ";".join(font[1:])
# print "#", fontname
# for i in comments:

View File

@ -51,9 +51,11 @@ BIT2MODE = {
32: ("RGB", "BGRX")
}
def _accept(prefix):
return prefix[:2] == b"BM"
##
# Image plugin for the Windows BMP format.
@ -62,8 +64,7 @@ class BmpImageFile(ImageFile.ImageFile):
format = "BMP"
format_description = "Windows Bitmap"
def _bitmap(self, header = 0, offset = 0):
def _bitmap(self, header=0, offset=0):
if header:
self.fp.seek(header)
@ -97,8 +98,9 @@ class BmpImageFile(ImageFile.ImageFile):
# upside-down storage
self.size = self.size[0], 2**32 - self.size[1]
direction = 0
self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm))
self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701),
pxperm))
else:
raise IOError("Unsupported BMP header type (%d)" % len(s))
@ -137,7 +139,7 @@ class BmpImageFile(ImageFile.ImageFile):
greyscale = 1
if colors == 2:
indices = (0, 255)
elif colors > 2**16 or colors <=0: #We're reading a i32.
elif colors > 2**16 or colors <= 0: # We're reading a i32.
raise IOError("Unsupported BMP Palette size (%d)" % colors)
else:
indices = list(range(colors))
@ -163,7 +165,8 @@ class BmpImageFile(ImageFile.ImageFile):
self.tile = [("raw",
(0, 0) + self.size,
offset,
(rawmode, ((self.size[0]*bits+31)>>3)&(~3), direction))]
(rawmode, ((self.size[0]*bits+31) >> 3) & (~3),
direction))]
self.info["compression"] = compression
@ -197,8 +200,8 @@ SAVE = {
"RGB": ("BGR", 24, 0),
}
def _save(im, fp, filename, check=0):
def _save(im, fp, filename, check=0):
try:
rawmode, bits, colors = SAVE[im.mode]
except KeyError:
@ -214,10 +217,10 @@ def _save(im, fp, filename, check=0):
# 1 meter == 39.3701 inches
ppm = tuple(map(lambda x: int(x * 39.3701), dpi))
stride = ((im.size[0]*bits+7)//8+3)&(~3)
header = 40 # or 64 for OS/2 version 2
stride = ((im.size[0]*bits+7)//8+3) & (~3)
header = 40 # or 64 for OS/2 version 2
offset = 14 + header + colors * 4
image = stride * im.size[1]
image = stride * im.size[1]
# bitmap header
fp.write(b"BM" + # file type (magic)
@ -248,7 +251,8 @@ def _save(im, fp, filename, check=0):
elif im.mode == "P":
fp.write(im.im.getpalette("RGB", "BGRX"))
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, stride, -1))])
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0,
(rawmode, stride, -1))])
#
# --------------------------------------------------------------------

View File

@ -13,6 +13,7 @@ from PIL import Image, ImageFile
_handler = None
##
# Install application-specific BUFR image handler.
#
@ -22,12 +23,14 @@ def register_handler(handler):
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
class BufrStubImageFile(ImageFile.StubImageFile):
format = "BUFR"
@ -53,6 +56,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("BUFR save handler not installed")

View File

@ -18,6 +18,7 @@
# A file object that provides read access to a part of an existing
# file (for example a TAR file).
class ContainerIO:
##
@ -48,7 +49,7 @@ class ContainerIO:
# for current offset, and 2 for end of region. You cannot move
# the pointer outside the defined region.
def seek(self, offset, mode = 0):
def seek(self, offset, mode=0):
if mode == 1:
self.pos = self.pos + offset
elif mode == 2:
@ -75,12 +76,12 @@ class ContainerIO:
# read until end of region.
# @return An 8-bit string.
def read(self, n = 0):
def read(self, n=0):
if n:
n = min(n, self.length - self.pos)
else:
n = self.length - self.pos
if not n: # EOF
if not n: # EOF
return ""
self.pos = self.pos + n
return self.fh.read(n)

View File

@ -11,7 +11,8 @@
# 1996-08-23 fl Handle files from Macintosh (0.3)
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
# 2014-05-07 e Handling of EPS with binary preview and fixed resolution resizing
# 2014-05-07 e Handling of EPS with binary preview and fixed resolution
# resizing
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2003 by Fredrik Lundh
@ -51,20 +52,21 @@ if sys.platform.startswith('win'):
else:
gs_windows_binary = False
def has_ghostscript():
if gs_windows_binary:
return True
if not sys.platform.startswith('win'):
import subprocess
try:
gs = subprocess.Popen(['gs','--version'], stdout=subprocess.PIPE)
gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE)
gs.stdout.read()
return True
except OSError:
# no ghostscript
pass
return False
def Ghostscript(tile, size, fp, scale=1):
"""Render an image using Ghostscript"""
@ -72,17 +74,20 @@ def Ghostscript(tile, size, fp, scale=1):
# Unpack decoder tile
decoder, tile, offset, data = tile[0]
length, bbox = data
#Hack to support hi-res rendering
scale = int(scale) or 1
orig_size = size
orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
# resolution is dependend on bbox and size
res = ( float((72.0 * size[0]) / (bbox[2]-bbox[0])), float((72.0 * size[1]) / (bbox[3]-bbox[1])) )
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
import tempfile, os, subprocess
# Hack to support hi-res rendering
scale = int(scale) or 1
# orig_size = size
# orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
# resolution is dependent on bbox and size
res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
# print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
import os
import subprocess
import tempfile
out_fd, outfile = tempfile.mkstemp()
os.close(out_fd)
@ -94,9 +99,9 @@ def Ghostscript(tile, size, fp, scale=1):
in_fd, infile_temp = tempfile.mkstemp()
os.close(in_fd)
infile = infile_temp
# ignore length and offset!
# ghostscript can read it
# ghostscript can read it
# copy whole file to read in ghostscript
with open(infile_temp, 'wb') as f:
# fetch length of fp
@ -115,17 +120,18 @@ def Ghostscript(tile, size, fp, scale=1):
# Build ghostscript command
command = ["gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dNOPAUSE -dSAFER", # don't pause between pages,
# safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin
"-f", infile, # input file
]
# adjust for image origin
"-f", infile, # input file
]
if gs_windows_binary is not None:
if not gs_windows_binary:
raise WindowsError('Unable to locate Ghostscript on paths')
@ -133,7 +139,8 @@ def Ghostscript(tile, size, fp, scale=1):
# push data through ghostscript
try:
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
gs = subprocess.Popen(command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
gs.stdin.close()
status = gs.wait()
if status:
@ -142,21 +149,26 @@ def Ghostscript(tile, size, fp, scale=1):
finally:
try:
os.unlink(outfile)
if infile_temo:
if infile_temp:
os.unlink(infile_temp)
except: pass
except:
pass
return im
class PSFile:
"""Wrapper for bytesio object 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 seek(self, offset, whence=0):
self.char = None
self.fp.seek(offset, whence)
def readline(self):
s = self.char or b""
self.char = None
@ -170,8 +182,9 @@ class PSFile:
# 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')
return s.decode('latin-1')
def _accept(prefix):
return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5
@ -180,13 +193,14 @@ def _accept(prefix):
# Image plugin for Encapsulated Postscript. This plugin supports only
# a few variants of this format.
class EpsImageFile(ImageFile.ImageFile):
"""EPS File Parser for the Python Imaging Library"""
format = "EPS"
format_description = "Encapsulated Postscript"
mode_map = { 1:"L", 2:"LAB", 3:"RGB" }
mode_map = {1: "L", 2: "LAB", 3: "RGB"}
def _open(self):
(length, offset) = self._find_offset(self.fp)
@ -198,9 +212,9 @@ class EpsImageFile(ImageFile.ImageFile):
# Python2, no encoding conversion necessary
fp = open(self.fp.name, "Ur")
else:
# Python3, can use bare open command.
# Python3, can use bare open command.
fp = open(self.fp.name, "Ur", encoding='latin-1')
except Exception as msg:
except:
# Expect this for bytesio/stringio
fp = PSFile(self.fp)
@ -210,13 +224,13 @@ class EpsImageFile(ImageFile.ImageFile):
box = None
self.mode = "RGB"
self.size = 1, 1 # FIXME: huh?
self.size = 1, 1 # FIXME: huh?
#
# Load EPS header
s = fp.readline().strip('\r\n')
while s:
if len(s) > 255:
raise SyntaxError("not an EPS file")
@ -236,7 +250,7 @@ class EpsImageFile(ImageFile.ImageFile):
# put floating point values there anyway.
box = [int(float(s)) for s in v.split()]
self.size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0,0) + self.size, offset,
self.tile = [("eps", (0, 0) + self.size, offset,
(length, box))]
except:
pass
@ -264,7 +278,6 @@ class EpsImageFile(ImageFile.ImageFile):
if s[0] != "%":
break
#
# Scan for an "ImageData" descriptor
@ -283,10 +296,10 @@ class EpsImageFile(ImageFile.ImageFile):
self.mode = self.mode_map[int(mo)]
except:
break
self.size = int(x), int(y)
return
s = fp.readline().strip('\r\n')
if not s:
break
@ -295,19 +308,20 @@ class EpsImageFile(ImageFile.ImageFile):
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
# 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
# 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:
@ -324,11 +338,12 @@ class EpsImageFile(ImageFile.ImageFile):
self.size = self.im.size
self.tile = []
def load_seek(self,*args,**kwargs):
def load_seek(self, *args, **kwargs):
# we can't incrementally load, so force ImageFile.parser to
# use our custom load method by defining this method.
# use our custom load method by defining this method.
pass
#
# --------------------------------------------------------------------
@ -353,8 +368,10 @@ def _save(im, fp, filename, eps=1):
class NoCloseStream:
def __init__(self, fp):
self.fp = fp
def __getattr__(self, name):
return getattr(self.fp, name)
def close(self):
pass
@ -368,7 +385,7 @@ def _save(im, fp, filename, eps=1):
# write EPS header
fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
fp.write("%%Creator: PIL 0.1 EpsEncode\n")
#fp.write("%%CreationDate: %s"...)
# fp.write("%%CreationDate: %s"...)
fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
fp.write("%%Pages: 1\n")
fp.write("%%EndComments\n")
@ -382,13 +399,13 @@ def _save(im, fp, filename, eps=1):
fp.write("10 dict begin\n")
fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
fp.write("%d %d scale\n" % im.size)
fp.write("%d %d 8\n" % im.size) # <= bits
fp.write("%d %d 8\n" % im.size) # <= bits
fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
fp.write("{ currentfile buf readhexstring pop } bind\n")
fp.write(operator[2] + "\n")
fp.flush()
ImageFile._save(im, base_fp, [("eps", (0,0)+im.size, 0, None)])
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
fp.write("\n%%%%EndBinary\n")
fp.write("grestore end\n")

View File

@ -25,12 +25,14 @@ i16 = _binary.i16le
i32 = _binary.i32le
o8 = _binary.o8
#
# decoder
def _accept(prefix):
return i16(prefix[4:6]) in [0xAF11, 0xAF12]
##
# Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
# method to load individual frames.
@ -47,7 +49,7 @@ class FliImageFile(ImageFile.ImageFile):
magic = i16(s[4:6])
if not (magic in [0xAF11, 0xAF12] and
i16(s[14:16]) in [0, 3] and # flags
s[20:22] == b"\x00\x00"): # reserved
s[20:22] == b"\x00\x00"): # reserved
raise SyntaxError("not an FLI/FLC file")
# image characteristics
@ -61,7 +63,7 @@ class FliImageFile(ImageFile.ImageFile):
self.info["duration"] = duration
# look for palette
palette = [(a,a,a) for a in range(256)]
palette = [(a, a, a) for a in range(256)]
s = self.fp.read(16)
@ -80,7 +82,7 @@ class FliImageFile(ImageFile.ImageFile):
elif i16(s[4:6]) == 4:
self._palette(palette, 0)
palette = [o8(r)+o8(g)+o8(b) for (r,g,b) in palette]
palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
self.palette = ImagePalette.raw("RGB", b"".join(palette))
# set things up to decode first frame
@ -124,7 +126,7 @@ class FliImageFile(ImageFile.ImageFile):
framesize = i32(s)
self.decodermaxblock = framesize
self.tile = [("fli", (0,0)+self.size, self.__offset, None)]
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
self.__offset = self.__offset + framesize

View File

@ -34,16 +34,18 @@ MODES = {
(0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"),
(0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"),
# standard RGB (NIFRGB)
(0x00030000, 0x00030001, 0x00030002): ("RGB","RGB"),
(0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA","RGBA"),
(0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"),
(0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"),
}
#
# --------------------------------------------------------------------
def _accept(prefix):
return prefix[:8] == MAGIC
##
# Image plugin for the FlashPix images.
@ -67,7 +69,7 @@ class FpxImageFile(ImageFile.ImageFile):
self._open_index(1)
def _open_index(self, index = 1):
def _open_index(self, index=1):
#
# get the Image Contents Property Set
@ -95,7 +97,7 @@ class FpxImageFile(ImageFile.ImageFile):
id = self.maxid << 16
s = prop[0x2000002|id]
s = prop[0x2000002 | id]
colors = []
for i in range(i32(s, 4)):
@ -107,7 +109,7 @@ class FpxImageFile(ImageFile.ImageFile):
# load JPEG tables, if any
self.jpeg = {}
for i in range(256):
id = 0x3000001|(i << 16)
id = 0x3000001 | (i << 16)
if id in prop:
self.jpeg[i] = prop[id]
@ -115,7 +117,7 @@ class FpxImageFile(ImageFile.ImageFile):
self._open_subimage(1, self.maxid)
def _open_subimage(self, index = 1, subimage = 0):
def _open_subimage(self, index=1, subimage=0):
#
# setup tile descriptors for a given subimage
@ -159,14 +161,14 @@ class FpxImageFile(ImageFile.ImageFile):
compression = i32(s, i+8)
if compression == 0:
self.tile.append(("raw", (x,y,x+xtile,y+ytile),
i32(s, i) + 28, (self.rawmode)))
self.tile.append(("raw", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (self.rawmode)))
elif compression == 1:
# FIXME: the fill decoder is not implemented
self.tile.append(("fill", (x,y,x+xtile,y+ytile),
i32(s, i) + 28, (self.rawmode, s[12:16])))
self.tile.append(("fill", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (self.rawmode, s[12:16])))
elif compression == 2:
@ -182,14 +184,14 @@ class FpxImageFile(ImageFile.ImageFile):
# this problem :
jpegmode, rawmode = "YCbCrK", "CMYK"
else:
jpegmode = None # let the decoder decide
jpegmode = None # let the decoder decide
else:
# The image is stored as defined by rawmode
jpegmode = rawmode
self.tile.append(("jpeg", (x,y,x+xtile,y+ytile),
i32(s, i) + 28, (rawmode, jpegmode)))
self.tile.append(("jpeg", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (rawmode, jpegmode)))
# FIXME: jpeg tables are tile dependent; the prefix
# data must be placed in the tile descriptor itself!
@ -204,7 +206,7 @@ class FpxImageFile(ImageFile.ImageFile):
if x >= xsize:
x, y = 0, y + ytile
if y >= ysize:
break # isn't really required
break # isn't really required
self.stream = stream
self.fp = None
@ -212,7 +214,8 @@ class FpxImageFile(ImageFile.ImageFile):
def load(self):
if not self.fp:
self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])
self.fp = self.ole.openstream(self.stream[:2] +
["Subimage 0000 Data"])
ImageFile.ImageFile.load(self)

View File

@ -17,9 +17,11 @@ from PIL import Image, ImageFile, _binary
i32 = _binary.i32be
def _accept(prefix):
return i32(prefix) >= 20 and i32(prefix[4:8]) == 1
##
# Image plugin for the GIMP brush format.

View File

@ -36,6 +36,7 @@ except ImportError:
i16 = _binary.i16be
##
# Image plugin for the GD uncompressed format. Note that this format
# is not supported by the standard <b>Image.open</b> function. To use
@ -52,7 +53,7 @@ class GdImageFile(ImageFile.ImageFile):
# Header
s = self.fp.read(775)
self.mode = "L" # FIXME: "P"
self.mode = "L" # FIXME: "P"
self.size = i16(s[0:2]), i16(s[2:4])
# transparency index
@ -62,7 +63,8 @@ class GdImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("RGB", s[7:])
self.tile = [("raw", (0,0)+self.size, 775, ("L", 0, -1))]
self.tile = [("raw", (0, 0)+self.size, 775, ("L", 0, -1))]
##
# Load texture from a GD image file.
@ -73,7 +75,7 @@ class GdImageFile(ImageFile.ImageFile):
# @return An image instance.
# @exception IOError If the image could not be read.
def open(fp, mode = "r"):
def open(fp, mode="r"):
if mode != "r":
raise ValueError("bad mode")

View File

@ -46,6 +46,7 @@ o16 = _binary.o16le
def _accept(prefix):
return prefix[:6] in [b"GIF87a", b"GIF89a"]
##
# Image plugin for GIF images. This plugin supports both GIF87 and
# GIF89 images.
@ -79,16 +80,16 @@ class GifImageFile(ImageFile.ImageFile):
# get global palette
self.info["background"] = i8(s[11])
# check if palette contains colour indices
p = self.fp.read(3<<bits)
p = self.fp.read(3 << bits)
for i in range(0, len(p), 3):
if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])):
p = ImagePalette.raw("RGB", p)
self.global_palette = self.palette = p
break
self.__fp = self.fp # FIXME: hack
self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell()
self.seek(0) # get ready to read first frame
self.seek(0) # get ready to read first frame
def seek(self, frame):
@ -96,7 +97,7 @@ class GifImageFile(ImageFile.ImageFile):
# rewind
self.__offset = 0
self.dispose = None
self.dispose_extent = [0, 0, 0, 0] #x0, y0, x1, y1
self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1
self.__frame = -1
self.__fp.seek(self.__rewind)
self._prev_im = None
@ -185,7 +186,7 @@ class GifImageFile(ImageFile.ImageFile):
if flags & 128:
bits = (flags & 7) + 1
self.palette =\
ImagePalette.raw("RGB", self.fp.read(3<<bits))
ImagePalette.raw("RGB", self.fp.read(3 << bits))
# image data
bits = i8(self.fp.read(1))
@ -219,7 +220,6 @@ class GifImageFile(ImageFile.ImageFile):
except (AttributeError, KeyError):
pass
if not self.tile:
# self.__fp = None
raise EOFError("no more images in GIF file")
@ -240,7 +240,8 @@ class GifImageFile(ImageFile.ImageFile):
# we do this by pasting the updated area onto the previous
# frame which we then use as the current image content
updated = self.im.crop(self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent, updated.convert('RGBA'))
self._prev_im.paste(updated, self.dispose_extent,
updated.convert('RGBA'))
self.im = self._prev_im
self._prev_im = self.im.copy()
@ -258,6 +259,7 @@ RAWMODE = {
"P": "P",
}
def _save(im, fp, filename):
if _imaging_gif:
@ -266,7 +268,7 @@ def _save(im, fp, filename):
_imaging_gif.save(im, fp, filename)
return
except IOError:
pass # write uncompressed file
pass # write uncompressed file
if im.mode in RAWMODE:
imOut = im
@ -343,15 +345,17 @@ def _save(im, fp, filename):
o8(8)) # bits
imOut.encoderconfig = (8, interlace)
ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, RAWMODE[imOut.mode])])
ImageFile._save(imOut, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[imOut.mode])])
fp.write(b"\0") # end of image data
fp.write(b"\0") # end of image data
fp.write(b";") # end of file
fp.write(b";") # end of file
try:
fp.flush()
except: pass
except:
pass
def _save_netpbm(im, fp, filename):
@ -363,7 +367,7 @@ def _save_netpbm(im, fp, filename):
import os
from subprocess import Popen, check_call, PIPE, CalledProcessError
import tempfile
import tempfile
file = im._dump()
if im.mode != "RGB":
@ -380,8 +384,9 @@ def _save_netpbm(im, fp, filename):
stderr = tempfile.TemporaryFile()
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
stderr = tempfile.TemporaryFile()
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=stderr)
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f,
stderr=stderr)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close()
@ -420,7 +425,7 @@ def getheader(im, palette=None, info=None):
sourcePalette = palette[:768]
else:
sourcePalette = im.im.getpalette("RGB")[:768]
else: # L-mode
else: # L-mode
if palette and isinstance(palette, bytes):
sourcePalette = palette[:768]
else:
@ -455,9 +460,11 @@ def getheader(im, palette=None, info=None):
for i in range(len(imageBytes)):
imageBytes[i] = newPositions[imageBytes[i]]
im.frombytes(bytes(imageBytes))
newPaletteBytes = paletteBytes + (768 - len(paletteBytes)) * b'\x00'
im.putpalette(newPaletteBytes)
im.palette = ImagePalette.ImagePalette("RGB", palette = paletteBytes, size = len(paletteBytes))
newPaletteBytes = (paletteBytes +
(768 - len(paletteBytes)) * b'\x00')
im.putpalette(newPaletteBytes)
im.palette = ImagePalette.ImagePalette("RGB", palette=paletteBytes,
size=len(paletteBytes))
if not paletteBytes:
paletteBytes = sourcePalette
@ -466,7 +473,8 @@ def getheader(im, palette=None, info=None):
# calculate the palette size for the header
import math
colorTableSize = int(math.ceil(math.log(len(paletteBytes)//3, 2)))-1
if colorTableSize < 0: colorTableSize = 0
if colorTableSize < 0:
colorTableSize = 0
# size of global color table + global color table flag
header.append(o8(colorTableSize + 128))
# background + reserved/aspect
@ -475,7 +483,7 @@ def getheader(im, palette=None, info=None):
# add the missing amount of bytes
# the palette has to be 2<<n in size
actualTargetSizeDiff = (2<<colorTableSize) - len(paletteBytes)//3
actualTargetSizeDiff = (2 << colorTableSize) - len(paletteBytes)//3
if actualTargetSizeDiff > 0:
paletteBytes += o8(0) * 3 * actualTargetSizeDiff
@ -484,17 +492,18 @@ def getheader(im, palette=None, info=None):
return header, usedPaletteColors
def getdata(im, offset = (0, 0), **params):
def getdata(im, offset=(0, 0), **params):
"""Return a list of strings representing this image.
The first string is a local image header, the rest contains
encoded image data."""
class collector:
data = []
def write(self, data):
self.data.append(data)
im.load() # make sure raster data is available
im.load() # make sure raster data is available
fp = collector()
@ -510,9 +519,9 @@ def getdata(im, offset = (0, 0), **params):
o8(0) + # flags
o8(8)) # bits
ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])])
fp.write(b"\0") # end of image data
fp.write(b"\0") # end of image data
finally:
del im.encoderinfo

View File

@ -17,6 +17,7 @@
import re
from PIL._binary import o8
##
# File handler for GIMP's palette format.
@ -56,7 +57,6 @@ class GimpPaletteFile:
self.palette = b"".join(self.palette)
def getpalette(self):
return self.palette, self.rawmode

View File

@ -13,6 +13,7 @@ from PIL import Image, ImageFile
_handler = None
##
# Install application-specific GRIB image handler.
#
@ -22,12 +23,14 @@ def register_handler(handler):
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01'
class GribStubImageFile(ImageFile.StubImageFile):
format = "GRIB"
@ -53,6 +56,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("GRIB save handler not installed")

View File

@ -13,6 +13,7 @@ from PIL import Image, ImageFile
_handler = None
##
# Install application-specific HDF5 image handler.
#
@ -22,12 +23,14 @@ def register_handler(handler):
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:8] == b"\x89HDF\r\n\x1a\n"
class HDF5StubImageFile(ImageFile.StubImageFile):
format = "HDF5"

View File

@ -16,7 +16,8 @@
#
from PIL import Image, ImageFile, PngImagePlugin, _binary
import struct, io
import io
import struct
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k:
@ -26,9 +27,11 @@ i8 = _binary.i8
HEADERSIZE = 8
def nextheader(fobj):
return struct.unpack('>4sI', fobj.read(HEADERSIZE))
def read_32t(fobj, start_length, size):
# The 128x128 icon seems to have an extra header for some reason.
(start, length) = start_length
@ -38,6 +41,7 @@ def read_32t(fobj, start_length, size):
raise SyntaxError('Unknown signature, expecting 0x00000000')
return read_32(fobj, (start + 4, length - 4), size)
def read_32(fobj, start_length, size):
"""
Read a 32bit RGB icon resource. Seems to be either uncompressed or
@ -83,6 +87,7 @@ def read_32(fobj, start_length, size):
im.im.putband(band.im, band_ix)
return {"RGB": im}
def read_mk(fobj, start_length, size):
# Alpha masks seem to be uncompressed
(start, length) = start_length
@ -94,6 +99,7 @@ def read_mk(fobj, start_length, size):
)
return {"A": band}
def read_png_or_jpeg2000(fobj, start_length, size):
(start, length) = start_length
fobj.seek(start)
@ -103,10 +109,11 @@ def read_png_or_jpeg2000(fobj, start_length, size):
im = PngImagePlugin.PngImageFile(fobj)
return {"RGBA": im}
elif sig[:4] == b'\xff\x4f\xff\x51' \
or sig[:4] == b'\x0d\x0a\x87\x0a' \
or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
or sig[:4] == b'\x0d\x0a\x87\x0a' \
or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
if not enable_jpeg2k:
raise ValueError('Unsupported icon subimage format (rebuild PIL with JPEG 2000 support to fix this)')
raise ValueError('Unsupported icon subimage format (rebuild PIL '
'with JPEG 2000 support to fix this)')
# j2k, jpc or j2c
fobj.seek(start)
jp2kstream = fobj.read(length)
@ -118,6 +125,7 @@ def read_png_or_jpeg2000(fobj, start_length, size):
else:
raise ValueError('Unsupported icon subimage format')
class IcnsFile:
SIZES = {
@ -225,7 +233,7 @@ class IcnsFile:
im = channels.get('RGBA', None)
if im:
return im
im = channels.get("RGB").copy()
try:
im.putalpha(channels["A"])
@ -233,6 +241,7 @@ class IcnsFile:
pass
return im
##
# Image plugin for Mac OS icons.
@ -275,7 +284,7 @@ class IcnsImageFile(ImageFile.ImageFile):
# If this is a PNG or JPEG 2000, it won't be loaded yet
im.load()
self.im = im.im
self.mode = im.mode
self.size = im.size
@ -288,7 +297,8 @@ Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns')
Image.register_extension("ICNS", '.icns')
if __name__ == '__main__':
import os, sys
import os
import sys
imf = IcnsImageFile(open(sys.argv[1], 'rb'))
for size in imf.info['sizes']:
imf.size = size

View File

@ -13,7 +13,8 @@
# See the README file for information on usage and redistribution.
#
# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis <casadebender@gmail.com>.
# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
# <casadebender@gmail.com>.
# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin
#
# Icon format references:
@ -35,6 +36,7 @@ i32 = _binary.i32le
_MAGIC = b"\0\0\1\0"
def _accept(prefix):
return prefix[:4] == _MAGIC
@ -63,7 +65,7 @@ class IcoFile:
icon_header = {
'width': i8(s[0]),
'height': i8(s[1]),
'nb_color': i8(s[2]), # Number of colors in image (0 if >=8bpp)
'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp)
'reserved': i8(s[3]),
'planes': i16(s[4:]),
'bpp': i16(s[6:]),
@ -78,10 +80,14 @@ class IcoFile:
# See Wikipedia notes about color depth.
# We need this just to differ images with equal sizes
icon_header['color_depth'] = (icon_header['bpp'] or (icon_header['nb_color'] != 0 and ceil(log(icon_header['nb_color'],2))) or 256)
icon_header['color_depth'] = (icon_header['bpp'] or
(icon_header['nb_color'] != 0 and
ceil(log(icon_header['nb_color'],
2))) or 256)
icon_header['dim'] = (icon_header['width'], icon_header['height'])
icon_header['square'] = icon_header['width'] * icon_header['height']
icon_header['square'] = (icon_header['width'] *
icon_header['height'])
self.entry.append(icon_header)
@ -102,7 +108,7 @@ class IcoFile:
Get an image from the icon
"""
for (i, h) in enumerate(self.entry):
if size == h['dim'] and (bpp == False or bpp == h['color_depth']):
if size == h['dim'] and (bpp is False or bpp == h['color_depth']):
return self.frame(i)
return self.frame(0)
@ -127,7 +133,7 @@ class IcoFile:
# change tile dimension to only encompass XOR image
im.size = (im.size[0], int(im.size[1] / 2))
d, e, o, a = im.tile[0]
im.tile[0] = d, (0,0) + im.size, o, a
im.tile[0] = d, (0, 0) + im.size, o, a
# figure out where AND mask image starts
mode = a[0]
@ -139,8 +145,9 @@ class IcoFile:
if 32 == bpp:
# 32-bit color depth icon image allows semitransparent areas
# PIL's DIB format ignores transparency bits, recover them
# The DIB is packed in BGRX byte order where X is the alpha channel
# PIL's DIB format ignores transparency bits, recover them.
# The DIB is packed in BGRX byte order where X is the alpha
# channel.
# Back up to start of bmp data
self.buf.seek(o)
@ -162,9 +169,11 @@ class IcoFile:
# bitmap row data is aligned to word boundaries
w += 32 - (im.size[0] % 32)
# the total mask data is padded row size * height / bits per char
# the total mask data is
# padded row size * height / bits per char
and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0))
and_mask_offset = o + int(im.size[0] * im.size[1] *
(bpp / 8.0))
total_bytes = int((w * im.size[1]) / 8)
self.buf.seek(and_mask_offset)
@ -187,6 +196,7 @@ class IcoFile:
return im
##
# Image plugin for Windows Icon files.
@ -194,15 +204,16 @@ class IcoImageFile(ImageFile.ImageFile):
"""
PIL read-only image support for Microsoft Windows .ico files.
By default the largest resolution image in the file will be loaded. This can
be changed by altering the 'size' attribute before calling 'load'.
By default the largest resolution image in the file will be loaded. This
can be changed by altering the 'size' attribute before calling 'load'.
The info dictionary has a key 'sizes' that is a list of the sizes available
in the icon file.
Handles classic, XP and Vista icon formats.
This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis <casadebender@gmail.com>.
This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
<casadebender@gmail.com>.
https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin
"""
format = "ICO"
@ -222,9 +233,9 @@ class IcoImageFile(ImageFile.ImageFile):
self.mode = im.mode
self.size = im.size
def load_seek(self):
# Flage the ImageFile.Parser so that it just does all the decode at the end.
# Flage the ImageFile.Parser so that it
# just does all the decode at the end.
pass
#
# --------------------------------------------------------------------

View File

@ -30,7 +30,7 @@ __version__ = "0.7"
import re
from PIL import Image, ImageFile, ImagePalette
from PIL._binary import i8, o8
from PIL._binary import i8
# --------------------------------------------------------------------
@ -46,8 +46,8 @@ SCALE = "Scale (x,y)"
SIZE = "Image size (x*y)"
MODE = "Image type"
TAGS = { COMMENT:0, DATE:0, EQUIPMENT:0, FRAMES:0, LUT:0, NAME:0,
SCALE:0, SIZE:0, MODE:0 }
TAGS = {COMMENT: 0, DATE: 0, EQUIPMENT: 0, FRAMES: 0, LUT: 0, NAME: 0,
SCALE: 0, SIZE: 0, MODE: 0}
OPEN = {
# ifunc93/p3cfunc formats
@ -94,12 +94,14 @@ for i in range(2, 33):
split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$")
def number(s):
try:
return int(s)
except ValueError:
return float(s)
##
# Image plugin for the IFUNC IM file format.
@ -113,7 +115,7 @@ class ImImageFile(ImageFile.ImageFile):
# Quick rejection: if there's not an LF among the first
# 100 bytes, this is (probably) not a text header.
if not b"\n" in self.fp.read(100):
if b"\n" not in self.fp.read(100):
raise SyntaxError("not an IM file")
self.fp.seek(0)
@ -155,10 +157,10 @@ class ImImageFile(ImageFile.ImageFile):
if m:
k, v = m.group(1,2)
k, v = m.group(1, 2)
# Don't know if this is the correct encoding, but a decent guess
# (I guess)
# Don't know if this is the correct encoding,
# but a decent guess (I guess)
k = k.decode('latin-1', 'replace')
v = v.decode('latin-1', 'replace')
@ -186,7 +188,8 @@ class ImImageFile(ImageFile.ImageFile):
else:
raise SyntaxError("Syntax error in IM header: " + s.decode('ascii', 'replace'))
raise SyntaxError("Syntax error in IM header: " +
s.decode('ascii', 'replace'))
if not n:
raise SyntaxError("Not an IM file")
@ -204,8 +207,8 @@ class ImImageFile(ImageFile.ImageFile):
if LUT in self.info:
# convert lookup table to palette or lut attribute
palette = self.fp.read(768)
greyscale = 1 # greyscale palette
linear = 1 # linear greyscale palette
greyscale = 1 # greyscale palette
linear = 1 # linear greyscale palette
for i in range(256):
if palette[i] == palette[i+256] == palette[i+512]:
if i8(palette[i]) != i:
@ -230,7 +233,7 @@ class ImImageFile(ImageFile.ImageFile):
self.__offset = offs = self.fp.tell()
self.__fp = self.fp # FIXME: hack
self.__fp = self.fp # FIXME: hack
if self.rawmode[:2] == "F;":
@ -239,7 +242,7 @@ class ImImageFile(ImageFile.ImageFile):
# use bit decoder (if necessary)
bits = int(self.rawmode[2:])
if bits not in [8, 16, 32]:
self.tile = [("bit", (0,0)+self.size, offs,
self.tile = [("bit", (0, 0)+self.size, offs,
(bits, 8, 3, 0, -1))]
return
except ValueError:
@ -249,12 +252,13 @@ class ImImageFile(ImageFile.ImageFile):
# Old LabEye/3PC files. Would be very surprised if anyone
# ever stumbled upon such a file ;-)
size = self.size[0] * self.size[1]
self.tile = [("raw", (0,0)+self.size, offs, ("G", 0, -1)),
("raw", (0,0)+self.size, offs+size, ("R", 0, -1)),
("raw", (0,0)+self.size, offs+2*size, ("B", 0, -1))]
self.tile = [("raw", (0, 0)+self.size, offs, ("G", 0, -1)),
("raw", (0, 0)+self.size, offs+size, ("R", 0, -1)),
("raw", (0, 0)+self.size, offs+2*size, ("B", 0, -1))]
else:
# LabEye/IFUNC files
self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))]
self.tile = [("raw", (0, 0)+self.size, offs,
(self.rawmode, 0, -1))]
def seek(self, frame):
@ -276,7 +280,7 @@ class ImImageFile(ImageFile.ImageFile):
self.fp = self.__fp
self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))]
self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]
def tell(self):
@ -305,6 +309,7 @@ SAVE = {
"YCbCr": ("YCC", "YCbCr;L")
}
def _save(im, fp, filename, check=0):
try:
@ -329,8 +334,8 @@ def _save(im, fp, filename, check=0):
fp.write(b"Lut: 1\r\n")
fp.write(b"\000" * (511-fp.tell()) + b"\032")
if im.mode == "P":
fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))])
fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))])
#
# --------------------------------------------------------------------

View File

@ -34,6 +34,7 @@ import warnings
class DecompressionBombWarning(RuntimeWarning):
pass
class _imaging_not_installed:
# module placeholder
def __getattr__(self, id):
@ -530,7 +531,7 @@ class Image:
"""
Closes the file pointer, if possible.
This operation will destroy the image core and release it's memory.
This operation will destroy the image core and release its memory.
The image data will be unusable afterward.
This function is only required to close images that have not
@ -664,6 +665,10 @@ class Image:
# Declare tostring as alias to tobytes
def tostring(self, *args, **kw):
"""Deprecated alias to tobytes.
.. deprecated:: 2.0
"""
warnings.warn(
'tostring() is deprecated. Please call tobytes() instead.',
DeprecationWarning,
@ -847,8 +852,9 @@ class Image:
t = self.info['transparency']
if isinstance(t, bytes):
# Dragons. This can't be represented by a single color
warnings.warn('Palette images with Transparency expressed ' +
' in bytes should be converted to RGBA images')
warnings.warn('Palette images with Transparency ' +
' expressed in bytes should be converted ' +
'to RGBA images')
delete_trns = True
else:
# get the new transparency color.
@ -864,10 +870,20 @@ class Image:
# can't just retrieve the palette number, got to do it
# after quantization.
trns_im = trns_im.convert('RGB')
trns = trns_im.getpixel((0,0))
trns = trns_im.getpixel((0, 0))
elif self.mode == 'P' and mode == 'RGBA':
t = self.info['transparency']
delete_trns = True
if isinstance(t, bytes):
self.im.putpalettealphas(t)
elif isinstance(t, int):
self.im.putpalettealpha(t,0)
else:
raise ValueError("Transparency for P mode should" +
" be bytes or int")
if mode == "P" and palette == ADAPTIVE:
im = self.im.quantize(colors)

View File

@ -1,19 +1,19 @@
## The Python Imaging Library.
## $Id$
# The Python Imaging Library.
# $Id$
## Optional color managment support, based on Kevin Cazabon's PyCMS
## library.
# Optional color managment support, based on Kevin Cazabon's PyCMS
# library.
## History:
# History:
## 2009-03-08 fl Added to PIL.
# 2009-03-08 fl Added to PIL.
## Copyright (C) 2002-2003 Kevin Cazabon
## Copyright (c) 2009 by Fredrik Lundh
## Copyright (c) 2013 by Eric Soroos
# Copyright (C) 2002-2003 Kevin Cazabon
# Copyright (c) 2009 by Fredrik Lundh
# Copyright (c) 2013 by Eric Soroos
## See the README file for information on usage and redistribution. See
## below for the original description.
# See the README file for information on usage and redistribution. See
# below for the original description.
from __future__ import print_function
@ -154,9 +154,9 @@ class ImageCmsProfile:
:param profile: Either a string representing a filename,
a file like object containing a profile or a
low-level profile object
"""
if isStringType(profile):
self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"):
@ -181,9 +181,10 @@ class ImageCmsProfile:
:returns: a bytes object containing the ICC profile.
"""
return core.profile_tobytes(self.profile)
class ImageCmsTransform(Image.ImagePointHandler):
# Transform. This can be used with the procedural API, or with the
@ -191,7 +192,6 @@ class ImageCmsTransform(Image.ImagePointHandler):
#
# Will return the output profile in the output.info['icc_profile'].
def __init__(self, input, output, input_mode, output_mode,
intent=INTENT_PERCEPTUAL, proof=None,
proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):

View File

@ -20,6 +20,7 @@
from PIL import Image
import re
def getrgb(color):
"""
Convert a color string to an RGB tuple. If the string cannot be parsed,
@ -86,7 +87,8 @@ def getrgb(color):
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
color)
if m:
return (
int(m.group(1)),
@ -96,6 +98,7 @@ def getrgb(color):
)
raise ValueError("unknown color specifier: %r" % color)
def getcolor(color, mode):
"""
Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a

View File

@ -40,6 +40,7 @@ try:
except ImportError:
warnings = None
##
# A simple 2D drawing interface for PIL images.
# <p>
@ -61,7 +62,7 @@ class ImageDraw:
def __init__(self, im, mode=None):
im.load()
if im.readonly:
im._copy() # make it writable
im._copy() # make it writeable
blend = 0
if mode is None:
mode = im.mode
@ -85,7 +86,7 @@ class ImageDraw:
# FIXME: fix Fill2 to properly support matte for I+F images
self.fontmode = "1"
else:
self.fontmode = "L" # aliasing is okay for other modes
self.fontmode = "L" # aliasing is okay for other modes
self.fill = 0
self.font = None
@ -280,6 +281,7 @@ class ImageDraw:
font = self.getfont()
return font.getsize(text)
##
# A simple 2D drawing interface for PIL images.
#
@ -302,6 +304,7 @@ try:
except:
Outline = None
##
# (Experimental) A more advanced 2D drawing interface for PIL images,
# based on the WCK interface.
@ -325,6 +328,7 @@ def getdraw(im=None, hints=None):
im = handler.Draw(im)
return im, handler
##
# (experimental) Fills a bounded region with a given color.
#
@ -344,10 +348,10 @@ def floodfill(image, xy, value, border=None):
try:
background = pixel[x, y]
if background == value:
return # seed point already has fill color
return # seed point already has fill color
pixel[x, y] = value
except IndexError:
return # seed point outside image
return # seed point outside image
edge = [(x, y)]
if border is None:
while edge:

View File

@ -18,21 +18,25 @@
from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen:
def __init__(self, color, width=1, opacity=255):
self.color = ImageColor.getrgb(color)
self.width = width
class Brush:
def __init__(self, color, opacity=255):
self.color = ImageColor.getrgb(color)
class Font:
def __init__(self, color, file, size=12):
# FIXME: add support for bitmap fonts
self.color = ImageColor.getrgb(color)
self.font = ImageFont.truetype(file, size)
class Draw:
def __init__(self, image, size=None, color=None):
@ -47,7 +51,8 @@ class Draw:
def render(self, op, xy, pen, brush=None):
# handle color arguments
outline = fill = None; width = 1
outline = fill = None
width = 1
if isinstance(pen, Pen):
outline = pen.color
width = pen.width

View File

@ -47,8 +47,11 @@ class Color(_Enhance):
"""
def __init__(self, image):
self.image = image
self.degenerate = image.convert("L").convert(image.mode)
self.intermediate_mode = 'L'
if 'A' in image.getbands():
self.intermediate_mode = 'LA'
self.degenerate = image.convert(self.intermediate_mode).convert(image.mode)
class Contrast(_Enhance):
"""Adjust image contrast.
@ -62,6 +65,9 @@ class Contrast(_Enhance):
mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5)
self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
class Brightness(_Enhance):
"""Adjust image brightness.
@ -74,6 +80,9 @@ class Brightness(_Enhance):
self.image = image
self.degenerate = Image.new(image.mode, image.size, 0)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
class Sharpness(_Enhance):
"""Adjust image sharpness.
@ -85,3 +94,6 @@ class Sharpness(_Enhance):
def __init__(self, image):
self.image = image
self.degenerate = image.filter(ImageFilter.SMOOTH)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])

View File

@ -29,8 +29,10 @@
from PIL import Image
from PIL._util import isPath
import traceback, os, sys
import io
import os
import sys
import traceback
MAXBLOCK = 65536
@ -46,6 +48,7 @@ ERRORS = {
-9: "out of memory error"
}
def raise_ioerror(error):
try:
message = Image.core.getcodecstatus(error)
@ -55,6 +58,7 @@ def raise_ioerror(error):
message = "decoder error %d" % error
raise IOError(message + " when reading image file")
#
# --------------------------------------------------------------------
# Helpers
@ -63,6 +67,7 @@ def _tilesort(t):
# sort on offset
return t[2]
#
# --------------------------------------------------------------------
# ImageFile base class
@ -74,7 +79,7 @@ class ImageFile(Image.Image):
Image.Image.__init__(self)
self.tile = None
self.readonly = 1 # until we know better
self.readonly = 1 # until we know better
self.decoderconfig = ()
self.decodermaxblock = MAXBLOCK
@ -90,19 +95,19 @@ class ImageFile(Image.Image):
try:
self._open()
except IndexError as v: # end of data
except IndexError as v: # end of data
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
except TypeError as v: # end of data (ord)
except TypeError as v: # end of data (ord)
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
except KeyError as v: # unsupported mode
except KeyError as v: # unsupported mode
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
except EOFError as v: # got header but not the first frame
except EOFError as v: # got header but not the first frame
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError(v)
@ -135,8 +140,8 @@ class ImageFile(Image.Image):
self.map = None
use_mmap = self.filename and len(self.tile) == 1
# As of pypy 2.1.0, memory mapping was failing here.
use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info')
use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info')
readonly = 0
# look for read/seek overrides
@ -203,23 +208,25 @@ class ImageFile(Image.Image):
while True:
try:
s = read(self.decodermaxblock)
except IndexError as ie: # truncated png/gif
except IndexError as ie: # truncated png/gif
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IndexError(ie)
if not s and not d.handles_eof: # truncated jpeg
if not s and not d.handles_eof: # truncated jpeg
self.tile = []
# JpegDecode needs to clean things up here either way
# If we don't destroy the decompressor, we have a memory leak.
# If we don't destroy the decompressor,
# we have a memory leak.
d.cleanup()
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError("image file is truncated (%d bytes not processed)" % len(b))
raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b))
b = b + s
n, e = d.decode(b)
@ -233,7 +240,7 @@ class ImageFile(Image.Image):
self.tile = []
self.readonly = readonly
self.fp = None # might be shared
self.fp = None # might be shared
if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0:
# still raised if decoder fails to return anything
@ -380,10 +387,10 @@ class Parser:
fp = io.BytesIO(self.data)
im = Image.open(fp)
finally:
fp.close() # explicitly close the virtual file
fp.close() # explicitly close the virtual file
except IOError:
# traceback.print_exc()
pass # not enough data
pass # not enough data
else:
flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
if flag or len(im.tile) != 1:
@ -433,9 +440,10 @@ class Parser:
self.image = Image.open(fp)
finally:
self.image.load()
fp.close() # explicitly close the virtual file
fp.close() # explicitly close the virtual file
return self.image
# --------------------------------------------------------------------
def _save(im, fp, tile, bufsize=0):
@ -452,10 +460,10 @@ def _save(im, fp, tile, bufsize=0):
im.encoderconfig = ()
tile.sort(key=_tilesort)
# FIXME: make MAXBLOCK a configuration parameter
# It would be great if we could have the encoder specifiy what it needs
# It would be great if we could have the encoder specify what it needs
# But, it would need at least the image size in most cases. RawEncode is
# a tricky case.
bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
try:
fh = fp.fileno()
fp.flush()
@ -487,7 +495,8 @@ def _save(im, fp, tile, bufsize=0):
e.cleanup()
try:
fp.flush()
except: pass
except:
pass
def _safe_read(fp, size):

View File

@ -43,7 +43,7 @@ class Kernel(Filter):
def __init__(self, size, kernel, scale=None, offset=0):
if scale is None:
# default scale is sum of kernel
scale = reduce(lambda a,b: a+b, kernel)
scale = reduce(lambda a, b: a+b, kernel)
if size[0] * size[1] != len(kernel):
raise ValueError("not enough coefficients in kernel")
self.filterargs = size, scale, offset, kernel
@ -162,7 +162,8 @@ class UnsharpMask(Filter):
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of
the parameters.
.. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
.. _digital unsharp masking:
https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
"""
name = "UnsharpMask"

View File

@ -17,6 +17,9 @@
from PIL import Image
import sys
if sys.platform != "win32":
raise ImportError("ImageGrab is Windows only")
try:
# built-in driver (1.1.3 and later)
@ -40,7 +43,7 @@ def grab(bbox=None):
def grabclipboard():
debug = 0 # temporary interface
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes):
from PIL import BmpImagePlugin

View File

@ -16,6 +16,7 @@
# mode descriptor cache
_modes = {}
##
# Wrapper for mode strings.
@ -30,6 +31,7 @@ class ModeDescriptor:
def __str__(self):
return self.mode
##
# Gets a mode descriptor for the given mode.

View File

@ -35,14 +35,14 @@ class LutBuilder:
returned if no other match is found.
Operations:
- 4 - 4 way rotation
- N - Negate
- 1 - Dummy op for no other operation (an op must always be given)
- M - Mirroring
Example::
lb = LutBuilder(patterns = ["4:(... .1. 111)->1"])
lut = lb.build_lut()

View File

@ -22,6 +22,7 @@ from PIL._util import isStringType
import operator
from functools import reduce
#
# helpers
@ -35,12 +36,14 @@ def _border(border):
left = top = right = bottom = border
return left, top, right, bottom
def _color(color, mode):
if isStringType(color):
from PIL import ImageColor
color = ImageColor.getcolor(color, mode)
return color
def _lut(image, lut):
if image.mode == "P":
# FIXME: apply to lookup table, not image data
@ -147,7 +150,9 @@ def colorize(image, black, white):
assert image.mode == "L"
black = _color(black, "RGB")
white = _color(white, "RGB")
red = []; green = []; blue = []
red = []
green = []
blue = []
for i in range(256):
red.append(black[0]+i*(white[0]-black[0])//255)
green.append(black[1]+i*(white[1]-black[1])//255)
@ -273,7 +278,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
centering = [centering[0], centering[1]]
if centering[0] > 1.0 or centering[0] < 0.0:
centering [0] = 0.50
centering[0] = 0.50
if centering[1] > 1.0 or centering[1] < 0.0:
centering[1] = 0.50
@ -404,6 +409,7 @@ def solarize(image, threshold=128):
lut.append(255-i)
return _lut(image, lut)
# --------------------------------------------------------------------
# PIL USM components, from Kevin Cazabon.
@ -419,6 +425,7 @@ def gaussian_blur(im, radius=None):
gblur = gaussian_blur
def unsharp_mask(im, radius=None, percent=None, threshold=None):
""" PIL_usm.usm(im, [radius, percent, threshold])"""

View File

@ -24,6 +24,7 @@ try:
except:
from PyQt4.QtGui import QImage, qRgba
##
# (Internal) Turns an RGB color into a Qt compatible color integer.
@ -32,6 +33,7 @@ def rgb(r, g, b, a=255):
# into a negative integer with the same bitpattern.
return (qRgba(r, g, b, a) & 0xffffffff)
##
# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage
# class.

View File

@ -15,6 +15,7 @@
##
class Iterator:
"""
This class implements an iterator object that can be used to loop
@ -38,4 +39,4 @@ class Iterator:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError # end of sequence
raise IndexError # end of sequence

View File

@ -15,7 +15,8 @@
from __future__ import print_function
from PIL import Image
import os, sys
import os
import sys
if sys.version_info >= (3, 3):
from shlex import quote
@ -24,17 +25,19 @@ else:
_viewers = []
def register(viewer, order=1):
try:
if issubclass(viewer, Viewer):
viewer = viewer()
except TypeError:
pass # raised if viewer wasn't a class
pass # raised if viewer wasn't a class
if order > 0:
_viewers.append(viewer)
elif order < 0:
_viewers.insert(0, viewer)
##
# Displays a given image.
#
@ -49,6 +52,7 @@ def show(image, title=None, **options):
return 1
return 0
##
# Base class for viewers.
@ -102,6 +106,7 @@ if sys.platform == "win32":
class WindowsViewer(Viewer):
format = "BMP"
def get_command(self, file, **options):
return ('start "Pillow" /WAIT "%s" '
'&& ping -n 2 127.0.0.1 >NUL '
@ -113,11 +118,13 @@ elif sys.platform == "darwin":
class MacViewer(Viewer):
format = "BMP"
def get_command(self, file, **options):
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a /Applications/Preview.app"
command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file))
command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file),
quote(file))
return command
register(MacViewer)
@ -140,7 +147,8 @@ else:
class UnixViewer(Viewer):
def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options)
command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
command = "(%s %s; rm -f %s)&" % (command, quote(file),
quote(file))
os.system(command)
return 1

View File

@ -21,20 +21,21 @@
# See the README file for information on usage and redistribution.
#
import operator, math
import math
import operator
from functools import reduce
class Stat:
def __init__(self, image_or_list, mask = None):
def __init__(self, image_or_list, mask=None):
try:
if mask:
self.h = image_or_list.histogram(mask)
else:
self.h = image_or_list.histogram()
except AttributeError:
self.h = image_or_list # assume it to be a histogram list
self.h = image_or_list # assume it to be a histogram list
if not isinstance(self.h, list):
raise TypeError("first argument must be image or list")
self.bands = list(range(len(self.h) // 256))
@ -58,7 +59,7 @@ class Stat:
if histogram[i]:
n = min(n, i)
x = max(x, i)
return n, x # returns (255, 0) if there's no data in the histogram
return n, x # returns (255, 0) if there's no data in the histogram
v = []
for i in range(0, len(self.h), 256):
@ -126,7 +127,6 @@ class Stat:
v.append(math.sqrt(self.sum2[i] / self.count[i]))
return v
def _getvar(self):
"Get variance for each layer"
@ -144,4 +144,4 @@ class Stat:
v.append(math.sqrt(self.var[i]))
return v
Global = Stat # compatibility
Global = Stat # compatibility

View File

@ -40,17 +40,19 @@ from PIL import Image
_pilbitmap_ok = None
def _pilbitmap_check():
global _pilbitmap_ok
if _pilbitmap_ok is None:
try:
im = Image.new("1", (1,1))
im = Image.new("1", (1, 1))
tkinter.BitmapImage(data="PIL:%d" % im.im.id)
_pilbitmap_ok = 1
except tkinter.TclError:
_pilbitmap_ok = 0
return _pilbitmap_ok
# --------------------------------------------------------------------
# PhotoImage
@ -95,7 +97,7 @@ class PhotoImage:
try:
mode = image.palette.mode
except AttributeError:
mode = "RGB" # default
mode = "RGB" # default
size = image.size
kw["width"], kw["height"] = size
else:
@ -118,8 +120,7 @@ class PhotoImage:
try:
self.__photo.tk.call("image", "delete", name)
except:
pass # ignore internal errors
pass # ignore internal errors
def __str__(self):
"""
@ -131,7 +132,6 @@ class PhotoImage:
"""
return str(self.__photo)
def width(self):
"""
Get the width of the image.
@ -140,7 +140,6 @@ class PhotoImage:
"""
return self.__size[0]
def height(self):
"""
Get the height of the image.
@ -149,7 +148,6 @@ class PhotoImage:
"""
return self.__size[1]
def paste(self, im, box=None):
"""
Paste a PIL image into the photo image. Note that this can
@ -170,13 +168,13 @@ class PhotoImage:
block = image
else:
block = image.new_block(self.__mode, im.size)
image.convert2(block, image) # convert directly between buffers
image.convert2(block, image) # convert directly between buffers
tk = self.__photo.tk
try:
tk.call("PyImagingPhoto", self.__photo, block.id)
except tkinter.TclError as v:
except tkinter.TclError:
# activate Tkinter hook
try:
from PIL import _imagingtk
@ -186,7 +184,7 @@ class PhotoImage:
_imagingtk.tkinit(id(tk), 0)
tk.call("PyImagingPhoto", self.__photo, block.id)
except (ImportError, AttributeError, tkinter.TclError):
raise # configuration problem; cannot attach to Tkinter
raise # configuration problem; cannot attach to Tkinter
# --------------------------------------------------------------------
# BitmapImage
@ -226,7 +224,7 @@ class BitmapImage:
# fast way (requires the pilbitmap booster patch)
image.load()
kw["data"] = "PIL:%d" % image.im.id
self.__im = image # must keep a reference
self.__im = image # must keep a reference
else:
# slow but safe way
kw["data"] = image.tobitmap()
@ -238,8 +236,7 @@ class BitmapImage:
try:
self.__photo.tk.call("image", "delete", name)
except:
pass # ignore internal errors
pass # ignore internal errors
def width(self):
"""
@ -249,7 +246,6 @@ class BitmapImage:
"""
return self.__size[0]
def height(self):
"""
Get the height of the image.
@ -258,7 +254,6 @@ class BitmapImage:
"""
return self.__size[1]
def __str__(self):
"""
Get the Tkinter bitmap image identifier. This method is automatically
@ -274,6 +269,7 @@ def getimage(photo):
"""Copies the contents of a PhotoImage to a PIL image memory."""
photo.tk.call("PyImagingPhotoGet", photo)
# --------------------------------------------------------------------
# Helper for the Image.show method.
@ -286,7 +282,7 @@ def _show(image, title):
else:
self.image = PhotoImage(im, master=master)
tkinter.Label.__init__(self, master, image=self.image,
bg="black", bd=0)
bg="black", bd=0)
if not tkinter._default_root:
raise IOError("tkinter not initialized")

View File

@ -15,16 +15,20 @@
from PIL import Image
class Transform(Image.ImageTransformHandler):
def __init__(self, data):
self.data = data
def getdata(self):
return self.method, self.data
def transform(self, size, image, **options):
# can be overridden
method, data = self.getdata()
return image.transform(size, method, data, **options)
##
# Define an affine image transform.
# <p>
@ -43,9 +47,11 @@ class Transform(Image.ImageTransformHandler):
# the first two rows from an affine transform matrix.
# @see Image#Image.transform
class AffineTransform(Transform):
method = Image.AFFINE
##
# Define a transform to extract a subregion from an image.
# <p>
@ -68,6 +74,7 @@ class AffineTransform(Transform):
class ExtentTransform(Transform):
method = Image.EXTENT
##
# Define an quad image transform.
# <p>
@ -83,6 +90,7 @@ class ExtentTransform(Transform):
class QuadTransform(Transform):
method = Image.QUAD
##
# Define an mesh image transform. A mesh transform consists of one
# or more individual quad transforms.

View File

@ -23,23 +23,26 @@ from PIL import Image
class HDC:
"""
Wraps a HDC integer. The resulting object can be passed to the
Wraps an HDC integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
methods.
"""
def __init__(self, dc):
self.dc = dc
def __int__(self):
return self.dc
class HWND:
"""
Wraps a HWND integer. The resulting object can be passed to the
Wraps an HWND integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
methods, instead of a DC.
"""
def __init__(self, wnd):
self.wnd = wnd
def __int__(self):
return self.wnd
@ -79,13 +82,12 @@ class Dib:
if image:
self.paste(image)
def expose(self, handle):
"""
Copy the bitmap contents to a device context.
:param handle: Device context (HDC), cast to a Python integer, or a HDC
or HWND instance. In PythonWin, you can use the
:param handle: Device context (HDC), cast to a Python integer, or an
HDC or HWND instance. In PythonWin, you can use the
:py:meth:`CDC.GetHandleAttrib` to get a suitable handle.
"""
if isinstance(handle, HWND):
@ -109,7 +111,7 @@ class Dib:
necessary.
"""
if not src:
src = (0,0) + self.size
src = (0, 0) + self.size
if isinstance(handle, HWND):
dc = self.image.getdc(handle)
try:
@ -120,7 +122,6 @@ class Dib:
result = self.image.draw(handle, dst, src)
return result
def query_palette(self, handle):
"""
Installs the palette associated with the image in the given device
@ -146,7 +147,6 @@ class Dib:
result = self.image.query_palette(handle)
return result
def paste(self, im, box=None):
"""
Paste a PIL image into the bitmap image.
@ -166,7 +166,6 @@ class Dib:
else:
self.image.paste(im.im)
def frombytes(self, buffer):
"""
Load display memory contents from byte data.
@ -176,7 +175,6 @@ class Dib:
"""
return self.image.frombytes(buffer)
def tobytes(self):
"""
Copy display memory contents to bytes object.
@ -204,6 +202,7 @@ class Dib:
)
return self.tobytes()
##
# Create a Window with the given title size.
@ -235,6 +234,7 @@ class Window:
def mainloop(self):
Image.core.eventloop()
##
# Create an image window which displays the given image.

View File

@ -26,6 +26,7 @@ from PIL import Image, ImageFile
field = re.compile(br"([a-z]*) ([^ \r\n]*)")
##
# Image plugin for IM Tools images.
@ -39,7 +40,7 @@ class ImtImageFile(ImageFile.ImageFile):
# Quick rejection: if there's not a LF among the first
# 100 bytes, this is (probably) not a text header.
if not b"\n" in self.fp.read(100):
if b"\n" not in self.fp.read(100):
raise SyntaxError("not an IM file")
self.fp.seek(0)
@ -54,7 +55,7 @@ class ImtImageFile(ImageFile.ImageFile):
if s == b'\x0C':
# image data begins
self.tile = [("raw", (0,0)+self.size,
self.tile = [("raw", (0, 0)+self.size,
self.fp.tell(),
(self.mode, 0, 1))]
@ -68,12 +69,12 @@ class ImtImageFile(ImageFile.ImageFile):
if len(s) == 1 or len(s) > 100:
break
if s[0] == b"*":
continue # comment
continue # comment
m = field.match(s)
if not m:
break
k, v = m.group(1,2)
k, v = m.group(1, 2)
if k == "width":
xsize = int(v)
self.size = xsize, ysize

View File

@ -72,7 +72,7 @@ def _parse_jp2_header(fp):
if lbox < hlen:
raise SyntaxError('Invalid JP2 header length')
if tbox == b'jp2h':
header = fp.read(lbox - hlen)
break

View File

@ -115,7 +115,8 @@ def APP(self, marker):
elif marker == 0xFFE2 and s[:4] == b"MPF\0":
# extract MPO information
self.info["mp"] = s[4:]
# offset is current location minus buffer size plus constant header size
# offset is current location minus buffer size
# plus constant header size
self.info["mpoffset"] = self.fp.tell() - n + 4
@ -321,7 +322,8 @@ class JpegImageFile(ImageFile.ImageFile):
rawmode = self.mode
if self.mode == "CMYK":
rawmode = "CMYK;I" # assume adobe conventions
self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))]
self.tile = [("jpeg", (0, 0) + self.size, 0,
(rawmode, ""))]
# self.__offset = self.fp.tell()
break
s = self.fp.read(1)
@ -472,14 +474,18 @@ def _getmp(self):
for entrynum in range(0, quant):
rawmpentry = mp[0xB002][entrynum * 16:(entrynum + 1) * 16]
unpackedentry = unpack('{0}LLLHH'.format(endianness), rawmpentry)
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', 'EntryNo2')
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
'EntryNo2')
mpentry = dict(zip(labels, unpackedentry))
mpentryattr = {
'DependentParentImageFlag': bool(mpentry['Attribute'] & (1<<31)),
'DependentChildImageFlag': bool(mpentry['Attribute'] & (1<<30)),
'RepresentativeImageFlag': bool(mpentry['Attribute'] & (1<<29)),
'Reserved': (mpentry['Attribute'] & (3<<27)) >> 27,
'ImageDataFormat': (mpentry['Attribute'] & (7<<24)) >> 24,
'DependentParentImageFlag': bool(mpentry['Attribute'] &
(1 << 31)),
'DependentChildImageFlag': bool(mpentry['Attribute'] &
(1 << 30)),
'RepresentativeImageFlag': bool(mpentry['Attribute'] &
(1 << 29)),
'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,
'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,
'MPType': mpentry['Attribute'] & 0x00FFFFFF
}
if mpentryattr['ImageDataFormat'] == 0:
@ -496,7 +502,7 @@ def _getmp(self):
0x030000: 'Baseline MP Primary Image'
}
mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],
'Unknown')
'Unknown')
mpentry['Attribute'] = mpentryattr
mpentries.append(mpentry)
mp[0xB002] = mpentries
@ -530,11 +536,10 @@ zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63)
samplings = {
(1, 1, 1, 1, 1, 1): 0,
samplings = {(1, 1, 1, 1, 1, 1): 0,
(2, 1, 1, 1, 1, 1): 1,
(2, 2, 1, 1, 1, 1): 2,
}
}
def convert_dict_qtables(qtables):
@ -598,7 +603,8 @@ def _save(im, fp, filename):
subsampling = 2
elif subsampling == "keep":
if im.format != "JPEG":
raise ValueError("Cannot use 'keep' when original image is not a JPEG")
raise ValueError(
"Cannot use 'keep' when original image is not a JPEG")
subsampling = get_sampling(im)
def validate_qtables(qtables):
@ -632,7 +638,8 @@ def _save(im, fp, filename):
if qtables == "keep":
if im.format != "JPEG":
raise ValueError("Cannot use 'keep' when original image is not a JPEG")
raise ValueError(
"Cannot use 'keep' when original image is not a JPEG")
qtables = getattr(im, "quantization", None)
qtables = validate_qtables(qtables)
@ -650,7 +657,8 @@ def _save(im, fp, filename):
i = 1
for marker in markers:
size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
extra += b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + marker
extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) +
o8(len(markers)) + marker)
i += 1
# get keyword arguments

View File

@ -48,8 +48,8 @@ You can get the quantization tables of a JPEG with::
im.quantization
This will return a dict with a number of arrays. You can pass this dict directly
as the qtables argument when saving a JPEG.
This will return a dict with a number of arrays. You can pass this dict
directly as the qtables argument when saving a JPEG.
The tables format between im.quantization and quantization in presets differ in
3 ways:
@ -238,4 +238,4 @@ presets = {
15, 12, 12, 12, 12, 12, 12, 12,
15, 12, 12, 12, 12, 12, 12, 12]
]},
}
}

View File

@ -21,9 +21,11 @@ __version__ = "0.2"
import struct
from PIL import Image, ImageFile
def _accept(s):
return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04"
##
# Image plugin for McIdas area images.
@ -47,10 +49,12 @@ class McIdasImageFile(ImageFile.ImageFile):
mode = rawmode = "L"
elif w[11] == 2:
# FIXME: add memory map support
mode = "I"; rawmode = "I;16B"
mode = "I"
rawmode = "I;16B"
elif w[11] == 4:
# FIXME: add memory map support
mode = "I"; rawmode = "I;32B"
mode = "I"
rawmode = "I;32B"
else:
raise SyntaxError("unsupported McIdas format")

View File

@ -31,6 +31,7 @@ from PIL.OleFileIO import *
def _accept(prefix):
return prefix[:8] == MAGIC
##
# Image plugin for Microsoft's Image Composer file format.

View File

@ -18,6 +18,7 @@ __version__ = "0.1"
from PIL import Image, ImageFile
from PIL._binary import i8
#
# Bitstream parser
@ -52,6 +53,7 @@ class BitStream:
self.bits = self.bits - bits
return v
##
# Image plugin for MPEG streams. This plugin can identify a stream,
# but it cannot read it.

View File

@ -22,13 +22,16 @@ __version__ = "0.1"
from PIL import Image, JpegImagePlugin
def _accept(prefix):
return JpegImagePlugin._accept(prefix)
def _save(im, fp, filename):
# Note that we can only save the current frame at present
return JpegImagePlugin._save(im, fp, filename)
##
# Image plugin for MPO images.
@ -38,19 +41,19 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
format_description = "MPO (CIPA DC-007)"
def _open(self):
self.fp.seek(0) # prep the fp in order to pass the JPEG test
self.fp.seek(0) # prep the fp in order to pass the JPEG test
JpegImagePlugin.JpegImageFile._open(self)
self.mpinfo = self._getmp()
self.__framecount = self.mpinfo[0xB001]
self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset'] \
for mpent in self.mpinfo[0xB002]]
self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset']
for mpent in self.mpinfo[0xB002]]
self.__mpoffsets[0] = 0
# Note that the following assertion will only be invalid if something
# gets broken within JpegImagePlugin.
assert self.__framecount == len(self.__mpoffsets)
del self.info['mpoffset'] # no longer needed
self.__fp = self.fp # FIXME: hack
self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame
del self.info['mpoffset'] # no longer needed
self.__fp = self.fp # FIXME: hack
self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame
self.__frame = 0
self.offset = 0
# for now we can only handle reading and individual frame extraction
@ -79,7 +82,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
# Note that since MPO shares a factory with JPEG, we do not need to do a
# separate registration for it here.
#Image.register_open("MPO", JpegImagePlugin.jpeg_factory, _accept)
# Image.register_open("MPO", JpegImagePlugin.jpeg_factory, _accept)
Image.register_save("MPO", _save)
Image.register_extension("MPO", ".mpo")

View File

@ -27,9 +27,11 @@ from PIL import Image, ImageFile, _binary
i16 = _binary.i16le
def _accept(prefix):
return prefix[:4] in [b"DanM", b"LinS"]
##
# Image plugin for Windows MSP images. This plugin supports both
# uncompressed (Windows 1.0).
@ -57,15 +59,16 @@ class MspImageFile(ImageFile.ImageFile):
self.size = i16(s[4:]), i16(s[6:])
if s[:4] == b"DanM":
self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))]
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
else:
self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)]
self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)]
#
# write MSP files (uncompressed only)
o16 = _binary.o16le
def _save(im, fp, filename):
if im.mode != "1":
@ -74,7 +77,7 @@ def _save(im, fp, filename):
# create MSP header
header = [0] * 16
header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1
header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1
header[2], header[3] = im.size
header[4], header[5] = 1, 1
header[6], header[7] = 1, 1
@ -83,14 +86,14 @@ def _save(im, fp, filename):
sum = 0
for h in header:
sum = sum ^ h
header[12] = sum # FIXME: is this the right field?
header[12] = sum # FIXME: is this the right field?
# header
for h in header:
fp.write(o16(h))
# image body
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 32, ("1", 0, 1))])
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))])
#
# registry

View File

@ -19,6 +19,7 @@ from __future__ import print_function
from PIL import EpsImagePlugin
##
# Simple Postscript graphics interface.
@ -34,7 +35,7 @@ class PSDraw:
fp = sys.stdout
self.fp = fp
def begin_document(self, id = None):
def begin_document(self, id=None):
"""Set up printing of a document. (Write Postscript DSC header.)"""
# FIXME: incomplete
self.fp.write("%!PS-Adobe-3.0\n"
@ -42,7 +43,7 @@ class PSDraw:
"/showpage { } def\n"
"%%EndComments\n"
"%%BeginDocument\n")
#self.fp.write(ERROR_PS) # debugging!
# self.fp.write(ERROR_PS) # debugging!
self.fp.write(EDROFF_PS)
self.fp.write(VDI_PS)
self.fp.write("%%EndProlog\n")
@ -65,7 +66,7 @@ class PSDraw:
"""
if font not in self.isofont:
# reencode font
self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\
self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %
(font, font))
self.isofont[font] = 1
# rough
@ -112,14 +113,14 @@ class PSDraw:
xy = xy + (text,)
self.fp.write("%d %d M (%s) S\n" % xy)
def image(self, box, im, dpi = None):
def image(self, box, im, dpi=None):
"""Draw a PIL image, centered in the given box."""
# default resolution depends on mode
if not dpi:
if im.mode == "1":
dpi = 200 # fax
dpi = 200 # fax
else:
dpi = 100 # greyscale
dpi = 100 # greyscale
# image size (on paper)
x = float(im.size[0] * 72) / dpi
y = float(im.size[1] * 72) / dpi
@ -127,9 +128,11 @@ class PSDraw:
xmax = float(box[2] - box[0])
ymax = float(box[3] - box[1])
if x > xmax:
y = y * xmax / x; x = xmax
y = y * xmax / x
x = xmax
if y > ymax:
x = x * ymax / y; y = ymax
x = x * ymax / y
y = ymax
dx = (xmax - x) / 2 + box[0]
dy = (ymax - y) / 2 + box[1]
self.fp.write("gsave\n%f %f translate\n" % (dx, dy))

View File

@ -15,6 +15,7 @@
from PIL._binary import o8
##
# File handler for Teragon-style palette files.
@ -49,7 +50,6 @@ class PaletteFile:
self.palette = b"".join(self.palette)
def getpalette(self):
return self.palette, self.rawmode

View File

@ -22,6 +22,7 @@ from PIL import Image, ImageFile, _binary
i8 = _binary.i8
##
# Image plugin for PhotoCD images. This plugin only reads the 768x512
# image from the file; higher resolutions are encoded in a proprietary
@ -43,13 +44,13 @@ class PcdImageFile(ImageFile.ImageFile):
orientation = i8(s[1538]) & 3
if orientation == 1:
self.tile_post_rotate = 90 # hack
self.tile_post_rotate = 90 # hack
elif orientation == 3:
self.tile_post_rotate = -90
self.mode = "RGB"
self.size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [("pcd", (0,0)+self.size, 96*2048, None)]
self.size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
def draft(self, mode, size):
@ -60,7 +61,7 @@ class PcdImageFile(ImageFile.ImageFile):
if size:
scale = max(self.size[0] / size[0], self.size[1] / size[1])
for s, o in [(4,0*2048), (2,0*2048), (1,96*2048)]:
for s, o in [(4, 0*2048), (2, 0*2048), (1, 96*2048)]:
if scale >= s:
break
# e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1]

View File

@ -23,20 +23,20 @@ from PIL import _binary
# --------------------------------------------------------------------
# declarations
PCF_MAGIC = 0x70636601 # "\x01fcp"
PCF_MAGIC = 0x70636601 # "\x01fcp"
PCF_PROPERTIES = (1<<0)
PCF_ACCELERATORS = (1<<1)
PCF_METRICS = (1<<2)
PCF_BITMAPS = (1<<3)
PCF_INK_METRICS = (1<<4)
PCF_BDF_ENCODINGS = (1<<5)
PCF_SWIDTHS = (1<<6)
PCF_GLYPH_NAMES = (1<<7)
PCF_BDF_ACCELERATORS = (1<<8)
PCF_PROPERTIES = (1 << 0)
PCF_ACCELERATORS = (1 << 1)
PCF_METRICS = (1 << 2)
PCF_BITMAPS = (1 << 3)
PCF_INK_METRICS = (1 << 4)
PCF_BDF_ENCODINGS = (1 << 5)
PCF_SWIDTHS = (1 << 6)
PCF_GLYPH_NAMES = (1 << 7)
PCF_BDF_ACCELERATORS = (1 << 8)
BYTES_PER_ROW = [
lambda bits: ((bits+7) >> 3),
lambda bits: ((bits+7) >> 3),
lambda bits: ((bits+15) >> 3) & ~1,
lambda bits: ((bits+31) >> 3) & ~3,
lambda bits: ((bits+63) >> 3) & ~7,
@ -48,9 +48,11 @@ l32 = _binary.i32le
b16 = _binary.i16be
b32 = _binary.i32be
def sz(s, o):
return s[o:s.index(b"\0", o)]
##
# Font file plugin for the X11 PCF format.
@ -122,7 +124,7 @@ class PcfFontFile(FontFile.FontFile):
for i in range(nprops):
p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
if nprops & 3:
fp.seek(4 - (nprops & 3), 1) # pad
fp.seek(4 - (nprops & 3), 1) # pad
data = fp.read(i32(fp.read(4)))
@ -202,16 +204,16 @@ class PcfFontFile(FontFile.FontFile):
for i in range(4):
bitmapSizes.append(i32(fp.read(4)))
byteorder = format & 4 # non-zero => MSB
bitorder = format & 8 # non-zero => MSB
padindex = format & 3
byteorder = format & 4 # non-zero => MSB
bitorder = format & 8 # non-zero => MSB
padindex = format & 3
bitmapsize = bitmapSizes[padindex]
offsets.append(bitmapsize)
data = fp.read(bitmapsize)
pad = BYTES_PER_ROW[padindex]
pad = BYTES_PER_ROW[padindex]
mode = "1;R"
if bitorder:
mode = "1"
@ -245,6 +247,6 @@ class PcfFontFile(FontFile.FontFile):
try:
encoding[i+firstCol] = encodingOffset
except IndexError:
break # only load ISO-8859-1 glyphs
break # only load ISO-8859-1 glyphs
return encoding

View File

@ -33,9 +33,11 @@ i8 = _binary.i8
i16 = _binary.i16le
o8 = _binary.o8
def _accept(prefix):
return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5]
##
# Image plugin for Paintbrush images.
@ -52,23 +54,22 @@ class PcxImageFile(ImageFile.ImageFile):
raise SyntaxError("not a PCX file")
# image
bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1
bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1
if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
raise SyntaxError("bad PCX image size")
if Image.DEBUG:
print ("BBox: %s %s %s %s" % bbox)
# format
version = i8(s[1])
bits = i8(s[3])
planes = i8(s[65])
stride = i16(s,66)
stride = i16(s, 66)
if Image.DEBUG:
print ("PCX version %s, bits %s, planes %s, stride %s" %
(version, bits, planes, stride))
self.info["dpi"] = i16(s,12), i16(s,14)
self.info["dpi"] = i16(s, 12), i16(s, 14)
if bits == 1 and planes == 1:
mode = rawmode = "1"
@ -106,7 +107,7 @@ class PcxImageFile(ImageFile.ImageFile):
bbox = (0, 0) + self.size
if Image.DEBUG:
print ("size: %sx%s" % self.size)
self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
# --------------------------------------------------------------------
@ -122,6 +123,7 @@ SAVE = {
o16 = _binary.o16le
def _save(im, fp, filename, check=0):
try:
@ -138,8 +140,7 @@ def _save(im, fp, filename, check=0):
stride += stride % 2
# Stride needs to be kept in sync with the PcxEncode.c version.
# Ideally it should be passed in in the state, but the bytes value
# gets overwritten.
# gets overwritten.
if Image.DEBUG:
print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % (
@ -163,13 +164,13 @@ def _save(im, fp, filename, check=0):
assert fp.tell() == 128
ImageFile._save(im, fp, [("pcx", (0,0)+im.size, 0,
ImageFile._save(im, fp, [("pcx", (0, 0)+im.size, 0,
(rawmode, bits*planes))])
if im.mode == "P":
# colour palette
fp.write(o8(12))
fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes
fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes
elif im.mode == "L":
# greyscale palette
fp.write(o8(12))

View File

@ -29,6 +29,7 @@ from PIL import Image, ImageFile, _binary
i16 = _binary.i16le
i32 = _binary.i32le
##
# Image plugin for PIXAR raster images.
@ -57,7 +58,7 @@ class PixarImageFile(ImageFile.ImageFile):
# FIXME: to be continued...
# create tile descriptor (assuming "dumped")
self.tile = [("raw", (0,0)+self.size, 1024, (self.mode, 0, 1))]
self.tile = [("raw", (0, 0)+self.size, 1024, (self.mode, 0, 1))]
#
# --------------------------------------------------------------------

View File

@ -52,26 +52,27 @@ _MAGIC = b"\211PNG\r\n\032\n"
_MODES = {
# supported bits/color combinations, and corresponding modes/rawmodes
(1, 0): ("1", "1"),
(2, 0): ("L", "L;2"),
(4, 0): ("L", "L;4"),
(8, 0): ("L", "L"),
(16,0): ("I", "I;16B"),
(8, 2): ("RGB", "RGB"),
(16,2): ("RGB", "RGB;16B"),
(1, 3): ("P", "P;1"),
(2, 3): ("P", "P;2"),
(4, 3): ("P", "P;4"),
(8, 3): ("P", "P"),
(8, 4): ("LA", "LA"),
(16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
(8, 6): ("RGBA", "RGBA"),
(16,6): ("RGBA", "RGBA;16B"),
(1, 0): ("1", "1"),
(2, 0): ("L", "L;2"),
(4, 0): ("L", "L;4"),
(8, 0): ("L", "L"),
(16, 0): ("I", "I;16B"),
(8, 2): ("RGB", "RGB"),
(16, 2): ("RGB", "RGB;16B"),
(1, 3): ("P", "P;1"),
(2, 3): ("P", "P;2"),
(4, 3): ("P", "P;4"),
(8, 3): ("P", "P"),
(8, 4): ("LA", "LA"),
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
(8, 6): ("RGBA", "RGBA"),
(16, 6): ("RGBA", "RGBA;16B"),
}
_simple_palette = re.compile(b'^\xff+\x00\xff*$')
# --------------------------------------------------------------------
# Support classes. Suitable for PNG and related formats like MNG etc.
@ -123,15 +124,15 @@ class ChunkStream:
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)
raise SyntaxError("broken PNG file"
"(bad header checksum in %s)" % cid)
def crc_skip(self, cid, data):
"Read checksum. Used if the C module is not present"
self.fp.read(4)
def verify(self, endchunk = b"IEND"):
def verify(self, endchunk=b"IEND"):
# Simple approach; just calculate checksum for all remaining
# blocks. Must be called directly after open.
@ -147,6 +148,7 @@ class ChunkStream:
return cids
# --------------------------------------------------------------------
# Subclass of string to allow iTXt chunks to look like strings while
# keeping their extra information
@ -159,6 +161,7 @@ class iTXt(str):
self.tkey = tkey
return self
# --------------------------------------------------------------------
# PNG chunk container (for use with save(pnginfo=))
@ -182,9 +185,11 @@ class PngInfo:
if zip:
import zlib
self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value))
self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" +
zlib.compress(value))
else:
self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value)
self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" +
value)
def add_text(self, key, value, zip=0):
if isinstance(value, iTXt):
@ -206,6 +211,7 @@ class PngInfo:
else:
self.add(b"tEXt", key + b"\0" + value)
# --------------------------------------------------------------------
# PNG image stream (IHDR/IEND)
@ -218,7 +224,7 @@ class PngStream(ChunkStream):
# local copies of Image attributes
self.im_info = {}
self.im_text = {}
self.im_size = (0,0)
self.im_size = (0, 0)
self.im_mode = None
self.im_tile = None
self.im_palette = None
@ -238,11 +244,12 @@ class PngStream(ChunkStream):
print("Compression method", i8(s[i]))
comp_method = i8(s[i])
if comp_method != 0:
raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
raise SyntaxError("Unknown compression method %s in iCCP chunk" %
comp_method)
try:
icc_profile = zlib.decompress(s[i+2:])
except zlib.error:
icc_profile = None # FIXME
icc_profile = None # FIXME
self.im_info["icc_profile"] = icc_profile
return s
@ -264,7 +271,7 @@ class PngStream(ChunkStream):
def chunk_IDAT(self, pos, length):
# image data
self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)]
self.im_idat = length
raise EOFError
@ -311,7 +318,7 @@ class PngStream(ChunkStream):
s = ImageFile._safe_read(self.fp, length)
px, py = i32(s), i32(s[4:])
unit = i8(s[8])
if unit == 1: # meter
if unit == 1: # meter
dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
self.im_info["dpi"] = dpi
elif unit == 0:
@ -325,7 +332,9 @@ class PngStream(ChunkStream):
try:
k, v = s.split(b"\0", 1)
except ValueError:
k = s; v = b"" # fallback for broken tEXt tags
# fallback for broken tEXt tags
k = s
v = b""
if k:
if bytes is not str:
k = k.decode('latin-1', 'strict')
@ -341,13 +350,15 @@ class PngStream(ChunkStream):
try:
k, v = s.split(b"\0", 1)
except ValueError:
k = s; v = b""
k = s
v = b""
if v:
comp_method = i8(v[0])
else:
comp_method = 0
if comp_method != 0:
raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method)
raise SyntaxError("Unknown compression method %s in zTXt chunk" %
comp_method)
import zlib
try:
v = zlib.decompress(v[1:])
@ -396,15 +407,17 @@ class PngStream(ChunkStream):
return s
self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
return s
# --------------------------------------------------------------------
# PNG reader
def _accept(prefix):
return prefix[:8] == _MAGIC
##
# Image plugin for PNG images.
@ -451,7 +464,7 @@ class PngImageFile(ImageFile.ImageFile):
self.mode = self.png.im_mode
self.size = self.png.im_size
self.info = self.png.im_info
self.text = self.png.im_text # experimental
self.text = self.png.im_text # experimental
self.tile = self.png.im_tile
if self.png.im_palette:
@ -460,7 +473,6 @@ class PngImageFile(ImageFile.ImageFile):
self.__idat = length # used by load_read()
def verify(self):
"Verify PNG file"
@ -489,7 +501,7 @@ class PngImageFile(ImageFile.ImageFile):
while self.__idat == 0:
# end of chunk, skip forward to next one
self.fp.read(4) # CRC
self.fp.read(4) # CRC
cid, pos, length = self.png.read()
@ -509,7 +521,6 @@ class PngImageFile(ImageFile.ImageFile):
return self.fp.read(read_bytes)
def load_end(self):
"internal: finished reading image data"
@ -526,21 +537,22 @@ o32 = _binary.o32be
_OUTMODES = {
# supported PIL modes, and corresponding rawmodes/bits/color combinations
"1": ("1", b'\x01\x00'),
"L;1": ("L;1", b'\x01\x00'),
"L;2": ("L;2", b'\x02\x00'),
"L;4": ("L;4", b'\x04\x00'),
"L": ("L", b'\x08\x00'),
"LA": ("LA", b'\x08\x04'),
"I": ("I;16B", b'\x10\x00'),
"P;1": ("P;1", b'\x01\x03'),
"P;2": ("P;2", b'\x02\x03'),
"P;4": ("P;4", b'\x04\x03'),
"P": ("P", b'\x08\x03'),
"RGB": ("RGB", b'\x08\x02'),
"RGBA":("RGBA", b'\x08\x06'),
"1": ("1", b'\x01\x00'),
"L;1": ("L;1", b'\x01\x00'),
"L;2": ("L;2", b'\x02\x00'),
"L;4": ("L;4", b'\x04\x00'),
"L": ("L", b'\x08\x00'),
"LA": ("LA", b'\x08\x04'),
"I": ("I;16B", b'\x10\x00'),
"P;1": ("P;1", b'\x01\x03'),
"P;2": ("P;2", b'\x02\x03'),
"P;4": ("P;4", b'\x04\x03'),
"P": ("P", b'\x08\x03'),
"RGB": ("RGB", b'\x08\x02'),
"RGBA": ("RGBA", b'\x08\x06'),
}
def putchunk(fp, cid, *data):
"Write a PNG chunk (including CRC field)"
@ -551,15 +563,18 @@ def putchunk(fp, cid, *data):
hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
fp.write(o16(hi) + o16(lo))
class _idat:
# wrap output from the encoder in IDAT chunks
def __init__(self, fp, chunk):
self.fp = fp
self.chunk = chunk
def write(self, data):
self.chunk(self.fp, b"IDAT", data)
def _save(im, fp, filename, chunk=putchunk, check=0):
# save an image to disk (called by the save method)
@ -597,9 +612,9 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
dictionary = b""
im.encoderconfig = ("optimize" in im.encoderinfo,
im.encoderinfo.get("compress_level", -1),
im.encoderinfo.get("compress_type", -1),
dictionary)
im.encoderinfo.get("compress_level", -1),
im.encoderinfo.get("compress_type", -1),
dictionary)
# get the corresponding PNG mode
try:
@ -616,8 +631,8 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
fp.write(_MAGIC)
chunk(fp, b"IHDR",
o32(im.size[0]), o32(im.size[1]), # 0: size
mode, # 8: depth/type
o32(im.size[0]), o32(im.size[1]), # 0: size
mode, # 8: depth/type
b'\0', # 10: compression
b'\0', # 11: filter category
b'\0') # 12: interlace flag
@ -629,7 +644,8 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
palette_bytes += b'\0'
chunk(fp, b"PLTE", palette_bytes)
transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None))
transparency = im.encoderinfo.get('transparency',
im.info.get('transparency', None))
if transparency or transparency == 0:
if im.mode == "P":
@ -686,7 +702,8 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
data = name + b"\0\0" + zlib.compress(im.info["icc_profile"])
chunk(fp, b"iCCP", data)
ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
ImageFile._save(im, _idat(fp, chunk),
[("zip", (0, 0)+im.size, 0, rawmode)])
chunk(fp, b"IEND", b"")
@ -704,8 +721,10 @@ def getchunks(im, **params):
class collector:
data = []
def write(self, data):
pass
def append(self, chunk):
self.data.append(chunk)

View File

@ -27,12 +27,13 @@ from PIL import Image, ImageFile
b_whitespace = string.whitespace
try:
import locale
locale_lang,locale_enc = locale.getlocale()
locale_lang, locale_enc = locale.getlocale()
if locale_enc is None:
locale_lang,locale_enc = locale.getdefaultlocale()
locale_lang, locale_enc = locale.getdefaultlocale()
b_whitespace = b_whitespace.decode(locale_enc)
except: pass
b_whitespace = b_whitespace.encode('ascii','ignore')
except:
pass
b_whitespace = b_whitespace.encode('ascii', 'ignore')
MODES = {
# standard
@ -47,9 +48,11 @@ MODES = {
b"PyCMYK": "CMYK"
}
def _accept(prefix):
return prefix[0:1] == b"P" and prefix[1] in b"0456y"
##
# Image plugin for PBM, PGM, and PPM images.
@ -58,8 +61,8 @@ class PpmImageFile(ImageFile.ImageFile):
format = "PPM"
format_description = "Pbmplus image"
def _token(self, s = b""):
while True: # read until next whitespace
def _token(self, s=b""):
while True: # read until next whitespace
c = self.fp.read(1)
if not c or c in b_whitespace:
break
@ -104,14 +107,14 @@ class PpmImageFile(ImageFile.ImageFile):
# maxgrey
if s > 255:
if not mode == 'L':
raise ValueError("Too many colors for band: %s" %s)
raise ValueError("Too many colors for band: %s" % s)
if s < 2**16:
self.mode = 'I'
rawmode = 'I;16B'
else:
self.mode = 'I';
self.mode = 'I'
rawmode = 'I;32B'
self.size = xsize, ysize
self.tile = [("raw",
(0, 0, xsize, ysize),
@ -123,6 +126,7 @@ class PpmImageFile(ImageFile.ImageFile):
# self.mode = self.im.mode
# self.size = self.im.size
#
# --------------------------------------------------------------------
@ -152,7 +156,7 @@ def _save(im, fp, filename):
fp.write(b"65535\n")
elif rawmode == "I;32B":
fp.write(b"2147483648\n")
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))])
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
# ALTERNATIVE: save via builtin debug function
# im._dump(filename)

View File

@ -28,8 +28,8 @@ MODES = {
(2, 8): ("P", 1),
(3, 8): ("RGB", 3),
(4, 8): ("CMYK", 4),
(7, 8): ("L", 1), # FIXME: multilayer
(8, 8): ("L", 1), # duotone
(7, 8): ("L", 1), # FIXME: multilayer
(8, 8): ("L", 1), # duotone
(9, 8): ("LAB", 3)
}
@ -40,12 +40,14 @@ i8 = _binary.i8
i16 = _binary.i16be
i32 = _binary.i32be
# --------------------------------------------------------------------.
# read PSD images
def _accept(prefix):
return prefix[:4] == b"8BPS"
##
# Image plugin for Photoshop images.
@ -100,12 +102,12 @@ class PsdImageFile(ImageFile.ImageFile):
id = i16(read(2))
name = read(i8(read(1)))
if not (len(name) & 1):
read(1) # padding
read(1) # padding
data = read(i32(read(4)))
if (len(data) & 1):
read(1) # padding
read(1) # padding
self.resources.append((id, name, data))
if id == 1039: # ICC profile
if id == 1039: # ICC profile
self.info["icc_profile"] = data
#
@ -159,6 +161,7 @@ class PsdImageFile(ImageFile.ImageFile):
if self.mode == "P":
Image.Image.load(self)
def _layerinfo(file):
# read layerinfo block
layers = []
@ -166,8 +169,10 @@ def _layerinfo(file):
for i in range(abs(i16(read(2)))):
# bounding box
y0 = i32(read(4)); x0 = i32(read(4))
y1 = i32(read(4)); x1 = i32(read(4))
y0 = i32(read(4))
x0 = i32(read(4))
y1 = i32(read(4))
x1 = i32(read(4))
# image info
info = []
@ -197,7 +202,7 @@ def _layerinfo(file):
elif mode == ["A", "B", "G", "R"]:
mode = "RGBA"
else:
mode = None # unknown
mode = None # unknown
# skip over blend flags and extra information
filler = read(12)
@ -207,8 +212,10 @@ def _layerinfo(file):
if size:
length = i32(read(4))
if length:
mask_y = i32(read(4)); mask_x = i32(read(4))
mask_h = i32(read(4)) - mask_y; mask_w = i32(read(4)) - mask_x
mask_y = i32(read(4))
mask_x = i32(read(4))
mask_h = i32(read(4)) - mask_y
mask_w = i32(read(4)) - mask_x
file.seek(length - 16, 1)
combined += length + 4
@ -219,7 +226,8 @@ def _layerinfo(file):
length = i8(read(1))
if length:
# Don't know the proper encoding, Latin-1 should be a good guess
# Don't know the proper encoding,
# Latin-1 should be a good guess
name = read(length).decode('latin-1', 'replace')
combined += length + 1
@ -239,6 +247,7 @@ def _layerinfo(file):
return layers
def _maketile(file, mode, bbox, channels):
tile = None
@ -283,7 +292,7 @@ def _maketile(file, mode, bbox, channels):
file.seek(offset)
if offset & 1:
read(1) # padding
read(1) # padding
return tile

View File

@ -16,7 +16,8 @@
# * Implements the pixel access object following Access.
# * Does not implement the line functions, as they don't appear to be used
# * Taking only the tuple form, which is used from python.
# * Fill.c uses the integer form, but it's still going to use the old Access.c implementation.
# * Fill.c uses the integer form, but it's still going to use the old
# Access.c implementation.
#
from __future__ import print_function
@ -25,7 +26,7 @@ from cffi import FFI
import sys
DEBUG = 0
defs = """
struct Pixel_RGBA {
unsigned char r,g,b,a;
@ -39,8 +40,8 @@ ffi.cdef(defs)
class PyAccess(object):
def __init__(self, img, readonly = False):
def __init__(self, img, readonly=False):
vals = dict(img.im.unsafe_ptrs)
self.readonly = readonly
self.image8 = ffi.cast('unsigned char **', vals['image8'])
@ -48,13 +49,14 @@ class PyAccess(object):
self.image = ffi.cast('unsigned char **', vals['image'])
self.xsize = vals['xsize']
self.ysize = vals['ysize']
if DEBUG:
print (vals)
self._post_init()
def _post_init(): pass
def _post_init():
pass
def __setitem__(self, xy, color):
"""
Modifies the pixel at x,y. The color is given as a single
@ -62,11 +64,12 @@ class PyAccess(object):
multi-band images
:param xy: The pixel coordinate, given as (x, y).
:param value: The pixel value.
:param value: The pixel value.
"""
if self.readonly: raise ValueError('Attempt to putpixel a read only image')
(x,y) = self.check_xy(xy)
return self.set_pixel(x,y,color)
if self.readonly:
raise ValueError('Attempt to putpixel a read only image')
(x, y) = self.check_xy(xy)
return self.set_pixel(x, y, color)
def __getitem__(self, xy):
"""
@ -76,94 +79,98 @@ class PyAccess(object):
:param xy: The pixel coordinate, given as (x, y).
"""
(x,y) = self.check_xy(xy)
return self.get_pixel(x,y)
(x, y) = self.check_xy(xy)
return self.get_pixel(x, y)
putpixel = __setitem__
getpixel = __getitem__
def check_xy(self, xy):
(x,y) = xy
(x, y) = xy
if not (0 <= x < self.xsize and 0 <= y < self.ysize):
raise ValueError('pixel location out of range')
return xy
class _PyAccess32_2(PyAccess):
""" PA, LA, stored in first and last bytes of a 32 bit word """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
pixel = self.pixels[y][x]
return (pixel.r, pixel.a)
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
pixel = self.pixels[y][x]
# tuple
pixel.r = min(color[0],255)
pixel.a = min(color[1],255)
pixel.r = min(color[0], 255)
pixel.a = min(color[1], 255)
class _PyAccess32_3(PyAccess):
""" RGB and friends, stored in the first three bytes of a 32 bit word """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
pixel = self.pixels[y][x]
return (pixel.r, pixel.g, pixel.b)
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
pixel = self.pixels[y][x]
# tuple
pixel.r = min(color[0],255)
pixel.g = min(color[1],255)
pixel.b = min(color[2],255)
pixel.r = min(color[0], 255)
pixel.g = min(color[1], 255)
pixel.b = min(color[2], 255)
class _PyAccess32_4(PyAccess):
""" RGBA etc, all 4 bytes of a 32 bit word """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
pixel = self.pixels[y][x]
return (pixel.r, pixel.g, pixel.b, pixel.a)
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
pixel = self.pixels[y][x]
# tuple
pixel.r = min(color[0],255)
pixel.g = min(color[1],255)
pixel.b = min(color[2],255)
pixel.a = min(color[3],255)
pixel.r = min(color[0], 255)
pixel.g = min(color[1], 255)
pixel.b = min(color[2], 255)
pixel.a = min(color[3], 255)
class _PyAccess8(PyAccess):
""" 1, L, P, 8 bit images stored as uint8 """
def _post_init(self, *args, **kwargs):
self.pixels = self.image8
def get_pixel(self, x,y):
def get_pixel(self, x, y):
return self.pixels[y][x]
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
try:
# integer
self.pixels[y][x] = min(color,255)
self.pixels[y][x] = min(color, 255)
except:
# tuple
self.pixels[y][x] = min(color[0],255)
self.pixels[y][x] = min(color[0], 255)
class _PyAccessI16_N(PyAccess):
""" I;16 access, native bitendian without conversion """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast('unsigned short **', self.image)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
return self.pixels[y][x]
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
try:
# integer
self.pixels[y][x] = min(color, 65535)
@ -171,16 +178,17 @@ class _PyAccessI16_N(PyAccess):
# tuple
self.pixels[y][x] = min(color[0], 65535)
class _PyAccessI16_L(PyAccess):
""" I;16L access, with conversion """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast('struct Pixel_I16 **', self.image)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
pixel = self.pixels[y][x]
return pixel.l + pixel.r * 256
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
pixel = self.pixels[y][x]
try:
color = min(color, 65535)
@ -190,16 +198,17 @@ class _PyAccessI16_L(PyAccess):
pixel.l = color & 0xFF
pixel.r = color >> 8
class _PyAccessI16_B(PyAccess):
""" I;16B access, with conversion """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast('struct Pixel_I16 **', self.image)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
pixel = self.pixels[y][x]
return pixel.l *256 + pixel.r
return pixel.l * 256 + pixel.r
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
pixel = self.pixels[y][x]
try:
color = min(color, 65535)
@ -209,17 +218,19 @@ class _PyAccessI16_B(PyAccess):
pixel.l = color >> 8
pixel.r = color & 0xFF
class _PyAccessI32_N(PyAccess):
""" Signed Int32 access, native endian """
def _post_init(self, *args, **kwargs):
self.pixels = self.image32
def get_pixel(self, x,y):
def get_pixel(self, x, y):
return self.pixels[y][x]
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
self.pixels[y][x] = color
class _PyAccessI32_Swap(PyAccess):
""" I;32L/B access, with byteswapping conversion """
def _post_init(self, *args, **kwargs):
@ -228,24 +239,26 @@ class _PyAccessI32_Swap(PyAccess):
def reverse(self, i):
orig = ffi.new('int *', i)
chars = ffi.cast('unsigned char *', orig)
chars[0],chars[1],chars[2],chars[3] = chars[3], chars[2],chars[1],chars[0]
chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], \
chars[1], chars[0]
return ffi.cast('int *', chars)[0]
def get_pixel(self, x,y):
def get_pixel(self, x, y):
return self.reverse(self.pixels[y][x])
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
self.pixels[y][x] = self.reverse(color)
class _PyAccessF(PyAccess):
""" 32 bit float access """
def _post_init(self, *args, **kwargs):
self.pixels = ffi.cast('float **', self.image32)
def get_pixel(self, x,y):
def get_pixel(self, x, y):
return self.pixels[y][x]
def set_pixel(self, x,y, color):
def set_pixel(self, x, y, color):
try:
# not a tuple
self.pixels[y][x] = color
@ -275,7 +288,7 @@ if sys.byteorder == 'little':
mode_map['I;16'] = _PyAccessI16_N
mode_map['I;16L'] = _PyAccessI16_N
mode_map['I;16B'] = _PyAccessI16_B
mode_map['I;32L'] = _PyAccessI32_N
mode_map['I;32B'] = _PyAccessI32_Swap
else:
@ -285,14 +298,16 @@ else:
mode_map['I;32L'] = _PyAccessI32_Swap
mode_map['I;32B'] = _PyAccessI32_N
def new(img, readonly=False):
def new(img, readonly=False):
access_type = mode_map.get(img.mode, None)
if not access_type:
if DEBUG: print ("PyAccess Not Implemented: %s" % img.mode)
if DEBUG:
print("PyAccess Not Implemented: %s" % img.mode)
return None
if DEBUG: print ("New PyAccess: %s" % img.mode)
if DEBUG:
print("New PyAccess: %s" % img.mode)
return access_type(img, readonly)
# End of file

View File

@ -16,6 +16,7 @@
from PIL import ContainerIO
##
# A file object that provides read access to a given member of a TAR
# file.

View File

@ -33,6 +33,7 @@ except ImportError:
i32 = _binary.i32le
##
# Load texture from a Quake2 WAL texture file.
# <p>

View File

@ -12,7 +12,7 @@ _VALID_WEBP_MODES = {
_VP8_MODES_BY_IDENTIFIER = {
b"VP8 ": "RGB",
b"VP8X": "RGBA",
b"VP8L": "RGBA", # lossless
b"VP8L": "RGBA", # lossless
}
@ -30,7 +30,8 @@ class WebPImageFile(ImageFile.ImageFile):
format_description = "WebP image"
def _open(self):
data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode(self.fp.read())
data, width, height, self.mode, icc_profile, exif = \
_webp.WebPDecode(self.fp.read())
if icc_profile:
self.info["icc_profile"] = icc_profile

View File

@ -24,6 +24,7 @@ _handler = None
if str != bytes:
long = int
##
# Install application-specific WMF image handler.
#
@ -43,7 +44,7 @@ if hasattr(Image.core, "drawwmf"):
self.bbox = im.info["wmf_bbox"]
def load(self, im):
im.fp.seek(0) # rewind
im.fp.seek(0) # rewind
return Image.frombytes(
"RGB", im.size,
Image.core.drawwmf(im.fp.read(), im.size, self.bbox),
@ -56,6 +57,7 @@ if hasattr(Image.core, "drawwmf"):
word = _binary.i16le
def short(c, o=0):
v = word(c, o)
if v >= 32768:
@ -64,6 +66,7 @@ def short(c, o=0):
dword = _binary.i32le
#
# --------------------------------------------------------------------
# Read WMF file
@ -74,6 +77,7 @@ def _accept(prefix):
prefix[:4] == b"\x01\x00\x00\x00"
)
##
# Image plugin for Windows metafiles.
@ -95,8 +99,10 @@ class WmfStubImageFile(ImageFile.StubImageFile):
inch = word(s, 14)
# get bounding box
x0 = short(s, 6); y0 = short(s, 8)
x1 = short(s, 10); y1 = short(s, 12)
x0 = short(s, 6)
y0 = short(s, 8)
x1 = short(s, 10)
y1 = short(s, 12)
# normalize size to 72 dots per inch
size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch
@ -115,8 +121,10 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# enhanced metafile
# get bounding box
x0 = dword(s, 8); y0 = dword(s, 12)
x1 = dword(s, 16); y1 = dword(s, 20)
x0 = dword(s, 8)
y0 = dword(s, 12)
x1 = dword(s, 16)
y1 = dword(s, 20)
# get frame (in 0.01 millimeter units)
frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)

View File

@ -30,6 +30,7 @@ for r in range(8):
for b in range(4):
PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3))
##
# Image plugin for XV thumbnail images.

View File

@ -35,9 +35,11 @@ xbm_head = re.compile(
b"[\\000-\\377]*_bits\\[\\]"
)
def _accept(prefix):
return prefix.lstrip()[:7] == b"#define"
##
# Image plugin for X11 bitmaps.
@ -81,7 +83,7 @@ def _save(im, fp, filename):
fp.write(b"static char im_bits[] = {\n")
ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)])
ImageFile._save(im, fp, [("xbm", (0, 0)+im.size, 0, None)])
fp.write(b"};\n")

View File

@ -12,7 +12,7 @@
# ;-)
VERSION = '1.1.7' # PIL version
PILLOW_VERSION = '2.5.3' # Pillow
PILLOW_VERSION = '2.5.3' # Pillow
_plugins = ['BmpImagePlugin',
'BufrStubImagePlugin',

View File

@ -16,16 +16,17 @@ if bytes is str:
return ord(c)
def o8(i):
return chr(i&255)
return chr(i & 255)
else:
def i8(c):
return c if c.__class__ is int else c[0]
def o8(i):
return bytes((i&255,))
return bytes((i & 255,))
# Input, le = little endian, be = big endian
#TODO: replace with more readable struct.unpack equivalent
# TODO: replace with more readable struct.unpack equivalent
def i16le(c, o=0):
"""
Converts a 2-bytes (16 bits) string to an integer.
@ -33,7 +34,8 @@ def i16le(c, o=0):
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return i8(c[o]) | (i8(c[o+1])<<8)
return i8(c[o]) | (i8(c[o+1]) << 8)
def i32le(c, o=0):
"""
@ -42,24 +44,33 @@ def i32le(c, o=0):
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24)
return (i8(c[o]) | (i8(c[o+1]) << 8) | (i8(c[o+2]) << 16) |
(i8(c[o+3]) << 24))
def i16be(c, o=0):
return (i8(c[o])<<8) | i8(c[o+1])
return (i8(c[o]) << 8) | i8(c[o+1])
def i32be(c, o=0):
return (i8(c[o])<<24) | (i8(c[o+1])<<16) | (i8(c[o+2])<<8) | i8(c[o+3])
return ((i8(c[o]) << 24) | (i8(c[o+1]) << 16) |
(i8(c[o+2]) << 8) | i8(c[o+3]))
# Output, le = little endian, be = big endian
def o16le(i):
return o8(i) + o8(i>>8)
return o8(i) + o8(i >> 8)
def o32le(i):
return o8(i) + o8(i>>8) + o8(i>>16) + o8(i>>24)
return o8(i) + o8(i >> 8) + o8(i >> 16) + o8(i >> 24)
def o16be(i):
return o8(i>>8) + o8(i)
return o8(i >> 8) + o8(i)
def o32be(i):
return o8(i>>24) + o8(i>>16) + o8(i>>8) + o8(i)
return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i)
# End of file

View File

@ -19,7 +19,3 @@ Pillow is the "friendly" PIL fork by `Alex Clark and Contributors <https://githu
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png
:target: https://landscape.io/github/python-pillow/Pillow/master
:alt: Code Health

55
RELEASING.md Normal file
View File

@ -0,0 +1,55 @@
# Release Checklist
## Main Release
Released quarterly.
* [ ] Get master to the appropriate code release state. [Travis CI](https://travis-ci.org/python-pillow/Pillow) should be running cleanly for all merges to master.
* [ ] Update version in `PIL/__init__.py`, `setup.py`, `_imaging.c`, Update date in `CHANGES.rst`.
* [ ] Tag and push to release branch in python-pillow repo.
* [ ] Upload binaries.
## Point Release
Released as required for security or installation fixes.
* [ ] Make necessary changes in master.
* [ ] Cherry pick individual commits. Touch up `CHANGES.rst` to reflect reality.
* [ ] Update version in `PIL/__init__.py`, `setup.py`, `_imaging.c`
* [ ] Push to release branch in personal repo. Let Travis run cleanly.
* [ ] Tag and push to release branch in python-pillow repo.
* [ ] Upload binaries.
## Embargoed Release
Security fixes that need to be pushed to the distros prior to public release.
* [ ] Prepare patch for all versions that will get a fix. Test against local installations.
* [ ] Commit against master, cherry pick to affected release branches.
* [ ] Run local test matrix on each release & Python version.
* [ ] Privately send to distros.
* [ ] Amend any commits with the CVE #
* [ ] On release date, tag and push to GitHub.
```
git checkout 2.5.x
git tag 2.5.3
git push origin 2.5.x
git push origin --tags
```
* [ ] Upload binaries
## Binary Upload Process
* [ ] Ping cgohlke for Windows binaries
* [ ] From a clean source directory with no extra temp files:
```
python setup.py register
python setup.py sdist --format=zip upload
python setup.py sdist upload
```
(Debian requests a tarball, everyone else would just prefer that we choose one and stick to it. So both it is)
* [ ] Push a commit to https://github.com/python-pillow/pillow-wheels to build OSX versions (UNDONE latest tag or specific release???)
* [ ] Retrieve the OS X Wheels from Rackspace files, upload to PyPi (Twine?)
* [ ] Grab Windows binaries, `twine upload dist/*.[whl|egg]`. Manually upload .exe installers.
* [ ] Announce release availability. [Twitter](https://twitter.com/pythonpillow), web.

View File

@ -6,5 +6,3 @@ import sys
if sys.maxsize < 2**32:
im = Image.new('L', (999999, 999999), 0)

View File

@ -1,4 +1,4 @@
from helper import *
from helper import unittest, PillowTestCase, hopper
# Not running this test by default. No DOS against Travis CI.
@ -39,7 +39,7 @@ def timer(func, label, *args):
class BenchCffiAccess(PillowTestCase):
def test_direct(self):
im = lena()
im = hopper()
im.load()
# im = Image.new( "RGB", (2000, 2000), (1, 3, 2))
caccess = im.im.pixel_access(False)

View File

@ -6,7 +6,7 @@ import timeit
def bench(mode):
im = helper.lena(mode)
im = helper.hopper(mode)
get = im.im.getpixel
xy = 50, 50 # position shouldn't really matter
t0 = timeit.default_timer()

View File

@ -1,5 +1,5 @@
# Tests potential DOS of IcnsImagePlugin with 0 length block.
# Run from anywhere that PIL is importable.
# Run from anywhere that PIL is importable.
from PIL import Image
from io import BytesIO
@ -7,4 +7,5 @@ from io import BytesIO
if bytes is str:
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00')))
else:
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00', 'latin-1')))
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00',
'latin-1')))

View File

@ -1,11 +1,13 @@
# Tests potential DOS of Jpeg2kImagePlugin with 0 length block.
# Run from anywhere that PIL is importable.
# Run from anywhere that PIL is importable.
from PIL import Image
from io import BytesIO
if bytes is str:
Image.open(BytesIO(bytes('\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang')))
Image.open(BytesIO(bytes(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang')))
else:
Image.open(BytesIO(bytes('\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang', 'latin-1')))
Image.open(BytesIO(bytes(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang',
'latin-1')))

212
Tests/check_jpeg_leaks.py Normal file
View File

@ -0,0 +1,212 @@
from helper import unittest, PillowTestCase, hopper
from PIL import Image
from io import BytesIO
import sys
iterations = 5000
"""
When run on a system without the jpeg leak fixes, the valgrind runs look like this.
NOSE_PROCESSES=0 NOSE_TIMEOUT=600 valgrind --tool=massif \
python test-installed.py -s -v Tests/check_jpeg_leaks.py
"""
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class TestJpegLeaks(PillowTestCase):
"""
pre patch:
MB
31.62^ :
| @:@:@:@#::
| @:@:@@:@:@:@:@:@#::
| ::::::::@:@:@@:@:@:@:@:@#::
| :::::@::::::: ::::@:@:@@:@:@:@:@:@#::
| @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| ::::::@::::@:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| ::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| ::::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| : ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| @: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| @@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :@@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :@@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :@:@@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :@:@@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
| :@:@@: ::::::::: : :@: : @:::::::::::::@: : ::: ::::@:@:@@:@:@:@:@:@#::
0 +----------------------------------------------------------------------->Gi
0 8.535
post-patch:
MB
21.03^ :::@@:::@::::@@:::::::@@::::::::@::::::::::::@:::@:::::::@::::
| #:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| #:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :::#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| : :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| : :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| @: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| @@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| @@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| @@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
| :@:@@: :#:::@ :::@::::@ : :: : @ :::::: :@:: ::: :::: @:: @:::::::@::::
0 +----------------------------------------------------------------------->Gi
0 8.421
"""
def test_qtables_leak(self):
im = hopper('RGB')
standard_l_qtable = [int(s) for s in """
16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56
14 17 22 29 51 87 80 62
18 22 37 56 68 109 103 77
24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99
""".split(None)]
standard_chrominance_qtable = [int(s) for s in """
17 18 24 47 99 99 99 99
18 21 26 66 99 99 99 99
24 26 56 99 99 99 99 99
47 66 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99
""".split(None)]
qtables = [standard_l_qtable,
standard_chrominance_qtable]
for count in range(iterations):
test_output = BytesIO()
im.save(test_output, "JPEG", qtables=qtables)
"""
pre patch:
MB
177.1^ #
| @@@#
| :@@@@@@#
| ::::@@@@@@#
| ::::::::@@@@@@#
| @@::::: ::::@@@@@@#
| @@@@ ::::: ::::@@@@@@#
| @@@@@@@ ::::: ::::@@@@@@#
| @@::@@@@@@@ ::::: ::::@@@@@@#
| @@@@ : @@@@@@@ ::::: ::::@@@@@@#
| @@@@@@ @@ : @@@@@@@ ::::: ::::@@@@@@#
| @@@@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| @::@@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| ::::@: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| :@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| ::@@::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| @@::: @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| @::@ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| :::@: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
| @@@:: @: @ : : @ ::@@: : @: @@ @@ @@ @ @@ : @@@@@@@ ::::: ::::@@@@@@#
0 +----------------------------------------------------------------------->Gi
0 11.37
post patch:
MB
21.06^ ::::::::::::::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| ##::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| # ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @@@@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
| @ @@@# ::: ::::: : ::::::::::@::::@::::@::::@::::@::::@:::::::::@::::::
0 +----------------------------------------------------------------------->Gi
0 11.33
"""
def test_exif_leak(self):
im = hopper('RGB')
exif = b'12345678'*4096
for count in range(iterations):
test_output = BytesIO()
im.save(test_output, "JPEG", exif=exif)
"""
base case:
MB
20.99^ ::::: :::::::::::::::::::::::::::::::::::::::::::@:::
| ##: : ::::::@::::::: :::: :::: : : : : : : :::::::::::: :::@:::
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@# : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@@ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| :@@@@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
| :@ @@ @ # : : :: :: @:: :::: :::: :::: : : : : : : :::::::::::: :::@:::
0 +----------------------------------------------------------------------->Gi
0 7.882
"""
def test_base_save(self):
im = hopper('RGB')
for count in range(iterations):
test_output = BytesIO()
im.save(test_output, "JPEG")
if __name__ == '__main__':
unittest.main()

View File

@ -9,6 +9,6 @@ i = Image.new("RGB", (500, h), "white")
d = ImageDraw.Draw(i)
# this line causes a MemoryError
d.text((0,0), s, font=f, fill=0)
d.text((0, 0), s, font=f, fill=0)
i.show()

View File

@ -100,7 +100,8 @@ class PillowTestCase(unittest.TestCase):
ave_diff = float(diff)/(a.size[0]*a.size[1])
self.assertGreaterEqual(
epsilon, ave_diff,
(msg or '') + " average pixel value difference %.4f > epsilon %.4f" % (
(msg or '') +
" average pixel value difference %.4f > epsilon %.4f" % (
ave_diff, epsilon))
def assert_warning(self, warn_class, func):
@ -138,7 +139,8 @@ class PillowTestCase(unittest.TestCase):
if travis is not None:
skip = skip and (travis == bool(os.environ.get('TRAVIS', False)))
if interpreter is not None:
skip = skip and (interpreter == 'pypy' and hasattr(sys, 'pypy_version_info'))
skip = skip and (interpreter == 'pypy' and
hasattr(sys, 'pypy_version_info'))
if skip:
self.skipTest(msg or "Known Bad Test")
@ -155,7 +157,7 @@ class PillowTestCase(unittest.TestCase):
raise IOError()
outfile = self.tempfile("temp.png")
if command_succeeds(['convert', f, outfile]):
if command_succeeds([IMCONVERT, f, outfile]):
from PIL import Image
return Image.open(outfile)
raise IOError()
@ -200,26 +202,6 @@ def hopper(mode="RGB", cache={}):
return im
def lena(mode="RGB", cache={}):
from PIL import Image
im = None
# FIXME: Implement caching to reduce reading from disk but so an original
# copy is returned each time and the cached image isn't modified by tests
# (for fast, isolated, repeatable tests).
# im = cache.get(mode)
if im is None:
if mode == "RGB":
im = Image.open("Tests/images/lena.ppm")
elif mode == "F":
im = lena("L").convert(mode)
elif mode[:4] == "I;16":
im = lena("I").convert(mode)
else:
im = lena("RGB").convert(mode)
# cache[mode] = im
return im
def command_succeeds(cmd):
"""
Runs the command, which must be a list of strings. Returns True if the
@ -249,6 +231,14 @@ def netpbm_available():
def imagemagick_available():
return command_succeeds(['convert', '-version'])
return IMCONVERT and command_succeeds([IMCONVERT, '-version'])
if sys.platform == 'win32':
IMCONVERT = os.environ.get('MAGICK_HOME', '')
if IMCONVERT:
IMCONVERT = os.path.join(IMCONVERT, 'convert.exe')
else:
IMCONVERT = 'convert'
# End of file

Binary file not shown.

25
Tests/icc/LICENSE.txt Normal file
View File

@ -0,0 +1,25 @@
from http://www.color.org/srgbprofiles.xalter
Terms of use
To anyone who acknowledges that the file "sRGB_v4_ICC_preference.icc"
is provided "AS IS" WITH NO EXPRESS OR IMPLIED WARRANTY, permission
to use, copy and distribute this file for any purpose is hereby
granted without fee, provided that the file is not changed including
the ICC copyright notice tag, and that the name of ICC shall not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission. ICC makes no
representations about the suitability of this software for any
purpose.
To anyone who acknowledges that the file
"sRGB_IEC61966-2-1_blackscaled.icc" is provided "AS IS" WITH NO
EXPRESS OR IMPLIED WARRANTY, permission to use, copy and distribute
these file for any purpose is hereby granted without fee, provided
that the file is not changed including the ICC copyright notice tag,
and that the name of ICC shall not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission. ICC makes no representations about the suitability
of this software for any purpose.

Binary file not shown.

Binary file not shown.

BIN
Tests/images/hopper.Lab.tif Normal file

Binary file not shown.

BIN
Tests/images/hopper.fli Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
Tests/images/hopper.psd Normal file

Binary file not shown.

BIN
Tests/images/hopper.tif Normal file

Binary file not shown.

BIN
Tests/images/hopper.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

178
Tests/images/hopper.xpm Normal file
View File

@ -0,0 +1,178 @@
/* XPM */
static char *hopper[] = {
/* columns rows colors chars-per-pixel */
"128 128 44 1 ",
" c #110E13",
". c #2A1110",
"X c #13122C",
"o c #25192A",
"O c #312831",
"+ c #302219",
"@ c #5F241A",
"# c #6F4832",
"$ c #17174A",
"% c #26264F",
"& c #65595D",
"* c #4B3C54",
"= c #8E1F1A",
"- c #914E33",
"; c #A75335",
": c #AC6736",
"> c #B26F4E",
", c #9B6657",
"< c #CA7652",
"1 c #CC7866",
"2 c #C25135",
"3 c #B0896D",
"4 c #D28859",
"5 c #D28E6D",
"6 c #E79371",
"7 c #E5A36E",
"8 c #D58932",
"9 c #3E65AE",
"0 c #4970B5",
"q c #606D95",
"w c #557CC1",
"e c #91778A",
"r c #6A86B9",
"t c #5C83C3",
"y c #6D8FCA",
"u c #7799E3",
"i c #A89A9D",
"p c #CFAA90",
"a c #F4B08F",
"s c #D6A799",
"d c #ECCCAB",
"f c #F9F4F4",
"g c #E3D8D5",
"h c #AAAECA",
/* pixels */
"$XX$$XXX$%$$$$$$$$$$$$$$$$$$$egip3dr %% @p# Oqgffgfgfgo%q999q9999999999999999999099999099099990999999909909990w999000wwww00w00w0",
"$$X$X$XX$$$$$$$$9$$$$XX$$$$$$ws&7+O&%$Xo@3#XXX&ihffggg%%9q*9*%q99qq9q99999999999999090900909909909099999999999999999000wwwwww0ww",
"X$X$$XXX$$$$$$$$$$$$$$$$$$$$$eq@i#+$$$%X#pOX$%$%*dfffgiX%%OX%XO*%**990909999999009090990900999999099990999w999990990000wwwwwwwww",
"$XX$$$XX$$$$$$$$$$$$XX$$$$$$$*O*7#XX%$%X@p*XX$%Xohfi3hOXOoX*r3*OOO%O99099999q0q990990009090909000999090999999099909999ww0w0000w0",
"$XX$$XXX$$$$$$$$$9$$$$$$$$$$o$.&3++X$$$o#p+X$$%$OhgOX oo+ii*if&+ri&OO%q0990099009090090990909009990999099w999990999090w0000w0ww0",
"X$X$$XXX$$$$$$$$$$$$$$$$$$$$$oX#p#OO%$$o#7O%X$$XOg&O o.&fffgifirfffiOO*&qq990990000000009909099000990990999990999w90099000000000",
"XX$$$XXX$$$$$$$$$$$$$$$$$$$$$oo*3+o$$%$.#7+X$%XOOhii+X,fgifffffffddfgOOrhi0qq000090000090099990909909009w9w9909990999099900000w0",
"X$X$$$XX$$$$$%$$$$$$X$X$$$$$X$X#7@X$$$$o:3+XX$OqgffrXOe&&#*&iggi&&3&*&OOfffgrq900009000099099090000909009999990999900900000000w0",
"X$q$XXXX$$$X%$$$$$$$X$$$$$$$$$X*p+O$%$$o-3oOX%gffff&o++3pgg3fddd3fdg&+OOhffffgt0099090090009090990909990999990999000099090090000",
"X$g&$XXX%$X$%$$$$$$$$$X$$$$$$$o*p+X%$%$X,5+ *gfffff& ++&pddffffffgdd#OOOiffffffhq9qt009w990090900999w9w9900999w90999909009900000",
"X$ggO %$$$X$%$$$$$$$$$$$$$$$Xo*3#$$%%$X,3+&ffffffg#+.+Odddgggggggpd*+OOifffffffgtqt09t909090990090909099990099w0990090909090000",
"X%hfr&h&$$$$%q$X$$$$XXX$$$$$Xoo*p@o$$$$X:3OffffgppdO. ++,ddfffffffd3+ ++&ffffffffgq900909090900999099090w9w990990009009090009900",
"Xohfffh&X$o$$h&$$$$$$$X$$$$$Xo$*7# %%$$ 35dffg73&#++++ +pd3gfffffg3d,++++3ddffffffhq90009099990999909090990909w99990099900900000",
"oegffgiO$$$$$gh$$%&qXXXX$%XX$X.*3# X%X$+#pgfd3&+. + ++3iddgfffddpd&+O++++#pgfffffrq9900900q99090909990099009990009990999090000",
"hffffg&X$%$X$gg*&hg*XXXX%i%$$Xo*5#XX$$$o,dfg&+.. .#&++ +&ddd3gfgdddd#++++&+++#3dfgghq009099090000909090990999090909000099000000w",
"hgffffp*X$X%Ogggfgh$XXX$%g*XXooO5#o$%$$+&df3...&#++3 O#+#pd7di&,ppdd&+++#3+&&..#iffgqq009009009909900990090009990009999009990000",
"o%qffgh&o%o*iffgfg%%X$XX*gi OqO*p#+$%$XO&h3 . +pdpp3pdgdd3#+++++++#ppggpp3ddp+##+ifghqrq09090090090090900909999009009w900009w0w9",
"Xo*fh**%$%igfgfgg&%$$X$X&ggih3o+p# X%X$XX .+33+pdppdddfp,#+O&dfggdddd3pdddd+++dh&%%&q0090909009909090090000900w000w99w0900ww",
"X%&fq%oX$%&hgfffgi%oXXX &fgshOoO3#+X%%XX+ . +#&++7dp3&#O&O&#&33dd3#3&.+#ppiiO#+ X%9909000909099090090900009w9w009ww9ww9w00",
"X$%g*o$XX%Xo*gffffq$X%&egfhp&oXO7# XXX + #++.+++ +..O+ ++O++#ppp+++&3++ o XXq9000900009000090009000009w909w09w00w00w",
"$$*i$$oX$XoX%hfeegg%$%&idggh+X$O4#+XOX + . + ..o + + ++ +&3gg#. X999909000000000000009000000000w09w0www0w",
"$$%%$$$X$$o$$hh*$$q&$o o3ggd,ooo3#+ ++ +++ ++*++ X%0q9q900000ww000000000000ww99w00w9w99ww0",
"$$$$%%X$$$X$%g0$$%%%oX .&fshg*Xo3: X + + X*0000000000w000000000w00000ww0www90wwww0",
"$%$$%$$XX$$X%h$%$%$$XXX qg*XiqXo3:+ %0q0000000w00000w0ww000w0ww990009w0w99ww",
"$$$%%%$$$XX$$%%$%$$$XXXXihXXX%X.,: %t000t90w0w0000090w00w00009w000wwww9www9",
"$$$$$%X$X$$$$%$$$$$$XXXXq*XXXXXo3: . %0q0000t0w0ww00ww00ww00ww0w9w0w0w99ww9ww",
"$$$$$$$XX$X$$$$$$$$$$XXX*$$XXXX :> %q0t0t00w0w0w0w00ww0w0w0w00w0w0www90w09w",
"$$$$$$XX$$XX$$$$%$$$$XXX$$$$XXX.:3. . . .... .. . ....... X%qw000t00www0wwwwwww0ww0www0w00w00w09w9w",
"$$$$%%$$$XX$$$$$$$$$XXXX$$$$XXX -3 . ....+@@@@@@.@@....@@@@#####*@.. %ttt9ttt0t0tw0www0ww0ww00w9ww0w09w9www0w",
"$$$%$%$$X$$$$$$$%%9$XXXX$$$$XXX :3 ...#->1>>1,>>,,--;,--,,,1531>13>#@. X09q9r00t0t0wwwww0w0wwwwwwww0wwwwww0w00w",
"$$$$$$XXX$$$$$$$$$$$XX X$$$$$XX.:: .#11ss65assa56a511155557a775555p5,,. Xqttr0trt0wtwwwwww0ww0wwwwww0wwwww0ww0ww",
"$$$$$$$X$$$$9$9$$$$$XXXXX$$$XX$ >;. ...#,pps55paaaaasadasaaaaaaaaa777a77p53,*... Xt0tq9t0ttwww0wwwwwwww0wwww0wwww0wwwwwww",
"*o$%$%$XX$$$$$$$%$$$$XXXX$$$$XXX,> +#*-,5pp7apasaadddaaaaddadaaadddaaa6pp733,##. Xqrtttttt0t0rt0tt000t0t0t0t00t0t0ttt0ttt",
"go%$$*%$XXX$$$$$$$$$$X XX$$$$XX :> .&,#,5p77p7aaaaadaaadaddaaadaaaasaapp7775534#@ Xr90tq0ttt0tt0ttt0ttttt00tttt0tt0t0tttt0",
"fio%whXXX%$$$$$$$$$$$XXX$$$$X$X.,8. o###-155pppadaasasaaadaaaaaaaaa51>3355455535>>#+ %t0r0r0rttt00tt0ttttttt0tttttt0ttttttttt",
"gg&gf&X *qo$$$$$$$$$$X X$$$$$XX 3: OO##,135565431>113sadaaaddsss611>1>>55s5355333,*+ %rr0r0ttt0tttttrtt0ttqttt0ttttttttt0tttt",
"gffghOXX*g*$$$%$$$$$XXXX$$$$XXX -> +###,>3575p5<><355saaaaaaaap615>;;-;>5a7p335333#+ %rrtttt0t0trtttttttttttttttttttttttttttt",
"fgfg&oo &giO$qi%X$$$XX XX$$$XX .::+ +#,,,3355p55>>;;;,>16s777aaas61>;=--@#@@@@##&##&&+X*rrtttttttt0tttttttttttttttttttttttttttt",
"gggg&o &gg,sfe$X$$$$X XX$$$XXX :: +&#-,,35333>>,-,-,>>15apaadap1>>;@@@#;-##@+,&@##&O+&rttttt0ttttttt0tttttttttttttttttttttttt",
"gffgg*oX&gfggh%X$X$$XX X%%XXX 3:+ ++3:-####@@.@..@++@@->6aaaaaa74>=@,3,11335d&+@+O*&##qrrttttytttttttrtttttttttttttttttttttttt",
"sggggho*dgggg&O$$$$$ *+Oii$X X .::+++&,-##+@+@@:ss1,1i1--=:4a77764::a1,@@@@=,,4d#+##*&,-eqyrwyrwttttttttttttttytytttytytttyttttt",
"id**qi&Ogggdd+X$$$$$o*&eh*X XXX.::.O#,>-#@...+,,,-@@@@-5><<<76748:2<>-@#.@@@@=-@##@@+#,isiryywyyrttrttttyttytytttttytttttttttytu",
",hO%X$Xo&gggg&O$$$$XoO,e&OXXXXX.3:.@33;#+@@@.-,>-@.@..@=::;<4<4<:28<2@#@.@@@@==-,-##-,1,eheywyywyttttyytytyttttttyttttttttytyttt",
"i&o$$X Ogggfg%X$$$$oO,&+ XX X :3+@3,,;##@#@##,@@@..@-;==;426a76<<<;>;--#==;;::4>4>:;:==1ryewywttyrtytttytyytyyttyttuytyttyttyt",
"**%$$$XX+hiO&h&%$$$XX*,,& XXX X ::+#1-#-;,,>,#-==#=#;-;<4444;6ad7<661<<11>2;2<<<445<>:<<@,ryytytyytyyttyyttytyttytyttyttyttyuytt",
"%X$$$$X Og*o%$*X$$$$X*&&i* XXXX :3.#,=;>,-35:-#===:4s51446a6;6aaa6661<66aaa6<664445>:15<-,ewyytyytytyyytyyytytytytyyytyyttuttttu",
"X$$$$XXXOi$$$$$X$$$XXX.o&qXo X .>>.#-@>;-:<6:<;==2<66a6<<6a<<aaaa7<a76167661<665655326a1-1ityytyyytytyyyyyyyyytyyttuyyyyytutyyyt",
"$XX$$XX %%$$$$$X$%$$X ooX$XXX -8.@,=11>-:44:<82<<11<<<aa621addad167aa4<8<2<<7545511:66;eqrryytytyyyytyyttyytytyytttyttyytyttyy",
"X$$$$XXXXX$$$$$$$$X$X XXXXXXXX :3.+>=54;;<<<<1<;2<<<1aada6:7adadd74aaadaa6446764444;=>s,eitryyyyyyyyyyyyttyyyyyyyytyyttyttutytu",
"XX$$$XXXX$$$$XX$$$$$XXXXXX$X$X .::.@,;6<-;<<1<4::46aadddaa46aadddad66aaaaaa7a76564>4<-;51eryyttyyyttryytyyyyyryrtuyytyyyyyyttytt",
"X$$$$XX XX$$$$X$$$XXXXX XXXXXXX :8+ia>44;;<<<44<;87dadddda<aaadddaaa1saadaa676444>>5a221eiiwtyyyyyyyyyyyyyyyyyyytyytytyytttytuyy",
"$X$X$X XX$X$$X$$$$$XX XX$XX$XX #73pda6<>56>2<44<26addaa6>:6adaaadda2;16666a7a6<4>47a1<1ieyyyyytyyytyyyyyyryyyyyyyyytutyyuyyytyy",
"X$$$XX XX$$$XX$$$$XXXX XXXXXX :75ps565577>:<:46<<1aa11;;6aaadddaada<2>2;<6676<<435a1>5eiyytyyyyyyyyyyyyytyyyyyyyyyyyyyyytyyuty",
"XX$$$XX XX$$$$$$$$X$XXXXXXXXX X3773sdp565p742<:<61<2;2;2=<66aaaaadaaa1>6a66a665<5>456>15ewyyyyyyyytyyyyyyyyyyyyytyytyyyyytuyyttu",
"$$$XXX X$$$$$X$$$XXXXXXXXXX &pp73dd357577<<:>481611612;661a1saas6111;21aa664444>56<163iyyytyyyyyyyyyyyyyyyyryyutyyyyyyyyyyyyyy",
"$X$$XX XX$$X$$$$XXXXXXXXXX *ip773ss345775<<<848111aa1;=;-=><1511;;====116166165>4>6615rtyyyyyyyyyyyyyyyyyyyyyyyyyyyyytyyyyytuy",
"X$$X$X XX$$$XX$X$XXXXX X XX*ipp773dd33783::<<<4<<6664;=6<@===-==#;=;1;@21<<646655>>>>6iryyyryyyyyyyyyyyyyyyyyywyryyyyyyyyyyyutu",
"XXX$XX X$$$$$$X$XXXX XXXX&iipp783pp33735771<<46<644<=<66641,-@@:41112==2<4<44545a775piiyyyyyryyyyyyyyyyyyywyyyyyywyyyytyyyutyy",
"XX$*XX XX$$$$$$$$XXXXXXX oriipsp784dp333a7p761446<442=2661666611<6161<1<@=;2<46555aa5ppqryyyyrryryyyyyyyyyyyyyryywyywyywuytyyyyy",
"*oe*XX XXXXX$XXX$XXX Xoiihhisp774ppp837a7dp>4464<<2=<16666666666116661===;<1<445a77p3iryyyyyyyyyyyyyyyyyryyyyyyyyyyyyytyyyyyyy",
"iiiXXXX XXXXX$XX$X X o@iihishpp788dpp#:7p7a,3447842=;166aaa6aaaaaa16666<;=;<<4643ap33iirryyyryyryyyyyyyyyyryyyyyyyyyyyytyyttyyy",
"di&o o %*%XXXX$XXXX Oeiishippp773dd3:#:57>.-47664;@8<61a6a1aaaaaa676a11<=<<6548->53iirrryyyyyyyyyyyyyyyyyyyryyyrryyywyyyyyutuy",
"ppO.oX *q&XXXXXX$XXX..@,eshsipdp783pp3:##:,@.#:4474<:<<11<>1<1661<21<<<6<6;166643##,7irrryyyyyyyyyyyyyryyyyyywyyyryrywtyyyyyyyty",
"di*+X. *&OoXXXXXXX$ .o@&,sshsiip377pd3,##:3,@@4466718<<<2=====22=2===-=;2<6676158##333iryyyyytyyyyyyyyyyyyyyyyyyyyyyyyyyytytyyyy",
"gge. *OoXXXXXXXXXX..@@,3shsipp778dp3,-@@@@+@-46667<22==;22,22=2111<6<<<<8677453##33iriyyyyyyyyyyyyyyyyyyyyyyyryyyyytyyyyyyyyyy",
"esi* &*XXXXXXXXXX .@@=;ppssppp77377:-:@+.+++-446774<<<1116666aa16a61<66<6766558#,3ihrryyyyyyyyyyyyyyyyyyyrryyyyyyyyyytytyyyyyy",
"&.&eo oi& XXX$XXXXXX.@@@=ishhpsp873p3,:28@@.@+=4<4774868<1116aa6a611<88668664453##&3iryyyyyyyyhyyyyyyyyyyyyyyyyyyyryyyyyytyyyyyy",
"oXXo XX&eXXXXX XXXXX..=@@1ssipsp875p3::888:@.@@;44464<68<<2112;;;22<<6664<16443>##3iiryhuyyyyyyyyyyyyryyryyyyrryyyyyyryyyyyyyyyy",
"XXXXX X&OXXXXXXXX .@@===6ssis577377>::88882@@-:<<68<66611<2;;;2=;:<<<<644<<<5-#3&3iyhyyyyyyyyyyyyyyyyyyyyyyyyyryyyyyryyyyyyyyy",
"XXXX X %OoXXXXXXXX..@=@=@1ssis578377:::82822;@;:4<8244<8<1<46a56144<<<656<6<<1#&eerryryyyyyyuyyyyyyyyyyyyyyyyyyyyyyyyyytytytyty",
"XXXXX XXXXXXXXX..@=@====sspp578877:8228282;=;<<><46666666aaaaaa66777a5<4<1,i-&3riryyyyyyyyyuyyyyyyyyyyyyyyyyyyyyyyyryyyyyyyyy",
"X$XXX XXXXXX ..@@==@===1ppp5878p8222828282==<:<<6<<<84667aaaa6aa777744<;113,eiryyyyyyyyyyuyuyhyyyiyyyyyyyyyyyyyyyyyyyytyyyyy",
"XXXXX XXXXXX X .@===@=====1ap387838:8:282822=@>4:>44>44644666a66aaa744>>:::>3,erryyuuyyyyyyyyhyyyyyyuyyyyyyyyyyyyyyytyyyyyyuty",
"XXXX XXXXXXXo@@@===@@===11s>87888:8:828828@-p4;::<:<44<4446455656548>#---33Oirryyuyyyyyyuyyuyyyyyyyyhyyyyhyyyuyyyyuyyytuyyyy",
"XXXXX XXXXXX .@@==========;1s187882;8:28888=+sp1,-=;-:<<444<<><141<<:;=@@=>p,XOrryuuuyyyyyyyyyhyyyyyyyyyyyyyyyyyyyyyyyyyytyyyy",
"XXXX X XXXX .@@======@===@=1118788:;8:8288;.+d151-@@@@;:>2:---;,,-;--#@@@:333 ++qyyyyuyyyyyyyyyyuyyyuyyyyyyyyyuyyyyyyyyyyyutuy",
"XXXXX XXXXX..@@=======@==@==>5<8788:;8:8:8:..&d,35>-@@@@-@@@@@@@@@@@@==@@,55p, Xqyyyyyyyyyhyyyyyyyyyyyyyyyyyyyyyuyyyyyyuttyyy",
"$XXX o...@@=========@====262888:;-82::-.. ip#513:=@@@@@..@......@@---;1>53, Xqhyyhyyyyuyyyyyhyuyhhuuyhyyyyyyyyyyyyyyyyuyy",
"XXXXX oXoO@@@=======@=@=====s;878:=-:-;@.. hp@74>:---##@@......@@#-,--,<51p3 +Oryyuhuyyyyyyyyyyyyyyuyyyyyyyyyyuyyyyuyyyyy",
"XXXX X .o**==========@@@=@===1;878:;-:#.. gd@54>3:-:--@@@.....@@-;--;<>13d& + +O&ryyyyhyuyuyyyuyyyyyyyyyyyuyyyyyuyyyyyyyu",
"XXXXX .&h1@=========@@@=====2;778:@@. hd@343<;;;,--@@@@O@@@#;;-->1>5pg&X X%qyyyyyyyyyuyyyuyyyyyyyyyuyuyyyyyyyyyyy",
"XXXXX Xisi1;===@======@@=====>>88#+ gg#,5>:>:-;---@@@@@@=-;;-;<>53df& X X&ryyyyyyyyuhyuyhyhyyyyhyyyuyyyyyyyyy",
"XXXX &idi31=========@@@=@==@=. + dg+-51<:;;;-#==#@#---->;-:>>5dff& XXXX*rhyyhuyyyyyyuyyyuyyyyyuyyhyyyyyyy",
"X XX +iipi3s2==@======@@@=@@.. df@#55<>:>,---#=#-;=-;;;:>>35gggO X XXX$XOqrhyyyyhuyuyyyyyyyuuyuyyyyyyyuy",
"X X X+pssiiss2======@....... igs@>1<>::>;-----;;;;;>;>>3>dffg . XX XO %qyyyyyyyhhyyuuyyyuyyuyyyyhyyy",
"XXX .iissshss===@@... .. egf,-1<:<:>:----;>;;;>:;;>>5ggfs X XO+OX O*qiyyyyyyyyyyuyuyyuyyyyyyyy",
"XXXX .ppssssdg,...... &gfd@>4::>>>----;;;;;>>;>>,gfggq X+ X XXXOOO9rryyyyhyyyyyyyuyyyyyyy",
"XXX X .iphhshhsO X&gff,#><>:>;;---;>;2;>::--pgfgf* o ++ + X XXX++O++%&ryyyhyyuyyyyyyyyyy",
"XXXX +3pdphi3 *ggfg#-><;:>:-;=;-;;;>;-#3gfgfg X+XX +X XXXXXXXXX%qyhyyyyyyuyyyyyy",
"XXX iphi++ %ggffd-><><3---@----;>:@,gfffgi. +XX X+X X+ XXXoXXXXXX%qyyrhryyuyyyyy",
"XXXX. pdi+ . Ogffffg;>1<>:-;@---=2,-,gffffgO XXXX X +X++X+X+ X+X XXX%%riryyyuyyyyy",
" X X .ih& hgffffd-56>::-=--#->,-dfffffg X XXX X + X+o X+XOXXXX%ryyyyyyyyy",
" o X ii + *ggffffp:553>;-,--,>#gffffffr XX X XX+X+ Xo o+ OXo XXX%yyyyyyyyy",
"O o i& +ggfffff3,555>,3>,1,gfffffffO o+XXXX XXX X o XoXX +XXX$ryuyyuyyy",
",. 3+. qgffffff3>531>1>13gfffffffh . X X++XX +X+XX X X X + + X$*yyyuyyyy",
"iO & o&gfffffff35pddddsiffffffff& X X XXXX X X o X X X X XXrhyyytyy",
"si . O ogfffffffffgfdhpO Ogfffffg ++ + X X+XX+ +X X+ X X XX X Xrryuyyry",
"si# ... Xifffffffe**O+o ++ OgffffqX +X OX +X X X+X XX X+ X X X&yyyyyyy",
"ssi@ &ffffffq+ + + o OgffgOX + X++ X++ OX+ XX+XXXXXXXX XX X X X$yyyyyyy",
"sip&.... &gffff& +o + %gfr + + X X X X+XX+X X +X+ +X X X X$yyyyyyy",
"pisi+. . +hfff&X + + &gO X X+ X+XO+X + X X X XX o XX X X Xqryryuy",
"ppp3#. +eff& o + *+ + + + + XX X OXXX X +X X+ X XX X Xhyyyyu",
"ppi3,. X*fq X + + O + ++ X++XX+X X X+XXXX+ Xo X X X XXrrhyyy",
"p5pi,.. O& + +XX X + X X X O+X X X X X X X XX X*yryyu",
"p5pi3o o + X o+OO X X +X+ X+ + +X X o o X X+ XX X X XXryyuy",
"spp3,. X riggggghy+ + + + X+ X Xo XXo+X X X X+ X X X X Xqyyyu",
"ppi3, . X O+ &gfffffffg&+ + +X X X +X o +X X+ X qiyyu",
"6pp3,. X +hgfffffffgqX o +XX + X X ooX X X X X X X X %yyyu",
"pp5i, X+ +X &gffffffffgO X X + ++ + X oX X X XX Xyhyy",
"ppi3# . +X Ohgfffffffgi+ + ++ X X X +X +X o X XX XX X Xryyu",
"p5ii# Oigffffffffg* X X+X ++XX+ + +XXXoX XX X X X Xryyu",
"5pp1*. X +XX Oiggfffffffhh OO X+ + + X+XX +.X X o+ X XX X X&uyy",
"5pp3@ X +XX ihffffffffgh#oX X+O + X X o o X X XX X X*uuu",
"p3pi. + X+eggffffffgiqO +X++X oo o + . ooo + X X X%uuu",
"p53p. &ii&&&&#O+ + + O*hgfffffgi&O X o O ++ oo .o.o$OO% . X X X $hyu",
"p5i3. 3hfffi3rihhhii3r&& X oihffffgi&&XXoX+X X O+ X ooooo+*@*=#=*=*&, X . XX X$hyu",
"p5i3 .idffg&iiiiphhddhip& Xo &hgffgi&&* o X oX X X X oXX o.Oe,22,,2112s%oXX . X Xryy",
"5ii,. Oihgggiiiihhphhhiihi* X X XXOhdggh&ii XX O XXX+X+X+X+ oo .ooe,=222e22eso.o$... . XXryy",
"p3p# Opgghfiiihgggdgghhhhi* oX ehgheid*XXoXXX X+ XX+X+ X ..&ieeq*1ee,eeee&,e=2=#--&* X Xquh",
"pppO *rpggiqqiiiiihiphpphiio OX o.&gsieii. X+ XX+X + .@&ifseeeheeeeehq,de122=17i*. . X*yu",
"p3p. . O&&q&&iiiiiihihihiiiii X X + hp&&3O X + X+ + + +.@=,sgh1e,heeesqhe,s11222221&% + X+ X%uu",
"ppi. . +oO*&&e&i,iiiiiX XX X &i**& + X + XX X +@-,2212eeieqee,,ee*,22222222@o X X X%hu",
"ii,. .XoO +&OO+ XX ++ +..+@>6a5=2,eih*ihf*&hie=22222==1o + X XXOyy",
"5p,. X +** X +X ..*-375522=eigqeig&,ie,222222=2<oo X X X Xyh",
"5i# + O+ + XX oo.@O##O@@@@O**oo+*+OOO..@@....@. X XXX XXXuy",
"ip# X o .oo....+..ooo.XooX Xoo..oo..+.. X + Xrh",
"p3@ + X oXoX .oo .oXo .oo .oo o .oo X + + X X X X XXqy",
"pi@ X XX +o+ X X XXX XX.. XOoX X oo X X + X X XX&y"
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
Tests/images/hopper_g4.tif Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Some files were not shown because too many files have changed in this diff Show More