merge from master

This commit is contained in:
wiredfool 2014-01-31 13:45:31 -08:00
commit 288a563d1a
20 changed files with 254 additions and 139 deletions

View File

@ -14,6 +14,7 @@ install:
- "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev"
- "pip install cffi"
script:
- python setup.py clean
- python setup.py build_ext --inplace

View File

@ -4,6 +4,18 @@ Changelog (Pillow)
2.4.0 (2014-04-01)
------------------
- Minor patch on booleans + Travis
[sciunto]
- Look in multiarch paths in GNU platforms
[pinotree]
- Add arch support for pcc64, s390, s390x, armv7l, aarch64
[manisandro]
- Add arch support for ppc
[wiredfool]
- Correctly quote file names for WindowsViewer command
[cgohlke]

View File

@ -67,43 +67,48 @@ def Ghostscript(tile, size, fp, scale=1):
import tempfile, os, subprocess
file = tempfile.mktemp()
outfile = tempfile.mktemp()
infile = tempfile.mktemp()
with open(infile, 'wb') as f:
fp.seek(offset)
while length >0:
s = fp.read(100*1024)
if not s:
break
length = length - len(s)
f.write(s)
# Build ghostscript command
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
"-q", # quiet 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" % outfile, # output file
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin
"-f", infile, # input file
]
if gs_windows_binary is not None:
if gs_windows_binary is False:
if not gs_windows_binary:
raise WindowsError('Unable to locate Ghostscript on paths')
command[0] = gs_windows_binary
# push data through ghostscript
try:
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# adjust for image origin
if bbox[0] != 0 or bbox[1] != 0:
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.stdin.write(s)
gs.stdin.close()
status = gs.wait()
if status:
raise IOError("gs failed (status %d)" % status)
im = Image.core.open_ppm(file)
im = Image.core.open_ppm(outfile)
finally:
try: os.unlink(file)
try:
os.unlink(outfile)
os.unlink(infile)
except: pass
return im
@ -320,6 +325,11 @@ class EpsImageFile(ImageFile.ImageFile):
self.size = self.im.size
self.tile = []
def load_seek(self,*args,**kwargs):
# we can't incrementally load, so force ImageFile.parser to
# use our custom load method by defining this method.
pass
#
# --------------------------------------------------------------------
@ -350,7 +360,9 @@ def _save(im, fp, filename, eps=1):
pass
base_fp = fp
fp = io.TextIOWrapper(NoCloseStream(fp), encoding='latin-1')
fp = NoCloseStream(fp)
if sys.version_info[0] > 2:
fp = io.TextIOWrapper(fp, encoding='latin-1')
if eps:
#

View File

