Merge branch 'main' into closed

This commit is contained in:
Andrew Murray 2023-03-13 08:00:11 +11:00 committed by GitHub
commit a2290358d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 317 additions and 92 deletions

View File

@ -5,6 +5,18 @@ Changelog (Pillow)
9.5.0 (unreleased) 9.5.0 (unreleased)
------------------ ------------------
- Close OleFileIO instance when closing or exiting FPX or MIC #7005
[radarhere]
- Added __int__ to IFDRational for Python >= 3.11 #6998
[radarhere]
- Added memoryview support to Dib.frombytes() #6988
[radarhere, nulano]
- Close file pointer copy in the libtiff encoder if still open #6986
[fcarron, radarhere]
- Raise an error if ImageDraw co-ordinates are incorrectly ordered #6978 - Raise an error if ImageDraw co-ordinates are incorrectly ordered #6978
[radarhere] [radarhere]

View File

@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
HAS_UPLOADER = False HAS_UPLOADER = False
if os.environ.get("SHOW_ERRORS", None): if os.environ.get("SHOW_ERRORS"):
# local img.show for errors. # local img.show for errors.
HAS_UPLOADER = True HAS_UPLOADER = True
@ -271,7 +271,7 @@ def netpbm_available():
def magick_command(): def magick_command():
if sys.platform == "win32": if sys.platform == "win32":
magickhome = os.environ.get("MAGICK_HOME", "") magickhome = os.environ.get("MAGICK_HOME")
if magickhome: if magickhome:
imagemagick = [os.path.join(magickhome, "convert.exe")] imagemagick = [os.path.join(magickhome, "convert.exe")]
graphicsmagick = [os.path.join(magickhome, "gm.exe"), "convert"] graphicsmagick = [os.path.join(magickhome, "gm.exe"), "convert"]

BIN
Tests/images/hopper.qoi Normal file

Binary file not shown.

BIN
Tests/images/pil123rgba.qoi Normal file

Binary file not shown.

View File

@ -56,6 +56,7 @@ def test_handler(tmp_path):
def load(self, im): def load(self, im):
self.loaded = True self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))
def save(self, im, fp, filename): def save(self, im, fp, filename):

View File

@ -60,6 +60,7 @@ def test_stub_deprecated():
def load(self, im): def load(self, im):
self.loaded = True self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))
handler = Handler() handler = Handler()

View File

@ -18,6 +18,16 @@ def test_sanity():
assert_image_equal_tofile(im, "Tests/images/input_bw_one_band.png") assert_image_equal_tofile(im, "Tests/images/input_bw_one_band.png")
def test_close():
with Image.open("Tests/images/input_bw_one_band.fpx") as im:
pass
assert im.ole.fp.closed
im = Image.open("Tests/images/input_bw_one_band.fpx")
im.close()
assert im.ole.fp.closed
def test_invalid_file(): def test_invalid_file():
# Test an invalid OLE file # Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"

View File

@ -56,6 +56,7 @@ def test_handler(tmp_path):
def load(self, im): def load(self, im):
self.loaded = True self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))
def save(self, im, fp, filename): def save(self, im, fp, filename):

View File

@ -57,6 +57,7 @@ def test_handler(tmp_path):
def load(self, im): def load(self, im):
self.loaded = True self.loaded = True
im.fp.close()
return Image.new("RGB", (1, 1)) return Image.new("RGB", (1, 1))
def save(self, im, fp, filename): def save(self, im, fp, filename):

View File

