Merge from master

This commit is contained in:
wiredfool 2013-11-30 09:57:00 -08:00
commit 543f96de61
24 changed files with 340 additions and 51 deletions

View File

@ -6,7 +6,7 @@ python:
- 3.2 - 3.2
- 3.3 - 3.3
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev" install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev ghostscript"
script: script:
- python setup.py clean - python setup.py clean

View File

@ -4,6 +4,36 @@ Changelog (Pillow)
2.3.0 (2014-01-01) 2.3.0 (2014-01-01)
------------------ ------------------
- Quote filenames and title before using on command line
[tmccombs]
- Fixed Viewer.show to return properly
[tmccombs]
- Documentation fixes
[wiredfool]
- Fixed memory leak saving images as webp when webpmux is available
[cezarsa]
- Fix compiling with FreeType 2.5.1
[stromnov]
- Adds directories for NetBSD.
[deepy]
- Support RGBA TIFF with missing ExtraSamples tag
[cgohlke]
- Lossless WEBP Support
[wiredfool]
- Take compression as an option in the save call for tiffs
[wiredfool]
- Add support for saving lossless WebP. Just pass 'lossless=True' to save()
[liftoff]
- LCMS support upgraded from version 1 to version 2, fixes #343 - LCMS support upgraded from version 1 to version 2, fixes #343
[wiredfool] [wiredfool]

View File

@ -50,14 +50,22 @@ if sys.platform.startswith('win'):
else: else:
gs_windows_binary = False gs_windows_binary = False
def Ghostscript(tile, size, fp): def Ghostscript(tile, size, fp, scale=1):
"""Render an image using Ghostscript""" """Render an image using Ghostscript"""
# Unpack decoder tile # Unpack decoder tile
decoder, tile, offset, data = tile[0] decoder, tile, offset, data = tile[0]
length, bbox = data length, bbox = data
import tempfile, os #Hack to support hi-res rendering
scale = int(scale) or 1
orig_size = size
orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale]
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox)
import tempfile, os, subprocess
file = tempfile.mktemp() file = tempfile.mktemp()
@ -65,33 +73,32 @@ def Ghostscript(tile, size, fp):
command = ["gs", command = ["gs",
"-q", # quite mode "-q", # quite mode
"-g%dx%d" % size, # set output geometry (pixels) "-g%dx%d" % size, # set output geometry (pixels)
"-r%d" % (72*scale), # 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 "-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % file,# output file "-sOutputFile=%s" % file,# output file
"- >/dev/null 2>/dev/null"] ]
if gs_windows_binary is not None: if gs_windows_binary is not None:
if gs_windows_binary is False: if gs_windows_binary is False:
raise WindowsError('Unable to locate Ghostscript on paths') raise WindowsError('Unable to locate Ghostscript on paths')
command[0] = gs_windows_binary command[0] = gs_windows_binary
command[-1] = '- >nul 2>nul'
command = " ".join(command)
# push data through ghostscript # push data through ghostscript
try: try:
gs = os.popen(command, "w") gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# adjust for image origin # adjust for image origin
if bbox[0] != 0 or bbox[1] != 0: if bbox[0] != 0 or bbox[1] != 0:
gs.write("%d %d translate\n" % (-bbox[0], -bbox[1])) gs.stdin.write(("%d %d translate\n" % (-bbox[0], -bbox[1])).encode('ascii'))
fp.seek(offset) fp.seek(offset)
while length > 0: while length > 0:
s = fp.read(8192) s = fp.read(8192)
if not s: if not s:
break break
length = length - len(s) length = length - len(s)
gs.write(s) gs.stdin.write(s)
status = gs.close() gs.stdin.close()
status = gs.wait()
if status: if status:
raise IOError("gs failed (status %d)" % status) raise IOError("gs failed (status %d)" % status)
im = Image.core.open_ppm(file) im = Image.core.open_ppm(file)
@ -304,11 +311,11 @@ class EpsImageFile(ImageFile.ImageFile):
if not box: if not box:
raise IOError("cannot determine EPS bounding box") raise IOError("cannot determine EPS bounding box")
def load(self): def load(self, scale=1):
# Load EPS via Ghostscript # Load EPS via Ghostscript
if not self.tile: if not self.tile:
return return
self.im = Ghostscript(self.tile, self.size, self.fp) self.im = Ghostscript(self.tile, self.size, self.fp, scale)
self.mode = self.im.mode self.mode = self.im.mode
self.size = self.im.size self.size = self.im.size
self.tile = [] self.tile = []

