diff --git a/Tests/test_font_crash.py b/Tests/test_font_crash.py index b82340ef70b..c394551f590 100644 --- a/Tests/test_font_crash.py +++ b/Tests/test_font_crash.py @@ -1,10 +1,8 @@ from __future__ import annotations -import pytest - from PIL import Image, ImageDraw, ImageFont -from .helper import skip_unless_feature +from .helper import skip_unless_feature_version class TestFontCrash: @@ -17,8 +15,7 @@ def _fuzz_font(self, font: ImageFont.FreeTypeFont) -> None: draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2) draw.text((10, 10), "Test Text", font=font, fill="#000") - @skip_unless_feature("freetype2") + @skip_unless_feature_version("freetype2", "2.12.0") def test_segfault(self) -> None: - with pytest.raises(OSError): - font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784") - self._fuzz_font(font) + font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784") + self._fuzz_font(font) diff --git a/src/_imagingft.c b/src/_imagingft.c index 8bef45baf87..0de07af16cb 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -1031,130 +1031,99 @@ font_render(FontObject *self, PyObject *args) { yy = -(py + glyph_slot->bitmap_top); } - // Null buffer, is dereferenced in FT_Bitmap_Convert - if (!bitmap.buffer && bitmap.rows) { - PyErr_SetString(PyExc_OSError, "Bitmap missing for glyph"); - goto glyph_error; - } - - /* convert non-8bpp bitmaps */ - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: - convert_scale = 255; - break; - case FT_PIXEL_MODE_GRAY2: - convert_scale = 255 / 3; - break; - case FT_PIXEL_MODE_GRAY4: - convert_scale = 255 / 15; - break; - default: - convert_scale = 1; - } - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: - case FT_PIXEL_MODE_GRAY2: - case FT_PIXEL_MODE_GRAY4: - if (!bitmap_converted_ready) { - FT_Bitmap_Init(&bitmap_converted); - bitmap_converted_ready = 1; - } - error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1); - if (error) { - geterror(error); - goto glyph_error; - } - bitmap = bitmap_converted; - /* bitmap is now FT_PIXEL_MODE_GRAY, fall through */ - case FT_PIXEL_MODE_GRAY: - break; - case FT_PIXEL_MODE_BGRA: - if (color) { + if (bitmap.buffer) { + /* convert non-8bpp bitmaps */ + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + convert_scale = 255; break; - } - /* we didn't ask for color, fall through to default */ - default: - PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); - goto glyph_error; - } + case FT_PIXEL_MODE_GRAY2: + convert_scale = 255 / 3; + break; + case FT_PIXEL_MODE_GRAY4: + convert_scale = 255 / 15; + break; + default: + convert_scale = 1; + } + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + if (!bitmap_converted_ready) { + FT_Bitmap_Init(&bitmap_converted); + bitmap_converted_ready = 1; + } + error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1); + if (error) { + geterror(error); + goto glyph_error; + } + bitmap = bitmap_converted; + /* bitmap is now FT_PIXEL_MODE_GRAY, fall through */ + case FT_PIXEL_MODE_GRAY: + break; + case FT_PIXEL_MODE_BGRA: + if (color) { + break; + } + /* we didn't ask for color, fall through to default */ + default: + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); + goto glyph_error; + } - /* clip glyph bitmap width to target image bounds */ - x0 = 0; - x1 = bitmap.width; - if (xx < 0) { - x0 = -xx; - } - if (xx + x1 > im->xsize) { - x1 = im->xsize - xx; - } + /* clip glyph bitmap width to target image bounds */ + x0 = 0; + x1 = bitmap.width; + if (xx < 0) { + x0 = -xx; + } + if (xx + x1 > im->xsize) { + x1 = im->xsize - xx; + } - source = (unsigned char *)bitmap.buffer; - for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { - /* clip glyph bitmap height to target image bounds */ - if (yy >= 0 && yy < im->ysize) { - /* blend this glyph into the buffer */ - int k; - unsigned char *target; - unsigned int tmp; - if (color) { - /* target[RGB] returns the color, target[A] returns the mask */ - /* target bands get split again in ImageDraw.text */ - target = (unsigned char *)im->image[yy] + xx * 4; - } else { - target = im->image8[yy] + xx; - } - if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { - /* paste color glyph */ - for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k * 4 + 3]; - - /* paste only if source has data */ - if (src_alpha > 0) { - /* unpremultiply BGRa */ - int src_red = - CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); - int src_green = - CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); - int src_blue = - CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); - - /* blend required if target has data */ - if (target[k * 4 + 3] > 0) { - /* blend RGBA colors */ - target[k * 4 + 0] = - BLEND(src_alpha, target[k * 4 + 0], src_red, tmp); - target[k * 4 + 1] = - BLEND(src_alpha, target[k * 4 + 1], src_green, tmp); - target[k * 4 + 2] = - BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp); - target[k * 4 + 3] = CLIP8( - src_alpha + - MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp) - ); - } else { - /* paste unpremultiplied RGBA values */ - target[k * 4 + 0] = src_red; - target[k * 4 + 1] = src_green; - target[k * 4 + 2] = src_blue; - target[k * 4 + 3] = src_alpha; - } - } - } - } else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + source = (unsigned char *)bitmap.buffer; + for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { + /* clip glyph bitmap height to target image bounds */ + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + int k; + unsigned char *target; + unsigned int tmp; if (color) { - unsigned char *ink = (unsigned char *)&foreground_ink; + /* target[RGB] returns the color, target[A] returns the mask */ + /* target bands get split again in ImageDraw.text */ + target = (unsigned char *)im->image[yy] + xx * 4; + } else { + target = im->image8[yy] + xx; + } + if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + /* paste color glyph */ for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k] * convert_scale; + unsigned int src_alpha = source[k * 4 + 3]; + + /* paste only if source has data */ if (src_alpha > 0) { + /* unpremultiply BGRa */ + int src_red = + CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); + int src_green = + CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); + int src_blue = + CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); + + /* blend required if target has data */ if (target[k * 4 + 3] > 0) { + /* blend RGBA colors */ target[k * 4 + 0] = BLEND( - src_alpha, target[k * 4 + 0], ink[0], tmp + src_alpha, target[k * 4 + 0], src_red, tmp ); target[k * 4 + 1] = BLEND( - src_alpha, target[k * 4 + 1], ink[1], tmp + src_alpha, target[k * 4 + 1], src_green, tmp ); target[k * 4 + 2] = BLEND( - src_alpha, target[k * 4 + 2], ink[2], tmp + src_alpha, target[k * 4 + 2], src_blue, tmp ); target[k * 4 + 3] = CLIP8( src_alpha + @@ -1163,35 +1132,68 @@ font_render(FontObject *self, PyObject *args) { ) ); } else { - target[k * 4 + 0] = ink[0]; - target[k * 4 + 1] = ink[1]; - target[k * 4 + 2] = ink[2]; + /* paste unpremultiplied RGBA values */ + target[k * 4 + 0] = src_red; + target[k * 4 + 1] = src_green; + target[k * 4 + 2] = src_blue; target[k * 4 + 3] = src_alpha; } } } - } else { - for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k] * convert_scale; - if (src_alpha > 0) { - target[k] = - target[k] > 0 - ? CLIP8( - src_alpha + - MULDIV255( - target[k], (255 - src_alpha), tmp + } else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + if (color) { + unsigned char *ink = (unsigned char *)&foreground_ink; + for (k = x0; k < x1; k++) { + unsigned int src_alpha = source[k] * convert_scale; + if (src_alpha > 0) { + if (target[k * 4 + 3] > 0) { + target[k * 4 + 0] = BLEND( + src_alpha, target[k * 4 + 0], ink[0], tmp + ); + target[k * 4 + 1] = BLEND( + src_alpha, target[k * 4 + 1], ink[1], tmp + ); + target[k * 4 + 2] = BLEND( + src_alpha, target[k * 4 + 2], ink[2], tmp + ); + target[k * 4 + 3] = CLIP8( + src_alpha + MULDIV255( + target[k * 4 + 3], + (255 - src_alpha), + tmp + ) + ); + } else { + target[k * 4 + 0] = ink[0]; + target[k * 4 + 1] = ink[1]; + target[k * 4 + 2] = ink[2]; + target[k * 4 + 3] = src_alpha; + } + } + } + } else { + for (k = x0; k < x1; k++) { + unsigned int src_alpha = source[k] * convert_scale; + if (src_alpha > 0) { + target[k] = + target[k] > 0 + ? CLIP8( + src_alpha + + MULDIV255( + target[k], (255 - src_alpha), tmp + ) ) - ) - : src_alpha; + : src_alpha; + } } } + } else { + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); + goto glyph_error; } - } else { - PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); - goto glyph_error; } + source += bitmap.pitch; } - source += bitmap.pitch; } x += glyph_info[i].x_advance; y += glyph_info[i].y_advance;