mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-12 18:26:17 +03:00
merge from master
This commit is contained in:
commit
288a563d1a
|
@ -14,6 +14,7 @@ install:
|
||||||
- "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev"
|
- "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev"
|
||||||
- "pip install cffi"
|
- "pip install cffi"
|
||||||
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- python setup.py clean
|
- python setup.py clean
|
||||||
- python setup.py build_ext --inplace
|
- python setup.py build_ext --inplace
|
||||||
|
|
12
CHANGES.rst
12
CHANGES.rst
|
@ -4,6 +4,18 @@ Changelog (Pillow)
|
||||||
2.4.0 (2014-04-01)
|
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
|
- Correctly quote file names for WindowsViewer command
|
||||||
[cgohlke]
|
[cgohlke]
|
||||||
|
|
||||||
|
|
|
@ -67,43 +67,48 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
|
|
||||||
import tempfile, os, subprocess
|
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
|
# Build ghostscript command
|
||||||
command = ["gs",
|
command = ["gs",
|
||||||
"-q", # quite mode
|
"-q", # quiet 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)
|
"-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" % 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 not None:
|
||||||
if gs_windows_binary is False:
|
if not gs_windows_binary:
|
||||||
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
|
||||||
|
|
||||||
# push data through ghostscript
|
# push data through ghostscript
|
||||||
try:
|
try:
|
||||||
gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
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()
|
gs.stdin.close()
|
||||||
status = gs.wait()
|
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(outfile)
|
||||||
finally:
|
finally:
|
||||||
try: os.unlink(file)
|
try:
|
||||||
|
os.unlink(outfile)
|
||||||
|
os.unlink(infile)
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
return im
|
return im
|
||||||
|
@ -320,6 +325,11 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
self.size = self.im.size
|
self.size = self.im.size
|
||||||
self.tile = []
|
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
|
pass
|
||||||
|
|
||||||
base_fp = fp
|
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:
|
if eps:
|
||||||
#
|
#
|
||||||
|
|
|
@ -820,6 +820,8 @@ class Image:
|
||||||
|
|
||||||
def draft(self, mode, size):
|
def draft(self, mode, size):
|
||||||
"""
|
"""
|
||||||
|
NYI
|
||||||
|
|
||||||
Configures the image file loader so it returns a version of the
|
Configures the image file loader so it returns a version of the
|
||||||
image that as closely as possible matches the given mode and
|
image that as closely as possible matches the given mode and
|
||||||
size. For example, you can use this method to convert a color
|
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:
|
:param size: The requested size in pixels, as a 2-tuple:
|
||||||
(width, height).
|
(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),
|
one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
|
||||||
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
||||||
environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline
|
environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline
|
||||||
|
|
|
@ -229,7 +229,7 @@ class ImageFile(Image.Image):
|
||||||
|
|
||||||
self.fp = None # might be shared
|
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
|
# still raised if decoder fails to return anything
|
||||||
raise_ioerror(e)
|
raise_ioerror(e)
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ class OleFileIO:
|
||||||
if entry[1:2] == "Image":
|
if entry[1:2] == "Image":
|
||||||
fin = ole.openstream(entry)
|
fin = ole.openstream(entry)
|
||||||
fout = open(entry[0:1], "wb")
|
fout = open(entry[0:1], "wb")
|
||||||
while 1:
|
while True:
|
||||||
s = fin.read(8192)
|
s = fin.read(8192)
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
|
|
|
@ -182,7 +182,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
# given a list of filenames, return a list of images
|
# given a list of filenames, return a list of images
|
||||||
def loadImageSeries(filelist=None):
|
def loadImageSeries(filelist=None):
|
||||||
" create a list of Image.images for use in montage "
|
" 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
|
return
|
||||||
|
|
||||||
imglist = []
|
imglist = []
|
||||||
|
|
|
@ -1141,7 +1141,7 @@ def _save(im, fp, filename):
|
||||||
# print (im.mode, compression, a, im.encoderconfig)
|
# print (im.mode, compression, a, im.encoderconfig)
|
||||||
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
|
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
|
||||||
e.setimage(im.im, (0,0)+im.size)
|
e.setimage(im.im, (0,0)+im.size)
|
||||||
while 1:
|
while True:
|
||||||
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
|
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
|
||||||
if not _fp:
|
if not _fp:
|
||||||
fp.write(d)
|
fp.write(d)
|
||||||
|
|
|
@ -32,8 +32,10 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
def _open(self):
|
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())
|
||||||
|
|
||||||
self.info["icc_profile"] = icc_profile
|
if icc_profile:
|
||||||
self.info["exif"] = exif
|
self.info["icc_profile"] = icc_profile
|
||||||
|
if exif:
|
||||||
|
self.info["exif"] = exif
|
||||||
|
|
||||||
self.size = width, height
|
self.size = width, height
|
||||||
self.fp = BytesIO(data)
|
self.fp = BytesIO(data)
|
||||||
|
|
|
@ -47,7 +47,7 @@ TEST_misc = False
|
||||||
#######################################################################
|
#######################################################################
|
||||||
def outputImage(im, funcName = None):
|
def outputImage(im, funcName = None):
|
||||||
# save or display the image, depending on value of SHOW_IMAGES
|
# save or display the image, depending on value of SHOW_IMAGES
|
||||||
if SHOW == True:
|
if SHOW:
|
||||||
im.show()
|
im.show()
|
||||||
else:
|
else:
|
||||||
im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName))
|
im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName))
|
||||||
|
@ -57,7 +57,7 @@ def outputImage(im, funcName = None):
|
||||||
# The tests themselves
|
# The tests themselves
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
if TEST_error_catching == True:
|
if TEST_error_catching:
|
||||||
im = Image.open(IMAGE)
|
im = Image.open(IMAGE)
|
||||||
try:
|
try:
|
||||||
#neither of these proifles exists (unless you make them), so we should
|
#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 \
|
print("error catching test completed successfully (if you see the message \
|
||||||
above that we caught the error).")
|
above that we caught the error).")
|
||||||
|
|
||||||
if TEST_profileToProfile == True:
|
if TEST_profileToProfile:
|
||||||
# open the image file using the standard PIL function Image.open()
|
# open the image file using the standard PIL function Image.open()
|
||||||
im = Image.open(IMAGE)
|
im = Image.open(IMAGE)
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ if TEST_profileToProfile == True:
|
||||||
|
|
||||||
print("profileToProfile test completed successfully.")
|
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
|
# we'll do the same test as profileToProfile, but modify im in place
|
||||||
# instead of getting a new image returned to us
|
# instead of getting a new image returned to us
|
||||||
im = Image.open(IMAGE)
|
im = Image.open(IMAGE)
|
||||||
|
@ -94,7 +94,7 @@ if TEST_profileToProfile_inPlace == True:
|
||||||
outputMode = OUTMODE, inPlace = True)
|
outputMode = OUTMODE, inPlace = True)
|
||||||
|
|
||||||
# now that the image is converted, save or display it
|
# 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
|
# this is the normal result when modifying in-place
|
||||||
outputImage(im, "profileToProfile_inPlace")
|
outputImage(im, "profileToProfile_inPlace")
|
||||||
else:
|
else:
|
||||||
|
@ -103,7 +103,7 @@ if TEST_profileToProfile_inPlace == True:
|
||||||
|
|
||||||
print("profileToProfile in-place test completed successfully.")
|
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
|
# make a transform using the input and output profile path strings
|
||||||
transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \
|
transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \
|
||||||
OUTMODE)
|
OUTMODE)
|
||||||
|
@ -126,7 +126,7 @@ if TEST_buildTransform == True:
|
||||||
# Python should also do this automatically when it goes out of scope.
|
# Python should also do this automatically when it goes out of scope.
|
||||||
del(transform)
|
del(transform)
|
||||||
|
|
||||||
if TEST_buildTransformFromOpenProfiles == True:
|
if TEST_buildTransformFromOpenProfiles:
|
||||||
# we'll actually test a couple profile open/creation functions here too
|
# 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
|
# 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(outputProfile)
|
||||||
del(transform)
|
del(transform)
|
||||||
|
|
||||||
if TEST_buildProofTransform == True:
|
if TEST_buildProofTransform:
|
||||||
# make a transform using the input and output and proof profile path
|
# make a transform using the input and output and proof profile path
|
||||||
# strings
|
# strings
|
||||||
# images converted with this transform will simulate the appearance
|
# 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.
|
# Python should also do this automatically when it goes out of scope.
|
||||||
del(transform)
|
del(transform)
|
||||||
|
|
||||||
if TEST_getProfileInfo == True:
|
if TEST_getProfileInfo:
|
||||||
# get a profile handle
|
# get a profile handle
|
||||||
profile = ImageCms.getOpenProfile(INPUT_PROFILE)
|
profile = ImageCms.getOpenProfile(INPUT_PROFILE)
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ if TEST_getProfileInfo == True:
|
||||||
|
|
||||||
print("getProfileInfo test completed successfully.")
|
print("getProfileInfo test completed successfully.")
|
||||||
|
|
||||||
if TEST_misc == True:
|
if TEST_misc:
|
||||||
# test the versions, about, and copyright functions
|
# test the versions, about, and copyright functions
|
||||||
print("Versions: %s" %str(ImageCms.versions()))
|
print("Versions: %s" %str(ImageCms.versions()))
|
||||||
print("About:\n\n%s" %ImageCms.about())
|
print("About:\n\n%s" %ImageCms.about())
|
||||||
|
|
|
@ -2,11 +2,12 @@ from tester import *
|
||||||
|
|
||||||
from PIL import Image, EpsImagePlugin
|
from PIL import Image, EpsImagePlugin
|
||||||
import sys
|
import sys
|
||||||
|
import io
|
||||||
|
|
||||||
if EpsImagePlugin.gs_windows_binary == False:
|
if not EpsImagePlugin.gs_windows_binary:
|
||||||
# already checked. Not there.
|
# already checked. Not there.
|
||||||
skip()
|
skip()
|
||||||
|
|
||||||
if not sys.platform.startswith('win'):
|
if not sys.platform.startswith('win'):
|
||||||
import subprocess
|
import subprocess
|
||||||
try:
|
try:
|
||||||
|
@ -54,6 +55,18 @@ def test_sanity():
|
||||||
assert_equal(image2_scale2.size, (720, 504))
|
assert_equal(image2_scale2.size, (720, 504))
|
||||||
assert_equal(image2_scale2.format, "EPS")
|
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():
|
def test_render_scale1():
|
||||||
#We need png support for these render test
|
#We need png support for these render test
|
||||||
codecs = dir(Image.core)
|
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 = Image.open(file2_compare_scale2).convert("RGB")
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare.load()
|
||||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from tester import *
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
except:
|
except:
|
||||||
|
@ -10,10 +11,7 @@ except:
|
||||||
|
|
||||||
def test_version():
|
def test_version():
|
||||||
assert_no_exception(lambda: _webp.WebPDecoderVersion())
|
assert_no_exception(lambda: _webp.WebPDecoderVersion())
|
||||||
|
assert_no_exception(lambda: _webp.WebPDecoderBuggyAlpha())
|
||||||
def test_good_alpha():
|
|
||||||
assert_equal(_webp.WebPDecoderBuggyAlpha(), 0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_read_rgb():
|
def test_read_rgb():
|
||||||
|
|
||||||
|
@ -66,92 +64,5 @@ 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():
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
82
Tests/test_file_webp_alpha.py
Normal file
82
Tests/test_file_webp_alpha.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
33
Tests/test_file_webp_lossless.py
Normal file
33
Tests/test_file_webp_lossless.py
Normal 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"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -82,3 +82,20 @@ def test_write_icc_metadata():
|
||||||
assert_true(webp_icc_profile)
|
assert_true(webp_icc_profile)
|
||||||
if webp_icc_profile:
|
if webp_icc_profile:
|
||||||
assert_equal(webp_icc_profile, expected_icc_profile, "Webp ICC didn't match")
|
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())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,12 @@ def test_parser():
|
||||||
assert_image_equal(*roundtrip("PPM"))
|
assert_image_equal(*roundtrip("PPM"))
|
||||||
assert_image_equal(*roundtrip("TIFF"))
|
assert_image_equal(*roundtrip("TIFF"))
|
||||||
assert_image_equal(*roundtrip("XBM"))
|
assert_image_equal(*roundtrip("XBM"))
|
||||||
#assert_image_equal(*roundtrip("EPS")) #no eps_decoder
|
|
||||||
assert_image_equal(*roundtrip("TGA"))
|
assert_image_equal(*roundtrip("TGA"))
|
||||||
assert_image_equal(*roundtrip("PCX"))
|
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:
|
if "jpeg_encoder" in codecs:
|
||||||
im1, im2 = roundtrip("JPEG") # lossy compression
|
im1, im2 = roundtrip("JPEG") # lossy compression
|
||||||
assert_image(im1, im2.mode, im2.size)
|
assert_image(im1, im2.mode, im2.size)
|
||||||
|
|
|
@ -18,7 +18,7 @@ result = []
|
||||||
|
|
||||||
class Worker(threading.Thread):
|
class Worker(threading.Thread):
|
||||||
def run(self):
|
def run(self):
|
||||||
while 1:
|
while True:
|
||||||
im = queue.get()
|
im = queue.get()
|
||||||
if im is None:
|
if im is None:
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
|
|
10
_webp.c
10
_webp.c
|
@ -34,18 +34,24 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
|
||||||
if (size < width * height * 4){
|
if (size < width * height * 4){
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
|
||||||
if (lossless) {
|
if (lossless) {
|
||||||
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
|
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
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 WEBP_ENCODER_ABI_VERSION >= 0x0100
|
||||||
if (lossless) {
|
if (lossless) {
|
||||||
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
|
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
|
ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,7 +3,7 @@ Porting existing PIL-based code to Pillow
|
||||||
|
|
||||||
Pillow is a functional drop-in replacement for the Python Imaging Library. To
|
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
|
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::
|
global namespace. Change this::
|
||||||
|
|
||||||
import Image
|
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
|
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
|
automatically imports any file in the Python path with a name ending
|
||||||
in :file:`ImagePlugin.py`. You will need to import your image plugin
|
in :file:`ImagePlugin.py`. You will need to import your image plugin
|
||||||
manually.
|
manually.
|
||||||
|
|
23
setup.py
23
setup.py
|
@ -235,6 +235,29 @@ class pil_build_ext(build_ext):
|
||||||
elif platform_ in ["i386", "i686", "32bit"]:
|
elif platform_ in ["i386", "i686", "32bit"]:
|
||||||
_add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
|
_add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
|
||||||
break
|
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:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Unable to identify Linux platform: `%s`" % platform_)
|
"Unable to identify Linux platform: `%s`" % platform_)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user