Coverage Report

Created: 2025-02-19 06:30

/src/cairo/src/cairo-path-in-fill.c
Line
Count
Source (jump to first uncovered line)
1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2008 Chris Wilson
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 Chris Wilson.
31
 *
32
 * Contributor(s):
33
 *  Chris Wilson <chris@chris-wilson.co.uk>
34
 */
35
36
#include "cairoint.h"
37
#include "cairo-path-fixed-private.h"
38
39
typedef struct cairo_in_fill {
40
    double tolerance;
41
    cairo_bool_t on_edge;
42
    int winding;
43
44
    cairo_fixed_t x, y;
45
46
    cairo_bool_t has_current_point;
47
    cairo_point_t current_point;
48
    cairo_point_t first_point;
49
} cairo_in_fill_t;
50
51
static void
52
_cairo_in_fill_init (cairo_in_fill_t  *in_fill,
53
         double    tolerance,
54
         double    x,
55
         double    y)
56
0
{
57
0
    in_fill->on_edge = FALSE;
58
0
    in_fill->winding = 0;
59
0
    in_fill->tolerance = tolerance;
60
61
0
    in_fill->x = _cairo_fixed_from_double (x);
62
0
    in_fill->y = _cairo_fixed_from_double (y);
63
64
0
    in_fill->has_current_point = FALSE;
65
0
    in_fill->current_point.x = 0;
66
0
    in_fill->current_point.y = 0;
67
0
}
68
69
static void
70
_cairo_in_fill_fini (cairo_in_fill_t *in_fill)
71
0
{
72
0
}
73
74
static int
75
edge_compare_for_y_against_x (const cairo_point_t *p1,
76
            const cairo_point_t *p2,
77
            cairo_fixed_t y,
78
            cairo_fixed_t x)
79
0
{
80
0
    cairo_fixed_t adx, ady;
81
0
    cairo_fixed_t dx, dy;
82
0
    cairo_int64_t L, R;
83
84
0
    adx = p2->x - p1->x;
85
0
    dx = x - p1->x;
86
87
0
    if (adx == 0)
88
0
  return -dx;
89
0
    if ((adx ^ dx) < 0)
90
0
  return adx;
91
92
0
    dy = y - p1->y;
93
0
    ady = p2->y - p1->y;
94
95
0
    L = _cairo_int32x32_64_mul (dy, adx);
96
0
    R = _cairo_int32x32_64_mul (dx, ady);
97
98
0
    return _cairo_int64_cmp (L, R);
99
0
}
100
101
static void
102
_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill,
103
       const cairo_point_t *p1,
104
       const cairo_point_t *p2)
105
0
{
106
0
    int dir;
107
108
0
    if (in_fill->on_edge)
109
0
  return;
110
111
    /* count the number of edge crossing to -∞ */
112
113
0
    dir = 1;
114
0
    if (p2->y < p1->y) {
115
0
  const cairo_point_t *tmp;
116
117
0
  tmp = p1;
118
0
  p1 = p2;
119
0
  p2 = tmp;
120
121
0
  dir = -1;
122
0
    }
123
124
    /* First check whether the query is on an edge */
125
0
    if ((p1->x == in_fill->x && p1->y == in_fill->y) ||
126
0
  (p2->x == in_fill->x && p2->y == in_fill->y) ||
127
0
  (! (p2->y < in_fill->y || p1->y > in_fill->y ||
128
0
     (p1->x > in_fill->x && p2->x > in_fill->x) ||
129
0
     (p1->x < in_fill->x && p2->x < in_fill->x)) &&
130
0
   edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0))
131
0
    {
132
0
  in_fill->on_edge = TRUE;
133
0
  return;
134
0
    }
135
136
    /* edge is entirely above or below, note the shortening rule */
137
0
    if (p2->y <= in_fill->y || p1->y > in_fill->y)
138
0
  return;
139
140
    /* edge lies wholly to the right */
141
0
    if (p1->x >= in_fill->x && p2->x >= in_fill->x)
142
0
  return;
143
144
0
    if ((p1->x <= in_fill->x && p2->x <= in_fill->x) ||
145
0
  edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0)
146
0
    {
147
0
  in_fill->winding += dir;
148
0
    }
149
0
}
150
151
static cairo_status_t
152
_cairo_in_fill_move_to (void *closure,
153
      const cairo_point_t *point)
154
0
{
155
0
    cairo_in_fill_t *in_fill = closure;
156
157
    /* implicit close path */
158
0
    if (in_fill->has_current_point) {
159
0
  _cairo_in_fill_add_edge (in_fill,
160
0
         &in_fill->current_point,
161
0
         &in_fill->first_point);
162
0
    }
163
164
0
    in_fill->first_point = *point;
165
0
    in_fill->current_point = *point;
166
0
    in_fill->has_current_point = TRUE;
167
168
0
    return CAIRO_STATUS_SUCCESS;
169
0
}
170
171
static cairo_status_t
172
_cairo_in_fill_line_to (void *closure,
173
      const cairo_point_t *point)