View File

@ -475,7 +475,7 @@ class Image:
new.mode = im.mode new.mode = im.mode
new.size = im.size new.size = im.size
new.palette = self.palette new.palette = self.palette
if im.mode == "P": if im.mode == "P" and not new.palette:
from PIL import ImagePalette from PIL import ImagePalette
new.palette = ImagePalette.ImagePalette() new.palette = ImagePalette.ImagePalette()
try: try:
@ -675,15 +675,18 @@ class Image:
L = R * 299/1000 + G * 587/1000 + B * 114/1000 L = R * 299/1000 + G * 587/1000 + B * 114/1000
When translating a greyscale image into a bilevel image (mode The default method of converting a greyscale ("L") or "RGB"
"1"), all non-zero values are set to 255 (white). To use other image into a bilevel (mode "1") image uses Floyd-Steinberg
thresholds, use the :py:meth:`~PIL.Image.Image.point` method. dither to approximate the original image luminosity levels. If
dither is NONE, all non-zero values are set to 255 (white). To
use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
method.
:param mode: The requested mode. :param mode: The requested mode.
:param matrix: An optional conversion matrix. If given, this :param matrix: An optional conversion matrix. If given, this
should be 4- or 16-tuple containing floating point values. should be 4- or 16-tuple containing floating point values.
:param dither: Dithering method, used when converting from :param dither: Dithering method, used when converting from
mode "RGB" to "P". mode "RGB" to "P" or from "RGB" or "L" to "1".
Available methods are NONE or FLOYDSTEINBERG (default). Available methods are NONE or FLOYDSTEINBERG (default).
:param palette: Palette to use when converting from mode "RGB" :param palette: Palette to use when converting from mode "RGB"
to "P". Available palettes are WEB or ADAPTIVE. to "P". Available palettes are WEB or ADAPTIVE.

View File

@ -17,6 +17,11 @@ from __future__ import print_function
from PIL import Image from PIL import Image
import os, sys import os, sys
if(sys.version_info >= (3, 3)):
from shlex import quote
else:
from pipes import quote
_viewers = [] _viewers = []
def register(viewer, order=1): def register(viewer, order=1):
@ -65,7 +70,7 @@ class Viewer:
if base != image.mode and image.mode != "1": if base != image.mode and image.mode != "1":
image = image.convert(base) image = image.convert(base)
self.show_image(image, **options) return self.show_image(image, **options)
# hook methods # hook methods
@ -99,7 +104,7 @@ if sys.platform == "win32":
format = "BMP" format = "BMP"
def get_command(self, file, **options): def get_command(self, file, **options):
return ("start /wait %s && ping -n 2 127.0.0.1 >NUL " return ("start /wait %s && ping -n 2 127.0.0.1 >NUL "
"&& del /f %s" % (file, file)) "&& del /f %s" % (quote(file), quote(file)))
register(WindowsViewer) register(WindowsViewer)
@ -111,7 +116,7 @@ elif sys.platform == "darwin":
# on darwin open returns immediately resulting in the temp # on darwin open returns immediately resulting in the temp
# file removal while app is opening # file removal while app is opening
command = "open -a /Applications/Preview.app" command = "open -a /Applications/Preview.app"
command = "(%s %s; sleep 20; rm -f %s)&" % (command, file, file) command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file))
return command return command
register(MacViewer) register(MacViewer)
@ -134,7 +139,7 @@ else:
class UnixViewer(Viewer): class UnixViewer(Viewer):
def show_file(self, file, **options): def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options) command, executable = self.get_command_ex(file, **options)
command = "(%s %s; rm -f %s)&" % (command, file, file) command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
os.system(command) os.system(command)
return 1 return 1
@ -154,8 +159,7 @@ else:
# imagemagick's display command instead. # imagemagick's display command instead.
command = executable = "xv" command = executable = "xv"
if title: if title:
# FIXME: do full escaping command = command + " -name %s" % quote(title)
command = command + " -name \"%s\"" % title
return command, executable return command, executable
if which("xv"): if which("xv"):

