- flake8 formatting fixes

- webp => WebP doc and comment changes
This commit is contained in:
Jason Douglas 2017-10-01 15:23:18 -07:00
parent c9258d6cdb
commit 28bec69e98
8 changed files with 52 additions and 44 deletions

View File

@ -44,6 +44,7 @@ class WebPImageFile(ImageFile.ImageFile):
self.size = width, height self.size = width, height
self.fp = BytesIO(data) self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
self._n_frames = 1
return return
# Use the newer AnimDecoder API to parse the (possibly) animated file, # Use the newer AnimDecoder API to parse the (possibly) animated file,
@ -51,7 +52,8 @@ class WebPImageFile(ImageFile.ImageFile):
self._decoder = _webp.WebPAnimDecoder(self.fp.read()) self._decoder = _webp.WebPAnimDecoder(self.fp.read())
# Get info from decoder # Get info from decoder
width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() width, height, loop_count, bgcolor, frame_count, mode = \
self._decoder.get_info()
self.size = width, height self.size = width, height
self.info["loop"] = loop_count self.info["loop"] = loop_count
bg_a, bg_r, bg_g, bg_b = \ bg_a, bg_r, bg_g, bg_b = \
@ -85,14 +87,10 @@ class WebPImageFile(ImageFile.ImageFile):
@property @property
def n_frames(self): def n_frames(self):
if not _webp.HAVE_WEBPANIM:
return 1
return self._n_frames return self._n_frames
@property @property
def is_animated(self): def is_animated(self):
if not _webp.HAVE_WEBPANIM:
return False
return self._n_frames > 1 return self._n_frames > 1
def seek(self, frame): def seek(self, frame):
@ -122,7 +120,7 @@ class WebPImageFile(ImageFile.ImageFile):
# Check if an error occurred # Check if an error occurred
if ret is None: if ret is None:
self._reset() # Reset just to be safe self._reset() # Reset just to be safe
self.seek(0) self.seek(0)
raise EOFError("failed to decode next frame in WebP file") raise EOFError("failed to decode next frame in WebP file")
@ -130,20 +128,18 @@ class WebPImageFile(ImageFile.ImageFile):
data, timestamp = ret data, timestamp = ret
duration = timestamp - self.__timestamp duration = timestamp - self.__timestamp
self.__timestamp = timestamp self.__timestamp = timestamp
timestamp -= duration # libwebp gives frame end, adjust to start of frame
# libwebp gives frame end, adjust to start of frame
timestamp -= duration
return data, timestamp, duration return data, timestamp, duration
def _seek(self, frame): def _seek(self, frame):
if self.__physical_frame == frame: if self.__physical_frame == frame:
return # Nothing to do return # Nothing to do
if frame < self.__physical_frame: if frame < self.__physical_frame:
# Rewind to beginning self._reset() # Rewind to beginning
self._reset()
# Advance to the requested frame
while self.__physical_frame < frame: while self.__physical_frame < frame:
self._get_next() self._get_next() # Advance to the requested frame
def load(self): def load(self):
if _webp.HAVE_WEBPANIM: if _webp.HAVE_WEBPANIM:
@ -168,6 +164,7 @@ class WebPImageFile(ImageFile.ImageFile):
return self.__logical_frame return self.__logical_frame
def _save_all(im, fp, filename): def _save_all(im, fp, filename):
encoderinfo = im.encoderinfo.copy() encoderinfo = im.encoderinfo.copy()
append_images = encoderinfo.get("append_images", []) append_images = encoderinfo.get("append_images", [])
@ -207,9 +204,12 @@ def _save_all(im, fp, filename):
# Validate background color # Validate background color
if (not isinstance(background, (list, tuple)) or len(background) != 4 or if (not isinstance(background, (list, tuple)) or len(background) != 4 or
not all(v >= 0 and v < 256 for v in background)): not all(v >= 0 and v < 256 for v in background)):
raise IOError("Background color is not an RGBA tuple clamped to (0-255): %s" % str(background)) raise IOError("Background color is not an RGBA tuple clamped "
"to (0-255): %s" % str(background))
# Convert to packed uint
bg_r, bg_g, bg_b, bg_a = background bg_r, bg_g, bg_b, bg_a = background
background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) # Convert to packed uint background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0)
# Setup the WebP animation encoder # Setup the WebP animation encoder
enc = _webp.WebPAnimEncoder( enc = _webp.WebPAnimEncoder(
@ -240,7 +240,7 @@ def _save_all(im, fp, filename):
# Make sure image mode is supported # Make sure image mode is supported
frame = ims frame = ims
if not ims.mode in _VALID_WEBP_MODES: if ims.mode not in _VALID_WEBP_MODES:
alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode() alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()
frame = ims.convert('RGBA' if alpha else 'RGBX') frame = ims.convert('RGBA' if alpha else 'RGBX')
@ -256,7 +256,10 @@ def _save_all(im, fp, filename):
) )
# Update timestamp and frame index # Update timestamp and frame index
timestamp += duration[frame_idx] if isinstance(duration, (list, tuple)) else duration if isinstance(duration, (list, tuple)):
timestamp += duration[frame_idx]
else:
timestamp += duration
frame_idx += 1 frame_idx += 1
finally: finally:
@ -272,10 +275,11 @@ def _save_all(im, fp, filename):
# Get the final output from the encoder # Get the final output from the encoder
data = enc.assemble(icc_profile, exif, xmp) data = enc.assemble(icc_profile, exif, xmp)
if data is None: if data is None:
raise IOError("cannot write file as WEBP (encoder returned None)") raise IOError("cannot write file as WebP (encoder returned None)")
fp.write(data) fp.write(data)
def _save(im, fp, filename): def _save(im, fp, filename):
lossless = im.encoderinfo.get("lossless", False) lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80) quality = im.encoderinfo.get("quality", 80)
@ -299,7 +303,7 @@ def _save(im, fp, filename):
xmp xmp
) )
if data is None: if data is None:
raise IOError("cannot write file as WEBP (encoder returned None)") raise IOError("cannot write file as WebP (encoder returned None)")
fp.write(data) fp.write(data)

