Skip to content

Commit 1138ea5

Browse files
authored
Merge pull request #7921 from Yay295/testing
Fix ImagingAccess for I;16N on big-endian
2 parents 3823675 + a4080a7 commit 1138ea5

File tree

5 files changed

+64
-98
lines changed

5 files changed

+64
-98
lines changed

Tests/helper.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,33 @@
2929
uploader = "github_actions"
3030

3131

32+
modes = (
33+
"1",
34+
"L",
35+
"LA",
36+
"La",
37+
"P",
38+
"PA",
39+
"F",
40+
"I",
41+
"I;16",
42+
"I;16L",
43+
"I;16B",
44+
"I;16N",
45+
"RGB",
46+
"RGBA",
47+
"RGBa",
48+
"RGBX",
49+
"BGR;15",
50+
"BGR;16",
51+
"BGR;24",
52+
"CMYK",
53+
"YCbCr",
54+
"HSV",
55+
"LAB",
56+
)
57+
58+
3259
def upload(a: Image.Image, b: Image.Image) -> str | None:
3360
if uploader == "show":
3461
# local img.show for errors.

Tests/test_image.py

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,43 +28,16 @@
2828
assert_image_similar_tofile,
2929
assert_not_all_same,
3030
hopper,
31+
is_big_endian,
3132
is_win32,
3233
mark_if_feature_version,
34+
modes,
3335
skip_unless_feature,
3436
)
3537

36-
# name, pixel size
37-
image_modes = (
38-
("1", 1),
39-
("L", 1),
40-
("LA", 4),
41-
("La", 4),
42-
("P", 1),
43-
("PA", 4),
44-
("F", 4),
45-
("I", 4),
46-
("I;16", 2),
47-
("I;16L", 2),
48-
("I;16B", 2),
49-
("I;16N", 2),
50-
("RGB", 4),
51-
("RGBA", 4),
52-
("RGBa", 4),
53-
("RGBX", 4),
54-
("BGR;15", 2),
55-
("BGR;16", 2),
56-
("BGR;24", 3),
57-
("CMYK", 4),
58-
("YCbCr", 4),
59-
("HSV", 4),
60-
("LAB", 4),
61-
)
62-
63-
image_mode_names = [name for name, _ in image_modes]
64-
6538

6639
class TestImage:
67-
@pytest.mark.parametrize("mode", image_mode_names)
40+
@pytest.mark.parametrize("mode", modes)
6841
def test_image_modes_success(self, mode: str) -> None:
6942
Image.new(mode, (1, 1))
7043

@@ -1045,15 +1018,15 @@ def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None:
10451018

10461019

10471020
class TestImageBytes:
1048-
@pytest.mark.parametrize("mode", image_mode_names)
1021+
@pytest.mark.parametrize("mode", modes)
10491022
def test_roundtrip_bytes_constructor(self, mode: str) -> None:
10501023
im = hopper(mode)
10511024
source_bytes = im.tobytes()
10521025

10531026
reloaded = Image.frombytes(mode, im.size, source_bytes)
10541027
assert reloaded.tobytes() == source_bytes
10551028

1056-
@pytest.mark.parametrize("mode", image_mode_names)
1029+
@pytest.mark.parametrize("mode", modes)
10571030
def test_roundtrip_bytes_method(self, mode: str) -> None:
10581031
im = hopper(mode)
10591032
source_bytes = im.tobytes()
@@ -1062,12 +1035,11 @@ def test_roundtrip_bytes_method(self, mode: str) -> None:
10621035
reloaded.frombytes(source_bytes)
10631036
assert reloaded.tobytes() == source_bytes
10641037

1065-
@pytest.mark.parametrize(("mode", "pixelsize"), image_modes)
1066-
def test_getdata_putdata(self, mode: str, pixelsize: int) -> None:
1067-
im = Image.new(mode, (2, 2))
1068-
source_bytes = bytes(range(im.width * im.height * pixelsize))
1069-
im.frombytes(source_bytes)
1070-
1038+
@pytest.mark.parametrize("mode", modes)
1039+
def test_getdata_putdata(self, mode: str) -> None:
1040+
if is_big_endian and mode == "BGR;15":
1041+
pytest.xfail("Known failure of BGR;15 on big-endian")
1042+
im = hopper(mode)
10711043
reloaded = Image.new(mode, im.size)
10721044
reloaded.putdata(im.getdata())
10731045
assert_image_equal(im, reloaded)

Tests/test_image_access.py

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from PIL import Image
1212

13-
from .helper import assert_image_equal, hopper, is_win32
13+
from .helper import assert_image_equal, hopper, is_win32, modes
1414

