From 70149885a78017475ebedd732ca5d3b0d4d8c595 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 14:27:45 -0700 Subject: [PATCH 01/22] [font] Towards implementing emboldening --- src/hb-draw-embolden.hh | 349 ++++++++++++++++++++++++++++++++++++++++ src/hb-ft.cc | 6 +- src/hb-ot-font.cc | 20 ++- src/hb-vector.hh | 5 +- 4 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 src/hb-draw-embolden.hh diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh new file mode 100644 index 000000000..cbb8e4c68 --- /dev/null +++ b/src/hb-draw-embolden.hh @@ -0,0 +1,349 @@ +/* + * Copyright © 2023 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DRAW_EMBOLDEN_HH +#define HB_DRAW_EMBOLDEN_HH + +#include "hb.hh" + +#include "hb-draw.hh" + + +struct hb_contour_point_t +{ + enum class type_t + { + MOVE_TO, + LINE_TO, + QUADRATIC_TO, + CUBIC_TO, + }; + + hb_contour_point_t (float x, float y, type_t type) : + x (x), y (y), type (type) {} + + float x, y; + type_t type; +}; + +using hb_contour_t = hb_vector_t; + +template +struct hb_filter_outline_t +{ + void replay (hb_draw_funcs_t *pen, void *pen_data) + { + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + + unsigned first = 0; + for (unsigned contour : contours) + { + auto it = points.as_array ().sub_array (first, contour - first); + while (it) + { + hb_contour_point_t p1 = *it++; + switch (p1.type) + { + case hb_contour_point_t::type_t::MOVE_TO: + { + pen->move_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_contour_point_t::type_t::LINE_TO: + { + pen->line_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_contour_point_t::type_t::QUADRATIC_TO: + { + hb_contour_point_t p2 = *it++; + pen->quadratic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y); + } + break; + case hb_contour_point_t::type_t::CUBIC_TO: + { + hb_contour_point_t p2 = *it++; + hb_contour_point_t p3 = *it++; + pen->cubic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y, + p3.x, p3.y); + } + break; + } + } + pen->close_path (pen_data, st); + first = contour; + } + } + + hb_vector_t points; + hb_vector_t contours; +}; + +template +static void +hb_filter_outline_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + context_t *c = (context_t *) data; + + c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::MOVE_TO}); +} + +template +static void +hb_filter_outline_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + context_t *c = (context_t *) data; + + c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::LINE_TO}); +} + +template +static void +hb_filter_outline_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + context_t *c = (context_t *) data; + + c->points.push (hb_contour_point_t {control_x, control_y, hb_contour_point_t::type_t::QUADRATIC_TO}); + c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::QUADRATIC_TO}); +} + +template +static void +hb_filter_outline_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + context_t *c = (context_t *) data; + + c->points.push (hb_contour_point_t {control1_x, control1_y, hb_contour_point_t::type_t::CUBIC_TO}); + c->points.push (hb_contour_point_t {control2_x, control2_y, hb_contour_point_t::type_t::CUBIC_TO}); + c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::CUBIC_TO}); +} + +template +static void +hb_filter_outline_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + context_t *c = (context_t *) data; + + c->contours.push (c->points.length); +} + + +struct hb_draw_embolden_context_t : hb_filter_outline_t +{ + void operator () (float x_strength, float y_strength) + { + if (!x_strength && !y_strength) return; + if (!points) return; + + x_strength /= 2.f; + y_strength /= 2.f; + + bool orientation_negative = false; + + struct vector_t + { + float normalize_len () + { + float len = hypotf (x, y); + if (len) + { + x /= len; + y /= len; + } + return len; + } + + float x, y; + }; + + signed first = 0; + for (unsigned c = 0; c < contours.length; c++) + { + vector_t in, out, anchor, shift; + float l_in, l_out, l_anchor = 0, l, q, d; + + l_in = 0; + signed last = (int) contours[c] - 1; + + /* pacify compiler */ + in.x = in.y = anchor.x = anchor.y = 0; + + /* Counter j cycles though the points; counter i advances only */ + /* when points are moved; anchor k marks the first moved point. */ + for ( signed i = last, j = first, k = -1; + j != i && i != k; + j = j < last ? j + 1 : first ) + { + if ( j != k ) + { + out.x = points[j].x - points[i].x; + out.y = points[j].y - points[i].y; + l_out = out.normalize_len (); + + if ( l_out == 0 ) + continue; + } + else + { + out = anchor; + l_out = l_anchor; + } + + if ( l_in != 0 ) + { + if ( k < 0 ) + { + k = i; + anchor = in; + l_anchor = l_in; + } + + d = in.x * out.x + in.y * out.y; + + /* shift only if turn is less than ~160 degrees */ + if ( d > -15.f/16.f ) + { + d = d + 1.f; + + /* shift components along lateral bisector in proper orientation */ + shift.x = in.y + out.y; + shift.y = in.x + out.x; + + if ( orientation_negative ) + shift.x = -shift.x; + else + shift.y = -shift.y; + + /* restrict shift magnitude to better handle collapsing segments */ + q = out.x * in.y - out.y * in.x; + if ( orientation_negative ) + q = -q; + + l = hb_min (l_in, l_out); + + /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ + if (x_strength * q <= l * d) + shift.x = shift.x * x_strength / d; + else + shift.x = shift.x * l / q; + + + if (y_strength * q <= l * d) + shift.y = shift.y * y_strength / d; + else + shift.y = shift.y * l / q; + } + else + shift.x = shift.y = 0; + + for ( ; + i != j; + i = i < last ? i + 1 : first ) + { + points[i].x += x_strength + shift.x; + points[i].y += y_strength + shift.y; + } + } + else + i = j; + + in = out; + l_in = l_out; + } + + first = last + 1; + } + } + + hb_draw_funcs_t *pen; + void *pen_data; + float x_strength, y_strength; +}; + +static inline void free_static_draw_embolden_funcs (); + +static struct hb_draw_embolden_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + using t = hb_draw_embolden_context_t; + + hb_draw_funcs_set_move_to_func (funcs, hb_filter_outline_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_filter_outline_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_filter_outline_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_filter_outline_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_filter_outline_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_draw_embolden_funcs); + + return funcs; + } +} static_draw_embolden_funcs; + +static inline +void free_static_draw_embolden_funcs () +{ + static_draw_embolden_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_draw_embolden_get_funcs () +{ + return static_draw_embolden_funcs.get_unconst (); +} + + +#endif /* HB_DRAW_EMBOLDEN_HH */ diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 063ffb314..3005828f0 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -45,6 +45,7 @@ #include FT_MULTIPLE_MASTERS_H #include FT_OUTLINE_H #include FT_TRUETYPE_TABLES_H +#include FT_SYNTHESIS_H #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 #include FT_COLOR_H #endif @@ -815,7 +816,7 @@ _hb_ft_cubic_to (const FT_Vector *control1, } static void -hb_ft_draw_glyph (hb_font_t *font HB_UNUSED, +hb_ft_draw_glyph (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_draw_funcs_t *draw_funcs, void *draw_data, @@ -843,6 +844,9 @@ hb_ft_draw_glyph (hb_font_t *font HB_UNUSED, hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + hb_position_t xstr = font->x_scale / 20; + hb_position_t ystr = font->y_scale / 20; + FT_Outline_EmboldenXY (&ft_face->glyph->outline, xstr, ystr); FT_Outline_Decompose (&ft_face->glyph->outline, &outline_funcs, &draw_session); diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 92f9e3969..42f7a6dd2 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -34,6 +34,7 @@ #include "hb-font.hh" #include "hb-machinery.hh" #include "hb-ot-face.hh" +#include "hb-draw-embolden.hh" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" @@ -462,12 +463,23 @@ hb_ot_draw_glyph (hb_font_t *font, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data) { - hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); - if (font->face->table.glyf->get_path (font, glyph, draw_session)) return; + + hb_draw_embolden_context_t emboldener; + auto *embolden_funcs = hb_draw_embolden_get_funcs (); + + hb_draw_session_t draw_session (embolden_funcs, &emboldener, font->slant_xy); + if (!font->face->table.glyf->get_path (font, glyph, draw_session)) #ifndef HB_NO_CFF - if (font->face->table.cff1->get_path (font, glyph, draw_session)) return; - if (font->face->table.cff2->get_path (font, glyph, draw_session)) return; + if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (!font->face->table.cff2->get_path (font, glyph, draw_session)) #endif + {} + + float xstr = font->x_scale / 20; + float ystr = font->y_scale / 20; + emboldener (xstr, ystr); + emboldener.replay (draw_funcs, draw_data); + } #endif diff --git a/src/hb-vector.hh b/src/hb-vector.hh index b78605fcd..58d467a40 100644 --- a/src/hb-vector.hh +++ b/src/hb-vector.hh @@ -459,7 +459,7 @@ struct hb_vector_t length--; } - void shrink (int size_) + void shrink (int size_, bool shrink_memory = true) { unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; if (size >= length) @@ -467,7 +467,8 @@ struct hb_vector_t shrink_vector (size); - alloc (size, true); /* To force shrinking memory if needed. */ + if (shrink_memory) + alloc (size, true); /* To force shrinking memory if needed. */ } From 1817f18085a7476759e794cfb0b4a627fc1487cc Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 15:49:05 -0700 Subject: [PATCH 02/22] [embolden] Simplify recording-pen --- src/hb-draw-embolden.hh | 109 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh index cbb8e4c68..60ac3dc13 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-draw-embolden.hh @@ -49,8 +49,7 @@ struct hb_contour_point_t using hb_contour_t = hb_vector_t; -template -struct hb_filter_outline_t +struct hb_recording_pen_t { void replay (hb_draw_funcs_t *pen, void *pen_data) { @@ -106,78 +105,73 @@ struct hb_filter_outline_t hb_vector_t contours; }; -template static void -hb_filter_outline_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) { - context_t *c = (context_t *) data; + hb_recording_pen_t *c = (hb_recording_pen_t *) data; c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::MOVE_TO}); } -template static void -hb_filter_outline_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) { - context_t *c = (context_t *) data; + hb_recording_pen_t *c = (hb_recording_pen_t *) data; c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::LINE_TO}); } -template static void -hb_filter_outline_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control_x, float control_y, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) { - context_t *c = (context_t *) data; + hb_recording_pen_t *c = (hb_recording_pen_t *) data; c->points.push (hb_contour_point_t {control_x, control_y, hb_contour_point_t::type_t::QUADRATIC_TO}); c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::QUADRATIC_TO}); } -template static void -hb_filter_outline_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control1_x, float control1_y, - float control2_x, float control2_y, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) { - context_t *c = (context_t *) data; + hb_recording_pen_t *c = (hb_recording_pen_t *) data; c->points.push (hb_contour_point_t {control1_x, control1_y, hb_contour_point_t::type_t::CUBIC_TO}); c->points.push (hb_contour_point_t {control2_x, control2_y, hb_contour_point_t::type_t::CUBIC_TO}); c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::CUBIC_TO}); } -template static void -hb_filter_outline_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - void *user_data HB_UNUSED) +hb_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) { - context_t *c = (context_t *) data; + hb_recording_pen_t *c = (hb_recording_pen_t *) data; c->contours.push (c->points.length); } -struct hb_draw_embolden_context_t : hb_filter_outline_t +struct hb_draw_embolden_context_t : hb_recording_pen_t { void operator () (float x_strength, float y_strength) { @@ -187,7 +181,7 @@ struct hb_draw_embolden_context_t : hb_filter_outline_t +static struct hb_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t { static hb_draw_funcs_t *create () { hb_draw_funcs_t *funcs = hb_draw_funcs_create (); - using t = hb_draw_embolden_context_t; - - hb_draw_funcs_set_move_to_func (funcs, hb_filter_outline_move_to, nullptr, nullptr); - hb_draw_funcs_set_line_to_func (funcs, hb_filter_outline_line_to, nullptr, nullptr); - hb_draw_funcs_set_quadratic_to_func (funcs, hb_filter_outline_quadratic_to, nullptr, nullptr); - hb_draw_funcs_set_cubic_to_func (funcs, hb_filter_outline_cubic_to, nullptr, nullptr); - hb_draw_funcs_set_close_path_func (funcs, hb_filter_outline_close_path, nullptr, nullptr); + hb_draw_funcs_set_move_to_func (funcs, hb_recording_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_recording_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_recording_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_recording_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_recording_pen_close_path, nullptr, nullptr); hb_draw_funcs_make_immutable (funcs); - hb_atexit (free_static_draw_embolden_funcs); + hb_atexit (free_static_recording_pen_funcs); return funcs; } -} static_draw_embolden_funcs; +} static_recording_pen_funcs; static inline -void free_static_draw_embolden_funcs () +void free_static_recording_pen_funcs () { - static_draw_embolden_funcs.free_instance (); + static_recording_pen_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_recording_pen_get_funcs () +{ + return static_recording_pen_funcs.get_unconst (); } static hb_draw_funcs_t * hb_draw_embolden_get_funcs () { - return static_draw_embolden_funcs.get_unconst (); + return hb_recording_pen_get_funcs (); } - #endif /* HB_DRAW_EMBOLDEN_HH */ From 6b4a6fbedded342182cca5356707050696912753 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 15:59:37 -0700 Subject: [PATCH 03/22] [embolden] Add orientation detection --- src/hb-draw-embolden.hh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh index 60ac3dc13..a9c95ab97 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-draw-embolden.hh @@ -51,7 +51,7 @@ using hb_contour_t = hb_vector_t; struct hb_recording_pen_t { - void replay (hb_draw_funcs_t *pen, void *pen_data) + void replay (hb_draw_funcs_t *pen, void *pen_data) const { hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; @@ -101,6 +101,26 @@ struct hb_recording_pen_t } } + float area () const + { + float a = 0; + unsigned first = 0; + for (unsigned contour : contours) + { + for (unsigned i = first; i < contour; i++) + { + unsigned j = i + 1 < contour ? i + 1 : first; + + auto &pi = points[i]; + auto &pj = points[j]; + a += pi.x * pj.y - pi.y * pj.x; + } + + first = contour; + } + return a * .5f; + } + hb_vector_t points; hb_vector_t contours; }; @@ -181,7 +201,7 @@ struct hb_draw_embolden_context_t : hb_recording_pen_t x_strength /= 2.f; y_strength /= 2.f; - bool orientation_negative = true; + bool orientation_negative = area () < 0; struct vector_t { From c06f95ebe18e7f6a093e28e8dcb322ca6e4d5a8d Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:02:48 -0700 Subject: [PATCH 04/22] [embolden] Move code --- src/hb-draw-embolden.hh | 72 +++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh index a9c95ab97..3119a8d64 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-draw-embolden.hh @@ -190,6 +190,40 @@ hb_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, c->contours.push (c->points.length); } +static inline void free_static_recording_pen_funcs (); + +static struct hb_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_recording_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_recording_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_recording_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_recording_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_recording_pen_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_recording_pen_funcs); + + return funcs; + } +} static_recording_pen_funcs; + +static inline +void free_static_recording_pen_funcs () +{ + static_recording_pen_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_recording_pen_get_funcs () +{ + return static_recording_pen_funcs.get_unconst (); +} + struct hb_draw_embolden_context_t : hb_recording_pen_t { @@ -317,46 +351,8 @@ struct hb_draw_embolden_context_t : hb_recording_pen_t first = last + 1; } } - - hb_draw_funcs_t *pen; - void *pen_data; - float x_strength, y_strength; }; -static inline void free_static_recording_pen_funcs (); - -static struct hb_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t -{ - static hb_draw_funcs_t *create () - { - hb_draw_funcs_t *funcs = hb_draw_funcs_create (); - - hb_draw_funcs_set_move_to_func (funcs, hb_recording_pen_move_to, nullptr, nullptr); - hb_draw_funcs_set_line_to_func (funcs, hb_recording_pen_line_to, nullptr, nullptr); - hb_draw_funcs_set_quadratic_to_func (funcs, hb_recording_pen_quadratic_to, nullptr, nullptr); - hb_draw_funcs_set_cubic_to_func (funcs, hb_recording_pen_cubic_to, nullptr, nullptr); - hb_draw_funcs_set_close_path_func (funcs, hb_recording_pen_close_path, nullptr, nullptr); - - hb_draw_funcs_make_immutable (funcs); - - hb_atexit (free_static_recording_pen_funcs); - - return funcs; - } -} static_recording_pen_funcs; - -static inline -void free_static_recording_pen_funcs () -{ - static_recording_pen_funcs.free_instance (); -} - -static hb_draw_funcs_t * -hb_recording_pen_get_funcs () -{ - return static_recording_pen_funcs.get_unconst (); -} - static hb_draw_funcs_t * hb_draw_embolden_get_funcs () { From 7774bccb48404f4b998d16b701463039bf0955da Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:12:10 -0700 Subject: [PATCH 05/22] [embolden] Renames --- src/hb-draw-embolden.hh | 134 ++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh index 3119a8d64..f52bdeb3c 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-draw-embolden.hh @@ -30,7 +30,7 @@ #include "hb-draw.hh" -struct hb_contour_point_t +struct hb_outline_point_t { enum class type_t { @@ -40,16 +40,14 @@ struct hb_contour_point_t CUBIC_TO, }; - hb_contour_point_t (float x, float y, type_t type) : + hb_outline_point_t (float x, float y, type_t type) : x (x), y (y), type (type) {} float x, y; type_t type; }; -using hb_contour_t = hb_vector_t; - -struct hb_recording_pen_t +struct hb_outline_t { void replay (hb_draw_funcs_t *pen, void *pen_data) const { @@ -61,33 +59,33 @@ struct hb_recording_pen_t auto it = points.as_array ().sub_array (first, contour - first); while (it) { - hb_contour_point_t p1 = *it++; + hb_outline_point_t p1 = *it++; switch (p1.type) { - case hb_contour_point_t::type_t::MOVE_TO: + case hb_outline_point_t::type_t::MOVE_TO: { pen->move_to (pen_data, st, p1.x, p1.y); } break; - case hb_contour_point_t::type_t::LINE_TO: + case hb_outline_point_t::type_t::LINE_TO: { pen->line_to (pen_data, st, p1.x, p1.y); } break; - case hb_contour_point_t::type_t::QUADRATIC_TO: + case hb_outline_point_t::type_t::QUADRATIC_TO: { - hb_contour_point_t p2 = *it++; + hb_outline_point_t p2 = *it++; pen->quadratic_to (pen_data, st, p1.x, p1.y, p2.x, p2.y); } break; - case hb_contour_point_t::type_t::CUBIC_TO: + case hb_outline_point_t::type_t::CUBIC_TO: { - hb_contour_point_t p2 = *it++; - hb_contour_point_t p3 = *it++; + hb_outline_point_t p2 = *it++; + hb_outline_point_t p3 = *it++; pen->cubic_to (pen_data, st, p1.x, p1.y, p2.x, p2.y, @@ -121,111 +119,111 @@ struct hb_recording_pen_t return a * .5f; } - hb_vector_t points; + hb_vector_t points; hb_vector_t contours; }; static void -hb_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_outline_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) { - hb_recording_pen_t *c = (hb_recording_pen_t *) data; + hb_outline_t *c = (hb_outline_t *) data; - c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::MOVE_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::MOVE_TO}); } static void -hb_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_outline_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) { - hb_recording_pen_t *c = (hb_recording_pen_t *) data; + hb_outline_t *c = (hb_outline_t *) data; - c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::LINE_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::LINE_TO}); } static void -hb_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control_x, float control_y, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_outline_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) { - hb_recording_pen_t *c = (hb_recording_pen_t *) data; + hb_outline_t *c = (hb_outline_t *) data; - c->points.push (hb_contour_point_t {control_x, control_y, hb_contour_point_t::type_t::QUADRATIC_TO}); - c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::QUADRATIC_TO}); + c->points.push (hb_outline_point_t {control_x, control_y, hb_outline_point_t::type_t::QUADRATIC_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::QUADRATIC_TO}); } static void -hb_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control1_x, float control1_y, - float control2_x, float control2_y, - float to_x, float to_y, - void *user_data HB_UNUSED) +hb_outline_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) { - hb_recording_pen_t *c = (hb_recording_pen_t *) data; + hb_outline_t *c = (hb_outline_t *) data; - c->points.push (hb_contour_point_t {control1_x, control1_y, hb_contour_point_t::type_t::CUBIC_TO}); - c->points.push (hb_contour_point_t {control2_x, control2_y, hb_contour_point_t::type_t::CUBIC_TO}); - c->points.push (hb_contour_point_t {to_x, to_y, hb_contour_point_t::type_t::CUBIC_TO}); + c->points.push (hb_outline_point_t {control1_x, control1_y, hb_outline_point_t::type_t::CUBIC_TO}); + c->points.push (hb_outline_point_t {control2_x, control2_y, hb_outline_point_t::type_t::CUBIC_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::CUBIC_TO}); } static void -hb_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - void *user_data HB_UNUSED) +hb_outline_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) { - hb_recording_pen_t *c = (hb_recording_pen_t *) data; + hb_outline_t *c = (hb_outline_t *) data; c->contours.push (c->points.length); } -static inline void free_static_recording_pen_funcs (); +static inline void free_static_outline_recording_pen_funcs (); -static struct hb_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +static struct hb_outline_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t { static hb_draw_funcs_t *create () { hb_draw_funcs_t *funcs = hb_draw_funcs_create (); - hb_draw_funcs_set_move_to_func (funcs, hb_recording_pen_move_to, nullptr, nullptr); - hb_draw_funcs_set_line_to_func (funcs, hb_recording_pen_line_to, nullptr, nullptr); - hb_draw_funcs_set_quadratic_to_func (funcs, hb_recording_pen_quadratic_to, nullptr, nullptr); - hb_draw_funcs_set_cubic_to_func (funcs, hb_recording_pen_cubic_to, nullptr, nullptr); - hb_draw_funcs_set_close_path_func (funcs, hb_recording_pen_close_path, nullptr, nullptr); + hb_draw_funcs_set_move_to_func (funcs, hb_outline_recording_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_outline_recording_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_outline_recording_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_outline_recording_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_outline_recording_pen_close_path, nullptr, nullptr); hb_draw_funcs_make_immutable (funcs); - hb_atexit (free_static_recording_pen_funcs); + hb_atexit (free_static_outline_recording_pen_funcs); return funcs; } -} static_recording_pen_funcs; +} static_outline_recording_pen_funcs; static inline -void free_static_recording_pen_funcs () +void free_static_outline_recording_pen_funcs () { - static_recording_pen_funcs.free_instance (); + static_outline_recording_pen_funcs.free_instance (); } static hb_draw_funcs_t * -hb_recording_pen_get_funcs () +hb_outline_recording_pen_get_funcs () { - return static_recording_pen_funcs.get_unconst (); + return static_outline_recording_pen_funcs.get_unconst (); } -struct hb_draw_embolden_context_t : hb_recording_pen_t +struct hb_draw_embolden_context_t : hb_outline_t { void operator () (float x_strength, float y_strength) { @@ -356,7 +354,7 @@ struct hb_draw_embolden_context_t : hb_recording_pen_t static hb_draw_funcs_t * hb_draw_embolden_get_funcs () { - return hb_recording_pen_get_funcs (); + return hb_outline_recording_pen_get_funcs (); } #endif /* HB_DRAW_EMBOLDEN_HH */ From fda2f6f64e5033c824187b50fcdd07b1d65d1080 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:16:10 -0700 Subject: [PATCH 06/22] [embolden] Shuffle under hb_outline_t --- src/hb-draw-embolden.hh | 401 ++++++++++++++++++++-------------------- src/hb-ot-font.cc | 10 +- 2 files changed, 204 insertions(+), 207 deletions(-) diff --git a/src/hb-draw-embolden.hh b/src/hb-draw-embolden.hh index f52bdeb3c..1bc234a29 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-draw-embolden.hh @@ -47,82 +47,213 @@ struct hb_outline_point_t type_t type; }; +struct hb_outline_vector_t +{ + float normalize_len () + { + float len = hypotf (x, y); + if (len) + { + x /= len; + y /= len; + } + return len; + } + + float x, y; +}; + struct hb_outline_t { - void replay (hb_draw_funcs_t *pen, void *pen_data) const - { - hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + void reset () { points.shrink (0, false); contours.resize (0); } - unsigned first = 0; - for (unsigned contour : contours) - { - auto it = points.as_array ().sub_array (first, contour - first); - while (it) - { - hb_outline_point_t p1 = *it++; - switch (p1.type) - { - case hb_outline_point_t::type_t::MOVE_TO: - { - pen->move_to (pen_data, st, - p1.x, p1.y); - } - break; - case hb_outline_point_t::type_t::LINE_TO: - { - pen->line_to (pen_data, st, - p1.x, p1.y); - } - break; - case hb_outline_point_t::type_t::QUADRATIC_TO: - { - hb_outline_point_t p2 = *it++; - pen->quadratic_to (pen_data, st, - p1.x, p1.y, - p2.x, p2.y); - } - break; - case hb_outline_point_t::type_t::CUBIC_TO: - { - hb_outline_point_t p2 = *it++; - hb_outline_point_t p3 = *it++; - pen->cubic_to (pen_data, st, - p1.x, p1.y, - p2.x, p2.y, - p3.x, p3.y); - } - break; - } - } - pen->close_path (pen_data, st); - first = contour; - } - } - - float area () const - { - float a = 0; - unsigned first = 0; - for (unsigned contour : contours) - { - for (unsigned i = first; i < contour; i++) - { - unsigned j = i + 1 < contour ? i + 1 : first; - - auto &pi = points[i]; - auto &pj = points[j]; - a += pi.x * pj.y - pi.y * pj.x; - } - - first = contour; - } - return a * .5f; - } + HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; + HB_INTERNAL float area () const; + HB_INTERNAL void embolden (float x_strength, float y_strength); hb_vector_t points; hb_vector_t contours; }; +void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + + unsigned first = 0; + for (unsigned contour : contours) + { + auto it = points.as_array ().sub_array (first, contour - first); + while (it) + { + hb_outline_point_t p1 = *it++; + switch (p1.type) + { + case hb_outline_point_t::type_t::MOVE_TO: + { + pen->move_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_outline_point_t::type_t::LINE_TO: + { + pen->line_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_outline_point_t::type_t::QUADRATIC_TO: + { + hb_outline_point_t p2 = *it++; + pen->quadratic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y); + } + break; + case hb_outline_point_t::type_t::CUBIC_TO: + { + hb_outline_point_t p2 = *it++; + hb_outline_point_t p3 = *it++; + pen->cubic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y, + p3.x, p3.y); + } + break; + } + } + pen->close_path (pen_data, st); + first = contour; + } +} + +float hb_outline_t::area () const +{ + float a = 0; + unsigned first = 0; + for (unsigned contour : contours) + { + for (unsigned i = first; i < contour; i++) + { + unsigned j = i + 1 < contour ? i + 1 : first; + + auto &pi = points[i]; + auto &pj = points[j]; + a += pi.x * pj.y - pi.y * pj.x; + } + + first = contour; + } + return a * .5f; +} + +void hb_outline_t::embolden (float x_strength, float y_strength) +{ + if (!x_strength && !y_strength) return; + if (!points) return; + + x_strength /= 2.f; + y_strength /= 2.f; + + bool orientation_negative = area () < 0; + + signed first = 0; + for (unsigned c = 0; c < contours.length; c++) + { + hb_outline_vector_t in, out, anchor, shift; + float l_in, l_out, l_anchor = 0, l, q, d; + + l_in = 0; + signed last = (int) contours[c] - 1; + + /* pacify compiler */ + in.x = in.y = anchor.x = anchor.y = 0; + + /* Counter j cycles though the points; counter i advances only */ + /* when points are moved; anchor k marks the first moved point. */ + for ( signed i = last, j = first, k = -1; + j != i && i != k; + j = j < last ? j + 1 : first ) + { + if ( j != k ) + { + out.x = points[j].x - points[i].x; + out.y = points[j].y - points[i].y; + l_out = out.normalize_len (); + + if ( l_out == 0 ) + continue; + } + else + { + out = anchor; + l_out = l_anchor; + } + + if ( l_in != 0 ) + { + if ( k < 0 ) + { + k = i; + anchor = in; + l_anchor = l_in; + } + + d = in.x * out.x + in.y * out.y; + + /* shift only if turn is less than ~160 degrees */ + if ( d > -15.f/16.f ) + { + d = d + 1.f; + + /* shift components along lateral bisector in proper orientation */ + shift.x = in.y + out.y; + shift.y = in.x + out.x; + + if ( orientation_negative ) + shift.x = -shift.x; + else + shift.y = -shift.y; + + /* restrict shift magnitude to better handle collapsing segments */ + q = out.x * in.y - out.y * in.x; + if ( orientation_negative ) + q = -q; + + l = hb_min (l_in, l_out); + + /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ + if (x_strength * q <= l * d) + shift.x = shift.x * x_strength / d; + else + shift.x = shift.x * l / q; + + + if (y_strength * q <= l * d) + shift.y = shift.y * y_strength / d; + else + shift.y = shift.y * l / q; + } + else + shift.x = shift.y = 0; + + for ( ; + i != j; + i = i < last ? i + 1 : first ) + { + points[i].x += x_strength + shift.x; + points[i].y += y_strength + shift.y; + } + } + else + i = j; + + in = out; + l_in = l_out; + } + + first = last + 1; + } +} + static void hb_outline_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, @@ -223,138 +354,4 @@ hb_outline_recording_pen_get_funcs () } -struct hb_draw_embolden_context_t : hb_outline_t -{ - void operator () (float x_strength, float y_strength) - { - if (!x_strength && !y_strength) return; - if (!points) return; - - x_strength /= 2.f; - y_strength /= 2.f; - - bool orientation_negative = area () < 0; - - struct vector_t - { - float normalize_len () - { - float len = hypotf (x, y); - if (len) - { - x /= len; - y /= len; - } - return len; - } - - float x, y; - }; - - signed first = 0; - for (unsigned c = 0; c < contours.length; c++) - { - vector_t in, out, anchor, shift; - float l_in, l_out, l_anchor = 0, l, q, d; - - l_in = 0; - signed last = (int) contours[c] - 1; - - /* pacify compiler */ - in.x = in.y = anchor.x = anchor.y = 0; - - /* Counter j cycles though the points; counter i advances only */ - /* when points are moved; anchor k marks the first moved point. */ - for ( signed i = last, j = first, k = -1; - j != i && i != k; - j = j < last ? j + 1 : first ) - { - if ( j != k ) - { - out.x = points[j].x - points[i].x; - out.y = points[j].y - points[i].y; - l_out = out.normalize_len (); - - if ( l_out == 0 ) - continue; - } - else - { - out = anchor; - l_out = l_anchor; - } - - if ( l_in != 0 ) - { - if ( k < 0 ) - { - k = i; - anchor = in; - l_anchor = l_in; - } - - d = in.x * out.x + in.y * out.y; - - /* shift only if turn is less than ~160 degrees */ - if ( d > -15.f/16.f ) - { - d = d + 1.f; - - /* shift components along lateral bisector in proper orientation */ - shift.x = in.y + out.y; - shift.y = in.x + out.x; - - if ( orientation_negative ) - shift.x = -shift.x; - else - shift.y = -shift.y; - - /* restrict shift magnitude to better handle collapsing segments */ - q = out.x * in.y - out.y * in.x; - if ( orientation_negative ) - q = -q; - - l = hb_min (l_in, l_out); - - /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ - if (x_strength * q <= l * d) - shift.x = shift.x * x_strength / d; - else - shift.x = shift.x * l / q; - - - if (y_strength * q <= l * d) - shift.y = shift.y * y_strength / d; - else - shift.y = shift.y * l / q; - } - else - shift.x = shift.y = 0; - - for ( ; - i != j; - i = i < last ? i + 1 : first ) - { - points[i].x += x_strength + shift.x; - points[i].y += y_strength + shift.y; - } - } - else - i = j; - - in = out; - l_in = l_out; - } - - first = last + 1; - } - } -}; - -static hb_draw_funcs_t * -hb_draw_embolden_get_funcs () -{ - return hb_outline_recording_pen_get_funcs (); -} - #endif /* HB_DRAW_EMBOLDEN_HH */ diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 42f7a6dd2..86e778615 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -464,10 +464,10 @@ hb_ot_draw_glyph (hb_font_t *font, void *user_data) { - hb_draw_embolden_context_t emboldener; - auto *embolden_funcs = hb_draw_embolden_get_funcs (); + hb_outline_t outline; + auto *pen = hb_outline_recording_pen_get_funcs (); - hb_draw_session_t draw_session (embolden_funcs, &emboldener, font->slant_xy); + hb_draw_session_t draw_session (pen, &outline, font->slant_xy); if (!font->face->table.glyf->get_path (font, glyph, draw_session)) #ifndef HB_NO_CFF if (!font->face->table.cff1->get_path (font, glyph, draw_session)) @@ -477,8 +477,8 @@ hb_ot_draw_glyph (hb_font_t *font, float xstr = font->x_scale / 20; float ystr = font->y_scale / 20; - emboldener (xstr, ystr); - emboldener.replay (draw_funcs, draw_data); + outline.embolden (xstr, ystr); + outline.replay (draw_funcs, draw_data); } #endif From ae522a1372c34bd013990de1b09d5cfa84433590 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:24:44 -0700 Subject: [PATCH 07/22] [embolden] Rename to hb-outline --- src/Makefile.sources | 2 + src/harfbuzz-subset.cc | 1 + src/harfbuzz.cc | 1 + src/hb-ot-font.cc | 2 +- src/{hb-draw-embolden.hh => hb-outline.cc} | 54 ++------------ src/hb-outline.hh | 82 ++++++++++++++++++++++ src/meson.build | 2 + 7 files changed, 94 insertions(+), 50 deletions(-) rename src/{hb-draw-embolden.hh => hb-outline.cc} (89%) create mode 100644 src/hb-outline.hh diff --git a/src/Makefile.sources b/src/Makefile.sources index 748dd17f7..9a96ad69f 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -85,6 +85,8 @@ HB_BASE_sources = \ hb-ot-layout-common.hh \ hb-ot-layout-gdef-table.hh \ hb-ot-layout-gpos-table.hh \ + hb-outline.hh \ + hb-outline.cc \ hb-paint.cc \ hb-paint.hh \ hb-paint-extents.cc \ diff --git a/src/harfbuzz-subset.cc b/src/harfbuzz-subset.cc index 196e32f5c..c0e23b3eb 100644 --- a/src/harfbuzz-subset.cc +++ b/src/harfbuzz-subset.cc @@ -41,6 +41,7 @@ #include "hb-ot-shaper-vowel-constraints.cc" #include "hb-ot-tag.cc" #include "hb-ot-var.cc" +#include "hb-outline.cc" #include "hb-paint-extents.cc" #include "hb-paint.cc" #include "hb-set.cc" diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc index 436f23dc2..d7e8a93f3 100644 --- a/src/harfbuzz.cc +++ b/src/harfbuzz.cc @@ -46,6 +46,7 @@ #include "hb-ot-shaper-vowel-constraints.cc" #include "hb-ot-tag.cc" #include "hb-ot-var.cc" +#include "hb-outline.cc" #include "hb-paint-extents.cc" #include "hb-paint.cc" #include "hb-set.cc" diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 86e778615..70e7c5e22 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -34,7 +34,7 @@ #include "hb-font.hh" #include "hb-machinery.hh" #include "hb-ot-face.hh" -#include "hb-draw-embolden.hh" +#include "hb-outline.hh" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" diff --git a/src/hb-draw-embolden.hh b/src/hb-outline.cc similarity index 89% rename from src/hb-draw-embolden.hh rename to src/hb-outline.cc index 1bc234a29..7ed486a80 100644 --- a/src/hb-draw-embolden.hh +++ b/src/hb-outline.cc @@ -22,58 +22,14 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ -#ifndef HB_DRAW_EMBOLDEN_HH -#define HB_DRAW_EMBOLDEN_HH - #include "hb.hh" -#include "hb-draw.hh" +#ifndef HB_NO_OUTLINE +#include "hb-outline.hh" -struct hb_outline_point_t -{ - enum class type_t - { - MOVE_TO, - LINE_TO, - QUADRATIC_TO, - CUBIC_TO, - }; +#include "hb-machinery.hh" - hb_outline_point_t (float x, float y, type_t type) : - x (x), y (y), type (type) {} - - float x, y; - type_t type; -}; - -struct hb_outline_vector_t -{ - float normalize_len () - { - float len = hypotf (x, y); - if (len) - { - x /= len; - y /= len; - } - return len; - } - - float x, y; -}; - -struct hb_outline_t -{ - void reset () { points.shrink (0, false); contours.resize (0); } - - HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; - HB_INTERNAL float area () const; - HB_INTERNAL void embolden (float x_strength, float y_strength); - - hb_vector_t points; - hb_vector_t contours; -}; void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const { @@ -347,11 +303,11 @@ void free_static_outline_recording_pen_funcs () static_outline_recording_pen_funcs.free_instance (); } -static hb_draw_funcs_t * +hb_draw_funcs_t * hb_outline_recording_pen_get_funcs () { return static_outline_recording_pen_funcs.get_unconst (); } -#endif /* HB_DRAW_EMBOLDEN_HH */ +#endif diff --git a/src/hb-outline.hh b/src/hb-outline.hh new file mode 100644 index 000000000..ffb3e52d1 --- /dev/null +++ b/src/hb-outline.hh @@ -0,0 +1,82 @@ +/* + * Copyright © 2023 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OUTLINE_HH +#define HB_OUTLINE_HH + +#include "hb.hh" + +#include "hb-draw.hh" + + +struct hb_outline_point_t +{ + enum class type_t + { + MOVE_TO, + LINE_TO, + QUADRATIC_TO, + CUBIC_TO, + }; + + hb_outline_point_t (float x, float y, type_t type) : + x (x), y (y), type (type) {} + + float x, y; + type_t type; +}; + +struct hb_outline_vector_t +{ + float normalize_len () + { + float len = hypotf (x, y); + if (len) + { + x /= len; + y /= len; + } + return len; + } + + float x, y; +}; + +struct hb_outline_t +{ + void reset () { points.shrink (0, false); contours.resize (0); } + + HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; + HB_INTERNAL float area () const; + HB_INTERNAL void embolden (float x_strength, float y_strength); + + hb_vector_t points; + hb_vector_t contours; +}; + +HB_INTERNAL hb_draw_funcs_t * +hb_outline_recording_pen_get_funcs (); + + +#endif /* HB_OUTLINE_HH */ diff --git a/src/meson.build b/src/meson.build index 62610ecb5..7e10de8d8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -94,6 +94,8 @@ hb_base_sources = files( 'hb-ot-layout-gdef-table.hh', 'hb-ot-layout-gpos-table.hh', 'hb-ot-layout-gsub-table.hh', + 'hb-outline.hh', + 'hb-outline.cc', 'OT/Color/CBDT/CBDT.hh', 'OT/Color/COLR/COLR.hh', 'OT/Color/CPAL/CPAL.hh', From 4247b78e31e00d02d3a6951888d5cae89d4e9060 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:26:07 -0700 Subject: [PATCH 08/22] [outline] Comment --- src/hb-outline.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hb-outline.cc b/src/hb-outline.cc index 7ed486a80..3dd53c7c5 100644 --- a/src/hb-outline.cc +++ b/src/hb-outline.cc @@ -103,6 +103,8 @@ float hb_outline_t::area () const void hb_outline_t::embolden (float x_strength, float y_strength) { + /* This function is a straight port of FreeType's FT_Outline_EmboldenXY */ + if (!x_strength && !y_strength) return; if (!points) return; From e39104ba1920a1bd2f6b4a56ace6cb66f7fcab6e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 16:56:56 -0700 Subject: [PATCH 09/22] [font/util] Add emboldening API, --font-bold Needs documentation. --- src/hb-font.cc | 34 ++++++++++++++++++++++++++++++++-- src/hb-font.h | 6 ++++++ src/hb-font.hh | 12 ++++++++++++ src/hb-ft.cc | 4 +--- src/hb-ot-font.cc | 4 +--- util/font-options.hh | 25 ++++++++++++++++++++++++- 6 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/hb-font.cc b/src/hb-font.cc index 8a43ceb86..752b05594 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -1772,8 +1772,12 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ - 0., /* slant */ - 0., /* slant_xy; */ + 0.f, /* x_embolden */ + 0.f, /* y_embolden */ + 0, /* x_shift */ + 0, /* y_shift */ + 0.f, /* slant */ + 0.f, /* slant_xy; */ 1.f, /* x_multf */ 1.f, /* y_multf */ 1<<16, /* x_mult */ @@ -1895,6 +1899,8 @@ hb_font_create_sub_font (hb_font_t *parent) font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->x_embolden = parent->x_embolden; + font->y_embolden = parent->y_embolden; font->slant = parent->slant; font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; @@ -2442,6 +2448,30 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +void +hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_embolden == x_embolden && + font->y_embolden == y_embolden) + return; + + font->serial++; + + font->x_embolden = x_embolden; + font->y_embolden = y_embolden; + font->mults_changed (); +} + +void +hb_font_get_synthetic_bold (hb_font_t *font, float *x_embolden, float *y_embolden) +{ + if (x_embolden) *x_embolden = font->x_embolden; + if (y_embolden) *y_embolden = font->y_embolden; +} + /** * hb_font_set_synthetic_slant: * @font: #hb_font_t to work upon diff --git a/src/hb-font.h b/src/hb-font.h index f187ddfaa..f90b6e20b 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -1131,6 +1131,12 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN void +hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden); + +HB_EXTERN void +hb_font_get_synthetic_bold (hb_font_t *font, float *x_embolden, float *y_embolden); + HB_EXTERN void hb_font_set_synthetic_slant (hb_font_t *font, float slant); diff --git a/src/hb-font.hh b/src/hb-font.hh index 292860a26..7a5b7eddc 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -113,8 +113,15 @@ struct hb_font_t int32_t x_scale; int32_t y_scale; + + float x_embolden; + float y_embolden; + int32_t x_shift; /* x_embolden, in scaled units. */ + int32_t y_shift; /* y_embolden, in scaled units. */ + float slant; float slant_xy; + float x_multf; float y_multf; int64_t x_mult; @@ -666,12 +673,17 @@ struct hb_font_t void mults_changed () { float upem = face->get_upem (); + x_multf = x_scale / upem; y_multf = y_scale / upem; bool x_neg = x_scale < 0; x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem; bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + + x_shift = roundf (x_scale * x_embolden); + y_shift = roundf (y_scale * y_embolden); + slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; data.fini (); diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 3005828f0..35313569d 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -844,9 +844,7 @@ hb_ft_draw_glyph (hb_font_t *font, hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); - hb_position_t xstr = font->x_scale / 20; - hb_position_t ystr = font->y_scale / 20; - FT_Outline_EmboldenXY (&ft_face->glyph->outline, xstr, ystr); + FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_shift, font->y_shift); FT_Outline_Decompose (&ft_face->glyph->outline, &outline_funcs, &draw_session); diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 70e7c5e22..6b188ee9e 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -475,9 +475,7 @@ hb_ot_draw_glyph (hb_font_t *font, #endif {} - float xstr = font->x_scale / 20; - float ystr = font->y_scale / 20; - outline.embolden (xstr, ystr); + outline.embolden (font->x_shift, font->y_shift); outline.replay (draw_funcs, draw_data); } diff --git a/util/font-options.hh b/util/font-options.hh index efc5a1a8b..5b5de2265 100644 --- a/util/font-options.hh +++ b/util/font-options.hh @@ -63,6 +63,8 @@ struct font_options_t : face_options_t int x_ppem = 0; int y_ppem = 0; double ptem = 0.; + double x_embolden = 0.; + double y_embolden = 0.; double slant = 0.; unsigned int subpixel_bits = SUBPIXEL_BITS; mutable double font_size_x = DEFAULT_FONT_SIZE; @@ -101,6 +103,7 @@ font_options_t::post_parse (GError **error) hb_font_set_ppem (font, x_ppem, y_ppem); hb_font_set_ptem (font, ptem); + hb_font_set_synthetic_bold (font, (float) x_embolden, (float) y_embolden); hb_font_set_synthetic_slant (font, slant); int scale_x = (int) scalbnf (font_size_x, subpixel_bits); @@ -245,6 +248,24 @@ parse_font_ppem (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; + switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->x_embolden, &font_opts->y_embolden)) { + case 1: font_opts->y_embolden = font_opts->x_embolden; HB_FALLTHROUGH; + case 2: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one or two space-separated numbers", + name); + return false; + } +} + void font_options_t::add_options (option_parser_t *parser) { @@ -286,7 +307,9 @@ font_options_t::add_options (option_parser_t *parser) G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, {"font-ptem", 0, font_size_flags, G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, - {"font-slant", 0, 0, + {"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"}, + {"font-slant", 0, font_size_flags, 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"}, {"sub-font", 0, G_OPTION_FLAG_HIDDEN, From 6b3fe8ac1beeb97194e5171b5fe3873236879fdd Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 17:00:14 -0700 Subject: [PATCH 10/22] [embolden] Semi-handle with negative scales --- src/hb-font.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hb-font.hh b/src/hb-font.hh index 7a5b7eddc..767382e7c 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -681,8 +681,8 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; - x_shift = roundf (x_scale * x_embolden); - y_shift = roundf (y_scale * y_embolden); + x_shift = fabs (roundf (x_scale * x_embolden)); + y_shift = fabs (roundf (y_scale * y_embolden)); slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; From 36dcc9a4327f824ccaa5751412707731504e1023 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 17:06:15 -0700 Subject: [PATCH 11/22] [ot-font] Fix emboldening CFF --- src/hb-ot-font.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 6b188ee9e..341b4fbe1 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -467,13 +467,15 @@ hb_ot_draw_glyph (hb_font_t *font, hb_outline_t outline; auto *pen = hb_outline_recording_pen_get_funcs (); - hb_draw_session_t draw_session (pen, &outline, font->slant_xy); - if (!font->face->table.glyf->get_path (font, glyph, draw_session)) + { // Need draw_session to be destructed before emboldening. + hb_draw_session_t draw_session (pen, &outline, font->slant_xy); + if (!font->face->table.glyf->get_path (font, glyph, draw_session)) #ifndef HB_NO_CFF - if (!font->face->table.cff1->get_path (font, glyph, draw_session)) - if (!font->face->table.cff2->get_path (font, glyph, draw_session)) + if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (!font->face->table.cff2->get_path (font, glyph, draw_session)) #endif - {} + {} + } outline.embolden (font->x_shift, font->y_shift); outline.replay (draw_funcs, draw_data); From b087266e511d21b5c63b02fa7eed45af4061e543 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 17:09:29 -0700 Subject: [PATCH 12/22] [ot-font] Conditionalize emboldening --- src/hb-ot-font.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 341b4fbe1..43ec4227d 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -463,12 +463,12 @@ hb_ot_draw_glyph (hb_font_t *font, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data) { - + bool embolden = font->x_shift || font->y_shift; hb_outline_t outline; - auto *pen = hb_outline_recording_pen_get_funcs (); { // Need draw_session to be destructed before emboldening. - hb_draw_session_t draw_session (pen, &outline, font->slant_xy); + hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, + embolden ? &outline : draw_data, font->slant_xy); if (!font->face->table.glyf->get_path (font, glyph, draw_session)) #ifndef HB_NO_CFF if (!font->face->table.cff1->get_path (font, glyph, draw_session)) @@ -477,9 +477,11 @@ hb_ot_draw_glyph (hb_font_t *font, {} } - outline.embolden (font->x_shift, font->y_shift); - outline.replay (draw_funcs, draw_data); - + if (embolden) + { + outline.embolden (font->x_shift, font->y_shift); + outline.replay (draw_funcs, draw_data); + } } #endif From 2119eab69f5e8c5323fa23ab6c7dc26c2ab5aab3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 1 Feb 2023 17:37:10 -0700 Subject: [PATCH 13/22] [embolden] Adjust advance values --- src/hb-ft.cc | 16 +++++++++++++++- src/hb-ot-font.cc | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 35313569d..064fff15d 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -448,6 +448,7 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_position_t *orig_first_advance = first_advance; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; int load_flags = ft_font->load_flags; @@ -488,6 +489,18 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + + if (font->x_shift) + { + /* Emboldening. */ + hb_position_t x_shift = font->x_scale >= 0 ? font->x_shift : -font->x_shift; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_shift : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #ifndef HB_NO_VERTICAL @@ -523,7 +536,8 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ - return (-v + (1<<9)) >> 10; + hb_position_t y_shift = font->y_scale >= 0 ? font->y_shift : -font->y_shift; + return ((-v + (1<<9)) >> 10) + y_shift; } #endif diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 43ec4227d..6cfe8cf56 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -181,10 +181,13 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + hb_position_t *orig_first_advance = first_advance; + #ifndef HB_NO_VAR const OT::HVAR &HVAR = *hmtx.var_table; const OT::VariationStore &varStore = &HVAR + HVAR.varStore; @@ -258,6 +261,18 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, #ifndef HB_NO_VAR OT::VariationStore::destroy_cache (varStore_cache); #endif + + if (font->x_shift) + { + /* Emboldening. */ + hb_position_t x_shift = font->x_scale >= 0 ? font->x_shift : -font->x_shift; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_shift : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #ifndef HB_NO_VERTICAL @@ -274,6 +289,8 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + hb_position_t *orig_first_advance = first_advance; + if (vmtx.has_data ()) { #ifndef HB_NO_VAR @@ -308,6 +325,18 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } } + + if (font->y_shift) + { + /* Emboldening. */ + hb_position_t y_shift = font->y_scale >= 0 ? font->y_shift : -font->y_shift; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? y_shift : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #endif From 061f995845f347a481e4ff6f66fd66c6b50bfcfb Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 2 Feb 2023 08:15:02 +0100 Subject: [PATCH 14/22] [font] Document synthetic boldness APIs --- src/hb-font.cc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/hb-font.cc b/src/hb-font.cc index 752b05594..715042d0d 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -2448,6 +2448,27 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_set_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: the amount to embolden horizontally + * @y_embolden: the amount to embolden vertically + * + * Sets the "synthetic boldness" of a font. + * + * Positive values make a font bolder, negative values + * thinner. Typical values are in the 0.01 to 0.05 range. + * The default value is zero. + * + * Synthetic boldness is applied by offsetting the contour + * points of the glyph shape. + * + * HarfBuzz applies synthetic boldness when rendering a glyph + * via hb_font_draw_glyph(), and also uses the values to adjust + * the advance width of emboldened glyphs. + * + * Since: REPLACEME + **/ void hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden) { @@ -2465,6 +2486,16 @@ hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden) font->mults_changed (); } +/** + * hb_font_get_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: return location for horizontal value + * @y_embolden: return location for vertical value + * + * Fetches the "synthetic boldness" of a font. + * + * Since: REPLACEME + **/ void hb_font_get_synthetic_bold (hb_font_t *font, float *x_embolden, float *y_embolden) { From cf39d316d86edb253873143596484baaeddce30e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 2 Feb 2023 10:45:35 -0700 Subject: [PATCH 15/22] [outline] Add FreeType authors copyrights --- COPYING | 4 ++-- src/hb-outline.cc | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/COPYING b/COPYING index 1350547ff..1dd917e9f 100644 --- a/COPYING +++ b/COPYING @@ -12,13 +12,13 @@ Copyright © 2009 Keith Stribley Copyright © 2011 Martin Hosken and SIL International Copyright © 2007 Chris Wilson Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod -Copyright © 2005 David Turner Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. -Copyright © 1998-2004 David Turner and Werner Lemberg +Copyright © 1998-2005 David Turner and Werner Lemberg Copyright © 2016 Igalia S.L. Copyright © 2022 Matthias Clasen Copyright © 2018,2021 Khaled Hosny Copyright © 2018,2019,2020 Adobe, Inc +Copyright © 2013-2015 Alexei Podtelezhnikov For full copyright notices consult the individual files in the package. diff --git a/src/hb-outline.cc b/src/hb-outline.cc index 3dd53c7c5..1cf166336 100644 --- a/src/hb-outline.cc +++ b/src/hb-outline.cc @@ -1,5 +1,9 @@ /* * Copyright © 2023 Behdad Esfahbod + * Copyright © 1999 David Turner + * Copyright © 2005 Werner Lemberg + * Copyright © 2013-2015 Alexei Podtelezhnikov + * * * This is part of HarfBuzz, a text shaping library. * @@ -103,7 +107,9 @@ float hb_outline_t::area () const void hb_outline_t::embolden (float x_strength, float y_strength) { - /* 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 + * to relicense it under the HarfBuzz license. */ if (!x_strength && !y_strength) return; if (!points) return; From d250fd979b54d26b7f432c809a153c3f90f020a9 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 2 Feb 2023 10:57:30 -0700 Subject: [PATCH 16/22] [font] Docs --- docs/harfbuzz-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 254e809b7..885a71a91 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -416,6 +416,8 @@ hb_font_set_ptem hb_font_get_ptem hb_font_set_scale hb_font_get_scale +hb_font_get_synthetic_bold +hb_font_set_synthetic_bold hb_font_set_synthetic_slant hb_font_get_synthetic_slant hb_font_set_variations From aef002e0d92caeed512ae1f40904d02ebcb8d506 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 7 Feb 2023 11:29:49 -0700 Subject: [PATCH 17/22] [embolden] Add in-place option Adds --font-grade to hb-view and hb-shape. --- src/hb-font.cc | 44 ++++++++++++++++++++++++++++++-------------- src/hb-font.h | 8 ++++++-- src/hb-font.hh | 9 +++++---- src/hb-ft.cc | 43 +++++++++++++++++++++++++++++++++++++------ src/hb-ot-font.cc | 22 ++++++++++++++-------- src/hb-outline.cc | 7 ++++--- src/hb-outline.hh | 3 ++- util/font-options.hh | 37 ++++++++++++++++++++++++++++++++----- 8 files changed, 130 insertions(+), 43 deletions(-) diff --git a/src/hb-font.cc b/src/hb-font.cc index 715042d0d..a3d394f87 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -1774,8 +1774,9 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* y_scale */ 0.f, /* x_embolden */ 0.f, /* y_embolden */ - 0, /* x_shift */ - 0, /* y_shift */ + true, /* embolden_in_place */ + 0, /* x_strength */ + 0, /* y_strength */ 0.f, /* slant */ 0.f, /* slant_xy; */ 1.f, /* x_multf */ @@ -1815,6 +1816,7 @@ _hb_font_create (hb_face_t *face) font->klass = hb_font_funcs_get_empty (); font->data.init0 (font); font->x_scale = font->y_scale = face->get_upem (); + font->embolden_in_place = true; font->x_multf = font->y_multf = 1.f; font->x_mult = font->y_mult = 1 << 16; 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->x_embolden = parent->x_embolden; font->y_embolden = parent->y_embolden; + font->embolden_in_place = parent->embolden_in_place; font->slant = parent->slant; font->x_ppem = parent->x_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 * @x_embolden: the amount to embolden horizontally * @y_embolden: the amount to embolden vertically + * @in_place: whether to embolden glyphs in-place * * Sets the "synthetic boldness" of a font. * - * Positive values make a font bolder, negative values - * thinner. Typical values are in the 0.01 to 0.05 range. - * The default value is zero. + * Positive values for @x_embolden / @y_embolden make a font + * bolder, negative values thinner. Typical values are in the + * 0.01 to 0.05 range. The default value is zero. * * Synthetic boldness is applied by offsetting the contour * points of the glyph shape. * - * HarfBuzz applies synthetic boldness when rendering a glyph - * via hb_font_draw_glyph(), and also uses the values to adjust - * the advance width of emboldened glyphs. + * Synthetic boldness is applied when rendering a glyph via + * hb_font_draw_glyph(). + * + * If @in_place is %FALSE, then glyph advance-widths are also + * adjusted, otherwise they are not. * * Since: REPLACEME **/ 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)) return; if (font->x_embolden == x_embolden && - font->y_embolden == y_embolden) + font->y_embolden == y_embolden && + font->embolden_in_place == in_place) return; font->serial++; font->x_embolden = x_embolden; font->y_embolden = y_embolden; + font->embolden_in_place = in_place; font->mults_changed (); } /** * hb_font_get_synthetic_bold: * @font: #hb_font_t to work upon - * @x_embolden: return location for horizontal value - * @y_embolden: return location for vertical value + * @x_embolden: (out): return location for horizontal 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 **/ 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 (y_embolden) *y_embolden = font->y_embolden; + if (in_place) *in_place = font->embolden_in_place; } /** diff --git a/src/hb-font.h b/src/hb-font.h index f90b6e20b..f5d79e3f0 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -1132,10 +1132,14 @@ HB_EXTERN float hb_font_get_ptem (hb_font_t *font); 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_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_font_set_synthetic_slant (hb_font_t *font, float slant); diff --git a/src/hb-font.hh b/src/hb-font.hh index 767382e7c..4520c7885 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -116,8 +116,9 @@ struct hb_font_t float x_embolden; float y_embolden; - int32_t x_shift; /* x_embolden, in scaled units. */ - int32_t y_shift; /* y_embolden, in scaled units. */ + bool embolden_in_place; + int32_t x_strength; /* x_embolden, in scaled units. */ + int32_t y_strength; /* y_embolden, in scaled units. */ float slant; float slant_xy; @@ -681,8 +682,8 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; - x_shift = fabs (roundf (x_scale * x_embolden)); - y_shift = fabs (roundf (y_scale * y_embolden)); + x_strength = fabs (roundf (x_scale * x_embolden)); + y_strength = fabs (roundf (y_scale * y_embolden)); slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 064fff15d..af15c9e63 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -490,14 +490,14 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } - if (font->x_shift) + if (font->x_strength && !font->embolden_in_place) { /* 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; 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 (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 * have a Y growing upward. Hence the extra negation. */ - hb_position_t y_shift = font->y_scale >= 0 ? font->y_shift : -font->y_shift; - return ((-v + (1<<9)) >> 10) + y_shift; + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); } #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); - 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, &outline_funcs, &draw_session); diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 6cfe8cf56..41c8c1f6d 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -262,14 +262,14 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, OT::VariationStore::destroy_cache (varStore_cache); #endif - if (font->x_shift) + if (font->x_strength && !font->embolden_in_place) { /* 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; 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 (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. */ - 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; 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 (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, void *user_data) { - bool embolden = font->x_shift || font->y_shift; + bool embolden = font->x_strength || font->y_strength; hb_outline_t outline; { // Need draw_session to be destructed before emboldening. @@ -508,7 +508,13 @@ hb_ot_draw_glyph (hb_font_t *font, 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); } } diff --git a/src/hb-outline.cc b/src/hb-outline.cc index 1cf166336..184e48cfb 100644 --- a/src/hb-outline.cc +++ b/src/hb-outline.cc @@ -105,7 +105,8 @@ float hb_outline_t::area () const 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. * 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 = i < last ? i + 1 : first ) { - points[i].x += x_strength + shift.x; - points[i].y += y_strength + shift.y; + points[i].x += x_shift + shift.x; + points[i].y += y_shift + shift.y; } } else diff --git a/src/hb-outline.hh b/src/hb-outline.hh index ffb3e52d1..c463993cf 100644 --- a/src/hb-outline.hh +++ b/src/hb-outline.hh @@ -69,7 +69,8 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) 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 points; hb_vector_t contours; diff --git a/util/font-options.hh b/util/font-options.hh index 5b5de2265..8ec36c97e 100644 --- a/util/font-options.hh +++ b/util/font-options.hh @@ -65,6 +65,7 @@ struct font_options_t : face_options_t double ptem = 0.; double x_embolden = 0.; double y_embolden = 0.; + hb_bool_t embolden_in_place = false; double slant = 0.; unsigned int subpixel_bits = SUBPIXEL_BITS; 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_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); 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 -parse_font_bold (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) +parse_font_embolden (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; 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 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"}, {"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"}, + {"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, 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"}, From 560a65e456275e927d64f650235bdaa10049ee50 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 7 Feb 2023 13:46:13 -0700 Subject: [PATCH 18/22] [embolden] Update glyph_extents in hb-ot-font --- src/hb-font.hh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/hb-font.hh b/src/hb-font.hh index 4520c7885..a9b1e0bce 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -208,6 +208,21 @@ struct hb_font_t extents->width = ceilf (x2) - extents->x_bearing; extents->height = ceilf (y2) - extents->y_bearing; + if (x_strength || y_strength) + { + /* Y */ + int y_shift = y_strength; + if (y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = x_strength; + if (x_scale < 0) x_shift = -x_shift; + if (embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } } From 61a1a88940f808f0f1184c6afdfbf025f21c1527 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 7 Feb 2023 13:47:04 -0700 Subject: [PATCH 19/22] [hb-ft] Fix --font-grade --- src/hb-ft.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index af15c9e63..5ca08d0a6 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -881,7 +881,7 @@ hb_ft_draw_glyph (hb_font_t *font, if (x_shift || y_shift) { auto &outline = ft_face->glyph->outline; - for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1])) + for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) { point.x += x_shift; point.y += y_shift; From b350122fb3af6d4eff9a2cf9c8fc3b7157601944 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 7 Feb 2023 13:49:16 -0700 Subject: [PATCH 20/22] [embolden] Fix glyph_extents in hb-ft --- src/hb-ft.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 5ca08d0a6..6cf27203d 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -657,6 +657,22 @@ hb_ft_get_glyph_extents (hb_font_t *font, extents->width = ceilf (x2) - extents->x_bearing; extents->height = ceilf (y2) - extents->y_bearing; + if (font->x_strength || font->y_strength) + { + /* Y */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = font->x_strength; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + return true; } From be1c14ee0ad7702250f2a8b1969387d8018d4012 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 7 Feb 2023 13:52:53 -0700 Subject: [PATCH 21/22] [embolden] Adjust font_h_extents --- src/hb-ft.cc | 2 +- src/hb-ot-font.cc | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 6cf27203d..4bc10e062 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -794,7 +794,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); } - metrics->ascender = (hb_position_t) (y_mult * metrics->ascender); + metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); metrics->descender = (hb_position_t) (y_mult * metrics->descender); metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 41c8c1f6d..578405dd9 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -466,9 +466,16 @@ hb_ot_get_font_h_extents (hb_font_t *font, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + + /* Embolden */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + metrics->ascender += y_shift; + + return ret; } #ifndef HB_NO_VERTICAL From 737b15c5a0251d1579bc4b6a41cb08bc8c66e275 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 8 Feb 2023 17:45:59 -0700 Subject: [PATCH 22/22] [embolden] Docs --- src/hb-font.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hb-font.cc b/src/hb-font.cc index a3d394f87..743305107 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -2471,7 +2471,9 @@ hb_font_get_ptem (hb_font_t *font) * hb_font_draw_glyph(). * * If @in_place is %FALSE, then glyph advance-widths are also - * adjusted, otherwise they are not. + * adjusted, otherwise they are not. The in-place mode is + * useful for simulating [font grading](https://fonts.google.com/knowledge/glossary/grade). + * * * Since: REPLACEME **/