Skip to content

Commit 24b9ca7

Browse files
authored
Merge branch 'main' into progress
2 parents ac009d0 + 478ccb4 commit 24b9ca7

60 files changed

Lines changed: 871 additions & 344 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Tests/check_j2k_overflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
def test_j2k_overflow(tmp_path: Path) -> None:
1111
im = Image.new("RGBA", (1024, 131584))
12-
target = str(tmp_path / "temp.jpc")
12+
target = tmp_path / "temp.jpc"
1313
with pytest.raises(OSError):
1414
im.save(target)

Tests/check_large_memory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333

3434
def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None:
35-
f = str(tmp_path / "temp.png")
35+
f = tmp_path / "temp.png"
3636
im = Image.new("L", (xdim, ydim), 0)
3737
im.save(f)
3838

Tests/check_large_memory_numpy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
def _write_png(tmp_path: Path, xdim: int, ydim: int) -> None:
2929
dtype = np.uint8
3030
a = np.zeros((xdim, ydim), dtype=dtype)
31-
f = str(tmp_path / "temp.png")
31+
f = tmp_path / "temp.png"
3232
im = Image.fromarray(a, "L")
3333
im.save(f)
3434

Tests/helper.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from collections.abc import Sequence
1414
from functools import lru_cache
1515
from io import BytesIO
16+
from pathlib import Path
1617
from typing import Any, Callable
1718

1819
import pytest
@@ -95,7 +96,10 @@ def assert_image_equal(a: Image.Image, b: Image.Image, msg: str | None = None) -
9596

9697

