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