View File

@ -8,6 +8,7 @@ try:
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestFileWebp(PillowTestCase): class TestFileWebp(PillowTestCase):
def setUp(self): def setUp(self):
@ -24,7 +25,7 @@ class TestFileWebp(PillowTestCase):
def test_read_rgb(self): def test_read_rgb(self):
""" """
Can we read a RGB mode webp file without error. Can we read a RGB mode WebP file without error?
Does it have the bits we expect? Does it have the bits we expect?
""" """
@ -39,7 +40,8 @@ class TestFileWebp(PillowTestCase):
# generated with: # generated with:
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
target = Image.open('Tests/images/hopper_webp_bits.ppm').convert(self.rgb_mode) target = Image.open('Tests/images/hopper_webp_bits.ppm')
target = target.convert(self.rgb_mode)
self.assert_image_similar(image, target, 20.0) self.assert_image_similar(image, target, 20.0)
def test_write_rgb(self): def test_write_rgb(self):
@ -77,7 +79,7 @@ class TestFileWebp(PillowTestCase):
def test_write_unsupported_mode_L(self): def test_write_unsupported_mode_L(self):
""" """
Saving a black-and-white file to webp format should work, and be Saving a black-and-white file to WebP format should work, and be
similar to the original file. similar to the original file.
""" """
@ -97,7 +99,7 @@ class TestFileWebp(PillowTestCase):
def test_write_unsupported_mode_P(self): def test_write_unsupported_mode_P(self):
""" """
Saving a palette-based file to webp format should work, and be Saving a palette-based file to WebP format should work, and be
similar to the original file. similar to the original file.
""" """

View File

