Skip to content

Commit

Permalink
Merge branch 'main' into use-ptr
Browse files Browse the repository at this point in the history
  • Loading branch information
homm authored Sep 18, 2024
2 parents bc97369 + 1ee3bd1 commit af521a1
Show file tree
Hide file tree
Showing 42 changed files with 500 additions and 239 deletions.
9 changes: 2 additions & 7 deletions .ci/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ set -e

if [[ $(uname) != CYGWIN* ]]; then
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
ghostscript libjpeg-turbo-progs libopenjp2-7-dev\
cmake meson imagemagick libharfbuzz-dev libfribidi-dev\
sway wl-clipboard libopenblas-dev
fi
Expand All @@ -38,12 +38,7 @@ python3 -m pip install -U pytest-timeout
python3 -m pip install pyroma

if [[ $(uname) != CYGWIN* ]]; then
# TODO Update condition when NumPy supports free-threading
if [[ "$PYTHON_GIL" == "0" ]]; then
python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
else
python3 -m pip install numpy
fi
python3 -m pip install numpy

# PyQt6 doesn't support PyPy3
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
Expand Down
2 changes: 1 addition & 1 deletion .ci/requirements-cibw.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cibuildwheel==2.20.0
cibuildwheel==2.21.1
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
fail-fast: false
matrix:
os: [
"macos-14",
"macos-latest",
"ubuntu-latest",
]
python-version: [
Expand All @@ -56,7 +56,7 @@ jobs:
# M1 only available for 3.10+
- { os: "macos-13", python-version: "3.9" }
exclude:
- { os: "macos-14", python-version: "3.9" }
- { os: "macos-latest", python-version: "3.9" }

runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }} ${{ matrix.disable-gil && 'free-threaded' || '' }}
Expand All @@ -76,7 +76,7 @@ jobs:
"pyproject.toml"
- name: Set up Python ${{ matrix.python-version }} (free-threaded)
uses: deadsnakes/action@v3.1.0
uses: deadsnakes/action@v3.2.0
if: "${{ matrix.disable-gil }}"
with:
python-version: ${{ matrix.python-version }}
Expand Down
53 changes: 37 additions & 16 deletions .github/workflows/wheels-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ ARCHIVE_SDIR=pillow-depends-main

# Package versions for fresh source builds
FREETYPE_VERSION=2.13.2
HARFBUZZ_VERSION=8.5.0
LIBPNG_VERSION=1.6.43
JPEGTURBO_VERSION=3.0.3
if [[ "$MB_ML_VER" != 2014 ]]; then
HARFBUZZ_VERSION=9.0.0
else
HARFBUZZ_VERSION=8.5.0
fi
LIBPNG_VERSION=1.6.44
JPEGTURBO_VERSION=3.0.4
OPENJPEG_VERSION=2.5.2
XZ_VERSION=5.4.5
XZ_VERSION=5.6.2
TIFF_VERSION=4.6.0
LCMS2_VERSION=2.16
if [[ -n "$IS_MACOS" ]]; then
Expand All @@ -40,7 +44,7 @@ BROTLI_VERSION=1.1.0

if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
function build_openjpeg {
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v${OPENJPEG_VERSION}.tar.gz openjpeg-${OPENJPEG_VERSION}.tar.gz)
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v$OPENJPEG_VERSION.tar.gz openjpeg-$OPENJPEG_VERSION.tar.gz)
(cd $out_dir \
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
&& make install)
Expand All @@ -50,7 +54,7 @@ fi

function build_brotli {
local cmake=$(get_modern_cmake)
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-1.1.0.tar.gz)
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-$BROTLI_VERSION.tar.gz)
(cd $out_dir \
&& $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
&& make install)
Expand All @@ -60,6 +64,25 @@ function build_brotli {
fi
}

function build_harfbuzz {
if [[ "$HARFBUZZ_VERSION" == 8.5.0 ]]; then
export FREETYPE_LIBS=-lfreetype
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
export FREETYPE_LIBS=""
export FREETYPE_CFLAGS=""
else
local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz)
(cd $out_dir \
&& meson setup build --buildtype=release -Dfreetype=enabled -Dglib=disabled)
(cd $out_dir/build \
&& meson install)
if [[ "$MB_ML_LIBC" == "manylinux" ]]; then
cp /usr/local/lib64/libharfbuzz* /usr/local/lib
fi
fi
}

