Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/cairo/src/cairo-pen.c
Line
Count
Source
1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2002 University of Southern California
4
 * Copyright © 2008 Chris Wilson
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * The Initial Developer of the Original Code is University of Southern
32
 * California.
33
 *
34
 * Contributor(s):
35
 *  Carl D. Worth <cworth@cworth.org>
36
 *  Chris Wilson <chris@chris-wilson.co.uk>
37
 */
38
39
#include "cairoint.h"
40
41
#include "cairo-error-private.h"
42
#include "cairo-slope-private.h"
43
44
static void
45
_cairo_pen_compute_slopes (cairo_pen_t *pen);
46
47
cairo_status_t
48
_cairo_pen_init (cairo_pen_t  *pen,
49
     double    radius,
50
     double    tolerance,
51
     const cairo_matrix_t *ctm)
52
38.5k
{
53
38.5k
    int i;
54
38.5k
    int reflect;
55
56
38.5k
    if (CAIRO_INJECT_FAULT ())
57
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
58
59
38.5k
    VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
60
61
38.5k
    pen->radius = radius;
62
38.5k
    pen->tolerance = tolerance;
63
64
38.5k
    reflect = _cairo_matrix_compute_determinant (ctm) < 0.;
65
66
38.5k
    pen->num_vertices = _cairo_pen_vertices_needed (tolerance,
67
38.5k
                radius,
68
38.5k
                ctm);
69
70
38.5k
    if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
71
63
  pen->vertices = _cairo_malloc_ab (pen->num_vertices,
72
63
            sizeof (cairo_pen_vertex_t));
73
63
  if (unlikely (pen->vertices == NULL))
74
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
75
38.4k
    } else {
76
38.4k
  pen->vertices = pen->vertices_embedded;
77
38.4k
    }
78
79
    /*
80
     * Compute pen coordinates.  To generate the right ellipse, compute points around
81
     * a circle in user space and transform them to device space.  To get a consistent
82
     * orientation in device space, flip the pen if the transformation matrix
83
     * is reflecting
84
     */
85
428k
    for (i=0; i < pen->num_vertices; i++) {
86
390k
  cairo_pen_vertex_t *v = &pen->vertices[i];
87
390k
  double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy;
88
390k
  if (reflect)
89
0
      theta = -theta;
90
390k
  dx = radius * cos (theta);
91
390k
  dy = radius * sin (theta);
92
390k
  cairo_matrix_transform_distance (ctm, &dx, &dy);
93
390k
  v->point.x = _cairo_fixed_from_double (dx);
94
390k
  v->point.y = _cairo_fixed_from_double (dy);
95
390k
    }
96
97
38.5k
    _cairo_pen_compute_slopes (pen);
98
99
38.5k
    return CAIRO_STATUS_SUCCESS;
100
38.5k
}
101
102
void
103
_cairo_pen_fini (cairo_pen_t *pen)
104
38.5k
{
105
38.5k
    if (pen->vertices != pen->vertices_embedded)
106
63
  free (pen->vertices);
107
108
109
38.5k
    VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
110
38.5k
}
111
112
cairo_status_t
113
_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other)
114
0
{
115
0
    VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
116
117
0
    *pen = *other;
118
119
0
    if (CAIRO_INJECT_FAULT ())
120
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
121
122
0
    pen->vertices = pen->vertices_embedded;
123
0
    if (pen->num_vertices) {
124
0
  if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
125
0
      pen->vertices = _cairo_malloc_ab (pen->num_vertices,
126
0
                sizeof (cairo_pen_vertex_t));
127
0
      if (unlikely (pen->vertices == NULL))
128
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
129
0
  }
130
131
0
  memcpy (pen->vertices, other->vertices,
132
0
    pen->num_vertices * sizeof (cairo_pen_vertex_t));
133
0
    }
134
135
0
    return CAIRO_STATUS_SUCCESS;
