add support for CBDT and embedded bitmaps in truetype fonts

This commit is contained in:
nulano 2020-03-29 10:31:10 +02:00
parent 82a28d12e2
commit 9151da162c
5 changed files with 86 additions and 45 deletions

View File

@ -105,6 +105,10 @@ jobs:
- name: Build dependencies / WebP - name: Build dependencies / WebP
if: steps.build-cache.outputs.cache-hit != 'true' if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libwebp.cmd" run: "& winbuild\\build\\build_dep_libwebp.cmd"
# for FreeType CBDT font support
- name: Build dependencies / libpng
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libpng.cmd"
- name: Build dependencies / FreeType - name: Build dependencies / FreeType
if: steps.build-cache.outputs.cache-hit != 'true' if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_freetype.cmd" run: "& winbuild\\build\\build_dep_freetype.cmd"

View File

@ -26,6 +26,7 @@ def test_similar():
im_outline = im_bitmap.copy() im_outline = im_bitmap.copy()
draw_bitmap = ImageDraw.Draw(im_bitmap) draw_bitmap = ImageDraw.Draw(im_bitmap)
draw_outline = ImageDraw.Draw(im_outline) draw_outline = ImageDraw.Draw(im_outline)
draw_outline.fontmode = "1" # disable anti-aliasing to match bitmap font
# Metrics are different on the bitmap and TTF fonts, # Metrics are different on the bitmap and TTF fonts,
# more so on some platforms and versions of FreeType than others. # more so on some platforms and versions of FreeType than others.

View File

@ -352,7 +352,7 @@ Methods
.. versionadded:: 6.2.0 .. versionadded:: 6.2.0
:param embedded_color: Whether to use embedded color info in COLR and CPAL tables. :param embedded_color: Whether to use font embedded color glyphs (COLR or CBDT).
.. versionadded:: 8.0.0 .. versionadded:: 8.0.0
@ -413,7 +413,7 @@ Methods
.. versionadded:: 6.2.0 .. versionadded:: 6.2.0
:param embedded_color: Whether to use embedded color info in COLR and CPAL tables. :param embedded_color: Whether to use font embedded color glyphs (COLR or CBDT).
.. versionadded:: 8.0.0 .. versionadded:: 8.0.0

View File

@ -564,7 +564,7 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObje
return 0; return 0;
} }
load_flags = FT_LOAD_NO_BITMAP; load_flags = FT_LOAD_DEFAULT;
if (mask) { if (mask) {
load_flags |= FT_LOAD_TARGET_MONO; load_flags |= FT_LOAD_TARGET_MONO;
} }
@ -663,10 +663,7 @@ font_getsize(FontObject* self, PyObject* args)
return NULL; return NULL;
} }
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 load_flags = FT_LOAD_DEFAULT;
* Yifu Yu<root@jackyyf.com>, 2014-10-15
*/
load_flags = FT_LOAD_NO_BITMAP;
if (mask) { if (mask) {
load_flags |= FT_LOAD_TARGET_MONO; load_flags |= FT_LOAD_TARGET_MONO;
} }
@ -903,9 +900,7 @@ font_render(FontObject* self, PyObject* args)
} }
im = (Imaging) id; im = (Imaging) id;
load_flags = FT_LOAD_DEFAULT;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
load_flags = FT_LOAD_NO_BITMAP;
if (mask) { if (mask) {
load_flags |= FT_LOAD_TARGET_MONO; load_flags |= FT_LOAD_TARGET_MONO;
} }
@ -1000,28 +995,33 @@ font_render(FontObject* self, PyObject* args)
/* 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
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 */
/* target bands get split again in ImageDraw.text */ /* target bands get split again in ImageDraw.text */
unsigned char *target = im->image[yy] + xx * 4; target = im->image[yy] + xx * 4;
} else {
target = im->image8[yy] + xx;
}
#ifdef FT_LOAD_COLOR #ifdef FT_LOAD_COLOR
if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
// paste color glyph // paste color glyph
int k; 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 */
target[k * 4 + 0] = CLIP8((255 * (int)source[k * 4 + 2]) / source[k * 4 + 3]); target[k * 4 + 0] = CLIP8((255 * (int)source[k * 4 + 2]) / source[k * 4 + 3]);
target[k * 4 + 1] = CLIP8((255 * (int)source[k * 4 + 1]) / source[k * 4 + 3]); target[k * 4 + 1] = CLIP8((255 * (int)source[k * 4 + 1]) / source[k * 4 + 3]);
target[k * 4 + 2] = CLIP8((255 * (int)source[k * 4 + 0]) / source[k * 4 + 3]); target[k * 4 + 2] = CLIP8((255 * (int)source[k * 4 + 0]) / source[k * 4 + 3]);
target[k * 4 + 3] = source[k * 4 + 3]; target[k * 4 + 3] = source[k * 4 + 3];
}
} }
} else }
} else
#endif #endif
{ // pixel_mode should be FT_PIXEL_MODE_GRAY // handle 8bpp separately for performance
// fill with ink if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
int k; int k;
if (color) {
for (k = x0; k < x1; k++) { for (k = x0; k < x1; k++) {
if (target[k * 4 + 3] < source[k]) { if (target[k * 4 + 3] < source[k]) {
target[k * 4 + 0] = (unsigned char) (foreground_ink); target[k * 4 + 0] = (unsigned char) (foreground_ink);
@ -1030,30 +1030,50 @@ font_render(FontObject* self, PyObject* args)
target[k * 4 + 3] = source[k]; target[k * 4 + 3] = source[k];
} }
} }
}
} else {
unsigned char *target = im->image8[yy] + xx;
if (mask) {
// use monochrome mask (on palette images, etc)
int j, k, m = 128;
for (j = k = 0; j < x1; j++) {
if (j >= x0 && (source[k] & m)) {
target[j] = 255;
}
if (!(m >>= 1)) {
m = 128;
k++;
}
}
} else { } else {
// use antialiased rendering
int k;
for (k = x0; k < x1; k++) { for (k = x0; k < x1; k++) {
if (target[k] < source[k]) { if (target[k] < source[k]) {
target[k] = source[k]; 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) {
target[k] = v;
}
}
}
} }
} }
source += bitmap.pitch; source += bitmap.pitch;