function build {
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
sudo chown -R runner /usr/local
Expand Down Expand Up @@ -109,15 +132,7 @@ function build {
build_freetype
fi

if [ -z "$IS_MACOS" ]; then
export FREETYPE_LIBS=-lfreetype
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
fi
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
if [ -z "$IS_MACOS" ]; then
export FREETYPE_LIBS=""
export FREETYPE_CFLAGS=""
fi
build_harfbuzz
}

# Any stuff that you need to do before you start building the wheels
Expand All @@ -140,7 +155,13 @@ if [[ -n "$IS_MACOS" ]]; then
brew remove --ignore-dependencies webp
fi

brew install pkg-config
brew install meson pkg-config
elif [[ "$MB_ML_LIBC" == "manylinux" ]]; then
if [[ "$HARFBUZZ_VERSION" != 8.5.0 ]]; then
yum install -y meson
fi
else
apk add meson
fi

wrap_wheel_builder build
Expand Down
9 changes: 1 addition & 8 deletions .github/workflows/wheels-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@ else
yum install -y fribidi
fi

if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then
# TODO Update condition when NumPy supports free-threading
if [ $(python3 -c "import sysconfig;print(sysconfig.get_config_var('Py_GIL_DISABLED'))") == "1" ]; then
python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
else
python3 -m pip install numpy
fi
fi
python3 -m pip install numpy

if [ ! -d "test-images-main" ]; then
curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,18 @@ jobs:
fail-fast: false
matrix:
include:
- name: "macOS x86_64"
- name: "macOS 10.10 x86_64"
os: macos-13
cibw_arch: x86_64
build: "pp310* cp3{9,10,11}*"
macosx_deployment_target: "10.10"
- name: "macOS 10.13 x86_64"
os: macos-13
cibw_arch: x86_64
build: "cp3{12,13}*"
macosx_deployment_target: "10.13"
- name: "macOS arm64"
os: macos-14
os: macos-latest
cibw_arch: arm64
macosx_deployment_target: "11.0"
- name: "manylinux2014 and musllinux x86_64"
Expand Down Expand Up @@ -145,7 +151,7 @@ jobs:

- uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.os }}-${{ matrix.cibw_arch }}${{ matrix.manylinux && format('-{0}', matrix.manylinux) }}
name: dist-${{ matrix.os }}${{ matrix.macosx_deployment_target && format('-{0}', matrix.macosx_deployment_target) }}-${{ matrix.cibw_arch }}${{ matrix.manylinux && format('-{0}', matrix.manylinux) }}
path: ./wheelhouse/*.whl

windows:
Expand Down
18 changes: 18 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ Changelog (Pillow)
11.0.0 (unreleased)
-------------------

- Accept float stroke widths #8369
[radarhere]

- Deprecate ICNS (width, height, scale) sizes in favour of load(scale) #8352
[radarhere]

- Improved handling of RGBA palettes when saving GIF images #8366
[radarhere]

- Deprecate isImageType #8364
[radarhere]

- Support converting more modes to LAB by converting to RGBA first #8358
[radarhere]

- Deprecate support for FreeType 2.9.0 #8356
[hugovk, radarhere]

- Removed unused TiffImagePlugin IFD_LEGACY_API #8355
[radarhere]

Expand Down
Binary file added Tests/images/imagedraw_stroke_float.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -1429,3 +1429,21 @@ def test_saving_rgba(tmp_path: Path) -> None:
with Image.open(out) as reloaded:
reloaded_rgba = reloaded.convert("RGBA")
assert reloaded_rgba.load()[0, 0][3] == 0


def test_optimizing_p_rgba(tmp_path: Path) -> None:
out = str(tmp_path / "temp.gif")

im1 = Image.new("P", (100, 100))
d = ImageDraw.Draw(im1)
d.ellipse([(40, 40), (60, 60)], fill=1)
data = [0, 0, 0, 0, 0, 0, 0, 255] + [0, 0, 0, 0] * 254
im1.putpalette(data, "RGBA")

im2 = Image.new("P", (100, 100))
im2.putpalette(data, "RGBA")

im1.save(out, save_all=True, append_images=[im2])

with Image.open(out) as reloaded:
assert reloaded.n_frames == 2
23 changes: 15 additions & 8 deletions Tests/test_file_icns.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def test_save_append_images(tmp_path: Path) -> None:
assert_image_similar_tofile(im, temp_file, 1)

with Image.open(temp_file) as reread:
reread.size = (16, 16, 2)
reread.load()
reread.size = (16, 16)
reread.load(2)
assert_image_equal(reread, provided_im)


Expand All @@ -87,14 +87,21 @@ def test_sizes() -> None:
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im.size = (w, h, r)
with pytest.warns(DeprecationWarning):
im.size = (w, h, r)
im.load()
assert im.mode == "RGBA"
assert im.size == (wr, hr)

# Test using load() with scale
im.size = (w, h)
im.load(scale=r)
assert im.mode == "RGBA"
assert im.size == (wr, hr)

# Check that we cannot load an incorrect size
with pytest.raises(ValueError):
im.size = (1, 1)
im.size = (1, 2)


def test_older_icon() -> None:
Expand All @@ -105,8 +112,8 @@ def test_older_icon() -> None:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow2.icns") as im2:
im2.size = (w, h, r)
im2.load()
im2.size = (w, h)
im2.load(r)
assert im2.mode == "RGBA"
assert im2.size == (wr, hr)

Expand All @@ -122,8 +129,8 @@ def test_jp2_icon() -> None:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow3.icns") as im2:
im2.size = (w, h, r)
im2.load()
im2.size = (w, h)
im2.load(r)
assert im2.mode == "RGBA"
assert im2.size == (wr, hr)

Expand Down
19 changes: 9 additions & 10 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1086,22 +1086,17 @@ def test_overrun(self, path: str) -> None:
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
"""
with Image.open(os.path.join("Tests/images", path)) as im:
try:
with pytest.raises(OSError) as e:
im.load()
pytest.fail()
except OSError as e:
buffer_overrun = str(e) == "buffer overrun when reading image file"
truncated = "image file is truncated" in str(e)
buffer_overrun = str(e.value) == "buffer overrun when reading image file"
truncated = "image file is truncated" in str(e.value)

assert buffer_overrun or truncated
assert buffer_overrun or truncated

def test_fli_overrun2(self) -> None:
with Image.open("Tests/images/fli_overrun2.bin") as im:
try:
with pytest.raises(OSError, match="buffer overrun when reading image file"):
im.seek(1)
pytest.fail()
except OSError as e:
assert str(e) == "buffer overrun when reading image file"

def test_exit_fp(self) -> None:
with Image.new("L", (1, 1)) as im:
Expand All @@ -1117,6 +1112,10 @@ def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None:
assert len(caplog.records) == 0
assert im.fp is None

def test_deprecation(self) -> None:
with pytest.warns(DeprecationWarning):
assert not Image.isImageType(None)


class TestImageBytes:
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
Expand Down
2 changes: 2 additions & 0 deletions Tests/test_image_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ def test_copy_zero() -> None:
@skip_unless_feature("libtiff")
def test_deepcopy() -> None:
with Image.open("Tests/images/g4_orientation_5.tif") as im:
assert im.size == (590, 88)

out = copy.deepcopy(im)
assert out.size == (590, 88)
4 changes: 1 addition & 3 deletions Tests/test_image_resize.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,7 @@ def resize(mode: str, size: tuple[int, int] | list[int]) -> None:
im.resize((10, 10), "unknown")

@skip_unless_feature("libtiff")
def test_load_first(self) -> None:
# load() may change the size of the image
# Test that resize() is calling it before getting the size
def test_transposed(self) -> None:
with Image.open("Tests/images/g4_orientation_5.tif") as im:
im = im.resize((64, 64))
assert im.size == (64, 64)
Expand Down
8 changes: 3 additions & 5 deletions Tests/test_image_thumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,13 @@ def test_no_resize() -> None:


@skip_unless_feature("libtiff")
def test_load_first() -> None:
# load() may change the size of the image
# Test that thumbnail() is calling it before performing size calculations
def test_transposed() -> None:
with Image.open("Tests/images/g4_orientation_5.tif") as im:
assert im.size == (590, 88)

im.thumbnail((64, 64))
assert im.size == (64, 10)

# Test thumbnail(), without draft(),
# on an image that is large enough once load() has changed the size
with Image.open("Tests/images/g4_orientation_5.tif") as im:
im.thumbnail((590, 88), reducing_gap=None)
assert im.size == (590, 88)
Expand Down
6 changes: 6 additions & 0 deletions Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,12 @@ def test_rgb_lab(mode: str) -> None:
assert value[:3] == (0, 255, 255)


def test_cmyk_lab() -> None:
im = Image.new("CMYK", (1, 1))
converted_im = im.convert("LAB")
assert converted_im.getpixel((0, 0)) == (255, 128, 128)


def test_deprecation() -> None:
with pytest.warns(DeprecationWarning):
assert ImageCms.DESCRIPTION.strip().startswith("pyCMS")
Expand Down
Loading

0 comments on commit af521a1

Please sign in to comment.