View File

@ -156,6 +156,7 @@ OPEN_INFO = {
(II, 1, 3, 1, (32,), ()): ("F", "F;32F"), (II, 1, 3, 1, (32,), ()): ("F", "F;32F"),
(II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"), (II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"),
(II, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"), (II, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"),
(II, 2, 1, 1, (8,8,8,8), ()): ("RGBA", "RGBA"), # missing ExtraSamples
(II, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"), (II, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"),
(II, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"), (II, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"),
(II, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"), (II, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"),
@ -973,7 +974,7 @@ def _save(im, fp, filename):
ifd = ImageFileDirectory(prefix) ifd = ImageFileDirectory(prefix)
compression = im.info.get('compression','raw') compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
libtiff = compression in ["tiff_ccitt", "group3", "group4", libtiff = compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate", "tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate", "tiff_thunderscan", "tiff_deflate",

View File

@ -12,6 +12,7 @@ _VALID_WEBP_MODES = {
_VP8_MODES_BY_IDENTIFIER = { _VP8_MODES_BY_IDENTIFIER = {
b"VP8 ": "RGB", b"VP8 ": "RGB",
b"VP8X": "RGBA", b"VP8X": "RGBA",
b"VP8L": "RGBA", # lossless
} }
@ -48,6 +49,7 @@ def _save(im, fp, filename):
if im.mode not in _VALID_WEBP_MODES: if im.mode not in _VALID_WEBP_MODES:
raise IOError("cannot write mode %s as WEBP" % image_mode) raise IOError("cannot write mode %s as WEBP" % image_mode)
lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80) quality = im.encoderinfo.get("quality", 80)
icc_profile = im.encoderinfo.get("icc_profile", "") icc_profile = im.encoderinfo.get("icc_profile", "")
exif = im.encoderinfo.get("exif", "") exif = im.encoderinfo.get("exif", "")
@ -56,6 +58,7 @@ def _save(im, fp, filename):
im.tobytes(), im.tobytes(),
im.size[0], im.size[0],
im.size[1], im.size[1],
lossless,
float(quality), float(quality),
im.mode, im.mode,
icc_profile, icc_profile,

View File

@ -0,0 +1,30 @@
#!/usr/bin/gnuplot
#This is the script that was used to create our sample EPS files
#We used the following version of the gnuplot program
#G N U P L O T
#Version 4.6 patchlevel 3 last modified 2013-04-12
#Build System: Darwin x86_64
#This file will generate the non_zero_bb.eps variant, in order to get the
#zero_bb.eps variant you will need to edit line6 in the result file to
#be "%%BoundingBox: 0 0 460 352" instead of "%%BoundingBox: 50 50 410 302"
set t postscript eps color
set o "sample.eps"
set dummy u,v
set key bmargin center horizontal Right noreverse enhanced autotitles nobox
set parametric
set view 50, 30, 1, 1
set isosamples 10, 10
set hidden3d back offset 1 trianglepattern 3 undefined 1 altdiagonal bentover
set ticslevel 0
set title "Interlocking Tori"
set style line 1 lt 1 lw 1 pt 3 lc rgb "red"
set style line 2 lt 1 lw 1 pt 3 lc rgb "blue"
set urange [ -3.14159 : 3.14159 ] noreverse nowriteback
set vrange [ -3.14159 : 3.14159 ] noreverse nowriteback
splot cos(u)+.5*cos(u)*cos(v),sin(u)+.5*sin(u)*cos(v),.5*sin(v) ls 1,\
1+cos(u)+.5*cos(u)*cos(v),.5*sin(v),sin(u)+.5*sin(u)*cos(v) ls 2

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
Tests/images/zero_bb.eps Normal file

Binary file not shown.

BIN
Tests/images/zero_bb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

82
Tests/test_file_eps.py Normal file
View File

@ -0,0 +1,82 @@
from tester import *
from PIL import Image
#Our two EPS test files (they are identical except for their bounding boxes)
file1 = "Tests/images/zero_bb.eps"
file2 = "Tests/images/non_zero_bb.eps"
#Due to palletization, we'll need to convert these to RGB after load
file1_compare = "Tests/images/zero_bb.png"
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
file2_compare = "Tests/images/non_zero_bb.png"
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
def test_sanity():
#Regular scale
image1 = Image.open(file1)
image1.load()
assert_equal(image1.mode, "RGB")
assert_equal(image1.size, (460, 352))
assert_equal(image1.format, "EPS")
image2 = Image.open(file2)
image2.load()
assert_equal(image2.mode, "RGB")
assert_equal(image2.size, (360, 252))
assert_equal(image2.format, "EPS")
#Double scale
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
assert_equal(image1_scale2.mode, "RGB")
assert_equal(image1_scale2.size, (920, 704))
assert_equal(image1_scale2.format, "EPS")
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
assert_equal(image2_scale2.mode, "RGB")
assert_equal(image2_scale2.size, (720, 504))
assert_equal(image2_scale2.format, "EPS")
def test_render_scale1():
#We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
image1_scale1 = Image.open(file1)
image1_scale1.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
#Non-Zero bounding box
image2_scale1 = Image.open(file2)
image2_scale1.load()
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
image2_scale1_compare.load()
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
def test_render_scale2():
#We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
image1_scale2_compare.load()
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
#Non-Zero bounding box
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
image2_scale2_compare.load()
assert_image_similar(image2_scale2, image2_scale2_compare, 10)

View File

@ -27,3 +27,22 @@ def test_optimize():
return len(file.getvalue()) return len(file.getvalue())
assert_equal(test(0), 800) assert_equal(test(0), 800)
assert_equal(test(1), 38) assert_equal(test(1), 38)
def test_roundtrip():
out = tempfile('temp.gif')
im = lena()
im.save(out)
reread = Image.open(out)
assert_image_similar(reread.convert('RGB'), im, 50)
def test_roundtrip2():
#see https://github.com/python-imaging/Pillow/issues/403
out = 'temp.gif'#tempfile('temp.gif')
im = Image.open('Images/lena.gif')
im2 = im.copy()
im2.save(out)
reread = Image.open(out)
assert_image_similar(reread.convert('RGB'), lena(), 50)

View File

@ -92,6 +92,7 @@ def test_g4_write():
assert_equal(reread.size,(500,500)) assert_equal(reread.size,(500,500))
_assert_noerr(reread) _assert_noerr(reread)
assert_image_equal(reread, rot) assert_image_equal(reread, rot)
assert_equal(reread.info['compression'], 'group4')
assert_equal(reread.info['compression'], orig.info['compression']) assert_equal(reread.info['compression'], orig.info['compression'])
@ -106,7 +107,6 @@ def test_adobe_deflate_tiff():
assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
assert_no_exception(lambda: im.load()) assert_no_exception(lambda: im.load())
def test_write_metadata(): def test_write_metadata():
""" Test metadata writing through libtiff """ """ Test metadata writing through libtiff """
img = Image.open('Tests/images/lena_g4.tif') img = Image.open('Tests/images/lena_g4.tif')
@ -131,6 +131,15 @@ def test_write_metadata():
assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
def test_g3_compression():
i = Image.open('Tests/images/lena_g4_500.tif')
out = tempfile("temp.tif")
i.save(out, compression='group3')
reread = Image.open(out)
assert_equal(reread.info['compression'], 'group3')
assert_image_equal(reread, i)
def test_little_endian(): def test_little_endian():
im = Image.open('Tests/images/12bit.deflate.tif') im = Image.open('Tests/images/12bit.deflate.tif')
assert_equal(im.getpixel((0,0)), 480) assert_equal(im.getpixel((0,0)), 480)

View File

@ -66,6 +66,26 @@ def test_write_rgb():
assert_image_similar(image, target, 20.0) assert_image_similar(image, target, 20.0)
def test_write_lossless_rgb():
temp_file = tempfile("temp.webp")
lena("RGB").save(temp_file, lossless=True)
image = Image.open(temp_file)
image.load()
assert_equal(image.mode, "RGB")
assert_equal(image.size, (128, 128))
assert_equal(image.format, "WEBP")
assert_no_exception(lambda: image.load())
assert_no_exception(lambda: image.getdata())
assert_image_equal(image, lena("RGB"))
def test_write_rgba(): def test_write_rgba():
""" """
Can we write a RGBA mode file to webp without error. Does it have the bits we Can we write a RGBA mode file to webp without error. Does it have the bits we
@ -111,3 +131,27 @@ def test_read_rgba():
target = Image.open('Images/transparent.png') target = Image.open('Images/transparent.png')
assert_image_similar(image, target, 20.0) assert_image_similar(image, target, 20.0)
def test_write_lossless_rgb():
temp_file = tempfile("temp.webp")
#temp_file = "temp.webp"
pil_image = lena('RGBA')
mask = Image.new("RGBA", (64, 64), (128,128,128,128))
pil_image.paste(mask, (0,0), mask) # add some partially transparent bits.
pil_image.save(temp_file, lossless=True)
image = Image.open(temp_file)
image.load()
assert_equal(image.mode, "RGBA")
assert_equal(image.size, pil_image.size)
assert_equal(image.format, "WEBP")
assert_no_exception(lambda: image.load())
assert_no_exception(lambda: image.getdata())
assert_image_equal(image, pil_image)

View File

@ -59,7 +59,11 @@ struct {
const char* message; const char* message;
} ft_errors[] = } ft_errors[] =
#if defined(USE_FREETYPE_2_1)
#include FT_ERRORS_H
#else
#include <freetype/fterrors.h> #include <freetype/fterrors.h>
#endif
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* font objects */ /* font objects */

18
_webp.c
View File

@ -13,6 +13,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
{ {
int width; int width;
int height; int height;
int lossless;
float quality_factor; float quality_factor;
uint8_t *rgb; uint8_t *rgb;
uint8_t *icc_bytes; uint8_t *icc_bytes;
@ -24,22 +25,29 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
Py_ssize_t exif_size; Py_ssize_t exif_size;
size_t ret_size; size_t ret_size;
if (!PyArg_ParseTuple(args, "s#iifss#s#", if (!PyArg_ParseTuple(args, "s#iiifss#s#",
(char**)&rgb, &size, &width, &height, &quality_factor, &mode, (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
&icc_bytes, &icc_size, &exif_bytes, &exif_size)) { &icc_bytes, &icc_size, &exif_bytes, &exif_size)) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
if (strcmp(mode, "RGBA")==0){ if (strcmp(mode, "RGBA")==0){
if (size < width * height * 4){ if (size < width * height * 4){
Py_RETURN_NONE; Py_RETURN_NONE;
} }
if (lossless) {
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
} else {
ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output); ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output);
}
} else if (strcmp(mode, "RGB")==0){ } else if (strcmp(mode, "RGB")==0){
if (size < width * height * 3){ if (size < width * height * 3){
Py_RETURN_NONE; Py_RETURN_NONE;
} }
if (lossless) {
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
} else {
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output); ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
}
} else { } else {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -105,11 +113,11 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
WebPMuxAssemble(mux, &output_data); WebPMuxAssemble(mux, &output_data);
WebPMuxDelete(mux); WebPMuxDelete(mux);
free(output);
output = (uint8_t*)output_data.bytes;
ret_size = output_data.size; ret_size = output_data.size;
if (ret_size > 0) { if (ret_size > 0) {
PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); PyObject *ret = PyBytes_FromStringAndSize((char*)output_data.bytes, ret_size);
WebPDataClear(&output_data); WebPDataClear(&output_data);
return ret; return ret;
} }

View File

@ -36,6 +36,20 @@ PIL identifies EPS files containing image data, and can read files that contain
embedded raster images (ImageData descriptors). If Ghostscript is available, embedded raster images (ImageData descriptors). If Ghostscript is available,
other EPS files can be read as well. The EPS driver can also write EPS images. other EPS files can be read as well. The EPS driver can also write EPS images.
If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load`
method with the following parameter to affect how Ghostscript renders the EPS
**scale**
Affects the scale of the resultant rasterized image. If the EPS suggests
that the image be rendered at 100px x 100px, setting this parameter to
2 will make the Ghostscript render a 200px x 200px image instead. The
relative position of the bounding box is maintained::
im = Image.open(...)
im.size #(100,100)
im.load(scale=2)
im.size #(200,200)
GIF GIF
^^^ ^^^
@ -322,6 +336,24 @@ WebP
PIL reads and writes WebP files. The specifics of PIL's capabilities with this PIL reads and writes WebP files. The specifics of PIL's capabilities with this
format are currently undocumented. format are currently undocumented.
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**lossless**
If present, instructs the WEBP writer to use lossless
compression.
**quality**
Integer, 1-100, Defaults to 80. Sets the quality level for
lossy compression.
**icc_procfile**
The ICC Profile to include in the saved file. Only supported if
the system webp library was built with webpmux support.
**exif**
The exif data to include in the saved file. Only supported if
the system webp library was built with webpmux support.
XBM XBM
^^^ ^^^

View File

@ -1,10 +1,13 @@
Writing your own file decoder Writing your own file decoder
============================= =============================
The Python Imaging Library uses a plug-in model which allows you to add your The Python Imaging Library uses a plug-in model which allows you to
own decoders to the library, without any changes to the library itself. Such add your own decoders to the library, without any changes to the
plug-ins have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique library itself. Such plug-ins usually have names like
format name (usually an abbreviation). :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name
(usually an abbreviation).
.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually.
A decoder plug-in should contain a decoder class, based on the A decoder plug-in should contain a decoder class, based on the
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an :py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an

View File

@ -15,3 +15,9 @@ to this::
The :py:mod:`_imaging` module has been moved. You can now import it like this:: The :py:mod:`_imaging` module has been moved. You can now import it like this::
from PIL.Image import core as _imaging from PIL.Image import core as _imaging
The image plugin loading mechanisim has changed. Pillow no longer
automatically imports any file in the Python path with a name ending
in :file:`ImagePlugin.py`. You will need to import your image plugin
manually.

View File

@ -211,6 +211,10 @@ class pil_build_ext(build_ext):
# work ;-) # work ;-)
self.add_multiarch_paths() self.add_multiarch_paths()
elif sys.platform.startswith("netbsd"):
_add_directory(library_dirs, "/usr/pkg/lib")
_add_directory(include_dirs, "/usr/pkg/include")
_add_directory(library_dirs, "/usr/local/lib") _add_directory(library_dirs, "/usr/local/lib")
# FIXME: check /opt/stuff directories here? # FIXME: check /opt/stuff directories here?