/* * Copyright © 2022 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_GEOMETRY_HH #define HB_GEOMETRY_HH #include "hb.hh" #include "hb-algs.hh" template struct hb_extents_t { hb_extents_t () {} hb_extents_t (const hb_glyph_extents_t &extents) : xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)), ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) : xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} bool is_empty () const { return xmin >= xmax || ymin >= ymax; } bool is_void () const { return xmin > xmax; } void union_ (const hb_extents_t &o) { if (o.is_empty ()) return; if (is_empty ()) { *this = o; return; } xmin = hb_min (xmin, o.xmin); ymin = hb_min (ymin, o.ymin); xmax = hb_max (xmax, o.xmax); ymax = hb_max (ymax, o.ymax); } void intersect (const hb_extents_t &o) { if (o.is_empty () || is_empty ()) { *this = hb_extents_t {}; return; } xmin = hb_max (xmin, o.xmin); ymin = hb_max (ymin, o.ymin); xmax = hb_min (xmax, o.xmax); ymax = hb_min (ymax, o.ymax); } void add_point (Float x, Float y) { if (unlikely (is_void ())) { xmin = xmax = x; ymin = ymax = y; } else { xmin = hb_min (xmin, x); ymin = hb_min (ymin, y); xmax = hb_max (xmax, x); ymax = hb_max (ymax, y); } } hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const { hb_position_t x0 = (hb_position_t) roundf (xmin); hb_position_t y0 = (hb_position_t) roundf (ymin); hb_position_t x1 = (hb_position_t) roundf (xmax); hb_position_t y1 = (hb_position_t) roundf (ymax); return hb_glyph_extents_t {xneg ? x1 : x0, yneg ? y0 : y1, xneg ? x0 - x1 : x1 - x0, yneg ? y1 - y0 : y0 - y1}; } Float xmin = 0; Float ymin = 0; Float xmax = -1; Float ymax = -1; }; template struct hb_transform_t { hb_transform_t () {} hb_transform_t (Float xx, Float yx, Float xy, Float yy, Float x0, Float y0) : xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} bool is_identity () const { return xx == 1 && yx == 0 && xy == 0 && yy == 1 && x0 == 0 && y0 == 0; } bool is_translation () const { return xx == 1 && yx == 0 && xy == 0 && yy == 1; } void multiply (const hb_transform_t &o, bool before=false) { // Copied from cairo-matrix.c const hb_transform_t &a = before ? o : *this; const hb_transform_t &b = before ? *this : o; *this = { a.xx * b.xx + a.xy * b.yx, a.yx * b.xx + a.yy * b.yx, a.xx * b.xy + a.xy * b.yy, a.yx * b.xy + a.yy * b.yy, a.xx * b.x0 + a.xy * b.y0 + a.x0, a.yx * b.x0 + a.yy * b.y0 + a.y0 }; } HB_ALWAYS_INLINE void transform_distance (Float &dx, Float &dy) const { Float new_x = xx * dx + xy * dy; Float new_y = yx * dx + yy * dy; dx = new_x; dy = new_y; } HB_ALWAYS_INLINE void transform_point (Float &x, Float &y) const { Float new_x = x0 + xx * x + xy * y; Float new_y = y0 + yx * x + yy * y; x = new_x; y = new_y; } void transform_extents (hb_extents_t &extents) const { Float quad_x[4], quad_y[4]; quad_x[0] = extents.xmin; quad_y[0] = extents.ymin; quad_x[1] = extents.xmin; quad_y[1] = extents.ymax; quad_x[2] = extents.xmax; quad_y[2] = extents.ymin; quad_x[3] = extents.xmax; quad_y[3] = extents.ymax; extents = hb_extents_t {}; for (unsigned i = 0; i < 4; i++) { transform_point (quad_x[i], quad_y[i]); extents.add_point (quad_x[i], quad_y[i]); } } void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); } static hb_transform_t translation (Float x, Float y) { return {1, 0, 0, 1, x, y}; } void translate (Float x, Float y, bool before=false) { if (before) { x0 += x; y0 += y; } else { if (x == 0 && y == 0) return; x0 += xx * x + xy * y; y0 += yx * x + yy * y; } } static hb_transform_t scaling (Float scaleX, Float scaleY) { return {scaleX, 0, 0, scaleY, 0, 0}; } void scale (Float scaleX, Float scaleY) { if (scaleX == 1 && scaleY == 1) return; xx *= scaleX; yx *= scaleX; xy *= scaleY; yy *= scaleY; } static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) { return {scaleX, 0, 0, scaleY, center_x ? (1 - scaleX) * center_x : 0, center_y ? (1 - scaleY) * center_y : 0}; } void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) { if (scaleX == 1 && scaleY == 1) return; transform (scaling_around_center (scaleX, scaleY, center_x, center_y)); } static hb_transform_t rotation (Float radians) { // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 Float c; Float s; hb_sincos (radians, s, c); return {c, s, -s, c, 0, 0}; } void rotate (Float radians, bool before=false) { if (radians == 0) return; transform (rotation (radians), before); } static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y) { Float s, c; hb_sincos (radians, s, c); return { c, s, -s, c, (1 - c) * center_x + s * center_y, -s * center_x + (1 - c) * center_y }; } void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false) { if (radians == 0) return; transform (rotation_around_center (radians, center_x, center_y), before); } static hb_transform_t skewing (Float skewX, Float skewY) { return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0}; } void skew (Float skewX, Float skewY) { if (skewX == 0 && skewY == 0) return; transform (skewing (skewX, skewY)); } static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y) { skewX = skewX ? tanf (skewX) : 0; skewY = skewY ? tanf (skewY) : 0; return { 1, skewY, skewX, 1, center_y ? -skewX * center_y : 0, center_x ? -skewY * center_x : 0 }; } void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y) { if (skewX == 0 && skewY == 0) return; transform (skewing_around_center (skewX, skewY, center_x, center_y)); } Float xx = 1; Float yx = 0; Float xy = 0; Float yy = 1; Float x0 = 0; Float y0 = 0; }; #define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0} template struct hb_bounds_t { enum status_t { UNBOUNDED, BOUNDED, EMPTY, }; hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} hb_bounds_t (const hb_extents_t &extents) : status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} void union_ (const hb_bounds_t &o) { if (o.status == UNBOUNDED) status = UNBOUNDED; else if (o.status == BOUNDED) { if (status == EMPTY) *this = o; else if (status == BOUNDED) extents.union_ (o.extents); } } void intersect (const hb_bounds_t &o) { if (o.status == EMPTY) status = EMPTY; else if (o.status == BOUNDED) { if (status == UNBOUNDED) *this = o; else if (status == BOUNDED) { extents.intersect (o.extents); if (extents.is_empty ()) status = EMPTY; } } } status_t status; hb_extents_t extents; }; template struct hb_transform_decomposed_t { Float translateX = 0; Float translateY = 0; Float rotation = 0; // in radians, counter-clockwise Float scaleX = 1; Float scaleY = 1; Float skewX = 0; // in radians, counter-clockwise Float skewY = 0; // in radians, counter-clockwise Float tCenterX = 0; Float tCenterY = 0; operator bool () const { return translateX || translateY || rotation || scaleX != 1 || scaleY != 1 || skewX || skewY || tCenterX || tCenterY; } hb_transform_t to_transform () const { hb_transform_t t; t.translate (translateX + tCenterX, translateY + tCenterY); t.rotate (rotation); t.scale (scaleX, scaleY); t.skew (-skewX, skewY); t.translate (-tCenterX, -tCenterY); return t; } }; #endif /* HB_GEOMETRY_HH */