Coverage Report

Created: 2025-11-04 06:44

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