[embolden] Add in-place option

Adds --font-grade to hb-view and hb-shape.
This commit is contained in:
Behdad Esfahbod 2023-02-07 11:29:49 -07:00
parent d250fd979b
commit aef002e0d9
8 changed files with 130 additions and 43 deletions

View File

@ -1774,8 +1774,9 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
1000, /* y_scale */ 1000, /* y_scale */
0.f, /* x_embolden */ 0.f, /* x_embolden */
0.f, /* y_embolden */ 0.f, /* y_embolden */
0, /* x_shift */ true, /* embolden_in_place */
0, /* y_shift */ 0, /* x_strength */
0, /* y_strength */
0.f, /* slant */ 0.f, /* slant */
0.f, /* slant_xy; */ 0.f, /* slant_xy; */
1.f, /* x_multf */ 1.f, /* x_multf */
@ -1815,6 +1816,7 @@ _hb_font_create (hb_face_t *face)
font->klass = hb_font_funcs_get_empty (); font->klass = hb_font_funcs_get_empty ();
font->data.init0 (font); font->data.init0 (font);
font->x_scale = font->y_scale = face->get_upem (); font->x_scale = font->y_scale = face->get_upem ();
font->embolden_in_place = true;
font->x_multf = font->y_multf = 1.f; font->x_multf = font->y_multf = 1.f;
font->x_mult = font->y_mult = 1 << 16; font->x_mult = font->y_mult = 1 << 16;
font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE; font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE;
@ -1901,6 +1903,7 @@ hb_font_create_sub_font (hb_font_t *parent)
font->y_scale = parent->y_scale; font->y_scale = parent->y_scale;
font->x_embolden = parent->x_embolden; font->x_embolden = parent->x_embolden;
font->y_embolden = parent->y_embolden; font->y_embolden = parent->y_embolden;
font->embolden_in_place = parent->embolden_in_place;
font->slant = parent->slant; font->slant = parent->slant;
font->x_ppem = parent->x_ppem; font->x_ppem = parent->x_ppem;
font->y_ppem = parent->y_ppem; font->y_ppem = parent->y_ppem;
@ -2453,54 +2456,67 @@ hb_font_get_ptem (hb_font_t *font)
* @font: #hb_font_t to work upon * @font: #hb_font_t to work upon
* @x_embolden: the amount to embolden horizontally * @x_embolden: the amount to embolden horizontally
* @y_embolden: the amount to embolden vertically * @y_embolden: the amount to embolden vertically
* @in_place: whether to embolden glyphs in-place
* *
* Sets the "synthetic boldness" of a font. * Sets the "synthetic boldness" of a font.
* *
* Positive values make a font bolder, negative values * Positive values for @x_embolden / @y_embolden make a font
* thinner. Typical values are in the 0.01 to 0.05 range. * bolder, negative values thinner. Typical values are in the
* The default value is zero. * 0.01 to 0.05 range. The default value is zero.
* *
* Synthetic boldness is applied by offsetting the contour * Synthetic boldness is applied by offsetting the contour
* points of the glyph shape. * points of the glyph shape.
* *
* HarfBuzz applies synthetic boldness when rendering a glyph * Synthetic boldness is applied when rendering a glyph via
* via hb_font_draw_glyph(), and also uses the values to adjust * hb_font_draw_glyph().
* the advance width of emboldened glyphs. *
* If @in_place is %FALSE, then glyph advance-widths are also
* adjusted, otherwise they are not.
* *
* Since: REPLACEME * Since: REPLACEME
**/ **/
void void
hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden) hb_font_set_synthetic_bold (hb_font_t *font,
float x_embolden,
float y_embolden,
hb_bool_t in_place)
{ {
if (hb_object_is_immutable (font)) if (hb_object_is_immutable (font))
return; return;
if (font->x_embolden == x_embolden && if (font->x_embolden == x_embolden &&
font->y_embolden == y_embolden) font->y_embolden == y_embolden &&
font->embolden_in_place == in_place)
return; return;
font->serial++; font->serial++;
font->x_embolden = x_embolden; font->x_embolden = x_embolden;
font->y_embolden = y_embolden; font->y_embolden = y_embolden;
font->embolden_in_place = in_place;
font->mults_changed (); font->mults_changed ();
} }
/** /**
* hb_font_get_synthetic_bold: * hb_font_get_synthetic_bold:
* @font: #hb_font_t to work upon * @font: #hb_font_t to work upon
* @x_embolden: return location for horizontal value * @x_embolden: (out): return location for horizontal value
* @y_embolden: return location for vertical value * @y_embolden: (out): return location for vertical value
* @in_place: (out): return location for in-place value
* *
* Fetches the "synthetic boldness" of a font. * Fetches the "synthetic boldness" parameters of a font.
* *
* Since: REPLACEME * Since: REPLACEME
**/ **/
void void
hb_font_get_synthetic_bold (hb_font_t *font, float *x_embolden, float *y_embolden) hb_font_get_synthetic_bold (hb_font_t *font,
float *x_embolden,
float *y_embolden,
hb_bool_t *in_place)
{ {
if (x_embolden) *x_embolden = font->x_embolden; if (x_embolden) *x_embolden = font->x_embolden;
if (y_embolden) *y_embolden = font->y_embolden; if (y_embolden) *y_embolden = font->y_embolden;
if (in_place) *in_place = font->embolden_in_place;
} }
/** /**

View File

@ -1132,10 +1132,14 @@ HB_EXTERN float
hb_font_get_ptem (hb_font_t *font); hb_font_get_ptem (hb_font_t *font);
HB_EXTERN void HB_EXTERN void
hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden); hb_font_set_synthetic_bold (hb_font_t *font,
float x_embolden, float y_embolden,
hb_bool_t in_place);
HB_EXTERN void HB_EXTERN void
hb_font_get_synthetic_bold (hb_font_t *font, float *x_embolden, float *y_embolden); hb_font_get_synthetic_bold (hb_font_t *font,
float *x_embolden, float *y_embolden,
hb_bool_t *in_place);
HB_EXTERN void HB_EXTERN void
hb_font_set_synthetic_slant (hb_font_t *font, float slant); hb_font_set_synthetic_slant (hb_font_t *font, float slant);

View File

@ -116,8 +116,9 @@ struct hb_font_t
float x_embolden; float x_embolden;
float y_embolden; float y_embolden;
int32_t x_shift; /* x_embolden, in scaled units. */ bool embolden_in_place;
int32_t y_shift; /* y_embolden, in scaled units. */ int32_t x_strength; /* x_embolden, in scaled units. */
int32_t y_strength; /* y_embolden, in scaled units. */
float slant; float slant;
float slant_xy; float slant_xy;
@ -681,8 +682,8 @@ struct hb_font_t
bool y_neg = y_scale < 0; bool y_neg = y_scale < 0;
y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem;
x_shift = fabs (roundf (x_scale * x_embolden)); x_strength = fabs (roundf (x_scale * x_embolden));
y_shift = fabs (roundf (y_scale * y_embolden)); y_strength = fabs (roundf (y_scale * y_embolden));
slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; slant_xy = y_scale ? slant * x_scale / y_scale : 0.f;

