Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-contour.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2004 Carl Worth
3
 * Copyright © 2006 Red Hat, Inc.
4
 * Copyright © 2008 Chris Wilson
5
 * Copyright © 2011 Intel Corporation
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it either under the terms of the GNU Lesser General Public
9
 * License version 2.1 as published by the Free Software Foundation
10
 * (the "LGPL") or, at your option, under the terms of the Mozilla
11
 * Public License Version 1.1 (the "MPL"). If you do not alter this
12
 * notice, a recipient may use your version of this file under either
13
 * the MPL or the LGPL.
14
 *
15
 * You should have received a copy of the LGPL along with this library
16
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18
 * You should have received a copy of the MPL along with this library
19
 * in the file COPYING-MPL-1.1
20
 *
21
 * The contents of this file are subject to the Mozilla Public License
22
 * Version 1.1 (the "License"); you may not use this file except in
23
 * compliance with the License. You may obtain a copy of the License at
24
 * http://www.mozilla.org/MPL/
25
 *
26
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28
 * the specific language governing rights and limitations.
29
 *
30
 * The Original Code is the cairo graphics library.
31
 *
32
 * The Initial Developer of the Original Code is Carl Worth
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-freelist-private.h"
43
#include "cairo-combsort-inline.h"
44
#include "cairo-contour-inline.h"
45
#include "cairo-contour-private.h"
46
47
void
48
_cairo_contour_init (cairo_contour_t *contour,
49
         int direction)
50
59.0k
{
51
59.0k
    contour->direction = direction;
52
59.0k
    contour->chain.points = contour->embedded_points;
53
59.0k
    contour->chain.next = NULL;
54
59.0k
    contour->chain.num_points = 0;
55
59.0k
    contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
56
59.0k
    contour->tail = &contour->chain;
57
59.0k
}
58
59
cairo_int_status_t
60
__cairo_contour_add_point (cairo_contour_t *contour,
61
        const cairo_point_t *point)
62
10.1k
{
63
10.1k
    cairo_contour_chain_t *tail = contour->tail;
64
10.1k
    cairo_contour_chain_t *next;
65
66
10.1k
    assert (tail->next == NULL);
67
68
10.1k
    next = _cairo_malloc_ab_plus_c (tail->size_points*2,
69
10.1k
            sizeof (cairo_point_t),
70
10.1k
            sizeof (cairo_contour_chain_t));
71
10.1k
    if (unlikely (next == NULL))
72
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
73
74
10.1k
    next->size_points = tail->size_points*2;
75
10.1k
    next->num_points = 1;
76
10.1k
    next->points = (cairo_point_t *)(next+1);
77
10.1k
    next->next = NULL;
78
10.1k
    tail->next = next;
79
10.1k
    contour->tail = next;
80
81
10.1k
    next->points[0] = *point;
82
10.1k
    return CAIRO_INT_STATUS_SUCCESS;
83
10.1k
}
84
85
static void
86
first_inc (cairo_contour_t *contour,
87
     cairo_point_t **p,
88
     cairo_contour_chain_t **chain)
89
0
{
90
0
    if (*p == (*chain)->points + (*chain)->num_points) {
91
0
  assert ((*chain)->next);
92
0
  *chain = (*chain)->next;
93
0
  *p = &(*chain)->points[0];
94
0
    } else
95
0
  ++*p;
96
0
}
97
98
static void
99
last_dec (cairo_contour_t *contour,
100
    cairo_point_t **p,
101
    cairo_contour_chain_t **chain)
102
0
{
103
0
    if (*p == (*chain)->points) {
104
0
  cairo_contour_chain_t *prev;
105
0
  assert (*chain != &contour->chain);
106
0
  for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
107
0
      ;
108
0
  *chain = prev;
109
0
  *p = &(*chain)->points[(*chain)->num_points-1];
110
0
    } else
111
0
  --*p;
112
0
}
113
114
void
115
_cairo_contour_reverse (cairo_contour_t *contour)
116
0
{
117
0
    cairo_contour_chain_t *first_chain, *last_chain;
118
0
    cairo_point_t *first, *last;
119
120
0
    contour->direction = -contour->direction;
121
122
0
    if (contour->chain.num_points <= 1)
123
0
  return;
124
125
0
    first_chain = &contour->chain;
126
0
    last_chain = contour->tail;
127
128
0
    first = &first_chain->points[0];
129
0
    last = &last_chain->points[last_chain->num_points-1];
130
131
0
    while (first != last) {
132
0
  cairo_point_t p;
133
134
0
  p = *first;
135
0
  *first = *last;
136
0
  *last = p;
137
138
0
  first_inc (contour, &first, &first_chain);
139
0
  last_dec (contour, &last, &last_chain);
140
0
    }
141
0
}
142
143
cairo_int_status_t
144
_cairo_contour_add (cairo_contour_t *dst,
145
        const cairo_contour_t *src)
