Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/harfbuzz/src/hb-geometry.hh
Line
Count
Source (jump to first uncovered line)
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
30
struct hb_extents_t
31
{
32
0
  hb_extents_t () {}
33
  hb_extents_t (const hb_glyph_extents_t &extents) :
34
    xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)),
35
    ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)),
36
    xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)),
37
0
    ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {}
38
  hb_extents_t (float xmin, float ymin, float xmax, float ymax) :
39
0
    xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {}
40
41
0
  bool is_empty () const { return xmin >= xmax || ymin >= ymax; }
42
0
  bool is_void () const { return xmin > xmax; }
43
44
  void union_ (const hb_extents_t &o)
45
0
  {
46
0
    if (o.is_empty ()) return;
47
0
    if (is_empty ())
48
0
    {
49
0
      *this = o;
50
0
      return;
51
0
    }
52
0
    xmin = hb_min (xmin, o.xmin);
53
0
    ymin = hb_min (ymin, o.ymin);
54
0
    xmax = hb_max (xmax, o.xmax);
55
0
    ymax = hb_max (ymax, o.ymax);
56
0
  }
57
58
  void intersect (const hb_extents_t &o)
59
0
  {
60
0
    if (o.is_empty () || is_empty ())
61
0
    {
62
0
      *this = hb_extents_t {};
63
0
      return;
64
0
    }
65
0
    xmin = hb_max (xmin, o.xmin);
66
0
    ymin = hb_max (ymin, o.ymin);
67
0
    xmax = hb_min (xmax, o.xmax);
68
0
    ymax = hb_min (ymax, o.ymax);
69
0
  }
70
71
  void
72
  add_point (float x, float y)
73
0
  {
74
0
    if (unlikely (is_void ()))
75
0
    {
76
0
      xmin = xmax = x;
77
0
      ymin = ymax = y;
78
0
    }
79
0
    else
80
0
    {
81
0
      xmin = hb_min (xmin, x);
82
0
      ymin = hb_min (ymin, y);
83
0
      xmax = hb_max (xmax, x);
84
0
      ymax = hb_max (ymax, y);
85
0
    }
86
0
  }
87
88
  hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const
89
0
  {
90
0
    hb_position_t x0 = (hb_position_t) roundf (xmin);
91
0
    hb_position_t y0 = (hb_position_t) roundf (ymin);
92
0
    hb_position_t x1 = (hb_position_t) roundf (xmax);
93
0
    hb_position_t y1 = (hb_position_t) roundf (ymax);
94
0
    return hb_glyph_extents_t {xneg ? x1 : x0,
95
0
             yneg ? y0 : y1,
96
0
             xneg ? x0 - x1 : x1 - x0,
97
0
             yneg ? y1 - y0 : y0 - y1};
98
0
  }
99
100
  float xmin = 0.f;
101
  float ymin = 0.f;
102
  float xmax = -1.f;
103
  float ymax = -1.f;
104
};
105
106
struct hb_transform_t
107
{
108
0
  hb_transform_t () {}
109
  hb_transform_t (float xx, float yx,
110
      float xy, float yy,
111
      float x0, float y0) :
112
0
    xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
113
114
  bool is_identity () const
115
0
  {
116
0
    return xx == 1.f && yx == 0.f &&
117
0
     xy == 0.f && yy == 1.f &&
118
0
     x0 == 0.f && y0 == 0.f;
119
0
  }
120
121
  void multiply (const hb_transform_t &o)
122
0
  {
123
    /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
124
0
    hb_transform_t r;
125
126
0
    r.xx = o.xx * xx + o.yx * xy;
127
0
    r.yx = o.xx * yx + o.yx * yy;
128
129
0
    r.xy = o.xy * xx + o.yy * xy;
130
0
    r.yy = o.xy * yx + o.yy * yy;
131
132
0
    r.x0 = o.x0 * xx + o.y0 * xy + x0;
133
0
    r.y0 = o.x0 * yx + o.y0 * yy + y0;
134
135
0
    *this = r;
136
0
  }
137
138
  void transform_distance (float &dx, float &dy) const
139
0
  {
140
0
    float new_x = xx * dx + xy * dy;
141
0
    float new_y = yx * dx + yy * dy;
142
0
    dx = new_x;
143
0
    dy = new_y;
144
0
  }
145
146
  void transform_point (float &x, float &y) const
147
0
  {
148
0
    transform_distance (x, y);
149
0
    x += x0;
150
0
    y += y0;
151
0
  }
152
153
  void transform_extents (hb_extents_t &extents) const
154
0
  {
155
0
    float quad_x[4], quad_y[4];
156
157
0
    quad_x[0] = extents.xmin;
158
0
    quad_y[0] = extents.ymin;
159
0
    quad_x[1] = extents.xmin;
160
0
    quad_y[1] = extents.ymax;
161
0
    quad_x[2] = extents.xmax;
162
0
    quad_y[2] = extents.ymin;
163
0
    quad_x[3] = extents.xmax;
164
0
    quad_y[3] = extents.ymax;
165
166
0
    extents = hb_extents_t {};
167
0
    for (unsigned i = 0; i < 4; i++)
168
0
    {
169
0
      transform_point (quad_x[i], quad_y[i]);
170
0
      extents.add_point (quad_x[i], quad_y[i]);
171
0
    }
172
0
  }
173
174
0
  void transform (const hb_transform_t &o) { multiply (o); }
175
176
  void translate (float x, float y)
177
0
  {
178
0
    if (x == 0.f && y == 0.f)
179
0
      return;
180
0
181
0
    x0 += xx * x + xy * y;
182
0
    y0 += yx * x + yy * y;
183
0
  }
184
185
  void scale (float scaleX, float scaleY)
186
0
  {
187
0
    if (scaleX == 1.f && scaleY == 1.f)
188
0
      return;
189
0
190
0
    xx *= scaleX;
191
0
    yx *= scaleX;
192
0
    xy *= scaleY;
193
0
    yy *= scaleY;
194
0
  }
195
196
  void rotate (float rotation)
197
0
  {
198
0
    if (rotation == 0.f)
199
0
      return;
200
0
201
0
    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
202
0
    rotation = rotation * HB_PI;
203
0
    float c;
204
0
    float s;
205
0
#ifdef HAVE_SINCOSF
206
0
    sincosf (rotation, &s, &c);
207
0
#else
208
0
    c = cosf (rotation);
209
0
    s = sinf (rotation);
210
0
#endif
211
0
    auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f};
212
0
    transform (other);
213
0
  }
