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