mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 00:46:16 +03:00
Merge from master
This commit is contained in:
commit
543f96de61
|
@ -6,7 +6,7 @@ python:
|
|||
- 3.2
|
||||
- 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:
|
||||
- python setup.py clean
|
||||
|
|
30
CHANGES.rst
30
CHANGES.rst
|
@ -4,6 +4,36 @@ Changelog (Pillow)
|
|||
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
|
||||
[wiredfool]
|
||||
|
||||
|
|
|
@ -50,14 +50,22 @@ if sys.platform.startswith('win'):
|
|||
else:
|
||||
gs_windows_binary = False
|
||||
|
||||
def Ghostscript(tile, size, fp):
|
||||
def Ghostscript(tile, size, fp, scale=1):
|
||||
"""Render an image using Ghostscript"""
|
||||
|
||||
# Unpack decoder tile
|
||||
decoder, tile, offset, data = tile[0]
|
||||
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()
|
||||
|
||||
|
@ -65,33 +73,32 @@ def Ghostscript(tile, size, fp):
|
|||
command = ["gs",
|
||||
"-q", # quite mode
|
||||
"-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
|
||||
"-sDEVICE=ppmraw", # ppm driver
|
||||
"-sOutputFile=%s" % file,# output file
|
||||
"- >/dev/null 2>/dev/null"]
|
||||
]
|
||||
|
||||
if gs_windows_binary is not None:
|
||||
if gs_windows_binary is False:
|
||||
raise WindowsError('Unable to locate Ghostscript on paths')
|
||||
command[0] = gs_windows_binary
|
||||
command[-1] = '- >nul 2>nul'
|
||||
|
||||
command = " ".join(command)
|
||||
|
||||
# push data through ghostscript
|
||||
try:
|
||||
gs = os.popen(command, "w")
|
||||
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
# adjust for image origin
|
||||
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)
|
||||
while length > 0:
|
||||
s = fp.read(8192)
|
||||
if not s:
|
||||
break
|
||||
length = length - len(s)
|
||||
gs.write(s)
|
||||
status = gs.close()
|
||||
gs.stdin.write(s)
|
||||
gs.stdin.close()
|
||||
status = gs.wait()
|
||||
if status:
|
||||
raise IOError("gs failed (status %d)" % status)
|
||||
im = Image.core.open_ppm(file)
|
||||
|
@ -304,11 +311,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
if not box:
|
||||
raise IOError("cannot determine EPS bounding box")
|
||||
|
||||
def load(self):
|
||||
def load(self, scale=1):
|
||||
# Load EPS via Ghostscript
|
||||
if not self.tile:
|
||||
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.size = self.im.size
|
||||
self.tile = []
|
||||
|
|
13
PIL/Image.py
13
PIL/Image.py
|
@ -475,7 +475,7 @@ class Image:
|
|||
new.mode = im.mode
|
||||
new.size = im.size
|
||||
new.palette = self.palette
|
||||
if im.mode == "P":
|
||||
if im.mode == "P" and not new.palette:
|
||||
from PIL import ImagePalette
|
||||
new.palette = ImagePalette.ImagePalette()
|
||||
try:
|
||||
|
@ -675,15 +675,18 @@ class Image:
|
|||
|
||||
L = R * 299/1000 + G * 587/1000 + B * 114/1000
|
||||
|
||||
When translating a greyscale image into a bilevel image (mode
|
||||
"1"), all non-zero values are set to 255 (white). To use other
|
||||
thresholds, use the :py:meth:`~PIL.Image.Image.point` method.
|
||||
The default method of converting a greyscale ("L") or "RGB"
|
||||
image into a bilevel (mode "1") image uses Floyd-Steinberg
|
||||
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 matrix: An optional conversion matrix. If given, this
|
||||
should be 4- or 16-tuple containing floating point values.
|
||||
: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).
|
||||
:param palette: Palette to use when converting from mode "RGB"
|
||||
to "P". Available palettes are WEB or ADAPTIVE.
|
||||
|
|
|
@ -17,6 +17,11 @@ from __future__ import print_function
|
|||
from PIL import Image
|
||||
import os, sys
|
||||
|
||||
if(sys.version_info >= (3, 3)):
|
||||
from shlex import quote
|
||||
else:
|
||||
from pipes import quote
|
||||
|
||||
_viewers = []
|
||||
|
||||
def register(viewer, order=1):
|
||||
|
@ -65,7 +70,7 @@ class Viewer:
|
|||
if base != image.mode and image.mode != "1":
|
||||
image = image.convert(base)
|
||||
|
||||
self.show_image(image, **options)
|
||||
return self.show_image(image, **options)
|
||||
|
||||
# hook methods
|
||||
|
||||
|
@ -99,7 +104,7 @@ if sys.platform == "win32":
|
|||
format = "BMP"
|
||||
def get_command(self, file, **options):
|
||||
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)
|
||||
|
||||
|
@ -111,7 +116,7 @@ elif sys.platform == "darwin":
|
|||
# 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, file, file)
|
||||
command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file))
|
||||
return command
|
||||
|
||||
register(MacViewer)
|
||||
|
@ -134,7 +139,7 @@ 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, file, file)
|
||||
command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
|
||||
os.system(command)
|
||||
return 1
|
||||
|
||||
|
@ -154,8 +159,7 @@ else:
|
|||
# imagemagick's display command instead.
|
||||
command = executable = "xv"
|
||||
if title:
|
||||
# FIXME: do full escaping
|
||||
command = command + " -name \"%s\"" % title
|
||||
command = command + " -name %s" % quote(title)
|
||||
return command, executable
|
||||
|
||||
if which("xv"):
|
||||
|
|
|
@ -156,6 +156,7 @@ OPEN_INFO = {
|
|||
(II, 1, 3, 1, (32,), ()): ("F", "F;32F"),
|
||||
(II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"),
|
||||
(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), (1,)): ("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)
|
||||
|
||||
compression = im.info.get('compression','raw')
|
||||
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
|
||||
libtiff = compression in ["tiff_ccitt", "group3", "group4",
|
||||
"tiff_jpeg", "tiff_adobe_deflate",
|
||||
"tiff_thunderscan", "tiff_deflate",
|
||||
|
|
|
@ -12,6 +12,7 @@ _VALID_WEBP_MODES = {
|
|||
_VP8_MODES_BY_IDENTIFIER = {
|
||||
b"VP8 ": "RGB",
|
||||
b"VP8X": "RGBA",
|
||||
b"VP8L": "RGBA", # lossless
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,6 +49,7 @@ def _save(im, fp, filename):
|
|||
if im.mode not in _VALID_WEBP_MODES:
|
||||
raise IOError("cannot write mode %s as WEBP" % image_mode)
|
||||
|
||||
lossless = im.encoderinfo.get("lossless", False)
|
||||
quality = im.encoderinfo.get("quality", 80)
|
||||
icc_profile = im.encoderinfo.get("icc_profile", "")
|
||||
exif = im.encoderinfo.get("exif", "")
|
||||
|
@ -56,6 +58,7 @@ def _save(im, fp, filename):
|
|||
im.tobytes(),
|
||||
im.size[0],
|
||||
im.size[1],
|
||||
lossless,
|
||||
float(quality),
|
||||
im.mode,
|
||||
icc_profile,
|
||||
|
|
30
Tests/images/create_eps.gnuplot
Normal file
30
Tests/images/create_eps.gnuplot
Normal 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
|
BIN
Tests/images/non_zero_bb.eps
Normal file
BIN
Tests/images/non_zero_bb.eps
Normal file
Binary file not shown.
BIN
Tests/images/non_zero_bb.png
Normal file
BIN
Tests/images/non_zero_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
Tests/images/non_zero_bb_scale2.png
Normal file
BIN
Tests/images/non_zero_bb_scale2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
Tests/images/zero_bb.eps
Normal file
BIN
Tests/images/zero_bb.eps
Normal file
Binary file not shown.
BIN
Tests/images/zero_bb.png
Normal file
BIN
Tests/images/zero_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
Tests/images/zero_bb_scale2.png
Normal file
BIN
Tests/images/zero_bb_scale2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
82
Tests/test_file_eps.py
Normal file
82
Tests/test_file_eps.py
Normal 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)
|
||||
|
|
@ -27,3 +27,22 @@ def test_optimize():
|
|||
return len(file.getvalue())
|
||||
assert_equal(test(0), 800)
|
||||
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)
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ def test_g4_write():
|
|||
assert_equal(reread.size,(500,500))
|
||||
_assert_noerr(reread)
|
||||
assert_image_equal(reread, rot)
|
||||
assert_equal(reread.info['compression'], 'group4')
|
||||
|
||||
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_no_exception(lambda: im.load())
|
||||
|
||||
|
||||
def test_write_metadata():
|
||||
""" Test metadata writing through libtiff """
|
||||
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)
|
||||
|
||||
|
||||
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():
|
||||
im = Image.open('Tests/images/12bit.deflate.tif')
|
||||
assert_equal(im.getpixel((0,0)), 480)
|
||||
|
|
|
@ -66,6 +66,26 @@ def test_write_rgb():
|
|||
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():
|
||||
"""
|
||||
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')
|
||||
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)
|
||||
|
|
|
@ -59,7 +59,11 @@ struct {
|
|||
const char* message;
|
||||
} ft_errors[] =
|
||||
|
||||
#if defined(USE_FREETYPE_2_1)
|
||||
#include FT_ERRORS_H
|
||||
#else
|
||||
#include <freetype/fterrors.h>
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* font objects */
|
||||
|
|
50
_webp.c
50
_webp.c
|
@ -13,6 +13,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|||
{
|
||||
int width;
|
||||
int height;
|
||||
int lossless;
|
||||
float quality_factor;
|
||||
uint8_t *rgb;
|
||||
uint8_t *icc_bytes;
|
||||
|
@ -20,29 +21,36 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|||
uint8_t *output;
|
||||
char *mode;
|
||||
Py_ssize_t size;
|
||||
Py_ssize_t icc_size;
|
||||
Py_ssize_t icc_size;
|
||||
Py_ssize_t exif_size;
|
||||
size_t ret_size;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#iifss#s#",
|
||||
(char**)&rgb, &size, &width, &height, &quality_factor, &mode,
|
||||
if (!PyArg_ParseTuple(args, "s#iiifss#s#",
|
||||
(char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
|
||||
&icc_bytes, &icc_size, &exif_bytes, &exif_size)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
if (strcmp(mode, "RGBA")==0){
|
||||
if (size < width * height * 4){
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output);
|
||||
} else if (strcmp(mode, "RGB")==0){
|
||||
if (size < width * height * 3){
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
|
||||
} else {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
if (strcmp(mode, "RGBA")==0){
|
||||
if (size < width * height * 4){
|
||||
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);
|
||||
}
|
||||
} else if (strcmp(mode, "RGB")==0){
|
||||
if (size < width * height * 3){
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifndef HAVE_WEBPMUX
|
||||
if (ret_size > 0) {
|
||||
|
@ -53,10 +61,10 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|||
#else
|
||||
{
|
||||
/* I want to truncate the *_size items that get passed into webp
|
||||
data. Pypy2.1.0 had some issues where the Py_ssize_t items had
|
||||
data. Pypy2.1.0 had some issues where the Py_ssize_t items had
|
||||
data in the upper byte. (Not sure why, it shouldn't have been there)
|
||||
*/
|
||||
int i_icc_size = (int)icc_size;
|
||||
int i_icc_size = (int)icc_size;
|
||||
int i_exif_size = (int)exif_size;
|
||||
WebPData output_data = {0};
|
||||
WebPData image = { output, ret_size };
|
||||
|
@ -105,11 +113,11 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
|||
|
||||
WebPMuxAssemble(mux, &output_data);
|
||||
WebPMuxDelete(mux);
|
||||
free(output);
|
||||
|
||||
output = (uint8_t*)output_data.bytes;
|
||||
ret_size = output_data.size;
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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
|
||||
^^^
|
||||
|
||||
|
@ -322,6 +336,24 @@ WebP
|
|||
PIL reads and writes WebP files. The specifics of PIL's capabilities with this
|
||||
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
|
||||
^^^
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
Writing your own file decoder
|
||||
=============================
|
||||
|
||||
The Python Imaging Library uses a plug-in model which allows you to add your
|
||||
own decoders to the library, without any changes to the library itself. Such
|
||||
plug-ins have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique
|
||||
format name (usually an abbreviation).
|
||||
The Python Imaging Library uses a plug-in model which allows you to
|
||||
add your own decoders to the library, without any changes to the
|
||||
library itself. Such plug-ins usually have names like
|
||||
: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
|
||||
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
|
||||
|
|
|
@ -15,3 +15,9 @@ to this::
|
|||
The :py:mod:`_imaging` module has been moved. You can now import it like this::
|
||||
|
||||
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.
|
||||
|
||||
|
|
4
setup.py
4
setup.py
|
@ -211,6 +211,10 @@ class pil_build_ext(build_ext):
|
|||
# work ;-)
|
||||
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")
|
||||
# FIXME: check /opt/stuff directories here?
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user