BLF: Refactor blf_glyph.c
Cleanup and Simplification of blf_glyph.c See D13095 for details. Differential Revision: https://developer.blender.org/D13095 Reviewed by Campbell Barton
This commit is contained in:
parent
6002914f14
commit
eddf5ad581
|
@ -55,27 +55,25 @@
|
|||
#include "BLI_math_vector.h"
|
||||
#include "BLI_strict_flags.h"
|
||||
|
||||
/* Find a glyph cache that matches a size, dpi, styles. */
|
||||
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
|
||||
{
|
||||
GlyphCacheBLF *p;
|
||||
|
||||
p = (GlyphCacheBLF *)font->cache.first;
|
||||
while (p) {
|
||||
if (p->size == size && p->dpi == dpi && (p->bold == ((font->flags & BLF_BOLD) != 0)) &&
|
||||
(p->italic == ((font->flags & BLF_ITALIC) != 0))) {
|
||||
return p;
|
||||
GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first;
|
||||
while (gc) {
|
||||
if (gc->size == size && gc->dpi == dpi && (gc->bold == ((font->flags & BLF_BOLD) != 0)) &&
|
||||
(gc->italic == ((font->flags & BLF_ITALIC) != 0))) {
|
||||
return gc;
|
||||
}
|
||||
p = p->next;
|
||||
gc = gc->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a new glyph cache for the current size, dpi, bold, italic. */
|
||||
/* Create a new glyph cache for the current size, dpi, styles. */
|
||||
GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
|
||||
{
|
||||
GlyphCacheBLF *gc;
|
||||
GlyphCacheBLF *gc = (GlyphCacheBLF *)MEM_callocN(sizeof(GlyphCacheBLF), "blf_glyph_cache_new");
|
||||
|
||||
gc = (GlyphCacheBLF *)MEM_callocN(sizeof(GlyphCacheBLF), "blf_glyph_cache_new");
|
||||
gc->next = NULL;
|
||||
gc->prev = NULL;
|
||||
gc->size = font->size;
|
||||
|
@ -138,34 +136,85 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc)
|
|||
MEM_freeN(gc);
|
||||
}
|
||||
|
||||
static GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c)
|
||||
/* Try to find a glyph in cache, return NULL if not found. */
|
||||
static GlyphBLF *blf_glyph_cache_find_glyph(GlyphCacheBLF *gc, uint charcode)
|
||||
{
|
||||
GlyphBLF *p;
|
||||
unsigned int key;
|
||||
if (charcode < GLYPH_ASCII_TABLE_SIZE) {
|
||||
return gc->glyph_ascii_table[charcode];
|
||||
}
|
||||
|
||||
key = blf_hash(c);
|
||||
p = gc->bucket[key].first;
|
||||
while (p) {
|
||||
if (p->c == c) {
|
||||
return p;
|
||||
GlyphBLF *g = gc->bucket[blf_hash(charcode)].first;
|
||||
while (g) {
|
||||
if (g->c == charcode) {
|
||||
return g;
|
||||
}
|
||||
p = p->next;
|
||||
g = g->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index)
|
||||
/* Add a rendered glyph to a cache. */
|
||||
static GlyphBLF *blf_glyph_cache_add_glyph(
|
||||
FontBLF *font, GlyphCacheBLF *gc, FT_GlyphSlot glyph, uint charcode, FT_UInt glyph_index)
|
||||
{
|
||||
GlyphBLF *g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get");
|
||||
g->c = charcode;
|
||||
g->idx = glyph_index;
|
||||
g->advance = ((float)glyph->advance.x) / 64.0f;
|
||||
g->advance_i = (int)g->advance;
|
||||
g->pos[0] = glyph->bitmap_left;
|
||||
g->pos[1] = glyph->bitmap_top;
|
||||
g->dims[0] = (int)glyph->bitmap.width;
|
||||
g->dims[1] = (int)glyph->bitmap.rows;
|
||||
g->pitch = glyph->bitmap.pitch;
|
||||
|
||||
FT_BBox bbox;
|
||||
FT_Outline_Get_CBox(&(glyph->outline), &bbox);
|
||||
g->box.xmin = ((float)bbox.xMin) / 64.0f;
|
||||
g->box.xmax = ((float)bbox.xMax) / 64.0f;
|
||||
g->box.ymin = ((float)bbox.yMin) / 64.0f;
|
||||
g->box.ymax = ((float)bbox.yMax) / 64.0f;
|
||||
|
||||
const int buffer_size = (int)(glyph->bitmap.width * glyph->bitmap.rows);
|
||||
if (buffer_size != 0) {
|
||||
if (font->flags & BLF_MONOCHROME) {
|
||||
/* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range. */
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
glyph->bitmap.buffer[i] = glyph->bitmap.buffer[i] ? 255 : 0;
|
||||
}
|
||||
}
|
||||
g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap");
|
||||
memcpy(g->bitmap, glyph->bitmap.buffer, (size_t)buffer_size);
|
||||
}
|
||||
|
||||
unsigned int key = blf_hash(g->c);
|
||||
BLI_addhead(&(gc->bucket[key]), g);
|
||||
if (charcode < GLYPH_ASCII_TABLE_SIZE) {
|
||||
gc->glyph_ascii_table[charcode] = g;
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/* Return a glyph index from a charcode. Not found returns zero, which is a valid
|
||||
* printable character (.notdef or "tofu"). Font is allowed to change here. */
|
||||
static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode)
|
||||
{
|
||||
FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode);
|
||||
/* TODO: If not found in this font, check others, update font pointer. */
|
||||
return glyph_index;
|
||||
}
|
||||
|
||||
/* Load a glyph into the glyph slot of a font's face object. */
|
||||
static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index)
|
||||
{
|
||||
int load_flags;
|
||||
int render_mode;
|
||||
|
||||
if (font->flags & BLF_MONOCHROME) {
|
||||
load_flags = FT_LOAD_TARGET_MONO;
|
||||
render_mode = FT_RENDER_MODE_MONO;
|
||||
}
|
||||
else {
|
||||
load_flags = FT_LOAD_NO_BITMAP;
|
||||
render_mode = FT_RENDER_MODE_NORMAL;
|
||||
if (font->flags & BLF_HINTING_NONE) {
|
||||
load_flags |= FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
|
||||
}
|
||||
|
@ -182,47 +231,30 @@ static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index)
|
|||
}
|
||||
}
|
||||
|
||||
FT_Error err = FT_Load_Glyph(font->face, glyph_index, load_flags);
|
||||
if (FT_Load_Glyph(font->face, glyph_index, load_flags) == 0) {
|
||||
return font->face->glyph;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert a glyph from outlines to a bitmap that we can display. */
|
||||
static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
|
||||
{
|
||||
int render_mode;
|
||||
|
||||
if (font->flags & BLF_MONOCHROME) {
|
||||
render_mode = FT_RENDER_MODE_MONO;
|
||||
}
|
||||
else {
|
||||
render_mode = FT_RENDER_MODE_NORMAL;
|
||||
}
|
||||
|
||||
/* Render the glyph curves to a bitmap. */
|
||||
FT_Error err = FT_Render_Glyph(glyph, render_mode);
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Do not oblique a font that is designed to be italic! */
|
||||
if (((font->flags & BLF_ITALIC) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_ITALIC) &&
|
||||
(font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
|
||||
/* For (fake) italic: a shear transform with a 6 degree angle. */
|
||||
FT_Matrix transform;
|
||||
transform.xx = 0x10000L;
|
||||
transform.yx = 0x00000L;
|
||||
transform.xy = 0x03000L;
|
||||
transform.yy = 0x10000L;
|
||||
FT_Outline_Transform(&font->face->glyph->outline, &transform);
|
||||
}
|
||||
|
||||
/* Do not embolden an already bold font! */
|
||||
if (((font->flags & BLF_BOLD) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_BOLD) &&
|
||||
(font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
|
||||
/* Strengthen the width more than the height. */
|
||||
const FT_Pos extra_x = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.x_scale) /
|
||||
14;
|
||||
const FT_Pos extra_y = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.y_scale) /
|
||||
28;
|
||||
FT_Outline_EmboldenXY(&font->face->glyph->outline, extra_x, extra_y);
|
||||
if ((font->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) == 0) {
|
||||
/* Need to increase advance, but not for fixed-width fonts. */
|
||||
font->face->glyph->advance.x += (FT_Pos)(((float)extra_x) * 1.05f);
|
||||
font->face->glyph->advance.y += extra_y;
|
||||
}
|
||||
else {
|
||||
/* Widened fixed-pitch font gets a nudge left. */
|
||||
FT_Outline_Translate(&font->face->glyph->outline, (extra_x / -2), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* get the glyph. */
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
err = FT_Render_Glyph(slot, render_mode);
|
||||
|
||||
FT_Bitmap tempbitmap;
|
||||
|
||||
if (font->flags & BLF_MONOCHROME) {
|
||||
|
@ -231,78 +263,163 @@ static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index)
|
|||
FT_Bitmap_New(&tempbitmap);
|
||||
|
||||
/* Does Blender use Pitch 1 always? It works so far */
|
||||
err += FT_Bitmap_Convert(font->ft_lib, &slot->bitmap, &tempbitmap, 1);
|
||||
err += FT_Bitmap_Copy(font->ft_lib, &tempbitmap, &slot->bitmap);
|
||||
err += FT_Bitmap_Convert(font->ft_lib, &glyph->bitmap, &tempbitmap, 1);
|
||||
err += FT_Bitmap_Copy(font->ft_lib, &tempbitmap, &glyph->bitmap);
|
||||
err += FT_Bitmap_Done(font->ft_lib, &tempbitmap);
|
||||
}
|
||||
|
||||
if (err || slot->format != FT_GLYPH_FORMAT_BITMAP) {
|
||||
if (err || glyph->format != FT_GLYPH_FORMAT_BITMAP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Convert a floating point value to a FreeType 16.16 fixed point value. */
|
||||
static FT_Fixed to_16dot16(double val)
|
||||
{
|
||||
return (FT_Fixed)(lround(val * 65536.0));
|
||||
}
|
||||
|
||||
/* Glyph weight by factor. -1 (min stroke width) <= 0 (normal) => 1 (max boldness). */
|
||||
static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool monospaced)
|
||||
{
|
||||
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
|
||||
/* Fake bold if the font does not have this variable axis. */
|
||||
const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM,
|
||||
glyph->face->size->metrics.x_scale);
|
||||
FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f);
|
||||
FT_Outline_EmboldenXY(&glyph->outline, change, change / 2);
|
||||
if (monospaced) {
|
||||
/* Widened fixed-pitch font needs a nudge left. */
|
||||
FT_Outline_Translate(&glyph->outline, change / -2, 0);
|
||||
}
|
||||
else {
|
||||
/* Need to increase advance. */
|
||||
glyph->advance.x += change;
|
||||
glyph->advance.y += change / 2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Glyph oblique by factor. -1 (max negative) <= 0 (no slant) => 1 (max positive).
|
||||
* Note that left-leaning italics are possibile in some RTL writing systems. */
|
||||
static bool blf_glyph_transform_slant(FT_GlyphSlot glyph, float factor)
|
||||
{
|
||||
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
|
||||
FT_Matrix transform = {to_16dot16(1), to_16dot16(factor / 2.0f), 0, to_16dot16(1)};
|
||||
FT_Outline_Transform(&glyph->outline, &transform);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Glyph width by factor. -1 (min width) <= 0 (normal) => 1 (max width). */
|
||||
static bool UNUSED_FUNCTION(blf_glyph_transform_width)(FT_GlyphSlot glyph, float factor)
|
||||
{
|
||||
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
|
||||
float scale = (factor * 0.4f) + 1.0f; /* 0.6f - 1.4f */
|
||||
FT_Matrix matrix = {to_16dot16(scale), 0, 0, to_16dot16(1)};
|
||||
FT_Outline_Transform(&glyph->outline, &matrix);
|
||||
glyph->advance.x = (FT_Pos)((double)glyph->advance.x * scale);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Transform glyph to fit nicely within a fixed column width. */
|
||||
static bool UNUSED_FUNCTION(blf_glyph_transform_monospace)(FT_GlyphSlot glyph, int width)
|
||||
{
|
||||
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
|
||||
int gwidth = (int)(glyph->linearHoriAdvance >> 16);
|
||||
if (gwidth > width) {
|
||||
float scale = (float)width / (float)gwidth;
|
||||
FT_Matrix matrix = {to_16dot16(scale), 0, 0, to_16dot16(1)};
|
||||
/* Narrowing all points also thins vertical strokes. */
|
||||
FT_Outline_Transform(&glyph->outline, &matrix);
|
||||
const FT_Pos extra_x = (int)((float)(gwidth - width) * 5.65f);
|
||||
/* Horizontally widen strokes to counteract narrowing. */
|
||||
FT_Outline_EmboldenXY(&glyph->outline, extra_x, 0);
|
||||
}
|
||||
else if (gwidth < width) {
|
||||
/* Narrow glyphs only need to be centered. */
|
||||
int nudge = (width - gwidth) / 2;
|
||||
FT_Outline_Translate(&glyph->outline, (FT_Pos)nudge * 64, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create and return a fully-rendered bitmap glyph. */
|
||||
static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
|
||||
FontBLF *glyph_font,
|
||||
FT_UInt glyph_index)
|
||||
{
|
||||
if (glyph_font != settings_font) {
|
||||
FT_Set_Char_Size(glyph_font->face,
|
||||
0,
|
||||
((FT_F26Dot6)(settings_font->size)) * 64,
|
||||
settings_font->dpi,
|
||||
settings_font->dpi);
|
||||
glyph_font->size = settings_font->size;
|
||||
glyph_font->dpi = settings_font->dpi;
|
||||
}
|
||||
|
||||
FT_GlyphSlot glyph = blf_glyph_load(glyph_font, glyph_index);
|
||||
if (!glyph) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((settings_font->flags & BLF_ITALIC) != 0) {
|
||||
/* 37.5% of maximum rightward slant results in 6 degree slope, matching italic
|
||||
* version (DejaVuSans-Oblique.ttf) of our current font. But a nice median when
|
||||
* checking others. Worth reevaluating if we change default font. We could also
|
||||
* narrow the glyph slightly as most italics do, but this one does not. */
|
||||
blf_glyph_transform_slant(glyph, 0.375f);
|
||||
}
|
||||
|
||||
if ((settings_font->flags & BLF_BOLD) != 0) {
|
||||
/* 70% of maximum weight results in the same amount of boldness and horizontal
|
||||
* expansion as the bold version (DejaVuSans-Bold.ttf) of our default font.
|
||||
* Worth reevaluating if we change default font. */
|
||||
blf_glyph_transform_weight(glyph, 0.7f, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
|
||||
}
|
||||
|
||||
if (blf_glyph_render_bitmap(glyph_font, glyph)) {
|
||||
return glyph;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create (or load from cache) a fully-rendered bitmap glyph. */
|
||||
GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode)
|
||||
{
|
||||
GlyphBLF *g = (charcode < GLYPH_ASCII_TABLE_SIZE) ? (gc->glyph_ascii_table)[charcode] :
|
||||
blf_glyph_search(gc, charcode);
|
||||
GlyphBLF *g = blf_glyph_cache_find_glyph(gc, charcode);
|
||||
if (g) {
|
||||
return g;
|
||||
}
|
||||
|
||||
FT_UInt glyph_index = FT_Get_Char_Index(font->face, charcode);
|
||||
/* Glyph might not come from the initial font. */
|
||||
FontBLF *font_with_glyph = font;
|
||||
FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode);
|
||||
|
||||
if (!blf_glyph_render(font, glyph_index)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
|
||||
/* glyphs are dynamically created as needed by font rendering. this means that
|
||||
/* Glyphs are dynamically created as needed by font rendering. this means that
|
||||
* to make font rendering thread safe we have to do locking here. note that this
|
||||
* must be a lock for the whole library and not just per font, because the font
|
||||
* renderer uses a shared buffer internally */
|
||||
BLI_spin_lock(font->ft_lib_mutex);
|
||||
* renderer uses a shared buffer internally. */
|
||||
BLI_spin_lock(font_with_glyph->ft_lib_mutex);
|
||||
|
||||
g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get");
|
||||
g->c = charcode;
|
||||
g->idx = glyph_index;
|
||||
g->advance = ((float)slot->advance.x) / 64.0f;
|
||||
g->advance_i = (int)g->advance;
|
||||
g->pos[0] = slot->bitmap_left;
|
||||
g->pos[1] = slot->bitmap_top;
|
||||
g->dims[0] = (int)slot->bitmap.width;
|
||||
g->dims[1] = (int)slot->bitmap.rows;
|
||||
g->pitch = slot->bitmap.pitch;
|
||||
FT_GlyphSlot glyph = blf_glyph_render(font, font_with_glyph, glyph_index);
|
||||
|
||||
FT_BBox bbox;
|
||||
FT_Outline_Get_CBox(&(slot->outline), &bbox);
|
||||
g->box.xmin = ((float)bbox.xMin) / 64.0f;
|
||||
g->box.xmax = ((float)bbox.xMax) / 64.0f;
|
||||
g->box.ymin = ((float)bbox.yMin) / 64.0f;
|
||||
g->box.ymax = ((float)bbox.yMax) / 64.0f;
|
||||
|
||||
const int buffer_size = (int)(slot->bitmap.width * slot->bitmap.rows);
|
||||
if (buffer_size != 0) {
|
||||
if (font->flags & BLF_MONOCHROME) {
|
||||
/* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
slot->bitmap.buffer[i] = slot->bitmap.buffer[i] ? 255 : 0;
|
||||
}
|
||||
}
|
||||
g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap");
|
||||
memcpy(g->bitmap, slot->bitmap.buffer, (size_t)buffer_size);
|
||||
if (glyph) {
|
||||
/* Save this glyph in the initial font's cache. */
|
||||
g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index);
|
||||
}
|
||||
|
||||
unsigned int key = blf_hash(g->c);
|
||||
BLI_addhead(&(gc->bucket[key]), g);
|
||||
if (charcode < GLYPH_ASCII_TABLE_SIZE) {
|
||||
gc->glyph_ascii_table[charcode] = g;
|
||||
}
|
||||
|
||||
BLI_spin_unlock(font->ft_lib_mutex);
|
||||
|
||||
BLI_spin_unlock(font_with_glyph->ft_lib_mutex);
|
||||
return g;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue