Skip to content

Commit 33eaf5f

Browse files
committed
Implement correct draw functions for 16-bpp modes
1 parent 3ef3339 commit 33eaf5f

2 files changed

Lines changed: 90 additions & 38 deletions

File tree

docs/releasenotes/13.0.0.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ Two new filters are available for :py:meth:`~PIL.Image.Image.resize` and
119119
Other changes
120120
=============
121121

122+
Drawing on I;16B images
123+
^^^^^^^^^^^^^^^^^^^^^^^
124+
125+
When drawing on an ``I;16B`` image, some drawing methods wrote in the wrong byte order.
126+
All drawing operations now use the byte order of the image mode.
127+
122128
Python 3.15
123129
^^^^^^^^^^^
124130

src/libImaging/Draw.c

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,36 @@
4141
#define FLOOR(v) ((v) >= 0.0 ? (int)(v) : (int)floor(v))
4242

4343
#define INK8(ink) (*(UINT8 *)ink)
44-
#define INK16(ink) (*(UINT16 *)ink)
44+
45+
// True when the given I;16 mode stores its pixels most significant byte first.
46+
static inline int
47+
isModeI16BigEndian(const ModeID mode) {
48+
#ifdef WORDS_BIGENDIAN
49+
return mode == IMAGING_MODE_I_16B || mode == IMAGING_MODE_I_16N;
50+
#else
51+
return mode == IMAGING_MODE_I_16B;
52+
#endif
53+
}
54+
55+
// Convert getink()'s ink value into the image's storage order
56+
// and return in the native order so the drawing functions don't
57+
// need to care about it.
58+
static inline INT32
59+
ink16(Imaging im, const void *ink_) {
60+
const UINT8 *in = ink_;
61+
UINT8 out[2];
62+
UINT16 ink;
63+
64+
if (isModeI16BigEndian(im->mode)) {
65+
out[0] = in[1];
66+
out[1] = in[0];
67+
} else {
68+
out[0] = in[0];
69+
out[1] = in[1];
70+
}
71+
memcpy(&ink, out, sizeof(ink));
72+
return ink;
73+
}
4574

4675
/*
4776
* Rounds around zero (up=away from zero, down=towards zero)
@@ -68,17 +97,14 @@ typedef void (*hline_handler)(Imaging, int, int, int, int, Imaging);
6897
static inline void
6998
point8(Imaging im, int x, int y, int ink) {
7099
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
71-
if (isModeI16(im->mode)) {
72-
#ifdef WORDS_BIGENDIAN
73-
im->image8[y][x * 2] = (UINT8)(ink >> 8);
74-
im->image8[y][x * 2 + 1] = (UINT8)ink;
75-
#else
76-
im->image8[y][x * 2] = (UINT8)ink;
77-
im->image8[y][x * 2 + 1] = (UINT8)(ink >> 8);
78-
#endif
79-
} else {
80-
im->image8[y][x] = (UINT8)ink;
81-
}
100+
im->image8[y][x] = (UINT8)ink;
101+
}
102+
}
103+
104+
static inline void
105+
point16(Imaging im, int x, int y, int ink) {
106+
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
107+
((UINT16 *)im->image8[y])[x] = (UINT16)ink;
82108
}
83109
}
84110

@@ -116,33 +142,46 @@ hline8(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
116142
x1 = im->xsize - 1;
117143
}
118144
if (x0 <= x1) {
119-
int bigendian = -1;
120-
if (isModeI16(im->mode)) {
121-
bigendian =
122-
(
123-
#ifdef WORDS_BIGENDIAN
124-
im->mode == IMAGING_MODE_I_16 || im->mode == IMAGING_MODE_I_16L
125-
#else
126-
im->mode == IMAGING_MODE_I_16B
127-
#endif
128-
)
129-
? 1
130-
: 0;
145+
UINT8 *p = im->image8[y0];
146+
if (mask == NULL) {
147+
memset(p + x0, (UINT8)ink, (x1 - x0 + 1));
148+
} else {
149+
UINT8 *mask_row = mask->image8[y0];
150+
for (; x0 <= x1; x0++) {
151+
if (mask_row[x0]) {
152+
p[x0] = (UINT8)ink;
153+
}
154+
}
131155
}
132-
if (mask == NULL && bigendian == -1) {
133-
memset(im->image8[y0] + x0, (UINT8)ink, (x1 - x0 + 1));
156+
}
157+
}
158+
}
159+
160+
static inline void
161+
hline16(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
162+
if (y0 >= 0 && y0 < im->ysize) {
163+
if (x0 < 0) {
164+
x0 = 0;
165+
} else if (x0 >= im->xsize) {
166+
return;
167+
}
168+
if (x1 < 0) {
169+
return;
170+
} else if (x1 >= im->xsize) {
171+
x1 = im->xsize - 1;
172+
}
173+
if (x0 <= x1) {
174+
UINT16 *p = (UINT16 *)im->image8[y0];
175+
if (mask == NULL) {
176+
for (; x0 <= x1; x0++) {
177+
p[x0] = (UINT16)ink;
178+
}
134179
} else {
135-
UINT8 *p = im->image8[y0];
136-
while (x0 <= x1) {
137-
if (mask == NULL || mask->image8[y0][x0]) {
138-
if (bigendian == -1) {
139-
p[x0] = ink;
140-
} else {
141-
p[x0 * 2 + (bigendian ? 1 : 0)] = ink;
142-
p[x0 * 2 + (bigendian ? 0 : 1)] = ink >> 8;
143-
}
180+
UINT8 *mask_row = mask->image8[y0];
181+
for (; x0 <= x1; x0++) {
182+
if (mask_row[x0]) {
183+
p[x0] = (UINT16)ink;
144184
}
145-
x0++;
146185
}
147186
}
148187
}
@@ -275,6 +314,11 @@ line8(Imaging im, int x0, int y0, int x1, int y1, int ink) {
275314
GEN_LINE(point8, hline8);
276315
}
277316

317+
static inline void
318+
line16(Imaging im, int x0, int y0, int x1, int y1, int ink) {
319+
GEN_LINE(point16, hline16);
320+
}
321+
278322
static inline void
279323
line32(Imaging im, int x0, int y0, int x1, int y1, int ink) {
280324
GEN_LINE(point32, hline32);
@@ -533,6 +577,7 @@ typedef struct {
533577
} DRAW;
534578

535579
DRAW draw8 = {point8, hline8, line8};
580+
DRAW draw16 = {point16, hline16, line16};
536581
DRAW draw32 = {point32, hline32, line32};
537582
DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba};
538583

@@ -542,10 +587,11 @@ DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba};
542587

543588
#define DRAWINIT() \
544589
if (im->image8) { \
545-
draw = &draw8; \
546590
if (isModeI16(im->mode)) { \
547-
ink = INK16(ink_); \
591+
draw = &draw16; \
592+
ink = ink16(im, ink_); \
548593
} else { \
594+
draw = &draw8; \
549595
ink = INK8(ink_); \
550596
} \
551597
} else { \

0 commit comments

Comments
 (0)