Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-paint-nodes.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2011  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author:
22  *   Emmanuele Bassi <ebassi@linux.intel.com>
23  */
24
25 /**
26  * SECTION:clutter-paint-nodes
27  * @Title: Paint Nodes
28  * @Short_Description: ClutterPaintNode implementations
29  *
30  * Clutter provides a set of predefined #ClutterPaintNode implementations
31  * that cover all the state changes available.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #define CLUTTER_ENABLE_EXPERIMENTAL_API
39
40 #include "clutter-paint-node-private.h"
41
42 #include <pango/pango.h>
43 #include <cogl/cogl.h>
44
45 #include "clutter-actor-private.h"
46 #include "clutter-color.h"
47 #include "clutter-debug.h"
48 #include "clutter-private.h"
49
50 #include "clutter-paint-nodes.h"
51
52 static CoglPipeline *default_color_pipeline   = NULL;
53 static CoglPipeline *default_texture_pipeline = NULL;
54
55 /*< private >
56  * _clutter_paint_node_init_types:
57  *
58  * Initializes the required types for ClutterPaintNode subclasses
59  */
60 void
61 _clutter_paint_node_init_types (void)
62 {
63   CoglContext *ctx;
64   CoglColor cogl_color;
65   GType node_type G_GNUC_UNUSED;
66
67   if (G_LIKELY (default_color_pipeline != NULL))
68     return;
69
70   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
71
72   node_type = clutter_paint_node_get_type ();
73
74   cogl_color_init_from_4f (&cogl_color, 1.0, 1.0, 1.0, 1.0);
75
76   default_color_pipeline = cogl_pipeline_new (ctx);
77   cogl_pipeline_set_color (default_color_pipeline, &cogl_color);
78
79   default_texture_pipeline = cogl_pipeline_new (ctx);
80   cogl_pipeline_set_layer_null_texture (default_texture_pipeline, 0,
81                                         COGL_TEXTURE_TYPE_2D);
82   cogl_pipeline_set_color (default_texture_pipeline, &cogl_color);
83   cogl_pipeline_set_layer_wrap_mode (default_texture_pipeline, 0,
84                                      COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
85 }
86
87 /*
88  * Root node, private
89  *
90  * any frame can only have a since RootNode instance for each
91  * top-level actor.
92  */
93
94 #define clutter_root_node_get_type      _clutter_root_node_get_type
95
96 typedef struct _ClutterRootNode         ClutterRootNode;
97 typedef struct _ClutterPaintNodeClass   ClutterRootNodeClass;
98
99 struct _ClutterRootNode
100 {
101   ClutterPaintNode parent_instance;
102
103   CoglFramebuffer *framebuffer;
104
105   CoglBufferBit clear_flags;
106   CoglColor clear_color;
107   CoglMatrix modelview;
108 };
109
110 G_DEFINE_TYPE (ClutterRootNode, clutter_root_node, CLUTTER_TYPE_PAINT_NODE)
111
112 static gboolean
113 clutter_root_node_pre_draw (ClutterPaintNode *node)
114 {
115   ClutterRootNode *rnode = (ClutterRootNode *) node;
116
117   cogl_push_matrix ();
118
119   cogl_framebuffer_set_modelview_matrix (rnode->framebuffer,
120                                          &rnode->modelview);
121
122   cogl_framebuffer_clear (rnode->framebuffer,
123                           rnode->clear_flags,
124                           &rnode->clear_color);
125
126   return TRUE;
127 }
128
129 static void
130 clutter_root_node_post_draw (ClutterPaintNode *node)
131 {
132   cogl_pop_matrix ();
133 }
134
135 static void
136 clutter_root_node_finalize (ClutterPaintNode *node)
137 {
138   ClutterRootNode *rnode = (ClutterRootNode *) node;
139
140   cogl_object_unref (rnode->framebuffer);
141
142   CLUTTER_PAINT_NODE_CLASS (clutter_root_node_parent_class)->finalize (node);
143 }
144
145 static void
146 clutter_root_node_class_init (ClutterRootNodeClass *klass)
147 {
148   ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
149
150   node_class->pre_draw = clutter_root_node_pre_draw;
151   node_class->post_draw = clutter_root_node_post_draw;
152   node_class->finalize = clutter_root_node_finalize;
153 }
154
155 static void
156 clutter_root_node_init (ClutterRootNode *self)
157 {
158   cogl_matrix_init_identity (&self->modelview);
159 }
160
161 ClutterPaintNode *
162 _clutter_root_node_new (CoglFramebuffer    *framebuffer,
163                         const ClutterColor *clear_color,
164                         CoglBufferBit       clear_flags,
165                         const CoglMatrix   *matrix)
166 {
167   ClutterRootNode *res;
168
169   res = _clutter_paint_node_create (_clutter_root_node_get_type ());
170
171   cogl_color_init_from_4ub (&res->clear_color,
172                             clear_color->red,
173                             clear_color->green,
174                             clear_color->blue,
175                             clear_color->alpha);
176   cogl_color_premultiply (&res->clear_color);
177
178   res->framebuffer = cogl_object_ref (framebuffer);
179   res->clear_flags = clear_flags;
180   res->modelview = *matrix;
181
182   return (ClutterPaintNode *) res;
183 }
184
185 /*
186  * Transform node
187  *
188  * A private PaintNode, that changes the modelview of its child
189  * nodes.
190  */
191
192 #define clutter_transform_node_get_type _clutter_transform_node_get_type
193
194 typedef struct _ClutterTransformNode {
195   ClutterPaintNode parent_instance;
196
197   CoglMatrix modelview;
198 } ClutterTransformNode;
199
200 typedef struct _ClutterPaintNodeClass   ClutterTransformNodeClass;
201
202 G_DEFINE_TYPE (ClutterTransformNode, clutter_transform_node, CLUTTER_TYPE_PAINT_NODE)
203
204 static gboolean
205 clutter_transform_node_pre_draw (ClutterPaintNode *node)
206 {
207   ClutterTransformNode *tnode = (ClutterTransformNode *) node;
208   CoglMatrix matrix;
209
210   cogl_push_matrix ();
211
212   cogl_get_modelview_matrix (&matrix);
213   cogl_matrix_multiply (&matrix, &matrix, &tnode->modelview);
214   cogl_set_modelview_matrix (&matrix);
215
216   return TRUE;
217 }
218
219 static void
220 clutter_transform_node_post_draw (ClutterPaintNode *node)
221 {
222   cogl_pop_matrix ();
223 }
224
225 static void
226 clutter_transform_node_class_init (ClutterTransformNodeClass *klass)
227 {
228   ClutterPaintNodeClass *node_class;
229
230   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
231   node_class->pre_draw = clutter_transform_node_pre_draw;
232   node_class->post_draw = clutter_transform_node_post_draw;
233 }
234
235 static void
236 clutter_transform_node_init (ClutterTransformNode *self)
237 {
238   cogl_matrix_init_identity (&self->modelview);
239 }
240
241 ClutterPaintNode *
242 _clutter_transform_node_new (const CoglMatrix *modelview)
243 {
244   ClutterTransformNode *res;
245
246   res = _clutter_paint_node_create (_clutter_transform_node_get_type ());
247
248   if (modelview != NULL)
249     res->modelview = *modelview;
250
251   return (ClutterPaintNode *) res;
252 }
253
254 /*
255  * Dummy node, private
256  *
257  * an empty node, used temporarily until we can drop API compatibility,
258  * and we'll be able to build a full render tree for each frame.
259  */
260
261 #define clutter_dummy_node_get_type      _clutter_dummy_node_get_type
262
263 typedef struct _ClutterDummyNode        ClutterDummyNode;
264 typedef struct _ClutterPaintNodeClass   ClutterDummyNodeClass;
265
266 struct _ClutterDummyNode
267 {
268   ClutterPaintNode parent_instance;
269
270   ClutterActor *actor;
271 };
272
273 G_DEFINE_TYPE (ClutterDummyNode, clutter_dummy_node, CLUTTER_TYPE_PAINT_NODE)
274
275 static gboolean
276 clutter_dummy_node_pre_draw (ClutterPaintNode *node)
277 {
278   return TRUE;
279 }
280
281 static JsonNode *
282 clutter_dummy_node_serialize (ClutterPaintNode *node)
283 {
284   ClutterDummyNode *dnode = (ClutterDummyNode *) node;
285   JsonBuilder *builder;
286   JsonNode *res;
287
288   if (dnode->actor == NULL)
289     return json_node_new (JSON_NODE_NULL);
290
291   builder = json_builder_new ();
292   json_builder_begin_object (builder);
293
294   json_builder_set_member_name (builder, "actor");
295   json_builder_add_string_value (builder, _clutter_actor_get_debug_name (dnode->actor));
296
297   json_builder_end_object (builder);
298
299   res = json_builder_get_root (builder);
300   g_object_unref (builder);
301
302   return res;
303 }
304
305 static void
306 clutter_dummy_node_class_init (ClutterDummyNodeClass *klass)
307 {
308   ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
309
310   node_class->pre_draw = clutter_dummy_node_pre_draw;
311   node_class->serialize = clutter_dummy_node_serialize;
312 }
313
314 static void
315 clutter_dummy_node_init (ClutterDummyNode *self)
316 {
317 }
318
319 ClutterPaintNode *
320 _clutter_dummy_node_new (ClutterActor *actor)
321 {
322   ClutterPaintNode *res;
323
324   res = _clutter_paint_node_create (_clutter_dummy_node_get_type ());
325
326   ((ClutterDummyNode *) res)->actor = actor;
327
328   return res;
329 }
330
331 /*
332  * Pipeline node
333  */
334
335 struct _ClutterPipelineNode
336 {
337   ClutterPaintNode parent_instance;
338
339   CoglPipeline *pipeline;
340 };
341
342 /**
343  * ClutterPipelineNodeClass:
344  *
345  * The <structname>ClutterPipelineNodeClass</structname> structure is an opaque
346  * type whose members cannot be directly accessed.
347  *
348  * Since: 1.10
349  */
350 struct _ClutterPipelineNodeClass
351 {
352   ClutterPaintNodeClass parent_class;
353 };
354
355 G_DEFINE_TYPE (ClutterPipelineNode, clutter_pipeline_node, CLUTTER_TYPE_PAINT_NODE)
356
357 static void
358 clutter_pipeline_node_finalize (ClutterPaintNode *node)
359 {
360   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
361
362   if (pnode->pipeline != NULL)
363     cogl_object_unref (pnode->pipeline);
364
365   CLUTTER_PAINT_NODE_CLASS (clutter_pipeline_node_parent_class)->finalize (node);
366 }
367
368 static gboolean
369 clutter_pipeline_node_pre_draw (ClutterPaintNode *node)
370 {
371   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
372
373   if (node->operations != NULL &&
374       pnode->pipeline != NULL)
375     {
376       cogl_push_source (pnode->pipeline);
377       return TRUE;
378     }
379
380   return FALSE;
381 }
382
383 static void
384 clutter_pipeline_node_draw (ClutterPaintNode *node)
385 {
386   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
387   guint i;
388
389   if (pnode->pipeline == NULL)
390     return;
391
392   if (node->operations == NULL)
393     return;
394
395   for (i = 0; i < node->operations->len; i++)
396     {
397       const ClutterPaintOperation *op;
398
399       op = &g_array_index (node->operations, ClutterPaintOperation, i);
400
401       switch (op->opcode)
402         {
403         case PAINT_OP_INVALID:
404           break;
405
406         case PAINT_OP_TEX_RECT:
407           cogl_rectangle_with_texture_coords (op->op.texrect[0],
408                                               op->op.texrect[1],
409                                               op->op.texrect[2],
410                                               op->op.texrect[3],
411                                               op->op.texrect[4],
412                                               op->op.texrect[5],
413                                               op->op.texrect[6],
414                                               op->op.texrect[7]);
415           break;
416
417         case PAINT_OP_PATH:
418           cogl_path_fill (op->op.path);
419           break;
420
421         case PAINT_OP_PRIMITIVE:
422           {
423             CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
424
425             cogl_framebuffer_draw_primitive (fb, pnode->pipeline,
426                                              op->op.primitive);
427           }
428           break;
429         }
430     }
431 }
432
433 static void
434 clutter_pipeline_node_post_draw (ClutterPaintNode *node)
435 {
436   cogl_pop_source ();
437 }
438
439 static JsonNode *
440 clutter_pipeline_node_serialize (ClutterPaintNode *node)
441 {
442   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
443   JsonBuilder *builder;
444   CoglColor color;
445   JsonNode *res;
446
447   if (pnode->pipeline == NULL)
448     return json_node_new (JSON_NODE_NULL);
449
450   builder = json_builder_new ();
451   json_builder_begin_object (builder);
452
453   cogl_pipeline_get_color (pnode->pipeline, &color);
454   json_builder_set_member_name (builder, "color");
455   json_builder_begin_array (builder);
456   json_builder_add_double_value (builder, cogl_color_get_red (&color));
457   json_builder_add_double_value (builder, cogl_color_get_green (&color));
458   json_builder_add_double_value (builder, cogl_color_get_blue (&color));
459   json_builder_add_double_value (builder, cogl_color_get_alpha (&color));
460   json_builder_end_array (builder);
461
462 #if 0
463   json_builder_set_member_name (builder, "layers");
464   json_builder_begin_array (builder);
465   cogl_pipeline_foreach_layer (pnode->pipeline,
466                                clutter_pipeline_node_serialize_layer,
467                                builder);
468   json_builder_end_array (builder);
469 #endif
470
471   json_builder_end_object (builder);
472
473   res = json_builder_get_root (builder);
474   g_object_unref (builder);
475
476   return res;
477 }
478
479 static void
480 clutter_pipeline_node_class_init (ClutterPipelineNodeClass *klass)
481 {
482   ClutterPaintNodeClass *node_class;
483
484   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
485   node_class->pre_draw = clutter_pipeline_node_pre_draw;
486   node_class->draw = clutter_pipeline_node_draw;
487   node_class->post_draw = clutter_pipeline_node_post_draw;
488   node_class->finalize = clutter_pipeline_node_finalize;
489   node_class->serialize = clutter_pipeline_node_serialize;
490 }
491
492 static void
493 clutter_pipeline_node_init (ClutterPipelineNode *self)
494 {
495 }
496
497 /**
498  * clutter_pipeline_node_new:
499  * @pipeline: (allow-none): a Cogl pipeline state object, or %NULL
500  *
501  * Creates a new #ClutterPaintNode that will use the @pipeline to
502  * paint its contents.
503  *
504  * This function will acquire a reference on the passed @pipeline,
505  * so it is safe to call cogl_object_unref() when it returns.
506  *
507  * Return value: (transfer full): the newly created #ClutterPaintNode.
508  *   Use clutter_paint_node_unref() when done.
509  *
510  * Since: 1.10
511  */
512 ClutterPaintNode *
513 clutter_pipeline_node_new (CoglPipeline *pipeline)
514 {
515   ClutterPipelineNode *res;
516
517   g_return_val_if_fail (pipeline == NULL || cogl_is_pipeline (pipeline), NULL);
518
519   res = _clutter_paint_node_create (CLUTTER_TYPE_PIPELINE_NODE);
520
521   if (pipeline != NULL)
522     res->pipeline = cogl_object_ref (pipeline);
523
524   return (ClutterPaintNode *) res;
525 }
526
527 /*
528  * Color node
529  */
530
531 struct _ClutterColorNode
532 {
533   ClutterPipelineNode parent_instance;
534 };
535
536 /**
537  * ClutterColorNodeClass:
538  *
539  * The <structname>ClutterColorNodeClass</structname> structure is an
540  * opaque type whose members cannot be directly accessed.
541  *
542  * Since: 1.10
543  */
544 struct _ClutterColorNodeClass
545 {
546   ClutterPipelineNodeClass parent_class;
547 };
548
549 G_DEFINE_TYPE (ClutterColorNode, clutter_color_node, CLUTTER_TYPE_PIPELINE_NODE)
550
551 static void
552 clutter_color_node_class_init (ClutterColorNodeClass *klass)
553 {
554
555 }
556
557 static void
558 clutter_color_node_init (ClutterColorNode *cnode)
559 {
560   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (cnode);
561
562   g_assert (default_color_pipeline != NULL);
563   pnode->pipeline = cogl_pipeline_copy (default_color_pipeline);
564 }
565
566 /**
567  * clutter_color_node_new:
568  * @color: (allow-none): the color to paint, or %NULL
569  *
570  * Creates a new #ClutterPaintNode that will paint a solid color
571  * fill using @color.
572  *
573  * Return value: (transfer full): the newly created #ClutterPaintNode. Use
574  *   clutter_paint_node_unref() when done
575  *
576  * Since: 1.10
577  */
578 ClutterPaintNode *
579 clutter_color_node_new (const ClutterColor *color)
580 {
581   ClutterPipelineNode *cnode;
582
583   cnode = _clutter_paint_node_create (CLUTTER_TYPE_COLOR_NODE);
584
585   if (color != NULL)
586     {
587       CoglColor cogl_color;
588
589       cogl_color_init_from_4ub (&cogl_color,
590                                 color->red,
591                                 color->green,
592                                 color->blue,
593                                 color->alpha);
594       cogl_color_premultiply (&cogl_color);
595
596       cogl_pipeline_set_color (cnode->pipeline, &cogl_color);
597     }
598
599   return (ClutterPaintNode *) cnode;
600 }
601
602 /*
603  * Texture node
604  */
605
606 struct _ClutterTextureNode
607 {
608   ClutterPipelineNode parent_instance;
609 };
610
611 /**
612  * ClutterTextureNodeClass:
613  *
614  * The <structname>ClutterTextureNodeClass</structname> structure is an
615  * opaque type whose members cannot be directly accessed.
616  *
617  * Since: 1.10
618  */
619 struct _ClutterTextureNodeClass
620 {
621   ClutterPipelineNodeClass parent_class;
622 };
623
624 G_DEFINE_TYPE (ClutterTextureNode, clutter_texture_node, CLUTTER_TYPE_PIPELINE_NODE)
625
626 static void
627 clutter_texture_node_class_init (ClutterTextureNodeClass *klass)
628 {
629 }
630
631 static void
632 clutter_texture_node_init (ClutterTextureNode *self)
633 {
634   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (self);
635
636   g_assert (default_texture_pipeline != NULL);
637   pnode->pipeline = cogl_pipeline_copy (default_texture_pipeline);
638 }
639
640 static CoglPipelineFilter
641 clutter_scaling_filter_to_cogl_pipeline_filter (ClutterScalingFilter filter)
642 {
643   switch (filter)
644     {
645     case CLUTTER_SCALING_FILTER_NEAREST:
646       return COGL_PIPELINE_FILTER_NEAREST;
647
648     case CLUTTER_SCALING_FILTER_LINEAR:
649       return COGL_PIPELINE_FILTER_LINEAR;
650
651     case CLUTTER_SCALING_FILTER_TRILINEAR:
652       return COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR;
653     }
654
655   return COGL_PIPELINE_FILTER_LINEAR;
656 }
657
658 /**
659  * clutter_texture_node_new:
660  * @texture: a #CoglTexture
661  * @color: a #ClutterColor
662  * @min_filter: the minification filter for the texture
663  * @mag_filter: the magnification filter for the texture
664  *
665  * Creates a new #ClutterPaintNode that will paint the passed @texture.
666  *
667  * This function will take a reference on @texture, so it is safe to
668  * call cogl_object_unref() on @texture when it returns.
669  *
670  * Return value: (transfer full): the newly created #ClutterPaintNode.
671  *   Use clutter_paint_node_unref() when done
672  *
673  * Since: 1.10
674  */
675 ClutterPaintNode *
676 clutter_texture_node_new (CoglTexture          *texture,
677                           const ClutterColor   *color,
678                           ClutterScalingFilter  min_filter,
679                           ClutterScalingFilter  mag_filter)
680 {
681   ClutterPipelineNode *tnode;
682   CoglColor cogl_color;
683   CoglPipelineFilter min_f, mag_f;
684
685   g_return_val_if_fail (cogl_is_texture (texture), NULL);
686
687   tnode = _clutter_paint_node_create (CLUTTER_TYPE_TEXTURE_NODE);
688
689   cogl_pipeline_set_layer_texture (tnode->pipeline, 0, texture);
690
691   min_f = clutter_scaling_filter_to_cogl_pipeline_filter (min_filter);
692   mag_f = clutter_scaling_filter_to_cogl_pipeline_filter (mag_filter);
693   cogl_pipeline_set_layer_filters (tnode->pipeline, 0, min_f, mag_f);
694
695   cogl_color_init_from_4ub (&cogl_color,
696                             color->red,
697                             color->green,
698                             color->blue,
699                             color->alpha);
700   cogl_color_premultiply (&cogl_color);
701   cogl_pipeline_set_color (tnode->pipeline, &cogl_color);
702
703   return (ClutterPaintNode *) tnode;
704 }
705
706 /*
707  * Text node
708  */
709
710 struct _ClutterTextNode
711 {
712   ClutterPaintNode parent_instance;
713
714   PangoLayout *layout;
715   CoglColor color;
716 };
717
718 /**
719  * ClutterTextNodeClass:
720  *
721  * The <structname>ClutterTextNodeClass</structname> structure is an opaque
722  * type whose contents cannot be directly accessed.
723  *
724  * Since: 1.10
725  */
726 struct _ClutterTextNodeClass
727 {
728   ClutterPaintNodeClass parent_class;
729 };
730
731 G_DEFINE_TYPE (ClutterTextNode, clutter_text_node, CLUTTER_TYPE_PAINT_NODE)
732
733 static void
734 clutter_text_node_finalize (ClutterPaintNode *node)
735 {
736   ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
737
738   if (tnode->layout != NULL)
739     g_object_unref (tnode->layout);
740
741   CLUTTER_PAINT_NODE_CLASS (clutter_text_node_parent_class)->finalize (node);
742 }
743
744 static gboolean
745 clutter_text_node_pre_draw (ClutterPaintNode *node)
746 {
747   ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
748
749   return tnode->layout != NULL;
750 }
751
752 static void
753 clutter_text_node_draw (ClutterPaintNode *node)
754 {
755   ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
756   PangoRectangle extents;
757   guint i;
758
759   if (node->operations == NULL)
760     return;
761
762   pango_layout_get_pixel_extents (tnode->layout, NULL, &extents);
763
764   for (i = 0; i < node->operations->len; i++)
765     {
766       const ClutterPaintOperation *op;
767       float op_width, op_height;
768       gboolean clipped = FALSE;
769
770       op = &g_array_index (node->operations, ClutterPaintOperation, i);
771
772       switch (op->opcode)
773         {
774         case PAINT_OP_TEX_RECT:
775           op_width = op->op.texrect[2] - op->op.texrect[0];
776           op_height = op->op.texrect[3] - op->op.texrect[1];
777
778           /* if the primitive size was smaller than the layout,
779            * we clip the layout when drawin, to avoid spilling
780            * it out
781            */
782           if (extents.width > op_width ||
783               extents.height > op_height)
784             {
785               cogl_clip_push_rectangle (op->op.texrect[0],
786                                         op->op.texrect[1],
787                                         op->op.texrect[2],
788                                         op->op.texrect[3]);
789               clipped = TRUE;
790             }
791
792           cogl_pango_render_layout (tnode->layout,
793                                     op->op.texrect[0],
794                                     op->op.texrect[1],
795                                     &tnode->color,
796                                     0);
797
798           if (clipped)
799             cogl_clip_pop ();
800           break;
801
802         case PAINT_OP_PATH:
803         case PAINT_OP_PRIMITIVE:
804         case PAINT_OP_INVALID:
805           break;
806         }
807     }
808 }
809
810 static JsonNode *
811 clutter_text_node_serialize (ClutterPaintNode *node)
812 {
813   ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
814   JsonBuilder *builder;
815   JsonNode *res;
816
817   builder = json_builder_new ();
818
819   json_builder_begin_object (builder);
820
821   json_builder_set_member_name (builder, "layout");
822
823   if (pango_layout_get_character_count (tnode->layout) > 12)
824     {
825       const char *text = pango_layout_get_text (tnode->layout);
826       char *str;
827
828       str = g_strndup (text, 12);
829       json_builder_add_string_value (builder, str);
830       g_free (str);
831     }
832   else
833     json_builder_add_string_value (builder, pango_layout_get_text (tnode->layout));
834
835   json_builder_set_member_name (builder, "color");
836   json_builder_begin_array (builder);
837   json_builder_add_double_value (builder, cogl_color_get_red (&tnode->color));
838   json_builder_add_double_value (builder, cogl_color_get_green (&tnode->color));
839   json_builder_add_double_value (builder, cogl_color_get_blue (&tnode->color));
840   json_builder_add_double_value (builder, cogl_color_get_alpha (&tnode->color));
841   json_builder_end_array (builder);
842
843   json_builder_end_object (builder);
844
845   res = json_builder_get_root (builder);
846   g_object_unref (builder);
847
848   return res;
849 }
850
851 static void
852 clutter_text_node_class_init (ClutterTextNodeClass *klass)
853 {
854   ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
855
856   node_class->pre_draw = clutter_text_node_pre_draw;
857   node_class->draw = clutter_text_node_draw;
858   node_class->finalize = clutter_text_node_finalize;
859   node_class->serialize = clutter_text_node_serialize;
860 }
861
862 static void
863 clutter_text_node_init (ClutterTextNode *self)
864 {
865   cogl_color_init_from_4f (&self->color, 0.0, 0.0, 0.0, 1.0);
866 }
867
868 /**
869  * clutter_text_node_new:
870  * @layout: (allow-none): a #PangoLayout, or %NULL
871  * @color: (allow-none): the color used to paint the layout,
872  *   or %NULL
873  *
874  * Creates a new #ClutterPaintNode that will paint a #PangoLayout
875  * with the given color.
876  *
877  * This function takes a reference on the passed @layout, so it
878  * is safe to call g_object_unref() after it returns.
879  *
880  * Return value: (transfer full): the newly created #ClutterPaintNode.
881  *   Use clutter_paint_node_unref() when done
882  *
883  * Since: 1.10
884  */
885 ClutterPaintNode *
886 clutter_text_node_new (PangoLayout        *layout,
887                        const ClutterColor *color)
888 {
889   ClutterTextNode *res;
890
891   g_return_val_if_fail (layout == NULL || PANGO_IS_LAYOUT (layout), NULL);
892
893   res = _clutter_paint_node_create (CLUTTER_TYPE_TEXT_NODE);
894
895   if (layout != NULL)
896     res->layout = g_object_ref (layout);
897
898   if (color != NULL)
899     {
900       cogl_color_init_from_4ub (&res->color,
901                                 color->red,
902                                 color->green,
903                                 color->blue,
904                                 color->alpha);
905     }
906
907   return (ClutterPaintNode *) res;
908 }
909
910 /*
911  * Clip node
912  */
913 struct _ClutterClipNode
914 {
915   ClutterPaintNode parent_instance;
916 };
917
918 /**
919  * ClutterClipNodeClass:
920  *
921  * The <structname>ClutterClipNodeClass</structname> structure is an opaque
922  * type whose members cannot be directly accessed.
923  *
924  * Since: 1.10
925  */
926 struct _ClutterClipNodeClass
927 {
928   ClutterPaintNodeClass parent_class;
929 };
930
931 G_DEFINE_TYPE (ClutterClipNode, clutter_clip_node, CLUTTER_TYPE_PAINT_NODE)
932
933 static gboolean
934 clutter_clip_node_pre_draw (ClutterPaintNode *node)
935 {
936   gboolean retval = FALSE;
937   CoglFramebuffer *fb;
938   guint i;
939
940   if (node->operations == NULL)
941     return FALSE;
942
943   fb = cogl_get_draw_framebuffer ();
944
945   for (i = 0; i < node->operations->len; i++)
946     {
947       const ClutterPaintOperation *op;
948
949       op = &g_array_index (node->operations, ClutterPaintOperation, i);
950
951       switch (op->opcode)
952         {
953         case PAINT_OP_TEX_RECT:
954           cogl_framebuffer_push_rectangle_clip (fb,
955                                                 op->op.texrect[0],
956                                                 op->op.texrect[1],
957                                                 op->op.texrect[2],
958                                                 op->op.texrect[3]);
959           retval = TRUE;
960           break;
961
962         case PAINT_OP_PATH:
963           cogl_framebuffer_push_path_clip (fb, op->op.path);
964           retval = TRUE;
965           break;
966
967         case PAINT_OP_PRIMITIVE:
968         case PAINT_OP_INVALID:
969           break;
970         }
971     }
972
973   return retval;
974 }
975
976 static void
977 clutter_clip_node_post_draw (ClutterPaintNode *node)
978 {
979   CoglFramebuffer *fb;
980   guint i;
981
982   if (node->operations == NULL)
983     return;
984
985   fb = cogl_get_draw_framebuffer ();
986
987   for (i = 0; i < node->operations->len; i++)
988     {
989       const ClutterPaintOperation *op;
990
991       op = &g_array_index (node->operations, ClutterPaintOperation, i);
992
993       switch (op->opcode)
994         {
995         case PAINT_OP_PATH:
996         case PAINT_OP_TEX_RECT:
997           cogl_framebuffer_pop_clip (fb);
998           break;
999
1000         case PAINT_OP_PRIMITIVE:
1001         case PAINT_OP_INVALID:
1002           break;
1003         }
1004     }
1005 }
1006
1007 static void
1008 clutter_clip_node_class_init (ClutterClipNodeClass *klass)
1009 {
1010   ClutterPaintNodeClass *node_class;
1011
1012   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1013   node_class->pre_draw = clutter_clip_node_pre_draw;
1014   node_class->post_draw = clutter_clip_node_post_draw;
1015 }
1016
1017 static void
1018 clutter_clip_node_init (ClutterClipNode *self)
1019 {
1020 }
1021
1022 /**
1023  * clutter_clip_node_new:
1024  *
1025  * Creates a new #ClutterPaintNode that will clip its child
1026  * nodes to the 2D regions added to it.
1027  *
1028  * Return value: (transfer full): the newly created #ClutterPaintNode.
1029  *   Use clutter_paint_node_unref() when done.
1030  *
1031  * Since: 1.10
1032  */
1033 ClutterPaintNode *
1034 clutter_clip_node_new (void)
1035 {
1036   return _clutter_paint_node_create (CLUTTER_TYPE_CLIP_NODE);
1037 }
1038
1039 /*
1040  * ClutterLayerNode (private)
1041  */
1042
1043 #define clutter_layer_node_get_type     _clutter_layer_node_get_type
1044
1045 struct _ClutterLayerNode
1046 {
1047   ClutterPaintNode parent_instance;
1048
1049   cairo_rectangle_t viewport;
1050
1051   CoglMatrix projection;
1052
1053   float fbo_width;
1054   float fbo_height;
1055
1056   CoglPipeline *state;
1057   CoglFramebuffer *offscreen;
1058   CoglTexture *texture;
1059
1060   guint8 opacity;
1061 };
1062
1063 struct _ClutterLayerNodeClass
1064 {
1065   ClutterPaintNodeClass parent_class;
1066 };
1067
1068 G_DEFINE_TYPE (ClutterLayerNode, clutter_layer_node, CLUTTER_TYPE_PAINT_NODE)
1069
1070 static gboolean
1071 clutter_layer_node_pre_draw (ClutterPaintNode *node)
1072 {
1073   ClutterLayerNode *lnode = (ClutterLayerNode *) node;
1074   CoglMatrix matrix;
1075
1076   /* if we were unable to create an offscreen buffer for this node, then
1077    * we simply ignore it
1078    */
1079   if (lnode->offscreen == NULL)
1080     return FALSE;
1081
1082   /* if no geometry was submitted for this node then we simply ignore it */
1083   if (node->operations == NULL)
1084     return FALSE;
1085
1086   /* copy the same modelview from the current framebuffer to the one we
1087    * are going to use
1088    */
1089   cogl_get_modelview_matrix (&matrix);
1090
1091   cogl_push_framebuffer (lnode->offscreen);
1092
1093   cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
1094
1095   cogl_framebuffer_set_viewport (lnode->offscreen,
1096                                  lnode->viewport.x,
1097                                  lnode->viewport.y,
1098                                  lnode->viewport.width,
1099                                  lnode->viewport.height);
1100
1101   cogl_framebuffer_set_projection_matrix (lnode->offscreen,
1102                                           &lnode->projection);
1103
1104   /* clear out the target framebuffer */
1105   cogl_framebuffer_clear4f (lnode->offscreen,
1106                             COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
1107                             0.f, 0.f, 0.f, 0.f);
1108
1109   cogl_push_matrix ();
1110
1111   /* every draw operation after this point will happen an offscreen
1112    * framebuffer
1113    */
1114
1115   return TRUE;
1116 }
1117
1118 static void
1119 clutter_layer_node_post_draw (ClutterPaintNode *node)
1120 {
1121   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1122   CoglFramebuffer *fb;
1123   guint i;
1124
1125   /* switch to the previous framebuffer */
1126   cogl_pop_matrix ();
1127   cogl_pop_framebuffer ();
1128
1129   fb = cogl_get_draw_framebuffer ();
1130
1131   for (i = 0; i < node->operations->len; i++)
1132     {
1133       const ClutterPaintOperation *op;
1134
1135       op = &g_array_index (node->operations, ClutterPaintOperation, i);
1136       switch (op->opcode)
1137         {
1138         case PAINT_OP_INVALID:
1139           break;
1140
1141         case PAINT_OP_TEX_RECT:
1142           /* now we need to paint the texture */
1143           cogl_push_source (lnode->state);
1144           cogl_rectangle_with_texture_coords (op->op.texrect[0],
1145                                               op->op.texrect[1],
1146                                               op->op.texrect[2],
1147                                               op->op.texrect[3],
1148                                               op->op.texrect[4],
1149                                               op->op.texrect[5],
1150                                               op->op.texrect[6],
1151                                               op->op.texrect[7]);
1152           cogl_pop_source ();
1153           break;
1154
1155         case PAINT_OP_PATH:
1156           cogl_push_source (lnode->state);
1157           cogl_path_fill (op->op.path);
1158           cogl_pop_source ();
1159           break;
1160
1161         case PAINT_OP_PRIMITIVE:
1162           cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
1163           break;
1164         }
1165     }
1166 }
1167
1168 static void
1169 clutter_layer_node_finalize (ClutterPaintNode *node)
1170 {
1171   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1172
1173   if (lnode->state != NULL)
1174     cogl_object_unref (lnode->state);
1175
1176   if (lnode->offscreen != NULL)
1177     cogl_object_unref (lnode->offscreen);
1178
1179   CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node);
1180 }
1181
1182 static void
1183 clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
1184 {
1185   ClutterPaintNodeClass *node_class;
1186
1187   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1188   node_class->pre_draw = clutter_layer_node_pre_draw;
1189   node_class->post_draw = clutter_layer_node_post_draw;
1190   node_class->finalize = clutter_layer_node_finalize;
1191 }
1192
1193 static void
1194 clutter_layer_node_init (ClutterLayerNode *self)
1195 {
1196   cogl_matrix_init_identity (&self->projection);
1197 }
1198
1199 /*
1200  * clutter_layer_node_new:
1201  * @projection: the projection matrix to use to set up the layer
1202  * @viewport: (type cairo.Rectangle): the viewport to use to set up the layer
1203  * @width: the width of the layer
1204  * @height: the height of the layer
1205  * @opacity: the opacity to be used when drawing the layer
1206  *
1207  * Creates a new #ClutterLayerNode.
1208  *
1209  * All children of this node will be painted inside a separate
1210  * framebuffer; the framebuffer will then be painted using the
1211  * given @opacity.
1212  *
1213  * Return value: (transfer full): the newly created #ClutterLayerNode.
1214  *   Use clutter_paint_node_unref() when done.
1215  *
1216  * Since: 1.10
1217  */
1218 ClutterPaintNode *
1219 _clutter_layer_node_new (const CoglMatrix        *projection,
1220                          const cairo_rectangle_t *viewport,
1221                          float                    width,
1222                          float                    height,
1223                          guint8                   opacity)
1224 {
1225   ClutterLayerNode *res;
1226   CoglColor color;
1227
1228   res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
1229
1230   res->projection = *projection;
1231   res->viewport = *viewport;
1232   res->fbo_width = width;
1233   res->fbo_height = height;
1234   res->opacity = opacity;
1235
1236   /* the texture backing the FBO */
1237   res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
1238                                              MAX (res->fbo_height, 1),
1239                                              COGL_TEXTURE_NO_SLICING,
1240                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE);
1241
1242   res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture));
1243   if (res->offscreen == NULL)
1244     {
1245       g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
1246
1247       cogl_object_unref (res->texture);
1248       res->texture = NULL;
1249
1250       goto out;
1251     }
1252
1253   cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
1254
1255   /* the pipeline used to paint the texture; we use nearest
1256    * interpolation filters because the texture is always
1257    * going to be painted at a 1:1 texel:pixel ratio
1258    */
1259   res->state = cogl_pipeline_copy (default_texture_pipeline);
1260   cogl_pipeline_set_layer_filters (res->state, 0,
1261                                    COGL_PIPELINE_FILTER_NEAREST,
1262                                    COGL_PIPELINE_FILTER_NEAREST);
1263   cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
1264   cogl_pipeline_set_color (res->state, &color);
1265   cogl_object_unref (res->texture);
1266
1267 out:
1268   return (ClutterPaintNode *) res;
1269 }