Merge remote branch 'origin/master' into nvc0-new
[mesa.git] / src / gallium / state_trackers / vega / path.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "path.h"
28
29 #include "stroker.h"
30 #include "polygon.h"
31 #include "bezier.h"
32 #include "matrix.h"
33 #include "vg_context.h"
34 #include "util_array.h"
35 #include "arc.h"
36 #include "path_utils.h"
37 #include "paint.h"
38 #include "shader.h"
39
40 #include "util/u_memory.h"
41
42 #include <assert.h>
43
44 #define DEBUG_PATH 0
45
46 struct path {
47 struct vg_object base;
48 VGbitfield caps;
49 VGboolean dirty;
50 VGboolean dirty_stroke;
51
52 VGPathDatatype datatype;
53
54 VGfloat scale;
55 VGfloat bias;
56
57 VGint num_segments;
58
59 struct array * segments;
60 struct array * control_points;
61
62 struct {
63 struct polygon_array polygon_array;
64 struct matrix matrix;
65 } fill_polys;
66
67 struct {
68 struct path *path;
69 struct matrix matrix;
70 VGfloat stroke_width;
71 VGfloat miter_limit;
72 VGCapStyle cap_style;
73 VGJoinStyle join_style;
74 } stroked;
75 };
76
77
78 static INLINE void data_at(void **data,
79 struct path *p,
80 VGint start, VGint count,
81 VGfloat *out)
82 {
83 VGPathDatatype dt = p->datatype;
84 VGint i;
85 VGint end = start + count;
86 VGfloat *itr = out;
87
88 switch(dt) {
89 case VG_PATH_DATATYPE_S_8: {
90 VGbyte **bdata = (VGbyte **)data;
91 for (i = start; i < end; ++i) {
92 *itr = (*bdata)[i];
93 ++itr;
94 }
95 *bdata += count;
96 }
97 break;
98 case VG_PATH_DATATYPE_S_16: {
99 VGshort **bdata = (VGshort **)data;
100 for (i = start; i < end; ++i) {
101 *itr = (*bdata)[i];
102 ++itr;
103 }
104 *bdata += count;
105 }
106 break;
107 case VG_PATH_DATATYPE_S_32: {
108 VGint **bdata = (VGint **)data;
109 for (i = start; i < end; ++i) {
110 *itr = (*bdata)[i];
111 ++itr;
112 }
113 *bdata += count;
114 }
115 break;
116 case VG_PATH_DATATYPE_F: {
117 VGfloat **fdata = (VGfloat **)data;
118 for (i = start; i < end; ++i) {
119 *itr = (*fdata)[i];
120 ++itr;
121 }
122 *fdata += count;
123 }
124 break;
125 default:
126 debug_assert(!"Unknown path datatype!");
127 }
128 }
129
130
131 void vg_float_to_datatype(VGPathDatatype datatype,
132 VGubyte *common_data,
133 const VGfloat *data,
134 VGint num_coords)
135 {
136 VGint i;
137 switch(datatype) {
138 case VG_PATH_DATATYPE_S_8: {
139 for (i = 0; i < num_coords; ++i) {
140 common_data[i] = (VGubyte)data[i];
141 }
142 }
143 break;
144 case VG_PATH_DATATYPE_S_16: {
145 VGshort *buf = (VGshort*)common_data;
146 for (i = 0; i < num_coords; ++i) {
147 buf[i] = (VGshort)data[i];
148 }
149 }
150 break;
151 case VG_PATH_DATATYPE_S_32: {
152 VGint *buf = (VGint*)common_data;
153 for (i = 0; i < num_coords; ++i) {
154 buf[i] = (VGint)data[i];
155 }
156 }
157 break;
158 case VG_PATH_DATATYPE_F: {
159 memcpy(common_data, data, sizeof(VGfloat) * num_coords);
160 }
161 break;
162 default:
163 debug_assert(!"Unknown path datatype!");
164 }
165 }
166
167 static void coords_adjust_by_scale_bias(struct path *p,
168 void *pdata, VGint num_coords,
169 VGfloat scale, VGfloat bias,
170 VGPathDatatype datatype)
171 {
172 VGfloat data[8];
173 void *coords = (VGfloat *)pdata;
174 VGubyte *common_data = (VGubyte *)pdata;
175 VGint size_dst = size_for_datatype(datatype);
176 VGint i;
177
178 for (i = 0; i < num_coords; ++i) {
179 data_at(&coords, p, 0, 1, data);
180 data[0] = data[0] * scale + bias;
181 vg_float_to_datatype(datatype, common_data, data, 1);
182 common_data += size_dst;
183 }
184 }
185
186 struct path * path_create(VGPathDatatype dt, VGfloat scale, VGfloat bias,
187 VGint segmentCapacityHint,
188 VGint coordCapacityHint,
189 VGbitfield capabilities)
190 {
191 struct path *path = CALLOC_STRUCT(path);
192
193 vg_init_object(&path->base, vg_current_context(), VG_OBJECT_PATH);
194 path->caps = capabilities & VG_PATH_CAPABILITY_ALL;
195 vg_context_add_object(vg_current_context(), VG_OBJECT_PATH, path);
196
197 path->datatype = dt;
198 path->scale = scale;
199 path->bias = bias;
200
201 path->segments = array_create(size_for_datatype(VG_PATH_DATATYPE_S_8));
202 path->control_points = array_create(size_for_datatype(dt));
203
204 path->dirty = VG_TRUE;
205 path->dirty_stroke = VG_TRUE;
206
207 return path;
208 }
209
210 static void polygon_array_cleanup(struct polygon_array *polyarray)
211 {
212 if (polyarray->array) {
213 VGint i;
214
215 for (i = 0; i < polyarray->array->num_elements; i++) {
216 struct polygon *p = ((struct polygon **) polyarray->array->data)[i];
217 polygon_destroy(p);
218 }
219
220 array_destroy(polyarray->array);
221 polyarray->array = NULL;
222 }
223 }
224
225 void path_destroy(struct path *p)
226 {
227 vg_context_remove_object(vg_current_context(), VG_OBJECT_PATH, p);
228
229 array_destroy(p->segments);
230 array_destroy(p->control_points);
231
232 polygon_array_cleanup(&p->fill_polys.polygon_array);
233
234 if (p->stroked.path)
235 path_destroy(p->stroked.path);
236
237 FREE(p);
238 }
239
240 VGbitfield path_capabilities(struct path *p)
241 {
242 return p->caps;
243 }
244
245 void path_set_capabilities(struct path *p, VGbitfield bf)
246 {
247 p->caps = (bf & VG_PATH_CAPABILITY_ALL);
248 }
249
250 void path_append_data(struct path *p,
251 VGint numSegments,
252 const VGubyte * pathSegments,
253 const void * pathData)
254 {
255 VGint old_segments = p->num_segments;
256 VGint num_new_coords = num_elements_for_segments(pathSegments, numSegments);
257 array_append_data(p->segments, pathSegments, numSegments);
258 array_append_data(p->control_points, pathData, num_new_coords);
259
260 p->num_segments += numSegments;
261 if (!floatsEqual(p->scale, 1.f) || !floatsEqual(p->bias, 0.f)) {
262 VGubyte *coords = (VGubyte*)p->control_points->data;
263 coords_adjust_by_scale_bias(p,
264 coords + old_segments * p->control_points->datatype_size,
265 num_new_coords,
266 p->scale, p->bias, p->datatype);
267 }
268 p->dirty = VG_TRUE;
269 p->dirty_stroke = VG_TRUE;
270 }
271
272 VGint path_num_segments(struct path *p)
273 {
274 return p->num_segments;
275 }
276
277 static INLINE void map_if_relative(VGfloat ox, VGfloat oy,
278 VGboolean relative,
279 VGfloat *x, VGfloat *y)
280 {
281 if (relative) {
282 if (x)
283 *x += ox;
284 if (y)
285 *y += oy;
286 }
287 }
288
289 static INLINE void close_polygon(struct polygon *current,
290 VGfloat sx, VGfloat sy,
291 VGfloat ox, VGfloat oy,
292 struct matrix *matrix)
293 {
294 if (!floatsEqual(sx, ox) ||
295 !floatsEqual(sy, oy)) {
296 VGfloat x0 = sx;
297 VGfloat y0 = sy;
298 matrix_map_point(matrix, x0, y0, &x0, &y0);
299 polygon_vertex_append(current, x0, y0);
300 }
301 }
302
303 static void convert_path(struct path *p,
304 VGPathDatatype to,
305 void *dst,
306 VGint num_coords)
307 {
308 VGfloat data[8];
309 void *coords = (VGfloat *)p->control_points->data;
310 VGubyte *common_data = (VGubyte *)dst;
311 VGint size_dst = size_for_datatype(to);
312 VGint i;
313
314 for (i = 0; i < num_coords; ++i) {
315 data_at(&coords, p, 0, 1, data);
316 vg_float_to_datatype(to, common_data, data, 1);
317 common_data += size_dst;
318 }
319 }
320
321 static void polygon_array_calculate_bounds( struct polygon_array *polyarray )
322 {
323 struct array *polys = polyarray->array;
324 VGfloat min_x, max_x;
325 VGfloat min_y, max_y;
326 VGfloat bounds[4];
327 unsigned i;
328
329 assert(polys);
330
331 if (!polys->num_elements) {
332 polyarray->min_x = 0.0f;
333 polyarray->min_y = 0.0f;
334 polyarray->max_x = 0.0f;
335 polyarray->max_y = 0.0f;
336 return;
337 }
338
339 polygon_bounding_rect((((struct polygon**)polys->data)[0]), bounds);
340 min_x = bounds[0];
341 min_y = bounds[1];
342 max_x = bounds[0] + bounds[2];
343 max_y = bounds[1] + bounds[3];
344 for (i = 1; i < polys->num_elements; ++i) {
345 struct polygon *p = (((struct polygon**)polys->data)[i]);
346 polygon_bounding_rect(p, bounds);
347 min_x = MIN2(min_x, bounds[0]);
348 min_y = MIN2(min_y, bounds[1]);
349 max_x = MAX2(max_x, bounds[0] + bounds[2]);
350 max_y = MAX2(max_y, bounds[1] + bounds[3]);
351 }
352
353 polyarray->min_x = min_x;
354 polyarray->min_y = min_y;
355 polyarray->max_x = max_x;
356 polyarray->max_y = max_y;
357 }
358
359
360 static struct polygon_array * path_get_fill_polygons(struct path *p, struct matrix *matrix)
361 {
362 VGint i;
363 struct polygon *current = 0;
364 VGfloat sx, sy, px, py, ox, oy;
365 VGfloat x0, y0, x1, y1, x2, y2, x3, y3;
366 VGfloat data[8];
367 void *coords = (VGfloat *)p->control_points->data;
368 struct array *array;
369
370 if (p->fill_polys.polygon_array.array)
371 {
372 if (memcmp( &p->fill_polys.matrix,
373 matrix,
374 sizeof *matrix ) == 0 && p->dirty == VG_FALSE)
375 {
376 return &p->fill_polys.polygon_array;
377 }
378 else {
379 polygon_array_cleanup(&p->fill_polys.polygon_array);
380 }
381 }
382
383 /* an array of pointers to polygons */
384 array = array_create(sizeof(struct polygon *));
385
386 sx = sy = px = py = ox = oy = 0.f;
387
388 if (p->num_segments)
389 current = polygon_create(32);
390
391 for (i = 0; i < p->num_segments; ++i) {
392 VGubyte segment = ((VGubyte*)(p->segments->data))[i];
393 VGint command = SEGMENT_COMMAND(segment);
394 VGboolean relative = SEGMENT_ABS_REL(segment);
395
396 switch(command) {
397 case VG_CLOSE_PATH:
398 close_polygon(current, sx, sy, ox, oy, matrix);
399 ox = sx;
400 oy = sy;
401 break;
402 case VG_MOVE_TO:
403 if (current && polygon_vertex_count(current) > 0) {
404 /* add polygon */
405 close_polygon(current, sx, sy, ox, oy, matrix);
406 array_append_data(array, &current, 1);
407 current = polygon_create(32);
408 }
409 data_at(&coords, p, 0, 2, data);
410 x0 = data[0];
411 y0 = data[1];
412 map_if_relative(ox, oy, relative, &x0, &y0);
413 sx = x0;
414 sy = y0;
415 ox = x0;
416 oy = y0;
417 px = x0;
418 py = y0;
419 matrix_map_point(matrix, x0, y0, &x0, &y0);
420 polygon_vertex_append(current, x0, y0);
421 break;
422 case VG_LINE_TO:
423 data_at(&coords, p, 0, 2, data);
424 x0 = data[0];
425 y0 = data[1];
426 map_if_relative(ox, oy, relative, &x0, &y0);
427 ox = x0;
428 oy = y0;
429 px = x0;
430 py = y0;
431 matrix_map_point(matrix, x0, y0, &x0, &y0);
432 polygon_vertex_append(current, x0, y0);
433 break;
434 case VG_HLINE_TO:
435 data_at(&coords, p, 0, 1, data);
436 x0 = data[0];
437 y0 = oy;
438 map_if_relative(ox, oy, relative, &x0, 0);
439 ox = x0;
440 px = x0;
441 py = y0;
442 matrix_map_point(matrix, x0, y0, &x0, &y0);
443 polygon_vertex_append(current, x0, y0);
444 break;
445 case VG_VLINE_TO:
446 data_at(&coords, p, 0, 1, data);
447 x0 = ox;
448 y0 = data[0];
449 map_if_relative(ox, oy, relative, 0, &y0);
450 oy = y0;
451 px = x0;
452 py = y0;
453 matrix_map_point(matrix, x0, y0, &x0, &y0);
454 polygon_vertex_append(current, x0, y0);
455 break;
456 case VG_CUBIC_TO: {
457 struct bezier bezier;
458 data_at(&coords, p, 0, 6, data);
459 x0 = ox;
460 y0 = oy;
461 x1 = data[0];
462 y1 = data[1];
463 x2 = data[2];
464 y2 = data[3];
465 x3 = data[4];
466 y3 = data[5];
467 map_if_relative(ox, oy, relative, &x1, &y1);
468 map_if_relative(ox, oy, relative, &x2, &y2);
469 map_if_relative(ox, oy, relative, &x3, &y3);
470 ox = x3;
471 oy = y3;
472 px = x2;
473 py = y2;
474 assert(matrix_is_affine(matrix));
475 matrix_map_point(matrix, x0, y0, &x0, &y0);
476 matrix_map_point(matrix, x1, y1, &x1, &y1);
477 matrix_map_point(matrix, x2, y2, &x2, &y2);
478 matrix_map_point(matrix, x3, y3, &x3, &y3);
479 bezier_init(&bezier, x0, y0, x1, y1,
480 x2, y2, x3, y3);
481 bezier_add_to_polygon(&bezier, current);
482 }
483 break;
484 case VG_QUAD_TO: {
485 struct bezier bezier;
486 data_at(&coords, p, 0, 4, data);
487 x0 = ox;
488 y0 = oy;
489 x1 = data[0];
490 y1 = data[1];
491 x3 = data[2];
492 y3 = data[3];
493 map_if_relative(ox, oy, relative, &x1, &y1);
494 map_if_relative(ox, oy, relative, &x3, &y3);
495 px = x1;
496 py = y1;
497 { /* form a cubic out of it */
498 x2 = (x3 + 2*x1) / 3.f;
499 y2 = (y3 + 2*y1) / 3.f;
500 x1 = (x0 + 2*x1) / 3.f;
501 y1 = (y0 + 2*y1) / 3.f;
502 }
503 ox = x3;
504 oy = y3;
505 assert(matrix_is_affine(matrix));
506 matrix_map_point(matrix, x0, y0, &x0, &y0);
507 matrix_map_point(matrix, x1, y1, &x1, &y1);
508 matrix_map_point(matrix, x2, y2, &x2, &y2);
509 matrix_map_point(matrix, x3, y3, &x3, &y3);
510 bezier_init(&bezier, x0, y0, x1, y1,
511 x2, y2, x3, y3);
512 bezier_add_to_polygon(&bezier, current);
513 }
514 break;
515 case VG_SQUAD_TO: {
516 struct bezier bezier;
517 data_at(&coords, p, 0, 2, data);
518 x0 = ox;
519 y0 = oy;
520 x1 = 2*ox-px;
521 y1 = 2*oy-py;
522 x3 = data[0];
523 y3 = data[1];
524 map_if_relative(ox, oy, relative, &x3, &y3);
525 px = x1;
526 py = y1;
527 { /* form a cubic out of it */
528 x2 = (x3 + 2*x1) / 3.f;
529 y2 = (y3 + 2*y1) / 3.f;
530 x1 = (x0 + 2*x1) / 3.f;
531 y1 = (y0 + 2*y1) / 3.f;
532 }
533 ox = x3;
534 oy = y3;
535 assert(matrix_is_affine(matrix));
536 matrix_map_point(matrix, x0, y0, &x0, &y0);
537 matrix_map_point(matrix, x1, y1, &x1, &y1);
538 matrix_map_point(matrix, x2, y2, &x2, &y2);
539 matrix_map_point(matrix, x3, y3, &x3, &y3);
540 bezier_init(&bezier, x0, y0, x1, y1,
541 x2, y2, x3, y3);
542 bezier_add_to_polygon(&bezier, current);
543 }
544 break;
545 case VG_SCUBIC_TO: {
546 struct bezier bezier;
547 data_at(&coords, p, 0, 4, data);
548 x0 = ox;
549 y0 = oy;
550 x1 = 2*ox-px;
551 y1 = 2*oy-py;
552 x2 = data[0];
553 y2 = data[1];
554 x3 = data[2];
555 y3 = data[3];
556 map_if_relative(ox, oy, relative, &x2, &y2);
557 map_if_relative(ox, oy, relative, &x3, &y3);
558 ox = x3;
559 oy = y3;
560 px = x2;
561 py = y2;
562 assert(matrix_is_affine(matrix));
563 matrix_map_point(matrix, x0, y0, &x0, &y0);
564 matrix_map_point(matrix, x1, y1, &x1, &y1);
565 matrix_map_point(matrix, x2, y2, &x2, &y2);
566 matrix_map_point(matrix, x3, y3, &x3, &y3);
567 bezier_init(&bezier, x0, y0, x1, y1,
568 x2, y2, x3, y3);
569 bezier_add_to_polygon(&bezier, current);
570 }
571 break;
572 case VG_SCCWARC_TO:
573 case VG_SCWARC_TO:
574 case VG_LCCWARC_TO:
575 case VG_LCWARC_TO: {
576 VGfloat rh, rv, rot;
577 struct arc arc;
578
579 data_at(&coords, p, 0, 5, data);
580 x0 = ox;
581 y0 = oy;
582 rh = data[0];
583 rv = data[1];
584 rot = data[2];
585 x1 = data[3];
586 y1 = data[4];
587 map_if_relative(ox, oy, relative, &x1, &y1);
588 #if 0
589 debug_printf("------- ARC (%f, %f), (%f, %f) %f, %f, %f\n",
590 x0, y0, x1, y1, rh, rv, rot);
591 #endif
592 arc_init(&arc, command, x0, y0, x1, y1,
593 rh, rv, rot);
594 arc_add_to_polygon(&arc, current,
595 matrix);
596 ox = x1;
597 oy = y1;
598 px = x1;
599 py = y1;
600 }
601 break;
602 default:
603 abort();
604 assert(!"Unknown segment!");
605 }
606 }
607 if (current) {
608 if (polygon_vertex_count(current) > 0) {
609 close_polygon(current, sx, sy, ox, oy, matrix);
610 array_append_data(array, &current, 1);
611 } else
612 polygon_destroy(current);
613 }
614
615 p->fill_polys.polygon_array.array = array;
616 p->fill_polys.matrix = *matrix;
617
618 polygon_array_calculate_bounds( &p->fill_polys.polygon_array );
619
620 p->dirty = VG_FALSE;
621
622 return &p->fill_polys.polygon_array;
623 }
624
625 VGbyte path_datatype_size(struct path *p)
626 {
627 return size_for_datatype(p->datatype);
628 }
629
630 VGPathDatatype path_datatype(struct path *p)
631 {
632 return p->datatype;
633 }
634
635 VGfloat path_scale(struct path *p)
636 {
637 return p->scale;
638 }
639
640 VGfloat path_bias(struct path *p)
641 {
642 return p->bias;
643 }
644
645 VGint path_num_coords(struct path *p)
646 {
647 return num_elements_for_segments((VGubyte*)p->segments->data,
648 p->num_segments);
649 }
650
651 void path_modify_coords(struct path *p,
652 VGint startIndex,
653 VGint numSegments,
654 const void * pathData)
655 {
656 VGubyte *segments = (VGubyte*)(p->segments->data);
657 VGint count = num_elements_for_segments(&segments[startIndex], numSegments);
658 VGint start_cp = num_elements_for_segments(segments, startIndex);
659
660 array_change_data(p->control_points, pathData, start_cp, count);
661 coords_adjust_by_scale_bias(p,
662 ((VGubyte*)p->control_points->data) +
663 (startIndex * p->control_points->datatype_size),
664 path_num_coords(p),
665 p->scale, p->bias, p->datatype);
666 p->dirty = VG_TRUE;
667 p->dirty_stroke = VG_TRUE;
668 }
669
670 void path_for_each_segment(struct path *path,
671 path_for_each_cb cb,
672 void *user_data)
673 {
674 VGint i;
675 struct path_for_each_data p;
676 VGfloat data[8];
677 void *coords = (VGfloat *)path->control_points->data;
678
679 p.coords = data;
680 p.sx = p.sy = p.px = p.py = p.ox = p.oy = 0.f;
681 p.user_data = user_data;
682
683 for (i = 0; i < path->num_segments; ++i) {
684 VGint command;
685 VGboolean relative;
686
687 p.segment = ((VGubyte*)(path->segments->data))[i];
688 command = SEGMENT_COMMAND(p.segment);
689 relative = SEGMENT_ABS_REL(p.segment);
690
691 switch(command) {
692 case VG_CLOSE_PATH:
693 cb(path, &p);
694 break;
695 case VG_MOVE_TO:
696 data_at(&coords, path, 0, 2, data);
697 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
698 cb(path, &p);
699 p.sx = data[0];
700 p.sy = data[1];
701 p.ox = data[0];
702 p.oy = data[1];
703 p.px = data[0];
704 p.py = data[1];
705 break;
706 case VG_LINE_TO:
707 data_at(&coords, path, 0, 2, data);
708 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
709 cb(path, &p);
710 p.ox = data[0];
711 p.oy = data[1];
712 p.px = data[0];
713 p.py = data[1];
714 break;
715 case VG_HLINE_TO:
716 data_at(&coords, path, 0, 1, data);
717 map_if_relative(p.ox, p.oy, relative, &data[0], 0);
718 p.segment = VG_LINE_TO;
719 data[1] = p.oy;
720 cb(path, &p);
721 p.ox = data[0];
722 p.oy = data[1];
723 p.px = data[0];
724 p.py = data[1];
725 break;
726 case VG_VLINE_TO:
727 data_at(&coords, path, 0, 1, data);
728 map_if_relative(p.ox, p.oy, relative, 0, &data[0]);
729 p.segment = VG_LINE_TO;
730 data[1] = data[0];
731 data[0] = p.ox;
732 cb(path, &p);
733 p.ox = data[0];
734 p.oy = data[1];
735 p.px = data[0];
736 p.py = data[1];
737 break;
738 case VG_CUBIC_TO: {
739 data_at(&coords, path, 0, 6, data);
740 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
741 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]);
742 map_if_relative(p.ox, p.oy, relative, &data[4], &data[5]);
743 cb(path, &p);
744 p.px = data[2];
745 p.py = data[3];
746 p.ox = data[4];
747 p.oy = data[5];
748 }
749 break;
750 case VG_QUAD_TO: {
751 data_at(&coords, path, 0, 4, data);
752 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
753 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]);
754 cb(path, &p);
755 p.px = data[0];
756 p.py = data[1];
757 p.ox = data[2];
758 p.oy = data[3];
759 }
760 break;
761 case VG_SQUAD_TO: {
762 data_at(&coords, path, 0, 2, data);
763 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
764 cb(path, &p);
765 p.px = 2*p.ox-p.px;
766 p.py = 2*p.oy-p.py;
767 p.ox = data[2];
768 p.oy = data[3];
769 }
770 break;
771 case VG_SCUBIC_TO: {
772 data_at(&coords, path, 0, 4, data);
773 map_if_relative(p.ox, p.oy, relative, &data[0], &data[1]);
774 map_if_relative(p.ox, p.oy, relative, &data[2], &data[3]);
775 cb(path, &p);
776 p.px = data[0];
777 p.py = data[1];
778 p.ox = data[2];
779 p.oy = data[3];
780 }
781 break;
782 case VG_SCCWARC_TO:
783 case VG_SCWARC_TO:
784 case VG_LCCWARC_TO:
785 case VG_LCWARC_TO: {
786 data_at(&coords, path, 0, 5, data);
787 map_if_relative(p.ox, p.oy, relative, &data[3], &data[4]);
788 #if 0
789 debug_printf("------- ARC (%f, %f), (%f, %f) %f, %f, %f\n",
790 p.ox, p.oy, data[3], data[4], data[0], data[1], data[2]);
791 #endif
792 cb(path, &p);
793 p.ox = data[3];
794 p.oy = data[4];
795 p.px = data[3];
796 p.py = data[4];
797 }
798 break;
799 default:
800 abort();
801 assert(!"Unknown segment!");
802 }
803 }
804 }
805
806 struct transform_data {
807 struct array *segments;
808 struct array *coords;
809
810 struct matrix *matrix;
811
812 VGPathDatatype datatype;
813 };
814
815 static VGboolean transform_cb(struct path *p,
816 struct path_for_each_data *pd)
817 {
818 struct transform_data *td = (struct transform_data *)pd->user_data;
819 VGint num_coords = num_elements_for_segments(&pd->segment, 1);
820 VGubyte segment = SEGMENT_COMMAND(pd->segment);/* abs bit is 0 */
821 VGfloat data[8];
822 VGubyte common_data[sizeof(VGfloat)*8];
823
824 memcpy(data, pd->coords, sizeof(VGfloat) * num_coords);
825
826 switch(segment) {
827 case VG_CLOSE_PATH:
828 break;
829 case VG_MOVE_TO:
830 matrix_map_point(td->matrix,
831 data[0], data[1], &data[0], &data[1]);
832 break;
833 case VG_LINE_TO:
834 matrix_map_point(td->matrix,
835 data[0], data[1], &data[0], &data[1]);
836 break;
837 case VG_HLINE_TO:
838 case VG_VLINE_TO:
839 assert(0);
840 break;
841 case VG_QUAD_TO:
842 matrix_map_point(td->matrix,
843 data[0], data[1], &data[0], &data[1]);
844 matrix_map_point(td->matrix,
845 data[2], data[3], &data[2], &data[3]);
846 break;
847 case VG_CUBIC_TO:
848 matrix_map_point(td->matrix,
849 data[0], data[1], &data[0], &data[1]);
850 matrix_map_point(td->matrix,
851 data[2], data[3], &data[2], &data[3]);
852 matrix_map_point(td->matrix,
853 data[4], data[5], &data[4], &data[5]);
854 break;
855 case VG_SQUAD_TO:
856 matrix_map_point(td->matrix,
857 data[0], data[1], &data[0], &data[1]);
858 break;
859 case VG_SCUBIC_TO:
860 matrix_map_point(td->matrix,
861 data[0], data[1], &data[0], &data[1]);
862 matrix_map_point(td->matrix,
863 data[2], data[3], &data[2], &data[3]);
864 break;
865 case VG_SCCWARC_TO:
866 case VG_SCWARC_TO:
867 case VG_LCCWARC_TO:
868 case VG_LCWARC_TO: {
869 struct arc arc;
870 struct path *path = path_create(td->datatype,
871 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL);
872 arc_init(&arc, segment,
873 pd->ox, pd->oy, data[3], data[4],
874 data[0], data[1], data[2]);
875
876 arc_to_path(&arc, path, td->matrix);
877
878 num_coords = path_num_coords(path);
879
880 array_append_data(td->segments, path->segments->data,
881 path->num_segments);
882 array_append_data(td->coords, path->control_points->data,
883 num_coords);
884 path_destroy(path);
885
886 return VG_TRUE;
887 }
888 break;
889 default:
890 break;
891 }
892
893 vg_float_to_datatype(td->datatype, common_data, data, num_coords);
894
895 array_append_data(td->segments, &pd->segment, 1);
896 array_append_data(td->coords, common_data, num_coords);
897 return VG_TRUE;
898 }
899
900 void path_transform(struct path *dst, struct path *src)
901 {
902 struct transform_data data;
903 struct vg_context *ctx = dst->base.ctx;
904
905 data.segments = dst->segments;
906 data.coords = dst->control_points;
907 data.matrix = &ctx->state.vg.path_user_to_surface_matrix;
908 data.datatype = dst->datatype;
909
910 path_for_each_segment(src, transform_cb, (void*)&data);
911
912 dst->num_segments = dst->segments->num_elements;
913 dst->dirty = VG_TRUE;
914 dst->dirty_stroke = VG_TRUE;
915 }
916
917 void path_append_path(struct path *dst,
918 struct path *src)
919 {
920 VGint num_coords = path_num_coords(src);
921 void *dst_data = malloc(size_for_datatype(dst->datatype) * num_coords);
922 array_append_data(dst->segments,
923 src->segments->data,
924 src->num_segments);
925 convert_path(src, dst->datatype,
926 dst_data, num_coords);
927 array_append_data(dst->control_points,
928 dst_data,
929 num_coords);
930 free(dst_data);
931
932 dst->num_segments += src->num_segments;
933 dst->dirty = VG_TRUE;
934 dst->dirty_stroke = VG_TRUE;
935 }
936
937 static INLINE VGboolean is_segment_arc(VGubyte segment)
938 {
939 VGubyte scommand = SEGMENT_COMMAND(segment);
940 return (scommand == VG_SCCWARC_TO ||
941 scommand == VG_SCWARC_TO ||
942 scommand == VG_LCCWARC_TO ||
943 scommand == VG_LCWARC_TO);
944 }
945
946 struct path_iter_data {
947 struct path *path;
948 VGubyte segment;
949 void *coords;
950 VGfloat px, py, ox, oy, sx, sy;
951 };
952 static INLINE VGubyte normalize_coords(struct path_iter_data *pd,
953 VGint *num_coords,
954 VGfloat *data)
955 {
956 VGint command = SEGMENT_COMMAND(pd->segment);
957 VGboolean relative = SEGMENT_ABS_REL(pd->segment);
958
959 switch(command) {
960 case VG_CLOSE_PATH:
961 *num_coords = 0;
962 pd->ox = pd->sx;
963 pd->oy = pd->sy;
964 return VG_CLOSE_PATH;
965 break;
966 case VG_MOVE_TO:
967 data_at(&pd->coords, pd->path, 0, 2, data);
968 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]);
969 pd->sx = data[0];
970 pd->sy = data[1];
971 pd->ox = data[0];
972 pd->oy = data[1];
973 pd->px = data[0];
974 pd->py = data[1];
975 *num_coords = 2;
976 return VG_MOVE_TO_ABS;
977 break;
978 case VG_LINE_TO:
979 data_at(&pd->coords, pd->path, 0, 2, data);
980 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]);
981 pd->ox = data[0];
982 pd->oy = data[1];
983 pd->px = data[0];
984 pd->py = data[1];
985 *num_coords = 2;
986 return VG_LINE_TO_ABS;
987 break;
988 case VG_HLINE_TO:
989 data_at(&pd->coords, pd->path, 0, 1, data);
990 map_if_relative(pd->ox, pd->oy, relative, &data[0], 0);
991 data[1] = pd->oy;
992 pd->ox = data[0];
993 pd->oy = data[1];
994 pd->px = data[0];
995 pd->py = data[1];
996 *num_coords = 2;
997 return VG_LINE_TO_ABS;
998 break;
999 case VG_VLINE_TO:
1000 data_at(&pd->coords, pd->path, 0, 1, data);
1001 map_if_relative(pd->ox, pd->oy, relative, 0, &data[0]);
1002 data[1] = data[0];
1003 data[0] = pd->ox;
1004 pd->ox = data[0];
1005 pd->oy = data[1];
1006 pd->px = data[0];
1007 pd->py = data[1];
1008 *num_coords = 2;
1009 return VG_LINE_TO_ABS;
1010 break;
1011 case VG_CUBIC_TO: {
1012 data_at(&pd->coords, pd->path, 0, 6, data);
1013 map_if_relative(pd->ox, pd->oy, relative, &data[0], &data[1]);
1014 map_if_relative(pd->ox, pd->oy, relative, &data[2], &data[3]);
1015 map_if_relative(pd->ox, pd->oy, relative, &data[4], &data[5]);
1016 pd->px = data[2];
1017 pd->py = data[3];
1018 pd->ox = data[4];
1019 pd->oy = data[5];
1020 *num_coords = 6;
1021 return VG_CUBIC_TO_ABS;
1022 }
1023 break;
1024 case VG_QUAD_TO: {
1025 VGfloat x0, y0, x1, y1, x2, y2, x3, y3;
1026 data_at(&pd->coords, pd->path, 0, 4, data);
1027 x0 = pd->ox;
1028 y0 = pd->oy;
1029 x1 = data[0];
1030 y1 = data[1];
1031 x3 = data[2];
1032 y3 = data[3];
1033 map_if_relative(pd->ox, pd->oy, relative, &x1, &y1);
1034 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3);
1035 pd->px = x1;
1036 pd->py = y1;
1037 { /* form a cubic out of it */
1038 x2 = (x3 + 2*x1) / 3.f;
1039 y2 = (y3 + 2*y1) / 3.f;
1040 x1 = (x0 + 2*x1) / 3.f;
1041 y1 = (y0 + 2*y1) / 3.f;
1042 }
1043 pd->ox = x3;
1044 pd->oy = y3;
1045 data[0] = x1;
1046 data[1] = y1;
1047 data[2] = x2;
1048 data[3] = y2;
1049 data[4] = x3;
1050 data[5] = y3;
1051 *num_coords = 6;
1052 return VG_CUBIC_TO_ABS;
1053 }
1054 break;
1055 case VG_SQUAD_TO: {
1056 VGfloat x0, y0, x1, y1, x2, y2, x3, y3;
1057 data_at(&pd->coords, pd->path, 0, 2, data);
1058 x0 = pd->ox;
1059 y0 = pd->oy;
1060 x1 = 2 * pd->ox - pd->px;
1061 y1 = 2 * pd->oy - pd->py;
1062 x3 = data[0];
1063 y3 = data[1];
1064 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3);
1065 pd->px = x1;
1066 pd->py = y1;
1067 { /* form a cubic out of it */
1068 x2 = (x3 + 2*x1) / 3.f;
1069 y2 = (y3 + 2*y1) / 3.f;
1070 x1 = (x0 + 2*x1) / 3.f;
1071 y1 = (y0 + 2*y1) / 3.f;
1072 }
1073 pd->ox = x3;
1074 pd->oy = y3;
1075 data[0] = x1;
1076 data[1] = y1;
1077 data[2] = x2;
1078 data[3] = y2;
1079 data[4] = x3;
1080 data[5] = y3;
1081 *num_coords = 6;
1082 return VG_CUBIC_TO_ABS;
1083 }
1084 break;
1085 case VG_SCUBIC_TO: {
1086 VGfloat x0, y0, x1, y1, x2, y2, x3, y3;
1087 data_at(&pd->coords, pd->path, 0, 4, data);
1088 x0 = pd->ox;
1089 y0 = pd->oy;
1090 x1 = 2*pd->ox-pd->px;
1091 y1 = 2*pd->oy-pd->py;
1092 x2 = data[0];
1093 y2 = data[1];
1094 x3 = data[2];
1095 y3 = data[3];
1096 map_if_relative(pd->ox, pd->oy, relative, &x2, &y2);
1097 map_if_relative(pd->ox, pd->oy, relative, &x3, &y3);
1098 pd->ox = x3;
1099 pd->oy = y3;
1100 pd->px = x2;
1101 pd->py = y2;
1102 data[0] = x1;
1103 data[1] = y1;
1104 data[2] = x2;
1105 data[3] = y2;
1106 data[4] = x3;
1107 data[5] = y3;
1108 *num_coords = 6;
1109 return VG_CUBIC_TO_ABS;
1110 }
1111 break;
1112 case VG_SCCWARC_TO:
1113 case VG_SCWARC_TO:
1114 case VG_LCCWARC_TO:
1115 case VG_LCWARC_TO: {
1116 data_at(&pd->coords, pd->path, 0, 5, data);
1117 map_if_relative(pd->ox, pd->oy, relative, &data[3], &data[4]);
1118 pd->ox = data[3];
1119 pd->oy = data[4];
1120 pd->px = data[3];
1121 pd->py = data[4];
1122 *num_coords = 5;
1123 return command | VG_ABSOLUTE;
1124 }
1125 break;
1126 default:
1127 abort();
1128 assert(!"Unknown segment!");
1129 }
1130 }
1131
1132 static void linearly_interpolate(VGfloat *result,
1133 const VGfloat *start,
1134 const VGfloat *end,
1135 VGfloat amount,
1136 VGint number)
1137 {
1138 VGint i;
1139 for (i = 0; i < number; ++i) {
1140 result[i] = start[i] + (end[i] - start[i]) * amount;
1141 }
1142 }
1143
1144 VGboolean path_interpolate(struct path *dst,
1145 struct path *start, struct path *end,
1146 VGfloat amount)
1147 {
1148 /* temporary path that we can discard if it will turn
1149 * out that start is not compatible with end */
1150 struct path *res_path = path_create(dst->datatype,
1151 1.0, 0.0,
1152 0, 0, dst->caps);
1153 VGint i;
1154 VGfloat start_coords[8];
1155 VGfloat end_coords[8];
1156 VGfloat results[8];
1157 VGubyte common_data[sizeof(VGfloat)*8];
1158 struct path_iter_data start_iter, end_iter;
1159
1160 memset(&start_iter, 0, sizeof(struct path_iter_data));
1161 memset(&end_iter, 0, sizeof(struct path_iter_data));
1162
1163 start_iter.path = start;
1164 start_iter.coords = start->control_points->data;
1165 end_iter.path = end;
1166 end_iter.coords = end->control_points->data;
1167
1168 for (i = 0; i < start->num_segments; ++i) {
1169 VGubyte segment;
1170 VGubyte ssegment, esegment;
1171 VGint snum_coords, enum_coords;
1172 start_iter.segment = ((VGubyte*)(start->segments->data))[i];
1173 end_iter.segment = ((VGubyte*)(end->segments->data))[i];
1174
1175 ssegment = normalize_coords(&start_iter, &snum_coords,
1176 start_coords);
1177 esegment = normalize_coords(&end_iter, &enum_coords,
1178 end_coords);
1179
1180 if (is_segment_arc(ssegment)) {
1181 if (!is_segment_arc(esegment)) {
1182 path_destroy(res_path);
1183 return VG_FALSE;
1184 }
1185 if (amount > 0.5)
1186 segment = esegment;
1187 else
1188 segment = ssegment;
1189 } else if (is_segment_arc(esegment)) {
1190 path_destroy(res_path);
1191 return VG_FALSE;
1192 }
1193 else if (ssegment != esegment) {
1194 path_destroy(res_path);
1195 return VG_FALSE;
1196 }
1197 else
1198 segment = ssegment;
1199
1200 linearly_interpolate(results, start_coords, end_coords,
1201 amount, snum_coords);
1202 vg_float_to_datatype(dst->datatype, common_data, results, snum_coords);
1203 path_append_data(res_path, 1, &segment, common_data);
1204 }
1205
1206 path_append_path(dst, res_path);
1207 path_destroy(res_path);
1208
1209 dst->dirty = VG_TRUE;
1210 dst->dirty_stroke = VG_TRUE;
1211
1212 return VG_TRUE;
1213 }
1214
1215 void path_clear(struct path *p, VGbitfield capabilities)
1216 {
1217 path_set_capabilities(p, capabilities);
1218 array_destroy(p->segments);
1219 array_destroy(p->control_points);
1220 p->segments = array_create(size_for_datatype(VG_PATH_DATATYPE_S_8));
1221 p->control_points = array_create(size_for_datatype(p->datatype));
1222 p->num_segments = 0;
1223 p->dirty = VG_TRUE;
1224 p->dirty_stroke = VG_TRUE;
1225 }
1226
1227 struct path * path_create_stroke(struct path *p,
1228 struct matrix *matrix)
1229 {
1230 VGint i;
1231 VGfloat sx, sy, px, py, ox, oy;
1232 VGfloat x0, y0, x1, y1, x2, y2, x3, y3;
1233 VGfloat data[8];
1234 void *coords = (VGfloat *)p->control_points->data;
1235 int dashed = (p->base.ctx->state.vg.stroke.dash_pattern_num ? 1 : 0);
1236 struct dash_stroker stroker;
1237 struct vg_state *vg_state = &p->base.ctx->state.vg;
1238
1239 if (p->stroked.path)
1240 {
1241 /* ### compare the dash patterns to see if we can cache them.
1242 * for now we simply always bail out if the path is dashed.
1243 */
1244 if (memcmp( &p->stroked.matrix,
1245 matrix,
1246 sizeof *matrix ) == 0 &&
1247 !dashed && !p->dirty_stroke &&
1248 floatsEqual(p->stroked.stroke_width, vg_state->stroke.line_width.f) &&
1249 floatsEqual(p->stroked.miter_limit, vg_state->stroke.miter_limit.f) &&
1250 p->stroked.cap_style == vg_state->stroke.cap_style &&
1251 p->stroked.join_style == vg_state->stroke.join_style)
1252 {
1253 return p->stroked.path;
1254 }
1255 else {
1256 path_destroy( p->stroked.path );
1257 p->stroked.path = NULL;
1258 }
1259 }
1260
1261
1262 sx = sy = px = py = ox = oy = 0.f;
1263
1264 if (dashed)
1265 dash_stroker_init((struct stroker *)&stroker, vg_state);
1266 else
1267 stroker_init((struct stroker *)&stroker, vg_state);
1268
1269 stroker_begin((struct stroker *)&stroker);
1270
1271 for (i = 0; i < p->num_segments; ++i) {
1272 VGubyte segment = ((VGubyte*)(p->segments->data))[i];
1273 VGint command = SEGMENT_COMMAND(segment);
1274 VGboolean relative = SEGMENT_ABS_REL(segment);
1275
1276 switch(command) {
1277 case VG_CLOSE_PATH: {
1278 VGfloat x0 = sx;
1279 VGfloat y0 = sy;
1280 matrix_map_point(matrix, x0, y0, &x0, &y0);
1281 stroker_line_to((struct stroker *)&stroker, x0, y0);
1282 }
1283 break;
1284 case VG_MOVE_TO:
1285 data_at(&coords, p, 0, 2, data);
1286 x0 = data[0];
1287 y0 = data[1];
1288 map_if_relative(ox, oy, relative, &x0, &y0);
1289 sx = x0;
1290 sy = y0;
1291 ox = x0;
1292 oy = y0;
1293 px = x0;
1294 py = y0;
1295 matrix_map_point(matrix, x0, y0, &x0, &y0);
1296 stroker_move_to((struct stroker *)&stroker, x0, y0);
1297 break;
1298 case VG_LINE_TO:
1299 data_at(&coords, p, 0, 2, data);
1300 x0 = data[0];
1301 y0 = data[1];
1302 map_if_relative(ox, oy, relative, &x0, &y0);
1303 ox = x0;
1304 oy = y0;
1305 px = x0;
1306 py = y0;
1307 matrix_map_point(matrix, x0, y0, &x0, &y0);
1308 stroker_line_to((struct stroker *)&stroker, x0, y0);
1309 break;
1310 case VG_HLINE_TO:
1311 data_at(&coords, p, 0, 1, data);
1312 x0 = data[0];
1313 y0 = oy;
1314 map_if_relative(ox, oy, relative, &x0, 0);
1315 ox = x0;
1316 px = x0;
1317 py = y0;
1318 matrix_map_point(matrix, x0, y0, &x0, &y0);
1319 stroker_line_to((struct stroker *)&stroker, x0, y0);
1320 break;
1321 case VG_VLINE_TO:
1322 data_at(&coords, p, 0, 1, data);
1323 x0 = ox;
1324 y0 = data[0];
1325 map_if_relative(ox, oy, relative, 0, &y0);
1326 oy = y0;
1327 px = x0;
1328 py = y0;
1329 matrix_map_point(matrix, x0, y0, &x0, &y0);
1330 stroker_line_to((struct stroker *)&stroker, x0, y0);
1331 break;
1332 case VG_CUBIC_TO: {
1333 data_at(&coords, p, 0, 6, data);
1334 x0 = ox;
1335 y0 = oy;
1336 x1 = data[0];
1337 y1 = data[1];
1338 x2 = data[2];
1339 y2 = data[3];
1340 x3 = data[4];
1341 y3 = data[5];
1342 map_if_relative(ox, oy, relative, &x1, &y1);
1343 map_if_relative(ox, oy, relative, &x2, &y2);
1344 map_if_relative(ox, oy, relative, &x3, &y3);
1345 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) &&
1346 floatsEqual(x1, x2) && floatsEqual(y1, y2) &&
1347 floatsEqual(x2, x3) && floatsEqual(y2, y3)) {
1348 /*ignore the empty segment */
1349 continue;
1350 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) {
1351 /* if dup vertex, emit a line */
1352 ox = x3;
1353 oy = y3;
1354 matrix_map_point(matrix, x3, y3, &x3, &y3);
1355 stroker_line_to((struct stroker *)&stroker, x3, y3);
1356 continue;
1357 }
1358 ox = x3;
1359 oy = y3;
1360 px = x2;
1361 py = y2;
1362 assert(matrix_is_affine(matrix));
1363 matrix_map_point(matrix, x0, y0, &x0, &y0);
1364 matrix_map_point(matrix, x1, y1, &x1, &y1);
1365 matrix_map_point(matrix, x2, y2, &x2, &y2);
1366 matrix_map_point(matrix, x3, y3, &x3, &y3);
1367 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3);
1368 }
1369 break;
1370 case VG_QUAD_TO: {
1371 data_at(&coords, p, 0, 4, data);
1372 x0 = ox;
1373 y0 = oy;
1374 x1 = data[0];
1375 y1 = data[1];
1376 x3 = data[2];
1377 y3 = data[3];
1378 map_if_relative(ox, oy, relative, &x1, &y1);
1379 map_if_relative(ox, oy, relative, &x3, &y3);
1380 px = x1;
1381 py = y1;
1382 { /* form a cubic out of it */
1383 x2 = (x3 + 2*x1) / 3.f;
1384 y2 = (y3 + 2*y1) / 3.f;
1385 x1 = (x0 + 2*x1) / 3.f;
1386 y1 = (y0 + 2*y1) / 3.f;
1387 }
1388 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) &&
1389 floatsEqual(x1, x2) && floatsEqual(y1, y2) &&
1390 floatsEqual(x2, x3) && floatsEqual(y2, y3)) {
1391 /*ignore the empty segment */
1392 continue;
1393 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) {
1394 /* if dup vertex, emit a line */
1395 ox = x3;
1396 oy = y3;
1397 matrix_map_point(matrix, x3, y3, &x3, &y3);
1398 stroker_line_to((struct stroker *)&stroker, x3, y3);
1399 continue;
1400 }
1401 ox = x3;
1402 oy = y3;
1403 assert(matrix_is_affine(matrix));
1404 matrix_map_point(matrix, x0, y0, &x0, &y0);
1405 matrix_map_point(matrix, x1, y1, &x1, &y1);
1406 matrix_map_point(matrix, x2, y2, &x2, &y2);
1407 matrix_map_point(matrix, x3, y3, &x3, &y3);
1408 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3);
1409 }
1410 break;
1411 case VG_SQUAD_TO: {
1412 data_at(&coords, p, 0, 2, data);
1413 x0 = ox;
1414 y0 = oy;
1415 x1 = 2*ox-px;
1416 y1 = 2*oy-py;
1417 x3 = data[0];
1418 y3 = data[1];
1419 map_if_relative(ox, oy, relative, &x3, &y3);
1420 px = x1;
1421 py = y1;
1422 { /* form a cubic out of it */
1423 x2 = (x3 + 2*x1) / 3.f;
1424 y2 = (y3 + 2*y1) / 3.f;
1425 x1 = (x0 + 2*x1) / 3.f;
1426 y1 = (y0 + 2*y1) / 3.f;
1427 }
1428 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) &&
1429 floatsEqual(x1, x2) && floatsEqual(y1, y2) &&
1430 floatsEqual(x2, x3) && floatsEqual(y2, y3)) {
1431 /*ignore the empty segment */
1432 continue;
1433 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) {
1434 /* if dup vertex, emit a line */
1435 ox = x3;
1436 oy = y3;
1437 matrix_map_point(matrix, x3, y3, &x3, &y3);
1438 stroker_line_to((struct stroker *)&stroker, x3, y3);
1439 continue;
1440 }
1441 ox = x3;
1442 oy = y3;
1443 assert(matrix_is_affine(matrix));
1444 matrix_map_point(matrix, x0, y0, &x0, &y0);
1445 matrix_map_point(matrix, x1, y1, &x1, &y1);
1446 matrix_map_point(matrix, x2, y2, &x2, &y2);
1447 matrix_map_point(matrix, x3, y3, &x3, &y3);
1448 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3);
1449 }
1450 break;
1451 case VG_SCUBIC_TO: {
1452 data_at(&coords, p, 0, 4, data);
1453 x0 = ox;
1454 y0 = oy;
1455 x1 = 2*ox-px;
1456 y1 = 2*oy-py;
1457 x2 = data[0];
1458 y2 = data[1];
1459 x3 = data[2];
1460 y3 = data[3];
1461 map_if_relative(ox, oy, relative, &x2, &y2);
1462 map_if_relative(ox, oy, relative, &x3, &y3);
1463 if (floatsEqual(x1, ox) && floatsEqual(y1, oy) &&
1464 floatsEqual(x1, x2) && floatsEqual(y1, y2) &&
1465 floatsEqual(x2, x3) && floatsEqual(y2, y3)) {
1466 /*ignore the empty segment */
1467 continue;
1468 } else if (floatsEqual(x3, ox) && floatsEqual(y3, oy)) {
1469 /* if dup vertex, emit a line */
1470 ox = x3;
1471 oy = y3;
1472 matrix_map_point(matrix, x3, y3, &x3, &y3);
1473 stroker_line_to((struct stroker *)&stroker, x3, y3);
1474 continue;
1475 }
1476 ox = x3;
1477 oy = y3;
1478 px = x2;
1479 py = y2;
1480 assert(matrix_is_affine(matrix));
1481 matrix_map_point(matrix, x0, y0, &x0, &y0);
1482 matrix_map_point(matrix, x1, y1, &x1, &y1);
1483 matrix_map_point(matrix, x2, y2, &x2, &y2);
1484 matrix_map_point(matrix, x3, y3, &x3, &y3);
1485 stroker_curve_to((struct stroker *)&stroker, x1, y1, x2, y2, x3, y3);
1486 }
1487 break;
1488 case VG_SCCWARC_TO:
1489 case VG_SCWARC_TO:
1490 case VG_LCCWARC_TO:
1491 case VG_LCWARC_TO: {
1492 VGfloat rh, rv, rot;
1493 struct arc arc;
1494
1495 data_at(&coords, p, 0, 5, data);
1496 x0 = ox;
1497 y0 = oy;
1498 rh = data[0];
1499 rv = data[1];
1500 rot = data[2];
1501 x1 = data[3];
1502 y1 = data[4];
1503 map_if_relative(ox, oy, relative, &x1, &y1);
1504 if (floatsEqual(x1, ox) && floatsEqual(y1, oy)) {
1505 /* if dup vertex, emit a line */
1506 ox = x1;
1507 oy = y1;
1508 matrix_map_point(matrix, x1, y1, &x1, &y1);
1509 stroker_line_to((struct stroker *)&stroker, x1, y1);
1510 continue;
1511 }
1512 arc_init(&arc, command, x0, y0, x1, y1,
1513 rh, rv, rot);
1514 arc_stroke_cb(&arc, (struct stroker *)&stroker,
1515 matrix);
1516 ox = x1;
1517 oy = y1;
1518 px = x1;
1519 py = y1;
1520 }
1521 break;
1522 default:
1523 abort();
1524 assert(!"Unknown segment!");
1525 }
1526 }
1527
1528 stroker_end((struct stroker *)&stroker);
1529
1530 if (dashed)
1531 dash_stroker_cleanup((struct dash_stroker *)&stroker);
1532 else
1533 stroker_cleanup((struct stroker *)&stroker);
1534
1535 p->stroked.path = stroker.base.path;
1536 p->stroked.matrix = *matrix;
1537 p->dirty_stroke = VG_FALSE;
1538 p->stroked.stroke_width = vg_state->stroke.line_width.f;
1539 p->stroked.miter_limit = vg_state->stroke.miter_limit.f;
1540 p->stroked.cap_style = vg_state->stroke.cap_style;
1541 p->stroked.join_style = vg_state->stroke.join_style;
1542
1543 return stroker.base.path;
1544 }
1545
1546 void path_render(struct path *p, VGbitfield paintModes,
1547 struct matrix *mat)
1548 {
1549 struct vg_context *ctx = vg_current_context();
1550 struct matrix paint_matrix;
1551
1552 vg_validate_state(ctx);
1553
1554 shader_set_drawing_image(ctx->shader, VG_FALSE);
1555 shader_set_image(ctx->shader, 0);
1556 #if 0
1557 fprintf(stderr, "Matrix(11=%f 12=%f 13=%f 21=%f 22=%f 23=%f 31=%f 32=%f 33=%f)\n",
1558 mat->m[0], mat->m[1], mat->m[2],
1559 mat->m[3], mat->m[4], mat->m[5],
1560 mat->m[6], mat->m[7], mat->m[8]);
1561 #endif
1562 if ((paintModes & VG_FILL_PATH) &&
1563 vg_get_paint_matrix(ctx,
1564 &ctx->state.vg.fill_paint_to_user_matrix,
1565 mat,
1566 &paint_matrix)) {
1567 /* First the fill */
1568 shader_set_surface_matrix(ctx->shader, mat);
1569 shader_set_paint(ctx->shader, ctx->state.vg.fill_paint);
1570 shader_set_paint_matrix(ctx->shader, &paint_matrix);
1571 shader_bind(ctx->shader);
1572 path_fill(p);
1573 }
1574
1575 if ((paintModes & VG_STROKE_PATH) &&
1576 vg_get_paint_matrix(ctx,
1577 &ctx->state.vg.stroke_paint_to_user_matrix,
1578 mat,
1579 &paint_matrix)) {
1580 /* 8.7.5: "line width less than or equal to 0 prevents stroking from
1581 * taking place."*/
1582 if (ctx->state.vg.stroke.line_width.f <= 0)
1583 return;
1584 shader_set_surface_matrix(ctx->shader, mat);
1585 shader_set_paint(ctx->shader, ctx->state.vg.stroke_paint);
1586 shader_set_paint_matrix(ctx->shader, &paint_matrix);
1587 shader_bind(ctx->shader);
1588 path_stroke(p);
1589 }
1590 }
1591
1592 void path_fill(struct path *p)
1593 {
1594 struct vg_context *ctx = vg_current_context();
1595 struct matrix identity;
1596
1597 matrix_load_identity(&identity);
1598
1599 {
1600 struct polygon_array *polygon_array = path_get_fill_polygons(p, &identity);
1601 struct array *polys = polygon_array->array;
1602
1603 if (!polygon_array || !polys || !polys->num_elements) {
1604 return;
1605 }
1606 polygon_array_fill(polygon_array, ctx);
1607 }
1608 }
1609
1610 void path_stroke(struct path *p)
1611 {
1612 struct vg_context *ctx = vg_current_context();
1613 VGFillRule old_fill = ctx->state.vg.fill_rule;
1614 struct matrix identity;
1615 struct path *stroke;
1616
1617 matrix_load_identity(&identity);
1618 stroke = path_create_stroke(p, &identity);
1619 if (stroke && !path_is_empty(stroke)) {
1620 ctx->state.vg.fill_rule = VG_NON_ZERO;
1621
1622 path_fill(stroke);
1623
1624 ctx->state.vg.fill_rule = old_fill;
1625 }
1626 }
1627
1628 void path_move_to(struct path *p, float x, float y)
1629 {
1630 VGubyte segment = VG_MOVE_TO_ABS;
1631 VGubyte common_data[sizeof(VGfloat) * 2];
1632 VGfloat data[2] = {x, y};
1633
1634 vg_float_to_datatype(p->datatype, common_data, data, 2);
1635 path_append_data(p, 1, &segment, common_data);
1636 }
1637
1638 void path_line_to(struct path *p, float x, float y)
1639 {
1640 VGubyte segment = VG_LINE_TO_ABS;
1641 VGubyte common_data[sizeof(VGfloat) * 2];
1642 VGfloat data[2] = {x, y};
1643
1644 vg_float_to_datatype(p->datatype, common_data, data, 2);
1645
1646 path_append_data(p, 1, &segment, common_data);
1647 }
1648
1649 void path_cubic_to(struct path *p, float px1, float py1,
1650 float px2, float py2,
1651 float x, float y)
1652 {
1653 VGubyte segment = VG_CUBIC_TO_ABS;
1654 VGubyte common_data[sizeof(VGfloat) * 6];
1655 VGfloat data[6];
1656
1657 data[0] = px1; data[1] = py1;
1658 data[2] = px2; data[3] = py2;
1659 data[4] = x; data[5] = y;
1660
1661 vg_float_to_datatype(p->datatype, common_data, data, 6);
1662
1663 path_append_data(p, 1, &segment, common_data);
1664 }
1665
1666 static INLINE void line_bounds(VGfloat *line /*x1,y1,x2,y2*/,
1667 VGfloat *bounds)
1668 {
1669 bounds[0] = MIN2(line[0], line[2]);
1670 bounds[1] = MIN2(line[1], line[3]);
1671 bounds[2] = MAX2(line[0], line[2]) - bounds[0];
1672 bounds[3] = MAX2(line[1], line[3]) - bounds[1];
1673 }
1674
1675 static INLINE void unite_bounds(VGfloat *bounds,
1676 VGfloat *el)
1677 {
1678 VGfloat cx1, cy1, cx2, cy2;
1679 VGfloat nx1, ny1, nx2, ny2;
1680
1681 cx1 = bounds[0];
1682 cy1 = bounds[1];
1683 cx2 = bounds[0] + bounds[2];
1684 cy2 = bounds[1] + bounds[3];
1685
1686 nx1 = el[0];
1687 ny1 = el[1];
1688 nx2 = el[0] + el[2];
1689 ny2 = el[1] + el[3];
1690
1691 bounds[0] = MIN2(cx1, nx1);
1692 bounds[1] = MIN2(cy1, ny1);
1693 bounds[2] = MAX2(cx2, nx2) - bounds[0];
1694 bounds[3] = MAX2(cy2, ny2) - bounds[1];
1695 }
1696
1697 static INLINE void set_bounds(VGfloat *bounds,
1698 VGfloat *element_bounds,
1699 VGboolean *initialized)
1700 {
1701 if (!(*initialized)) {
1702 memcpy(bounds, element_bounds, 4 * sizeof(VGfloat));
1703 *initialized = VG_TRUE;
1704 } else
1705 unite_bounds(bounds, element_bounds);
1706 }
1707
1708 void path_bounding_rect(struct path *p, float *x, float *y,
1709 float *w, float *h)
1710 {
1711 VGint i;
1712 VGfloat coords[8];
1713 struct path_iter_data iter;
1714 VGint num_coords;
1715 VGfloat bounds[4];
1716 VGfloat element_bounds[4];
1717 VGfloat ox, oy;
1718 VGboolean bounds_inited = VG_FALSE;
1719
1720 memset(&iter, 0, sizeof(struct path_iter_data));
1721 memset(&bounds, 0, sizeof(bounds));
1722
1723 if (!p->num_segments) {
1724 bounds[2] = -1;
1725 bounds[3] = -1;
1726 }
1727
1728
1729 iter.path = p;
1730 iter.coords = p->control_points->data;
1731
1732 for (i = 0; i < p->num_segments; ++i) {
1733 VGubyte segment;
1734 iter.segment = ((VGubyte*)(p->segments->data))[i];
1735
1736 ox = iter.ox;
1737 oy = iter.oy;
1738
1739 segment = normalize_coords(&iter, &num_coords, coords);
1740
1741 switch(segment) {
1742 case VG_CLOSE_PATH:
1743 case VG_MOVE_TO_ABS:
1744 break;
1745 case VG_LINE_TO_ABS: {
1746 VGfloat line[4] = {ox, oy, coords[0], coords[1]};
1747 line_bounds(line, element_bounds);
1748 set_bounds(bounds, element_bounds, &bounds_inited);
1749 }
1750 break;
1751 case VG_CUBIC_TO_ABS: {
1752 struct bezier bezier;
1753 bezier_init(&bezier, ox, oy,
1754 coords[0], coords[1],
1755 coords[2], coords[3],
1756 coords[4], coords[5]);
1757 bezier_exact_bounds(&bezier, element_bounds);
1758 set_bounds(bounds, element_bounds, &bounds_inited);
1759 }
1760 break;
1761 case VG_SCCWARC_TO:
1762 case VG_SCWARC_TO:
1763 case VG_LCCWARC_TO:
1764 case VG_LCWARC_TO: {
1765 struct arc arc;
1766 struct matrix identity;
1767 struct path *path = path_create(VG_PATH_DATATYPE_F,
1768 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL);
1769
1770 matrix_load_identity(&identity);
1771 arc_init(&arc, segment,
1772 ox, oy, coords[3], coords[4],
1773 coords[0], coords[1], coords[2]);
1774
1775 arc_to_path(&arc, path, &identity);
1776
1777 path_bounding_rect(path, element_bounds + 0, element_bounds + 1,
1778 element_bounds + 2, element_bounds + 3);
1779 set_bounds(bounds, element_bounds, &bounds_inited);
1780 }
1781 break;
1782 default:
1783 assert(0);
1784 }
1785 }
1786
1787 *x = bounds[0];
1788 *y = bounds[1];
1789 *w = bounds[2];
1790 *h = bounds[3];
1791 }
1792
1793 float path_length(struct path *p, int start_segment, int num_segments)
1794 {
1795 VGint i;
1796 VGfloat coords[8];
1797 struct path_iter_data iter;
1798 VGint num_coords;
1799 VGfloat length = 0;
1800 VGfloat ox, oy;
1801 VGboolean in_range = VG_FALSE;
1802
1803 memset(&iter, 0, sizeof(struct path_iter_data));
1804
1805 iter.path = p;
1806 iter.coords = p->control_points->data;
1807
1808 for (i = 0; i < (start_segment + num_segments); ++i) {
1809 VGubyte segment;
1810
1811 iter.segment = ((VGubyte*)(p->segments->data))[i];
1812
1813 ox = iter.ox;
1814 oy = iter.oy;
1815
1816 segment = normalize_coords(&iter, &num_coords, coords);
1817
1818 in_range = (i >= start_segment) && i <= (start_segment + num_segments);
1819 if (!in_range)
1820 continue;
1821
1822 switch(segment) {
1823 case VG_MOVE_TO_ABS:
1824 break;
1825 case VG_CLOSE_PATH: {
1826 VGfloat line[4] = {ox, oy, iter.sx, iter.sy};
1827 length += line_lengthv(line);
1828 }
1829 break;
1830 case VG_LINE_TO_ABS: {
1831 VGfloat line[4] = {ox, oy, coords[0], coords[1]};
1832 length += line_lengthv(line);
1833 }
1834 break;
1835 case VG_CUBIC_TO_ABS: {
1836 struct bezier bezier;
1837 bezier_init(&bezier, ox, oy,
1838 coords[0], coords[1],
1839 coords[2], coords[3],
1840 coords[4], coords[5]);
1841 length += bezier_length(&bezier, BEZIER_DEFAULT_ERROR);
1842 }
1843 break;
1844 case VG_SCCWARC_TO:
1845 case VG_SCWARC_TO:
1846 case VG_LCCWARC_TO:
1847 case VG_LCWARC_TO: {
1848 struct arc arc;
1849 struct matrix identity;
1850 struct path *path = path_create(VG_PATH_DATATYPE_F,
1851 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL);
1852
1853 matrix_load_identity(&identity);
1854 arc_init(&arc, segment,
1855 ox, oy, coords[3], coords[4],
1856 coords[0], coords[1], coords[2]);
1857
1858 arc_to_path(&arc, path, &identity);
1859
1860 length += path_length(path, 0, path_num_segments(path));
1861 }
1862 break;
1863 default:
1864 assert(0);
1865 }
1866 }
1867
1868 return length;
1869 }
1870
1871 static INLINE VGboolean point_on_current_segment(VGfloat distance,
1872 VGfloat length,
1873 VGfloat segment_length)
1874 {
1875 return
1876 (((floatIsZero(distance) || distance < 0) && floatIsZero(length)) ||
1877 ((distance > length || floatsEqual(distance, length)) &&
1878 (floatsEqual(distance, length + segment_length) ||
1879 distance < (length + segment_length))));
1880 }
1881
1882 static VGboolean path_point_segment(struct path_iter_data iter,
1883 struct path_iter_data prev_iter,
1884 VGfloat coords[8],
1885 VGfloat distance,
1886 VGfloat length, VGfloat *current_length,
1887 VGfloat *point, VGfloat *normal)
1888 {
1889 switch (iter.segment) {
1890 case VG_MOVE_TO_ABS:
1891 break;
1892 case VG_CLOSE_PATH: {
1893 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, iter.sx, iter.sy};
1894 VGboolean on_current_segment = VG_FALSE;
1895 *current_length = line_lengthv(line);
1896 on_current_segment = point_on_current_segment(distance,
1897 length,
1898 *current_length);
1899 if (on_current_segment) {
1900 VGfloat at = (distance - length) / line_lengthv(line);
1901 line_normal_vector(line, normal);
1902 line_point_at(line, at, point);
1903 return VG_TRUE;
1904 }
1905 }
1906 break;
1907 case VG_LINE_TO_ABS: {
1908 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, coords[0], coords[1]};
1909 VGboolean on_current_segment = VG_FALSE;
1910 *current_length = line_lengthv(line);
1911 on_current_segment = point_on_current_segment(distance,
1912 length,
1913 *current_length);
1914 if (on_current_segment) {
1915 VGfloat at = (distance - length) / line_lengthv(line);
1916 line_normal_vector(line, normal);
1917 line_point_at(line, at, point);
1918 return VG_TRUE;
1919 }
1920 }
1921 break;
1922 case VG_CUBIC_TO_ABS: {
1923 struct bezier bezier;
1924 bezier_init(&bezier, prev_iter.ox, prev_iter.oy,
1925 coords[0], coords[1],
1926 coords[2], coords[3],
1927 coords[4], coords[5]);
1928 *current_length = bezier_length(&bezier, BEZIER_DEFAULT_ERROR);
1929 if (point_on_current_segment(distance, length, *current_length)) {
1930 bezier_point_at_length(&bezier, distance - length,
1931 point, normal);
1932 return VG_TRUE;
1933 }
1934 }
1935 break;
1936 case VG_SCCWARC_TO:
1937 case VG_SCWARC_TO:
1938 case VG_LCCWARC_TO:
1939 case VG_LCWARC_TO: {
1940 struct arc arc;
1941 struct matrix identity;
1942 struct path *path = path_create(VG_PATH_DATATYPE_F,
1943 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL);
1944
1945 matrix_load_identity(&identity);
1946 arc_init(&arc, iter.segment,
1947 prev_iter.ox, prev_iter.oy, coords[3], coords[4],
1948 coords[0], coords[1], coords[2]);
1949
1950 arc_to_path(&arc, path, &identity);
1951
1952 *current_length = path_length(path, 0, path_num_segments(path));
1953 if (point_on_current_segment(distance, length, *current_length)) {
1954 path_point(path, 0, path_num_segments(path),
1955 distance - length, point, normal);
1956 return VG_TRUE;
1957 }
1958 }
1959 break;
1960 default:
1961 assert(0);
1962 }
1963 return VG_FALSE;
1964 }
1965
1966 void path_point(struct path *p, VGint start_segment, VGint num_segments,
1967 VGfloat distance, VGfloat *point, VGfloat *normal)
1968 {
1969 VGint i;
1970 VGfloat coords[8];
1971 struct path_iter_data iter, prev_iter;
1972 VGint num_coords;
1973 VGfloat length = 0;
1974 VGfloat current_length = 0;
1975
1976 memset(&iter, 0, sizeof(struct path_iter_data));
1977 memset(&prev_iter, 0, sizeof(struct path_iter_data));
1978
1979 point[0] = 0;
1980 point[1] = 0;
1981
1982 normal[0] = 0;
1983 normal[1] = -1;
1984
1985 iter.path = p;
1986 iter.coords = p->control_points->data;
1987 if (distance < 0)
1988 distance = 0;
1989
1990 for (i = 0; i < (start_segment + num_segments); ++i) {
1991 VGboolean outside_range = (i < start_segment ||
1992 i >= (start_segment + num_segments));
1993
1994 prev_iter = iter;
1995
1996 iter.segment = ((VGubyte*)(p->segments->data))[i];
1997 iter.segment = normalize_coords(&iter, &num_coords, coords);
1998
1999 if (outside_range)
2000 continue;
2001
2002 if (path_point_segment(iter, prev_iter, coords,
2003 distance, length, &current_length,
2004 point, normal))
2005 return;
2006
2007 length += current_length;
2008 }
2009
2010 /*
2011 *OpenVG 1.0 - 8.6.11 vgPointAlongPath
2012 *
2013 * If distance is greater than or equal to the path length
2014 *(i.e., the value returned by vgPathLength when called with the same
2015 *startSegment and numSegments parameters), the visual ending point of
2016 *the path is used.
2017 */
2018 {
2019 switch (iter.segment) {
2020 case VG_MOVE_TO_ABS:
2021 break;
2022 case VG_CLOSE_PATH: {
2023 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, iter.sx, iter.sy};
2024 line_normal_vector(line, normal);
2025 line_point_at(line, 1.f, point);
2026 }
2027 break;
2028 case VG_LINE_TO_ABS: {
2029 VGfloat line[4] = {prev_iter.ox, prev_iter.oy, coords[0], coords[1]};
2030 line_normal_vector(line, normal);
2031 line_point_at(line, 1.f, point);
2032 }
2033 break;
2034 case VG_CUBIC_TO_ABS: {
2035 struct bezier bezier;
2036 bezier_init(&bezier, prev_iter.ox, prev_iter.oy,
2037 coords[0], coords[1],
2038 coords[2], coords[3],
2039 coords[4], coords[5]);
2040 bezier_point_at_t(&bezier, 1.f, point, normal);
2041 }
2042 break;
2043 case VG_SCCWARC_TO:
2044 case VG_SCWARC_TO:
2045 case VG_LCCWARC_TO:
2046 case VG_LCWARC_TO: {
2047 struct arc arc;
2048 struct matrix identity;
2049 struct path *path = path_create(VG_PATH_DATATYPE_F,
2050 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL);
2051
2052 matrix_load_identity(&identity);
2053 arc_init(&arc, iter.segment,
2054 prev_iter.ox, prev_iter.oy, coords[3], coords[4],
2055 coords[0], coords[1], coords[2]);
2056
2057 arc_to_path(&arc, path, &identity);
2058
2059 path_point(path, 0, path_num_segments(path),
2060 /* to make sure we're bigger than len * 2 it */
2061 2 * path_length(path, 0, path_num_segments(path)),
2062 point, normal);
2063 }
2064 break;
2065 default:
2066 assert(0);
2067 }
2068 }
2069 }
2070
2071 VGboolean path_is_empty(struct path *p)
2072 {
2073 return p->segments->num_elements == 0;
2074 }