View File

@ -490,14 +490,14 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
if (font->x_shift) if (font->x_strength && !font->embolden_in_place)
{ {
/* Emboldening. */ /* Emboldening. */
hb_position_t x_shift = font->x_scale >= 0 ? font->x_shift : -font->x_shift; hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength;
first_advance = orig_first_advance; first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
*first_advance += *first_advance ? x_shift : 0; *first_advance += *first_advance ? x_strength : 0;
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
@ -536,8 +536,8 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
/* Note: FreeType's vertical metrics grows downward while other FreeType coordinates /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
* have a Y growing upward. Hence the extra negation. */ * have a Y growing upward. Hence the extra negation. */
hb_position_t y_shift = font->y_scale >= 0 ? font->y_shift : -font->y_shift; hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
return ((-v + (1<<9)) >> 10) + y_shift; return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
} }
#endif #endif
@ -858,7 +858,38 @@ hb_ft_draw_glyph (hb_font_t *font,
hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy);
FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_shift, font->y_shift); /* Embolden */
if (font->x_strength || font->y_strength)
{
FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength);
int x_shift = 0;
int y_shift = 0;
if (font->embolden_in_place)
{
/* Undo the FreeType shift. */
x_shift = -font->x_strength / 2;
y_shift = 0;
if (font->y_scale < 0) y_shift = -font->y_strength;
}
else
{
/* FreeType applied things in the wrong direction for negative scale; fix up. */
if (font->x_scale < 0) x_shift = -font->x_strength;
if (font->y_scale < 0) y_shift = -font->y_strength;
}
if (x_shift || y_shift)
{
auto &outline = ft_face->glyph->outline;
for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1]))
{
point.x += x_shift;
point.y += y_shift;
}
}
}
FT_Outline_Decompose (&ft_face->glyph->outline, FT_Outline_Decompose (&ft_face->glyph->outline,
&outline_funcs, &outline_funcs,
&draw_session); &draw_session);

