1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2008, 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.
21 #include "cpml-path.h"
22 #include "cpml-alloca.h"
27 static cairo_bool_t
strip_leadings (CpmlPath
*path
);
28 static cairo_bool_t
path_to_segment (CpmlPath
*segment
,
29 const CpmlPath
*path
);
30 static cairo_bool_t
path_to_primitive (CpmlPath
*primitive
,
31 const CpmlPath
*path
);
34 * cpml_path_from_cairo:
35 * @path: the destination #CpmlPath structure
36 * @src: the source cairo_path_t
37 * @cr: a cairo context
39 * Builds a CpmlPath from a cairo_path_t structure. This operations involves
40 * stripping the leading %MOVE_TO primitives and setting the @path->org
41 * coordinates. If not needed, @cr could be %NULL.
43 * The origin is got from the prepending %MOVE_TO coordinates on @src.
44 * If not found, the @cr current point is used. If @cr is %NULL, the
45 * (0,0) fallback pair is used.
47 * @path and @src can be the same object (a #CpmlPath structure).
49 * Return value: 1 on success, 0 on errors
52 cpml_path_from_cairo(CpmlPath
*path
, const cairo_path_t
*src
, cairo_t
*cr
)
54 CpmlPair org
= { 0., 0. };
56 if (cr
&& cairo_has_current_point(cr
))
57 cairo_get_current_point(cr
, &org
.x
, &org
.y
);
59 return cpml_path_from_cairo_explicit(path
, src
, &org
);
63 * cpml_path_from_cairo_explicit:
64 * @path: an allocated #CpmlPath structure
65 * @src: the source cairo_path_t
66 * @org: a specific origin
68 * Similar to cpml_path_from_cairo() but using an explicit origin.
69 * @path and @src can be the same object (a #CpmlPath structure).
71 * @org must be defined. If not needed consider using cpml_path_from_cairo()
72 * with a %NULL cr argument.
74 * Return value: 1 on success, 0 on errors
77 cpml_path_from_cairo_explicit(CpmlPath
*path
, const cairo_path_t
*src
,
80 if (path
== NULL
|| src
== NULL
|| org
== NULL
|| src
->data
== NULL
)
83 if (path
!= (CpmlPath
*) src
)
84 memcpy(&path
->cairo_path
, src
, sizeof(cairo_path_t
));
86 if (strip_leadings(path
)) {
87 /* org taken from leadings MOVE_TO */
88 } else if (path
->cairo_path
.status
!= CAIRO_STATUS_SUCCESS
) {
89 /* Error: probably an empty path provided */
92 /* Use the provided org */
93 cpml_pair_copy(&path
->org
, org
);
100 * cpml_path_to_cairo:
102 * @cr: a cairo context
104 * Appends @path to the specified cairo context.
107 cpml_path_to_cairo(const CpmlPath
*path
, cairo_t
*cr
)
109 cairo_move_to(cr
, path
->org
.x
, path
->org
.y
);
110 cairo_append_path(cr
, (cairo_path_t
*) path
);
115 * @path: an allocated #CpmlPath
116 * @src: the source path to copy
118 * Strips the leading CAIRO_PATH_MOVE_TO primitives, updating the CpmlPath
119 * structure accordling. Also the path org is update.
121 * Return value: @path or %NULL on errors
124 cpml_path_copy(CpmlPath
*path
, const CpmlPath
*src
)
126 if (path
== NULL
|| src
== NULL
)
129 return memcpy(path
, src
, sizeof(CpmlPath
));
134 * @path: an allocated #CpmlPath
136 * Dumps the specified @path to stdout. Useful for debug purposes.
138 * Return value: 1 on success, 0 on errors
141 cpml_path_dump(CpmlPath
*path
)
143 cairo_path_t
*cairo_path
;
144 cairo_path_data_t
*data
;
150 printf("Origin in (%lf, %lf)\n", path
->org
.x
, path
->org
.y
);
152 cairo_path
= &path
->cairo_path
;
153 for (n_data
= 0; n_data
< cairo_path
->num_data
; ++n_data
) {
154 data
= cairo_path
->data
+ n_data
;
156 switch (data
->header
.type
) {
157 case CAIRO_PATH_MOVE_TO
:
160 case CAIRO_PATH_LINE_TO
:
163 case CAIRO_PATH_CURVE_TO
:
166 case CAIRO_PATH_CLOSE_PATH
:
167 printf("Path close");
170 printf("Unknown entity (%d)", data
->header
.type
);
174 for (n_point
= 1; n_point
< data
->header
.length
; ++n_point
)
175 printf("(%lf, %lf) ", data
[n_point
].point
.x
,
176 data
[n_point
].point
.y
);
178 n_data
+= n_point
- 1;
186 * cpml_segment_from_path:
187 * @segment: an allocated #CpmlPath struct
188 * @path: the source path
189 * @index: the segment to retrieve (starting from 1);
190 * %CPML_FIRST or %CPML_LAST can be used
192 * Gets a specific segment from a path.
194 * Return value: 1 if a valid segment was found, 0 on errors
197 cpml_segment_from_path(CpmlPath
*segment
, const CpmlPath
*path
, int index
)
199 CpmlPath residue
, result
;
202 if (!cpml_path_copy(&residue
, path
))
208 if (!path_to_segment(&result
, &residue
))
209 return index
== CPML_LAST
&& i
> 0;
211 residue
.cairo_path
.data
+= result
.cairo_path
.num_data
;
212 residue
.cairo_path
.num_data
-= result
.cairo_path
.num_data
;
215 if (index
== CPML_LAST
) {
216 cpml_path_copy(segment
, &result
);
221 cpml_path_copy(segment
, &result
);
226 * cpml_segment_reverse:
227 * @segment: a #CpmlPath
228 * @src: the source segment
230 * Reverses @src and stores the result in @segment. @src and @segment
231 * can be the same structure.
233 * Return value: 1 on success, 0 on errors
236 cpml_segment_reverse(CpmlPath
*segment
, const CpmlPath
*src
)
238 cairo_path_data_t
*data
, *dst_data
;
241 int num_data
, n_data
;
242 int num_points
, n_point
;
243 const cairo_path_data_t
*src_data
;
245 num_data
= src
->cairo_path
.num_data
;
246 data_size
= sizeof(cairo_path_data_t
) * num_data
;
247 data
= cpml_alloca(data_size
);
248 cpml_pair_copy(&end
, &src
->org
);
250 for (n_data
= 0; n_data
< num_data
; ++n_data
) {
251 src_data
= src
->cairo_path
.data
+ n_data
;
252 num_points
= src_data
->header
.length
;
254 dst_data
= data
+ num_data
- n_data
- num_points
;
255 dst_data
->header
.type
= src_data
->header
.type
;
256 dst_data
->header
.length
= num_points
;
258 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
259 dst_data
[num_points
- n_point
].point
.x
= end
.x
;
260 dst_data
[num_points
- n_point
].point
.y
= end
.y
;
261 end
.x
= src_data
[n_point
].point
.x
;
262 end
.y
= src_data
[n_point
].point
.y
;
265 n_data
+= n_point
- 1;
268 cpml_pair_copy(&segment
->org
, &end
);
269 memcpy(segment
->cairo_path
.data
, data
, data_size
);
275 * cpml_primitive_from_path:
276 * @primitive: an allocated #CpmlPath struct
277 * @path: the source path
278 * @index: the primitive to retrieve (starting from 1);
279 * %CPML_FIRST or %CPML_LAST can be used
281 * Gets a specific primitive from a path.
283 * Return value: 1 if a valid primitive was found, 0 on errors
286 cpml_primitive_from_path(CpmlPath
*primitive
, const CpmlPath
*path
, int index
)
288 CpmlPath residue
, result
;
291 if (!cpml_path_copy(&residue
, path
))
297 if (!path_to_primitive(&result
, &residue
))
298 return index
== CPML_LAST
&& i
> 0;
300 residue
.cairo_path
.data
+= result
.cairo_path
.num_data
;
301 residue
.cairo_path
.num_data
-= result
.cairo_path
.num_data
;
304 if (index
== CPML_LAST
) {
305 cpml_path_copy(primitive
, &result
);
310 cpml_path_copy(primitive
, &result
);
315 * cpml_primitive_get_pair:
316 * @primitive: the source primitive
317 * @pair: the allocated CpmlPair destination
318 * @index: index of the pair to retrieve, starting from 1
320 * Shortcut to get a pair from a primitive.
322 * Return value: 1 on success, 0 on errors
325 cpml_primitive_get_pair(const CpmlPath
*primitive
, CpmlPair
*pair
, int index
)
327 cairo_path_data_t
*data
= primitive
->cairo_path
.data
;
329 if (index
== 0 || index
> data
[0].header
.length
)
332 pair
->x
= data
[index
].point
.x
;
333 pair
->y
= data
[index
].point
.y
;
338 * cpml_primitive_set_pair:
339 * @primitive: an allocated CpmlPath primitive
340 * @pair: the source pair
341 * @index: index of the pair to change, starting from 1
343 * Shortcut to set a pair on a primitive.
345 * Return value: 1 on success, 0 on errors
348 cpml_primitive_set_pair(CpmlPath
*primitive
, const CpmlPair
*pair
, int index
)
350 cairo_path_data_t
*data
= primitive
->cairo_path
.data
;
352 if (index
== 0 || index
> data
[0].header
.length
)
355 data
[index
].point
.x
= pair
->x
;
356 data
[index
].point
.y
= pair
->y
;
361 * cpml_primitive_get_point:
362 * @primitive: the source primitive
363 * @point: the allocated CpmlPair destination
364 * @pos: the position factor, being 0 the starting and 1 the ending point
366 * Gets a point lying on @primitive at a specific percentual position.
368 * Return value: 1 on success, 0 on errors
371 cpml_primitive_get_point(const CpmlPath
*primitive
, CpmlPair
*point
, double pos
)
373 int type
= primitive
->cairo_path
.data
[0].header
.type
;
375 if (type
!= CAIRO_PATH_CLOSE_PATH
&& type
!= CAIRO_PATH_LINE_TO
&&
376 type
!= CAIRO_PATH_CURVE_TO
)
380 if (type
== CAIRO_PATH_CLOSE_PATH
|| pos
== 0.) {
381 return cpml_pair_copy(point
, &primitive
->org
);
382 } else if (pos
== 1.0) {
383 int n
= type
== CAIRO_PATH_LINE_TO
? 1 : 3;
384 return cpml_primitive_get_pair(primitive
, point
, n
);
392 * cpml_primitive_reverse:
393 * @primitive: an allocated #CpmlPath struct
395 * Reverses a primitive.
397 * Return value: 1 on success, 0 on errors
400 cpml_primitive_reverse(CpmlPath
*primitive
)
404 switch (primitive
->cairo_path
.data
[0].header
.type
) {
405 case CAIRO_PATH_LINE_TO
:
406 cpml_pair_copy(&tmp
, &primitive
->org
);
407 cpml_primitive_get_pair(primitive
, &primitive
->org
, 1);
408 cpml_primitive_set_pair(primitive
, &tmp
, 1);
410 case CAIRO_PATH_CURVE_TO
:
411 cpml_pair_copy(&tmp
, &primitive
->org
);
412 cpml_primitive_get_pair(primitive
, &primitive
->org
, 3);
413 cpml_primitive_set_pair(primitive
, &tmp
, 3);
415 cpml_primitive_get_pair(primitive
, &tmp
, 2);
416 primitive
->cairo_path
.data
[2].point
.x
=
417 primitive
->cairo_path
.data
[3].point
.x
;
418 primitive
->cairo_path
.data
[2].point
.y
=
419 primitive
->cairo_path
.data
[3].point
.y
;
420 cpml_primitive_set_pair(primitive
, &tmp
, 3);
433 * Strips the leading CAIRO_PATH_MOVE_TO primitives, updating the CpmlPath
434 * structure accordling. Also the path org is update.
436 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
437 * (check for path->cairo_path.status == CAIRO_STATUS_SUCCESS)
440 strip_leadings(CpmlPath
*path
)
442 if (path
->cairo_path
.data
[0].header
.type
!= CAIRO_PATH_MOVE_TO
)
446 ++path
->cairo_path
.data
;
447 path
->org
.x
= path
->cairo_path
.data
->point
.x
;
448 path
->org
.y
= path
->cairo_path
.data
->point
.y
;
449 ++path
->cairo_path
.data
;
450 path
->cairo_path
.num_data
-= 2;
451 if (path
->cairo_path
.num_data
<= 0) {
452 path
->cairo_path
.status
= CAIRO_STATUS_INVALID_PATH_DATA
;
455 } while (path
->cairo_path
.data
->header
.type
== CAIRO_PATH_MOVE_TO
);
462 * @segment: an allocated #CpmlPath struct
463 * @path: the source path
465 * Converts a path to a segment. @segment and @path can be the same struct.
467 * Return value: 1 if a valid segment is found, 0 on errors
470 path_to_segment(CpmlPath
*segment
, const CpmlPath
*path
)
472 cairo_path_data_t
*path_data
;
476 cpml_path_copy(segment
, path
);
478 if (!strip_leadings(segment
) &&
479 segment
->cairo_path
.status
!= CAIRO_STATUS_SUCCESS
)
482 path_data
= segment
->cairo_path
.data
;
486 if (path_data
->header
.type
== CAIRO_PATH_MOVE_TO
) {
489 } else if (path_data
->header
.type
== CAIRO_PATH_CLOSE_PATH
) {
492 i
+= path_data
->header
.length
;
493 path_data
+= path_data
->header
.length
;
494 } while (i
< segment
->cairo_path
.num_data
);
496 segment
->cairo_path
.num_data
= i
;
502 * @primitive: an allocated #CpmlPath struct
503 * @path: the source path
505 * Converts a path to a primitive. @primitive and @path can be the same struct.
507 * Return value: 1 if a valid primitive is found, 0 on errors
510 path_to_primitive(CpmlPath
*primitive
, const CpmlPath
*path
)
512 cairo_path_data_t
*path_data
;
514 if (primitive
!= path
)
515 cpml_path_copy(primitive
, path
);
517 if (!strip_leadings(primitive
) &&
518 primitive
->cairo_path
.status
!= CAIRO_STATUS_SUCCESS
)
521 primitive
->cairo_path
.num_data
= 1;