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.
This commit is contained in:
parent
d0a70adf8f
commit
ae43872ad5
Notes:
blender-bot
2023-02-14 02:22:07 +01:00
Referenced by issue #97310, Tooltips broken after recent BLF commit
|
@ -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;
|
||||
|
||||
/* Small adjust if there is hinting. */
|
||||
adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0);
|
||||
|
||||
if (FT_HAS_KERNING(font->face) && g_prev) {
|
||||
FT_Vector delta = {KERNING_ENTRY_UNSET};
|
||||
|
||||
/* 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 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. */
|
||||
adjustment += blf_unscaled_F26Dot6_to_pixels(font, delta.x);
|
||||
}
|
||||
}
|
||||
|
||||
FT_Vector delta = {KERNING_ENTRY_UNSET};
|
||||
|
||||
/* 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 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
|
||||
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_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_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)
|
||||
{
|
||||
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 = (ft_pix)face->size->metrics.max_advance;
|
||||
}
|
||||
/* can happen with size 1 fonts */
|
||||
return MAX2(width_max, ft_pix_from_int(1));
|
||||
}
|
||||
|
||||
int blf_font_width_max(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)));
|
||||
}
|
||||
else {
|
||||
width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
|
||||
}
|
||||
/* can happen with size 1 fonts */
|
||||
return MAX2(width_max, 1);
|
||||
return ft_pix_to_int(blf_font_width_max_ft_pix(font));
|
||||
}
|
||||
|
||||
float blf_font_descender(FontBLF *font)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
/* advance size. */
|
||||
float advance;
|
||||
/* avoid conversion to int while drawing */
|
||||
int advance_i;
|
||||
ft_pix advance_x;
|
||||
|
||||
/* 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;
|
||||
|
|
Loading…
Reference in New Issue