From ae43872ad572eb3e6ad1ebfd02921fc2403059bc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 Apr 2022 20:10:55 +1000 Subject: BLF: sub-pixel positioning support Support sub-pixel kerning and hinting for future support for improved character placement. No user visible changes have been made. - Calculate sub-pixel offsets, using integer maths. - Use convenience functions to perform the conversions and hide the underlying values. - Use `ft_pix` type to distinguish values that use sub-pixel integer values from freetype and values rounded to pixels. This was originally based on D12999 by @harley with the user visible changes removed so they can be applied separately. --- source/blender/blenfont/intern/blf_font.c | 293 +++++++++++---------- source/blender/blenfont/intern/blf_glyph.c | 25 +- source/blender/blenfont/intern/blf_internal.h | 2 +- .../blender/blenfont/intern/blf_internal_types.h | 87 +++++- 4 files changed, 257 insertions(+), 150 deletions(-) (limited to 'source') diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 60ff5f6470f..e5d7cb05408 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -57,34 +57,26 @@ static SpinLock blf_glyph_cache_mutex; /* May be set to #UI_widgetbase_draw_cache_flush. */ static void (*blf_draw_cache_flush)(void) = NULL; +static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); +static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); + /* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ -/** - * Convert a FreeType 26.6 value representing an unscaled design size to pixels. - * This is an exact copy of the scaling done inside FT_Get_Kerning when called - * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. - */ -static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */ +static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { /* Scale value by font size using integer-optimized multiplication. */ FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); - /* FreeType states that this '25' has been determined heuristically. */ + /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ + /* kerning distances at small ppem values so that they don't become too big. */ if (font->face->size->metrics.x_ppem < 25) { scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); } - /* Copies of internal FreeType macros needed here. */ -#define FT_PIX_FLOOR(x) ((x) & ~63) -#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) - - /* Round to even 64ths, then divide by 64. */ - return (int)FT_PIX_ROUND(scaled) >> 6; - -#undef FT_PIX_FLOOR -#undef FT_PIX_ROUND + return (ft_pix)scaled; } /** \} */ @@ -293,36 +285,39 @@ BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step( return blf_glyph_ensure(font, gc, charcode); } -BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) +BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) { - if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { - return 0; - } + ft_pix adjustment = 0; - FT_Vector delta = {KERNING_ENTRY_UNSET}; + /* Small adjust if there is hinting. */ + adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - /* Get unscaled kerning value from our cache if ASCII. */ - if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { - delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; - } + if (FT_HAS_KERNING(font->face) && g_prev) { + FT_Vector delta = {KERNING_ENTRY_UNSET}; - /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { - /* Note that this function sets delta values to zero on any error. */ - FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); - } + /* Get unscaled kerning value from our cache if ASCII. */ + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; + } - /* If ASCII we save this value to our cache for quicker access next time. */ - if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; - } + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } + + /* If ASCII we save this value to our cache for quicker access next time. */ + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; + } - if (delta.x != 0) { - /* Convert unscaled design units to pixels and move pen. */ - return blf_unscaled_F26Dot6_to_pixels(font, delta.x); + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + adjustment += blf_unscaled_F26Dot6_to_pixels(font, delta.x); + } } - return 0; + return adjustment; } /** \} */ @@ -336,10 +331,10 @@ static void blf_font_draw_ex(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0; if (str_len == 0) { @@ -358,9 +353,9 @@ static void blf_font_draw_ex(FontBLF *font, pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ - blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y)); - pen_x += g->advance_i; + pen_x = ft_pix_round_advance(pen_x, g->advance_x); g_prev = g; } @@ -368,7 +363,7 @@ static void blf_font_draw_ex(FontBLF *font, if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info) @@ -382,7 +377,9 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int { GlyphBLF *g; int col, columns = 0; - int pen_x = 0, pen_y = 0; + ft_pix pen_x = 0, pen_y = 0; + ft_pix cwidth_fpx = ft_pix_from_int(cwidth); + size_t i = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -396,7 +393,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int continue; } /* do not return this loop if clipped, we want every character tested */ - blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y)); col = BLI_wcwidth((char32_t)g->c); if (col < 0) { @@ -404,7 +401,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int } columns += col; - pen_x += cwidth * col; + pen_x += cwidth_fpx * col; } blf_batch_draw_end(); @@ -425,11 +422,11 @@ static void blf_font_draw_buffer_ex(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = (int)font->pos[0]; - int pen_y_basis = (int)font->pos[1] + pen_y; + ft_pix pen_x = ft_pix_from_float(font->pos[0]); + ft_pix pen_y_basis = ft_pix_from_float(font->pos[1]) + pen_y; size_t i = 0; /* buffer specific vars */ @@ -449,18 +446,18 @@ static void blf_font_draw_buffer_ex(FontBLF *font, } pen_x += blf_kerning(font, g_prev, g); - chx = pen_x + ((int)g->pos[0]); - chy = pen_y_basis + g->dims[1]; + chx = ft_pix_to_int(pen_x + ft_pix_from_int(g->pos[0])); + chy = ft_pix_to_int(pen_y_basis + ft_pix_from_int(g->dims[1])); if (g->pitch < 0) { - pen_y = pen_y_basis + (g->dims[1] - g->pos[1]); + pen_y = pen_y_basis + ft_pix_from_int(g->dims[1] - g->pos[1]); } else { - pen_y = pen_y_basis - (g->dims[1] - g->pos[1]); + pen_y = pen_y_basis - ft_pix_from_int(g->dims[1] - g->pos[1]); } - if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && (pen_y + g->dims[1]) >= 0 && - pen_y < buf_info->dims[1]) { + if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && + (ft_pix_to_int(pen_y) + g->dims[1]) >= 0 && ft_pix_to_int(pen_y) < buf_info->dims[1]) { /* don't draw beyond the buffer bounds */ int width_clip = g->dims[0]; int height_clip = g->dims[1]; @@ -469,17 +466,20 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (width_clip + chx > buf_info->dims[0]) { width_clip -= chx + width_clip - buf_info->dims[0]; } - if (height_clip + pen_y > buf_info->dims[1]) { - height_clip -= pen_y + height_clip - buf_info->dims[1]; + if (height_clip + ft_pix_to_int(pen_y) > buf_info->dims[1]) { + height_clip -= ft_pix_to_int(pen_y) + height_clip - buf_info->dims[1]; } /* drawing below the image? */ if (pen_y < 0) { - yb_start += (g->pitch < 0) ? -pen_y : pen_y; - height_clip += pen_y; + yb_start += (g->pitch < 0) ? -ft_pix_to_int(pen_y) : ft_pix_to_int(pen_y); + height_clip += ft_pix_to_int(pen_y); pen_y = 0; } + /* Avoid conversions in the pixel writing loop. */ + const int pen_y_px = ft_pix_to_int(pen_y); + if (buf_info->fbuf) { int yb = yb_start; for (y = ((chy >= 0) ? 0 : -chy); y < height_clip; y++) { @@ -488,7 +488,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * + ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); float *fbuf = buf_info->fbuf + buf_ofs; @@ -519,7 +519,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * + ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); unsigned char *cbuf = buf_info->cbuf + buf_ofs; @@ -542,13 +542,13 @@ static void blf_font_draw_buffer_ex(FontBLF *font, } } - pen_x += g->advance_i; + pen_x = ft_pix_round_advance(pen_x, g->advance_x); g_prev = g; } if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } @@ -573,23 +573,24 @@ void blf_font_draw_buffer(FontBLF *font, * \{ */ static bool blf_font_width_to_strlen_glyph_process( - FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i) + FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, ft_pix *pen_x, const int width_i) { if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } *pen_x += blf_kerning(font, g_prev, g); - *pen_x += g->advance_i; + *pen_x = ft_pix_round_advance(*pen_x, g->advance_x); /* When true, break the calling loop. */ - return (*pen_x >= width_i); + return (ft_pix_to_int(*pen_x) >= width_i); } size_t blf_font_width_to_strlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { GlyphBLF *g, *g_prev; - int pen_x, width_new; + ft_pix pen_x; + ft_pix width_new; size_t i, i_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -605,7 +606,7 @@ size_t blf_font_width_to_strlen( } if (r_width) { - *r_width = (float)width_new; + *r_width = (float)ft_pix_to_int(width_new); } blf_glyph_cache_release(font); @@ -616,7 +617,7 @@ size_t blf_font_width_to_rstrlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { GlyphBLF *g, *g_prev; - int pen_x, width_new; + ft_pix pen_x, width_new; size_t i, i_prev, i_tmp; const char *s, *s_prev; @@ -648,7 +649,7 @@ size_t blf_font_width_to_rstrlen( } if (r_width) { - *r_width = (float)width_new; + *r_width = (float)ft_pix_to_int(width_new); } blf_glyph_cache_release(font); @@ -667,17 +668,16 @@ static void blf_font_boundbox_ex(FontBLF *font, const size_t str_len, rctf *box, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0; - rctf gbox; - box->xmin = 32000.0f; - box->xmax = -32000.0f; - box->ymin = 32000.0f; - box->ymax = -32000.0f; + ft_pix box_xmin = ft_pix_from_int(32000); + ft_pix box_xmax = ft_pix_from_int(-32000); + ft_pix box_ymin = ft_pix_from_int(32000); + ft_pix box_ymax = ft_pix_from_int(-32000); while ((i < str_len) && str[i]) { g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); @@ -686,40 +686,46 @@ static void blf_font_boundbox_ex(FontBLF *font, continue; } pen_x += blf_kerning(font, g_prev, g); + const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); - gbox.xmin = (float)pen_x; - gbox.xmax = (float)pen_x + g->advance; - gbox.ymin = g->box.ymin + (float)pen_y; - gbox.ymax = g->box.ymax + (float)pen_y; + const ft_pix gbox_xmin = pen_x; + const ft_pix gbox_xmax = pen_x_next; + const ft_pix gbox_ymin = g->box_ymin + pen_y; + const ft_pix gbox_ymax = g->box_ymax + pen_y; - if (gbox.xmin < box->xmin) { - box->xmin = gbox.xmin; + if (gbox_xmin < box_xmin) { + box_xmin = gbox_xmin; } - if (gbox.ymin < box->ymin) { - box->ymin = gbox.ymin; + if (gbox_ymin < box_ymin) { + box_ymin = gbox_ymin; } - if (gbox.xmax > box->xmax) { - box->xmax = gbox.xmax; + if (gbox_xmax > box_xmax) { + box_xmax = gbox_xmax; } - if (gbox.ymax > box->ymax) { - box->ymax = gbox.ymax; + if (gbox_ymax > box_ymax) { + box_ymax = gbox_ymax; } - pen_x += g->advance_i; + pen_x = pen_x_next; g_prev = g; } - if (box->xmin > box->xmax) { - box->xmin = 0.0f; - box->ymin = 0.0f; - box->xmax = 0.0f; - box->ymax = 0.0f; + if (box_xmin > box_xmax) { + box_xmin = 0; + box_ymin = 0; + box_xmax = 0; + box_ymax = 0; } + box->xmin = (float)ft_pix_to_int_floor(box_xmin); + box->xmax = (float)ft_pix_to_int_ceil(box_xmax); + box->ymin = (float)ft_pix_to_int_floor(box_ymin); + box->ymax = (float)ft_pix_to_int_ceil(box_ymax); + if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_boundbox( @@ -822,12 +828,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, BLF_GlyphBoundsFn user_fn, void *user_data, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0, i_curr; - rcti gbox; + rcti gbox_px; if (str_len == 0) { /* early output. */ @@ -842,15 +848,23 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, continue; } pen_x += blf_kerning(font, g_prev, g); + const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + gbox_px.xmin = ft_pix_to_int_floor(pen_x); + gbox_px.xmax = ft_pix_to_int_ceil(pen_x_next); + gbox_px.ymin = ft_pix_to_int_floor(pen_y); + gbox_px.ymax = gbox_px.ymin - g->dims[1]; + const int advance_x_px = gbox_px.xmax - gbox_px.xmin; - pen_x += g->advance_i; + pen_x = pen_x_next; + + rctf box_px; + box_px.xmin = (float)ft_pix_to_int_floor(g->box_xmin); + box_px.xmax = (float)ft_pix_to_int_ceil(g->box_xmax); + box_px.ymin = (float)ft_pix_to_int_floor(g->box_ymin); + box_px.ymax = (float)ft_pix_to_int_ceil(g->box_ymax); - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + if (user_fn(str, i_curr, &gbox_px, advance_x_px, &box_px, g->pos, user_data) == false) { break; } @@ -859,7 +873,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_boundbox_foreach_glyph(FontBLF *font, @@ -897,22 +911,23 @@ static void blf_font_wrap_apply(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *userdata), void *userdata) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0, pen_y = 0; + ft_pix pen_x = 0; + ft_pix pen_y = 0; size_t i = 0; int lines = 0; - int pen_x_next = 0; + ft_pix pen_x_next = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); struct WordWrapVars { - int wrap_width; + ft_pix wrap_width; size_t start, last[2]; - } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; + } wrap = {font->wrap_width != -1 ? ft_pix_from_int(font->wrap_width) : INT_MAX, 0, {0, 0}}; // printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str); while ((i < str_len) && str[i]) { @@ -936,7 +951,7 @@ static void blf_font_wrap_apply(FontBLF *font, * * This is _only_ done when we know for sure the character is ascii (newline or a space). */ - pen_x_next = pen_x + g->advance_i; + pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { do_draw = true; } @@ -964,7 +979,7 @@ static void blf_font_wrap_apply(FontBLF *font, wrap.start = wrap.last[0]; i = wrap.last[1]; pen_x = 0; - pen_y -= blf_font_height_max(font); + pen_y -= blf_font_height_max_ft_pix(font); g_prev = NULL; lines += 1; continue; @@ -979,7 +994,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (r_info) { r_info->lines = lines; /* width of last line only (with wrapped lines) */ - r_info->width = pen_x_next; + r_info->width = ft_pix_to_int(pen_x_next); } blf_glyph_cache_release(font); @@ -990,7 +1005,7 @@ static void blf_font_draw__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *UNUSED(userdata)) { blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y); @@ -1008,7 +1023,7 @@ static void blf_font_boundbox_wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *userdata) { rctf *box = userdata; @@ -1033,7 +1048,7 @@ static void blf_font_draw_buffer__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *UNUSED(userdata)) { blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y); @@ -1084,34 +1099,46 @@ int blf_font_count_missing_chars(FontBLF *font, /** \name Font Query: Attributes * \{ */ -int blf_font_height_max(FontBLF *font) +static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - int height_max; - if (FT_IS_SCALABLE(font->face)) { - height_max = (int)((float)(font->face->ascender - font->face->descender) * - (((float)font->face->size->metrics.y_ppem) / - ((float)font->face->units_per_EM))); + ft_pix height_max; + FT_Face face = font->face; + if (FT_IS_SCALABLE(face)) { + height_max = ft_pix_from_int((int)(face->ascender - face->descender) * + (int)face->size->metrics.y_ppem) / + (ft_pix)face->units_per_EM; } else { - height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); + height_max = (ft_pix)face->size->metrics.height; } /* can happen with size 1 fonts */ - return MAX2(height_max, 1); + return MAX2(height_max, ft_pix_from_int(1)); } -int blf_font_width_max(FontBLF *font) +int blf_font_height_max(FontBLF *font) +{ + return ft_pix_to_int(blf_font_height_max_ft_pix(font)); +} + +static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - int width_max; - if (FT_IS_SCALABLE(font->face)) { - width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * - (((float)font->face->size->metrics.x_ppem) / - ((float)font->face->units_per_EM))); + ft_pix width_max; + const FT_Face face = font->face; + if (FT_IS_SCALABLE(face)) { + width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * + (int)face->size->metrics.x_ppem) / + (ft_pix)face->units_per_EM; } else { - width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); + width_max = (ft_pix)face->size->metrics.max_advance; } /* can happen with size 1 fonts */ - return MAX2(width_max, 1); + return MAX2(width_max, ft_pix_from_int(1)); +} + +int blf_font_width_max(FontBLF *font) +{ + return ft_pix_to_int(blf_font_width_max_ft_pix(font)); } float blf_font_descender(FontBLF *font) diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 28531c5afc1..497c797fd36 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -183,8 +183,7 @@ static GlyphBLF *blf_glyph_cache_add_glyph( 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->advance_x = (ft_pix)glyph->advance.x; g->pos[0] = glyph->bitmap_left; g->pos[1] = glyph->bitmap_top; g->dims[0] = (int)glyph->bitmap.width; @@ -193,10 +192,14 @@ static GlyphBLF *blf_glyph_cache_add_glyph( 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; + g->box_xmin = (ft_pix)bbox.xMin; + g->box_xmax = (ft_pix)bbox.xMax; + g->box_ymin = (ft_pix)bbox.yMin; + g->box_ymax = (ft_pix)bbox.yMax; + + /* Used to improve advance when hinting is enabled. */ + g->lsb_delta = (ft_pix)glyph->lsb_delta; + g->rsb_delta = (ft_pix)glyph->rsb_delta; const int buffer_size = (int)(glyph->bitmap.width * glyph->bitmap.rows); if (buffer_size != 0) { @@ -502,7 +505,7 @@ static void blf_glyph_calc_rect_test(rctf *rect, GlyphBLF *g, float x, float y) * width used by BLF_width. This allows that the text slightly * overlaps the clipping border to achieve better alignment. */ rect->xmin = floorf(x); - rect->xmax = rect->xmin + MIN2(g->advance, (float)g->dims[0]); + rect->xmax = rect->xmin + MIN2((float)ft_pix_to_int(g->advance_x), (float)g->dims[0]); rect->ymin = floorf(y); rect->ymax = rect->ymin - (float)g->dims[1]; } @@ -576,7 +579,7 @@ static void blf_texture3_draw(const unsigned char color_in[4], blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2); } -void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) +void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, int x, int y) { if ((!g->dims[0]) || (!g->dims[1])) { return; @@ -616,7 +619,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa if (font->flags & BLF_CLIPPING) { rctf rect_test; - blf_glyph_calc_rect_test(&rect_test, g, x, y); + blf_glyph_calc_rect_test(&rect_test, g, (float)x, (float)y); BLI_rctf_translate(&rect_test, font->pos[0], font->pos[1]); if (!BLI_rctf_inside_rctf(&font->clip_rec, &rect_test)) { @@ -631,7 +634,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa if (font->flags & BLF_SHADOW) { rctf rect_ofs; - blf_glyph_calc_rect_shadow(&rect_ofs, g, x, y, font); + blf_glyph_calc_rect_shadow(&rect_ofs, g, (float)x, (float)y, font); if (font->shadow == 0) { blf_texture_draw(font->shadow_color, @@ -663,7 +666,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa } rctf rect; - blf_glyph_calc_rect(&rect, g, x, y); + blf_glyph_calc_rect(&rect, g, (float)x, (float)y); #if BLF_BLUR_ENABLE switch (font->blur) { diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 1018dc09ff8..c38b1654d8e 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -133,7 +133,7 @@ struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc void blf_glyph_free(struct GlyphBLF *g); void blf_glyph_draw( - struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y); + struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, int x, int y); #ifdef WIN32 /* blf_font_win32_compat.c */ diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 23dc2fda38c..34fa82629b4 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -10,6 +10,79 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +/* -------------------------------------------------------------------- */ +/** \name Sub-Pixel Offset & Utilities + * + * Free-type uses fixed point precision for sub-pixel offsets. + * Utility functions here avoid exposing the details in the BLF API. + * \{ */ + +/** + * This is an internal type that represents sub-pixel positioning, + * users of this type are to use `ft_pix_*` functions to keep scaling/rounding in one place. + */ +typedef int32_t ft_pix; + +/* Macros copied from `include/freetype/internal/ftobjs.h`. */ + +/* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older. + * This is what users will expect and changing this creates wider spaced text. + * Use this macro to communicate that rounding should be used, using floor is to avoid + * user visible changes, which can be reviewed and handled separately. */ +#define USE_LEGACY_SPACING + +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) +#define FT_PIX_CEIL(x) ((x) + 63) + +#ifdef USE_LEGACY_SPACING +# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_FLOOR(x) +#else +# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_ROUND(x) +#endif + +BLI_INLINE int ft_pix_to_int(ft_pix v) +{ +#ifdef USE_LEGACY_SPACING + return (int)(v >> 6); +#else + return (int)(FT_PIX_DEFAULT_ROUNDING(v) >> 6); +#endif +} + +BLI_INLINE int ft_pix_to_int_floor(ft_pix v) +{ + return (int)(v >> 6); /* No need for explicit floor as the bits are removed when shifting. */ +} + +BLI_INLINE int ft_pix_to_int_ceil(ft_pix v) +{ + return (int)(FT_PIX_CEIL(v) >> 6); +} + +BLI_INLINE ft_pix ft_pix_from_int(int v) +{ + return v * 64; +} + +BLI_INLINE ft_pix ft_pix_from_float(float v) +{ + return lroundf(v * 64.0f); +} + +BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step) +{ + /* See #USE_LEGACY_SPACING, rounding logic could change here. */ + return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step); +} + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +#undef FT_PIX_CEIL +#undef FT_PIX_DEFAULT_ROUNDING + +/** \} */ + #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ /* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ @@ -86,12 +159,16 @@ typedef struct GlyphBLF { FT_UInt idx; /* glyph box. */ - rctf box; + ft_pix box_xmin; + ft_pix box_xmax; + ft_pix box_ymin; + ft_pix box_ymax; + + ft_pix advance_x; - /* advance size. */ - float advance; - /* avoid conversion to int while drawing */ - int advance_i; + /* The difference in bearings when hinting is active, zero otherwise. */ + ft_pix lsb_delta; + ft_pix rsb_delta; /* position inside the texture where this glyph is store. */ int offset; -- cgit v1.2.3