136
0
}
137
138
cairo_status_t
139
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
140
0
{
141
0
    cairo_status_t status;
142
0
    int num_vertices;
143
0
    int i;
144
145
0
    if (CAIRO_INJECT_FAULT ())
146
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
147
148
0
    num_vertices = pen->num_vertices + num_points;
149
0
    if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) ||
150
0
  pen->vertices != pen->vertices_embedded)
151
0
    {
152
0
  cairo_pen_vertex_t *vertices;
153
154
0
  if (pen->vertices == pen->vertices_embedded) {
155
0
      vertices = _cairo_malloc_ab (num_vertices,
156
0
                             sizeof (cairo_pen_vertex_t));
157
0
      if (unlikely (vertices == NULL))
158
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
159
160
0
      memcpy (vertices, pen->vertices,
161
0
        pen->num_vertices * sizeof (cairo_pen_vertex_t));
162
0
  } else {
163
0
      vertices = _cairo_realloc_ab (pen->vertices,
164
0
            num_vertices,
165
0
            sizeof (cairo_pen_vertex_t));
166
0
      if (unlikely (vertices == NULL))
167
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
168
0
  }
169
170
0
  pen->vertices = vertices;
171
0
    }
172
173
0
    pen->num_vertices = num_vertices;
174
175
    /* initialize new vertices */
176
0
    for (i=0; i < num_points; i++)
177
0
  pen->vertices[pen->num_vertices-num_points+i].point = point[i];
178
179
0
    status = _cairo_hull_compute (pen->vertices, &pen->num_vertices);
180
0
    if (unlikely (status))
181
0
  return status;
182
183
0
    _cairo_pen_compute_slopes (pen);
184
185
0
    return CAIRO_STATUS_SUCCESS;
186
0
}
187
188
/*
189
The circular pen in user space is transformed into an ellipse in
190
device space.
191
192
We construct the pen by computing points along the circumference
193
using equally spaced angles.
194
195
We show that this approximation to the ellipse has maximum error at the
196
major axis of the ellipse.
197
198
Set
199
200
      M = major axis length
201
      m = minor axis length
202
203
Align 'M' along the X axis and 'm' along the Y axis and draw
204
an ellipse parameterized by angle 't':
205
206
      x = M cos t     y = m sin t
207
208
Perturb t by ± d and compute two new points (x+,y+), (x-,y-).
209
The distance from the average of these two points to (x,y) represents
210
the maximum error in approximating the ellipse with a polygon formed
211
from vertices 2∆ radians apart.
212
213
      x+ = M cos (t+∆)    y+ = m sin (t+∆)
214
      x- = M cos (t-∆)    y- = m sin (t-∆)
215
216
Now compute the approximation error, E:
217
218
  Ex = (x - (x+ + x-) / 2)
219
  Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2)
220
     = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) +
221
        cos(t)cos(∆) - sin(t)sin(∆))/2)
222
     = M(cos(t) - cos(t)cos(∆))
223
     = M cos(t) (1 - cos(∆))
224
225
  Ey = y - (y+ - y-) / 2
226
     = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2
227
     = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) +
228
        sin(t)cos(∆) - cos(t)sin(∆))/2)
229
     = m (sin(t) - sin(t)cos(∆))
230
     = m sin(t) (1 - cos(∆))
231
232
  E² = Ex² + Ey²
233
     = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))²
234
     = (1 - cos(∆))² (M² cos²(t) + m² sin²(t))
235
     = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t))
236
     = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m²
237
238
Find the extremum by differentiation wrt t and setting that to zero
239
240
∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t))
241
242
         0 = 2 cos (t) sin (t)
243
   0 = sin (2t)
244
   t = nπ
245
246
Which is to say that the maximum and minimum errors occur on the
247
axes of the ellipse at 0 and π radians:
248
249
  E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m²
250
        = (1-cos(∆))² M²
251
  E²(π) = (1-cos(∆))² m²
252
253
maximum error = M (1-cos(∆))
254
minimum error = m (1-cos(∆))
255
256
We must make maximum error ≤ tolerance, so compute the ∆ needed:
257
258
      tolerance = M (1-cos(∆))
259
  tolerance / M = 1 - cos (∆)
260
         cos(∆) = 1 - tolerance/M
261
                    ∆ = acos (1 - tolerance / M);
262
263
Remembering that ∆ is half of our angle between vertices,
264
the number of vertices is then
265
266
             vertices = ceil(2π/2∆).
267
                      = ceil(π/∆).
268
269
Note that this also equation works for M == m (a circle) as it
270
doesn't matter where on the circle the error is computed.
271
*/
272
273
int
274
_cairo_pen_vertices_needed (double      tolerance,
275
          double      radius,
276
          const cairo_matrix_t  *matrix)
