Merge branch 'mesa_7_5_branch' into mesa_7_6_branch
[mesa.git] / src / gallium / state_trackers / vega / stroker.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "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 "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
85 static VGPathCommand stroke_itr_command(struct stroke_iterator *itr)
86 {
87 return itr->current_command(itr);
88 }
89
90 static void stroke_itr_coords(struct stroke_iterator *itr, VGfloat *coords)
91 {
92 itr->current_coords(itr, coords);
93 }
94
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
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
149 static VGPathCommand stroke_fw_current_command(struct stroke_iterator *it)
150 {
151 return it->cmds[it->position];
152 }
153
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
164 static VGboolean stroke_fw_has_next(struct stroke_iterator *itr)
165 {
166 return itr->position < (itr->num_commands - 1);
167 }
168
169 static VGboolean stroke_bw_has_next(struct stroke_iterator *itr)
170 {
171 return itr->position > 0;
172 }
173
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
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
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
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
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
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
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
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
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
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
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)
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
386 static INLINE VGfloat line_dx(const VGfloat *l)
387 {
388 return l[2] - l[0];
389 }
390
391 static INLINE VGfloat line_dy(const VGfloat *l)
392 {
393 return l[3] - l[1];
394 }
395
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
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
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
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
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
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
475 static enum intersection_type line_intersect(const VGfloat *l1,
476 const VGfloat *l2,
477 float *intersection_point)
478 {
479 VGfloat isect[2];
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
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
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
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
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
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
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_ABS,
627 x1, y1, x2, y2, width/2, height/2, 0);
628 arc_stroker_emit(&arc, stroker, &matrix);
629 }
630
631
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];
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
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
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
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
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
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
868 static VGboolean vg_stroke_outline(struct stroke_iterator *it,
869 struct stroker *stroker,
870 VGboolean cap_first,
871 VGfloat *start_tangent)
872 {
873 const int 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 }
1021
1022 static void stroker_process_subpath(struct stroker *stroker)
1023 {
1024 VGboolean fwclosed, bwclosed;
1025 VGfloat fw_start_tangent[4], bw_start_tangent[4];
1026 struct stroke_iterator fwit;
1027 struct stroke_iterator bwit;
1028 debug_assert(stroker->segments->num_elements > 0);
1029
1030 memset(fw_start_tangent, 0,
1031 sizeof(VGfloat)*4);
1032 memset(bw_start_tangent, 0,
1033 sizeof(VGfloat)*4);
1034
1035 stroke_forward_iterator(&fwit, stroker->segments,
1036 stroker->control_points);
1037 stroke_backward_iterator(&bwit, stroker->segments,
1038 stroker->control_points);
1039
1040 debug_assert(fwit.cmds[0] == VG_MOVE_TO_ABS);
1041
1042 fwclosed = vg_stroke_outline(&fwit, stroker, VG_FALSE, fw_start_tangent);
1043 bwclosed = vg_stroke_outline(&bwit, stroker, !fwclosed, bw_start_tangent);
1044
1045 if (!bwclosed)
1046 create_joins(stroker,
1047 fwit.coords[0], fwit.coords[1], fw_start_tangent,
1048 stroker_cap_mode(stroker));
1049 else {
1050 /* hack to handle the requirement of the VG spec that says that strokes
1051 * of len==0 that have butt cap or round cap still need
1052 * to be rendered. (8.7.4 Stroke Generation) */
1053 if (stroker->segments->num_elements <= 3) {
1054 VGPathCommand cmd;
1055 VGfloat data[8], coords[8];
1056 struct stroke_iterator *it = &fwit;
1057
1058 stroke_forward_iterator(it, stroker->segments,
1059 stroker->control_points);
1060 cmd = stroke_itr_command(it);
1061 stroke_itr_coords(it, coords);
1062 if (cmd != VG_MOVE_TO_ABS) {
1063 memset(data, 0, sizeof(VGfloat) * 8);
1064 if (!is_segment_null(cmd, coords, data))
1065 return;
1066 } else {
1067 data[0] = coords[0];
1068 data[1] = coords[1];
1069 }
1070 while (it->has_next(it)) {
1071 it->next(it);
1072 cmd = stroke_itr_command(it);
1073 stroke_itr_coords(it, coords);
1074 if (!is_segment_null(cmd, coords, data))
1075 return;
1076 }
1077 /* generate the square/round cap */
1078 if (stroker->cap_style == VG_CAP_SQUARE) {
1079 VGfloat offset = stroker->stroke_width / 2;
1080 stroker_emit_move_to(stroker, data[0] - offset,
1081 data[1] - offset);
1082 stroker_emit_line_to(stroker, data[0] + offset,
1083 data[1] - offset);
1084 stroker_emit_line_to(stroker, data[0] + offset,
1085 data[1] + offset);
1086 stroker_emit_line_to(stroker, data[0] - offset,
1087 data[1] + offset);
1088 stroker_emit_line_to(stroker, data[0] - offset,
1089 data[1] - offset);
1090 } else if (stroker->cap_style == VG_CAP_ROUND) {
1091 VGfloat offset = stroker->stroke_width / 2;
1092 VGfloat cx = data[0], cy = data[1];
1093 { /*circle */
1094 struct arc arc;
1095 struct matrix matrix;
1096 matrix_load_identity(&matrix);
1097
1098 stroker_emit_move_to(stroker, cx + offset, cy);
1099 arc_init(&arc, VG_SCCWARC_TO_ABS,
1100 cx + offset, cy,
1101 cx - offset, cy,
1102 offset, offset, 0);
1103 arc_stroker_emit(&arc, stroker, &matrix);
1104 arc_init(&arc, VG_SCCWARC_TO_ABS,
1105 cx - offset, cy,
1106 cx + offset, cy,
1107 offset, offset, 0);
1108 arc_stroker_emit(&arc, stroker, &matrix);
1109 }
1110 }
1111 }
1112 }
1113 }
1114
1115 static INLINE VGfloat dash_pattern(struct dash_stroker *stroker,
1116 VGint idx)
1117 {
1118 if (stroker->dash_pattern[idx] < 0)
1119 return 0.f;
1120 return stroker->dash_pattern[idx];
1121 }
1122
1123 static void dash_stroker_process_subpath(struct stroker *str)
1124 {
1125 struct dash_stroker *stroker = (struct dash_stroker *)str;
1126 VGfloat sum_length = 0;
1127 VGint i;
1128 VGint idash = 0;
1129 VGfloat pos = 0;
1130 VGfloat elen = 0;
1131 VGfloat doffset = stroker->dash_phase;
1132 VGfloat estart = 0;
1133 VGfloat estop = 0;
1134 VGfloat cline[4];
1135 struct stroke_iterator it;
1136 VGfloat prev[2];
1137 VGfloat move_to_pos[2];
1138 VGfloat line_to_pos[2];
1139
1140 VGboolean has_move_to = VG_FALSE;
1141
1142 stroke_flat_iterator(&it, stroker->base.segments,
1143 stroker->base.control_points);
1144
1145 stroke_itr_coords(&it, prev);
1146 move_to_pos[0] = prev[0];
1147 move_to_pos[1] = prev[1];
1148
1149 debug_assert(stroker->dash_pattern_num > 0);
1150
1151 for (i = 0; i < stroker->dash_pattern_num; ++i) {
1152 sum_length += dash_pattern(stroker, i);
1153 }
1154
1155 if (floatIsZero(sum_length)) {
1156 return;
1157 }
1158
1159 doffset -= floorf(doffset / sum_length) * sum_length;
1160
1161 while (!floatIsZero(doffset) && doffset >= dash_pattern(stroker, idash)) {
1162 doffset -= dash_pattern(stroker, idash);
1163 idash = (idash + 1) % stroker->dash_pattern_num;
1164 }
1165
1166 while (it.has_next(&it)) {
1167 VGPathCommand cmd;
1168 VGfloat coords[8];
1169 VGboolean done;
1170
1171 it.next(&it);
1172 cmd = stroke_itr_command(&it);
1173 stroke_itr_coords(&it, coords);
1174
1175 debug_assert(cmd == VG_LINE_TO_ABS);
1176 cline[0] = prev[0];
1177 cline[1] = prev[1];
1178 cline[2] = coords[0];
1179 cline[3] = coords[1];
1180
1181 elen = line_lengthv(cline);
1182
1183 estop = estart + elen;
1184
1185 done = pos >= estop;
1186 while (!done) {
1187 VGfloat p2[2];
1188
1189 VGint idash_incr = 0;
1190 VGboolean has_offset = doffset > 0;
1191 VGfloat dpos = pos + dash_pattern(stroker, idash) - doffset - estart;
1192
1193 debug_assert(dpos >= 0);
1194
1195 if (dpos > elen) { /* dash extends this line */
1196 doffset = dash_pattern(stroker, idash) - (dpos - elen);
1197 pos = estop;
1198 done = VG_TRUE;
1199 p2[0] = cline[2];
1200 p2[1] = cline[3];
1201 } else { /* Dash is on this line */
1202 line_point_at(cline, dpos/elen, p2);
1203 pos = dpos + estart;
1204 done = pos >= estop;
1205 idash_incr = 1;
1206 doffset = 0;
1207 }
1208
1209 if (idash % 2 == 0) {
1210 line_to_pos[0] = p2[0];
1211 line_to_pos[1] = p2[1];
1212
1213 if (!has_offset || !has_move_to) {
1214 stroker_move_to(&stroker->stroker, move_to_pos[0], move_to_pos[1]);
1215 has_move_to = VG_TRUE;
1216 }
1217 stroker_line_to(&stroker->stroker, line_to_pos[0], line_to_pos[1]);
1218 } else {
1219 move_to_pos[0] = p2[0];
1220 move_to_pos[1] = p2[1];
1221 }
1222
1223 idash = (idash + idash_incr) % stroker->dash_pattern_num;
1224 }
1225
1226 estart = estop;
1227 prev[0] = coords[0];
1228 prev[1] = coords[1];
1229 }
1230
1231 if (it.curve_poly) {
1232 polygon_destroy(it.curve_poly);
1233 it.curve_poly = 0;
1234 }
1235
1236 stroker->base.path = stroker->stroker.path;
1237 }
1238
1239 static void default_begin(struct stroker *stroker)
1240 {
1241 array_reset(stroker->segments);
1242 array_reset(stroker->control_points);
1243 }
1244
1245 static void default_end(struct stroker *stroker)
1246 {
1247 if (stroker->segments->num_elements > 0)
1248 stroker->process_subpath(stroker);
1249 }
1250
1251
1252 static void dash_stroker_begin(struct stroker *stroker)
1253 {
1254 struct dash_stroker *dasher =
1255 (struct dash_stroker *)stroker;
1256
1257 default_begin(&dasher->stroker);
1258 default_begin(stroker);
1259 }
1260
1261 static void dash_stroker_end(struct stroker *stroker)
1262 {
1263 struct dash_stroker *dasher =
1264 (struct dash_stroker *)stroker;
1265
1266 default_end(stroker);
1267 default_end(&dasher->stroker);
1268 }
1269
1270 void stroker_init(struct stroker *stroker,
1271 struct vg_state *state)
1272 {
1273 stroker->stroke_width = state->stroke.line_width.f;
1274 stroker->miter_limit = state->stroke.miter_limit.f;
1275 stroker->cap_style = state->stroke.cap_style;
1276 stroker->join_style = state->stroke.join_style;
1277
1278 stroker->begin = default_begin;
1279 stroker->process_subpath = stroker_process_subpath;
1280 stroker->end = default_end;
1281
1282 stroker->segments = array_create(sizeof(VGubyte));
1283 stroker->control_points = array_create(sizeof(VGfloat));
1284
1285 stroker->back1_x = 0;
1286 stroker->back1_y = 0;
1287 stroker->back2_x = 0;
1288 stroker->back2_y = 0;
1289
1290 stroker->path = path_create(VG_PATH_DATATYPE_F, 1.0f, 0.0f,
1291 0, 0, VG_PATH_CAPABILITY_ALL);
1292
1293 stroker->last_cmd = VG_CLOSE_PATH;
1294 }
1295
1296 void dash_stroker_init(struct stroker *str,
1297 struct vg_state *state)
1298 {
1299 struct dash_stroker *stroker = (struct dash_stroker *)str;
1300 int i;
1301
1302 stroker_init(str, state);
1303 stroker_init(&stroker->stroker, state);
1304
1305 {
1306 int real_num = state->stroke.dash_pattern_num;
1307 if (real_num % 2)/* if odd, ignore the last one */
1308 --real_num;
1309 for (i = 0; i < real_num; ++i)
1310 stroker->dash_pattern[i] = state->stroke.dash_pattern[i].f;
1311 stroker->dash_pattern_num = real_num;
1312 }
1313
1314 stroker->dash_phase = state->stroke.dash_phase.f;
1315 stroker->dash_phase_reset = state->stroke.dash_phase_reset;
1316
1317 stroker->base.begin = dash_stroker_begin;
1318 stroker->base.process_subpath = dash_stroker_process_subpath;
1319 stroker->base.end = dash_stroker_end;
1320 path_destroy(stroker->base.path);
1321 stroker->base.path = NULL;
1322 }
1323
1324 void stroker_begin(struct stroker *stroker)
1325 {
1326 stroker->begin(stroker);
1327 }
1328
1329 void stroker_end(struct stroker *stroker)
1330 {
1331 stroker->end(stroker);
1332 }
1333
1334 void stroker_cleanup(struct stroker *stroker)
1335 {
1336 array_destroy(stroker->segments);
1337 array_destroy(stroker->control_points);
1338 }
1339
1340 void dash_stroker_cleanup(struct dash_stroker *stroker)
1341 {
1342 /* if stroker->base.path is null means we never
1343 * processed a valid path so delete the temp one
1344 * we already created */
1345 if (!stroker->base.path)
1346 path_destroy(stroker->stroker.path);
1347 stroker_cleanup(&stroker->stroker);
1348 stroker_cleanup((struct stroker*)stroker);
1349 }