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