mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-03-12 16:55:47 +03:00
Merge branch 'main' into msys
This commit is contained in:
commit
799d908783
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
aptget_update()
|
aptget_update()
|
||||||
{
|
{
|
||||||
if [ ! -z $1 ]; then
|
if [ -n "$1" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Retrying apt-get update..."
|
echo "Retrying apt-get update..."
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
output=`sudo apt-get update 2>&1`
|
output=$(sudo apt-get update 2>&1)
|
||||||
echo "$output"
|
echo "$output"
|
||||||
if [[ $output == *[WE]:\ * ]]; then
|
if [[ $output == *[WE]:\ * ]]; then
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
cibuildwheel==2.22.0
|
cibuildwheel==2.23.0
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mypy==1.14.1
|
mypy==1.15.0
|
||||||
IceSpringPySideStubs-PyQt6
|
IceSpringPySideStubs-PyQt6
|
||||||
IceSpringPySideStubs-PySide6
|
IceSpringPySideStubs-PySide6
|
||||||
ipython
|
ipython
|
||||||
|
|
2
.github/workflows/test-windows.yml
vendored
2
.github/workflows/test-windows.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||||
architecture: ["x64"]
|
architecture: ["x64"]
|
||||||
os: ["windows-latest"]
|
os: ["windows-latest"]
|
||||||
include:
|
include:
|
||||||
|
|
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -41,6 +41,7 @@ jobs:
|
||||||
"ubuntu-latest",
|
"ubuntu-latest",
|
||||||
]
|
]
|
||||||
python-version: [
|
python-version: [
|
||||||
|
"pypy3.11",
|
||||||
"pypy3.10",
|
"pypy3.10",
|
||||||
"3.14",
|
"3.14",
|
||||||
"3.13t",
|
"3.13t",
|
||||||
|
|
29
.github/workflows/wheels-dependencies.sh
vendored
29
.github/workflows/wheels-dependencies.sh
vendored
|
@ -38,14 +38,14 @@ ARCHIVE_SDIR=pillow-depends-main
|
||||||
|
|
||||||
# Package versions for fresh source builds
|
# Package versions for fresh source builds
|
||||||
FREETYPE_VERSION=2.13.3
|
FREETYPE_VERSION=2.13.3
|
||||||
HARFBUZZ_VERSION=10.2.0
|
HARFBUZZ_VERSION=10.4.0
|
||||||
LIBPNG_VERSION=1.6.46
|
LIBPNG_VERSION=1.6.47
|
||||||
JPEGTURBO_VERSION=3.1.0
|
JPEGTURBO_VERSION=3.1.0
|
||||||
OPENJPEG_VERSION=2.5.3
|
OPENJPEG_VERSION=2.5.3
|
||||||
XZ_VERSION=5.6.4
|
XZ_VERSION=5.6.4
|
||||||
TIFF_VERSION=4.6.0
|
TIFF_VERSION=4.6.0
|
||||||
LCMS2_VERSION=2.16
|
LCMS2_VERSION=2.17
|
||||||
ZLIB_NG_VERSION=2.2.3
|
ZLIB_NG_VERSION=2.2.4
|
||||||
LIBWEBP_VERSION=1.5.0
|
LIBWEBP_VERSION=1.5.0
|
||||||
BZIP2_VERSION=1.0.8
|
BZIP2_VERSION=1.0.8
|
||||||
LIBXCB_VERSION=1.17.0
|
LIBXCB_VERSION=1.17.0
|
||||||
|
@ -54,13 +54,10 @@ BROTLI_VERSION=1.1.0
|
||||||
function build_pkg_config {
|
function build_pkg_config {
|
||||||
if [ -e pkg-config-stamp ]; then return; fi
|
if [ -e pkg-config-stamp ]; then return; fi
|
||||||
# This essentially duplicates the Homebrew recipe
|
# This essentially duplicates the Homebrew recipe
|
||||||
ORIGINAL_CFLAGS=$CFLAGS
|
CFLAGS="$CFLAGS -Wno-int-conversion" build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
|
||||||
CFLAGS="$CFLAGS -Wno-int-conversion"
|
|
||||||
build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
|
|
||||||
--disable-debug --disable-host-tool --with-internal-glib \
|
--disable-debug --disable-host-tool --with-internal-glib \
|
||||||
--with-pc-path=$BUILD_PREFIX/share/pkgconfig:$BUILD_PREFIX/lib/pkgconfig \
|
--with-pc-path=$BUILD_PREFIX/share/pkgconfig:$BUILD_PREFIX/lib/pkgconfig \
|
||||||
--with-system-include-path=$(xcrun --show-sdk-path --sdk macosx)/usr/include
|
--with-system-include-path=$(xcrun --show-sdk-path --sdk macosx)/usr/include
|
||||||
CFLAGS=$ORIGINAL_CFLAGS
|
|
||||||
export PKG_CONFIG=$BUILD_PREFIX/bin/pkg-config
|
export PKG_CONFIG=$BUILD_PREFIX/bin/pkg-config
|
||||||
touch pkg-config-stamp
|
touch pkg-config-stamp
|
||||||
}
|
}
|
||||||
|
@ -72,6 +69,14 @@ function build_zlib_ng {
|
||||||
&& ./configure --prefix=$BUILD_PREFIX --zlib-compat \
|
&& ./configure --prefix=$BUILD_PREFIX --zlib-compat \
|
||||||
&& make -j4 \
|
&& make -j4 \
|
||||||
&& make install)
|
&& make install)
|
||||||
|
|
||||||
|
if [ -n "$IS_MACOS" ]; then
|
||||||
|
# Ensure that on macOS, the library name is an absolute path, not an
|
||||||
|
# @rpath, so that delocate picks up the right library (and doesn't need
|
||||||
|
# DYLD_LIBRARY_PATH to be set). The default Makefile doesn't have an
|
||||||
|
# option to control the install_name.
|
||||||
|
install_name_tool -id $BUILD_PREFIX/lib/libz.1.dylib $BUILD_PREFIX/lib/libz.1.dylib
|
||||||
|
fi
|
||||||
touch zlib-stamp
|
touch zlib-stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,15 +135,13 @@ function build {
|
||||||
build_lcms2
|
build_lcms2
|
||||||
build_openjpeg
|
build_openjpeg
|
||||||
|
|
||||||
ORIGINAL_CFLAGS=$CFLAGS
|
webp_cflags="-O3 -DNDEBUG"
|
||||||
CFLAGS="$CFLAGS -O3 -DNDEBUG"
|
|
||||||
if [[ -n "$IS_MACOS" ]]; then
|
if [[ -n "$IS_MACOS" ]]; then
|
||||||
CFLAGS="$CFLAGS -Wl,-headerpad_max_install_names"
|
webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names"
|
||||||
fi
|
fi
|
||||||
build_simple libwebp $LIBWEBP_VERSION \
|
CFLAGS="$CFLAGS $webp_cflags" build_simple libwebp $LIBWEBP_VERSION \
|
||||||
https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \
|
https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \
|
||||||
--enable-libwebpmux --enable-libwebpdemux
|
--enable-libwebpmux --enable-libwebpdemux
|
||||||
CFLAGS=$ORIGINAL_CFLAGS
|
|
||||||
|
|
||||||
build_brotli
|
build_brotli
|
||||||
|
|
||||||
|
|
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
|
@ -63,7 +63,7 @@ jobs:
|
||||||
- name: "macOS 10.15 x86_64"
|
- name: "macOS 10.15 x86_64"
|
||||||
os: macos-13
|
os: macos-13
|
||||||
cibw_arch: x86_64
|
cibw_arch: x86_64
|
||||||
build: "pp310*"
|
build: "pp3*"
|
||||||
macosx_deployment_target: "10.15"
|
macosx_deployment_target: "10.15"
|
||||||
- name: "macOS arm64"
|
- name: "macOS arm64"
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.9.4
|
rev: v0.9.9
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: [--exit-non-zero-on-fix]
|
args: [--exit-non-zero-on-fix]
|
||||||
|
@ -11,7 +11,7 @@ repos:
|
||||||
- id: black
|
- id: black
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/bandit
|
- repo: https://github.com/PyCQA/bandit
|
||||||
rev: 1.8.2
|
rev: 1.8.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: bandit
|
- id: bandit
|
||||||
args: [--severity-level=high]
|
args: [--severity-level=high]
|
||||||
|
@ -50,14 +50,14 @@ repos:
|
||||||
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
|
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
|
||||||
|
|
||||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||||
rev: 0.31.1
|
rev: 0.31.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-github-workflows
|
- id: check-github-workflows
|
||||||
- id: check-readthedocs
|
- id: check-readthedocs
|
||||||
- id: check-renovate
|
- id: check-renovate
|
||||||
|
|
||||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||||
rev: v1.3.0
|
rev: v1.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: zizmor
|
- id: zizmor
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ repos:
|
||||||
- id: sphinx-lint
|
- id: sphinx-lint
|
||||||
|
|
||||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
rev: v2.5.0
|
rev: v2.5.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyproject-fmt
|
- id: pyproject-fmt
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
|
||||||
import tempfile
|
import tempfile
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
@ -342,10 +341,6 @@ def is_pypy() -> bool:
|
||||||
return hasattr(sys, "pypy_translation_info")
|
return hasattr(sys, "pypy_translation_info")
|
||||||
|
|
||||||
|
|
||||||
def is_mingw() -> bool:
|
|
||||||
return sysconfig.get_platform() == "mingw"
|
|
||||||
|
|
||||||
|
|
||||||
class CachedProperty:
|
class CachedProperty:
|
||||||
def __init__(self, func: Callable[[Any], Any]) -> None:
|
def __init__(self, func: Callable[[Any], Any]) -> None:
|
||||||
self.func = func
|
self.func = func
|
||||||
|
|
|
@ -26,12 +26,12 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
|
|
@ -331,11 +331,13 @@ def test_dxt5_colorblock_alpha_issue_4142() -> None:
|
||||||
|
|
||||||
with Image.open("Tests/images/dxt5-colorblock-alpha-issue-4142.dds") as im:
|
with Image.open("Tests/images/dxt5-colorblock-alpha-issue-4142.dds") as im:
|
||||||
px = im.getpixel((0, 0))
|
px = im.getpixel((0, 0))
|
||||||
|
assert isinstance(px, tuple)
|
||||||
assert px[0] != 0
|
assert px[0] != 0
|
||||||
assert px[1] != 0
|
assert px[1] != 0
|
||||||
assert px[2] != 0
|
assert px[2] != 0
|
||||||
|
|
||||||
px = im.getpixel((1, 0))
|
px = im.getpixel((1, 0))
|
||||||
|
assert isinstance(px, tuple)
|
||||||
assert px[0] != 0
|
assert px[0] != 0
|
||||||
assert px[1] != 0
|
assert px[1] != 0
|
||||||
assert px[2] != 0
|
assert px[2] != 0
|
||||||
|
|
|
@ -95,10 +95,14 @@ def test_sanity(filename: str, size: tuple[int, int], scale: int) -> None:
|
||||||
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with Image.open(FILE1) as im:
|
with Image.open(FILE1) as im:
|
||||||
assert im.load()[0, 0] == (255, 255, 255)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (255, 255, 255)
|
||||||
|
|
||||||
# Test again now that it has already been loaded once
|
# Test again now that it has already been loaded once
|
||||||
assert im.load()[0, 0] == (255, 255, 255)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (255, 255, 255)
|
||||||
|
|
||||||
|
|
||||||
def test_binary() -> None:
|
def test_binary() -> None:
|
||||||
|
|
|
@ -52,12 +52,12 @@ def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import io
|
||||||
|
import struct
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FtexImagePlugin, Image
|
from PIL import FtexImagePlugin, Image
|
||||||
|
@ -23,3 +26,15 @@ def test_invalid_file() -> None:
|
||||||
|
|
||||||
with pytest.raises(SyntaxError):
|
with pytest.raises(SyntaxError):
|
||||||
FtexImagePlugin.FtexImageFile(invalid_file)
|
FtexImagePlugin.FtexImageFile(invalid_file)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_texture() -> None:
|
||||||
|
with open("Tests/images/ftex_dxt1.ftc", "rb") as fp:
|
||||||
|
data = fp.read()
|
||||||
|
|
||||||
|
# Change texture compression format
|
||||||
|
data = data[:24] + struct.pack("<i", 2) + data[28:]
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Invalid texture compression format: 2"):
|
||||||
|
with Image.open(io.BytesIO(data)):
|
||||||
|
pass
|
||||||
|
|
|
@ -14,10 +14,14 @@ def test_gbr_file() -> None:
|
||||||
|
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with Image.open("Tests/images/gbr.gbr") as im:
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (0, 0, 0, 0)
|
||||||
|
|
||||||
# Test again now that it has already been loaded once
|
# Test again now that it has already been loaded once
|
||||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_load_operations() -> None:
|
def test_multiple_load_operations() -> None:
|
||||||
|
|
|
@ -4,6 +4,8 @@ import pytest
|
||||||
|
|
||||||
from PIL import GdImageFile, UnidentifiedImageError
|
from PIL import GdImageFile, UnidentifiedImageError
|
||||||
|
|
||||||
|
from .helper import assert_image_similar_tofile
|
||||||
|
|
||||||
TEST_GD_FILE = "Tests/images/hopper.gd"
|
TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,6 +13,7 @@ def test_sanity() -> None:
|
||||||
with GdImageFile.open(TEST_GD_FILE) as im:
|
with GdImageFile.open(TEST_GD_FILE) as im:
|
||||||
assert im.size == (128, 128)
|
assert im.size == (128, 128)
|
||||||
assert im.format == "GD"
|
assert im.format == "GD"
|
||||||
|
assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.jpg", 14)
|
||||||
|
|
||||||
|
|
||||||
def test_bad_mode() -> None:
|
def test_bad_mode() -> None:
|
||||||
|
|
|
@ -22,9 +22,6 @@ from .helper import (
|
||||||
# sample gif stream
|
# sample gif stream
|
||||||
TEST_GIF = "Tests/images/hopper.gif"
|
TEST_GIF = "Tests/images/hopper.gif"
|
||||||
|
|
||||||
with open(TEST_GIF, "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
|
|
||||||
def test_sanity() -> None:
|
def test_sanity() -> None:
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
|
@ -37,12 +34,12 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(TEST_GIF)
|
im = Image.open(TEST_GIF)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
@ -310,6 +307,7 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
|
||||||
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
||||||
with Image.open(path) as im:
|
with Image.open(path) as im:
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
|
assert im.palette is not None
|
||||||
first_frame_colors = im.palette.colors.keys()
|
first_frame_colors = im.palette.colors.keys()
|
||||||
original_color = im.convert("RGB").getpixel((0, 0))
|
original_color = im.convert("RGB").getpixel((0, 0))
|
||||||
|
|
||||||
|
@ -528,6 +526,7 @@ def test_dispose_background_transparency() -> None:
|
||||||
with Image.open("Tests/images/dispose_bgnd_transparency.gif") as img:
|
with Image.open("Tests/images/dispose_bgnd_transparency.gif") as img:
|
||||||
img.seek(2)
|
img.seek(2)
|
||||||
px = img.load()
|
px = img.load()
|
||||||
|
assert px is not None
|
||||||
assert px[35, 30][3] == 0
|
assert px[35, 30][3] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -762,6 +761,21 @@ def test_dispose2_previous_frame(tmp_path: Path) -> None:
|
||||||
assert im.getpixel((0, 0)) == (0, 0, 0, 255)
|
assert im.getpixel((0, 0)) == (0, 0, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dispose2_without_transparency(tmp_path: Path) -> None:
|
||||||
|
out = str(tmp_path / "temp.gif")
|
||||||
|
|
||||||
|
im = Image.new("P", (100, 100))
|
||||||
|
|
||||||
|
im2 = Image.new("P", (100, 100), (0, 0, 0))
|
||||||
|
im2.putpixel((50, 50), (255, 0, 0))
|
||||||
|
|
||||||
|
im.save(out, save_all=True, append_images=[im2], disposal=2)
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
reloaded.seek(1)
|
||||||
|
assert reloaded.tile[0].extents == (0, 0, 100, 100)
|
||||||
|
|
||||||
|
|
||||||
def test_transparency_in_second_frame(tmp_path: Path) -> None:
|
def test_transparency_in_second_frame(tmp_path: Path) -> None:
|
||||||
out = str(tmp_path / "temp.gif")
|
out = str(tmp_path / "temp.gif")
|
||||||
with Image.open("Tests/images/different_transparency.gif") as im:
|
with Image.open("Tests/images/different_transparency.gif") as im:
|
||||||
|
@ -1311,6 +1325,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
|
||||||
with Image.open(out) as im:
|
with Image.open(out) as im:
|
||||||
# Assert that the frames are correct, and each frame has the same palette
|
# Assert that the frames are correct, and each frame has the same palette
|
||||||
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
||||||
|
assert im.palette is not None
|
||||||
assert im.palette.palette == im.global_palette.palette
|
assert im.palette.palette == im.global_palette.palette
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
@ -1345,32 +1360,30 @@ def test_save_I(tmp_path: Path) -> None:
|
||||||
assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
||||||
|
|
||||||
|
|
||||||
def test_getdata() -> None:
|
def test_getdata(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
# Test getheader/getdata against legacy values.
|
# Test getheader/getdata against legacy values.
|
||||||
# Create a 'P' image with holes in the palette.
|
# Create a 'P' image with holes in the palette.
|
||||||
im = Image._wedge().resize((16, 16), Image.Resampling.NEAREST)
|
im = Image.linear_gradient(mode="L").resize((16, 16), Image.Resampling.NEAREST)
|
||||||
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
||||||
im.info = {"background": 0}
|
im.info = {"background": 0}
|
||||||
|
|
||||||
passed_palette = bytes(255 - i // 3 for i in range(768))
|
passed_palette = bytes(255 - i // 3 for i in range(768))
|
||||||
|
|
||||||
GifImagePlugin._FORCE_OPTIMIZE = True
|
monkeypatch.setattr(GifImagePlugin, "_FORCE_OPTIMIZE", True)
|
||||||
try:
|
|
||||||
h = GifImagePlugin.getheader(im, passed_palette)
|
|
||||||
d = GifImagePlugin.getdata(im)
|
|
||||||
|
|
||||||
import pickle
|
h = GifImagePlugin.getheader(im, passed_palette)
|
||||||
|
d = GifImagePlugin.getdata(im)
|
||||||
|
|
||||||
# Enable to get target values on pre-refactor version
|
import pickle
|
||||||
# with open('Tests/images/gif_header_data.pkl', 'wb') as f:
|
|
||||||
# pickle.dump((h, d), f, 1)
|
|
||||||
with open("Tests/images/gif_header_data.pkl", "rb") as f:
|
|
||||||
(h_target, d_target) = pickle.load(f)
|
|
||||||
|
|
||||||
assert h == h_target
|
# Enable to get target values on pre-refactor version
|
||||||
assert d == d_target
|
# with open('Tests/images/gif_header_data.pkl', 'wb') as f:
|
||||||
finally:
|
# pickle.dump((h, d), f, 1)
|
||||||
GifImagePlugin._FORCE_OPTIMIZE = False
|
with open("Tests/images/gif_header_data.pkl", "rb") as f:
|
||||||
|
(h_target, d_target) = pickle.load(f)
|
||||||
|
|
||||||
|
assert h == h_target
|
||||||
|
assert d == d_target
|
||||||
|
|
||||||
|
|
||||||
def test_lzw_bits() -> None:
|
def test_lzw_bits() -> None:
|
||||||
|
|
|
@ -32,10 +32,14 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (0, 0, 0, 0)
|
||||||
|
|
||||||
# Test again now that it has already been loaded once
|
# Test again now that it has already been loaded once
|
||||||
assert im.load()[0, 0] == (0, 0, 0, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
def test_save(tmp_path: Path) -> None:
|
def test_save(tmp_path: Path) -> None:
|
||||||
|
|
|
@ -24,7 +24,9 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with Image.open(TEST_ICO_FILE) as im:
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
assert im.load()[0, 0] == (1, 1, 9, 255)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (1, 1, 9, 255)
|
||||||
|
|
||||||
|
|
||||||
def test_mask() -> None:
|
def test_mask() -> None:
|
||||||
|
|
|
@ -31,12 +31,12 @@ def test_name_limit(tmp_path: Path) -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(TEST_IM)
|
im = Image.open(TEST_IM)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
|
|
@ -63,6 +63,7 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
px = im.load()
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
assert px[0, 0] == (0, 0, 0)
|
assert px[0, 0] == (0, 0, 0)
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
assert im.size == (640, 480)
|
assert im.size == (640, 480)
|
||||||
|
@ -421,6 +422,7 @@ def test_subsampling_decode(name: str) -> None:
|
||||||
def test_pclr() -> None:
|
def test_pclr() -> None:
|
||||||
with Image.open(f"{EXTRA_DIR}/issue104_jpxstream.jp2") as im:
|
with Image.open(f"{EXTRA_DIR}/issue104_jpxstream.jp2") as im:
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
|
assert im.palette is not None
|
||||||
assert len(im.palette.colors) == 256
|
assert len(im.palette.colors) == 256
|
||||||
assert im.palette.colors[(255, 255, 255)] == 0
|
assert im.palette.colors[(255, 255, 255)] == 0
|
||||||
|
|
||||||
|
@ -428,6 +430,7 @@ def test_pclr() -> None:
|
||||||
f"{EXTRA_DIR}/147af3f1083de4393666b7d99b01b58b_signal_sigsegv_130c531_6155_5136.jp2"
|
f"{EXTRA_DIR}/147af3f1083de4393666b7d99b01b58b_signal_sigsegv_130c531_6155_5136.jp2"
|
||||||
) as im:
|
) as im:
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
|
assert im.palette is not None
|
||||||
assert len(im.palette.colors) == 139
|
assert len(im.palette.colors) == 139
|
||||||
assert im.palette.colors[(0, 0, 0, 0)] == 0
|
assert im.palette.colors[(0, 0, 0, 0)] == 0
|
||||||
|
|
||||||
|
|
|
@ -29,21 +29,26 @@ def roundtrip(im: Image.Image, **options: Any) -> ImageFile.ImageFile:
|
||||||
|
|
||||||
@pytest.mark.parametrize("test_file", test_files)
|
@pytest.mark.parametrize("test_file", test_files)
|
||||||
def test_sanity(test_file: str) -> None:
|
def test_sanity(test_file: str) -> None:
|
||||||
with Image.open(test_file) as im:
|
def check(im: ImageFile.ImageFile) -> None:
|
||||||
im.load()
|
im.load()
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
assert im.size == (640, 480)
|
assert im.size == (640, 480)
|
||||||
assert im.format == "MPO"
|
assert im.format == "MPO"
|
||||||
|
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
check(im)
|
||||||
|
with MpoImagePlugin.MpoImageFile(test_file) as im:
|
||||||
|
check(im)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(test_files[0])
|
im = Image.open(test_files[0])
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
@ -77,8 +82,8 @@ def test_app(test_file: str) -> None:
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
assert im.applist[0][0] == "APP1"
|
assert im.applist[0][0] == "APP1"
|
||||||
assert im.applist[1][0] == "APP2"
|
assert im.applist[1][0] == "APP2"
|
||||||
assert (
|
assert im.applist[1][1].startswith(
|
||||||
im.applist[1][1][:16] == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
|
b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
|
||||||
)
|
)
|
||||||
assert len(im.applist) == 2
|
assert len(im.applist) == 2
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ def test_arbitrary_maxval(
|
||||||
assert im.mode == mode
|
assert im.mode == mode
|
||||||
|
|
||||||
px = im.load()
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
assert tuple(px[x, 0] for x in range(3)) == pixels
|
assert tuple(px[x, 0] for x in range(3)) == pixels
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,12 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
|
|
@ -24,12 +24,12 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
|
|
||||||
def test_closed_file() -> None:
|
def test_closed_file() -> None:
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, SunImagePlugin
|
from PIL import Image, SunImagePlugin, _binary
|
||||||
|
|
||||||
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
|
from .helper import assert_image_equal_tofile, assert_image_similar, hopper
|
||||||
|
|
||||||
|
@ -33,6 +34,60 @@ def test_im1() -> None:
|
||||||
assert_image_equal_tofile(im, "Tests/images/sunraster.im1.png")
|
assert_image_equal_tofile(im, "Tests/images/sunraster.im1.png")
|
||||||
|
|
||||||
|
|
||||||
|
def _sun_header(
|
||||||
|
depth: int = 0, file_type: int = 0, palette_length: int = 0
|
||||||
|
) -> io.BytesIO:
|
||||||
|
return io.BytesIO(
|
||||||
|
_binary.o32be(0x59A66A95)
|
||||||
|
+ b"\x00" * 8
|
||||||
|
+ _binary.o32be(depth)
|
||||||
|
+ b"\x00" * 4
|
||||||
|
+ _binary.o32be(file_type)
|
||||||
|
+ b"\x00" * 4
|
||||||
|
+ _binary.o32be(palette_length)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_mode_bit_depth() -> None:
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported Mode/Bit Depth"):
|
||||||
|
with SunImagePlugin.SunImageFile(_sun_header()):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_color_palette_length() -> None:
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported Color Palette Length"):
|
||||||
|
with SunImagePlugin.SunImageFile(_sun_header(depth=1, palette_length=1025)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_palette_type() -> None:
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported Palette Type"):
|
||||||
|
with SunImagePlugin.SunImageFile(_sun_header(depth=1, palette_length=1)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_file_type() -> None:
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported Sun Raster file type"):
|
||||||
|
with SunImagePlugin.SunImageFile(_sun_header(depth=1, file_type=6)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
|
||||||
|
)
|
||||||
|
def test_rgbx() -> None:
|
||||||
|
with open(os.path.join(EXTRA_DIR, "32bpp.ras"), "rb") as fp:
|
||||||
|
data = fp.read()
|
||||||
|
|
||||||
|
# Set file type to 3
|
||||||
|
data = data[:20] + _binary.o32be(3) + data[24:]
|
||||||
|
|
||||||
|
with Image.open(io.BytesIO(data)) as im:
|
||||||
|
r, g, b = im.split()
|
||||||
|
im = Image.merge("RGB", (b, g, r))
|
||||||
|
assert_image_equal_tofile(im, os.path.join(EXTRA_DIR, "32bpp.png"))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
|
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -29,6 +30,22 @@ def test_sanity(codec: str, test_path: str, format: str) -> None:
|
||||||
assert im.format == format
|
assert im.format == format
|
||||||
|
|
||||||
|
|
||||||
|
def test_unexpected_end(tmp_path: Path) -> None:
|
||||||
|
tmpfile = str(tmp_path / "temp.tar")
|
||||||
|
with open(tmpfile, "w"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with pytest.raises(OSError, match="unexpected end of tar file"):
|
||||||
|
with TarIO.TarIO(tmpfile, "test"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_cannot_find_subfile() -> None:
|
||||||
|
with pytest.raises(OSError, match="cannot find subfile"):
|
||||||
|
with TarIO.TarIO(TEST_TAR_FILE, "test"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file() -> None:
|
def test_unclosed_file() -> None:
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
|
|
|
@ -72,6 +72,7 @@ def test_palette_depth_8(tmp_path: Path) -> None:
|
||||||
|
|
||||||
def test_palette_depth_16(tmp_path: Path) -> None:
|
def test_palette_depth_16(tmp_path: Path) -> None:
|
||||||
with Image.open("Tests/images/p_16.tga") as im:
|
with Image.open("Tests/images/p_16.tga") as im:
|
||||||
|
assert im.palette is not None
|
||||||
assert im.palette.mode == "RGBA"
|
assert im.palette.mode == "RGBA"
|
||||||
assert_image_equal_tofile(im.convert("RGBA"), "Tests/images/p_16.png")
|
assert_image_equal_tofile(im.convert("RGBA"), "Tests/images/p_16.png")
|
||||||
|
|
||||||
|
@ -213,10 +214,14 @@ def test_save_orientation(tmp_path: Path) -> None:
|
||||||
def test_horizontal_orientations() -> None:
|
def test_horizontal_orientations() -> None:
|
||||||
# These images have been manually hexedited to have the relevant orientations
|
# These images have been manually hexedited to have the relevant orientations
|
||||||
with Image.open("Tests/images/rgb32rle_top_right.tga") as im:
|
with Image.open("Tests/images/rgb32rle_top_right.tga") as im:
|
||||||
assert im.load()[90, 90][:3] == (0, 0, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[90, 90][:3] == (0, 0, 0)
|
||||||
|
|
||||||
with Image.open("Tests/images/rgb32rle_bottom_right.tga") as im:
|
with Image.open("Tests/images/rgb32rle_bottom_right.tga") as im:
|
||||||
assert im.load()[90, 90][:3] == (0, 255, 0)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[90, 90][:3] == (0, 255, 0)
|
||||||
|
|
||||||
|
|
||||||
def test_save_rle(tmp_path: Path) -> None:
|
def test_save_rle(tmp_path: Path) -> None:
|
||||||
|
|
|
@ -63,12 +63,12 @@ class TestFileTiff:
|
||||||
|
|
||||||
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
@pytest.mark.skipif(is_pypy(), reason="Requires CPython")
|
||||||
def test_unclosed_file(self) -> None:
|
def test_unclosed_file(self) -> None:
|
||||||
def open() -> None:
|
def open_test_image() -> None:
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
with pytest.warns(ResourceWarning):
|
with pytest.warns(ResourceWarning):
|
||||||
open()
|
open_test_image()
|
||||||
|
|
||||||
def test_closed_file(self) -> None:
|
def test_closed_file(self) -> None:
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
|
|
|
@ -21,7 +21,11 @@ def test_open() -> None:
|
||||||
|
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with WalImageFile.open(TEST_FILE) as im:
|
with WalImageFile.open(TEST_FILE) as im:
|
||||||
assert im.load()[0, 0] == 122
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == 122
|
||||||
|
|
||||||
# Test again now that it has already been loaded once
|
# Test again now that it has already been loaded once
|
||||||
assert im.load()[0, 0] == 122
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == 122
|
||||||
|
|
|
@ -40,7 +40,7 @@ def test_read_exif_metadata() -> None:
|
||||||
def test_read_exif_metadata_without_prefix() -> None:
|
def test_read_exif_metadata_without_prefix() -> None:
|
||||||
with Image.open("Tests/images/flower2.webp") as im:
|
with Image.open("Tests/images/flower2.webp") as im:
|
||||||
# Assert prefix is not present
|
# Assert prefix is not present
|
||||||
assert im.info["exif"][:6] != b"Exif\x00\x00"
|
assert not im.info["exif"].startswith(b"Exif\x00\x00")
|
||||||
|
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
assert exif[305] == "Adobe Photoshop CS6 (Macintosh)"
|
assert exif[305] == "Adobe Photoshop CS6 (Macintosh)"
|
||||||
|
|
|
@ -32,7 +32,9 @@ def test_load_raw() -> None:
|
||||||
def test_load() -> None:
|
def test_load() -> None:
|
||||||
with Image.open("Tests/images/drawing.emf") as im:
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
assert im.load()[0, 0] == (255, 255, 255)
|
px = im.load()
|
||||||
|
assert px is not None
|
||||||
|
assert px[0, 0] == (255, 255, 255)
|
||||||
|
|
||||||
|
|
||||||
def test_load_zero_inch() -> None:
|
def test_load_zero_inch() -> None:
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BdfFontFile, FontFile
|
from PIL import BdfFontFile, FontFile
|
||||||
|
@ -8,13 +10,20 @@ filename = "Tests/images/courB08.bdf"
|
||||||
|
|
||||||
|
|
||||||
def test_sanity() -> None:
|
def test_sanity() -> None:
|
||||||
with open(filename, "rb") as test_file:
|
with open(filename, "rb") as fp:
|
||||||
font = BdfFontFile.BdfFontFile(test_file)
|
font = BdfFontFile.BdfFontFile(fp)
|
||||||
|
|
||||||
assert isinstance(font, FontFile.FontFile)
|
assert isinstance(font, FontFile.FontFile)
|
||||||
assert len([_f for _f in font.glyph if _f]) == 190
|
assert len([_f for _f in font.glyph if _f]) == 190
|
||||||
|
|
||||||
|
|
||||||
|
def test_zero_width_chars() -> None:
|
||||||
|
with open(filename, "rb") as fp:
|
||||||
|
data = fp.read()
|
||||||
|
data = data[:2650] + b"\x00\x00" + data[2652:]
|
||||||
|
BdfFontFile.BdfFontFile(io.BytesIO(data))
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_file() -> None:
|
def test_invalid_file() -> None:
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
with pytest.raises(SyntaxError):
|
with pytest.raises(SyntaxError):
|
||||||
|
|
|
@ -4,7 +4,20 @@ from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FontFile
|
from PIL import FontFile, Image
|
||||||
|
|
||||||
|
|
||||||
|
def test_compile() -> None:
|
||||||
|
font = FontFile.FontFile()
|
||||||
|
font.glyph[0] = ((0, 0), (0, 0, 0, 0), (0, 0, 0, 1), Image.new("L", (0, 0)))
|
||||||
|
font.compile()
|
||||||
|
assert font.ysize == 1
|
||||||
|
|
||||||
|
font.ysize = 2
|
||||||
|
font.compile()
|
||||||
|
|
||||||
|
# Assert that compiling again did not change anything
|
||||||
|
assert font.ysize == 2
|
||||||
|
|
||||||
|
|
||||||
def test_save(tmp_path: Path) -> None:
|
def test_save(tmp_path: Path) -> None:
|
||||||
|
|
|
@ -22,28 +22,26 @@ def test_sanity() -> None:
|
||||||
Image.new("HSV", (100, 100))
|
Image.new("HSV", (100, 100))
|
||||||
|
|
||||||
|
|
||||||
def wedge() -> Image.Image:
|
def linear_gradient() -> Image.Image:
|
||||||
w = Image._wedge()
|
im = Image.linear_gradient(mode="L")
|
||||||
w90 = w.rotate(90)
|
im90 = im.rotate(90)
|
||||||
|
|
||||||
(px, h) = w.size
|
(px, h) = im.size
|
||||||
|
|
||||||
r = Image.new("L", (px * 3, h))
|
r = Image.new("L", (px * 3, h))
|
||||||
g = r.copy()
|
g = r.copy()
|
||||||
b = r.copy()
|
b = r.copy()
|
||||||
|
|
||||||
r.paste(w, (0, 0))
|
r.paste(im, (0, 0))
|
||||||
r.paste(w90, (px, 0))
|
r.paste(im90, (px, 0))
|
||||||
|
|
||||||
g.paste(w90, (0, 0))
|
g.paste(im90, (0, 0))
|
||||||
g.paste(w, (2 * px, 0))
|
g.paste(im, (2 * px, 0))
|
||||||
|
|
||||||
b.paste(w, (px, 0))
|
b.paste(im, (px, 0))
|
||||||
b.paste(w90, (2 * px, 0))
|
b.paste(im90, (2 * px, 0))
|
||||||
|
|
||||||
img = Image.merge("RGB", (r, g, b))
|
return Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
return img
|
|
||||||
|
|
||||||
|
|
||||||
def to_xxx_colorsys(
|
def to_xxx_colorsys(
|
||||||
|
@ -79,8 +77,8 @@ def to_rgb_colorsys(im: Image.Image) -> Image.Image:
|
||||||
return to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB")
|
return to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB")
|
||||||
|
|
||||||
|
|
||||||
def test_wedge() -> None:
|
def test_linear_gradient() -> None:
|
||||||
src = wedge().resize((3 * 32, 32), Image.Resampling.BILINEAR)
|
src = linear_gradient().resize((3 * 32, 32), Image.Resampling.BILINEAR)
|
||||||
im = src.convert("HSV")
|
im = src.convert("HSV")
|
||||||
comparable = to_hsv_colorsys(src)
|
comparable = to_hsv_colorsys(src)
|
||||||
|
|
||||||
|
|
|
@ -74,12 +74,12 @@ class TestImage:
|
||||||
|
|
||||||
def test_sanity(self) -> None:
|
def test_sanity(self) -> None:
|
||||||
im = Image.new("L", (100, 100))
|
im = Image.new("L", (100, 100))
|
||||||
assert repr(im)[:45] == "<PIL.Image.Image image mode=L size=100x100 at"
|
assert repr(im).startswith("<PIL.Image.Image image mode=L size=100x100 at")
|
||||||
assert im.mode == "L"
|
assert im.mode == "L"
|
||||||
assert im.size == (100, 100)
|
assert im.size == (100, 100)
|
||||||
|
|
||||||
im = Image.new("RGB", (100, 100))
|
im = Image.new("RGB", (100, 100))
|
||||||
assert repr(im)[:45] == "<PIL.Image.Image image mode=RGB size=100x100 "
|
assert repr(im).startswith("<PIL.Image.Image image mode=RGB size=100x100 ")
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
assert im.size == (100, 100)
|
assert im.size == (100, 100)
|
||||||
|
|
||||||
|
@ -658,6 +658,7 @@ class TestImage:
|
||||||
im.putpalette(list(range(256)) * 4, "RGBA")
|
im.putpalette(list(range(256)) * 4, "RGBA")
|
||||||
im_remapped = im.remap_palette(list(range(256)))
|
im_remapped = im.remap_palette(list(range(256)))
|
||||||
assert_image_equal(im, im_remapped)
|
assert_image_equal(im, im_remapped)
|
||||||
|
assert im.palette is not None
|
||||||
assert im.palette.palette == im_remapped.palette.palette
|
assert im.palette.palette == im_remapped.palette.palette
|
||||||
|
|
||||||
# Test illegal image mode
|
# Test illegal image mode
|
||||||
|
|
|
@ -234,6 +234,7 @@ def test_gif_with_rgba_palette_to_p() -> None:
|
||||||
with Image.open("Tests/images/hopper.gif") as im:
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
im.info["transparency"] = 255
|
im.info["transparency"] = 255
|
||||||
im.load()
|
im.load()
|
||||||
|
assert im.palette is not None
|
||||||
assert im.palette.mode == "RGB"
|
assert im.palette.mode == "RGB"
|
||||||
im_p = im.convert("P")
|
im_p = im.convert("P")
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class TestImageTransform:
|
||||||
transformed = im.transform(
|
transformed = im.transform(
|
||||||
im.size, Image.Transform.AFFINE, [1, 0, 0, 0, 1, 0]
|
im.size, Image.Transform.AFFINE, [1, 0, 0, 0, 1, 0]
|
||||||
)
|
)
|
||||||
|
assert im.palette is not None
|
||||||
assert im.palette.palette == transformed.palette.palette
|
assert im.palette.palette == transformed.palette.palette
|
||||||
|
|
||||||
def test_extent(self) -> None:
|
def test_extent(self) -> None:
|
||||||
|
|
|
@ -448,7 +448,6 @@ def test_shape1() -> None:
|
||||||
x3, y3 = 95, 5
|
x3, y3 = 95, 5
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
assert ImageDraw.Outline is not None
|
|
||||||
s = ImageDraw.Outline()
|
s = ImageDraw.Outline()
|
||||||
s.move(x0, y0)
|
s.move(x0, y0)
|
||||||
s.curve(x1, y1, x2, y2, x3, y3)
|
s.curve(x1, y1, x2, y2, x3, y3)
|
||||||
|
@ -470,7 +469,6 @@ def test_shape2() -> None:
|
||||||
x3, y3 = 5, 95
|
x3, y3 = 5, 95
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
assert ImageDraw.Outline is not None
|
|
||||||
s = ImageDraw.Outline()
|
s = ImageDraw.Outline()
|
||||||
s.move(x0, y0)
|
s.move(x0, y0)
|
||||||
s.curve(x1, y1, x2, y2, x3, y3)
|
s.curve(x1, y1, x2, y2, x3, y3)
|
||||||
|
@ -489,7 +487,6 @@ def test_transform() -> None:
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
assert ImageDraw.Outline is not None
|
|
||||||
s = ImageDraw.Outline()
|
s = ImageDraw.Outline()
|
||||||
s.line(0, 0)
|
s.line(0, 0)
|
||||||
s.transform((0, 0, 0, 0, 0, 0))
|
s.transform((0, 0, 0, 0, 0, 0))
|
||||||
|
@ -1526,7 +1523,6 @@ def test_same_color_outline(bbox: Coords) -> None:
|
||||||
x2, y2 = 95, 50
|
x2, y2 = 95, 50
|
||||||
x3, y3 = 95, 5
|
x3, y3 = 95, 5
|
||||||
|
|
||||||
assert ImageDraw.Outline is not None
|
|
||||||
s = ImageDraw.Outline()
|
s = ImageDraw.Outline()
|
||||||
s.move(x0, y0)
|
s.move(x0, y0)
|
||||||
s.curve(x1, y1, x2, y2, x3, y3)
|
s.curve(x1, y1, x2, y2, x3, y3)
|
||||||
|
|
|
@ -448,6 +448,15 @@ def test_exif_transpose() -> None:
|
||||||
assert 0x0112 not in transposed_im.getexif()
|
assert 0x0112 not in transposed_im.getexif()
|
||||||
|
|
||||||
|
|
||||||
|
def test_exif_transpose_with_xmp_tuple() -> None:
|
||||||
|
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||||
|
assert im.getexif()[0x0112] == 3
|
||||||
|
|
||||||
|
im.info["xmp"] = (b"test",)
|
||||||
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert 0x0112 not in transposed_im.getexif()
|
||||||
|
|
||||||
|
|
||||||
def test_exif_transpose_xml_without_xmp() -> None:
|
def test_exif_transpose_xml_without_xmp() -> None:
|
||||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||||
assert im.getexif()[0x0112] == 3
|
assert im.getexif()[0x0112] == 3
|
||||||
|
|
|
@ -17,6 +17,7 @@ def test_sanity() -> None:
|
||||||
def test_reload() -> None:
|
def test_reload() -> None:
|
||||||
with Image.open("Tests/images/hopper.gif") as im:
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
original = im.copy()
|
original = im.copy()
|
||||||
|
assert im.palette is not None
|
||||||
im.palette.dirty = 1
|
im.palette.dirty = 1
|
||||||
assert_image_equal(im.convert("RGB"), original.convert("RGB"))
|
assert_image_equal(im.convert("RGB"), original.convert("RGB"))
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import PIL
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
needs_sphinx = "8.1"
|
needs_sphinx = "8.2"
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
@ -121,7 +121,7 @@ nitpicky = True
|
||||||
# generating warnings in “nitpicky mode”. Note that type should include the domain name
|
# generating warnings in “nitpicky mode”. Note that type should include the domain name
|
||||||
# if present. Example entries would be ('py:func', 'int') or
|
# if present. Example entries would be ('py:func', 'int') or
|
||||||
# ('envvar', 'LD_LIBRARY_PATH').
|
# ('envvar', 'LD_LIBRARY_PATH').
|
||||||
nitpick_ignore = [("py:class", "_io.BytesIO"), ("py:class", "_CmsProfileCompatible")]
|
nitpick_ignore = [("py:class", "_CmsProfileCompatible")]
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ----------------------------------------------
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
|
@ -285,7 +285,7 @@ Image.register_decoder("DXT5", DXT5Decoder)
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"DDS "
|
return prefix.startswith(b"DDS ")
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
||||||
|
|
|
@ -454,7 +454,8 @@ The :py:meth:`~PIL.Image.open` method may set the following
|
||||||
Raw EXIF data from the image.
|
Raw EXIF data from the image.
|
||||||
|
|
||||||
**comment**
|
**comment**
|
||||||
A comment about the image.
|
A comment about the image, from the COM marker. This is separate from the
|
||||||
|
UserComment tag that may be stored in the EXIF data.
|
||||||
|
|
||||||
.. versionadded:: 7.1.0
|
.. versionadded:: 7.1.0
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ true color.
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"SPAM"
|
return prefix.startswith(b"SPAM")
|
||||||
|
|
||||||
|
|
||||||
class SpamImageFile(ImageFile.ImageFile):
|
class SpamImageFile(ImageFile.ImageFile):
|
||||||
|
|
|
@ -51,7 +51,7 @@ Many of Pillow's features require external libraries:
|
||||||
* **littlecms** provides color management
|
* **littlecms** provides color management
|
||||||
|
|
||||||
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
|
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
|
||||||
above uses liblcms2. Tested with **1.19** and **2.7-2.16**.
|
above uses liblcms2. Tested with **1.19** and **2.7-2.17**.
|
||||||
|
|
||||||
* **libwebp** provides the WebP format.
|
* **libwebp** provides the WebP format.
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ dynamic = [
|
||||||
optional-dependencies.docs = [
|
optional-dependencies.docs = [
|
||||||
"furo",
|
"furo",
|
||||||
"olefile",
|
"olefile",
|
||||||
"sphinx>=8.1",
|
"sphinx>=8.2",
|
||||||
"sphinx-copybutton",
|
"sphinx-copybutton",
|
||||||
"sphinx-inline-tabs",
|
"sphinx-inline-tabs",
|
||||||
"sphinxext-opengraph",
|
"sphinxext-opengraph",
|
||||||
|
@ -104,7 +104,6 @@ test-extras = "tests"
|
||||||
|
|
||||||
[tool.cibuildwheel.macos.environment]
|
[tool.cibuildwheel.macos.environment]
|
||||||
PATH = "$(pwd)/build/deps/darwin/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
|
PATH = "$(pwd)/build/deps/darwin/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
|
||||||
DYLD_LIBRARY_PATH = "$(pwd)/build/deps/darwin/lib"
|
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
exclude = "wheels/multibuild"
|
exclude = "wheels/multibuild"
|
||||||
|
|
|
@ -26,17 +26,6 @@ from typing import BinaryIO
|
||||||
|
|
||||||
from . import FontFile, Image
|
from . import FontFile, Image
|
||||||
|
|
||||||
bdf_slant = {
|
|
||||||
"R": "Roman",
|
|
||||||
"I": "Italic",
|
|
||||||
"O": "Oblique",
|
|
||||||
"RI": "Reverse Italic",
|
|
||||||
"RO": "Reverse Oblique",
|
|
||||||
"OT": "Other",
|
|
||||||
}
|
|
||||||
|
|
||||||
bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
|
|
||||||
|
|
||||||
|
|
||||||
def bdf_char(
|
def bdf_char(
|
||||||
f: BinaryIO,
|
f: BinaryIO,
|
||||||
|
@ -54,7 +43,7 @@ def bdf_char(
|
||||||
s = f.readline()
|
s = f.readline()
|
||||||
if not s:
|
if not s:
|
||||||
return None
|
return None
|
||||||
if s[:9] == b"STARTCHAR":
|
if s.startswith(b"STARTCHAR"):
|
||||||
break
|
break
|
||||||
id = s[9:].strip().decode("ascii")
|
id = s[9:].strip().decode("ascii")
|
||||||
|
|
||||||
|
@ -62,7 +51,7 @@ def bdf_char(
|
||||||
props = {}
|
props = {}
|
||||||
while True:
|
while True:
|
||||||
s = f.readline()
|
s = f.readline()
|
||||||
if not s or s[:6] == b"BITMAP":
|
if not s or s.startswith(b"BITMAP"):
|
||||||
break
|
break
|
||||||
i = s.find(b" ")
|
i = s.find(b" ")
|
||||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||||
|
@ -71,7 +60,7 @@ def bdf_char(
|
||||||
bitmap = bytearray()
|
bitmap = bytearray()
|
||||||
while True:
|
while True:
|
||||||
s = f.readline()
|
s = f.readline()
|
||||||
if not s or s[:7] == b"ENDCHAR":
|
if not s or s.startswith(b"ENDCHAR"):
|
||||||
break
|
break
|
||||||
bitmap += s[:-1]
|
bitmap += s[:-1]
|
||||||
|
|
||||||
|
@ -107,7 +96,7 @@ class BdfFontFile(FontFile.FontFile):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
if s[:13] != b"STARTFONT 2.1":
|
if not s.startswith(b"STARTFONT 2.1"):
|
||||||
msg = "not a valid BDF file"
|
msg = "not a valid BDF file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
|
@ -116,7 +105,7 @@ class BdfFontFile(FontFile.FontFile):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
s = fp.readline()
|
s = fp.readline()
|
||||||
if not s or s[:13] == b"ENDPROPERTIES":
|
if not s or s.startswith(b"ENDPROPERTIES"):
|
||||||
break
|
break
|
||||||
i = s.find(b" ")
|
i = s.find(b" ")
|
||||||
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
|
||||||
|
|
|
@ -246,7 +246,7 @@ class BLPFormatError(NotImplementedError):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] in (b"BLP1", b"BLP2")
|
return prefix.startswith((b"BLP1", b"BLP2"))
|
||||||
|
|
||||||
|
|
||||||
class BlpImageFile(ImageFile.ImageFile):
|
class BlpImageFile(ImageFile.ImageFile):
|
||||||
|
|
|
@ -50,7 +50,7 @@ BIT2MODE = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:2] == b"BM"
|
return prefix.startswith(b"BM")
|
||||||
|
|
||||||
|
|
||||||
def _dib_accept(prefix: bytes) -> bool:
|
def _dib_accept(prefix: bytes) -> bool:
|
||||||
|
|
|
@ -33,7 +33,7 @@ def register_handler(handler: ImageFile.StubHandler | None) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
|
return prefix.startswith((b"BUFR", b"ZCZC"))
|
||||||
|
|
||||||
|
|
||||||
class BufrStubImageFile(ImageFile.StubImageFile):
|
class BufrStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
|
@ -26,7 +26,7 @@ from ._binary import i32le as i32
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"\0\0\2\0"
|
return prefix.startswith(b"\0\0\2\0")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -564,7 +564,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"DDS "
|
return prefix.startswith(b"DDS ")
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
||||||
|
|
|
@ -170,7 +170,9 @@ def Ghostscript(
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
|
return prefix.startswith(b"%!PS") or (
|
||||||
|
len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -295,7 +297,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
m = field.match(s)
|
m = field.match(s)
|
||||||
if m:
|
if m:
|
||||||
k = m.group(1)
|
k = m.group(1)
|
||||||
if k[:8] == "PS-Adobe":
|
if k.startswith("PS-Adobe"):
|
||||||
self.info["PS-Adobe"] = k[9:]
|
self.info["PS-Adobe"] = k[9:]
|
||||||
else:
|
else:
|
||||||
self.info[k] = ""
|
self.info[k] = ""
|
||||||
|
|
|
@ -17,7 +17,7 @@ from . import Image, ImageFile
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:6] == b"SIMPLE"
|
return prefix.startswith(b"SIMPLE")
|
||||||
|
|
||||||
|
|
||||||
class FitsImageFile(ImageFile.ImageFile):
|
class FitsImageFile(ImageFile.ImageFile):
|
||||||
|
|
|
@ -42,7 +42,7 @@ MODES = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:8] == olefile.MAGIC
|
return prefix.startswith(olefile.MAGIC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -79,8 +79,6 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||||
self._size = struct.unpack("<2i", self.fp.read(8))
|
self._size = struct.unpack("<2i", self.fp.read(8))
|
||||||
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
|
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
|
||||||
|
|
||||||
self._mode = "RGB"
|
|
||||||
|
|
||||||
# Only support single-format files.
|
# Only support single-format files.
|
||||||
# I don't know of any multi-format file.
|
# I don't know of any multi-format file.
|
||||||
assert format_count == 1
|
assert format_count == 1
|
||||||
|
@ -95,6 +93,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||||
self._mode = "RGBA"
|
self._mode = "RGBA"
|
||||||
self.tile = [ImageFile._Tile("bcn", (0, 0) + self.size, 0, (1,))]
|
self.tile = [ImageFile._Tile("bcn", (0, 0) + self.size, 0, (1,))]
|
||||||
elif format == Format.UNCOMPRESSED:
|
elif format == Format.UNCOMPRESSED:
|
||||||
|
self._mode = "RGB"
|
||||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, "RGB")]
|
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, "RGB")]
|
||||||
else:
|
else:
|
||||||
msg = f"Invalid texture compression format: {repr(format)}"
|
msg = f"Invalid texture compression format: {repr(format)}"
|
||||||
|
@ -108,7 +107,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == MAGIC
|
return prefix.startswith(MAGIC)
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(FtexImageFile.format, FtexImageFile, _accept)
|
Image.register_open(FtexImageFile.format, FtexImageFile, _accept)
|
||||||
|
|
|
@ -56,7 +56,7 @@ class GdImageFile(ImageFile.ImageFile):
|
||||||
msg = "Not a valid GD 2.x .gd file"
|
msg = "Not a valid GD 2.x .gd file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
self._mode = "L" # FIXME: "P"
|
self._mode = "P"
|
||||||
self._size = i16(s, 2), i16(s, 4)
|
self._size = i16(s, 2), i16(s, 4)
|
||||||
|
|
||||||
true_color = s[6]
|
true_color = s[6]
|
||||||
|
@ -68,14 +68,14 @@ class GdImageFile(ImageFile.ImageFile):
|
||||||
self.info["transparency"] = tindex
|
self.info["transparency"] = tindex
|
||||||
|
|
||||||
self.palette = ImagePalette.raw(
|
self.palette = ImagePalette.raw(
|
||||||
"XBGR", s[7 + true_color_offset + 4 : 7 + true_color_offset + 4 + 256 * 4]
|
"RGBX", s[7 + true_color_offset + 6 : 7 + true_color_offset + 6 + 256 * 4]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.tile = [
|
self.tile = [
|
||||||
ImageFile._Tile(
|
ImageFile._Tile(
|
||||||
"raw",
|
"raw",
|
||||||
(0, 0) + self.size,
|
(0, 0) + self.size,
|
||||||
7 + true_color_offset + 4 + 256 * 4,
|
7 + true_color_offset + 6 + 256 * 4,
|
||||||
"L",
|
"L",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -67,7 +67,7 @@ LOADING_STRATEGY = LoadingStrategy.RGB_AFTER_FIRST
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:6] in [b"GIF87a", b"GIF89a"]
|
return prefix.startswith((b"GIF87a", b"GIF89a"))
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -257,7 +257,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
# application extension
|
# application extension
|
||||||
#
|
#
|
||||||
info["extension"] = block, self.fp.tell()
|
info["extension"] = block, self.fp.tell()
|
||||||
if block[:11] == b"NETSCAPE2.0":
|
if block.startswith(b"NETSCAPE2.0"):
|
||||||
block = self.data()
|
block = self.data()
|
||||||
if block and len(block) >= 3 and block[0] == 1:
|
if block and len(block) >= 3 and block[0] == 1:
|
||||||
self.info["loop"] = i16(block, 1)
|
self.info["loop"] = i16(block, 1)
|
||||||
|
@ -689,16 +689,21 @@ def _write_multiple_frames(
|
||||||
im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"]
|
im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"]
|
||||||
continue
|
continue
|
||||||
if im_frames[-1].encoderinfo.get("disposal") == 2:
|
if im_frames[-1].encoderinfo.get("disposal") == 2:
|
||||||
if background_im is None:
|
# To appear correctly in viewers using a convention,
|
||||||
color = im.encoderinfo.get(
|
# only consider transparency, and not background color
|
||||||
"transparency", im.info.get("transparency", (0, 0, 0))
|
color = im.encoderinfo.get(
|
||||||
)
|
"transparency", im.info.get("transparency")
|
||||||
background = _get_background(im_frame, color)
|
)
|
||||||
background_im = Image.new("P", im_frame.size, background)
|
if color is not None:
|
||||||
first_palette = im_frames[0].im.palette
|
if background_im is None:
|
||||||
assert first_palette is not None
|
background = _get_background(im_frame, color)
|
||||||
background_im.putpalette(first_palette, first_palette.mode)
|
background_im = Image.new("P", im_frame.size, background)
|
||||||
bbox = _getbbox(background_im, im_frame)[1]
|
first_palette = im_frames[0].im.palette
|
||||||
|
assert first_palette is not None
|
||||||
|
background_im.putpalette(first_palette, first_palette.mode)
|
||||||
|
bbox = _getbbox(background_im, im_frame)[1]
|
||||||
|
else:
|
||||||
|
bbox = (0, 0) + im_frame.size
|
||||||
elif encoderinfo.get("optimize") and im_frame.mode != "1":
|
elif encoderinfo.get("optimize") and im_frame.mode != "1":
|
||||||
if "transparency" not in encoderinfo:
|
if "transparency" not in encoderinfo:
|
||||||
assert im_frame.palette is not None
|
assert im_frame.palette is not None
|
||||||
|
@ -764,7 +769,8 @@ def _write_multiple_frames(
|
||||||
if not palette:
|
if not palette:
|
||||||
frame_data.encoderinfo["include_color_table"] = True
|
frame_data.encoderinfo["include_color_table"] = True
|
||||||
|
|
||||||
im_frame = im_frame.crop(frame_data.bbox)
|
if frame_data.bbox != (0, 0) + im_frame.size:
|
||||||
|
im_frame = im_frame.crop(frame_data.bbox)
|
||||||
offset = frame_data.bbox[:2]
|
offset = frame_data.bbox[:2]
|
||||||
_write_frame_data(fp, im_frame, offset, frame_data.encoderinfo)
|
_write_frame_data(fp, im_frame, offset, frame_data.encoderinfo)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -116,7 +116,7 @@ class GimpGradientFile(GradientFile):
|
||||||
"""File handler for GIMP's gradient format."""
|
"""File handler for GIMP's gradient format."""
|
||||||
|
|
||||||
def __init__(self, fp: IO[bytes]) -> None:
|
def __init__(self, fp: IO[bytes]) -> None:
|
||||||
if fp.readline()[:13] != b"GIMP Gradient":
|
if not fp.readline().startswith(b"GIMP Gradient"):
|
||||||
msg = "not a GIMP gradient file"
|
msg = "not a GIMP gradient file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ class GimpPaletteFile:
|
||||||
def __init__(self, fp: IO[bytes]) -> None:
|
def __init__(self, fp: IO[bytes]) -> None:
|
||||||
palette = [o8(i) * 3 for i in range(256)]
|
palette = [o8(i) * 3 for i in range(256)]
|
||||||
|
|
||||||
if fp.readline()[:12] != b"GIMP Palette":
|
if not fp.readline().startswith(b"GIMP Palette"):
|
||||||
msg = "not a GIMP palette file"
|
msg = "not a GIMP palette file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ def register_handler(handler: ImageFile.StubHandler | None) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"GRIB" and prefix[7] == 1
|
return prefix.startswith(b"GRIB") and prefix[7] == 1
|
||||||
|
|
||||||
|
|
||||||
class GribStubImageFile(ImageFile.StubImageFile):
|
class GribStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
|
@ -33,7 +33,7 @@ def register_handler(handler: ImageFile.StubHandler | None) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:8] == b"\x89HDF\r\n\x1a\n"
|
return prefix.startswith(b"\x89HDF\r\n\x1a\n")
|
||||||
|
|
||||||
|
|
||||||
class HDF5StubImageFile(ImageFile.StubImageFile):
|
class HDF5StubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
|
@ -117,14 +117,14 @@ def read_png_or_jpeg2000(
|
||||||
sig = fobj.read(12)
|
sig = fobj.read(12)
|
||||||
|
|
||||||
im: Image.Image
|
im: Image.Image
|
||||||
if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a":
|
if sig.startswith(b"\x89PNG\x0d\x0a\x1a\x0a"):
|
||||||
fobj.seek(start)
|
fobj.seek(start)
|
||||||
im = PngImagePlugin.PngImageFile(fobj)
|
im = PngImagePlugin.PngImageFile(fobj)
|
||||||
Image._decompression_bomb_check(im.size)
|
Image._decompression_bomb_check(im.size)
|
||||||
return {"RGBA": im}
|
return {"RGBA": im}
|
||||||
elif (
|
elif (
|
||||||
sig[:4] == b"\xff\x4f\xff\x51"
|
sig.startswith(b"\xff\x4f\xff\x51")
|
||||||
or sig[:4] == b"\x0d\x0a\x87\x0a"
|
or sig.startswith(b"\x0d\x0a\x87\x0a")
|
||||||
or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a"
|
or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a"
|
||||||
):
|
):
|
||||||
if not enable_jpeg2k:
|
if not enable_jpeg2k:
|
||||||
|
@ -387,7 +387,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == MAGIC
|
return prefix.startswith(MAGIC)
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept)
|
Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept)
|
||||||
|
|
|
@ -118,7 +118,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == _MAGIC
|
return prefix.startswith(_MAGIC)
|
||||||
|
|
||||||
|
|
||||||
class IconHeader(NamedTuple):
|
class IconHeader(NamedTuple):
|
||||||
|
|
|
@ -155,9 +155,9 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
msg = "not an IM file"
|
msg = "not an IM file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
if s[-2:] == b"\r\n":
|
if s.endswith(b"\r\n"):
|
||||||
s = s[:-2]
|
s = s[:-2]
|
||||||
elif s[-1:] == b"\n":
|
elif s.endswith(b"\n"):
|
||||||
s = s[:-1]
|
s = s[:-1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -209,7 +209,7 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
self._mode = self.info[MODE]
|
self._mode = self.info[MODE]
|
||||||
|
|
||||||
# Skip forward to start of image data
|
# Skip forward to start of image data
|
||||||
while s and s[:1] != b"\x1a":
|
while s and not s.startswith(b"\x1a"):
|
||||||
s = self.fp.read(1)
|
s = self.fp.read(1)
|
||||||
if not s:
|
if not s:
|
||||||
msg = "File truncated"
|
msg = "File truncated"
|
||||||
|
@ -247,7 +247,7 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
self._fp = self.fp # FIXME: hack
|
self._fp = self.fp # FIXME: hack
|
||||||
|
|
||||||
if self.rawmode[:2] == "F;":
|
if self.rawmode.startswith("F;"):
|
||||||
# ifunc95 formats
|
# ifunc95 formats
|
||||||
try:
|
try:
|
||||||
# use bit decoder (if necessary)
|
# use bit decoder (if necessary)
|
||||||
|
|
|
@ -2996,15 +2996,6 @@ class ImageTransformHandler:
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Factories
|
# Factories
|
||||||
|
|
||||||
#
|
|
||||||
# Debugging
|
|
||||||
|
|
||||||
|
|
||||||
def _wedge() -> Image:
|
|
||||||
"""Create grayscale wedge (for debugging only)"""
|
|
||||||
|
|
||||||
return Image()._new(core.wedge("L"))
|
|
||||||
|
|
||||||
|
|
||||||
def _check_size(size: Any) -> None:
|
def _check_size(size: Any) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -4007,7 +3998,7 @@ class Exif(_ExifBase):
|
||||||
if tag == ExifTags.IFD.MakerNote:
|
if tag == ExifTags.IFD.MakerNote:
|
||||||
from .TiffImagePlugin import ImageFileDirectory_v2
|
from .TiffImagePlugin import ImageFileDirectory_v2
|
||||||
|
|
||||||
if tag_data[:8] == b"FUJIFILM":
|
if tag_data.startswith(b"FUJIFILM"):
|
||||||
ifd_offset = i32le(tag_data, 8)
|
ifd_offset = i32le(tag_data, 8)
|
||||||
ifd_data = tag_data[ifd_offset:]
|
ifd_data = tag_data[ifd_offset:]
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,7 @@ from ._deprecate import deprecate
|
||||||
from ._typing import Coords
|
from ._typing import Coords
|
||||||
|
|
||||||
# experimental access to the outline API
|
# experimental access to the outline API
|
||||||
Outline: Callable[[], Image.core._Outline] | None
|
Outline: Callable[[], Image.core._Outline] = Image.core.outline
|
||||||
try:
|
|
||||||
Outline = Image.core.outline
|
|
||||||
except AttributeError:
|
|
||||||
Outline = None
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import ImageDraw2, ImageFont
|
from . import ImageDraw2, ImageFont
|
||||||
|
|
|
@ -729,11 +729,15 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image
|
||||||
r"<tiff:Orientation>([0-9])</tiff:Orientation>",
|
r"<tiff:Orientation>([0-9])</tiff:Orientation>",
|
||||||
):
|
):
|
||||||
value = exif_image.info[key]
|
value = exif_image.info[key]
|
||||||
exif_image.info[key] = (
|
if isinstance(value, str):
|
||||||
re.sub(pattern, "", value)
|
value = re.sub(pattern, "", value)
|
||||||
if isinstance(value, str)
|
elif isinstance(value, tuple):
|
||||||
else re.sub(pattern.encode(), b"", value)
|
value = tuple(
|
||||||
)
|
re.sub(pattern.encode(), b"", v) for v in value
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
value = re.sub(pattern.encode(), b"", value)
|
||||||
|
exif_image.info[key] = value
|
||||||
if not in_place:
|
if not in_place:
|
||||||
return transposed_image
|
return transposed_image
|
||||||
elif not in_place:
|
elif not in_place:
|
||||||
|
|
|
@ -28,7 +28,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import tkinter
|
import tkinter
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import TYPE_CHECKING, Any, cast
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from . import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
|
@ -263,28 +263,3 @@ def getimage(photo: PhotoImage) -> Image.Image:
|
||||||
_pyimagingtkcall("PyImagingPhotoGet", photo, im.getim())
|
_pyimagingtkcall("PyImagingPhotoGet", photo, im.getim())
|
||||||
|
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
def _show(image: Image.Image, title: str | None) -> None:
|
|
||||||
"""Helper for the Image.show method."""
|
|
||||||
|
|
||||||
class UI(tkinter.Label):
|
|
||||||
def __init__(self, master: tkinter.Toplevel, im: Image.Image) -> None:
|
|
||||||
self.image: BitmapImage | PhotoImage
|
|
||||||
if im.mode == "1":
|
|
||||||
self.image = BitmapImage(im, foreground="white", master=master)
|
|
||||||
else:
|
|
||||||
self.image = PhotoImage(im, master=master)
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
image = cast(tkinter._Image, self.image)
|
|
||||||
else:
|
|
||||||
image = self.image
|
|
||||||
super().__init__(master, image=image, bg="black", bd=0)
|
|
||||||
|
|
||||||
if not getattr(tkinter, "_default_root"):
|
|
||||||
msg = "tkinter not initialized"
|
|
||||||
raise OSError(msg)
|
|
||||||
top = tkinter.Toplevel()
|
|
||||||
if title:
|
|
||||||
top.title(title)
|
|
||||||
UI(top, image).pack()
|
|
||||||
|
|
|
@ -352,9 +352,8 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return (
|
return prefix.startswith(
|
||||||
prefix[:4] == b"\xff\x4f\xff\x51"
|
(b"\xff\x4f\xff\x51", b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a")
|
||||||
or prefix[:12] == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
self.app[app] = s # compatibility
|
self.app[app] = s # compatibility
|
||||||
self.applist.append((app, s))
|
self.applist.append((app, s))
|
||||||
|
|
||||||
if marker == 0xFFE0 and s[:4] == b"JFIF":
|
if marker == 0xFFE0 and s.startswith(b"JFIF"):
|
||||||
# extract JFIF information
|
# extract JFIF information
|
||||||
self.info["jfif"] = version = i16(s, 5) # version
|
self.info["jfif"] = version = i16(s, 5) # version
|
||||||
self.info["jfif_version"] = divmod(version, 256)
|
self.info["jfif_version"] = divmod(version, 256)
|
||||||
|
@ -95,19 +95,19 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
self.info["dpi"] = tuple(d * 2.54 for d in jfif_density)
|
self.info["dpi"] = tuple(d * 2.54 for d in jfif_density)
|
||||||
self.info["jfif_unit"] = jfif_unit
|
self.info["jfif_unit"] = jfif_unit
|
||||||
self.info["jfif_density"] = jfif_density
|
self.info["jfif_density"] = jfif_density
|
||||||
elif marker == 0xFFE1 and s[:6] == b"Exif\0\0":
|
elif marker == 0xFFE1 and s.startswith(b"Exif\0\0"):
|
||||||
# extract EXIF information
|
# extract EXIF information
|
||||||
if "exif" in self.info:
|
if "exif" in self.info:
|
||||||
self.info["exif"] += s[6:]
|
self.info["exif"] += s[6:]
|
||||||
else:
|
else:
|
||||||
self.info["exif"] = s
|
self.info["exif"] = s
|
||||||
self._exif_offset = self.fp.tell() - n + 6
|
self._exif_offset = self.fp.tell() - n + 6
|
||||||
elif marker == 0xFFE1 and s[:29] == b"http://ns.adobe.com/xap/1.0/\x00":
|
elif marker == 0xFFE1 and s.startswith(b"http://ns.adobe.com/xap/1.0/\x00"):
|
||||||
self.info["xmp"] = s.split(b"\x00", 1)[1]
|
self.info["xmp"] = s.split(b"\x00", 1)[1]
|
||||||
elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
|
elif marker == 0xFFE2 and s.startswith(b"FPXR\0"):
|
||||||
# extract FlashPix information (incomplete)
|
# extract FlashPix information (incomplete)
|
||||||
self.info["flashpix"] = s # FIXME: value will change
|
self.info["flashpix"] = s # FIXME: value will change
|
||||||
elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
|
elif marker == 0xFFE2 and s.startswith(b"ICC_PROFILE\0"):
|
||||||
# Since an ICC profile can be larger than the maximum size of
|
# Since an ICC profile can be larger than the maximum size of
|
||||||
# a JPEG marker (64K), we need provisions to split it into
|
# a JPEG marker (64K), we need provisions to split it into
|
||||||
# multiple markers. The format defined by the ICC specifies
|
# multiple markers. The format defined by the ICC specifies
|
||||||
|
@ -120,7 +120,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
# reassemble the profile, rather than assuming that the APP2
|
# reassemble the profile, rather than assuming that the APP2
|
||||||
# markers appear in the correct sequence.
|
# markers appear in the correct sequence.
|
||||||
self.icclist.append(s)
|
self.icclist.append(s)
|
||||||
elif marker == 0xFFED and s[:14] == b"Photoshop 3.0\x00":
|
elif marker == 0xFFED and s.startswith(b"Photoshop 3.0\x00"):
|
||||||
# parse the image resource block
|
# parse the image resource block
|
||||||
offset = 14
|
offset = 14
|
||||||
photoshop = self.info.setdefault("photoshop", {})
|
photoshop = self.info.setdefault("photoshop", {})
|
||||||
|
@ -153,7 +153,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
except struct.error:
|
except struct.error:
|
||||||
break # insufficient data
|
break # insufficient data
|
||||||
|
|
||||||
elif marker == 0xFFEE and s[:5] == b"Adobe":
|
elif marker == 0xFFEE and s.startswith(b"Adobe"):
|
||||||
self.info["adobe"] = i16(s, 5)
|
self.info["adobe"] = i16(s, 5)
|
||||||
# extract Adobe custom properties
|
# extract Adobe custom properties
|
||||||
try:
|
try:
|
||||||
|
@ -162,7 +162,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.info["adobe_transform"] = adobe_transform
|
self.info["adobe_transform"] = adobe_transform
|
||||||
elif marker == 0xFFE2 and s[:4] == b"MPF\0":
|
elif marker == 0xFFE2 and s.startswith(b"MPF\0"):
|
||||||
# extract MPO information
|
# extract MPO information
|
||||||
self.info["mp"] = s[4:]
|
self.info["mp"] = s[4:]
|
||||||
# offset is current location minus buffer size
|
# offset is current location minus buffer size
|
||||||
|
@ -325,7 +325,7 @@ MARKER = {
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
# Magic number was taken from https://en.wikipedia.org/wiki/JPEG
|
# Magic number was taken from https://en.wikipedia.org/wiki/JPEG
|
||||||
return prefix[:3] == b"\xff\xd8\xff"
|
return prefix.startswith(b"\xff\xd8\xff")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -547,7 +547,7 @@ def _getmp(self: JpegImageFile) -> dict[int, Any] | None:
|
||||||
return None
|
return None
|
||||||
file_contents = io.BytesIO(data)
|
file_contents = io.BytesIO(data)
|
||||||
head = file_contents.read(8)
|
head = file_contents.read(8)
|
||||||
endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<"
|
endianness = ">" if head.startswith(b"\x4d\x4d\x00\x2a") else "<"
|
||||||
# process dictionary
|
# process dictionary
|
||||||
from . import TiffImagePlugin
|
from . import TiffImagePlugin
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ from . import Image, ImageFile
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04"
|
return prefix.startswith(b"\x00\x00\x00\x00\x00\x00\x00\x04")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -26,7 +26,7 @@ from . import Image, TiffImagePlugin
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:8] == olefile.MAGIC
|
return prefix.startswith(olefile.MAGIC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -54,7 +54,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
self.images = [
|
self.images = [
|
||||||
path
|
path
|
||||||
for path in self.ole.listdir()
|
for path in self.ole.listdir()
|
||||||
if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image"
|
if path[1:] and path[0].endswith(".ACI") and path[1] == "Image"
|
||||||
]
|
]
|
||||||
|
|
||||||
# if we didn't find any images, this is probably not
|
# if we didn't find any images, this is probably not
|
||||||
|
@ -73,12 +73,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
def seek(self, frame: int) -> None:
|
def seek(self, frame: int) -> None:
|
||||||
if not self._seek_check(frame):
|
if not self._seek_check(frame):
|
||||||
return
|
return
|
||||||
try:
|
filename = self.images[frame]
|
||||||
filename = self.images[frame]
|
|
||||||
except IndexError as e:
|
|
||||||
msg = "no such frame"
|
|
||||||
raise EOFError(msg) from e
|
|
||||||
|
|
||||||
self.fp = self.ole.openstream(filename)
|
self.fp = self.ole.openstream(filename)
|
||||||
|
|
||||||
TiffImagePlugin.TiffImageFile._open(self)
|
TiffImagePlugin.TiffImageFile._open(self)
|
||||||
|
|
|
@ -54,7 +54,7 @@ class BitStream:
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"\x00\x00\x01\xb3"
|
return prefix.startswith(b"\x00\x00\x01\xb3")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -37,7 +37,7 @@ from ._binary import o16le as o16
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] in [b"DanM", b"LinS"]
|
return prefix.startswith((b"DanM", b"LinS"))
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -69,7 +69,7 @@ class MspImageFile(ImageFile.ImageFile):
|
||||||
self._mode = "1"
|
self._mode = "1"
|
||||||
self._size = i16(s, 4), i16(s, 6)
|
self._size = i16(s, 4), i16(s, 6)
|
||||||
|
|
||||||
if s[:4] == b"DanM":
|
if s.startswith(b"DanM"):
|
||||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")]
|
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")]
|
||||||
else:
|
else:
|
||||||
self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)]
|
self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)]
|
||||||
|
|
|
@ -32,7 +32,7 @@ class PaletteFile:
|
||||||
|
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
if s[:1] == b"#":
|
if s.startswith(b"#"):
|
||||||
continue
|
continue
|
||||||
if len(s) > 100:
|
if len(s) > 100:
|
||||||
msg = "bad palette file"
|
msg = "bad palette file"
|
||||||
|
|
|
@ -34,7 +34,7 @@ class PcdImageFile(ImageFile.ImageFile):
|
||||||
self.fp.seek(2048)
|
self.fp.seek(2048)
|
||||||
s = self.fp.read(2048)
|
s = self.fp.read(2048)
|
||||||
|
|
||||||
if s[:4] != b"PCD_":
|
if not s.startswith(b"PCD_"):
|
||||||
msg = "not a PCD file"
|
msg = "not a PCD file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ from ._binary import i16le as i16
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"\200\350\000\000"
|
return prefix.startswith(b"\200\350\000\000")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -740,7 +740,7 @@ class PngStream(ChunkStream):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:8] == _MAGIC
|
return prefix.startswith(_MAGIC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -47,7 +47,7 @@ MODES = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[0:1] == b"P" and prefix[1] in b"0123456fy"
|
return prefix.startswith(b"P") and prefix[1] in b"0123456fy"
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -47,7 +47,7 @@ MODES = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"8BPS"
|
return prefix.startswith(b"8BPS")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -169,15 +169,11 @@ class PsdImageFile(ImageFile.ImageFile):
|
||||||
return
|
return
|
||||||
|
|
||||||
# seek to given layer (1..max)
|
# seek to given layer (1..max)
|
||||||
try:
|
_, mode, _, tile = self.layers[layer - 1]
|
||||||
_, mode, _, tile = self.layers[layer - 1]
|
self._mode = mode
|
||||||
self._mode = mode
|
self.tile = tile
|
||||||
self.tile = tile
|
self.frame = layer
|
||||||
self.frame = layer
|
self.fp = self._fp
|
||||||
self.fp = self._fp
|
|
||||||
except IndexError as e:
|
|
||||||
msg = "no such layer"
|
|
||||||
raise EOFError(msg) from e
|
|
||||||
|
|
||||||
def tell(self) -> int:
|
def tell(self) -> int:
|
||||||
# return layer number (0=image, 1..max=layers)
|
# return layer number (0=image, 1..max=layers)
|
||||||
|
|
|
@ -14,7 +14,7 @@ from ._binary import i32be as i32
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] == b"qoif"
|
return prefix.startswith(b"qoif")
|
||||||
|
|
||||||
|
|
||||||
class QoiImageFile(ImageFile.ImageFile):
|
class QoiImageFile(ImageFile.ImageFile):
|
||||||
|
|
|
@ -288,7 +288,7 @@ if not getattr(Image.core, "libtiff_support_custom_tags", True):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:4] in PREFIXES
|
return prefix.startswith(tuple(PREFIXES))
|
||||||
|
|
||||||
|
|
||||||
def _limit_rational(
|
def _limit_rational(
|
||||||
|
@ -404,7 +404,7 @@ class IFDRational(Rational):
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return str(float(self._val))
|
return str(float(self._val))
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int: # type: ignore[override]
|
||||||
return self._val.__hash__()
|
return self._val.__hash__()
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
def __eq__(self, other: object) -> bool:
|
||||||
|
@ -1280,7 +1280,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
blocks = {}
|
blocks = {}
|
||||||
val = self.tag_v2.get(ExifTags.Base.ImageResources)
|
val = self.tag_v2.get(ExifTags.Base.ImageResources)
|
||||||
if val:
|
if val:
|
||||||
while val[:4] == b"8BIM":
|
while val.startswith(b"8BIM"):
|
||||||
id = i16(val[4:6])
|
id = i16(val[4:6])
|
||||||
n = math.ceil((val[6] + 1) / 2) * 2
|
n = math.ceil((val[6] + 1) / 2) * 2
|
||||||
size = i32(val[6 + n : 10 + n])
|
size = i32(val[6 + n : 10 + n])
|
||||||
|
|
|
@ -21,7 +21,7 @@ _VP8_MODES_BY_IDENTIFIER = {
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool | str:
|
def _accept(prefix: bytes) -> bool | str:
|
||||||
is_riff_file_format = prefix[:4] == b"RIFF"
|
is_riff_file_format = prefix.startswith(b"RIFF")
|
||||||
is_webp_file = prefix[8:12] == b"WEBP"
|
is_webp_file = prefix[8:12] == b"WEBP"
|
||||||
is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER
|
is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER
|
||||||
|
|
||||||
|
@ -46,8 +46,7 @@ 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()
|
self._size, loop_count, bgcolor, frame_count, mode = self._decoder.get_info()
|
||||||
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 = (
|
||||||
(bgcolor >> 24) & 0xFF,
|
(bgcolor >> 24) & 0xFF,
|
||||||
|
|
|
@ -68,9 +68,7 @@ if hasattr(Image.core, "drawwmf"):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return (
|
return prefix.startswith((b"\xd7\xcd\xc6\x9a\x00\x00", b"\x01\x00\x00\x00"))
|
||||||
prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -87,7 +85,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
# check placable header
|
# check placable header
|
||||||
s = self.fp.read(80)
|
s = self.fp.read(80)
|
||||||
|
|
||||||
if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00":
|
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
||||||
# placeable windows metafile
|
# placeable windows metafile
|
||||||
|
|
||||||
# get units per inch
|
# get units per inch
|
||||||
|
@ -116,7 +114,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
msg = "Unsupported WMF file format"
|
msg = "Unsupported WMF file format"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
elif s[:4] == b"\x01\x00\x00\x00" and s[40:44] == b" EMF":
|
elif s.startswith(b"\x01\x00\x00\x00") and s[40:44] == b" EMF":
|
||||||
# enhanced metafile
|
# enhanced metafile
|
||||||
|
|
||||||
# get bounding box
|
# get bounding box
|
||||||
|
|
|
@ -34,7 +34,7 @@ for r in range(8):
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:6] == _MAGIC
|
return prefix.startswith(_MAGIC)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -38,7 +38,7 @@ xbm_head = re.compile(
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix.lstrip()[:7] == b"#define"
|
return prefix.lstrip().startswith(b"#define")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -25,7 +25,7 @@ xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)')
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix: bytes) -> bool:
|
def _accept(prefix: bytes) -> bool:
|
||||||
return prefix[:9] == b"/* XPM */"
|
return prefix.startswith(b"/* XPM */")
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -67,9 +67,9 @@ class XpmImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
for _ in range(pal):
|
for _ in range(pal):
|
||||||
s = self.fp.readline()
|
s = self.fp.readline()
|
||||||
if s[-2:] == b"\r\n":
|
if s.endswith(b"\r\n"):
|
||||||
s = s[:-2]
|
s = s[:-2]
|
||||||
elif s[-1:] in b"\r\n":
|
elif s.endswith((b"\r", b"\n")):
|
||||||
s = s[:-1]
|
s = s[:-1]
|
||||||
|
|
||||||
c = s[1]
|
c = s[1]
|
||||||
|
@ -81,7 +81,7 @@ class XpmImageFile(ImageFile.ImageFile):
|
||||||
rgb = s[i + 1]
|
rgb = s[i + 1]
|
||||||
if rgb == b"None":
|
if rgb == b"None":
|
||||||
self.info["transparency"] = c
|
self.info["transparency"] = c
|
||||||
elif rgb[:1] == b"#":
|
elif rgb.startswith(b"#"):
|
||||||
# FIXME: handle colour names (see ImagePalette.py)
|
# FIXME: handle colour names (see ImagePalette.py)
|
||||||
rgb = int(rgb[1:], 16)
|
rgb = int(rgb[1:], 16)
|
||||||
palette[c] = (
|
palette[c] = (
|
||||||
|
|
123
src/_imaging.c
123
src/_imaging.c
|
@ -3769,102 +3769,26 @@ static PySequenceMethods image_as_sequence = {
|
||||||
/* type description */
|
/* type description */
|
||||||
|
|
||||||
static PyTypeObject Imaging_Type = {
|
static PyTypeObject Imaging_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingCore", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingCore",
|
||||||
sizeof(ImagingObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_dealloc,
|
||||||
/* methods */
|
.tp_as_sequence = &image_as_sequence,
|
||||||
(destructor)_dealloc, /*tp_dealloc*/
|
.tp_methods = methods,
|
||||||
0, /*tp_vectorcall_offset*/
|
.tp_getset = getsetters,
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
&image_as_sequence, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
getsetters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject ImagingFont_Type = {
|
static PyTypeObject ImagingFont_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingFont", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingFont",
|
||||||
sizeof(ImagingFontObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingFontObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_font_dealloc,
|
||||||
/* methods */
|
.tp_methods = _font_methods,
|
||||||
(destructor)_font_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_font_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject ImagingDraw_Type = {
|
static PyTypeObject ImagingDraw_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingDraw", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingDraw",
|
||||||
sizeof(ImagingDrawObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingDrawObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_draw_dealloc,
|
||||||
/* methods */
|
.tp_methods = _draw_methods,
|
||||||
(destructor)_draw_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_draw_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMappingMethods pixel_access_as_mapping = {
|
static PyMappingMethods pixel_access_as_mapping = {
|
||||||
|
@ -3876,20 +3800,10 @@ static PyMappingMethods pixel_access_as_mapping = {
|
||||||
/* type description */
|
/* type description */
|
||||||
|
|
||||||
static PyTypeObject PixelAccess_Type = {
|
static PyTypeObject PixelAccess_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "PixelAccess", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "PixelAccess",
|
||||||
sizeof(PixelAccessObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(PixelAccessObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)pixel_access_dealloc,
|
||||||
/* methods */
|
.tp_as_mapping = &pixel_access_as_mapping,
|
||||||
(destructor)pixel_access_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
&pixel_access_as_mapping, /*tp_as_mapping*/
|
|
||||||
0 /*tp_hash*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -4256,7 +4170,6 @@ static PyMethodDef functions[] = {
|
||||||
{"effect_noise", (PyCFunction)_effect_noise, METH_VARARGS},
|
{"effect_noise", (PyCFunction)_effect_noise, METH_VARARGS},
|
||||||
{"linear_gradient", (PyCFunction)_linear_gradient, METH_VARARGS},
|
{"linear_gradient", (PyCFunction)_linear_gradient, METH_VARARGS},
|
||||||
{"radial_gradient", (PyCFunction)_radial_gradient, METH_VARARGS},
|
{"radial_gradient", (PyCFunction)_radial_gradient, METH_VARARGS},
|
||||||
{"wedge", (PyCFunction)_linear_gradient, METH_VARARGS}, /* Compatibility */
|
|
||||||
|
|
||||||
/* Drawing support stuff */
|
/* Drawing support stuff */
|
||||||
{"font", (PyCFunction)_font_new, METH_VARARGS},
|
{"font", (PyCFunction)_font_new, METH_VARARGS},
|
||||||
|
|
|
@ -1410,36 +1410,11 @@ static struct PyGetSetDef cms_profile_getsetters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject CmsProfile_Type = {
|
static PyTypeObject CmsProfile_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsProfile", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "PIL.ImageCms.core.CmsProfile",
|
||||||
sizeof(CmsProfileObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(CmsProfileObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)cms_profile_dealloc,
|
||||||
/* methods */
|
.tp_methods = cms_profile_methods,
|
||||||
(destructor)cms_profile_dealloc, /*tp_dealloc*/
|
.tp_getset = cms_profile_getsetters,
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
cms_profile_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
cms_profile_getsetters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct PyMethodDef cms_transform_methods[] = {
|
static struct PyMethodDef cms_transform_methods[] = {
|
||||||
|
@ -1447,36 +1422,10 @@ static struct PyMethodDef cms_transform_methods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject CmsTransform_Type = {
|
static PyTypeObject CmsTransform_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsTransform", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "PIL.ImageCms.core.CmsTransform",
|
||||||
sizeof(CmsTransformObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(CmsTransformObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)cms_transform_dealloc,
|
||||||
/* methods */
|
.tp_methods = cms_transform_methods,
|
||||||
(destructor)cms_transform_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
cms_transform_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -1518,36 +1518,11 @@ static struct PyGetSetDef font_getsetters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject Font_Type = {
|
static PyTypeObject Font_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "Font", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "Font",
|
||||||
sizeof(FontObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(FontObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)font_dealloc,
|
||||||
/* methods */
|
.tp_methods = font_methods,
|
||||||
(destructor)font_dealloc, /*tp_dealloc*/
|
.tp_getset = font_getsetters,
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
font_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
font_getsetters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMethodDef _functions[] = {
|
static PyMethodDef _functions[] = {
|
||||||
|
|
70
src/_webp.c
70
src/_webp.c
|
@ -449,7 +449,7 @@ _anim_decoder_get_info(PyObject *self) {
|
||||||
WebPAnimInfo *info = &(decp->info);
|
WebPAnimInfo *info = &(decp->info);
|
||||||
|
|
||||||
return Py_BuildValue(
|
return Py_BuildValue(
|
||||||
"IIIIIs",
|
"(II)IIIs",
|
||||||
info->canvas_width,
|
info->canvas_width,
|
||||||
info->canvas_height,
|
info->canvas_height,
|
||||||
info->loop_count,
|
info->loop_count,
|
||||||
|
@ -530,36 +530,10 @@ static struct PyMethodDef _anim_encoder_methods[] = {
|
||||||
|
|
||||||
// WebPAnimEncoder type definition
|
// WebPAnimEncoder type definition
|
||||||
static PyTypeObject WebPAnimEncoder_Type = {
|
static PyTypeObject WebPAnimEncoder_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "WebPAnimEncoder", /*tp_name */
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "WebPAnimEncoder",
|
||||||
sizeof(WebPAnimEncoderObject), /*tp_basicsize */
|
.tp_basicsize = sizeof(WebPAnimEncoderObject),
|
||||||
0, /*tp_itemsize */
|
.tp_dealloc = (destructor)_anim_encoder_dealloc,
|
||||||
/* methods */
|
.tp_methods = _anim_encoder_methods,
|
||||||
(destructor)_anim_encoder_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_anim_encoder_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// WebPAnimDecoder methods
|
// WebPAnimDecoder methods
|
||||||
|
@ -573,36 +547,10 @@ static struct PyMethodDef _anim_decoder_methods[] = {
|
||||||
|
|
||||||
// WebPAnimDecoder type definition
|
// WebPAnimDecoder type definition
|
||||||
static PyTypeObject WebPAnimDecoder_Type = {
|
static PyTypeObject WebPAnimDecoder_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "WebPAnimDecoder", /*tp_name */
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "WebPAnimDecoder",
|
||||||
sizeof(WebPAnimDecoderObject), /*tp_basicsize */
|
.tp_basicsize = sizeof(WebPAnimDecoderObject),
|
||||||
0, /*tp_itemsize */
|
.tp_dealloc = (destructor)_anim_decoder_dealloc,
|
||||||
/* methods */
|
.tp_methods = _anim_decoder_methods,
|
||||||
(destructor)_anim_decoder_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_anim_decoder_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
35
src/decode.c
35
src/decode.c
|
@ -256,36 +256,11 @@ static struct PyGetSetDef getseters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject ImagingDecoderType = {
|
static PyTypeObject ImagingDecoderType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingDecoder", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingDecoder",
|
||||||
sizeof(ImagingDecoderObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingDecoderObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_dealloc,
|
||||||
/* methods */
|
.tp_methods = methods,
|
||||||
(destructor)_dealloc, /*tp_dealloc*/
|
.tp_getset = getseters,
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
getseters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
|
@ -248,36 +248,11 @@ static struct PyGetSetDef getsetters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject ImagingDisplayType = {
|
static PyTypeObject ImagingDisplayType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingDisplay", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingDisplay",
|
||||||
sizeof(ImagingDisplayObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingDisplayObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_delete,
|
||||||
/* methods */
|
.tp_methods = methods,
|
||||||
(destructor)_delete, /*tp_dealloc*/
|
.tp_getset = getsetters,
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
getsetters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
41
src/encode.c
41
src/encode.c
|
@ -323,36 +323,11 @@ static struct PyGetSetDef getseters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject ImagingEncoderType = {
|
static PyTypeObject ImagingEncoderType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingEncoder", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingEncoder",
|
||||||
sizeof(ImagingEncoderObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(ImagingEncoderObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_dealloc,
|
||||||
/* methods */
|
.tp_methods = methods,
|
||||||
(destructor)_dealloc, /*tp_dealloc*/
|
.tp_getset = getseters,
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
getseters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -1253,7 +1228,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||||
PyObject *quality_layers = NULL;
|
PyObject *quality_layers = NULL;
|
||||||
Py_ssize_t num_resolutions = 0;
|
Py_ssize_t num_resolutions = 0;
|
||||||
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
||||||
PyObject *irreversible = NULL;
|
int irreversible = 0;
|
||||||
char *progression = "LRCP";
|
char *progression = "LRCP";
|
||||||
OPJ_PROG_ORDER prog_order;
|
OPJ_PROG_ORDER prog_order;
|
||||||
char *cinema_mode = "no";
|
char *cinema_mode = "no";
|
||||||
|
@ -1267,7 +1242,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(
|
if (!PyArg_ParseTuple(
|
||||||
args,
|
args,
|
||||||
"ss|OOOsOnOOOssbbnz#p",
|
"ss|OOOsOnOOpssbbnz#p",
|
||||||
&mode,
|
&mode,
|
||||||
&format,
|
&format,
|
||||||
&offset,
|
&offset,
|
||||||
|
@ -1402,7 +1377,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) {
|
||||||
precinct_size, &context->precinct_width, &context->precinct_height
|
precinct_size, &context->precinct_width, &context->precinct_height
|
||||||
);
|
);
|
||||||
|
|
||||||
context->irreversible = PyObject_IsTrue(irreversible);
|
context->irreversible = irreversible;
|
||||||
context->progression = prog_order;
|
context->progression = prog_order;
|
||||||
context->cinema_mode = cine_mode;
|
context->cinema_mode = cine_mode;
|
||||||
context->mct = mct;
|
context->mct = mct;
|
||||||
|
|
|
@ -149,34 +149,8 @@ static struct PyMethodDef _outline_methods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject OutlineType = {
|
static PyTypeObject OutlineType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "Outline", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "Outline",
|
||||||
sizeof(OutlineObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(OutlineObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)_outline_dealloc,
|
||||||
/* methods */
|
.tp_methods = _outline_methods,
|
||||||
(destructor)_outline_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_vectorcall_offset*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
0, /*tp_as_sequence*/
|
|
||||||
0, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
_outline_methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
37
src/path.c
37
src/path.c
|
@ -598,34 +598,11 @@ static PyMappingMethods path_as_mapping = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject PyPathType = {
|
static PyTypeObject PyPathType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "Path",
|
||||||
sizeof(PyPathObject), /*tp_basicsize*/
|
.tp_basicsize = sizeof(PyPathObject),
|
||||||
0, /*tp_itemsize*/
|
.tp_dealloc = (destructor)path_dealloc,
|
||||||
/* methods */
|
.tp_as_sequence = &path_as_sequence,
|
||||||
(destructor)path_dealloc, /*tp_dealloc*/
|
.tp_as_mapping = &path_as_mapping,
|
||||||
0, /*tp_vectorcall_offset*/
|
.tp_methods = methods,
|
||||||
0, /*tp_getattr*/
|
.tp_getset = getsetters,
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_as_async*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number*/
|
|
||||||
&path_as_sequence, /*tp_as_sequence*/
|
|
||||||
&path_as_mapping, /*tp_as_mapping*/
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
getsetters, /*tp_getset*/
|
|
||||||
};
|
};
|
||||||
|
|
521
src/thirdparty/pythoncapi_compat.h
vendored
521
src/thirdparty/pythoncapi_compat.h
vendored
|
@ -10,7 +10,7 @@
|
||||||
// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h
|
// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h
|
||||||
//
|
//
|
||||||
// This file was vendored from the following commit:
|
// This file was vendored from the following commit:
|
||||||
// https://github.com/python/pythoncapi-compat/commit/0041177c4f348c8952b4c8980b2c90856e61c7c7
|
// https://github.com/python/pythoncapi-compat/commit/c84545f0e1e21757d4901f75c47333d25a3fcff0
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: 0BSD
|
// SPDX-License-Identifier: 0BSD
|
||||||
|
|
||||||
|
@ -22,11 +22,15 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <stddef.h> // offsetof()
|
||||||
|
|
||||||
// Python 3.11.0b4 added PyFrame_Back() to Python.h
|
// Python 3.11.0b4 added PyFrame_Back() to Python.h
|
||||||
#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)
|
#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)
|
||||||
# include "frameobject.h" // PyFrameObject, PyFrame_GetBack()
|
# include "frameobject.h" // PyFrameObject, PyFrame_GetBack()
|
||||||
#endif
|
#endif
|
||||||
|
#if PY_VERSION_HEX < 0x030C00A3
|
||||||
|
# include <structmember.h> // T_SHORT, READONLY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifndef _Py_CAST
|
#ifndef _Py_CAST
|
||||||
|
@ -290,7 +294,7 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
|
||||||
|
|
||||||
|
|
||||||
// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5
|
// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5
|
||||||
#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION)
|
#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000)
|
||||||
static inline PyInterpreterState *
|
static inline PyInterpreterState *
|
||||||
PyThreadState_GetInterpreter(PyThreadState *tstate)
|
PyThreadState_GetInterpreter(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
|
@ -583,7 +587,7 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
*pobj = Py_NewRef(obj);
|
*pobj = Py_NewRef(obj);
|
||||||
return (*pobj != NULL);
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -921,7 +925,7 @@ static inline int
|
||||||
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
|
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
PyObject **dict = _PyObject_GetDictPtr(obj);
|
PyObject **dict = _PyObject_GetDictPtr(obj);
|
||||||
if (*dict == NULL) {
|
if (dict == NULL || *dict == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_VISIT(*dict);
|
Py_VISIT(*dict);
|
||||||
|
@ -932,7 +936,7 @@ static inline void
|
||||||
PyObject_ClearManagedDict(PyObject *obj)
|
PyObject_ClearManagedDict(PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject **dict = _PyObject_GetDictPtr(obj);
|
PyObject **dict = _PyObject_GetDictPtr(obj);
|
||||||
if (*dict == NULL) {
|
if (dict == NULL || *dict == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Py_CLEAR(*dict);
|
Py_CLEAR(*dict);
|
||||||
|
@ -1207,11 +1211,11 @@ static inline int PyTime_PerfCounter(PyTime_t *result)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// gh-111389 added hash constants to Python 3.13.0a5. These constants were
|
// gh-111389 added hash constants to Python 3.13.0a5. These constants were
|
||||||
// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9.
|
// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8.
|
||||||
#if (!defined(PyHASH_BITS) \
|
#if (!defined(PyHASH_BITS) \
|
||||||
&& ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \
|
&& ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \
|
||||||
|| (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \
|
|| (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \
|
||||||
&& PYPY_VERSION_NUM >= 0x07090000)))
|
&& PYPY_VERSION_NUM >= 0x07030800)))
|
||||||
# define PyHASH_BITS _PyHASH_BITS
|
# define PyHASH_BITS _PyHASH_BITS
|
||||||
# define PyHASH_MODULUS _PyHASH_MODULUS
|
# define PyHASH_MODULUS _PyHASH_MODULUS
|
||||||
# define PyHASH_INF _PyHASH_INF
|
# define PyHASH_INF _PyHASH_INF
|
||||||
|
@ -1523,6 +1527,36 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2
|
||||||
|
#if PY_VERSION_HEX < 0x030E00A2
|
||||||
|
static inline int PyLong_IsPositive(PyObject *obj)
|
||||||
|
{
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _PyLong_Sign(obj) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int PyLong_IsNegative(PyObject *obj)
|
||||||
|
{
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _PyLong_Sign(obj) == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int PyLong_IsZero(PyObject *obj)
|
||||||
|
{
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _PyLong_Sign(obj) == 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0
|
// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0
|
||||||
#if PY_VERSION_HEX < 0x030E00A0
|
#if PY_VERSION_HEX < 0x030E00A0
|
||||||
|
@ -1693,6 +1727,479 @@ static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// gh-102471 added import and export API for integers to 3.14.0a2.
|
||||||
|
#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)
|
||||||
|
// Helpers to access PyLongObject internals.
|
||||||
|
static inline void
|
||||||
|
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
|
||||||
|
{
|
||||||
|
#if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3);
|
||||||
|
#elif PY_VERSION_HEX >= 0x030900A4
|
||||||
|
Py_SET_SIZE(op, sign * size);
|
||||||
|
#else
|
||||||
|
Py_SIZE(op) = sign * size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Py_ssize_t
|
||||||
|
_PyLong_DigitCount(const PyLongObject *op)
|
||||||
|
{
|
||||||
|
#if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
return (Py_ssize_t)(op->long_value.lv_tag >> 3);
|
||||||
|
#else
|
||||||
|
return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline digit*
|
||||||
|
_PyLong_GetDigits(const PyLongObject *op)
|
||||||
|
{
|
||||||
|
#if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
return (digit*)(op->long_value.ob_digit);
|
||||||
|
#else
|
||||||
|
return (digit*)(op->ob_digit);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct PyLongLayout {
|
||||||
|
uint8_t bits_per_digit;
|
||||||
|
uint8_t digit_size;
|
||||||
|
int8_t digits_order;
|
||||||
|
int8_t digit_endianness;
|
||||||
|
} PyLongLayout;
|
||||||
|
|
||||||
|
typedef struct PyLongExport {
|
||||||
|
int64_t value;
|
||||||
|
uint8_t negative;
|
||||||
|
Py_ssize_t ndigits;
|
||||||
|
const void *digits;
|
||||||
|
Py_uintptr_t _reserved;
|
||||||
|
} PyLongExport;
|
||||||
|
|
||||||
|
typedef struct PyLongWriter PyLongWriter;
|
||||||
|
|
||||||
|
static inline const PyLongLayout*
|
||||||
|
PyLong_GetNativeLayout(void)
|
||||||
|
{
|
||||||
|
static const PyLongLayout PyLong_LAYOUT = {
|
||||||
|
PyLong_SHIFT,
|
||||||
|
sizeof(digit),
|
||||||
|
-1, // least significant first
|
||||||
|
PY_LITTLE_ENDIAN ? -1 : 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return &PyLong_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
PyLong_Export(PyObject *obj, PyLongExport *export_long)
|
||||||
|
{
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
memset(export_long, 0, sizeof(*export_long));
|
||||||
|
PyErr_Format(PyExc_TypeError, "expected int, got %s",
|
||||||
|
Py_TYPE(obj)->tp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast-path: try to convert to a int64_t
|
||||||
|
PyLongObject *self = (PyLongObject*)obj;
|
||||||
|
int overflow;
|
||||||
|
#if SIZEOF_LONG == 8
|
||||||
|
long value = PyLong_AsLongAndOverflow(obj, &overflow);
|
||||||
|
#else
|
||||||
|
// Windows has 32-bit long, so use 64-bit long long instead
|
||||||
|
long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);
|
||||||
|
#endif
|
||||||
|
Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t));
|
||||||
|
// the function cannot fail since obj is a PyLongObject
|
||||||
|
assert(!(value == -1 && PyErr_Occurred()));
|
||||||
|
|
||||||
|
if (!overflow) {
|
||||||
|
export_long->value = value;
|
||||||
|
export_long->negative = 0;
|
||||||
|
export_long->ndigits = 0;
|
||||||
|
export_long->digits = 0;
|
||||||
|
export_long->_reserved = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
export_long->value = 0;
|
||||||
|
export_long->negative = _PyLong_Sign(obj) < 0;
|
||||||
|
export_long->ndigits = _PyLong_DigitCount(self);
|
||||||
|
if (export_long->ndigits == 0) {
|
||||||
|
export_long->ndigits = 1;
|
||||||
|
}
|
||||||
|
export_long->digits = _PyLong_GetDigits(self);
|
||||||
|
export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
PyLong_FreeExport(PyLongExport *export_long)
|
||||||
|
{
|
||||||
|
PyObject *obj = (PyObject*)export_long->_reserved;
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
export_long->_reserved = 0;
|
||||||
|
Py_DECREF(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline PyLongWriter*
|
||||||
|
PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits)
|
||||||
|
{
|
||||||
|
if (ndigits <= 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "ndigits must be positive");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(digits != NULL);
|
||||||
|
|
||||||
|
PyLongObject *obj = _PyLong_New(ndigits);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits);
|
||||||
|
|
||||||
|
*digits = _PyLong_GetDigits(obj);
|
||||||
|
return (PyLongWriter*)obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
PyLongWriter_Discard(PyLongWriter *writer)
|
||||||
|
{
|
||||||
|
PyLongObject *obj = (PyLongObject *)writer;
|
||||||
|
|
||||||
|
assert(Py_REFCNT(obj) == 1);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline PyObject*
|
||||||
|
PyLongWriter_Finish(PyLongWriter *writer)
|
||||||
|
{
|
||||||
|
PyObject *obj = (PyObject *)writer;
|
||||||
|
PyLongObject *self = (PyLongObject*)obj;
|
||||||
|
Py_ssize_t j = _PyLong_DigitCount(self);
|
||||||
|
Py_ssize_t i = j;
|
||||||
|
int sign = _PyLong_Sign(obj);
|
||||||
|
|
||||||
|
assert(Py_REFCNT(obj) == 1);
|
||||||
|
|
||||||
|
// Normalize and get singleton if possible
|
||||||
|
while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) {
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
if (i != j) {
|
||||||
|
if (i == 0) {
|
||||||
|
sign = 0;
|
||||||
|
}
|
||||||
|
_PyLong_SetSignAndDigitCount(self, sign, i);
|
||||||
|
}
|
||||||
|
if (i <= 1) {
|
||||||
|
long val = sign * (long)(_PyLong_GetDigits(self)[0]);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return PyLong_FromLong(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX < 0x030C00A3
|
||||||
|
# define Py_T_SHORT T_SHORT
|
||||||
|
# define Py_T_INT T_INT
|
||||||
|
# define Py_T_LONG T_LONG
|
||||||
|
# define Py_T_FLOAT T_FLOAT
|
||||||
|
# define Py_T_DOUBLE T_DOUBLE
|
||||||
|
# define Py_T_STRING T_STRING
|
||||||
|
# define _Py_T_OBJECT T_OBJECT
|
||||||
|
# define Py_T_CHAR T_CHAR
|
||||||
|
# define Py_T_BYTE T_BYTE
|
||||||
|
# define Py_T_UBYTE T_UBYTE
|
||||||
|
# define Py_T_USHORT T_USHORT
|
||||||
|
# define Py_T_UINT T_UINT
|
||||||
|
# define Py_T_ULONG T_ULONG
|
||||||
|
# define Py_T_STRING_INPLACE T_STRING_INPLACE
|
||||||
|
# define Py_T_BOOL T_BOOL
|
||||||
|
# define Py_T_OBJECT_EX T_OBJECT_EX
|
||||||
|
# define Py_T_LONGLONG T_LONGLONG
|
||||||
|
# define Py_T_ULONGLONG T_ULONGLONG
|
||||||
|
# define Py_T_PYSSIZET T_PYSSIZET
|
||||||
|
|
||||||
|
# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)
|
||||||
|
# define _Py_T_NONE T_NONE
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# define Py_READONLY READONLY
|
||||||
|
# define Py_AUDIT_READ READ_RESTRICTED
|
||||||
|
# define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4
|
||||||
|
#if PY_VERSION_HEX < 0x030E00A4
|
||||||
|
static inline FILE* Py_fopen(PyObject *path, const char *mode)
|
||||||
|
{
|
||||||
|
#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION)
|
||||||
|
PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode);
|
||||||
|
|
||||||
|
return _Py_fopen_obj(path, mode);
|
||||||
|
#else
|
||||||
|
FILE *f;
|
||||||
|
PyObject *bytes;
|
||||||
|
#if PY_VERSION_HEX >= 0x03000000
|
||||||
|
if (!PyUnicode_FSConverter(path, &bytes)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!PyString_Check(path)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "except str");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bytes = Py_NewRef(path);
|
||||||
|
#endif
|
||||||
|
const char *path_bytes = PyBytes_AS_STRING(bytes);
|
||||||
|
|
||||||
|
f = fopen(path_bytes, mode);
|
||||||
|
Py_DECREF(bytes);
|
||||||
|
|
||||||
|
if (f == NULL) {
|
||||||
|
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int Py_fclose(FILE *file)
|
||||||
|
{
|
||||||
|
return fclose(file);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION)
|
||||||
|
static inline PyObject*
|
||||||
|
PyConfig_Get(const char *name)
|
||||||
|
{
|
||||||
|
typedef enum {
|
||||||
|
_PyConfig_MEMBER_INT,
|
||||||
|
_PyConfig_MEMBER_UINT,
|
||||||
|
_PyConfig_MEMBER_ULONG,
|
||||||
|
_PyConfig_MEMBER_BOOL,
|
||||||
|
_PyConfig_MEMBER_WSTR,
|
||||||
|
_PyConfig_MEMBER_WSTR_OPT,
|
||||||
|
_PyConfig_MEMBER_WSTR_LIST,
|
||||||
|
} PyConfigMemberType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
size_t offset;
|
||||||
|
PyConfigMemberType type;
|
||||||
|
const char *sys_attr;
|
||||||
|
} PyConfigSpec;
|
||||||
|
|
||||||
|
#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \
|
||||||
|
{#MEMBER, offsetof(PyConfig, MEMBER), \
|
||||||
|
_PyConfig_MEMBER_##TYPE, sys_attr}
|
||||||
|
|
||||||
|
static const PyConfigSpec config_spec[] = {
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL),
|
||||||
|
#if 0x030C0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL),
|
||||||
|
#if 0x030B0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL),
|
||||||
|
#if 0x030B0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL),
|
||||||
|
#if 0x030D0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL),
|
||||||
|
#if 0x030B0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL),
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL),
|
||||||
|
#if 0x030A0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL),
|
||||||
|
#if 0x030C0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL),
|
||||||
|
#if 0x030B0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL),
|
||||||
|
#if 0x030B0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL),
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL),
|
||||||
|
#if 0x030A0000 <= PY_VERSION_HEX
|
||||||
|
PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL),
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef PYTHONCAPI_COMPAT_SPEC
|
||||||
|
|
||||||
|
const PyConfigSpec *spec;
|
||||||
|
int found = 0;
|
||||||
|
for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) {
|
||||||
|
spec = &config_spec[i];
|
||||||
|
if (strcmp(spec->name, name) == 0) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
if (spec->sys_attr != NULL) {
|
||||||
|
PyObject *value = PySys_GetObject(spec->sys_attr);
|
||||||
|
if (value == NULL) {
|
||||||
|
PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return Py_NewRef(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);
|
||||||
|
|
||||||
|
const PyConfig *config = _Py_GetConfig();
|
||||||
|
void *member = (char *)config + spec->offset;
|
||||||
|
switch (spec->type) {
|
||||||
|
case _PyConfig_MEMBER_INT:
|
||||||
|
case _PyConfig_MEMBER_UINT:
|
||||||
|
{
|
||||||
|
int value = *(int *)member;
|
||||||
|
return PyLong_FromLong(value);
|
||||||
|
}
|
||||||
|
case _PyConfig_MEMBER_BOOL:
|
||||||
|
{
|
||||||
|
int value = *(int *)member;
|
||||||
|
return PyBool_FromLong(value != 0);
|
||||||
|
}
|
||||||
|
case _PyConfig_MEMBER_ULONG:
|
||||||
|
{
|
||||||
|
unsigned long value = *(unsigned long *)member;
|
||||||
|
return PyLong_FromUnsignedLong(value);
|
||||||
|
}
|
||||||
|
case _PyConfig_MEMBER_WSTR:
|
||||||
|
case _PyConfig_MEMBER_WSTR_OPT:
|
||||||
|
{
|
||||||
|
wchar_t *wstr = *(wchar_t **)member;
|
||||||
|
if (wstr != NULL) {
|
||||||
|
return PyUnicode_FromWideChar(wstr, -1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Py_NewRef(Py_None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _PyConfig_MEMBER_WSTR_LIST:
|
||||||
|
{
|
||||||
|
const PyWideStringList *list = (const PyWideStringList *)member;
|
||||||
|
PyObject *tuple = PyTuple_New(list->length);
|
||||||
|
if (tuple == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Py_ssize_t i = 0; i < list->length; i++) {
|
||||||
|
PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);
|
||||||
|
if (item == NULL) {
|
||||||
|
Py_DECREF(tuple);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyTuple_SET_ITEM(tuple, i, item);
|
||||||
|
}
|
||||||
|
return tuple;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Py_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
PyConfig_GetInt(const char *name, int *value)
|
||||||
|
{
|
||||||
|
PyObject *obj = PyConfig_Get(name);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyLong_Check(obj)) {
|
||||||
|
Py_DECREF(obj);
|
||||||
|
PyErr_Format(PyExc_TypeError, "config option %s is not an int", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int as_int = PyLong_AsInt(obj);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
if (as_int == -1 && PyErr_Occurred()) {
|
||||||
|
PyErr_Format(PyExc_OverflowError,
|
||||||
|
"config option %s value does not fit into a C int", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = as_int;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -11,12 +11,8 @@ deps =
|
||||||
extras =
|
extras =
|
||||||
tests
|
tests
|
||||||
commands =
|
commands =
|
||||||
make clean
|
|
||||||
{envpython} -m pip install .
|
|
||||||
{envpython} selftest.py
|
{envpython} selftest.py
|
||||||
{envpython} -m pytest -W always {posargs}
|
{envpython} -m pytest -W always {posargs}
|
||||||
allowlist_externals =
|
|
||||||
make
|
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
skip_install = true
|
skip_install = true
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user