1515
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
1616
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
@@ -33,7 +33,7 @@
3333

3434

3535
class AccessTest:
36-
# initial value
36+
# Initial value
3737
_init_cffi_access = Image.USE_CFFI_ACCESS
3838
_need_cffi_access = False
3939

@@ -138,8 +138,8 @@ def color(mode: str) -> int | tuple[int, ...]:
138138
if bands == 1:
139139
return 1
140140
if mode in ("BGR;15", "BGR;16"):
141-
# These modes have less than 8 bits per band
142-
# So (1, 2, 3) cannot be roundtripped
141+
# These modes have less than 8 bits per band,
142+
# so (1, 2, 3) cannot be roundtripped.
143143
return (16, 32, 49)
144144
return tuple(range(1, bands + 1))
145145

@@ -151,7 +151,7 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None:
151151
self.color(mode) if expected_color_int is None else expected_color_int
152152
)
153153

154-
# check putpixel
154+
# Check putpixel
155155
im = Image.new(mode, (1, 1), None)
156156
im.putpixel((0, 0), expected_color)
157157
actual_color = im.getpixel((0, 0))
@@ -160,74 +160,52 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None:
160160
f"expected {expected_color} got {actual_color}"
161161
)
162162

163-
# check putpixel negative index
163+
# Check putpixel negative index
164164
im.putpixel((-1, -1), expected_color)
165165
actual_color = im.getpixel((-1, -1))
166166
assert actual_color == expected_color, (
167167
f"put/getpixel roundtrip negative index failed for mode {mode}, "
168168
f"expected {expected_color} got {actual_color}"
169169
)
170170

171-
# Check 0
171+
# Check 0x0 image with None initial color
172172
im = Image.new(mode, (0, 0), None)
173173
assert im.load() is not None
174-
175174
error = ValueError if self._need_cffi_access else IndexError
176175
with pytest.raises(error):
177176
im.putpixel((0, 0), expected_color)
178177
with pytest.raises(error):
179178
im.getpixel((0, 0))
180-
# Check 0 negative index
179+
# Check negative index
181180
with pytest.raises(error):
182181
im.putpixel((-1, -1), expected_color)
183182
with pytest.raises(error):
184183
im.getpixel((-1, -1))
185184

186-
# check initial color
185+
# Check initial color
187186
im = Image.new(mode, (1, 1), expected_color)
188187
actual_color = im.getpixel((0, 0))
189188
assert actual_color == expected_color, (
190189
f"initial color failed for mode {mode}, "
191190
f"expected {expected_color} got {actual_color}"
192191
)
193192

194-
# check initial color negative index
193+
# Check initial color negative index
195194
actual_color = im.getpixel((-1, -1))
196195
assert actual_color == expected_color, (
197196
f"initial color failed with negative index for mode {mode}, "
198197
f"expected {expected_color} got {actual_color}"
199198
)
200199

201-
# Check 0
200+
# Check 0x0 image with initial color
202201
im = Image.new(mode, (0, 0), expected_color)
203202
with pytest.raises(error):
204203
im.getpixel((0, 0))
205-
# Check 0 negative index
204+
# Check negative index
206205
with pytest.raises(error):
207206
im.getpixel((-1, -1))
208207

209-
@pytest.mark.parametrize(
210-
"mode",
211-
(
212-
"1",
213-
"L",
214-
"LA",
215-
"I",
216-
"I;16",
217-
"I;16B",
218-
"F",
219-
"P",
220-
"PA",
221-
"BGR;15",
222-
"BGR;16",
223-
"BGR;24",
224-
"RGB",
225-
"RGBA",
226-
"RGBX",
227-
"CMYK",
228-
"YCbCr",
229-
),
230-
)
208+
@pytest.mark.parametrize("mode", modes)
231209
def test_basic(self, mode: str) -> None:
232210
self.check(mode)
233211

@@ -238,7 +216,7 @@ def test_list(self) -> None:
238216
@pytest.mark.parametrize("mode", ("I;16", "I;16B"))
239217
@pytest.mark.parametrize("expected_color", (2**15 - 1, 2**15, 2**15 + 1, 2**16 - 1))
240218
def test_signedness(self, mode: str, expected_color: int) -> None:
241-
# see https://github.com/python-pillow/Pillow/issues/452
219+
# See https://github.com/python-pillow/Pillow/issues/452
242220
# pixelaccess is using signed int* instead of uint*
243221
self.check(mode, expected_color)
244222

