hb-geometry.hh (9804B)
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 #include "hb-algs.hh" 30 31 32 template <typename Float = float> 33 struct hb_extents_t 34 { 35 hb_extents_t () {} 36 hb_extents_t (const hb_glyph_extents_t &extents) : 37 xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)), 38 ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), 39 xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), 40 ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} 41 hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) : 42 xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} 43 44 bool is_empty () const { return xmin >= xmax || ymin >= ymax; } 45 bool is_void () const { return xmin > xmax; } 46 47 void union_ (const hb_extents_t &o) 48 { 49 if (o.is_empty ()) return; 50 if (is_empty ()) 51 { 52 *this = o; 53 return; 54 } 55 xmin = hb_min (xmin, o.xmin); 56 ymin = hb_min (ymin, o.ymin); 57 xmax = hb_max (xmax, o.xmax); 58 ymax = hb_max (ymax, o.ymax); 59 } 60 61 void intersect (const hb_extents_t &o) 62 { 63 if (o.is_empty () || is_empty ()) 64 { 65 *this = hb_extents_t {}; 66 return; 67 } 68 xmin = hb_max (xmin, o.xmin); 69 ymin = hb_max (ymin, o.ymin); 70 xmax = hb_min (xmax, o.xmax); 71 ymax = hb_min (ymax, o.ymax); 72 } 73 74 void 75 add_point (Float x, Float y) 76 { 77 if (unlikely (is_void ())) 78 { 79 xmin = xmax = x; 80 ymin = ymax = y; 81 } 82 else 83 { 84 xmin = hb_min (xmin, x); 85 ymin = hb_min (ymin, y); 86 xmax = hb_max (xmax, x); 87 ymax = hb_max (ymax, y); 88 } 89 } 90 91 hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const 92 { 93 hb_position_t x0 = (hb_position_t) roundf (xmin); 94 hb_position_t y0 = (hb_position_t) roundf (ymin); 95 hb_position_t x1 = (hb_position_t) roundf (xmax); 96 hb_position_t y1 = (hb_position_t) roundf (ymax); 97 return hb_glyph_extents_t {xneg ? x1 : x0, 98 yneg ? y0 : y1, 99 xneg ? x0 - x1 : x1 - x0, 100 yneg ? y1 - y0 : y0 - y1}; 101 } 102 103 Float xmin = 0; 104 Float ymin = 0; 105 Float xmax = -1; 106 Float ymax = -1; 107 }; 108 109 template <typename Float = float> 110 struct hb_transform_t 111 { 112 hb_transform_t () {} 113 hb_transform_t (Float xx, Float yx, 114 Float xy, Float yy, 115 Float x0, Float y0) : 116 xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} 117 118 bool is_identity () const 119 { 120 return xx == 1 && yx == 0 && 121 xy == 0 && yy == 1 && 122 x0 == 0 && y0 == 0; 123 } 124 bool is_translation () const 125 { 126 return xx == 1 && yx == 0 && 127 xy == 0 && yy == 1; 128 } 129 130 void multiply (const hb_transform_t &o, bool before=false) 131 { 132 // Copied from cairo-matrix.c 133 const hb_transform_t &a = before ? o : *this; 134 const hb_transform_t &b = before ? *this : o; 135 *this = { 136 a.xx * b.xx + a.xy * b.yx, 137 a.yx * b.xx + a.yy * b.yx, 138 a.xx * b.xy + a.xy * b.yy, 139 a.yx * b.xy + a.yy * b.yy, 140 a.xx * b.x0 + a.xy * b.y0 + a.x0, 141 a.yx * b.x0 + a.yy * b.y0 + a.y0 142 }; 143 } 144 145 HB_ALWAYS_INLINE 146 void transform_distance (Float &dx, Float &dy) const 147 { 148 Float new_x = xx * dx + xy * dy; 149 Float new_y = yx * dx + yy * dy; 150 dx = new_x; 151 dy = new_y; 152 } 153 154 HB_ALWAYS_INLINE 155 void transform_point (Float &x, Float &y) const 156 { 157 Float new_x = x0 + xx * x + xy * y; 158 Float new_y = y0 + yx * x + yy * y; 159 x = new_x; 160 y = new_y; 161 } 162 163 void transform_extents (hb_extents_t<Float> &extents) const 164 { 165 Float quad_x[4], quad_y[4]; 166 167 quad_x[0] = extents.xmin; 168 quad_y[0] = extents.ymin; 169 quad_x[1] = extents.xmin; 170 quad_y[1] = extents.ymax; 171 quad_x[2] = extents.xmax; 172 quad_y[2] = extents.ymin; 173 quad_x[3] = extents.xmax; 174 quad_y[3] = extents.ymax; 175 176 extents = hb_extents_t<Float> {}; 177 for (unsigned i = 0; i < 4; i++) 178 { 179 transform_point (quad_x[i], quad_y[i]); 180 extents.add_point (quad_x[i], quad_y[i]); 181 } 182 } 183 184 void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); } 185 186 static hb_transform_t translation (Float x, Float y) 187 { 188 return {1, 0, 0, 1, x, y}; 189 } 190 void translate (Float x, Float y, bool before=false) 191 { 192 if (before) 193 { 194 x0 += x; 195 y0 += y; 196 } 197 else 198 { 199 if (x == 0 && y == 0) 200 return; 201 202 x0 += xx * x + xy * y; 203 y0 += yx * x + yy * y; 204 } 205 } 206 207 static hb_transform_t scaling (Float scaleX, Float scaleY) 208 { 209 return {scaleX, 0, 0, scaleY, 0, 0}; 210 } 211 void scale (Float scaleX, Float scaleY) 212 { 213 if (scaleX == 1 && scaleY == 1) 214 return; 215 216 xx *= scaleX; 217 yx *= scaleX; 218 xy *= scaleY; 219 yy *= scaleY; 220 } 221 static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) 222 { 223 return {scaleX, 0, 0, scaleY, 224 center_x ? (1 - scaleX) * center_x : 0, 225 center_y ? (1 - scaleY) * center_y : 0}; 226 } 227 void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) 228 { 229 if (scaleX == 1 && scaleY == 1) 230 return; 231 232 transform (scaling_around_center (scaleX, scaleY, center_x, center_y)); 233 } 234 235 static hb_transform_t rotation (Float radians) 236 { 237 // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 238 Float c; 239 Float s; 240 hb_sincos (radians, s, c); 241 return {c, s, -s, c, 0, 0}; 242 } 243 void rotate (Float radians, bool before=false) 244 { 245 if (radians == 0) 246 return; 247 248 transform (rotation (radians), before); 249 } 250 251 static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y) 252 { 253 Float s, c; 254 hb_sincos (radians, s, c); 255 return { 256 c, s, -s, c, 257 (1 - c) * center_x + s * center_y, 258 -s * center_x + (1 - c) * center_y 259 }; 260 } 261 void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false) 262 { 263 if (radians == 0) 264 return; 265 266 transform (rotation_around_center (radians, center_x, center_y), before); 267 } 268 269 static hb_transform_t skewing (Float skewX, Float skewY) 270 { 271 return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0}; 272 } 273 void skew (Float skewX, Float skewY) 274 { 275 if (skewX == 0 && skewY == 0) 276 return; 277 278 transform (skewing (skewX, skewY)); 279 } 280 static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y) 281 { 282 skewX = skewX ? tanf (skewX) : 0; 283 skewY = skewY ? tanf (skewY) : 0; 284 return { 285 1, skewY, skewX, 1, 286 center_y ? -skewX * center_y : 0, 287 center_x ? -skewY * center_x : 0 288 }; 289 } 290 void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y) 291 { 292 if (skewX == 0 && skewY == 0) 293 return; 294 295 transform (skewing_around_center (skewX, skewY, center_x, center_y)); 296 } 297 298 Float xx = 1; 299 Float yx = 0; 300 Float xy = 0; 301 Float yy = 1; 302 Float x0 = 0; 303 Float y0 = 0; 304 }; 305 306 #define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0} 307 308 template <typename Float = float> 309 struct hb_bounds_t 310 { 311 enum status_t { 312 UNBOUNDED, 313 BOUNDED, 314 EMPTY, 315 }; 316 317 hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} 318 hb_bounds_t (const hb_extents_t<Float> &extents) : 319 status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} 320 321 void union_ (const hb_bounds_t &o) 322 { 323 if (o.status == UNBOUNDED) 324 status = UNBOUNDED; 325 else if (o.status == BOUNDED) 326 { 327 if (status == EMPTY) 328 *this = o; 329 else if (status == BOUNDED) 330 extents.union_ (o.extents); 331 } 332 } 333 334 void intersect (const hb_bounds_t &o) 335 { 336 if (o.status == EMPTY) 337 status = EMPTY; 338 else if (o.status == BOUNDED) 339 { 340 if (status == UNBOUNDED) 341 *this = o; 342 else if (status == BOUNDED) 343 { 344 extents.intersect (o.extents); 345 if (extents.is_empty ()) 346 status = EMPTY; 347 } 348 } 349 } 350 351 status_t status; 352 hb_extents_t<Float> extents; 353 }; 354 355 template <typename Float = float> 356 struct hb_transform_decomposed_t 357 { 358 Float translateX = 0; 359 Float translateY = 0; 360 Float rotation = 0; // in radians, counter-clockwise 361 Float scaleX = 1; 362 Float scaleY = 1; 363 Float skewX = 0; // in radians, counter-clockwise 364 Float skewY = 0; // in radians, counter-clockwise 365 Float tCenterX = 0; 366 Float tCenterY = 0; 367 368 operator bool () const 369 { 370 return translateX || translateY || 371 rotation || 372 scaleX != 1 || scaleY != 1 || 373 skewX || skewY || 374 tCenterX || tCenterY; 375 } 376 377 hb_transform_t<Float> to_transform () const 378 { 379 hb_transform_t<Float> t; 380 t.translate (translateX + tCenterX, translateY + tCenterY); 381 t.rotate (rotation); 382 t.scale (scaleX, scaleY); 383 t.skew (-skewX, skewY); 384 t.translate (-tCenterX, -tCenterY); 385 return t; 386 } 387 }; 388 389 390 #endif /* HB_GEOMETRY_HH */