@ -984,6 +984,36 @@ class TestFileLibTiff(LibTiffTestCase):
) as im: ) as im:
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png") assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
@pytest.mark.parametrize(
"file_name, mode, size, tile",
[
(
"tiff_wrong_bits_per_sample.tiff",
"RGBA",
(52, 53),
[("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_2.tiff",
"RGB",
(16, 16),
[("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_3.tiff",
"RGBA",
(512, 256),
[("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))],
),
],
)
def test_wrong_bits_per_sample(self, file_name, mode, size, tile):
with Image.open("Tests/images/" + file_name) as im:
assert im.mode == mode
assert im.size == size
assert im.tile == tile
im.load()
def test_no_rows_per_strip(self): def test_no_rows_per_strip(self):
# This image does not have a RowsPerStrip TIFF tag # This image does not have a RowsPerStrip TIFF tag
infile = "Tests/images/no_rows_per_strip.tif" infile = "Tests/images/no_rows_per_strip.tif"
@ -1071,3 +1101,21 @@ class TestFileLibTiff(LibTiffTestCase):
out = str(tmp_path / "temp.tif") out = str(tmp_path / "temp.tif")
for _ in range(10000): for _ in range(10000):
im.save(out, compression="jpeg") im.save(out, compression="jpeg")
@pytest.mark.parametrize(
"path, sizes",
(
("Tests/images/hopper.tif", ()),
("Tests/images/child_ifd.tiff", (16, 8)),
("Tests/images/child_ifd_jpeg.tiff", (20,)),
),
)
def test_get_child_images(self, path, sizes):
with Image.open(path) as im:
ims = im.get_child_images()
assert len(ims) == len(sizes)
for i, im in enumerate(ims):
w = sizes[i]
expected = Image.new("RGB", (w, w), "#f00")
assert_image_similar(im, expected, 1)

View File

@ -51,6 +51,16 @@ def test_seek():
assert im.tell() == 0 assert im.tell() == 0
def test_close():
with Image.open(TEST_FILE) as im:
pass
assert im.ole.fp.closed
im = Image.open(TEST_FILE)
im.close()
assert im.ole.fp.closed
def test_invalid_file(): def test_invalid_file():
# Test an invalid OLE file # Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"

View File

@ -8,7 +8,7 @@ import pytest
from PIL import Image, PdfParser, features from PIL import Image, PdfParser, features
from .helper import hopper, mark_if_feature_version from .helper import hopper, mark_if_feature_version, skip_unless_feature
def helper_save_as_pdf(tmp_path, mode, **kwargs): def helper_save_as_pdf(tmp_path, mode, **kwargs):
@ -42,6 +42,11 @@ def test_save(tmp_path, mode):
helper_save_as_pdf(tmp_path, mode) helper_save_as_pdf(tmp_path, mode)
@skip_unless_feature("jpg_2000")
def test_save_rgba(tmp_path):
helper_save_as_pdf(tmp_path, "RGBA")
def test_monochrome(tmp_path): def test_monochrome(tmp_path):
# Arrange # Arrange
mode = "1" mode = "1"

28
Tests/test_file_qoi.py Normal file
View File

@ -0,0 +1,28 @@
import pytest
from PIL import Image, QoiImagePlugin
from .helper import assert_image_equal_tofile, assert_image_similar_tofile
def test_sanity():
with Image.open("Tests/images/hopper.qoi") as im:
assert im.mode == "RGB"
assert im.size == (128, 128)
assert im.format == "QOI"
assert_image_equal_tofile(im, "Tests/images/hopper.png")
with Image.open("Tests/images/pil123rgba.qoi") as im:
assert im.mode == "RGBA"
assert im.size == (162, 150)
assert im.format == "QOI"
assert_image_similar_tofile(im, "Tests/images/pil123rgba.png", 0.03)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
QoiImagePlugin.QoiImageFile(invalid_file)

View File

@ -84,24 +84,6 @@ class TestFileTiff:
with Image.open("Tests/images/multipage.tiff") as im: with Image.open("Tests/images/multipage.tiff") as im:
im.load() im.load()
@pytest.mark.parametrize(
"path, sizes",
(
("Tests/images/hopper.tif", ()),
("Tests/images/child_ifd.tiff", (16, 8)),
("Tests/images/child_ifd_jpeg.tiff", (20,)),
),
)
def test_get_child_images(self, path, sizes):
with Image.open(path) as im:
ims = im.get_child_images()
assert len(ims) == len(sizes)
for i, im in enumerate(ims):
w = sizes[i]
expected = Image.new("RGB", (w, w), "#f00")
assert_image_similar(im, expected, 1)
def test_mac_tiff(self): def test_mac_tiff(self):
# Read RGBa images from macOS [@PIL136] # Read RGBa images from macOS [@PIL136]
@ -118,36 +100,6 @@ class TestFileTiff:
with Image.open("Tests/images/hopper_bigtiff.tif") as im: with Image.open("Tests/images/hopper_bigtiff.tif") as im:
assert_image_equal_tofile(im, "Tests/images/hopper.tif") assert_image_equal_tofile(im, "Tests/images/hopper.tif")
@pytest.mark.parametrize(
"file_name,mode,size,tile",
[
(
"tiff_wrong_bits_per_sample.tiff",
"RGBA",
(52, 53),
[("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_2.tiff",
"RGB",
(16, 16),
[("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))],
),
(
"tiff_wrong_bits_per_sample_3.tiff",
"RGBA",
(512, 256),
[("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))],
),
],
)
def test_wrong_bits_per_sample(self, file_name, mode, size, tile):
with Image.open("Tests/images/" + file_name) as im:
assert im.mode == mode
assert im.size == size
assert im.tile == tile
im.load()
def test_set_legacy_api(self): def test_set_legacy_api(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2() ifd = TiffImagePlugin.ImageFileDirectory_v2()
with pytest.raises(Exception) as e: with pytest.raises(Exception) as e:

View File

@ -254,17 +254,6 @@ def test_p2pa_palette():
assert im_pa.getpalette() == im.getpalette() assert im_pa.getpalette() == im.getpalette()
@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode):
im = Image.new(mode, (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (0, 128, 128)
im = Image.new("LAB", (1, 1), (255, 0, 0))
converted_im = im.convert(mode)
assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255)
def test_matrix_illegal_conversion(): def test_matrix_illegal_conversion():
# Arrange # Arrange
im = hopper("CMYK") im = hopper("CMYK")

View File

@ -625,3 +625,14 @@ def test_constants_deprecation():
for name in enum.__members__: for name in enum.__members__:
with pytest.warns(DeprecationWarning): with pytest.warns(DeprecationWarning):
assert getattr(ImageCms, prefix + name) == enum[name] assert getattr(ImageCms, prefix + name) == enum[name]
@pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode):
im = Image.new(mode, (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (0, 128, 128)
im = Image.new("LAB", (1, 1), (255, 0, 0))
converted_im = im.convert(mode)
assert converted_im.getpixel((0, 0))[:3] == (0, 255, 255)

View File

@ -55,8 +55,8 @@ def test_show_without_viewers():
viewers = ImageShow._viewers viewers = ImageShow._viewers
ImageShow._viewers = [] ImageShow._viewers = []
im = hopper() with hopper() as im:
assert not ImageShow.show(im) assert not ImageShow.show(im)
ImageShow._viewers = viewers ImageShow._viewers = viewers

View File

@ -100,8 +100,11 @@ class TestImageWinDib:
# Act # Act
# Make one the same as the using tobytes()/frombytes() # Make one the same as the using tobytes()/frombytes()
test_buffer = dib1.tobytes() test_buffer = dib1.tobytes()
dib2.frombytes(test_buffer) for datatype in ("bytes", "memoryview"):
if datatype == "memoryview":
test_buffer = memoryview(test_buffer)
dib2.frombytes(test_buffer)
# Assert # Assert
# Confirm they're the same # Confirm they're the same
assert dib1.tobytes() == dib2.tobytes() assert dib1.tobytes() == dib2.tobytes()

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# install libimagequant # install libimagequant
archive=libimagequant-4.1.0 archive=libimagequant-4.1.1
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz

View File

@ -1457,8 +1457,13 @@ PDF
^^^ ^^^
Pillow can write PDF (Acrobat) images. Such images are written as binary PDF 1.4 Pillow can write PDF (Acrobat) images. Such images are written as binary PDF 1.4
files, using either JPEG or HEX encoding depending on the image mode (and files. Different encoding methods are used, depending on the image mode.
whether JPEG support is available or not).
* 1 mode images are saved using TIFF encoding, or JPEG encoding if libtiff support is
unavailable
* L, RGB and CMYK mode images use JPEG encoding
* P mode images use HEX encoding
* RGBA mode images use JPEG2000 encoding
.. _pdf-saving: .. _pdf-saving:
@ -1544,6 +1549,13 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
.. versionadded:: 5.3.0 .. versionadded:: 5.3.0
QOI
^^^
.. versionadded:: 9.5.0
Pillow identifies and reads images in Quite OK Image format.
XV Thumbnails XV Thumbnails
^^^^^^^^^^^^^ ^^^^^^^^^^^^^

View File

@ -169,7 +169,7 @@ Many of Pillow's features require external libraries:
* **libimagequant** provides improved color quantization * **libimagequant** provides improved color quantization
* Pillow has been tested with libimagequant **2.6-4.1** * Pillow has been tested with libimagequant **2.6-4.1.1**
* Libimagequant is licensed GPLv3, which is more restrictive than * Libimagequant is licensed GPLv3, which is more restrictive than
the Pillow license, therefore we will not be distributing binaries the Pillow license, therefore we will not be distributing binaries
with libimagequant support enabled. with libimagequant support enabled.

View File

@ -28,6 +28,11 @@ TODO
API Additions API Additions
============= =============
QOI file format
^^^^^^^^^^^^^^^
Pillow can now read images in Quite OK Image format.
Added ``closed`` property to images Added ``closed`` property to images
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -61,7 +66,7 @@ TODO
Other Changes Other Changes
============= =============
TODO Added support for saving PDFs in RGBA mode
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TODO Using the JPXDecode filter, PDFs can now be saved in RGBA mode.

View File

@ -242,7 +242,9 @@ def _find_include_dir(self, dirname, include):
return subdir return subdir
def _cmd_exists(cmd): def _cmd_exists(cmd: str) -> bool:
if "PATH" not in os.environ:
return False
return any( return any(
os.access(os.path.join(path, cmd), os.X_OK) os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep) for path in os.environ["PATH"].split(os.pathsep)
@ -570,9 +572,7 @@ class pil_build_ext(build_ext):
): ):
for dirname in _find_library_dirs_ldconfig(): for dirname in _find_library_dirs_ldconfig():
_add_directory(library_dirs, dirname) _add_directory(library_dirs, dirname)
if sys.platform.startswith("linux") and os.environ.get( if sys.platform.startswith("linux") and os.environ.get("ANDROID_ROOT"):
"ANDROID_ROOT", None
):
# termux support for android. # termux support for android.
# system libraries (zlib) are installed in /system/lib # system libraries (zlib) are installed in /system/lib
# headers are at $PREFIX/include # headers are at $PREFIX/include

View File

@ -235,6 +235,14 @@ class FpxImageFile(ImageFile.ImageFile):
return ImageFile.ImageFile.load(self) return ImageFile.ImageFile.load(self)
def close(self):
self.ole.close()
super().close()
def __exit__(self, *args):
self.ole.close()
super().__exit__()
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -1012,7 +1012,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
if windir: if windir:
dirs.append(os.path.join(windir, "fonts")) dirs.append(os.path.join(windir, "fonts"))
elif sys.platform in ("linux", "linux2"): elif sys.platform in ("linux", "linux2"):
lindirs = os.environ.get("XDG_DATA_DIRS", "") lindirs = os.environ.get("XDG_DATA_DIRS")
if not lindirs: if not lindirs:
# According to the freedesktop spec, XDG_DATA_DIRS should # According to the freedesktop spec, XDG_DATA_DIRS should
# default to /usr/share # default to /usr/share

View File

@ -89,6 +89,14 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
def tell(self): def tell(self):
return self.frame return self.frame
def close(self):
self.ole.close()
super().close()
def __exit__(self, *args):
self.ole.close()
super().__exit__()
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -173,6 +173,10 @@ def _save(im, fp, filename, save_all=False):
filter = "DCTDecode" filter = "DCTDecode"
colorspace = PdfParser.PdfName("DeviceRGB") colorspace = PdfParser.PdfName("DeviceRGB")
procset = "ImageC" # color images procset = "ImageC" # color images
elif im.mode == "RGBA":
filter = "JPXDecode"
colorspace = PdfParser.PdfName("DeviceRGB")
procset = "ImageC" # color images
elif im.mode == "CMYK": elif im.mode == "CMYK":
filter = "DCTDecode" filter = "DCTDecode"
colorspace = PdfParser.PdfName("DeviceCMYK") colorspace = PdfParser.PdfName("DeviceCMYK")
@ -199,6 +203,8 @@ def _save(im, fp, filename, save_all=False):
) )
elif filter == "DCTDecode": elif filter == "DCTDecode":
Image.SAVE["JPEG"](im, op, filename) Image.SAVE["JPEG"](im, op, filename)
elif filter == "JPXDecode":
Image.SAVE["JPEG2000"](im, op, filename)
elif filter == "FlateDecode": elif filter == "FlateDecode":
ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)]) ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
elif filter == "RunLengthDecode": elif filter == "RunLengthDecode":

105
src/PIL/QoiImagePlugin.py Normal file
View File

@ -0,0 +1,105 @@
#
# The Python Imaging Library.
#
# QOI support for PIL
#
# See the README file for information on usage and redistribution.
#
import os
from . import Image, ImageFile
from ._binary import i32be as i32
from ._binary import o8
def _accept(prefix):
return prefix[:4] == b"qoif"
class QoiImageFile(ImageFile.ImageFile):
format = "QOI"
format_description = "Quite OK Image"
def _open(self):
if not _accept(self.fp.read(4)):
msg = "not a QOI file"
raise SyntaxError(msg)
self._size = tuple(i32(self.fp.read(4)) for i in range(2))
channels = self.fp.read(1)[0]
self.mode = "RGB" if channels == 3 else "RGBA"
self.fp.seek(1, os.SEEK_CUR) # colorspace
self.tile = [("qoi", (0, 0) + self._size, self.fp.tell(), None)]
class QoiDecoder(ImageFile.PyDecoder):
_pulls_fd = True
def _add_to_previous_pixels(self, value):
self._previous_pixel = value
r, g, b, a = value
hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
self._previously_seen_pixels[hash_value] = value
def decode(self, buffer):
self._previously_seen_pixels = {}
self._previous_pixel = None
self._add_to_previous_pixels(b"".join(o8(i) for i in (0, 0, 0, 255)))
data = bytearray()
bands = Image.getmodebands(self.mode)
while len(data) < self.state.xsize * self.state.ysize * bands:
byte = self.fd.read(1)[0]
if byte == 0b11111110: # QOI_OP_RGB
value = self.fd.read(3) + o8(255)
elif byte == 0b11111111: # QOI_OP_RGBA
value = self.fd.read(4)
else:
op = byte >> 6
if op == 0: # QOI_OP_INDEX
op_index = byte & 0b00111111
value = self._previously_seen_pixels.get(op_index, (0, 0, 0, 0))
elif op == 1: # QOI_OP_DIFF
value = (
(self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)
% 256,
(self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)
% 256,
(self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,
)
value += (self._previous_pixel[3],)
elif op == 2: # QOI_OP_LUMA
second_byte = self.fd.read(1)[0]
diff_green = (byte & 0b00111111) - 32
diff_red = ((second_byte & 0b11110000) >> 4) - 8
diff_blue = (second_byte & 0b00001111) - 8
value = tuple(
(self._previous_pixel[i] + diff_green + diff) % 256
for i, diff in enumerate((diff_red, 0, diff_blue))
)
value += (self._previous_pixel[3],)
elif op == 3: # QOI_OP_RUN
run_length = (byte & 0b00111111) + 1
value = self._previous_pixel
if bands == 3:
value = value[:3]
data += value * run_length
continue
value = b"".join(o8(i) for i in value)
self._add_to_previous_pixels(value)
if bands == 3:
value = value[:3]
data += value
self.set_as_raw(bytes(data))
return -1, 0
Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
Image.register_decoder("qoi", QoiDecoder)
Image.register_extension(QoiImageFile.format, ".qoi")

View File

@ -425,6 +425,9 @@ class IFDRational(Rational):
__ceil__ = _delegate("__ceil__") __ceil__ = _delegate("__ceil__")
__floor__ = _delegate("__floor__") __floor__ = _delegate("__floor__")
__round__ = _delegate("__round__") __round__ = _delegate("__round__")
# Python >= 3.11
if hasattr(Fraction, "__int__"):
__int__ = _delegate("__int__")
class ImageFileDirectory_v2(MutableMapping): class ImageFileDirectory_v2(MutableMapping):

View File

@ -59,6 +59,7 @@ _plugins = [
"PngImagePlugin", "PngImagePlugin",
"PpmImagePlugin", "PpmImagePlugin",
"PsdImagePlugin", "PsdImagePlugin",
"QoiImagePlugin",
"SgiImagePlugin", "SgiImagePlugin",
"SpiderImagePlugin", "SpiderImagePlugin",
"SunImagePlugin", "SunImagePlugin",

View File

@ -955,6 +955,13 @@ addTransparencyFlagToModule(PyObject *m) {
static int static int
setup_module(PyObject *m) { setup_module(PyObject *m) {
#ifdef HAVE_WEBPANIM
/* Ready object types */
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
PyType_Ready(&WebPAnimEncoder_Type) < 0) {
return -1;
}
#endif
PyObject *d = PyModule_GetDict(m); PyObject *d = PyModule_GetDict(m);
addMuxFlagToModule(m); addMuxFlagToModule(m);
addAnimFlagToModule(m); addAnimFlagToModule(m);
@ -963,13 +970,6 @@ setup_module(PyObject *m) {
PyDict_SetItemString( PyDict_SetItemString(
d, "webpdecoder_version", PyUnicode_FromString(WebPDecoderVersion_str())); d, "webpdecoder_version", PyUnicode_FromString(WebPDecoderVersion_str()));
#ifdef HAVE_WEBPANIM
/* Ready object types */
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
PyType_Ready(&WebPAnimEncoder_Type) < 0) {
return -1;
}
#endif
return 0; return 0;
} }

View File

@ -195,20 +195,21 @@ _releasedc(ImagingDisplayObject *display, PyObject *args) {
static PyObject * static PyObject *
_frombytes(ImagingDisplayObject *display, PyObject *args) { _frombytes(ImagingDisplayObject *display, PyObject *args) {
char *ptr; Py_buffer buffer;
Py_ssize_t bytes;
if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) { if (!PyArg_ParseTuple(args, "y*:frombytes", &buffer)) {
return NULL; return NULL;
} }
if (display->dib->ysize * display->dib->linesize != bytes) { if (display->dib->ysize * display->dib->linesize != buffer.len) {
PyBuffer_Release(&buffer);
PyErr_SetString(PyExc_ValueError, "wrong size"); PyErr_SetString(PyExc_ValueError, "wrong size");
return NULL; return NULL;
} }
memcpy(display->dib->bits, ptr, bytes); memcpy(display->dib->bits, buffer.buf, buffer.len);
PyBuffer_Release(&buffer);
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }

View File

@ -487,6 +487,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
goto quick_exit; goto quick_exit;
} }
if (strcmp(im->mode, "RGBA") == 0) {
image->comps[3].alpha = 1;
}
opj_set_error_handler(codec, j2k_error, context); opj_set_error_handler(codec, j2k_error, context);
opj_set_info_handler(codec, j2k_warn, context); opj_set_info_handler(codec, j2k_warn, context);
opj_set_warning_handler(codec, j2k_warn, context); opj_set_warning_handler(codec, j2k_warn, context);