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 "stroker.h"
28 
29 #include "path.h"
30 #include "vg_state.h"
31 #include "util_array.h"
32 #include "arc.h"
33 #include "bezier.h"
34 #include "matrix.h"
35 #include "path_utils.h"
36 #include "polygon.h"
37 
38 #include "util/u_math.h"
39 
40 #ifndef M_2PI
41 #define M_2PI 6.28318530717958647692528676655900576
42 #endif
43 
44 #define STROKE_SEGMENTS 0
45 #define STROKE_DEBUG 0
46 #define DEBUG_EMITS 0
47 
48 static const VGfloat curve_threshold = 0.25f;
49 
50 static const VGfloat zero_coords[] = {0.f, 0.f};
51 
52 enum intersection_type {
53    NoIntersections,
54    BoundedIntersection,
55    UnboundedIntersection,
56 };
57 
58 enum line_join_mode {
59    FlatJoin,
60    SquareJoin,
61    MiterJoin,
62    RoundJoin,
63    RoundCap
64 };
65 
66 struct stroke_iterator {
67    void (*next)(struct stroke_iterator *);
68    VGboolean (*has_next)(struct stroke_iterator *);
69 
70    VGPathCommand (*current_command)(struct stroke_iterator *it);
71    void (*current_coords)(struct stroke_iterator *it, VGfloat *coords);
72 
73    VGint position;
74    VGint coord_position;
75 
76    const VGubyte *cmds;
77    const VGfloat *coords;
78    VGint num_commands;
79    VGint num_coords;
80 
81    struct polygon *curve_poly;
82    VGint curve_index;
83 };
84 
stroke_itr_command(struct stroke_iterator * itr)85 static VGPathCommand stroke_itr_command(struct stroke_iterator *itr)
86 {
87    return itr->current_command(itr);
88 }
89 
stroke_itr_coords(struct stroke_iterator * itr,VGfloat * coords)90 static void stroke_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
91 {
92    itr->current_coords(itr, coords);
93 }
94 
stroke_fw_itr_coords(struct stroke_iterator * itr,VGfloat * coords)95 static void stroke_fw_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
96 {
97    if (itr->position >= itr->num_commands)
98       return;
99    switch (stroke_itr_command(itr)) {
100    case VG_MOVE_TO_ABS:
101       coords[0] = itr->coords[itr->coord_position];
102       coords[1] = itr->coords[itr->coord_position + 1];
103       break;
104    case VG_LINE_TO_ABS:
105       coords[0] = itr->coords[itr->coord_position];
106       coords[1] = itr->coords[itr->coord_position + 1];
107       break;
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];
115       break;
116    default:
117       debug_assert(!"invalid command!\n");
118    }
119 }
120 
121 
stroke_bw_itr_coords(struct stroke_iterator * itr,VGfloat * coords)122 static void stroke_bw_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
123 {
124    if (itr->position >= itr->num_commands)
125       return;
126    switch (stroke_itr_command(itr)) {
127    case VG_MOVE_TO_ABS:
128       coords[0] = itr->coords[itr->coord_position];
129       coords[1] = itr->coords[itr->coord_position + 1];
130       break;
131    case VG_LINE_TO_ABS:
132       coords[0] = itr->coords[itr->coord_position];
133       coords[1] = itr->coords[itr->coord_position + 1];
134       break;
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];
142       break;
143    default:
144       debug_assert(!"invalid command!\n");
145    }
146 }
147 
148 
stroke_fw_current_command(struct stroke_iterator * it)149 static VGPathCommand stroke_fw_current_command(struct stroke_iterator *it)
150 {
151    return it->cmds[it->position];
152 }
153 
stroke_bw_current_command(struct stroke_iterator * it)154 static VGPathCommand stroke_bw_current_command(struct stroke_iterator *it)
155 {
156    VGPathCommand prev_cmd;
157    if (it->position == it->num_commands  -1)
158       return VG_MOVE_TO_ABS;
159 
160    prev_cmd = it->cmds[it->position + 1];
161    return prev_cmd;
162 }
163 
stroke_fw_has_next(struct stroke_iterator * itr)164 static VGboolean stroke_fw_has_next(struct stroke_iterator *itr)
165 {
166    return itr->position < (itr->num_commands - 1);
167 }
168 
stroke_bw_has_next(struct stroke_iterator * itr)169 static VGboolean stroke_bw_has_next(struct stroke_iterator *itr)
170 {
171    return itr->position > 0;
172 }
173 
stroke_fw_next(struct stroke_iterator * itr)174 static void stroke_fw_next(struct stroke_iterator *itr)
175 {
176    VGubyte cmd;
177    debug_assert(stroke_fw_has_next(itr));
178 
179    cmd = stroke_itr_command(itr);
180 
181    itr->coord_position += num_elements_for_segments(&cmd, 1);
182    ++itr->position;
183 }
184 
stroke_bw_next(struct stroke_iterator * itr)185 static void stroke_bw_next(struct stroke_iterator *itr)
186 {
187    VGubyte cmd;
188    debug_assert(stroke_bw_has_next(itr));
189 
190    --itr->position;
191    cmd = stroke_itr_command(itr);
192 
193    itr->coord_position -= num_elements_for_segments(&cmd, 1);
194 }
195 
stroke_itr_common_init(struct stroke_iterator * itr,struct array * cmds,struct array * coords)196 static void stroke_itr_common_init(struct stroke_iterator *itr,
197                                    struct array *cmds,
198                                    struct array *coords)
199 {
200    itr->cmds = (VGubyte*)cmds->data;
201    itr->num_commands = cmds->num_elements;
202 
203    itr->coords = (VGfloat*)coords->data;
204    itr->num_coords = coords->num_elements;
205 }
206 
stroke_forward_iterator(struct stroke_iterator * itr,struct array * cmds,struct array * coords)207 static void stroke_forward_iterator(struct stroke_iterator *itr,
208                                     struct array *cmds,
209                                     struct array *coords)
210 {
211    stroke_itr_common_init(itr, cmds, coords);
212    itr->position = 0;
213    itr->coord_position = 0;
214 
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;
219 }
220 
stroke_backward_iterator(struct stroke_iterator * itr,struct array * cmds,struct array * coords)221 static void stroke_backward_iterator(struct stroke_iterator *itr,
222                                      struct array *cmds,
223                                      struct array *coords)
224 {
225    VGubyte cmd;
226    stroke_itr_common_init(itr, cmds, coords);
227    itr->position = itr->num_commands - 1;
228 
229    cmd = stroke_bw_current_command(itr);
230    itr->coord_position = itr->num_coords -
231                          num_elements_for_segments(&cmd, 1);
232 
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;
237 }
238 
239 
240 
stroke_flat_next(struct stroke_iterator * itr)241 static void stroke_flat_next(struct stroke_iterator *itr)
242 {
243    VGubyte cmd;
244 
245    if (itr->curve_index >= 0) {
246       ++itr->curve_index;
247       if (itr->curve_index >= polygon_vertex_count(itr->curve_poly)) {
248          itr->curve_index = -1;
249          polygon_destroy(itr->curve_poly);
250          itr->curve_poly = 0;
251       } else
252          return;
253    }
254    debug_assert(stroke_fw_has_next(itr));
255 
256    cmd = itr->cmds[itr->position];
257    itr->coord_position += num_elements_for_segments(&cmd, 1);
258    ++itr->position;
259 
260    cmd = itr->cmds[itr->position];
261 
262    if (cmd == VG_CUBIC_TO_ABS) {
263       struct bezier bezier;
264       VGfloat bez[8];
265 
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];
274 
275       bezier_init(&bezier,
276                   bez[0], bez[1],
277                   bez[2], bez[3],
278                   bez[4], bez[5],
279                   bez[6], bez[7]);
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);
284          itr->curve_poly = 0;
285       }
286       itr->curve_poly = bezier_to_polygon(&bezier);
287    }
288 }
289 
stroke_flat_has_next(struct stroke_iterator * itr)290 static VGboolean stroke_flat_has_next(struct stroke_iterator *itr)
291 {
292    return  (itr->curve_index >= 0 &&
293             itr->curve_index < (polygon_vertex_count(itr->curve_poly)-1))
294             || itr->position < (itr->num_commands - 1);
295 }
296 
stroke_flat_current_command(struct stroke_iterator * it)297 static VGPathCommand stroke_flat_current_command(struct stroke_iterator *it)
298 {
299    if (it->cmds[it->position] == VG_CUBIC_TO_ABS) {
300       return VG_LINE_TO_ABS;
301    }
302    return it->cmds[it->position];
303 }
304 
stroke_flat_itr_coords(struct stroke_iterator * itr,VGfloat * coords)305 static void stroke_flat_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
306 {
307    if (itr->curve_index <= -1 && itr->position >= itr->num_commands)
308       return;
309 
310    if (itr->curve_index >= 0) {
311       polygon_vertex(itr->curve_poly, itr->curve_index,
312                      coords);
313       return;
314    }
315 
316    switch (stroke_itr_command(itr)) {
317    case VG_MOVE_TO_ABS:
318       coords[0] = itr->coords[itr->coord_position];
319       coords[1] = itr->coords[itr->coord_position + 1];
320       break;
321    case VG_LINE_TO_ABS:
322       coords[0] = itr->coords[itr->coord_position];
323       coords[1] = itr->coords[itr->coord_position + 1];
324       break;
325    case VG_CUBIC_TO_ABS:
326    default:
327       debug_assert(!"invalid command!\n");
328    }
329 }
330 
stroke_flat_iterator(struct stroke_iterator * itr,struct array * cmds,struct array * coords)331 static void stroke_flat_iterator(struct stroke_iterator *itr,
332                                  struct array *cmds,
333                                  struct array *coords)
334 {
335    stroke_itr_common_init(itr, cmds, coords);
336    itr->position = 0;
337    itr->coord_position = 0;
338 
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;
344    itr->curve_poly = 0;
345 }
346 
347 
finite_coords4(const VGfloat * c)348 static INLINE VGboolean finite_coords4(const VGfloat *c)
349 {
350    return
351       isfinite(c[0]) && isfinite(c[1]) &&
352       isfinite(c[2]) && isfinite(c[3]);
353 }
354 
355 /* from Graphics Gems II */
356 #define SAME_SIGNS(a, b) ((a) * (b) >= 0)
do_lines_intersect(VGfloat x1,VGfloat y1,VGfloat x2,VGfloat y2,VGfloat x3,VGfloat y3,VGfloat x4,VGfloat y4)357 static VGboolean do_lines_intersect(VGfloat x1, VGfloat y1, VGfloat x2, VGfloat y2,
358                                     VGfloat x3, VGfloat y3, VGfloat x4, VGfloat y4)
359 {
360    VGfloat a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns */
361    VGfloat r1, r2, r3, r4;         /* 'sign' values */
362 
363    a1 = y2 - y1;
364    b1 = x1 - x2;
365    c1 = x2 * y1 - x1 * y2;
366 
367    r3 = a1 * x3 + b1 * y3 + c1;
368    r4 = a1 * x4 + b1 * y4 + c1;
369 
370    if (r3 != 0 && r4 != 0 && SAME_SIGNS(r3, r4))
371       return VG_FALSE;
372 
373    a2 = y4 - y3;
374    b2 = x3 - x4;
375    c2 = x4 * y3 - x3 * y4;
376 
377    r1 = a2 * x1 + b2 * y1 + c2;
378    r2 = a2 * x2 + b2 * y2 + c2;
379 
380    if (r1 != 0 && r2 != 0 && SAME_SIGNS(r1, r2))
381       return VG_FALSE;
382 
383    return VG_TRUE;
384 }
385 
line_dx(const VGfloat * l)386 static INLINE VGfloat line_dx(const VGfloat *l)
387 {
388    return l[2] - l[0];
389 }
390 
line_dy(const VGfloat * l)391 static INLINE VGfloat line_dy(const VGfloat *l)
392 {
393    return l[3] - l[1];
394 }
395 
line_angle(const VGfloat * l)396 static INLINE VGfloat line_angle(const VGfloat *l)
397 {
398    const VGfloat dx = line_dx(l);
399    const VGfloat dy = line_dy(l);
400 
401    const VGfloat theta = atan2(-dy, dx) * 360.0 / M_2PI;
402 
403    const VGfloat theta_normalized = theta < 0 ? theta + 360 : theta;
404 
405    if (floatsEqual(theta_normalized, 360.f))
406       return 0;
407    else
408       return theta_normalized;
409 }
410 
line_set_length(VGfloat * l,VGfloat len)411 static INLINE void line_set_length(VGfloat *l, VGfloat len)
412 {
413    VGfloat uv[] = {l[0], l[1], l[2], l[3]};
414    if (null_line(l))
415       return;
416    line_normalize(uv);
417    l[2] = l[0] + line_dx(uv) * len;
418    l[3] = l[1] + line_dy(uv) * len;
419 }
420 
line_translate(VGfloat * l,VGfloat x,VGfloat y)421 static INLINE void line_translate(VGfloat *l, VGfloat x, VGfloat y)
422 {
423    l[0] += x;
424    l[1] += y;
425    l[2] += x;
426    l[3] += y;
427 }
428 
line_angle_to(const VGfloat * l1,const VGfloat * l2)429 static INLINE VGfloat line_angle_to(const VGfloat *l1,
430                                     const VGfloat *l2)
431 {
432    VGfloat a1, a2, delta, delta_normalized;
433    if (null_line(l1) || null_line(l1))
434       return 0;
435 
436    a1 = line_angle(l1);
437    a2 = line_angle(l2);
438 
439    delta = a2 - a1;
440    delta_normalized = delta < 0 ? delta + 360 : delta;
441 
442    if (floatsEqual(delta, 360.f))
443       return 0;
444    else
445       return delta_normalized;
446 }
447 
line_angles(const VGfloat * l1,const VGfloat * l2)448 static INLINE VGfloat line_angles(const VGfloat *l1,
449                                   const VGfloat *l2)
450 {
451    VGfloat cos_line, rad = 0;
452 
453    if (null_line(l1) || null_line(l2))
454       return 0;
455 
456    cos_line = (line_dx(l1)*line_dx(l2) + line_dy(l1)*line_dy(l2)) /
457               (line_lengthv(l1)*line_lengthv(l2));
458    rad = 0;
459 
460    if (cos_line >= -1.0 && cos_line <= 1.0)
461       rad = acos(cos_line);
462    return rad * 360 / M_2PI;
463 }
464 
465 
adapted_angle_on_x(const VGfloat * line)466 static INLINE VGfloat adapted_angle_on_x(const VGfloat *line)
467 {
468    const VGfloat identity[] = {0, 0, 1, 0};
469    VGfloat angle = line_angles(line, identity);
470    if (line_dy(line) > 0)
471       angle = 360 - angle;
472    return angle;
473 }
474 
line_intersect(const VGfloat * l1,const VGfloat * l2,float * intersection_point)475 static enum intersection_type line_intersect(const VGfloat *l1,
476                                              const VGfloat *l2,
477                                              float *intersection_point)
478 {
479    VGfloat isect[2] = { 0 };
480    enum intersection_type type;
481    VGboolean dx_zero, ldx_zero;
482 
483    if (null_line(l1) || null_line(l2) ||
484        !finite_coords4(l1) || !finite_coords4(l2))
485       return NoIntersections;
486 
487    type = do_lines_intersect(l1[0], l1[1], l1[2], l1[3], l2[0], l2[1], l2[2], l2[3])
488           ? BoundedIntersection : UnboundedIntersection;
489 
490    dx_zero  = floatsEqual(line_dx(l1) + 1, 1);
491    ldx_zero = floatsEqual(line_dx(l2) + 1, 1);
492 
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);
498       isect[0] = l1[0];
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);
502       isect[0] = l2[0];
503       isect[1] = ta * l2[0] + l1[1] - ta*l1[0];
504    } else {
505       VGfloat x;
506       VGfloat ta = line_dy(l1) / line_dx(l1);
507       VGfloat la = line_dy(l2) / line_dx(l2);
508       if (ta == la)
509          return NoIntersections;
510 
511       x = ( - l2[1] + la * l2[0] + l1[1] - ta * l1[0] ) / (la - ta);
512       isect[0] = x;
513       isect[1] = ta*(x - l1[0]) + l1[1];
514    }
515    if (intersection_point) {
516       intersection_point[0] = isect[0];
517       intersection_point[1] = isect[1];
518    }
519    return type;
520 }
521 
stroker_join_mode(struct stroker * s)522 static INLINE enum line_join_mode stroker_join_mode(struct stroker *s)
523 {
524    switch(s->join_style) {
525    case VG_JOIN_MITER:
526       return MiterJoin;
527    case VG_JOIN_ROUND:
528       return RoundJoin;
529    case VG_JOIN_BEVEL:
530       return FlatJoin;
531    default:
532       return FlatJoin;
533    }
534 }
535 
stroker_cap_mode(struct stroker * s)536 static INLINE enum line_join_mode stroker_cap_mode(struct stroker *s)
537 {
538    switch(s->cap_style) {
539    case VG_CAP_BUTT:
540       return FlatJoin;
541    case VG_CAP_ROUND:
542       return RoundCap;
543    case VG_CAP_SQUARE:
544       return SquareJoin;
545    default:
546       return FlatJoin;
547    }
548 }
549 
stroker_emit_move_to(struct stroker * stroker,VGfloat x,VGfloat y)550 void stroker_emit_move_to(struct stroker *stroker, VGfloat x, VGfloat y)
551 {
552    VGubyte cmds = VG_MOVE_TO_ABS;
553    VGfloat coords[2] = {x, y};
554 #if DEBUG_EMITS
555    debug_printf("emit move %f, %f\n", x, y);
556 #endif
557    stroker->back2_x = stroker->back1_x;
558    stroker->back2_y = stroker->back1_y;
559    stroker->back1_x = x;
560    stroker->back1_y = y;
561 
562    path_append_data(stroker->path,
563                     1,
564                     &cmds, &coords);
565 }
566 
stroker_emit_line_to(struct stroker * stroker,VGfloat x,VGfloat y)567 void stroker_emit_line_to(struct stroker *stroker, VGfloat x, VGfloat y)
568 {
569    VGubyte cmds = VG_LINE_TO_ABS;
570    VGfloat coords[2] = {x, y};
571 #if DEBUG_EMITS
572    debug_printf("emit line %f, %f\n", x, y);
573 #endif
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,
579                     1,
580                     &cmds, &coords);
581 }
582 
stroker_emit_curve_to(struct stroker * stroker,VGfloat px1,VGfloat py1,VGfloat px2,VGfloat py2,VGfloat x,VGfloat y)583 void stroker_emit_curve_to(struct stroker *stroker, VGfloat px1, VGfloat py1,
584                                   VGfloat px2, VGfloat py2,
585                                   VGfloat x, VGfloat y)
586 {
587    VGubyte cmds = VG_CUBIC_TO_ABS;
588    VGfloat coords[6] = {px1, py1, px2, py2, x, y};
589 #if DEBUG_EMITS
590    debug_printf("emit curve %f, %f, %f, %f, %f, %f\n", px1, py1,
591                 px2, py2, x, y);
592 #endif
593 
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;
598       } else {
599          stroker->back2_x = px1;
600          stroker->back2_y = py1;
601       }
602    } else {
603       stroker->back2_x = px2;
604       stroker->back2_y = py2;
605    }
606    stroker->back1_x = x;
607    stroker->back1_y = y;
608 
609    path_append_data(stroker->path,
610                     1,
611                     &cmds, &coords);
612 }
613 
create_round_join(struct stroker * stroker,VGfloat x1,VGfloat y1,VGfloat x2,VGfloat y2,VGfloat width,VGfloat height)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)
618 {
619    struct arc arc;
620    struct matrix matrix;
621 
622    matrix_load_identity(&matrix);
623 
624    /*stroker_emit_line_to(stroker, nx, ny);*/
625 
626    arc_init(&arc, VG_SCCWARC_TO,
627             x1, y1, x2, y2, width/2, height/2, 0);
628    arc_stroker_emit(&arc, stroker, &matrix);
629 }
630 
631 
create_joins(struct stroker * stroker,VGfloat focal_x,VGfloat focal_y,const VGfloat * next_line,enum line_join_mode join)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)
635 {
636 #if DEBUG_EMITS
637    debug_printf("create_joins: focal=[%f, %f], next_line=[%f, %f,%f, %f]\n",
638                 focal_x, focal_y,
639                 next_line[0], next_line[1], next_line[2], next_line[3]);
640 #endif
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]))
644       return;
645 
646    if (join == FlatJoin) {
647       stroker_emit_line_to(stroker, next_line[0], next_line[1]);
648    } else {
649       VGfloat prev_line[] = {stroker->back2_x, stroker->back2_y,
650                              stroker->back1_x, stroker->back1_y};
651 
652       VGfloat isect[2] = { 0 };
653       enum intersection_type type = line_intersect(prev_line, next_line, isect);
654 
655       if (join == SquareJoin) {
656          VGfloat offset = stroker->stroke_width / 2;
657          VGfloat l1[4] = {prev_line[0],
658                           prev_line[1],
659                           prev_line[2],
660                           prev_line[3]};
661          VGfloat l2[4] = {next_line[2],
662                           next_line[3],
663                           next_line[0],
664                           next_line[1]};
665 
666          line_translate(l1, line_dx(l1), line_dy(l1));
667          line_set_length(l1, offset);
668 
669          line_translate(l2, line_dx(l2), line_dy(l2));
670          line_set_length(l2, offset);
671 
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);
680 
681          if (type == BoundedIntersection ||
682              (angle > 90 && !floatsEqual(angle, 90.f))) {
683             stroker_emit_line_to(stroker, next_line[0], next_line[1]);
684             return;
685          }
686          create_round_join(stroker, prev_line[2], prev_line[3],
687                            next_line[0], next_line[1],
688                            offset * 2, offset * 2);
689 
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]};
697 
698          line_translate(l1, line_dx(l1), line_dy(l1));
699          line_set_length(l1, KAPPA * offset);
700 
701          /* normal between prev_line and focal */
702          line_translate(l2, -line_dy(l2), line_dx(l2));
703          line_set_length(l2, KAPPA * offset);
704 
705          stroker_emit_curve_to(stroker, l1[2], l1[3],
706                                l2[2], l2[3],
707                                l2[0], l2[1]);
708 
709          l2[0] = l2[0];
710          l2[1] = l2[1];
711          l2[2] = l2[0] - line_dx(l2);
712          l2[3] = l2[1] - line_dy(l2);
713 
714          line_translate(l1, next_line[0] - l1[0], next_line[1] - l1[1]);
715 
716          stroker_emit_curve_to(stroker,
717                                l2[2], l2[3],
718                                l1[2], l1[3],
719                                l1[0], l1[1]);
720       } else if (join == MiterJoin) {
721          VGfloat miter_line[4] = {stroker->back1_x, stroker->back1_y,
722                                   isect[0], isect[1]};
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);
727 
728          if (type == BoundedIntersection ||
729              (angle > 90 && !floatsEqual(angle, 90.f))) {
730             /*
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]);
735             return;
736          }
737 
738          if (type == NoIntersections || line_lengthv(miter_line) > sl) {
739             stroker_emit_line_to(stroker, next_line[0], next_line[1]);
740          } else {
741             stroker_emit_line_to(stroker, isect[0], isect[1]);
742             stroker_emit_line_to(stroker, next_line[0], next_line[1]);
743          }
744       } else {
745          debug_assert(!"create_joins bad join style");
746       }
747    }
748 }
749 
stroker_add_segment(struct stroker * stroker,VGPathCommand cmd,const VGfloat * coords,VGint num_coords)750 static void stroker_add_segment(struct stroker *stroker,
751                                 VGPathCommand cmd,
752                                 const VGfloat *coords,
753                                 VGint num_coords)
754 {
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;
760       data -= num_coords;
761       switch (cmd) {
762       case VG_MOVE_TO_ABS:
763          if (floatsEqual(coords[0], data[0]) &&
764              floatsEqual(coords[1], data[1]))
765             return;
766          break;
767       case VG_LINE_TO_ABS:
768          if (floatsEqual(coords[0], data[0]) &&
769              floatsEqual(coords[1], data[1]))
770             return;
771          break;
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]))
779             return;
780          break;
781       default:
782          debug_assert(!"Invalid stroke segment");
783       }
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;
788       data -= 2;
789       if (floatsEqual(coords[0], data[0]) &&
790           floatsEqual(coords[1], data[1]))
791          return;
792    }
793    stroker->last_cmd = cmd;
794    array_append_data(stroker->segments, &cmd, 1);
795    array_append_data(stroker->control_points, coords, num_coords);
796 }
797 
stroker_move_to(struct stroker * stroker,VGfloat x,VGfloat y)798 void stroker_move_to(struct stroker *stroker, VGfloat x, VGfloat y)
799 {
800    VGfloat coords[2] = {x, y};
801 #if STROKE_SEGMENTS
802    debug_printf("stroker_move_to(%f, %f)\n", x, y);
803 #endif
804 
805    if (stroker->segments->num_elements > 0)
806       stroker->process_subpath(stroker);
807 
808    array_reset(stroker->segments);
809    array_reset(stroker->control_points);
810 
811    stroker_add_segment(stroker, VG_MOVE_TO_ABS, coords, 2);
812 }
813 
stroker_line_to(struct stroker * stroker,VGfloat x,VGfloat y)814 void stroker_line_to(struct stroker *stroker, VGfloat x, VGfloat y)
815 {
816    VGfloat coords[] = {x, y};
817 
818 #if STROKE_SEGMENTS
819    debug_printf("stroker_line_to(%f, %f)\n", x, y);
820 #endif
821    if (!stroker->segments->num_elements)
822       stroker_add_segment(stroker, VG_MOVE_TO_ABS, zero_coords, 2);
823 
824    stroker_add_segment(stroker, VG_LINE_TO_ABS, coords, 2);
825 }
826 
stroker_curve_to(struct stroker * stroker,VGfloat px1,VGfloat py1,VGfloat px2,VGfloat py2,VGfloat x,VGfloat y)827 void stroker_curve_to(struct stroker *stroker, VGfloat px1, VGfloat py1,
828                       VGfloat px2, VGfloat py2,
829                       VGfloat x, VGfloat y)
830 {
831    VGfloat coords[] = {px1, py1,
832                        px2, py2,
833                        x, y};
834 #if STROKE_SEGMENTS
835    debug_printf("stroker_curve_to(%f, %f, %f, %f, %f, %f)\n",
836                 px1, py1, px2, py2, x, y);
837 #endif
838    if (!stroker->segments->num_elements)
839       stroker_add_segment(stroker, VG_MOVE_TO_ABS, zero_coords, 2);
840 
841    stroker_add_segment(stroker, VG_CUBIC_TO_ABS, coords, 6);
842 }
843 
is_segment_null(VGPathCommand cmd,VGfloat * coords,VGfloat * res)844 static INLINE VGboolean is_segment_null(VGPathCommand cmd,
845                                         VGfloat *coords,
846                                         VGfloat *res)
847 {
848    switch(cmd) {
849    case VG_MOVE_TO_ABS:
850    case VG_LINE_TO_ABS:
851       return floatsEqual(coords[0], res[0]) &&
852          floatsEqual(coords[1], res[1]);
853       break;
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]);
861       break;
862    default:
863       assert(0);
864    }
865    return VG_FALSE;
866 }
867 
vg_stroke_outline(struct stroke_iterator * it,struct stroker * stroker,VGboolean cap_first,VGfloat * start_tangent)868 static VGboolean vg_stroke_outline(struct stroke_iterator *it,
869                                 struct stroker *stroker,
870                                 VGboolean cap_first,
871                                 VGfloat *start_tangent)
872 {
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;
878    VGfloat offset;
879 
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);
883       prev[0] = 0.f;
884       prev[1] = 0.f;
885    }
886    stroke_itr_coords(it, start);
887 #if STROKE_DEBUG
888    debug_printf(" -> (side) [%.2f, %.2f]\n",
889                 start[0],
890                 start[1]);
891 #endif
892 
893    prev[0] = start[0];
894    prev[1] = start[1];
895 
896    offset = stroker->stroke_width / 2;
897 
898    if (!it->has_next(it)) {
899       /* single point */
900 
901       return VG_TRUE;
902    }
903 
904    while (it->has_next(it)) {
905       VGPathCommand cmd;
906       VGfloat coords[8];
907 
908       it->next(it);
909       cmd = stroke_itr_command(it);
910       stroke_itr_coords(it, coords);
911 
912       if (cmd == VG_LINE_TO_ABS) {
913          VGfloat line[4] = {prev[0], prev[1], coords[0], coords[1]};
914          VGfloat normal[4];
915          line_normal(line, normal);
916 
917 #if STROKE_DEBUG
918          debug_printf("\n ---> (side) lineto [%.2f, %.2f]\n", coords[0], coords[1]);
919 #endif
920          line_set_length(normal, offset);
921          line_translate(line, line_dx(normal), line_dy(normal));
922 
923          /* if we are starting a new subpath, move to correct starting point */
924          if (first) {
925             if (cap_first)
926                create_joins(stroker, prev[0], prev[1], line,
927                             stroker_cap_mode(stroker));
928             else
929                stroker_emit_move_to(stroker, line[0], line[1]);
930             memcpy(start_tangent, line,
931                    sizeof(VGfloat) * 4);
932             first = VG_FALSE;
933          } else {
934             create_joins(stroker, prev[0], prev[1], line,
935                          stroker_join_mode(stroker));
936          }
937 
938          /* add the stroke for this line */
939          stroker_emit_line_to(stroker, line[2], line[3]);
940          prev[0] = coords[0];
941          prev[1] = coords[1];
942       } else if (cmd == VG_CUBIC_TO_ABS) {
943 #if STROKE_DEBUG
944          debug_printf("\n ---> (side) cubicTo [%.2f, %.2f]\n",
945                 coords[4],
946                 coords[5]);
947 #endif
948          struct bezier bezier;
949          int count;
950 
951          bezier_init(&bezier,
952                      prev[0], prev[1], coords[0], coords[1],
953                      coords[2], coords[3], coords[4], coords[5]);
954 
955          count = bezier_translate_by_normal(&bezier,
956                                             offset_curves,
957                                             MAX_OFFSET,
958                                             offset,
959                                             curve_threshold);
960 
961          if (count) {
962             /* if we are starting a new subpath, move to correct starting point */
963             VGfloat tangent[4];
964             VGint i;
965 
966             bezier_start_tangent(&bezier, tangent);
967             line_translate(tangent,
968                            offset_curves[0].x1 - bezier.x1,
969                            offset_curves[0].y1 - bezier.y1);
970             if (first) {
971                VGfloat pt[2] = {offset_curves[0].x1,
972                                 offset_curves[0].y1};
973 
974                if (cap_first) {
975                   create_joins(stroker, prev[0], prev[1], tangent,
976                                stroker_cap_mode(stroker));
977                } else {
978                   stroker_emit_move_to(stroker, pt[0], pt[1]);
979                }
980                start_tangent[0] = tangent[0];
981                start_tangent[1] = tangent[1];
982                start_tangent[2] = tangent[2];
983                start_tangent[3] = tangent[3];
984                first = VG_FALSE;
985             } else {
986                create_joins(stroker, prev[0], prev[1], tangent,
987                             stroker_join_mode(stroker));
988             }
989 
990             /* add these beziers */
991             for (i = 0; i < count; ++i) {
992                struct bezier *bez = &offset_curves[i];
993                stroker_emit_curve_to(stroker,
994                                      bez->x2, bez->y2,
995                                      bez->x3, bez->y3,
996                                      bez->x4, bez->y4);
997             }
998          }
999 
1000          prev[0] = coords[4];
1001          prev[1] = coords[5];
1002       }
1003    }
1004 
1005    if (floatsEqual(start[0], prev[0]) &&
1006        floatsEqual(start[1], prev[1])) {
1007       /* closed subpath, join first and last point */
1008 #if STROKE_DEBUG
1009       debug_printf("\n stroker: closed subpath\n");
1010 #endif
1011       create_joins(stroker, prev[0], prev[1], start_tangent,
1012                    stroker_join_mode(stroker));
1013       return VG_TRUE;
1014    } else {
1015 #if STROKE_DEBUG
1016       debug_printf("\n stroker: open subpath\n");
1017 #endif
1018       return VG_FALSE;
1019    }
1020 #undef MAX_OFFSET
1021 }
1022 
stroker_process_subpath(struct stroker * stroker)1023 static void stroker_process_subpath(struct stroker *stroker)
1024 {
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);
1030 
1031    memset(fw_start_tangent, 0,
1032           sizeof(VGfloat)*4);
1033    memset(bw_start_tangent, 0,
1034           sizeof(VGfloat)*4);
1035 
1036    stroke_forward_iterator(&fwit, stroker->segments,
1037                            stroker->control_points);
1038    stroke_backward_iterator(&bwit, stroker->segments,
1039                             stroker->control_points);
1040 
1041    debug_assert(fwit.cmds[0] == VG_MOVE_TO_ABS);
1042 
1043    fwclosed = vg_stroke_outline(&fwit, stroker, VG_FALSE, fw_start_tangent);
1044    bwclosed = vg_stroke_outline(&bwit, stroker, !fwclosed, bw_start_tangent);
1045 
1046    if (!bwclosed)
1047       create_joins(stroker,
1048                    fwit.coords[0], fwit.coords[1], fw_start_tangent,
1049                    stroker_cap_mode(stroker));
1050    else {
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) {
1055          VGPathCommand cmd;
1056          VGfloat data[8], coords[8];
1057          struct stroke_iterator *it = &fwit;
1058 
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))
1066                return;
1067          } else {
1068             data[0] = coords[0];
1069             data[1] = coords[1];
1070          }
1071          while (it->has_next(it)) {
1072             it->next(it);
1073             cmd = stroke_itr_command(it);
1074             stroke_itr_coords(it, coords);
1075             if (!is_segment_null(cmd, coords, data))
1076                return;
1077          }
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,
1082                                  data[1] - offset);
1083             stroker_emit_line_to(stroker, data[0] + offset,
1084                                  data[1] - offset);
1085             stroker_emit_line_to(stroker, data[0] + offset,
1086                                  data[1] + offset);
1087             stroker_emit_line_to(stroker, data[0] - offset,
1088                                  data[1] + offset);
1089             stroker_emit_line_to(stroker, data[0] - offset,
1090                                  data[1] - 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];
1094             { /*circle */
1095                struct arc arc;
1096                struct matrix matrix;
1097                matrix_load_identity(&matrix);
1098 
1099                stroker_emit_move_to(stroker, cx + offset, cy);
1100                arc_init(&arc, VG_SCCWARC_TO,
1101                         cx + offset, cy,
1102                         cx - offset, cy,
1103                         offset, offset, 0);
1104                arc_stroker_emit(&arc, stroker, &matrix);
1105                arc_init(&arc, VG_SCCWARC_TO,
1106                          cx - offset, cy,
1107                          cx + offset, cy,
1108                          offset, offset, 0);
1109                arc_stroker_emit(&arc, stroker, &matrix);
1110             }
1111          }
1112       }
1113    }
1114 }
1115 
dash_pattern(struct dash_stroker * stroker,VGint idx)1116 static INLINE VGfloat dash_pattern(struct dash_stroker *stroker,
1117                                    VGint idx)
1118 {
1119    if (stroker->dash_pattern[idx] < 0)
1120       return 0.f;
1121    return stroker->dash_pattern[idx];
1122 }
1123 
dash_stroker_process_subpath(struct stroker * str)1124 static void dash_stroker_process_subpath(struct stroker *str)
1125 {
1126    struct dash_stroker *stroker = (struct dash_stroker *)str;
1127    VGfloat sum_length = 0;
1128    VGint i;
1129    VGint idash = 0;
1130    VGfloat pos = 0;
1131    VGfloat elen = 0;
1132    VGfloat doffset = stroker->dash_phase;
1133    VGfloat estart = 0;
1134    VGfloat estop = 0;
1135    VGfloat cline[4];
1136    struct stroke_iterator it;
1137    VGfloat prev[2];
1138    VGfloat move_to_pos[2];
1139    VGfloat line_to_pos[2];
1140 
1141    VGboolean has_move_to = VG_FALSE;
1142 
1143    stroke_flat_iterator(&it, stroker->base.segments,
1144                         stroker->base.control_points);
1145 
1146    stroke_itr_coords(&it, prev);
1147    move_to_pos[0] = prev[0];
1148    move_to_pos[1] = prev[1];
1149 
1150    debug_assert(stroker->dash_pattern_num > 0);
1151 
1152    for (i = 0; i < stroker->dash_pattern_num; ++i) {
1153       sum_length += dash_pattern(stroker, i);
1154    }
1155 
1156    if (floatIsZero(sum_length)) {
1157       return;
1158    }
1159 
1160    doffset -= floorf(doffset / sum_length) * sum_length;
1161 
1162    while (!floatIsZero(doffset) && doffset >= dash_pattern(stroker, idash)) {
1163       doffset -= dash_pattern(stroker, idash);
1164       idash = (idash + 1) % stroker->dash_pattern_num;
1165    }
1166 
1167    while (it.has_next(&it)) {
1168       VGPathCommand cmd;
1169       VGfloat coords[8];
1170       VGboolean done;
1171 
1172       it.next(&it);
1173       cmd = stroke_itr_command(&it);
1174       stroke_itr_coords(&it, coords);
1175 
1176       debug_assert(cmd == VG_LINE_TO_ABS);
1177       cline[0] = prev[0];
1178       cline[1] = prev[1];
1179       cline[2] = coords[0];
1180       cline[3] = coords[1];
1181 
1182       elen = line_lengthv(cline);
1183 
1184       estop = estart + elen;
1185 
1186       done = pos >= estop;
1187       while (!done) {
1188          VGfloat p2[2];
1189 
1190          VGint idash_incr = 0;
1191          VGboolean has_offset = doffset > 0;
1192          VGfloat dpos = pos + dash_pattern(stroker, idash) - doffset - estart;
1193 
1194          debug_assert(dpos >= 0);
1195 
1196          if (dpos > elen) { /* dash extends this line */
1197             doffset = dash_pattern(stroker, idash) - (dpos - elen);
1198             pos = estop;
1199             done = VG_TRUE;
1200             p2[0] = cline[2];
1201             p2[1] = cline[3];
1202          } else { /* Dash is on this line */
1203             line_point_at(cline, dpos/elen, p2);
1204             pos = dpos + estart;
1205             done = pos >= estop;
1206             idash_incr = 1;
1207             doffset = 0;
1208          }
1209 
1210          if (idash % 2 == 0) {
1211             line_to_pos[0] = p2[0];
1212             line_to_pos[1] = p2[1];
1213 
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;
1217             }
1218             stroker_line_to(&stroker->stroker, line_to_pos[0], line_to_pos[1]);
1219          } else {
1220             move_to_pos[0] = p2[0];
1221             move_to_pos[1] = p2[1];
1222          }
1223 
1224          idash = (idash + idash_incr) % stroker->dash_pattern_num;
1225       }
1226 
1227       estart = estop;
1228       prev[0] = coords[0];
1229       prev[1] = coords[1];
1230    }
1231 
1232    if (it.curve_poly) {
1233       polygon_destroy(it.curve_poly);
1234       it.curve_poly = 0;
1235    }
1236 
1237    stroker->base.path = stroker->stroker.path;
1238 }
1239 
default_begin(struct stroker * stroker)1240 static void default_begin(struct stroker *stroker)
1241 {
1242    array_reset(stroker->segments);
1243    array_reset(stroker->control_points);
1244 }
1245 
default_end(struct stroker * stroker)1246 static void default_end(struct stroker *stroker)
1247 {
1248    if (stroker->segments->num_elements > 0)
1249       stroker->process_subpath(stroker);
1250 }
1251 
1252 
dash_stroker_begin(struct stroker * stroker)1253 static void dash_stroker_begin(struct stroker *stroker)
1254 {
1255    struct dash_stroker *dasher =
1256       (struct dash_stroker *)stroker;
1257 
1258    default_begin(&dasher->stroker);
1259    default_begin(stroker);
1260 }
1261 
dash_stroker_end(struct stroker * stroker)1262 static void dash_stroker_end(struct stroker *stroker)
1263 {
1264    struct dash_stroker *dasher =
1265       (struct dash_stroker *)stroker;
1266 
1267    default_end(stroker);
1268    default_end(&dasher->stroker);
1269 }
1270 
stroker_init(struct stroker * stroker,struct vg_state * state)1271 void stroker_init(struct stroker *stroker,
1272                   struct vg_state *state)
1273 {
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;
1278 
1279    stroker->begin = default_begin;
1280    stroker->process_subpath = stroker_process_subpath;
1281    stroker->end = default_end;
1282 
1283    stroker->segments = array_create(sizeof(VGubyte));
1284    stroker->control_points = array_create(sizeof(VGfloat));
1285 
1286    stroker->back1_x = 0;
1287    stroker->back1_y = 0;
1288    stroker->back2_x = 0;
1289    stroker->back2_y = 0;
1290 
1291    stroker->path = path_create(VG_PATH_DATATYPE_F, 1.0f, 0.0f,
1292                                0, 0, VG_PATH_CAPABILITY_ALL);
1293 
1294    /* Initialize with an invalid value */
1295    stroker->last_cmd = (VGPathCommand)0;
1296 }
1297 
dash_stroker_init(struct stroker * str,struct vg_state * state)1298 void dash_stroker_init(struct stroker *str,
1299                        struct vg_state *state)
1300 {
1301    struct dash_stroker *stroker = (struct dash_stroker *)str;
1302    int i;
1303 
1304    stroker_init(str, state);
1305    stroker_init(&stroker->stroker, state);
1306 
1307    {
1308       int real_num = state->stroke.dash_pattern_num;
1309       if (real_num % 2)/* if odd, ignore the last one */
1310          --real_num;
1311       for (i = 0; i < real_num; ++i)
1312          stroker->dash_pattern[i] = state->stroke.dash_pattern[i].f;
1313       stroker->dash_pattern_num = real_num;
1314    }
1315 
1316    stroker->dash_phase = state->stroke.dash_phase.f;
1317    stroker->dash_phase_reset = state->stroke.dash_phase_reset;
1318 
1319    stroker->base.begin = dash_stroker_begin;
1320    stroker->base.process_subpath = dash_stroker_process_subpath;
1321    stroker->base.end = dash_stroker_end;
1322    path_destroy(stroker->base.path);
1323    stroker->base.path = NULL;
1324 }
1325 
stroker_begin(struct stroker * stroker)1326 void stroker_begin(struct stroker *stroker)
1327 {
1328    stroker->begin(stroker);
1329 }
1330 
stroker_end(struct stroker * stroker)1331 void stroker_end(struct stroker *stroker)
1332 {
1333    stroker->end(stroker);
1334 }
1335 
stroker_cleanup(struct stroker * stroker)1336 void stroker_cleanup(struct stroker *stroker)
1337 {
1338    array_destroy(stroker->segments);
1339    array_destroy(stroker->control_points);
1340 }
1341 
dash_stroker_cleanup(struct dash_stroker * stroker)1342 void dash_stroker_cleanup(struct dash_stroker *stroker)
1343 {
1344    /* if stroker->base.path is null means we never
1345     * processed a valid path so delete the temp one
1346     * we already created */
1347    if (!stroker->base.path)
1348       path_destroy(stroker->stroker.path);
1349    stroker_cleanup(&stroker->stroker);
1350    stroker_cleanup((struct stroker*)stroker);
1351 }
1352