/src/harfbuzz/src/hb-geometry.hh
Line | Count | Source |
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 | 0 | hb_extents_t () {} |
36 | | hb_extents_t (const hb_glyph_extents_t &extents) : |
37 | 0 | xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)), |
38 | 0 | ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), |
39 | 0 | xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), |
40 | 0 | ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} |
41 | | hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) : |
42 | 0 | xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} |
43 | | |
44 | 0 | bool is_empty () const { return xmin >= xmax || ymin >= ymax; } |
45 | 0 | bool is_void () const { return xmin > xmax; } |
46 | | |
47 | | void union_ (const hb_extents_t &o) |
48 | 0 | { |
49 | 0 | if (o.is_empty ()) return; |
50 | 0 | if (is_empty ()) |
51 | 0 | { |
52 | 0 | *this = o; |
53 | 0 | return; |
54 | 0 | } |
55 | 0 | xmin = hb_min (xmin, o.xmin); |
56 | 0 | ymin = hb_min (ymin, o.ymin); |
57 | 0 | xmax = hb_max (xmax, o.xmax); |
58 | 0 | ymax = hb_max (ymax, o.ymax); |
59 | 0 | } |
60 | | |
61 | | void intersect (const hb_extents_t &o) |
62 | 0 | { |
63 | 0 | if (o.is_empty () || is_empty ()) |
64 | 0 | { |
65 | 0 | *this = hb_extents_t {}; |
66 | 0 | return; |
67 | 0 | } |
68 | 0 | xmin = hb_max (xmin, o.xmin); |
69 | 0 | ymin = hb_max (ymin, o.ymin); |
70 | 0 | xmax = hb_min (xmax, o.xmax); |
71 | 0 | ymax = hb_min (ymax, o.ymax); |
72 | 0 | } |
73 | | |
74 | | void |
75 | | add_point (Float x, Float y) |
76 | 0 | { |
77 | 0 | if (unlikely (is_void ())) |
78 | 0 | { |
79 | 0 | xmin = xmax = x; |
80 | 0 | ymin = ymax = y; |
81 | 0 | } |
82 | 0 | else |
83 | 0 | { |
84 | 0 | xmin = hb_min (xmin, x); |
85 | 0 | ymin = hb_min (ymin, y); |
86 | 0 | xmax = hb_max (xmax, x); |
87 | 0 | ymax = hb_max (ymax, y); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const |
92 | 0 | { |
93 | 0 | hb_position_t x0 = (hb_position_t) roundf (xmin); |
94 | 0 | hb_position_t y0 = (hb_position_t) roundf (ymin); |
95 | 0 | hb_position_t x1 = (hb_position_t) roundf (xmax); |
96 | 0 | hb_position_t y1 = (hb_position_t) roundf (ymax); |
97 | 0 | return hb_glyph_extents_t {xneg ? x1 : x0, |
98 | 0 | yneg ? y0 : y1, |
99 | 0 | xneg ? x0 - x1 : x1 - x0, |
100 | 0 | yneg ? y1 - y0 : y0 - y1}; |
101 | 0 | } |
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 | 0 | hb_transform_t () {} |
113 | | hb_transform_t (Float xx, Float yx, |
114 | | Float xy, Float yy, |
115 | | Float x0, Float y0) : |
116 | 0 | xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} |
117 | | |
118 | | bool is_identity () const |
119 | 0 | { |
120 | 0 | return xx == 1 && yx == 0 && |
121 | 0 | xy == 0 && yy == 1 && |
122 | 0 | x0 == 0 && y0 == 0; |
123 | 0 | } |
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 | 0 | { |
132 | | // Copied from cairo-matrix.c |
133 | 0 | const hb_transform_t &a = before ? o : *this; |
134 | 0 | const hb_transform_t &b = before ? *this : o; |
135 | 0 | *this = { |
136 | 0 | a.xx * b.xx + a.xy * b.yx, |
137 | 0 | a.yx * b.xx + a.yy * b.yx, |
138 | 0 | a.xx * b.xy + a.xy * b.yy, |
139 | 0 | a.yx * b.xy + a.yy * b.yy, |
140 | 0 | a.xx * b.x0 + a.xy * b.y0 + a.x0, |
141 | 0 | a.yx * b.x0 + a.yy * b.y0 + a.y0 |
142 | 0 | }; |
143 | 0 | } |
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 | 0 | { |
157 | 0 | Float new_x = x0 + xx * x + xy * y; |
158 | 0 | Float new_y = y0 + yx * x + yy * y; |
159 | 0 | x = new_x; |
160 | 0 | y = new_y; |
161 | 0 | } |
162 | | |
163 | | void transform_extents (hb_extents_t<Float> &extents) const |
164 | 0 | { |
165 | 0 | Float quad_x[4], quad_y[4]; |
166 | |
|
167 | 0 | quad_x[0] = extents.xmin; |
168 | 0 | quad_y[0] = extents.ymin; |
169 | 0 | quad_x[1] = extents.xmin; |
170 | 0 | quad_y[1] = extents.ymax; |
171 | 0 | quad_x[2] = extents.xmax; |
172 | 0 | quad_y[2] = extents.ymin; |
173 | 0 | quad_x[3] = extents.xmax; |
174 | 0 | quad_y[3] = extents.ymax; |
175 | |
|
176 | 0 | extents = hb_extents_t<Float> {}; |
177 | 0 | for (unsigned i = 0; i < 4; i++) |
178 | 0 | { |
179 | 0 | transform_point (quad_x[i], quad_y[i]); |
180 | 0 | extents.add_point (quad_x[i], quad_y[i]); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | 0 | 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 | 0 | { |
188 | 0 | return {1, 0, 0, 1, x, y}; |
189 | 0 | } |
190 | | void translate (Float x, Float y, bool before=false) |
191 | 0 | { |
192 | 0 | if (before) |
193 | 0 | { |
194 | 0 | x0 += x; |
195 | 0 | y0 += y; |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | if (x == 0 && y == 0) |
200 | 0 | return; |
201 | | |
202 | 0 | x0 += xx * x + xy * y; |
203 | 0 | y0 += yx * x + yy * y; |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | static hb_transform_t scaling (Float scaleX, Float scaleY) |
208 | 0 | { |
209 | 0 | return {scaleX, 0, 0, scaleY, 0, 0}; |
210 | 0 | } |
211 | | void scale (Float scaleX, Float scaleY) |
212 | 0 | { |
213 | 0 | if (scaleX == 1 && scaleY == 1) |
214 | 0 | return; |
215 | | |
216 | 0 | xx *= scaleX; |
217 | 0 | yx *= scaleX; |
218 | 0 | xy *= scaleY; |
219 | 0 | yy *= scaleY; |
220 | 0 | } |
221 | | static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) |
222 | 0 | { |
223 | 0 | return {scaleX, 0, 0, scaleY, |
224 | 0 | center_x ? (1 - scaleX) * center_x : 0, |
225 | 0 | center_y ? (1 - scaleY) * center_y : 0}; |
226 | 0 | } |
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 | 0 | { |
237 | | // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 |
238 | 0 | Float c; |
239 | 0 | Float s; |
240 | 0 | hb_sincos (radians, s, c); |
241 | 0 | return {c, s, -s, c, 0, 0}; |
242 | 0 | } |
243 | | void rotate (Float radians, bool before=false) |
244 | 0 | { |
245 | 0 | if (radians == 0) |
246 | 0 | return; |
247 | | |
248 | 0 | transform (rotation (radians), before); |
249 | 0 | } |
250 | | |
251 | | static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y) |
252 | 0 | { |
253 | 0 | Float s, c; |
254 | 0 | hb_sincos (radians, s, c); |
255 | 0 | return { |
256 | 0 | c, s, -s, c, |
257 | 0 | (1 - c) * center_x + s * center_y, |
258 | 0 | -s * center_x + (1 - c) * center_y |
259 | 0 | }; |
260 | 0 | } |
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 | 0 | { |
271 | 0 | return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0}; |
272 | 0 | } |
273 | | void skew (Float skewX, Float skewY) |
274 | 0 | { |
275 | 0 | if (skewX == 0 && skewY == 0) |
276 | 0 | return; |
277 | | |
278 | 0 | transform (skewing (skewX, skewY)); |
279 | 0 | } |
280 | | static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y) |
281 | 0 | { |
282 | 0 | skewX = skewX ? tanf (skewX) : 0; |
283 | 0 | skewY = skewY ? tanf (skewY) : 0; |
284 | 0 | return { |
285 | 0 | 1, skewY, skewX, 1, |
286 | 0 | center_y ? -skewX * center_y : 0, |
287 | 0 | center_x ? -skewY * center_x : 0 |
288 | 0 | }; |
289 | 0 | } |
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 | 0 | hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} |
318 | | hb_bounds_t (const hb_extents_t<Float> &extents) : |
319 | 0 | status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} |
320 | | |
321 | | void union_ (const hb_bounds_t &o) |
322 | 0 | { |
323 | 0 | if (o.status == UNBOUNDED) |
324 | 0 | status = UNBOUNDED; |
325 | 0 | else if (o.status == BOUNDED) |
326 | 0 | { |
327 | 0 | if (status == EMPTY) |
328 | 0 | *this = o; |
329 | 0 | else if (status == BOUNDED) |
330 | 0 | extents.union_ (o.extents); |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | void intersect (const hb_bounds_t &o) |
335 | 0 | { |
336 | 0 | if (o.status == EMPTY) |
337 | 0 | status = EMPTY; |
338 | 0 | else if (o.status == BOUNDED) |
339 | 0 | { |
340 | 0 | if (status == UNBOUNDED) |
341 | 0 | *this = o; |
342 | 0 | else if (status == BOUNDED) |
343 | 0 | { |
344 | 0 | extents.intersect (o.extents); |
345 | 0 | if (extents.is_empty ()) |
346 | 0 | status = EMPTY; |
347 | 0 | } |
348 | 0 | } |
349 | 0 | } |
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 | 0 | { |
379 | 0 | hb_transform_t<Float> t; |
380 | 0 | t.translate (translateX + tCenterX, translateY + tCenterY); |
381 | 0 | t.rotate (rotation); |
382 | 0 | t.scale (scaleX, scaleY); |
383 | 0 | t.skew (-skewX, skewY); |
384 | 0 | t.translate (-tCenterX, -tCenterY); |
385 | 0 | return t; |
386 | 0 | } |
387 | | }; |
388 | | |
389 | | |
390 | | #endif /* HB_GEOMETRY_HH */ |