277
2.03M
{
278
    /*
279
     * the pen is a circle that gets transformed to an ellipse by matrix.
280
     * compute major axis length for a pen with the specified radius.
281
     * we don't need the minor axis length.
282
     */
283
2.03M
    double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
284
2.03M
                     radius);
285
2.03M
    int num_vertices;
286
287
2.03M
    if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */
288
1.97k
  num_vertices = 1;
289
2.03M
    } else if (tolerance >= major_axis) {
290
16
  num_vertices = 4;
291
2.03M
    } else {
292
2.03M
  double divisor = acos (1 - tolerance / major_axis);
293
294
2.03M
  if (divisor == 0.0)
295
33
      return 4;
296
297
2.03M
  num_vertices = ceil (2*M_PI / divisor);
298
299
  /* number of vertices must be even */
300
2.03M
  if (num_vertices % 2)
301
1.50k
      num_vertices++;
302
303
  /* And we must always have at least 4 vertices. */
304
2.03M
  if (num_vertices < 4)
305
0
      num_vertices = 4;
306
2.03M
    }
307
308
2.03M
    return num_vertices;
309
2.03M
}
310
311
static void
312
_cairo_pen_compute_slopes (cairo_pen_t *pen)
313
38.5k
{
314
38.5k
    int i, i_prev;
315
38.5k
    cairo_pen_vertex_t *prev, *v, *next;
316
317
38.5k
    for (i=0, i_prev = pen->num_vertices - 1;
318
428k
   i < pen->num_vertices;
319
390k
   i_prev = i++) {
320
390k
  prev = &pen->vertices[i_prev];
321
390k
  v = &pen->vertices[i];
322
390k
  next = &pen->vertices[(i + 1) % pen->num_vertices];
323
324
390k
  _cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
325
390k
  _cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
326
390k
    }
327
38.5k
}
328
/*
329
 * Find active pen vertex for clockwise edge of stroke at the given slope.
330
 *
331
 * The strictness of the inequalities here is delicate. The issue is
332
 * that the slope_ccw member of one pen vertex will be equivalent to
333
 * the slope_cw member of the next pen vertex in a counterclockwise
334
 * order. However, for this function, we care strongly about which
335
 * vertex is returned.
336
 *
337
 * [I think the "care strongly" above has to do with ensuring that the
338
 * pen's "extra points" from the spline's initial and final slopes are
339
 * properly found when beginning the spline stroking.]
340
 */
341
int
342
_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
343
          const cairo_slope_t *slope)
344
0
{
345
0
    int i;
346
347
0
    for (i=0; i < pen->num_vertices; i++) {
348
0
  if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) &&
349
0
      (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0))
350
0
      break;
351
0
    }
352
353
    /* If the desired slope cannot be found between any of the pen
354
     * vertices, then we must have a degenerate pen, (such as a pen
355
     * that's been transformed to a line). In that case, we consider
356
     * the first pen vertex as the appropriate clockwise vertex.
357
     */
358
0
    if (i == pen->num_vertices)
359
0
  i = 0;
360
361
0
    return i;
362
0
}
363
364
/* Find active pen vertex for counterclockwise edge of stroke at the given slope.
365
 *
366
 * Note: See the comments for _cairo_pen_find_active_cw_vertex_index
367
 * for some details about the strictness of the inequalities here.
368
 */