View File

@ -262,14 +262,14 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
OT::VariationStore::destroy_cache (varStore_cache); OT::VariationStore::destroy_cache (varStore_cache);
#endif #endif
if (font->x_shift) if (font->x_strength && !font->embolden_in_place)
{ {
/* Emboldening. */ /* Emboldening. */
hb_position_t x_shift = font->x_scale >= 0 ? font->x_shift : -font->x_shift; hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength;
first_advance = orig_first_advance; first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
*first_advance += *first_advance ? x_shift : 0; *first_advance += *first_advance ? x_strength : 0;
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
@ -326,14 +326,14 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
} }
} }
if (font->y_shift) if (font->y_strength && !font->embolden_in_place)
{ {
/* Emboldening. */ /* Emboldening. */
hb_position_t y_shift = font->y_scale >= 0 ? font->y_shift : -font->y_shift; hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
first_advance = orig_first_advance; first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
*first_advance += *first_advance ? y_shift : 0; *first_advance += *first_advance ? y_strength : 0;
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
@ -492,7 +492,7 @@ hb_ot_draw_glyph (hb_font_t *font,
hb_draw_funcs_t *draw_funcs, void *draw_data, hb_draw_funcs_t *draw_funcs, void *draw_data,
void *user_data) void *user_data)
{ {
bool embolden = font->x_shift || font->y_shift; bool embolden = font->x_strength || font->y_strength;
hb_outline_t outline; hb_outline_t outline;
{ // Need draw_session to be destructed before emboldening. { // Need draw_session to be destructed before emboldening.
@ -508,7 +508,13 @@ hb_ot_draw_glyph (hb_font_t *font,
if (embolden) if (embolden)
{ {
outline.embolden (font->x_shift, font->y_shift); float x_shift = font->embolden_in_place ? 0 : font->x_strength / 2;
float y_shift = font->y_strength / 2;
if (font->x_scale < 0) x_shift = -x_shift;
if (font->y_scale < 0) y_shift = -y_shift;
outline.embolden (font->x_strength, font->y_strength,
x_shift, y_shift);
outline.replay (draw_funcs, draw_data); outline.replay (draw_funcs, draw_data);
} }
} }

View File

@ -105,7 +105,8 @@ float hb_outline_t::area () const
return a * .5f; return a * .5f;
} }
void hb_outline_t::embolden (float x_strength, float y_strength) void hb_outline_t::embolden (float x_strength, float y_strength,
float x_shift, float y_shift)
{ {
/* This function is a straight port of FreeType's FT_Outline_EmboldenXY. /* This function is a straight port of FreeType's FT_Outline_EmboldenXY.
* Permission has been obtained from the FreeType authors of the code * Permission has been obtained from the FreeType authors of the code
@ -203,8 +204,8 @@ void hb_outline_t::embolden (float x_strength, float y_strength)
i != j; i != j;
i = i < last ? i + 1 : first ) i = i < last ? i + 1 : first )
{ {
points[i].x += x_strength + shift.x; points[i].x += x_shift + shift.x;
points[i].y += y_strength + shift.y; points[i].y += y_shift + shift.y;
} }
} }
else else

View File

@ -69,7 +69,8 @@ struct hb_outline_t
HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const;
HB_INTERNAL float area () const; HB_INTERNAL float area () const;
HB_INTERNAL void embolden (float x_strength, float y_strength); HB_INTERNAL void embolden (float x_strength, float y_strength,
float x_shift, float y_shift);
hb_vector_t<hb_outline_point_t> points; hb_vector_t<hb_outline_point_t> points;
hb_vector_t<unsigned> contours; hb_vector_t<unsigned> contours;

View File

@ -65,6 +65,7 @@ struct font_options_t : face_options_t
double ptem = 0.; double ptem = 0.;
double x_embolden = 0.; double x_embolden = 0.;
double y_embolden = 0.; double y_embolden = 0.;
hb_bool_t embolden_in_place = false;
double slant = 0.; double slant = 0.;
unsigned int subpixel_bits = SUBPIXEL_BITS; unsigned int subpixel_bits = SUBPIXEL_BITS;
mutable double font_size_x = DEFAULT_FONT_SIZE; mutable double font_size_x = DEFAULT_FONT_SIZE;
@ -103,7 +104,9 @@ font_options_t::post_parse (GError **error)
hb_font_set_ppem (font, x_ppem, y_ppem); hb_font_set_ppem (font, x_ppem, y_ppem);
hb_font_set_ptem (font, ptem); hb_font_set_ptem (font, ptem);
hb_font_set_synthetic_bold (font, (float) x_embolden, (float) y_embolden); hb_font_set_synthetic_bold (font,
(float) x_embolden, (float) y_embolden,
embolden_in_place);
hb_font_set_synthetic_slant (font, slant); hb_font_set_synthetic_slant (font, slant);
int scale_x = (int) scalbnf (font_size_x, subpixel_bits); int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
@ -249,10 +252,10 @@ parse_font_ppem (const char *name G_GNUC_UNUSED,
} }
static gboolean static gboolean
parse_font_bold (const char *name G_GNUC_UNUSED, parse_font_embolden (const char *name G_GNUC_UNUSED,
const char *arg, const char *arg,
gpointer data, gpointer data,
GError **error G_GNUC_UNUSED) GError **error G_GNUC_UNUSED)
{ {
font_options_t *font_opts = (font_options_t *) data; font_options_t *font_opts = (font_options_t *) data;
switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->x_embolden, &font_opts->y_embolden)) { switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->x_embolden, &font_opts->y_embolden)) {
@ -266,6 +269,28 @@ parse_font_bold (const char *name G_GNUC_UNUSED,
} }
} }
static gboolean
parse_font_bold (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
font_opts->embolden_in_place = false;
return parse_font_embolden ( name, arg, data, error);
}
static gboolean
parse_font_grade (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
font_options_t *font_opts = (font_options_t *) data;
font_opts->embolden_in_place = true;
return parse_font_embolden ( name, arg, data, error);
}
void void
font_options_t::add_options (option_parser_t *parser) font_options_t::add_options (option_parser_t *parser)
{ {
@ -309,6 +334,8 @@ font_options_t::add_options (option_parser_t *parser)
G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"},
{"font-bold", 0, font_size_flags, {"font-bold", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_bold, "Set synthetic bold (default: 0)", "1/2 numbers; eg. 0.05"}, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_bold, "Set synthetic bold (default: 0)", "1/2 numbers; eg. 0.05"},
{"font-grade", 0, font_size_flags,
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_grade, "Set synthetic grade (default: 0)", "1/2 numbers; eg. 0.05"},
{"font-slant", 0, font_size_flags, {"font-slant", 0, font_size_flags,
G_OPTION_ARG_DOUBLE, &this->slant, "Set synthetic slant (default: 0)", "slant ratio; eg. 0.2"}, G_OPTION_ARG_DOUBLE, &this->slant, "Set synthetic slant (default: 0)", "slant ratio; eg. 0.2"},
{"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"},