214
215
  void skew (float skewX, float skewY)
216
0
  {
217
0
    if (skewX == 0.f && skewY == 0.f)
218
0
      return;
219
0
220
0
    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
221
0
    skewX = skewX * HB_PI;
222
0
    skewY = skewY * HB_PI;
223
0
    auto other = hb_transform_t{1.f,
224
0
        skewY ? tanf (skewY) : 0.f,
225
0
        skewX ? tanf (skewX) : 0.f,
226
0
        1.f,
227
0
        0.f, 0.f};
228
0
    transform (other);
229
0
  }
230
231
  float xx = 1.f;
232
  float yx = 0.f;
233
  float xy = 0.f;
234
  float yy = 1.f;
235
  float x0 = 0.f;
236
  float y0 = 0.f;
237
};
238
239
#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
240
241
struct hb_bounds_t
242
{
243
  enum status_t {
244
    UNBOUNDED,
245
    BOUNDED,
246
    EMPTY,
247
  };
248
249
0
  hb_bounds_t (status_t status = UNBOUNDED) : status (status) {}
250
  hb_bounds_t (const hb_extents_t &extents) :
251
0
    status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}
252
253
  void union_ (const hb_bounds_t &o)
254
0
  {
255
0
    if (o.status == UNBOUNDED)
256
0
      status = UNBOUNDED;
257
0
    else if (o.status == BOUNDED)
258
0
    {
259
0
      if (status == EMPTY)
260
0
  *this = o;
261
0
      else if (status == BOUNDED)
262
0
        extents.union_ (o.extents);
263
0
    }
264
0
  }
265
266
  void intersect (const hb_bounds_t &o)
267
0
  {
268
0
    if (o.status == EMPTY)
269
0
      status = EMPTY;
270
0
    else if (o.status == BOUNDED)
271
0
    {
272
0
      if (status == UNBOUNDED)
273
0
  *this = o;
274
0
      else if (status == BOUNDED)
275
0
      {
276
0
        extents.intersect (o.extents);
277
0
  if (extents.is_empty ())
278
0
    status = EMPTY;
279
0
      }
280
0
    }
281
0
  }
282
283
  status_t status;
284
  hb_extents_t extents;
285
};
286
287
struct hb_transform_decomposed_t
288
{
289
  float translateX = 0;
290
  float translateY = 0;
291
  float rotation = 0;  // in degrees, counter-clockwise
292
  float scaleX = 1;
293
  float scaleY = 1;
294
  float skewX = 0;  // in degrees, counter-clockwise
295
  float skewY = 0;  // in degrees, counter-clockwise
296
  float tCenterX = 0;
297
  float tCenterY = 0;
298
299
  operator bool () const
300
0
  {
301
0
    return translateX || translateY ||
302
0
     rotation ||
303
0
     scaleX != 1 || scaleY != 1 ||
304
0
     skewX || skewY ||
305
0
     tCenterX || tCenterY;
306
0
  }
307
308
  hb_transform_t to_transform () const
309
0
  {
310
0
    hb_transform_t t;
311
0
    t.translate (translateX + tCenterX, translateY + tCenterY);
312
0
    t.rotate (rotation);
313
0
    t.scale (scaleX, scaleY);
314
0
    t.skew (-skewX, skewY);
315
0
    t.translate (-tCenterX, -tCenterY);
316
0
    return t;
317
0
  }
318
};
319
320
321
#endif /* HB_GEOMETRY_HH */