1 /**************************************************************************
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
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:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
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.
25 **************************************************************************/
31 #include "util_array.h"
35 #include "path_utils.h"
38 #include "util/u_math.h"
41 #define M_2PI 6.28318530717958647692528676655900576
44 #define STROKE_SEGMENTS 0
45 #define STROKE_DEBUG 0
48 static const VGfloat curve_threshold
= 0.25f
;
50 static const VGfloat zero_coords
[] = {0.f
, 0.f
};
52 enum intersection_type
{
55 UnboundedIntersection
,
66 struct stroke_iterator
{
67 void (*next
)(struct stroke_iterator
*);
68 VGboolean (*has_next
)(struct stroke_iterator
*);
70 VGPathCommand (*current_command
)(struct stroke_iterator
*it
);
71 void (*current_coords
)(struct stroke_iterator
*it
, VGfloat
*coords
);
77 const VGfloat
*coords
;
81 struct polygon
*curve_poly
;
85 static VGPathCommand
stroke_itr_command(struct stroke_iterator
*itr
)
87 return itr
->current_command(itr
);
90 static void stroke_itr_coords(struct stroke_iterator
*itr
, VGfloat
*coords
)
92 itr
->current_coords(itr
, coords
);
95 static void stroke_fw_itr_coords(struct stroke_iterator
*itr
, VGfloat
*coords
)
97 if (itr
->position
>= itr
->num_commands
)
99 switch (stroke_itr_command(itr
)) {
101 coords
[0] = itr
->coords
[itr
->coord_position
];
102 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
105 coords
[0] = itr
->coords
[itr
->coord_position
];
106 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
108 case VG_CUBIC_TO_ABS
:
109 coords
[0] = itr
->coords
[itr
->coord_position
];
110 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
111 coords
[2] = itr
->coords
[itr
->coord_position
+ 2];
112 coords
[3] = itr
->coords
[itr
->coord_position
+ 3];
113 coords
[4] = itr
->coords
[itr
->coord_position
+ 4];
114 coords
[5] = itr
->coords
[itr
->coord_position
+ 5];
117 debug_assert(!"invalid command!\n");
122 static void stroke_bw_itr_coords(struct stroke_iterator
*itr
, VGfloat
*coords
)
124 if (itr
->position
>= itr
->num_commands
)
126 switch (stroke_itr_command(itr
)) {
128 coords
[0] = itr
->coords
[itr
->coord_position
];
129 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
132 coords
[0] = itr
->coords
[itr
->coord_position
];
133 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
135 case VG_CUBIC_TO_ABS
:
136 coords
[0] = itr
->coords
[itr
->coord_position
+ 4];
137 coords
[1] = itr
->coords
[itr
->coord_position
+ 5];
138 coords
[2] = itr
->coords
[itr
->coord_position
+ 2];
139 coords
[3] = itr
->coords
[itr
->coord_position
+ 3];
140 coords
[4] = itr
->coords
[itr
->coord_position
+ 0];
141 coords
[5] = itr
->coords
[itr
->coord_position
+ 1];
144 debug_assert(!"invalid command!\n");
149 static VGPathCommand
stroke_fw_current_command(struct stroke_iterator
*it
)
151 return it
->cmds
[it
->position
];
154 static VGPathCommand
stroke_bw_current_command(struct stroke_iterator
*it
)
156 VGPathCommand prev_cmd
;
157 if (it
->position
== it
->num_commands
-1)
158 return VG_MOVE_TO_ABS
;
160 prev_cmd
= it
->cmds
[it
->position
+ 1];
164 static VGboolean
stroke_fw_has_next(struct stroke_iterator
*itr
)
166 return itr
->position
< (itr
->num_commands
- 1);
169 static VGboolean
stroke_bw_has_next(struct stroke_iterator
*itr
)
171 return itr
->position
> 0;
174 static void stroke_fw_next(struct stroke_iterator
*itr
)
177 debug_assert(stroke_fw_has_next(itr
));
179 cmd
= stroke_itr_command(itr
);
181 itr
->coord_position
+= num_elements_for_segments(&cmd
, 1);
185 static void stroke_bw_next(struct stroke_iterator
*itr
)
188 debug_assert(stroke_bw_has_next(itr
));
191 cmd
= stroke_itr_command(itr
);
193 itr
->coord_position
-= num_elements_for_segments(&cmd
, 1);
196 static void stroke_itr_common_init(struct stroke_iterator
*itr
,
198 struct array
*coords
)
200 itr
->cmds
= (VGubyte
*)cmds
->data
;
201 itr
->num_commands
= cmds
->num_elements
;
203 itr
->coords
= (VGfloat
*)coords
->data
;
204 itr
->num_coords
= coords
->num_elements
;
207 static void stroke_forward_iterator(struct stroke_iterator
*itr
,
209 struct array
*coords
)
211 stroke_itr_common_init(itr
, cmds
, coords
);
213 itr
->coord_position
= 0;
215 itr
->next
= stroke_fw_next
;
216 itr
->has_next
= stroke_fw_has_next
;
217 itr
->current_command
= stroke_fw_current_command
;
218 itr
->current_coords
= stroke_fw_itr_coords
;
221 static void stroke_backward_iterator(struct stroke_iterator
*itr
,
223 struct array
*coords
)
226 stroke_itr_common_init(itr
, cmds
, coords
);
227 itr
->position
= itr
->num_commands
- 1;
229 cmd
= stroke_bw_current_command(itr
);
230 itr
->coord_position
= itr
->num_coords
-
231 num_elements_for_segments(&cmd
, 1);
233 itr
->next
= stroke_bw_next
;
234 itr
->has_next
= stroke_bw_has_next
;
235 itr
->current_command
= stroke_bw_current_command
;
236 itr
->current_coords
= stroke_bw_itr_coords
;
241 static void stroke_flat_next(struct stroke_iterator
*itr
)
245 if (itr
->curve_index
>= 0) {
247 if (itr
->curve_index
>= polygon_vertex_count(itr
->curve_poly
)) {
248 itr
->curve_index
= -1;
249 polygon_destroy(itr
->curve_poly
);
254 debug_assert(stroke_fw_has_next(itr
));
256 cmd
= itr
->cmds
[itr
->position
];
257 itr
->coord_position
+= num_elements_for_segments(&cmd
, 1);
260 cmd
= itr
->cmds
[itr
->position
];
262 if (cmd
== VG_CUBIC_TO_ABS
) {
263 struct bezier bezier
;
266 bez
[0] = itr
->coords
[itr
->coord_position
- 2];
267 bez
[1] = itr
->coords
[itr
->coord_position
- 1];
268 bez
[2] = itr
->coords
[itr
->coord_position
];
269 bez
[3] = itr
->coords
[itr
->coord_position
+ 1];
270 bez
[4] = itr
->coords
[itr
->coord_position
+ 2];
271 bez
[5] = itr
->coords
[itr
->coord_position
+ 3];
272 bez
[6] = itr
->coords
[itr
->coord_position
+ 4];
273 bez
[7] = itr
->coords
[itr
->coord_position
+ 5];
280 /* skip the first one, it's the same as the prev point */
281 itr
->curve_index
= 1;
282 if (itr
->curve_poly
) {
283 polygon_destroy(itr
->curve_poly
);
286 itr
->curve_poly
= bezier_to_polygon(&bezier
);
290 static VGboolean
stroke_flat_has_next(struct stroke_iterator
*itr
)
292 return (itr
->curve_index
>= 0 &&
293 itr
->curve_index
< (polygon_vertex_count(itr
->curve_poly
)-1))
294 || itr
->position
< (itr
->num_commands
- 1);
297 static VGPathCommand
stroke_flat_current_command(struct stroke_iterator
*it
)
299 if (it
->cmds
[it
->position
] == VG_CUBIC_TO_ABS
) {
300 return VG_LINE_TO_ABS
;
302 return it
->cmds
[it
->position
];
305 static void stroke_flat_itr_coords(struct stroke_iterator
*itr
, VGfloat
*coords
)
307 if (itr
->curve_index
<= -1 && itr
->position
>= itr
->num_commands
)
310 if (itr
->curve_index
>= 0) {
311 polygon_vertex(itr
->curve_poly
, itr
->curve_index
,
316 switch (stroke_itr_command(itr
)) {
318 coords
[0] = itr
->coords
[itr
->coord_position
];
319 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
322 coords
[0] = itr
->coords
[itr
->coord_position
];
323 coords
[1] = itr
->coords
[itr
->coord_position
+ 1];
325 case VG_CUBIC_TO_ABS
:
327 debug_assert(!"invalid command!\n");
331 static void stroke_flat_iterator(struct stroke_iterator
*itr
,
333 struct array
*coords
)
335 stroke_itr_common_init(itr
, cmds
, coords
);
337 itr
->coord_position
= 0;
339 itr
->next
= stroke_flat_next
;
340 itr
->has_next
= stroke_flat_has_next
;
341 itr
->current_command
= stroke_flat_current_command
;
342 itr
->current_coords
= stroke_flat_itr_coords
;
343 itr
->curve_index
= -1;
348 static INLINE VGboolean
finite_coords4(const VGfloat
*c
)
351 isfinite(c
[0]) && isfinite(c
[1]) &&
352 isfinite(c
[2]) && isfinite(c
[3]);
355 /* from Graphics Gems II */
356 #define SAME_SIGNS(a, b) ((a) * (b) >= 0)
357 static VGboolean
do_lines_intersect(VGfloat x1
, VGfloat y1
, VGfloat x2
, VGfloat y2
,
358 VGfloat x3
, VGfloat y3
, VGfloat x4
, VGfloat y4
)
360 VGfloat a1
, a2
, b1
, b2
, c1
, c2
; /* Coefficients of line eqns */
361 VGfloat r1
, r2
, r3
, r4
; /* 'sign' values */
365 c1
= x2
* y1
- x1
* y2
;
367 r3
= a1
* x3
+ b1
* y3
+ c1
;
368 r4
= a1
* x4
+ b1
* y4
+ c1
;
370 if (r3
!= 0 && r4
!= 0 && SAME_SIGNS(r3
, r4
))
375 c2
= x4
* y3
- x3
* y4
;
377 r1
= a2
* x1
+ b2
* y1
+ c2
;
378 r2
= a2
* x2
+ b2
* y2
+ c2
;
380 if (r1
!= 0 && r2
!= 0 && SAME_SIGNS(r1
, r2
))
386 static INLINE VGfloat
line_dx(const VGfloat
*l
)
391 static INLINE VGfloat
line_dy(const VGfloat
*l
)
396 static INLINE VGfloat
line_angle(const VGfloat
*l
)
398 const VGfloat dx
= line_dx(l
);
399 const VGfloat dy
= line_dy(l
);
401 const VGfloat theta
= atan2(-dy
, dx
) * 360.0 / M_2PI
;
403 const VGfloat theta_normalized
= theta
< 0 ? theta
+ 360 : theta
;
405 if (floatsEqual(theta_normalized
, 360.f
))
408 return theta_normalized
;
411 static INLINE
void line_set_length(VGfloat
*l
, VGfloat len
)
413 VGfloat uv
[] = {l
[0], l
[1], l
[2], l
[3]};
417 l
[2] = l
[0] + line_dx(uv
) * len
;
418 l
[3] = l
[1] + line_dy(uv
) * len
;
421 static INLINE
void line_translate(VGfloat
*l
, VGfloat x
, VGfloat y
)
429 static INLINE VGfloat
line_angle_to(const VGfloat
*l1
,
432 VGfloat a1
, a2
, delta
, delta_normalized
;
433 if (null_line(l1
) || null_line(l1
))
440 delta_normalized
= delta
< 0 ? delta
+ 360 : delta
;
442 if (floatsEqual(delta
, 360.f
))
445 return delta_normalized
;
448 static INLINE VGfloat
line_angles(const VGfloat
*l1
,
451 VGfloat cos_line
, rad
= 0;
453 if (null_line(l1
) || null_line(l2
))
456 cos_line
= (line_dx(l1
)*line_dx(l2
) + line_dy(l1
)*line_dy(l2
)) /
457 (line_lengthv(l1
)*line_lengthv(l2
));
460 if (cos_line
>= -1.0 && cos_line
<= 1.0)
461 rad
= acos(cos_line
);
462 return rad
* 360 / M_2PI
;
466 static INLINE VGfloat
adapted_angle_on_x(const VGfloat
*line
)
468 const VGfloat identity
[] = {0, 0, 1, 0};
469 VGfloat angle
= line_angles(line
, identity
);
470 if (line_dy(line
) > 0)
475 static enum intersection_type
line_intersect(const VGfloat
*l1
,
477 float *intersection_point
)
479 VGfloat isect
[2] = { 0 };
480 enum intersection_type type
;
481 VGboolean dx_zero
, ldx_zero
;
483 if (null_line(l1
) || null_line(l2
) ||
484 !finite_coords4(l1
) || !finite_coords4(l2
))
485 return NoIntersections
;
487 type
= do_lines_intersect(l1
[0], l1
[1], l1
[2], l1
[3], l2
[0], l2
[1], l2
[2], l2
[3])
488 ? BoundedIntersection
: UnboundedIntersection
;
490 dx_zero
= floatsEqual(line_dx(l1
) + 1, 1);
491 ldx_zero
= floatsEqual(line_dx(l2
) + 1, 1);
493 /* one of the lines is vertical */
494 if (dx_zero
&& ldx_zero
) {
495 type
= NoIntersections
;
496 } else if (dx_zero
) {
497 VGfloat la
= line_dy(l2
) / line_dx(l2
);
499 isect
[1] = la
* l1
[0] + l2
[1] - la
* l2
[0];
500 } else if (ldx_zero
) {
501 VGfloat ta
= line_dy(l1
) / line_dx(l1
);
503 isect
[1] = ta
* l2
[0] + l1
[1] - ta
*l1
[0];
506 VGfloat ta
= line_dy(l1
) / line_dx(l1
);
507 VGfloat la
= line_dy(l2
) / line_dx(l2
);
509 return NoIntersections
;
511 x
= ( - l2
[1] + la
* l2
[0] + l1
[1] - ta
* l1
[0] ) / (la
- ta
);
513 isect
[1] = ta
*(x
- l1
[0]) + l1
[1];
515 if (intersection_point
) {
516 intersection_point
[0] = isect
[0];
517 intersection_point
[1] = isect
[1];
522 static INLINE
enum line_join_mode
stroker_join_mode(struct stroker
*s
)
524 switch(s
->join_style
) {
536 static INLINE
enum line_join_mode
stroker_cap_mode(struct stroker
*s
)
538 switch(s
->cap_style
) {
550 void stroker_emit_move_to(struct stroker
*stroker
, VGfloat x
, VGfloat y
)
552 VGubyte cmds
= VG_MOVE_TO_ABS
;
553 VGfloat coords
[2] = {x
, y
};
555 debug_printf("emit move %f, %f\n", x
, y
);
557 stroker
->back2_x
= stroker
->back1_x
;
558 stroker
->back2_y
= stroker
->back1_y
;
559 stroker
->back1_x
= x
;
560 stroker
->back1_y
= y
;
562 path_append_data(stroker
->path
,
567 void stroker_emit_line_to(struct stroker
*stroker
, VGfloat x
, VGfloat y
)
569 VGubyte cmds
= VG_LINE_TO_ABS
;
570 VGfloat coords
[2] = {x
, y
};
572 debug_printf("emit line %f, %f\n", x
, y
);
574 stroker
->back2_x
= stroker
->back1_x
;
575 stroker
->back2_y
= stroker
->back1_y
;
576 stroker
->back1_x
= x
;
577 stroker
->back1_y
= y
;
578 path_append_data(stroker
->path
,
583 void stroker_emit_curve_to(struct stroker
*stroker
, VGfloat px1
, VGfloat py1
,
584 VGfloat px2
, VGfloat py2
,
585 VGfloat x
, VGfloat y
)
587 VGubyte cmds
= VG_CUBIC_TO_ABS
;
588 VGfloat coords
[6] = {px1
, py1
, px2
, py2
, x
, y
};
590 debug_printf("emit curve %f, %f, %f, %f, %f, %f\n", px1
, py1
,
594 if (px2
== x
&& py2
== y
) {
595 if (px1
== x
&& py1
== y
) {
596 stroker
->back2_x
= stroker
->back1_x
;
597 stroker
->back2_y
= stroker
->back1_y
;
599 stroker
->back2_x
= px1
;
600 stroker
->back2_y
= py1
;
603 stroker
->back2_x
= px2
;
604 stroker
->back2_y
= py2
;
606 stroker
->back1_x
= x
;
607 stroker
->back1_y
= y
;
609 path_append_data(stroker
->path
,
614 static INLINE
void create_round_join(struct stroker
*stroker
,
615 VGfloat x1
, VGfloat y1
,
616 VGfloat x2
, VGfloat y2
,
617 VGfloat width
, VGfloat height
)
620 struct matrix matrix
;
622 matrix_load_identity(&matrix
);
624 /*stroker_emit_line_to(stroker, nx, ny);*/
626 arc_init(&arc
, VG_SCCWARC_TO_ABS
,
627 x1
, y1
, x2
, y2
, width
/2, height
/2, 0);
628 arc_stroker_emit(&arc
, stroker
, &matrix
);
632 static void create_joins(struct stroker
*stroker
,
633 VGfloat focal_x
, VGfloat focal_y
,
634 const VGfloat
*next_line
, enum line_join_mode join
)
637 debug_printf("create_joins: focal=[%f, %f], next_line=[%f, %f,%f, %f]\n",
639 next_line
[0], next_line
[1], next_line
[2], next_line
[3]);
641 /* if we're alredy connected do nothing */
642 if (floatsEqual(stroker
->back1_x
, next_line
[0]) &&
643 floatsEqual(stroker
->back1_y
, next_line
[1]))
646 if (join
== FlatJoin
) {
647 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
649 VGfloat prev_line
[] = {stroker
->back2_x
, stroker
->back2_y
,
650 stroker
->back1_x
, stroker
->back1_y
};
652 VGfloat isect
[2] = { 0 };
653 enum intersection_type type
= line_intersect(prev_line
, next_line
, isect
);
655 if (join
== SquareJoin
) {
656 VGfloat offset
= stroker
->stroke_width
/ 2;
657 VGfloat l1
[4] = {prev_line
[0],
661 VGfloat l2
[4] = {next_line
[2],
666 line_translate(l1
, line_dx(l1
), line_dy(l1
));
667 line_set_length(l1
, offset
);
669 line_translate(l2
, line_dx(l2
), line_dy(l2
));
670 line_set_length(l2
, offset
);
672 stroker_emit_line_to(stroker
, l1
[2], l1
[3]);
673 stroker_emit_line_to(stroker
, l2
[2], l2
[3]);
674 stroker_emit_line_to(stroker
, l2
[0], l2
[1]);
675 } else if (join
== RoundJoin
) {
676 VGfloat offset
= stroker
->stroke_width
/ 2;
677 VGfloat short_cut
[4] = {prev_line
[2], prev_line
[3],
678 next_line
[0], next_line
[1]};
679 VGfloat angle
= line_angles(prev_line
, short_cut
);
681 if (type
== BoundedIntersection
||
682 (angle
> 90 && !floatsEqual(angle
, 90.f
))) {
683 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
686 create_round_join(stroker
, prev_line
[2], prev_line
[3],
687 next_line
[0], next_line
[1],
688 offset
* 2, offset
* 2);
690 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
691 } else if (join
== RoundCap
) {
692 VGfloat offset
= stroker
->stroke_width
/ 2;
693 VGfloat l1
[4] = { prev_line
[0], prev_line
[1],
694 prev_line
[2], prev_line
[3] };
695 VGfloat l2
[4] = {focal_x
, focal_y
,
696 prev_line
[2], prev_line
[3]};
698 line_translate(l1
, line_dx(l1
), line_dy(l1
));
699 line_set_length(l1
, KAPPA
* offset
);
701 /* normal between prev_line and focal */
702 line_translate(l2
, -line_dy(l2
), line_dx(l2
));
703 line_set_length(l2
, KAPPA
* offset
);
705 stroker_emit_curve_to(stroker
, l1
[2], l1
[3],
711 l2
[2] = l2
[0] - line_dx(l2
);
712 l2
[3] = l2
[1] - line_dy(l2
);
714 line_translate(l1
, next_line
[0] - l1
[0], next_line
[1] - l1
[1]);
716 stroker_emit_curve_to(stroker
,
720 } else if (join
== MiterJoin
) {
721 VGfloat miter_line
[4] = {stroker
->back1_x
, stroker
->back1_y
,
723 VGfloat sl
= (stroker
->stroke_width
* stroker
->miter_limit
);
724 VGfloat inside_line
[4] = {prev_line
[2], prev_line
[3],
725 next_line
[0], next_line
[1]};
726 VGfloat angle
= line_angle_to(inside_line
, prev_line
);
728 if (type
== BoundedIntersection
||
729 (angle
> 90 && !floatsEqual(angle
, 90.f
))) {
731 debug_printf("f = %f, nl = %f, pl = %f, is = %f\n",
732 focal_x, next_line[0],
733 prev_line[2], isect[0]);*/
734 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
738 if (type
== NoIntersections
|| line_lengthv(miter_line
) > sl
) {
739 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
741 stroker_emit_line_to(stroker
, isect
[0], isect
[1]);
742 stroker_emit_line_to(stroker
, next_line
[0], next_line
[1]);
745 debug_assert(!"create_joins bad join style");
750 static void stroker_add_segment(struct stroker
*stroker
,
752 const VGfloat
*coords
,
755 /* skip duplicated points */
756 if (stroker
->segments
->num_elements
&&
757 stroker
->last_cmd
== cmd
) {
758 VGfloat
*data
= stroker
->control_points
->data
;
759 data
+= stroker
->control_points
->num_elements
;
763 if (floatsEqual(coords
[0], data
[0]) &&
764 floatsEqual(coords
[1], data
[1]))
768 if (floatsEqual(coords
[0], data
[0]) &&
769 floatsEqual(coords
[1], data
[1]))
772 case VG_CUBIC_TO_ABS
:
773 if (floatsEqual(coords
[0], data
[0]) &&
774 floatsEqual(coords
[1], data
[1]) &&
775 floatsEqual(coords
[2], data
[2]) &&
776 floatsEqual(coords
[3], data
[3]) &&
777 floatsEqual(coords
[4], data
[4]) &&
778 floatsEqual(coords
[5], data
[5]))
782 debug_assert(!"Invalid stroke segment");
784 } else if (stroker
->last_cmd
== VG_CUBIC_TO_ABS
&&
785 cmd
== VG_LINE_TO_ABS
) {
786 VGfloat
*data
= stroker
->control_points
->data
;
787 data
+= stroker
->control_points
->num_elements
;
789 if (floatsEqual(coords
[0], data
[0]) &&
790 floatsEqual(coords
[1], data
[1]))
793 stroker
->last_cmd
= cmd
;
794 array_append_data(stroker
->segments
, &cmd
, 1);
795 array_append_data(stroker
->control_points
, coords
, num_coords
);
798 void stroker_move_to(struct stroker
*stroker
, VGfloat x
, VGfloat y
)
800 VGfloat coords
[2] = {x
, y
};
802 debug_printf("stroker_move_to(%f, %f)\n", x
, y
);
805 if (stroker
->segments
->num_elements
> 0)
806 stroker
->process_subpath(stroker
);
808 array_reset(stroker
->segments
);
809 array_reset(stroker
->control_points
);
811 stroker_add_segment(stroker
, VG_MOVE_TO_ABS
, coords
, 2);
814 void stroker_line_to(struct stroker
*stroker
, VGfloat x
, VGfloat y
)
816 VGfloat coords
[] = {x
, y
};
819 debug_printf("stroker_line_to(%f, %f)\n", x
, y
);
821 if (!stroker
->segments
->num_elements
)
822 stroker_add_segment(stroker
, VG_MOVE_TO_ABS
, zero_coords
, 2);
824 stroker_add_segment(stroker
, VG_LINE_TO_ABS
, coords
, 2);
827 void stroker_curve_to(struct stroker
*stroker
, VGfloat px1
, VGfloat py1
,
828 VGfloat px2
, VGfloat py2
,
829 VGfloat x
, VGfloat y
)
831 VGfloat coords
[] = {px1
, py1
,
835 debug_printf("stroker_curve_to(%f, %f, %f, %f, %f, %f)\n",
836 px1
, py1
, px2
, py2
, x
, y
);
838 if (!stroker
->segments
->num_elements
)
839 stroker_add_segment(stroker
, VG_MOVE_TO_ABS
, zero_coords
, 2);
841 stroker_add_segment(stroker
, VG_CUBIC_TO_ABS
, coords
, 6);
844 static INLINE VGboolean
is_segment_null(VGPathCommand cmd
,
851 return floatsEqual(coords
[0], res
[0]) &&
852 floatsEqual(coords
[1], res
[1]);
854 case VG_CUBIC_TO_ABS
:
855 return floatsEqual(coords
[0], res
[0]) &&
856 floatsEqual(coords
[1], res
[1]) &&
857 floatsEqual(coords
[2], res
[0]) &&
858 floatsEqual(coords
[3], res
[1]) &&
859 floatsEqual(coords
[4], res
[0]) &&
860 floatsEqual(coords
[5], res
[1]);
868 static VGboolean
vg_stroke_outline(struct stroke_iterator
*it
,
869 struct stroker
*stroker
,
871 VGfloat
*start_tangent
)
873 #define MAX_OFFSET 16
874 struct bezier offset_curves
[MAX_OFFSET
];
875 VGPathCommand first_element
;
876 VGfloat start
[2], prev
[2];
877 VGboolean first
= VG_TRUE
;
880 first_element
= stroke_itr_command(it
);
881 if (first_element
!= VG_MOVE_TO_ABS
) {
882 stroker_emit_move_to(stroker
, 0.f
, 0.f
);
886 stroke_itr_coords(it
, start
);
888 debug_printf(" -> (side) [%.2f, %.2f]\n",
896 offset
= stroker
->stroke_width
/ 2;
898 if (!it
->has_next(it
)) {
904 while (it
->has_next(it
)) {
909 cmd
= stroke_itr_command(it
);
910 stroke_itr_coords(it
, coords
);
912 if (cmd
== VG_LINE_TO_ABS
) {
913 VGfloat line
[4] = {prev
[0], prev
[1], coords
[0], coords
[1]};
915 line_normal(line
, normal
);
918 debug_printf("\n ---> (side) lineto [%.2f, %.2f]\n", coords
[0], coords
[1]);
920 line_set_length(normal
, offset
);
921 line_translate(line
, line_dx(normal
), line_dy(normal
));
923 /* if we are starting a new subpath, move to correct starting point */
926 create_joins(stroker
, prev
[0], prev
[1], line
,
927 stroker_cap_mode(stroker
));
929 stroker_emit_move_to(stroker
, line
[0], line
[1]);
930 memcpy(start_tangent
, line
,
931 sizeof(VGfloat
) * 4);
934 create_joins(stroker
, prev
[0], prev
[1], line
,
935 stroker_join_mode(stroker
));
938 /* add the stroke for this line */
939 stroker_emit_line_to(stroker
, line
[2], line
[3]);
942 } else if (cmd
== VG_CUBIC_TO_ABS
) {
944 debug_printf("\n ---> (side) cubicTo [%.2f, %.2f]\n",
948 struct bezier bezier
;
952 prev
[0], prev
[1], coords
[0], coords
[1],
953 coords
[2], coords
[3], coords
[4], coords
[5]);
955 count
= bezier_translate_by_normal(&bezier
,
962 /* if we are starting a new subpath, move to correct starting point */
966 bezier_start_tangent(&bezier
, tangent
);
967 line_translate(tangent
,
968 offset_curves
[0].x1
- bezier
.x1
,
969 offset_curves
[0].y1
- bezier
.y1
);
971 VGfloat pt
[2] = {offset_curves
[0].x1
,
972 offset_curves
[0].y1
};
975 create_joins(stroker
, prev
[0], prev
[1], tangent
,
976 stroker_cap_mode(stroker
));
978 stroker_emit_move_to(stroker
, pt
[0], pt
[1]);
980 start_tangent
[0] = tangent
[0];
981 start_tangent
[1] = tangent
[1];
982 start_tangent
[2] = tangent
[2];
983 start_tangent
[3] = tangent
[3];
986 create_joins(stroker
, prev
[0], prev
[1], tangent
,
987 stroker_join_mode(stroker
));
990 /* add these beziers */
991 for (i
= 0; i
< count
; ++i
) {
992 struct bezier
*bez
= &offset_curves
[i
];
993 stroker_emit_curve_to(stroker
,
1000 prev
[0] = coords
[4];
1001 prev
[1] = coords
[5];
1005 if (floatsEqual(start
[0], prev
[0]) &&
1006 floatsEqual(start
[1], prev
[1])) {
1007 /* closed subpath, join first and last point */
1009 debug_printf("\n stroker: closed subpath\n");
1011 create_joins(stroker
, prev
[0], prev
[1], start_tangent
,
1012 stroker_join_mode(stroker
));
1016 debug_printf("\n stroker: open subpath\n");
1023 static void stroker_process_subpath(struct stroker
*stroker
)
1025 VGboolean fwclosed
, bwclosed
;
1026 VGfloat fw_start_tangent
[4], bw_start_tangent
[4];
1027 struct stroke_iterator fwit
;
1028 struct stroke_iterator bwit
;
1029 debug_assert(stroker
->segments
->num_elements
> 0);
1031 memset(fw_start_tangent
, 0,
1033 memset(bw_start_tangent
, 0,
1036 stroke_forward_iterator(&fwit
, stroker
->segments
,
1037 stroker
->control_points
);
1038 stroke_backward_iterator(&bwit
, stroker
->segments
,
1039 stroker
->control_points
);
1041 debug_assert(fwit
.cmds
[0] == VG_MOVE_TO_ABS
);
1043 fwclosed
= vg_stroke_outline(&fwit
, stroker
, VG_FALSE
, fw_start_tangent
);
1044 bwclosed
= vg_stroke_outline(&bwit
, stroker
, !fwclosed
, bw_start_tangent
);
1047 create_joins(stroker
,
1048 fwit
.coords
[0], fwit
.coords
[1], fw_start_tangent
,
1049 stroker_cap_mode(stroker
));
1051 /* hack to handle the requirement of the VG spec that says that strokes
1052 * of len==0 that have butt cap or round cap still need
1053 * to be rendered. (8.7.4 Stroke Generation) */
1054 if (stroker
->segments
->num_elements
<= 3) {
1056 VGfloat data
[8], coords
[8];
1057 struct stroke_iterator
*it
= &fwit
;
1059 stroke_forward_iterator(it
, stroker
->segments
,
1060 stroker
->control_points
);
1061 cmd
= stroke_itr_command(it
);
1062 stroke_itr_coords(it
, coords
);
1063 if (cmd
!= VG_MOVE_TO_ABS
) {
1064 memset(data
, 0, sizeof(VGfloat
) * 8);
1065 if (!is_segment_null(cmd
, coords
, data
))
1068 data
[0] = coords
[0];
1069 data
[1] = coords
[1];
1071 while (it
->has_next(it
)) {
1073 cmd
= stroke_itr_command(it
);
1074 stroke_itr_coords(it
, coords
);
1075 if (!is_segment_null(cmd
, coords
, data
))
1078 /* generate the square/round cap */
1079 if (stroker
->cap_style
== VG_CAP_SQUARE
) {
1080 VGfloat offset
= stroker
->stroke_width
/ 2;
1081 stroker_emit_move_to(stroker
, data
[0] - offset
,
1083 stroker_emit_line_to(stroker
, data
[0] + offset
,
1085 stroker_emit_line_to(stroker
, data
[0] + offset
,
1087 stroker_emit_line_to(stroker
, data
[0] - offset
,
1089 stroker_emit_line_to(stroker
, data
[0] - offset
,
1091 } else if (stroker
->cap_style
== VG_CAP_ROUND
) {
1092 VGfloat offset
= stroker
->stroke_width
/ 2;
1093 VGfloat cx
= data
[0], cy
= data
[1];
1096 struct matrix matrix
;
1097 matrix_load_identity(&matrix
);
1099 stroker_emit_move_to(stroker
, cx
+ offset
, cy
);
1100 arc_init(&arc
, VG_SCCWARC_TO_ABS
,
1104 arc_stroker_emit(&arc
, stroker
, &matrix
);
1105 arc_init(&arc
, VG_SCCWARC_TO_ABS
,
1109 arc_stroker_emit(&arc
, stroker
, &matrix
);
1116 static INLINE VGfloat
dash_pattern(struct dash_stroker
*stroker
,
1119 if (stroker
->dash_pattern
[idx
] < 0)
1121 return stroker
->dash_pattern
[idx
];
1124 static void dash_stroker_process_subpath(struct stroker
*str
)
1126 struct dash_stroker
*stroker
= (struct dash_stroker
*)str
;
1127 VGfloat sum_length
= 0;
1132 VGfloat doffset
= stroker
->dash_phase
;
1136 struct stroke_iterator it
;
1138 VGfloat move_to_pos
[2];
1139 VGfloat line_to_pos
[2];
1141 VGboolean has_move_to
= VG_FALSE
;
1143 stroke_flat_iterator(&it
, stroker
->base
.segments
,
1144 stroker
->base
.control_points
);
1146 stroke_itr_coords(&it
, prev
);
1147 move_to_pos
[0] = prev
[0];
1148 move_to_pos
[1] = prev
[1];
1150 debug_assert(stroker
->dash_pattern_num
> 0);
1152 for (i
= 0; i
< stroker
->dash_pattern_num
; ++i
) {
1153 sum_length
+= dash_pattern(stroker
, i
);
1156 if (floatIsZero(sum_length
)) {
1160 doffset
-= floorf(doffset
/ sum_length
) * sum_length
;
1162 while (!floatIsZero(doffset
) && doffset
>= dash_pattern(stroker
, idash
)) {
1163 doffset
-= dash_pattern(stroker
, idash
);
1164 idash
= (idash
+ 1) % stroker
->dash_pattern_num
;
1167 while (it
.has_next(&it
)) {
1173 cmd
= stroke_itr_command(&it
);
1174 stroke_itr_coords(&it
, coords
);
1176 debug_assert(cmd
== VG_LINE_TO_ABS
);
1179 cline
[2] = coords
[0];
1180 cline
[3] = coords
[1];
1182 elen
= line_lengthv(cline
);
1184 estop
= estart
+ elen
;
1186 done
= pos
>= estop
;
1190 VGint idash_incr
= 0;
1191 VGboolean has_offset
= doffset
> 0;
1192 VGfloat dpos
= pos
+ dash_pattern(stroker
, idash
) - doffset
- estart
;
1194 debug_assert(dpos
>= 0);
1196 if (dpos
> elen
) { /* dash extends this line */
1197 doffset
= dash_pattern(stroker
, idash
) - (dpos
- elen
);
1202 } else { /* Dash is on this line */
1203 line_point_at(cline
, dpos
/elen
, p2
);
1204 pos
= dpos
+ estart
;
1205 done
= pos
>= estop
;
1210 if (idash
% 2 == 0) {
1211 line_to_pos
[0] = p2
[0];
1212 line_to_pos
[1] = p2
[1];
1214 if (!has_offset
|| !has_move_to
) {
1215 stroker_move_to(&stroker
->stroker
, move_to_pos
[0], move_to_pos
[1]);
1216 has_move_to
= VG_TRUE
;
1218 stroker_line_to(&stroker
->stroker
, line_to_pos
[0], line_to_pos
[1]);
1220 move_to_pos
[0] = p2
[0];
1221 move_to_pos
[1] = p2
[1];
1224 idash
= (idash
+ idash_incr
) % stroker
->dash_pattern_num
;
1228 prev
[0] = coords
[0];
1229 prev
[1] = coords
[1];
1232 if (it
.curve_poly
) {
1233 polygon_destroy(it
.curve_poly
);
1237 stroker
->base
.path
= stroker
->stroker
.path
;
1240 static void default_begin(struct stroker
*stroker
)
1242 array_reset(stroker
->segments
);
1243 array_reset(stroker
->control_points
);
1246 static void default_end(struct stroker
*stroker
)
1248 if (stroker
->segments
->num_elements
> 0)
1249 stroker
->process_subpath(stroker
);
1253 static void dash_stroker_begin(struct stroker
*stroker
)
1255 struct dash_stroker
*dasher
=
1256 (struct dash_stroker
*)stroker
;
1258 default_begin(&dasher
->stroker
);
1259 default_begin(stroker
);
1262 static void dash_stroker_end(struct stroker
*stroker
)
1264 struct dash_stroker
*dasher
=
1265 (struct dash_stroker
*)stroker
;
1267 default_end(stroker
);
1268 default_end(&dasher
->stroker
);
1271 void stroker_init(struct stroker
*stroker
,
1272 struct vg_state
*state
)
1274 stroker
->stroke_width
= state
->stroke
.line_width
.f
;
1275 stroker
->miter_limit
= state
->stroke
.miter_limit
.f
;
1276 stroker
->cap_style
= state
->stroke
.cap_style
;
1277 stroker
->join_style
= state
->stroke
.join_style
;
1279 stroker
->begin
= default_begin
;
1280 stroker
->process_subpath
= stroker_process_subpath
;
1281 stroker
->end
= default_end
;
1283 stroker
->segments
= array_create(sizeof(VGubyte
));
1284 stroker
->control_points
= array_create(sizeof(VGfloat
));
1286 stroker
->back1_x
= 0;
1287 stroker
->back1_y
= 0;
1288 stroker
->back2_x
= 0;
1289 stroker
->back2_y
= 0;
1291 stroker
->path
= path_create(VG_PATH_DATATYPE_F
, 1.0f
, 0.0f
,
1292 0, 0, VG_PATH_CAPABILITY_ALL
);
1294 stroker
->last_cmd
= VG_CLOSE_PATH
;
1297 void dash_stroker_init(struct stroker
*str
,
1298 struct vg_state
*state
)
1300 struct dash_stroker
*stroker
= (struct dash_stroker
*)str
;
1303 stroker_init(str
, state
);
1304 stroker_init(&stroker
->stroker
, state
);
1307 int real_num
= state
->stroke
.dash_pattern_num
;
1308 if (real_num
% 2)/* if odd, ignore the last one */
1310 for (i
= 0; i
< real_num
; ++i
)
1311 stroker
->dash_pattern
[i
] = state
->stroke
.dash_pattern
[i
].f
;
1312 stroker
->dash_pattern_num
= real_num
;
1315 stroker
->dash_phase
= state
->stroke
.dash_phase
.f
;
1316 stroker
->dash_phase_reset
= state
->stroke
.dash_phase_reset
;
1318 stroker
->base
.begin
= dash_stroker_begin
;
1319 stroker
->base
.process_subpath
= dash_stroker_process_subpath
;
1320 stroker
->base
.end
= dash_stroker_end
;
1321 path_destroy(stroker
->base
.path
);
1322 stroker
->base
.path
= NULL
;
1325 void stroker_begin(struct stroker
*stroker
)
1327 stroker
->begin(stroker
);
1330 void stroker_end(struct stroker
*stroker
)
1332 stroker
->end(stroker
);
1335 void stroker_cleanup(struct stroker
*stroker
)
1337 array_destroy(stroker
->segments
);
1338 array_destroy(stroker
->control_points
);
1341 void dash_stroker_cleanup(struct dash_stroker
*stroker
)
1343 /* if stroker->base.path is null means we never
1344 * processed a valid path so delete the temp one
1345 * we already created */
1346 if (!stroker
->base
.path
)
1347 path_destroy(stroker
->stroker
.path
);
1348 stroker_cleanup(&stroker
->stroker
);
1349 stroker_cleanup((struct stroker
*)stroker
);