cleanup specfile for packaging
[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 struct _ClutterPipelineNodeClass
343 {
344   ClutterPaintNodeClass parent_class;
345 };
346
347 G_DEFINE_TYPE (ClutterPipelineNode, clutter_pipeline_node, CLUTTER_TYPE_PAINT_NODE)
348
349 static void
350 clutter_pipeline_node_finalize (ClutterPaintNode *node)
351 {
352   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
353
354   if (pnode->pipeline != NULL)
355     cogl_object_unref (pnode->pipeline);
356
357   CLUTTER_PAINT_NODE_CLASS (clutter_pipeline_node_parent_class)->finalize (node);
358 }
359
360 static gboolean
361 clutter_pipeline_node_pre_draw (ClutterPaintNode *node)
362 {
363   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
364
365   if (node->operations != NULL &&
366       pnode->pipeline != NULL)
367     {
368       cogl_push_source (pnode->pipeline);
369       return TRUE;
370     }
371
372   return FALSE;
373 }
374
375 static void
376 clutter_pipeline_node_draw (ClutterPaintNode *node)
377 {
378   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
379   guint i;
380
381   if (pnode->pipeline == NULL)
382     return;
383
384   if (node->operations == NULL)
385     return;
386
387   for (i = 0; i < node->operations->len; i++)
388     {
389       const ClutterPaintOperation *op;
390
391       op = &g_array_index (node->operations, ClutterPaintOperation, i);
392
393       switch (op->opcode)
394         {
395         case PAINT_OP_INVALID:
396           break;
397
398         case PAINT_OP_TEX_RECT:
399           cogl_rectangle_with_texture_coords (op->op.texrect[0],
400                                               op->op.texrect[1],
401                                               op->op.texrect[2],
402                                               op->op.texrect[3],
403                                               op->op.texrect[4],
404                                               op->op.texrect[5],
405                                               op->op.texrect[6],
406                                               op->op.texrect[7]);
407           break;
408
409         case PAINT_OP_PATH:
410           cogl_path_fill (op->op.path);
411           break;
412
413         case PAINT_OP_PRIMITIVE:
414           {
415             CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
416
417             cogl_framebuffer_draw_primitive (fb, pnode->pipeline,
418                                              op->op.primitive);
419           }
420           break;
421         }
422     }
423 }
424
425 static void
426 clutter_pipeline_node_post_draw (ClutterPaintNode *node)
427 {
428   cogl_pop_source ();
429 }
430
431 static JsonNode *
432 clutter_pipeline_node_serialize (ClutterPaintNode *node)
433 {
434   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
435   JsonBuilder *builder;
436   CoglColor color;
437   JsonNode *res;
438
439   if (pnode->pipeline == NULL)
440     return json_node_new (JSON_NODE_NULL);
441
442   builder = json_builder_new ();
443   json_builder_begin_object (builder);
444
445   cogl_pipeline_get_color (pnode->pipeline, &color);
446   json_builder_set_member_name (builder, "color");
447   json_builder_begin_array (builder);
448   json_builder_add_double_value (builder, cogl_color_get_red (&color));
449   json_builder_add_double_value (builder, cogl_color_get_green (&color));
450   json_builder_add_double_value (builder, cogl_color_get_blue (&color));
451   json_builder_add_double_value (builder, cogl_color_get_alpha (&color));
452   json_builder_end_array (builder);
453
454 #if 0
455   json_builder_set_member_name (builder, "layers");
456   json_builder_begin_array (builder);
457   cogl_pipeline_foreach_layer (pnode->pipeline,
458                                clutter_pipeline_node_serialize_layer,
459                                builder);
460   json_builder_end_array (builder);
461 #endif
462
463   json_builder_end_object (builder);
464
465   res = json_builder_get_root (builder);
466   g_object_unref (builder);
467
468   return res;
469 }
470
471 static void
472 clutter_pipeline_node_class_init (ClutterPipelineNodeClass *klass)
473 {
474   ClutterPaintNodeClass *node_class;
475
476   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
477   node_class->pre_draw = clutter_pipeline_node_pre_draw;
478   node_class->draw = clutter_pipeline_node_draw;
479   node_class->post_draw = clutter_pipeline_node_post_draw;
480   node_class->finalize = clutter_pipeline_node_finalize;
481   node_class->serialize = clutter_pipeline_node_serialize;
482 }
483
484 static void
485 clutter_pipeline_node_init (ClutterPipelineNode *self)
486 {
487 }
488
489 /**
490  * clutter_pipeline_node_new:
491  * @pipeline: (allow-none): a Cogl pipeline state object, or %NULL
492  *
493  * Creates a new #ClutterPaintNode that will use the @pipeline to
494  * paint its contents.
495  *
496  * This function will acquire a reference on the passed @pipeline,
497  * so it is safe to call cogl_object_unref() when it returns.
498  *
499  * Return value: (transfer full): the newly created #ClutterPaintNode.
500  *   Use clutter_paint_node_unref() when done.
501  *
502  * Since: 1.10
503  */
504 ClutterPaintNode *
505 clutter_pipeline_node_new (CoglPipeline *pipeline)
506 {
507   ClutterPipelineNode *res;
508
509   g_return_val_if_fail (pipeline == NULL || cogl_is_pipeline (pipeline), NULL);
510
511   res = _clutter_paint_node_create (CLUTTER_TYPE_PIPELINE_NODE);
512
513   if (pipeline != NULL)
514     res->pipeline = cogl_object_ref (pipeline);
515
516   return (ClutterPaintNode *) res;
517 }
518
519 /*
520  * Color node
521  */
522
523 /**
524  * ClutterColorNode:
525  *
526  * The <structname>ClutterColorNode</structname> structure is an opaque
527  * type whose members cannot be directly accessed.
528  *
529  * Since: 1.10
530  */
531 struct _ClutterColorNode
532 {
533   ClutterPipelineNode parent_instance;
534 };
535
536 /**
537  * ClutterColorNode:
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 /**
607  * ClutterTextureNode:
608  *
609  * The <structname>ClutterTextureNode</structname> structure is an opaque
610  * type whose members cannot be directly accessed.
611  *
612  * Since: 1.10
613  */
614 struct _ClutterTextureNode
615 {
616   ClutterPipelineNode parent_instance;
617 };
618
619 /**
620  * ClutterTextureNode:
621  *
622  * The <structname>ClutterTextureNodeClass</structname> structure is an
623  * opaque type whose members cannot be directly accessed.
624  *
625  * Since: 1.10
626  */
627 struct _ClutterTextureNodeClass
628 {
629   ClutterPipelineNodeClass parent_class;
630 };
631
632 G_DEFINE_TYPE (ClutterTextureNode, clutter_texture_node, CLUTTER_TYPE_PIPELINE_NODE)
633
634 static void
635 clutter_texture_node_class_init (ClutterTextureNodeClass *klass)
636 {
637 }
638
639 static void
640 clutter_texture_node_init (ClutterTextureNode *self)
641 {
642   ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (self);
643
644   g_assert (default_texture_pipeline != NULL);
645   pnode->pipeline = cogl_pipeline_copy (default_texture_pipeline);
646 }
647
648 static CoglPipelineFilter
649 clutter_scaling_filter_to_cogl_pipeline_filter (ClutterScalingFilter filter)
650 {
651   switch (filter)
652     {
653     case CLUTTER_SCALING_FILTER_NEAREST:
654       return COGL_PIPELINE_FILTER_NEAREST;
655
656     case CLUTTER_SCALING_FILTER_LINEAR:
657       return COGL_PIPELINE_FILTER_LINEAR;
658
659     case CLUTTER_SCALING_FILTER_TRILINEAR:
660       return COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR;
661     }
662
663   return COGL_PIPELINE_FILTER_LINEAR;
664 }
665
666 /**
667  * clutter_texture_node_new:
668  * @texture: a #CoglTexture
669  * @color: a #ClutterColor
670  * @min_filter: the minification filter for the texture
671  * @mag_filter: the magnification filter for the texture
672  *
673  * Creates a new #ClutterPaintNode that will paint the passed @texture.
674  *
675  * This function will take a reference on @texture, so it is safe to
676  * call cogl_object_unref() on @texture when it returns.
677  *
678  * Return value: (transfer full): the newly created #ClutterPaintNode.
679  *   Use clutter_paint_node_unref() when done
680  *
681  * Since: 1.10
682  */
683 ClutterPaintNode *
684 clutter_texture_node_new (CoglTexture          *texture,
685                           const ClutterColor   *color,
686                           ClutterScalingFilter  min_filter,
687                           ClutterScalingFilter  mag_filter)
688 {
689   ClutterPipelineNode *tnode;
690   CoglColor cogl_color;
691   CoglPipelineFilter min_f, mag_f;
692
693   g_return_val_if_fail (cogl_is_texture (texture), NULL);
694
695   tnode = _clutter_paint_node_create (CLUTTER_TYPE_TEXTURE_NODE);
696
697   cogl_pipeline_set_layer_texture (tnode->pipeline, 0, texture);
698
699   min_f = clutter_scaling_filter_to_cogl_pipeline_filter (min_filter);
700   mag_f = clutter_scaling_filter_to_cogl_pipeline_filter (mag_filter);
701   cogl_pipeline_set_layer_filters (tnode->pipeline, 0, min_f, mag_f);
702
703   cogl_color_init_from_4ub (&cogl_color,
704                             color->red,
705                             color->green,
706                             color->blue,
707                             color->alpha);
708   cogl_color_premultiply (&cogl_color);
709   cogl_pipeline_set_color (tnode->pipeline, &cogl_color);
710
711   return (ClutterPaintNode *) tnode;
712 }
713
714 /*
715  * Text node
716  */
717
718 struct _ClutterTextNode
719 {
720   ClutterPaintNode parent_instance;
721
722   PangoLayout *layout;
723   CoglColor color;
724 };
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 struct _ClutterClipNodeClass
919 {
920   ClutterPaintNodeClass parent_class;
921 };
922
923 G_DEFINE_TYPE (ClutterClipNode, clutter_clip_node, CLUTTER_TYPE_PAINT_NODE)
924
925 static gboolean
926 clutter_clip_node_pre_draw (ClutterPaintNode *node)
927 {
928   gboolean retval = FALSE;
929   CoglFramebuffer *fb;
930   guint i;
931
932   if (node->operations == NULL)
933     return FALSE;
934
935   fb = cogl_get_draw_framebuffer ();
936
937   for (i = 0; i < node->operations->len; i++)
938     {
939       const ClutterPaintOperation *op;
940
941       op = &g_array_index (node->operations, ClutterPaintOperation, i);
942
943       switch (op->opcode)
944         {
945         case PAINT_OP_TEX_RECT:
946           cogl_framebuffer_push_rectangle_clip (fb,
947                                                 op->op.texrect[0],
948                                                 op->op.texrect[1],
949                                                 op->op.texrect[2],
950                                                 op->op.texrect[3]);
951           retval = TRUE;
952           break;
953
954         case PAINT_OP_PATH:
955           cogl_framebuffer_push_path_clip (fb, op->op.path);
956           retval = TRUE;
957           break;
958
959         case PAINT_OP_PRIMITIVE:
960         case PAINT_OP_INVALID:
961           break;
962         }
963     }
964
965   return retval;
966 }
967
968 static void
969 clutter_clip_node_post_draw (ClutterPaintNode *node)
970 {
971   CoglFramebuffer *fb;
972   guint i;
973
974   if (node->operations == NULL)
975     return;
976
977   fb = cogl_get_draw_framebuffer ();
978
979   for (i = 0; i < node->operations->len; i++)
980     {
981       const ClutterPaintOperation *op;
982
983       op = &g_array_index (node->operations, ClutterPaintOperation, i);
984
985       switch (op->opcode)
986         {
987         case PAINT_OP_PATH:
988         case PAINT_OP_TEX_RECT:
989           cogl_framebuffer_pop_clip (fb);
990           break;
991
992         case PAINT_OP_PRIMITIVE:
993         case PAINT_OP_INVALID:
994           break;
995         }
996     }
997 }
998
999 static void
1000 clutter_clip_node_class_init (ClutterClipNodeClass *klass)
1001 {
1002   ClutterPaintNodeClass *node_class;
1003
1004   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1005   node_class->pre_draw = clutter_clip_node_pre_draw;
1006   node_class->post_draw = clutter_clip_node_post_draw;
1007 }
1008
1009 static void
1010 clutter_clip_node_init (ClutterClipNode *self)
1011 {
1012 }
1013
1014 /**
1015  * clutter_clip_node_new:
1016  *
1017  * Creates a new #ClutterPaintNode that will clip its child
1018  * nodes to the 2D regions added to it.
1019  *
1020  * Return value: (transfer full): the newly created #ClutterPaintNode.
1021  *   Use clutter_paint_node_unref() when done.
1022  *
1023  * Since: 1.10
1024  */
1025 ClutterPaintNode *
1026 clutter_clip_node_new (void)
1027 {
1028   return _clutter_paint_node_create (CLUTTER_TYPE_CLIP_NODE);
1029 }
1030
1031 /*
1032  * ClutterLayerNode (private)
1033  */
1034
1035 #define clutter_layer_node_get_type     _clutter_layer_node_get_type
1036
1037 struct _ClutterLayerNode
1038 {
1039   ClutterPaintNode parent_instance;
1040
1041   cairo_rectangle_t viewport;
1042
1043   CoglMatrix projection;
1044
1045   float fbo_width;
1046   float fbo_height;
1047
1048   CoglPipeline *state;
1049   CoglFramebuffer *offscreen;
1050   CoglTexture *texture;
1051
1052   guint8 opacity;
1053 };
1054
1055 struct _ClutterLayerNodeClass
1056 {
1057   ClutterPaintNodeClass parent_class;
1058 };
1059
1060 G_DEFINE_TYPE (ClutterLayerNode, clutter_layer_node, CLUTTER_TYPE_PAINT_NODE)
1061
1062 static gboolean
1063 clutter_layer_node_pre_draw (ClutterPaintNode *node)
1064 {
1065   ClutterLayerNode *lnode = (ClutterLayerNode *) node;
1066   CoglMatrix matrix;
1067
1068   /* if we were unable to create an offscreen buffer for this node, then
1069    * we simply ignore it
1070    */
1071   if (lnode->offscreen == NULL)
1072     return FALSE;
1073
1074   /* if no geometry was submitted for this node then we simply ignore it */
1075   if (node->operations == NULL)
1076     return FALSE;
1077
1078   /* copy the same modelview from the current framebuffer to the one we
1079    * are going to use
1080    */
1081   cogl_get_modelview_matrix (&matrix);
1082
1083   cogl_push_framebuffer (lnode->offscreen);
1084
1085   cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
1086
1087   cogl_framebuffer_set_viewport (lnode->offscreen,
1088                                  lnode->viewport.x,
1089                                  lnode->viewport.y,
1090                                  lnode->viewport.width,
1091                                  lnode->viewport.height);
1092
1093   cogl_framebuffer_set_projection_matrix (lnode->offscreen,
1094                                           &lnode->projection);
1095
1096   /* clear out the target framebuffer */
1097   cogl_framebuffer_clear4f (lnode->offscreen,
1098                             COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
1099                             0.f, 0.f, 0.f, 0.f);
1100
1101   cogl_push_matrix ();
1102
1103   /* every draw operation after this point will happen an offscreen
1104    * framebuffer
1105    */
1106
1107   return TRUE;
1108 }
1109
1110 static void
1111 clutter_layer_node_post_draw (ClutterPaintNode *node)
1112 {
1113   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1114   CoglFramebuffer *fb;
1115   guint i;
1116
1117   /* switch to the previous framebuffer */
1118   cogl_pop_matrix ();
1119   cogl_pop_framebuffer ();
1120
1121   fb = cogl_get_draw_framebuffer ();
1122
1123   for (i = 0; i < node->operations->len; i++)
1124     {
1125       const ClutterPaintOperation *op;
1126
1127       op = &g_array_index (node->operations, ClutterPaintOperation, i);
1128       switch (op->opcode)
1129         {
1130         case PAINT_OP_INVALID:
1131           break;
1132
1133         case PAINT_OP_TEX_RECT:
1134           /* now we need to paint the texture */
1135           cogl_push_source (lnode->state);
1136           cogl_rectangle_with_texture_coords (op->op.texrect[0],
1137                                               op->op.texrect[1],
1138                                               op->op.texrect[2],
1139                                               op->op.texrect[3],
1140                                               op->op.texrect[4],
1141                                               op->op.texrect[5],
1142                                               op->op.texrect[6],
1143                                               op->op.texrect[7]);
1144           cogl_pop_source ();
1145           break;
1146
1147         case PAINT_OP_PATH:
1148           cogl_push_source (lnode->state);
1149           cogl_path_fill (op->op.path);
1150           cogl_pop_source ();
1151           break;
1152
1153         case PAINT_OP_PRIMITIVE:
1154           cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
1155           break;
1156         }
1157     }
1158 }
1159
1160 static void
1161 clutter_layer_node_finalize (ClutterPaintNode *node)
1162 {
1163   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1164
1165   if (lnode->state != NULL)
1166     cogl_object_unref (lnode->state);
1167
1168   if (lnode->offscreen != NULL)
1169     cogl_object_unref (lnode->offscreen);
1170
1171   CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node);
1172 }
1173
1174 static void
1175 clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
1176 {
1177   ClutterPaintNodeClass *node_class;
1178
1179   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1180   node_class->pre_draw = clutter_layer_node_pre_draw;
1181   node_class->post_draw = clutter_layer_node_post_draw;
1182   node_class->finalize = clutter_layer_node_finalize;
1183 }
1184
1185 static void
1186 clutter_layer_node_init (ClutterLayerNode *self)
1187 {
1188   cogl_matrix_init_identity (&self->projection);
1189 }
1190
1191 /*
1192  * clutter_layer_node_new:
1193  * @projection: the projection matrix to use to set up the layer
1194  * @viewport: (type cairo.Rectangle): the viewport to use to set up the layer
1195  * @width: the width of the layer
1196  * @height: the height of the layer
1197  * @opacity: the opacity to be used when drawing the layer
1198  *
1199  * Creates a new #ClutterLayerNode.
1200  *
1201  * All children of this node will be painted inside a separate
1202  * framebuffer; the framebuffer will then be painted using the
1203  * given @opacity.
1204  *
1205  * Return value: (transfer full): the newly created #ClutterLayerNode.
1206  *   Use clutter_paint_node_unref() when done.
1207  *
1208  * Since: 1.10
1209  */
1210 ClutterPaintNode *
1211 _clutter_layer_node_new (const CoglMatrix        *projection,
1212                          const cairo_rectangle_t *viewport,
1213                          float                    width,
1214                          float                    height,
1215                          guint8                   opacity)
1216 {
1217   ClutterLayerNode *res;
1218   CoglColor color;
1219
1220   res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
1221
1222   res->projection = *projection;
1223   res->viewport = *viewport;
1224   res->fbo_width = width;
1225   res->fbo_height = height;
1226   res->opacity = opacity;
1227
1228   /* the texture backing the FBO */
1229   res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
1230                                              MAX (res->fbo_height, 1),
1231                                              COGL_TEXTURE_NO_SLICING,
1232                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE);
1233
1234   res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture));
1235   if (res->offscreen == NULL)
1236     {
1237       g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
1238
1239       cogl_object_unref (res->texture);
1240       res->texture = NULL;
1241
1242       goto out;
1243     }
1244
1245   cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
1246
1247   /* the pipeline used to paint the texture; we use nearest
1248    * interpolation filters because the texture is always
1249    * going to be painted at a 1:1 texel:pixel ratio
1250    */
1251   res->state = cogl_pipeline_copy (default_texture_pipeline);
1252   cogl_pipeline_set_layer_filters (res->state, 0,
1253                                    COGL_PIPELINE_FILTER_NEAREST,
1254                                    COGL_PIPELINE_FILTER_NEAREST);
1255   cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
1256   cogl_pipeline_set_color (res->state, &color);
1257   cogl_object_unref (res->texture);
1258
1259 out:
1260   return (ClutterPaintNode *) res;
1261 }