stroker.c revision e55cf4854d594eae9ac3f6abd24f4e616eea894f
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
48static const VGfloat curve_threshold = 0.25f;
49
50static const VGfloat zero_coords[] = {0.f, 0.f};
51
52enum intersection_type {
53   NoIntersections,
54   BoundedIntersection,
55   UnboundedIntersection,
56};
57
58enum line_join_mode {
59   FlatJoin,
60   SquareJoin,
61   MiterJoin,
62   RoundJoin,
63   RoundCap
64};
65
66struct 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
85static VGPathCommand stroke_itr_command(struct stroke_iterator *itr)
86{
87   return itr->current_command(itr);
88}
89
90static void stroke_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
91{
92   itr->current_coords(itr, coords);
93}
94
95static 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
122static 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
149static VGPathCommand stroke_fw_current_command(struct stroke_iterator *it)
150{
151   return it->cmds[it->position];
152}
153
154static 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
164static VGboolean stroke_fw_has_next(struct stroke_iterator *itr)
165{
166   return itr->position < (itr->num_commands - 1);
167}
168
169static VGboolean stroke_bw_has_next(struct stroke_iterator *itr)
170{
171   return itr->position > 0;
172}
173
174static 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
185static 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
196static 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
207static 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
221static 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
241static 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
290static 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
297static 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
305static 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
331static 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
348static 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)
357static 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
386static INLINE VGfloat line_dx(const VGfloat *l)
387{
388   return l[2] - l[0];
389}
390
391static INLINE VGfloat line_dy(const VGfloat *l)
392{
393   return l[3] - l[1];
394}
395
396static 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
411static 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
421static 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
429static 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
448static 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
466static 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
475static 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
522static 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
536static 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
550void 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
567void 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
583void 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
614static 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
632static 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
750static 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
798void 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
814void 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
827void 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
844static 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
868static 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
1023static 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
1116static 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
1124static 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
1240static void default_begin(struct stroker *stroker)
1241{
1242   array_reset(stroker->segments);
1243   array_reset(stroker->control_points);
1244}
1245
1246static void default_end(struct stroker *stroker)
1247{
1248   if (stroker->segments->num_elements > 0)
1249      stroker->process_subpath(stroker);
1250}
1251
1252
1253static 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
1262static 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
1271void 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
1298void 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
1326void stroker_begin(struct stroker *stroker)
1327{
1328   stroker->begin(stroker);
1329}
1330
1331void stroker_end(struct stroker *stroker)
1332{
1333   stroker->end(stroker);
1334}
1335
1336void stroker_cleanup(struct stroker *stroker)
1337{
1338   array_destroy(stroker->segments);
1339   array_destroy(stroker->control_points);
1340}
1341
1342void 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