use FT_Bitmap_Convert instead of reimplementing the wheel

This commit is contained in:
nulano 2020-09-23 11:48:24 +02:00
parent 55db572467
commit bf529303de

View File

@ -25,6 +25,7 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include FT_GLYPH_H #include FT_GLYPH_H
#include FT_BITMAP_H
#include FT_STROKER_H #include FT_STROKER_H
#include FT_MULTIPLE_MASTERS_H #include FT_MULTIPLE_MASTERS_H
#include FT_SFNT_NAMES_H #include FT_SFNT_NAMES_H
@ -837,14 +838,17 @@ font_render(FontObject* self, PyObject* args)
FT_Glyph glyph; FT_Glyph glyph;
FT_GlyphSlot glyph_slot; FT_GlyphSlot glyph_slot;
FT_Bitmap bitmap; FT_Bitmap bitmap;
FT_Bitmap bitmap_converted; /* initialized lazily, for non-8bpp fonts */
FT_BitmapGlyph bitmap_glyph; FT_BitmapGlyph bitmap_glyph;
FT_Stroker stroker = NULL; FT_Stroker stroker = NULL;
int bitmap_converted_ready = 0; /* has bitmap_converted been initialized */
GlyphInfo *glyph_info = NULL; /* computed text layout */ GlyphInfo *glyph_info = NULL; /* computed text layout */
size_t i, count; /* glyph_info index and length */ size_t i, count; /* glyph_info index and length */
int xx, yy; /* pixel offset of current glyph bitmap */ int xx, yy; /* pixel offset of current glyph bitmap */
int x0, x1; /* horizontal bounds of glyph bitmap to copy */ int x0, x1; /* horizontal bounds of glyph bitmap to copy */
unsigned int bitmap_y; /* glyph bitmap y index */ unsigned int bitmap_y; /* glyph bitmap y index */
unsigned char *source; /* glyph bitmap source buffer */ unsigned char *source; /* glyph bitmap source buffer */
unsigned char convert_scale; /* scale factor for non-8bpp bitmaps */
Imaging im; Imaging im;
Py_ssize_t id; Py_ssize_t id;
int horizontal_dir; /* is primary axis horizontal? */ int horizontal_dir; /* is primary axis horizontal? */
@ -980,6 +984,49 @@ font_render(FontObject* self, PyObject* args)
yy = -(py + glyph_slot->bitmap_top); yy = -(py + glyph_slot->bitmap_top);
} }
/* 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;
#ifdef FT_LOAD_COLOR
case FT_PIXEL_MODE_BGRA:
if (color) {
break;
}
/* we didn't ask for color, fall through to default */
#endif
default:
PyErr_SetString(PyExc_IOError, "unsupported bitmap pixel mode");
goto glyph_error;
}
/* clip glyph bitmap width to target image bounds */ /* clip glyph bitmap width to target image bounds */
x0 = 0; x0 = 0;
x1 = bitmap.width; x1 = bitmap.width;
@ -994,7 +1041,9 @@ font_render(FontObject* self, PyObject* args)
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) {
/* clip glyph bitmap height to target image bounds */ /* clip glyph bitmap height to target image bounds */
if (yy >= 0 && yy < im->ysize) { if (yy >= 0 && yy < im->ysize) {
// blend this glyph into the buffer /* blend this glyph into the buffer */
int k;
unsigned char v;
unsigned char* target; unsigned char* target;
if (color) { if (color) {
/* target[RGB] returns the color, target[A] returns the mask */ /* target[RGB] returns the color, target[A] returns the mask */
@ -1005,8 +1054,7 @@ font_render(FontObject* self, PyObject* args)
} }
#ifdef FT_LOAD_COLOR #ifdef FT_LOAD_COLOR
if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
// paste color glyph /* paste color glyph */
int k;
for (k = x0; k < x1; k++) { for (k = x0; k < x1; k++) {
if (target[k * 4 + 3] < source[k * 4 + 3]) { if (target[k * 4 + 3] < source[k * 4 + 3]) {
/* unpremultiply BGRa to RGBA */ /* unpremultiply BGRa to RGBA */
@ -1018,62 +1066,28 @@ font_render(FontObject* self, PyObject* args)
} }
} else } else
#endif #endif
// handle 8bpp separately for performance
if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
int k;
if (color) { if (color) {
for (k = x0; k < x1; k++) { for (k = x0; k < x1; k++) {
if (target[k * 4 + 3] < source[k]) { v = source[k] * convert_scale;
target[k * 4 + 0] = (unsigned char) (foreground_ink); if (target[k * 4 + 3] < v) {
target[k * 4 + 1] = (unsigned char) (foreground_ink >> 8); target[k * 4 + 0] = (unsigned char)(foreground_ink);
target[k * 4 + 2] = (unsigned char) (foreground_ink >> 16); target[k * 4 + 1] = (unsigned char)(foreground_ink >> 8);
target[k * 4 + 3] = source[k]; target[k * 4 + 2] = (unsigned char)(foreground_ink >> 16);
target[k * 4 + 3] = v;
} }
} }
} else { } else {
for (k = x0; k < x1; k++) { for (k = x0; k < x1; k++) {
if (target[k] < source[k]) { v = source[k] * convert_scale;
target[k] = source[k];
}
}
}
} else {
int k, v, m, a, b;
switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
a = 3;
b = 7;
m = 0x80;
break;
case FT_PIXEL_MODE_GRAY2:
a = 2;
b = 3;
m = 0xC0;
break;
case FT_PIXEL_MODE_GRAY4:
a = 1;
b = 1;
m = 0xF0;
break;
default:
PyErr_SetString(PyExc_IOError, "unsupported bitmap pixel mode");
return NULL;
}
for (k = x0; k < x1; k++) {
v = CLIP8(255 * ((source[k >> a] << (k & b)) & m) / m);
if (color) {
if (target[k * 4 + 3] < v) {
target[k * 4 + 0] = (unsigned char) foreground_ink;
target[k * 4 + 1] = (unsigned char) (foreground_ink >> 8);
target[k * 4 + 2] = (unsigned char) (foreground_ink >> 16);
target[k * 4 + 3] = v;
}
} else {
if (target[k] < v) { if (target[k] < v) {
target[k] = v; target[k] = v;
} }
} }
} }
} else {
PyErr_SetString(PyExc_IOError, "unsupported bitmap pixel mode");
goto glyph_error;
} }
} }
source += bitmap.pitch; source += bitmap.pitch;
@ -1085,9 +1099,23 @@ font_render(FontObject* self, PyObject* args)
} }
} }
if (bitmap_converted_ready) {
FT_Bitmap_Done(library, &bitmap_converted);
}
FT_Stroker_Done(stroker); FT_Stroker_Done(stroker);
PyMem_Del(glyph_info); PyMem_Del(glyph_info);
Py_RETURN_NONE; Py_RETURN_NONE;
glyph_error:
if (stroker != NULL) {
FT_Done_Glyph(glyph);
}
if (bitmap_converted_ready) {
FT_Bitmap_Done(library, &bitmap_converted);
}
FT_Stroker_Done(stroker);
PyMem_Del(glyph_info);
return NULL;
} }
#if FREETYPE_MAJOR > 2 ||\ #if FREETYPE_MAJOR > 2 ||\