update to 1.10.4
[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_VERSION_CHECK (1, 30, 0)
824   if (pango_layout_get_character_count (tnode->layout) > 12)
825     {
826       const char *text = pango_layout_get_text (tnode->layout);
827       char *str;
828
829       str = g_strndup (text, 12);
830       json_builder_add_string_value (builder, str);
831       g_free (str);
832     }
833   else
834 #endif /* PANGO_VERSION_CHECK (1, 30, 0) */
835     json_builder_add_string_value (builder, pango_layout_get_text (tnode->layout));
836
837   json_builder_set_member_name (builder, "color");
838   json_builder_begin_array (builder);
839   json_builder_add_double_value (builder, cogl_color_get_red (&tnode->color));
840   json_builder_add_double_value (builder, cogl_color_get_green (&tnode->color));
841   json_builder_add_double_value (builder, cogl_color_get_blue (&tnode->color));
842   json_builder_add_double_value (builder, cogl_color_get_alpha (&tnode->color));
843   json_builder_end_array (builder);
844
845   json_builder_end_object (builder);
846
847   res = json_builder_get_root (builder);
848   g_object_unref (builder);
849
850   return res;
851 }
852
853 static void
854 clutter_text_node_class_init (ClutterTextNodeClass *klass)
855 {
856   ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
857
858   node_class->pre_draw = clutter_text_node_pre_draw;
859   node_class->draw = clutter_text_node_draw;
860   node_class->finalize = clutter_text_node_finalize;
861   node_class->serialize = clutter_text_node_serialize;
862 }
863
864 static void
865 clutter_text_node_init (ClutterTextNode *self)
866 {
867   cogl_color_init_from_4f (&self->color, 0.0, 0.0, 0.0, 1.0);
868 }
869
870 /**
871  * clutter_text_node_new:
872  * @layout: (allow-none): a #PangoLayout, or %NULL
873  * @color: (allow-none): the color used to paint the layout,
874  *   or %NULL
875  *
876  * Creates a new #ClutterPaintNode that will paint a #PangoLayout
877  * with the given color.
878  *
879  * This function takes a reference on the passed @layout, so it
880  * is safe to call g_object_unref() after it returns.
881  *
882  * Return value: (transfer full): the newly created #ClutterPaintNode.
883  *   Use clutter_paint_node_unref() when done
884  *
885  * Since: 1.10
886  */
887 ClutterPaintNode *
888 clutter_text_node_new (PangoLayout        *layout,
889                        const ClutterColor *color)
890 {
891   ClutterTextNode *res;
892
893   g_return_val_if_fail (layout == NULL || PANGO_IS_LAYOUT (layout), NULL);
894
895   res = _clutter_paint_node_create (CLUTTER_TYPE_TEXT_NODE);
896
897   if (layout != NULL)
898     res->layout = g_object_ref (layout);
899
900   if (color != NULL)
901     {
902       cogl_color_init_from_4ub (&res->color,
903                                 color->red,
904                                 color->green,
905                                 color->blue,
906                                 color->alpha);
907     }
908
909   return (ClutterPaintNode *) res;
910 }
911
912 /*
913  * Clip node
914  */
915 struct _ClutterClipNode
916 {
917   ClutterPaintNode parent_instance;
918 };
919
920 /**
921  * ClutterClipNodeClass:
922  *
923  * The <structname>ClutterClipNodeClass</structname> structure is an opaque
924  * type whose members cannot be directly accessed.
925  *
926  * Since: 1.10
927  */
928 struct _ClutterClipNodeClass
929 {
930   ClutterPaintNodeClass parent_class;
931 };
932
933 G_DEFINE_TYPE (ClutterClipNode, clutter_clip_node, CLUTTER_TYPE_PAINT_NODE)
934
935 static gboolean
936 clutter_clip_node_pre_draw (ClutterPaintNode *node)
937 {
938   gboolean retval = FALSE;
939   CoglFramebuffer *fb;
940   guint i;
941
942   if (node->operations == NULL)
943     return FALSE;
944
945   fb = cogl_get_draw_framebuffer ();
946
947   for (i = 0; i < node->operations->len; i++)
948     {
949       const ClutterPaintOperation *op;
950
951       op = &g_array_index (node->operations, ClutterPaintOperation, i);
952
953       switch (op->opcode)
954         {
955         case PAINT_OP_TEX_RECT:
956           cogl_framebuffer_push_rectangle_clip (fb,
957                                                 op->op.texrect[0],
958                                                 op->op.texrect[1],
959                                                 op->op.texrect[2],
960                                                 op->op.texrect[3]);
961           retval = TRUE;
962           break;
963
964         case PAINT_OP_PATH:
965           cogl_framebuffer_push_path_clip (fb, op->op.path);
966           retval = TRUE;
967           break;
968
969         case PAINT_OP_PRIMITIVE:
970         case PAINT_OP_INVALID:
971           break;
972         }
973     }
974
975   return retval;
976 }
977
978 static void
979 clutter_clip_node_post_draw (ClutterPaintNode *node)
980 {
981   CoglFramebuffer *fb;
982   guint i;
983
984   if (node->operations == NULL)
985     return;
986
987   fb = cogl_get_draw_framebuffer ();
988
989   for (i = 0; i < node->operations->len; i++)
990     {
991       const ClutterPaintOperation *op;
992
993       op = &g_array_index (node->operations, ClutterPaintOperation, i);
994
995       switch (op->opcode)
996         {
997         case PAINT_OP_PATH:
998         case PAINT_OP_TEX_RECT:
999           cogl_framebuffer_pop_clip (fb);
1000           break;
1001
1002         case PAINT_OP_PRIMITIVE:
1003         case PAINT_OP_INVALID:
1004           break;
1005         }
1006     }
1007 }
1008
1009 static void
1010 clutter_clip_node_class_init (ClutterClipNodeClass *klass)
1011 {
1012   ClutterPaintNodeClass *node_class;
1013
1014   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1015   node_class->pre_draw = clutter_clip_node_pre_draw;
1016   node_class->post_draw = clutter_clip_node_post_draw;
1017 }
1018
1019 static void
1020 clutter_clip_node_init (ClutterClipNode *self)
1021 {
1022 }
1023
1024 /**
1025  * clutter_clip_node_new:
1026  *
1027  * Creates a new #ClutterPaintNode that will clip its child
1028  * nodes to the 2D regions added to it.
1029  *
1030  * Return value: (transfer full): the newly created #ClutterPaintNode.
1031  *   Use clutter_paint_node_unref() when done.
1032  *
1033  * Since: 1.10
1034  */
1035 ClutterPaintNode *
1036 clutter_clip_node_new (void)
1037 {
1038   return _clutter_paint_node_create (CLUTTER_TYPE_CLIP_NODE);
1039 }
1040
1041 /*
1042  * ClutterLayerNode (private)
1043  */
1044
1045 #define clutter_layer_node_get_type     _clutter_layer_node_get_type
1046
1047 struct _ClutterLayerNode
1048 {
1049   ClutterPaintNode parent_instance;
1050
1051   cairo_rectangle_t viewport;
1052
1053   CoglMatrix projection;
1054
1055   float fbo_width;
1056   float fbo_height;
1057
1058   CoglPipeline *state;
1059   CoglFramebuffer *offscreen;
1060   CoglTexture *texture;
1061
1062   guint8 opacity;
1063 };
1064
1065 struct _ClutterLayerNodeClass
1066 {
1067   ClutterPaintNodeClass parent_class;
1068 };
1069
1070 G_DEFINE_TYPE (ClutterLayerNode, clutter_layer_node, CLUTTER_TYPE_PAINT_NODE)
1071
1072 static gboolean
1073 clutter_layer_node_pre_draw (ClutterPaintNode *node)
1074 {
1075   ClutterLayerNode *lnode = (ClutterLayerNode *) node;
1076   CoglMatrix matrix;
1077
1078   /* if we were unable to create an offscreen buffer for this node, then
1079    * we simply ignore it
1080    */
1081   if (lnode->offscreen == NULL)
1082     return FALSE;
1083
1084   /* if no geometry was submitted for this node then we simply ignore it */
1085   if (node->operations == NULL)
1086     return FALSE;
1087
1088   /* copy the same modelview from the current framebuffer to the one we
1089    * are going to use
1090    */
1091   cogl_get_modelview_matrix (&matrix);
1092
1093   cogl_push_framebuffer (lnode->offscreen);
1094
1095   cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
1096
1097   cogl_framebuffer_set_viewport (lnode->offscreen,
1098                                  lnode->viewport.x,
1099                                  lnode->viewport.y,
1100                                  lnode->viewport.width,
1101                                  lnode->viewport.height);
1102
1103   cogl_framebuffer_set_projection_matrix (lnode->offscreen,
1104                                           &lnode->projection);
1105
1106   /* clear out the target framebuffer */
1107   cogl_framebuffer_clear4f (lnode->offscreen,
1108                             COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
1109                             0.f, 0.f, 0.f, 0.f);
1110
1111   cogl_push_matrix ();
1112
1113   /* every draw operation after this point will happen an offscreen
1114    * framebuffer
1115    */
1116
1117   return TRUE;
1118 }
1119
1120 static void
1121 clutter_layer_node_post_draw (ClutterPaintNode *node)
1122 {
1123   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1124   CoglFramebuffer *fb;
1125   guint i;
1126
1127   /* switch to the previous framebuffer */
1128   cogl_pop_matrix ();
1129   cogl_pop_framebuffer ();
1130
1131   fb = cogl_get_draw_framebuffer ();
1132
1133   for (i = 0; i < node->operations->len; i++)
1134     {
1135       const ClutterPaintOperation *op;
1136
1137       op = &g_array_index (node->operations, ClutterPaintOperation, i);
1138       switch (op->opcode)
1139         {
1140         case PAINT_OP_INVALID:
1141           break;
1142
1143         case PAINT_OP_TEX_RECT:
1144           /* now we need to paint the texture */
1145           cogl_push_source (lnode->state);
1146           cogl_rectangle_with_texture_coords (op->op.texrect[0],
1147                                               op->op.texrect[1],
1148                                               op->op.texrect[2],
1149                                               op->op.texrect[3],
1150                                               op->op.texrect[4],
1151                                               op->op.texrect[5],
1152                                               op->op.texrect[6],
1153                                               op->op.texrect[7]);
1154           cogl_pop_source ();
1155           break;
1156
1157         case PAINT_OP_PATH:
1158           cogl_push_source (lnode->state);
1159           cogl_path_fill (op->op.path);
1160           cogl_pop_source ();
1161           break;
1162
1163         case PAINT_OP_PRIMITIVE:
1164           cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
1165           break;
1166         }
1167     }
1168 }
1169
1170 static void
1171 clutter_layer_node_finalize (ClutterPaintNode *node)
1172 {
1173   ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
1174
1175   if (lnode->state != NULL)
1176     cogl_object_unref (lnode->state);
1177
1178   if (lnode->offscreen != NULL)
1179     cogl_object_unref (lnode->offscreen);
1180
1181   CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node);
1182 }
1183
1184 static void
1185 clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
1186 {
1187   ClutterPaintNodeClass *node_class;
1188
1189   node_class = CLUTTER_PAINT_NODE_CLASS (klass);
1190   node_class->pre_draw = clutter_layer_node_pre_draw;
1191   node_class->post_draw = clutter_layer_node_post_draw;
1192   node_class->finalize = clutter_layer_node_finalize;
1193 }
1194
1195 static void
1196 clutter_layer_node_init (ClutterLayerNode *self)
1197 {
1198   cogl_matrix_init_identity (&self->projection);
1199 }
1200
1201 /*
1202  * clutter_layer_node_new:
1203  * @projection: the projection matrix to use to set up the layer
1204  * @viewport: (type cairo.Rectangle): the viewport to use to set up the layer
1205  * @width: the width of the layer
1206  * @height: the height of the layer
1207  * @opacity: the opacity to be used when drawing the layer
1208  *
1209  * Creates a new #ClutterLayerNode.
1210  *
1211  * All children of this node will be painted inside a separate
1212  * framebuffer; the framebuffer will then be painted using the
1213  * given @opacity.
1214  *
1215  * Return value: (transfer full): the newly created #ClutterLayerNode.
1216  *   Use clutter_paint_node_unref() when done.
1217  *
1218  * Since: 1.10
1219  */
1220 ClutterPaintNode *
1221 _clutter_layer_node_new (const CoglMatrix        *projection,
1222                          const cairo_rectangle_t *viewport,
1223                          float                    width,
1224                          float                    height,
1225                          guint8                   opacity)
1226 {
1227   ClutterLayerNode *res;
1228   CoglColor color;
1229
1230   res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
1231
1232   res->projection = *projection;
1233   res->viewport = *viewport;
1234   res->fbo_width = width;
1235   res->fbo_height = height;
1236   res->opacity = opacity;
1237
1238   /* the texture backing the FBO */
1239   res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
1240                                              MAX (res->fbo_height, 1),
1241                                              COGL_TEXTURE_NO_SLICING,
1242                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE);
1243
1244   res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture));
1245   if (res->offscreen == NULL)
1246     {
1247       g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
1248
1249       cogl_object_unref (res->texture);
1250       res->texture = NULL;
1251
1252       goto out;
1253     }
1254
1255   cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
1256
1257   /* the pipeline used to paint the texture; we use nearest
1258    * interpolation filters because the texture is always
1259    * going to be painted at a 1:1 texel:pixel ratio
1260    */
1261   res->state = cogl_pipeline_copy (default_texture_pipeline);
1262   cogl_pipeline_set_layer_filters (res->state, 0,
1263                                    COGL_PIPELINE_FILTER_NEAREST,
1264                                    COGL_PIPELINE_FILTER_NEAREST);
1265   cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
1266   cogl_pipeline_set_color (res->state, &color);
1267   cogl_object_unref (res->texture);
1268
1269 out:
1270   return (ClutterPaintNode *) res;
1271 }