1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2007,2008,2009,2010,2013 Intel Corporation.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use, copy,
12 * modify, merge, publish, distribute, sublicense, and/or sell copies
13 * of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 * Authors:
29 * Ivan Leben <ivan@openedhand.com>
30 * Øyvind Kolås <pippin@linux.intel.com>
31 * Neil Roberts <neil@linux.intel.com>
32 * Robert Bragg <robert@linux.intel.com>
33 */
34
35 #include "config.h"
36
37 #include "cogl-util.h"
38 #include "cogl-object.h"
39 #include "cogl-context-private.h"
40 #include "cogl-journal-private.h"
41 #include "cogl-pipeline-private.h"
42 #include "cogl-framebuffer-private.h"
43 #include "cogl-primitive-private.h"
44 #include "cogl-texture-private.h"
45 #include "cogl-primitives-private.h"
46 #include "cogl-private.h"
47 #include "cogl-attribute-private.h"
48 #include "cogl1-context.h"
49 #include "tesselator/tesselator.h"
50
51 #include "cogl-path/cogl-path.h"
52 #include "cogl-path-private.h"
53 #include "cogl-gtype-private.h"
54
55 #include <string.h>
56 #include <math.h>
57
58 #define _COGL_MAX_BEZ_RECURSE_DEPTH 16
59
60 static void _cogl_path_free (CoglPath *path);
61
62 static void _cogl_path_build_fill_attribute_buffer (CoglPath *path);
63 static CoglPrimitive *_cogl_path_get_fill_primitive (CoglPath *path);
64 static void _cogl_path_build_stroke_attribute_buffer (CoglPath *path);
65
66 COGL_OBJECT_DEFINE (Path, path);
67 COGL_GTYPE_DEFINE_CLASS (Path, path);
68
69 static void
_cogl_path_data_clear_vbos(CoglPathData * data)70 _cogl_path_data_clear_vbos (CoglPathData *data)
71 {
72 int i;
73
74 if (data->fill_attribute_buffer)
75 {
76 cogl_object_unref (data->fill_attribute_buffer);
77 cogl_object_unref (data->fill_vbo_indices);
78
79 for (i = 0; i < COGL_PATH_N_ATTRIBUTES; i++)
80 cogl_object_unref (data->fill_attributes[i]);
81
82 data->fill_attribute_buffer = NULL;
83 }
84
85 if (data->fill_primitive)
86 {
87 cogl_object_unref (data->fill_primitive);
88 data->fill_primitive = NULL;
89 }
90
91 if (data->stroke_attribute_buffer)
92 {
93 cogl_object_unref (data->stroke_attribute_buffer);
94
95 for (i = 0; i < data->stroke_n_attributes; i++)
96 cogl_object_unref (data->stroke_attributes[i]);
97
98 g_free (data->stroke_attributes);
99
100 data->stroke_attribute_buffer = NULL;
101 }
102 }
103
104 static void
_cogl_path_data_unref(CoglPathData * data)105 _cogl_path_data_unref (CoglPathData *data)
106 {
107 if (--data->ref_count <= 0)
108 {
109 _cogl_path_data_clear_vbos (data);
110
111 g_array_free (data->path_nodes, TRUE);
112
113 g_slice_free (CoglPathData, data);
114 }
115 }
116
117 static void
_cogl_path_modify(CoglPath * path)118 _cogl_path_modify (CoglPath *path)
119 {
120 /* This needs to be called whenever the path is about to be modified
121 to implement copy-on-write semantics */
122
123 /* If there is more than one path using the data then we need to
124 copy the data instead */
125 if (path->data->ref_count != 1)
126 {
127 CoglPathData *old_data = path->data;
128
129 path->data = g_slice_dup (CoglPathData, old_data);
130 path->data->path_nodes = g_array_new (FALSE, FALSE,
131 sizeof (CoglPathNode));
132 g_array_append_vals (path->data->path_nodes,
133 old_data->path_nodes->data,
134 old_data->path_nodes->len);
135
136 path->data->fill_attribute_buffer = NULL;
137 path->data->fill_primitive = NULL;
138 path->data->stroke_attribute_buffer = NULL;
139 path->data->ref_count = 1;
140
141 _cogl_path_data_unref (old_data);
142 }
143 else
144 /* The path is altered so the vbos will now be invalid */
145 _cogl_path_data_clear_vbos (path->data);
146 }
147
148 void
cogl2_path_set_fill_rule(CoglPath * path,CoglPathFillRule fill_rule)149 cogl2_path_set_fill_rule (CoglPath *path,
150 CoglPathFillRule fill_rule)
151 {
152 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
153
154 if (path->data->fill_rule != fill_rule)
155 {
156 _cogl_path_modify (path);
157
158 path->data->fill_rule = fill_rule;
159 }
160 }
161
162 CoglPathFillRule
cogl2_path_get_fill_rule(CoglPath * path)163 cogl2_path_get_fill_rule (CoglPath *path)
164 {
165 _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (path), COGL_PATH_FILL_RULE_NON_ZERO);
166
167 return path->data->fill_rule;
168 }
169
170 static void
_cogl_path_add_node(CoglPath * path,CoglBool new_sub_path,float x,float y)171 _cogl_path_add_node (CoglPath *path,
172 CoglBool new_sub_path,
173 float x,
174 float y)
175 {
176 CoglPathNode new_node;
177 CoglPathData *data;
178
179 _cogl_path_modify (path);
180
181 data = path->data;
182
183 new_node.x = x;
184 new_node.y = y;
185 new_node.path_size = 0;
186
187 if (new_sub_path || data->path_nodes->len == 0)
188 data->last_path = data->path_nodes->len;
189
190 g_array_append_val (data->path_nodes, new_node);
191
192 g_array_index (data->path_nodes, CoglPathNode, data->last_path).path_size++;
193
194 if (data->path_nodes->len == 1)
195 {
196 data->path_nodes_min.x = data->path_nodes_max.x = x;
197 data->path_nodes_min.y = data->path_nodes_max.y = y;
198 }
199 else
200 {
201 if (x < data->path_nodes_min.x)
202 data->path_nodes_min.x = x;
203 if (x > data->path_nodes_max.x)
204 data->path_nodes_max.x = x;
205 if (y < data->path_nodes_min.y)
206 data->path_nodes_min.y = y;
207 if (y > data->path_nodes_max.y)
208 data->path_nodes_max.y = y;
209 }
210
211 /* Once the path nodes have been modified then we'll assume it's no
212 longer a rectangle. cogl2_path_rectangle will set this back to
213 TRUE if this has been called from there */
214 data->is_rectangle = FALSE;
215 }
216
217 static void
_cogl_path_stroke_nodes(CoglPath * path,CoglFramebuffer * framebuffer,CoglPipeline * pipeline)218 _cogl_path_stroke_nodes (CoglPath *path,
219 CoglFramebuffer *framebuffer,
220 CoglPipeline *pipeline)
221 {
222 CoglPathData *data;
223 CoglPipeline *copy = NULL;
224 unsigned int path_start;
225 int path_num = 0;
226 CoglPathNode *node;
227
228 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
229 _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
230 _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
231
232 data = path->data;
233
234 if (data->path_nodes->len == 0)
235 return;
236
237 if (cogl_pipeline_get_n_layers (pipeline) != 0)
238 {
239 copy = cogl_pipeline_copy (pipeline);
240 _cogl_pipeline_prune_to_n_layers (copy, 0);
241 pipeline = copy;
242 }
243
244 _cogl_path_build_stroke_attribute_buffer (path);
245
246 for (path_start = 0;
247 path_start < data->path_nodes->len;
248 path_start += node->path_size)
249 {
250 CoglPrimitive *primitive;
251
252 node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
253
254 primitive =
255 cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP,
256 node->path_size,
257 &data->stroke_attributes[path_num],
258 1);
259 cogl_primitive_draw (primitive, framebuffer, pipeline);
260 cogl_object_unref (primitive);
261
262 path_num++;
263 }
264
265 if (copy)
266 cogl_object_unref (copy);
267 }
268
269 void
_cogl_path_get_bounds(CoglPath * path,float * min_x,float * min_y,float * max_x,float * max_y)270 _cogl_path_get_bounds (CoglPath *path,
271 float *min_x,
272 float *min_y,
273 float *max_x,
274 float *max_y)
275 {
276 CoglPathData *data = path->data;
277
278 if (data->path_nodes->len == 0)
279 {
280 *min_x = 0.0f;
281 *min_y = 0.0f;
282 *max_x = 0.0f;
283 *max_y = 0.0f;
284 }
285 else
286 {
287 *min_x = data->path_nodes_min.x;
288 *min_y = data->path_nodes_min.y;
289 *max_x = data->path_nodes_max.x;
290 *max_y = data->path_nodes_max.y;
291 }
292 }
293
294 static void
_cogl_path_fill_nodes_with_clipped_rectangle(CoglPath * path,CoglFramebuffer * framebuffer,CoglPipeline * pipeline)295 _cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path,
296 CoglFramebuffer *framebuffer,
297 CoglPipeline *pipeline)
298 {
299 /* We need at least three stencil bits to combine clips */
300 if (_cogl_framebuffer_get_stencil_bits (framebuffer) >= 3)
301 {
302 static CoglBool seen_warning = FALSE;
303
304 if (!seen_warning)
305 {
306 g_warning ("Paths can not be filled using materials with "
307 "sliced textures unless there is a stencil "
308 "buffer");
309 seen_warning = TRUE;
310 }
311 }
312
313 cogl_framebuffer_push_path_clip (framebuffer, path);
314 cogl_framebuffer_draw_rectangle (framebuffer,
315 pipeline,
316 path->data->path_nodes_min.x,
317 path->data->path_nodes_min.y,
318 path->data->path_nodes_max.x,
319 path->data->path_nodes_max.y);
320 cogl_framebuffer_pop_clip (framebuffer);
321 }
322
323 static CoglBool
validate_layer_cb(CoglPipelineLayer * layer,void * user_data)324 validate_layer_cb (CoglPipelineLayer *layer, void *user_data)
325 {
326 CoglBool *needs_fallback = user_data;
327 CoglTexture *texture = _cogl_pipeline_layer_get_texture (layer);
328
329 /* If any of the layers of the current pipeline contain sliced
330 * textures or textures with waste then it won't work to draw the
331 * path directly. Instead we fallback to pushing the path as a clip
332 * on the clip-stack and drawing the path's bounding rectangle
333 * instead.
334 */
335
336 if (texture != NULL && (cogl_texture_is_sliced (texture) ||
337 !_cogl_texture_can_hardware_repeat (texture)))
338 *needs_fallback = TRUE;
339
340 return !*needs_fallback;
341 }
342
343 static void
_cogl_path_fill_nodes(CoglPath * path,CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglDrawFlags flags)344 _cogl_path_fill_nodes (CoglPath *path,
345 CoglFramebuffer *framebuffer,
346 CoglPipeline *pipeline,
347 CoglDrawFlags flags)
348 {
349 if (path->data->path_nodes->len == 0)
350 return;
351
352 /* If the path is a simple rectangle then we can divert to using
353 cogl_framebuffer_draw_rectangle which should be faster because it
354 can go through the journal instead of uploading the geometry just
355 for two triangles */
356 if (path->data->is_rectangle && flags == 0)
357 {
358 float x_1, y_1, x_2, y_2;
359
360 _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
361 cogl_framebuffer_draw_rectangle (framebuffer,
362 pipeline,
363 x_1, y_1,
364 x_2, y_2);
365 }
366 else
367 {
368 CoglBool needs_fallback = FALSE;
369 CoglPrimitive *primitive;
370
371 _cogl_pipeline_foreach_layer_internal (pipeline,
372 validate_layer_cb,
373 &needs_fallback);
374 if (needs_fallback)
375 {
376 _cogl_path_fill_nodes_with_clipped_rectangle (path,
377 framebuffer,
378 pipeline);
379 return;
380 }
381
382 primitive = _cogl_path_get_fill_primitive (path);
383
384 _cogl_primitive_draw (primitive, framebuffer, pipeline, flags);
385 }
386 }
387
388 /* TODO: Update to the protoype used in the Cogl master branch.
389 * This is experimental API but not in sync with the cogl_path_fill()
390 * api in Cogl master which takes explicit framebuffer and pipeline
391 * arguments */
392 void
cogl2_path_fill(CoglPath * path)393 cogl2_path_fill (CoglPath *path)
394 {
395 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
396
397 _cogl_path_fill_nodes (path,
398 cogl_get_draw_framebuffer (),
399 cogl_get_source (),
400 0 /* flags */);
401 }
402
403 /* TODO: Update to the protoype used in the Cogl master branch.
404 * This is experimental API but not in sync with the cogl_path_fill()
405 * api in Cogl master which takes explicit framebuffer and pipeline
406 * arguments */
407 void
cogl2_path_stroke(CoglPath * path)408 cogl2_path_stroke (CoglPath *path)
409 {
410 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
411
412 if (path->data->path_nodes->len == 0)
413 return;
414
415 _cogl_path_stroke_nodes (path,
416 cogl_get_draw_framebuffer (),
417 cogl_get_source ());
418 }
419
420 void
cogl2_path_move_to(CoglPath * path,float x,float y)421 cogl2_path_move_to (CoglPath *path,
422 float x,
423 float y)
424 {
425 CoglPathData *data;
426
427 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
428
429 _cogl_path_add_node (path, TRUE, x, y);
430
431 data = path->data;
432
433 data->path_start.x = x;
434 data->path_start.y = y;
435
436 data->path_pen = data->path_start;
437 }
438
439 void
cogl2_path_rel_move_to(CoglPath * path,float x,float y)440 cogl2_path_rel_move_to (CoglPath *path,
441 float x,
442 float y)
443 {
444 CoglPathData *data;
445
446 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
447
448 data = path->data;
449
450 cogl2_path_move_to (path,
451 data->path_pen.x + x,
452 data->path_pen.y + y);
453 }
454
455 void
cogl2_path_line_to(CoglPath * path,float x,float y)456 cogl2_path_line_to (CoglPath *path,
457 float x,
458 float y)
459 {
460 CoglPathData *data;
461
462 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
463
464 _cogl_path_add_node (path, FALSE, x, y);
465
466 data = path->data;
467
468 data->path_pen.x = x;
469 data->path_pen.y = y;
470 }
471
472 void
cogl2_path_rel_line_to(CoglPath * path,float x,float y)473 cogl2_path_rel_line_to (CoglPath *path,
474 float x,
475 float y)
476 {
477 CoglPathData *data;
478
479 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
480
481 data = path->data;
482
483 cogl2_path_line_to (path,
484 data->path_pen.x + x,
485 data->path_pen.y + y);
486 }
487
488 void
cogl2_path_close(CoglPath * path)489 cogl2_path_close (CoglPath *path)
490 {
491 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
492
493 _cogl_path_add_node (path, FALSE, path->data->path_start.x,
494 path->data->path_start.y);
495
496 path->data->path_pen = path->data->path_start;
497 }
498
499 void
cogl2_path_line(CoglPath * path,float x_1,float y_1,float x_2,float y_2)500 cogl2_path_line (CoglPath *path,
501 float x_1,
502 float y_1,
503 float x_2,
504 float y_2)
505 {
506 cogl2_path_move_to (path, x_1, y_1);
507 cogl2_path_line_to (path, x_2, y_2);
508 }
509
510 void
cogl2_path_polyline(CoglPath * path,const float * coords,int num_points)511 cogl2_path_polyline (CoglPath *path,
512 const float *coords,
513 int num_points)
514 {
515 int c = 0;
516
517 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
518
519 cogl2_path_move_to (path, coords[0], coords[1]);
520
521 for (c = 1; c < num_points; ++c)
522 cogl2_path_line_to (path, coords[2*c], coords[2*c+1]);
523 }
524
525 void
cogl2_path_polygon(CoglPath * path,const float * coords,int num_points)526 cogl2_path_polygon (CoglPath *path,
527 const float *coords,
528 int num_points)
529 {
530 cogl2_path_polyline (path, coords, num_points);
531 cogl2_path_close (path);
532 }
533
534 void
cogl2_path_rectangle(CoglPath * path,float x_1,float y_1,float x_2,float y_2)535 cogl2_path_rectangle (CoglPath *path,
536 float x_1,
537 float y_1,
538 float x_2,
539 float y_2)
540 {
541 CoglBool is_rectangle;
542
543 /* If the path was previously empty and the rectangle isn't mirrored
544 then we'll record that this is a simple rectangle path so that we
545 can optimise it */
546 is_rectangle = (path->data->path_nodes->len == 0 &&
547 x_2 >= x_1 &&
548 y_2 >= y_1);
549
550 cogl2_path_move_to (path, x_1, y_1);
551 cogl2_path_line_to (path, x_2, y_1);
552 cogl2_path_line_to (path, x_2, y_2);
553 cogl2_path_line_to (path, x_1, y_2);
554 cogl2_path_close (path);
555
556 path->data->is_rectangle = is_rectangle;
557 }
558
559 CoglBool
_cogl_path_is_rectangle(CoglPath * path)560 _cogl_path_is_rectangle (CoglPath *path)
561 {
562 return path->data->is_rectangle;
563 }
564
565 static void
_cogl_path_arc(CoglPath * path,float center_x,float center_y,float radius_x,float radius_y,float angle_1,float angle_2,float angle_step,unsigned int move_first)566 _cogl_path_arc (CoglPath *path,
567 float center_x,
568 float center_y,
569 float radius_x,
570 float radius_y,
571 float angle_1,
572 float angle_2,
573 float angle_step,
574 unsigned int move_first)
575 {
576 float a = 0x0;
577 float cosa = 0x0;
578 float sina = 0x0;
579 float px = 0x0;
580 float py = 0x0;
581
582 /* Fix invalid angles */
583
584 if (angle_1 == angle_2 || angle_step == 0x0)
585 return;
586
587 if (angle_step < 0x0)
588 angle_step = -angle_step;
589
590 /* Walk the arc by given step */
591
592 a = angle_1;
593 while (a != angle_2)
594 {
595 cosa = cosf (a * (G_PI/180.0));
596 sina = sinf (a * (G_PI/180.0));
597
598 px = center_x + (cosa * radius_x);
599 py = center_y + (sina * radius_y);
600
601 if (a == angle_1 && move_first)
602 cogl2_path_move_to (path, px, py);
603 else
604 cogl2_path_line_to (path, px, py);
605
606 if (G_LIKELY (angle_2 > angle_1))
607 {
608 a += angle_step;
609 if (a > angle_2)
610 a = angle_2;
611 }
612 else
613 {
614 a -= angle_step;
615 if (a < angle_2)
616 a = angle_2;
617 }
618 }
619
620 /* Make sure the final point is drawn */
621
622 cosa = cosf (angle_2 * (G_PI/180.0));
623 sina = sinf (angle_2 * (G_PI/180.0));
624
625 px = center_x + (cosa * radius_x);
626 py = center_y + (sina * radius_y);
627
628 cogl2_path_line_to (path, px, py);
629 }
630
631 void
cogl2_path_arc(CoglPath * path,float center_x,float center_y,float radius_x,float radius_y,float angle_1,float angle_2)632 cogl2_path_arc (CoglPath *path,
633 float center_x,
634 float center_y,
635 float radius_x,
636 float radius_y,
637 float angle_1,
638 float angle_2)
639 {
640 float angle_step = 10;
641
642 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
643
644 /* it is documented that a move to is needed to create a freestanding
645 * arc
646 */
647 _cogl_path_arc (path,
648 center_x, center_y,
649 radius_x, radius_y,
650 angle_1, angle_2,
651 angle_step, 0 /* no move */);
652 }
653
654
655 static void
_cogl_path_rel_arc(CoglPath * path,float center_x,float center_y,float radius_x,float radius_y,float angle_1,float angle_2,float angle_step)656 _cogl_path_rel_arc (CoglPath *path,
657 float center_x,
658 float center_y,
659 float radius_x,
660 float radius_y,
661 float angle_1,
662 float angle_2,
663 float angle_step)
664 {
665 CoglPathData *data;
666
667 data = path->data;
668
669 _cogl_path_arc (path,
670 data->path_pen.x + center_x,
671 data->path_pen.y + center_y,
672 radius_x, radius_y,
673 angle_1, angle_2,
674 angle_step, 0 /* no move */);
675 }
676
677 void
cogl2_path_ellipse(CoglPath * path,float center_x,float center_y,float radius_x,float radius_y)678 cogl2_path_ellipse (CoglPath *path,
679 float center_x,
680 float center_y,
681 float radius_x,
682 float radius_y)
683 {
684 float angle_step = 10;
685
686 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
687
688 /* FIXME: if shows to be slow might be optimized
689 * by mirroring just a quarter of it */
690
691 _cogl_path_arc (path,
692 center_x, center_y,
693 radius_x, radius_y,
694 0, 360,
695 angle_step, 1 /* move first */);
696
697 cogl2_path_close (path);
698 }
699
700 void
cogl2_path_round_rectangle(CoglPath * path,float x_1,float y_1,float x_2,float y_2,float radius,float arc_step)701 cogl2_path_round_rectangle (CoglPath *path,
702 float x_1,
703 float y_1,
704 float x_2,
705 float y_2,
706 float radius,
707 float arc_step)
708 {
709 float inner_width = x_2 - x_1 - radius * 2;
710 float inner_height = y_2 - y_1 - radius * 2;
711
712 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
713
714 cogl2_path_move_to (path, x_1, y_1 + radius);
715 _cogl_path_rel_arc (path,
716 radius, 0,
717 radius, radius,
718 180,
719 270,
720 arc_step);
721
722 cogl2_path_line_to (path,
723 path->data->path_pen.x + inner_width,
724 path->data->path_pen.y);
725 _cogl_path_rel_arc (path,
726 0, radius,
727 radius, radius,
728 -90,
729 0,
730 arc_step);
731
732 cogl2_path_line_to (path,
733 path->data->path_pen.x,
734 path->data->path_pen.y + inner_height);
735
736 _cogl_path_rel_arc (path,
737 -radius, 0,
738 radius, radius,
739 0,
740 90,
741 arc_step);
742
743 cogl2_path_line_to (path,
744 path->data->path_pen.x - inner_width,
745 path->data->path_pen.y);
746 _cogl_path_rel_arc (path,
747 0, -radius,
748 radius, radius,
749 90,
750 180,
751 arc_step);
752
753 cogl2_path_close (path);
754 }
755
756 static void
_cogl_path_bezier3_sub(CoglPath * path,CoglBezCubic * cubic)757 _cogl_path_bezier3_sub (CoglPath *path,
758 CoglBezCubic *cubic)
759 {
760 CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH];
761 CoglBezCubic *cleft;
762 CoglBezCubic *cright;
763 CoglBezCubic *c;
764 floatVec2 dif1;
765 floatVec2 dif2;
766 floatVec2 mm;
767 floatVec2 c1;
768 floatVec2 c2;
769 floatVec2 c3;
770 floatVec2 c4;
771 floatVec2 c5;
772 int cindex;
773
774 /* Put first curve on stack */
775 cubics[0] = *cubic;
776 cindex = 0;
777
778 while (cindex >= 0)
779 {
780 c = &cubics[cindex];
781
782
783 /* Calculate distance of control points from their
784 * counterparts on the line between end points */
785 dif1.x = (c->p2.x * 3) - (c->p1.x * 2) - c->p4.x;
786 dif1.y = (c->p2.y * 3) - (c->p1.y * 2) - c->p4.y;
787 dif2.x = (c->p3.x * 3) - (c->p4.x * 2) - c->p1.x;
788 dif2.y = (c->p3.y * 3) - (c->p4.y * 2) - c->p1.y;
789
790 if (dif1.x < 0)
791 dif1.x = -dif1.x;
792 if (dif1.y < 0)
793 dif1.y = -dif1.y;
794 if (dif2.x < 0)
795 dif2.x = -dif2.x;
796 if (dif2.y < 0)
797 dif2.y = -dif2.y;
798
799
800 /* Pick the greatest of two distances */
801 if (dif1.x < dif2.x) dif1.x = dif2.x;
802 if (dif1.y < dif2.y) dif1.y = dif2.y;
803
804 /* Cancel if the curve is flat enough */
805 if (dif1.x + dif1.y <= 1.0 ||
806 cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1)
807 {
808 /* Add subdivision point (skip last) */
809 if (cindex == 0)
810 return;
811
812 _cogl_path_add_node (path, FALSE, c->p4.x, c->p4.y);
813
814 --cindex;
815
816 continue;
817 }
818
819 /* Left recursion goes on top of stack! */
820 cright = c; cleft = &cubics[++cindex];
821
822 /* Subdivide into 2 sub-curves */
823 c1.x = ((c->p1.x + c->p2.x) / 2);
824 c1.y = ((c->p1.y + c->p2.y) / 2);
825 mm.x = ((c->p2.x + c->p3.x) / 2);
826 mm.y = ((c->p2.y + c->p3.y) / 2);
827 c5.x = ((c->p3.x + c->p4.x) / 2);
828 c5.y = ((c->p3.y + c->p4.y) / 2);
829
830 c2.x = ((c1.x + mm.x) / 2);
831 c2.y = ((c1.y + mm.y) / 2);
832 c4.x = ((mm.x + c5.x) / 2);
833 c4.y = ((mm.y + c5.y) / 2);
834
835 c3.x = ((c2.x + c4.x) / 2);
836 c3.y = ((c2.y + c4.y) / 2);
837
838 /* Add left recursion to stack */
839 cleft->p1 = c->p1;
840 cleft->p2 = c1;
841 cleft->p3 = c2;
842 cleft->p4 = c3;
843
844 /* Add right recursion to stack */
845 cright->p1 = c3;
846 cright->p2 = c4;
847 cright->p3 = c5;
848 cright->p4 = c->p4;
849 }
850 }
851
852 void
cogl2_path_curve_to(CoglPath * path,float x_1,float y_1,float x_2,float y_2,float x_3,float y_3)853 cogl2_path_curve_to (CoglPath *path,
854 float x_1,
855 float y_1,
856 float x_2,
857 float y_2,
858 float x_3,
859 float y_3)
860 {
861 CoglBezCubic cubic;
862
863 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
864
865 /* Prepare cubic curve */
866 cubic.p1 = path->data->path_pen;
867 cubic.p2.x = x_1;
868 cubic.p2.y = y_1;
869 cubic.p3.x = x_2;
870 cubic.p3.y = y_2;
871 cubic.p4.x = x_3;
872 cubic.p4.y = y_3;
873
874 /* Run subdivision */
875 _cogl_path_bezier3_sub (path, &cubic);
876
877 /* Add last point */
878 _cogl_path_add_node (path, FALSE, cubic.p4.x, cubic.p4.y);
879 path->data->path_pen = cubic.p4;
880 }
881
882 void
cogl2_path_rel_curve_to(CoglPath * path,float x_1,float y_1,float x_2,float y_2,float x_3,float y_3)883 cogl2_path_rel_curve_to (CoglPath *path,
884 float x_1,
885 float y_1,
886 float x_2,
887 float y_2,
888 float x_3,
889 float y_3)
890 {
891 CoglPathData *data;
892
893 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
894
895 data = path->data;
896
897 cogl2_path_curve_to (path,
898 data->path_pen.x + x_1,
899 data->path_pen.y + y_1,
900 data->path_pen.x + x_2,
901 data->path_pen.y + y_2,
902 data->path_pen.x + x_3,
903 data->path_pen.y + y_3);
904 }
905
906 CoglPath *
cogl2_path_new(void)907 cogl2_path_new (void)
908 {
909 CoglPath *path;
910 CoglPathData *data;
911
912 _COGL_GET_CONTEXT (ctx, NULL);
913
914 path = g_slice_new (CoglPath);
915 data = path->data = g_slice_new (CoglPathData);
916
917 data->ref_count = 1;
918 data->context = ctx;
919 data->fill_rule = COGL_PATH_FILL_RULE_EVEN_ODD;
920 data->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
921 data->last_path = 0;
922 data->fill_attribute_buffer = NULL;
923 data->stroke_attribute_buffer = NULL;
924 data->fill_primitive = NULL;
925 data->is_rectangle = FALSE;
926
927 return _cogl_path_object_new (path);
928 }
929
930 CoglPath *
cogl_path_copy(CoglPath * old_path)931 cogl_path_copy (CoglPath *old_path)
932 {
933 CoglPath *new_path;
934
935 _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (old_path), NULL);
936
937 new_path = g_slice_new (CoglPath);
938 new_path->data = old_path->data;
939 new_path->data->ref_count++;
940
941 return _cogl_path_object_new (new_path);
942 }
943
944 static void
_cogl_path_free(CoglPath * path)945 _cogl_path_free (CoglPath *path)
946 {
947 _cogl_path_data_unref (path->data);
948 g_slice_free (CoglPath, path);
949 }
950
951 /* If second order beziers were needed the following code could
952 * be re-enabled:
953 */
954 #if 0
955
956 static void
957 _cogl_path_bezier2_sub (CoglPath *path,
958 CoglBezQuad *quad)
959 {
960 CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH];
961 CoglBezQuad *qleft;
962 CoglBezQuad *qright;
963 CoglBezQuad *q;
964 floatVec2 mid;
965 floatVec2 dif;
966 floatVec2 c1;
967 floatVec2 c2;
968 floatVec2 c3;
969 int qindex;
970
971 /* Put first curve on stack */
972 quads[0] = *quad;
973 qindex = 0;
974
975 /* While stack is not empty */
976 while (qindex >= 0)
977 {
978
979 q = &quads[qindex];
980
981 /* Calculate distance of control point from its
982 * counterpart on the line between end points */
983 mid.x = ((q->p1.x + q->p3.x) / 2);
984 mid.y = ((q->p1.y + q->p3.y) / 2);
985 dif.x = (q->p2.x - mid.x);
986 dif.y = (q->p2.y - mid.y);
987 if (dif.x < 0) dif.x = -dif.x;
988 if (dif.y < 0) dif.y = -dif.y;
989
990 /* Cancel if the curve is flat enough */
991 if (dif.x + dif.y <= 1.0 ||
992 qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1)
993 {
994 /* Add subdivision point (skip last) */
995 if (qindex == 0) return;
996 _cogl_path_add_node (path, FALSE, q->p3.x, q->p3.y);
997 --qindex; continue;
998 }
999
1000 /* Left recursion goes on top of stack! */
1001 qright = q; qleft = &quads[++qindex];
1002
1003 /* Subdivide into 2 sub-curves */
1004 c1.x = ((q->p1.x + q->p2.x) / 2);
1005 c1.y = ((q->p1.y + q->p2.y) / 2);
1006 c3.x = ((q->p2.x + q->p3.x) / 2);
1007 c3.y = ((q->p2.y + q->p3.y) / 2);
1008 c2.x = ((c1.x + c3.x) / 2);
1009 c2.y = ((c1.y + c3.y) / 2);
1010
1011 /* Add left recursion onto stack */
1012 qleft->p1 = q->p1;
1013 qleft->p2 = c1;
1014 qleft->p3 = c2;
1015
1016 /* Add right recursion onto stack */
1017 qright->p1 = c2;
1018 qright->p2 = c3;
1019 qright->p3 = q->p3;
1020 }
1021 }
1022
1023 void
1024 cogl_path_curve2_to (CoglPath *path,
1025 float x_1,
1026 float y_1,
1027 float x_2,
1028 float y_2)
1029 {
1030 CoglBezQuad quad;
1031
1032 /* Prepare quadratic curve */
1033 quad.p1 = path->data->path_pen;
1034 quad.p2.x = x_1;
1035 quad.p2.y = y_1;
1036 quad.p3.x = x_2;
1037 quad.p3.y = y_2;
1038
1039 /* Run subdivision */
1040 _cogl_path_bezier2_sub (&quad);
1041
1042 /* Add last point */
1043 _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y);
1044 path->data->path_pen = quad.p3;
1045 }
1046
1047 void
1048 cogl_rel_curve2_to (CoglPath *path,
1049 float x_1,
1050 float y_1,
1051 float x_2,
1052 float y_2)
1053 {
1054 CoglPathData *data;
1055
1056 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
1057
1058 data = path->data;
1059
1060 cogl_path_curve2_to (data->path_pen.x + x_1,
1061 data->path_pen.y + y_1,
1062 data->path_pen.x + x_2,
1063 data->path_pen.y + y_2);
1064 }
1065
1066 #endif
1067
1068 typedef struct _CoglPathTesselator CoglPathTesselator;
1069 typedef struct _CoglPathTesselatorVertex CoglPathTesselatorVertex;
1070
1071 struct _CoglPathTesselator
1072 {
1073 GLUtesselator *glu_tess;
1074 GLenum primitive_type;
1075 int vertex_number;
1076 /* Array of CoglPathTesselatorVertex. This needs to grow when the
1077 combine callback is called */
1078 GArray *vertices;
1079 /* Array of integers for the indices into the vertices array. Each
1080 element will either be uint8_t, uint16_t or uint32_t depending on
1081 the number of vertices */
1082 GArray *indices;
1083 CoglIndicesType indices_type;
1084 /* Indices used to split fans and strips */
1085 int index_a, index_b;
1086 };
1087
1088 struct _CoglPathTesselatorVertex
1089 {
1090 float x, y, s, t;
1091 };
1092
1093 static void
_cogl_path_tesselator_begin(GLenum type,CoglPathTesselator * tess)1094 _cogl_path_tesselator_begin (GLenum type,
1095 CoglPathTesselator *tess)
1096 {
1097 g_assert (type == GL_TRIANGLES ||
1098 type == GL_TRIANGLE_FAN ||
1099 type == GL_TRIANGLE_STRIP);
1100
1101 tess->primitive_type = type;
1102 tess->vertex_number = 0;
1103 }
1104
1105 static CoglIndicesType
_cogl_path_tesselator_get_indices_type_for_size(int n_vertices)1106 _cogl_path_tesselator_get_indices_type_for_size (int n_vertices)
1107 {
1108 if (n_vertices <= 256)
1109 return COGL_INDICES_TYPE_UNSIGNED_BYTE;
1110 else if (n_vertices <= 65536)
1111 return COGL_INDICES_TYPE_UNSIGNED_SHORT;
1112 else
1113 return COGL_INDICES_TYPE_UNSIGNED_INT;
1114 }
1115
1116 static void
_cogl_path_tesselator_allocate_indices_array(CoglPathTesselator * tess)1117 _cogl_path_tesselator_allocate_indices_array (CoglPathTesselator *tess)
1118 {
1119 switch (tess->indices_type)
1120 {
1121 case COGL_INDICES_TYPE_UNSIGNED_BYTE:
1122 tess->indices = g_array_new (FALSE, FALSE, sizeof (uint8_t));
1123 break;
1124
1125 case COGL_INDICES_TYPE_UNSIGNED_SHORT:
1126 tess->indices = g_array_new (FALSE, FALSE, sizeof (uint16_t));
1127 break;
1128
1129 case COGL_INDICES_TYPE_UNSIGNED_INT:
1130 tess->indices = g_array_new (FALSE, FALSE, sizeof (uint32_t));
1131 break;
1132 }
1133 }
1134
1135 static void
_cogl_path_tesselator_add_index(CoglPathTesselator * tess,int vertex_index)1136 _cogl_path_tesselator_add_index (CoglPathTesselator *tess, int vertex_index)
1137 {
1138 switch (tess->indices_type)
1139 {
1140 case COGL_INDICES_TYPE_UNSIGNED_BYTE:
1141 {
1142 uint8_t val = vertex_index;
1143 g_array_append_val (tess->indices, val);
1144 }
1145 break;
1146
1147 case COGL_INDICES_TYPE_UNSIGNED_SHORT:
1148 {
1149 uint16_t val = vertex_index;
1150 g_array_append_val (tess->indices, val);
1151 }
1152 break;
1153
1154 case COGL_INDICES_TYPE_UNSIGNED_INT:
1155 {
1156 uint32_t val = vertex_index;
1157 g_array_append_val (tess->indices, val);
1158 }
1159 break;
1160 }
1161 }
1162
1163 static void
_cogl_path_tesselator_vertex(void * vertex_data,CoglPathTesselator * tess)1164 _cogl_path_tesselator_vertex (void *vertex_data,
1165 CoglPathTesselator *tess)
1166 {
1167 int vertex_index;
1168
1169 vertex_index = GPOINTER_TO_INT (vertex_data);
1170
1171 /* This tries to convert all of the primitives into GL_TRIANGLES
1172 with indices to share vertices */
1173 switch (tess->primitive_type)
1174 {
1175 case GL_TRIANGLES:
1176 /* Directly use the vertex */
1177 _cogl_path_tesselator_add_index (tess, vertex_index);
1178 break;
1179
1180 case GL_TRIANGLE_FAN:
1181 if (tess->vertex_number == 0)
1182 tess->index_a = vertex_index;
1183 else if (tess->vertex_number == 1)
1184 tess->index_b = vertex_index;
1185 else
1186 {
1187 /* Create a triangle with the first vertex, the previous
1188 vertex and this vertex */
1189 _cogl_path_tesselator_add_index (tess, tess->index_a);
1190 _cogl_path_tesselator_add_index (tess, tess->index_b);
1191 _cogl_path_tesselator_add_index (tess, vertex_index);
1192 /* Next time we will use this vertex as the previous
1193 vertex */
1194 tess->index_b = vertex_index;
1195 }
1196 break;
1197
1198 case GL_TRIANGLE_STRIP:
1199 if (tess->vertex_number == 0)
1200 tess->index_a = vertex_index;
1201 else if (tess->vertex_number == 1)
1202 tess->index_b = vertex_index;
1203 else
1204 {
1205 _cogl_path_tesselator_add_index (tess, tess->index_a);
1206 _cogl_path_tesselator_add_index (tess, tess->index_b);
1207 _cogl_path_tesselator_add_index (tess, vertex_index);
1208 if (tess->vertex_number & 1)
1209 tess->index_b = vertex_index;
1210 else
1211 tess->index_a = vertex_index;
1212 }
1213 break;
1214
1215 default:
1216 g_assert_not_reached ();
1217 }
1218
1219 tess->vertex_number++;
1220 }
1221
1222 static void
_cogl_path_tesselator_end(CoglPathTesselator * tess)1223 _cogl_path_tesselator_end (CoglPathTesselator *tess)
1224 {
1225 tess->primitive_type = GL_FALSE;
1226 }
1227
1228 static void
_cogl_path_tesselator_combine(double coords[3],void * vertex_data[4],float weight[4],void ** out_data,CoglPathTesselator * tess)1229 _cogl_path_tesselator_combine (double coords[3],
1230 void *vertex_data[4],
1231 float weight[4],
1232 void **out_data,
1233 CoglPathTesselator *tess)
1234 {
1235 CoglPathTesselatorVertex *vertex;
1236 CoglIndicesType new_indices_type;
1237 int i;
1238
1239 /* Add a new vertex to the array */
1240 g_array_set_size (tess->vertices, tess->vertices->len + 1);
1241 vertex = &g_array_index (tess->vertices,
1242 CoglPathTesselatorVertex,
1243 tess->vertices->len - 1);
1244 /* The data is just the index to the vertex */
1245 *out_data = GINT_TO_POINTER (tess->vertices->len - 1);
1246 /* Set the coordinates of the new vertex */
1247 vertex->x = coords[0];
1248 vertex->y = coords[1];
1249 /* Generate the texture coordinates as the weighted average of the
1250 four incoming coordinates */
1251 vertex->s = 0.0f;
1252 vertex->t = 0.0f;
1253 for (i = 0; i < 4; i++)
1254 {
1255 CoglPathTesselatorVertex *old_vertex =
1256 &g_array_index (tess->vertices, CoglPathTesselatorVertex,
1257 GPOINTER_TO_INT (vertex_data[i]));
1258 vertex->s += old_vertex->s * weight[i];
1259 vertex->t += old_vertex->t * weight[i];
1260 }
1261
1262 /* Check if we've reached the limit for the data type of our indices */
1263 new_indices_type =
1264 _cogl_path_tesselator_get_indices_type_for_size (tess->vertices->len);
1265 if (new_indices_type != tess->indices_type)
1266 {
1267 CoglIndicesType old_indices_type = new_indices_type;
1268 GArray *old_vertices = tess->indices;
1269
1270 /* Copy the indices to an array of the new type */
1271 tess->indices_type = new_indices_type;
1272 _cogl_path_tesselator_allocate_indices_array (tess);
1273
1274 switch (old_indices_type)
1275 {
1276 case COGL_INDICES_TYPE_UNSIGNED_BYTE:
1277 for (i = 0; i < old_vertices->len; i++)
1278 _cogl_path_tesselator_add_index (tess,
1279 g_array_index (old_vertices,
1280 uint8_t, i));
1281 break;
1282
1283 case COGL_INDICES_TYPE_UNSIGNED_SHORT:
1284 for (i = 0; i < old_vertices->len; i++)
1285 _cogl_path_tesselator_add_index (tess,
1286 g_array_index (old_vertices,
1287 uint16_t, i));
1288 break;
1289
1290 case COGL_INDICES_TYPE_UNSIGNED_INT:
1291 for (i = 0; i < old_vertices->len; i++)
1292 _cogl_path_tesselator_add_index (tess,
1293 g_array_index (old_vertices,
1294 uint32_t, i));
1295 break;
1296 }
1297
1298 g_array_free (old_vertices, TRUE);
1299 }
1300 }
1301
1302 static void
_cogl_path_build_fill_attribute_buffer(CoglPath * path)1303 _cogl_path_build_fill_attribute_buffer (CoglPath *path)
1304 {
1305 CoglPathTesselator tess;
1306 unsigned int path_start = 0;
1307 CoglPathData *data = path->data;
1308 int i;
1309
1310 /* If we've already got a vbo then we don't need to do anything */
1311 if (data->fill_attribute_buffer)
1312 return;
1313
1314 tess.primitive_type = FALSE;
1315
1316 /* Generate a vertex for each point on the path */
1317 tess.vertices = g_array_new (FALSE, FALSE, sizeof (CoglPathTesselatorVertex));
1318 g_array_set_size (tess.vertices, data->path_nodes->len);
1319 for (i = 0; i < data->path_nodes->len; i++)
1320 {
1321 CoglPathNode *node =
1322 &g_array_index (data->path_nodes, CoglPathNode, i);
1323 CoglPathTesselatorVertex *vertex =
1324 &g_array_index (tess.vertices, CoglPathTesselatorVertex, i);
1325
1326 vertex->x = node->x;
1327 vertex->y = node->y;
1328
1329 /* Add texture coordinates so that a texture would be drawn to
1330 fit the bounding box of the path and then cropped by the
1331 path */
1332 if (data->path_nodes_min.x == data->path_nodes_max.x)
1333 vertex->s = 0.0f;
1334 else
1335 vertex->s = ((node->x - data->path_nodes_min.x)
1336 / (data->path_nodes_max.x - data->path_nodes_min.x));
1337 if (data->path_nodes_min.y == data->path_nodes_max.y)
1338 vertex->t = 0.0f;
1339 else
1340 vertex->t = ((node->y - data->path_nodes_min.y)
1341 / (data->path_nodes_max.y - data->path_nodes_min.y));
1342 }
1343
1344 tess.indices_type =
1345 _cogl_path_tesselator_get_indices_type_for_size (data->path_nodes->len);
1346 _cogl_path_tesselator_allocate_indices_array (&tess);
1347
1348 tess.glu_tess = gluNewTess ();
1349
1350 if (data->fill_rule == COGL_PATH_FILL_RULE_EVEN_ODD)
1351 gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE,
1352 GLU_TESS_WINDING_ODD);
1353 else
1354 gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE,
1355 GLU_TESS_WINDING_NONZERO);
1356
1357 /* All vertices are on the xy-plane */
1358 gluTessNormal (tess.glu_tess, 0.0, 0.0, 1.0);
1359
1360 gluTessCallback (tess.glu_tess, GLU_TESS_BEGIN_DATA,
1361 _cogl_path_tesselator_begin);
1362 gluTessCallback (tess.glu_tess, GLU_TESS_VERTEX_DATA,
1363 _cogl_path_tesselator_vertex);
1364 gluTessCallback (tess.glu_tess, GLU_TESS_END_DATA,
1365 _cogl_path_tesselator_end);
1366 gluTessCallback (tess.glu_tess, GLU_TESS_COMBINE_DATA,
1367 _cogl_path_tesselator_combine);
1368
1369 gluTessBeginPolygon (tess.glu_tess, &tess);
1370
1371 while (path_start < data->path_nodes->len)
1372 {
1373 CoglPathNode *node =
1374 &g_array_index (data->path_nodes, CoglPathNode, path_start);
1375
1376 gluTessBeginContour (tess.glu_tess);
1377
1378 for (i = 0; i < node->path_size; i++)
1379 {
1380 double vertex[3] = { node[i].x, node[i].y, 0.0 };
1381 gluTessVertex (tess.glu_tess, vertex,
1382 GINT_TO_POINTER (i + path_start));
1383 }
1384
1385 gluTessEndContour (tess.glu_tess);
1386
1387 path_start += node->path_size;
1388 }
1389
1390 gluTessEndPolygon (tess.glu_tess);
1391
1392 gluDeleteTess (tess.glu_tess);
1393
1394 data->fill_attribute_buffer =
1395 cogl_attribute_buffer_new (data->context,
1396 sizeof (CoglPathTesselatorVertex) *
1397 tess.vertices->len,
1398 tess.vertices->data);
1399 g_array_free (tess.vertices, TRUE);
1400
1401 data->fill_attributes[0] =
1402 cogl_attribute_new (data->fill_attribute_buffer,
1403 "cogl_position_in",
1404 sizeof (CoglPathTesselatorVertex),
1405 G_STRUCT_OFFSET (CoglPathTesselatorVertex, x),
1406 2, /* n_components */
1407 COGL_ATTRIBUTE_TYPE_FLOAT);
1408 data->fill_attributes[1] =
1409 cogl_attribute_new (data->fill_attribute_buffer,
1410 "cogl_tex_coord0_in",
1411 sizeof (CoglPathTesselatorVertex),
1412 G_STRUCT_OFFSET (CoglPathTesselatorVertex, s),
1413 2, /* n_components */
1414 COGL_ATTRIBUTE_TYPE_FLOAT);
1415
1416 data->fill_vbo_indices = cogl_indices_new (data->context,
1417 tess.indices_type,
1418 tess.indices->data,
1419 tess.indices->len);
1420 data->fill_vbo_n_indices = tess.indices->len;
1421 g_array_free (tess.indices, TRUE);
1422 }
1423
1424 static CoglPrimitive *
_cogl_path_get_fill_primitive(CoglPath * path)1425 _cogl_path_get_fill_primitive (CoglPath *path)
1426 {
1427 if (path->data->fill_primitive)
1428 return path->data->fill_primitive;
1429
1430 _cogl_path_build_fill_attribute_buffer (path);
1431
1432 path->data->fill_primitive =
1433 cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
1434 path->data->fill_vbo_n_indices,
1435 path->data->fill_attributes,
1436 COGL_PATH_N_ATTRIBUTES);
1437 cogl_primitive_set_indices (path->data->fill_primitive,
1438 path->data->fill_vbo_indices,
1439 path->data->fill_vbo_n_indices);
1440
1441 return path->data->fill_primitive;
1442 }
1443
1444 static CoglClipStack *
_cogl_clip_stack_push_from_path(CoglClipStack * stack,CoglPath * path,CoglMatrixEntry * modelview_entry,CoglMatrixEntry * projection_entry,const float * viewport)1445 _cogl_clip_stack_push_from_path (CoglClipStack *stack,
1446 CoglPath *path,
1447 CoglMatrixEntry *modelview_entry,
1448 CoglMatrixEntry *projection_entry,
1449 const float *viewport)
1450 {
1451 float x_1, y_1, x_2, y_2;
1452
1453 _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
1454
1455 /* If the path is a simple rectangle then we can divert to pushing a
1456 rectangle clip instead which usually won't involve the stencil
1457 buffer */
1458 if (_cogl_path_is_rectangle (path))
1459 return _cogl_clip_stack_push_rectangle (stack,
1460 x_1, y_1,
1461 x_2, y_2,
1462 modelview_entry,
1463 projection_entry,
1464 viewport);
1465 else
1466 {
1467 CoglPrimitive *primitive = _cogl_path_get_fill_primitive (path);
1468
1469 return _cogl_clip_stack_push_primitive (stack,
1470 primitive,
1471 x_1, y_1, x_2, y_2,
1472 modelview_entry,
1473 projection_entry,
1474 viewport);
1475 }
1476 }
1477
1478 void
cogl_framebuffer_push_path_clip(CoglFramebuffer * framebuffer,CoglPath * path)1479 cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer,
1480 CoglPath *path)
1481 {
1482 CoglMatrixEntry *modelview_entry =
1483 _cogl_framebuffer_get_modelview_entry (framebuffer);
1484 CoglMatrixEntry *projection_entry =
1485 _cogl_framebuffer_get_projection_entry (framebuffer);
1486 /* XXX: It would be nicer if we stored the private viewport as a
1487 * vec4 so we could avoid this redundant copy. */
1488 float viewport[] = {
1489 framebuffer->viewport_x,
1490 framebuffer->viewport_y,
1491 framebuffer->viewport_width,
1492 framebuffer->viewport_height
1493 };
1494
1495 framebuffer->clip_stack =
1496 _cogl_clip_stack_push_from_path (framebuffer->clip_stack,
1497 path,
1498 modelview_entry,
1499 projection_entry,
1500 viewport);
1501
1502 if (framebuffer->context->current_draw_buffer == framebuffer)
1503 framebuffer->context->current_draw_buffer_changes |=
1504 COGL_FRAMEBUFFER_STATE_CLIP;
1505 }
1506
1507 /* XXX: deprecated */
1508 void
cogl_clip_push_from_path(CoglPath * path)1509 cogl_clip_push_from_path (CoglPath *path)
1510 {
1511 cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (), path);
1512 }
1513
1514 static void
_cogl_path_build_stroke_attribute_buffer(CoglPath * path)1515 _cogl_path_build_stroke_attribute_buffer (CoglPath *path)
1516 {
1517 CoglPathData *data = path->data;
1518 CoglBuffer *buffer;
1519 unsigned int n_attributes = 0;
1520 unsigned int path_start;
1521 CoglPathNode *node;
1522 floatVec2 *buffer_p;
1523 unsigned int i;
1524
1525 /* If we've already got a cached vbo then we don't need to do anything */
1526 if (data->stroke_attribute_buffer)
1527 return;
1528
1529 data->stroke_attribute_buffer =
1530 cogl_attribute_buffer_new_with_size (data->context,
1531 data->path_nodes->len *
1532 sizeof (floatVec2));
1533
1534 buffer = COGL_BUFFER (data->stroke_attribute_buffer);
1535 buffer_p = _cogl_buffer_map_for_fill_or_fallback (buffer);
1536
1537 /* Copy the vertices in and count the number of sub paths. Each sub
1538 path will form a separate attribute so we can paint the disjoint
1539 line strips */
1540 for (path_start = 0;
1541 path_start < data->path_nodes->len;
1542 path_start += node->path_size)
1543 {
1544 node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
1545
1546 for (i = 0; i < node->path_size; i++)
1547 {
1548 buffer_p[path_start + i].x = node[i].x;
1549 buffer_p[path_start + i].y = node[i].y;
1550 }
1551
1552 n_attributes++;
1553 }
1554
1555 _cogl_buffer_unmap_for_fill_or_fallback (buffer);
1556
1557 data->stroke_attributes = g_new (CoglAttribute *, n_attributes);
1558
1559 /* Now we can loop the sub paths again to create the attributes */
1560 for (i = 0, path_start = 0;
1561 path_start < data->path_nodes->len;
1562 i++, path_start += node->path_size)
1563 {
1564 node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
1565
1566 data->stroke_attributes[i] =
1567 cogl_attribute_new (data->stroke_attribute_buffer,
1568 "cogl_position_in",
1569 sizeof (floatVec2),
1570 path_start * sizeof (floatVec2),
1571 2, /* n_components */
1572 COGL_ATTRIBUTE_TYPE_FLOAT);
1573 }
1574
1575 data->stroke_n_attributes = n_attributes;
1576 }
1577
1578 /* XXX: deprecated */
1579 void
cogl_framebuffer_fill_path(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglPath * path)1580 cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
1581 CoglPipeline *pipeline,
1582 CoglPath *path)
1583 {
1584 _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
1585 _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
1586 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
1587
1588 _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */);
1589 }
1590
1591 /* XXX: deprecated */
1592 void
cogl_framebuffer_stroke_path(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglPath * path)1593 cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
1594 CoglPipeline *pipeline,
1595 CoglPath *path)
1596 {
1597 _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
1598 _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
1599 _COGL_RETURN_IF_FAIL (cogl_is_path (path));
1600
1601 _cogl_path_stroke_nodes (path, framebuffer, pipeline);
1602 }
1603