1 /* 2 * Copyright © 2022 Behdad Esfahbod 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 */ 24 #ifndef HB_GEOMETRY_HH 25 #define HB_GEOMETRY_HH 26 27 #include "hb.hh" 28 29 30 struct hb_extents_t 31 { hb_extents_thb_extents_t32 hb_extents_t () {} hb_extents_thb_extents_t33 hb_extents_t (float xmin, float ymin, float xmax, float ymax) : 34 xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} 35 is_emptyhb_extents_t36 bool is_empty () const { return xmin >= xmax || ymin >= ymax; } is_voidhb_extents_t37 bool is_void () const { return xmin > xmax; } 38 union_hb_extents_t39 void union_ (const hb_extents_t &o) 40 { 41 xmin = hb_min (xmin, o.xmin); 42 ymin = hb_min (ymin, o.ymin); 43 xmax = hb_max (xmax, o.xmax); 44 ymax = hb_max (ymax, o.ymax); 45 } 46 intersecthb_extents_t47 void intersect (const hb_extents_t &o) 48 { 49 xmin = hb_max (xmin, o.xmin); 50 ymin = hb_max (ymin, o.ymin); 51 xmax = hb_min (xmax, o.xmax); 52 ymax = hb_min (ymax, o.ymax); 53 } 54 55 void add_pointhb_extents_t56 add_point (float x, float y) 57 { 58 if (unlikely (is_void ())) 59 { 60 xmin = xmax = x; 61 ymin = ymax = y; 62 } 63 else 64 { 65 xmin = hb_min (xmin, x); 66 ymin = hb_min (ymin, y); 67 xmax = hb_max (xmax, x); 68 ymax = hb_max (ymax, y); 69 } 70 } 71 72 float xmin = 0.f; 73 float ymin = 0.f; 74 float xmax = -1.f; 75 float ymax = -1.f; 76 }; 77 78 struct hb_transform_t 79 { hb_transform_thb_transform_t80 hb_transform_t () {} hb_transform_thb_transform_t81 hb_transform_t (float xx, float yx, 82 float xy, float yy, 83 float x0, float y0) : 84 xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} 85 multiplyhb_transform_t86 void multiply (const hb_transform_t &o) 87 { 88 /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ 89 hb_transform_t r; 90 91 r.xx = o.xx * xx + o.yx * xy; 92 r.yx = o.xx * yx + o.yx * yy; 93 94 r.xy = o.xy * xx + o.yy * xy; 95 r.yy = o.xy * yx + o.yy * yy; 96 97 r.x0 = o.x0 * xx + o.y0 * xy + x0; 98 r.y0 = o.x0 * yx + o.y0 * yy + y0; 99 100 *this = r; 101 } 102 transform_distancehb_transform_t103 void transform_distance (float &dx, float &dy) const 104 { 105 float new_x = xx * dx + xy * dy; 106 float new_y = yx * dx + yy * dy; 107 dx = new_x; 108 dy = new_y; 109 } 110 transform_pointhb_transform_t111 void transform_point (float &x, float &y) const 112 { 113 transform_distance (x, y); 114 x += x0; 115 y += y0; 116 } 117 transform_extentshb_transform_t118 void transform_extents (hb_extents_t &extents) const 119 { 120 float quad_x[4], quad_y[4]; 121 122 quad_x[0] = extents.xmin; 123 quad_y[0] = extents.ymin; 124 quad_x[1] = extents.xmin; 125 quad_y[1] = extents.ymax; 126 quad_x[2] = extents.xmax; 127 quad_y[2] = extents.ymin; 128 quad_x[3] = extents.xmax; 129 quad_y[3] = extents.ymax; 130 131 extents = hb_extents_t {}; 132 for (unsigned i = 0; i < 4; i++) 133 { 134 transform_point (quad_x[i], quad_y[i]); 135 extents.add_point (quad_x[i], quad_y[i]); 136 } 137 } 138 transformhb_transform_t139 void transform (const hb_transform_t &o) { multiply (o); } 140 translatehb_transform_t141 void translate (float x, float y) 142 { 143 if (x == 0.f && y == 0.f) 144 return; 145 146 x0 += xx * x + xy * y; 147 y0 += yx * x + yy * y; 148 } 149 scalehb_transform_t150 void scale (float scaleX, float scaleY) 151 { 152 if (scaleX == 1.f && scaleY == 1.f) 153 return; 154 155 xx *= scaleX; 156 yx *= scaleX; 157 xy *= scaleY; 158 yy *= scaleY; 159 } 160 rotatehb_transform_t161 void rotate (float rotation) 162 { 163 if (rotation == 0.f) 164 return; 165 166 // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 167 rotation = rotation * HB_PI; 168 float c; 169 float s; 170 #ifdef HAVE_SINCOSF 171 sincosf (rotation, &s, &c); 172 #else 173 c = cosf (rotation); 174 s = sinf (rotation); 175 #endif 176 auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f}; 177 transform (other); 178 } 179 skewhb_transform_t180 void skew (float skewX, float skewY) 181 { 182 if (skewX == 0.f && skewY == 0.f) 183 return; 184 185 // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 186 skewX = skewX * HB_PI; 187 skewY = skewY * HB_PI; 188 auto other = hb_transform_t{1.f, 189 skewY ? tanf (skewY) : 0.f, 190 skewX ? tanf (skewX) : 0.f, 191 1.f, 192 0.f, 0.f}; 193 transform (other); 194 } 195 196 float xx = 1.f; 197 float yx = 0.f; 198 float xy = 0.f; 199 float yy = 1.f; 200 float x0 = 0.f; 201 float y0 = 0.f; 202 }; 203 204 struct hb_bounds_t 205 { 206 enum status_t { 207 UNBOUNDED, 208 BOUNDED, 209 EMPTY, 210 }; 211 hb_bounds_thb_bounds_t212 hb_bounds_t (status_t status) : status (status) {} hb_bounds_thb_bounds_t213 hb_bounds_t (const hb_extents_t &extents) : 214 status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} 215 union_hb_bounds_t216 void union_ (const hb_bounds_t &o) 217 { 218 if (o.status == UNBOUNDED) 219 status = UNBOUNDED; 220 else if (o.status == BOUNDED) 221 { 222 if (status == EMPTY) 223 *this = o; 224 else if (status == BOUNDED) 225 extents.union_ (o.extents); 226 } 227 } 228 intersecthb_bounds_t229 void intersect (const hb_bounds_t &o) 230 { 231 if (o.status == EMPTY) 232 status = EMPTY; 233 else if (o.status == BOUNDED) 234 { 235 if (status == UNBOUNDED) 236 *this = o; 237 else if (status == BOUNDED) 238 { 239 extents.intersect (o.extents); 240 if (extents.is_empty ()) 241 status = EMPTY; 242 } 243 } 244 } 245 246 status_t status; 247 hb_extents_t extents; 248 }; 249 250 struct hb_transform_decomposed_t 251 { 252 float translateX = 0; 253 float translateY = 0; 254 float rotation = 0; // in degrees, counter-clockwise 255 float scaleX = 1; 256 float scaleY = 1; 257 float skewX = 0; // in degrees, counter-clockwise 258 float skewY = 0; // in degrees, counter-clockwise 259 float tCenterX = 0; 260 float tCenterY = 0; 261 operator boolhb_transform_decomposed_t262 operator bool () const 263 { 264 return translateX || translateY || 265 rotation || 266 scaleX != 1 || scaleY != 1 || 267 skewX || skewY || 268 tCenterX || tCenterY; 269 } 270 to_transformhb_transform_decomposed_t271 hb_transform_t to_transform () const 272 { 273 hb_transform_t t; 274 t.translate (translateX + tCenterX, translateY + tCenterY); 275 t.rotate (rotation); 276 t.scale (scaleX, scaleY); 277 t.skew (-skewX, skewY); 278 t.translate (-tCenterX, -tCenterY); 279 return t; 280 } 281 }; 282 283 284 #endif /* HB_GEOMETRY_HH */ 285