@@ -298,13 +276,6 @@ def test_get_vs_c(self) -> None:
298276
im = Image.new(mode, (10, 10), 40000)
299277
self._test_get_access(im)
300278

301-
# These don't actually appear to be modes that I can actually make,
302-
# as unpack sets them directly into the I mode.
303-
# im = Image.new('I;32L', (10, 10), -2**10)
304-
# self._test_get_access(im)
305-
# im = Image.new('I;32B', (10, 10), 2**10)
306-
# self._test_get_access(im)
307-
308279
def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> None:
309280
"""Are we writing the correct bits into the image?
310281
@@ -336,23 +307,18 @@ def test_set_vs_c(self) -> None:
336307
self._test_set_access(hopper("LA"), (128, 128))
337308
self._test_set_access(hopper("1"), 255)
338309
self._test_set_access(hopper("P"), 128)
339-
# self._test_set_access(i, (128, 128)) #PA -- undone how to make
310+
self._test_set_access(hopper("PA"), (128, 128))
340311
self._test_set_access(hopper("F"), 1024.0)
341312

342313
for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
343314
im = Image.new(mode, (10, 10), 40000)
344315
self._test_set_access(im, 45000)
345316

346-
# im = Image.new('I;32L', (10, 10), -(2**10))
347-
# self._test_set_access(im, -(2**13)+1)
348-
# im = Image.new('I;32B', (10, 10), 2**10)
349-
# self._test_set_access(im, 2**13-1)
350-
351317
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
352318
def test_not_implemented(self) -> None:
353319
assert PyAccess.new(hopper("BGR;15")) is None
354320

355-
# ref https://github.com/python-pillow/Pillow/pull/2009
321+
# Ref https://github.com/python-pillow/Pillow/pull/2009
356322
def test_reference_counting(self) -> None:
357323
size = 10
358324

@@ -361,7 +327,7 @@ def test_reference_counting(self) -> None:
361327
with pytest.warns(DeprecationWarning):
362328
px = Image.new("L", (size, 1), 0).load()
363329
for i in range(size):
364-
# pixels can contain garbage if image is released
330+
# Pixels can contain garbage if image is released
365331
assert px[i, 0] == 0
366332

367333
@pytest.mark.parametrize("mode", ("P", "PA"))
@@ -478,7 +444,7 @@ def test_embeddable(self) -> None:
478444
env = os.environ.copy()
479445
env["PATH"] = sys.prefix + ";" + env["PATH"]
480446

481-
# do not display the Windows Error Reporting dialog
447+
# Do not display the Windows Error Reporting dialog
482448
getattr(ctypes, "windll").kernel32.SetErrorMode(0x0002)
483449

484450
process = subprocess.Popen(["embed_pil.exe"], env=env)

Tests/test_lib_pack.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ def test_I(self) -> None:
216216
)
217217

218218
def test_I16(self) -> None:
219-
self.assert_pack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605)
219+
if sys.byteorder == "little":
220+
self.assert_pack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605)
221+
else:
222+
self.assert_pack("I;16N", "I;16N", 2, 0x0102, 0x0304, 0x0506)
220223

221224
def test_F_float(self) -> None:
222225
self.assert_pack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34)

src/libImaging/Access.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,6 @@ get_pixel_16B(Imaging im, int x, int y, void *color) {
8181
#endif
8282
}
8383

84-
static void
85-
get_pixel_16(Imaging im, int x, int y, void *color) {
86-
UINT8 *in = (UINT8 *)&im->image[y][x + x];
87-
memcpy(color, in, sizeof(UINT16));
88-
}
89-
9084
static void
9185
get_pixel_BGR15(Imaging im, int x, int y, void *color) {
9286
UINT8 *in = (UINT8 *)&im->image8[y][x * 2];
@@ -207,7 +201,11 @@ ImagingAccessInit() {
207201
ADD("I;16", get_pixel_16L, put_pixel_16L);
208202
ADD("I;16L", get_pixel_16L, put_pixel_16L);
209203
ADD("I;16B", get_pixel_16B, put_pixel_16B);
210-
ADD("I;16N", get_pixel_16, put_pixel_16L);
204+
#ifdef WORDS_BIGENDIAN
205+
ADD("I;16N", get_pixel_16B, put_pixel_16B);
206+
#else
207+
ADD("I;16N", get_pixel_16L, put_pixel_16L);
208+
#endif
211209
ADD("I;32L", get_pixel_32L, put_pixel_32L);
212210
ADD("I;32B", get_pixel_32B, put_pixel_32B);
213211
ADD("F", get_pixel_32, put_pixel_32);

0 commit comments

Comments
 (0)