mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Updated merge from master
This commit is contained in:
commit
460160a1e3
|
@ -60,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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -51,9 +51,11 @@ BIT2MODE = {
|
|||
32: ("RGB", "BGRX")
|
||||
}
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:2] == b"BM"
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for the Windows BMP format.
|
||||
|
||||
|
@ -63,7 +65,6 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
format_description = "Windows Bitmap"
|
||||
|
||||
def _bitmap(self, header=0, offset=0):
|
||||
|
||||
if header:
|
||||
self.fp.seek(header)
|
||||
|
||||
|
@ -98,7 +99,8 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
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))
|
||||
|
@ -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:
|
||||
|
@ -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))])
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
||||
##
|
||||
|
|
|
@ -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,6 +52,7 @@ if sys.platform.startswith('win'):
|
|||
else:
|
||||
gs_windows_binary = False
|
||||
|
||||
|
||||
def has_ghostscript():
|
||||
if gs_windows_binary:
|
||||
return True
|
||||
|
@ -75,14 +77,17 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
|
||||
# Hack to support hi-res rendering
|
||||
scale = int(scale) or 1
|
||||
orig_size = size
|
||||
orig_bbox = bbox
|
||||
# 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])) )
|
||||
# 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 tempfile, os, subprocess
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
out_fd, outfile = tempfile.mkstemp()
|
||||
os.close(out_fd)
|
||||
|
@ -118,7 +123,8 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
"-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
|
||||
"-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]),
|
||||
|
@ -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
|
||||
|
@ -173,6 +185,7 @@ class PSFile:
|
|||
|
||||
return s.decode('latin-1')
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5
|
||||
|
||||
|
@ -180,6 +193,7 @@ 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"""
|
||||
|
||||
|
@ -200,7 +214,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
else:
|
||||
# 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)
|
||||
|
||||
|
@ -264,7 +278,6 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
if s[0] != "%":
|
||||
break
|
||||
|
||||
|
||||
#
|
||||
# Scan for an "ImageData" descriptor
|
||||
|
||||
|
@ -307,7 +320,8 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
# 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:
|
||||
|
@ -329,6 +343,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
# 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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -38,12 +38,14 @@ MODES = {
|
|||
(0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"),
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:8] == MAGIC
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for the FlashPix images.
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -64,6 +65,7 @@ class GdImageFile(ImageFile.ImageFile):
|
|||
|
||||
self.tile = [("raw", (0, 0)+self.size, 775, ("L", 0, -1))]
|
||||
|
||||
|
||||
##
|
||||
# Load texture from a GD image 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.
|
||||
|
@ -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:
|
||||
|
@ -343,7 +345,8 @@ 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
|
||||
|
||||
|
@ -351,7 +354,8 @@ def _save(im, fp, filename):
|
|||
|
||||
try:
|
||||
fp.flush()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def _save_netpbm(im, fp, filename):
|
||||
|
@ -380,7 +384,8 @@ 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()
|
||||
|
@ -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'
|
||||
newPaletteBytes = (paletteBytes +
|
||||
(768 - len(paletteBytes)) * b'\x00')
|
||||
im.putpalette(newPaletteBytes)
|
||||
im.palette = ImagePalette.ImagePalette("RGB", palette = paletteBytes, size = len(paletteBytes))
|
||||
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
|
||||
|
@ -491,6 +499,7 @@ def getdata(im, offset = (0, 0), **params):
|
|||
|
||||
class collector:
|
||||
data = []
|
||||
|
||||
def write(self, data):
|
||||
self.data.append(data)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
@ -106,7 +112,8 @@ def read_png_or_jpeg2000(fobj, start_length, size):
|
|||
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 = {
|
||||
|
@ -233,6 +241,7 @@ class IcnsFile:
|
|||
pass
|
||||
return im
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for Mac OS icons.
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
|
||||
|
@ -157,8 +159,8 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
|
||||
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")
|
||||
|
@ -254,7 +257,8 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
("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):
|
||||
|
||||
|
@ -305,6 +309,7 @@ SAVE = {
|
|||
"YCbCr": ("YCC", "YCbCr;L")
|
||||
}
|
||||
|
||||
|
||||
def _save(im, fp, filename, check=0):
|
||||
|
||||
try:
|
||||
|
|
|
@ -34,6 +34,7 @@ import warnings
|
|||
class DecompressionBombWarning(RuntimeWarning):
|
||||
pass
|
||||
|
||||
|
||||
class _imaging_not_installed:
|
||||
# module placeholder
|
||||
def __getattr__(self, id):
|
||||
|
@ -851,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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -184,6 +184,7 @@ class ImageCmsProfile:
|
|||
|
||||
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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -213,13 +218,15 @@ class ImageFile(Image.Image):
|
|||
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)
|
||||
|
@ -436,6 +443,7 @@ class Parser:
|
|||
fp.close() # explicitly close the virtual file
|
||||
return self.image
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
def _save(im, fp, tile, bufsize=0):
|
||||
|
@ -452,7 +460,7 @@ 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
|
||||
|
@ -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):
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -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])"""
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
##
|
||||
|
||||
|
||||
class Iterator:
|
||||
"""
|
||||
This class implements an iterator object that can be used to loop
|
||||
|
|
|
@ -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,6 +25,7 @@ else:
|
|||
|
||||
_viewers = []
|
||||
|
||||
|
||||
def register(viewer, order=1):
|
||||
try:
|
||||
if issubclass(viewer, Viewer):
|
||||
|
@ -35,6 +37,7 @@ def register(viewer, order=1):
|
|||
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
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
import operator, math
|
||||
import math
|
||||
import operator
|
||||
from functools import reduce
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ from PIL import Image
|
|||
|
||||
_pilbitmap_ok = None
|
||||
|
||||
|
||||
def _pilbitmap_check():
|
||||
global _pilbitmap_ok
|
||||
if _pilbitmap_ok is None:
|
||||
|
@ -51,6 +52,7 @@ def _pilbitmap_check():
|
|||
_pilbitmap_ok = 0
|
||||
return _pilbitmap_ok
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# PhotoImage
|
||||
|
||||
|
@ -120,7 +122,6 @@ class PhotoImage:
|
|||
except:
|
||||
pass # ignore internal errors
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Get the Tkinter photo image identifier. This method is automatically
|
||||
|
@ -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
|
||||
|
@ -176,7 +174,7 @@ class PhotoImage:
|
|||
|
||||
try:
|
||||
tk.call("PyImagingPhoto", self.__photo, block.id)
|
||||
except tkinter.TclError as v:
|
||||
except tkinter.TclError:
|
||||
# activate Tkinter hook
|
||||
try:
|
||||
from PIL import _imagingtk
|
||||
|
@ -240,7 +238,6 @@ class BitmapImage:
|
|||
except:
|
||||
pass # ignore internal errors
|
||||
|
||||
|
||||
def width(self):
|
||||
"""
|
||||
Get the width of the image.
|
||||
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,12 +474,16 @@ 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)),
|
||||
'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
|
||||
|
@ -530,8 +536,7 @@ 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,
|
||||
}
|
||||
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ from PIL.OleFileIO import *
|
|||
def _accept(prefix):
|
||||
return prefix[:8] == MAGIC
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for Microsoft's Image Composer file format.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -42,7 +45,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
JpegImagePlugin.JpegImageFile._open(self)
|
||||
self.mpinfo = self._getmp()
|
||||
self.__framecount = self.mpinfo[0xB001]
|
||||
self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset'] \
|
||||
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
|
||||
|
|
|
@ -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).
|
||||
|
@ -66,6 +68,7 @@ class MspImageFile(ImageFile.ImageFile):
|
|||
|
||||
o16 = _binary.o16le
|
||||
|
||||
|
||||
def _save(im, fp, filename):
|
||||
|
||||
if im.mode != "1":
|
||||
|
|
|
@ -19,6 +19,7 @@ from __future__ import print_function
|
|||
|
||||
from PIL import EpsImagePlugin
|
||||
|
||||
|
||||
##
|
||||
# Simple Postscript graphics interface.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -58,7 +60,6 @@ class PcxImageFile(ImageFile.ImageFile):
|
|||
if Image.DEBUG:
|
||||
print ("BBox: %s %s %s %s" % bbox)
|
||||
|
||||
|
||||
# format
|
||||
version = i8(s[1])
|
||||
bits = i8(s[3])
|
||||
|
@ -122,6 +123,7 @@ SAVE = {
|
|||
|
||||
o16 = _binary.o16le
|
||||
|
||||
|
||||
def _save(im, fp, filename, check=0):
|
||||
|
||||
try:
|
||||
|
@ -140,7 +142,6 @@ def _save(im, fp, filename, check=0):
|
|||
# Ideally it should be passed in in the state, but the bytes value
|
||||
# gets overwritten.
|
||||
|
||||
|
||||
if Image.DEBUG:
|
||||
print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % (
|
||||
im.size[0], bits, stride))
|
||||
|
|
|
@ -29,6 +29,7 @@ from PIL import Image, ImageFile, _binary
|
|||
i16 = _binary.i16le
|
||||
i32 = _binary.i32le
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for PIXAR raster images.
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ _MODES = {
|
|||
|
||||
_simple_palette = re.compile(b'^\xff+\x00\xff*$')
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Support classes. Suitable for PNG and related formats like MNG etc.
|
||||
|
||||
|
@ -123,7 +124,7 @@ 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"\
|
||||
raise SyntaxError("broken PNG file"
|
||||
"(bad header checksum in %s)" % cid)
|
||||
|
||||
def crc_skip(self, cid, data):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -238,7 +244,8 @@ 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:
|
||||
|
@ -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:])
|
||||
|
@ -399,12 +410,14 @@ class PngStream(ChunkStream):
|
|||
|
||||
return s
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# PNG reader
|
||||
|
||||
def _accept(prefix):
|
||||
return prefix[:8] == _MAGIC
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for PNG images.
|
||||
|
||||
|
@ -460,7 +473,6 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
self.__idat = length # used by load_read()
|
||||
|
||||
|
||||
def verify(self):
|
||||
"Verify PNG file"
|
||||
|
||||
|
@ -509,7 +521,6 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
return self.fp.read(read_bytes)
|
||||
|
||||
|
||||
def load_end(self):
|
||||
"internal: finished reading image data"
|
||||
|
||||
|
@ -541,6 +552,7 @@ _OUTMODES = {
|
|||
"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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ try:
|
|||
if locale_enc is None:
|
||||
locale_lang, locale_enc = locale.getdefaultlocale()
|
||||
b_whitespace = b_whitespace.decode(locale_enc)
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
b_whitespace = b_whitespace.encode('ascii', 'ignore')
|
||||
|
||||
MODES = {
|
||||
|
@ -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.
|
||||
|
||||
|
@ -109,7 +112,7 @@ class PpmImageFile(ImageFile.ImageFile):
|
|||
self.mode = 'I'
|
||||
rawmode = 'I;16B'
|
||||
else:
|
||||
self.mode = 'I';
|
||||
self.mode = 'I'
|
||||
rawmode = 'I;32B'
|
||||
|
||||
self.size = xsize, ysize
|
||||
|
@ -123,6 +126,7 @@ class PpmImageFile(ImageFile.ImageFile):
|
|||
# self.mode = self.im.mode
|
||||
# self.size = self.im.size
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -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 = []
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -53,7 +54,8 @@ class PyAccess(object):
|
|||
print (vals)
|
||||
self._post_init()
|
||||
|
||||
def _post_init(): pass
|
||||
def _post_init():
|
||||
pass
|
||||
|
||||
def __setitem__(self, xy, color):
|
||||
"""
|
||||
|
@ -64,7 +66,8 @@ class PyAccess(object):
|
|||
:param xy: The pixel coordinate, given as (x, y).
|
||||
:param value: The pixel value.
|
||||
"""
|
||||
if self.readonly: raise ValueError('Attempt to putpixel a read only image')
|
||||
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)
|
||||
|
||||
|
@ -89,6 +92,7 @@ class PyAccess(object):
|
|||
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):
|
||||
|
@ -104,6 +108,7 @@ class _PyAccess32_2(PyAccess):
|
|||
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 """
|
||||
|
||||
|
@ -121,6 +126,7 @@ class _PyAccess32_3(PyAccess):
|
|||
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):
|
||||
|
@ -155,6 +161,7 @@ class _PyAccess8(PyAccess):
|
|||
# tuple
|
||||
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):
|
||||
|
@ -171,6 +178,7 @@ 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):
|
||||
|
@ -190,6 +198,7 @@ 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):
|
||||
|
@ -209,6 +218,7 @@ 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):
|
||||
|
@ -220,6 +230,7 @@ class _PyAccessI32_N(PyAccess):
|
|||
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,7 +239,8 @@ 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):
|
||||
|
@ -237,6 +249,7 @@ class _PyAccessI32_Swap(PyAccess):
|
|||
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):
|
||||
|
@ -286,13 +299,15 @@ 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
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
from PIL import ContainerIO
|
||||
|
||||
|
||||
##
|
||||
# A file object that provides read access to a given member of a TAR
|
||||
# file.
|
||||
|
|
|
@ -33,6 +33,7 @@ except ImportError:
|
|||
|
||||
i32 = _binary.i32le
|
||||
|
||||
|
||||
##
|
||||
# Load texture from a Quake2 WAL texture file.
|
||||
# <p>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,6 +24,7 @@ _handler = None
|
|||
if str != bytes:
|
||||
long = int
|
||||
|
||||
|
||||
##
|
||||
# Install application-specific WMF image handler.
|
||||
#
|
||||
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ else:
|
|||
def o8(i):
|
||||
return bytes((i & 255,))
|
||||
|
||||
|
||||
# Input, le = little endian, be = big endian
|
||||
# TODO: replace with more readable struct.unpack equivalent
|
||||
def i16le(c, o=0):
|
||||
|
@ -35,6 +36,7 @@ def i16le(c, o=0):
|
|||
"""
|
||||
return i8(c[o]) | (i8(c[o+1]) << 8)
|
||||
|
||||
|
||||
def i32le(c, o=0):
|
||||
"""
|
||||
Converts a 4-bytes (32 bits) string to an integer.
|
||||
|
@ -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])
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def o32le(i):
|
||||
return o8(i) + o8(i >> 8) + o8(i >> 16) + o8(i >> 24)
|
||||
|
||||
|
||||
def o16be(i):
|
||||
return o8(i >> 8) + o8(i)
|
||||
|
||||
|
||||
def o32be(i):
|
||||
return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i)
|
||||
|
||||
# End of file
|
||||
|
|
55
RELEASING.md
Normal file
55
RELEASING.md
Normal 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.
|
|
@ -6,5 +6,3 @@ import sys
|
|||
|
||||
if sys.maxsize < 2**32:
|
||||
im = Image.new('L', (999999, 999999), 0)
|
||||
|
||||
|
||||
|
|
|
@ -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')))
|
||||
|
|
|
@ -5,7 +5,9 @@ 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
212
Tests/check_jpeg_leaks.py
Normal 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()
|
|
@ -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")
|
||||
|
||||
|
|
Binary file not shown.
25
Tests/icc/LICENSE.txt
Normal file
25
Tests/icc/LICENSE.txt
Normal 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/icc/sRGB_v4_ICC_preference.icc
Normal file
BIN
Tests/icc/sRGB_v4_ICC_preference.icc
Normal file
Binary file not shown.
|
@ -1,7 +1,8 @@
|
|||
import sys
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
import glob, os
|
||||
import glob
|
||||
import os
|
||||
import traceback
|
||||
|
||||
for file in glob.glob("PIL/*.py"):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
|
||||
from helper import *
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
# This test is not run automatically.
|
||||
#
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
|
||||
from helper import *
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
# This test is not run automatically.
|
||||
#
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
|
|
|
@ -156,7 +156,8 @@ class TestFileGif(PillowTestCase):
|
|||
img = Image.open("Tests/images/iss634.gif")
|
||||
# seek to the second frame
|
||||
img.seek(img.tell() + 1)
|
||||
# all transparent pixels should be replaced with the color from the first frame
|
||||
# all transparent pixels should be replaced with the color from the
|
||||
# first frame
|
||||
self.assertEqual(img.histogram()[img.info['transparency']], 0)
|
||||
|
||||
|
||||
|
|
|
@ -277,17 +277,17 @@ class TestFileJpeg(PillowTestCase):
|
|||
99 99 99 99 99 99 99 99
|
||||
""".split(None)]
|
||||
# list of qtable lists
|
||||
self.assert_image_similar(im,
|
||||
self.roundtrip(im,
|
||||
qtables=[standard_l_qtable,
|
||||
standard_chrominance_qtable]),
|
||||
self.assert_image_similar(
|
||||
im, self.roundtrip(
|
||||
im, qtables=[standard_l_qtable, standard_chrominance_qtable]),
|
||||
30)
|
||||
|
||||
# tuple of qtable lists
|
||||
self.assert_image_similar(im,
|
||||
self.roundtrip(im,
|
||||
qtables=(standard_l_qtable,
|
||||
standard_chrominance_qtable)),
|
||||
self.assert_image_similar(
|
||||
im, self.roundtrip(
|
||||
im, qtables=(standard_l_qtable, standard_chrominance_qtable)),
|
||||
30)
|
||||
|
||||
# dict of qtable lists
|
||||
self.assert_image_similar(im,
|
||||
self.roundtrip(im,
|
||||
|
@ -295,6 +295,21 @@ class TestFileJpeg(PillowTestCase):
|
|||
1: standard_chrominance_qtable}),
|
||||
30)
|
||||
|
||||
# not a sequence
|
||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))
|
||||
# sequence wrong length
|
||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))
|
||||
# sequence wrong length
|
||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1,2,3,4,5]))
|
||||
|
||||
# qtable entry not a sequence
|
||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))
|
||||
# qtable entry has wrong number of items
|
||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[[1,2,3,4]]))
|
||||
|
||||
|
||||
|
||||
|
||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||
def test_load_djpeg(self):
|
||||
img = Image.open(TEST_FILE)
|
||||
|
|
|
@ -82,7 +82,8 @@ class TestFileMpo(PillowTestCase):
|
|||
# prior to first image raises an error, both blatant and borderline
|
||||
self.assertRaises(EOFError, im.seek, -1)
|
||||
self.assertRaises(EOFError, im.seek, -523)
|
||||
# after the final image raises an error, both blatant and borderline
|
||||
# after the final image raises an error,
|
||||
# both blatant and borderline
|
||||
self.assertRaises(EOFError, im.seek, 2)
|
||||
self.assertRaises(EOFError, im.seek, 523)
|
||||
# bad calls shouldn't change the frame
|
||||
|
|
|
@ -29,7 +29,6 @@ class TestFilePalm(PillowTestCase):
|
|||
converted = self.open_withImagemagick(outfile)
|
||||
self.assert_image_equal(converted, im)
|
||||
|
||||
|
||||
def test_monochrome(self):
|
||||
# Arrange
|
||||
mode = "1"
|
||||
|
|
|
@ -151,13 +151,16 @@ class TestFilePng(PillowTestCase):
|
|||
self.assertEqual(im.info["spam"].lang, "en")
|
||||
self.assertEqual(im.info["spam"].tkey, "Spam")
|
||||
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + zlib.compress(b"egg")[:1]) + TAIL)
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' +
|
||||
zlib.compress(b"egg")[:1]) + TAIL)
|
||||
self.assertEqual(im.info, {})
|
||||
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\1en\0Spam\0' + zlib.compress(b"egg")) + TAIL)
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\1en\0Spam\0' +
|
||||
zlib.compress(b"egg")) + TAIL)
|
||||
self.assertEqual(im.info, {})
|
||||
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + zlib.compress(b"egg")) + TAIL)
|
||||
im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' +
|
||||
zlib.compress(b"egg")) + TAIL)
|
||||
self.assertEqual(im.info, {"spam": "egg"})
|
||||
self.assertEqual(im.info["spam"].lang, "en")
|
||||
self.assertEqual(im.info["spam"].tkey, "Spam")
|
||||
|
@ -271,7 +274,8 @@ class TestFilePng(PillowTestCase):
|
|||
im = Image.new("RGB", (32, 32))
|
||||
info = PngImagePlugin.PngInfo()
|
||||
info.add_itxt("spam", "Eggs", "en", "Spam")
|
||||
info.add_text("eggs", PngImagePlugin.iTXt("Spam", "en", "Eggs"), zip=True)
|
||||
info.add_text("eggs", PngImagePlugin.iTXt("Spam", "en", "Eggs"),
|
||||
zip=True)
|
||||
|
||||
im = roundtrip(im, pnginfo=info)
|
||||
self.assertEqual(im.info, {"spam": "Eggs", "eggs": "Spam"})
|
||||
|
|
|
@ -18,7 +18,8 @@ class TestFileWebpAlpha(PillowTestCase):
|
|||
self.skipTest('WebP support not installed')
|
||||
|
||||
if _webp.WebPDecoderBuggyAlpha(self):
|
||||
self.skipTest("Buggy early version of WebP installed, not testing transparency")
|
||||
self.skipTest("Buggy early version of WebP installed, "
|
||||
"not testing transparency")
|
||||
|
||||
def test_read_rgba(self):
|
||||
# Generated with `cwebp transparent.png -o transparent.webp`
|
||||
|
|
|
@ -2,22 +2,28 @@ from helper import unittest, PillowTestCase, hopper
|
|||
|
||||
from PIL import Image
|
||||
|
||||
import colorsys, itertools
|
||||
import colorsys
|
||||
import itertools
|
||||
|
||||
|
||||
class TestFormatHSV(PillowTestCase):
|
||||
|
||||
def int_to_float(self, i):
|
||||
return float(i)/255.0
|
||||
|
||||
def str_to_float(self, i):
|
||||
|
||||
return float(ord(i))/255.0
|
||||
|
||||
def to_int(self, f):
|
||||
return int(f*255.0)
|
||||
|
||||
def tuple_to_ints(self, tp):
|
||||
x, y, z = tp
|
||||
return (int(x*255.0), int(y*255.0), int(z*255.0))
|
||||
|
||||
def test_sanity(self):
|
||||
im = Image.new('HSV', (100,100))
|
||||
Image.new('HSV', (100, 100))
|
||||
|
||||
def wedge(self):
|
||||
w = Image._wedge()
|
||||
|
@ -61,14 +67,17 @@ class TestFormatHSV(PillowTestCase):
|
|||
else:
|
||||
iter_helper = itertools.zip_longest
|
||||
|
||||
|
||||
converted = [self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b)))
|
||||
for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), b.tobytes())]
|
||||
converted = [self.tuple_to_ints(func(conv_func(_r), conv_func(_g),
|
||||
conv_func(_b)))
|
||||
for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(),
|
||||
b.tobytes())]
|
||||
|
||||
if str is bytes:
|
||||
new_bytes = b''.join(chr(h)+chr(s)+chr(v) for (h,s,v) in converted)
|
||||
new_bytes = b''.join(chr(h)+chr(s)+chr(v) for (
|
||||
h, s, v) in converted)
|
||||
else:
|
||||
new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for (h,s,v) in converted)
|
||||
new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for (
|
||||
h, s, v) in converted)
|
||||
|
||||
hsv = Image.frombytes(mode, r.size, new_bytes)
|
||||
|
||||
|
@ -118,7 +127,6 @@ class TestFormatHSV(PillowTestCase):
|
|||
self.assert_image_similar(im.split()[2], comparable.split()[2],
|
||||
3, "B conversion is wrong")
|
||||
|
||||
|
||||
def test_convert(self):
|
||||
im = hopper('RGB').convert('HSV')
|
||||
comparable = self.to_hsv_colorsys(hopper('RGB'))
|
||||
|
@ -136,7 +144,6 @@ class TestFormatHSV(PillowTestCase):
|
|||
self.assert_image_similar(im.split()[2], comparable.split()[2],
|
||||
1, "Value conversion is wrong")
|
||||
|
||||
|
||||
def test_hsv_to_rgb(self):
|
||||
comparable = self.to_hsv_colorsys(hopper('RGB'))
|
||||
converted = comparable.convert('RGB')
|
||||
|
@ -148,7 +155,6 @@ class TestFormatHSV(PillowTestCase):
|
|||
# print ([ord(x) for x in target.split()[1].tobytes()[:80]])
|
||||
# print ([ord(x) for x in converted.split()[1].tobytes()[:80]])
|
||||
|
||||
|
||||
self.assert_image_similar(converted.split()[0], comparable.split()[0],
|
||||
3, "R conversion is wrong")
|
||||
self.assert_image_similar(converted.split()[1], comparable.split()[1],
|
||||
|
@ -157,12 +163,6 @@ class TestFormatHSV(PillowTestCase):
|
|||
3, "B conversion is wrong")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class TestImageCopy(PillowTestCase):
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
class TestImagePoint(PillowTestCase):
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ class TestImagePutData(PillowTestCase):
|
|||
else:
|
||||
self.assertEqual(put(sys.maxsize), (255, 255, 255, 127))
|
||||
|
||||
|
||||
def test_pypy_performance(self):
|
||||
im = Image.new('L', (256, 256))
|
||||
im.putdata(list(range(256))*256)
|
||||
|
@ -65,7 +64,6 @@ class TestImagePutData(PillowTestCase):
|
|||
self.assertEqual(list(im.getdata()), target)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from helper import unittest, PillowTestCase, hopper
|
|||
from PIL import Image
|
||||
|
||||
from io import BytesIO
|
||||
import os
|
||||
|
||||
try:
|
||||
from PIL import ImageCms
|
||||
|
@ -13,8 +14,8 @@ except ImportError as v:
|
|||
pass
|
||||
|
||||
|
||||
SRGB = "Tests/icc/sRGB.icm"
|
||||
|
||||
SRGB = "Tests/icc/sRGB_IEC61966-2-1_black_scaled.icc"
|
||||
HAVE_PROFILE = os.path.exists(SRGB)
|
||||
|
||||
class TestImageCms(PillowTestCase):
|
||||
|
||||
|
@ -26,6 +27,10 @@ class TestImageCms(PillowTestCase):
|
|||
except ImportError as v:
|
||||
self.skipTest(v)
|
||||
|
||||
def skip_missing(self):
|
||||
if not HAVE_PROFILE:
|
||||
self.skipTest("SRGB profile not available")
|
||||
|
||||
def test_sanity(self):
|
||||
|
||||
# basic smoke test.
|
||||
|
@ -38,6 +43,7 @@ class TestImageCms(PillowTestCase):
|
|||
# internal version number
|
||||
self.assertRegexpMatches(ImageCms.core.littlecms_version, "\d+\.\d+$")
|
||||
|
||||
self.skip_missing()
|
||||
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
||||
self.assert_image(i, "RGB", (128, 128))
|
||||
|
||||
|
@ -70,38 +76,45 @@ class TestImageCms(PillowTestCase):
|
|||
hopper().point(t)
|
||||
|
||||
def test_name(self):
|
||||
self.skip_missing()
|
||||
# get profile information for file
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileName(SRGB).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
|
||||
def test_info(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileInfo(SRGB).splitlines(), [
|
||||
'sRGB IEC61966-2.1', '',
|
||||
'Copyright (c) 1998 Hewlett-Packard Company', ''])
|
||||
'sRGB IEC61966-2-1 black scaled', '',
|
||||
'Copyright International Color Consortium, 2009', ''])
|
||||
|
||||
def test_copyright(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileCopyright(SRGB).strip(),
|
||||
'Copyright (c) 1998 Hewlett-Packard Company')
|
||||
'Copyright International Color Consortium, 2009')
|
||||
|
||||
def test_manufacturer(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileManufacturer(SRGB).strip(),
|
||||
'IEC http://www.iec.ch')
|
||||
'')
|
||||
|
||||
def test_model(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileModel(SRGB).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
|
||||
def test_description(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(
|
||||
ImageCms.getProfileDescription(SRGB).strip(),
|
||||
'sRGB IEC61966-2.1')
|
||||
'sRGB IEC61966-2-1 black scaled')
|
||||
|
||||
def test_intent(self):
|
||||
self.skip_missing()
|
||||
self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0)
|
||||
self.assertEqual(ImageCms.isIntentSupported(
|
||||
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
|
@ -139,6 +152,7 @@ class TestImageCms(PillowTestCase):
|
|||
self.assertRaises(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.getProfileName(None))
|
||||
self.skip_missing()
|
||||
self.assertRaises(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
||||
|
@ -154,8 +168,9 @@ class TestImageCms(PillowTestCase):
|
|||
def test_simple_lab(self):
|
||||
i = Image.new('RGB', (10, 10), (128, 128, 128))
|
||||
|
||||
p_lab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, p_lab, "RGB", "LAB")
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB")
|
||||
|
||||
i_lab = ImageCms.applyTransform(i, t)
|
||||
|
||||
|
@ -174,8 +189,10 @@ class TestImageCms(PillowTestCase):
|
|||
self.assertEqual(list(b), [128] * 100)
|
||||
|
||||
def test_lab_color(self):
|
||||
p_lab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, p_lab, "RGB", "LAB")
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB")
|
||||
|
||||
# Need to add a type mapping for some PIL type to TYPE_Lab_8 in
|
||||
# findLCMSType, and have that mapping work back to a PIL mode
|
||||
# (likely RGB).
|
||||
|
@ -189,8 +206,9 @@ class TestImageCms(PillowTestCase):
|
|||
self.assert_image_similar(i, target, 30)
|
||||
|
||||
def test_lab_srgb(self):
|
||||
p_lab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(p_lab, SRGB, "LAB", "RGB")
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB")
|
||||
|
||||
img = Image.open('Tests/images/hopper.Lab.tif')
|
||||
|
||||
|
@ -206,15 +224,16 @@ class TestImageCms(PillowTestCase):
|
|||
|
||||
def test_lab_roundtrip(self):
|
||||
# check to see if we're at least internally consistent.
|
||||
p_lab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, p_lab, "RGB", "LAB")
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB")
|
||||
|
||||
t2 = ImageCms.buildTransform(p_lab, SRGB, "LAB", "RGB")
|
||||
t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB")
|
||||
|
||||
i = ImageCms.applyTransform(hopper(), t)
|
||||
|
||||
self.assertEqual(i.info['icc_profile'],
|
||||
ImageCmsProfile(p_lab).tobytes())
|
||||
ImageCmsProfile(pLab).tobytes())
|
||||
|
||||
out = ImageCms.applyTransform(i, t2)
|
||||
|
||||
|
|
|
@ -255,7 +255,6 @@ class TestImageDraw(PillowTestCase):
|
|||
self.assert_image_equal(
|
||||
im, Image.open("Tests/images/imagedraw_floodfill2.png"))
|
||||
|
||||
|
||||
def create_base_image_draw(self, size,
|
||||
mode=DEFAULT_MODE,
|
||||
background1=WHITE,
|
||||
|
@ -267,23 +266,25 @@ class TestImageDraw(PillowTestCase):
|
|||
img.putpixel((x, y), background2)
|
||||
return (img, ImageDraw.Draw(img))
|
||||
|
||||
|
||||
def test_square(self):
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'square.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((10, 10))
|
||||
draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK)
|
||||
self.assert_image_equal(img, expected, 'square as normal polygon failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'square as normal polygon failed')
|
||||
img, draw = self.create_base_image_draw((10, 10))
|
||||
draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK)
|
||||
self.assert_image_equal(img, expected, 'square as inverted polygon failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'square as inverted polygon failed')
|
||||
img, draw = self.create_base_image_draw((10, 10))
|
||||
draw.rectangle((2, 2, 7, 7), BLACK)
|
||||
self.assert_image_equal(img, expected, 'square as normal rectangle failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'square as normal rectangle failed')
|
||||
img, draw = self.create_base_image_draw((10, 10))
|
||||
draw.rectangle((7, 7, 2, 2), BLACK)
|
||||
self.assert_image_equal(img, expected, 'square as inverted rectangle failed')
|
||||
|
||||
self.assert_image_equal(
|
||||
img, expected, 'square as inverted rectangle failed')
|
||||
|
||||
def test_triangle_right(self):
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'triangle_right.png'))
|
||||
|
@ -292,93 +293,117 @@ class TestImageDraw(PillowTestCase):
|
|||
draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK)
|
||||
self.assert_image_equal(img, expected, 'triangle right failed')
|
||||
|
||||
|
||||
def test_line_horizontal(self):
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w2px_normal.png'))
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_horizontal_w2px_normal.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 14, 5), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line straigth horizontal normal 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w2px_inverted.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth horizontal normal 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_horizontal_w2px_inverted.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((14, 5, 5, 5), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line straigth horizontal inverted 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w3px.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth horizontal inverted 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_horizontal_w3px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 14, 5), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line straigth horizontal normal 3px wide failed')
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth horizontal normal 3px wide failed')
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((14, 5, 5, 5), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line straigth horizontal inverted 3px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w101px.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth horizontal inverted 3px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_horizontal_w101px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((200, 110))
|
||||
draw.line((5, 55, 195, 55), BLACK, 101)
|
||||
self.assert_image_equal(img, expected, 'line straigth horizontal 101px wide failed')
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth horizontal 101px wide failed')
|
||||
|
||||
def test_line_h_s1_w2(self):
|
||||
self.skipTest('failing')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_slope1px_w2px.png'))
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_horizontal_slope1px_w2px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 14, 6), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line horizontal 1px slope 2px wide failed')
|
||||
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line horizontal 1px slope 2px wide failed')
|
||||
|
||||
def test_line_vertical(self):
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w2px_normal.png'))
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_vertical_w2px_normal.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 5, 14), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line straigth vertical normal 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w2px_inverted.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth vertical normal 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_vertical_w2px_inverted.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 14, 5, 5), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line straigth vertical inverted 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w3px.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth vertical inverted 2px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_vertical_w3px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 5, 14), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line straigth vertical normal 3px wide failed')
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth vertical normal 3px wide failed')
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 14, 5, 5), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line straigth vertical inverted 3px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w101px.png'))
|
||||
self.assert_image_equal(
|
||||
img, expected, 'line straigth vertical inverted 3px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_vertical_w101px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((110, 200))
|
||||
draw.line((55, 5, 55, 195), BLACK, 101)
|
||||
self.assert_image_equal(img, expected, 'line straigth vertical 101px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_slope1px_w2px.png'))
|
||||
self.assert_image_equal(img, expected,
|
||||
'line straigth vertical 101px wide failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_vertical_slope1px_w2px.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 6, 14), BLACK, 2)
|
||||
self.assert_image_equal(img, expected, 'line vertical 1px slope 2px wide failed')
|
||||
|
||||
self.assert_image_equal(img, expected,
|
||||
'line vertical 1px slope 2px wide failed')
|
||||
|
||||
def test_line_oblique_45(self):
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_oblique_45_w3px_a.png'))
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_oblique_45_w3px_a.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 5, 14, 14), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line oblique 45 normal 3px wide A failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'line oblique 45 normal 3px wide A failed')
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((14, 14, 5, 5), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line oblique 45 inverted 3px wide A failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH, 'line_oblique_45_w3px_b.png'))
|
||||
self.assert_image_equal(img, expected,
|
||||
'line oblique 45 inverted 3px wide A failed')
|
||||
expected = Image.open(os.path.join(IMAGES_PATH,
|
||||
'line_oblique_45_w3px_b.png'))
|
||||
expected.load()
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((14, 5, 5, 14), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line oblique 45 normal 3px wide B failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'line oblique 45 normal 3px wide B failed')
|
||||
img, draw = self.create_base_image_draw((20, 20))
|
||||
draw.line((5, 14, 14, 5), BLACK, 3)
|
||||
self.assert_image_equal(img, expected, 'line oblique 45 inverted 3px wide B failed')
|
||||
self.assert_image_equal(img, expected,
|
||||
'line oblique 45 inverted 3px wide B failed')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
# End of file
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Test the ImageMorphology functionality
|
||||
from helper import *
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageMorph
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from helper import *
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
try:
|
||||
import pyroma
|
||||
|
|
|
@ -17,6 +17,7 @@ test_filenames = (
|
|||
"temp_'\"&&",
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
|
||||
class TestShellInjection(PillowTestCase):
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from PIL import Image
|
||||
|
||||
import sys, time
|
||||
import io
|
||||
import threading, queue
|
||||
import queue
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
try:
|
||||
format = sys.argv[1]
|
||||
|
@ -16,6 +18,7 @@ queue = queue.Queue()
|
|||
|
||||
result = []
|
||||
|
||||
|
||||
class Worker(threading.Thread):
|
||||
def run(self):
|
||||
while True:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from PIL import Image
|
||||
|
||||
|
||||
def version(module, version):
|
||||
v = getattr(module.core, version + "_version", None)
|
||||
if v:
|
||||
|
|
|
@ -117,20 +117,48 @@ converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
|
|||
their original size while loading them. The :py:meth:`~PIL.Image.Image.draft`
|
||||
method also configures the JPEG decoder to trade some quality for speed.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
The :py:meth:`~PIL.Image.Image.open` method may set the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties if available:
|
||||
|
||||
**jfif**
|
||||
JFIF application marker found. If the file is not a JFIF file, this key is
|
||||
not present.
|
||||
|
||||
**jfif_version**
|
||||
A tuple representing the jfif version, (major version, minor version).
|
||||
|
||||
**jfif_density**
|
||||
A tuple representing the pixel density of the image, in units specified
|
||||
by jfif_unit.
|
||||
|
||||
**jfif_unit**
|
||||
Units for the jfif_density:
|
||||
|
||||
* 0 - No Units
|
||||
* 1 - Pixels per Inch
|
||||
* 2 - Pixels per Centimeter
|
||||
|
||||
**dpi**
|
||||
A tuple representing the reported pixel density in pixels per inch, if
|
||||
the file is a jfif file and the units are in inches.
|
||||
|
||||
**adobe**
|
||||
Adobe application marker found. If the file is not an Adobe JPEG file, this
|
||||
key is not present.
|
||||
|
||||
**adobe_transform**
|
||||
Vendor Specific Tag.
|
||||
|
||||
**progression**
|
||||
Indicates that this is a progressive JPEG file.
|
||||
|
||||
**icc-profile**
|
||||
The ICC color profile for the image.
|
||||
|
||||
**exif**
|
||||
Raw EXIF data from the image.
|
||||
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||
|
||||
**quality**
|
||||
|
@ -147,6 +175,19 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
|||
If present, indicates that this image should be stored as a progressive
|
||||
JPEG file.
|
||||
|
||||
**dpi**
|
||||
A tuple of integers representing the pixel density, ``(x,y)``.
|
||||
|
||||
**icc-profile**
|
||||
If present, the image is stored with the provided ICC profile. If
|
||||
this parameter is not provided, the image will be saved with no
|
||||
profile attached. To preserve the existing profile::
|
||||
|
||||
im.save(filename, 'jpeg', icc_profile=im.info.get('icc_profile'))
|
||||
|
||||
**exif**
|
||||
If present, the image will be stored with the provided raw EXIF data.
|
||||
|
||||
**subsampling**
|
||||
If present, sets the subsampling for the encoder.
|
||||
|
||||
|
@ -670,7 +711,7 @@ files, using either JPEG or HEX encoding depending on the image mode (and
|
|||
whether JPEG support is available or not).
|
||||
|
||||
PIXAR (read only)
|
||||
^^^^
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
PIL provides limited support for PIXAR raster files. The library can identify
|
||||
and read “dumped” RGB files.
|
||||
|
|
40
encode.c
40
encode.c
|
@ -535,12 +535,12 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
#include "Jpeg.h"
|
||||
|
||||
static unsigned int** get_qtables_arrays(PyObject* qtables, int* qtablesLen) {
|
||||
static unsigned int* get_qtables_arrays(PyObject* qtables, int* qtablesLen) {
|
||||
PyObject* tables;
|
||||
PyObject* table;
|
||||
PyObject* table_data;
|
||||
int i, j, num_tables;
|
||||
unsigned int **qarrays;
|
||||
unsigned int *qarrays;
|
||||
|
||||
if ((qtables == NULL) || (qtables == Py_None)) {
|
||||
return NULL;
|
||||
|
@ -554,10 +554,12 @@ static unsigned int** get_qtables_arrays(PyObject* qtables, int* qtablesLen) {
|
|||
tables = PySequence_Fast(qtables, "expected a sequence");
|
||||
num_tables = PySequence_Size(qtables);
|
||||
if (num_tables < 1 || num_tables > NUM_QUANT_TBLS) {
|
||||
PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 1 and 4.");
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Not a valid number of quantization tables. Should be between 1 and 4.");
|
||||
Py_DECREF(tables);
|
||||
return NULL;
|
||||
}
|
||||
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*));
|
||||
qarrays = (unsigned int*) malloc(num_tables * DCTSIZE2 * sizeof(unsigned int));
|
||||
if (!qarrays) {
|
||||
Py_DECREF(tables);
|
||||
PyErr_NoMemory();
|
||||
|
@ -566,39 +568,33 @@ static unsigned int** get_qtables_arrays(PyObject* qtables, int* qtablesLen) {
|
|||
for (i = 0; i < num_tables; i++) {
|
||||
table = PySequence_Fast_GET_ITEM(tables, i);
|
||||
if (!PySequence_Check(table)) {
|
||||
Py_DECREF(tables);
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
||||
return NULL;
|
||||
goto JPEG_QTABLES_ERR;
|
||||
}
|
||||
if (PySequence_Size(table) != DCTSIZE2) {
|
||||
Py_DECREF(tables);
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
||||
return NULL;
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid quantization table size");
|
||||
goto JPEG_QTABLES_ERR;
|
||||
}
|
||||
table_data = PySequence_Fast(table, "expected a sequence");
|
||||
qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int));
|
||||
if (!qarrays[i]) {
|
||||
Py_DECREF(tables);
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
for (j = 0; j < DCTSIZE2; j++) {
|
||||
qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
|
||||
qarrays[i * DCTSIZE2 + j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
|
||||
}
|
||||
Py_DECREF(table_data);
|
||||
}
|
||||
|
||||
Py_DECREF(tables);
|
||||
*qtablesLen = num_tables;
|
||||
|
||||
JPEG_QTABLES_ERR:
|
||||
Py_DECREF(tables); // Run on both error and not error
|
||||
if (PyErr_Occurred()) {
|
||||
PyMem_Free(qarrays);
|
||||
free(qarrays);
|
||||
qarrays = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qarrays;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||
{
|
||||
|
@ -614,7 +610,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|||
int xdpi = 0, ydpi = 0;
|
||||
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||
PyObject* qtables=NULL;
|
||||
unsigned int **qarrays = NULL;
|
||||
unsigned int *qarrays = NULL;
|
||||
int qtablesLen = 0;
|
||||
char* extra = NULL;
|
||||
int extra_size;
|
||||
|
@ -638,7 +634,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|||
qarrays = get_qtables_arrays(qtables, &qtablesLen);
|
||||
|
||||
if (extra && extra_size > 0) {
|
||||
char* p = malloc(extra_size);
|
||||
char* p = malloc(extra_size); // Freed in JpegEncode, Case 5
|
||||
if (!p)
|
||||
return PyErr_NoMemory();
|
||||
memcpy(p, extra, extra_size);
|
||||
|
@ -647,7 +643,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|||
extra = NULL;
|
||||
|
||||
if (rawExif && rawExifLen > 0) {
|
||||
char* pp = malloc(rawExifLen);
|
||||
char* pp = malloc(rawExifLen); // Freed in JpegEncode, Case 5
|
||||
if (!pp)
|
||||
return PyErr_NoMemory();
|
||||
memcpy(pp, rawExif, rawExifLen);
|
||||
|
|
|
@ -89,7 +89,9 @@ typedef struct {
|
|||
int subsampling;
|
||||
|
||||
/* Custom quantization tables () */
|
||||
unsigned int **qtables;
|
||||
unsigned int *qtables;
|
||||
|
||||
/* in factors of DCTSIZE2 */
|
||||
int qtablesLen;
|
||||
|
||||
/* Extra data (to be injected after header) */
|
||||
|
|
|
@ -153,7 +153,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
}
|
||||
for (i = 0; i < context->qtablesLen; i++) {
|
||||
// TODO: Should add support for none baseline
|
||||
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
|
||||
jpeg_add_quant_table(&context->cinfo, i, &context->qtables[i * DCTSIZE2],
|
||||
quality, TRUE);
|
||||
}
|
||||
} else if (context->quality > 0) {
|
||||
|
@ -289,8 +289,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
|||
jpeg_finish_compress(&context->cinfo);
|
||||
|
||||
/* Clean up */
|
||||
if (context->extra)
|
||||
if (context->extra) {
|
||||
free(context->extra);
|
||||
context->extra = NULL;
|
||||
}
|
||||
if (context->rawExif) {
|
||||
free(context->rawExif);
|
||||
context->rawExif = NULL;
|
||||
}
|
||||
if (context->qtables) {
|
||||
free(context->qtables);
|
||||
context->qtables = NULL;
|
||||
}
|
||||
|
||||
jpeg_destroy_compress(&context->cinfo);
|
||||
/* if (jerr.pub.num_warnings) return BROKEN; */
|
||||
state->errcode = IMAGING_CODEC_END;
|
||||
|
|
|
@ -59,4 +59,5 @@ if MAX_PROCS != 1 and not sys.platform.startswith('win'):
|
|||
except Exception as msg:
|
||||
print("Exception installing mp_compile, proceeding without: %s" % msg)
|
||||
else:
|
||||
print("Single threaded build, not installing mp_compile: %s processes" %MAX_PROCS)
|
||||
print("Single threaded build, not installing mp_compile: %s processes" %
|
||||
MAX_PROCS)
|
||||
|
|
Loading…
Reference in New Issue
Block a user