mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-03 19:33:07 +03:00
Merge branch 'main' into test_lists_and_tuples
This commit is contained in:
commit
fa97a1af10
|
@ -26,9 +26,9 @@ install:
|
||||||
- 7z x pillow-test-images.zip -oc:\
|
- 7z x pillow-test-images.zip -oc:\
|
||||||
- mv c:\pillow-depends-main c:\pillow-depends
|
- mv c:\pillow-depends-main c:\pillow-depends
|
||||||
- xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images
|
- xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images
|
||||||
- 7z x ..\pillow-depends\nasm-2.15.05-win64.zip -oc:\
|
- 7z x ..\pillow-depends\nasm-2.16.01-win64.zip -oc:\
|
||||||
- choco install ghostscript --version=10.0.0.20230317
|
- choco install ghostscript --version=10.0.0.20230317
|
||||||
- path c:\nasm-2.15.05;C:\Program Files\gs\gs10.00.0\bin;%PATH%
|
- path c:\nasm-2.16.01;C:\Program Files\gs\gs10.00.0\bin;%PATH%
|
||||||
- cd c:\pillow\winbuild\
|
- cd c:\pillow\winbuild\
|
||||||
- ps: |
|
- ps: |
|
||||||
c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
|
c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
|
||||||
|
|
8
.github/workflows/test-cygwin.yml
vendored
8
.github/workflows/test-cygwin.yml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Cygwin
|
- name: Install Cygwin
|
||||||
uses: cygwin/cygwin-install-action@v3
|
uses: cygwin/cygwin-install-action@v4
|
||||||
with:
|
with:
|
||||||
platform: x86_64
|
platform: x86_64
|
||||||
packages: >
|
packages: >
|
||||||
|
@ -84,6 +84,10 @@ jobs:
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}-
|
${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}-
|
||||||
|
|
||||||
|
- name: Select Python version
|
||||||
|
run: |
|
||||||
|
ln -sf c:/cygwin/bin/python3.${{ matrix.python-minor-version }} c:/cygwin/bin/python3
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: |
|
run: |
|
||||||
dash.exe -c "python3 .github/workflows/system-info.py"
|
dash.exe -c "python3 .github/workflows/system-info.py"
|
||||||
|
@ -95,7 +99,7 @@ jobs:
|
||||||
- name: Install a different NumPy
|
- name: Install a different NumPy
|
||||||
shell: dash.exe -l "{0}"
|
shell: dash.exe -l "{0}"
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install -U 'numpy!=1.21.*'
|
python3 -m pip install -U numpy
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash.exe -eo pipefail -o igncr "{0}"
|
shell: bash.exe -eo pipefail -o igncr "{0}"
|
||||||
|
|
3
.github/workflows/test-docker.yml
vendored
3
.github/workflows/test-docker.yml
vendored
|
@ -39,10 +39,9 @@ jobs:
|
||||||
centos-stream-8-amd64,
|
centos-stream-8-amd64,
|
||||||
centos-stream-9-amd64,
|
centos-stream-9-amd64,
|
||||||
debian-11-bullseye-x86,
|
debian-11-bullseye-x86,
|
||||||
fedora-36-amd64,
|
|
||||||
fedora-37-amd64,
|
fedora-37-amd64,
|
||||||
|
fedora-38-amd64,
|
||||||
gentoo,
|
gentoo,
|
||||||
ubuntu-18.04-bionic-amd64,
|
|
||||||
ubuntu-20.04-focal-amd64,
|
ubuntu-20.04-focal-amd64,
|
||||||
ubuntu-22.04-jammy-amd64,
|
ubuntu-22.04-jammy-amd64,
|
||||||
]
|
]
|
||||||
|
|
2
.github/workflows/test-mingw.yml
vendored
2
.github/workflows/test-mingw.yml
vendored
|
@ -80,7 +80,7 @@ jobs:
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
||||||
- name: Build Pillow
|
- name: Build Pillow
|
||||||
run: CFLAGS="-coverage" python3 -m pip install --global-option="build_ext" .
|
run: SETUPTOOLS_USE_DISTUTILS="stdlib" CFLAGS="-coverage" python3 -m pip install --global-option="build_ext" .
|
||||||
|
|
||||||
- name: Test Pillow
|
- name: Test Pillow
|
||||||
run: |
|
run: |
|
||||||
|
|
4
.github/workflows/test-windows.yml
vendored
4
.github/workflows/test-windows.yml
vendored
|
@ -71,8 +71,8 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
id: install
|
id: install
|
||||||
run: |
|
run: |
|
||||||
7z x winbuild\depends\nasm-2.15.05-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
7z x winbuild\depends\nasm-2.16.01-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
||||||
echo "$env:RUNNER_WORKSPACE\nasm-2.15.05" >> $env:GITHUB_PATH
|
echo "$env:RUNNER_WORKSPACE\nasm-2.16.01" >> $env:GITHUB_PATH
|
||||||
|
|
||||||
choco install ghostscript --version=10.0.0.20230317
|
choco install ghostscript --version=10.0.0.20230317
|
||||||
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
|
echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH
|
||||||
|
|
|
@ -57,7 +57,7 @@ repos:
|
||||||
- id: sphinx-lint
|
- id: sphinx-lint
|
||||||
|
|
||||||
- repo: https://github.com/tox-dev/tox-ini-fmt
|
- repo: https://github.com/tox-dev/tox-ini-fmt
|
||||||
rev: 1.0.0
|
rev: 1.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: tox-ini-fmt
|
- id: tox-ini-fmt
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
|
formats: all
|
||||||
|
|
||||||
build:
|
build:
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
tools:
|
tools:
|
||||||
|
|
24
CHANGES.rst
24
CHANGES.rst
|
@ -5,6 +5,30 @@ Changelog (Pillow)
|
||||||
10.0.0 (unreleased)
|
10.0.0 (unreleased)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
- Support reading signed 8-bit TIFF images #7111
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added width argument to ImageDraw regular_polygon #7132
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Support I mode for ImageFilter.BuiltinFilter #7108
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Raise error from stderr of Linux ImageGrab.grabclipboard() command #7112
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added unpacker from I;16B to I;16 #7125
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Support float font sizes #7107
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Use later value for duplicate xref entries in PdfParser #7102
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Load before getting size in __getstate__ #7105
|
||||||
|
[bigcat88, radarhere]
|
||||||
|
|
||||||
- Fixed type handling for include and lib directories #7069
|
- Fixed type handling for include and lib directories #7069
|
||||||
[adisbladis, radarhere]
|
[adisbladis, radarhere]
|
||||||
|
|
||||||
|
|
|
@ -75,45 +75,42 @@ post-patch:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_qtables_leak():
|
standard_l_qtable = (
|
||||||
im = hopper("RGB")
|
# fmt: off
|
||||||
|
16, 11, 10, 16, 24, 40, 51, 61,
|
||||||
standard_l_qtable = [
|
12, 12, 14, 19, 26, 58, 60, 55,
|
||||||
int(s)
|
14, 13, 16, 24, 40, 57, 69, 56,
|
||||||
for s in """
|
14, 17, 22, 29, 51, 87, 80, 62,
|
||||||
16 11 10 16 24 40 51 61
|
18, 22, 37, 56, 68, 109, 103, 77,
|
||||||
12 12 14 19 26 58 60 55
|
24, 35, 55, 64, 81, 104, 113, 92,
|
||||||
14 13 16 24 40 57 69 56
|
49, 64, 78, 87, 103, 121, 120, 101,
|
||||||
14 17 22 29 51 87 80 62
|
72, 92, 95, 98, 112, 100, 103, 99,
|
||||||
18 22 37 56 68 109 103 77
|
# fmt: on
|
||||||
24 35 55 64 81 104 113 92
|
|
||||||
49 64 78 87 103 121 120 101
|
|
||||||
72 92 95 98 112 100 103 99
|
|
||||||
""".split(
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
]
|
|
||||||
|
|
||||||
standard_chrominance_qtable = [
|
standard_chrominance_qtable = (
|
||||||
int(s)
|
# fmt: off
|
||||||
for s in """
|
17, 18, 24, 47, 99, 99, 99, 99,
|
||||||
17 18 24 47 99 99 99 99
|
18, 21, 26, 66, 99, 99, 99, 99,
|
||||||
18 21 26 66 99 99 99 99
|
24, 26, 56, 99, 99, 99, 99, 99,
|
||||||
24 26 56 99 99 99 99 99
|
47, 66, 99, 99, 99, 99, 99, 99,
|
||||||
47 66 99 99 99 99 99 99
|
99, 99, 99, 99, 99, 99, 99, 99,
|
||||||
99 99 99 99 99 99 99 99
|
99, 99, 99, 99, 99, 99, 99, 99,
|
||||||
99 99 99 99 99 99 99 99
|
99, 99, 99, 99, 99, 99, 99, 99,
|
||||||
99 99 99 99 99 99 99 99
|
99, 99, 99, 99, 99, 99, 99, 99,
|
||||||
99 99 99 99 99 99 99 99
|
# fmt: on
|
||||||
""".split(
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
]
|
|
||||||
|
|
||||||
for qtables in (
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"qtables",
|
||||||
|
(
|
||||||
(standard_l_qtable, standard_chrominance_qtable),
|
(standard_l_qtable, standard_chrominance_qtable),
|
||||||
[standard_l_qtable, standard_chrominance_qtable],
|
[standard_l_qtable, standard_chrominance_qtable],
|
||||||
):
|
),
|
||||||
|
)
|
||||||
|
def test_qtables_leak(qtables):
|
||||||
|
im = hopper("RGB")
|
||||||
for _ in range(iterations):
|
for _ in range(iterations):
|
||||||
test_output = BytesIO()
|
test_output = BytesIO()
|
||||||
im.save(test_output, "JPEG", qtables=qtables)
|
im.save(test_output, "JPEG", qtables=qtables)
|
||||||
|
|
BIN
Tests/images/8bit.s.tif
Normal file
BIN
Tests/images/8bit.s.tif
Normal file
Binary file not shown.
BIN
Tests/images/duplicate_xref_entry.pdf
Normal file
BIN
Tests/images/duplicate_xref_entry.pdf
Normal file
Binary file not shown.
BIN
Tests/images/hopper_emboss_I.png
Normal file
BIN
Tests/images/hopper_emboss_I.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
Tests/images/hopper_emboss_more_I.png
Normal file
BIN
Tests/images/hopper_emboss_more_I.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
Tests/images/imagedraw_triangle_width.png
Normal file
BIN
Tests/images/imagedraw_triangle_width.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 499 B |
|
@ -198,6 +198,12 @@ class TestFileTiff:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
|
def test_8bit_s(self):
|
||||||
|
with Image.open("Tests/images/8bit.s.tif") as im:
|
||||||
|
im.load()
|
||||||
|
assert im.mode == "L"
|
||||||
|
assert im.getpixel((50, 50)) == 184
|
||||||
|
|
||||||
def test_little_endian(self):
|
def test_little_endian(self):
|
||||||
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
||||||
assert im.getpixel((0, 0)) == 480
|
assert im.getpixel((0, 0)) == 480
|
||||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper, skip_unless_feature
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
|
@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
|
||||||
|
@ -42,3 +42,10 @@ def test_copy_zero():
|
||||||
out = im.copy()
|
out = im.copy()
|
||||||
assert out.mode == im.mode
|
assert out.mode == im.mode
|
||||||
assert out.size == im.size
|
assert out.size == im.size
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("libtiff")
|
||||||
|
def test_deepcopy():
|
||||||
|
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
||||||
|
out = copy.deepcopy(im)
|
||||||
|
assert out.size == (590, 88)
|
||||||
|
|
|
@ -30,15 +30,16 @@ from .helper import assert_image_equal, hopper
|
||||||
ImageFilter.UnsharpMask(10),
|
ImageFilter.UnsharpMask(10),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
||||||
def test_sanity(filter_to_apply, mode):
|
def test_sanity(filter_to_apply, mode):
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
|
if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
|
||||||
out = im.filter(filter_to_apply)
|
out = im.filter(filter_to_apply)
|
||||||
assert out.mode == im.mode
|
assert out.mode == im.mode
|
||||||
assert out.size == im.size
|
assert out.size == im.size
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
|
||||||
def test_sanity_error(mode):
|
def test_sanity_error(mode):
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
|
@ -130,10 +131,12 @@ def test_kernel_not_enough_coefficients():
|
||||||
ImageFilter.Kernel((3, 3), (0, 0))
|
ImageFilter.Kernel((3, 3), (0, 0))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
||||||
def test_consistency_3x3(mode):
|
def test_consistency_3x3(mode):
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
with Image.open("Tests/images/hopper_emboss.bmp") as reference:
|
reference_name = "hopper_emboss"
|
||||||
|
reference_name += "_I.png" if mode == "I" else ".bmp"
|
||||||
|
with Image.open("Tests/images/" + reference_name) as reference:
|
||||||
kernel = ImageFilter.Kernel(
|
kernel = ImageFilter.Kernel(
|
||||||
(3, 3),
|
(3, 3),
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
@ -146,16 +149,20 @@ def test_consistency_3x3(mode):
|
||||||
source = source.split() * 2
|
source = source.split() * 2
|
||||||
reference = reference.split() * 2
|
reference = reference.split() * 2
|
||||||
|
|
||||||
assert_image_equal(
|
if mode == "I":
|
||||||
Image.merge(mode, source[: len(mode)]).filter(kernel),
|
source = source[0].convert(mode)
|
||||||
Image.merge(mode, reference[: len(mode)]),
|
else:
|
||||||
)
|
source = Image.merge(mode, source[: len(mode)])
|
||||||
|
reference = Image.merge(mode, reference[: len(mode)])
|
||||||
|
assert_image_equal(source.filter(kernel), reference)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
|
@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
|
||||||
def test_consistency_5x5(mode):
|
def test_consistency_5x5(mode):
|
||||||
with Image.open("Tests/images/hopper.bmp") as source:
|
with Image.open("Tests/images/hopper.bmp") as source:
|
||||||
with Image.open("Tests/images/hopper_emboss_more.bmp") as reference:
|
reference_name = "hopper_emboss_more"
|
||||||
|
reference_name += "_I.png" if mode == "I" else ".bmp"
|
||||||
|
with Image.open("Tests/images/" + reference_name) as reference:
|
||||||
kernel = ImageFilter.Kernel(
|
kernel = ImageFilter.Kernel(
|
||||||
(5, 5),
|
(5, 5),
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
@ -170,10 +177,12 @@ def test_consistency_5x5(mode):
|
||||||
source = source.split() * 2
|
source = source.split() * 2
|
||||||
reference = reference.split() * 2
|
reference = reference.split() * 2
|
||||||
|
|
||||||
assert_image_equal(
|
if mode == "I":
|
||||||
Image.merge(mode, source[: len(mode)]).filter(kernel),
|
source = source[0].convert(mode)
|
||||||
Image.merge(mode, reference[: len(mode)]),
|
else:
|
||||||
)
|
source = Image.merge(mode, source[: len(mode)])
|
||||||
|
reference = Image.merge(mode, reference[: len(mode)])
|
||||||
|
assert_image_equal(source.filter(kernel), reference)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_box_blur_filter():
|
def test_invalid_box_blur_filter():
|
||||||
|
|
|
@ -1380,20 +1380,20 @@ def test_same_color_outline(bbox):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"n_sides, rotation, polygon_name",
|
"n_sides, polygon_name, args",
|
||||||
[(4, 0, "square"), (8, 0, "regular_octagon"), (4, 45, "square")],
|
[
|
||||||
|
(4, "square", {}),
|
||||||
|
(8, "regular_octagon", {}),
|
||||||
|
(4, "square_rotate_45", {"rotation": 45}),
|
||||||
|
(3, "triangle_width", {"width": 5, "outline": "yellow"}),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
def test_draw_regular_polygon(n_sides, rotation, polygon_name):
|
def test_draw_regular_polygon(n_sides, polygon_name, args):
|
||||||
im = Image.new("RGBA", size=(W, H), color=(255, 0, 0, 0))
|
im = Image.new("RGBA", size=(W, H), color=(255, 0, 0, 0))
|
||||||
filename_base = f"Tests/images/imagedraw_{polygon_name}"
|
filename = f"Tests/images/imagedraw_{polygon_name}.png"
|
||||||
filename = (
|
|
||||||
f"{filename_base}.png"
|
|
||||||
if rotation == 0
|
|
||||||
else f"{filename_base}_rotate_{rotation}.png"
|
|
||||||
)
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
bounding_circle = ((W // 2, H // 2), 25)
|
bounding_circle = ((W // 2, H // 2), 25)
|
||||||
draw.regular_polygon(bounding_circle, n_sides, rotation=rotation, fill="red")
|
draw.regular_polygon(bounding_circle, n_sides, fill="red", **args)
|
||||||
assert_image_equal_tofile(im, filename)
|
assert_image_equal_tofile(im, filename)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,16 @@ def test_getlength(
|
||||||
assert length == length_raqm
|
assert length == length_raqm
|
||||||
|
|
||||||
|
|
||||||
|
def test_float_size():
|
||||||
|
lengths = []
|
||||||
|
for size in (48, 48.5, 49):
|
||||||
|
f = ImageFont.truetype(
|
||||||
|
"Tests/fonts/NotoSans-Regular.ttf", size, layout_engine=layout_engine
|
||||||
|
)
|
||||||
|
lengths.append(f.getlength("text"))
|
||||||
|
assert lengths[0] != lengths[1] != lengths[2]
|
||||||
|
|
||||||
|
|
||||||
def test_render_multiline(font):
|
def test_render_multiline(font):
|
||||||
im = Image.new(mode="RGB", size=(300, 100))
|
im = Image.new(mode="RGB", size=(300, 100))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
|
@ -28,7 +28,7 @@ def test_path():
|
||||||
(6.0, 7.0),
|
(6.0, 7.0),
|
||||||
(8.0, 9.0),
|
(8.0, 9.0),
|
||||||
]
|
]
|
||||||
assert p.tolist(1) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
|
assert p.tolist(True) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
|
||||||
|
|
||||||
assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
|
assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
|
||||||
|
|
||||||
|
@ -38,48 +38,65 @@ def test_path():
|
||||||
p.transform((1, 0, 1, 0, 1, 1))
|
p.transform((1, 0, 1, 0, 1, 1))
|
||||||
assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
|
assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
|
||||||
|
|
||||||
# alternative constructors
|
|
||||||
p = ImagePath.Path([0, 1])
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path([0.0, 1.0])
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path([0, 1])
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path([(0, 1)])
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path(p)
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path(p.tolist(0))
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path(p.tolist(1))
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
p = ImagePath.Path(array.array("f", [0, 1]))
|
|
||||||
assert list(p) == [(0.0, 1.0)]
|
|
||||||
|
|
||||||
arr = array.array("f", [0, 1])
|
@pytest.mark.parametrize(
|
||||||
p = ImagePath.Path(arr.tobytes())
|
"coords",
|
||||||
|
(
|
||||||
|
(0, 1),
|
||||||
|
[0, 1],
|
||||||
|
(0.0, 1.0),
|
||||||
|
[0.0, 1.0],
|
||||||
|
((0, 1),),
|
||||||
|
[(0, 1)],
|
||||||
|
((0.0, 1.0),),
|
||||||
|
[(0.0, 1.0)],
|
||||||
|
array.array("f", [0, 1]),
|
||||||
|
array.array("f", [0, 1]).tobytes(),
|
||||||
|
ImagePath.Path((0, 1)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_path_constructors(coords):
|
||||||
|
# Arrange / Act
|
||||||
|
p = ImagePath.Path(coords)
|
||||||
|
|
||||||
|
# Assert
|
||||||
assert list(p) == [(0.0, 1.0)]
|
assert list(p) == [(0.0, 1.0)]
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_coords():
|
@pytest.mark.parametrize(
|
||||||
# Arrange
|
"coords",
|
||||||
coords = ["a", "b"]
|
(
|
||||||
|
("a", "b"),
|
||||||
# Act / Assert
|
([0, 1],),
|
||||||
|
[[0, 1]],
|
||||||
|
([0.0, 1.0],),
|
||||||
|
[[0.0, 1.0]],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_invalid_path_constructors(coords):
|
||||||
|
# Act
|
||||||
with pytest.raises(ValueError) as e:
|
with pytest.raises(ValueError) as e:
|
||||||
ImagePath.Path(coords)
|
ImagePath.Path(coords)
|
||||||
|
|
||||||
|
# Assert
|
||||||
assert str(e.value) == "incorrect coordinate type"
|
assert str(e.value) == "incorrect coordinate type"
|
||||||
|
|
||||||
|
|
||||||
def test_path_odd_number_of_coordinates():
|
@pytest.mark.parametrize(
|
||||||
# Arrange
|
"coords",
|
||||||
coords = [0]
|
(
|
||||||
|
(0,),
|
||||||
# Act / Assert
|
[0],
|
||||||
|
(0, 1, 2),
|
||||||
|
[0, 1, 2],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_path_odd_number_of_coordinates(coords):
|
||||||
|
# Act
|
||||||
with pytest.raises(ValueError) as e:
|
with pytest.raises(ValueError) as e:
|
||||||
ImagePath.Path(coords)
|
ImagePath.Path(coords)
|
||||||
|
|
||||||
|
# Assert
|
||||||
assert str(e.value) == "wrong number of coordinates"
|
assert str(e.value) == "wrong number of coordinates"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -757,6 +757,7 @@ class TestLibUnpack:
|
||||||
|
|
||||||
def test_I16(self):
|
def test_I16(self):
|
||||||
self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605)
|
self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605)
|
||||||
|
self.assert_unpack("I;16", "I;16B", 2, 0x0102, 0x0304, 0x0506)
|
||||||
self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506)
|
self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506)
|
||||||
self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605)
|
self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605)
|
||||||
self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040)
|
self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040)
|
||||||
|
|
|
@ -117,3 +117,9 @@ def test_pdf_repr():
|
||||||
assert pdf_repr(b"a)/b\\(c") == rb"(a\)/b\\\(c)"
|
assert pdf_repr(b"a)/b\\(c") == rb"(a\)/b\\\(c)"
|
||||||
assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]"
|
assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]"
|
||||||
assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"
|
assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_xref_entry():
|
||||||
|
pdf = PdfParser("Tests/images/duplicate_xref_entry.pdf")
|
||||||
|
assert pdf.xref_table.existing_entries[6][0] == 1197
|
||||||
|
pdf.close()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install libimagequant
|
# install libimagequant
|
||||||
|
|
||||||
archive=libimagequant-4.1.1
|
archive=libimagequant-4.2.0
|
||||||
|
|
||||||
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# install raqm
|
# install raqm
|
||||||
|
|
||||||
|
|
||||||
archive=libraqm-0.10.0
|
archive=libraqm-0.10.1
|
||||||
|
|
||||||
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
* **libimagequant** provides improved color quantization
|
* **libimagequant** provides improved color quantization
|
||||||
|
|
||||||
* Pillow has been tested with libimagequant **2.6-4.1.1**
|
* Pillow has been tested with libimagequant **2.6-4.2**
|
||||||
* Libimagequant is licensed GPLv3, which is more restrictive than
|
* Libimagequant is licensed GPLv3, which is more restrictive than
|
||||||
the Pillow license, therefore we will not be distributing binaries
|
the Pillow license, therefore we will not be distributing binaries
|
||||||
with libimagequant support enabled.
|
with libimagequant support enabled.
|
||||||
|
@ -448,17 +448,15 @@ These platforms are built and tested for every change.
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Debian 11 Bullseye | 3.9 | x86 |
|
| Debian 11 Bullseye | 3.9 | x86 |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Fedora 36 | 3.10 | x86-64 |
|
|
||||||
+----------------------------------+----------------------------+---------------------+
|
|
||||||
| Fedora 37 | 3.11 | x86-64 |
|
| Fedora 37 | 3.11 | x86-64 |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
|
| Fedora 38 | 3.11 | x86-64 |
|
||||||
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Gentoo | 3.9 | x86-64 |
|
| Gentoo | 3.9 | x86-64 |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| macOS 12 Monterey | 3.8, 3.9, 3.10, 3.11, | x86-64 |
|
| macOS 12 Monterey | 3.8, 3.9, 3.10, 3.11, | x86-64 |
|
||||||
| | 3.12, PyPy3 | |
|
| | 3.12, PyPy3 | |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 |
|
|
||||||
+----------------------------------+----------------------------+---------------------+
|
|
||||||
| Ubuntu Linux 20.04 LTS (Focal) | 3.8 | x86-64 |
|
| Ubuntu Linux 20.04 LTS (Focal) | 3.8 | x86-64 |
|
||||||
+----------------------------------+----------------------------+---------------------+
|
+----------------------------------+----------------------------+---------------------+
|
||||||
| Ubuntu Linux 22.04 LTS (Jammy) | 3.8, 3.9, 3.10, 3.11, | x86-64 |
|
| Ubuntu Linux 22.04 LTS (Jammy) | 3.8, 3.9, 3.10, 3.11, | x86-64 |
|
||||||
|
@ -492,7 +490,7 @@ These platforms have been reported to work at the versions mentioned.
|
||||||
| Operating system | | Tested Python | | Latest tested | | Tested |
|
| Operating system | | Tested Python | | Latest tested | | Tested |
|
||||||
| | | versions | | Pillow version | | processors |
|
| | | versions | | Pillow version | | processors |
|
||||||
+==================================+===========================+==================+==============+
|
+==================================+===========================+==================+==============+
|
||||||
| macOS 13 Ventura | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0 |arm |
|
| macOS 13 Ventura | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.5.0 |arm |
|
||||||
+----------------------------------+---------------------------+------------------+--------------+
|
+----------------------------------+---------------------------+------------------+--------------+
|
||||||
| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
|
| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
|
||||||
+----------------------------------+---------------------------+------------------+--------------+
|
+----------------------------------+---------------------------+------------------+--------------+
|
||||||
|
|
|
@ -439,7 +439,7 @@ Used to specify the dithering method to use for the
|
||||||
Palettes
|
Palettes
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
Used to specify the pallete to use for the :meth:`~Image.convert` method.
|
Used to specify the palette to use for the :meth:`~Image.convert` method.
|
||||||
|
|
||||||
.. autoclass:: Palette
|
.. autoclass:: Palette
|
||||||
:members:
|
:members:
|
||||||
|
|
|
@ -296,7 +296,7 @@ Methods
|
||||||
:param width: The line width, in pixels.
|
:param width: The line width, in pixels.
|
||||||
|
|
||||||
|
|
||||||
.. py:method:: ImageDraw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None)
|
.. py:method:: ImageDraw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1)
|
||||||
|
|
||||||
Draws a regular polygon inscribed in ``bounding_circle``,
|
Draws a regular polygon inscribed in ``bounding_circle``,
|
||||||
with ``n_sides``, and rotation of ``rotation`` degrees.
|
with ``n_sides``, and rotation of ``rotation`` degrees.
|
||||||
|
@ -311,6 +311,7 @@ Methods
|
||||||
(e.g. ``rotation=90``, applies a 90 degree rotation).
|
(e.g. ``rotation=90``, applies a 90 degree rotation).
|
||||||
:param fill: Color to use for the fill.
|
:param fill: Color to use for the fill.
|
||||||
:param outline: Color to use for the outline.
|
:param outline: Color to use for the outline.
|
||||||
|
:param width: The line width, in pixels.
|
||||||
|
|
||||||
|
|
||||||
.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
|
||||||
|
|
|
@ -48,7 +48,7 @@ vector data. Path objects can be passed to the methods on the
|
||||||
|
|
||||||
Maps the path through a function.
|
Maps the path through a function.
|
||||||
|
|
||||||
.. py:method:: PIL.ImagePath.Path.tolist(flat=0)
|
.. py:method:: PIL.ImagePath.Path.tolist(flat=False)
|
||||||
|
|
||||||
Converts the path to a Python list [(x, y), …].
|
Converts the path to a Python list [(x, y), …].
|
||||||
|
|
||||||
|
|
|
@ -135,10 +135,11 @@ TODO
|
||||||
API Changes
|
API Changes
|
||||||
===========
|
===========
|
||||||
|
|
||||||
TODO
|
Added line width parameter to ImageDraw regular_polygon
|
||||||
^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
TODO
|
An optional line ``width`` parameter has been added to
|
||||||
|
``ImageDraw.Draw.regular_polygon``.
|
||||||
|
|
||||||
API Additions
|
API Additions
|
||||||
=============
|
=============
|
||||||
|
@ -159,7 +160,8 @@ TODO
|
||||||
Other Changes
|
Other Changes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
TODO
|
Support reading signed 8-bit TIFF images
|
||||||
^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
TODO
|
TIFF images with signed integer data, 8 bits per sample and a photometric
|
||||||
|
interpretaton of BlackIsZero can now be read.
|
||||||
|
|
|
@ -672,7 +672,8 @@ class Image:
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return [self.info, self.mode, self.size, self.getpalette(), self.tobytes()]
|
im_data = self.tobytes() # load image first
|
||||||
|
return [self.info, self.mode, self.size, self.getpalette(), im_data]
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
Image.__init__(self)
|
Image.__init__(self)
|
||||||
|
|
|
@ -185,12 +185,8 @@ class ImageCmsProfile:
|
||||||
def _set(self, profile, filename=None):
|
def _set(self, profile, filename=None):
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
if profile:
|
|
||||||
self.product_name = None # profile.product_name
|
self.product_name = None # profile.product_name
|
||||||
self.product_info = None # profile.product_info
|
self.product_info = None # profile.product_info
|
||||||
else:
|
|
||||||
self.product_name = None
|
|
||||||
self.product_info = None
|
|
||||||
|
|
||||||
def tobytes(self):
|
def tobytes(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -279,11 +279,11 @@ class ImageDraw:
|
||||||
self.im.paste(im.im, (0, 0) + im.size, mask.im)
|
self.im.paste(im.im, (0, 0) + im.size, mask.im)
|
||||||
|
|
||||||
def regular_polygon(
|
def regular_polygon(
|
||||||
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None
|
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
|
||||||
):
|
):
|
||||||
"""Draw a regular polygon."""
|
"""Draw a regular polygon."""
|
||||||
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
|
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
|
||||||
self.polygon(xy, fill, outline)
|
self.polygon(xy, fill, outline, width)
|
||||||
|
|
||||||
def rectangle(self, xy, fill=None, outline=None, width=1):
|
def rectangle(self, xy, fill=None, outline=None, width=1):
|
||||||
"""Draw a rectangle."""
|
"""Draw a rectangle."""
|
||||||
|
|
|
@ -141,8 +141,11 @@ def grabclipboard():
|
||||||
msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
|
msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
|
||||||
raise NotImplementedError(msg)
|
raise NotImplementedError(msg)
|
||||||
fh, filepath = tempfile.mkstemp()
|
fh, filepath = tempfile.mkstemp()
|
||||||
subprocess.call(args, stdout=fh)
|
err = subprocess.run(args, stdout=fh, stderr=subprocess.PIPE).stderr
|
||||||
os.close(fh)
|
os.close(fh)
|
||||||
|
if err:
|
||||||
|
msg = f"{args[0]} error: {err.strip().decode()}"
|
||||||
|
raise ChildProcessError(msg)
|
||||||
im = Image.open(filepath)
|
im = Image.open(filepath)
|
||||||
im.load()
|
im.load()
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
|
|
|
@ -957,13 +957,10 @@ class PdfParser:
|
||||||
check_format_condition(m, "xref entry not found")
|
check_format_condition(m, "xref entry not found")
|
||||||
offset = m.end()
|
offset = m.end()
|
||||||
is_free = m.group(3) == b"f"
|
is_free = m.group(3) == b"f"
|
||||||
generation = int(m.group(2))
|
|
||||||
if not is_free:
|
if not is_free:
|
||||||
|
generation = int(m.group(2))
|
||||||
new_entry = (int(m.group(1)), generation)
|
new_entry = (int(m.group(1)), generation)
|
||||||
check_format_condition(
|
if i not in self.xref_table:
|
||||||
i not in self.xref_table or self.xref_table[i] == new_entry,
|
|
||||||
"xref entry duplicated (and not identical)",
|
|
||||||
)
|
|
||||||
self.xref_table[i] = new_entry
|
self.xref_table[i] = new_entry
|
||||||
return offset
|
return offset
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,8 @@ OPEN_INFO = {
|
||||||
(MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
|
(MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
|
||||||
(II, 1, (1,), 1, (8,), ()): ("L", "L"),
|
(II, 1, (1,), 1, (8,), ()): ("L", "L"),
|
||||||
(MM, 1, (1,), 1, (8,), ()): ("L", "L"),
|
(MM, 1, (1,), 1, (8,), ()): ("L", "L"),
|
||||||
|
(II, 1, (2,), 1, (8,), ()): ("L", "L"),
|
||||||
|
(MM, 1, (2,), 1, (8,), ()): ("L", "L"),
|
||||||
(II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
(II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
||||||
(MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
(MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
|
||||||
(II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
|
(II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
|
||||||
|
|
|
@ -116,7 +116,9 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
char *filename = NULL;
|
char *filename = NULL;
|
||||||
Py_ssize_t size;
|
float size;
|
||||||
|
FT_Size_RequestRec req;
|
||||||
|
FT_Long width;
|
||||||
Py_ssize_t index = 0;
|
Py_ssize_t index = 0;
|
||||||
Py_ssize_t layout_engine = 0;
|
Py_ssize_t layout_engine = 0;
|
||||||
unsigned char *encoding;
|
unsigned char *encoding;
|
||||||
|
@ -133,7 +135,7 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
||||||
if (!PyArg_ParseTupleAndKeywords(
|
if (!PyArg_ParseTupleAndKeywords(
|
||||||
args,
|
args,
|
||||||
kw,
|
kw,
|
||||||
"etn|nsy#n",
|
"etf|nsy#n",
|
||||||
kwlist,
|
kwlist,
|
||||||
Py_FileSystemDefaultEncoding,
|
Py_FileSystemDefaultEncoding,
|
||||||
&filename,
|
&filename,
|
||||||
|
@ -179,7 +181,13 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = FT_Set_Pixel_Sizes(self->face, 0, size);
|
width = size * 64;
|
||||||
|
req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
|
||||||
|
req.width = width;
|
||||||
|
req.height = width;
|
||||||
|
req.horiResolution = 0;
|
||||||
|
req.vertResolution = 0;
|
||||||
|
error = FT_Request_Size(self->face, &req);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error && encoding && strlen((char *)encoding) == 4) {
|
if (!error && encoding && strlen((char *)encoding) == 4) {
|
||||||
|
|
|
@ -37,6 +37,17 @@ clip8(float in) {
|
||||||
return (UINT8)in;
|
return (UINT8)in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline INT32
|
||||||
|
clip32(float in) {
|
||||||
|
if (in <= 0.0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (in >= pow(2, 31) - 1) {
|
||||||
|
return pow(2, 31) - 1;
|
||||||
|
}
|
||||||
|
return (INT32)in;
|
||||||
|
}
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
|
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
|
@ -96,8 +107,8 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
|
||||||
void
|
void
|
||||||
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
#define KERNEL1x3(in0, x, kernel, d) \
|
#define KERNEL1x3(in0, x, kernel, d) \
|
||||||
(_i2f((UINT8)in0[x - d]) * (kernel)[0] + _i2f((UINT8)in0[x]) * (kernel)[1] + \
|
(_i2f(in0[x - d]) * (kernel)[0] + _i2f(in0[x]) * (kernel)[1] + \
|
||||||
_i2f((UINT8)in0[x + d]) * (kernel)[2])
|
_i2f(in0[x + d]) * (kernel)[2])
|
||||||
|
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
@ -105,6 +116,24 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
if (im->bands == 1) {
|
if (im->bands == 1) {
|
||||||
// Add one time for rounding
|
// Add one time for rounding
|
||||||
offset += 0.5;
|
offset += 0.5;
|
||||||
|
if (im->type == IMAGING_TYPE_INT32) {
|
||||||
|
for (y = 1; y < im->ysize - 1; y++) {
|
||||||
|
INT32 *in_1 = (INT32 *)im->image[y - 1];
|
||||||
|
INT32 *in0 = (INT32 *)im->image[y];
|
||||||
|
INT32 *in1 = (INT32 *)im->image[y + 1];
|
||||||
|
INT32 *out = (INT32 *)imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = in0[0];
|
||||||
|
for (x = 1; x < im->xsize - 1; x++) {
|
||||||
|
float ss = offset;
|
||||||
|
ss += KERNEL1x3(in1, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x3(in0, x, &kernel[3], 1);
|
||||||
|
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
|
||||||
|
out[x] = clip32(ss);
|
||||||
|
}
|
||||||
|
out[x] = in0[x];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (y = 1; y < im->ysize - 1; y++) {
|
for (y = 1; y < im->ysize - 1; y++) {
|
||||||
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
||||||
UINT8 *in0 = (UINT8 *)im->image[y];
|
UINT8 *in0 = (UINT8 *)im->image[y];
|
||||||
|
@ -121,6 +150,7 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
}
|
}
|
||||||
out[x] = in0[x];
|
out[x] = in0[x];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add one time for rounding
|
// Add one time for rounding
|
||||||
offset += 0.5;
|
offset += 0.5;
|
||||||
|
@ -195,10 +225,10 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
void
|
void
|
||||||
ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
#define KERNEL1x5(in0, x, kernel, d) \
|
#define KERNEL1x5(in0, x, kernel, d) \
|
||||||
(_i2f((UINT8)in0[x - d - d]) * (kernel)[0] + \
|
(_i2f(in0[x - d - d]) * (kernel)[0] + \
|
||||||
_i2f((UINT8)in0[x - d]) * (kernel)[1] + _i2f((UINT8)in0[x]) * (kernel)[2] + \
|
_i2f(in0[x - d]) * (kernel)[1] + _i2f(in0[x]) * (kernel)[2] + \
|
||||||
_i2f((UINT8)in0[x + d]) * (kernel)[3] + \
|
_i2f(in0[x + d]) * (kernel)[3] + \
|
||||||
_i2f((UINT8)in0[x + d + d]) * (kernel)[4])
|
_i2f(in0[x + d + d]) * (kernel)[4])
|
||||||
|
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
@ -207,6 +237,30 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
if (im->bands == 1) {
|
if (im->bands == 1) {
|
||||||
// Add one time for rounding
|
// Add one time for rounding
|
||||||
offset += 0.5;
|
offset += 0.5;
|
||||||
|
if (im->type == IMAGING_TYPE_INT32) {
|
||||||
|
for (y = 2; y < im->ysize - 2; y++) {
|
||||||
|
INT32 *in_2 = (INT32 *)im->image[y - 2];
|
||||||
|
INT32 *in_1 = (INT32 *)im->image[y - 1];
|
||||||
|
INT32 *in0 = (INT32 *)im->image[y];
|
||||||
|
INT32 *in1 = (INT32 *)im->image[y + 1];
|
||||||
|
INT32 *in2 = (INT32 *)im->image[y + 2];
|
||||||
|
INT32 *out = (INT32 *)imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = in0[0];
|
||||||
|
out[1] = in0[1];
|
||||||
|
for (x = 2; x < im->xsize - 2; x++) {
|
||||||
|
float ss = offset;
|
||||||
|
ss += KERNEL1x5(in2, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x5(in1, x, &kernel[5], 1);
|
||||||
|
ss += KERNEL1x5(in0, x, &kernel[10], 1);
|
||||||
|
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
|
||||||
|
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
|
||||||
|
out[x] = clip32(ss);
|
||||||
|
}
|
||||||
|
out[x + 0] = in0[x + 0];
|
||||||
|
out[x + 1] = in0[x + 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (y = 2; y < im->ysize - 2; y++) {
|
for (y = 2; y < im->ysize - 2; y++) {
|
||||||
UINT8 *in_2 = (UINT8 *)im->image[y - 2];
|
UINT8 *in_2 = (UINT8 *)im->image[y - 2];
|
||||||
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
UINT8 *in_1 = (UINT8 *)im->image[y - 1];
|
||||||
|
@ -229,6 +283,7 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
|
||||||
out[x + 0] = in0[x + 0];
|
out[x + 0] = in0[x + 0];
|
||||||
out[x + 1] = in0[x + 1];
|
out[x + 1] = in0[x + 1];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add one time for rounding
|
// Add one time for rounding
|
||||||
offset += 0.5;
|
offset += 0.5;
|
||||||
|
@ -327,7 +382,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32 *kernel, FLOAT32 o
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
ImagingSectionCookie cookie;
|
ImagingSectionCookie cookie;
|
||||||
|
|
||||||
if (!im || im->type != IMAGING_TYPE_UINT8) {
|
if (im->type != IMAGING_TYPE_UINT8 && im->type != IMAGING_TYPE_INT32) {
|
||||||
return (Imaging)ImagingError_ModeError();
|
return (Imaging)ImagingError_ModeError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,6 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
unsigned prec = 8;
|
unsigned prec = 8;
|
||||||
unsigned bpp = 8;
|
|
||||||
unsigned _overflow_scale_factor;
|
unsigned _overflow_scale_factor;
|
||||||
|
|
||||||
stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE);
|
stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE);
|
||||||
|
@ -313,7 +312,6 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
|
||||||
color_space = OPJ_CLRSPC_GRAY;
|
color_space = OPJ_CLRSPC_GRAY;
|
||||||
pack = j2k_pack_i16;
|
pack = j2k_pack_i16;
|
||||||
prec = 16;
|
prec = 16;
|
||||||
bpp = 12;
|
|
||||||
} else if (strcmp(im->mode, "LA") == 0) {
|
} else if (strcmp(im->mode, "LA") == 0) {
|
||||||
components = 2;
|
components = 2;
|
||||||
color_space = OPJ_CLRSPC_GRAY;
|
color_space = OPJ_CLRSPC_GRAY;
|
||||||
|
@ -342,7 +340,6 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
|
||||||
image_params[n].h = im->ysize;
|
image_params[n].h = im->ysize;
|
||||||
image_params[n].x0 = image_params[n].y0 = 0;
|
image_params[n].x0 = image_params[n].y0 = 0;
|
||||||
image_params[n].prec = prec;
|
image_params[n].prec = prec;
|
||||||
image_params[n].bpp = bpp;
|
|
||||||
image_params[n].sgnd = context->sgnd == 0 ? 0 : 1;
|
image_params[n].sgnd = context->sgnd == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1149,6 +1149,16 @@ unpackI16N_I16(UINT8 *out, const UINT8 *in, int pixels) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
|
unpackI16B_I16(UINT8 *out, const UINT8 *in, int pixels) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[0] = in[1];
|
||||||
|
out[1] = in[0];
|
||||||
|
in += 2;
|
||||||
|
out += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
unpackI16R_I16(UINT8 *out, const UINT8 *in, int pixels) {
|
unpackI16R_I16(UINT8 *out, const UINT8 *in, int pixels) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < pixels; i++) {
|
for (i = 0; i < pixels; i++) {
|
||||||
|
@ -1764,6 +1774,7 @@ static struct {
|
||||||
{"I;16L", "I;16L", 16, copy2},
|
{"I;16L", "I;16L", 16, copy2},
|
||||||
{"I;16N", "I;16N", 16, copy2},
|
{"I;16N", "I;16N", 16, copy2},
|
||||||
|
|
||||||
|
{"I;16", "I;16B", 16, unpackI16B_I16},
|
||||||
{"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
|
{"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
|
||||||
{"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
|
{"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
|
||||||
{"I;16B", "I;16N", 16, unpackI16N_I16B},
|
{"I;16B", "I;16N", 16, unpackI16N_I16B},
|
||||||
|
|
2
src/thirdparty/raqm/COPYING
vendored
2
src/thirdparty/raqm/COPYING
vendored
|
@ -1,7 +1,7 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
||||||
Copyright © 2016-2022 Khaled Hosny <khaled@aliftype.com>
|
Copyright © 2016-2023 Khaled Hosny <khaled@aliftype.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
35
src/thirdparty/raqm/NEWS
vendored
35
src/thirdparty/raqm/NEWS
vendored
|
@ -1,3 +1,38 @@
|
||||||
|
Overview of changes leading to 0.10.1
|
||||||
|
Wednesday, April 12, 2023
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Make combining marks always inherit the script of their base.
|
||||||
|
|
||||||
|
Overview of changes leading to 0.10.0
|
||||||
|
Wednesday, January 11, 2023
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Fix font feature ranges.
|
||||||
|
|
||||||
|
Fix resolved direction for all-neutral text.
|
||||||
|
|
||||||
|
Implement letter and word spacing support.
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_set_text_utf16
|
||||||
|
|
||||||
|
Overview of changes leading to 0.9.0
|
||||||
|
Sunday, January 30, 2022
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Raise the minimum versions of Raqm dependencies: no longer conditionally
|
||||||
|
enabling any features based on specific dependency version.
|
||||||
|
|
||||||
|
raqm_t objects can now be reused by calling raqm_clear_contents() before
|
||||||
|
re-use, to potentially reduce the number memory allocations.
|
||||||
|
|
||||||
|
Don't hardcode python3 in tests.
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_set_freetype_load_flags_range
|
||||||
|
* raqm_clear_contents
|
||||||
|
|
||||||
Overview of changes leading to 0.8.0
|
Overview of changes leading to 0.8.0
|
||||||
Monday, December 13, 2021
|
Monday, December 13, 2021
|
||||||
====================================
|
====================================
|
||||||
|
|
2
src/thirdparty/raqm/README.md
vendored
2
src/thirdparty/raqm/README.md
vendored
|
@ -81,5 +81,5 @@ The following projects have patches to support complex text layout using Raqm:
|
||||||
[1]: https://github.com/fribidi/fribidi
|
[1]: https://github.com/fribidi/fribidi
|
||||||
[2]: https://github.com/Tehreer/SheenBidi
|
[2]: https://github.com/Tehreer/SheenBidi
|
||||||
[3]: https://github.com/harfbuzz/harfbuzz
|
[3]: https://github.com/harfbuzz/harfbuzz
|
||||||
[4]: https://www.freetype.org
|
[4]: https://freetype.org/
|
||||||
[5]: https://www.gtk.org/gtk-doc
|
[5]: https://www.gtk.org/gtk-doc
|
||||||
|
|
4
src/thirdparty/raqm/raqm-version.h
vendored
4
src/thirdparty/raqm/raqm-version.h
vendored
|
@ -33,9 +33,9 @@
|
||||||
|
|
||||||
#define RAQM_VERSION_MAJOR 0
|
#define RAQM_VERSION_MAJOR 0
|
||||||
#define RAQM_VERSION_MINOR 10
|
#define RAQM_VERSION_MINOR 10
|
||||||
#define RAQM_VERSION_MICRO 0
|
#define RAQM_VERSION_MICRO 1
|
||||||
|
|
||||||
#define RAQM_VERSION_STRING "0.10.0"
|
#define RAQM_VERSION_STRING "0.10.1"
|
||||||
|
|
||||||
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
|
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
|
||||||
((major)*10000+(minor)*100+(micro) <= \
|
((major)*10000+(minor)*100+(micro) <= \
|
||||||
|
|
25
src/thirdparty/raqm/raqm.c
vendored
25
src/thirdparty/raqm/raqm.c
vendored
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
||||||
* Copyright © 2016-2022 Khaled Hosny <khaled@aliftype.com>
|
* Copyright © 2016-2023 Khaled Hosny <khaled@aliftype.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
@ -1432,7 +1432,7 @@ raqm_get_glyphs (raqm_t *rq,
|
||||||
*
|
*
|
||||||
* Since: 0.8
|
* Since: 0.8
|
||||||
*/
|
*/
|
||||||
RAQM_API raqm_direction_t
|
raqm_direction_t
|
||||||
raqm_get_par_resolved_direction (raqm_t *rq)
|
raqm_get_par_resolved_direction (raqm_t *rq)
|
||||||
{
|
{
|
||||||
if (!rq)
|
if (!rq)
|
||||||
|
@ -1455,7 +1455,7 @@ raqm_get_par_resolved_direction (raqm_t *rq)
|
||||||
*
|
*
|
||||||
* Since: 0.8
|
* Since: 0.8
|
||||||
*/
|
*/
|
||||||
RAQM_API raqm_direction_t
|
raqm_direction_t
|
||||||
raqm_get_direction_at_index (raqm_t *rq,
|
raqm_get_direction_at_index (raqm_t *rq,
|
||||||
size_t index)
|
size_t index)
|
||||||
{
|
{
|
||||||
|
@ -2021,6 +2021,22 @@ _get_pair_index (const uint32_t ch)
|
||||||
#define STACK_IS_EMPTY(script) ((script)->size <= 0)
|
#define STACK_IS_EMPTY(script) ((script)->size <= 0)
|
||||||
#define IS_OPEN(pair_index) (((pair_index) & 1) == 0)
|
#define IS_OPEN(pair_index) (((pair_index) & 1) == 0)
|
||||||
|
|
||||||
|
static hb_script_t
|
||||||
|
_raqm_unicode_script (hb_codepoint_t u)
|
||||||
|
{
|
||||||
|
static hb_unicode_funcs_t* unicode_funcs;
|
||||||
|
|
||||||
|
unicode_funcs = hb_unicode_funcs_get_default ();
|
||||||
|
|
||||||
|
/* Make combining marks inherit the script of their bases, regardless of
|
||||||
|
* their own script.
|
||||||
|
*/
|
||||||
|
if (hb_unicode_general_category (unicode_funcs, u) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
|
||||||
|
return HB_SCRIPT_INHERITED;
|
||||||
|
|
||||||
|
return hb_unicode_script (unicode_funcs, u);
|
||||||
|
}
|
||||||
|
|
||||||
/* Resolve the script for each character in the input string, if the character
|
/* Resolve the script for each character in the input string, if the character
|
||||||
* script is common or inherited it takes the script of the character before it
|
* script is common or inherited it takes the script of the character before it
|
||||||
* except paired characters which we try to make them use the same script. We
|
* except paired characters which we try to make them use the same script. We
|
||||||
|
@ -2033,10 +2049,9 @@ _raqm_resolve_scripts (raqm_t *rq)
|
||||||
int last_set_index = -1;
|
int last_set_index = -1;
|
||||||
hb_script_t last_script = HB_SCRIPT_INVALID;
|
hb_script_t last_script = HB_SCRIPT_INVALID;
|
||||||
_raqm_stack_t *stack = NULL;
|
_raqm_stack_t *stack = NULL;
|
||||||
hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default ();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < rq->text_len; ++i)
|
for (size_t i = 0; i < rq->text_len; ++i)
|
||||||
rq->text_info[i].script = hb_unicode_script (unicode_funcs, rq->text[i]);
|
rq->text_info[i].script = _raqm_unicode_script (rq->text[i]);
|
||||||
|
|
||||||
#ifdef RAQM_TESTING
|
#ifdef RAQM_TESTING
|
||||||
RAQM_TEST ("Before script detection:\n");
|
RAQM_TEST ("Before script detection:\n");
|
||||||
|
|
2
src/thirdparty/raqm/raqm.h
vendored
2
src/thirdparty/raqm/raqm.h
vendored
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
||||||
* Copyright © 2016-2022 Khaled Hosny <khaled@aliftype.com>
|
* Copyright © 2016-2023 Khaled Hosny <khaled@aliftype.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
|
7
tox.ini
7
tox.ini
|
@ -1,6 +1,7 @@
|
||||||
[tox]
|
[tox]
|
||||||
minversion = 1.9
|
requires =
|
||||||
envlist =
|
tox>=4.2
|
||||||
|
env_list =
|
||||||
lint
|
lint
|
||||||
py{py3, 311, 310, 39, 38}
|
py{py3, 311, 310, 39, 38}
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ skip_install = true
|
||||||
deps =
|
deps =
|
||||||
check-manifest
|
check-manifest
|
||||||
pre-commit
|
pre-commit
|
||||||
passenv =
|
pass_env =
|
||||||
PRE_COMMIT_COLOR
|
PRE_COMMIT_COLOR
|
||||||
commands =
|
commands =
|
||||||
pre-commit run --all-files --show-diff-on-failure
|
pre-commit run --all-files --show-diff-on-failure
|
||||||
|
|
|
@ -337,9 +337,9 @@ deps = {
|
||||||
"libs": [r"imagequant.lib"],
|
"libs": [r"imagequant.lib"],
|
||||||
},
|
},
|
||||||
"harfbuzz": {
|
"harfbuzz": {
|
||||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/7.1.0.zip",
|
"url": "https://github.com/harfbuzz/harfbuzz/archive/7.2.0.zip",
|
||||||
"filename": "harfbuzz-7.1.0.zip",
|
"filename": "harfbuzz-7.2.0.zip",
|
||||||
"dir": "harfbuzz-7.1.0",
|
"dir": "harfbuzz-7.2.0",
|
||||||
"license": "COPYING",
|
"license": "COPYING",
|
||||||
"build": [
|
"build": [
|
||||||
*cmds_cmake(
|
*cmds_cmake(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user