1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2008, 2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 * @short_description: Contiguous lines
25 * A segment is a single contiguous line got from a cairo path.
27 * Every #cairo_path_t struct can contain more than one segment:
28 * the CPML library provides iteration APIs to browse these segments.
29 * The <structname>CpmlSegment</structname> struct internally keeps
30 * a reference to the source #cairo_path_t so it can be used both
31 * for getting segment data and browsing the segments (that is,
32 * it is used also as an iterator).
34 * Use cpml_segment_reset() to reset the iterator at the start of the
35 * cairo path (will point the first segment) and cpml_segment_next()
36 * to get the next segment. When initialized,
37 * <structname>CpmlSegment</structname> yet refers to the first
38 * segment, so the initial reset is useless.
40 * Getting the previous segment is not provided, as the underlying
41 * cairo struct is not accessible in reverse order.
46 * @cairo_path: the source #cairo_path_t struct
47 * @data: the segment data
48 * @num_data: size of @data
50 * This is an unobtrusive struct to identify a segment inside a
51 * cairo path. Unobtrusive means that the real coordinates are
52 * still stored in @source: CpmlSegment only provides a way to
53 * access the underlying cairo path.
56 #include "cpml-segment.h"
57 #include "cpml-primitive.h"
58 #include "cpml-line.h"
59 #include "cpml-curve.h"
60 #include "cpml-pair.h"
61 #include "cpml-alloca.h"
66 static cairo_bool_t
normalize (CpmlSegment
*segment
);
67 static cairo_bool_t
ensure_one_move_to (CpmlSegment
*segment
);
68 static void reshape (CpmlSegment
*segment
);
72 * cpml_segment_from_cairo:
73 * @segment: a #CpmlSegment
74 * @cairo_path: the source #cairo_path_t
76 * Builds a CpmlSegment from a cairo_path_t structure. This operation
77 * involves stripping the leading %CAIRO_PATH_MOVE_TO primitives and
78 * setting the internal segment structure accordling. A pointer to the
79 * source cairo path is kept.
81 * This function will fail if @cairo_path is null, empty or if its
82 * <structfield>status</structfield> member is not %CAIRO_STATUS_SUCCESS.
83 * Also, the first primitive must be a %CAIRO_PATH_MOVE_TO, so no
84 * dependency on the cairo context is needed.
86 * Return value: 1 on success, 0 on errors
89 cpml_segment_from_cairo(CpmlSegment
*segment
, cairo_path_t
*cairo_path
)
91 /* The cairo path should be defined and in perfect state */
92 if (cairo_path
== NULL
|| cairo_path
->num_data
== 0 ||
93 cairo_path
->status
!= CAIRO_STATUS_SUCCESS
)
96 segment
->cairo_path
= cairo_path
;
97 segment
->data
= cairo_path
->data
;
98 segment
->num_data
= cairo_path
->num_data
;
100 return normalize(segment
);
105 * @segment: a #CpmlSegment
106 * @src: the source segment to copy
108 * Makes a shallow copy of @src into @segment.
110 * Return value: @segment or %NULL on errors
113 cpml_segment_copy(CpmlSegment
*segment
, const CpmlSegment
*src
)
115 if (segment
== NULL
|| src
== NULL
)
118 return memcpy(segment
, src
, sizeof(CpmlSegment
));
123 * cpml_segment_reset:
124 * @segment: a #CpmlSegment
126 * Modifies @segment to point to the first segment of the source cairo path.
129 cpml_segment_reset(CpmlSegment
*segment
)
131 segment
->data
= segment
->cairo_path
->data
;
132 segment
->num_data
= segment
->cairo_path
->num_data
;
138 * @segment: a #CpmlSegment
140 * Modifies @segment to point to the next segment of the source cairo path.
142 * Return value: 1 on success, 0 if no next segment found or errors
145 cpml_segment_next(CpmlSegment
*segment
)
147 int rest
= segment
->cairo_path
->num_data
- segment
->num_data
+
148 segment
->cairo_path
->data
- segment
->data
;
153 segment
->data
+= segment
->num_data
;
154 segment
->num_data
= rest
;
156 return normalize(segment
);
161 * cpml_segment_to_cairo:
162 * @segment: a #CpmlSegment
163 * @cr: the destination cairo context
165 * Appends the path of @segment to @cr using cairo_append_path().
168 cpml_segment_to_cairo(const CpmlSegment
*segment
, cairo_t
*cr
)
172 path
.status
= CAIRO_STATUS_SUCCESS
;
173 path
.data
= segment
->data
;
174 path
.num_data
= segment
->num_data
;
176 cairo_append_path(cr
, &path
);
181 * @segment: a #CpmlSegment
183 * Dumps the specified @segment to stdout. Useful for debugging purposes.
186 cpml_segment_dump(const CpmlSegment
*segment
)
188 CpmlPrimitive primitive
;
189 cairo_bool_t first_call
= 1;
191 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
194 cpml_primitive_dump(&primitive
, first_call
);
196 } while (cpml_primitive_next(&primitive
));
201 * cpml_segment_reverse:
202 * @segment: a #CpmlSegment
204 * Reverses @segment in-place. The resulting rendering will be the same,
205 * but with the primitives generated in reverse order.
208 cpml_segment_reverse(CpmlSegment
*segment
)
210 cairo_path_data_t
*data
, *dst_data
;
213 int n
, num_points
, n_point
;
214 const cairo_path_data_t
*src_data
;
216 data_size
= sizeof(cairo_path_data_t
) * segment
->num_data
;
217 data
= cpml_alloca(data_size
);
218 end_x
= segment
->data
[1].point
.x
;
219 end_y
= segment
->data
[1].point
.y
;
221 for (n
= 2; n
< segment
->num_data
; ++n
) {
222 src_data
= segment
->data
+ n
;
223 num_points
= src_data
->header
.length
;
225 dst_data
= data
+ segment
->num_data
- n
- num_points
+ 2;
226 dst_data
->header
.type
= src_data
->header
.type
;
227 dst_data
->header
.length
= num_points
;
229 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
230 dst_data
[num_points
- n_point
].point
.x
= end_x
;
231 dst_data
[num_points
- n_point
].point
.y
= end_y
;
232 end_x
= src_data
[n_point
].point
.x
;
233 end_y
= src_data
[n_point
].point
.y
;
239 data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
240 data
[0].header
.length
= 2;
241 data
[1].point
.x
= end_x
;
242 data
[1].point
.y
= end_y
;
243 memcpy(segment
->data
, data
, data_size
);
247 * cpml_segment_transform:
248 * @segment: a #CpmlSegment
249 * @matrix: the matrix to be applied
251 * Applies @matrix on all the points of @segment.
254 cpml_segment_transform(CpmlSegment
*segment
, const cairo_matrix_t
*matrix
)
256 cairo_path_data_t
*data
;
257 int n
, n_point
, num_points
;
259 data
= segment
->data
;
261 for (n
= 0; n
< segment
->num_data
; n
+= num_points
) {
262 num_points
= data
->header
.length
;
264 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
265 cairo_matrix_transform_point(matrix
, &data
->point
.x
, &data
->point
.y
);
272 * cpml_segment_intersection:
273 * @segment: the first #CpmlSegment
274 * @segment2: the second #CpmlSegment
275 * @dest: the destination vector of #CpmlPair
276 * @max: maximum number of intersections to return
278 * Computes the intersections between @segment and @segment2 and
279 * returns the found points in @dest. If the intersections are more
280 * than @max, only the first @max pairs are stored in @dest.
282 * To get the job done, the primitives of @segment are sequentially
283 * scanned for intersections with any primitive in @segment2. This
284 * means @segment has a higher precedence over @segment2.
286 * Return value: the number of intersections found
289 cpml_segment_intersection(const CpmlSegment
*segment
,
290 const CpmlSegment
*segment2
,
291 CpmlPair
*dest
, int max
)
293 CpmlPrimitive portion
;
296 cpml_primitive_from_segment(&portion
, (CpmlSegment
*) segment
);
300 partial
= cpml_primitive_intersection_with_segment(&portion
,
305 } while (total
< max
&& cpml_primitive_next(&portion
));
311 * cpml_segment_offset:
312 * @segment: a #CpmlSegment
313 * @offset: the offset distance
315 * Offsets a segment of the specified amount, that is builds a "parallel"
316 * segment at the @offset distance from the original one and returns the
317 * result by replacing the original @segment.
320 * <title>TODO</title>
322 * <listitem>Closed path are not yet managed: an elegant solution is not
323 * so obvious: use cpml_close_offset() when available.</listitem>
324 * <listitem>Degenerated primitives, such as lines of length 0, are not
325 * managed properly.</listitem>
330 cpml_segment_offset(CpmlSegment
*segment
, double offset
)
332 CpmlPrimitive primitive
;
333 CpmlPrimitive last_primitive
;
334 cairo_path_data_t org
, old_end
;
335 cairo_bool_t first_cycle
;
337 cpml_primitive_from_segment(&primitive
, segment
);
343 primitive
.org
= &org
;
346 old_end
= *cpml_primitive_get_point(&primitive
, -1);
347 cpml_primitive_offset(&primitive
, offset
);
350 cpml_primitive_join(&last_primitive
, &primitive
);
351 primitive
.org
= cpml_primitive_get_point(&last_primitive
, -1);
354 cpml_primitive_copy(&last_primitive
, &primitive
);
356 } while (cpml_primitive_next(&primitive
));
361 * @segment: a #CpmlSegment
363 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
364 * the CpmlSegment structure accordling. One, and only one,
365 * %CAIRO_PATH_MOVE_TO primitive is left.
367 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
370 normalize(CpmlSegment
*segment
)
372 if (!ensure_one_move_to(segment
))
380 * ensure_one_move_to:
381 * @segment: a #CpmlSegment
383 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
384 * the <structname>CpmlSegment</structname> structure accordling.
385 * One, and only one, %CAIRO_PATH_MOVE_TO primitive is left.
387 * Return value: 1 on success, 0 on no leading MOVE_TOs or on empty path
390 ensure_one_move_to(CpmlSegment
*segment
)
392 cairo_path_data_t
*new_data
;
393 int new_num_data
, length
;
395 new_data
= segment
->data
;
397 /* Check for at least one move to */
398 if (new_data
->header
.type
!= CAIRO_PATH_MOVE_TO
)
401 new_num_data
= segment
->num_data
;
404 /* Strip the leading CAIRO_PATH_MOVE_TO, leaving only the last one */
407 new_num_data
-= length
;
408 length
= new_data
->header
.length
;
410 /* Check for end of cairo path data */
411 if (length
>= new_num_data
)
413 } while (new_data
[length
].header
.type
== CAIRO_PATH_MOVE_TO
);
415 segment
->data
= new_data
;
416 segment
->num_data
= new_num_data
;
423 * @segment: a #CpmlSegment
425 * Looks for the segment termination and modify the
426 * <structfield>num_data</structfield> field of @segment accordling.
427 * @segment must have only one leading %CAIRO_PATH_MOVE_TO and
428 * it is supposed to be non-empty, conditions yet imposed by the
429 * ensure_one_move_to() function.
432 reshape(CpmlSegment
*segment
)
434 cairo_path_data_t
*data
;
435 int num_data
, new_num_data
, length
;
437 /* Skip the leading move to */
439 data
= segment
->data
+ new_num_data
;
441 /* Calculate the remaining data in the cairo path */
442 num_data
= segment
->cairo_path
->num_data
-
443 (segment
->data
- segment
->cairo_path
->data
);
445 while (new_num_data
< num_data
) {
446 /* A primitive is considered valid if it has implemented
447 * its own type_get_npoints() */
448 if (cpml_primitive_type_get_npoints(data
->header
.type
) < 0)
451 length
= data
->header
.length
;
453 new_num_data
+= length
;
456 segment
->num_data
= new_num_data
;