~ubuntu-branches/ubuntu/hardy/cairo/hardy-updates

« back to all changes in this revision

Viewing changes to src/cairo-pen.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-01-17 13:00:59 UTC
  • Revision ID: james.westby@ubuntu.com-20080117130059-3gbudaudr2w8bl4w
Tags: upstream-1.5.6
Import upstream version 1.5.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
static int
 
40
_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix);
 
41
 
 
42
static void
 
43
_cairo_pen_compute_slopes (cairo_pen_t *pen);
 
44
 
 
45
static void
 
46
_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon);
 
47
 
 
48
void
 
49
_cairo_pen_init_empty (cairo_pen_t *pen)
 
50
{
 
51
    pen->radius = 0;
 
52
    pen->tolerance = 0;
 
53
    pen->vertices = NULL;
 
54
    pen->num_vertices = 0;
 
55
}
 
56
 
 
57
cairo_status_t
 
58
_cairo_pen_init (cairo_pen_t    *pen,
 
59
                 double          radius,
 
60
                 double          tolerance,
 
61
                 cairo_matrix_t *ctm)
 
62
{
 
63
    int i;
 
64
    int reflect;
 
65
    double  det;
 
66
 
 
67
    pen->radius = radius;
 
68
    pen->tolerance = tolerance;
 
69
 
 
70
    _cairo_matrix_compute_determinant (ctm, &det);
 
71
    if (det >= 0) {
 
72
        reflect = 0;
 
73
    } else {
 
74
        reflect = 1;
 
75
    }
 
76
 
 
77
    pen->num_vertices = _cairo_pen_vertices_needed (tolerance,
 
78
                                                    radius,
 
79
                                                    ctm);
 
80
 
 
81
    pen->vertices = _cairo_malloc_ab (pen->num_vertices,
 
82
                                      sizeof (cairo_pen_vertex_t));
 
83
    if (pen->vertices == NULL)
 
84
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
85
 
 
86
    /*
 
87
     * Compute pen coordinates.  To generate the right ellipse, compute points around
 
88
     * a circle in user space and transform them to device space.  To get a consistent
 
89
     * orientation in device space, flip the pen if the transformation matrix
 
90
     * is reflecting
 
91
     */
 
92
    for (i=0; i < pen->num_vertices; i++) {
 
93
        double theta = 2 * M_PI * i / (double) pen->num_vertices;
 
94
        double dx = radius * cos (reflect ? -theta : theta);
 
95
        double dy = radius * sin (reflect ? -theta : theta);
 
96
        cairo_pen_vertex_t *v = &pen->vertices[i];
 
97
        cairo_matrix_transform_distance (ctm, &dx, &dy);
 
98
        v->point.x = _cairo_fixed_from_double (dx);
 
99
        v->point.y = _cairo_fixed_from_double (dy);
 
100
    }
 
101
 
 
102
    _cairo_pen_compute_slopes (pen);
 
103
 
 
104
    return CAIRO_STATUS_SUCCESS;
 
105
}
 
106
 
 
107
void
 
108
_cairo_pen_fini (cairo_pen_t *pen)
 
109
{
 
110
    free (pen->vertices);
 
111
    pen->vertices = NULL;
 
112
 
 
113
    _cairo_pen_init_empty (pen);
 
114
}
 
115
 
 
116
cairo_status_t
 
117
_cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
 
118
{
 
119
    *pen = *other;
 
120
 
 
121
    if (pen->num_vertices) {
 
122
        pen->vertices = _cairo_malloc_ab (pen->num_vertices,
 
123
                                          sizeof (cairo_pen_vertex_t));
 
124
        if (pen->vertices == NULL)
 
125
            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
126
 
 
127
        memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t));
 
128
    }
 
129
 
 
130
    return CAIRO_STATUS_SUCCESS;
 
131
}
 
132
 
 
133
cairo_status_t
 
134
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
 
