Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */