mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-11 16:52:29 +03:00
Merge branch 'main' into context_manager
This commit is contained in:
commit
254e9292c9
|
@ -1,4 +1,4 @@
|
||||||
mypy==1.15.0
|
mypy==1.16.0
|
||||||
IceSpringPySideStubs-PyQt6
|
IceSpringPySideStubs-PyQt6
|
||||||
IceSpringPySideStubs-PySide6
|
IceSpringPySideStubs-PySide6
|
||||||
ipython
|
ipython
|
||||||
|
|
60
.github/workflows/test-valgrind-memory.yml
vendored
Normal file
60
.github/workflows/test-valgrind-memory.yml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
name: Test Valgrind Memory Leaks
|
||||||
|
|
||||||
|
# like the Docker tests, but running valgrind only on *.c/*.h changes.
|
||||||
|
|
||||||
|
# this is very expensive. Only run on the pull request.
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches:
|
||||||
|
# - "**"
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/test-valgrind.yml"
|
||||||
|
# - "**.c"
|
||||||
|
# - "**.h"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/test-valgrind.yml"
|
||||||
|
- "**.c"
|
||||||
|
- "**.h"
|
||||||
|
- "depends/docker-test-valgrind-memory.sh"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
docker: [
|
||||||
|
ubuntu-22.04-jammy-amd64-valgrind,
|
||||||
|
]
|
||||||
|
dockerTag: [main]
|
||||||
|
|
||||||
|
name: ${{ matrix.docker }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build system information
|
||||||
|
run: python3 .github/workflows/system-info.py
|
||||||
|
|
||||||
|
- name: Docker pull
|
||||||
|
run: |
|
||||||
|
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||||
|
|
||||||
|
- name: Build and Run Valgrind
|
||||||
|
run: |
|
||||||
|
# The Pillow user in the docker container is UID 1001
|
||||||
|
sudo chown -R 1001 $GITHUB_WORKSPACE
|
||||||
|
docker run --name pillow_container -e "PILLOW_VALGRIND_TEST=true" -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }} /Pillow/depends/docker-test-valgrind-memory.sh
|
||||||
|
sudo chown -R runner $GITHUB_WORKSPACE
|
5
.github/workflows/test-windows.yml
vendored
5
.github/workflows/test-windows.yml
vendored
|
@ -31,16 +31,15 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: windows-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["pypy3.11", "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"]
|
|
||||||
include:
|
include:
|
||||||
# Test the oldest Python on 32-bit
|
# Test the oldest Python on 32-bit
|
||||||
- { python-version: "3.9", architecture: "x86", os: "windows-2019" }
|
- { python-version: "3.9", architecture: "x86" }
|
||||||
|
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
|
|
||||||
|
|
|
@ -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.11.8
|
rev: v0.11.12
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: [--exit-non-zero-on-fix]
|
args: [--exit-non-zero-on-fix]
|
||||||
|
@ -24,7 +24,7 @@ repos:
|
||||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: v20.1.3
|
rev: v20.1.5
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
types: [c]
|
types: [c]
|
||||||
|
@ -58,7 +58,7 @@ repos:
|
||||||
- id: check-renovate
|
- id: check-renovate
|
||||||
|
|
||||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||||
rev: v1.6.0
|
rev: v1.9.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: zizmor
|
- id: zizmor
|
||||||
|
|
||||||
|
@ -68,7 +68,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.1
|
rev: v2.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyproject-fmt
|
- id: pyproject-fmt
|
||||||
|
|
||||||
|
|
16
Makefile
16
Makefile
|
@ -97,13 +97,27 @@ test:
|
||||||
python3 -c "import pytest" > /dev/null 2>&1 || python3 -m pip install pytest
|
python3 -c "import pytest" > /dev/null 2>&1 || python3 -m pip install pytest
|
||||||
python3 -m pytest -qq
|
python3 -m pytest -qq
|
||||||
|
|
||||||
|
.PHONY: test-p
|
||||||
|
test-p:
|
||||||
|
python3 -c "import xdist" > /dev/null 2>&1 || python3 -m pip install pytest-xdist
|
||||||
|
python3 -m pytest -qq -n auto
|
||||||
|
|
||||||
|
|
||||||
.PHONY: valgrind
|
.PHONY: valgrind
|
||||||
valgrind:
|
valgrind:
|
||||||
python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
|
python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
|
||||||
PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
|
PILLOW_VALGRIND_TEST=true PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
|
||||||
--log-file=/tmp/valgrind-output \
|
--log-file=/tmp/valgrind-output \
|
||||||
python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output
|
python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output
|
||||||
|
|
||||||
|
.PHONY: valgrind-leak
|
||||||
|
valgrind-leak:
|
||||||
|
python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
|
||||||
|
PILLOW_VALGRIND_TEST=true PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp \
|
||||||
|
--leak-check=full --show-leak-kinds=definite --errors-for-leak-kinds=definite \
|
||||||
|
--log-file=/tmp/valgrind-output \
|
||||||
|
python3 -m pytest -vv --valgrind --valgrind-log=/tmp/valgrind-output
|
||||||
|
|
||||||
.PHONY: readme
|
.PHONY: readme
|
||||||
readme:
|
readme:
|
||||||
python3 -c "import markdown2" > /dev/null 2>&1 || python3 -m pip install markdown2
|
python3 -c "import markdown2" > /dev/null 2>&1 || python3 -m pip install markdown2
|
||||||
|
|
|
@ -14,3 +14,23 @@
|
||||||
fun:_TIFFReadEncodedTileAndAllocBuffer
|
fun:_TIFFReadEncodedTileAndAllocBuffer
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
<python_alloc_possible_leak>
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: all
|
||||||
|
fun:malloc
|
||||||
|
fun:_PyMem_RawMalloc
|
||||||
|
fun:PyObject_Malloc
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
<python_realloc_possible_leak>
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: all
|
||||||
|
fun:malloc
|
||||||
|
fun:_PyMem_RawRealloc
|
||||||
|
fun:PyMem_Realloc
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ def test_unknown_version() -> None:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_old_version(deprecated: str, plural: bool, expected: str) -> None:
|
def test_old_version(deprecated: str, plural: bool, expected: str) -> None:
|
||||||
expected = r""
|
|
||||||
with pytest.raises(RuntimeError, match=expected):
|
with pytest.raises(RuntimeError, match=expected):
|
||||||
_deprecate.deprecate(deprecated, 1, plural=plural)
|
_deprecate.deprecate(deprecated, 1, plural=plural)
|
||||||
|
|
||||||
|
|
|
@ -133,30 +133,30 @@ class TestFileJpeg:
|
||||||
f = "Tests/images/pil_sample_cmyk.jpg"
|
f = "Tests/images/pil_sample_cmyk.jpg"
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
# the source image has red pixels in the upper left corner.
|
# the source image has red pixels in the upper left corner.
|
||||||
value = im.getpixel((0, 0))
|
cmyk = im.getpixel((0, 0))
|
||||||
assert isinstance(value, tuple)
|
assert isinstance(cmyk, tuple)
|
||||||
c, m, y, k = (x / 255.0 for x in value)
|
c, m, y, k = (x / 255.0 for x in cmyk)
|
||||||
assert c == 0.0
|
assert c == 0.0
|
||||||
assert m > 0.8
|
assert m > 0.8
|
||||||
assert y > 0.8
|
assert y > 0.8
|
||||||
assert k == 0.0
|
assert k == 0.0
|
||||||
# the opposite corner is black
|
# the opposite corner is black
|
||||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
cmyk = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
assert isinstance(value, tuple)
|
assert isinstance(cmyk, tuple)
|
||||||
c, m, y, k = (x / 255.0 for x in value)
|
k = cmyk[3] / 255.0
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
# roundtrip, and check again
|
# roundtrip, and check again
|
||||||
im = self.roundtrip(im)
|
im = self.roundtrip(im)
|
||||||
value = im.getpixel((0, 0))
|
cmyk = im.getpixel((0, 0))
|
||||||
assert isinstance(value, tuple)
|
assert isinstance(cmyk, tuple)
|
||||||
c, m, y, k = (x / 255.0 for x in value)
|
c, m, y, k = (x / 255.0 for x in cmyk)
|
||||||
assert c == 0.0
|
assert c == 0.0
|
||||||
assert m > 0.8
|
assert m > 0.8
|
||||||
assert y > 0.8
|
assert y > 0.8
|
||||||
assert k == 0.0
|
assert k == 0.0
|
||||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
cmyk = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
assert isinstance(value, tuple)
|
assert isinstance(cmyk, tuple)
|
||||||
c, m, y, k = (x / 255.0 for x in value)
|
k = cmyk[3] / 255.0
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
|
|
||||||
def test_rgb(self) -> None:
|
def test_rgb(self) -> None:
|
||||||
|
|
11
depends/docker-test-valgrind-memory.sh
Executable file
11
depends/docker-test-valgrind-memory.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
## Run this as the test script in the Docker valgrind image.
|
||||||
|
## Note -- can be included directly into the Docker image,
|
||||||
|
## but requires the current python.supp.
|
||||||
|
|
||||||
|
source /vpy3/bin/activate
|
||||||
|
cd /Pillow
|
||||||
|
make clean
|
||||||
|
make install
|
||||||
|
make valgrind-leak
|
|
@ -194,9 +194,9 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
pacman -S \
|
pacman -S \
|
||||||
mingw-w64-x86_64-gcc \
|
mingw-w64-x86_64-gcc \
|
||||||
mingw-w64-x86_64-python3 \
|
mingw-w64-x86_64-python \
|
||||||
mingw-w64-x86_64-python3-pip \
|
mingw-w64-x86_64-python-pip \
|
||||||
mingw-w64-x86_64-python3-setuptools
|
mingw-w64-x86_64-python-setuptools
|
||||||
|
|
||||||
Prerequisites are installed on **MSYS2 MinGW 64-bit** with::
|
Prerequisites are installed on **MSYS2 MinGW 64-bit** with::
|
||||||
|
|
||||||
|
|
|
@ -42,15 +42,17 @@ These platforms are built and tested for every change.
|
||||||
| macOS 14 Sonoma | 3.10, 3.11, 3.12, 3.13, | arm64 |
|
| macOS 14 Sonoma | 3.10, 3.11, 3.12, 3.13, | arm64 |
|
||||||
| | PyPy3 | |
|
| | PyPy3 | |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 22.04 LTS (Jammy) | 3.9, 3.10, 3.11, | x86-64 |
|
| Ubuntu Linux 22.04 LTS (Jammy) | 3.10 | x86-64 |
|
||||||
|
+----------------------------------+----------------------------+---------------------+
|
||||||
|
| Ubuntu Linux 24.04 LTS (Noble) | 3.9, 3.10, 3.11, | x86-64 |
|
||||||
| | 3.12, 3.13, PyPy3 | |
|
| | 3.12, 3.13, PyPy3 | |
|
||||||
|
| +----------------------------+---------------------+
|
||||||
|
| | 3.12 | arm64v8, ppc64le, |
|
||||||
|
| | | s390x |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 24.04 LTS (Noble) | 3.12 | x86-64, arm64v8, |
|
| Windows Server 2022 | 3.9 | x86 |
|
||||||
| | | ppc64le, s390x |
|
| +----------------------------+---------------------+
|
||||||
+----------------------------------+----------------------------+---------------------+
|
| | 3.10, 3.11, 3.12, 3.13, | x86-64 |
|
||||||
| Windows Server 2019 | 3.9 | x86 |
|
|
||||||
+----------------------------------+----------------------------+---------------------+
|
|
||||||
| Windows Server 2022 | 3.10, 3.11, 3.12, 3.13, | x86-64 |
|
|
||||||
| | PyPy3 | |
|
| | PyPy3 | |
|
||||||
| +----------------------------+---------------------+
|
| +----------------------------+---------------------+
|
||||||
| | 3.12 (MinGW) | x86-64 |
|
| | 3.12 (MinGW) | x86-64 |
|
||||||
|
|
|
@ -70,6 +70,7 @@ optional-dependencies.tests = [
|
||||||
"pytest",
|
"pytest",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"pytest-timeout",
|
"pytest-timeout",
|
||||||
|
"pytest-xdist",
|
||||||
"trove-classifiers>=2024.10.12",
|
"trove-classifiers>=2024.10.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
return (color, color, color)
|
return (color, color, color)
|
||||||
|
|
||||||
self.dispose = None
|
self.dispose = None
|
||||||
self.dispose_extent = frame_dispose_extent
|
self.dispose_extent: tuple[int, int, int, int] | None = frame_dispose_extent
|
||||||
if self.dispose_extent and self.disposal_method >= 2:
|
if self.dispose_extent and self.disposal_method >= 2:
|
||||||
try:
|
try:
|
||||||
if self.disposal_method == 2:
|
if self.disposal_method == 2:
|
||||||
|
|
|
@ -797,7 +797,9 @@ class Image:
|
||||||
e = _getencoder(self.mode, encoder_name, encoder_args)
|
e = _getencoder(self.mode, encoder_name, encoder_args)
|
||||||
e.setimage(self.im)
|
e.setimage(self.im)
|
||||||
|
|
||||||
bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
|
from . import ImageFile
|
||||||
|
|
||||||
|
bufsize = max(ImageFile.MAXBLOCK, self.size[0] * 4) # see RawEncode.c
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -33,11 +33,7 @@ class BitStream:
|
||||||
|
|
||||||
def peek(self, bits: int) -> int:
|
def peek(self, bits: int) -> int:
|
||||||
while self.bits < bits:
|
while self.bits < bits:
|
||||||
c = self.next()
|
self.bitbuffer = (self.bitbuffer << 8) + self.next()
|
||||||
if c < 0:
|
|
||||||
self.bits = 0
|
|
||||||
continue
|
|
||||||
self.bitbuffer = (self.bitbuffer << 8) + c
|
|
||||||
self.bits += 8
|
self.bits += 8
|
||||||
return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
|
return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
|
||||||
|
|
||||||
|
|
|
@ -308,9 +308,9 @@ _new_arrow(PyObject *self, PyObject *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImagingBorrowArrow is responsible for retaining the array_capsule
|
// ImagingBorrowArrow is responsible for retaining the array_capsule
|
||||||
ret =
|
ret = PyImagingNew(
|
||||||
PyImagingNew(ImagingNewArrow(mode, xsize, ysize, schema_capsule, array_capsule)
|
ImagingNewArrow(mode, xsize, ysize, schema_capsule, array_capsule)
|
||||||
);
|
);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return ImagingError_ValueError("Invalid Arrow array mode or size mismatch");
|
return ImagingError_ValueError("Invalid Arrow array mode or size mismatch");
|
||||||
}
|
}
|
||||||
|
@ -1665,7 +1665,8 @@ _putdata(ImagingObject *self, PyObject *args) {
|
||||||
int bigendian = 0;
|
int bigendian = 0;
|
||||||
if (image->type == IMAGING_TYPE_SPECIAL) {
|
if (image->type == IMAGING_TYPE_SPECIAL) {
|
||||||
// I;16*
|
// I;16*
|
||||||
if (strcmp(image->mode, "I;16B") == 0
|
if (
|
||||||
|
strcmp(image->mode, "I;16B") == 0
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|| strcmp(image->mode, "I;16N") == 0
|
|| strcmp(image->mode, "I;16N") == 0
|
||||||
#endif
|
#endif
|
||||||
|
@ -2226,6 +2227,7 @@ _unsharp_mask(ImagingObject *self, PyObject *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) {
|
if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) {
|
||||||
|
ImagingDelete(imOut);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,6 +275,7 @@ text_layout_raqm(
|
||||||
if (!text || !size) {
|
if (!text || !size) {
|
||||||
/* return 0 and clean up, no glyphs==no size,
|
/* return 0 and clean up, no glyphs==no size,
|
||||||
and raqm fails with empty strings */
|
and raqm fails with empty strings */
|
||||||
|
PyMem_Free(text);
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
set_text = raqm_set_text(rq, text, size);
|
set_text = raqm_set_text(rq, text, size);
|
||||||
|
@ -425,6 +426,7 @@ text_layout_fallback(
|
||||||
"setting text direction, language or font features is not supported "
|
"setting text direction, language or font features is not supported "
|
||||||
"without libraqm"
|
"without libraqm"
|
||||||
);
|
);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyUnicode_Check(string)) {
|
if (PyUnicode_Check(string)) {
|
||||||
|
|
|
@ -641,6 +641,10 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
WebPPictureFree(&pic);
|
WebPPictureFree(&pic);
|
||||||
|
|
||||||
|
output = writer.mem;
|
||||||
|
ret_size = writer.size;
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
int error_code = (&pic)->error_code;
|
int error_code = (&pic)->error_code;
|
||||||
char message[50] = "";
|
char message[50] = "";
|
||||||
|
@ -652,10 +656,9 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
PyErr_Format(PyExc_ValueError, "encoding error %d%s", error_code, message);
|
PyErr_Format(PyExc_ValueError, "encoding error %d%s", error_code, message);
|
||||||
|
free(output);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
output = writer.mem;
|
|
||||||
ret_size = writer.size;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/* I want to truncate the *_size items that get passed into WebP
|
/* I want to truncate the *_size items that get passed into WebP
|
||||||
|
|
|
@ -327,11 +327,11 @@ PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
|
||||||
// added in Windows 10 (1607)
|
// added in Windows 10 (1607)
|
||||||
// loaded dynamically to avoid link errors
|
// loaded dynamically to avoid link errors
|
||||||
user32 = LoadLibraryA("User32.dll");
|
user32 = LoadLibraryA("User32.dll");
|
||||||
SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext
|
SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext)
|
||||||
)GetProcAddress(user32, "SetThreadDpiAwarenessContext");
|
GetProcAddress(user32, "SetThreadDpiAwarenessContext");
|
||||||
if (SetThreadDpiAwarenessContext_function != NULL) {
|
if (SetThreadDpiAwarenessContext_function != NULL) {
|
||||||
GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext
|
GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext)
|
||||||
)GetProcAddress(user32, "GetWindowDpiAwarenessContext");
|
GetProcAddress(user32, "GetWindowDpiAwarenessContext");
|
||||||
if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
|
if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
|
||||||
dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
|
dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -703,6 +703,8 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoder->cleanup = ImagingLibTiffEncodeCleanup;
|
||||||
|
|
||||||
num_core_tags = sizeof(core_tags) / sizeof(int);
|
num_core_tags = sizeof(core_tags) / sizeof(int);
|
||||||
for (pos = 0; pos < tags_size; pos++) {
|
for (pos = 0; pos < tags_size; pos++) {
|
||||||
item = PyList_GetItemRef(tags, pos);
|
item = PyList_GetItemRef(tags, pos);
|
||||||
|
|
|
@ -36,7 +36,10 @@ ReleaseExportedSchema(struct ArrowSchema *array) {
|
||||||
child->release(child);
|
child->release(child);
|
||||||
child->release = NULL;
|
child->release = NULL;
|
||||||
}
|
}
|
||||||
// UNDONE -- should I be releasing the children?
|
free(array->children[i]);
|
||||||
|
}
|
||||||
|
if (array->children) {
|
||||||
|
free(array->children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release dictionary
|
// Release dictionary
|
||||||
|
@ -117,6 +120,7 @@ export_imaging_schema(Imaging im, struct ArrowSchema *schema) {
|
||||||
retval = export_named_type(schema->children[0], im->arrow_band_format, "pixel");
|
retval = export_named_type(schema->children[0], im->arrow_band_format, "pixel");
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
free(schema->children[0]);
|
free(schema->children[0]);
|
||||||
|
free(schema->children);
|
||||||
schema->release(schema);
|
schema->release(schema);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -127,9 +131,7 @@ static void
|
||||||
release_const_array(struct ArrowArray *array) {
|
release_const_array(struct ArrowArray *array) {
|
||||||
Imaging im = (Imaging)array->private_data;
|
Imaging im = (Imaging)array->private_data;
|
||||||
|
|
||||||
if (array->n_children == 0) {
|
ImagingDelete(im);
|
||||||
ImagingDelete(im);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the buffers and the buffers array
|
// Free the buffers and the buffers array
|
||||||
if (array->buffers) {
|
if (array->buffers) {
|
||||||
|
|
|
@ -118,8 +118,9 @@ ImagingFillRadialGradient(const char *mode) {
|
||||||
|
|
||||||
for (y = 0; y < 256; y++) {
|
for (y = 0; y < 256; y++) {
|
||||||
for (x = 0; x < 256; x++) {
|
for (x = 0; x < 256; x++) {
|
||||||
d = (int
|
d = (int)sqrt(
|
||||||
)sqrt((double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0);
|
(double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0
|
||||||
|
);
|
||||||
if (d >= 255) {
|
if (d >= 255) {
|
||||||
d = 255;
|
d = 255;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,8 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
} else {
|
} else {
|
||||||
int bigendian = 0;
|
int bigendian = 0;
|
||||||
if (im->type == IMAGING_TYPE_SPECIAL) {
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
if (strcmp(im->mode, "I;16B") == 0
|
if (
|
||||||
|
strcmp(im->mode, "I;16B") == 0
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|| strcmp(im->mode, "I;16N") == 0
|
|| strcmp(im->mode, "I;16N") == 0
|
||||||
#endif
|
#endif
|
||||||
|
@ -308,7 +309,8 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
} else {
|
} else {
|
||||||
int bigendian = 0;
|
int bigendian = 0;
|
||||||
if (im->type == IMAGING_TYPE_SPECIAL) {
|
if (im->type == IMAGING_TYPE_SPECIAL) {
|
||||||
if (strcmp(im->mode, "I;16B") == 0
|
if (
|
||||||
|
strcmp(im->mode, "I;16B") == 0
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|| strcmp(im->mode, "I;16N") == 0
|
|| strcmp(im->mode, "I;16N") == 0
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -207,8 +207,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
|
||||||
|
|
||||||
if (params->cp_cinema == OPJ_CINEMA4K_24) {
|
if (params->cp_cinema == OPJ_CINEMA4K_24) {
|
||||||
float max_rate =
|
float max_rate =
|
||||||
((float)(components * im->xsize * im->ysize * 8) / (CINEMA_24_CS_LENGTH * 8)
|
((float)(components * im->xsize * im->ysize * 8) /
|
||||||
);
|
(CINEMA_24_CS_LENGTH * 8));
|
||||||
|
|
||||||
params->POC[0].tile = 1;
|
params->POC[0].tile = 1;
|
||||||
params->POC[0].resno0 = 0;
|
params->POC[0].resno0 = 0;
|
||||||
|
@ -243,8 +243,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
|
||||||
params->max_comp_size = COMP_24_CS_MAX_LENGTH;
|
params->max_comp_size = COMP_24_CS_MAX_LENGTH;
|
||||||
} else {
|
} else {
|
||||||
float max_rate =
|
float max_rate =
|
||||||
((float)(components * im->xsize * im->ysize * 8) / (CINEMA_48_CS_LENGTH * 8)
|
((float)(components * im->xsize * im->ysize * 8) /
|
||||||
);
|
(CINEMA_48_CS_LENGTH * 8));
|
||||||
|
|
||||||
for (n = 0; n < params->tcp_numlayers; ++n) {
|
for (n = 0; n < params->tcp_numlayers; ++n) {
|
||||||
rate = 0;
|
rate = 0;
|
||||||
|
|
|
@ -131,6 +131,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->errcode = IMAGING_CODEC_CONFIG;
|
state->errcode = IMAGING_CODEC_CONFIG;
|
||||||
|
jpeg_destroy_compress(&context->cinfo);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +162,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
||||||
/* Would subsample the green and blue
|
/* Would subsample the green and blue
|
||||||
channels, which doesn't make sense */
|
channels, which doesn't make sense */
|
||||||
state->errcode = IMAGING_CODEC_CONFIG;
|
state->errcode = IMAGING_CODEC_CONFIG;
|
||||||
|
jpeg_destroy_compress(&context->cinfo);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
jpeg_set_colorspace(&context->cinfo, JCS_RGB);
|
jpeg_set_colorspace(&context->cinfo, JCS_RGB);
|
||||||
|
|
|
@ -197,8 +197,9 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) {
|
||||||
return imOut;
|
return imOut;
|
||||||
|
|
||||||
mode_mismatch:
|
mode_mismatch:
|
||||||
return (Imaging
|
return (Imaging)ImagingError_ValueError(
|
||||||
)ImagingError_ValueError("point operation not supported for this mode");
|
"point operation not supported for this mode"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
|
|
|
@ -470,7 +470,8 @@ ImagingResampleHorizontal_16bpc(
|
||||||
double *k;
|
double *k;
|
||||||
|
|
||||||
int bigendian = 0;
|
int bigendian = 0;
|
||||||
if (strcmp(imIn->mode, "I;16N") == 0
|
if (
|
||||||
|
strcmp(imIn->mode, "I;16N") == 0
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|| strcmp(imIn->mode, "I;16B") == 0
|
|| strcmp(imIn->mode, "I;16B") == 0
|
||||||
#endif
|
#endif
|
||||||
|
@ -509,7 +510,8 @@ ImagingResampleVertical_16bpc(
|
||||||
double *k;
|
double *k;
|
||||||
|
|
||||||
int bigendian = 0;
|
int bigendian = 0;
|
||||||
if (strcmp(imIn->mode, "I;16N") == 0
|
if (
|
||||||
|
strcmp(imIn->mode, "I;16N") == 0
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
|| strcmp(imIn->mode, "I;16B") == 0
|
|| strcmp(imIn->mode, "I;16B") == 0
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -602,8 +602,9 @@ ImagingBorrowArrow(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!borrowed_buffer) {
|
if (!borrowed_buffer) {
|
||||||
return (Imaging
|
return (Imaging)ImagingError_ValueError(
|
||||||
)ImagingError_ValueError("Arrow Array, exactly 2 buffers required");
|
"Arrow Array, exactly 2 buffers required"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (y = i = 0; y < im->ysize; y++) {
|
for (y = i = 0; y < im->ysize; y++) {
|
||||||
|
|
|
@ -557,7 +557,8 @@ _decodeStrip(
|
||||||
(tdata_t)state->buffer,
|
(tdata_t)state->buffer,
|
||||||
strip_size
|
strip_size
|
||||||
) == -1) {
|
) == -1) {
|
||||||
TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
|
TRACE(
|
||||||
|
("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
|
||||||
);
|
);
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -929,6 +930,27 @@ ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ImagingLibTiffEncodeCleanup(ImagingCodecState state) {
|
||||||
|
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
||||||
|
TIFF *tiff = clientstate->tiff;
|
||||||
|
|
||||||
|
if (!tiff) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// TIFFClose in libtiff calls tif_closeproc and TIFFCleanup
|
||||||
|
if (clientstate->fp) {
|
||||||
|
// Python will manage the closing of the file rather than libtiff
|
||||||
|
// So only call TIFFCleanup
|
||||||
|
TIFFCleanup(tiff);
|
||||||
|
} else {
|
||||||
|
// When tif_closeproc refers to our custom _tiffCloseProc though,
|
||||||
|
// that is fine, as it does not close the file
|
||||||
|
TIFFClose(tiff);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes) {
|
ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes) {
|
||||||
/* One shot encoder. Encode everything to the tiff in the clientstate.
|
/* One shot encoder. Encode everything to the tiff in the clientstate.
|
||||||
|
@ -1010,16 +1032,6 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
|
||||||
TRACE(("Encode Error, row %d\n", state->y));
|
TRACE(("Encode Error, row %d\n", state->y));
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
|
||||||
// TIFFClose in libtiff calls tif_closeproc and TIFFCleanup
|
|
||||||
if (clientstate->fp) {
|
|
||||||
// Python will manage the closing of the file rather than libtiff
|
|
||||||
// So only call TIFFCleanup
|
|
||||||
TIFFCleanup(tiff);
|
|
||||||
} else {
|
|
||||||
// When tif_closeproc refers to our custom _tiffCloseProc though,
|
|
||||||
// that is fine, as it does not close the file
|
|
||||||
TIFFClose(tiff);
|
|
||||||
}
|
|
||||||
if (!clientstate->fp) {
|
if (!clientstate->fp) {
|
||||||
free(clientstate->data);
|
free(clientstate->data);
|
||||||
}
|
}
|
||||||
|
@ -1036,22 +1048,11 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
|
||||||
TRACE(("Error flushing the tiff"));
|
TRACE(("Error flushing the tiff"));
|
||||||
// likely reason is memory.
|
// likely reason is memory.
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
if (clientstate->fp) {
|
|
||||||
TIFFCleanup(tiff);
|
|
||||||
} else {
|
|
||||||
TIFFClose(tiff);
|
|
||||||
}
|
|
||||||
if (!clientstate->fp) {
|
if (!clientstate->fp) {
|
||||||
free(clientstate->data);
|
free(clientstate->data);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
TRACE(("Closing \n"));
|
|
||||||
if (clientstate->fp) {
|
|
||||||
TIFFCleanup(tiff);
|
|
||||||
} else {
|
|
||||||
TIFFClose(tiff);
|
|
||||||
}
|
|
||||||
// reset the clientstate metadata to use it to read out the buffer.
|
// reset the clientstate metadata to use it to read out the buffer.
|
||||||
clientstate->loc = 0;
|
clientstate->loc = 0;
|
||||||
clientstate->size = clientstate->eof; // redundant?
|
clientstate->size = clientstate->eof; // redundant?
|
||||||
|
|
|
@ -40,6 +40,8 @@ ImagingLibTiffInit(ImagingCodecState state, int fp, uint32_t offset);
|
||||||
extern int
|
extern int
|
||||||
ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
|
ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
|
||||||
extern int
|
extern int
|
||||||
|
ImagingLibTiffEncodeCleanup(ImagingCodecState state);
|
||||||
|
extern int
|
||||||
ImagingLibTiffMergeFieldInfo(
|
ImagingLibTiffMergeFieldInfo(
|
||||||
ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length
|
ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,8 +11,7 @@ For more extensive info, see the [Windows build instructions](build.rst).
|
||||||
* Requires Microsoft Visual Studio 2017 or newer with C++ component.
|
* Requires Microsoft Visual Studio 2017 or newer with C++ component.
|
||||||
* Requires NASM for libjpeg-turbo, a required dependency when using this script.
|
* Requires NASM for libjpeg-turbo, a required dependency when using this script.
|
||||||
* Requires CMake 3.15 or newer (available as Visual Studio component).
|
* Requires CMake 3.15 or newer (available as Visual Studio component).
|
||||||
* Tested on Windows Server 2022 with Visual Studio 2022 Enterprise and Windows Server
|
* Tested on Windows Server 2022 with Visual Studio 2022 Enterprise (GitHub Actions).
|
||||||
2019 with Visual Studio 2019 Enterprise (GitHub Actions).
|
|
||||||
|
|
||||||
Here's an example script to build on Windows:
|
Here's an example script to build on Windows:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user