View File

@ -169,6 +169,20 @@ deps = {
], ],
"libs": [r"output\release-static\{architecture}\lib\*.lib"], "libs": [r"output\release-static\{architecture}\lib\*.lib"],
}, },
"libpng": {
"url": SF_MIRROR + "/project/libpng/libpng16/1.6.37/lpng1637.zip",
"filename": "lpng1637.zip",
"dir": "lpng1637",
"build": [
# lint: do not inline
cmd_cmake(("-DPNG_SHARED:BOOL=OFF", "-DPNG_TESTS:BOOL=OFF")),
cmd_nmake(target="clean"),
cmd_nmake(),
cmd_copy("libpng16_static.lib", "libpng16.lib"),
],
"headers": [r"png*.h"],
"libs": [r"libpng16.lib"],
},
"freetype": { "freetype": {
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.2.tar.gz", # noqa: E501 "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.2.tar.gz", # noqa: E501
"filename": "freetype-2.10.2.tar.gz", "filename": "freetype-2.10.2.tar.gz",
@ -181,8 +195,10 @@ deps = {
'<PropertyGroup Label="Globals">': '<PropertyGroup Label="Globals">\n <WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>', # noqa: E501 '<PropertyGroup Label="Globals">': '<PropertyGroup Label="Globals">\n <WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>', # noqa: E501
}, },
r"builds\windows\vc2010\freetype.user.props": { r"builds\windows\vc2010\freetype.user.props": {
"<UserDefines></UserDefines>": "<UserDefines>FT_CONFIG_OPTION_USE_HARFBUZZ</UserDefines>", # noqa: E501 "<UserDefines></UserDefines>": "<UserDefines>FT_CONFIG_OPTION_SYSTEM_ZLIB;FT_CONFIG_OPTION_USE_PNG;FT_CONFIG_OPTION_USE_HARFBUZZ</UserDefines>", # noqa: E501
"<UserIncludeDirectories></UserIncludeDirectories>": r"<UserIncludeDirectories>{dir_harfbuzz}\src</UserIncludeDirectories>", # noqa: E501 "<UserIncludeDirectories></UserIncludeDirectories>": r"<UserIncludeDirectories>{dir_harfbuzz}\src;{inc_dir}</UserIncludeDirectories>", # noqa: E501
"<UserLibraryDirectories></UserLibraryDirectories>": "<UserLibraryDirectories>{lib_dir}</UserLibraryDirectories>", # noqa: E501
"<UserDependencies></UserDependencies>": "<UserDependencies>zlib.lib;libpng16.lib</UserDependencies>", # noqa: E501
}, },
r"src/autofit/afshaper.c": { r"src/autofit/afshaper.c": {
# link against harfbuzz.lib once it becomes available # link against harfbuzz.lib once it becomes available