@ -820,6 +820,8 @@ class Image:
def draft(self, mode, size):
"""
NYI
Configures the image file loader so it returns a version of the
image that as closely as possible matches the given mode and
size. For example, you can use this method to convert a color
@ -1323,7 +1325,7 @@ class Image:
:param size: The requested size in pixels, as a 2-tuple:
(width, height).
:param filter: An optional resampling filter. This can be
:param resample: An optional resampling filter. This can be
one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline

View File

@ -229,7 +229,7 @@ class ImageFile(Image.Image):
self.fp = None # might be shared
if (not LOAD_TRUNCATED_IMAGES or t == 0) and not self.map and e < 0:
if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0:
# still raised if decoder fails to return anything
raise_ioerror(e)

View File

@ -248,7 +248,7 @@ class OleFileIO:
if entry[1:2] == "Image":
fin = ole.openstream(entry)
fout = open(entry[0:1], "wb")
while 1:
while True:
s = fin.read(8192)
if not s:
break

View File

@ -182,7 +182,7 @@ class SpiderImageFile(ImageFile.ImageFile):
# given a list of filenames, return a list of images
def loadImageSeries(filelist=None):
" create a list of Image.images for use in montage "
if filelist == None or len(filelist) < 1:
if filelist is None or len(filelist) < 1:
return
imglist = []

View File

@ -1141,7 +1141,7 @@ def _save(im, fp, filename):
# print (im.mode, compression, a, im.encoderconfig)
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
e.setimage(im.im, (0,0)+im.size)
while 1:
while True:
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
if not _fp:
fp.write(d)

View File

@ -32,8 +32,10 @@ class WebPImageFile(ImageFile.ImageFile):
def _open(self):
data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode(self.fp.read())
self.info["icc_profile"] = icc_profile
self.info["exif"] = exif
if icc_profile:
self.info["icc_profile"] = icc_profile
if exif:
self.info["exif"] = exif
self.size = width, height
self.fp = BytesIO(data)

View File

@ -47,7 +47,7 @@ TEST_misc = False
#######################################################################
def outputImage(im, funcName = None):
# save or display the image, depending on value of SHOW_IMAGES
if SHOW == True:
if SHOW:
im.show()
else:
im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName))
@ -57,7 +57,7 @@ def outputImage(im, funcName = None):
# The tests themselves
#######################################################################
if TEST_error_catching == True:
if TEST_error_catching:
im = Image.open(IMAGE)
try:
#neither of these proifles exists (unless you make them), so we should
@ -70,7 +70,7 @@ if TEST_error_catching == True:
print("error catching test completed successfully (if you see the message \
above that we caught the error).")
if TEST_profileToProfile == True:
if TEST_profileToProfile:
# open the image file using the standard PIL function Image.open()
im = Image.open(IMAGE)
@ -84,7 +84,7 @@ if TEST_profileToProfile == True:
print("profileToProfile test completed successfully.")
if TEST_profileToProfile_inPlace == True:
if TEST_profileToProfile_inPlace:
# we'll do the same test as profileToProfile, but modify im in place
# instead of getting a new image returned to us
im = Image.open(IMAGE)
@ -94,7 +94,7 @@ if TEST_profileToProfile_inPlace == True:
outputMode = OUTMODE, inPlace = True)
# now that the image is converted, save or display it
if result == None:
if result is None:
# this is the normal result when modifying in-place
outputImage(im, "profileToProfile_inPlace")
else:
@ -103,7 +103,7 @@ if TEST_profileToProfile_inPlace == True:
print("profileToProfile in-place test completed successfully.")
if TEST_buildTransform == True:
if TEST_buildTransform:
# make a transform using the input and output profile path strings
transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \
OUTMODE)
@ -126,7 +126,7 @@ if TEST_buildTransform == True:
# Python should also do this automatically when it goes out of scope.
del(transform)
if TEST_buildTransformFromOpenProfiles == True:
if TEST_buildTransformFromOpenProfiles:
# we'll actually test a couple profile open/creation functions here too
# first, get a handle to an input profile, in this case we'll create
@ -160,7 +160,7 @@ if TEST_buildTransformFromOpenProfiles == True:
del(outputProfile)
del(transform)
if TEST_buildProofTransform == True:
if TEST_buildProofTransform:
# make a transform using the input and output and proof profile path
# strings
# images converted with this transform will simulate the appearance
@ -188,7 +188,7 @@ if TEST_buildProofTransform == True:
# Python should also do this automatically when it goes out of scope.
del(transform)
if TEST_getProfileInfo == True:
if TEST_getProfileInfo:
# get a profile handle
profile = ImageCms.getOpenProfile(INPUT_PROFILE)
@ -212,7 +212,7 @@ if TEST_getProfileInfo == True:
print("getProfileInfo test completed successfully.")
if TEST_misc == True:
if TEST_misc:
# test the versions, about, and copyright functions
print("Versions: %s" %str(ImageCms.versions()))
print("About:\n\n%s" %ImageCms.about())

View File

@ -2,11 +2,12 @@ from tester import *
from PIL import Image, EpsImagePlugin
import sys
import io
if EpsImagePlugin.gs_windows_binary == False:
# already checked. Not there.
if not EpsImagePlugin.gs_windows_binary:
# already checked. Not there.
skip()
if not sys.platform.startswith('win'):
import subprocess
try:
@ -54,6 +55,18 @@ def test_sanity():
assert_equal(image2_scale2.size, (720, 504))
assert_equal(image2_scale2.format, "EPS")
def test_file_object():
#issue 479
image1 = Image.open(file1)
with open(tempfile('temp_file.eps'), 'wb') as fh:
image1.save(fh, 'EPS')
def test_iobase_object():
#issue 479
image1 = Image.open(file1)
with io.open(tempfile('temp_iobase.eps'), 'wb') as fh:
image1.save(fh, 'EPS')
def test_render_scale1():
#We need png support for these render test
codecs = dir(Image.core)
@ -93,4 +106,3 @@ def test_render_scale2():
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

@ -2,6 +2,7 @@ from tester import *
from PIL import Image
try:
from PIL import _webp
except:
@ -10,10 +11,7 @@ except:
def test_version():
assert_no_exception(lambda: _webp.WebPDecoderVersion())
def test_good_alpha():
assert_equal(_webp.WebPDecoderBuggyAlpha(), 0)
assert_no_exception(lambda: _webp.WebPDecoderBuggyAlpha())
def test_read_rgb():
@ -66,92 +64,5 @@ 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
expect?
"""
temp_file = tempfile("temp.webp")
pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20))
pil_image.save(temp_file)
if _webp.WebPDecoderBuggyAlpha():
return
image = Image.open(temp_file)
image.load()
assert_equal(image.mode, "RGBA")
assert_equal(image.size, (10, 10))
assert_equal(image.format, "WEBP")
assert_no_exception(image.load)
assert_no_exception(image.getdata)
assert_image_similar(image, pil_image, 1.0)
if _webp.WebPDecoderBuggyAlpha():
skip("Buggy early version of webp installed, not testing transparency")
def test_read_rgba():
# Generated with `cwebp transparent.png -o transparent.webp`
file_path = "Images/transparent.webp"
image = Image.open(file_path)
assert_equal(image.mode, "RGBA")
assert_equal(image.size, (200, 150))
assert_equal(image.format, "WEBP")
assert_no_exception(lambda: image.load())
assert_no_exception(lambda: image.getdata())
orig_bytes = image.tobytes()
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)

