Merge branch 'main' into arrow_malloc_guard
This commit is contained in:
commit
f298638632
3
.github/workflows/cifuzz.yml
vendored
3
.github/workflows/cifuzz.yml
vendored
@ -24,6 +24,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
3
.github/workflows/release-drafter.yml
vendored
3
.github/workflows/release-drafter.yml
vendored
@ -14,6 +14,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
permissions:
|
||||
|
||||
3
.github/workflows/stale.yml
vendored
3
.github/workflows/stale.yml
vendored
@ -12,6 +12,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository_owner == 'python-pillow'
|
||||
|
||||
3
.github/workflows/test-docker.yml
vendored
3
.github/workflows/test-docker.yml
vendored
@ -26,6 +26,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
|
||||
1
.github/workflows/test-mingw.yml
vendored
1
.github/workflows/test-mingw.yml
vendored
@ -28,6 +28,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
COVERAGE_CORE: sysmon
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
3
.github/workflows/test-valgrind-memory.yml
vendored
3
.github/workflows/test-valgrind-memory.yml
vendored
@ -26,6 +26,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
|
||||
3
.github/workflows/test-valgrind.yml
vendored
3
.github/workflows/test-valgrind.yml
vendored
@ -24,6 +24,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
|
||||
1
.github/workflows/test-windows.yml
vendored
1
.github/workflows/test-windows.yml
vendored
@ -28,6 +28,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
COVERAGE_CORE: sysmon
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
4
.github/workflows/wheels-dependencies.sh
vendored
4
.github/workflows/wheels-dependencies.sh
vendored
@ -90,9 +90,9 @@ fi
|
||||
ARCHIVE_SDIR=pillow-depends-main
|
||||
|
||||
# Package versions for fresh source builds.
|
||||
FREETYPE_VERSION=2.14.2
|
||||
FREETYPE_VERSION=2.14.3
|
||||
HARFBUZZ_VERSION=13.2.1
|
||||
LIBPNG_VERSION=1.6.55
|
||||
LIBPNG_VERSION=1.6.56
|
||||
JPEGTURBO_VERSION=3.1.3
|
||||
OPENJPEG_VERSION=2.5.4
|
||||
XZ_VERSION=5.8.2
|
||||
|
||||
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
@ -104,7 +104,7 @@ jobs:
|
||||
cibw_arch: arm64_iphonesimulator
|
||||
- name: "iOS x86_64 simulator"
|
||||
platform: ios
|
||||
os: macos-26-intel
|
||||
os: macos-15-intel
|
||||
cibw_arch: x86_64_iphonesimulator
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@ -56,7 +56,7 @@ def test_questionable() -> None:
|
||||
im.load()
|
||||
if os.path.basename(f) not in supported:
|
||||
print(f"Please add {f} to the partially supported bmp specs.")
|
||||
except Exception: # as msg:
|
||||
except Exception: # noqa: PERF203
|
||||
if os.path.basename(f) in supported:
|
||||
raise
|
||||
|
||||
@ -106,7 +106,7 @@ def test_good() -> None:
|
||||
|
||||
assert_image_similar(im_converted, compare_converted, 5)
|
||||
|
||||
except Exception as msg:
|
||||
except Exception as msg: # noqa: PERF203
|
||||
# there are three here that are unsupported:
|
||||
unsupported = (
|
||||
os.path.join(base, "g", "rgb32bf.bmp"),
|
||||
|
||||
@ -179,9 +179,7 @@ def test_iter(bytesmode: bool) -> None:
|
||||
container = ContainerIO.ContainerIO(fh, 0, 120)
|
||||
|
||||
# Act
|
||||
data = []
|
||||
for line in container:
|
||||
data.append(line)
|
||||
data = list(container)
|
||||
|
||||
# Assert
|
||||
if bytesmode:
|
||||
|
||||
@ -148,6 +148,22 @@ def test_prog_res_rt(card: ImageFile.ImageFile) -> None:
|
||||
assert_image_equal(im, card)
|
||||
|
||||
|
||||
def test_unknown_progression(tmp_path: Path) -> None:
|
||||
outfile = tmp_path / "temp.jp2"
|
||||
|
||||
im = Image.new("1", (1, 1))
|
||||
with pytest.raises(ValueError, match="unknown progression"):
|
||||
im.save(outfile, progression="invalid")
|
||||
|
||||
|
||||
def test_unknown_cinema_mode(tmp_path: Path) -> None:
|
||||
outfile = tmp_path / "temp.jp2"
|
||||
|
||||
im = Image.new("1", (1, 1))
|
||||
with pytest.raises(ValueError, match="unknown cinema mode"):
|
||||
im.save(outfile, cinema_mode="invalid")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("num_resolutions", range(2, 6))
|
||||
def test_default_num_resolutions(
|
||||
card: ImageFile.ImageFile, num_resolutions: int
|
||||
@ -440,6 +456,13 @@ def test_pclr() -> None:
|
||||
assert len(im.palette.colors) == 256
|
||||
assert im.palette.colors[(255, 255, 255)] == 0
|
||||
|
||||
for enumcs in (0, 15, 17):
|
||||
with open(f"{EXTRA_DIR}/issue104_jpxstream.jp2", "rb") as fp:
|
||||
data = bytearray(fp.read())
|
||||
data[114:115] = bytes([enumcs])
|
||||
with Image.open(BytesIO(data)) as im:
|
||||
assert im.mode == "L"
|
||||
|
||||
with Image.open(
|
||||
f"{EXTRA_DIR}/147af3f1083de4393666b7d99b01b58b_signal_sigsegv_130c531_6155_5136.jp2"
|
||||
) as im:
|
||||
|
||||
@ -224,10 +224,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||
with Image.open("Tests/images/hopper_g4.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
for tag in im.tag_v2:
|
||||
try:
|
||||
del core_items[tag]
|
||||
except KeyError:
|
||||
pass
|
||||
core_items.pop(tag, None)
|
||||
del core_items[320] # colormap is special, tested below
|
||||
|
||||
# Type codes:
|
||||
|
||||
@ -6,7 +6,14 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageFile, JpegImagePlugin, MpoImagePlugin
|
||||
from PIL import (
|
||||
Image,
|
||||
ImageFile,
|
||||
JpegImagePlugin,
|
||||
MpoImagePlugin,
|
||||
TiffImagePlugin,
|
||||
_binary,
|
||||
)
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
@ -145,6 +152,32 @@ def test_parallax() -> None:
|
||||
assert exif.get_ifd(0x927C)[0xB211] == -3.125
|
||||
|
||||
|
||||
def test_truncated_makernote() -> None:
|
||||
def check(ifd: TiffImagePlugin.ImageFileDirectory_v2) -> None:
|
||||
fp = BytesIO()
|
||||
ifd.save(fp)
|
||||
|
||||
e = Image.Exif()
|
||||
e.load(fp.getvalue())
|
||||
assert e.get_ifd(37500) == {}
|
||||
|
||||
# Nintendo
|
||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||
ifd[271] = "Nintendo"
|
||||
ifd[34665] = {37500: b" "}
|
||||
check(ifd)
|
||||
|
||||
# Fujifilm
|
||||
for data in (
|
||||
b"FUJIFILM",
|
||||
b"FUJIFILM" + _binary.o32le(50),
|
||||
b"FUJIFILM" + _binary.o32le(0),
|
||||
):
|
||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||
ifd[34665] = {37500: data}
|
||||
check(ifd)
|
||||
|
||||
|
||||
def test_reload_exif_after_seek() -> None:
|
||||
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||
exif = im.getexif()
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from PIL import Image, UnidentifiedImageError, _binary
|
||||
|
||||
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
||||
|
||||
@ -92,6 +93,25 @@ def test_rgba_16() -> None:
|
||||
assert im.getpixel((1, 0)) == (0, 255, 82, 0)
|
||||
|
||||
|
||||
def test_v2_no_alpha() -> None:
|
||||
test_file = "Tests/images/tga/common/200x32_rgba_tl_rle.tga"
|
||||
with open(test_file, "rb") as fp:
|
||||
data = fp.read()
|
||||
data += (
|
||||
b"\x00" * 495
|
||||
+ _binary.o32le(len(data))
|
||||
+ _binary.o32le(0)
|
||||
+ b"TRUEVISION-XFILE.\x00"
|
||||
)
|
||||
with Image.open(BytesIO(data)) as im:
|
||||
with Image.open(test_file) as im2:
|
||||
r, g, b = im2.split()[:3]
|
||||
a = Image.new("L", im2.size, 255)
|
||||
expected = Image.merge("RGBA", (r, g, b, a))
|
||||
|
||||
assert_image_equal(im, expected)
|
||||
|
||||
|
||||
def test_id_field() -> None:
|
||||
# tga file with id field
|
||||
test_file = "Tests/images/tga_id_field.tga"
|
||||
|
||||
@ -16,6 +16,7 @@ from PIL import (
|
||||
TiffImagePlugin,
|
||||
TiffTags,
|
||||
UnidentifiedImageError,
|
||||
_binary,
|
||||
)
|
||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||
|
||||
@ -941,6 +942,15 @@ class TestFileTiff:
|
||||
4001,
|
||||
]
|
||||
|
||||
def test_truncated_photoshop_blocks(self) -> None:
|
||||
with Image.open("Tests/images/hopper.tif") as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
im.tag_v2[34377] = b"8BIM"
|
||||
assert im.get_photoshop_blocks() == {}
|
||||
|
||||
im.tag_v2[34377] = b"8BIM" + _binary.o16be(0) + _binary.o8(2) + b" " * 5
|
||||
assert im.get_photoshop_blocks() == {}
|
||||
|
||||
def test_tiff_chunks(self, tmp_path: Path) -> None:
|
||||
tmpfile = tmp_path / "temp.tif"
|
||||
|
||||
|
||||
@ -5,7 +5,10 @@ archive=$1
|
||||
url=$2
|
||||
|
||||
if [ ! -f $archive.tar.gz ]; then
|
||||
wget --no-verbose -O $archive.tar.gz $url
|
||||
wget -O $archive.tar.gz $url \
|
||||
--no-verbose \
|
||||
--retry-connrefused \
|
||||
--retry-on-http-error=429,503,504
|
||||
fi
|
||||
|
||||
rmdir $archive
|
||||
|
||||
52
docs/releasenotes/12.2.0.rst
Normal file
52
docs/releasenotes/12.2.0.rst
Normal file
@ -0,0 +1,52 @@
|
||||
12.2.0
|
||||
------
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
TODO
|
||||
^^^^
|
||||
|
||||
TODO
|
||||
|
||||
:cve:`YYYY-XXXXX`: TODO
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TODO
|
||||
|
||||
API changes
|
||||
===========
|
||||
|
||||
Error when encoding an empty image
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Attempting to encode an image with zero width or height would previously raise
|
||||
a :py:exc:`SystemError`. That has now been changed to a :py:exc:`ValueError`.
|
||||
|
||||
This does not add any new errors. SGI, ICNS and ICO formats are still able to
|
||||
save (0, 0) images.
|
||||
|
||||
API additions
|
||||
=============
|
||||
|
||||
FontFile.to_imagefont()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:py:class:`~PIL.FontFile.FontFile` instances can now be directly converted to
|
||||
:py:class:`~PIL.ImageFont.ImageFont` instances::
|
||||
|
||||
>>> from PIL import PcfFontFile
|
||||
>>> with open("Tests/fonts/10x20-ISO8859-1.pcf", "rb") as fp:
|
||||
... pcffont = PcfFontFile.PcfFontFile(fp)
|
||||
... pcffont.to_imagefont()
|
||||
...
|
||||
<PIL.ImageFont.ImageFont object at 0x10457bb80>
|
||||
|
||||
Other changes
|
||||
=============
|
||||
|
||||
Support reading JPEG2000 images with CMYK palettes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
JPEG2000 images with CMYK palettes can now be read. This is the first integration of
|
||||
CMYK palettes into Pillow.
|
||||
@ -15,6 +15,7 @@ expected to be backported to earlier versions.
|
||||
:maxdepth: 2
|
||||
|
||||
versioning
|
||||
12.2.0
|
||||
12.1.1
|
||||
12.1.0
|
||||
12.0.0
|
||||
|
||||
@ -146,6 +146,7 @@ lint.select = [
|
||||
"I", # isort
|
||||
"ISC", # flake8-implicit-str-concat
|
||||
"LOG", # flake8-logging
|
||||
"PERF", # perflint
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
"PT", # flake8-pytest-style
|
||||
|
||||
10
setup.py
10
setup.py
@ -302,7 +302,7 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None:
|
||||
subprocess.check_output(command_cflags).decode("utf8").strip(),
|
||||
)[::2][1:]
|
||||
return libs, cflags
|
||||
except Exception:
|
||||
except Exception: # noqa: PERF203
|
||||
pass
|
||||
return None
|
||||
|
||||
@ -1078,10 +1078,10 @@ libraries: list[tuple[str, _BuildInfo]] = [
|
||||
]
|
||||
|
||||
files: list[str | os.PathLike[str]] = ["src/_imaging.c"]
|
||||
for src_file in _IMAGING:
|
||||
files.append("src/" + src_file + ".c")
|
||||
for src_file in _LIB_IMAGING:
|
||||
files.append(os.path.join("src/libImaging", src_file + ".c"))
|
||||
files.extend("src/" + src_file + ".c" for src_file in _IMAGING)
|
||||
files.extend(
|
||||
os.path.join("src/libImaging", src_file + ".c") for src_file in _LIB_IMAGING
|
||||
)
|
||||
ext_modules = [
|
||||
Extension("PIL._imaging", files),
|
||||
Extension("PIL._imagingft", ["src/_imagingft.c"]),
|
||||
|
||||
@ -164,13 +164,13 @@ class GifImageFile(ImageFile.ImageFile):
|
||||
self._seek(0)
|
||||
|
||||
last_frame = self.__frame
|
||||
for f in range(self.__frame + 1, frame + 1):
|
||||
try:
|
||||
try:
|
||||
for f in range(self.__frame + 1, frame + 1):
|
||||
self._seek(f)
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
msg = "no more images in GIF file"
|
||||
raise EOFError(msg) from e
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
msg = "no more images in GIF file"
|
||||
raise EOFError(msg) from e
|
||||
|
||||
def _seek(self, frame: int, update_image: bool = True) -> None:
|
||||
if isinstance(self._fp, DeferredError):
|
||||
|
||||
@ -80,8 +80,7 @@ def read_32(
|
||||
if byte_int & 0x80:
|
||||
blocksize = byte_int - 125
|
||||
byte = fobj.read(1)
|
||||
for i in range(blocksize):
|
||||
data.append(byte)
|
||||
data.extend([byte] * blocksize)
|
||||
else:
|
||||
blocksize = byte_int + 1
|
||||
data.append(fobj.read(blocksize))
|
||||
|
||||
133
src/PIL/Image.py
133
src/PIL/Image.py
@ -488,7 +488,7 @@ def init() -> bool:
|
||||
try:
|
||||
logger.debug("Importing %s", plugin)
|
||||
__import__(f"{__spec__.parent}.{plugin}", globals(), locals(), [])
|
||||
except ImportError as e:
|
||||
except ImportError as e: # noqa: PERF203
|
||||
logger.debug("Image: failed to import %s: %s", plugin, e)
|
||||
|
||||
if OPEN or SAVE:
|
||||
@ -4234,80 +4234,83 @@ class Exif(_ExifBase):
|
||||
if tag == ExifTags.IFD.MakerNote:
|
||||
from .TiffImagePlugin import ImageFileDirectory_v2
|
||||
|
||||
if tag_data.startswith(b"FUJIFILM"):
|
||||
ifd_offset = i32le(tag_data, 8)
|
||||
ifd_data = tag_data[ifd_offset:]
|
||||
try:
|
||||
if tag_data.startswith(b"FUJIFILM"):
|
||||
ifd_offset = i32le(tag_data, 8)
|
||||
ifd_data = tag_data[ifd_offset:]
|
||||
|
||||
makernote = {}
|
||||
for i in range(struct.unpack("<H", ifd_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
"<HHL4s", ifd_data[i * 12 + 2 : (i + 1) * 12 + 2]
|
||||
)
|
||||
try:
|
||||
(
|
||||
unit_size,
|
||||
handler,
|
||||
) = ImageFileDirectory_v2._load_dispatch[typ]
|
||||
except KeyError:
|
||||
continue
|
||||
size = count * unit_size
|
||||
if size > 4:
|
||||
(offset,) = struct.unpack("<L", data)
|
||||
data = ifd_data[offset - 12 : offset + size - 12]
|
||||
else:
|
||||
data = data[:size]
|
||||
|
||||
if len(data) != size:
|
||||
warnings.warn(
|
||||
"Possibly corrupt EXIF MakerNote data. "
|
||||
f"Expecting to read {size} bytes but only got "
|
||||
f"{len(data)}. Skipping tag {ifd_tag}"
|
||||
makernote = {}
|
||||
for i in range(struct.unpack("<H", ifd_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
"<HHL4s", ifd_data[i * 12 + 2 : (i + 1) * 12 + 2]
|
||||
)
|
||||
continue
|
||||
try:
|
||||
(
|
||||
unit_size,
|
||||
handler,
|
||||
) = ImageFileDirectory_v2._load_dispatch[typ]
|
||||
except KeyError:
|
||||
continue
|
||||
size = count * unit_size
|
||||
if size > 4:
|
||||
(offset,) = struct.unpack("<L", data)
|
||||
data = ifd_data[offset - 12 : offset + size - 12]
|
||||
else:
|
||||
data = data[:size]
|
||||
|
||||
if not data:
|
||||
continue
|
||||
if len(data) != size:
|
||||
warnings.warn(
|
||||
"Possibly corrupt EXIF MakerNote data. "
|
||||
f"Expecting to read {size} bytes but only got "
|
||||
f"{len(data)}. Skipping tag {ifd_tag}"
|
||||
)
|
||||
continue
|
||||
|
||||
makernote[ifd_tag] = handler(
|
||||
ImageFileDirectory_v2(), data, False
|
||||
)
|
||||
self._ifds[tag] = dict(self._fixup_dict(makernote))
|
||||
elif self.get(0x010F) == "Nintendo":
|
||||
makernote = {}
|
||||
for i in range(struct.unpack(">H", tag_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2]
|
||||
)
|
||||
if ifd_tag == 0x1101:
|
||||
# CameraInfo
|
||||
(offset,) = struct.unpack(">L", data)
|
||||
self.fp.seek(offset)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
camerainfo: dict[str, int | bytes] = {
|
||||
"ModelID": self.fp.read(4)
|
||||
}
|
||||
makernote[ifd_tag] = handler(
|
||||
ImageFileDirectory_v2(), data, False
|
||||
)
|
||||
self._ifds[tag] = dict(self._fixup_dict(makernote))
|
||||
elif self.get(0x010F) == "Nintendo":
|
||||
makernote = {}
|
||||
for i in range(struct.unpack(">H", tag_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2]
|
||||
)
|
||||
if ifd_tag == 0x1101:
|
||||
# CameraInfo
|
||||
(offset,) = struct.unpack(">L", data)
|
||||
self.fp.seek(offset)
|
||||
|
||||
self.fp.read(4)
|
||||
# Seconds since 2000
|
||||
camerainfo["TimeStamp"] = i32le(self.fp.read(12))
|
||||
camerainfo: dict[str, int | bytes] = {
|
||||
"ModelID": self.fp.read(4)
|
||||
}
|
||||
|
||||
self.fp.read(4)
|
||||
camerainfo["InternalSerialNumber"] = self.fp.read(4)
|
||||
self.fp.read(4)
|
||||
# Seconds since 2000
|
||||
camerainfo["TimeStamp"] = i32le(self.fp.read(12))
|
||||
|
||||
self.fp.read(12)
|
||||
parallax = self.fp.read(4)
|
||||
handler = ImageFileDirectory_v2._load_dispatch[
|
||||
TiffTags.FLOAT
|
||||
][1]
|
||||
camerainfo["Parallax"] = handler(
|
||||
ImageFileDirectory_v2(), parallax, False
|
||||
)[0]
|
||||
self.fp.read(4)
|
||||
camerainfo["InternalSerialNumber"] = self.fp.read(4)
|
||||
|
||||
self.fp.read(4)
|
||||
camerainfo["Category"] = self.fp.read(2)
|
||||
self.fp.read(12)
|
||||
parallax = self.fp.read(4)
|
||||
handler = ImageFileDirectory_v2._load_dispatch[
|
||||
TiffTags.FLOAT
|
||||
][1]
|
||||
camerainfo["Parallax"] = handler(
|
||||
ImageFileDirectory_v2(), parallax, False
|
||||
)[0]
|
||||
|
||||
makernote = {0x1101: camerainfo}
|
||||
self._ifds[tag] = makernote
|
||||
self.fp.read(4)
|
||||
camerainfo["Category"] = self.fp.read(2)
|
||||
|
||||
makernote = {0x1101: camerainfo}
|
||||
self._ifds[tag] = makernote
|
||||
except struct.error:
|
||||
pass
|
||||
else:
|
||||
# Interop
|
||||
ifd = self._get_ifd_dict(tag_data, tag)
|
||||
|
||||
@ -597,9 +597,7 @@ class ImageDraw:
|
||||
mode = self.fontmode
|
||||
if stroke_width == 0 and embedded_color:
|
||||
mode = "RGBA"
|
||||
coord = []
|
||||
for i in range(2):
|
||||
coord.append(int(xy[i]))
|
||||
coord = [int(xy[i]) for i in range(2)]
|
||||
start = (math.modf(xy[0])[0], math.modf(xy[1])[0])
|
||||
try:
|
||||
mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc]
|
||||
|
||||
@ -215,8 +215,10 @@ class ImageFile(Image.Image):
|
||||
if subifd_offsets:
|
||||
if not isinstance(subifd_offsets, tuple):
|
||||
subifd_offsets = (subifd_offsets,)
|
||||
for subifd_offset in subifd_offsets:
|
||||
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
||||
ifds = [
|
||||
(exif._get_ifd_dict(subifd_offset), subifd_offset)
|
||||
for subifd_offset in subifd_offsets
|
||||
]
|
||||
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
||||
if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset):
|
||||
assert exif._info is not None
|
||||
|
||||
@ -930,7 +930,7 @@ def load_path(filename: str | bytes) -> ImageFont:
|
||||
for directory in sys.path:
|
||||
try:
|
||||
return load(os.path.join(directory, filename))
|
||||
except OSError:
|
||||
except OSError: # noqa: PERF203
|
||||
pass
|
||||
msg = f'cannot find font file "{filename}" in sys.path'
|
||||
if os.path.exists(filename):
|
||||
|
||||
@ -198,13 +198,11 @@ class ImagePalette:
|
||||
try:
|
||||
fp.write("# Palette\n")
|
||||
fp.write(f"# Mode: {self.mode}\n")
|
||||
palette_len = len(self.palette)
|
||||
for i in range(256):
|
||||
fp.write(f"{i}")
|
||||
for j in range(i * len(self.mode), (i + 1) * len(self.mode)):
|
||||
try:
|
||||
fp.write(f" {self.palette[j]}")
|
||||
except IndexError:
|
||||
fp.write(" 0")
|
||||
fp.write(f" {self.palette[j] if j < palette_len else 0}")
|
||||
fp.write("\n")
|
||||
finally:
|
||||
if open_fp:
|
||||
|
||||
@ -185,13 +185,9 @@ def getiptcinfo(
|
||||
|
||||
data = None
|
||||
|
||||
info: dict[tuple[int, int], bytes | list[bytes]] = {}
|
||||
if isinstance(im, IptcImageFile):
|
||||
# return info dictionary right away
|
||||
for k, v in im.info.items():
|
||||
if isinstance(k, tuple):
|
||||
info[k] = v
|
||||
return info
|
||||
return {k: v for k, v in im.info.items() if isinstance(k, tuple)}
|
||||
|
||||
elif isinstance(im, JpegImagePlugin.JpegImageFile):
|
||||
# extract the IPTC/NAA resource
|
||||
@ -227,7 +223,4 @@ def getiptcinfo(
|
||||
except (IndexError, KeyError):
|
||||
pass # expected failure
|
||||
|
||||
for k, v in iptc_im.info.items():
|
||||
if isinstance(k, tuple):
|
||||
info[k] = v
|
||||
return info
|
||||
return {k: v for k, v in iptc_im.info.items() if isinstance(k, tuple)}
|
||||
|
||||
@ -176,7 +176,7 @@ def _parse_jp2_header(
|
||||
nc = None
|
||||
dpi = None # 2-tuple of DPI info, or None
|
||||
palette = None
|
||||
cmyk = False
|
||||
colr = None
|
||||
|
||||
while header.has_next_box():
|
||||
tbox = header.next_box_type()
|
||||
@ -199,10 +199,16 @@ def _parse_jp2_header(
|
||||
mode = "RGBA"
|
||||
elif tbox == b"colr":
|
||||
meth, _, _, enumcs = header.read_fields(">BBBI")
|
||||
if cmyk := (meth == 1 and enumcs == 12):
|
||||
if nc == 4:
|
||||
mode = "CMYK"
|
||||
elif tbox == b"pclr" and mode in ("L", "LA"):
|
||||
if meth == 1:
|
||||
if enumcs in (0, 15):
|
||||
colr = "1"
|
||||
elif enumcs == 12:
|
||||
colr = "CMYK"
|
||||
if nc == 4:
|
||||
mode = "CMYK"
|
||||
elif enumcs == 17:
|
||||
colr = "L"
|
||||
elif tbox == b"pclr" and mode in ("L", "LA") and colr not in ("1", "L"):
|
||||
ne, npc = header.read_fields(">HB")
|
||||
assert isinstance(ne, int)
|
||||
assert isinstance(npc, int)
|
||||
@ -213,7 +219,7 @@ def _parse_jp2_header(
|
||||
max_bitdepth = bitdepth
|
||||
if max_bitdepth <= 8:
|
||||
if npc == 4:
|
||||
palette_mode = "CMYK" if cmyk else "RGBA"
|
||||
palette_mode = "CMYK" if colr == "CMYK" else "RGBA"
|
||||
else:
|
||||
palette_mode = "RGB"
|
||||
palette = ImagePalette.ImagePalette(palette_mode)
|
||||
|
||||
@ -127,8 +127,8 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||
# parse the image resource block
|
||||
offset = 14
|
||||
photoshop = self.info.setdefault("photoshop", {})
|
||||
while s[offset : offset + 4] == b"8BIM":
|
||||
try:
|
||||
try:
|
||||
while s[offset : offset + 4] == b"8BIM":
|
||||
offset += 4
|
||||
# resource code
|
||||
code = i16(s, offset)
|
||||
@ -153,8 +153,8 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||
photoshop[code] = data
|
||||
offset += size
|
||||
offset += offset & 1 # align
|
||||
except struct.error:
|
||||
break # insufficient data
|
||||
except struct.error:
|
||||
pass # insufficient data
|
||||
|
||||
elif marker == 0xFFEE and s.startswith(b"Adobe"):
|
||||
self.info["adobe"] = i16(s, 5)
|
||||
@ -738,17 +738,15 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
if not (0 < len(qtables) < 5):
|
||||
msg = "None or too many quantization tables"
|
||||
raise ValueError(msg)
|
||||
for idx, table in enumerate(qtables):
|
||||
try:
|
||||
try:
|
||||
for idx, table in enumerate(qtables):
|
||||
if len(table) != 64:
|
||||
msg = "Invalid quantization table"
|
||||
raise TypeError(msg)
|
||||
table_array = array.array("H", table)
|
||||
except TypeError as e:
|
||||
msg = "Invalid quantization table"
|
||||
raise ValueError(msg) from e
|
||||
else:
|
||||
qtables[idx] = list(table_array)
|
||||
qtables[idx] = list(array.array("H", table))
|
||||
except TypeError as e:
|
||||
msg = "Invalid quantization table"
|
||||
raise ValueError(msg) from e
|
||||
return qtables
|
||||
|
||||
if qtables == "keep":
|
||||
|
||||
@ -251,7 +251,7 @@ class PcfFontFile(FontFile.FontFile):
|
||||
]
|
||||
if encoding_offset != 0xFFFF:
|
||||
encoding[i] = encoding_offset
|
||||
except UnicodeDecodeError:
|
||||
except UnicodeDecodeError: # noqa: PERF203
|
||||
# character is not supported in selected encoding
|
||||
pass
|
||||
|
||||
|
||||
@ -866,13 +866,13 @@ class PngImageFile(ImageFile.ImageFile):
|
||||
self._seek(0, True)
|
||||
|
||||
last_frame = self.__frame
|
||||
for f in range(self.__frame + 1, frame + 1):
|
||||
try:
|
||||
try:
|
||||
for f in range(self.__frame + 1, frame + 1):
|
||||
self._seek(f)
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
msg = "no more images in APNG file"
|
||||
raise EOFError(msg) from e
|
||||
except EOFError as e:
|
||||
self.seek(last_frame)
|
||||
msg = "no more images in APNG file"
|
||||
raise EOFError(msg) from e
|
||||
|
||||
def _seek(self, frame: int, rewind: bool = False) -> None:
|
||||
assert self.png is not None
|
||||
|
||||
@ -17,11 +17,13 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import warnings
|
||||
from typing import IO
|
||||
|
||||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i16le as i16
|
||||
from ._binary import i32le as i32
|
||||
from ._binary import o8
|
||||
from ._binary import o16le as o16
|
||||
|
||||
@ -157,6 +159,20 @@ class TgaImageFile(ImageFile.ImageFile):
|
||||
pass # cannot decode
|
||||
|
||||
def load_end(self) -> None:
|
||||
if self.mode == "RGBA":
|
||||
assert self.fp is not None
|
||||
self.fp.seek(-26, os.SEEK_END)
|
||||
footer = self.fp.read(26)
|
||||
if footer.endswith(b"TRUEVISION-XFILE.\x00"):
|
||||
# version 2
|
||||
extension_offset = i32(footer)
|
||||
if extension_offset:
|
||||
self.fp.seek(extension_offset + 494)
|
||||
attributes_type = self.fp.read(1)
|
||||
if attributes_type == b"\x00":
|
||||
# No alpha
|
||||
self.im.fillband(3, 255)
|
||||
|
||||
if self._flip_horizontally:
|
||||
self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
|
||||
|
||||
@ -1287,10 +1287,13 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
blocks = {}
|
||||
val = self.tag_v2.get(ExifTags.Base.ImageResources)
|
||||
if val:
|
||||
while val.startswith(b"8BIM"):
|
||||
while val.startswith(b"8BIM") and len(val) >= 12:
|
||||
id = i16(val[4:6])
|
||||
n = math.ceil((val[6] + 1) / 2) * 2
|
||||
size = i32(val[6 + n : 10 + n])
|
||||
try:
|
||||
size = i32(val[6 + n : 10 + n])
|
||||
except struct.error:
|
||||
break
|
||||
data = val[10 + n : 10 + n + size]
|
||||
blocks[id] = {"data": data}
|
||||
|
||||
|
||||
24
src/_avif.c
24
src/_avif.c
@ -425,7 +425,7 @@ end:
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
void
|
||||
_encoder_dealloc(AvifEncoderObject *self) {
|
||||
if (self->encoder) {
|
||||
avifEncoderDestroy(self->encoder);
|
||||
@ -433,7 +433,7 @@ _encoder_dealloc(AvifEncoderObject *self) {
|
||||
if (self->image) {
|
||||
avifImageDestroy(self->image);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
@ -687,13 +687,13 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
void
|
||||
_decoder_dealloc(AvifDecoderObject *self) {
|
||||
if (self->decoder) {
|
||||
avifDecoderDestroy(self->decoder);
|
||||
}
|
||||
PyBuffer_Release(&self->buffer);
|
||||
Py_RETURN_NONE;
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
@ -708,15 +708,27 @@ _decoder_get_info(AvifDecoderObject *self) {
|
||||
|
||||
if (image->xmp.size) {
|
||||
xmp = PyBytes_FromStringAndSize((const char *)image->xmp.data, image->xmp.size);
|
||||
if (!xmp) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (image->exif.size) {
|
||||
exif =
|
||||
PyBytes_FromStringAndSize((const char *)image->exif.data, image->exif.size);
|
||||
if (!exif) {
|
||||
Py_XDECREF(xmp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (image->icc.size) {
|
||||
icc = PyBytes_FromStringAndSize((const char *)image->icc.data, image->icc.size);
|
||||
if (!icc) {
|
||||
Py_XDECREF(xmp);
|
||||
Py_XDECREF(exif);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = Py_BuildValue(
|
||||
@ -799,6 +811,7 @@ _decoder_get_frame(AvifDecoderObject *self, PyObject *args) {
|
||||
|
||||
if (rgb.height > PY_SSIZE_T_MAX / rgb.rowBytes) {
|
||||
PyErr_SetString(PyExc_MemoryError, "Integer overflow in pixel size");
|
||||
avifRGBImageFreePixels(&rgb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -806,6 +819,9 @@ _decoder_get_frame(AvifDecoderObject *self, PyObject *args) {
|
||||
|
||||
bytes = PyBytes_FromStringAndSize((char *)rgb.pixels, size);
|
||||
avifRGBImageFreePixels(&rgb);
|
||||
if (!bytes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = Py_BuildValue(
|
||||
"SKKK",
|
||||
|
||||
@ -254,6 +254,9 @@ void
|
||||
ReleaseArrowSchemaPyCapsule(PyObject *capsule) {
|
||||
struct ArrowSchema *schema =
|
||||
(struct ArrowSchema *)PyCapsule_GetPointer(capsule, "arrow_schema");
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
if (schema->release != NULL) {
|
||||
schema->release(schema);
|
||||
}
|
||||
@ -276,6 +279,9 @@ void
|
||||
ReleaseArrowArrayPyCapsule(PyObject *capsule) {
|
||||
struct ArrowArray *array =
|
||||
(struct ArrowArray *)PyCapsule_GetPointer(capsule, "arrow_array");
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
if (array->release != NULL) {
|
||||
array->release(array);
|
||||
}
|
||||
@ -1219,7 +1225,9 @@ _getxy(PyObject *xy, int *x, int *y) {
|
||||
PyObject *int_value = PyObject_CallMethod(value, "__int__", NULL);
|
||||
if (int_value != NULL && PyLong_Check(int_value)) {
|
||||
*x = PyLong_AS_LONG(int_value);
|
||||
Py_DECREF(int_value);
|
||||
} else {
|
||||
Py_XDECREF(int_value);
|
||||
goto badval;
|
||||
}
|
||||
}
|
||||
@ -1233,7 +1241,9 @@ _getxy(PyObject *xy, int *x, int *y) {
|
||||
PyObject *int_value = PyObject_CallMethod(value, "__int__", NULL);
|
||||
if (int_value != NULL && PyLong_Check(int_value)) {
|
||||
*y = PyLong_AS_LONG(int_value);
|
||||
Py_DECREF(int_value);
|
||||
} else {
|
||||
Py_XDECREF(int_value);
|
||||
goto badval;
|
||||
}
|
||||
}
|
||||
@ -2472,6 +2482,9 @@ _split(ImagingObject *self) {
|
||||
}
|
||||
|
||||
list = PyTuple_New(self->image->bands);
|
||||
if (!list) {
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < self->image->bands; i++) {
|
||||
imaging_object = PyImagingNew(bands[i]);
|
||||
if (!imaging_object) {
|
||||
@ -3768,6 +3781,9 @@ _ptr_destructor(PyObject *capsule) {
|
||||
static PyObject *
|
||||
_getattr_ptr(ImagingObject *self, void *closure) {
|
||||
PyObject *capsule = PyCapsule_New(self->image, IMAGING_MAGIC, _ptr_destructor);
|
||||
if (!capsule) {
|
||||
return NULL;
|
||||
}
|
||||
Py_INCREF(self);
|
||||
PyCapsule_SetContext(capsule, self);
|
||||
return capsule;
|
||||
@ -4323,8 +4339,9 @@ setup_module(PyObject *m) {
|
||||
#else
|
||||
have_libjpegturbo = Py_False;
|
||||
#endif
|
||||
Py_INCREF(have_libjpegturbo);
|
||||
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", have_libjpegturbo);
|
||||
if (PyModule_AddObjectRef(m, "HAVE_LIBJPEGTURBO", have_libjpegturbo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *have_mozjpeg;
|
||||
#ifdef JPEG_C_PARAM_SUPPORTED
|
||||
@ -4332,8 +4349,9 @@ setup_module(PyObject *m) {
|
||||
#else
|
||||
have_mozjpeg = Py_False;
|
||||
#endif
|
||||
Py_INCREF(have_mozjpeg);
|
||||
PyModule_AddObject(m, "HAVE_MOZJPEG", have_mozjpeg);
|
||||
if (PyModule_AddObjectRef(m, "HAVE_MOZJPEG", have_mozjpeg) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *have_libimagequant;
|
||||
#ifdef HAVE_LIBIMAGEQUANT
|
||||
@ -4347,8 +4365,9 @@ setup_module(PyObject *m) {
|
||||
#else
|
||||
have_libimagequant = Py_False;
|
||||
#endif
|
||||
Py_INCREF(have_libimagequant);
|
||||
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", have_libimagequant);
|
||||
if (PyModule_AddObjectRef(m, "HAVE_LIBIMAGEQUANT", have_libimagequant) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
/* zip encoding strategies */
|
||||
@ -4376,8 +4395,9 @@ setup_module(PyObject *m) {
|
||||
#else
|
||||
have_zlibng = Py_False;
|
||||
#endif
|
||||
Py_INCREF(have_zlibng);
|
||||
PyModule_AddObject(m, "HAVE_ZLIBNG", have_zlibng);
|
||||
if (PyModule_AddObjectRef(m, "HAVE_ZLIBNG", have_zlibng) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBTIFF
|
||||
{
|
||||
@ -4394,8 +4414,9 @@ setup_module(PyObject *m) {
|
||||
#else
|
||||
have_xcb = Py_False;
|
||||
#endif
|
||||
Py_INCREF(have_xcb);
|
||||
PyModule_AddObject(m, "HAVE_XCB", have_xcb);
|
||||
if (PyModule_AddObjectRef(m, "HAVE_XCB", have_xcb) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *pillow_version = PyUnicode_FromString(version);
|
||||
PyDict_SetItemString(
|
||||
|
||||
@ -558,7 +558,13 @@ cms_transform_apply(CmsTransformObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
im = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!im) {
|
||||
return NULL;
|
||||
}
|
||||
imOut = (Imaging)PyCapsule_GetPointer(i1, IMAGING_MAGIC);
|
||||
if (!imOut) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Py_BuildValue("i", pyCMSdoTransform(im, imOut, self->transform));
|
||||
}
|
||||
@ -1447,14 +1453,14 @@ setup_module(PyObject *m) {
|
||||
int vn;
|
||||
|
||||
/* Ready object types */
|
||||
PyType_Ready(&CmsProfile_Type);
|
||||
PyType_Ready(&CmsTransform_Type);
|
||||
if (PyType_Ready(&CmsProfile_Type) < 0 || PyType_Ready(&CmsTransform_Type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_INCREF(&CmsProfile_Type);
|
||||
PyModule_AddObject(m, "CmsProfile", (PyObject *)&CmsProfile_Type);
|
||||
|
||||
Py_INCREF(&CmsTransform_Type);
|
||||
PyModule_AddObject(m, "CmsTransform", (PyObject *)&CmsTransform_Type);
|
||||
if (PyModule_AddObjectRef(m, "CmsProfile", (PyObject *)&CmsProfile_Type) < 0 ||
|
||||
PyModule_AddObjectRef(m, "CmsTransform", (PyObject *)&CmsTransform_Type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
d = PyModule_GetDict(m);
|
||||
|
||||
|
||||
@ -941,8 +941,18 @@ font_render(FontObject *self, PyObject *args) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *imagePtr = PyObject_GetAttrString(image, "ptr");
|
||||
if (!imagePtr) {
|
||||
PyMem_Del(glyph_info);
|
||||
Py_DECREF(image);
|
||||
return NULL;
|
||||
}
|
||||
im = (Imaging)PyCapsule_GetPointer(imagePtr, IMAGING_MAGIC);
|
||||
Py_XDECREF(imagePtr);
|
||||
Py_DECREF(imagePtr);
|
||||
if (!im) {
|
||||
PyMem_Del(glyph_info);
|
||||
Py_DECREF(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
x_offset = round(x_offset - stroke_width);
|
||||
y_offset = round(y_offset - stroke_width);
|
||||
@ -1545,7 +1555,9 @@ setup_module(PyObject *m) {
|
||||
d = PyModule_GetDict(m);
|
||||
|
||||
/* Ready object type */
|
||||
PyType_Ready(&Font_Type);
|
||||
if (PyType_Ready(&Font_Type) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FT_Init_FreeType(&library)) {
|
||||
return 0; /* leave it uninitialized */
|
||||
|
||||
@ -187,8 +187,17 @@ _unop(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
unop = (void *)PyCapsule_GetPointer(op, MATH_FUNC_UNOP_MAGIC);
|
||||
if (!unop) {
|
||||
return NULL;
|
||||
}
|
||||
out = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
im1 = (Imaging)PyCapsule_GetPointer(i1, IMAGING_MAGIC);
|
||||
if (!im1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unop(out, im1);
|
||||
|
||||
@ -219,9 +228,21 @@ _binop(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
binop = (void *)PyCapsule_GetPointer(op, MATH_FUNC_BINOP_MAGIC);
|
||||
if (!binop) {
|
||||
return NULL;
|
||||
}
|
||||
out = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
im1 = (Imaging)PyCapsule_GetPointer(i1, IMAGING_MAGIC);
|
||||
if (!im1) {
|
||||
return NULL;
|
||||
}
|
||||
im2 = (Imaging)PyCapsule_GetPointer(i2, IMAGING_MAGIC);
|
||||
if (!im2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
binop(out, im1, im2);
|
||||
|
||||
|
||||
@ -53,7 +53,13 @@ apply(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
imgin = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!imgin) {
|
||||
return NULL;
|
||||
}
|
||||
imgout = (Imaging)PyCapsule_GetPointer(i1, IMAGING_MAGIC);
|
||||
if (!imgout) {
|
||||
return NULL;
|
||||
}
|
||||
width = imgin->xsize;
|
||||
height = imgin->ysize;
|
||||
|
||||
@ -143,6 +149,9 @@ match(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
imgin = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!imgin) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
|
||||
@ -185,6 +194,10 @@ match(PyObject *self, PyObject *args) {
|
||||
(b6 << 6) | (b7 << 7) | (b8 << 8));
|
||||
if (lut[lut_idx]) {
|
||||
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
|
||||
if (!coordObj) {
|
||||
Py_DECREF(ret);
|
||||
return NULL;
|
||||
}
|
||||
PyList_Append(ret, coordObj);
|
||||
Py_XDECREF(coordObj);
|
||||
}
|
||||
@ -216,6 +229,9 @@ get_on_pixels(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
img = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!img) {
|
||||
return NULL;
|
||||
}
|
||||
rows = img->image8;
|
||||
width = img->xsize;
|
||||
height = img->ysize;
|
||||
@ -230,6 +246,10 @@ get_on_pixels(PyObject *self, PyObject *args) {
|
||||
for (col_idx = 0; col_idx < width; col_idx++) {
|
||||
if (row[col_idx]) {
|
||||
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
|
||||
if (!coordObj) {
|
||||
Py_DECREF(ret);
|
||||
return NULL;
|
||||
}
|
||||
PyList_Append(ret, coordObj);
|
||||
Py_XDECREF(coordObj);
|
||||
}
|
||||
|
||||
11
src/_webp.c
11
src/_webp.c
@ -219,6 +219,7 @@ _anim_encoder_dealloc(PyObject *self) {
|
||||
WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
|
||||
WebPPictureFree(&(encp->frame));
|
||||
WebPAnimEncoderDelete(encp->enc);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
@ -261,6 +262,9 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
im = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!im) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup config for this frame
|
||||
if (!WebPConfigInit(&config)) {
|
||||
@ -441,6 +445,7 @@ _anim_decoder_dealloc(PyObject *self) {
|
||||
WebPAnimDecoderObject *decp = (WebPAnimDecoderObject *)self;
|
||||
WebPDataClear(&(decp->data));
|
||||
WebPAnimDecoderDelete(decp->dec);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
@ -503,6 +508,9 @@ _anim_decoder_get_next(PyObject *self) {
|
||||
bytes = PyBytes_FromStringAndSize(
|
||||
(char *)buf, decp->info.canvas_width * 4 * decp->info.canvas_height
|
||||
);
|
||||
if (!bytes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = Py_BuildValue("Si", bytes, timestamp);
|
||||
|
||||
@ -605,6 +613,9 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
im = (Imaging)PyCapsule_GetPointer(i0, IMAGING_MAGIC);
|
||||
if (!im) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup config for this frame
|
||||
if (!WebPConfigInit(&config)) {
|
||||
|
||||
@ -905,6 +905,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject *self, PyObject *args) {
|
||||
} else if (strcmp(format, "jp2") == 0) {
|
||||
codec_format = OPJ_CODEC_JP2;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "unknown codec format");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -480,6 +480,9 @@ PyImaging_GrabClipboardWin32(PyObject *self, PyObject *args) {
|
||||
GlobalUnlock(handle);
|
||||
CloseClipboard();
|
||||
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
return Py_BuildValue("zN", format_names[format], result);
|
||||
}
|
||||
|
||||
|
||||
@ -727,6 +727,7 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
|
||||
const RawModeID rawmode = findRawModeID(rawmode_name);
|
||||
|
||||
if (get_packer(encoder, mode, rawmode) < 0) {
|
||||
Py_DECREF(encoder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -742,6 +743,7 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
|
||||
for (pos = 0; pos < tags_size; pos++) {
|
||||
item = PyList_GetItemRef(tags, pos);
|
||||
if (item == NULL) {
|
||||
Py_DECREF(encoder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -766,6 +768,7 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
|
||||
if (!is_core_tag) {
|
||||
PyObject *tag_type;
|
||||
if (PyDict_GetItemRef(types, key, &tag_type) < 0) {
|
||||
Py_DECREF(encoder);
|
||||
return NULL; // Exception has been already set
|
||||
}
|
||||
if (tag_type) {
|
||||
@ -837,6 +840,7 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
|
||||
if (key_int == TIFFTAG_COLORMAP) {
|
||||
int stride = 256;
|
||||
if (len != 768) {
|
||||
Py_DECREF(encoder);
|
||||
PyErr_SetString(
|
||||
PyExc_ValueError, "Requiring 768 items for Colormap"
|
||||
);
|
||||
@ -1343,11 +1347,10 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||
|
||||
if (strcmp(format, "j2k") == 0) {
|
||||
codec_format = OPJ_CODEC_J2K;
|
||||
} else if (strcmp(format, "jpt") == 0) {
|
||||
codec_format = OPJ_CODEC_JPT;
|
||||
} else if (strcmp(format, "jp2") == 0) {
|
||||
codec_format = OPJ_CODEC_JP2;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "unknown codec format");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1362,6 +1365,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||
} else if (strcmp(progression, "CPRL") == 0) {
|
||||
prog_order = OPJ_CPRL;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "unknown progression");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1374,6 +1378,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||
} else if (strcmp(cinema_mode, "cinema4k-24") == 0) {
|
||||
cine_mode = OPJ_CINEMA4K_24;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "unknown cinema mode");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -352,16 +352,16 @@ ImagingPaste(
|
||||
|
||||
static inline void
|
||||
fill(
|
||||
Imaging imOut, const void *ink_, int dx, int dy, int xsize, int ysize, int pixelsize
|
||||
Imaging imOut, const UINT8 *ink, int dx, int dy, int xsize, int ysize, int pixelsize
|
||||
) {
|
||||
/* fill opaque region */
|
||||
|
||||
int x, y;
|
||||
int x, y, i;
|
||||
UINT8 ink8 = 0;
|
||||
INT32 ink32 = 0L;
|
||||
|
||||
memcpy(&ink32, ink_, pixelsize);
|
||||
memcpy(&ink8, ink_, sizeof(ink8));
|
||||
memcpy(&ink32, ink, pixelsize);
|
||||
memcpy(&ink8, ink, sizeof(ink8));
|
||||
|
||||
if (imOut->image8 || ink32 == 0L) {
|
||||
dx *= pixelsize;
|
||||
@ -371,12 +371,24 @@ fill(
|
||||
}
|
||||
|
||||
} else {
|
||||
#if defined _WIN32 && !defined _WIN64
|
||||
dx *= pixelsize;
|
||||
for (y = 0; y < ysize; y++) {
|
||||
UINT8 *out = (UINT8 *)imOut->image[y + dy] + dx;
|
||||
for (x = 0; x < xsize; x++) {
|
||||
for (i = 0; i < pixelsize; i++) {
|
||||
*out++ = ink[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (y = 0; y < ysize; y++) {
|
||||
INT32 *out = imOut->image32[y + dy] + dx;
|
||||
for (x = 0; x < xsize; x++) {
|
||||
out[x] = ink32;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -175,8 +175,15 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t
|
||||
|
||||
/* Get all data from File descriptor */
|
||||
c = (SGISTATE *)state->context;
|
||||
_imaging_seek_pyFd(state->fd, 0L, SEEK_END);
|
||||
if (_imaging_seek_pyFd(state->fd, 0L, SEEK_END) == -1) {
|
||||
state->errcode = IMAGING_CODEC_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
c->bufsize = _imaging_tell_pyFd(state->fd);
|
||||
if (c->bufsize == -1) {
|
||||
state->errcode = IMAGING_CODEC_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
c->bufsize -= SGI_HEADER_SIZE;
|
||||
|
||||
c->tablen = im->bands * im->ysize;
|
||||
@ -194,8 +201,8 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t
|
||||
state->errcode = IMAGING_CODEC_MEMORY;
|
||||
return -1;
|
||||
}
|
||||
_imaging_seek_pyFd(state->fd, SGI_HEADER_SIZE, SEEK_SET);
|
||||
if (_imaging_read_pyFd(state->fd, (char *)ptr, c->bufsize) != c->bufsize) {
|
||||
if (_imaging_seek_pyFd(state->fd, SGI_HEADER_SIZE, SEEK_SET) == -1 ||
|
||||
_imaging_read_pyFd(state->fd, (char *)ptr, c->bufsize) != c->bufsize) {
|
||||
free(ptr);
|
||||
state->errcode = IMAGING_CODEC_UNKNOWN;
|
||||
return -1;
|
||||
|
||||
@ -677,9 +677,15 @@ ImagingNewArrow(
|
||||
Imaging im;
|
||||
struct ArrowSchema *schema =
|
||||
(struct ArrowSchema *)PyCapsule_GetPointer(schema_capsule, "arrow_schema");
|
||||
if (!schema) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ArrowArray *external_array =
|
||||
(struct ArrowArray *)PyCapsule_GetPointer(array_capsule, "arrow_array");
|
||||
if (!external_array) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (xsize < 0 || ysize < 0) {
|
||||
return (Imaging)ImagingError_ValueError("bad image size");
|
||||
|
||||
@ -12,6 +12,9 @@ _imaging_read_pyFd(PyObject *fd, char *dest, Py_ssize_t bytes) {
|
||||
int bytes_result;
|
||||
|
||||
result = PyObject_CallMethod(fd, "read", "n", bytes);
|
||||
if (result == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
bytes_result = PyBytes_AsStringAndSize(result, &buffer, &length);
|
||||
if (bytes_result == -1) {
|
||||
@ -28,7 +31,7 @@ _imaging_read_pyFd(PyObject *fd, char *dest, Py_ssize_t bytes) {
|
||||
return length;
|
||||
|
||||
err:
|
||||
Py_DECREF(result);
|
||||
Py_XDECREF(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -38,9 +41,16 @@ _imaging_write_pyFd(PyObject *fd, char *src, Py_ssize_t bytes) {
|
||||
PyObject *byteObj;
|
||||
|
||||
byteObj = PyBytes_FromStringAndSize(src, bytes);
|
||||
if (!byteObj) {
|
||||
return -1;
|
||||
}
|
||||
result = PyObject_CallMethod(fd, "write", "O", byteObj);
|
||||
|
||||
Py_DECREF(byteObj);
|
||||
if (result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(result);
|
||||
|
||||
return bytes;
|
||||
@ -51,6 +61,9 @@ _imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence) {
|
||||
PyObject *result;
|
||||
|
||||
result = PyObject_CallMethod(fd, "seek", "ni", offset, whence);
|
||||
if (result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(result);
|
||||
return 0;
|
||||
@ -62,6 +75,9 @@ _imaging_tell_pyFd(PyObject *fd) {
|
||||
Py_ssize_t location;
|
||||
|
||||
result = PyObject_CallMethod(fd, "tell", NULL);
|
||||
if (result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
location = PyLong_AsSsize_t(result);
|
||||
|
||||
Py_DECREF(result);
|
||||
|
||||
@ -114,14 +114,14 @@ ARCHITECTURES = {
|
||||
|
||||
V = {
|
||||
"BROTLI": "1.2.0",
|
||||
"FREETYPE": "2.14.2",
|
||||
"FREETYPE": "2.14.3",
|
||||
"FRIBIDI": "1.0.16",
|
||||
"HARFBUZZ": "13.2.1",
|
||||
"JPEGTURBO": "3.1.3",
|
||||
"LCMS2": "2.18",
|
||||
"LIBAVIF": "1.4.1",
|
||||
"LIBIMAGEQUANT": "4.4.1",
|
||||
"LIBPNG": "1.6.55",
|
||||
"LIBPNG": "1.6.56",
|
||||
"LIBWEBP": "1.6.0",
|
||||
"OPENJPEG": "2.5.4",
|
||||
"TIFF": "4.7.1",
|
||||
@ -542,14 +542,11 @@ def write_script(
|
||||
|
||||
|
||||
def get_footer(dep: dict[str, Any]) -> list[str]:
|
||||
lines = []
|
||||
for out in dep.get("headers", []):
|
||||
lines.append(cmd_copy(out, "{inc_dir}"))
|
||||
for out in dep.get("libs", []):
|
||||
lines.append(cmd_copy(out, "{lib_dir}"))
|
||||
for out in dep.get("bins", []):
|
||||
lines.append(cmd_copy(out, "{bin_dir}"))
|
||||
return lines
|
||||
return (
|
||||
[cmd_copy(out, "{inc_dir}") for out in dep.get("headers", [])]
|
||||
+ [cmd_copy(out, "{lib_dir}") for out in dep.get("libs", [])]
|
||||
+ [cmd_copy(out, "{bin_dir}") for out in dep.get("bins", [])]
|
||||
)
|
||||
|
||||
|
||||
def build_env(prefs: dict[str, str], verbose: bool) -> None:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user