| /* | |
| * 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. | |
| * | |
| * 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. | |
| */ | |
| #include "hb.hh" | |
| #ifndef HB_NO_OUTLINE | |
| #include "hb-outline.hh" | |
| #include "hb-machinery.hh" | |
| 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; | |
| } | |
| } | |
| void hb_outline_t::translate (float dx, float dy) | |
| { | |
| for (auto &p : points) | |
| { | |
| p.x += dx; | |
| p.y += dy; | |
| } | |
| } | |
| void hb_outline_t::slant (float slant_xy) | |
| { | |
| for (auto &p : points) | |
| p.x += slant_xy * p.y; | |
| } | |
| float hb_outline_t::control_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, | |
| 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 | |
| * to relicense it under the HarfBuzz license. */ | |
| if (!x_strength && !y_strength) return; | |
| if (!points) return; | |
| x_strength /= 2.f; | |
| y_strength /= 2.f; | |
| bool orientation_negative = control_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_shift + shift.x; | |
| points[i].y += y_shift + 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, | |
| hb_draw_state_t *st, | |
| float to_x, float to_y, | |
| void *user_data HB_UNUSED) | |
| { | |
| hb_outline_t *c = (hb_outline_t *) data; | |
| c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::MOVE_TO}); | |
| } | |
| static void | |
| 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_outline_t *c = (hb_outline_t *) data; | |
| c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::LINE_TO}); | |
| } | |
| static void | |
| 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_outline_t *c = (hb_outline_t *) data; | |
| 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_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_outline_t *c = (hb_outline_t *) data; | |
| 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_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_outline_t *c = (hb_outline_t *) data; | |
| c->contours.push (c->points.length); | |
| } | |
| static inline void free_static_outline_recording_pen_funcs (); | |
| static struct hb_outline_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_outline_recording_pen_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_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_outline_recording_pen_funcs); | |
| return funcs; | |
| } | |
| } static_outline_recording_pen_funcs; | |
| static inline | |
| void free_static_outline_recording_pen_funcs () | |
| { | |
| static_outline_recording_pen_funcs.free_instance (); | |
| } | |
| hb_draw_funcs_t * | |
| hb_outline_recording_pen_get_funcs () | |
| { | |
| return static_outline_recording_pen_funcs.get_unconst (); | |
| } | |
| #endif |