9798
def assert_image_equal_tofile(
98-
a: Image.Image, filename: str, msg: str | None = None, mode: str | None = None
99+
a: Image.Image,
100+
filename: str | Path,
101+
msg: str | None = None,
102+
mode: str | None = None,
99103
) -> None:
100104
with Image.open(filename) as img:
101105
if mode:
@@ -136,7 +140,7 @@ def assert_image_similar(
136140

137141
def assert_image_similar_tofile(
138142
a: Image.Image,
139-
filename: str,
143+
filename: str | Path,
140144
epsilon: float,
141145
msg: str | None = None,
142146
) -> None:

Tests/test_file_apng.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ def test_apng_sequence_errors(test_file: str) -> None:
346346

347347
def test_apng_save(tmp_path: Path) -> None:
348348
with Image.open("Tests/images/apng/single_frame.png") as im:
349-
test_file = str(tmp_path / "temp.png")
349+
test_file = tmp_path / "temp.png"
350350
im.save(test_file, save_all=True)
351351

352352
with Image.open(test_file) as im:
@@ -376,7 +376,7 @@ def test_apng_save(tmp_path: Path) -> None:
376376

377377

378378
def test_apng_save_alpha(tmp_path: Path) -> None:
379-
test_file = str(tmp_path / "temp.png")
379+
test_file = tmp_path / "temp.png"
380380

381381
im = Image.new("RGBA", (1, 1), (255, 0, 0, 255))
382382
im2 = Image.new("RGBA", (1, 1), (255, 0, 0, 127))
@@ -394,7 +394,7 @@ def test_apng_save_split_fdat(tmp_path: Path) -> None:
394394
# frames with image data spanning multiple fdAT chunks (in this case
395395
# both the default image and first animation frame will span multiple
396396
# data chunks)
397-
test_file = str(tmp_path / "temp.png")
397+
test_file = tmp_path / "temp.png"
398398
with Image.open("Tests/images/old-style-jpeg-compression.png") as im:
399399
frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))]
400400
im.save(
@@ -409,7 +409,7 @@ def test_apng_save_split_fdat(tmp_path: Path) -> None:
409409

410410

411411
def test_apng_save_duration_loop(tmp_path: Path) -> None:
412-
test_file = str(tmp_path / "temp.png")
412+
test_file = tmp_path / "temp.png"
413413
with Image.open("Tests/images/apng/delay.png") as im:
414414
frames = []
415415
durations = []
@@ -472,7 +472,7 @@ def test_apng_save_duration_loop(tmp_path: Path) -> None:
472472

473473

474474
def test_apng_save_disposal(tmp_path: Path) -> None:
475-
test_file = str(tmp_path / "temp.png")
475+
test_file = tmp_path / "temp.png"
476476
size = (128, 64)
477477
red = Image.new("RGBA", size, (255, 0, 0, 255))
478478
green = Image.new("RGBA", size, (0, 255, 0, 255))
@@ -573,7 +573,7 @@ def test_apng_save_disposal(tmp_path: Path) -> None:
573573

574574

575575
def test_apng_save_disposal_previous(tmp_path: Path) -> None:
576-
test_file = str(tmp_path / "temp.png")
576+
test_file = tmp_path / "temp.png"
577577
size = (128, 64)
578578
blue = Image.new("RGBA", size, (0, 0, 255, 255))
579579
red = Image.new("RGBA", size, (255, 0, 0, 255))
@@ -595,7 +595,7 @@ def test_apng_save_disposal_previous(tmp_path: Path) -> None:
595595

596596

597597
def test_apng_save_blend(tmp_path: Path) -> None:
598-
test_file = str(tmp_path / "temp.png")
598+
test_file = tmp_path / "temp.png"
599599
size = (128, 64)
600600
red = Image.new("RGBA", size, (255, 0, 0, 255))
601601
green = Image.new("RGBA", size, (0, 255, 0, 255))
@@ -715,7 +715,7 @@ def callback(state):
715715

716716

717717
def test_apng_save_size(tmp_path: Path) -> None:
718-
test_file = str(tmp_path / "temp.png")
718+
test_file = tmp_path / "temp.png"
719719

720720
im = Image.new("L", (100, 100))
721721
im.save(test_file, save_all=True, append_images=[Image.new("L", (200, 200))])
@@ -739,7 +739,7 @@ def test_seek_after_close() -> None:
739739
def test_different_modes_in_later_frames(
740740
mode: str, default_image: bool, duplicate: bool, tmp_path: Path
741741
) -> None:
742-
test_file = str(tmp_path / "temp.png")
742+
test_file = tmp_path / "temp.png"
743743

744744
im = Image.new("L", (1, 1))
745745
im.save(
@@ -753,7 +753,7 @@ def test_different_modes_in_later_frames(
753753

754754

755755
def test_different_durations(tmp_path: Path) -> None:
756-
test_file = str(tmp_path / "temp.png")
756+
test_file = tmp_path / "temp.png"
757757

758758
with Image.open("Tests/images/apng/different_durations.png") as im:
759759
for _ in range(3):

Tests/test_file_blp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_invalid_file() -> None:
4646

4747

4848
def test_save(tmp_path: Path) -> None:
49-
f = str(tmp_path / "temp.blp")
49+
f = tmp_path / "temp.blp"
5050

5151
for version in ("BLP1", "BLP2"):
5252
im = hopper("P")
@@ -56,7 +56,7 @@ def test_save(tmp_path: Path) -> None:
5656
assert_image_equal(im.convert("RGB"), reloaded)
5757

5858
with Image.open("Tests/images/transparent.png") as im:
59-
f = str(tmp_path / "temp.blp")
59+
f = tmp_path / "temp.blp"
6060
im.convert("P").save(f, blp_version=version)
6161

6262
with Image.open(f) as reloaded:

Tests/test_file_bmp.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
def test_sanity(tmp_path: Path) -> None:
1919
def roundtrip(im: Image.Image) -> None:
20-
outfile = str(tmp_path / "temp.bmp")
20+
outfile = tmp_path / "temp.bmp"
2121

2222
im.save(outfile, "BMP")
2323

@@ -66,15 +66,15 @@ def test_small_palette(tmp_path: Path) -> None:
6666
colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]
6767
im.putpalette(colors)
6868

69-
out = str(tmp_path / "temp.bmp")
69+
out = tmp_path / "temp.bmp"
7070
im.save(out)
7171

7272
with Image.open(out) as reloaded:
7373
assert reloaded.getpalette() == colors
7474

7575

7676
def test_save_too_large(tmp_path: Path) -> None:
77-
outfile = str(tmp_path / "temp.bmp")
77+
outfile = tmp_path / "temp.bmp"
7878
with Image.new("RGB", (1, 1)) as im:
7979
im._size = (37838, 37838)
8080
with pytest.raises(ValueError):
@@ -96,7 +96,7 @@ def test_dpi() -> None:
9696
def test_save_bmp_with_dpi(tmp_path: Path) -> None:
9797
# Test for #1301
9898
# Arrange
99-
outfile = str(tmp_path / "temp.jpg")
99+
outfile = tmp_path / "temp.jpg"
100100
with Image.open("Tests/images/hopper.bmp") as im:
101101
assert im.info["dpi"] == (95.98654816726399, 95.98654816726399)
102102

@@ -112,7 +112,7 @@ def test_save_bmp_with_dpi(tmp_path: Path) -> None:
112112

113113

114114
def test_save_float_dpi(tmp_path: Path) -> None:
115-
outfile = str(tmp_path / "temp.bmp")
115+
outfile = tmp_path / "temp.bmp"
116116
with Image.open("Tests/images/hopper.bmp") as im:
117117
im.save(outfile, dpi=(72.21216100543306, 72.21216100543306))
118118
with Image.open(outfile) as reloaded:
@@ -152,7 +152,7 @@ def test_dib_header_size(header_size: int, path: str) -> None:
152152

153153

154154
def test_save_dib(tmp_path: Path) -> None:
155-
outfile = str(tmp_path / "temp.dib")
155+
outfile = tmp_path / "temp.dib"
156156

157157
with Image.open("Tests/images/clipboard.dib") as im:
158158
im.save(outfile)

Tests/test_file_bufrstub.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_load() -> None:
4343
def test_save(tmp_path: Path) -> None:
4444
# Arrange
4545
im = hopper()
46-
tmpfile = str(tmp_path / "temp.bufr")
46+
tmpfile = tmp_path / "temp.bufr"
4747

4848
# Act / Assert: stub cannot save without an implemented handler
4949
with pytest.raises(OSError):
@@ -79,7 +79,7 @@ def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None:
7979
im.load()
8080
assert handler.is_loaded()
8181

82-
temp_file = str(tmp_path / "temp.bufr")
82+
temp_file = tmp_path / "temp.bufr"
8383
im.save(temp_file)
8484
assert handler.saved
8585

Tests/test_file_dds.py

Lines changed: 126 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99

1010
from PIL import DdsImagePlugin, Image
1111

12-
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
12+
from .helper import (
13+
assert_image_equal,
14+
assert_image_equal_tofile,
15+
assert_image_similar,
16+
assert_image_similar_tofile,
17+
hopper,
18+
)
1319

1420
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
1521
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
@@ -109,6 +115,32 @@ def test_sanity_ati1_bc4u(image_path: str) -> None:
109115
assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png"))
110116

111117

118+
def test_dx10_bc2(tmp_path: Path) -> None:
119+
out = tmp_path / "temp.dds"
120+
with Image.open(TEST_FILE_DXT3) as im:
121+
im.save(out, pixel_format="BC2")
122+
123+
with Image.open(out) as reloaded:
124+
assert reloaded.format == "DDS"
125+
assert reloaded.mode == "RGBA"
126+
assert reloaded.size == (256, 256)
127+
128+
assert_image_similar(im, reloaded, 3.81)
129+
130+
131+
def test_dx10_bc3(tmp_path: Path) -> None:
132+
out = tmp_path / "temp.dds"
133+
with Image.open(TEST_FILE_DXT5) as im:
134+
im.save(out, pixel_format="BC3")
135+
136+
with Image.open(out) as reloaded:
137+
assert reloaded.format == "DDS"
138+
assert reloaded.mode == "RGBA"
139+
assert reloaded.size == (256, 256)
140+
141+
assert_image_similar(im, reloaded, 3.69)
142+
143+
112144
@pytest.mark.parametrize(
113145
"image_path",
114146
(
@@ -368,9 +400,9 @@ def test_not_implemented(test_file: str) -> None:
368400

369401

370402
def test_save_unsupported_mode(tmp_path: Path) -> None:
371-
out = str(tmp_path / "temp.dds")
403+
out = tmp_path / "temp.dds"
372404
im = hopper("HSV")
373-
with pytest.raises(OSError):
405+
with pytest.raises(OSError, match="cannot write mode HSV as DDS"):
374406
im.save(out)
375407

376408

@@ -384,10 +416,98 @@ def test_save_unsupported_mode(tmp_path: Path) -> None:
384416
],
385417
)
386418
def test_save(mode: str, test_file: str, tmp_path: Path) -> None:
387-
out = str(tmp_path / "temp.dds")
419+
out = tmp_path / "temp.dds"
388420
with Image.open(test_file) as im:
389421
assert im.mode == mode
390422
im.save(out)
391423

392-
with Image.open(out) as reloaded:
393-
assert_image_equal(im, reloaded)
424+
assert_image_equal_tofile(im, out)
425+
426+
427+
def test_save_unsupported_pixel_format(tmp_path: Path) -> None:
428+
out = tmp_path / "temp.dds"
429+
im = hopper()
430+
with pytest.raises(OSError, match="cannot write pixel format UNKNOWN"):
431+
im.save(out, pixel_format="UNKNOWN")
432+
433+
434+
def test_save_dxt1(tmp_path: Path) -> None:
435+
# RGB
436+
out = tmp_path / "temp.dds"
437+
with Image.open(TEST_FILE_DXT1) as im:
438+
im.convert("RGB").save(out, pixel_format="DXT1")
439+
assert_image_similar_tofile(im, out, 1.84)
440+
441+
# RGBA
442+
im_alpha = im.copy()
443+
im_alpha.putpixel((0, 0), (0, 0, 0, 0))
444+
im_alpha.save(out, pixel_format="DXT1")
445+
with Image.open(out) as reloaded:
446+
assert reloaded.getpixel((0, 0)) == (0, 0, 0, 0)
447+
448+
# L
449+
im_l = im.convert("L")
450+
im_l.save(out, pixel_format="DXT1")
451+
assert_image_similar_tofile(im_l.convert("RGBA"), out, 6.07)
452+
453+
# LA
454+
im_alpha.convert("LA").save(out, pixel_format="DXT1")
455+
with Image.open(out) as reloaded:
456+
assert reloaded.getpixel((0, 0)) == (0, 0, 0, 0)
457+
458+
459+
def test_save_dxt3(tmp_path: Path) -> None:
460+
# RGB
461+
out = tmp_path / "temp.dds"
462+
with Image.open(TEST_FILE_DXT3) as im:
463+
im_rgb = im.convert("RGB")
464+
im_rgb.save(out, pixel_format="DXT3")
465+
assert_image_similar_tofile(im_rgb.convert("RGBA"), out, 1.26)
466+
467+
# RGBA
468+
im.save(out, pixel_format="DXT3")
469+
assert_image_similar_tofile(im, out, 3.81)
470+
471+
# L
472+
im_l = im.convert("L")
473+
im_l.save(out, pixel_format="DXT3")
474+
assert_image_similar_tofile(im_l.convert("RGBA"), out, 5.89)
475+
476+
# LA
477+
im_la = im.convert("LA")
478+
im_la.save(out, pixel_format="DXT3")
479+
assert_image_similar_tofile(im_la.convert("RGBA"), out, 8.44)
480+
481+
482+
def test_save_dxt5(tmp_path: Path) -> None:
483+
# RGB
484+
out = tmp_path / "temp.dds"
485+
with Image.open(TEST_FILE_DXT1) as im:
486+
im.convert("RGB").save(out, pixel_format="DXT5")
487+
assert_image_similar_tofile(im, out, 1.84)
488+
489+
# RGBA
490+
with Image.open(TEST_FILE_DXT5) as im_rgba:
491+
im_rgba.save(out, pixel_format="DXT5")
492+
assert_image_similar_tofile(im_rgba, out, 3.69)
493+
494+
# L
495+
im_l = im.convert("L")
496+
im_l.save(out, pixel_format="DXT5")
497+
assert_image_similar_tofile(im_l.convert("RGBA"), out, 6.07)
498+
499+
# LA
500+
im_la = im_rgba.convert("LA")
501+
im_la.save(out, pixel_format="DXT5")
502+
assert_image_similar_tofile(im_la.convert("RGBA"), out, 8.32)
503+
504+
505+
def test_save_dx10_bc5(tmp_path: Path) -> None:
506+
out = tmp_path / "temp.dds"
507+
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
508+
im.save(out, pixel_format="BC5")
509+
assert_image_similar_tofile(im, out, 9.56)
510+
511+
im = hopper("L")
512+
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
513+
im.save(out, pixel_format="BC5")

0 commit comments

Comments
 (0)