Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-spline.c
Line
Count
Source (jump to first uncovered line)
1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2002 University of Southern California
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it either under the terms of the GNU Lesser General Public
7
 * License version 2.1 as published by the Free Software Foundation
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
10
 * notice, a recipient may use your version of this file under either
11
 * the MPL or the LGPL.
12
 *
13
 * You should have received a copy of the LGPL along with this library
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
19
 * The contents of this file are subject to the Mozilla Public License
20
 * Version 1.1 (the "License"); you may not use this file except in
21
 * compliance with the License. You may obtain a copy of the License at
22
 * http://www.mozilla.org/MPL/
23
 *
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26
 * the specific language governing rights and limitations.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * The Initial Developer of the Original Code is University of Southern
31
 * California.
32
 *
33
 * Contributor(s):
34
 *  Carl D. Worth <cworth@cworth.org>
35
 */
36
37
#include "cairoint.h"
38
39
#include "cairo-box-inline.h"
40
#include "cairo-slope-private.h"
41
42
cairo_bool_t
43
_cairo_spline_intersects (const cairo_point_t *a,
44
        const cairo_point_t *b,
45
        const cairo_point_t *c,
46
        const cairo_point_t *d,
47
        const cairo_box_t *box)
48
53.0k
{
49
53.0k
    cairo_box_t bounds;
50
51
53.0k
    if (_cairo_box_contains_point (box, a) ||
52
53.0k
  _cairo_box_contains_point (box, b) ||
53
53.0k
  _cairo_box_contains_point (box, c) ||
54
53.0k
  _cairo_box_contains_point (box, d))
55
19.7k
    {
56
19.7k
  return TRUE;
57
19.7k
    }
58
59
33.3k
    bounds.p2 = bounds.p1 = *a;
60
33.3k
    _cairo_box_add_point (&bounds, b);
61
33.3k
    _cairo_box_add_point (&bounds, c);
62
33.3k
    _cairo_box_add_point (&bounds, d);
63
64
33.3k
    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
65
33.3k
  bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
66
30.5k
    {
67
30.5k
  return FALSE;
68
30.5k
    }
69
70
#if 0 /* worth refining? */
71
    bounds.p2 = bounds.p1 = *a;
72
    _cairo_box_add_curve_to (&bounds, b, c, d);
73
    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
74
  bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
75
    {
76
  return FALSE;
77
    }
78
#endif
79
80
2.83k
    return TRUE;
81
33.3k
}
82
83
cairo_bool_t
84
_cairo_spline_init (cairo_spline_t *spline,
85
        cairo_spline_add_point_func_t add_point_func,
86
        void *closure,
87
        const cairo_point_t *a, const cairo_point_t *b,
88
        const cairo_point_t *c, const cairo_point_t *d)
89
23.1k
{
90
    /* If both tangents are zero, this is just a straight line */
91
23.1k
    if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
92
59
  return FALSE;
93
94
23.0k
    spline->add_point_func = add_point_func;
95
23.0k
    spline->closure = closure;
96
97
23.0k
    spline->knots.a = *a;
98
23.0k
    spline->knots.b = *b;
99
23.0k
    spline->knots.c = *c;
100
23.0k
    spline->knots.d = *d;
101
102
23.0k
    if (a->x != b->x || a->y != b->y)
103
20.2k
  _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
104
2.83k
    else if (a->x != c->x || a->y != c->y)
105
1.75k
  _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
106
1.08k
    else if (a->x != d->x || a->y != d->y)
107
1.08k
  _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
108
0
    else
109
0
  return FALSE;
110
111
23.0k
    if (c->x != d->x || c->y != d->y)
112
21.0k
  _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
113
1.98k
    else if (b->x != d->x || b->y != d->y)
114
933
  _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
115
1.05k
    else
116
1.05k
  return FALSE; /* just treat this as a straight-line from a -> d */
117
118
    /* XXX if the initial, final and vector are all equal, this is just a line */
119
120
22.0k
    return TRUE;
121
23.0k
}
122
123
static cairo_status_t
124
_cairo_spline_add_point (cairo_spline_t *spline,
125
       const cairo_point_t *point,
126
       const cairo_point_t *knot)