146
0
{
147
0
    const cairo_contour_chain_t *chain;
148
0
    cairo_int_status_t status;
149
0
    int i;
150
151
0
    for (chain = &src->chain; chain; chain = chain->next) {
152
0
  for (i = 0; i < chain->num_points; i++) {
153
0
      status = _cairo_contour_add_point (dst, &chain->points[i]);
154
0
      if (unlikely (status))
155
0
    return status;
156
0
  }
157
0
    }
158
159
0
    return CAIRO_INT_STATUS_SUCCESS;
160
0
}
161
162
static inline cairo_bool_t
163
iter_next (cairo_contour_iter_t *iter)
164
0
{
165
0
    if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
166
0
  iter->chain = iter->chain->next;
167
0
  if (iter->chain == NULL)
168
0
      return FALSE;
169
170
0
  iter->point = &iter->chain->points[0];
171
0
  return TRUE;
172
0
    } else {
173
0
  iter->point++;
174
0
  return TRUE;
175
0
    }
176
0
}
177
178
static cairo_bool_t
179
iter_equal (const cairo_contour_iter_t *i1,
180
      const cairo_contour_iter_t *i2)
181
0
{
182
0
    return i1->chain == i2->chain && i1->point == i2->point;
183
0
}
184
185
static void
186
iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
187
0
{
188
0
    iter->chain = &contour->chain;
189
0
    iter->point = &contour->chain.points[0];
190
0
}
191
192
static void
193
iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
194
0
{
195
0
    iter->chain = contour->tail;
196
0
    iter->point = &contour->tail->points[contour->tail->num_points-1];
197
0
}
198
199
static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
200
                 const cairo_contour_chain_t *chain)
201
0
{
202
0
    const cairo_contour_chain_t *prev;
203
204
0
    if (chain == &contour->chain)
205
0
  return NULL;
206
207
0
    for (prev = &contour->chain; prev->next != chain; prev = prev->next)
208
0
  ;
209
210
0
    return prev;
211
0
}
212
213
cairo_int_status_t
214
_cairo_contour_add_reversed (cairo_contour_t *dst,
215
           const cairo_contour_t *src)
216
0
{
217
0
    const cairo_contour_chain_t *last;
218
0
    cairo_int_status_t status;
219
0
    int i;
220
221
0
    if (src->chain.num_points == 0)
222
0
  return CAIRO_INT_STATUS_SUCCESS;
223
224
0
    for (last = src->tail; last; last = prev_const_chain (src, last)) {
225
0
  for (i = last->num_points-1; i >= 0; i--) {
226
0
      status = _cairo_contour_add_point (dst, &last->points[i]);
227
0
      if (unlikely (status))
228
0
    return status;
229
0
  }
230
0
    }
231
232
0
    return CAIRO_INT_STATUS_SUCCESS;
233
0
}
234
235
static cairo_uint64_t
236
point_distance_sq (const cairo_point_t *p1,
237
       const cairo_point_t *p2)
238
0
{
239
0
    int32_t dx = p1->x - p2->x;
240
0
    int32_t dy = p1->y - p2->y;
241
0
    return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
242
0
}
243
244
0
#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
245
0
#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
246
247
static cairo_bool_t
248
_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
249
             const cairo_contour_iter_t *first,
250
             const cairo_contour_iter_t *last)
251
0
{
252
0
    cairo_contour_iter_t iter, furthest;
253
0
    uint64_t max_error;
254
0
    int x0, y0;
255
0
    int nx, ny;
256
0
    int count;
257
258
0
    iter = *first;
259
0
    iter_next (&iter);
260
0
    if (iter_equal (&iter, last))
261
0
  return FALSE;
262
263
0
    x0 = first->point->x;
264
0
    y0 = first->point->y;
265
0
    nx = last->point->y - y0;
266
0
    ny = x0 - last->point->x;
267
268
0
    count = 0;
269
0
    max_error = 0;
270
0
    do {
271
0
  cairo_point_t *p = iter.point;
272
0
  if (! DELETED(p)) {
273
0
      uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
274
0
      if (d * d > max_error) {
275
0
    max_error = d * d;
276
0
    furthest = iter;
277
0
      }
278
0
      count++;
279
0
  }
280
0
  iter_next (&iter);
281
0
    } while (! iter_equal (&iter, last));
282
0
    if (count == 0)
283
0
  return FALSE;
284
285
0
    if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
286
0
  cairo_bool_t simplified;
287
288
0
  simplified = FALSE;
289
0
  simplified |= _cairo_contour_simplify_chain (contour, tolerance,
290
0
                 first, &furthest);
291
0
  simplified |= _cairo_contour_simplify_chain (contour, tolerance,
292
0
                 &furthest, last);
293
0
  return simplified;
294
0
    } else {
295
0
  iter = *first;
296
0
  iter_next (&iter);
297
0
  do {
298
0
      MARK_DELETED (iter.point);
299
0
      iter_next (&iter);
300
0
  } while (! iter_equal (&iter, last));
301
302
0
  return TRUE;
303
0
    }
