diff --git a/.appveyor.yml b/.appveyor.yml index 1cca224ab..20908052b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,8 +25,8 @@ install: - mv c:\pillow-depends-main c:\pillow-depends - xcopy /S /Y c:\pillow-depends\test_images\* c:\pillow\tests\images - 7z x ..\pillow-depends\nasm-2.15.05-win64.zip -oc:\ -- ..\pillow-depends\gs9561w32.exe /S -- path c:\nasm-2.15.05;C:\Program Files (x86)\gs\gs9.56.1\bin;%PATH% +- ..\pillow-depends\gs1000w32.exe /S +- path c:\nasm-2.15.05;C:\Program Files (x86)\gs\gs10.0.0\bin;%PATH% - cd c:\pillow\winbuild\ - ps: | c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\ diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index fa1e8a503..db0307046 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -14,6 +14,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: Fuzzing: runs-on: ubuntu-latest diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 527f26d35..6195f973b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: @@ -16,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - name: pre-commit cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pre-commit key: lint-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }} @@ -24,7 +28,7 @@ jobs: lint-pre-commit- - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: "3.10" cache: pip diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 7ee76c4ac..9e2fdc096 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -10,6 +10,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: update_release_draft: permissions: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index cc5e0d488..ffac91cec 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,6 +8,10 @@ on: permissions: issues: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: stale: if: github.repository_owner == 'python-pillow' @@ -16,7 +20,7 @@ jobs: steps: - name: "Check issues" - uses: actions/stale@v5 + uses: actions/stale@v6 with: repo-token: ${{ secrets.GITHUB_TOKEN }} only-labels: "Awaiting OP Action" diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 794159cec..5b9ab0eda 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: windows-latest @@ -44,7 +48,7 @@ jobs: qt5-devel-tools subversion xorg-server-extra zlib-devel - name: Add Lapack to PATH - uses: egor-tensin/cleanup-path@v1 + uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index a78972607..c68d43935 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: @@ -79,7 +83,7 @@ jobs: MATRIX_DOCKER: ${{ matrix.docker }} - name: Upload coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: flags: GHA_Docker name: ${{ matrix.docker }} diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index 7ddb71e1f..ccf6e193a 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: windows-latest @@ -73,11 +77,11 @@ jobs: python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - name: Upload coverage - run: | - python3 -m pip install codecov - bash <(curl -s https://codecov.io/bash) -F GHA_Windows - env: - CODECOV_NAME: ${{ matrix.name }} + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: GHA_Windows + name: ${{ matrix.name }} success: permissions: diff --git a/.github/workflows/test-valgrind.yml b/.github/workflows/test-valgrind.yml index dda1b3577..219189cf2 100644 --- a/.github/workflows/test-valgrind.yml +++ b/.github/workflows/test-valgrind.yml @@ -16,6 +16,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index b9accfdf9..36bd03e7e 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: windows-latest @@ -36,7 +40,7 @@ jobs: # sets env: pythonLocation - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} @@ -55,8 +59,8 @@ jobs: 7z x winbuild\depends\nasm-2.15.05-win64.zip "-o$env:RUNNER_WORKSPACE\" echo "$env:RUNNER_WORKSPACE\nasm-2.15.05" >> $env:GITHUB_PATH - winbuild\depends\gs9561w32.exe /S - echo "C:\Program Files (x86)\gs\gs9.56.1\bin" >> $env:GITHUB_PATH + winbuild\depends\gs1000w32.exe /S + echo "C:\Program Files (x86)\gs\gs10.0.0\bin" >> $env:GITHUB_PATH xcopy /S /Y winbuild\depends\test_images\* Tests\images\ @@ -66,7 +70,7 @@ jobs: - name: Cache build id: build-cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: winbuild\build key: @@ -171,7 +175,7 @@ jobs: shell: pwsh - name: Upload coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: file: ./coverage.xml flags: GHA_Windows diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5614ad5f2..4c8a1b85f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,10 @@ on: [push, pull_request, workflow_dispatch] permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: @@ -30,11 +34,6 @@ jobs: REVERSE: "--reverse" - python-version: "3.8" PYTHONOPTIMIZE: 2 - # Include new variables for Codecov - - os: ubuntu-latest - codecov-flag: GHA_Ubuntu - - os: macos-latest - codecov-flag: GHA_macOS runs-on: ${{ matrix.os }} name: ${{ matrix.os }} Python ${{ matrix.python-version }} @@ -43,7 +42,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: pip @@ -99,7 +98,6 @@ jobs: - name: Docs if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.10 run: | - python3 -m pip install furo sphinx-copybutton sphinx-issues sphinx-removed-in sphinxext-opengraph make doccheck - name: After success @@ -107,9 +105,11 @@ jobs: .ci/after_success.sh - name: Upload coverage - run: bash <(curl -s https://codecov.io/bash) -F ${{ matrix.codecov-flag }} - env: - CODECOV_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: ${{ matrix.os == 'macos-latest' && 'GHA_macOS' || 'GHA_Ubuntu' }} + name: ${{ matrix.os }} Python ${{ matrix.python-version }} success: permissions: diff --git a/.github/workflows/tidelift.yml b/.github/workflows/tidelift.yml index c73f25431..69f9e5476 100644 --- a/.github/workflows/tidelift.yml +++ b/.github/workflows/tidelift.yml @@ -1,4 +1,5 @@ name: Tidelift Align + on: schedule: - cron: "30 2 * * *" # daily at 02:30 UTC @@ -15,6 +16,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: if: github.repository_owner == 'python-pillow' diff --git a/CHANGES.rst b/CHANGES.rst index 0ccb04f78..fb3879d79 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Improved ImageOps palette handling #6596 + [PososikTeam, radarhere] + +- Defer parsing of palette into colors #6567 + [radarhere] + +- Apply transparency to P images in ImageTk.PhotoImage #6559 + [radarhere] + - Use rounding in ImageOps contain() and pad() #6522 [bibinhashley, radarhere] diff --git a/Makefile b/Makefile index 219dda1de..8f2862948 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,12 @@ coverage: .PHONY: doc doc: + python3 -c "import PIL" > /dev/null 2>&1 || python3 -m pip install . $(MAKE) -C docs html .PHONY: doccheck doccheck: - $(MAKE) -C docs html + $(MAKE) doc # Don't make our tests rely on the links in the docs being up every single build. # We don't control them. But do check, and update them to the target of their redirects. $(MAKE) -C docs linkcheck || true diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 550578f8f..c9b2fd865 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -149,6 +149,21 @@ def test_pad_round(): assert new_im.load()[0, 2] == 1 +@pytest.mark.parametrize("mode", ("P", "PA")) +def test_palette(mode): + im = hopper(mode) + + # Expand + expanded_im = ImageOps.expand(im) + assert_image_equal(im.convert("RGB"), expanded_im.convert("RGB")) + + # Pad + padded_im = ImageOps.pad(im, (256, 128), centering=(0, 0)) + assert_image_equal( + im.convert("RGB"), padded_im.convert("RGB").crop((0, 0, 128, 128)) + ) + + def test_pil163(): # Division by zero in equalize if < 255 pixels in image (@PIL163) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index a929910b3..a848c786f 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -69,6 +69,13 @@ def test_photoimage(): assert_image_equal(reloaded, im.convert("RGBA")) +def test_photoimage_apply_transparency(): + with Image.open("Tests/images/pil123p.png") as im: + im_tk = ImageTk.PhotoImage(im) + reloaded = ImageTk.getimage(im_tk) + assert_image_equal(reloaded, im.convert("RGBA")) + + def test_photoimage_blank(): # test a image using mode/size: for mode in TK_MODES: diff --git a/docs/Makefile b/docs/Makefile index f11d6b189..458299aac 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -43,8 +43,7 @@ clean: -rm -rf $(BUILDDIR)/* install-sphinx: - $(PYTHON) -c "import sphinx" > /dev/null 2>&1 || $(PYTHON) -m pip install sphinx - $(PYTHON) -c "import furo" > /dev/null 2>&1 || $(PYTHON) -m pip install furo + $(PYTHON) -m pip install --quiet sphinx sphinx-copybutton sphinx-issues sphinx-removed-in sphinxext-opengraph furo olefile html: $(MAKE) install-sphinx diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 9be92770a..459cb47c1 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -253,7 +253,7 @@ Support for FreeType 2.7 has been removed. We recommend upgrading to at least `FreeType`_ 2.10.4, which fixed a severe vulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`). -.. _FreeType: https://www.freetype.org +.. _FreeType: https://freetype.org/ im.offset ~~~~~~~~~ diff --git a/docs/installation.rst b/docs/installation.rst index bb547c1ad..eb69d5805 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -184,7 +184,7 @@ Many of Pillow's features require external libraries: loads libfribidi at runtime if it is installed. On Windows this requires compiling FriBiDi and installing ``fribidi.dll`` into a directory listed in the `Dynamic-Link Library Search Order (Microsoft Docs) - `_ + `_ (``fribidi-0.dll`` or ``libfribidi-0.dll`` are also detected). See `Build Options`_ to see how to build this version. * Previous versions of Pillow (5.0.0 to 8.1.2) linked libraqm dynamically at runtime. diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 1ef9079fb..64066528d 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -731,4 +731,4 @@ Methods homogeneous, but similar, colors. .. _BCP 47 language code: https://www.w3.org/International/articles/language-tags/ -.. _OpenType docs: https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist +.. _OpenType docs: https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist diff --git a/docs/releasenotes/5.2.0.rst b/docs/releasenotes/5.2.0.rst index 75e8da655..d9b8f0fb7 100644 --- a/docs/releasenotes/5.2.0.rst +++ b/docs/releasenotes/5.2.0.rst @@ -105,7 +105,7 @@ Resolve confusion getting PIL / Pillow version string Re: "version constants deprecated" listed above, as user gnbl notes in #3082: - it's confusing that PIL.VERSION returns the version string of the former PIL instead of Pillow's -- there does not seem to be documentation on this version number (why this, will it ever change, ..) e.g. at https://pillow.readthedocs.io/en/5.1.x/about.html#why-a-fork +- ReadTheDocs documentation is missing for some version branches (why is this, will it ever change, ...) - it's confusing that PIL.version is a module and does not return the version information directly or hints on how to get it - the package information header is essentially useless (placeholder, does not even mention Pillow, nor the version) - PIL._version module documentation comment could explain how to access the version information diff --git a/docs/releasenotes/versioning.rst b/docs/releasenotes/versioning.rst index 87f2ba422..2a0af9e59 100644 --- a/docs/releasenotes/versioning.rst +++ b/docs/releasenotes/versioning.rst @@ -11,7 +11,7 @@ Pillow follows `Semantic Versioning `_: 2. MINOR version when you add functionality in a backwards compatible manner, and 3. PATCH version when you make backwards compatible bug fixes. -Quarterly releases ("`Main Release `_") +Quarterly releases ("`Main Release `_") bump at least the MINOR version, as new functionality has likely been added in the prior three months. @@ -21,8 +21,8 @@ these occur every 12-18 months, guided by `Python's EOL schedule `_, and any APIs that have been deprecated for at least a year are removed at the same time. -PATCH versions ("`Point Release `_" -or "`Embargoed Release `_") +PATCH versions ("`Point Release `_" +or "`Embargoed Release `_") are for security, installation or critical bug fixes. These are less common as it is preferred to stick to quarterly releases. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 9386d0086..310072dfc 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -338,7 +338,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. :param language: Language of the text. Different languages may use @@ -391,7 +391,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. :param language: Language of the text. Different languages may use @@ -456,7 +456,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. .. versionadded:: 4.2.0 @@ -520,7 +520,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. :param language: Language of the text. Different languages may use @@ -610,7 +610,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. .. versionadded:: 4.2.0 @@ -702,7 +702,7 @@ class FreeTypeFont: example '-liga' to disable ligatures or '-kern' to disable kerning. To get all supported features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist Requires libraqm. .. versionadded:: 4.2.0 diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index ae43fc3bd..443c540b6 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -21,7 +21,7 @@ import functools import operator import re -from . import Image +from . import Image, ImagePalette # # helpers @@ -291,6 +291,8 @@ def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5 out = resized else: out = Image.new(image.mode, size, color) + if resized.palette: + out.putpalette(resized.getpalette()) if resized.width != size[0]: x = round((size[0] - resized.width) * max(0, min(centering[0], 1))) out.paste(resized, (x, 0)) @@ -396,9 +398,8 @@ def expand(image, border=0, fill=0): width = left + image.size[0] + right height = top + image.size[1] + bottom color = _color(fill, image.mode) - if image.mode == "P" and image.palette: - image.load() - palette = image.palette.copy() + if image.palette: + palette = ImagePalette.ImagePalette(palette=image.getpalette()) if isinstance(color, tuple): color = palette.getcolor(color) else: diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 853147ac2..b73b2cd9d 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -50,15 +50,24 @@ class ImagePalette: @palette.setter def palette(self, palette): + self._colors = None self._palette = palette - mode_len = len(self.mode) - self.colors = {} - for i in range(0, len(self.palette), mode_len): - color = tuple(self.palette[i : i + mode_len]) - if color in self.colors: - continue - self.colors[color] = i // mode_len + @property + def colors(self): + if self._colors is None: + mode_len = len(self.mode) + self._colors = {} + for i in range(0, len(self.palette), mode_len): + color = tuple(self.palette[i : i + mode_len]) + if color in self._colors: + continue + self._colors[color] = i // mode_len + return self._colors + + @colors.setter + def colors(self, colors): + self._colors = colors def copy(self): new = ImagePalette() diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 7c90a0ad8..949cf1fbf 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -107,6 +107,7 @@ class PhotoImage: mode = image.mode if mode == "P": # palette mapped data + image.apply_transparency() image.load() try: mode = image.palette.mode diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 04a835dcd..7663f96a9 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -24,7 +24,7 @@ * * This cast is safe, as the top 32-bits of HFILE are guaranteed to be zero, * see - * https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication + * https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication */ #ifndef USE_WIN32_FILEIO #define fd_to_tiff_fd(fd) (fd) diff --git a/src/thirdparty/raqm/README.md b/src/thirdparty/raqm/README.md index 02e996e7a..3354a4d25 100644 --- a/src/thirdparty/raqm/README.md +++ b/src/thirdparty/raqm/README.md @@ -81,5 +81,5 @@ The following projects have patches to support complex text layout using Raqm: [1]: https://github.com/fribidi/fribidi [2]: https://github.com/Tehreer/SheenBidi [3]: https://github.com/harfbuzz/harfbuzz -[4]: https://www.freetype.org +[4]: https://freetype.org/ [5]: https://www.gtk.org/gtk-doc