1
from __future__ import annotations
10
from pathlib import Path
11
from types import ModuleType
12
from typing import IO, Any
22
UnidentifiedImageError,
28
assert_image_equal_tofile,
30
assert_image_similar_tofile,
35
mark_if_feature_version,
39
ElementTree: ModuleType | None
41
from defusedxml import ElementTree
45
PrettyPrinter: type | None
47
from IPython.lib.pretty import PrettyPrinter
53
def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image:
54
if mode.startswith("BGR;"):
55
with pytest.warns(DeprecationWarning):
56
return Image.new(mode, size)
58
return Image.new(mode, size)
62
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
63
def test_image_modes_success(self, mode: str) -> None:
64
helper_image_new(mode, (1, 1))
66
@pytest.mark.parametrize("mode", ("", "bad", "very very long"))
67
def test_image_modes_fail(self, mode: str) -> None:
68
with pytest.raises(ValueError) as e:
69
Image.new(mode, (1, 1))
70
assert str(e.value) == "unrecognized image mode"
72
def test_exception_inheritance(self) -> None:
73
assert issubclass(UnidentifiedImageError, OSError)
75
def test_sanity(self) -> None:
76
im = Image.new("L", (100, 100))
77
assert repr(im)[:45] == "<PIL.Image.Image image mode=L size=100x100 at"
79
assert im.size == (100, 100)
81
im = Image.new("RGB", (100, 100))
82
assert repr(im)[:45] == "<PIL.Image.Image image mode=RGB size=100x100 "
83
assert im.mode == "RGB"
84
assert im.size == (100, 100)
86
Image.new("L", (100, 100), None)
87
im2 = Image.new("L", (100, 100), 0)
88
im3 = Image.new("L", (100, 100), "black")
90
assert im2.getcolors() == [(10000, 0)]
91
assert im3.getcolors() == [(10000, 0)]
93
with pytest.raises(ValueError):
94
Image.new("X", (100, 100))
95
with pytest.raises(ValueError):
96
Image.new("", (100, 100))
100
@pytest.mark.skipif(PrettyPrinter is None, reason="IPython is not installed")
101
def test_repr_pretty(self) -> None:
102
im = Image.new("L", (100, 100))
104
output = io.StringIO()
105
assert PrettyPrinter is not None
106
p = PrettyPrinter(output)
107
im._repr_pretty_(p, False)
108
assert output.getvalue() == "<PIL.Image.Image image mode=L size=100x100>"
110
def test_open_formats(self) -> None:
111
PNGFILE = "Tests/images/hopper.png"
112
JPGFILE = "Tests/images/hopper.jpg"
114
with pytest.raises(TypeError):
115
with Image.open(PNGFILE, formats=123):
118
format_list: list[list[str] | tuple[str, ...]] = [
126
for formats in format_list:
127
with pytest.raises(UnidentifiedImageError):
128
with Image.open(PNGFILE, formats=formats):
131
with Image.open(JPGFILE, formats=formats) as im:
132
assert im.mode == "RGB"
133
assert im.size == (128, 128)
135
for file in [PNGFILE, JPGFILE]:
136
with Image.open(file, formats=None) as im:
137
assert im.mode == "RGB"
138
assert im.size == (128, 128)
140
def test_open_verbose_failure(self, monkeypatch: pytest.MonkeyPatch) -> None:
141
monkeypatch.setattr(Image, "WARN_POSSIBLE_FORMATS", True)
144
with pytest.warns(UserWarning):
145
with pytest.raises(UnidentifiedImageError):
149
def test_width_height(self) -> None:
150
im = Image.new("RGB", (1, 2))
152
assert im.height == 2
154
with pytest.raises(AttributeError):
157
def test_set_mode(self) -> None:
158
im = Image.new("RGB", (1, 1))
160
with pytest.raises(AttributeError):
163
def test_invalid_image(self) -> None:
165
with pytest.raises(UnidentifiedImageError):
169
def test_bad_mode(self) -> None:
170
with pytest.raises(ValueError):
171
with Image.open("filename", "bad mode"):
174
def test_stringio(self) -> None:
175
with pytest.raises(ValueError):
176
with Image.open(io.StringIO()):
179
def test_pathlib(self, tmp_path: Path) -> None:
180
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
181
assert im.mode == "P"
182
assert im.size == (10, 10)
184
with Image.open(Path("Tests/images/hopper.jpg")) as im:
185
assert im.mode == "RGB"
186
assert im.size == (128, 128)
188
for ext in (".jpg", ".jp2"):
189
if ext == ".jp2" and not features.check_codec("jpg_2000"):
190
pytest.skip("jpg_2000 not available")
191
temp_file = str(tmp_path / ("temp." + ext))
192
if os.path.exists(temp_file):
194
im.save(Path(temp_file))
196
def test_fp_name(self, tmp_path: Path) -> None:
197
temp_file = str(tmp_path / "temp.jpg")
199
class FP(io.BytesIO):
202
if sys.version_info >= (3, 12):
203
from collections.abc import Buffer
205
def write(self, data: Buffer) -> int:
210
def write(self, data: Any) -> int:
219
def test_tempfile(self) -> None:
223
with tempfile.TemporaryFile() as fp:
226
with Image.open(fp) as reloaded:
227
assert_image_similar(im, reloaded, 20)
229
def test_unknown_extension(self, tmp_path: Path) -> None:
231
temp_file = str(tmp_path / "temp.unknown")
232
with pytest.raises(ValueError):
235
def test_internals(self) -> None:
236
im = Image.new("L", (100, 100))
239
assert not im.readonly
242
im.paste(0, (0, 0, 100, 100))
243
assert not im.readonly
245
@pytest.mark.skipif(is_win32(), reason="Test requires opening tempfile twice")
247
sys.platform == "cygwin",
248
reason="Test requires opening an mmaped file for writing",
250
def test_readonly_save(self, tmp_path: Path) -> None:
251
temp_file = str(tmp_path / "temp.bmp")
252
shutil.copy("Tests/images/rgb32bf-rgba.bmp", temp_file)
254
with Image.open(temp_file) as im:
258
def test_dump(self, tmp_path: Path) -> None:
259
im = Image.new("L", (10, 10))
260
im._dump(str(tmp_path / "temp_L.ppm"))
262
im = Image.new("RGB", (10, 10))
263
im._dump(str(tmp_path / "temp_RGB.ppm"))
265
im = Image.new("HSV", (10, 10))
266
with pytest.raises(ValueError):
267
im._dump(str(tmp_path / "temp_HSV.ppm"))
269
def test_comparison_with_other_type(self) -> None:
271
item = Image.new("RGB", (25, 25), "#000")
276
assert item is not None
279
def test_expand_x(self) -> None:
286
im = im._expand(xmargin)
289
assert im.size[0] == orig_size[0] + 2 * xmargin
290
assert im.size[1] == orig_size[1] + 2 * xmargin
292
def test_expand_xy(self) -> None:
300
im = im._expand(xmargin, ymargin)
303
assert im.size[0] == orig_size[0] + 2 * xmargin
304
assert im.size[1] == orig_size[1] + 2 * ymargin
306
def test_getbands(self) -> None:
308
assert hopper("RGB").getbands() == ("R", "G", "B")
309
assert hopper("YCbCr").getbands() == ("Y", "Cb", "Cr")
311
def test_getchannel_wrong_params(self) -> None:
314
with pytest.raises(ValueError):
316
with pytest.raises(ValueError):
318
with pytest.raises(ValueError):
320
with pytest.raises(ValueError):
323
def test_getchannel(self) -> None:
325
Y, Cb, Cr = im.split()
327
assert_image_equal(Y, im.getchannel(0))
328
assert_image_equal(Y, im.getchannel("Y"))
329
assert_image_equal(Cb, im.getchannel(1))
330
assert_image_equal(Cb, im.getchannel("Cb"))
331
assert_image_equal(Cr, im.getchannel(2))
332
assert_image_equal(Cr, im.getchannel("Cr"))
334
def test_getbbox(self) -> None:
342
assert bbox == (0, 0, 128, 128)
344
def test_ne(self) -> None:
346
im1 = Image.new("RGB", (25, 25), "black")
347
im2 = Image.new("RGB", (25, 25), "white")
352
def test_alpha_composite(self) -> None:
355
expected_colors = sorted(
357
(1122, (128, 127, 0, 255)),
358
(1089, (0, 255, 0, 255)),
359
(3300, (255, 0, 0, 255)),
360
(1156, (170, 85, 0, 192)),
361
(1122, (0, 255, 0, 128)),
362
(1122, (255, 0, 0, 128)),
363
(1089, (0, 255, 0, 0)),
367
dst = Image.new("RGBA", size=(100, 100), color=(0, 255, 0, 255))
368
draw = ImageDraw.Draw(dst)
369
draw.rectangle((0, 33, 100, 66), fill=(0, 255, 0, 128))
370
draw.rectangle((0, 67, 100, 100), fill=(0, 255, 0, 0))
371
src = Image.new("RGBA", size=(100, 100), color=(255, 0, 0, 255))
372
draw = ImageDraw.Draw(src)
373
draw.rectangle((33, 0, 66, 100), fill=(255, 0, 0, 128))
374
draw.rectangle((67, 0, 100, 100), fill=(255, 0, 0, 0))
377
img = Image.alpha_composite(dst, src)
380
img_colors = img.getcolors()
381
assert img_colors is not None
382
assert sorted(img_colors) == expected_colors
384
def test_alpha_inplace(self) -> None:
385
src = Image.new("RGBA", (128, 128), "blue")
387
over = Image.new("RGBA", (128, 128), "red")
391
target = Image.alpha_composite(src, over)
395
full.alpha_composite(over)
396
assert_image_equal(full, target)
400
offset.alpha_composite(over, (64, 64))
401
assert_image_equal(offset.crop((64, 64, 127, 127)), target.crop((0, 0, 63, 63)))
402
assert offset.size == (128, 128)
406
offset.alpha_composite(over, (-64, -64))
407
assert_image_equal(offset.crop((0, 0, 63, 63)), target.crop((64, 64, 127, 127)))
408
assert offset.size == (128, 128)
412
box.alpha_composite(over, (64, 64), (0, 0, 32, 32))
413
assert_image_equal(box.crop((64, 64, 96, 96)), target.crop((0, 0, 32, 32)))
414
assert_image_equal(box.crop((96, 96, 128, 128)), src.crop((0, 0, 32, 32)))
415
assert box.size == (128, 128)
419
source.alpha_composite(over, (32, 32), (32, 32, 96, 96))
421
assert_image_equal(source.crop((32, 32, 96, 96)), target.crop((32, 32, 96, 96)))
422
assert source.size == (128, 128)
425
with pytest.raises(ValueError):
426
source.alpha_composite(over, "invalid destination")
427
with pytest.raises(ValueError):
428
source.alpha_composite(over, (0, 0), "invalid source")
429
with pytest.raises(ValueError):
430
source.alpha_composite(over, 0)
431
with pytest.raises(ValueError):
432
source.alpha_composite(over, (0, 0), 0)
433
with pytest.raises(ValueError):
434
source.alpha_composite(over, (0, 0), (0, -1))
436
def test_register_open_duplicates(self) -> None:
438
factory, accept = Image.OPEN["JPEG"]
439
id_length = len(Image.ID)
442
Image.register_open("JPEG", factory, accept)
445
assert len(Image.ID) == id_length
447
def test_registered_extensions_uninitialized(self) -> None:
449
Image._initialized = 0
452
Image.registered_extensions()
455
assert Image._initialized == 2
457
def test_registered_extensions(self) -> None:
460
with Image.open("Tests/images/rgb.jpg"):
464
extensions = Image.registered_extensions()
468
for ext in [".cur", ".icns", ".tif", ".tiff"]:
469
assert ext in extensions
471
def test_effect_mandelbrot(self) -> None:
474
extent = (-3, -2.5, 2, 2.5)
478
im = Image.effect_mandelbrot(size, extent, quality)
481
assert im.size == (512, 512)
482
assert_image_equal_tofile(im, "Tests/images/effect_mandelbrot.png")
484
def test_effect_mandelbrot_bad_arguments(self) -> None:
488
extent = (+3, +2.5, -2, -2.5)
493
with pytest.raises(ValueError):
494
Image.effect_mandelbrot(size, extent, quality)
496
def test_effect_noise(self) -> None:
502
im = Image.effect_noise(size, sigma)
505
assert im.size == (100, 100)
506
assert im.mode == "L"
507
p0 = im.getpixel((0, 0))
508
p1 = im.getpixel((0, 1))
509
p2 = im.getpixel((0, 2))
510
p3 = im.getpixel((0, 3))
511
p4 = im.getpixel((0, 4))
512
assert_not_all_same([p0, p1, p2, p3, p4])
514
def test_effect_spread(self) -> None:
520
im2 = im.effect_spread(distance)
523
assert im.size == (128, 128)
524
assert_image_similar_tofile(im2, "Tests/images/effect_spread.png", 110)
526
def test_effect_spread_zero(self) -> None:
532
im2 = im.effect_spread(distance)
535
assert_image_equal(im, im2)
537
def test_check_size(self) -> None:
539
with pytest.raises(ValueError):
542
with pytest.raises(ValueError):
544
Image.new("RGB", (0,))
545
with pytest.raises(ValueError):
546
Image.new("RGB", (-1, -1))
549
im = Image.new("L", (0, 0))
550
assert im.size == (0, 0)
552
im = Image.new("L", (0, 100))
553
assert im.size == (0, 100)
555
im = Image.new("L", (100, 0))
556
assert im.size == (100, 0)
558
assert Image.new("RGB", (1, 1))
560
i = Image.new("RGB", [1, 1])
561
assert isinstance(i.size, tuple)
563
@pytest.mark.timeout(0.75)
565
"PILLOW_VALGRIND_TEST" in os.environ, reason="Valgrind is slower"
567
@pytest.mark.parametrize("size", ((0, 100000000), (100000000, 0)))
568
def test_empty_image(self, size: tuple[int, int]) -> None:
569
Image.new("RGB", size)
571
def test_storage_neg(self) -> None:
577
with pytest.raises(ValueError):
578
Image.core.fill("RGB", (2, -2), (0, 0, 0))
580
def test_one_item_tuple(self) -> None:
581
for mode in ("I", "F", "L"):
582
im = Image.new(mode, (100, 100), (5,))
584
assert px is not None
587
def test_linear_gradient_wrong_mode(self) -> None:
592
with pytest.raises(ValueError):
593
Image.linear_gradient(wrong_mode)
595
@pytest.mark.parametrize("mode", ("L", "P", "I", "F"))
596
def test_linear_gradient(self, mode: str) -> None:
598
target_file = "Tests/images/linear_gradient.png"
601
im = Image.linear_gradient(mode)
604
assert im.size == (256, 256)
605
assert im.mode == mode
606
assert im.getpixel((0, 0)) == 0
607
assert im.getpixel((255, 255)) == 255
608
with Image.open(target_file) as target:
609
target = target.convert(mode)
610
assert_image_equal(im, target)
612
def test_radial_gradient_wrong_mode(self) -> None:
617
with pytest.raises(ValueError):
618
Image.radial_gradient(wrong_mode)
620
@pytest.mark.parametrize("mode", ("L", "P", "I", "F"))
621
def test_radial_gradient(self, mode: str) -> None:
623
target_file = "Tests/images/radial_gradient.png"
626
im = Image.radial_gradient(mode)
629
assert im.size == (256, 256)
630
assert im.mode == mode
631
assert im.getpixel((0, 0)) == 255
632
assert im.getpixel((128, 128)) == 0
633
with Image.open(target_file) as target:
634
target = target.convert(mode)
635
assert_image_equal(im, target)
637
def test_register_extensions(self) -> None:
641
Image.register_extension(test_format, ext)
642
ext_individual = Image.EXTENSION.copy()
644
del Image.EXTENSION[ext]
646
Image.register_extensions(test_format, exts)
647
ext_multiple = Image.EXTENSION.copy()
649
del Image.EXTENSION[ext]
651
assert ext_individual == ext_multiple
653
def test_remap_palette(self) -> None:
655
with Image.open("Tests/images/hopper.gif") as im:
656
assert_image_equal(im, im.remap_palette(list(range(256))))
659
im = Image.new("P", (256, 1))
661
im.putpixel((x, 0), x)
662
im.putpalette(list(range(256)) * 4, "RGBA")
663
im_remapped = im.remap_palette(list(range(256)))
664
assert_image_equal(im, im_remapped)
665
assert im.palette.palette == im_remapped.palette.palette
669
with pytest.raises(ValueError):
670
im.remap_palette(None)
672
def test_remap_palette_transparency(self) -> None:
673
im = Image.new("P", (1, 2), (0, 0, 0))
674
im.putpixel((0, 1), (255, 0, 0))
675
im.info["transparency"] = 0
677
im_remapped = im.remap_palette([1, 0])
678
assert im_remapped.info["transparency"] == 1
679
palette = im_remapped.getpalette()
680
assert palette is not None
681
assert len(palette) == 6
684
im.info["transparency"] = 2
686
im_remapped = im.remap_palette([1, 0])
687
assert "transparency" not in im_remapped.info
689
def test__new(self) -> None:
693
blank_p = Image.new("P", (10, 10))
694
blank_pa = Image.new("PA", (10, 10))
695
blank_p.palette = None
696
blank_pa.palette = None
699
base_image: Image.Image,
701
palette_result: ImagePalette.ImagePalette | None = None,
703
new_image = base_image._new(image.im)
704
assert new_image.mode == image.mode
705
assert new_image.size == image.size
706
assert new_image.info == base_image.info
707
if palette_result is not None:
708
assert new_image.palette is not None
709
assert new_image.palette.tobytes() == palette_result.tobytes()
711
assert new_image.palette is None
713
_make_new(im, im_p, ImagePalette.ImagePalette("RGB"))
714
_make_new(im_p, im, None)
715
_make_new(im, blank_p, ImagePalette.ImagePalette())
716
_make_new(im, blank_pa, ImagePalette.ImagePalette())
718
@pytest.mark.parametrize(
722
("RGB", (221, 238, 255)),
723
("RGBA", (221, 238, 255, 255)),
726
def test_p_from_rgb_rgba(self, mode: str, color: str | tuple[int, ...]) -> None:
727
im = Image.new("P", (100, 100), color)
728
expected = Image.new(mode, (100, 100), color)
729
assert_image_equal(im.convert(mode), expected)
731
def test_no_resource_warning_on_save(self, tmp_path: Path) -> None:
734
test_file = "Tests/images/hopper.png"
735
temp_file = str(tmp_path / "temp.jpg")
738
with Image.open(test_file) as im:
739
with warnings.catch_warnings():
742
def test_no_new_file_on_error(self, tmp_path: Path) -> None:
743
temp_file = str(tmp_path / "temp.jpg")
745
im = Image.new("RGB", (0, 0))
746
with pytest.raises(ValueError):
749
assert not os.path.exists(temp_file)
751
def test_load_on_nonexclusive_multiframe(self) -> None:
752
with open("Tests/images/frozenpond.mpo", "rb") as fp:
754
def act(fp: IO[bytes]) -> None:
760
with Image.open(fp) as im:
765
def test_empty_exif(self) -> None:
766
with Image.open("Tests/images/exif.png") as im:
772
assert not dict(exif)
775
exif.load(b"Exif\x00\x00")
776
assert not dict(exif)
778
@mark_if_feature_version(
779
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
781
def test_exif_jpeg(self, tmp_path: Path) -> None:
782
with Image.open("Tests/images/exif-72dpi-int.jpg") as im:
784
assert 258 not in exif
787
assert exif[296] == 2
788
assert exif[11] == "gThumb 3.0.1"
790
out = str(tmp_path / "temp.jpg")
795
exif[11] = "Pillow test"
796
im.save(out, exif=exif)
797
with Image.open(out) as reloaded:
798
reloaded_exif = reloaded.getexif()
799
assert reloaded_exif[258] == 8
800
assert 274 not in reloaded_exif
801
assert 282 not in reloaded_exif
802
assert reloaded_exif[296] == 455
803
assert reloaded_exif[11] == "Pillow test"
805
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
807
assert 258 not in exif
809
assert exif[274] == 1
810
assert exif[305] == "Adobe Photoshop CC 2017 (Macintosh)"
812
out = str(tmp_path / "temp.jpg")
816
exif[305] = "Pillow test"
817
im.save(out, exif=exif)
818
with Image.open(out) as reloaded:
819
reloaded_exif = reloaded.getexif()
820
assert reloaded_exif[258] == 8
821
assert 306 not in reloaded_exif
822
assert reloaded_exif[274] == 455
823
assert reloaded_exif[305] == "Pillow test"
825
@skip_unless_feature("webp")
826
def test_exif_webp(self, tmp_path: Path) -> None:
827
with Image.open("Tests/images/hopper.webp") as im:
831
out = str(tmp_path / "temp.webp")
834
exif[305] = "Pillow test"
836
def check_exif() -> None:
837
with Image.open(out) as reloaded:
838
reloaded_exif = reloaded.getexif()
839
assert reloaded_exif[258] == 8
840
assert reloaded_exif[40963] == 455
841
assert reloaded_exif[305] == "Pillow test"
843
im.save(out, exif=exif)
845
im.save(out, exif=exif, save_all=True)
848
def test_exif_png(self, tmp_path: Path) -> None:
849
with Image.open("Tests/images/exif.png") as im:
851
assert exif == {274: 1}
853
out = str(tmp_path / "temp.png")
857
exif[305] = "Pillow test"
858
im.save(out, exif=exif)
860
with Image.open(out) as reloaded:
861
reloaded_exif = reloaded.getexif()
862
assert reloaded_exif == {258: 8, 40963: 455, 305: "Pillow test"}
864
def test_exif_interop(self) -> None:
865
with Image.open("Tests/images/flower.jpg") as im:
867
assert exif.get_ifd(0xA005) == {
874
reloaded_exif = Image.Exif()
875
reloaded_exif.load(exif.tobytes())
876
assert reloaded_exif.get_ifd(0xA005) == exif.get_ifd(0xA005)
878
def test_exif_ifd1(self) -> None:
879
with Image.open("Tests/images/flower.jpg") as im:
881
assert exif.get_ifd(ExifTags.IFD.IFD1) == {
890
def test_exif_ifd(self) -> None:
891
with Image.open("Tests/images/flower.jpg") as im:
893
del exif.get_ifd(0x8769)[0xA005]
895
reloaded_exif = Image.Exif()
896
reloaded_exif.load(exif.tobytes())
897
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)
899
def test_exif_load_from_fp(self) -> None:
900
with Image.open("Tests/images/flower.jpg") as im:
901
data = im.info["exif"]
902
if data.startswith(b"Exif\x00\x00"):
904
fp = io.BytesIO(data)
907
exif.load_from_fp(fp)
910
272: "Canon PowerShot S40",
915
306: "2003:12:14 12:01:44",
920
def test_exif_hide_offsets(self) -> None:
921
with Image.open("Tests/images/flower.jpg") as im:
925
assert 0x8769 in exif
926
for tag in (0xA005, 0x927C):
927
assert tag in exif.get_ifd(0x8769)
928
assert exif.get_ifd(0xA005)
931
with Image.open("Tests/images/flower.jpg") as im:
932
new_exif = im.getexif()
934
for exif in (loaded_exif, new_exif):
939
assert 0x8769 not in exif
940
assert exif.get_ifd(0x8769)
941
for tag in (0xA005, 0x927C):
942
assert tag not in exif.get_ifd(0x8769)
943
assert exif.get_ifd(0xA005)
945
def test_empty_xmp(self) -> None:
946
with Image.open("Tests/images/hopper.gif") as im:
947
if ElementTree is None:
950
match="XMP data cannot be read without defusedxml dependency",
957
def test_getxmp_padded(self) -> None:
958
im = Image.new("RGB", (1, 1))
960
b'<?xpacket begin="\xef\xbb\xbf" id="W5M0MpCehiHzreSzNTczkc9d"?>\n'
961
b'<x:xmpmeta xmlns:x="adobe:ns:meta/" />\n<?xpacket end="w"?>\x00\x00'
963
if ElementTree is None:
966
match="XMP data cannot be read without defusedxml dependency",
968
assert im.getxmp() == {}
970
assert im.getxmp() == {"xmpmeta": None}
972
@pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0)))
973
def test_zero_tobytes(self, size: tuple[int, int]) -> None:
974
im = Image.new("RGB", size)
975
assert im.tobytes() == b""
977
@pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0)))
978
def test_zero_frombytes(self, size: tuple[int, int]) -> None:
979
Image.frombytes("RGB", size, b"")
981
im = Image.new("RGB", size)
984
def test_has_transparency_data(self) -> None:
985
for mode in ("1", "L", "P", "RGB"):
986
im = Image.new(mode, (1, 1))
987
assert not im.has_transparency_data
989
for mode in ("LA", "La", "PA", "RGBA", "RGBa"):
990
im = Image.new(mode, (1, 1))
991
assert im.has_transparency_data
994
with Image.open("Tests/images/first_frame_transparency.gif") as im:
995
assert "transparency" in im.info
996
assert im.has_transparency_data
999
with Image.open("Tests/images/rgb_trns.png") as im:
1000
assert "transparency" in im.info
1001
assert im.has_transparency_data
1004
im = Image.new("RGBA", (1, 1)).convert("P")
1005
assert im.mode == "P"
1006
assert im.palette is not None
1007
assert im.palette.mode == "RGBA"
1008
assert im.has_transparency_data
1010
def test_apply_transparency(self) -> None:
1011
im = Image.new("P", (1, 1))
1012
im.putpalette((0, 0, 0, 1, 1, 1))
1013
assert im.palette is not None
1014
assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1}
1017
im.apply_transparency()
1018
assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1}
1021
im.info["transparency"] = 0
1022
im.apply_transparency()
1023
assert "transparency" not in im.info
1024
assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 255): 1}
1027
im = Image.new("P", (1, 1))
1028
im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA")
1029
im.info["transparency"] = 0
1030
im.apply_transparency()
1031
assert im.palette is not None
1032
assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1}
1035
with Image.open("Tests/images/pil123p.png") as im:
1036
assert isinstance(im.info["transparency"], bytes)
1037
assert im.palette is not None
1038
assert im.palette.colors[(27, 35, 6)] == 24
1039
im.apply_transparency()
1040
assert im.palette is not None
1041
assert im.palette.colors[(27, 35, 6, 214)] == 24
1043
def test_constants(self) -> None:
1052
for name in enum.__members__:
1053
assert getattr(Image, name) == enum[name]
1055
@pytest.mark.parametrize(
1060
"sgi_overrun_expandrow.bin",
1061
"sgi_overrun_expandrow2.bin",
1064
"ossfuzz-4836216264589312.pcx",
1068
def test_overrun(self, path: str) -> None:
1069
"""For overrun completeness, test as:
1070
valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c
1072
with Image.open(os.path.join("Tests/images", path)) as im:
1076
except OSError as e:
1077
buffer_overrun = str(e) == "buffer overrun when reading image file"
1078
truncated = "image file is truncated" in str(e)
1080
assert buffer_overrun or truncated
1082
def test_fli_overrun2(self) -> None:
1083
with Image.open("Tests/images/fli_overrun2.bin") as im:
1087
except OSError as e:
1088
assert str(e) == "buffer overrun when reading image file"
1090
def test_exit_fp(self) -> None:
1091
with Image.new("L", (1, 1)) as im:
1093
assert not hasattr(im, "fp")
1095
def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None:
1096
with Image.open("Tests/images/hopper.jpg") as im:
1098
with caplog.at_level(logging.DEBUG):
1101
assert len(caplog.records) == 0
1102
assert im.fp is None
1105
class TestImageBytes:
1106
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
1107
def test_roundtrip_bytes_constructor(self, mode: str) -> None:
1109
source_bytes = im.tobytes()
1111
if mode.startswith("BGR;"):
1112
with pytest.warns(DeprecationWarning):
1113
reloaded = Image.frombytes(mode, im.size, source_bytes)
1115
reloaded = Image.frombytes(mode, im.size, source_bytes)
1116
assert reloaded.tobytes() == source_bytes
1118
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
1119
def test_roundtrip_bytes_method(self, mode: str) -> None:
1121
source_bytes = im.tobytes()
1123
reloaded = helper_image_new(mode, im.size)
1124
reloaded.frombytes(source_bytes)
1125
assert reloaded.tobytes() == source_bytes
1127
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
1128
def test_getdata_putdata(self, mode: str) -> None:
1129
if is_big_endian() and mode == "BGR;15":
1130
pytest.xfail("Known failure of BGR;15 on big-endian")
1132
reloaded = helper_image_new(mode, im.size)
1133
reloaded.putdata(im.getdata())
1134
assert_image_equal(im, reloaded)
1137
class MockEncoder(ImageFile.PyEncoder):
1142
def test_encode_registry(self) -> None:
1143
Image.register_encoder("MOCK", MockEncoder)
1144
assert "MOCK" in Image.ENCODERS
1146
enc = Image._getencoder("RGB", "MOCK", ("args",), extra=("extra",))
1148
assert isinstance(enc, MockEncoder)
1149
assert enc.mode == "RGB"
1150
assert enc.args == ("args", "extra")
1152
def test_encode_registry_fail(self) -> None:
1153
with pytest.raises(OSError):
1154
Image._getencoder("RGB", "DoesNotExist", ("args",), extra=("extra",))