369
int
370
_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
371
           const cairo_slope_t *slope)
372
0
{
373
0
    cairo_slope_t slope_reverse;
374
0
    int i;
375
376
0
    slope_reverse = *slope;
377
0
    slope_reverse.dx = -slope_reverse.dx;
378
0
    slope_reverse.dy = -slope_reverse.dy;
379
380
0
    for (i=pen->num_vertices-1; i >= 0; i--) {
381
0
  if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) &&
382
0
      (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0))
383
0
      break;
384
0
    }
385
386
    /* If the desired slope cannot be found between any of the pen
387
     * vertices, then we must have a degenerate pen, (such as a pen
388
     * that's been transformed to a line). In that case, we consider
389
     * the last pen vertex as the appropriate counterclockwise vertex.
390
     */
391
0
    if (i < 0)
392
0
  i = pen->num_vertices - 1;
393
394
0
    return i;
395
0
}
396
397
void
398
_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen,
399
            const cairo_slope_t *in,
400
            const cairo_slope_t *out,
401
            int *start, int *stop)
402
325
{
403
404
325
    int lo = 0, hi = pen->num_vertices;
405
325
    int i;
406
407
325
    i = (lo + hi) >> 1;
408
1.58k
    do {
409
1.58k
  if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
410
507
      lo = i;
411
1.07k
  else
412
1.07k
      hi = i;
413
1.58k
  i = (lo + hi) >> 1;
414
1.58k
    } while (hi - lo > 1);
415
325
    if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
416
325
  if (++i == pen->num_vertices)
417
2
      i = 0;
418
325
    *start = i;
419
420
325
    if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) {
421
322
  lo = i;
422
322
  hi = i + pen->num_vertices;
423
322
  i = (lo + hi) >> 1;
424
1.73k
  do {
425
1.73k
      int j = i;
426
1.73k
      if (j >= pen->num_vertices)
427
566
    j -= pen->num_vertices;
428
1.73k
      if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0)
429
918
    hi = i;
430
821
      else
431
821
    lo = i;
432
1.73k
      i = (lo + hi) >> 1;
433
1.73k
  } while (hi - lo > 1);
434
322
  if (i >= pen->num_vertices)
435
144
      i -= pen->num_vertices;
436
322
    }
437
325
    *stop = i;
438
325
}
439
440
void
441
_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen,
442
             const cairo_slope_t *in,
443
             const cairo_slope_t *out,
444
             int *start, int *stop)
445
2.48k
{
446
2.48k
    int lo = 0, hi = pen->num_vertices;
447
2.48k
    int i;
448
449
2.48k
    i = (lo + hi) >> 1;
450
10.5k
    do {
451
10.5k
  if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
452
6.86k
      lo = i;
453
3.67k
  else
454
3.67k
      hi = i;
455
10.5k
  i = (lo + hi) >> 1;
456
10.5k
    } while (hi - lo > 1);
457
2.48k
    if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
458
2.48k
  if (++i == pen->num_vertices)
459
62
      i = 0;
460
2.48k
    *start = i;
461
462
2.48k
    if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) {
463
2.46k
  lo = i;
464
2.46k
  hi = i + pen->num_vertices;
465
2.46k
  i = (lo + hi) >> 1;
466
11.2k
  do {
467
11.2k
      int j = i;
468
11.2k
      if (j >= pen->num_vertices)
469
5.92k
    j -= pen->num_vertices;
470
11.2k
      if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0)
471
4.19k
    hi = i;
472
7.09k
      else
473
7.09k
    lo = i;
474
11.2k
      i = (lo + hi) >> 1;
475
11.2k
  } while (hi - lo > 1);
476
2.46k
  if (i >= pen->num_vertices)
477
1.34k
      i -= pen->num_vertices;
478
2.46k
    }
479
2.48k
    *stop = i;
480
2.48k
}