174
0
{
175
0
    cairo_in_fill_t *in_fill = closure;
176
177
0
    if (in_fill->has_current_point)
178
0
  _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point);
179
180
0
    in_fill->current_point = *point;
181
0
    in_fill->has_current_point = TRUE;
182
183
0
    return CAIRO_STATUS_SUCCESS;
184
0
}
185
186
static cairo_status_t
187
_cairo_in_fill_add_point (void *closure,
188
                          const cairo_point_t *point,
189
                          const cairo_slope_t *tangent)
190
0
{
191
0
    return _cairo_in_fill_line_to (closure, point);
192
0
};
193
194
static cairo_status_t
195
_cairo_in_fill_curve_to (void *closure,
196
       const cairo_point_t *b,
197
       const cairo_point_t *c,
198
       const cairo_point_t *d)
199
0
{
200
0
    cairo_in_fill_t *in_fill = closure;
201
0
    cairo_spline_t spline;
202
0
    cairo_fixed_t top, bot, left;
203
204
    /* first reject based on bbox */
205
0
    bot = top = in_fill->current_point.y;
206
0
    if (b->y < top) top = b->y;
207
0
    if (b->y > bot) bot = b->y;
208
0
    if (c->y < top) top = c->y;
209
0
    if (c->y > bot) bot = c->y;
210
0
    if (d->y < top) top = d->y;
211
0
    if (d->y > bot) bot = d->y;
212
0
    if (bot < in_fill->y || top > in_fill->y) {
213
0
  in_fill->current_point = *d;
214
0
  return CAIRO_STATUS_SUCCESS;
215
0
    }
216
217
0
    left = in_fill->current_point.x;
218
0
    if (b->x < left) left = b->x;
219
0
    if (c->x < left) left = c->x;
220
0
    if (d->x < left) left = d->x;
221
0
    if (left > in_fill->x) {
222
0
  in_fill->current_point = *d;
223
0
  return CAIRO_STATUS_SUCCESS;
224
0
    }
225
226
    /* XXX Investigate direct inspection of the inflections? */
227
0
    if (! _cairo_spline_init (&spline,
228
0
            _cairo_in_fill_add_point,
229
0
            in_fill,
230
0
            &in_fill->current_point, b, c, d))
231
0
    {
232
0
  return CAIRO_STATUS_SUCCESS;
233
0
    }
234
235
0
    return _cairo_spline_decompose (&spline, in_fill->tolerance);
236
0
}
237
238
static cairo_status_t
239
_cairo_in_fill_close_path (void *closure)
240
0
{
241
0
    cairo_in_fill_t *in_fill = closure;
242
243
0
    if (in_fill->has_current_point) {
244
0
  _cairo_in_fill_add_edge (in_fill,
245
0
         &in_fill->current_point,
246
0
         &in_fill->first_point);
247
248
0
  in_fill->has_current_point = FALSE;
249
0
    }
250
251
0
    return CAIRO_STATUS_SUCCESS;
252
0
}
253
254
cairo_bool_t
255
_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path,
256
         cairo_fill_rule_t   fill_rule,
257
         double    tolerance,
258
         double    x,
259
         double    y)
260
0
{
261
0
    cairo_in_fill_t in_fill;
262
0
    cairo_status_t status;
263
0
    cairo_bool_t is_inside;
264
265
0
    if (_cairo_path_fixed_fill_is_empty (path))
266
0
  return FALSE;
267
268
0
    _cairo_in_fill_init (&in_fill, tolerance, x, y);
269
270
0
    status = _cairo_path_fixed_interpret (path,
271
0
            _cairo_in_fill_move_to,
272
0
            _cairo_in_fill_line_to,
273
0
            _cairo_in_fill_curve_to,
274
0
            _cairo_in_fill_close_path,
275
0
            &in_fill);
276
0
    assert (status == CAIRO_STATUS_SUCCESS);
277
278
0
    _cairo_in_fill_close_path (&in_fill);
279
280
0
    if (in_fill.on_edge) {
281
0
  is_inside = TRUE;
282
0
    } else switch (fill_rule) {
283
0
    case CAIRO_FILL_RULE_EVEN_ODD:
284
0
  is_inside = in_fill.winding & 1;
285
0
  break;
286
0
    case CAIRO_FILL_RULE_WINDING:
287
0
  is_inside = in_fill.winding != 0;
288
0
  break;
289
0
    default:
290
0
  ASSERT_NOT_REACHED;
291
0
  is_inside = FALSE;
292
0
  break;
293
0
    }
294
295
0
    _cairo_in_fill_fini (&in_fill);
296
297
0
    return is_inside;
298
0
}