304
0
}
305
306
void
307
_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
308
0
{
309
0
    cairo_contour_chain_t *chain;
310
0
    cairo_point_t *last = NULL;
311
0
    cairo_contour_iter_t iter, furthest;
312
0
    cairo_bool_t simplified;
313
0
    uint64_t max = 0;
314
0
    int i;
315
316
0
    if (contour->chain.num_points <= 2)
317
0
  return;
318
319
0
    tolerance = tolerance * CAIRO_FIXED_ONE;
320
0
    tolerance *= tolerance;
321
322
    /* stage 1: vertex reduction */
323
0
    for (chain = &contour->chain; chain; chain = chain->next) {
324
0
  for (i = 0; i < chain->num_points; i++) {
325
0
      if (last == NULL ||
326
0
    point_distance_sq (last, &chain->points[i]) > tolerance) {
327
0
    last = &chain->points[i];
328
0
      } else {
329
0
    MARK_DELETED (&chain->points[i]);
330
0
      }
331
0
  }
332
0
    }
333
334
    /* stage2: polygon simplification using Douglas-Peucker */
335
0
    do {
336
0
  last = &contour->chain.points[0];
337
0
  iter_init (&furthest, contour);
338
0
  max = 0;
339
0
  for (chain = &contour->chain; chain; chain = chain->next) {
340
0
      for (i = 0; i < chain->num_points; i++) {
341
0
    uint64_t d;
342
343
0
    if (DELETED (&chain->points[i]))
344
0
        continue;
345
346
0
    d = point_distance_sq (last, &chain->points[i]);
347
0
    if (d > max) {
348
0
        furthest.chain = chain;
349
0
        furthest.point = &chain->points[i];
350
0
        max = d;
351
0
    }
352
0
      }
353
0
  }
354
0
  assert (max);
355
356
0
  simplified = FALSE;
357
0
  iter_init (&iter, contour);
358
0
  simplified |= _cairo_contour_simplify_chain (contour, tolerance,
359
0
                 &iter, &furthest);
360
361
0
  iter_init_last (&iter, contour);
362
0
  if (! iter_equal (&furthest, &iter))
363
0
      simplified |= _cairo_contour_simplify_chain (contour, tolerance,
364
0
               &furthest, &iter);
365
0
    } while (simplified);
366
367
0
    iter_init (&iter, contour);
368
0
    for (chain = &contour->chain; chain; chain = chain->next) {
369
0
  int num_points = chain->num_points;
370
0
  chain->num_points = 0;
371
0
  for (i = 0; i < num_points; i++) {
372
0
      if (! DELETED(&chain->points[i])) {
373
0
    if (iter.point != &chain->points[i])
374
0
        *iter.point = chain->points[i];
375
0
    iter.chain->num_points++;
376
0
    iter_next (&iter);
377
0
      }
378
0
  }
379
0
    }
380
381
0
    if (iter.chain) {
382
0
  cairo_contour_chain_t *next;
383
384
0
  for (chain = iter.chain->next; chain; chain = next) {
385
0
      next = chain->next;
386
0
      free (chain);
387
0
  }
388
389
0
  iter.chain->next = NULL;
390
0
  contour->tail = iter.chain;
391
0
    }
392
0
}
393
394
void
395
_cairo_contour_reset (cairo_contour_t *contour)
396
50.5k
{
397
50.5k
    _cairo_contour_fini (contour);
398
50.5k
    _cairo_contour_init (contour, contour->direction);
399
50.5k
}
400
401
void
402
_cairo_contour_fini (cairo_contour_t *contour)
403
59.0k
{
404
59.0k
    cairo_contour_chain_t *chain, *next;
405
406
69.1k
    for (chain = contour->chain.next; chain; chain = next) {
407
10.1k
  next = chain->next;
408
10.1k
  free (chain);
409
10.1k
    }
410
59.0k
}
411
412
void
413
_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
414
0
{
415
0
    cairo_contour_chain_t *chain;
416
0
    int num_points, size_points;
417
0
    int i;
418
419
0
    num_points = 0;
420
0
    size_points = 0;
421
0
    for (chain = &contour->chain; chain; chain = chain->next) {
422
0
  num_points += chain->num_points;
423
0
  size_points += chain->size_points;
424
0
    }
425
426
0
    fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
427
0
       contour->direction, num_points, size_points);
428
429
0
    num_points = 0;
430
0
    for (chain = &contour->chain; chain; chain = chain->next) {
431
0
  for (i = 0; i < chain->num_points; i++) {
432
0
      fprintf (file, "  [%d] = (%f, %f)\n",
433
0
         num_points++,
434
0
         _cairo_fixed_to_double (chain->points[i].x),
435
0
         _cairo_fixed_to_double (chain->points[i].y));
436
0
  }
437
0
    }
438
0
}
439
440
void
441
__cairo_contour_remove_last_chain (cairo_contour_t *contour)
442
0
{
443
0
    cairo_contour_chain_t *chain;
444
445
0
    if (contour->tail == &contour->chain)
446
0
  return;
447
448
0
    for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
449
0
  ;
450
0
    free (contour->tail);
451
0
    contour->tail = chain;
452
0
    chain->next = NULL;
453
0
}