135
{
 
136
    cairo_pen_vertex_t *vertices;
 
137
    cairo_status_t status;
 
138
    int num_vertices;
 
139
    int i;
 
140
 
 
141
    num_vertices = pen->num_vertices + num_points;
 
142
    vertices = _cairo_realloc_ab (pen->vertices,
 
143
                                  num_vertices, sizeof (cairo_pen_vertex_t));
 
144
    if (vertices == NULL)
 
145
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
146
 
 
147
    pen->vertices = vertices;
 
148
    pen->num_vertices = num_vertices;
 
149
 
 
150
    /* initialize new vertices */
 
151
    for (i=0; i < num_points; i++)
 
152
        pen->vertices[pen->num_vertices-num_points+i].point = point[i];
 
153
 
 
154
    status = _cairo_hull_compute (pen->vertices, &pen->num_vertices);
 
155
    if (status)
 
156
        return status;
 
157
 
 
158
    _cairo_pen_compute_slopes (pen);
 
159
 
 
160
    return CAIRO_STATUS_SUCCESS;
 
161
}
 
162
 
 
163
/*
 
164
The circular pen in user space is transformed into an ellipse in
 
165
device space.
 
166
 
 
167
We construct the pen by computing points along the circumference
 
168
using equally spaced angles.
 
169
 
 
170
We show that this approximation to the ellipse has maximum error at the
 
171
major axis of the ellipse.
 
172
 
 
173
Set
 
174
 
 
175
            M = major axis length
 
176
            m = minor axis length
 
177
 
 
178
Align 'M' along the X axis and 'm' along the Y axis and draw
 
179
an ellipse parameterized by angle 't':
 
180
 
 
181
            x = M cos t                 y = m sin t
 
182
 
 
183
Perturb t by ± d and compute two new points (x+,y+), (x-,y-).
 
184
The distance from the average of these two points to (x,y) represents
 
185
the maximum error in approximating the ellipse with a polygon formed
 
186
from vertices 2∆ radians apart.
 
187
 
 
188
            x+ = M cos (t+∆)            y+ = m sin (t+∆)
 
189
            x- = M cos (t-∆)            y- = m sin (t-∆)
 
190
 
 
191
Now compute the approximation error, E:
 
192
 
 
193
        Ex = (x - (x+ + x-) / 2)
 
194
        Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2)
 
195
           = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) +
 
196
                          cos(t)cos(∆) - sin(t)sin(∆))/2)
 
197
           = M(cos(t) - cos(t)cos(∆))
 
198
           = M cos(t) (1 - cos(∆))
 
199
 
 
200
        Ey = y - (y+ - y-) / 2
 
201
           = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2
 
202
           = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) +
 
203
                          sin(t)cos(∆) - cos(t)sin(∆))/2)
 
204
           = m (sin(t) - sin(t)cos(∆))
 
205
           = m sin(t) (1 - cos(∆))
 
206
 
 
207
        E² = Ex² + Ey²
 
208
           = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))²
 
209
           = (1 - cos(∆))² (M² cos²(t) + m² sin²(t))
 
210
           = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t))
 
211
           = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m²
 
212
 
 
213
Find the extremum by differentiation wrt t and setting that to zero
 
214
 
 
215
∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t))
 
216
 
 
217
         0 = 2 cos (t) sin (t)
 
218
         0 = sin (2t)
 
219
         t = nπ
 
220
 
 
221
Which is to say that the maximum and minimum errors occur on the
 
222
axes of the ellipse at 0 and π radians:
 
223
 
 
224
        E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m²
 
225
              = (1-cos(∆))² M²
 
226
        E²(π) = (1-cos(∆))² m²
 
227
 
 
228
maximum error = M (1-cos(∆))
 
229
minimum error = m (1-cos(∆))
 
230
 
 
231
We must make maximum error ≤ tolerance, so compute the ∆ needed:
 
232
 
 
233
            tolerance = M (1-cos(∆))
 
234
        tolerance / M = 1 - cos (∆)
 
235
               cos(∆) = 1 - tolerance/M
 
236
                    ∆ = acos (1 - tolerance / M);
 
237
 
 
238
Remembering that ∆ is half of our angle between vertices,
 
239
the number of vertices is then
 
240
 
 
241
             vertices = ceil(2π/2∆).
 
242
                      = ceil(π/∆).
 
243
 
 
244
Note that this also equation works for M == m (a circle) as it
 
245
doesn't matter where on the circle the error is computed.
 
246
*/
 
247
 
 
248
static int
 
249
_cairo_pen_vertices_needed (double          tolerance,
 
250
                            double          radius,
 
251
                            cairo_matrix_t  *matrix)
 