127
33.0M
{
128
33.0M
    cairo_point_t *prev;
129
33.0M
    cairo_slope_t slope;
130
131
33.0M
    prev = &spline->last_point;
132
33.0M
    if (prev->x == point->x && prev->y == point->y)
133
22.0k
  return CAIRO_STATUS_SUCCESS;
134
135
32.9M
    _cairo_slope_init (&slope, point, knot);
136
137
32.9M
    spline->last_point = *point;
138
32.9M
    return spline->add_point_func (spline->closure, point, &slope);
139
33.0M
}
140
141
static void
142
_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
143
197M
{
144
197M
    result->x = a->x + ((b->x - a->x) >> 1);
145
197M
    result->y = a->y + ((b->y - a->y) >> 1);
146
197M
}
147
148
static void
149
_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
150
32.9M
{
151
32.9M
    cairo_point_t ab, bc, cd;
152
32.9M
    cairo_point_t abbc, bccd;
153
32.9M
    cairo_point_t final;
154
155
32.9M
    _lerp_half (&s1->a, &s1->b, &ab);
156
32.9M
    _lerp_half (&s1->b, &s1->c, &bc);
157
32.9M
    _lerp_half (&s1->c, &s1->d, &cd);
158
32.9M
    _lerp_half (&ab, &bc, &abbc);
159
32.9M
    _lerp_half (&bc, &cd, &bccd);
160
32.9M
    _lerp_half (&abbc, &bccd, &final);
161
162
32.9M
    s2->a = final;
163
32.9M
    s2->b = bccd;
164
32.9M
    s2->c = cd;
165
32.9M
    s2->d = s1->d;
166
167
32.9M
    s1->b = ab;
168
32.9M
    s1->c = abbc;
169
32.9M
    s1->d = final;
170
32.9M
}
171
172
/* Return an upper bound on the error (squared) that could result from
173
 * approximating a spline as a line segment connecting the two endpoints. */
174
static double
175
_cairo_spline_error_squared (const cairo_spline_knots_t *knots)
176
65.9M
{
177
65.9M
    double bdx, bdy, berr;
178
65.9M
    double cdx, cdy, cerr;
179
180
    /* We are going to compute the distance (squared) between each of the the b
181
     * and c control points and the segment a-b. The maximum of these two
182
     * distances will be our approximation error. */
183
184
65.9M
    bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
185
65.9M
    bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
186
187
65.9M
    cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
188
65.9M
    cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
189
190
65.9M
    if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
191
  /* Intersection point (px):
192
   *     px = p1 + u(p2 - p1)
193
   *     (p - px) ∙ (p2 - p1) = 0
194
   * Thus:
195
   *     u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
196
   */
197
198
65.9M
  double dx, dy, u, v;
199
200
65.9M
  dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
201
65.9M
  dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
202
65.9M
   v = dx * dx + dy * dy;
203
204
65.9M
  u = bdx * dx + bdy * dy;
205
65.9M
  if (u <= 0) {
206
      /* bdx -= 0;
207
       * bdy -= 0;
208
       */
209
65.9M
  } else if (u >= v) {
210
42.6k
      bdx -= dx;
211
42.6k
      bdy -= dy;
212
65.8M
  } else {
213
65.8M
      bdx -= u/v * dx;
214
65.8M
      bdy -= u/v * dy;
215
65.8M
  }
216
217
65.9M
  u = cdx * dx + cdy * dy;
218
65.9M
  if (u <= 0) {
219
      /* cdx -= 0;
220
       * cdy -= 0;
221
       */
222
65.9M
  } else if (u >= v) {
223
70.4k
      cdx -= dx;
224
70.4k
      cdy -= dy;
225
65.8M
  } else {
226
65.8M
      cdx -= u/v * dx;
227
65.8M
      cdy -= u/v * dy;
228
65.8M
  }
229
65.9M
    }
230
231
65.9M
    berr = bdx * bdx + bdy * bdy;
232
65.9M
    cerr = cdx * cdx + cdy * cdy;
233
65.9M
    if (berr > cerr)