@ -23,7 +23,7 @@ class TestFileWebpAlpha(PillowTestCase):
def test_read_rgba(self): def test_read_rgba(self):
""" """
Can we read a RGBA mode file without error. Can we read an RGBA mode file without error?
Does it have the bits we expect? Does it have the bits we expect?
""" """
@ -44,8 +44,8 @@ class TestFileWebpAlpha(PillowTestCase):
def test_write_lossless_rgb(self): def test_write_lossless_rgb(self):
""" """
Can we write a RGBA mode file with lossless compression without error. Can we write an RGBA mode file with lossless compression without
Does it have the bits we expect? error? Does it have the bits we expect?
""" """
temp_file = self.tempfile("temp.webp") temp_file = self.tempfile("temp.webp")
@ -102,7 +102,7 @@ class TestFileWebpAlpha(PillowTestCase):
def test_write_unsupported_mode_PA(self): def test_write_unsupported_mode_PA(self):
""" """
Saving a palette-based file with transparency to webp format Saving a palette-based file with transparency to WebP format
should work, and be similar to the original file. should work, and be similar to the original file.
""" """

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image
@ -8,6 +8,7 @@ try:
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestFileWebpAnimation(PillowTestCase): class TestFileWebpAnimation(PillowTestCase):
def setUp(self): def setUp(self):
@ -21,7 +22,7 @@ class TestFileWebpAnimation(PillowTestCase):
def test_n_frames(self): def test_n_frames(self):
""" """
Ensure that webp format sets n_frames and is_animated Ensure that WebP format sets n_frames and is_animated
attributes correctly. attributes correctly.
""" """
@ -68,16 +69,15 @@ class TestFileWebpAnimation(PillowTestCase):
temp_file2 = self.tempfile("temp.png") temp_file2 = self.tempfile("temp.png")
frame1 = Image.open('Tests/images/anim_frame1.webp') frame1 = Image.open('Tests/images/anim_frame1.webp')
frame2 = Image.open('Tests/images/anim_frame2.webp') frame2 = Image.open('Tests/images/anim_frame2.webp')
frame1.save(temp_file, save_all=True, append_images=[frame2], lossless=True) frame1.save(temp_file,
save_all=True, append_images=[frame2], lossless=True)
im = Image.open(temp_file) im = Image.open(temp_file)
self.assertEqual(im.n_frames, 2) self.assertEqual(im.n_frames, 2)
print("Test File: " + temp_file)
# Compare first frame to original # Compare first frame to original
im.load() im.load()
im.save(temp_file2) im.save(temp_file2)
print("Frame 1: " + temp_file2)
self.assert_image_equal(im, frame1.convert("RGBA")) self.assert_image_equal(im, frame1.convert("RGBA"))
# Compare second frame to original # Compare second frame to original
@ -103,7 +103,7 @@ class TestFileWebpAnimation(PillowTestCase):
self.assertEqual(im.n_frames, 5) self.assertEqual(im.n_frames, 5)
self.assertTrue(im.is_animated) self.assertTrue(im.is_animated)
# Double-check that timestamps and durations match original values specified # Check that timestamps and durations match original values specified
ts = 0 ts = 0
for frame in range(im.n_frames): for frame in range(im.n_frames):
im.seek(frame) im.seek(frame)
@ -114,7 +114,7 @@ class TestFileWebpAnimation(PillowTestCase):
def test_seeking(self): def test_seeking(self):
""" """
Create an animated webp file, and then try seeking through Create an animated WebP file, and then try seeking through
frames in reverse-order, verifying the timestamps and durations frames in reverse-order, verifying the timestamps and durations
are correct. are correct.
""" """
@ -131,7 +131,7 @@ class TestFileWebpAnimation(PillowTestCase):
self.assertEqual(im.n_frames, 5) self.assertEqual(im.n_frames, 5)
self.assertTrue(im.is_animated) self.assertTrue(im.is_animated)
# Traverse frames in reverse order, double-check timestamps and duration # Traverse frames in reverse, checking timestamps and durations
ts = dur * (im.n_frames-1) ts = dur * (im.n_frames-1)
for frame in reversed(range(im.n_frames)): for frame in reversed(range(im.n_frames)):
im.seek(frame) im.seek(frame)

View File

@ -8,6 +8,7 @@ try:
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestFileWebpLossless(PillowTestCase): class TestFileWebpLossless(PillowTestCase):
def setUp(self): def setUp(self):

View File

@ -8,6 +8,7 @@ try:
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestFileWebpMetadata(PillowTestCase): class TestFileWebpMetadata(PillowTestCase):
def setUp(self): def setUp(self):

View File

@ -12,7 +12,7 @@
/* /*
* Check the versions from mux.h and demux.h, to ensure the WebPAnimEncoder and * Check the versions from mux.h and demux.h, to ensure the WebPAnimEncoder and
* WebPAnimDecoder API's are present (initial support was added in 0.5.0). The * WebPAnimDecoder APIs are present (initial support was added in 0.5.0). The
* very early versions added had some significant differences, so we require * very early versions added had some significant differences, so we require
* later versions, before enabling animation support. * later versions, before enabling animation support.
*/ */
@ -591,7 +591,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
} }
#else #else
{ {
/* I want to truncate the *_size items that get passed into webp /* 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) data in the upper byte. (Not sure why, it shouldn't have been there)
*/ */

View File

@ -658,7 +658,7 @@ format are currently undocumented.
The :py:meth:`~PIL.Image.Image.save` method supports the following options: The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**lossless** **lossless**
If present and true, instructs the WEBP writer to use lossless compression. If present and true, instructs the WebP writer to use lossless compression.
**quality** **quality**
Integer, 1-100, Defaults to 80. For lossy, 0 gives the smallest Integer, 1-100, Defaults to 80. For lossy, 0 gives the smallest
@ -671,23 +671,23 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**icc_procfile** **icc_procfile**
The ICC Profile to include in the saved file. Only supported if The ICC Profile to include in the saved file. Only supported if
the system webp library was built with webpmux support. the system WebP library was built with webpmux support.
**exif** **exif**
The exif data to include in the saved file. Only supported if The exif data to include in the saved file. Only supported if
the system webp library was built with webpmux support. the system WebP library was built with webpmux support.
Saving sequences Saving sequences
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
.. note:: .. note::
Support for animated WebP files will only be enabled if the system webp Support for animated WebP files will only be enabled if the system WebP
library is v0.5.0 or later. You can check webp animation support at library is v0.5.0 or later. You can check webp animation support at
runtime by inspecting the `_webp.HAVE_WEBPANIM` module flag. runtime by calling `features.check("webp_anim")`.
When calling :py:meth:`~PIL.Image.Image.save`, the following options When calling :py:meth:`~PIL.Image.Image.save`, the following options
are available when the save_all argument is present and true. are available when the `save_all` argument is present and true.
**append_images** **append_images**
A list of images to append as additional frames. Each of the A list of images to append as additional frames. Each of the