Skip to content

Commit 6ab139e

Browse files
authored
If bitmap buffer is empty, do not render anything (#8324)
2 parents 46c529f + 94c3ee6 commit 6ab139e

2 files changed

Lines changed: 137 additions & 138 deletions

File tree

Tests/test_font_crash.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from __future__ import annotations
22

3-
import pytest
4-
53
from PIL import Image, ImageDraw, ImageFont
64

7-
from .helper import skip_unless_feature
5+
from .helper import skip_unless_feature_version
86

97

108
class TestFontCrash:
@@ -18,8 +16,7 @@ def _fuzz_font(self, font: ImageFont.FreeTypeFont) -> None:
1816
draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2)
1917
draw.text((10, 10), "Test Text", font=font, fill="#000")
2018

21-
@skip_unless_feature("freetype2")
19+
@skip_unless_feature_version("freetype2", "2.12.0")
2220
def test_segfault(self) -> None:
23-
with pytest.raises(OSError):
24-
font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784")
25-
self._fuzz_font(font)
21+
font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784")
22+
self._fuzz_font(font)

src/_imagingft.c

Lines changed: 133 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,130 +1042,99 @@ font_render(FontObject *self, PyObject *args) {
10421042
yy = -(py + glyph_slot->bitmap_top);
10431043
}
10441044

1045-
// Null buffer, is dereferenced in FT_Bitmap_Convert
1046-
if (!bitmap.buffer && bitmap.rows) {
1047-
PyErr_SetString(PyExc_OSError, "Bitmap missing for glyph");
1048-
goto glyph_error;
1049-
}
1050-
1051-
/* convert non-8bpp bitmaps */
1052-
switch (bitmap.pixel_mode) {
1053-
case FT_PIXEL_MODE_MONO:
1054-
convert_scale = 255;
1055-
break;
1056-
case FT_PIXEL_MODE_GRAY2:
1057-
convert_scale = 255 / 3;
1058-
break;
1059-
case FT_PIXEL_MODE_GRAY4:
1060-
convert_scale = 255 / 15;
1061-
break;
1062-
default:
1063-
convert_scale = 1;
1064-
}
1065-
switch (bitmap.pixel_mode) {
1066-
case FT_PIXEL_MODE_MONO:
1067-
case FT_PIXEL_MODE_GRAY2:
1068-
case FT_PIXEL_MODE_GRAY4:
1069-
if (!bitmap_converted_ready) {
1070-
FT_Bitmap_Init(&bitmap_converted);
1071-
bitmap_converted_ready = 1;
1072-
}
1073-
error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1);
1074-
if (error) {
1075-
geterror(error);
1076-
goto glyph_error;
1077-
}
1078-
bitmap = bitmap_converted;
1079-
/* bitmap is now FT_PIXEL_MODE_GRAY, fall through */
1080-
case FT_PIXEL_MODE_GRAY:
1081-
break;
1082-
case FT_PIXEL_MODE_BGRA:
1083-
if (color) {
1045+
if (bitmap.buffer) {
1046+
/* convert non-8bpp bitmaps */
1047+
switch (bitmap.pixel_mode) {
1048+
case FT_PIXEL_MODE_MONO:
1049+
convert_scale = 255;
10841050
break;
1085-
}
1086-
/* we didn't ask for color, fall through to default */
1087-
default:
1088-
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1089-
goto glyph_error;
1090-
}
1051+
case FT_PIXEL_MODE_GRAY2:
1052+
convert_scale = 255 / 3;
1053+
break;
1054+
case FT_PIXEL_MODE_GRAY4:
1055+
convert_scale = 255 / 15;
1056+
break;
1057+
default:
1058+
convert_scale = 1;
1059+
}
1060+
switch (bitmap.pixel_mode) {
1061+
case FT_PIXEL_MODE_MONO:
1062+
case FT_PIXEL_MODE_GRAY2:
1063+
case FT_PIXEL_MODE_GRAY4:
1064+
if (!bitmap_converted_ready) {
1065+
FT_Bitmap_Init(&bitmap_converted);
1066+
bitmap_converted_ready = 1;
1067+
}
1068+
error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1);
1069+
if (error) {
1070+
geterror(error);
1071+
goto glyph_error;
1072+
}
1073+
bitmap = bitmap_converted;
1074+
/* bitmap is now FT_PIXEL_MODE_GRAY, fall through */
1075+
case FT_PIXEL_MODE_GRAY:
1076+
break;
1077+
case FT_PIXEL_MODE_BGRA:
1078+
if (color) {
1079+
break;
1080+
}
1081+
/* we didn't ask for color, fall through to default */
1082+
default:
1083+
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1084+
goto glyph_error;
1085+
}
10911086