234
32.0M
  return berr;
235
33.9M
    else
236
33.9M
  return cerr;
237
65.9M
}
238
239
static cairo_status_t
240
_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
241
            double tolerance_squared,
242
            cairo_spline_t *result)
243
65.9M
{
244
65.9M
    cairo_spline_knots_t s2;
245
65.9M
    cairo_status_t status;
246
247
65.9M
    if (_cairo_spline_error_squared (s1) < tolerance_squared)
248
33.0M
  return _cairo_spline_add_point (result, &s1->a, &s1->b);
249
250
32.9M
    _de_casteljau (s1, &s2);
251
252
32.9M
    status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
253
32.9M
    if (unlikely (status))
254
0
  return status;
255
256
32.9M
    return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
257
32.9M
}
258
259
cairo_status_t
260
_cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
261
22.0k
{
262
22.0k
    cairo_spline_knots_t s1;
263
22.0k
    cairo_status_t status;
264
265
22.0k
    s1 = spline->knots;
266
22.0k
    spline->last_point = s1.a;
267
22.0k
    status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
268
22.0k
    if (unlikely (status))
269
0
  return status;
270
271
22.0k
    return spline->add_point_func (spline->closure,
272
22.0k
           &spline->knots.d, &spline->final_slope);
273
22.0k
}
274
275
/* Note: this function is only good for computing bounds in device space. */
276
cairo_status_t
277
_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
278
         void *closure,
279
         const cairo_point_t *p0, const cairo_point_t *p1,
280
         const cairo_point_t *p2, const cairo_point_t *p3)
