mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-06 21:03:18 +03:00
Merge branch 'master' into giflzw
This commit is contained in:
commit
06dfbb8e3e
|
@ -24,8 +24,8 @@ install:
|
||||||
- mv c:\pillow-depends-master c:\pillow-depends
|
- mv c:\pillow-depends-master c:\pillow-depends
|
||||||
- xcopy /S /Y c:\pillow-depends\test_images\* c:\pillow\tests\images
|
- xcopy /S /Y c:\pillow-depends\test_images\* c:\pillow\tests\images
|
||||||
- 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\
|
- 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\
|
||||||
- ..\pillow-depends\gs9533w32.exe /S
|
- ..\pillow-depends\gs9540w32.exe /S
|
||||||
- path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.53.3\bin;%PATH%
|
- path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.54.0\bin;%PATH%
|
||||||
- cd c:\pillow\winbuild\
|
- cd c:\pillow\winbuild\
|
||||||
- ps: |
|
- ps: |
|
||||||
c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
|
c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
|
||||||
|
|
9
.github/workflows/test-windows.yml
vendored
9
.github/workflows/test-windows.yml
vendored
|
@ -71,8 +71,8 @@ jobs:
|
||||||
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
||||||
echo "$env:RUNNER_WORKSPACE\nasm-2.14.02" >> $env:GITHUB_PATH
|
echo "$env:RUNNER_WORKSPACE\nasm-2.14.02" >> $env:GITHUB_PATH
|
||||||
|
|
||||||
winbuild\depends\gs9533w32.exe /S
|
winbuild\depends\gs9540w32.exe /S
|
||||||
echo "C:\Program Files (x86)\gs\gs9.53.3\bin" >> $env:GITHUB_PATH
|
echo "C:\Program Files (x86)\gs\gs9.54.0\bin" >> $env:GITHUB_PATH
|
||||||
|
|
||||||
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
|
xcopy /S /Y winbuild\depends\test_images\* Tests\images\
|
||||||
|
|
||||||
|
@ -137,14 +137,11 @@ jobs:
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
|
||||||
|
|
||||||
|
# Raqm dependencies
|
||||||
- name: Build dependencies / FriBidi
|
- name: Build dependencies / FriBidi
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
run: "& winbuild\\build\\build_dep_fribidi.cmd"
|
run: "& winbuild\\build\\build_dep_fribidi.cmd"
|
||||||
|
|
||||||
- name: Build dependencies / Raqm
|
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
|
||||||
run: "& winbuild\\build\\build_dep_libraqm.cmd"
|
|
||||||
|
|
||||||
# trim ~150MB x 9
|
# trim ~150MB x 9
|
||||||
- name: Optimize build cache
|
- name: Optimize build cache
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -88,3 +88,6 @@ Tests/images/jpeg2000
|
||||||
Tests/images/msp
|
Tests/images/msp
|
||||||
Tests/images/picins
|
Tests/images/picins
|
||||||
Tests/images/sunraster
|
Tests/images/sunraster
|
||||||
|
|
||||||
|
# pyinstaller
|
||||||
|
*.spec
|
||||||
|
|
18
CHANGES.rst
18
CHANGES.rst
|
@ -5,6 +5,24 @@ Changelog (Pillow)
|
||||||
8.2.0 (unreleased)
|
8.2.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Add preserve_tone option to autocontrast #5350
|
||||||
|
[elejke, radarhere]
|
||||||
|
|
||||||
|
- Fixed linear_gradient and radial_gradient I and F modes #5274
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add support for reading TIFFs with PlanarConfiguration=2 #5364
|
||||||
|
[kkopachev, wiredfool, nulano]
|
||||||
|
|
||||||
|
- Deprecated categories #5351
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Do not premultiply alpha when resizing with Image.NEAREST resampling #5304
|
||||||
|
[nulano]
|
||||||
|
|
||||||
|
- Dynamically link FriBiDi instead of Raqm #5062
|
||||||
|
[nulano]
|
||||||
|
|
||||||
- Allow fewer PNG palette entries than the bit depth maximum when saving #5330
|
- Allow fewer PNG palette entries than the bit depth maximum when saving #5330
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
BIN
Tests/images/tiff_strip_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_16bit_RGB.tiff
Normal file
Binary file not shown.
BIN
Tests/images/tiff_strip_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_16bit_RGBa.tiff
Normal file
Binary file not shown.
BIN
Tests/images/tiff_strip_planar_lzw.tiff
Normal file
BIN
Tests/images/tiff_strip_planar_lzw.tiff
Normal file
Binary file not shown.
BIN
Tests/images/tiff_tiled_planar_16bit_RGB.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGB.tiff
Normal file
Binary file not shown.
BIN
Tests/images/tiff_tiled_planar_16bit_RGBa.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_16bit_RGBa.tiff
Normal file
Binary file not shown.
BIN
Tests/images/tiff_tiled_planar_lzw.tiff
Normal file
BIN
Tests/images/tiff_tiled_planar_lzw.tiff
Normal file
Binary file not shown.
48
Tests/oss-fuzz/build.sh
Executable file
48
Tests/oss-fuzz/build.sh
Executable file
|
@ -0,0 +1,48 @@
|
||||||
|
#!/bin/bash -eu
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
python3 setup.py build --build-base=/tmp/build install
|
||||||
|
|
||||||
|
# Build fuzzers in $OUT.
|
||||||
|
for fuzzer in $(find $SRC -name 'fuzz_*.py'); do
|
||||||
|
fuzzer_basename=$(basename -s .py $fuzzer)
|
||||||
|
fuzzer_package=${fuzzer_basename}.pkg
|
||||||
|
pyinstaller \
|
||||||
|
--add-binary /usr/local/lib/libjpeg.so.9:. \
|
||||||
|
--add-binary /usr/local/lib/libfreetype.so.6:. \
|
||||||
|
--add-binary /usr/local/lib/liblcms2.so.2:. \
|
||||||
|
--add-binary /usr/local/lib/libopenjp2.so.7:. \
|
||||||
|
--add-binary /usr/local/lib/libpng16.so.16:. \
|
||||||
|
--add-binary /usr/local/lib/libtiff.so.5:. \
|
||||||
|
--add-binary /usr/local/lib/libwebp.so.7:. \
|
||||||
|
--add-binary /usr/local/lib/libwebpdemux.so.2:. \
|
||||||
|
--add-binary /usr/local/lib/libwebpmux.so.3:. \
|
||||||
|
--add-binary /usr/local/lib/libxcb.so.1:. \
|
||||||
|
--distpath $OUT --onefile --name $fuzzer_package $fuzzer
|
||||||
|
|
||||||
|
# Create execution wrapper.
|
||||||
|
echo "#!/bin/sh
|
||||||
|
# LLVMFuzzerTestOneInput for fuzzer detection.
|
||||||
|
this_dir=\$(dirname \"\$0\")
|
||||||
|
LD_PRELOAD=\$this_dir/sanitizer_with_fuzzer.so \
|
||||||
|
ASAN_OPTIONS=\$ASAN_OPTIONS:symbolize=1:external_symbolizer_path=\$this_dir/llvm-symbolizer:detect_leaks=0 \
|
||||||
|
\$this_dir/$fuzzer_package \$@" > $OUT/$fuzzer_basename
|
||||||
|
chmod u+x $OUT/$fuzzer_basename
|
||||||
|
done
|
||||||
|
|
||||||
|
find Tests/images Tests/icc -print | zip -q $OUT/fuzz_pillow_seed_corpus.zip -@
|
||||||
|
find Tests/fonts -print | zip -q $OUT/fuzz_font_seed_corpus.zip -@
|
33
Tests/oss-fuzz/build_dictionaries.sh
Executable file
33
Tests/oss-fuzz/build_dictionaries.sh
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/bash -eu
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Generate image dictionaries here for each of the fuzzers and put them in the
|
||||||
|
# $OUT directory, named for the fuzzer
|
||||||
|
|
||||||
|
git clone --depth 1 https://github.com/google/fuzzing
|
||||||
|
cat fuzzing/dictionaries/bmp.dict \
|
||||||
|
fuzzing/dictionaries/dds.dict \
|
||||||
|
fuzzing/dictionaries/gif.dict \
|
||||||
|
fuzzing/dictionaries/icns.dict \
|
||||||
|
fuzzing/dictionaries/jpeg.dict \
|
||||||
|
fuzzing/dictionaries/jpeg2000.dict \
|
||||||
|
fuzzing/dictionaries/pbm.dict \
|
||||||
|
fuzzing/dictionaries/png.dict \
|
||||||
|
fuzzing/dictionaries/psd.dict \
|
||||||
|
fuzzing/dictionaries/tiff.dict \
|
||||||
|
fuzzing/dictionaries/webp.dict \
|
||||||
|
> $OUT/fuzz_pillow.dict
|
40
Tests/oss-fuzz/fuzz_font.py
Executable file
40
Tests/oss-fuzz/fuzz_font.py
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import atheris_no_libfuzzer as atheris
|
||||||
|
import fuzzers
|
||||||
|
|
||||||
|
|
||||||
|
def TestOneInput(data):
|
||||||
|
try:
|
||||||
|
fuzzers.fuzz_font(data)
|
||||||
|
except Exception:
|
||||||
|
# We're catching all exceptions because Pillow's exceptions are
|
||||||
|
# directly inheriting from Exception.
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fuzzers.enable_decompressionbomb_error()
|
||||||
|
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||||
|
atheris.Fuzz()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -14,21 +14,15 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import io
|
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
|
|
||||||
import atheris_no_libfuzzer as atheris
|
import atheris_no_libfuzzer as atheris
|
||||||
|
import fuzzers
|
||||||
from PIL import Image, ImageFile, ImageFilter
|
|
||||||
|
|
||||||
|
|
||||||
def TestOneInput(data):
|
def TestOneInput(data):
|
||||||
try:
|
try:
|
||||||
with Image.open(io.BytesIO(data)) as im:
|
fuzzers.fuzz_image(data)
|
||||||
im.rotate(45)
|
|
||||||
im.filter(ImageFilter.DETAIL)
|
|
||||||
im.save(io.BytesIO(), "BMP")
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We're catching all exceptions because Pillow's exceptions are
|
# We're catching all exceptions because Pillow's exceptions are
|
||||||
# directly inheriting from Exception.
|
# directly inheriting from Exception.
|
||||||
|
@ -37,9 +31,7 @@ def TestOneInput(data):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
fuzzers.enable_decompressionbomb_error()
|
||||||
warnings.filterwarnings("ignore")
|
|
||||||
warnings.simplefilter("error", Image.DecompressionBombWarning)
|
|
||||||
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True)
|
||||||
atheris.Fuzz()
|
atheris.Fuzz()
|
||||||
|
|
||||||
|
|
36
Tests/oss-fuzz/fuzzers.py
Normal file
36
Tests/oss-fuzz/fuzzers.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import io
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFile, ImageFilter, ImageFont
|
||||||
|
|
||||||
|
|
||||||
|
def enable_decompressionbomb_error():
|
||||||
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
warnings.simplefilter("error", Image.DecompressionBombWarning)
|
||||||
|
|
||||||
|
|
||||||
|
def fuzz_image(data):
|
||||||
|
# This will fail on some images in the corpus, as we have many
|
||||||
|
# invalid images in the test suite.
|
||||||
|
with Image.open(io.BytesIO(data)) as im:
|
||||||
|
im.rotate(45)
|
||||||
|
im.filter(ImageFilter.DETAIL)
|
||||||
|
im.save(io.BytesIO(), "BMP")
|
||||||
|
|
||||||
|
|
||||||
|
def fuzz_font(data):
|
||||||
|
wrapper = io.BytesIO(data)
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype(wrapper)
|
||||||
|
except OSError:
|
||||||
|
# Catch pcf/pilfonts/random garbage here. They return
|
||||||
|
# different font objects.
|
||||||
|
return
|
||||||
|
|
||||||
|
font.getsize_multiline("ABC\nAaaa")
|
||||||
|
font.getmask("test text")
|
||||||
|
with Image.new(mode="RGBA", size=(200, 200)) as im:
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2)
|
||||||
|
draw.text((10, 10), "Test Text", font=font, fill="#000")
|
53
Tests/oss-fuzz/test_fuzzers.py
Normal file
53
Tests/oss-fuzz/test_fuzzers.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import fuzzers
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
if sys.platform.startswith("win32"):
|
||||||
|
pytest.skip("Fuzzer is linux only", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
subprocess.check_output("find Tests/images -type f", shell=True).split(b"\n"),
|
||||||
|
)
|
||||||
|
def test_fuzz_images(path):
|
||||||
|
fuzzers.enable_decompressionbomb_error()
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
fuzzers.fuzz_image(f.read())
|
||||||
|
assert True
|
||||||
|
except (
|
||||||
|
OSError,
|
||||||
|
SyntaxError,
|
||||||
|
MemoryError,
|
||||||
|
ValueError,
|
||||||
|
NotImplementedError,
|
||||||
|
OverflowError,
|
||||||
|
):
|
||||||
|
# Known exceptions that are through from Pillow
|
||||||
|
assert True
|
||||||
|
except (
|
||||||
|
Image.DecompressionBombError,
|
||||||
|
Image.DecompressionBombWarning,
|
||||||
|
Image.UnidentifiedImageError,
|
||||||
|
):
|
||||||
|
# Known Image.* exceptions
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path", subprocess.check_output("find Tests/fonts -type f", shell=True).split(b"\n")
|
||||||
|
)
|
||||||
|
def test_fuzz_fonts(path):
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
try:
|
||||||
|
fuzzers.fuzz_font(f.read())
|
||||||
|
except (Image.DecompressionBombError, Image.DecompressionBombWarning):
|
||||||
|
pass
|
||||||
|
assert True
|
|
@ -17,7 +17,6 @@ from .helper import (
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
assert_image_similar_tofile,
|
assert_image_similar_tofile,
|
||||||
hopper,
|
hopper,
|
||||||
is_big_endian,
|
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -824,14 +823,12 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
||||||
|
|
||||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
|
||||||
def test_strip_ycbcr_jpeg_2x2_sampling(self):
|
def test_strip_ycbcr_jpeg_2x2_sampling(self):
|
||||||
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
|
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
|
||||||
with Image.open(infile) as im:
|
with Image.open(infile) as im:
|
||||||
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
||||||
|
|
||||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
|
||||||
def test_strip_ycbcr_jpeg_1x1_sampling(self):
|
def test_strip_ycbcr_jpeg_1x1_sampling(self):
|
||||||
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
|
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
|
||||||
with Image.open(infile) as im:
|
with Image.open(infile) as im:
|
||||||
|
@ -843,20 +840,57 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
||||||
|
|
||||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
|
||||||
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
|
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
|
||||||
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
|
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
|
||||||
with Image.open(infile) as im:
|
with Image.open(infile) as im:
|
||||||
assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
||||||
|
|
||||||
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
||||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
|
||||||
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
|
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
|
||||||
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
|
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
|
||||||
with Image.open(infile) as im:
|
with Image.open(infile) as im:
|
||||||
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
def test_strip_planar_rgb(self):
|
||||||
|
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||||
|
# tiff_strip_raw.tif tiff_strip_planar_lzw.tiff
|
||||||
|
infile = "Tests/images/tiff_strip_planar_lzw.tiff"
|
||||||
|
with Image.open(infile) as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
|
|
||||||
|
def test_tiled_planar_rgb(self):
|
||||||
|
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||||
|
# tiff_tiled_raw.tif tiff_tiled_planar_lzw.tiff
|
||||||
|
infile = "Tests/images/tiff_tiled_planar_lzw.tiff"
|
||||||
|
with Image.open(infile) as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
|
|
||||||
|
def test_tiled_planar_16bit_RGB(self):
|
||||||
|
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||||
|
# tiff_16bit_RGB.tiff tiff_tiled_planar_16bit_RGB.tiff
|
||||||
|
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGB.tiff") as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
||||||
|
|
||||||
|
def test_strip_planar_16bit_RGB(self):
|
||||||
|
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
|
||||||
|
# tiff_16bit_RGB.tiff tiff_strip_planar_16bit_RGB.tiff
|
||||||
|
with Image.open("Tests/images/tiff_strip_planar_16bit_RGB.tiff") as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
||||||
|
|
||||||
|
def test_tiled_planar_16bit_RGBa(self):
|
||||||
|
# gdal_translate -co TILED=yes \
|
||||||
|
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
|
||||||
|
# tiff_16bit_RGBa.tiff tiff_tiled_planar_16bit_RGBa.tiff
|
||||||
|
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGBa.tiff") as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||||
|
|
||||||
|
def test_strip_planar_16bit_RGBa(self):
|
||||||
|
# gdal_translate -co TILED=no \
|
||||||
|
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
|
||||||
|
# tiff_16bit_RGBa.tiff tiff_strip_planar_16bit_RGBa.tiff
|
||||||
|
with Image.open("Tests/images/tiff_strip_planar_16bit_RGBa.tiff") as im:
|
||||||
|
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||||
|
|
||||||
def test_old_style_jpeg(self):
|
def test_old_style_jpeg(self):
|
||||||
infile = "Tests/images/old-style-jpeg-compression.tif"
|
infile = "Tests/images/old-style-jpeg-compression.tif"
|
||||||
with Image.open(infile) as im:
|
with Image.open(infile) as im:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -523,7 +524,7 @@ class TestImage:
|
||||||
|
|
||||||
# Arrange
|
# Arrange
|
||||||
target_file = "Tests/images/linear_gradient.png"
|
target_file = "Tests/images/linear_gradient.png"
|
||||||
for mode in ["L", "P"]:
|
for mode in ["L", "P", "I", "F"]:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.linear_gradient(mode)
|
im = Image.linear_gradient(mode)
|
||||||
|
@ -549,7 +550,7 @@ class TestImage:
|
||||||
|
|
||||||
# Arrange
|
# Arrange
|
||||||
target_file = "Tests/images/radial_gradient.png"
|
target_file = "Tests/images/radial_gradient.png"
|
||||||
for mode in ["L", "P"]:
|
for mode in ["L", "P", "I", "F"]:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.radial_gradient(mode)
|
im = Image.radial_gradient(mode)
|
||||||
|
@ -769,6 +770,20 @@ class TestImage:
|
||||||
reloaded_exif.load(exif.tobytes())
|
reloaded_exif.load(exif.tobytes())
|
||||||
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)
|
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
|
||||||
|
)
|
||||||
|
def test_categories_deprecation(self):
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert hopper().category == 0
|
||||||
|
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert Image.NORMAL == 0
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert Image.SEQUENCE == 1
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert Image.CONTAINER == 2
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_module",
|
"test_module",
|
||||||
[PIL, Image],
|
[PIL, Image],
|
||||||
|
|
|
@ -143,6 +143,41 @@ class TestImageTransform:
|
||||||
|
|
||||||
self._test_alpha_premult(op)
|
self._test_alpha_premult(op)
|
||||||
|
|
||||||
|
def _test_nearest(self, op, mode):
|
||||||
|
# create white image with half transparent,
|
||||||
|
# do op,
|
||||||
|
# the image should remain white with half transparent
|
||||||
|
transparent, opaque = {
|
||||||
|
"RGBA": ((255, 255, 255, 0), (255, 255, 255, 255)),
|
||||||
|
"LA": ((255, 0), (255, 255)),
|
||||||
|
}[mode]
|
||||||
|
im = Image.new(mode, (10, 10), transparent)
|
||||||
|
im2 = Image.new(mode, (5, 10), opaque)
|
||||||
|
im.paste(im2, (0, 0))
|
||||||
|
|
||||||
|
im = op(im, (40, 10))
|
||||||
|
|
||||||
|
colors = im.getcolors()
|
||||||
|
assert colors == [
|
||||||
|
(20 * 10, opaque),
|
||||||
|
(20 * 10, transparent),
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
||||||
|
def test_nearest_resize(self, mode):
|
||||||
|
def op(im, sz):
|
||||||
|
return im.resize(sz, Image.NEAREST)
|
||||||
|
|
||||||
|
self._test_nearest(op, mode)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mode", ("RGBA", "LA"))
|
||||||
|
def test_nearest_transform(self, mode):
|
||||||
|
def op(im, sz):
|
||||||
|
(w, h) = im.size
|
||||||
|
return im.transform(sz, Image.EXTENT, (0, 0, w, h), Image.NEAREST)
|
||||||
|
|
||||||
|
self._test_nearest(op, mode)
|
||||||
|
|
||||||
def test_blank_fill(self):
|
def test_blank_fill(self):
|
||||||
# attempting to hit
|
# attempting to hit
|
||||||
# https://github.com/python-pillow/Pillow/issues/254 reported
|
# https://github.com/python-pillow/Pillow/issues/254 reported
|
||||||
|
|
|
@ -29,6 +29,7 @@ def test_sanity():
|
||||||
ImageOps.autocontrast(hopper("L"), cutoff=(2, 10))
|
ImageOps.autocontrast(hopper("L"), cutoff=(2, 10))
|
||||||
ImageOps.autocontrast(hopper("L"), ignore=[0, 255])
|
ImageOps.autocontrast(hopper("L"), ignore=[0, 255])
|
||||||
ImageOps.autocontrast(hopper("L"), mask=hopper("L"))
|
ImageOps.autocontrast(hopper("L"), mask=hopper("L"))
|
||||||
|
ImageOps.autocontrast(hopper("L"), preserve_tone=True)
|
||||||
|
|
||||||
ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255))
|
ImageOps.colorize(hopper("L"), (0, 0, 0), (255, 255, 255))
|
||||||
ImageOps.colorize(hopper("L"), "black", "white")
|
ImageOps.colorize(hopper("L"), "black", "white")
|
||||||
|
@ -336,7 +337,7 @@ def test_autocontrast_mask_toy_input():
|
||||||
assert ImageStat.Stat(result_nomask).median == [128]
|
assert ImageStat.Stat(result_nomask).median == [128]
|
||||||
|
|
||||||
|
|
||||||
def test_auto_contrast_mask_real_input():
|
def test_autocontrast_mask_real_input():
|
||||||
# Test the autocontrast with a rectangular mask
|
# Test the autocontrast with a rectangular mask
|
||||||
with Image.open("Tests/images/iptc.jpg") as img:
|
with Image.open("Tests/images/iptc.jpg") as img:
|
||||||
|
|
||||||
|
@ -362,3 +363,52 @@ def test_auto_contrast_mask_real_input():
|
||||||
threshold=2,
|
threshold=2,
|
||||||
msg="autocontrast without mask pixel incorrect",
|
msg="autocontrast without mask pixel incorrect",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_autocontrast_preserve_tone():
|
||||||
|
def autocontrast(mode, preserve_tone):
|
||||||
|
im = hopper(mode)
|
||||||
|
return ImageOps.autocontrast(im, preserve_tone=preserve_tone).histogram()
|
||||||
|
|
||||||
|
assert autocontrast("RGB", True) != autocontrast("RGB", False)
|
||||||
|
assert autocontrast("L", True) == autocontrast("L", False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_autocontrast_preserve_gradient():
|
||||||
|
gradient = Image.linear_gradient("L")
|
||||||
|
|
||||||
|
# test with a grayscale gradient that extends to 0,255.
|
||||||
|
# Should be a noop.
|
||||||
|
out = ImageOps.autocontrast(gradient, cutoff=0, preserve_tone=True)
|
||||||
|
|
||||||
|
assert_image_equal(gradient, out)
|
||||||
|
|
||||||
|
# cutoff the top and bottom
|
||||||
|
# autocontrast should make the first and last histogram entries equal
|
||||||
|
# and, with rounding, should be 10% of the image pixels
|
||||||
|
out = ImageOps.autocontrast(gradient, cutoff=10, preserve_tone=True)
|
||||||
|
hist = out.histogram()
|
||||||
|
assert hist[0] == hist[-1]
|
||||||
|
assert hist[-1] == 256 * round(256 * 0.10)
|
||||||
|
|
||||||
|
# in rgb
|
||||||
|
img = gradient.convert("RGB")
|
||||||
|
out = ImageOps.autocontrast(img, cutoff=0, preserve_tone=True)
|
||||||
|
assert_image_equal(img, out)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"color", ((255, 255, 255), (127, 255, 0), (127, 127, 127), (0, 0, 0))
|
||||||
|
)
|
||||||
|
def test_autocontrast_preserve_one_color(color):
|
||||||
|
img = Image.new("RGB", (10, 10), color)
|
||||||
|
|
||||||
|
# single color images shouldn't change
|
||||||
|
out = ImageOps.autocontrast(img, cutoff=0, preserve_tone=True)
|
||||||
|
assert_image_equal(img, out) # single color, no cutoff
|
||||||
|
|
||||||
|
# even if there is a cutoff
|
||||||
|
out = ImageOps.autocontrast(
|
||||||
|
img, cutoff=10, preserve_tone=True
|
||||||
|
) # single color 10 cutoff
|
||||||
|
assert_image_equal(img, out)
|
||||||
|
|
|
@ -320,6 +320,23 @@ class TestLibUnpack:
|
||||||
self.assert_unpack("RGB", "G", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0))
|
self.assert_unpack("RGB", "G", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0))
|
||||||
self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
|
self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
|
||||||
|
|
||||||
|
self.assert_unpack("RGB", "R;16B", 2, (1, 0, 0), (3, 0, 0), (5, 0, 0))
|
||||||
|
self.assert_unpack("RGB", "G;16B", 2, (0, 1, 0), (0, 3, 0), (0, 5, 0))
|
||||||
|
self.assert_unpack("RGB", "B;16B", 2, (0, 0, 1), (0, 0, 3), (0, 0, 5))
|
||||||
|
|
||||||
|
self.assert_unpack("RGB", "R;16L", 2, (2, 0, 0), (4, 0, 0), (6, 0, 0))
|
||||||
|
self.assert_unpack("RGB", "G;16L", 2, (0, 2, 0), (0, 4, 0), (0, 6, 0))
|
||||||
|
self.assert_unpack("RGB", "B;16L", 2, (0, 0, 2), (0, 0, 4), (0, 0, 6))
|
||||||
|
|
||||||
|
if sys.byteorder == "little":
|
||||||
|
self.assert_unpack("RGB", "R;16N", 2, (2, 0, 0), (4, 0, 0), (6, 0, 0))
|
||||||
|
self.assert_unpack("RGB", "G;16N", 2, (0, 2, 0), (0, 4, 0), (0, 6, 0))
|
||||||
|
self.assert_unpack("RGB", "B;16N", 2, (0, 0, 2), (0, 0, 4), (0, 0, 6))
|
||||||
|
else:
|
||||||
|
self.assert_unpack("RGB", "R;16N", 2, (1, 0, 0), (3, 0, 0), (5, 0, 0))
|
||||||
|
self.assert_unpack("RGB", "G;16N", 2, (0, 1, 0), (0, 3, 0), (0, 5, 0))
|
||||||
|
self.assert_unpack("RGB", "B;16N", 2, (0, 0, 1), (0, 0, 3), (0, 0, 5))
|
||||||
|
|
||||||
def test_RGBA(self):
|
def test_RGBA(self):
|
||||||
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
|
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
|
||||||
self.assert_unpack(
|
self.assert_unpack(
|
||||||
|
@ -450,6 +467,43 @@ class TestLibUnpack:
|
||||||
self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||||
self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||||
|
|
||||||
|
self.assert_unpack("RGBA", "R;16B", 2, (1, 0, 0, 0), (3, 0, 0, 0), (5, 0, 0, 0))
|
||||||
|
self.assert_unpack("RGBA", "G;16B", 2, (0, 1, 0, 0), (0, 3, 0, 0), (0, 5, 0, 0))
|
||||||
|
self.assert_unpack("RGBA", "B;16B", 2, (0, 0, 1, 0), (0, 0, 3, 0), (0, 0, 5, 0))
|
||||||
|
self.assert_unpack("RGBA", "A;16B", 2, (0, 0, 0, 1), (0, 0, 0, 3), (0, 0, 0, 5))
|
||||||
|
|
||||||
|
self.assert_unpack("RGBA", "R;16L", 2, (2, 0, 0, 0), (4, 0, 0, 0), (6, 0, 0, 0))
|
||||||
|
self.assert_unpack("RGBA", "G;16L", 2, (0, 2, 0, 0), (0, 4, 0, 0), (0, 6, 0, 0))
|
||||||
|
self.assert_unpack("RGBA", "B;16L", 2, (0, 0, 2, 0), (0, 0, 4, 0), (0, 0, 6, 0))
|
||||||
|
self.assert_unpack("RGBA", "A;16L", 2, (0, 0, 0, 2), (0, 0, 0, 4), (0, 0, 0, 6))
|
||||||
|
|
||||||
|
if sys.byteorder == "little":
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "R;16N", 2, (2, 0, 0, 0), (4, 0, 0, 0), (6, 0, 0, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "G;16N", 2, (0, 2, 0, 0), (0, 4, 0, 0), (0, 6, 0, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "B;16N", 2, (0, 0, 2, 0), (0, 0, 4, 0), (0, 0, 6, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "A;16N", 2, (0, 0, 0, 2), (0, 0, 0, 4), (0, 0, 0, 6)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "R;16N", 2, (1, 0, 0, 0), (3, 0, 0, 0), (5, 0, 0, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "G;16N", 2, (0, 1, 0, 0), (0, 3, 0, 0), (0, 5, 0, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "B;16N", 2, (0, 0, 1, 0), (0, 0, 3, 0), (0, 0, 5, 0)
|
||||||
|
)
|
||||||
|
self.assert_unpack(
|
||||||
|
"RGBA", "A;16N", 2, (0, 0, 0, 1), (0, 0, 0, 3), (0, 0, 0, 5)
|
||||||
|
)
|
||||||
|
|
||||||
def test_RGBa(self):
|
def test_RGBa(self):
|
||||||
self.assert_unpack(
|
self.assert_unpack(
|
||||||
"RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)
|
"RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)
|
||||||
|
|
|
@ -33,6 +33,18 @@ Tk/Tcl 8.4
|
||||||
Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-01-02),
|
Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-01-02),
|
||||||
when Tk/Tcl 8.5 will be the minimum supported.
|
when Tk/Tcl 8.5 will be the minimum supported.
|
||||||
|
|
||||||
|
Categories
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 8.2.0
|
||||||
|
|
||||||
|
``im.category`` is deprecated and will be removed in Pillow 10.0.0 (2023-01-02),
|
||||||
|
along with the related ``Image.NORMAL``, ``Image.SEQUENCE`` and
|
||||||
|
``Image.CONTAINER`` attributes.
|
||||||
|
|
||||||
|
To determine if an image has multiple frames or not,
|
||||||
|
``getattr(im, "is_animated", False)`` can be used instead.
|
||||||
|
|
||||||
Image.show command parameter
|
Image.show command parameter
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,9 @@ Windows Installation
|
||||||
|
|
||||||
We provide Pillow binaries for Windows compiled for the matrix of
|
We provide Pillow binaries for Windows compiled for the matrix of
|
||||||
supported Pythons in both 32 and 64-bit versions in the wheel format.
|
supported Pythons in both 32 and 64-bit versions in the wheel format.
|
||||||
These binaries have all of the optional libraries included except
|
These binaries include support for all optional libraries except
|
||||||
for raqm, libimagequant, and libxcb::
|
libimagequant and libxcb. Raqm support requires
|
||||||
|
FriBiDi to be installed separately::
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install --upgrade Pillow
|
python3 -m pip install --upgrade Pillow
|
||||||
|
@ -71,8 +72,8 @@ macOS Installation
|
||||||
|
|
||||||
We provide binaries for macOS for each of the supported Python
|
We provide binaries for macOS for each of the supported Python
|
||||||
versions in the wheel format. These include support for all optional
|
versions in the wheel format. These include support for all optional
|
||||||
libraries except libimagequant and libxcb. Raqm support requires
|
libraries except libimagequant. Raqm support requires
|
||||||
libraqm, fribidi, and harfbuzz to be installed separately::
|
FriBiDi to be installed separately::
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install --upgrade Pillow
|
python3 -m pip install --upgrade Pillow
|
||||||
|
@ -83,7 +84,7 @@ Linux Installation
|
||||||
We provide binaries for Linux for each of the supported Python
|
We provide binaries for Linux for each of the supported Python
|
||||||
versions in the manylinux wheel format. These include support for all
|
versions in the manylinux wheel format. These include support for all
|
||||||
optional libraries except libimagequant. Raqm support requires
|
optional libraries except libimagequant. Raqm support requires
|
||||||
libraqm, fribidi, and harfbuzz to be installed separately::
|
FriBiDi to be installed separately::
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install --upgrade Pillow
|
python3 -m pip install --upgrade Pillow
|
||||||
|
@ -191,11 +192,15 @@ Many of Pillow's features require external libraries:
|
||||||
* libraqm depends on the following libraries: FreeType, HarfBuzz,
|
* libraqm depends on the following libraries: FreeType, HarfBuzz,
|
||||||
FriBiDi, make sure that you install them before installing libraqm
|
FriBiDi, make sure that you install them before installing libraqm
|
||||||
if not available as package in your system.
|
if not available as package in your system.
|
||||||
* setting text direction or font features is not supported without
|
* Setting text direction or font features is not supported without libraqm.
|
||||||
libraqm.
|
* Pillow wheels since version 8.2.0 include a modified version of libraqm that
|
||||||
* libraqm is dynamically loaded in Pillow 5.0.0 and above, so support
|
loads libfribidi at runtime if it is installed.
|
||||||
is available if all the libraries are installed.
|
On Windows this requires compiling FriBiDi and installing ``fribidi.dll``
|
||||||
* Windows support: Raqm is not included in prebuilt wheels
|
into a directory listed in the `Dynamic-Link Library Search Order (Microsoft Docs)
|
||||||
|
<https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#search-order-for-desktop-applications>`_
|
||||||
|
(``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.
|
||||||
|
|
||||||
* **libxcb** provides X11 screengrab support.
|
* **libxcb** provides X11 screengrab support.
|
||||||
|
|
||||||
|
@ -244,6 +249,12 @@ Build Options
|
||||||
an exception if the libraries are not found. Webpmux (WebP metadata)
|
an exception if the libraries are not found. Webpmux (WebP metadata)
|
||||||
relies on WebP support. Tcl and Tk also must be used together.
|
relies on WebP support. Tcl and Tk also must be used together.
|
||||||
|
|
||||||
|
* Build flags: ``--vendor-raqm --vendor-fribidi``
|
||||||
|
These flags are used to compile a modified version of libraqm and
|
||||||
|
a shim that dynamically loads libfribidi at runtime. These are
|
||||||
|
used to compile the standard Pillow wheels. Compiling libraqm requires
|
||||||
|
a C99-compliant compiler.
|
||||||
|
|
||||||
* Build flag: ``--disable-platform-guessing``. Skips all of the
|
* Build flag: ``--disable-platform-guessing``. Skips all of the
|
||||||
platform dependent guessing of include and library directories for
|
platform dependent guessing of include and library directories for
|
||||||
automated build systems that configure the proper paths in the
|
automated build systems that configure the proper paths in the
|
||||||
|
|
|
@ -486,15 +486,16 @@ Used to specify the quantization method to use for the :meth:`~Image.quantize` m
|
||||||
|
|
||||||
.. data:: MEDIANCUT
|
.. data:: MEDIANCUT
|
||||||
|
|
||||||
Median cut
|
Median cut. Default method, except for RGBA images. This method does not support
|
||||||
|
RGBA images.
|
||||||
|
|
||||||
.. data:: MAXCOVERAGE
|
.. data:: MAXCOVERAGE
|
||||||
|
|
||||||
Maximum coverage
|
Maximum coverage. This method does not support RGBA images.
|
||||||
|
|
||||||
.. data:: FASTOCTREE
|
.. data:: FASTOCTREE
|
||||||
|
|
||||||
Fast octree
|
Fast octree. Default method for RGBA images.
|
||||||
|
|
||||||
.. data:: LIBIMAGEQUANT
|
.. data:: LIBIMAGEQUANT
|
||||||
|
|
||||||
|
@ -502,10 +503,3 @@ Used to specify the quantization method to use for the :meth:`~Image.quantize` m
|
||||||
|
|
||||||
Check support using :py:func:`PIL.features.check_feature`
|
Check support using :py:func:`PIL.features.check_feature`
|
||||||
with ``feature="libimagequant"``.
|
with ``feature="libimagequant"``.
|
||||||
|
|
||||||
.. comment: These are not referenced anywhere?
|
|
||||||
Categories
|
|
||||||
^^^^^^^^^^
|
|
||||||
.. data:: NORMAL
|
|
||||||
.. data:: SEQUENCE
|
|
||||||
.. data:: CONTAINER
|
|
||||||
|
|
120
setup.py
120
setup.py
|
@ -29,6 +29,8 @@ def get_version():
|
||||||
NAME = "Pillow"
|
NAME = "Pillow"
|
||||||
PILLOW_VERSION = get_version()
|
PILLOW_VERSION = get_version()
|
||||||
FREETYPE_ROOT = None
|
FREETYPE_ROOT = None
|
||||||
|
HARFBUZZ_ROOT = None
|
||||||
|
FRIBIDI_ROOT = None
|
||||||
IMAGEQUANT_ROOT = None
|
IMAGEQUANT_ROOT = None
|
||||||
JPEG2K_ROOT = None
|
JPEG2K_ROOT = None
|
||||||
JPEG_ROOT = None
|
JPEG_ROOT = None
|
||||||
|
@ -228,6 +230,19 @@ def _find_library_file(self, library):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def _find_include_dir(self, dirname, include):
|
||||||
|
for directory in self.compiler.include_dirs:
|
||||||
|
_dbg("Checking for include file %s in %s", (include, directory))
|
||||||
|
if os.path.isfile(os.path.join(directory, include)):
|
||||||
|
_dbg("Found %s in %s", (include, directory))
|
||||||
|
return True
|
||||||
|
subdir = os.path.join(directory, dirname)
|
||||||
|
_dbg("Checking for include file %s in %s", (include, subdir))
|
||||||
|
if os.path.isfile(os.path.join(subdir, include)):
|
||||||
|
_dbg("Found %s in %s", (include, subdir))
|
||||||
|
return subdir
|
||||||
|
|
||||||
|
|
||||||
def _cmd_exists(cmd):
|
def _cmd_exists(cmd):
|
||||||
return any(
|
return any(
|
||||||
os.access(os.path.join(path, cmd), os.X_OK)
|
os.access(os.path.join(path, cmd), os.X_OK)
|
||||||
|
@ -267,6 +282,7 @@ class pil_build_ext(build_ext):
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"tiff",
|
"tiff",
|
||||||
"freetype",
|
"freetype",
|
||||||
|
"raqm",
|
||||||
"lcms",
|
"lcms",
|
||||||
"webp",
|
"webp",
|
||||||
"webpmux",
|
"webpmux",
|
||||||
|
@ -276,6 +292,7 @@ class pil_build_ext(build_ext):
|
||||||
]
|
]
|
||||||
|
|
||||||
required = {"jpeg", "zlib"}
|
required = {"jpeg", "zlib"}
|
||||||
|
vendor = set()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
for f in self.features:
|
for f in self.features:
|
||||||
|
@ -287,6 +304,9 @@ class pil_build_ext(build_ext):
|
||||||
def want(self, feat):
|
def want(self, feat):
|
||||||
return getattr(self, feat) is None
|
return getattr(self, feat) is None
|
||||||
|
|
||||||
|
def want_vendor(self, feat):
|
||||||
|
return feat in self.vendor
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield from self.features
|
yield from self.features
|
||||||
|
|
||||||
|
@ -296,6 +316,10 @@ class pil_build_ext(build_ext):
|
||||||
build_ext.user_options
|
build_ext.user_options
|
||||||
+ [(f"disable-{x}", None, f"Disable support for {x}") for x in feature]
|
+ [(f"disable-{x}", None, f"Disable support for {x}") for x in feature]
|
||||||
+ [(f"enable-{x}", None, f"Enable support for {x}") for x in feature]
|
+ [(f"enable-{x}", None, f"Enable support for {x}") for x in feature]
|
||||||
|
+ [
|
||||||
|
(f"vendor-{x}", None, f"Use vendored version of {x}")
|
||||||
|
for x in ("raqm", "fribidi")
|
||||||
|
]
|
||||||
+ [
|
+ [
|
||||||
("disable-platform-guessing", None, "Disable platform guessing on Linux"),
|
("disable-platform-guessing", None, "Disable platform guessing on Linux"),
|
||||||
("debug", None, "Debug logging"),
|
("debug", None, "Debug logging"),
|
||||||
|
@ -310,6 +334,8 @@ class pil_build_ext(build_ext):
|
||||||
for x in self.feature:
|
for x in self.feature:
|
||||||
setattr(self, f"disable_{x}", None)
|
setattr(self, f"disable_{x}", None)
|
||||||
setattr(self, f"enable_{x}", None)
|
setattr(self, f"enable_{x}", None)
|
||||||
|
for x in ("raqm", "fribidi"):
|
||||||
|
setattr(self, f"vendor_{x}", None)
|
||||||
|
|
||||||
def finalize_options(self):
|
def finalize_options(self):
|
||||||
build_ext.finalize_options(self)
|
build_ext.finalize_options(self)
|
||||||
|
@ -334,18 +360,40 @@ class pil_build_ext(build_ext):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Conflicting options: --enable-{x} and --disable-{x}"
|
f"Conflicting options: --enable-{x} and --disable-{x}"
|
||||||
)
|
)
|
||||||
|
if x == "freetype":
|
||||||
|
_dbg("--disable-freetype implies --disable-raqm")
|
||||||
|
if getattr(self, "enable_raqm"):
|
||||||
|
raise ValueError(
|
||||||
|
"Conflicting options: --enable-raqm and --disable-freetype"
|
||||||
|
)
|
||||||
|
setattr(self, "disable_raqm", True)
|
||||||
if getattr(self, f"enable_{x}"):
|
if getattr(self, f"enable_{x}"):
|
||||||
_dbg("Requiring %s", x)
|
_dbg("Requiring %s", x)
|
||||||
self.feature.required.add(x)
|
self.feature.required.add(x)
|
||||||
|
if x == "raqm":
|
||||||
|
_dbg("--enable-raqm implies --enable-freetype")
|
||||||
|
self.feature.required.add("freetype")
|
||||||
|
for x in ("raqm", "fribidi"):
|
||||||
|
if getattr(self, f"vendor_{x}"):
|
||||||
|
if getattr(self, "disable_raqm"):
|
||||||
|
raise ValueError(
|
||||||
|
f"Conflicting options: --vendor-{x} and --disable-raqm"
|
||||||
|
)
|
||||||
|
if x == "fribidi" and not getattr(self, "vendor_raqm"):
|
||||||
|
raise ValueError(
|
||||||
|
f"Conflicting options: --vendor-{x} and not --vendor-raqm"
|
||||||
|
)
|
||||||
|
_dbg("Using vendored version of %s", x)
|
||||||
|
self.feature.vendor.add(x)
|
||||||
|
|
||||||
def _update_extension(self, name, libraries, define_macros=None, include_dirs=None):
|
def _update_extension(self, name, libraries, define_macros=None, sources=None):
|
||||||
for extension in self.extensions:
|
for extension in self.extensions:
|
||||||
if extension.name == name:
|
if extension.name == name:
|
||||||
extension.libraries += libraries
|
extension.libraries += libraries
|
||||||
if define_macros is not None:
|
if define_macros is not None:
|
||||||
extension.define_macros += define_macros
|
extension.define_macros += define_macros
|
||||||
if include_dirs is not None:
|
if sources is not None:
|
||||||
extension.include_dirs += include_dirs
|
extension.sources += sources
|
||||||
if FUZZING_BUILD:
|
if FUZZING_BUILD:
|
||||||
extension.language = "c++"
|
extension.language = "c++"
|
||||||
extension.extra_link_args = ["--stdlib=libc++"]
|
extension.extra_link_args = ["--stdlib=libc++"]
|
||||||
|
@ -374,6 +422,8 @@ class pil_build_ext(build_ext):
|
||||||
TIFF_ROOT=("libtiff-5", "libtiff-4"),
|
TIFF_ROOT=("libtiff-5", "libtiff-4"),
|
||||||
ZLIB_ROOT="zlib",
|
ZLIB_ROOT="zlib",
|
||||||
FREETYPE_ROOT="freetype2",
|
FREETYPE_ROOT="freetype2",
|
||||||
|
HARFBUZZ_ROOT="harfbuzz",
|
||||||
|
FRIBIDI_ROOT="fribidi",
|
||||||
LCMS_ROOT="lcms2",
|
LCMS_ROOT="lcms2",
|
||||||
IMAGEQUANT_ROOT="libimagequant",
|
IMAGEQUANT_ROOT="libimagequant",
|
||||||
).items():
|
).items():
|
||||||
|
@ -659,6 +709,39 @@ class pil_build_ext(build_ext):
|
||||||
if subdir:
|
if subdir:
|
||||||
_add_directory(self.compiler.include_dirs, subdir, 0)
|
_add_directory(self.compiler.include_dirs, subdir, 0)
|
||||||
|
|
||||||
|
if feature.freetype and feature.want("raqm"):
|
||||||
|
if not feature.want_vendor("raqm"): # want system Raqm
|
||||||
|
_dbg("Looking for Raqm")
|
||||||
|
if _find_include_file(self, "raqm.h"):
|
||||||
|
if _find_library_file(self, "raqm"):
|
||||||
|
feature.raqm = "raqm"
|
||||||
|
elif _find_library_file(self, "libraqm"):
|
||||||
|
feature.raqm = "libraqm"
|
||||||
|
else: # want to build Raqm from src/thirdparty
|
||||||
|
_dbg("Looking for HarfBuzz")
|
||||||
|
feature.harfbuzz = None
|
||||||
|
hb_dir = _find_include_dir(self, "harfbuzz", "hb.h")
|
||||||
|
if hb_dir:
|
||||||
|
if isinstance(hb_dir, str):
|
||||||
|
_add_directory(self.compiler.include_dirs, hb_dir, 0)
|
||||||
|
if _find_library_file(self, "harfbuzz"):
|
||||||
|
feature.harfbuzz = "harfbuzz"
|
||||||
|
if feature.harfbuzz:
|
||||||
|
if not feature.want_vendor("fribidi"): # want system FriBiDi
|
||||||
|
_dbg("Looking for FriBiDi")
|
||||||
|
feature.fribidi = None
|
||||||
|
fribidi_dir = _find_include_dir(self, "fribidi", "fribidi.h")
|
||||||
|
if fribidi_dir:
|
||||||
|
if isinstance(fribidi_dir, str):
|
||||||
|
_add_directory(
|
||||||
|
self.compiler.include_dirs, fribidi_dir, 0
|
||||||
|
)
|
||||||
|
if _find_library_file(self, "fribidi"):
|
||||||
|
feature.fribidi = "fribidi"
|
||||||
|
feature.raqm = True
|
||||||
|
else: # want to build FriBiDi shim from src/thirdparty
|
||||||
|
feature.raqm = True
|
||||||
|
|
||||||
if feature.want("lcms"):
|
if feature.want("lcms"):
|
||||||
_dbg("Looking for lcms")
|
_dbg("Looking for lcms")
|
||||||
if _find_include_file(self, "lcms2.h"):
|
if _find_include_file(self, "lcms2.h"):
|
||||||
|
@ -754,9 +837,25 @@ class pil_build_ext(build_ext):
|
||||||
# additional libraries
|
# additional libraries
|
||||||
|
|
||||||
if feature.freetype:
|
if feature.freetype:
|
||||||
|
srcs = []
|
||||||
libs = ["freetype"]
|
libs = ["freetype"]
|
||||||
defs = []
|
defs = []
|
||||||
self._update_extension("PIL._imagingft", libs, defs)
|
if feature.raqm:
|
||||||
|
if not feature.want_vendor("raqm"): # using system Raqm
|
||||||
|
defs.append(("HAVE_RAQM", None))
|
||||||
|
defs.append(("HAVE_RAQM_SYSTEM", None))
|
||||||
|
libs.append(feature.raqm)
|
||||||
|
else: # building Raqm from src/thirdparty
|
||||||
|
defs.append(("HAVE_RAQM", None))
|
||||||
|
srcs.append("src/thirdparty/raqm/raqm.c")
|
||||||
|
libs.append(feature.harfbuzz)
|
||||||
|
if not feature.want_vendor("fribidi"): # using system FriBiDi
|
||||||
|
defs.append(("HAVE_FRIBIDI_SYSTEM", None))
|
||||||
|
libs.append(feature.fribidi)
|
||||||
|
else: # building FriBiDi shim from src/thirdparty
|
||||||
|
srcs.append("src/thirdparty/fribidi-shim/fribidi.c")
|
||||||
|
self._update_extension("PIL._imagingft", libs, defs, srcs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._remove_extension("PIL._imagingft")
|
self._remove_extension("PIL._imagingft")
|
||||||
|
|
||||||
|
@ -803,6 +902,12 @@ class pil_build_ext(build_ext):
|
||||||
print(f" [{v.strip()}")
|
print(f" [{v.strip()}")
|
||||||
print("-" * 68)
|
print("-" * 68)
|
||||||
|
|
||||||
|
raqm_extra_info = ""
|
||||||
|
if feature.want_vendor("raqm"):
|
||||||
|
raqm_extra_info += "bundled"
|
||||||
|
if feature.want_vendor("fribidi"):
|
||||||
|
raqm_extra_info += ", FriBiDi shim"
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
(feature.jpeg, "JPEG"),
|
(feature.jpeg, "JPEG"),
|
||||||
(feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version),
|
(feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version),
|
||||||
|
@ -810,6 +915,7 @@ class pil_build_ext(build_ext):
|
||||||
(feature.imagequant, "LIBIMAGEQUANT"),
|
(feature.imagequant, "LIBIMAGEQUANT"),
|
||||||
(feature.tiff, "LIBTIFF"),
|
(feature.tiff, "LIBTIFF"),
|
||||||
(feature.freetype, "FREETYPE2"),
|
(feature.freetype, "FREETYPE2"),
|
||||||
|
(feature.raqm, "RAQM (Text shaping)", raqm_extra_info),
|
||||||
(feature.lcms, "LITTLECMS2"),
|
(feature.lcms, "LITTLECMS2"),
|
||||||
(feature.webp, "WEBP"),
|
(feature.webp, "WEBP"),
|
||||||
(feature.webpmux, "WEBPMUX"),
|
(feature.webpmux, "WEBPMUX"),
|
||||||
|
@ -819,10 +925,10 @@ class pil_build_ext(build_ext):
|
||||||
all = 1
|
all = 1
|
||||||
for option in options:
|
for option in options:
|
||||||
if option[0]:
|
if option[0]:
|
||||||
version = ""
|
extra_info = ""
|
||||||
if len(option) >= 3 and option[2]:
|
if len(option) >= 3 and option[2]:
|
||||||
version = f" ({option[2]})"
|
extra_info = f" ({option[2]})"
|
||||||
print(f"--- {option[1]} support available{version}")
|
print(f"--- {option[1]} support available{extra_info}")
|
||||||
else:
|
else:
|
||||||
print(f"*** {option[1]} support not available")
|
print(f"*** {option[1]} support not available")
|
||||||
all = 0
|
all = 0
|
||||||
|
|
|
@ -59,6 +59,16 @@ if sys.version_info >= (3, 7):
|
||||||
if name == "PILLOW_VERSION":
|
if name == "PILLOW_VERSION":
|
||||||
_raise_version_warning()
|
_raise_version_warning()
|
||||||
return __version__
|
return __version__
|
||||||
|
else:
|
||||||
|
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
|
||||||
|
if name in categories:
|
||||||
|
warnings.warn(
|
||||||
|
"Image categories are deprecated and will be removed in Pillow 10 "
|
||||||
|
"(2023-01-02). Use is_animated instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return categories[name]
|
||||||
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +79,11 @@ else:
|
||||||
# Silence warning
|
# Silence warning
|
||||||
assert PILLOW_VERSION
|
assert PILLOW_VERSION
|
||||||
|
|
||||||
|
# categories
|
||||||
|
NORMAL = 0
|
||||||
|
SEQUENCE = 1
|
||||||
|
CONTAINER = 2
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -187,11 +202,6 @@ MAXCOVERAGE = 1
|
||||||
FASTOCTREE = 2
|
FASTOCTREE = 2
|
||||||
LIBIMAGEQUANT = 3
|
LIBIMAGEQUANT = 3
|
||||||
|
|
||||||
# categories
|
|
||||||
NORMAL = 0
|
|
||||||
SEQUENCE = 1
|
|
||||||
CONTAINER = 2
|
|
||||||
|
|
||||||
if hasattr(core, "DEFAULT_STRATEGY"):
|
if hasattr(core, "DEFAULT_STRATEGY"):
|
||||||
DEFAULT_STRATEGY = core.DEFAULT_STRATEGY
|
DEFAULT_STRATEGY = core.DEFAULT_STRATEGY
|
||||||
FILTERED = core.FILTERED
|
FILTERED = core.FILTERED
|
||||||
|
@ -213,28 +223,7 @@ DECODERS = {}
|
||||||
ENCODERS = {}
|
ENCODERS = {}
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Modes supported by this version
|
# Modes
|
||||||
|
|
||||||
_MODEINFO = {
|
|
||||||
# NOTE: this table will be removed in future versions. use
|
|
||||||
# getmode* functions or ImageMode descriptors instead.
|
|
||||||
# official modes
|
|
||||||
"1": ("L", "L", ("1",)),
|
|
||||||
"L": ("L", "L", ("L",)),
|
|
||||||
"I": ("L", "I", ("I",)),
|
|
||||||
"F": ("L", "F", ("F",)),
|
|
||||||
"P": ("P", "L", ("P",)),
|
|
||||||
"RGB": ("RGB", "L", ("R", "G", "B")),
|
|
||||||
"RGBX": ("RGB", "L", ("R", "G", "B", "X")),
|
|
||||||
"RGBA": ("RGB", "L", ("R", "G", "B", "A")),
|
|
||||||
"CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
|
|
||||||
"YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
|
|
||||||
"LAB": ("RGB", "L", ("L", "A", "B")),
|
|
||||||
"HSV": ("RGB", "L", ("H", "S", "V")),
|
|
||||||
# Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and
|
|
||||||
# BGR;24. Use these modes only if you know exactly what you're
|
|
||||||
# doing...
|
|
||||||
}
|
|
||||||
|
|
||||||
if sys.byteorder == "little":
|
if sys.byteorder == "little":
|
||||||
_ENDIAN = "<"
|
_ENDIAN = "<"
|
||||||
|
@ -280,7 +269,7 @@ def _conv_type_shape(im):
|
||||||
return (im.size[1], im.size[0], extra), typ
|
return (im.size[1], im.size[0], extra), typ
|
||||||
|
|
||||||
|
|
||||||
MODES = sorted(_MODEINFO)
|
MODES = ["1", "CMYK", "F", "HSV", "I", "L", "LAB", "P", "RGB", "RGBA", "RGBX", "YCbCr"]
|
||||||
|
|
||||||
# raw modes that may be memory mapped. NOTE: if you change this, you
|
# raw modes that may be memory mapped. NOTE: if you change this, you
|
||||||
# may have to modify the stride calculation in map.c too!
|
# may have to modify the stride calculation in map.c too!
|
||||||
|
@ -535,11 +524,22 @@ class Image:
|
||||||
self._size = (0, 0)
|
self._size = (0, 0)
|
||||||
self.palette = None
|
self.palette = None
|
||||||
self.info = {}
|
self.info = {}
|
||||||
self.category = NORMAL
|
self._category = 0
|
||||||
self.readonly = 0
|
self.readonly = 0
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
self._exif = None
|
self._exif = None
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name == "category":
|
||||||
|
warnings.warn(
|
||||||
|
"Image categories are deprecated and will be removed in Pillow 10 "
|
||||||
|
"(2023-01-02). Use is_animated instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return self._category
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
return self.size[0]
|
return self.size[0]
|
||||||
|
@ -648,7 +648,7 @@ class Image:
|
||||||
and self.mode == other.mode
|
and self.mode == other.mode
|
||||||
and self.size == other.size
|
and self.size == other.size
|
||||||
and self.info == other.info
|
and self.info == other.info
|
||||||
and self.category == other.category
|
and self._category == other._category
|
||||||
and self.readonly == other.readonly
|
and self.readonly == other.readonly
|
||||||
and self.getpalette() == other.getpalette()
|
and self.getpalette() == other.getpalette()
|
||||||
and self.tobytes() == other.tobytes()
|
and self.tobytes() == other.tobytes()
|
||||||
|
@ -1059,6 +1059,12 @@ class Image:
|
||||||
:data:`LIBIMAGEQUANT` (libimagequant; check support using
|
:data:`LIBIMAGEQUANT` (libimagequant; check support using
|
||||||
:py:func:`PIL.features.check_feature`
|
:py:func:`PIL.features.check_feature`
|
||||||
with ``feature="libimagequant"``).
|
with ``feature="libimagequant"``).
|
||||||
|
|
||||||
|
By default, :data:`MEDIANCUT` will be used.
|
||||||
|
|
||||||
|
The exception to this is RGBA images. :data:`MEDIANCUT` and
|
||||||
|
:data:`MAXCOVERAGE` do not support RGBA images, so
|
||||||
|
:data:`FASTOCTREE` is used by default instead.
|
||||||
:param kmeans: Integer
|
:param kmeans: Integer
|
||||||
:param palette: Quantize to the palette of given
|
:param palette: Quantize to the palette of given
|
||||||
:py:class:`PIL.Image.Image`.
|
:py:class:`PIL.Image.Image`.
|
||||||
|
@ -1074,11 +1080,11 @@ class Image:
|
||||||
|
|
||||||
if method is None:
|
if method is None:
|
||||||
# defaults:
|
# defaults:
|
||||||
method = 0
|
method = MEDIANCUT
|
||||||
if self.mode == "RGBA":
|
if self.mode == "RGBA":
|
||||||
method = 2
|
method = FASTOCTREE
|
||||||
|
|
||||||
if self.mode == "RGBA" and method not in (2, 3):
|
if self.mode == "RGBA" and method not in (FASTOCTREE, LIBIMAGEQUANT):
|
||||||
# Caller specified an invalid mode.
|
# Caller specified an invalid mode.
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Fast Octree (method == 2) and libimagequant (method == 3) "
|
"Fast Octree (method == 2) and libimagequant (method == 3) "
|
||||||
|
@ -1910,7 +1916,7 @@ class Image:
|
||||||
if self.mode in ("1", "P"):
|
if self.mode in ("1", "P"):
|
||||||
resample = NEAREST
|
resample = NEAREST
|
||||||
|
|
||||||
if self.mode in ["LA", "RGBA"]:
|
if self.mode in ["LA", "RGBA"] and resample != NEAREST:
|
||||||
im = self.convert(self.mode[:-1] + "a")
|
im = self.convert(self.mode[:-1] + "a")
|
||||||
im = im.resize(size, resample, box)
|
im = im.resize(size, resample, box)
|
||||||
return im.convert(self.mode)
|
return im.convert(self.mode)
|
||||||
|
@ -2394,14 +2400,14 @@ class Image:
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.mode == "LA":
|
if self.mode == "LA" and resample != NEAREST:
|
||||||
return (
|
return (
|
||||||
self.convert("La")
|
self.convert("La")
|
||||||
.transform(size, method, data, resample, fill, fillcolor)
|
.transform(size, method, data, resample, fill, fillcolor)
|
||||||
.convert("LA")
|
.convert("LA")
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.mode == "RGBA":
|
if self.mode == "RGBA" and resample != NEAREST:
|
||||||
return (
|
return (
|
||||||
self.convert("RGBa")
|
self.convert("RGBa")
|
||||||
.transform(size, method, data, resample, fill, fillcolor)
|
.transform(size, method, data, resample, fill, fillcolor)
|
||||||
|
|
|
@ -16,11 +16,6 @@
|
||||||
#
|
#
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
try:
|
|
||||||
import numpy
|
|
||||||
except ImportError: # pragma: no cover
|
|
||||||
numpy = None
|
|
||||||
|
|
||||||
|
|
||||||
class Filter:
|
class Filter:
|
||||||
pass
|
pass
|
||||||
|
@ -369,6 +364,13 @@ class Color3DLUT(MultibandFilter):
|
||||||
items = size[0] * size[1] * size[2]
|
items = size[0] * size[1] * size[2]
|
||||||
wrong_size = False
|
wrong_size = False
|
||||||
|
|
||||||
|
numpy = None
|
||||||
|
if hasattr(table, "shape"):
|
||||||
|
try:
|
||||||
|
import numpy
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
if numpy and isinstance(table, numpy.ndarray):
|
if numpy and isinstance(table, numpy.ndarray):
|
||||||
if copy_table:
|
if copy_table:
|
||||||
table = table.copy()
|
table = table.copy()
|
||||||
|
|
|
@ -35,18 +35,28 @@ def getmode(mode):
|
||||||
global _modes
|
global _modes
|
||||||
if not _modes:
|
if not _modes:
|
||||||
# initialize mode cache
|
# initialize mode cache
|
||||||
|
|
||||||
from . import Image
|
|
||||||
|
|
||||||
modes = {}
|
modes = {}
|
||||||
# core modes
|
for m, (basemode, basetype, bands) in {
|
||||||
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
|
# core modes
|
||||||
|
"1": ("L", "L", ("1",)),
|
||||||
|
"L": ("L", "L", ("L",)),
|
||||||
|
"I": ("L", "I", ("I",)),
|
||||||
|
"F": ("L", "F", ("F",)),
|
||||||
|
"P": ("P", "L", ("P",)),
|
||||||
|
"RGB": ("RGB", "L", ("R", "G", "B")),
|
||||||
|
"RGBX": ("RGB", "L", ("R", "G", "B", "X")),
|
||||||
|
"RGBA": ("RGB", "L", ("R", "G", "B", "A")),
|
||||||
|
"CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
|
||||||
|
"YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
|
||||||
|
"LAB": ("RGB", "L", ("L", "A", "B")),
|
||||||
|
"HSV": ("RGB", "L", ("H", "S", "V")),
|
||||||
|
# extra experimental modes
|
||||||
|
"RGBa": ("RGB", "L", ("R", "G", "B", "a")),
|
||||||
|
"LA": ("L", "L", ("L", "A")),
|
||||||
|
"La": ("L", "L", ("L", "a")),
|
||||||
|
"PA": ("RGB", "L", ("P", "A")),
|
||||||
|
}.items():
|
||||||
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
|
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
|
||||||
# extra experimental modes
|
|
||||||
modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
|
|
||||||
modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
|
|
||||||
modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
|
|
||||||
modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
|
|
||||||
# mapping modes
|
# mapping modes
|
||||||
for i16mode in (
|
for i16mode in (
|
||||||
"I;16",
|
"I;16",
|
||||||
|
|
|
@ -61,7 +61,7 @@ def _lut(image, lut):
|
||||||
# actions
|
# actions
|
||||||
|
|
||||||
|
|
||||||
def autocontrast(image, cutoff=0, ignore=None, mask=None):
|
def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False):
|
||||||
"""
|
"""
|
||||||
Maximize (normalize) image contrast. This function calculates a
|
Maximize (normalize) image contrast. This function calculates a
|
||||||
histogram of the input image (or mask region), removes ``cutoff`` percent of the
|
histogram of the input image (or mask region), removes ``cutoff`` percent of the
|
||||||
|
@ -77,9 +77,17 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None):
|
||||||
:param mask: Histogram used in contrast operation is computed using pixels
|
:param mask: Histogram used in contrast operation is computed using pixels
|
||||||
within the mask. If no mask is given the entire image is used
|
within the mask. If no mask is given the entire image is used
|
||||||
for histogram computation.
|
for histogram computation.
|
||||||
|
:param preserve_tone: Preserve image tone in Photoshop-like style autocontrast.
|
||||||
|
|
||||||
|
.. versionadded:: 8.2.0
|
||||||
|
|
||||||
:return: An image.
|
:return: An image.
|
||||||
"""
|
"""
|
||||||
histogram = image.histogram(mask)
|
if preserve_tone:
|
||||||
|
histogram = image.convert("L").histogram(mask)
|
||||||
|
else:
|
||||||
|
histogram = image.histogram(mask)
|
||||||
|
|
||||||
lut = []
|
lut = []
|
||||||
for layer in range(0, len(histogram), 256):
|
for layer in range(0, len(histogram), 256):
|
||||||
h = histogram[layer : layer + 256]
|
h = histogram[layer : layer + 256]
|
||||||
|
|
|
@ -68,7 +68,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
self.is_animated = self._n_frames > 1
|
self.is_animated = self._n_frames > 1
|
||||||
|
|
||||||
if len(self.images) > 1:
|
if len(self.images) > 1:
|
||||||
self.category = Image.CONTAINER
|
self._category = Image.CONTAINER
|
||||||
|
|
||||||
self.seek(0)
|
self.seek(0)
|
||||||
|
|
||||||
|
|
|
@ -1324,6 +1324,15 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
if ";16L" in rawmode:
|
if ";16L" in rawmode:
|
||||||
rawmode = rawmode.replace(";16L", ";16N")
|
rawmode = rawmode.replace(";16L", ";16N")
|
||||||
|
|
||||||
|
# YCbCr images with new jpeg compression with pixels in one plane
|
||||||
|
# unpacked straight into RGB values
|
||||||
|
if (
|
||||||
|
photo == 6
|
||||||
|
and self._compression == "jpeg"
|
||||||
|
and self._planar_configuration == 1
|
||||||
|
):
|
||||||
|
rawmode = "RGB"
|
||||||
|
|
||||||
# Offset in the tile tuple is 0, we go from 0,0 to
|
# Offset in the tile tuple is 0, we go from 0,0 to
|
||||||
# w,h, and we only do this once -- eds
|
# w,h, and we only do this once -- eds
|
||||||
a = (rawmode, self._compression, False, self.tag_v2.offset)
|
a = (rawmode, self._compression, False, self.tag_v2.offset)
|
||||||
|
|
|
@ -118,6 +118,8 @@ features = {
|
||||||
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None),
|
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None),
|
||||||
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None),
|
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None),
|
||||||
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
||||||
|
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
|
||||||
|
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
|
||||||
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
|
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
|
||||||
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
|
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
|
||||||
"xcb": ("PIL._imaging", "HAVE_XCB", None),
|
"xcb": ("PIL._imaging", "HAVE_XCB", None),
|
||||||
|
@ -274,6 +276,11 @@ def pilinfo(out=None, supported_formats=True):
|
||||||
# this check is also in src/_imagingcms.c:setup_module()
|
# this check is also in src/_imagingcms.c:setup_module()
|
||||||
version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
|
version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
|
||||||
t = "compiled for" if version_static else "loaded"
|
t = "compiled for" if version_static else "loaded"
|
||||||
|
if name == "raqm":
|
||||||
|
for f in ("fribidi", "harfbuzz"):
|
||||||
|
v2 = version_feature(f)
|
||||||
|
if v2 is not None:
|
||||||
|
v += f", {f} {v2}"
|
||||||
print("---", feature, "support ok,", t, v, file=out)
|
print("---", feature, "support ok,", t, v, file=out)
|
||||||
else:
|
else:
|
||||||
print("---", feature, "support ok", file=out)
|
print("---", feature, "support ok", file=out)
|
||||||
|
|
291
src/_imagingft.c
291
src/_imagingft.c
|
@ -35,10 +35,6 @@
|
||||||
|
|
||||||
#define KEEP_PY_UNICODE
|
#define KEEP_PY_UNICODE
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(FT_LOAD_TARGET_MONO)
|
#if !defined(FT_LOAD_TARGET_MONO)
|
||||||
#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
|
#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
|
||||||
#endif
|
#endif
|
||||||
|
@ -56,7 +52,21 @@
|
||||||
} \
|
} \
|
||||||
;
|
;
|
||||||
|
|
||||||
#include "libImaging/raqm.h"
|
#ifdef HAVE_RAQM
|
||||||
|
# ifdef HAVE_RAQM_SYSTEM
|
||||||
|
# include <raqm.h>
|
||||||
|
# else
|
||||||
|
# include "thirdparty/raqm/raqm.h"
|
||||||
|
# ifdef HAVE_FRIBIDI_SYSTEM
|
||||||
|
# include <fribidi.h>
|
||||||
|
# else
|
||||||
|
# include "thirdparty/fribidi-shim/fribidi.h"
|
||||||
|
# include <hb.h>
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int have_raqm = 0;
|
||||||
|
|
||||||
#define LAYOUT_FALLBACK 0
|
#define LAYOUT_FALLBACK 0
|
||||||
#define LAYOUT_RAQM 1
|
#define LAYOUT_RAQM 1
|
||||||
|
@ -86,42 +96,6 @@ typedef struct {
|
||||||
|
|
||||||
static PyTypeObject Font_Type;
|
static PyTypeObject Font_Type;
|
||||||
|
|
||||||
typedef const char *(*t_raqm_version_string)(void);
|
|
||||||
typedef bool (*t_raqm_version_atleast)(
|
|
||||||
unsigned int major, unsigned int minor, unsigned int micro);
|
|
||||||
typedef raqm_t *(*t_raqm_create)(void);
|
|
||||||
typedef int (*t_raqm_set_text)(raqm_t *rq, const uint32_t *text, size_t len);
|
|
||||||
typedef bool (*t_raqm_set_text_utf8)(raqm_t *rq, const char *text, size_t len);
|
|
||||||
typedef bool (*t_raqm_set_par_direction)(raqm_t *rq, raqm_direction_t dir);
|
|
||||||
typedef bool (*t_raqm_set_language)(
|
|
||||||
raqm_t *rq, const char *lang, size_t start, size_t len);
|
|
||||||
typedef bool (*t_raqm_add_font_feature)(raqm_t *rq, const char *feature, int len);
|
|
||||||
typedef bool (*t_raqm_set_freetype_face)(raqm_t *rq, FT_Face face);
|
|
||||||
typedef bool (*t_raqm_layout)(raqm_t *rq);
|
|
||||||
typedef raqm_glyph_t *(*t_raqm_get_glyphs)(raqm_t *rq, size_t *length);
|
|
||||||
typedef raqm_glyph_t_01 *(*t_raqm_get_glyphs_01)(raqm_t *rq, size_t *length);
|
|
||||||
typedef void (*t_raqm_destroy)(raqm_t *rq);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void *raqm;
|
|
||||||
int version;
|
|
||||||
t_raqm_version_string version_string;
|
|
||||||
t_raqm_version_atleast version_atleast;
|
|
||||||
t_raqm_create create;
|
|
||||||
t_raqm_set_text set_text;
|
|
||||||
t_raqm_set_text_utf8 set_text_utf8;
|
|
||||||
t_raqm_set_par_direction set_par_direction;
|
|
||||||
t_raqm_set_language set_language;
|
|
||||||
t_raqm_add_font_feature add_font_feature;
|
|
||||||
t_raqm_set_freetype_face set_freetype_face;
|
|
||||||
t_raqm_layout layout;
|
|
||||||
t_raqm_get_glyphs get_glyphs;
|
|
||||||
t_raqm_get_glyphs_01 get_glyphs_01;
|
|
||||||
t_raqm_destroy destroy;
|
|
||||||
} p_raqm_func;
|
|
||||||
|
|
||||||
static p_raqm_func p_raqm;
|
|
||||||
|
|
||||||
/* round a 26.6 pixel coordinate to the nearest integer */
|
/* round a 26.6 pixel coordinate to the nearest integer */
|
||||||
#define PIXEL(x) ((((x) + 32) & -64) >> 6)
|
#define PIXEL(x) ((((x) + 32) & -64) >> 6)
|
||||||
|
|
||||||
|
@ -140,105 +114,6 @@ geterror(int code) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
setraqm(void) {
|
|
||||||
/* set the static function pointers for dynamic raqm linking */
|
|
||||||
p_raqm.raqm = NULL;
|
|
||||||
|
|
||||||
/* Microsoft needs a totally different system */
|
|
||||||
#ifndef _WIN32
|
|
||||||
p_raqm.raqm = dlopen("libraqm.so.0", RTLD_LAZY);
|
|
||||||
if (!p_raqm.raqm) {
|
|
||||||
p_raqm.raqm = dlopen("libraqm.dylib", RTLD_LAZY);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
p_raqm.raqm = LoadLibrary("libraqm");
|
|
||||||
/* MSYS */
|
|
||||||
if (!p_raqm.raqm) {
|
|
||||||
p_raqm.raqm = LoadLibrary("libraqm-0");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!p_raqm.raqm) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
p_raqm.version_string =
|
|
||||||
(t_raqm_version_string)dlsym(p_raqm.raqm, "raqm_version_string");
|
|
||||||
p_raqm.version_atleast =
|
|
||||||
(t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast");
|
|
||||||
p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create");
|
|
||||||
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
|
||||||
p_raqm.set_text_utf8 =
|
|
||||||
(t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8");
|
|
||||||
p_raqm.set_par_direction =
|
|
||||||
(t_raqm_set_par_direction)dlsym(p_raqm.raqm, "raqm_set_par_direction");
|
|
||||||
p_raqm.set_language = (t_raqm_set_language)dlsym(p_raqm.raqm, "raqm_set_language");
|
|
||||||
p_raqm.add_font_feature =
|
|
||||||
(t_raqm_add_font_feature)dlsym(p_raqm.raqm, "raqm_add_font_feature");
|
|
||||||
p_raqm.set_freetype_face =
|
|
||||||
(t_raqm_set_freetype_face)dlsym(p_raqm.raqm, "raqm_set_freetype_face");
|
|
||||||
p_raqm.layout = (t_raqm_layout)dlsym(p_raqm.raqm, "raqm_layout");
|
|
||||||
p_raqm.destroy = (t_raqm_destroy)dlsym(p_raqm.raqm, "raqm_destroy");
|
|
||||||
if (dlsym(p_raqm.raqm, "raqm_index_to_position")) {
|
|
||||||
p_raqm.get_glyphs = (t_raqm_get_glyphs)dlsym(p_raqm.raqm, "raqm_get_glyphs");
|
|
||||||
p_raqm.version = 2;
|
|
||||||
} else {
|
|
||||||
p_raqm.version = 1;
|
|
||||||
p_raqm.get_glyphs_01 =
|
|
||||||
(t_raqm_get_glyphs_01)dlsym(p_raqm.raqm, "raqm_get_glyphs");
|
|
||||||
}
|
|
||||||
if (dlerror() ||
|
|
||||||
!(p_raqm.create && p_raqm.set_text && p_raqm.set_text_utf8 &&
|
|
||||||
p_raqm.set_par_direction && p_raqm.set_language && p_raqm.add_font_feature &&
|
|
||||||
p_raqm.set_freetype_face && p_raqm.layout &&
|
|
||||||
(p_raqm.get_glyphs || p_raqm.get_glyphs_01) && p_raqm.destroy)) {
|
|
||||||
dlclose(p_raqm.raqm);
|
|
||||||
p_raqm.raqm = NULL;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
p_raqm.version_string =
|
|
||||||
(t_raqm_version_string)GetProcAddress(p_raqm.raqm, "raqm_version_string");
|
|
||||||
p_raqm.version_atleast =
|
|
||||||
(t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast");
|
|
||||||
p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create");
|
|
||||||
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
|
||||||
p_raqm.set_text_utf8 =
|
|
||||||
(t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8");
|
|
||||||
p_raqm.set_par_direction =
|
|
||||||
(t_raqm_set_par_direction)GetProcAddress(p_raqm.raqm, "raqm_set_par_direction");
|
|
||||||
p_raqm.set_language =
|
|
||||||
(t_raqm_set_language)GetProcAddress(p_raqm.raqm, "raqm_set_language");
|
|
||||||
p_raqm.add_font_feature =
|
|
||||||
(t_raqm_add_font_feature)GetProcAddress(p_raqm.raqm, "raqm_add_font_feature");
|
|
||||||
p_raqm.set_freetype_face =
|
|
||||||
(t_raqm_set_freetype_face)GetProcAddress(p_raqm.raqm, "raqm_set_freetype_face");
|
|
||||||
p_raqm.layout = (t_raqm_layout)GetProcAddress(p_raqm.raqm, "raqm_layout");
|
|
||||||
p_raqm.destroy = (t_raqm_destroy)GetProcAddress(p_raqm.raqm, "raqm_destroy");
|
|
||||||
if (GetProcAddress(p_raqm.raqm, "raqm_index_to_position")) {
|
|
||||||
p_raqm.get_glyphs =
|
|
||||||
(t_raqm_get_glyphs)GetProcAddress(p_raqm.raqm, "raqm_get_glyphs");
|
|
||||||
p_raqm.version = 2;
|
|
||||||
} else {
|
|
||||||
p_raqm.version = 1;
|
|
||||||
p_raqm.get_glyphs_01 =
|
|
||||||
(t_raqm_get_glyphs_01)GetProcAddress(p_raqm.raqm, "raqm_get_glyphs");
|
|
||||||
}
|
|
||||||
if (!(p_raqm.create && p_raqm.set_text && p_raqm.set_text_utf8 &&
|
|
||||||
p_raqm.set_par_direction && p_raqm.set_language && p_raqm.add_font_feature &&
|
|
||||||
p_raqm.set_freetype_face && p_raqm.layout &&
|
|
||||||
(p_raqm.get_glyphs || p_raqm.get_glyphs_01) && p_raqm.destroy)) {
|
|
||||||
FreeLibrary(p_raqm.raqm);
|
|
||||||
p_raqm.raqm = NULL;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
||||||
/* create a font object from a file name and a size (in pixels) */
|
/* create a font object from a file name and a size (in pixels) */
|
||||||
|
@ -346,6 +221,8 @@ font_getchar(PyObject *string, int index, FT_ULong *char_out) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_RAQM
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
text_layout_raqm(
|
text_layout_raqm(
|
||||||
PyObject *string,
|
PyObject *string,
|
||||||
|
@ -359,10 +236,9 @@ text_layout_raqm(
|
||||||
size_t i = 0, count = 0, start = 0;
|
size_t i = 0, count = 0, start = 0;
|
||||||
raqm_t *rq;
|
raqm_t *rq;
|
||||||
raqm_glyph_t *glyphs = NULL;
|
raqm_glyph_t *glyphs = NULL;
|
||||||
raqm_glyph_t_01 *glyphs_01 = NULL;
|
|
||||||
raqm_direction_t direction;
|
raqm_direction_t direction;
|
||||||
|
|
||||||
rq = (*p_raqm.create)();
|
rq = raqm_create();
|
||||||
if (rq == NULL) {
|
if (rq == NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_create() failed.");
|
PyErr_SetString(PyExc_ValueError, "raqm_create() failed.");
|
||||||
goto failed;
|
goto failed;
|
||||||
|
@ -376,14 +252,14 @@ text_layout_raqm(
|
||||||
and raqm fails with empty strings */
|
and raqm fails with empty strings */
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
int set_text = (*p_raqm.set_text)(rq, text, size);
|
int set_text = raqm_set_text(rq, text, size);
|
||||||
PyMem_Free(text);
|
PyMem_Free(text);
|
||||||
if (!set_text) {
|
if (!set_text) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
if (lang) {
|
if (lang) {
|
||||||
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
if (!raqm_set_language(rq, lang, start, size)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
@ -401,12 +277,12 @@ text_layout_raqm(
|
||||||
direction = RAQM_DIRECTION_LTR;
|
direction = RAQM_DIRECTION_LTR;
|
||||||
} else if (strcmp(dir, "ttb") == 0) {
|
} else if (strcmp(dir, "ttb") == 0) {
|
||||||
direction = RAQM_DIRECTION_TTB;
|
direction = RAQM_DIRECTION_TTB;
|
||||||
if (p_raqm.version_atleast == NULL || !(*p_raqm.version_atleast)(0, 7, 0)) {
|
#if !defined(RAQM_VERSION_ATLEAST) || !RAQM_VERSION_ATLEAST(0, 7, 0)
|
||||||
PyErr_SetString(
|
PyErr_SetString(
|
||||||
PyExc_ValueError,
|
PyExc_ValueError,
|
||||||
"libraqm 0.7 or greater required for 'ttb' direction");
|
"libraqm 0.7 or greater required for 'ttb' direction");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
#endif
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(
|
PyErr_SetString(
|
||||||
PyExc_ValueError, "direction must be either 'rtl', 'ltr' or 'ttb'");
|
PyExc_ValueError, "direction must be either 'rtl', 'ltr' or 'ttb'");
|
||||||
|
@ -414,7 +290,7 @@ text_layout_raqm(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*p_raqm.set_par_direction)(rq, direction)) {
|
if (!raqm_set_par_direction(rq, direction)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_par_direction() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_par_direction() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
@ -446,37 +322,28 @@ text_layout_raqm(
|
||||||
feature = PyBytes_AS_STRING(bytes);
|
feature = PyBytes_AS_STRING(bytes);
|
||||||
size = PyBytes_GET_SIZE(bytes);
|
size = PyBytes_GET_SIZE(bytes);
|
||||||
}
|
}
|
||||||
if (!(*p_raqm.add_font_feature)(rq, feature, size)) {
|
if (!raqm_add_font_feature(rq, feature, size)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*p_raqm.set_freetype_face)(rq, self->face)) {
|
if (!raqm_set_freetype_face(rq, self->face)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
|
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*p_raqm.layout)(rq)) {
|
if (!raqm_layout(rq)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
|
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_raqm.version == 1) {
|
glyphs = raqm_get_glyphs(rq, &count);
|
||||||
glyphs_01 = (*p_raqm.get_glyphs_01)(rq, &count);
|
if (glyphs == NULL) {
|
||||||
if (glyphs_01 == NULL) {
|
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
|
count = 0;
|
||||||
count = 0;
|
goto failed;
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
} else { /* version == 2 */
|
|
||||||
glyphs = (*p_raqm.get_glyphs)(rq, &count);
|
|
||||||
if (glyphs == NULL) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
|
|
||||||
count = 0;
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(*glyph_info) = PyMem_New(GlyphInfo, count);
|
(*glyph_info) = PyMem_New(GlyphInfo, count);
|
||||||
|
@ -486,31 +353,22 @@ text_layout_raqm(
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_raqm.version == 1) {
|
for (i = 0; i < count; i++) {
|
||||||
for (i = 0; i < count; i++) {
|
(*glyph_info)[i].index = glyphs[i].index;
|
||||||
(*glyph_info)[i].index = glyphs_01[i].index;
|
(*glyph_info)[i].x_offset = glyphs[i].x_offset;
|
||||||
(*glyph_info)[i].x_offset = glyphs_01[i].x_offset;
|
(*glyph_info)[i].x_advance = glyphs[i].x_advance;
|
||||||
(*glyph_info)[i].x_advance = glyphs_01[i].x_advance;
|
(*glyph_info)[i].y_offset = glyphs[i].y_offset;
|
||||||
(*glyph_info)[i].y_offset = glyphs_01[i].y_offset;
|
(*glyph_info)[i].y_advance = glyphs[i].y_advance;
|
||||||
(*glyph_info)[i].y_advance = glyphs_01[i].y_advance;
|
(*glyph_info)[i].cluster = glyphs[i].cluster;
|
||||||
(*glyph_info)[i].cluster = glyphs_01[i].cluster;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
(*glyph_info)[i].index = glyphs[i].index;
|
|
||||||
(*glyph_info)[i].x_offset = glyphs[i].x_offset;
|
|
||||||
(*glyph_info)[i].x_advance = glyphs[i].x_advance;
|
|
||||||
(*glyph_info)[i].y_offset = glyphs[i].y_offset;
|
|
||||||
(*glyph_info)[i].y_advance = glyphs[i].y_advance;
|
|
||||||
(*glyph_info)[i].cluster = glyphs[i].cluster;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
(*p_raqm.destroy)(rq);
|
raqm_destroy(rq);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
text_layout_fallback(
|
text_layout_fallback(
|
||||||
PyObject *string,
|
PyObject *string,
|
||||||
|
@ -606,11 +464,13 @@ text_layout(
|
||||||
int mask,
|
int mask,
|
||||||
int color) {
|
int color) {
|
||||||
size_t count;
|
size_t count;
|
||||||
|
#ifdef HAVE_RAQM
|
||||||
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
|
if (have_raqm && self->layout_engine == LAYOUT_RAQM) {
|
||||||
count = text_layout_raqm(
|
count = text_layout_raqm(
|
||||||
string, self, dir, features, lang, glyph_info, mask, color);
|
string, self, dir, features, lang, glyph_info, mask, color);
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
count = text_layout_fallback(
|
count = text_layout_fallback(
|
||||||
string, self, dir, features, lang, glyph_info, mask, color);
|
string, self, dir, features, lang, glyph_info, mask, color);
|
||||||
}
|
}
|
||||||
|
@ -1490,12 +1350,51 @@ setup_module(PyObject *m) {
|
||||||
v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
|
v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
|
||||||
PyDict_SetItemString(d, "freetype2_version", v);
|
PyDict_SetItemString(d, "freetype2_version", v);
|
||||||
|
|
||||||
setraqm();
|
#ifdef HAVE_RAQM
|
||||||
v = PyBool_FromLong(!!p_raqm.raqm);
|
#if defined(HAVE_RAQM_SYSTEM) || defined(HAVE_FRIBIDI_SYSTEM)
|
||||||
|
have_raqm = 1;
|
||||||
|
#else
|
||||||
|
load_fribidi();
|
||||||
|
have_raqm = !!p_fribidi;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
have_raqm = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* if we have Raqm, we have all three (but possibly no version info) */
|
||||||
|
v = PyBool_FromLong(have_raqm);
|
||||||
PyDict_SetItemString(d, "HAVE_RAQM", v);
|
PyDict_SetItemString(d, "HAVE_RAQM", v);
|
||||||
if (p_raqm.version_string) {
|
PyDict_SetItemString(d, "HAVE_FRIBIDI", v);
|
||||||
PyDict_SetItemString(
|
PyDict_SetItemString(d, "HAVE_HARFBUZZ", v);
|
||||||
d, "raqm_version", PyUnicode_FromString(p_raqm.version_string()));
|
if (have_raqm) {
|
||||||
|
#ifdef RAQM_VERSION_MAJOR
|
||||||
|
v = PyUnicode_FromString(raqm_version_string());
|
||||||
|
#else
|
||||||
|
v = Py_None;
|
||||||
|
#endif
|
||||||
|
PyDict_SetItemString(d, "raqm_version", v);
|
||||||
|
|
||||||
|
#ifdef FRIBIDI_MAJOR_VERSION
|
||||||
|
{
|
||||||
|
const char *a = strchr(fribidi_version_info, ')');
|
||||||
|
const char *b = strchr(fribidi_version_info, '\n');
|
||||||
|
if (a && b && a + 2 < b) {
|
||||||
|
v = PyUnicode_FromStringAndSize(a + 2, b - (a + 2));
|
||||||
|
} else {
|
||||||
|
v = Py_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
v = Py_None;
|
||||||
|
#endif
|
||||||
|
PyDict_SetItemString(d, "fribidi_version", v);
|
||||||
|
|
||||||
|
#ifdef HB_VERSION_STRING
|
||||||
|
v = PyUnicode_FromString(hb_version_string());
|
||||||
|
#else
|
||||||
|
v = Py_None;
|
||||||
|
#endif
|
||||||
|
PyDict_SetItemString(d, "harfbuzz_version", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -76,8 +76,21 @@ ImagingFillLinearGradient(const char *mode) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (y = 0; y < 256; y++) {
|
if (im->image8) {
|
||||||
memset(im->image8[y], (unsigned char)y, 256);
|
for (y = 0; y < 256; y++) {
|
||||||
|
memset(im->image8[y], (unsigned char)y, 256);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int x;
|
||||||
|
for (y = 0; y < 256; y++) {
|
||||||
|
for (x = 0; x < 256; x++) {
|
||||||
|
if (im->type == IMAGING_TYPE_FLOAT32) {
|
||||||
|
IMAGING_PIXEL_FLOAT32(im, x, y) = y;
|
||||||
|
} else {
|
||||||
|
IMAGING_PIXEL_INT32(im, x, y) = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return im;
|
return im;
|
||||||
|
@ -103,9 +116,16 @@ ImagingFillRadialGradient(const char *mode) {
|
||||||
d = (int)sqrt(
|
d = (int)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) {
|
||||||
im->image8[y][x] = 255;
|
d = 255;
|
||||||
} else {
|
}
|
||||||
|
if (im->image8) {
|
||||||
im->image8[y][x] = d;
|
im->image8[y][x] = d;
|
||||||
|
} else {
|
||||||
|
if (im->type == IMAGING_TYPE_FLOAT32) {
|
||||||
|
IMAGING_PIXEL_FLOAT32(im, x, y) = d;
|
||||||
|
} else {
|
||||||
|
IMAGING_PIXEL_INT32(im, x, y) = d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t
|
||||||
|
|
||||||
if (context->next < GIFTABLE) {
|
if (context->next < GIFTABLE) {
|
||||||
/* We'll only add this symbol if we have room
|
/* We'll only add this symbol if we have room
|
||||||
for it (take advise, Netscape!) */
|
for it (take the advice, Netscape!) */
|
||||||
context->data[context->next] = c;
|
context->data[context->next] = c;
|
||||||
context->link[context->next] = context->lastcode;
|
context->link[context->next] = context->lastcode;
|
||||||
|
|
||||||
|
|
|
@ -789,7 +789,7 @@ resort_distance_tables(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void
|
||||||
build_distance_tables(
|
build_distance_tables(
|
||||||
uint32_t *avgDist, uint32_t **avgDistSortKey, Pixel *p, uint32_t nEntries) {
|
uint32_t *avgDist, uint32_t **avgDistSortKey, Pixel *p, uint32_t nEntries) {
|
||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
|
@ -811,7 +811,6 @@ build_distance_tables(
|
||||||
sizeof(uint32_t *),
|
sizeof(uint32_t *),
|
||||||
_sort_ulong_ptr_keys);
|
_sort_ulong_ptr_keys);
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1373,9 +1372,7 @@ quantize(
|
||||||
goto error_6;
|
goto error_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!build_distance_tables(avgDist, avgDistSortKey, p, nPaletteEntries)) {
|
build_distance_tables(avgDist, avgDistSortKey, p, nPaletteEntries);
|
||||||
goto error_7;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!map_image_pixels_from_median_box(
|
if (!map_image_pixels_from_median_box(
|
||||||
pixelData, nPixels, p, nPaletteEntries, h, avgDist, avgDistSortKey, qp)) {
|
pixelData, nPixels, p, nPaletteEntries, h, avgDist, avgDistSortKey, qp)) {
|
||||||
|
@ -1580,9 +1577,7 @@ quantize2(
|
||||||
goto error_3;
|
goto error_3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!build_distance_tables(avgDist, avgDistSortKey, p, nQuantPixels)) {
|
build_distance_tables(avgDist, avgDistSortKey, p, nQuantPixels);
|
||||||
goto error_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!map_image_pixels(
|
if (!map_image_pixels(
|
||||||
pixelData, nPixels, p, nQuantPixels, avgDist, avgDistSortKey, qp)) {
|
pixelData, nPixels, p, nQuantPixels, avgDist, avgDistSortKey, qp)) {
|
||||||
|
|
|
@ -213,24 +213,63 @@ ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
|
_pickUnpackers(Imaging im, ImagingCodecState state, TIFF *tiff, uint16 planarconfig, ImagingShuffler *unpackers) {
|
||||||
// To avoid dealing with YCbCr subsampling, let libtiff handle it
|
// if number of bands is 1, there is no difference with contig case
|
||||||
|
if (planarconfig == PLANARCONFIG_SEPARATE && im->bands > 1) {
|
||||||
|
uint16 bits_per_sample = 8;
|
||||||
|
|
||||||
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
|
||||||
|
if (bits_per_sample != 8 && bits_per_sample != 16) {
|
||||||
|
TRACE(("Invalid value for bits per sample: %d\n", bits_per_sample));
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll pick appropriate set of unpackers depending on planar_configuration
|
||||||
|
// It does not matter if data is RGB(A), CMYK or LUV really,
|
||||||
|
// we just copy it plane by plane
|
||||||
|
unpackers[0] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL);
|
||||||
|
unpackers[1] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL);
|
||||||
|
unpackers[2] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL);
|
||||||
|
unpackers[3] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL);
|
||||||
|
|
||||||
|
return im->bands;
|
||||||
|
} else {
|
||||||
|
unpackers[0] = state->shuffle;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) {
|
||||||
|
// To avoid dealing with YCbCr subsampling and other complications, let libtiff handle it
|
||||||
// Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle
|
// Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle
|
||||||
// all of the conversion. Metadata read from the TIFFRGBAImage could
|
// all of the conversion. Metadata read from the TIFFRGBAImage could
|
||||||
// be different from the metadata that the base tiff returns.
|
// be different from the metadata that the base tiff returns.
|
||||||
|
|
||||||
INT32 strip_row;
|
INT32 current_row;
|
||||||
UINT8 *new_data;
|
UINT8 *new_data;
|
||||||
UINT32 rows_per_strip, row_byte_size, rows_to_read;
|
UINT32 rows_per_block, row_byte_size, rows_to_read;
|
||||||
int ret;
|
int ret;
|
||||||
TIFFRGBAImage img;
|
TIFFRGBAImage img;
|
||||||
char emsg[1024] = "";
|
char emsg[1024] = "";
|
||||||
|
|
||||||
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
// Since using TIFFRGBAImage* functions, we can read whole tiff into rastrr in one call
|
||||||
if (ret != 1) {
|
// Let's select smaller block size. Multiplying image width by (tile length OR rows per strip)
|
||||||
rows_per_strip = state->ysize;
|
// gives us manageable block size in pixels
|
||||||
|
if (TIFFIsTiled(tiff)) {
|
||||||
|
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_TILELENGTH, &rows_per_block);
|
||||||
}
|
}
|
||||||
TRACE(("RowsPerStrip: %u \n", rows_per_strip));
|
else {
|
||||||
|
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 1) {
|
||||||
|
rows_per_block = state->ysize;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("RowsPerBlock: %u \n", rows_per_block));
|
||||||
|
|
||||||
if (!(TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg))) {
|
if (!(TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg))) {
|
||||||
TRACE(("Decode error, msg: %s", emsg));
|
TRACE(("Decode error, msg: %s", emsg));
|
||||||
|
@ -250,69 +289,73 @@ _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
|
||||||
state->ysize,
|
state->ysize,
|
||||||
img.height));
|
img.height));
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
goto decodeycbcr_err;
|
goto decodergba_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* overflow check for row byte size */
|
/* overflow check for row byte size */
|
||||||
if (INT_MAX / 4 < img.width) {
|
if (INT_MAX / 4 < img.width) {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
goto decodeycbcr_err;
|
goto decodergba_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TiffRGBAImages are 32bits/pixel.
|
// TiffRGBAImages are 32bits/pixel.
|
||||||
row_byte_size = img.width * 4;
|
row_byte_size = img.width * 4;
|
||||||
|
|
||||||
/* overflow check for realloc */
|
/* overflow check for realloc */
|
||||||
if (INT_MAX / row_byte_size < rows_per_strip) {
|
if (INT_MAX / row_byte_size < rows_per_block) {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
goto decodeycbcr_err;
|
goto decodergba_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->bytes = rows_per_strip * row_byte_size;
|
state->bytes = rows_per_block * row_byte_size;
|
||||||
|
|
||||||
TRACE(("StripSize: %d \n", state->bytes));
|
TRACE(("BlockSize: %d \n", state->bytes));
|
||||||
|
|
||||||
/* realloc to fit whole strip */
|
/* realloc to fit whole strip */
|
||||||
/* malloc check above */
|
/* malloc check above */
|
||||||
new_data = realloc(state->buffer, state->bytes);
|
new_data = realloc(state->buffer, state->bytes);
|
||||||
if (!new_data) {
|
if (!new_data) {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
goto decodeycbcr_err;
|
goto decodergba_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->buffer = new_data;
|
state->buffer = new_data;
|
||||||
|
|
||||||
for (; state->y < state->ysize; state->y += rows_per_strip) {
|
for (; state->y < state->ysize; state->y += rows_per_block) {
|
||||||
img.row_offset = state->y;
|
img.row_offset = state->y;
|
||||||
rows_to_read = min(rows_per_strip, img.height - state->y);
|
rows_to_read = min(rows_per_block, img.height - state->y);
|
||||||
|
|
||||||
if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read)) {
|
if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read)) {
|
||||||
TRACE(("Decode Error, y: %d\n", state->y));
|
TRACE(("Decode Error, y: %d\n", state->y));
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
goto decodeycbcr_err;
|
goto decodergba_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WORDS_BIGENDIAN
|
||||||
|
TIFFSwabArrayOfLong((UINT32 *)state->buffer, img.width * rows_to_read);
|
||||||
|
#endif
|
||||||
|
|
||||||
TRACE(("Decoded strip for row %d \n", state->y));
|
TRACE(("Decoded strip for row %d \n", state->y));
|
||||||
|
|
||||||
// iterate over each row in the strip and stuff data into image
|
// iterate over each row in the strip and stuff data into image
|
||||||
for (strip_row = 0;
|
for (current_row = 0;
|
||||||
strip_row < min((INT32)rows_per_strip, state->ysize - state->y);
|
current_row < min((INT32)rows_per_block, state->ysize - state->y);
|
||||||
strip_row++) {
|
current_row++) {
|
||||||
TRACE(("Writing data into line %d ; \n", state->y + strip_row));
|
TRACE(("Writing data into line %d ; \n", state->y + current_row));
|
||||||
|
|
||||||
// UINT8 * bbb = state->buffer + strip_row * (state->bytes /
|
// UINT8 * bbb = state->buffer + current_row * (state->bytes /
|
||||||
// rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
|
// rows_per_block); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
|
||||||
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
||||||
|
|
||||||
state->shuffle(
|
state->shuffle(
|
||||||
(UINT8 *)im->image[state->y + state->yoff + strip_row] +
|
(UINT8 *)im->image[state->y + state->yoff + current_row] +
|
||||||
state->xoff * im->pixelsize,
|
state->xoff * im->pixelsize,
|
||||||
state->buffer + strip_row * row_byte_size,
|
state->buffer + current_row * row_byte_size,
|
||||||
state->xsize);
|
state->xsize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeycbcr_err:
|
decodergba_err:
|
||||||
TIFFRGBAImageEnd(&img);
|
TIFFRGBAImageEnd(&img);
|
||||||
if (state->errcode != 0) {
|
if (state->errcode != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -321,41 +364,154 @@ decodeycbcr_err:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) {
|
_decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) {
|
||||||
INT32 strip_row;
|
INT32 x, y, tile_y, current_tile_length, current_tile_width;
|
||||||
|
UINT32 tile_width, tile_length;
|
||||||
|
tsize_t tile_bytes_size, row_byte_size;
|
||||||
UINT8 *new_data;
|
UINT8 *new_data;
|
||||||
UINT32 rows_per_strip, row_byte_size;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
tile_bytes_size = TIFFTileSize(tiff);
|
||||||
if (ret != 1) {
|
|
||||||
rows_per_strip = state->ysize;
|
if (tile_bytes_size == 0) {
|
||||||
|
TRACE(("Decode Error, Can not calculate TileSize\n"));
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
TRACE(("RowsPerStrip: %u \n", rows_per_strip));
|
|
||||||
|
|
||||||
// We could use TIFFStripSize, but for YCbCr data it returns subsampled data size
|
row_byte_size = TIFFTileRowSize(tiff);
|
||||||
row_byte_size = (state->xsize * state->bits + 7) / 8;
|
|
||||||
|
if (row_byte_size == 0 || row_byte_size > tile_bytes_size) {
|
||||||
|
TRACE(("Decode Error, Can not calculate TileRowSize\n"));
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* overflow check for realloc */
|
/* overflow check for realloc */
|
||||||
if (INT_MAX / row_byte_size < rows_per_strip) {
|
if (tile_bytes_size > INT_MAX - 1) {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->bytes = rows_per_strip * row_byte_size;
|
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
|
||||||
|
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);
|
||||||
|
|
||||||
|
if (tile_width > INT_MAX || tile_length > INT_MAX) {
|
||||||
|
// state->x and state->y are ints
|
||||||
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile_bytes_size > ((tile_length * state->bits / planes + 7) / 8) * tile_width) {
|
||||||
|
// If the tile size as expected by LibTiff isn't what we're expecting, abort.
|
||||||
|
// man: TIFFTileSize returns the equivalent size for a tile of data as it would be returned in a
|
||||||
|
// call to TIFFReadTile ...
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->bytes = tile_bytes_size;
|
||||||
|
|
||||||
|
TRACE(("TIFFTileSize: %d\n", state->bytes));
|
||||||
|
|
||||||
|
/* realloc to fit whole tile */
|
||||||
|
/* malloc check above */
|
||||||
|
new_data = realloc(state->buffer, state->bytes);
|
||||||
|
if (!new_data) {
|
||||||
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
state->buffer = new_data;
|
||||||
|
|
||||||
|
for (y = state->yoff; y < state->ysize; y += tile_length) {
|
||||||
|
int plane;
|
||||||
|
for (plane = 0; plane < planes; plane++) {
|
||||||
|
ImagingShuffler shuffler = unpackers[plane];
|
||||||
|
for (x = state->xoff; x < state->xsize; x += tile_width) {
|
||||||
|
/* Sanity Check. Apparently in some cases, the TiffReadRGBA* functions
|
||||||
|
have a different view of the size of the tiff than we're getting from
|
||||||
|
other functions. So, we need to check here.
|
||||||
|
*/
|
||||||
|
if (!TIFFCheckTile(tiff, x, y, 0, plane)) {
|
||||||
|
TRACE(("Check Tile Error, Tile at %dx%d\n", x, y));
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, plane) == -1) {
|
||||||
|
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("Read tile at %dx%d; \n\n", x, y));
|
||||||
|
|
||||||
|
current_tile_width = min((INT32) tile_width, state->xsize - x);
|
||||||
|
current_tile_length = min((INT32) tile_length, state->ysize - y);
|
||||||
|
// iterate over each line in the tile and stuff data into image
|
||||||
|
for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
|
||||||
|
TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width));
|
||||||
|
|
||||||
|
// UINT8 * bbb = state->buffer + tile_y * row_byte_size;
|
||||||
|
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
||||||
|
|
||||||
|
shuffler((UINT8*) im->image[tile_y + y] + x * im->pixelsize,
|
||||||
|
state->buffer + tile_y * row_byte_size,
|
||||||
|
current_tile_width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) {
|
||||||
|
INT32 strip_row = 0;
|
||||||
|
UINT8 *new_data;
|
||||||
|
UINT32 rows_per_strip;
|
||||||
|
int ret;
|
||||||
|
tsize_t strip_size, row_byte_size;
|
||||||
|
|
||||||
|
ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||||
|
if (ret != 1 || rows_per_strip==(UINT32)(-1)) {
|
||||||
|
rows_per_strip = state->ysize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows_per_strip > INT_MAX) {
|
||||||
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("RowsPerStrip: %u\n", rows_per_strip));
|
||||||
|
|
||||||
|
strip_size = TIFFStripSize(tiff);
|
||||||
|
if (strip_size > INT_MAX - 1) {
|
||||||
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strip_size > ((state->xsize * state->bits / planes + 7) / 8) * rows_per_strip) {
|
||||||
|
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
|
||||||
|
// man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a
|
||||||
|
// call to TIFFReadEncodedStrip ...
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->bytes = strip_size;
|
||||||
|
|
||||||
TRACE(("StripSize: %d \n", state->bytes));
|
TRACE(("StripSize: %d \n", state->bytes));
|
||||||
|
|
||||||
if (TIFFStripSize(tiff) > state->bytes) {
|
row_byte_size = TIFFScanlineSize(tiff);
|
||||||
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
|
|
||||||
// man: TIFFStripSize returns the equivalent size for a strip of data as it
|
|
||||||
// would be returned in a
|
|
||||||
// call to TIFFReadEncodedStrip ...
|
|
||||||
|
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
if (row_byte_size == 0 || row_byte_size > strip_size) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRACE(("RowsByteSize: %u \n", row_byte_size));
|
||||||
|
|
||||||
/* realloc to fit whole strip */
|
/* realloc to fit whole strip */
|
||||||
/* malloc check above */
|
/* malloc check above */
|
||||||
new_data = realloc(state->buffer, state->bytes);
|
new_data = realloc(state->buffer, state->bytes);
|
||||||
|
@ -367,35 +523,35 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) {
|
||||||
state->buffer = new_data;
|
state->buffer = new_data;
|
||||||
|
|
||||||
for (; state->y < state->ysize; state->y += rows_per_strip) {
|
for (; state->y < state->ysize; state->y += rows_per_strip) {
|
||||||
if (TIFFReadEncodedStrip(
|
int plane;
|
||||||
tiff,
|
for (plane = 0; plane < planes; plane++) {
|
||||||
TIFFComputeStrip(tiff, state->y, 0),
|
ImagingShuffler shuffler = unpackers[plane];
|
||||||
(tdata_t)state->buffer,
|
if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, plane), (tdata_t)state->buffer, strip_size) == -1) {
|
||||||
-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;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("Decoded strip for row %d \n", state->y));
|
TRACE(("Decoded strip for row %d \n", state->y));
|
||||||
|
|
||||||
// iterate over each row in the strip and stuff data into image
|
// iterate over each row in the strip and stuff data into image
|
||||||
for (strip_row = 0;
|
for (strip_row = 0;
|
||||||
strip_row < min((INT32)rows_per_strip, state->ysize - state->y);
|
strip_row < min((INT32) rows_per_strip, state->ysize - state->y);
|
||||||
strip_row++) {
|
strip_row++) {
|
||||||
TRACE(("Writing data into line %d ; \n", state->y + strip_row));
|
TRACE(("Writing data into line %d ; \n", state->y + strip_row));
|
||||||
|
|
||||||
// UINT8 * bbb = state->buffer + strip_row * (state->bytes /
|
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip);
|
||||||
// rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
|
// TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
||||||
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
|
||||||
|
|
||||||
state->shuffle(
|
shuffler(
|
||||||
(UINT8 *)im->image[state->y + state->yoff + strip_row] +
|
(UINT8*) im->image[state->y + state->yoff + strip_row] +
|
||||||
state->xoff * im->pixelsize,
|
state->xoff * im->pixelsize,
|
||||||
state->buffer + strip_row * row_byte_size,
|
state->buffer + strip_row * row_byte_size,
|
||||||
state->xsize);
|
state->xsize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +563,13 @@ ImagingLibTiffDecode(
|
||||||
char *mode = "r";
|
char *mode = "r";
|
||||||
TIFF *tiff;
|
TIFF *tiff;
|
||||||
uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR
|
uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR
|
||||||
int isYCbCr = 0;
|
uint16 compression;
|
||||||
|
int readAsRGBA = 0;
|
||||||
|
uint16 planarconfig = 0;
|
||||||
|
int planes = 1;
|
||||||
|
ImagingShuffler unpackers[4];
|
||||||
|
|
||||||
|
memset(unpackers, 0, sizeof(ImagingShuffler) * 4);
|
||||||
|
|
||||||
/* buffer is the encoded file, bytes is the length of the encoded file */
|
/* buffer is the encoded file, bytes is the length of the encoded file */
|
||||||
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */
|
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */
|
||||||
|
@ -502,134 +664,64 @@ ImagingLibTiffDecode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
|
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
|
||||||
isYCbCr = photometric == PHOTOMETRIC_YCBCR;
|
TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression);
|
||||||
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_PLANARCONFIG, &planarconfig);
|
||||||
|
|
||||||
if (TIFFIsTiled(tiff)) {
|
// Dealing with YCbCr images is complicated in case if subsampling
|
||||||
INT32 x, y, tile_y;
|
// Let LibTiff read them as RGBA
|
||||||
UINT32 tile_width, tile_length, current_tile_length, current_line,
|
readAsRGBA = photometric == PHOTOMETRIC_YCBCR;
|
||||||
current_tile_width, row_byte_size;
|
|
||||||
UINT8 *new_data;
|
|
||||||
|
|
||||||
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
|
if (readAsRGBA && compression == COMPRESSION_JPEG && planarconfig == PLANARCONFIG_CONTIG) {
|
||||||
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);
|
// If using new JPEG compression, let libjpeg do RGB convertion for performance reasons
|
||||||
|
TIFFSetField(tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
|
||||||
|
readAsRGBA = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* overflow check for row_byte_size calculation */
|
if (readAsRGBA) {
|
||||||
if ((UINT32)INT_MAX / state->bits < tile_width) {
|
_decodeAsRGBA(im, state, tiff);
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
}
|
||||||
|
else {
|
||||||
|
planes = _pickUnpackers(im, state, tiff, planarconfig, unpackers);
|
||||||
|
if (planes <= 0) {
|
||||||
goto decode_err;
|
goto decode_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isYCbCr) {
|
if (TIFFIsTiled(tiff)) {
|
||||||
row_byte_size = tile_width * 4;
|
_decodeTile(im, state, tiff, planes, unpackers);
|
||||||
/* sanity check, we use this value in shuffle below */
|
}
|
||||||
if (im->pixelsize != 4) {
|
else {
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
_decodeStrip(im, state, tiff, planes, unpackers);
|
||||||
goto decode_err;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We could use TIFFTileSize, but for YCbCr data it returns subsampled data
|
|
||||||
// size
|
|
||||||
row_byte_size = (tile_width * state->bits + 7) / 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* overflow check for realloc */
|
if (!state->errcode) {
|
||||||
if (INT_MAX / row_byte_size < tile_length) {
|
// Check if raw mode was RGBa and it was stored on separate planes
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
// so we have to convert it to RGBA
|
||||||
goto decode_err;
|
if (planes > 3 && strcmp(im->mode, "RGBA") == 0) {
|
||||||
}
|
uint16 extrasamples;
|
||||||
|
uint16* sampleinfo;
|
||||||
|
ImagingShuffler shuffle;
|
||||||
|
INT32 y;
|
||||||
|
|
||||||
state->bytes = row_byte_size * tile_length;
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo);
|
||||||
|
|
||||||
if (TIFFTileSize(tiff) > state->bytes) {
|
if (extrasamples >= 1 &&
|
||||||
// If the strip size as expected by LibTiff isn't what we're expecting,
|
(sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED || sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA)
|
||||||
// abort.
|
) {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
shuffle = ImagingFindUnpacker("RGBA", "RGBa", NULL);
|
||||||
goto decode_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* realloc to fit whole tile */
|
for (y = state->yoff; y < state->ysize; y++) {
|
||||||
/* malloc check above */
|
UINT8* ptr = (UINT8*) im->image[y + state->yoff] +
|
||||||
new_data = realloc(state->buffer, state->bytes);
|
state->xoff * im->pixelsize;
|
||||||
if (!new_data) {
|
shuffle(ptr, ptr, state->xsize);
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
|
||||||
goto decode_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->buffer = new_data;
|
|
||||||
|
|
||||||
TRACE(("TIFFTileSize: %d\n", state->bytes));
|
|
||||||
|
|
||||||
for (y = state->yoff; y < state->ysize; y += tile_length) {
|
|
||||||
for (x = state->xoff; x < state->xsize; x += tile_width) {
|
|
||||||
/* Sanity Check. Apparently in some cases, the TiffReadRGBA* functions
|
|
||||||
have a different view of the size of the tiff than we're getting from
|
|
||||||
other functions. So, we need to check here.
|
|
||||||
*/
|
|
||||||
if (!TIFFCheckTile(tiff, x, y, 0, 0)) {
|
|
||||||
TRACE(("Check Tile Error, Tile at %dx%d\n", x, y));
|
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
|
||||||
goto decode_err;
|
|
||||||
}
|
|
||||||
if (isYCbCr) {
|
|
||||||
/* To avoid dealing with YCbCr subsampling, let libtiff handle it */
|
|
||||||
if (!TIFFReadRGBATile(tiff, x, y, (UINT32 *)state->buffer)) {
|
|
||||||
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
|
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
|
||||||
goto decode_err;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, 0) == -1) {
|
|
||||||
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
|
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
|
||||||
goto decode_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("Read tile at %dx%d; \n\n", x, y));
|
|
||||||
|
|
||||||
current_tile_width = min((INT32)tile_width, state->xsize - x);
|
|
||||||
current_tile_length = min((INT32)tile_length, state->ysize - y);
|
|
||||||
// iterate over each line in the tile and stuff data into image
|
|
||||||
for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
|
|
||||||
TRACE(
|
|
||||||
("Writing tile data at %dx%d using tile_width: %d; \n",
|
|
||||||
tile_y + y,
|
|
||||||
x,
|
|
||||||
current_tile_width));
|
|
||||||
|
|
||||||
// UINT8 * bbb = state->buffer + tile_y * row_byte_size;
|
|
||||||
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1],
|
|
||||||
// ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
|
||||||
/*
|
|
||||||
* For some reason the TIFFReadRGBATile() function
|
|
||||||
* chooses the lower left corner as the origin.
|
|
||||||
* Vertically mirror by shuffling the scanlines
|
|
||||||
* backwards
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (isYCbCr) {
|
|
||||||
current_line = tile_length - tile_y - 1;
|
|
||||||
} else {
|
|
||||||
current_line = tile_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->shuffle(
|
|
||||||
(UINT8 *)im->image[tile_y + y] + x * im->pixelsize,
|
|
||||||
state->buffer + current_line * row_byte_size,
|
|
||||||
current_tile_width);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!isYCbCr) {
|
|
||||||
_decodeStrip(im, state, tiff);
|
|
||||||
} else {
|
|
||||||
_decodeStripYCbCr(im, state, tiff);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
decode_err:
|
decode_err:
|
||||||
TIFFClose(tiff);
|
TIFFClose(tiff);
|
||||||
TRACE(("Done Decoding, Returning \n"));
|
TRACE(("Done Decoding, Returning \n"));
|
||||||
// Returning -1 here to force ImageFile.load to break, rather than
|
// Returning -1 here to force ImageFile.load to break, rather than
|
||||||
|
|
|
@ -1363,6 +1363,94 @@ band3I(UINT8 *out, const UINT8 *in, int pixels) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band016B(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 0 only, big endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[0] = in[0];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band116B(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 1 only, big endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[1] = in[0];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band216B(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 2 only, big endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[2] = in[0];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band316B(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 3 only, big endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[3] = in[0];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band016L(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 0 only, little endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[0] = in[1];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band116L(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 1 only, little endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[1] = in[1];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band216L(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 2 only, little endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[2] = in[1];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
band316L(UINT8* out, const UINT8* in, int pixels)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* band 3 only, little endian */
|
||||||
|
for (i = 0; i < pixels; i++) {
|
||||||
|
out[3] = in[1];
|
||||||
|
out += 4; in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
const char *mode;
|
const char *mode;
|
||||||
const char *rawmode;
|
const char *rawmode;
|
||||||
|
@ -1448,6 +1536,12 @@ static struct {
|
||||||
{"RGB", "R", 8, band0},
|
{"RGB", "R", 8, band0},
|
||||||
{"RGB", "G", 8, band1},
|
{"RGB", "G", 8, band1},
|
||||||
{"RGB", "B", 8, band2},
|
{"RGB", "B", 8, band2},
|
||||||
|
{"RGB", "R;16L", 16, band016L},
|
||||||
|
{"RGB", "G;16L", 16, band116L},
|
||||||
|
{"RGB", "B;16L", 16, band216L},
|
||||||
|
{"RGB", "R;16B", 16, band016B},
|
||||||
|
{"RGB", "G;16B", 16, band116B},
|
||||||
|
{"RGB", "B;16B", 16, band216B},
|
||||||
|
|
||||||
/* true colour w. alpha */
|
/* true colour w. alpha */
|
||||||
{"RGBA", "LA", 16, unpackRGBALA},
|
{"RGBA", "LA", 16, unpackRGBALA},
|
||||||
|
@ -1476,17 +1570,42 @@ static struct {
|
||||||
{"RGBA", "G", 8, band1},
|
{"RGBA", "G", 8, band1},
|
||||||
{"RGBA", "B", 8, band2},
|
{"RGBA", "B", 8, band2},
|
||||||
{"RGBA", "A", 8, band3},
|
{"RGBA", "A", 8, band3},
|
||||||
|
{"RGBA", "R;16L", 16, band016L},
|
||||||
|
{"RGBA", "G;16L", 16, band116L},
|
||||||
|
{"RGBA", "B;16L", 16, band216L},
|
||||||
|
{"RGBA", "A;16L", 16, band316L},
|
||||||
|
{"RGBA", "R;16B", 16, band016B},
|
||||||
|
{"RGBA", "G;16B", 16, band116B},
|
||||||
|
{"RGBA", "B;16B", 16, band216B},
|
||||||
|
{"RGBA", "A;16B", 16, band316B},
|
||||||
|
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
{"RGB", "RGB;16N", 48, unpackRGB16B},
|
{"RGB", "RGB;16N", 48, unpackRGB16B},
|
||||||
{"RGBA", "RGBa;16N", 64, unpackRGBa16B},
|
{"RGBA", "RGBa;16N", 64, unpackRGBa16B},
|
||||||
{"RGBA", "RGBA;16N", 64, unpackRGBA16B},
|
{"RGBA", "RGBA;16N", 64, unpackRGBA16B},
|
||||||
{"RGBX", "RGBX;16N", 64, unpackRGBA16B},
|
{"RGBX", "RGBX;16N", 64, unpackRGBA16B},
|
||||||
|
{"RGB", "R;16N", 16, band016B},
|
||||||
|
{"RGB", "G;16N", 16, band116B},
|
||||||
|
{"RGB", "B;16N", 16, band216B},
|
||||||
|
|
||||||
|
{"RGBA", "R;16N", 16, band016B},
|
||||||
|
{"RGBA", "G;16N", 16, band116B},
|
||||||
|
{"RGBA", "B;16N", 16, band216B},
|
||||||
|
{"RGBA", "A;16N", 16, band316B},
|
||||||
#else
|
#else
|
||||||
{"RGB", "RGB;16N", 48, unpackRGB16L},
|
{"RGB", "RGB;16N", 48, unpackRGB16L},
|
||||||
{"RGBA", "RGBa;16N", 64, unpackRGBa16L},
|
{"RGBA", "RGBa;16N", 64, unpackRGBa16L},
|
||||||
{"RGBA", "RGBA;16N", 64, unpackRGBA16L},
|
{"RGBA", "RGBA;16N", 64, unpackRGBA16L},
|
||||||
{"RGBX", "RGBX;16N", 64, unpackRGBA16B},
|
{"RGBX", "RGBX;16N", 64, unpackRGBA16L},
|
||||||
|
{"RGB", "R;16N", 16, band016L},
|
||||||
|
{"RGB", "G;16N", 16, band116L},
|
||||||
|
{"RGB", "B;16N", 16, band216L},
|
||||||
|
|
||||||
|
|
||||||
|
{"RGBA", "R;16N", 16, band016L},
|
||||||
|
{"RGBA", "G;16N", 16, band116L},
|
||||||
|
{"RGBA", "B;16N", 16, band216L},
|
||||||
|
{"RGBA", "A;16N", 16, band316L},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* true colour w. alpha premultiplied */
|
/* true colour w. alpha premultiplied */
|
||||||
|
|
104
src/thirdparty/fribidi-shim/fribidi.c
vendored
Normal file
104
src/thirdparty/fribidi-shim/fribidi.c
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#else
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FRIBIDI_SHIM_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "fribidi.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* FriBiDi>=1.0.0 adds bracket_types param, ignore and call legacy function */
|
||||||
|
FriBidiLevel fribidi_get_par_embedding_levels_ex_compat(
|
||||||
|
const FriBidiCharType *bidi_types,
|
||||||
|
const FriBidiBracketType *bracket_types,
|
||||||
|
const FriBidiStrIndex len,
|
||||||
|
FriBidiParType *pbase_dir,
|
||||||
|
FriBidiLevel *embedding_levels)
|
||||||
|
{
|
||||||
|
return fribidi_get_par_embedding_levels(
|
||||||
|
bidi_types, len, pbase_dir, embedding_levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FriBiDi>=1.0.0 gets bracket types here, ignore */
|
||||||
|
void fribidi_get_bracket_types_compat(
|
||||||
|
const FriBidiChar *str,
|
||||||
|
const FriBidiStrIndex len,
|
||||||
|
const FriBidiCharType *types,
|
||||||
|
FriBidiBracketType *btypes)
|
||||||
|
{ /* no-op*/ }
|
||||||
|
|
||||||
|
|
||||||
|
int load_fribidi(void) {
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
p_fribidi = 0;
|
||||||
|
|
||||||
|
/* Microsoft needs a totally different system */
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define LOAD_FUNCTION(func) \
|
||||||
|
func = (t_##func)dlsym(p_fribidi, #func); \
|
||||||
|
error = error || (func == 0);
|
||||||
|
|
||||||
|
p_fribidi = dlopen("libfribidi.so", RTLD_LAZY);
|
||||||
|
if (!p_fribidi) {
|
||||||
|
p_fribidi = dlopen("libfribidi.so.0", RTLD_LAZY);
|
||||||
|
}
|
||||||
|
if (!p_fribidi) {
|
||||||
|
p_fribidi = dlopen("libfribidi.dylib", RTLD_LAZY);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define LOAD_FUNCTION(func) \
|
||||||
|
func = (t_##func)GetProcAddress(p_fribidi, #func); \
|
||||||
|
error = error || (func == 0);
|
||||||
|
|
||||||
|
p_fribidi = LoadLibrary("fribidi");
|
||||||
|
if (!p_fribidi) {
|
||||||
|
p_fribidi = LoadLibrary("fribidi-0");
|
||||||
|
}
|
||||||
|
/* MSYS2 */
|
||||||
|
if (!p_fribidi) {
|
||||||
|
p_fribidi = LoadLibrary("libfribidi-0");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!p_fribidi) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load FriBiDi>=1.0.0 functions first, use error to detect version */
|
||||||
|
LOAD_FUNCTION(fribidi_get_par_embedding_levels_ex);
|
||||||
|
LOAD_FUNCTION(fribidi_get_bracket_types);
|
||||||
|
if (error) {
|
||||||
|
/* using FriBiDi<1.0.0, ignore new parameters */
|
||||||
|
error = 0;
|
||||||
|
fribidi_get_par_embedding_levels_ex = &fribidi_get_par_embedding_levels_ex_compat;
|
||||||
|
fribidi_get_bracket_types = &fribidi_get_bracket_types_compat;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOAD_FUNCTION(fribidi_unicode_to_charset);
|
||||||
|
LOAD_FUNCTION(fribidi_charset_to_unicode);
|
||||||
|
LOAD_FUNCTION(fribidi_get_bidi_types);
|
||||||
|
LOAD_FUNCTION(fribidi_get_par_embedding_levels);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
fribidi_version_info = *(const char**)dlsym(p_fribidi, "fribidi_version_info");
|
||||||
|
if (dlerror() || error || (fribidi_version_info == 0)) {
|
||||||
|
dlclose(p_fribidi);
|
||||||
|
p_fribidi = 0;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fribidi_version_info = *(const char**)GetProcAddress(p_fribidi, "fribidi_version_info");
|
||||||
|
if (error || (fribidi_version_info == 0)) {
|
||||||
|
FreeLibrary(p_fribidi);
|
||||||
|
p_fribidi = 0;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
111
src/thirdparty/fribidi-shim/fribidi.h
vendored
Normal file
111
src/thirdparty/fribidi-shim/fribidi.h
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
#define FRIBIDI_MAJOR_VERSION 1
|
||||||
|
|
||||||
|
/* fribidi-types.h */
|
||||||
|
|
||||||
|
# if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
|
||||||
|
defined (_sgi) || defined (__sun) || defined (sun) || \
|
||||||
|
defined (__digital__) || defined (__HP_cc)
|
||||||
|
# include <inttypes.h>
|
||||||
|
# elif defined (_AIX)
|
||||||
|
# include <sys/inttypes.h>
|
||||||
|
# else
|
||||||
|
# include <stdint.h>
|
||||||
|
# endif
|
||||||
|
|
||||||
|
typedef uint32_t FriBidiChar;
|
||||||
|
typedef int FriBidiStrIndex;
|
||||||
|
|
||||||
|
typedef FriBidiChar FriBidiBracketType;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* fribidi-char-sets.h */
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
_FRIBIDI_CHAR_SET_NOT_FOUND,
|
||||||
|
FRIBIDI_CHAR_SET_UTF8,
|
||||||
|
FRIBIDI_CHAR_SET_CAP_RTL,
|
||||||
|
FRIBIDI_CHAR_SET_ISO8859_6,
|
||||||
|
FRIBIDI_CHAR_SET_ISO8859_8,
|
||||||
|
FRIBIDI_CHAR_SET_CP1255,
|
||||||
|
FRIBIDI_CHAR_SET_CP1256,
|
||||||
|
_FRIBIDI_CHAR_SETS_NUM_PLUS_ONE
|
||||||
|
}
|
||||||
|
FriBidiCharSet;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* fribidi-bidi-types.h */
|
||||||
|
|
||||||
|
typedef signed char FriBidiLevel;
|
||||||
|
|
||||||
|
#define FRIBIDI_TYPE_LTR_VAL 0x00000110L
|
||||||
|
#define FRIBIDI_TYPE_RTL_VAL 0x00000111L
|
||||||
|
#define FRIBIDI_TYPE_ON_VAL 0x00000040L
|
||||||
|
|
||||||
|
typedef uint32_t FriBidiCharType;
|
||||||
|
#define FRIBIDI_TYPE_LTR FRIBIDI_TYPE_LTR_VAL
|
||||||
|
|
||||||
|
typedef uint32_t FriBidiParType;
|
||||||
|
#define FRIBIDI_PAR_LTR FRIBIDI_TYPE_LTR_VAL
|
||||||
|
#define FRIBIDI_PAR_RTL FRIBIDI_TYPE_RTL_VAL
|
||||||
|
#define FRIBIDI_PAR_ON FRIBIDI_TYPE_ON_VAL
|
||||||
|
|
||||||
|
#define FRIBIDI_LEVEL_IS_RTL(lev) ((lev) & 1)
|
||||||
|
#define FRIBIDI_DIR_TO_LEVEL(dir) ((FriBidiLevel) (FRIBIDI_IS_RTL(dir) ? 1 : 0))
|
||||||
|
#define FRIBIDI_IS_RTL(p) ((p) & 0x00000001L)
|
||||||
|
#define FRIBIDI_IS_EXPLICIT_OR_BN_OR_WS(p) ((p) & 0x00901000L)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* functions */
|
||||||
|
|
||||||
|
#ifdef FRIBIDI_SHIM_IMPLEMENTATION
|
||||||
|
#define FRIBIDI_ENTRY
|
||||||
|
#else
|
||||||
|
#define FRIBIDI_ENTRY extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FRIBIDI_FUNC(ret, name, ...) \
|
||||||
|
typedef ret (*t_##name) (__VA_ARGS__); \
|
||||||
|
FRIBIDI_ENTRY t_##name name;
|
||||||
|
|
||||||
|
FRIBIDI_FUNC(FriBidiStrIndex, fribidi_unicode_to_charset,
|
||||||
|
FriBidiCharSet, const FriBidiChar *, FriBidiStrIndex, char *);
|
||||||
|
|
||||||
|
FRIBIDI_FUNC(FriBidiStrIndex, fribidi_charset_to_unicode,
|
||||||
|
FriBidiCharSet, const char *, FriBidiStrIndex, FriBidiChar *);
|
||||||
|
|
||||||
|
FRIBIDI_FUNC(void, fribidi_get_bidi_types,
|
||||||
|
const FriBidiChar *, const FriBidiStrIndex, FriBidiCharType *);
|
||||||
|
|
||||||
|
FRIBIDI_FUNC(FriBidiLevel, fribidi_get_par_embedding_levels,
|
||||||
|
const FriBidiCharType *, const FriBidiStrIndex, FriBidiParType *,
|
||||||
|
FriBidiLevel *);
|
||||||
|
|
||||||
|
/* FriBiDi>=1.0.0 */
|
||||||
|
FRIBIDI_FUNC(FriBidiLevel, fribidi_get_par_embedding_levels_ex,
|
||||||
|
const FriBidiCharType *, const FriBidiBracketType *, const FriBidiStrIndex,
|
||||||
|
FriBidiParType *, FriBidiLevel *);
|
||||||
|
|
||||||
|
/* FriBiDi>=1.0.0 */
|
||||||
|
FRIBIDI_FUNC(void, fribidi_get_bracket_types,
|
||||||
|
const FriBidiChar *, const FriBidiStrIndex, const FriBidiCharType *,
|
||||||
|
FriBidiBracketType *);
|
||||||
|
|
||||||
|
#undef FRIBIDI_FUNC
|
||||||
|
|
||||||
|
/* constant, not a function */
|
||||||
|
FRIBIDI_ENTRY const char *fribidi_version_info;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* shim */
|
||||||
|
|
||||||
|
FRIBIDI_ENTRY void *p_fribidi;
|
||||||
|
|
||||||
|
FRIBIDI_ENTRY int load_fribidi(void);
|
||||||
|
|
||||||
|
#undef FRIBIDI_ENTRY
|
9
src/thirdparty/raqm/AUTHORS
vendored
Normal file
9
src/thirdparty/raqm/AUTHORS
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Abderraouf Adjal
|
||||||
|
Ali Yousuf
|
||||||
|
Anood Almuharbi
|
||||||
|
Asma Albahanta
|
||||||
|
Fahad Alsaidi
|
||||||
|
Ibtisam Almabsali
|
||||||
|
Khaled Hosny
|
||||||
|
Mazoon Almaamari
|
||||||
|
Shamsa Alqassabi
|
22
src/thirdparty/raqm/COPYING
vendored
Normal file
22
src/thirdparty/raqm/COPYING
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
|
||||||
|
Copyright © 2016 Khaled Hosny <khaledhosny@eglug.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
89
src/thirdparty/raqm/NEWS
vendored
Normal file
89
src/thirdparty/raqm/NEWS
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
Overview of changes leading to 0.7.1
|
||||||
|
Sunday, November 22, 2020
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Require HarfBuzz >= 2.0.0
|
||||||
|
|
||||||
|
Build and documentation fixes.
|
||||||
|
|
||||||
|
Overview of changes leading to 0.7.0
|
||||||
|
Monday, May 27, 2019
|
||||||
|
====================================
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_version
|
||||||
|
* raqm_version_string
|
||||||
|
* raqm_version_atleast
|
||||||
|
* RAQM_VERSION_MAJOR
|
||||||
|
* RAQM_VERSION_MICRO
|
||||||
|
* RAQM_VERSION_MINOR
|
||||||
|
* RAQM_VERSION_STRING
|
||||||
|
* RAQM_VERSION_ATLEAST
|
||||||
|
|
||||||
|
Overview of changes leading to 0.6.0
|
||||||
|
Sunday, May 5, 2019
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Fix TTB direction regression from the previous release.
|
||||||
|
|
||||||
|
Correctly detect script of Common and Inherite characters at start of text.
|
||||||
|
|
||||||
|
Undef HAVE_CONFIG_H workaround, for older versions of Fribidi.
|
||||||
|
|
||||||
|
Drop test suite dependency on GLib.
|
||||||
|
|
||||||
|
Port test runner to Python instead of shell script.
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_set_invisible_glyph()
|
||||||
|
|
||||||
|
Overview of changes leading to 0.5.0
|
||||||
|
Saturday, February 24, 2018
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Use FriBiDi 1.x API when available.
|
||||||
|
|
||||||
|
Overview of changes leading to 0.4.0
|
||||||
|
Sunday, January 21, 2018
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Set begin-of-text and end-of-text HarfBuzz buffer flags.
|
||||||
|
|
||||||
|
Dynamically allocate memory instead of using stack allocation for input text.
|
||||||
|
|
||||||
|
Accept zero length text and do nothing instead of treating it as error.
|
||||||
|
|
||||||
|
Overview of changes leading to 0.3.0
|
||||||
|
Monday, August 21, 2017
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Fix stack corruption on MSVC.
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_set_freetype_load_flags
|
||||||
|
|
||||||
|
Overview of changes leading to 0.2.0
|
||||||
|
Wednesday, August 25, 2016
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Fix building with MSVC due to lacking C99 support.
|
||||||
|
|
||||||
|
Make multiple fonts support actually work. Start and length now respect the
|
||||||
|
input encoding.
|
||||||
|
|
||||||
|
New API:
|
||||||
|
* raqm_index_to_position
|
||||||
|
* raqm_position_to_index
|
||||||
|
* raqm_set_language
|
||||||
|
|
||||||
|
Overview of changes leading to 0.1.1
|
||||||
|
Sunday, May 1, 2016
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Fix make check on 32-bit systems.
|
||||||
|
|
||||||
|
Overview of changes leading to 0.1.0
|
||||||
|
Wednesday, January 20, 2016
|
||||||
|
====================================
|
||||||
|
|
||||||
|
First release.
|
85
src/thirdparty/raqm/README
vendored
Normal file
85
src/thirdparty/raqm/README
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
Raqm
|
||||||
|
====
|
||||||
|
|
||||||
|
[](https://travis-ci.org/HOST-Oman/libraqm)
|
||||||
|
[](https://ci.appveyor.com/project/HOSTOman/libraqm)
|
||||||
|
|
||||||
|
Raqm is a small library that encapsulates the logic for complex text layout and
|
||||||
|
provides a convenient API.
|
||||||
|
|
||||||
|
It currently provides bidirectional text support (using [FriBiDi][1]), shaping
|
||||||
|
(using [HarfBuzz][2]), and proper script itemization. As a result,
|
||||||
|
Raqm can support most writing systems covered by Unicode.
|
||||||
|
|
||||||
|
The documentation can be accessed on the web at:
|
||||||
|
> http://host-oman.github.io/libraqm/
|
||||||
|
|
||||||
|
Raqm (Arabic: رَقْم) is writing, also number or digit and the Arabic word for
|
||||||
|
digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”.
|
||||||
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
|
||||||
|
Raqm depends on the following libraries:
|
||||||
|
* [FreeType][3]
|
||||||
|
* [HarfBuzz][2]
|
||||||
|
* [FriBiDi][1]
|
||||||
|
|
||||||
|
To build the documentation you will also need:
|
||||||
|
* [GTK-Doc][4]
|
||||||
|
|
||||||
|
To install dependencies on Fedora:
|
||||||
|
|
||||||
|
sudo dnf install freetype-devel harfbuzz-devel fribidi-devel gtk-doc
|
||||||
|
|
||||||
|
To install dependencies on Ubuntu:
|
||||||
|
|
||||||
|
sudo apt-get install libfreetype6-dev libharfbuzz-dev libfribidi-dev \
|
||||||
|
gtk-doc-tools
|
||||||
|
|
||||||
|
On Mac OS X you can use Homebrew:
|
||||||
|
|
||||||
|
brew install freetype harfbuzz fribidi gtk-doc
|
||||||
|
export XML_CATALOG_FILES="/usr/local/etc/xml/catalog" # for the docs
|
||||||
|
|
||||||
|
Once you have the source code and the dependencies, you can proceed to build.
|
||||||
|
To do that, run the customary sequence of commands in the source code
|
||||||
|
directory:
|
||||||
|
|
||||||
|
$ ./configure
|
||||||
|
$ make
|
||||||
|
$ make install
|
||||||
|
|
||||||
|
To build the documentation, pass `--enable-gtk-doc` to the `configure` script.
|
||||||
|
|
||||||
|
To run the tests:
|
||||||
|
|
||||||
|
$ make check
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
Once you have made a change that you are happy with, contribute it back, we’ll
|
||||||
|
be happy to integrate it! Just fork the repository and make a pull request.
|
||||||
|
|
||||||
|
Projects using Raqm
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
1. [ImageMagick](https://github.com/ImageMagick/ImageMagick)
|
||||||
|
2. [LibGD](https://github.com/libgd/libgd)
|
||||||
|
3. [FontView](https://github.com/googlei18n/fontview)
|
||||||
|
4. [Pillow](https://github.com/python-pillow)
|
||||||
|
5. [mplcairo](https://github.com/anntzer/mplcairo)
|
||||||
|
|
||||||
|
The following projects have patches to support complex text layout using Raqm:
|
||||||
|
|
||||||
|
2. SDL_ttf: https://bugzilla.libsdl.org/show_bug.cgi?id=3211
|
||||||
|
3. Pygame: https://bitbucket.org/pygame/pygame/pull-requests/52
|
||||||
|
4. Blender: https://developer.blender.org/D1809
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1]: http://fribidi.org
|
||||||
|
[2]: http://harfbuzz.org
|
||||||
|
[3]: https://www.freetype.org
|
||||||
|
[4]: https://www.gtk.org/gtk-doc
|
44
src/thirdparty/raqm/raqm-version.h
vendored
Normal file
44
src/thirdparty/raqm/raqm-version.h
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2011 Google, Inc.
|
||||||
|
*
|
||||||
|
* This is part of HarfBuzz, a text shaping library.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, without written agreement and without
|
||||||
|
* license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
* software and its documentation for any purpose, provided that the
|
||||||
|
* above copyright notice and the following two paragraphs appear in
|
||||||
|
* all copies of this software.
|
||||||
|
*
|
||||||
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
* DAMAGE.
|
||||||
|
*
|
||||||
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
|
*
|
||||||
|
* Google Author(s): Behdad Esfahbod
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RAQM_H_IN_
|
||||||
|
#error "Include <raqm.h> instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _RAQM_VERSION_H_
|
||||||
|
#define _RAQM_VERSION_H_
|
||||||
|
|
||||||
|
#define RAQM_VERSION_MAJOR 0
|
||||||
|
#define RAQM_VERSION_MINOR 7
|
||||||
|
#define RAQM_VERSION_MICRO 1
|
||||||
|
|
||||||
|
#define RAQM_VERSION_STRING "0.7.1"
|
||||||
|
|
||||||
|
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
|
||||||
|
((major)*10000+(minor)*100+(micro) <= \
|
||||||
|
RAQM_VERSION_MAJOR*10000+RAQM_VERSION_MINOR*100+RAQM_VERSION_MICRO)
|
||||||
|
|
||||||
|
#endif /* _RAQM_VERSION_H_ */
|
2074
src/thirdparty/raqm/raqm.c
vendored
Normal file
2074
src/thirdparty/raqm/raqm.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -24,17 +24,14 @@
|
||||||
|
|
||||||
#ifndef _RAQM_H_
|
#ifndef _RAQM_H_
|
||||||
#define _RAQM_H_
|
#define _RAQM_H_
|
||||||
|
#define _RAQM_H_IN_
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef bool
|
#include <stdbool.h>
|
||||||
typedef int bool;
|
#include <stdint.h>
|
||||||
#endif
|
|
||||||
#ifndef uint32_t
|
|
||||||
typedef UINT32 uint32_t;
|
|
||||||
#endif
|
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
@ -42,6 +39,8 @@ typedef UINT32 uint32_t;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "raqm-version.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* raqm_t:
|
* raqm_t:
|
||||||
*
|
*
|
||||||
|
@ -63,7 +62,8 @@ typedef struct _raqm raqm_t;
|
||||||
*
|
*
|
||||||
* Since: 0.1
|
* Since: 0.1
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
RAQM_DIRECTION_DEFAULT,
|
RAQM_DIRECTION_DEFAULT,
|
||||||
RAQM_DIRECTION_RTL,
|
RAQM_DIRECTION_RTL,
|
||||||
RAQM_DIRECTION_LTR,
|
RAQM_DIRECTION_LTR,
|
||||||
|
@ -93,64 +93,93 @@ typedef struct raqm_glyph_t {
|
||||||
FT_Face ftface;
|
FT_Face ftface;
|
||||||
} raqm_glyph_t;
|
} raqm_glyph_t;
|
||||||
|
|
||||||
/**
|
raqm_t *
|
||||||
* version 0.1 of the raqm_glyph_t structure
|
raqm_create (void);
|
||||||
*/
|
|
||||||
typedef struct raqm_glyph_t_01 {
|
|
||||||
unsigned int index;
|
|
||||||
int x_advance;
|
|
||||||
int y_advance;
|
|
||||||
int x_offset;
|
|
||||||
int y_offset;
|
|
||||||
uint32_t cluster;
|
|
||||||
} raqm_glyph_t_01;
|
|
||||||
|
|
||||||
raqm_t *
|
raqm_t *
|
||||||
raqm_create(void);
|
raqm_reference (raqm_t *rq);
|
||||||
|
|
||||||
raqm_t *
|
|
||||||
raqm_reference(raqm_t *rq);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
raqm_destroy(raqm_t *rq);
|
raqm_destroy (raqm_t *rq);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_text(raqm_t *rq, const uint32_t *text, size_t len);
|
raqm_set_text (raqm_t *rq,
|
||||||
|
const uint32_t *text,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_text_utf8(raqm_t *rq, const char *text, size_t len);
|
raqm_set_text_utf8 (raqm_t *rq,
|
||||||
|
const char *text,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_par_direction(raqm_t *rq, raqm_direction_t dir);
|
raqm_set_par_direction (raqm_t *rq,
|
||||||
|
raqm_direction_t dir);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_language(raqm_t *rq, const char *lang, size_t start, size_t len);
|
raqm_set_language (raqm_t *rq,
|
||||||
|
const char *lang,
|
||||||
|
size_t start,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_add_font_feature(raqm_t *rq, const char *feature, int len);
|
raqm_add_font_feature (raqm_t *rq,
|
||||||
|
const char *feature,
|
||||||
|
int len);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_freetype_face(raqm_t *rq, FT_Face face);
|
raqm_set_freetype_face (raqm_t *rq,
|
||||||
|
FT_Face face);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_freetype_face_range(raqm_t *rq, FT_Face face, size_t start, size_t len);
|
raqm_set_freetype_face_range (raqm_t *rq,
|
||||||
|
FT_Face face,
|
||||||
|
size_t start,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_set_freetype_load_flags(raqm_t *rq, int flags);
|
raqm_set_freetype_load_flags (raqm_t *rq,
|
||||||
|
int flags);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_layout(raqm_t *rq);
|
raqm_set_invisible_glyph (raqm_t *rq,
|
||||||
|
int gid);
|
||||||
|
|
||||||
|
bool
|
||||||
|
raqm_layout (raqm_t *rq);
|
||||||
|
|
||||||
raqm_glyph_t *
|
raqm_glyph_t *
|
||||||
raqm_get_glyphs(raqm_t *rq, size_t *length);
|
raqm_get_glyphs (raqm_t *rq,
|
||||||
|
size_t *length);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_index_to_position(raqm_t *rq, size_t *index, int *x, int *y);
|
raqm_index_to_position (raqm_t *rq,
|
||||||
|
size_t *index,
|
||||||
|
int *x,
|
||||||
|
int *y);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
raqm_position_to_index(raqm_t *rq, int x, int y, size_t *index);
|
raqm_position_to_index (raqm_t *rq,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
size_t *index);
|
||||||
|
|
||||||
|
void
|
||||||
|
raqm_version (unsigned int *major,
|
||||||
|
unsigned int *minor,
|
||||||
|
unsigned int *micro);
|
||||||
|
|
||||||
|
const char *
|
||||||
|
raqm_version_string (void);
|
||||||
|
|
||||||
|
bool
|
||||||
|
raqm_version_atleast (unsigned int major,
|
||||||
|
unsigned int minor,
|
||||||
|
unsigned int micro);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#undef _RAQM_H_IN_
|
||||||
#endif /* _RAQM_H_ */
|
#endif /* _RAQM_H_ */
|
|
@ -87,7 +87,7 @@ and install Pillow in develop mode (instead of ``python3 -m pip install --editab
|
||||||
Testing Pillow
|
Testing Pillow
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Some binary dependencies (e.g. ``libraqm.dll``) will be stored in the
|
Some binary dependencies (e.g. ``fribidi.dll``) will be stored in the
|
||||||
``winbuild\build\bin`` directory; this directory should be added to ``PATH``
|
``winbuild\build\bin`` directory; this directory should be added to ``PATH``
|
||||||
before running tests.
|
before running tests.
|
||||||
|
|
||||||
|
|
|
@ -296,21 +296,7 @@ deps = {
|
||||||
cmd_nmake(target="clean"),
|
cmd_nmake(target="clean"),
|
||||||
cmd_nmake(target="fribidi"),
|
cmd_nmake(target="fribidi"),
|
||||||
],
|
],
|
||||||
"headers": [r"lib\*.h"],
|
"bins": [r"*.dll"],
|
||||||
"libs": [r"*.lib"],
|
|
||||||
},
|
|
||||||
"libraqm": {
|
|
||||||
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.1.zip",
|
|
||||||
"filename": "libraqm-0.7.1.zip",
|
|
||||||
"dir": "libraqm-0.7.1",
|
|
||||||
"build": [
|
|
||||||
cmd_copy(r"{winbuild_dir}\raqm.cmake", r"CMakeLists.txt"),
|
|
||||||
cmd_cmake(),
|
|
||||||
cmd_nmake(target="clean"),
|
|
||||||
cmd_nmake(target="libraqm"),
|
|
||||||
],
|
|
||||||
"headers": [r"src\*.h"],
|
|
||||||
"bins": [r"libraqm.dll"],
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,7 +472,7 @@ def build_pillow():
|
||||||
cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow
|
cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow
|
||||||
cmd_set("MSSdk", "1"), # for PyPy3.6
|
cmd_set("MSSdk", "1"), # for PyPy3.6
|
||||||
cmd_set("py_vcruntime_redist", "true"), # use /MD, not /MT
|
cmd_set("py_vcruntime_redist", "true"), # use /MD, not /MT
|
||||||
r'"{python_dir}\{python_exe}" setup.py build_ext %*',
|
r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501
|
||||||
]
|
]
|
||||||
|
|
||||||
write_script("build_pillow.cmd", lines)
|
write_script("build_pillow.cmd", lines)
|
||||||
|
@ -511,8 +497,8 @@ if __name__ == "__main__":
|
||||||
verbose = True
|
verbose = True
|
||||||
elif arg == "--no-imagequant":
|
elif arg == "--no-imagequant":
|
||||||
disabled += ["libimagequant"]
|
disabled += ["libimagequant"]
|
||||||
elif arg == "--no-raqm":
|
elif arg == "--no-raqm" or arg == "--no-fribidi":
|
||||||
disabled += ["fribidi", "libraqm"]
|
disabled += ["fribidi"]
|
||||||
elif arg.startswith("--depends="):
|
elif arg.startswith("--depends="):
|
||||||
depends_dir = arg[10:]
|
depends_dir = arg[10:]
|
||||||
elif arg.startswith("--python="):
|
elif arg.startswith("--python="):
|
||||||
|
|
|
@ -93,10 +93,10 @@ fribidi_tab(brackets-type unidata/BidiBrackets.txt)
|
||||||
file(GLOB FRIBIDI_SOURCES lib/*.c)
|
file(GLOB FRIBIDI_SOURCES lib/*.c)
|
||||||
file(GLOB FRIBIDI_HEADERS lib/*.h)
|
file(GLOB FRIBIDI_HEADERS lib/*.h)
|
||||||
|
|
||||||
add_library(fribidi STATIC
|
add_library(fribidi SHARED
|
||||||
${FRIBIDI_SOURCES}
|
${FRIBIDI_SOURCES}
|
||||||
${FRIBIDI_HEADERS}
|
${FRIBIDI_HEADERS}
|
||||||
${FRIBIDI_SOURCES_GENERATED})
|
${FRIBIDI_SOURCES_GENERATED})
|
||||||
fribidi_definitions(fribidi)
|
fribidi_definitions(fribidi)
|
||||||
target_compile_definitions(fribidi
|
target_compile_definitions(fribidi
|
||||||
PUBLIC -DFRIBIDI_LIB_STATIC)
|
PUBLIC "-DFRIBIDI_BUILD")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user