1092-
/* clip glyph bitmap width to target image bounds */
1093-
x0 = 0;
1094-
x1 = bitmap.width;
1095-
if (xx < 0) {
1096-
x0 = -xx;
1097-
}
1098-
if (xx + x1 > im->xsize) {
1099-
x1 = im->xsize - xx;
1100-
}
1087+
/* clip glyph bitmap width to target image bounds */
1088+
x0 = 0;
1089+
x1 = bitmap.width;
1090+
if (xx < 0) {
1091+
x0 = -xx;
1092+
}
1093+
if (xx + x1 > im->xsize) {
1094+
x1 = im->xsize - xx;
1095+
}
11011096

1102-
source = (unsigned char *)bitmap.buffer;
1103-
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
1104-
/* clip glyph bitmap height to target image bounds */
1105-
if (yy >= 0 && yy < im->ysize) {
1106-
/* blend this glyph into the buffer */
1107-
int k;
1108-
unsigned char *target;
1109-
unsigned int tmp;
1110-
if (color) {
1111-
/* target[RGB] returns the color, target[A] returns the mask */
1112-
/* target bands get split again in ImageDraw.text */
1113-
target = (unsigned char *)im->image[yy] + xx * 4;
1114-
} else {
1115-
target = im->image8[yy] + xx;
1116-
}
1117-
if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1118-
/* paste color glyph */
1119-
for (k = x0; k < x1; k++) {
1120-
unsigned int src_alpha = source[k * 4 + 3];
1121-
1122-
/* paste only if source has data */
1123-
if (src_alpha > 0) {
1124-
/* unpremultiply BGRa */
1125-
int src_red =
1126-
CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha);
1127-
int src_green =
1128-
CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha);
1129-
int src_blue =
1130-
CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha);
1131-
1132-
/* blend required if target has data */
1133-
if (target[k * 4 + 3] > 0) {
1134-
/* blend RGBA colors */
1135-
target[k * 4 + 0] =
1136-
BLEND(src_alpha, target[k * 4 + 0], src_red, tmp);
1137-
target[k * 4 + 1] =
1138-
BLEND(src_alpha, target[k * 4 + 1], src_green, tmp);
1139-
target[k * 4 + 2] =
1140-
BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp);
1141-
target[k * 4 + 3] = CLIP8(
1142-
src_alpha +
1143-
MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp)
1144-
);
1145-
} else {
1146-
/* paste unpremultiplied RGBA values */
1147-
target[k * 4 + 0] = src_red;
1148-
target[k * 4 + 1] = src_green;
1149-
target[k * 4 + 2] = src_blue;
1150-
target[k * 4 + 3] = src_alpha;
1151-
}
1152-
}
1153-
}
1154-
} else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1097+
source = (unsigned char *)bitmap.buffer;
1098+
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
1099+
/* clip glyph bitmap height to target image bounds */
1100+
if (yy >= 0 && yy < im->ysize) {
1101+
/* blend this glyph into the buffer */
1102+
int k;
1103+
unsigned char *target;
1104+
unsigned int tmp;
11551105
if (color) {
1156-
unsigned char *ink = (unsigned char *)&foreground_ink;
1106+
/* target[RGB] returns the color, target[A] returns the mask */
1107+
/* target bands get split again in ImageDraw.text */
1108+
target = (unsigned char *)im->image[yy] + xx * 4;
1109+
} else {
1110+
target = im->image8[yy] + xx;
1111+
}
1112+
if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1113+
/* paste color glyph */
11571114
for (k = x0; k < x1; k++) {
1158-
unsigned int src_alpha = source[k] * convert_scale;
1115+
unsigned int src_alpha = source[k * 4 + 3];
1116+
1117+
/* paste only if source has data */
11591118
if (src_alpha > 0) {
1119+
/* unpremultiply BGRa */
1120+
int src_red =
1121+
CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha);
1122+
int src_green =
1123+
CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha);
1124+
int src_blue =
1125+
CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha);
1126+
1127+
/* blend required if target has data */
11601128
if (target[k * 4 + 3] > 0) {
1129+
/* blend RGBA colors */
11611130
target[k * 4 + 0] = BLEND(
1162-
src_alpha, target[k * 4 + 0], ink[0], tmp
1131+
src_alpha, target[k * 4 + 0], src_red, tmp
11631132
);
11641133
target[k * 4 + 1] = BLEND(
1165-
src_alpha, target[k * 4 + 1], ink[1], tmp
1134+
src_alpha, target[k * 4 + 1], src_green, tmp
11661135
);
11671136
target[k * 4 + 2] = BLEND(
1168-
src_alpha, target[k * 4 + 2], ink[2], tmp
1137+
src_alpha, target[k * 4 + 2], src_blue, tmp
11691138
);
11701139
target[k * 4 + 3] = CLIP8(
11711140
src_alpha +
@@ -1174,35 +1143,68 @@ font_render(FontObject *self, PyObject *args) {
11741143
)
11751144
);
11761145
} else {
1177-
target[k * 4 + 0] = ink[0];
1178-
target[k * 4 + 1] = ink[1];
1179-
target[k * 4 + 2] = ink[2];
1146+
/* paste unpremultiplied RGBA values */
1147+
target[k * 4 + 0] = src_red;
1148+
target[k * 4 + 1] = src_green;
1149+
target[k * 4 + 2] = src_blue;
11801150
target[k * 4 + 3] = src_alpha;
11811151
}
11821152
}
11831153
}
1184-
} else {
1185-
for (k = x0; k < x1; k++) {
1186-
unsigned int src_alpha = source[k] * convert_scale;
1187-
if (src_alpha > 0) {
1188-
target[k] =
1189-
target[k] > 0
1190-
? CLIP8(
1191-
src_alpha +
1192-
MULDIV255(
1193-
target[k], (255 - src_alpha), tmp
1154+
} else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1155+
if (color) {
1156+
unsigned char *ink = (unsigned char *)&foreground_ink;
1157+
for (k = x0; k < x1; k++) {
1158+
unsigned int src_alpha = source[k] * convert_scale;
1159+
if (src_alpha > 0) {
1160+
if (target[k * 4 + 3] > 0) {
1161+
target[k * 4 + 0] = BLEND(
1162+
src_alpha, target[k * 4 + 0], ink[0], tmp
1163+
);
1164+
target[k * 4 + 1] = BLEND(
1165+
src_alpha, target[k * 4 + 1], ink[1], tmp
1166+
);
1167+
target[k * 4 + 2] = BLEND(
1168+
src_alpha, target[k * 4 + 2], ink[2], tmp
1169+
);
1170+
target[k * 4 + 3] = CLIP8(
1171+
src_alpha + MULDIV255(
1172+
target[k * 4 + 3],
1173+
(255 - src_alpha),
1174+
tmp
1175+
)
1176+
);
1177+
} else {
1178+
target[k * 4 + 0] = ink[0];
1179+
target[k * 4 + 1] = ink[1];
1180+
target[k * 4 + 2] = ink[2];
1181+
target[k * 4 + 3] = src_alpha;
1182+
}
1183+
}
1184+
}
1185+
} else {
1186+
for (k = x0; k < x1; k++) {
1187+
unsigned int src_alpha = source[k] * convert_scale;
1188+
if (src_alpha > 0) {
1189+
target[k] =
1190+
target[k] > 0
1191+
? CLIP8(
1192+
src_alpha +
1193+
MULDIV255(
1194+
target[k], (255 - src_alpha), tmp
1195+
)
11941196
)
1195-
)
1196-
: src_alpha;
1197+
: src_alpha;
1198+
}
11971199
}
11981200
}
1201+
} else {
1202+
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1203+
goto glyph_error;
11991204
}
1200-
} else {
1201-
PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode");
1202-
goto glyph_error;
12031205
}
1206+
source += bitmap.pitch;
12041207
}
1205-
source += bitmap.pitch;
12061208
}
12071209
x += glyph_info[i].x_advance;
12081210
y += glyph_info[i].y_advance;

0 commit comments

Comments
 (0)