281
4.75k
{
282
4.75k
    double x0, x1, x2, x3;
283
4.75k
    double y0, y1, y2, y3;
284
4.75k
    double a, b, c;
285
4.75k
    double t[4];
286
4.75k
    int t_num = 0, i;
287
4.75k
    cairo_status_t status;
288
289
4.75k
    x0 = _cairo_fixed_to_double (p0->x);
290
4.75k
    y0 = _cairo_fixed_to_double (p0->y);
291
4.75k
    x1 = _cairo_fixed_to_double (p1->x);
292
4.75k
    y1 = _cairo_fixed_to_double (p1->y);
293
4.75k
    x2 = _cairo_fixed_to_double (p2->x);
294
4.75k
    y2 = _cairo_fixed_to_double (p2->y);
295
4.75k
    x3 = _cairo_fixed_to_double (p3->x);
296
4.75k
    y3 = _cairo_fixed_to_double (p3->y);
297
298
    /* The spline can be written as a polynomial of the four points:
299
     *
300
     *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
301
     *
302
     * for 0≤t≤1.  Now, the X and Y components of the spline follow the
303
     * same polynomial but with x and y replaced for p.  To find the
304
     * bounds of the spline, we just need to find the X and Y bounds.
305
     * To find the bound, we take the derivative and equal it to zero,
306
     * and solve to find the t's that give the extreme points.
307
     *
308
     * Here is the derivative of the curve, sorted on t:
309
     *
310
     *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
311
     *
312
     * Let:
313
     *
314
     *   a = -p0+3p1-3p2+p3
315
     *   b =  p0-2p1+p2
316
     *   c = -p0+p1
317
     *
318
     * Gives:
319
     *
320
     *   a.t² + 2b.t + c = 0
321
     *
322
     * With:
323
     *
324
     *   delta = b*b - a*c
325
     *
326
     * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
327
     * delta is positive, and at -b/a if delta is zero.
328
     */
329
330
4.75k
#define ADD(t0) \
331
12.4k
    { \
332
12.4k
  double _t0 = (t0); \
333
12.4k
  if (0 < _t0 && _t0 < 1) \
334
12.4k
      t[t_num++] = _t0; \
335
12.4k
    }
336
337
4.75k
#define FIND_EXTREMES(a,b,c) \
338
9.50k
    { \
339
9.50k
  if (a == 0) { \
340
942
      if (b != 0) \
341
942
    ADD (-c / (2*b)); \
342
8.56k
  } else { \
343
8.56k
      double b2 = b * b; \
344
8.56k
      double delta = b2 - a * c; \
345
8.56k
      if (delta > 0) { \
346
7.90k
    cairo_bool_t feasible; \
347
7.90k
    double _2ab = 2 * a * b; \
348
    /* We are only interested in solutions t that satisfy 0<t<1 \
349
     * here.  We do some checks to avoid sqrt if the solutions \
350
     * are not in that range.  The checks can be derived from: \
351
     * \
352
     *   0 < (-b±√delta)/a < 1 \
353
     */ \
354
7.90k
    if (_2ab >= 0) \
355
7.90k
        feasible = delta > b2 && delta < a*a + b2 + _2ab; \
356
7.90k
    else if (-b / a >= 1) \
357
5.45k
        feasible = delta < b2 && delta > a*a + b2 + _2ab; \
358
5.45k
    else \
359
5.45k
        feasible = delta < b2 || delta < a*a + b2 + _2ab; \
360
7.90k
          \
361
7.90k
    if (unlikely (feasible)) { \
362
5.74k
        double sqrt_delta = sqrt (delta); \
363
5.74k
        ADD ((-b - sqrt_delta) / a); \
364
5.74k
        ADD ((-b + sqrt_delta) / a); \
365
5.74k
    } \
366
7.90k
      } else if (delta == 0) { \
367
328
    ADD (-b / a); \
368
328
      } \
369
8.56k
  } \
370
9.50k
    }
371
372
    /* Find X extremes */
373
4.75k
    a = -x0 + 3*x1 - 3*x2 + x3;
374
4.75k
    b =  x0 - 2*x1 + x2;
375
4.75k
    c = -x0 + x1;
376
4.75k
    FIND_EXTREMES (a, b, c);
377
378
    /* Find Y extremes */
379
4.75k
    a = -y0 + 3*y1 - 3*y2 + y3;
380
4.75k
    b =  y0 - 2*y1 + y2;
381
4.75k
    c = -y0 + y1;
382
4.75k
    FIND_EXTREMES (a, b, c);
383
384
4.75k
    status = add_point_func (closure, p0, NULL);
385
4.75k
    if (unlikely (status))
386
0
  return status;
387
388
12.0k
    for (i = 0; i < t_num; i++) {
389
7.31k
  cairo_point_t p;
390
7.31k
  double x, y;
391
7.31k
        double t_1_0, t_0_1;
392
7.31k
        double t_2_0, t_0_2;
393
7.31k
        double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
394
395
7.31k
        t_1_0 = t[i];          /*      t  */
396
7.31k
        t_0_1 = 1 - t_1_0;     /* (1 - t) */
397
398
7.31k
        t_2_0 = t_1_0 * t_1_0; /*      t  *      t  */
399
7.31k
        t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
400
401
7.31k
        t_3_0   = t_2_0 * t_1_0;     /*      t  *      t  *      t      */
402
7.31k
        t_2_1_3 = t_2_0 * t_0_1 * 3; /*      t  *      t  * (1 - t) * 3 */
403
7.31k
        t_1_2_3 = t_1_0 * t_0_2 * 3; /*      t  * (1 - t) * (1 - t) * 3 */
404
7.31k
        t_0_3   = t_0_1 * t_0_2;     /* (1 - t) * (1 - t) * (1 - t)     */
405
406
        /* Bezier polynomial */
407
7.31k
        x = x0 * t_0_3
408
7.31k
          + x1 * t_1_2_3
409
7.31k
          + x2 * t_2_1_3
410
7.31k
          + x3 * t_3_0;
411
7.31k
        y = y0 * t_0_3
412
7.31k
          + y1 * t_1_2_3
413
7.31k
          + y2 * t_2_1_3
414
7.31k
          + y3 * t_3_0;
415
416
7.31k
  p.x = _cairo_fixed_from_double (x);
417
7.31k
  p.y = _cairo_fixed_from_double (y);
418
7.31k
  status = add_point_func (closure, &p, NULL);
419
7.31k
  if (unlikely (status))
420
0
      return status;
421
7.31k
    }
422
423
4.75k
    return add_point_func (closure, p3, NULL);
424
4.75k
}