View File

@ -0,0 +1,82 @@
from tester import *
from PIL import Image
try:
from PIL import _webp
except:
skip('webp support not installed')
if _webp.WebPDecoderBuggyAlpha():
skip("Buggy early version of webp installed, not testing transparency")
def test_read_rgba():
# Generated with `cwebp transparent.png -o transparent.webp`
file_path = "Images/transparent.webp"
image = Image.open(file_path)
assert_equal(image.mode, "RGBA")
assert_equal(image.size, (200, 150))
assert_equal(image.format, "WEBP")
assert_no_exception(lambda: image.load())
assert_no_exception(lambda: image.getdata())
orig_bytes = image.tobytes()
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)
def test_write_rgba():
"""
Can we write a RGBA mode file to webp without error. Does it have the bits we
expect?
"""
temp_file = tempfile("temp.webp")
pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20))
pil_image.save(temp_file)
if _webp.WebPDecoderBuggyAlpha():
return
image = Image.open(temp_file)
image.load()
assert_equal(image.mode, "RGBA")
assert_equal(image.size, (10, 10))
assert_equal(image.format, "WEBP")
assert_no_exception(image.load)
assert_no_exception(image.getdata)
assert_image_similar(image, pil_image, 1.0)

View File

@ -0,0 +1,33 @@
from tester import *
from PIL import Image
try:
from PIL import _webp
except:
skip('webp support not installed')
if (_webp.WebPDecoderVersion() < 0x0200):
skip('lossless not included')
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"))

View File

@ -82,3 +82,20 @@ def test_write_icc_metadata():
assert_true(webp_icc_profile)
if webp_icc_profile:
assert_equal(webp_icc_profile, expected_icc_profile, "Webp ICC didn't match")
def test_read_no_exif():
file_path = "Tests/images/flower.jpg"
image = Image.open(file_path)
expected_exif = image.info['exif']
buffer = BytesIO()
image.save(buffer, "webp")
buffer.seek(0)
webp_image = Image.open(buffer)
assert_false(webp_image._getexif())

View File

@ -43,10 +43,12 @@ def test_parser():
assert_image_equal(*roundtrip("PPM"))
assert_image_equal(*roundtrip("TIFF"))
assert_image_equal(*roundtrip("XBM"))
#assert_image_equal(*roundtrip("EPS")) #no eps_decoder
assert_image_equal(*roundtrip("TGA"))
assert_image_equal(*roundtrip("PCX"))
im1, im2 = roundtrip("EPS")
assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB
if "jpeg_encoder" in codecs:
im1, im2 = roundtrip("JPEG") # lossy compression
assert_image(im1, im2.mode, im2.size)

View File

@ -18,7 +18,7 @@ result = []
class Worker(threading.Thread):
def run(self):
while 1:
while True:
im = queue.get()
if im is None:
queue.task_done()

10
_webp.c
View File

@ -34,18 +34,24 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
if (size < width * height * 4){
Py_RETURN_NONE;
}
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
if (lossless) {
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
} else {
} else
#endif
{
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 WEBP_ENCODER_ABI_VERSION >= 0x0100
if (lossless) {
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
} else {
} else
#endif
{
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
}
} else {

View File

@ -3,7 +3,7 @@ Porting existing PIL-based code to Pillow
Pillow is a functional drop-in replacement for the Python Imaging Library. To
run your existing PIL-compatible code with Pillow, it needs to be modified to
import the ``Imaging`` module from the ``PIL`` namespace *instead* of the
import the ``Image`` module from the ``PIL`` namespace *instead* of the
global namespace. Change this::
import Image
@ -16,7 +16,7 @@ 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
The image plugin loading mechanism 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

@ -235,6 +235,29 @@ class pil_build_ext(build_ext):
elif platform_ in ["i386", "i686", "32bit"]:
_add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
break
elif platform_ in ["aarch64"]:
_add_directory(library_dirs, "/usr/lib64")
_add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu")
break
elif platform_ in ["arm", "armv7l"]:
_add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi")
break
elif platform_ in ["ppc64"]:
_add_directory(library_dirs, "/usr/lib64")
_add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu")
_add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu")
break
elif platform_ in ["ppc"]:
_add_directory(library_dirs, "/usr/lib/ppc-linux-gnu")
_add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu")
break
elif platform_ in ["s390x"]:
_add_directory(library_dirs, "/usr/lib64")
_add_directory(library_dirs, "/usr/lib/s390x-linux-gnu")
break
elif platform_ in ["s390"]:
_add_directory(library_dirs, "/usr/lib/s390-linux-gnu")
break
else:
raise ValueError(
"Unable to identify Linux platform: `%s`" % platform_)