252
{
 
253
    /*
 
254
     * the pen is a circle that gets transformed to an ellipse by matrix.
 
255
     * compute major axis length for a pen with the specified radius.
 
256
     * we don't need the minor axis length.
 
257
     */
 
258
 
 
259
    double  major_axis = _cairo_matrix_transformed_circle_major_axis(matrix, radius);
 
260
 
 
261
    /*
 
262
     * compute number of vertices needed
 
263
     */
 
264
    int     num_vertices;
 
265
 
 
266
    /* Where tolerance / M is > 1, we use 4 points */
 
267
    if (tolerance >= major_axis) {
 
268
        num_vertices = 4;
 
269
    } else {
 
270
        double delta = acos (1 - tolerance / major_axis);
 
271
        num_vertices = ceil (M_PI / delta);
 
272
 
 
273
        /* number of vertices must be even */
 
274
        if (num_vertices % 2)
 
275
            num_vertices++;
 
276
 
 
277
        /* And we must always have at least 4 vertices. */
 
278
        if (num_vertices < 4)
 
279
            num_vertices = 4;
 
280
    }
 
281
 
 
282
    return num_vertices;
 
283
}
 
284
 
 
285
static void
 
286
_cairo_pen_compute_slopes (cairo_pen_t *pen)
 
287
{
 
288
    int i, i_prev;
 
289
    cairo_pen_vertex_t *prev, *v, *next;
 
290
 
 
291
    for (i=0, i_prev = pen->num_vertices - 1;
 
292
         i < pen->num_vertices;
 
293
         i_prev = i++) {
 
294
        prev = &pen->vertices[i_prev];
 
295
        v = &pen->vertices[i];
 
296
        next = &pen->vertices[(i + 1) % pen->num_vertices];
 
297
 
 
298
        _cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
 
299
        _cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
 
300
    }
 
301
}
 
302
/*
 
303
 * Find active pen vertex for clockwise edge of stroke at the given slope.
 
304
 *
 
305
 * NOTE: The behavior of this function is sensitive to the sense of
 
306
 * the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise.
 
307
 *
 
308
 * The issue is that the slope_ccw member of one pen vertex will be
 
309
 * equivalent to the slope_cw member of the next pen vertex in a
 
310
 * counterclockwise order. However, for this function, we care
 
311
 * strongly about which vertex is returned.
 
312
 */
 
313
void
 
314
_cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen,
 
315
                                        cairo_slope_t *slope,
 
316
                                        int *active)
 
317
{
 
318
    int i;
 
319
 
 
320
    for (i=0; i < pen->num_vertices; i++) {
 
321
        if (_cairo_slope_clockwise (slope, &pen->vertices[i].slope_ccw)
 
322
            && _cairo_slope_counter_clockwise (slope, &pen->vertices[i].slope_cw))
 
323
            break;
 
324
    }
 
325
 
 
326
    /* If the desired slope cannot be found between any of the pen
 
327
     * vertices, then we must have a degenerate pen, (such as a pen
 
328
     * that's been transformed to a line). In that case, we consider
 
329
     * the first pen vertex as the appropriate clockwise vertex.
 
330
     */
 
331
    if (i == pen->num_vertices)
 
332
        i = 0;
 
333
 
 
334
    *active = i;
 
335
}
 
336
 
 
337
/* Find active pen vertex for counterclockwise edge of stroke at the given slope.
 
338
 *
 
339
 * NOTE: The behavior of this function is sensitive to the sense of
 
340
 * the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise.
 
341
 */
 
342
void
 
343
_cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen,
 
344
                                         cairo_slope_t *slope,
 
345
                                         int *active)
 
346
{
 
347
    int i;
 
348
    cairo_slope_t slope_reverse;
 
349
 
 
350
    slope_reverse = *slope;
 
351
    slope_reverse.dx = -slope_reverse.dx;
 
352
    slope_reverse.dy = -slope_reverse.dy;
 
353
 
 
354
    for (i=pen->num_vertices-1; i >= 0; i--) {
 
355
        if (_cairo_slope_counter_clockwise (&pen->vertices[i].slope_ccw, &slope_reverse)
 
356
            && _cairo_slope_clockwise (&pen->vertices[i].slope_cw, &slope_reverse))
 
357
            break;
 
358
    }
 
359
 
 
360
    /* If the desired slope cannot be found between any of the pen
 
361
     * vertices, then we must have a degenerate pen, (such as a pen
 
362
     * that's been transformed to a line). In that case, we consider
 
363
     * the last pen vertex as the appropriate counterclockwise vertex.
 
364
     */
 
365
    if (i < 0)
 
366
        i = pen->num_vertices - 1;
 
367
 
 
368
    *active = i;
 
369
}
 
