diff --git a/recipes/pillow/meta.yaml b/recipes/pillow/meta.yaml index a50503c7..073b0fe8 100644 --- a/recipes/pillow/meta.yaml +++ b/recipes/pillow/meta.yaml @@ -1,11 +1,11 @@ package: name: Pillow - version: "12.2.0" + version: "12.3.0" requirements: # PNG support is internal: libpng is not used. # - # All three are linked STATICALLY into the extensions on iOS and dynamically on + # All four are linked STATICALLY into the extensions on iOS and dynamically on # Android, so where they belong differs by platform. Under `host` they become a # runtime Requires-Dist and every consuming app installs the wheel; on iOS that # is ~16 MB of .a already folded into _imaging.so / _webp.so and unloadable at @@ -17,11 +17,13 @@ requirements: - flet-libjpeg 3.0.90 - flet-libfreetype 2.13.3 - flet-libwebp 1.6.0 + - flet-libtiff 4.7.0 # {% else %} host_build: - flet-libjpeg 3.0.90 - flet-libfreetype 2.13.3 - flet-libwebp 1.6.0 + - flet-libtiff 4.7.0 # {% endif %} # {% if not version or version >= (12,0,0) %} @@ -41,7 +43,7 @@ patches: # {% endif %} build: - number: 2 + number: 1 script_env: # {% if sdk == 'android' %} # pillow's setup.py manually probes `self.compiler.{include,library}_dirs` diff --git a/recipes/pillow/tests/test_pillow.py b/recipes/pillow/tests/test_pillow.py index 9f186af7..f3b4153d 100644 --- a/recipes/pillow/tests/test_pillow.py +++ b/recipes/pillow/tests/test_pillow.py @@ -197,3 +197,43 @@ def test_webp_animation_encode(): for i, want in enumerate([(255, 0, 0), (0, 255, 0), (0, 0, 255)]): rt.seek(i) assert rt.convert("RGB").getpixel((4, 4)) == want + + +def test_libtiff_available(): + """libtiff is linked: Pillow lists it as a codec.""" + from PIL import features + + assert features.check("libtiff") is True + assert "libtiff" in features.get_supported_codecs() + + +def test_tiff_jpeg_roundtrip(): + """Write and decode JPEG-compressed TIFFs (compression 7), RGB and RGBA.""" + from PIL import Image + + for mode, colour in (("RGB", (200, 40, 40)), ("RGBA", (40, 200, 40, 128))): + src = Image.new(mode, (64, 48), colour) + out = io.BytesIO() + src.save(out, "TIFF", compression="jpeg") + + rt = Image.open(io.BytesIO(out.getvalue())) + assert rt.tag_v2[259] == 7, f"{mode}: compression tag {rt.tag_v2[259]}" + rt.load() # where a build without libtiff raises "decoder libtiff not available" + assert rt.mode == mode + got = rt.getpixel((32, 24)) + assert all(abs(g - w) <= 8 for g, w in zip(got, colour)), f"{mode}: {got}" + + +def test_tiff_lossless_compressions_roundtrip(): + """LZW, deflate and PackBits TIFFs round-trip exactly.""" + from PIL import Image + + src = Image.new("RGB", (16, 16)) + src.putdata([(x * 16, y * 16, 0) for y in range(16) for x in range(16)]) + + for compression in ("tiff_lzw", "tiff_adobe_deflate", "packbits"): + out = io.BytesIO() + src.save(out, "TIFF", compression=compression) + rt = Image.open(io.BytesIO(out.getvalue())) + assert rt.info["compression"] == compression + assert rt.tobytes() == src.tobytes(), compression