Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-texture.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22  *
23  *
24  */
25
26 /**
27  * SECTION:clutter-texture
28  * @short_description: An actor for displaying and manipulating images.
29  *
30  * #ClutterTexture is a base class for displaying and manipulating pixel
31  * buffer type data.
32  *
33  * The clutter_texture_set_from_rgb_data() and
34  * clutter_texture_set_from_file() functions are used to copy image
35  * data into texture memory and subsequently realize the texture.
36  *
37  * Note: a ClutterTexture will scale its contents to fit the bounding
38  * box requested using clutter_actor_set_size(). To display an area of
39  * a texture without scaling, you should set the clip area using
40  * clutter_actor_set_clip().
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 /* This file depends on the glib enum types which aren't exposed
48  * by cogl.h when COGL_ENABLE_EXPERIMENTAL_2_0_API is defined.
49  *
50  * Undefining COGL_ENABLE_EXPERIMENTAL_2_0_API will still expose
51  * us experimental api but will also expose Cogl 1.x api too...
52  */
53 #undef COGL_ENABLE_EXPERIMENTAL_2_0_API
54 #include <cogl/cogl.h>
55
56 #define CLUTTER_ENABLE_EXPERIMENTAL_API
57
58 /* sadly, we are still using ClutterShader internally */
59 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
60
61 #include "clutter-texture.h"
62
63 #include "clutter-actor-private.h"
64 #include "clutter-color.h"
65 #include "clutter-debug.h"
66 #include "clutter-enum-types.h"
67 #include "clutter-feature.h"
68 #include "clutter-main.h"
69 #include "clutter-marshal.h"
70 #include "clutter-private.h"
71 #include "clutter-scriptable.h"
72 #include "clutter-stage-private.h"
73
74 #include "deprecated/clutter-shader.h"
75 #include "deprecated/clutter-texture.h"
76 #include "deprecated/clutter-util.h"
77
78 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
79
80 G_DEFINE_TYPE_WITH_CODE (ClutterTexture,
81                          clutter_texture,
82                          CLUTTER_TYPE_ACTOR,
83                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
84                                                 clutter_scriptable_iface_init));
85
86 #define CLUTTER_TEXTURE_GET_PRIVATE(obj)        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXTURE, ClutterTexturePrivate))
87
88 typedef struct _ClutterTextureAsyncData ClutterTextureAsyncData;
89
90 struct _ClutterTexturePrivate
91 {
92   gint image_width;
93   gint image_height;
94
95   CoglPipeline *pipeline;
96
97   ClutterActor *fbo_source;
98   CoglHandle fbo_handle;
99
100   CoglPipeline *pick_pipeline;
101
102   gchar *filename;
103
104   ClutterTextureAsyncData *async_data;
105
106   guint no_slice : 1;
107   guint sync_actor_size : 1;
108   guint repeat_x : 1;
109   guint repeat_y : 1;
110   guint keep_aspect_ratio : 1;
111   guint load_size_async : 1;
112   guint load_data_async : 1;
113   guint load_async_set : 1;  /* used to make load_async possible */
114   guint pick_with_alpha : 1;
115   guint pick_with_alpha_supported : 1;
116   guint seen_create_pick_pipeline_warning : 1;
117 };
118
119 #define ASYNC_STATE_LOCKED      1
120 #define ASYNC_STATE_CANCELLED   2
121 #define ASYNC_STATE_QUEUED      3
122
123 struct _ClutterTextureAsyncData
124 {
125   /* The texture for which the data is being loaded */
126   ClutterTexture *texture;
127
128   gchar *load_filename;
129   CoglHandle load_bitmap;
130
131   guint load_idle;
132
133   GError *load_error;
134
135   gint state;
136 };
137
138 static inline void
139 clutter_texture_async_data_lock (ClutterTextureAsyncData *data)
140 {
141   g_bit_lock (&data->state, 0);
142 }
143
144 static inline void
145 clutter_texture_async_data_unlock (ClutterTextureAsyncData *data)
146 {
147   g_bit_unlock (&data->state, 0);
148 }
149
150 enum
151 {
152   PROP_0,
153   PROP_NO_SLICE,
154   PROP_MAX_TILE_WASTE,
155   PROP_PIXEL_FORMAT,
156   PROP_SYNC_SIZE,
157   PROP_REPEAT_Y,
158   PROP_REPEAT_X,
159   PROP_FILTER_QUALITY,
160   PROP_COGL_TEXTURE,
161   PROP_COGL_MATERIAL,
162   PROP_FILENAME,
163   PROP_KEEP_ASPECT_RATIO,
164   PROP_LOAD_ASYNC,
165   PROP_LOAD_DATA_ASYNC,
166   PROP_PICK_WITH_ALPHA,
167
168   PROP_LAST
169 };
170
171 static GParamSpec *obj_props[PROP_LAST];
172
173 enum
174 {
175   SIZE_CHANGE,
176   PIXBUF_CHANGE,
177   LOAD_SUCCESS,
178   LOAD_FINISHED,
179   LAST_SIGNAL
180 };
181
182 static int texture_signals[LAST_SIGNAL] = { 0 };
183
184 static GThreadPool *async_thread_pool = NULL;
185 static guint        repaint_upload_func = 0;
186 static GList       *upload_list = NULL;
187 static GMutex       upload_list_mutex;
188
189 static CoglPipeline *texture_template_pipeline = NULL;
190
191 static void
192 texture_fbo_free_resources (ClutterTexture *texture);
193
194 GQuark
195 clutter_texture_error_quark (void)
196 {
197   return g_quark_from_static_string ("clutter-texture-error-quark");
198 }
199
200 static const struct
201 {
202   gint min_filter;
203   gint mag_filter;
204 }
205 clutter_texture_quality_filters[] =
206   {
207     /* CLUTTER_TEXTURE_QUALITY_LOW */
208     { COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST },
209
210     /* CLUTTER_TEXTURE_QUALITY_MEDIUM */
211     { COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR },
212
213     /* CLUTTER_TEXTURE_QUALITY_HIGH */
214     { COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, COGL_PIPELINE_FILTER_LINEAR }
215   };
216
217 static inline void
218 clutter_texture_quality_to_filters (ClutterTextureQuality  quality,
219                                     gint                  *min_filter_p,
220                                     gint                  *mag_filter_p)
221 {
222   g_return_if_fail (quality < G_N_ELEMENTS (clutter_texture_quality_filters));
223
224   if (min_filter_p)
225     *min_filter_p = clutter_texture_quality_filters[quality].min_filter;
226
227   if (mag_filter_p)
228     *mag_filter_p = clutter_texture_quality_filters[quality].mag_filter;
229 }
230
231 static void
232 texture_free_gl_resources (ClutterTexture *texture)
233 {
234   ClutterTexturePrivate *priv = texture->priv;
235
236   if (priv->pipeline != NULL)
237     {
238       /* We want to keep the layer so that the filter settings will
239          remain but we want to free its resources so we clear the
240          texture handle */
241       cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL);
242     }
243 }
244
245 static void
246 clutter_texture_unrealize (ClutterActor *actor)
247 {
248   ClutterTexture        *texture;
249   ClutterTexturePrivate *priv;
250
251   texture = CLUTTER_TEXTURE(actor);
252   priv = texture->priv;
253
254   if (priv->pipeline == NULL)
255     return;
256
257   if (priv->fbo_source != NULL)
258     {
259       /* Free up our fbo handle and texture resources, realize will recreate */
260       cogl_object_unref (priv->fbo_handle);
261       priv->fbo_handle = NULL;
262       texture_free_gl_resources (texture);
263       return;
264     }
265
266   CLUTTER_NOTE (TEXTURE, "Texture unrealized");
267 }
268
269 static void
270 clutter_texture_realize (ClutterActor *actor)
271 {
272   ClutterTexture       *texture;
273   ClutterTexturePrivate *priv;
274
275   texture = CLUTTER_TEXTURE(actor);
276   priv = texture->priv;
277
278   if (priv->fbo_source)
279     {
280       CoglTextureFlags flags = COGL_TEXTURE_NONE;
281       CoglHandle tex;
282
283       /* Handle FBO's */
284
285       if (priv->no_slice)
286         flags |= COGL_TEXTURE_NO_SLICING;
287
288       tex = cogl_texture_new_with_size (priv->image_width,
289                                         priv->image_height,
290                                         flags,
291                                         COGL_PIXEL_FORMAT_RGBA_8888_PRE);
292
293       cogl_pipeline_set_layer_texture (priv->pipeline, 0, tex);
294
295       priv->fbo_handle = cogl_offscreen_new_to_texture (tex);
296
297       /* The pipeline now has a reference to the texture so it will
298          stick around */
299       cogl_object_unref (tex);
300
301       if (priv->fbo_handle == NULL)
302         {
303           g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
304           CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
305           return;
306         }
307
308       clutter_actor_set_size (actor, priv->image_width, priv->image_height);
309
310       return;
311     }
312
313   /* If the texture is not a FBO, then realization is a no-op but we
314    * still want to be in REALIZED state to maintain invariants.
315    * ClutterTexture doesn't need to be realized to have a Cogl texture
316    * because Clutter assumes that a GL context is always current so
317    * there is no need to wait to realization time to create the
318    * texture. Although this is slightly odd it would be wasteful to
319    * redundantly store a copy of the texture data in local memory just
320    * so that we can make a texture during realize.
321    */
322
323   CLUTTER_NOTE (TEXTURE, "Texture realized");
324 }
325
326 static void
327 clutter_texture_get_preferred_width (ClutterActor *self,
328                                      gfloat        for_height,
329                                      gfloat       *min_width_p,
330                                      gfloat       *natural_width_p)
331 {
332   ClutterTexture *texture = CLUTTER_TEXTURE (self);
333   ClutterTexturePrivate *priv = texture->priv;
334
335   /* Min request is always 0 since we can scale down or clip */
336   if (min_width_p)
337     *min_width_p = 0;
338
339   if (priv->sync_actor_size)
340     {
341       if (natural_width_p)
342         {
343           if (!priv->keep_aspect_ratio ||
344               for_height < 0 ||
345               priv->image_height <= 0)
346             {
347               *natural_width_p = priv->image_width;
348             }
349           else
350             {
351               /* Set the natural width so as to preserve the aspect ratio */
352               gfloat ratio = (gfloat) priv->image_width
353                            / (gfloat) priv->image_height;
354
355               *natural_width_p = ratio * for_height;
356             }
357         }
358     }
359   else
360     {
361       if (natural_width_p)
362         *natural_width_p = 0;
363     }
364 }
365
366 static void
367 clutter_texture_get_preferred_height (ClutterActor *self,
368                                       gfloat        for_width,
369                                       gfloat       *min_height_p,
370                                       gfloat       *natural_height_p)
371 {
372   ClutterTexture *texture = CLUTTER_TEXTURE (self);
373   ClutterTexturePrivate *priv = texture->priv;
374
375   /* Min request is always 0 since we can scale down or clip */
376   if (min_height_p)
377     *min_height_p = 0;
378
379   if (priv->sync_actor_size)
380     {
381       if (natural_height_p)
382         {
383           if (!priv->keep_aspect_ratio ||
384               for_width < 0 ||
385               priv->image_width <= 0)
386             {
387               *natural_height_p = priv->image_height;
388             }
389           else
390             {
391               /* Set the natural height so as to preserve the aspect ratio */
392               gfloat ratio = (gfloat) priv->image_height
393                            / (gfloat) priv->image_width;
394
395               *natural_height_p = ratio * for_width;
396             }
397         }
398     }
399   else
400     {
401       if (natural_height_p)
402         *natural_height_p = 0;
403     }
404 }
405
406 static void
407 clutter_texture_allocate (ClutterActor           *self,
408                           const ClutterActorBox  *box,
409                           ClutterAllocationFlags  flags)
410 {
411   ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv;
412
413   /* chain up to set actor->allocation */
414   CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->allocate (self,
415                                                                 box,
416                                                                 flags);
417
418   /* If we adopted the source fbo then allocate that at its preferred
419      size */
420   if (priv->fbo_source && clutter_actor_get_parent (priv->fbo_source) == self)
421     clutter_actor_allocate_preferred_size (priv->fbo_source, flags);
422 }
423
424 static gboolean
425 clutter_texture_has_overlaps (ClutterActor *self)
426 {
427   /* Textures never need an offscreen redirect because there are never
428      any overlapping primitives */
429   return FALSE;
430 }
431
432 static void
433 set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source,
434                                            int viewport_width,
435                                            int viewport_height)
436 {
437   ClutterActorBox box = { 0, };
438   float x_offset, y_offset;
439
440   if (clutter_actor_get_paint_box (fbo_source, &box))
441     clutter_actor_box_get_origin (&box, &x_offset, &y_offset);
442   else
443     {
444       /* As a fallback when the paint box can't be determined we use
445        * the transformed allocation to come up with an offset instead.
446        *
447        * FIXME: when we don't have a paint box we should instead be
448        * falling back to a stage sized fbo with an offset of (0,0)
449        */
450
451       ClutterVertex verts[4];
452       float x_min = G_MAXFLOAT, y_min = G_MAXFLOAT;
453       int i;
454
455       /* Get the actors allocation transformed into screen coordinates.
456        *
457        * XXX: Note: this may not be a bounding box for the actor, since an
458        * actor with depth may escape the box due to its perspective
459        * projection. */
460       clutter_actor_get_abs_allocation_vertices (fbo_source, verts);
461
462       for (i = 0; i < G_N_ELEMENTS (verts); ++i)
463         {
464           if (verts[i].x < x_min)
465            x_min = verts[i].x;
466           if (verts[i].y < y_min)
467            y_min = verts[i].y;
468         }
469
470       /* XXX: It's not good enough to round by simply truncating the fraction here
471        * via a cast, as it results in offscreen rendering being offset by 1 pixel
472        * in many cases... */
473 #define ROUND(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5))
474
475       x_offset = ROUND (x_min);
476       y_offset = ROUND (y_min);
477
478 #undef ROUND
479     }
480
481   /* translate the viewport so that the source actor lands on the
482    * sub-region backed by the offscreen framebuffer... */
483   cogl_set_viewport (-x_offset, -y_offset, viewport_width, viewport_height);
484 }
485
486 static void
487 update_fbo (ClutterActor *self)
488 {
489   ClutterTexture        *texture = CLUTTER_TEXTURE (self);
490   ClutterTexturePrivate *priv = texture->priv;
491   ClutterActor          *head;
492   ClutterShader         *shader = NULL;
493   ClutterActor          *stage = NULL;
494   CoglMatrix             projection;
495   CoglColor              transparent_col;
496
497   head = _clutter_context_peek_shader_stack ();
498   if (head != NULL)
499     shader = clutter_actor_get_shader (head);
500
501   /* Temporarily turn off the shader on the top of the context's
502    * shader stack, to restore the GL pipeline to it's natural state.
503    */
504   if (shader != NULL)
505     clutter_shader_set_is_enabled (shader, FALSE);
506
507   /* Redirect drawing to the fbo */
508   cogl_push_framebuffer (priv->fbo_handle);
509
510   if ((stage = clutter_actor_get_stage (self)) != NULL)
511     {
512       gfloat stage_width, stage_height;
513       ClutterActor *source_parent;
514
515       /* We copy the projection and modelview matrices from the stage to
516        * the offscreen framebuffer and create a viewport larger than the
517        * offscreen framebuffer - the same size as the stage.
518        *
519        * The fbo source actor gets rendered into this stage size viewport at the
520        * same position it normally would after applying all it's usual parent
521        * transforms and it's own scale and rotate transforms etc.
522        *
523        * The viewport is offset such that the offscreen buffer will be positioned
524        * under the actor.
525        */
526
527       _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
528
529       /* Set the projection matrix modelview matrix as it is for the
530        * stage... */
531       cogl_set_projection_matrix (&projection);
532
533       clutter_actor_get_size (stage, &stage_width, &stage_height);
534
535       /* Set a negatively offset the viewport so that the offscreen
536        * framebuffer is position underneath the fbo_source actor... */
537       set_viewport_with_buffer_under_fbo_source (priv->fbo_source,
538                                                  stage_width,
539                                                  stage_height);
540
541       /* Apply the source's parent transformations to the modelview */
542       if ((source_parent = clutter_actor_get_parent (priv->fbo_source)))
543         {
544           CoglMatrix modelview;
545           cogl_matrix_init_identity (&modelview);
546           _clutter_actor_apply_relative_transformation_matrix (source_parent,
547                                                                NULL,
548                                                                &modelview);
549           cogl_set_modelview_matrix (&modelview);
550         }
551     }
552
553
554   /* cogl_clear is called to clear the buffers */
555   cogl_color_init_from_4ub (&transparent_col, 0, 0, 0, 0);
556   cogl_clear (&transparent_col,
557               COGL_BUFFER_BIT_COLOR |
558               COGL_BUFFER_BIT_DEPTH);
559
560   /* Render the actor to the fbo */
561   clutter_actor_paint (priv->fbo_source);
562
563   /* Restore drawing to the previous framebuffer */
564   cogl_pop_framebuffer ();
565
566   /* If there is a shader on top of the shader stack, turn it back on. */
567   if (shader != NULL)
568     clutter_shader_set_is_enabled (shader, TRUE);
569 }
570
571 static void
572 gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self)
573 {
574   ClutterTexture *texture = CLUTTER_TEXTURE (self);
575   ClutterTexturePrivate *priv = texture->priv;
576   ClutterActorBox box;
577   float t_w, t_h;
578
579   clutter_actor_get_allocation_box (self, &box);
580
581   if (priv->repeat_x && priv->image_width > 0)
582     t_w = (box.x2 - box.x1) / (float) priv->image_width;
583   else
584     t_w = 1.0;
585
586   if (priv->repeat_y && priv->image_height > 0)
587     t_h = (box.y2 - box.y1) / (float) priv->image_height;
588   else
589     t_h = 1.0;
590
591   cogl_rectangle_with_texture_coords (0, 0,
592                                       box.x2 - box.x1,
593                                       box.y2 - box.y1,
594                                       0, 0, t_w, t_h);
595 }
596
597 static CoglPipeline *
598 create_pick_pipeline (ClutterActor *self)
599 {
600   ClutterTexture *texture = CLUTTER_TEXTURE (self);
601   ClutterTexturePrivate *priv = texture->priv;
602   CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline);
603   GError *error = NULL;
604
605   if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
606                                         "RGBA = "
607                                         "  MODULATE (CONSTANT, TEXTURE[A])",
608                                         &error))
609     {
610       if (!priv->seen_create_pick_pipeline_warning)
611         g_warning ("Error setting up texture combine for shaped "
612                    "texture picking: %s", error->message);
613       priv->seen_create_pick_pipeline_warning = TRUE;
614       g_error_free (error);
615       cogl_object_unref (pick_pipeline);
616       return NULL;
617     }
618
619   cogl_pipeline_set_blend (pick_pipeline,
620                            "RGBA = ADD (SRC_COLOR[RGBA], 0)",
621                            NULL);
622
623   cogl_pipeline_set_alpha_test_function (pick_pipeline,
624                                          COGL_PIPELINE_ALPHA_FUNC_EQUAL,
625                                          1.0);
626
627   return pick_pipeline;
628 }
629
630 static void
631 clutter_texture_pick (ClutterActor       *self,
632                       const ClutterColor *color)
633 {
634   ClutterTexture *texture = CLUTTER_TEXTURE (self);
635   ClutterTexturePrivate *priv = texture->priv;
636
637   if (!clutter_actor_should_pick_paint (self))
638     return;
639
640   if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
641     {
642       CoglColor pick_color;
643
644       if (priv->pick_pipeline == NULL)
645         priv->pick_pipeline = create_pick_pipeline (self);
646
647       if (priv->pick_pipeline == NULL)
648         {
649           priv->pick_with_alpha_supported = FALSE;
650           CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
651                                                                     color);
652           return;
653         }
654
655       if (priv->fbo_handle != NULL)
656         update_fbo (self);
657
658       cogl_color_init_from_4ub (&pick_color,
659                                 color->red,
660                                 color->green,
661                                 color->blue,
662                                 0xff);
663       cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
664                                                 0, &pick_color);
665       cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0,
666                                        clutter_texture_get_cogl_texture (texture));
667       cogl_set_source (priv->pick_pipeline);
668       gen_texcoords_and_draw_cogl_rectangle (self);
669     }
670   else
671     CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
672 }
673
674 static void
675 clutter_texture_paint (ClutterActor *self)
676 {
677   ClutterTexture *texture = CLUTTER_TEXTURE (self);
678   ClutterTexturePrivate *priv = texture->priv;
679   guint8 paint_opacity = clutter_actor_get_paint_opacity (self);
680
681   CLUTTER_NOTE (PAINT,
682                 "painting texture '%s'",
683                 clutter_actor_get_name (self) ? clutter_actor_get_name (self)
684                                               : "unknown");
685
686   if (priv->fbo_handle != NULL)
687     update_fbo (self);
688
689   cogl_pipeline_set_color4ub (priv->pipeline,
690                               paint_opacity,
691                               paint_opacity,
692                               paint_opacity,
693                               paint_opacity);
694   cogl_set_source (priv->pipeline);
695
696   gen_texcoords_and_draw_cogl_rectangle (self);
697 }
698
699 static gboolean
700 clutter_texture_get_paint_volume (ClutterActor       *self,
701                                   ClutterPaintVolume *volume)
702 {
703   ClutterTexturePrivate *priv;
704
705   priv = CLUTTER_TEXTURE (self)->priv;
706
707   if (priv->pipeline == NULL)
708     return FALSE;
709
710   if (priv->image_width == 0 || priv->image_height == 0)
711     return FALSE;
712
713   return _clutter_actor_set_default_paint_volume (self,
714                                                   CLUTTER_TYPE_TEXTURE,
715                                                   volume);
716 }
717
718 static void
719 clutter_texture_async_data_free (ClutterTextureAsyncData *data)
720 {
721   /* This function should only be called either from the main thread
722      once it is known that the load thread has completed or from the
723      load thread/upload function itself if the abort flag is true (in
724      which case the main thread has disowned the data) */
725   g_free (data->load_filename);
726
727   if (data->load_bitmap != NULL)
728     cogl_object_unref (data->load_bitmap);
729
730   if (data->load_error != NULL)
731     g_error_free (data->load_error);
732
733   g_slice_free (ClutterTextureAsyncData, data);
734 }
735
736 /*
737  * clutter_texture_async_load_cancel:
738  * @texture: a #ClutterTexture
739  *
740  * Cancels an asynchronous loading operation, whether done
741  * with threads enabled or just using the main loop
742  */
743 static void
744 clutter_texture_async_load_cancel (ClutterTexture *texture)
745 {
746   ClutterTexturePrivate *priv = texture->priv;
747
748   if (priv->async_data != NULL)
749     {
750       ClutterTextureAsyncData *async_data = priv->async_data;
751
752       priv->async_data = NULL;
753
754       if (async_data->load_idle != 0)
755         {
756           g_source_remove (async_data->load_idle);
757           async_data->load_idle = 0;
758
759           clutter_texture_async_data_free (async_data);
760         }
761       else
762         {
763           clutter_texture_async_data_lock (async_data);
764
765           CLUTTER_NOTE (TEXTURE, "[async] cancelling operation for '%s'",
766                         async_data->load_filename);
767
768           async_data->state |= ASYNC_STATE_CANCELLED;
769
770           clutter_texture_async_data_unlock (async_data);
771         }
772     }
773 }
774
775 static void
776 clutter_texture_dispose (GObject *object)
777 {
778   ClutterTexture *texture = CLUTTER_TEXTURE (object);
779   ClutterTexturePrivate *priv = texture->priv;
780
781   texture_free_gl_resources (texture);
782   texture_fbo_free_resources (texture);
783
784   clutter_texture_async_load_cancel (texture);
785
786   if (priv->pipeline != NULL)
787     {
788       cogl_object_unref (priv->pipeline);
789       priv->pipeline = NULL;
790     }
791
792   if (priv->pick_pipeline != NULL)
793     {
794       cogl_object_unref (priv->pick_pipeline);
795       priv->pick_pipeline = NULL;
796     }
797
798   G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
799 }
800
801 static void
802 clutter_texture_finalize (GObject *object)
803 {
804   ClutterTexturePrivate *priv = CLUTTER_TEXTURE (object)->priv;
805
806   g_free (priv->filename);
807
808   G_OBJECT_CLASS (clutter_texture_parent_class)->finalize (object);
809 }
810
811 static void
812 clutter_texture_set_property (GObject      *object,
813                               guint         prop_id,
814                               const GValue *value,
815                               GParamSpec   *pspec)
816 {
817   ClutterTexture *texture;
818   ClutterTexturePrivate *priv;
819
820   texture = CLUTTER_TEXTURE (object);
821   priv = texture->priv;
822
823   switch (prop_id)
824     {
825     case PROP_SYNC_SIZE:
826       clutter_texture_set_sync_size (texture, g_value_get_boolean (value));
827       break;
828
829     case PROP_REPEAT_X:
830       clutter_texture_set_repeat (texture,
831                                   g_value_get_boolean (value),
832                                   priv->repeat_y);
833       break;
834
835     case PROP_REPEAT_Y:
836       clutter_texture_set_repeat (texture,
837                                   priv->repeat_x,
838                                   g_value_get_boolean (value));
839       break;
840
841     case PROP_FILTER_QUALITY:
842       clutter_texture_set_filter_quality (texture,
843                                           g_value_get_enum (value));
844       break;
845
846     case PROP_COGL_TEXTURE:
847       {
848         CoglHandle hnd = g_value_get_boxed (value);
849
850         clutter_texture_set_cogl_texture (texture, hnd);
851       }
852       break;
853
854     case PROP_COGL_MATERIAL:
855       {
856         CoglHandle hnd = g_value_get_boxed (value);
857
858         clutter_texture_set_cogl_material (texture, hnd);
859       }
860       break;
861
862     case PROP_FILENAME:
863       clutter_texture_set_from_file (texture,
864                                      g_value_get_string (value),
865                                      NULL);
866       break;
867
868     case PROP_NO_SLICE:
869       priv->no_slice = g_value_get_boolean (value);
870       break;
871
872     case PROP_KEEP_ASPECT_RATIO:
873       clutter_texture_set_keep_aspect_ratio (texture,
874                                              g_value_get_boolean (value));
875       break;
876
877     case PROP_LOAD_DATA_ASYNC:
878       clutter_texture_set_load_data_async (texture,
879                                            g_value_get_boolean (value));
880       break;
881
882     case PROP_LOAD_ASYNC:
883       clutter_texture_set_load_async (texture, g_value_get_boolean (value));
884       break;
885
886     case PROP_PICK_WITH_ALPHA:
887       clutter_texture_set_pick_with_alpha (texture,
888                                            g_value_get_boolean (value));
889       break;
890
891     default:
892       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
893       break;
894     }
895 }
896
897 static void
898 clutter_texture_get_property (GObject    *object,
899                               guint       prop_id,
900                               GValue     *value,
901                               GParamSpec *pspec)
902 {
903   ClutterTexture        *texture;
904   ClutterTexturePrivate *priv;
905
906   texture = CLUTTER_TEXTURE(object);
907   priv = texture->priv;
908
909   switch (prop_id)
910     {
911     case PROP_PIXEL_FORMAT:
912       g_value_set_enum (value, clutter_texture_get_pixel_format (texture));
913       break;
914
915     case PROP_MAX_TILE_WASTE:
916       g_value_set_int (value, clutter_texture_get_max_tile_waste (texture));
917       break;
918
919     case PROP_SYNC_SIZE:
920       g_value_set_boolean (value, priv->sync_actor_size);
921       break;
922
923     case PROP_REPEAT_X:
924       g_value_set_boolean (value, priv->repeat_x);
925       break;
926
927     case PROP_REPEAT_Y:
928       g_value_set_boolean (value, priv->repeat_y);
929       break;
930
931     case PROP_FILTER_QUALITY:
932       g_value_set_enum (value, clutter_texture_get_filter_quality (texture));
933       break;
934
935     case PROP_COGL_TEXTURE:
936       g_value_set_boxed (value, clutter_texture_get_cogl_texture (texture));
937       break;
938
939     case PROP_COGL_MATERIAL:
940       g_value_set_boxed (value, clutter_texture_get_cogl_material (texture));
941       break;
942
943     case PROP_NO_SLICE:
944       g_value_set_boolean (value, priv->no_slice);
945       break;
946
947     case PROP_KEEP_ASPECT_RATIO:
948       g_value_set_boolean (value, priv->keep_aspect_ratio);
949       break;
950
951     case PROP_PICK_WITH_ALPHA:
952       g_value_set_boolean (value, priv->pick_with_alpha);
953       break;
954
955     case PROP_FILENAME:
956       g_value_set_string (value, priv->filename);
957       break;
958
959     default:
960       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
961       break;
962     }
963 }
964
965 static void
966 clutter_texture_class_init (ClutterTextureClass *klass)
967 {
968   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
969   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
970   GParamSpec *pspec;
971
972   g_type_class_add_private (klass, sizeof (ClutterTexturePrivate));
973
974   actor_class->paint            = clutter_texture_paint;
975   actor_class->pick             = clutter_texture_pick;
976   actor_class->get_paint_volume = clutter_texture_get_paint_volume;
977   actor_class->realize          = clutter_texture_realize;
978   actor_class->unrealize        = clutter_texture_unrealize;
979   actor_class->has_overlaps     = clutter_texture_has_overlaps;
980
981   actor_class->get_preferred_width  = clutter_texture_get_preferred_width;
982   actor_class->get_preferred_height = clutter_texture_get_preferred_height;
983   actor_class->allocate             = clutter_texture_allocate;
984
985   gobject_class->dispose      = clutter_texture_dispose;
986   gobject_class->finalize     = clutter_texture_finalize;
987   gobject_class->set_property = clutter_texture_set_property;
988   gobject_class->get_property = clutter_texture_get_property;
989
990   pspec = g_param_spec_boolean ("sync-size",
991                                 P_("Sync size of actor"),
992                                 P_("Auto sync size of actor to underlying pixbuf dimensions"),
993                                 TRUE,
994                                 CLUTTER_PARAM_READWRITE);
995   obj_props[PROP_SYNC_SIZE] = pspec;
996   g_object_class_install_property (gobject_class, PROP_SYNC_SIZE, pspec);
997
998   pspec = g_param_spec_boolean ("disable-slicing",
999                                 P_("Disable Slicing"),
1000                                 P_("Forces the underlying texture to be singular and not made of smaller space saving "
1001                                    "individual textures"),
1002                                 FALSE,
1003                                 G_PARAM_CONSTRUCT_ONLY |
1004                                 CLUTTER_PARAM_READWRITE);
1005   obj_props[PROP_NO_SLICE] = pspec;
1006   g_object_class_install_property (gobject_class, PROP_NO_SLICE, pspec);
1007
1008   pspec = g_param_spec_int ("tile-waste",
1009                             P_("Tile Waste"),
1010                             P_("Maximum waste area of a sliced texture"),
1011                             -1, G_MAXINT,
1012                             COGL_TEXTURE_MAX_WASTE,
1013                             CLUTTER_PARAM_READABLE);
1014   obj_props[PROP_MAX_TILE_WASTE] = pspec;
1015   g_object_class_install_property (gobject_class, PROP_MAX_TILE_WASTE, pspec);
1016
1017   pspec = g_param_spec_boolean ("repeat-x",
1018                                 P_("Horizontal repeat"),
1019                                 P_("Repeat the contents rather than scaling them horizontally"),
1020                                 FALSE,
1021                                 CLUTTER_PARAM_READWRITE);
1022   obj_props[PROP_REPEAT_X] = pspec;
1023   g_object_class_install_property (gobject_class, PROP_REPEAT_X, pspec);
1024
1025   pspec = g_param_spec_boolean ("repeat-y",
1026                                 P_("Vertical repeat"),
1027                                 P_("Repeat the contents rather than scaling them vertically"),
1028                                 FALSE,
1029                                 CLUTTER_PARAM_READWRITE);
1030   obj_props[PROP_REPEAT_Y] = pspec;
1031   g_object_class_install_property (gobject_class, PROP_REPEAT_Y, pspec);
1032
1033   pspec = g_param_spec_enum ("filter-quality",
1034                              P_("Filter Quality"),
1035                              P_("Rendering quality used when drawing the texture"),
1036                              CLUTTER_TYPE_TEXTURE_QUALITY,
1037                              CLUTTER_TEXTURE_QUALITY_MEDIUM,
1038                              G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
1039   obj_props[PROP_FILTER_QUALITY] = pspec;
1040   g_object_class_install_property (gobject_class, PROP_FILTER_QUALITY, pspec);
1041
1042   pspec = g_param_spec_enum ("pixel-format",
1043                              P_("Pixel Format"),
1044                              P_("The Cogl pixel format to use"),
1045                              COGL_TYPE_PIXEL_FORMAT,
1046                              COGL_PIXEL_FORMAT_RGBA_8888,
1047                              CLUTTER_PARAM_READABLE);
1048   obj_props[PROP_PIXEL_FORMAT] = pspec;
1049   g_object_class_install_property (gobject_class, PROP_PIXEL_FORMAT, pspec);
1050
1051   pspec = g_param_spec_boxed ("cogl-texture",
1052                               P_("Cogl Texture"),
1053                               P_("The underlying Cogl texture handle used to draw this actor"),
1054                               COGL_TYPE_HANDLE,
1055                               CLUTTER_PARAM_READWRITE);
1056   obj_props[PROP_COGL_TEXTURE] = pspec;
1057   g_object_class_install_property (gobject_class, PROP_COGL_TEXTURE, pspec);
1058
1059   pspec = g_param_spec_boxed ("cogl-material",
1060                               P_("Cogl Material"),
1061                               P_("The underlying Cogl material handle used to draw this actor"),
1062                               COGL_TYPE_HANDLE,
1063                               CLUTTER_PARAM_READWRITE);
1064   obj_props[PROP_COGL_MATERIAL] = pspec;
1065   g_object_class_install_property (gobject_class, PROP_COGL_MATERIAL, pspec);
1066
1067   /**
1068    * ClutterTexture:filename:
1069    *
1070    * The path of the file containing the image data to be displayed by
1071    * the texture.
1072    *
1073    * This property is unset when using the clutter_texture_set_from_*_data()
1074    * family of functions.
1075    */
1076   pspec = g_param_spec_string ("filename",
1077                                P_("Filename"),
1078                                P_("The path of the file containing the image data"),
1079                                NULL,
1080                                CLUTTER_PARAM_READWRITE);
1081   obj_props[PROP_FILENAME] = pspec;
1082   g_object_class_install_property (gobject_class, PROP_FILENAME, pspec);
1083
1084   pspec = g_param_spec_boolean ("keep-aspect-ratio",
1085                                 P_("Keep Aspect Ratio"),
1086                                 P_("Keep the aspect ratio of the texture when requesting the preferred width or height"),
1087                                 FALSE,
1088                                 CLUTTER_PARAM_READWRITE);
1089   obj_props[PROP_KEEP_ASPECT_RATIO] = pspec;
1090   g_object_class_install_property (gobject_class, PROP_KEEP_ASPECT_RATIO, pspec);
1091
1092   /**
1093    * ClutterTexture:load-async:
1094    *
1095    * Tries to load a texture from a filename by using a local thread to perform
1096    * the read operations. The initially created texture has dimensions 0x0 when
1097    * the true size becomes available the #ClutterTexture::size-change signal is
1098    * emitted and when the image has completed loading the
1099    * #ClutterTexture::load-finished signal is emitted.
1100    *
1101    * Threading is only enabled if g_thread_init() has been called prior to
1102    * clutter_init(), otherwise #ClutterTexture will use the main loop to load
1103    * the image.
1104    *
1105    * The upload of the texture data on the GL pipeline is not asynchronous, as
1106    * it must be performed from within the same thread that called
1107    * clutter_main().
1108    *
1109    * Since: 1.0
1110    */
1111   pspec = g_param_spec_boolean ("load-async",
1112                                 P_("Load asynchronously"),
1113                                 P_("Load files inside a thread to avoid blocking when loading images from disk"),
1114                                 FALSE,
1115                                 CLUTTER_PARAM_WRITABLE);
1116   obj_props[PROP_LOAD_ASYNC] = pspec;
1117   g_object_class_install_property (gobject_class, PROP_LOAD_ASYNC, pspec);
1118
1119
1120   /**
1121    * ClutterTexture:load-data-async:
1122    *
1123    * Like #ClutterTexture:load-async but loads the width and height
1124    * synchronously causing some blocking.
1125    *
1126    * Since: 1.0
1127    */
1128   pspec = g_param_spec_boolean ("load-data-async",
1129                                 P_("Load data asynchronously"),
1130                                 P_("Decode image data files inside a thread to reduce blocking when loading images from disk"),
1131                                 FALSE,
1132                                 CLUTTER_PARAM_WRITABLE);
1133   obj_props[PROP_LOAD_DATA_ASYNC] = pspec;
1134   g_object_class_install_property (gobject_class, PROP_LOAD_DATA_ASYNC, pspec);
1135
1136   /**
1137    * ClutterTexture::pick-with-alpha:
1138    *
1139    * Determines whether a #ClutterTexture should have it's shape defined
1140    * by its alpha channel when picking.
1141    *
1142    * Be aware that this is a bit more costly than the default picking
1143    * due to the texture lookup, extra test against the alpha value and
1144    * the fact that it will also interrupt the batching of geometry
1145    * done internally.
1146    *
1147    * Also there is currently no control over the threshold used to
1148    * determine what value of alpha is considered pickable, and so
1149    * only fully opaque parts of the texture will react to picking.
1150    *
1151    * Since: 1.4
1152    */
1153   pspec = g_param_spec_boolean ("pick-with-alpha",
1154                                 P_("Pick With Alpha"),
1155                                 P_("Shape actor with alpha channel when picking"),
1156                                 FALSE,
1157                                 CLUTTER_PARAM_READWRITE);
1158   obj_props[PROP_PICK_WITH_ALPHA] = pspec;
1159   g_object_class_install_property (gobject_class, PROP_PICK_WITH_ALPHA, pspec);
1160
1161   /**
1162    * ClutterTexture::size-change:
1163    * @texture: the texture which received the signal
1164    * @width: the width of the new texture
1165    * @height: the height of the new texture
1166    *
1167    * The ::size-change signal is emitted each time the size of the
1168    * pixbuf used by @texture changes.  The new size is given as
1169    * argument to the callback.
1170    */
1171   texture_signals[SIZE_CHANGE] =
1172     g_signal_new ("size-change",
1173                   G_TYPE_FROM_CLASS (gobject_class),
1174                   G_SIGNAL_RUN_LAST,
1175                   G_STRUCT_OFFSET (ClutterTextureClass, size_change),
1176                   NULL, NULL,
1177                   _clutter_marshal_VOID__INT_INT,
1178                   G_TYPE_NONE, 2,
1179                   G_TYPE_INT,
1180                   G_TYPE_INT);
1181   /**
1182    * ClutterTexture::pixbuf-change:
1183    * @texture: the texture which received the signal
1184    *
1185    * The ::pixbuf-change signal is emitted each time the pixbuf
1186    * used by @texture changes.
1187    */
1188   texture_signals[PIXBUF_CHANGE] =
1189     g_signal_new ("pixbuf-change",
1190                   G_TYPE_FROM_CLASS (gobject_class),
1191                   G_SIGNAL_RUN_LAST,
1192                   G_STRUCT_OFFSET (ClutterTextureClass, pixbuf_change),
1193                   NULL, NULL,
1194                   _clutter_marshal_VOID__VOID,
1195                   G_TYPE_NONE,
1196                   0);
1197   /**
1198    * ClutterTexture::load-finished:
1199    * @texture: the texture which received the signal
1200    * @error: A set error, or %NULL
1201    *
1202    * The ::load-finished signal is emitted when a texture load has
1203    * completed. If there was an error during loading, @error will
1204    * be set, otherwise it will be %NULL
1205    *
1206    * Since: 1.0
1207    */
1208   texture_signals[LOAD_FINISHED] =
1209     g_signal_new (I_("load-finished"),
1210                   G_TYPE_FROM_CLASS (gobject_class),
1211                   G_SIGNAL_RUN_LAST,
1212                   G_STRUCT_OFFSET (ClutterTextureClass, load_finished),
1213                   NULL, NULL,
1214                   _clutter_marshal_VOID__BOXED,
1215                   G_TYPE_NONE,
1216                   1,
1217                   G_TYPE_ERROR);
1218 }
1219
1220 static ClutterScriptableIface *parent_scriptable_iface = NULL;
1221
1222 static void
1223 clutter_texture_set_custom_property (ClutterScriptable *scriptable,
1224                                      ClutterScript     *script,
1225                                      const gchar       *name,
1226                                      const GValue      *value)
1227 {
1228   ClutterTexture *texture = CLUTTER_TEXTURE (scriptable);
1229
1230   if (strcmp ("filename", name) == 0)
1231     {
1232       const gchar *str = g_value_get_string (value);
1233       gchar *path;
1234       GError *error;
1235
1236       path = clutter_script_lookup_filename (script, str);
1237       if (G_UNLIKELY (!path))
1238         {
1239           g_warning ("Unable to find image %s", str);
1240           return;
1241         }
1242
1243       error = NULL;
1244       clutter_texture_set_from_file (texture, path, &error);
1245       if (error)
1246         {
1247           g_warning ("Unable to open image path at '%s': %s",
1248                      path,
1249                      error->message);
1250           g_error_free (error);
1251         }
1252
1253       g_free (path);
1254     }
1255   else
1256     {
1257       /* chain up */
1258       if (parent_scriptable_iface->set_custom_property)
1259         parent_scriptable_iface->set_custom_property (scriptable, script,
1260                                                       name,
1261                                                       value);
1262     }
1263 }
1264
1265 static void
1266 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
1267 {
1268   parent_scriptable_iface = g_type_interface_peek_parent (iface);
1269
1270   if (!parent_scriptable_iface)
1271     parent_scriptable_iface = g_type_default_interface_peek
1272                                           (CLUTTER_TYPE_SCRIPTABLE);
1273
1274   iface->set_custom_property = clutter_texture_set_custom_property;
1275 }
1276
1277 static void
1278 clutter_texture_init (ClutterTexture *self)
1279 {
1280   ClutterTexturePrivate *priv;
1281
1282   self->priv = priv = CLUTTER_TEXTURE_GET_PRIVATE (self);
1283
1284   priv->repeat_x          = FALSE;
1285   priv->repeat_y          = FALSE;
1286   priv->sync_actor_size   = TRUE;
1287   priv->fbo_handle        = NULL;
1288   priv->pick_pipeline     = NULL;
1289   priv->keep_aspect_ratio = FALSE;
1290   priv->pick_with_alpha   = FALSE;
1291   priv->pick_with_alpha_supported = TRUE;
1292   priv->seen_create_pick_pipeline_warning = FALSE;
1293
1294   if (G_UNLIKELY (texture_template_pipeline == NULL))
1295     {
1296       CoglPipeline *pipeline;
1297       CoglContext *ctx =
1298         clutter_backend_get_cogl_context (clutter_get_default_backend ());
1299
1300       texture_template_pipeline = cogl_pipeline_new (ctx);
1301       pipeline = COGL_PIPELINE (texture_template_pipeline);
1302       cogl_pipeline_set_layer_null_texture (pipeline,
1303                                             0, /* layer_index */
1304                                             COGL_TEXTURE_TYPE_2D);
1305     }
1306
1307   g_assert (texture_template_pipeline != NULL);
1308   priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1309 }
1310
1311 /**
1312  * clutter_texture_get_cogl_material:
1313  * @texture: A #ClutterTexture
1314  *
1315  * Returns a handle to the underlying COGL material used for drawing
1316  * the actor.
1317  *
1318  * Return value: (transfer none): a handle for a #CoglMaterial. The
1319  *   material is owned by the #ClutterTexture and it should not be
1320  *   unreferenced
1321  *
1322  * Since: 1.0
1323  */
1324 CoglHandle
1325 clutter_texture_get_cogl_material (ClutterTexture *texture)
1326 {
1327   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1328
1329   return texture->priv->pipeline;
1330 }
1331
1332 /**
1333  * clutter_texture_set_cogl_material:
1334  * @texture: A #ClutterTexture
1335  * @cogl_material: A CoglHandle for a material
1336  *
1337  * Replaces the underlying Cogl material drawn by this actor with
1338  * @cogl_material. A reference to the material is taken so if the
1339  * handle is no longer needed it should be deref'd with
1340  * cogl_handle_unref. Texture data is attached to the material so
1341  * calling this function also replaces the Cogl
1342  * texture. #ClutterTexture requires that the material have a texture
1343  * layer so you should set one on the material before calling this
1344  * function.
1345  *
1346  * Since: 0.8
1347  *
1348  */
1349 void
1350 clutter_texture_set_cogl_material (ClutterTexture *texture,
1351                                    CoglHandle cogl_material)
1352 {
1353   CoglPipeline *cogl_pipeline = cogl_material;
1354   CoglHandle cogl_texture;
1355
1356   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1357
1358   cogl_object_ref (cogl_pipeline);
1359
1360   if (texture->priv->pipeline)
1361     cogl_object_unref (texture->priv->pipeline);
1362
1363   texture->priv->pipeline = cogl_pipeline;
1364
1365   /* XXX: We are re-asserting the first layer of the new pipeline to ensure the
1366    * priv state is in sync with the contents of the pipeline. */
1367   cogl_texture = clutter_texture_get_cogl_texture (texture);
1368   clutter_texture_set_cogl_texture (texture, cogl_texture);
1369   /* XXX: If we add support for more pipeline layers, this will need
1370    * extending */
1371 }
1372
1373 typedef struct _GetLayerState
1374 {
1375   gboolean has_layer;
1376   int first_layer;
1377 } GetLayerState;
1378
1379 static gboolean
1380 layer_cb (CoglPipeline *pipeline, int layer, void *user_data)
1381 {
1382   GetLayerState *state = user_data;
1383
1384   state->has_layer = TRUE;
1385   state->first_layer = layer;
1386
1387   /* We only care about the first layer. */
1388   return FALSE;
1389 }
1390
1391 static gboolean
1392 get_first_layer_index (CoglPipeline *pipeline, int *layer_index)
1393 {
1394   GetLayerState state = { FALSE };
1395   cogl_pipeline_foreach_layer (pipeline,
1396                                layer_cb,
1397                                &state);
1398   if (state.has_layer)
1399     *layer_index = state.first_layer;
1400
1401   return state.has_layer;
1402 }
1403
1404 /**
1405  * clutter_texture_get_cogl_texture:
1406  * @texture: A #ClutterTexture
1407  *
1408  * Retrieves the handle to the underlying COGL texture used for drawing
1409  * the actor. No extra reference is taken so if you need to keep the
1410  * handle then you should call cogl_handle_ref() on it.
1411  *
1412  * The texture handle returned is the first layer of the material
1413  * handle used by the #ClutterTexture. If you need to access the other
1414  * layers you should use clutter_texture_get_cogl_material() instead
1415  * and use the #CoglMaterial API.
1416  *
1417  * Return value: (transfer none): a #CoglHandle for the texture. The returned
1418  *   handle is owned by the #ClutterTexture and it should not be unreferenced
1419  *
1420  * Since: 0.8
1421  */
1422 CoglHandle
1423 clutter_texture_get_cogl_texture (ClutterTexture *texture)
1424 {
1425   ClutterTexturePrivate *priv;
1426   int layer_index;
1427
1428   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1429
1430   priv = texture->priv;
1431
1432   if (get_first_layer_index (priv->pipeline, &layer_index))
1433     return cogl_pipeline_get_layer_texture (priv->pipeline, layer_index);
1434   else
1435     return NULL;
1436 }
1437
1438 /**
1439  * clutter_texture_set_cogl_texture:
1440  * @texture: A #ClutterTexture
1441  * @cogl_tex: A CoglHandle for a texture
1442  *
1443  * Replaces the underlying COGL texture drawn by this actor with
1444  * @cogl_tex. A reference to the texture is taken so if the handle is
1445  * no longer needed it should be deref'd with cogl_handle_unref.
1446  *
1447  * Since: 0.8
1448  */
1449 void
1450 clutter_texture_set_cogl_texture (ClutterTexture  *texture,
1451                                   CoglHandle       cogl_tex)
1452 {
1453   ClutterTexturePrivate  *priv;
1454   gboolean size_changed;
1455   guint width, height;
1456
1457   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1458   g_return_if_fail (cogl_is_texture (cogl_tex));
1459
1460   /* This function can set the texture without the actor being
1461      realized. This is ok because Clutter requires that the GL context
1462      always be current so there is no point in waiting to realization
1463      to set the texture. */
1464
1465   priv = texture->priv;
1466
1467   width = cogl_texture_get_width (cogl_tex);
1468   height = cogl_texture_get_height (cogl_tex);
1469
1470   /* Reference the new texture now in case it is the same one we are
1471      already using */
1472   cogl_object_ref (cogl_tex);
1473
1474   /* Remove FBO if exisiting */
1475   if (priv->fbo_source)
1476     texture_fbo_free_resources (texture);
1477
1478   /* Remove old texture */
1479   texture_free_gl_resources (texture);
1480
1481   /* Use the new texture */
1482   if (priv->pipeline == NULL)
1483     priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1484
1485   g_assert (priv->pipeline != NULL);
1486   cogl_pipeline_set_layer_texture (priv->pipeline, 0, cogl_tex);
1487
1488   /* The pipeline now holds a reference to the texture so we can
1489      safely release the reference we claimed above */
1490   cogl_object_unref (cogl_tex);
1491
1492   size_changed = (width != priv->image_width || height != priv->image_height);
1493   priv->image_width = width;
1494   priv->image_height = height;
1495
1496   CLUTTER_NOTE (TEXTURE, "set size (w:%d, h:%d)",
1497                 priv->image_width,
1498                 priv->image_height);
1499
1500   if (size_changed)
1501     {
1502       g_signal_emit (texture, texture_signals[SIZE_CHANGE], 0,
1503                      priv->image_width,
1504                      priv->image_height);
1505
1506       if (priv->sync_actor_size)
1507         {
1508           ClutterActor *actor = CLUTTER_ACTOR (texture);
1509
1510           /* we have been requested to keep the actor size in
1511            * sync with the texture data; if we also want to
1512            * maintain the aspect ratio we want to change the
1513            * requisition mode depending on the orientation of
1514            * the texture, so that the parent container can do
1515            * the right thing
1516            */
1517           if (priv->keep_aspect_ratio)
1518             {
1519               ClutterRequestMode request;
1520
1521               if (priv->image_width >= priv->image_height)
1522                 request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
1523               else
1524                 request = CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
1525
1526               clutter_actor_set_request_mode (actor, request);
1527             }
1528
1529           clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
1530         }
1531     }
1532
1533   /* rename signal */
1534   g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
1535
1536   /* If resized actor may need resizing but paint() will do this */
1537   clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
1538
1539   g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_COGL_TEXTURE]);
1540 }
1541
1542 static gboolean
1543 clutter_texture_set_from_data (ClutterTexture     *texture,
1544                                const guchar       *data,
1545                                CoglPixelFormat     source_format,
1546                                gint                width,
1547                                gint                height,
1548                                gint                rowstride,
1549                                gint                bpp,
1550                                GError            **error)
1551 {
1552   ClutterTexturePrivate *priv = texture->priv;
1553   CoglHandle new_texture = NULL;
1554   CoglTextureFlags flags = COGL_TEXTURE_NONE;
1555
1556   if (priv->no_slice)
1557     flags |= COGL_TEXTURE_NO_SLICING;
1558
1559   /* FIXME if we are not realized, we should store the data
1560    * for future use, instead of creating the texture.
1561    */
1562   new_texture = cogl_texture_new_from_data (width, height,
1563                                             flags,
1564                                             source_format,
1565                                             COGL_PIXEL_FORMAT_ANY,
1566                                             rowstride,
1567                                             data);
1568
1569   if (G_UNLIKELY (new_texture == NULL))
1570     {
1571       GError *inner_error = NULL;
1572
1573       g_set_error (&inner_error, CLUTTER_TEXTURE_ERROR,
1574                    CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1575                    _("Failed to load the image data"));
1576
1577       g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, inner_error);
1578
1579       if (error != NULL)
1580         g_propagate_error (error, inner_error);
1581       else
1582         g_error_free (inner_error);
1583
1584       return FALSE;
1585     }
1586
1587   g_free (priv->filename);
1588   priv->filename = NULL;
1589
1590   clutter_texture_set_cogl_texture (texture, new_texture);
1591
1592   cogl_object_unref (new_texture);
1593
1594   g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
1595
1596   return TRUE;
1597 }
1598
1599 static inline gboolean
1600 get_pixel_format_from_texture_flags (gint                 bpp,
1601                                      gboolean             has_alpha,
1602                                      ClutterTextureFlags  flags,
1603                                      CoglPixelFormat     *source_format)
1604 {
1605   /* Convert the flags to a CoglPixelFormat */
1606   if (has_alpha)
1607     {
1608       if (G_UNLIKELY (bpp != 4))
1609         {
1610           g_warning ("Unsupported bytes per pixel value '%d': "
1611                      "Clutter supports only a  value of 4 "
1612                      "for RGBA data",
1613                      bpp);
1614           return FALSE;
1615         }
1616
1617       *source_format = COGL_PIXEL_FORMAT_RGBA_8888;
1618     }
1619   else
1620     {
1621       if (G_UNLIKELY (bpp != 3))
1622         {
1623           g_warning ("Unsupported bytes per pixel value '%d': "
1624                      "Clutter supports only a BPP value of 3 "
1625                      "for RGB data",
1626                      bpp);
1627           return FALSE;
1628         }
1629
1630       *source_format = COGL_PIXEL_FORMAT_RGB_888;
1631     }
1632
1633   if ((flags & CLUTTER_TEXTURE_RGB_FLAG_BGR))
1634     *source_format |= COGL_BGR_BIT;
1635
1636   if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT))
1637     *source_format |= COGL_PREMULT_BIT;
1638
1639   return TRUE;
1640 }
1641
1642 /**
1643  * clutter_texture_set_from_rgb_data:
1644  * @texture: a #ClutterTexture
1645  * @data: (array): image data in RGBA type colorspace.
1646  * @has_alpha: set to %TRUE if image data has an alpha channel.
1647  * @width: width in pixels of image data.
1648  * @height: height in pixels of image data
1649  * @rowstride: distance in bytes between row starts.
1650  * @bpp: bytes per pixel (currently only 3 and 4 supported, depending
1651  *   on the value of @has_alpha)
1652  * @flags: #ClutterTextureFlags
1653  * @error: return location for a #GError, or %NULL.
1654  *
1655  * Sets #ClutterTexture image data.
1656  *
1657  * Return value: %TRUE on success, %FALSE on failure.
1658  *
1659  * Since: 0.4.
1660  */
1661 gboolean
1662 clutter_texture_set_from_rgb_data (ClutterTexture       *texture,
1663                                    const guchar         *data,
1664                                    gboolean              has_alpha,
1665                                    gint                  width,
1666                                    gint                  height,
1667                                    gint                  rowstride,
1668                                    gint                  bpp,
1669                                    ClutterTextureFlags   flags,
1670                                    GError              **error)
1671 {
1672   CoglPixelFormat source_format;
1673
1674   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1675
1676   if (!get_pixel_format_from_texture_flags (bpp,
1677                                             has_alpha,
1678                                             flags,
1679                                             &source_format))
1680     {
1681       return FALSE;
1682     }
1683
1684   return clutter_texture_set_from_data (texture, data,
1685                                         source_format,
1686                                         width, height,
1687                                         rowstride, bpp,
1688                                         error);
1689 }
1690
1691 /**
1692  * clutter_texture_set_from_yuv_data:
1693  * @texture: A #ClutterTexture
1694  * @data: (array): Image data in YUV type colorspace.
1695  * @width: Width in pixels of image data.
1696  * @height: Height in pixels of image data
1697  * @flags: #ClutterTextureFlags
1698  * @error: Return location for a #GError, or %NULL.
1699  *
1700  * Sets a #ClutterTexture from YUV image data. If an error occurred,
1701  * %FALSE is returned and @error is set.
1702  *
1703  * The YUV support depends on the driver; the format supported by the
1704  * few drivers exposing this capability are not really useful.
1705  *
1706  * The proper way to convert image data in any YUV colorspace to any
1707  * RGB colorspace is to use a fragment shader associated with the
1708  * #ClutterTexture material.
1709  *
1710  * Return value: %TRUE if the texture was successfully updated
1711  *
1712  * Since: 0.4
1713  *
1714  * Deprecated: 1.10: Use clutter_texture_get_cogl_material() and
1715  *   the Cogl API to install a fragment shader for decoding YUV
1716  *   formats on the GPU
1717  */
1718 gboolean
1719 clutter_texture_set_from_yuv_data (ClutterTexture     *texture,
1720                                    const guchar       *data,
1721                                    gint                width,
1722                                    gint                height,
1723                                    ClutterTextureFlags flags,
1724                                    GError            **error)
1725 {
1726   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1727
1728   if (!clutter_feature_available (CLUTTER_FEATURE_TEXTURE_YUV))
1729     {
1730       g_set_error (error, CLUTTER_TEXTURE_ERROR,
1731                    CLUTTER_TEXTURE_ERROR_NO_YUV,
1732                    _("YUV textures are not supported"));
1733       return FALSE;
1734     }
1735
1736   /* Convert the flags to a CoglPixelFormat */
1737   if ((flags & CLUTTER_TEXTURE_YUV_FLAG_YUV2))
1738     {
1739       g_set_error (error, CLUTTER_TEXTURE_ERROR,
1740                    CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1741                    _("YUV2 textues are not supported"));
1742       return FALSE;
1743     }
1744
1745   return clutter_texture_set_from_data (texture, data,
1746                                         COGL_PIXEL_FORMAT_YUV,
1747                                         width, height,
1748                                         width * 3, 3,
1749                                         error);
1750 }
1751
1752 /*
1753  * clutter_texture_async_load_complete:
1754  * @self: a #ClutterTexture
1755  * @bitmap: a handle to a CoglBitmap
1756  * @error: load error
1757  *
1758  * If @error is %NULL, loads @bitmap into a #CoglTexture.
1759  *
1760  * This function emits the ::load-finished signal on @self.
1761  */
1762 static void
1763 clutter_texture_async_load_complete (ClutterTexture *self,
1764                                      CoglHandle      bitmap,
1765                                      const GError   *error)
1766 {
1767   ClutterTexturePrivate *priv = self->priv;
1768   CoglTextureFlags flags = COGL_TEXTURE_NONE;
1769   CoglHandle handle;
1770
1771   priv->async_data = NULL;
1772
1773   if (error == NULL)
1774     {
1775       if (priv->no_slice)
1776         flags |= COGL_TEXTURE_NO_SLICING;
1777
1778       handle = cogl_texture_new_from_bitmap (bitmap,
1779                                              flags,
1780                                              COGL_PIXEL_FORMAT_ANY);
1781       clutter_texture_set_cogl_texture (self, handle);
1782
1783       if (priv->load_size_async)
1784         {
1785           g_signal_emit (self, texture_signals[SIZE_CHANGE], 0,
1786                          cogl_texture_get_width (handle),
1787                          cogl_texture_get_height (handle));
1788         }
1789
1790       cogl_object_unref (handle);
1791     }
1792
1793   g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, error);
1794
1795   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
1796 }
1797
1798 static gboolean
1799 texture_repaint_upload_func (gpointer user_data)
1800 {
1801   g_mutex_lock (&upload_list_mutex);
1802
1803   if (upload_list != NULL)
1804     {
1805       gint64 start_time = g_get_monotonic_time ();
1806
1807       /* continue uploading textures as long as we havent spent more
1808        * then 5ms doing so this stage redraw cycle.
1809        */
1810       do
1811         {
1812           ClutterTextureAsyncData *async_data = upload_list->data;
1813
1814           clutter_texture_async_data_lock (async_data);
1815
1816           if (async_data->state & ASYNC_STATE_QUEUED)
1817             {
1818               CLUTTER_NOTE (TEXTURE, "[async] operation complete for '%s'",
1819                             async_data->load_filename);
1820
1821               clutter_texture_async_load_complete (async_data->texture,
1822                                                    async_data->load_bitmap,
1823                                                    async_data->load_error);
1824             }
1825           else
1826             CLUTTER_NOTE (TEXTURE, "[async] operation cancelled for '%s'",
1827                           async_data->load_filename);
1828
1829           clutter_texture_async_data_unlock (async_data);
1830
1831           upload_list = g_list_remove (upload_list, async_data);
1832           clutter_texture_async_data_free (async_data);
1833         }
1834       while (upload_list != NULL &&
1835              g_get_monotonic_time () < start_time + 5 * 1000L);
1836     }
1837
1838   if (upload_list != NULL)
1839     {
1840       ClutterMasterClock *master_clock;
1841
1842       master_clock = _clutter_master_clock_get_default ();
1843       _clutter_master_clock_ensure_next_iteration (master_clock);
1844     }
1845
1846   g_mutex_unlock (&upload_list_mutex);
1847
1848   return TRUE;
1849 }
1850
1851 static void
1852 clutter_texture_thread_load (gpointer user_data,
1853                              gpointer pool_data)
1854 {
1855   ClutterTextureAsyncData *async_data = user_data;
1856   ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
1857
1858   clutter_texture_async_data_lock (async_data);
1859
1860   if (~async_data->state & ASYNC_STATE_CANCELLED)
1861     {
1862       CLUTTER_NOTE (TEXTURE, "[async] loading bitmap from file '%s'",
1863                     async_data->load_filename);
1864
1865       async_data->load_bitmap =
1866         cogl_bitmap_new_from_file (async_data->load_filename,
1867                                    &async_data->load_error);
1868
1869       g_mutex_lock (&upload_list_mutex);
1870
1871       if (repaint_upload_func == 0)
1872         {
1873           repaint_upload_func =
1874             clutter_threads_add_repaint_func (texture_repaint_upload_func,
1875                                               NULL, NULL);
1876         }
1877
1878       upload_list = g_list_append (upload_list, async_data);
1879       async_data->state |= ASYNC_STATE_QUEUED;
1880
1881       CLUTTER_NOTE (TEXTURE, "[async] operation queued");
1882
1883       g_mutex_unlock (&upload_list_mutex);
1884     }
1885   else
1886     {
1887       clutter_texture_async_data_unlock (async_data);
1888       clutter_texture_async_data_free (async_data);
1889
1890       return;
1891     }
1892
1893   clutter_texture_async_data_unlock (async_data);
1894
1895   _clutter_master_clock_ensure_next_iteration (master_clock);
1896 }
1897
1898 static gboolean
1899 clutter_texture_idle_load (gpointer data)
1900 {
1901   ClutterTextureAsyncData *async_data = data;
1902
1903   async_data->load_bitmap =
1904     cogl_bitmap_new_from_file (async_data->load_filename,
1905                                &async_data->load_error);
1906
1907   clutter_texture_async_load_complete (async_data->texture,
1908                                        async_data->load_bitmap,
1909                                        async_data->load_error);
1910
1911   clutter_texture_async_data_free (async_data);
1912
1913   return FALSE;
1914 }
1915
1916 /*
1917  * clutter_texture_async_load:
1918  * @self: a #ClutterTExture
1919  * @filename: name of the file to load
1920  * @error: return location for a #GError
1921  *
1922  * Starts an asynchronous load of the file name stored inside
1923  * the load_filename member of @data.
1924  *
1925  * If threading is enabled we use a GThread to perform the actual
1926  * I/O; if threading is not enabled, we use an idle GSource.
1927  *
1928  * The I/O is the only bit done in a thread -- uploading the
1929  * texture data to the GL pipeline must be done from within the
1930  * same thread that called clutter_main(). Threaded upload should
1931  * be part of the GL implementation.
1932  *
1933  * This function will block until we get a size from the file
1934  * so that we can effectively get the size the texture actor after
1935  * clutter_texture_set_from_file().
1936  *
1937  * Return value: %TRUE if the asynchronous loading was successfully
1938  *   initiated, %FALSE otherwise
1939  */
1940 static gboolean
1941 clutter_texture_async_load (ClutterTexture *self,
1942                             const gchar *filename,
1943                             GError **error)
1944 {
1945   ClutterTexturePrivate *priv = self->priv;
1946   ClutterTextureAsyncData *data;
1947   gint width, height;
1948   gboolean res;
1949
1950   /* ask the file for a size; if we cannot get the size then
1951    * there's no point in even continuing the asynchronous
1952    * loading, so we just stop there
1953    */
1954
1955   if (priv->load_size_async)
1956     {
1957       res = TRUE;
1958       width = 0;
1959       height = 0;
1960     }
1961   else
1962     res = cogl_bitmap_get_size_from_file (filename, &width, &height);
1963
1964   if (!res)
1965     {
1966       g_set_error (error, CLUTTER_TEXTURE_ERROR,
1967                    CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1968                    _("Failed to load the image data"));
1969       return FALSE;
1970     }
1971   else
1972     {
1973       priv->image_width = width;
1974       priv->image_height = height;
1975     }
1976
1977   clutter_texture_async_load_cancel (self);
1978
1979   data = g_slice_new0 (ClutterTextureAsyncData);
1980
1981   data->texture = self;
1982   data->load_filename = g_strdup (filename);
1983
1984   priv->async_data = data;
1985
1986   if (1)
1987     {
1988       if (G_UNLIKELY (async_thread_pool == NULL))
1989         {
1990           /* This apparently can't fail if exclusive == FALSE */
1991           async_thread_pool =
1992             g_thread_pool_new (clutter_texture_thread_load, NULL,
1993                                1,
1994                                FALSE,
1995                                NULL);
1996         }
1997
1998       g_thread_pool_push (async_thread_pool, data, NULL);
1999     }
2000   else
2001     {
2002       data->load_idle =
2003         clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
2004                                        clutter_texture_idle_load,
2005                                        data,
2006                                        NULL);
2007     }
2008
2009   return TRUE;
2010 }
2011
2012 /**
2013  * clutter_texture_set_from_file:
2014  * @texture: A #ClutterTexture
2015  * @filename: The filename of the image in GLib file name encoding
2016  * @error: Return location for a #GError, or %NULL
2017  *
2018  * Sets the #ClutterTexture image data from an image file. In case of
2019  * failure, %FALSE is returned and @error is set.
2020  *
2021  * If #ClutterTexture:load-async is set to %TRUE, this function
2022  * will return as soon as possible, and the actual image loading
2023  * from disk will be performed asynchronously. #ClutterTexture::size-change
2024  * will be emitten when the size of the texture is available and
2025  * #ClutterTexture::load-finished will be emitted when the image has been
2026  * loaded or if an error occurred.
2027  *
2028  * Return value: %TRUE if the image was successfully loaded and set
2029  *
2030  * Since: 0.8
2031  */
2032 gboolean
2033 clutter_texture_set_from_file (ClutterTexture *texture,
2034                                const gchar    *filename,
2035                                GError        **error)
2036 {
2037   ClutterTexturePrivate *priv;
2038   CoglHandle new_texture = NULL;
2039   GError *internal_error = NULL;
2040   CoglTextureFlags flags = COGL_TEXTURE_NONE;
2041
2042   priv = texture->priv;
2043
2044   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2045
2046   if (priv->load_data_async)
2047     return clutter_texture_async_load (texture, filename, error);
2048
2049   if (priv->no_slice)
2050     flags |= COGL_TEXTURE_NO_SLICING;
2051
2052   new_texture = cogl_texture_new_from_file (filename,
2053                                             flags,
2054                                             COGL_PIXEL_FORMAT_ANY,
2055                                             &internal_error);
2056
2057   /* If COGL didn't give an error then make one up */
2058   if (internal_error == NULL && new_texture == NULL)
2059     {
2060       g_set_error (&internal_error, CLUTTER_TEXTURE_ERROR,
2061                    CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2062                    _("Failed to load the image data"));
2063     }
2064
2065   if (internal_error != NULL)
2066     {
2067       g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0,
2068                      internal_error);
2069
2070       g_propagate_error (error, internal_error);
2071
2072       return FALSE;
2073     }
2074
2075   g_free (priv->filename);
2076   priv->filename = g_strdup (filename);
2077
2078   clutter_texture_set_cogl_texture (texture, new_texture);
2079
2080   cogl_object_unref (new_texture);
2081
2082   g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
2083
2084   g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILENAME]);
2085
2086   return TRUE;
2087 }
2088
2089 /**
2090  * clutter_texture_set_filter_quality:
2091  * @texture: a #ClutterTexture
2092  * @filter_quality: new filter quality value
2093  *
2094  * Sets the filter quality when scaling a texture. The quality is an
2095  * enumeration currently the following values are supported:
2096  * %CLUTTER_TEXTURE_QUALITY_LOW which is fast but only uses nearest neighbour
2097  * interpolation. %CLUTTER_TEXTURE_QUALITY_MEDIUM which is computationally a
2098  * bit more expensive (bilinear interpolation), and
2099  * %CLUTTER_TEXTURE_QUALITY_HIGH which uses extra texture memory resources to
2100  * improve scaled down rendering as well (by using mipmaps). The default value
2101  * is %CLUTTER_TEXTURE_QUALITY_MEDIUM.
2102  *
2103  * Since: 0.8
2104  */
2105 void
2106 clutter_texture_set_filter_quality (ClutterTexture        *texture,
2107                                     ClutterTextureQuality  filter_quality)
2108 {
2109   ClutterTexturePrivate *priv;
2110   ClutterTextureQuality  old_quality;
2111
2112   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2113
2114   priv = texture->priv;
2115
2116   old_quality = clutter_texture_get_filter_quality (texture);
2117
2118   if (filter_quality != old_quality)
2119     {
2120       gint min_filter, mag_filter;
2121
2122       min_filter = mag_filter = COGL_PIPELINE_FILTER_LINEAR;
2123       clutter_texture_quality_to_filters (filter_quality,
2124                                           &min_filter,
2125                                           &mag_filter);
2126
2127       cogl_pipeline_set_layer_filters (priv->pipeline, 0,
2128                                        min_filter, mag_filter);
2129
2130       clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2131
2132       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILTER_QUALITY]);
2133     }
2134 }
2135
2136 /**
2137  * clutter_texture_get_filter_quality:
2138  * @texture: A #ClutterTexture
2139  *
2140  * Gets the filter quality used when scaling a texture.
2141  *
2142  * Return value: The filter quality value.
2143  *
2144  * Since: 0.8
2145  */
2146 ClutterTextureQuality
2147 clutter_texture_get_filter_quality (ClutterTexture *texture)
2148 {
2149   ClutterTexturePrivate *priv;
2150   int layer_index;
2151   CoglPipelineFilter min_filter, mag_filter;
2152   int i;
2153
2154   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2155
2156   priv = texture->priv;
2157
2158   if (get_first_layer_index (priv->pipeline, &layer_index))
2159     {
2160       min_filter = cogl_pipeline_get_layer_min_filter (priv->pipeline,
2161                                                        layer_index);
2162       mag_filter = cogl_pipeline_get_layer_mag_filter (priv->pipeline,
2163                                                        layer_index);
2164     }
2165   else
2166     return CLUTTER_TEXTURE_QUALITY_MEDIUM;
2167
2168   for (i = 0; i < G_N_ELEMENTS (clutter_texture_quality_filters); i++)
2169     if (clutter_texture_quality_filters[i].min_filter == min_filter
2170         && clutter_texture_quality_filters[i].mag_filter == mag_filter)
2171       return i;
2172
2173   /* Unknown filter combination */
2174   return CLUTTER_TEXTURE_QUALITY_LOW;
2175 }
2176
2177 /**
2178  * clutter_texture_get_max_tile_waste:
2179  * @texture: A #ClutterTexture
2180  *
2181  * Gets the maximum waste that will be used when creating a texture or
2182  * -1 if slicing is disabled.
2183  *
2184  * Return value: The maximum waste or -1 if the texture waste is
2185  *   unlimited.
2186  *
2187  * Since: 0.8
2188  */
2189 gint
2190 clutter_texture_get_max_tile_waste (ClutterTexture *texture)
2191 {
2192   ClutterTexturePrivate *priv;
2193   CoglHandle             cogl_texture;
2194
2195   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2196
2197   priv = texture->priv;
2198
2199   cogl_texture = clutter_texture_get_cogl_texture (texture);
2200
2201   if (cogl_texture == NULL)
2202     return priv->no_slice ? -1 : COGL_TEXTURE_MAX_WASTE;
2203   else
2204     return cogl_texture_get_max_waste (cogl_texture);
2205 }
2206
2207 /**
2208  * clutter_texture_new_from_file:
2209  * @filename: The name of an image file to load.
2210  * @error: Return locatoin for an error.
2211  *
2212  * Creates a new ClutterTexture actor to display the image contained a
2213  * file. If the image failed to load then NULL is returned and @error
2214  * is set.
2215  *
2216  * Return value: A newly created #ClutterTexture object or NULL on
2217  * error.
2218  *
2219  * Since: 0.8
2220  **/
2221 ClutterActor*
2222 clutter_texture_new_from_file (const gchar *filename,
2223                                GError     **error)
2224 {
2225   ClutterActor *texture = clutter_texture_new ();
2226
2227   if (!clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
2228                                       filename, error))
2229     {
2230       g_object_ref_sink (texture);
2231       g_object_unref (texture);
2232
2233       return NULL;
2234     }
2235   else
2236     return texture;
2237 }
2238
2239 /**
2240  * clutter_texture_new:
2241  *
2242  * Creates a new empty #ClutterTexture object.
2243  *
2244  * Return value: A newly created #ClutterTexture object.
2245  **/
2246 ClutterActor *
2247 clutter_texture_new (void)
2248 {
2249   return g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
2250 }
2251
2252 /**
2253  * clutter_texture_get_base_size:
2254  * @texture: a #ClutterTexture
2255  * @width: (out): return location for the width, or %NULL
2256  * @height: (out): return location for the height, or %NULL
2257  *
2258  * Gets the size in pixels of the untransformed underlying image
2259  */
2260 void
2261 clutter_texture_get_base_size (ClutterTexture *texture,
2262                                gint           *width,
2263                                gint           *height)
2264 {
2265   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2266
2267   if (width)
2268     *width = texture->priv->image_width;
2269
2270   if (height)
2271     *height = texture->priv->image_height;
2272 }
2273
2274 /**
2275  * clutter_texture_set_area_from_rgb_data:
2276  * @texture: A #ClutterTexture
2277  * @data: (array): Image data in RGB type colorspace.
2278  * @has_alpha: Set to TRUE if image data has an alpha channel.
2279  * @x: X coordinate of upper left corner of region to update.
2280  * @y: Y coordinate of upper left corner of region to update.
2281  * @width: Width in pixels of region to update.
2282  * @height: Height in pixels of region to update.
2283  * @rowstride: Distance in bytes between row starts on source buffer.
2284  * @bpp: bytes per pixel (Currently only 3 and 4 supported,
2285  *                        depending on @has_alpha)
2286  * @flags: #ClutterTextureFlags
2287  * @error: return location for a #GError, or %NULL
2288  *
2289  * Updates a sub-region of the pixel data in a #ClutterTexture.
2290  *
2291  * Return value: %TRUE on success, %FALSE on failure.
2292  *
2293  * Since: 0.6
2294  */
2295 gboolean
2296 clutter_texture_set_area_from_rgb_data (ClutterTexture     *texture,
2297                                         const guchar       *data,
2298                                         gboolean            has_alpha,
2299                                         gint                x,
2300                                         gint                y,
2301                                         gint                width,
2302                                         gint                height,
2303                                         gint                rowstride,
2304                                         gint                bpp,
2305                                         ClutterTextureFlags flags,
2306                                         GError            **error)
2307 {
2308   CoglPixelFormat source_format;
2309   CoglHandle cogl_texture;
2310
2311   if (!get_pixel_format_from_texture_flags (bpp, has_alpha, flags,
2312                                             &source_format))
2313     {
2314       return FALSE;
2315     }
2316
2317   /* attempt to realize ... */
2318   if (!CLUTTER_ACTOR_IS_REALIZED (texture) &&
2319       clutter_actor_get_stage (CLUTTER_ACTOR (texture)) != NULL)
2320     {
2321       clutter_actor_realize (CLUTTER_ACTOR (texture));
2322     }
2323
2324   /* due to the fudging of clutter_texture_set_cogl_texture()
2325    * which allows setting a texture pre-realize, we may end
2326    * up having a texture even if we couldn't realize yet.
2327    */
2328   cogl_texture = clutter_texture_get_cogl_texture (texture);
2329   if (cogl_texture == NULL)
2330     {
2331       g_warning ("Failed to realize actor '%s'",
2332                  _clutter_actor_get_debug_name (CLUTTER_ACTOR (texture)));
2333       return FALSE;
2334     }
2335
2336   if (!cogl_texture_set_region (cogl_texture,
2337                                 0, 0,
2338                                 x, y, width, height,
2339                                 width, height,
2340                                 source_format,
2341                                 rowstride,
2342                                 data))
2343     {
2344       g_set_error (error, CLUTTER_TEXTURE_ERROR,
2345                    CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2346                    _("Failed to load the image data"));
2347       return FALSE;
2348     }
2349
2350   g_free (texture->priv->filename);
2351   texture->priv->filename = NULL;
2352
2353   /* rename signal */
2354   g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
2355
2356   clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2357
2358   return TRUE;
2359 }
2360
2361 static void
2362 on_fbo_source_size_change (GObject          *object,
2363                            GParamSpec       *param_spec,
2364                            ClutterTexture   *texture)
2365 {
2366   ClutterTexturePrivate *priv = texture->priv;
2367   gfloat w, h;
2368   ClutterActorBox box;
2369   gboolean status;
2370
2371   status = clutter_actor_get_paint_box (priv->fbo_source, &box);
2372   if (status)
2373     clutter_actor_box_get_size (&box, &w, &h);
2374
2375   /* In the end we will size the framebuffer according to the paint
2376    * box, but for code that does:
2377    *   tex = clutter_texture_new_from_actor (src);
2378    *   clutter_actor_get_size (tex, &width, &height);
2379    * it seems more helpfull to return the src actor size if it has a
2380    * degenerate paint box. The most likely reason it will have a
2381    * degenerate paint box is simply that the src currently has no
2382    * parent. */
2383   if (status == FALSE || w == 0 || h == 0)
2384     clutter_actor_get_size (priv->fbo_source, &w, &h);
2385
2386   /* We can't create a texture with a width or height of 0... */
2387   w = MAX (1, w);
2388   h = MAX (1, h);
2389
2390   if (w != priv->image_width || h != priv->image_height)
2391     {
2392       CoglTextureFlags flags = COGL_TEXTURE_NONE;
2393       CoglHandle tex;
2394
2395       /* tear down the FBO */
2396       if (priv->fbo_handle != NULL)
2397         cogl_object_unref (priv->fbo_handle);
2398
2399       texture_free_gl_resources (texture);
2400
2401       priv->image_width = w;
2402       priv->image_height = h;
2403
2404       flags |= COGL_TEXTURE_NO_SLICING;
2405
2406       tex = cogl_texture_new_with_size (MAX (priv->image_width, 1),
2407                                         MAX (priv->image_height, 1),
2408                                         flags,
2409                                         COGL_PIXEL_FORMAT_RGBA_8888_PRE);
2410
2411       cogl_pipeline_set_layer_texture (priv->pipeline, 0, tex);
2412
2413       priv->fbo_handle = cogl_offscreen_new_to_texture (tex);
2414
2415       /* The pipeline now has a reference to the texture so it will
2416          stick around */
2417       cogl_object_unref (tex);
2418
2419       if (priv->fbo_handle == NULL)
2420         {
2421           g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
2422           return;
2423         }
2424
2425       clutter_actor_set_size (CLUTTER_ACTOR (texture), w, h);
2426     }
2427 }
2428
2429 static void
2430 on_fbo_parent_change (ClutterActor        *actor,
2431                       ClutterActor        *old_parent,
2432                       ClutterTexture      *texture)
2433 {
2434   ClutterActor        *parent = CLUTTER_ACTOR(texture);
2435
2436   while ((parent = clutter_actor_get_parent (parent)) != NULL)
2437     {
2438       if (parent == actor)
2439         {
2440           g_warning ("Offscreen texture is ancestor of source!");
2441           /* Desperate but will avoid infinite loops */
2442           clutter_actor_remove_child (parent, actor);
2443         }
2444     }
2445 }
2446
2447 static void
2448 fbo_source_queue_redraw_cb (ClutterActor *source,
2449                             ClutterActor *origin,
2450                             ClutterTexture *texture)
2451 {
2452   clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2453 }
2454
2455 static void
2456 fbo_source_queue_relayout_cb (ClutterActor *source,
2457                               ClutterTexture *texture)
2458 {
2459   clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2460 }
2461
2462 /**
2463  * clutter_texture_new_from_actor:
2464  * @actor: A source #ClutterActor
2465  *
2466  * Creates a new #ClutterTexture object with its source a prexisting
2467  * actor (and associated children). The textures content will contain
2468  * 'live' redirected output of the actors scene.
2469  *
2470  * Note this function is intented as a utility call for uniformly applying
2471  * shaders to groups and other potential visual effects. It requires that
2472  * the %CLUTTER_FEATURE_OFFSCREEN feature is supported by the current backend
2473  * and the target system.
2474  *
2475  * Some tips on usage:
2476  *
2477  * <itemizedlist>
2478  *   <listitem>
2479  *     <para>The source actor must be made visible (i.e by calling
2480  *     #clutter_actor_show).</para>
2481  *   </listitem>
2482  *   <listitem>
2483  *     <para>The source actor must have a parent in order for it to be
2484  *     allocated a size from the layouting mechanism. If the source
2485  *     actor does not have a parent when this function is called then
2486  *     the ClutterTexture will adopt it and allocate it at its
2487  *     preferred size. Using this you can clone an actor that is
2488  *     otherwise not displayed. Because of this feature if you do
2489  *     intend to display the source actor then you must make sure that
2490  *     the actor is parented before calling
2491  *     clutter_texture_new_from_actor() or that you unparent it before
2492  *     adding it to a container.</para>
2493  *   </listitem>
2494  *   <listitem>
2495  *     <para>When getting the image for the clone texture, Clutter
2496  *     will attempt to render the source actor exactly as it would
2497  *     appear if it was rendered on screen. The source actor's parent
2498  *     transformations are taken into account. Therefore if your
2499  *     source actor is rotated along the X or Y axes so that it has
2500  *     some depth, the texture will appear differently depending on
2501  *     the on-screen location of the source actor. While painting the
2502  *     source actor, Clutter will set up a temporary asymmetric
2503  *     perspective matrix as the projection matrix so that the source
2504  *     actor will be projected as if a small section of the screen was
2505  *     being viewed. Before version 0.8.2, an orthogonal identity
2506  *     projection was used which meant that the source actor would be
2507  *     clipped if any part of it was not on the zero Z-plane.</para>
2508  *   </listitem>
2509  *   <listitem>
2510  *     <para>Avoid reparenting the source with the created texture.</para>
2511  *   </listitem>
2512  *   <listitem>
2513  *     <para>A group can be padded with a transparent rectangle as to
2514  *     provide a border to contents for shader output (blurring text
2515  *     for example).</para>
2516  *   </listitem>
2517  *   <listitem>
2518  *     <para>The texture will automatically resize to contain a further
2519  *     transformed source. However, this involves overhead and can be
2520  *     avoided by placing the source actor in a bounding group
2521  *     sized large enough to contain any child tranformations.</para>
2522  *   </listitem>
2523  *   <listitem>
2524  *     <para>Uploading pixel data to the texture (e.g by using
2525  *     clutter_texture_set_from_file()) will destroy the offscreen texture
2526  *     data and end redirection.</para>
2527  *   </listitem>
2528  *   <listitem>
2529  *     <para>cogl_texture_get_data() with the handle returned by
2530  *     clutter_texture_get_cogl_texture() can be used to read the
2531  *     offscreen texture pixels into a pixbuf.</para>
2532  *   </listitem>
2533  * </itemizedlist>
2534  *
2535  * Return value: A newly created #ClutterTexture object, or %NULL on failure.
2536  *
2537  * Deprecated: 1.8: Use the #ClutterOffscreenEffect and #ClutterShaderEffect
2538  *   directly on the intended #ClutterActor to replace the functionality of
2539  *   this function.
2540  *
2541  * Since: 0.6
2542  */
2543 ClutterActor *
2544 clutter_texture_new_from_actor (ClutterActor *actor)
2545 {
2546   ClutterTexture        *texture;
2547   ClutterTexturePrivate *priv;
2548   gfloat w, h;
2549   ClutterActorBox box;
2550   gboolean status;
2551
2552   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2553
2554   if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE)
2555     return NULL;
2556
2557   if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2558     {
2559       clutter_actor_realize (actor);
2560
2561       if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2562         return NULL;
2563     }
2564
2565   status = clutter_actor_get_paint_box (actor, &box);
2566   if (status)
2567     clutter_actor_box_get_size (&box, &w, &h);
2568
2569   /* In the end we will size the framebuffer according to the paint
2570    * box, but for code that does:
2571    *   tex = clutter_texture_new_from_actor (src);
2572    *   clutter_actor_get_size (tex, &width, &height);
2573    * it seems more helpfull to return the src actor size if it has a
2574    * degenerate paint box. The most likely reason it will have a
2575    * degenerate paint box is simply that the src currently has no
2576    * parent. */
2577   if (status == FALSE || w == 0 || h == 0)
2578     clutter_actor_get_size (actor, &w, &h);
2579
2580   /* We can't create a 0x0 fbo so always bump the size up to at least
2581    * 1 */
2582   w = MAX (1, w);
2583   h = MAX (1, h);
2584
2585   /* Hopefully now were good.. */
2586   texture = g_object_new (CLUTTER_TYPE_TEXTURE,
2587                           "disable-slicing", TRUE,
2588                           NULL);
2589
2590   priv = texture->priv;
2591
2592   priv->fbo_source = g_object_ref_sink (actor);
2593
2594   /* If the actor doesn't have a parent then claim it so that it will
2595      get a size allocation during layout */
2596   if (clutter_actor_get_parent (actor) == NULL)
2597     clutter_actor_add_child (CLUTTER_ACTOR (texture), actor);
2598
2599   /* Connect up any signals which could change our underlying size */
2600   g_signal_connect (actor,
2601                     "notify::width",
2602                     G_CALLBACK(on_fbo_source_size_change),
2603                     texture);
2604   g_signal_connect (actor,
2605                     "notify::height",
2606                     G_CALLBACK(on_fbo_source_size_change),
2607                     texture);
2608   g_signal_connect (actor,
2609                     "notify::scale-x",
2610                     G_CALLBACK(on_fbo_source_size_change),
2611                     texture);
2612   g_signal_connect (actor,
2613                     "notify::scale-y",
2614                     G_CALLBACK(on_fbo_source_size_change),
2615                     texture);
2616   g_signal_connect (actor,
2617                     "notify::rotation-angle-x",
2618                     G_CALLBACK(on_fbo_source_size_change),
2619                     texture);
2620   g_signal_connect (actor,
2621                     "notify::rotation-angle-y",
2622                     G_CALLBACK(on_fbo_source_size_change),
2623                     texture);
2624   g_signal_connect (actor,
2625                     "notify::rotation-angle-z",
2626                     G_CALLBACK(on_fbo_source_size_change),
2627                     texture);
2628
2629   g_signal_connect (actor, "queue-relayout",
2630                     G_CALLBACK (fbo_source_queue_relayout_cb), texture);
2631   g_signal_connect (actor, "queue-redraw",
2632                     G_CALLBACK (fbo_source_queue_redraw_cb), texture);
2633
2634   /* And a warning if the source becomes a child of the texture */
2635   g_signal_connect (actor,
2636                     "parent-set",
2637                     G_CALLBACK(on_fbo_parent_change),
2638                     texture);
2639
2640   priv->image_width = w;
2641   priv->image_height = h;
2642
2643   clutter_actor_set_size (CLUTTER_ACTOR (texture),
2644                           priv->image_width,
2645                           priv->image_height);
2646
2647   return CLUTTER_ACTOR (texture);
2648 }
2649
2650 static void
2651 texture_fbo_free_resources (ClutterTexture *texture)
2652 {
2653   ClutterTexturePrivate *priv;
2654
2655   priv = texture->priv;
2656
2657   if (priv->fbo_source != NULL)
2658     {
2659       ClutterActor *parent;
2660
2661       parent = clutter_actor_get_parent (priv->fbo_source);
2662
2663       /* If we parented the texture then unparent it again so that it
2664          will lose the reference */
2665       if (parent == CLUTTER_ACTOR (texture))
2666         clutter_actor_remove_child (parent, priv->fbo_source);
2667
2668       g_signal_handlers_disconnect_by_func
2669                             (priv->fbo_source,
2670                              G_CALLBACK(on_fbo_parent_change),
2671                              texture);
2672
2673       g_signal_handlers_disconnect_by_func
2674                             (priv->fbo_source,
2675                              G_CALLBACK(on_fbo_source_size_change),
2676                              texture);
2677
2678       g_signal_handlers_disconnect_by_func
2679                             (priv->fbo_source,
2680                              G_CALLBACK(fbo_source_queue_relayout_cb),
2681                              texture);
2682
2683       g_signal_handlers_disconnect_by_func
2684                             (priv->fbo_source,
2685                              G_CALLBACK(fbo_source_queue_redraw_cb),
2686                              texture);
2687
2688       g_object_unref (priv->fbo_source);
2689
2690       priv->fbo_source = NULL;
2691     }
2692
2693   if (priv->fbo_handle != NULL)
2694     {
2695       cogl_object_unref (priv->fbo_handle);
2696       priv->fbo_handle = NULL;
2697     }
2698 }
2699
2700 /**
2701  * clutter_texture_set_sync_size:
2702  * @texture: a #ClutterTexture
2703  * @sync_size: %TRUE if the texture should have the same size of the
2704  *    underlying image data
2705  *
2706  * Sets whether @texture should have the same preferred size as the
2707  * underlying image data.
2708  *
2709  * Since: 1.0
2710  */
2711 void
2712 clutter_texture_set_sync_size (ClutterTexture *texture,
2713                                gboolean        sync_size)
2714 {
2715   ClutterTexturePrivate *priv;
2716
2717   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2718
2719   priv = texture->priv;
2720
2721   if (priv->sync_actor_size != sync_size)
2722     {
2723       priv->sync_actor_size = sync_size;
2724
2725       clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2726
2727       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_SYNC_SIZE]);
2728     }
2729 }
2730
2731 /**
2732  * clutter_texture_get_sync_size:
2733  * @texture: a #ClutterTexture
2734  *
2735  * Retrieves the value set with clutter_texture_set_sync_size()
2736  *
2737  * Return value: %TRUE if the #ClutterTexture should have the same
2738  *   preferred size of the underlying image data
2739  *
2740  * Since: 1.0
2741  */
2742 gboolean
2743 clutter_texture_get_sync_size (ClutterTexture *texture)
2744 {
2745   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2746
2747   return texture->priv->sync_actor_size;
2748 }
2749
2750 /**
2751  * clutter_texture_set_repeat:
2752  * @texture: a #ClutterTexture
2753  * @repeat_x: %TRUE if the texture should repeat horizontally
2754  * @repeat_y: %TRUE if the texture should repeat vertically
2755  *
2756  * Sets whether the @texture should repeat horizontally or
2757  * vertically when the actor size is bigger than the image size
2758  *
2759  * Since: 1.0
2760  */
2761 void
2762 clutter_texture_set_repeat (ClutterTexture *texture,
2763                             gboolean        repeat_x,
2764                             gboolean        repeat_y)
2765 {
2766   ClutterTexturePrivate *priv;
2767   gboolean changed = FALSE;
2768
2769   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2770
2771   priv = texture->priv;
2772
2773   g_object_freeze_notify (G_OBJECT (texture));
2774
2775   if (priv->repeat_x != repeat_x)
2776     {
2777       priv->repeat_x = repeat_x;
2778
2779       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_X]);
2780
2781       changed = TRUE;
2782     }
2783
2784   if (priv->repeat_y != repeat_y)
2785     {
2786       priv->repeat_y = repeat_y;
2787
2788       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_Y]);
2789
2790       changed = TRUE;
2791     }
2792
2793   if (changed)
2794     clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2795
2796   g_object_thaw_notify (G_OBJECT (texture));
2797 }
2798
2799 /**
2800  * clutter_texture_get_repeat:
2801  * @texture: a #ClutterTexture
2802  * @repeat_x: (out): return location for the horizontal repeat
2803  * @repeat_y: (out): return location for the vertical repeat
2804  *
2805  * Retrieves the horizontal and vertical repeat values set
2806  * using clutter_texture_set_repeat()
2807  *
2808  * Since: 1.0
2809  */
2810 void
2811 clutter_texture_get_repeat (ClutterTexture *texture,
2812                             gboolean       *repeat_x,
2813                             gboolean       *repeat_y)
2814 {
2815   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2816
2817   if (repeat_x != NULL)
2818     *repeat_x = texture->priv->repeat_x;
2819
2820   if (repeat_y != NULL)
2821     *repeat_y = texture->priv->repeat_y;
2822 }
2823
2824 /**
2825  * clutter_texture_get_pixel_format:
2826  * @texture: a #ClutterTexture
2827  *
2828  * Retrieves the pixel format used by @texture. This is
2829  * equivalent to:
2830  *
2831  * |[
2832  *   handle = clutter_texture_get_pixel_format (texture);
2833  *
2834  *   if (handle != COGL_INVALID_HANDLE)
2835  *     format = cogl_texture_get_format (handle);
2836  * ]|
2837  *
2838  * Return value: a #CoglPixelFormat value
2839  *
2840  * Since: 1.0
2841  */
2842 CoglPixelFormat
2843 clutter_texture_get_pixel_format (ClutterTexture *texture)
2844 {
2845   CoglHandle cogl_texture;
2846
2847   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), COGL_PIXEL_FORMAT_ANY);
2848
2849   cogl_texture = clutter_texture_get_cogl_texture (texture);
2850   if (cogl_texture == NULL)
2851     return COGL_PIXEL_FORMAT_ANY;
2852
2853   return cogl_texture_get_format (cogl_texture);
2854 }
2855
2856 /**
2857  * clutter_texture_set_keep_aspect_ratio:
2858  * @texture: a #ClutterTexture
2859  * @keep_aspect: %TRUE to maintain aspect ratio
2860  *
2861  * Sets whether @texture should have a preferred size maintaining
2862  * the aspect ratio of the underlying image
2863  *
2864  * Since: 1.0
2865  */
2866 void
2867 clutter_texture_set_keep_aspect_ratio (ClutterTexture *texture,
2868                                        gboolean        keep_aspect)
2869 {
2870   ClutterTexturePrivate *priv;
2871
2872   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2873
2874   priv = texture->priv;
2875
2876   if (priv->keep_aspect_ratio != keep_aspect)
2877     {
2878       priv->keep_aspect_ratio = keep_aspect;
2879
2880       clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2881
2882       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_KEEP_ASPECT_RATIO]);
2883     }
2884 }
2885
2886 /**
2887  * clutter_texture_get_keep_aspect_ratio:
2888  * @texture: a #ClutterTexture
2889  *
2890  * Retrieves the value set using clutter_texture_set_keep_aspect_ratio()
2891  *
2892  * Return value: %TRUE if the #ClutterTexture should maintain the
2893  *   aspect ratio of the underlying image
2894  *
2895  * Since: 1.0
2896  */
2897 gboolean
2898 clutter_texture_get_keep_aspect_ratio (ClutterTexture *texture)
2899 {
2900   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2901
2902   return texture->priv->keep_aspect_ratio;
2903 }
2904
2905 /**
2906  * clutter_texture_set_load_async:
2907  * @texture: a #ClutterTexture
2908  * @load_async: %TRUE if the texture should asynchronously load data
2909  *   from a filename
2910  *
2911  * Sets whether @texture should use a worker thread to load the data
2912  * from disk asynchronously. Setting @load_async to %TRUE will make
2913  * clutter_texture_set_from_file() return immediately.
2914  *
2915  * See the #ClutterTexture:load-async property documentation, and
2916  * clutter_texture_set_load_data_async().
2917  *
2918  * Since: 1.0
2919  */
2920 void
2921 clutter_texture_set_load_async (ClutterTexture *texture,
2922                                 gboolean        load_async)
2923 {
2924   ClutterTexturePrivate *priv;
2925
2926   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2927
2928   priv = texture->priv;
2929
2930   load_async = !!load_async;
2931
2932   if (priv->load_async_set != load_async)
2933     {
2934       priv->load_data_async = load_async;
2935       priv->load_size_async = load_async;
2936
2937       priv->load_async_set = load_async;
2938
2939       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_ASYNC]);
2940       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_DATA_ASYNC]);
2941     }
2942 }
2943
2944 /**
2945  * clutter_texture_get_load_async:
2946  * @texture: a #ClutterTexture
2947  *
2948  * Retrieves the value set using clutter_texture_set_load_async()
2949  *
2950  * Return value: %TRUE if the #ClutterTexture should load the data from
2951  *   disk asynchronously
2952  *
2953  * Since: 1.0
2954  */
2955 gboolean
2956 clutter_texture_get_load_async (ClutterTexture *texture)
2957 {
2958   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2959
2960   return texture->priv->load_async_set;
2961 }
2962
2963 /**
2964  * clutter_texture_set_load_data_async:
2965  * @texture: a #ClutterTexture
2966  * @load_async: %TRUE if the texture should asynchronously load data
2967  *   from a filename
2968  *
2969  * Sets whether @texture should use a worker thread to load the data
2970  * from disk asynchronously. Setting @load_async to %TRUE will make
2971  * clutter_texture_set_from_file() block until the #ClutterTexture has
2972  * determined the width and height of the image data.
2973  *
2974  * See the #ClutterTexture:load-async property documentation, and
2975  * clutter_texture_set_load_async().
2976  *
2977  * Since: 1.0
2978  */
2979 void
2980 clutter_texture_set_load_data_async (ClutterTexture *texture,
2981                                      gboolean        load_async)
2982 {
2983   ClutterTexturePrivate *priv;
2984
2985   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2986
2987   priv = texture->priv;
2988
2989   if (priv->load_data_async != load_async)
2990     {
2991       /* load-data-async always unsets load-size-async */
2992       priv->load_data_async = load_async;
2993       priv->load_size_async = FALSE;
2994
2995       priv->load_async_set = load_async;
2996
2997       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_ASYNC]);
2998       g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_DATA_ASYNC]);
2999     }
3000 }
3001
3002 /**
3003  * clutter_texture_get_load_data_async:
3004  * @texture: a #ClutterTexture
3005  *
3006  * Retrieves the value set by clutter_texture_set_load_data_async()
3007  *
3008  * Return value: %TRUE if the #ClutterTexture should load the image
3009  *   data from a file asynchronously
3010  *
3011  * Since: 1.0
3012  */
3013 gboolean
3014 clutter_texture_get_load_data_async (ClutterTexture *texture)
3015 {
3016   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3017
3018   return texture->priv->load_async_set &&
3019          texture->priv->load_data_async;
3020 }
3021
3022 /**
3023  * clutter_texture_set_pick_with_alpha:
3024  * @texture: a #ClutterTexture
3025  * @pick_with_alpha: %TRUE if the alpha channel should affect the
3026  *   picking shape
3027  *
3028  * Sets whether @texture should have it's shape defined by the alpha
3029  * channel when picking.
3030  *
3031  * Be aware that this is a bit more costly than the default picking
3032  * due to the texture lookup, extra test against the alpha value and
3033  * the fact that it will also interrupt the batching of geometry done
3034  * internally.
3035  *
3036  * Also there is currently no control over the threshold used to
3037  * determine what value of alpha is considered pickable, and so only
3038  * fully opaque parts of the texture will react to picking.
3039  *
3040  * Since: 1.4
3041  */
3042 void
3043 clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
3044                                      gboolean        pick_with_alpha)
3045 {
3046   ClutterTexturePrivate *priv;
3047
3048   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
3049
3050   priv = texture->priv;
3051
3052   if (priv->pick_with_alpha == pick_with_alpha)
3053     return;
3054
3055   if (!pick_with_alpha && priv->pick_pipeline != NULL)
3056     {
3057       cogl_object_unref (priv->pick_pipeline);
3058       priv->pick_pipeline = NULL;
3059     }
3060
3061   /* NB: the pick pipeline is created lazily when we first pick */
3062   priv->pick_with_alpha = pick_with_alpha;
3063
3064   /* NB: actors are expected to call clutter_actor_queue_redraw when
3065    * ever some state changes that will affect painting *or picking...
3066    */
3067   clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
3068 }
3069
3070 /**
3071  * clutter_texture_get_pick_with_alpha:
3072  * @texture: a #ClutterTexture
3073  *
3074  * Retrieves the value set by clutter_texture_set_load_data_async()
3075  *
3076  * Return value: %TRUE if the #ClutterTexture should define its shape
3077  * using the alpha channel when picking.
3078  *
3079  * Since: 1.4
3080  */
3081 gboolean
3082 clutter_texture_get_pick_with_alpha (ClutterTexture *texture)
3083 {
3084   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3085
3086   return texture->priv->pick_with_alpha ? TRUE : FALSE;
3087 }
3088