370
 
 
371
static void
 
372
_cairo_pen_stroke_spline_half (cairo_pen_t *pen,
 
373
                               cairo_spline_t *spline,
 
374
                               cairo_direction_t dir,
 
375
                               cairo_polygon_t *polygon)
 
376
{
 
377
    int i;
 
378
    int start, stop, step;
 
379
    int active = 0;
 
380
    cairo_point_t hull_point;
 
381
    cairo_slope_t slope, initial_slope, final_slope;
 
382
    cairo_point_t *point = spline->points;
 
383
    int num_points = spline->num_points;
 
384
 
 
385
    if (dir == CAIRO_DIRECTION_FORWARD) {
 
386
        start = 0;
 
387
        stop = num_points;
 
388
        step = 1;
 
389
        initial_slope = spline->initial_slope;
 
390
        final_slope = spline->final_slope;
 
391
    } else {
 
392
        start = num_points - 1;
 
393
        stop = -1;
 
394
        step = -1;
 
395
        initial_slope = spline->final_slope;
 
396
        initial_slope.dx = -initial_slope.dx;
 
397
        initial_slope.dy = -initial_slope.dy;
 
398
        final_slope = spline->initial_slope;
 
399
        final_slope.dx = -final_slope.dx;
 
400
        final_slope.dy = -final_slope.dy;
 
401
    }
 
402
 
 
403
    _cairo_pen_find_active_cw_vertex_index (pen,
 
404
                                            &initial_slope,
 
405
                                            &active);
 
406
 
 
407
    i = start;
 
408
    while (i != stop) {
 
409
        hull_point.x = point[i].x + pen->vertices[active].point.x;
 
410
        hull_point.y = point[i].y + pen->vertices[active].point.y;
 
411
 
 
412
        _cairo_polygon_line_to (polygon, &hull_point);
 
413
 
 
414
        if (i + step == stop)
 
415
            slope = final_slope;
 
416
        else
 
417
            _cairo_slope_init (&slope, &point[i], &point[i+step]);
 
418
        if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) {
 
419
            if (++active == pen->num_vertices)
 
420
                active = 0;
 
421
        } else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) {
 
422
            if (--active == -1)
 
423
                active = pen->num_vertices - 1;
 
424
        } else {
 
425
            i += step;
 
426
        }
 
427
    }
 
428
}
 
429
 
 
430
/* Compute outline of a given spline using the pen.
 
431
   The trapezoids needed to fill that outline will be added to traps
 
432
*/
 
433
cairo_status_t
 
434
_cairo_pen_stroke_spline (cairo_pen_t           *pen,
 
435
                          cairo_spline_t        *spline,
 
436
                          double                tolerance,
 
437
                          cairo_traps_t         *traps)
 
438
{
 
439
    cairo_status_t status;
 
440
    cairo_polygon_t polygon;
 
441
 
 
442
    /* If the line width is so small that the pen is reduced to a
 
443
       single point, then we have nothing to do. */
 
444
    if (pen->num_vertices <= 1)
 
445
        return CAIRO_STATUS_SUCCESS;
 
446
 
 
447
    _cairo_polygon_init (&polygon);
 
448
 
 
449
    status = _cairo_spline_decompose (spline, tolerance);
 
450
    if (status)
 
451
        goto BAIL;
 
452
 
 
453
    _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_FORWARD, &polygon);
 
454
 
 
455
    _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_REVERSE, &polygon);
 
456
 
 
457
    _cairo_polygon_close (&polygon);
 
458
    status = _cairo_polygon_status (&polygon);
 
459
    if (status)
 
460
        goto BAIL;
 
461
 
 
462
    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, CAIRO_FILL_RULE_WINDING);
 
463
BAIL:
 
464
    _cairo_polygon_fini (&polygon);
 
465
 
 
466
    return status;
 
467
}