4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Matthew Allum <mallum@openedhand.com>
8 * Copyright (C) 2006 OpenedHand
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.
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.
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/>.
27 * SECTION:clutter-texture
28 * @short_description: An actor for displaying and manipulating images.
30 * #ClutterTexture is a base class for displaying and manipulating pixel
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.
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().
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.
50 * Undefining COGL_ENABLE_EXPERIMENTAL_2_0_API will still expose
51 * us experimental api but will also expose Cogl 1.x api too...
53 #undef COGL_ENABLE_EXPERIMENTAL_2_0_API
54 #include <cogl/cogl.h>
56 #define CLUTTER_ENABLE_EXPERIMENTAL_API
58 /* sadly, we are still using ClutterShader internally */
59 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
61 #include "clutter-texture.h"
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"
74 #include "deprecated/clutter-shader.h"
75 #include "deprecated/clutter-texture.h"
76 #include "deprecated/clutter-util.h"
78 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
80 G_DEFINE_TYPE_WITH_CODE (ClutterTexture,
83 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
84 clutter_scriptable_iface_init));
86 #define CLUTTER_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXTURE, ClutterTexturePrivate))
88 typedef struct _ClutterTextureAsyncData ClutterTextureAsyncData;
90 struct _ClutterTexturePrivate
95 CoglPipeline *pipeline;
97 ClutterActor *fbo_source;
98 CoglHandle fbo_handle;
100 CoglPipeline *pick_pipeline;
104 ClutterTextureAsyncData *async_data;
107 guint sync_actor_size : 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;
119 #define ASYNC_STATE_LOCKED 1
120 #define ASYNC_STATE_CANCELLED 2
121 #define ASYNC_STATE_QUEUED 3
123 struct _ClutterTextureAsyncData
125 /* The texture for which the data is being loaded */
126 ClutterTexture *texture;
128 gchar *load_filename;
129 CoglHandle load_bitmap;
139 clutter_texture_async_data_lock (ClutterTextureAsyncData *data)
141 g_bit_lock (&data->state, 0);
145 clutter_texture_async_data_unlock (ClutterTextureAsyncData *data)
147 g_bit_unlock (&data->state, 0);
163 PROP_KEEP_ASPECT_RATIO,
165 PROP_LOAD_DATA_ASYNC,
166 PROP_PICK_WITH_ALPHA,
171 static GParamSpec *obj_props[PROP_LAST];
182 static int texture_signals[LAST_SIGNAL] = { 0 };
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;
189 static CoglPipeline *texture_template_pipeline = NULL;
192 texture_fbo_free_resources (ClutterTexture *texture);
195 clutter_texture_error_quark (void)
197 return g_quark_from_static_string ("clutter-texture-error-quark");
205 clutter_texture_quality_filters[] =
207 /* CLUTTER_TEXTURE_QUALITY_LOW */
208 { COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST },
210 /* CLUTTER_TEXTURE_QUALITY_MEDIUM */
211 { COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR },
213 /* CLUTTER_TEXTURE_QUALITY_HIGH */
214 { COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, COGL_PIPELINE_FILTER_LINEAR }
218 clutter_texture_quality_to_filters (ClutterTextureQuality quality,
222 g_return_if_fail (quality < G_N_ELEMENTS (clutter_texture_quality_filters));
225 *min_filter_p = clutter_texture_quality_filters[quality].min_filter;
228 *mag_filter_p = clutter_texture_quality_filters[quality].mag_filter;
232 texture_free_gl_resources (ClutterTexture *texture)
234 ClutterTexturePrivate *priv = texture->priv;
236 if (priv->pipeline != NULL)
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
241 cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL);
246 clutter_texture_unrealize (ClutterActor *actor)
248 ClutterTexture *texture;
249 ClutterTexturePrivate *priv;
251 texture = CLUTTER_TEXTURE(actor);
252 priv = texture->priv;
254 if (priv->pipeline == NULL)
257 if (priv->fbo_source != NULL)
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);
266 CLUTTER_NOTE (TEXTURE, "Texture unrealized");
270 clutter_texture_realize (ClutterActor *actor)
272 ClutterTexture *texture;
273 ClutterTexturePrivate *priv;
275 texture = CLUTTER_TEXTURE(actor);
276 priv = texture->priv;
278 if (priv->fbo_source)
280 CoglTextureFlags flags = COGL_TEXTURE_NONE;
286 flags |= COGL_TEXTURE_NO_SLICING;
288 tex = cogl_texture_new_with_size (priv->image_width,
291 COGL_PIXEL_FORMAT_RGBA_8888_PRE);
293 cogl_pipeline_set_layer_texture (priv->pipeline, 0, tex);
295 priv->fbo_handle = cogl_offscreen_new_to_texture (tex);
297 /* The pipeline now has a reference to the texture so it will
299 cogl_object_unref (tex);
301 if (priv->fbo_handle == NULL)
303 g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
304 CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
308 clutter_actor_set_size (actor, priv->image_width, priv->image_height);
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.
323 CLUTTER_NOTE (TEXTURE, "Texture realized");
327 clutter_texture_get_preferred_width (ClutterActor *self,
330 gfloat *natural_width_p)
332 ClutterTexture *texture = CLUTTER_TEXTURE (self);
333 ClutterTexturePrivate *priv = texture->priv;
335 /* Min request is always 0 since we can scale down or clip */
339 if (priv->sync_actor_size)
343 if (!priv->keep_aspect_ratio ||
345 priv->image_height <= 0)
347 *natural_width_p = priv->image_width;
351 /* Set the natural width so as to preserve the aspect ratio */
352 gfloat ratio = (gfloat) priv->image_width
353 / (gfloat) priv->image_height;
355 *natural_width_p = ratio * for_height;
362 *natural_width_p = 0;
367 clutter_texture_get_preferred_height (ClutterActor *self,
369 gfloat *min_height_p,
370 gfloat *natural_height_p)
372 ClutterTexture *texture = CLUTTER_TEXTURE (self);
373 ClutterTexturePrivate *priv = texture->priv;
375 /* Min request is always 0 since we can scale down or clip */
379 if (priv->sync_actor_size)
381 if (natural_height_p)
383 if (!priv->keep_aspect_ratio ||
385 priv->image_width <= 0)
387 *natural_height_p = priv->image_height;
391 /* Set the natural height so as to preserve the aspect ratio */
392 gfloat ratio = (gfloat) priv->image_height
393 / (gfloat) priv->image_width;
395 *natural_height_p = ratio * for_width;
401 if (natural_height_p)
402 *natural_height_p = 0;
407 clutter_texture_allocate (ClutterActor *self,
408 const ClutterActorBox *box,
409 ClutterAllocationFlags flags)
411 ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv;
413 /* chain up to set actor->allocation */
414 CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->allocate (self,
418 /* If we adopted the source fbo then allocate that at its preferred
420 if (priv->fbo_source && clutter_actor_get_parent (priv->fbo_source) == self)
421 clutter_actor_allocate_preferred_size (priv->fbo_source, flags);
425 clutter_texture_has_overlaps (ClutterActor *self)
427 /* Textures never need an offscreen redirect because there are never
428 any overlapping primitives */
433 set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source,
437 ClutterActorBox box = { 0, };
438 float x_offset, y_offset;
440 if (clutter_actor_get_paint_box (fbo_source, &box))
441 clutter_actor_box_get_origin (&box, &x_offset, &y_offset);
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.
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)
451 ClutterVertex verts[4];
452 float x_min = G_MAXFLOAT, y_min = G_MAXFLOAT;
455 /* Get the actors allocation transformed into screen coordinates.
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
460 clutter_actor_get_abs_allocation_vertices (fbo_source, verts);
462 for (i = 0; i < G_N_ELEMENTS (verts); ++i)
464 if (verts[i].x < x_min)
466 if (verts[i].y < y_min)
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))
475 x_offset = ROUND (x_min);
476 y_offset = ROUND (y_min);
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);
487 update_fbo (ClutterActor *self)
489 ClutterTexture *texture = CLUTTER_TEXTURE (self);
490 ClutterTexturePrivate *priv = texture->priv;
492 ClutterShader *shader = NULL;
493 ClutterActor *stage = NULL;
494 CoglMatrix projection;
495 CoglColor transparent_col;
497 head = _clutter_context_peek_shader_stack ();
499 shader = clutter_actor_get_shader (head);
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.
505 clutter_shader_set_is_enabled (shader, FALSE);
507 /* Redirect drawing to the fbo */
508 cogl_push_framebuffer (priv->fbo_handle);
510 if ((stage = clutter_actor_get_stage (self)) != NULL)
512 gfloat stage_width, stage_height;
513 ClutterActor *source_parent;
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.
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.
523 * The viewport is offset such that the offscreen buffer will be positioned
527 _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
529 /* Set the projection matrix modelview matrix as it is for the
531 cogl_set_projection_matrix (&projection);
533 clutter_actor_get_size (stage, &stage_width, &stage_height);
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,
541 /* Apply the source's parent transformations to the modelview */
542 if ((source_parent = clutter_actor_get_parent (priv->fbo_source)))
544 CoglMatrix modelview;
545 cogl_matrix_init_identity (&modelview);
546 _clutter_actor_apply_relative_transformation_matrix (source_parent,
549 cogl_set_modelview_matrix (&modelview);
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);
560 /* Render the actor to the fbo */
561 clutter_actor_paint (priv->fbo_source);
563 /* Restore drawing to the previous framebuffer */
564 cogl_pop_framebuffer ();
566 /* If there is a shader on top of the shader stack, turn it back on. */
568 clutter_shader_set_is_enabled (shader, TRUE);
572 gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self)
574 ClutterTexture *texture = CLUTTER_TEXTURE (self);
575 ClutterTexturePrivate *priv = texture->priv;
579 clutter_actor_get_allocation_box (self, &box);
581 if (priv->repeat_x && priv->image_width > 0)
582 t_w = (box.x2 - box.x1) / (float) priv->image_width;
586 if (priv->repeat_y && priv->image_height > 0)
587 t_h = (box.y2 - box.y1) / (float) priv->image_height;
591 cogl_rectangle_with_texture_coords (0, 0,
597 static CoglPipeline *
598 create_pick_pipeline (ClutterActor *self)
600 ClutterTexture *texture = CLUTTER_TEXTURE (self);
601 ClutterTexturePrivate *priv = texture->priv;
602 CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline);
603 GError *error = NULL;
605 if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
607 " MODULATE (CONSTANT, TEXTURE[A])",
610 if (!priv->seen_create_pick_pipeline_warning)
611 g_warning ("Error setting up texture combine for shaped "
612 "texture picking: %s", error->message);
613 priv->seen_create_pick_pipeline_warning = TRUE;
614 g_error_free (error);
615 cogl_object_unref (pick_pipeline);
619 cogl_pipeline_set_blend (pick_pipeline,
620 "RGBA = ADD (SRC_COLOR[RGBA], 0)",
623 cogl_pipeline_set_alpha_test_function (pick_pipeline,
624 COGL_PIPELINE_ALPHA_FUNC_EQUAL,
627 return pick_pipeline;
631 clutter_texture_pick (ClutterActor *self,
632 const ClutterColor *color)
634 ClutterTexture *texture = CLUTTER_TEXTURE (self);
635 ClutterTexturePrivate *priv = texture->priv;
637 if (!clutter_actor_should_pick_paint (self))
640 if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
642 CoglColor pick_color;
644 if (priv->pick_pipeline == NULL)
645 priv->pick_pipeline = create_pick_pipeline (self);
647 if (priv->pick_pipeline == NULL)
649 priv->pick_with_alpha_supported = FALSE;
650 CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
655 if (priv->fbo_handle != NULL)
658 cogl_color_init_from_4ub (&pick_color,
663 cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
665 cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0,
666 clutter_texture_get_cogl_texture (texture));
667 cogl_set_source (priv->pick_pipeline);
668 gen_texcoords_and_draw_cogl_rectangle (self);
671 CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
675 clutter_texture_paint (ClutterActor *self)
677 ClutterTexture *texture = CLUTTER_TEXTURE (self);
678 ClutterTexturePrivate *priv = texture->priv;
679 guint8 paint_opacity = clutter_actor_get_paint_opacity (self);
682 "painting texture '%s'",
683 clutter_actor_get_name (self) ? clutter_actor_get_name (self)
686 if (priv->fbo_handle != NULL)
689 cogl_pipeline_set_color4ub (priv->pipeline,
694 cogl_set_source (priv->pipeline);
696 gen_texcoords_and_draw_cogl_rectangle (self);
700 clutter_texture_get_paint_volume (ClutterActor *self,
701 ClutterPaintVolume *volume)
703 ClutterTexturePrivate *priv;
705 priv = CLUTTER_TEXTURE (self)->priv;
707 if (priv->pipeline == NULL)
710 if (priv->image_width == 0 || priv->image_height == 0)
713 return _clutter_actor_set_default_paint_volume (self,
714 CLUTTER_TYPE_TEXTURE,
719 clutter_texture_async_data_free (ClutterTextureAsyncData *data)
721 /* This function should only be called either from the main thread
722 once it is known that the load thread has completed or from the
723 load thread/upload function itself if the abort flag is true (in
724 which case the main thread has disowned the data) */
725 g_free (data->load_filename);
727 if (data->load_bitmap != NULL)
728 cogl_object_unref (data->load_bitmap);
730 if (data->load_error != NULL)
731 g_error_free (data->load_error);
733 g_slice_free (ClutterTextureAsyncData, data);
737 * clutter_texture_async_load_cancel:
738 * @texture: a #ClutterTexture
740 * Cancels an asynchronous loading operation, whether done
741 * with threads enabled or just using the main loop
744 clutter_texture_async_load_cancel (ClutterTexture *texture)
746 ClutterTexturePrivate *priv = texture->priv;
748 if (priv->async_data != NULL)
750 ClutterTextureAsyncData *async_data = priv->async_data;
752 priv->async_data = NULL;
754 if (async_data->load_idle != 0)
756 g_source_remove (async_data->load_idle);
757 async_data->load_idle = 0;
759 clutter_texture_async_data_free (async_data);
763 clutter_texture_async_data_lock (async_data);
765 CLUTTER_NOTE (TEXTURE, "[async] cancelling operation for '%s'",
766 async_data->load_filename);
768 async_data->state |= ASYNC_STATE_CANCELLED;
770 clutter_texture_async_data_unlock (async_data);
776 clutter_texture_dispose (GObject *object)
778 ClutterTexture *texture = CLUTTER_TEXTURE (object);
779 ClutterTexturePrivate *priv = texture->priv;
781 texture_free_gl_resources (texture);
782 texture_fbo_free_resources (texture);
784 clutter_texture_async_load_cancel (texture);
786 if (priv->pipeline != NULL)
788 cogl_object_unref (priv->pipeline);
789 priv->pipeline = NULL;
792 if (priv->pick_pipeline != NULL)
794 cogl_object_unref (priv->pick_pipeline);
795 priv->pick_pipeline = NULL;
798 G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
802 clutter_texture_finalize (GObject *object)
804 ClutterTexturePrivate *priv = CLUTTER_TEXTURE (object)->priv;
806 g_free (priv->filename);
808 G_OBJECT_CLASS (clutter_texture_parent_class)->finalize (object);
812 clutter_texture_set_property (GObject *object,
817 ClutterTexture *texture;
818 ClutterTexturePrivate *priv;
820 texture = CLUTTER_TEXTURE (object);
821 priv = texture->priv;
826 clutter_texture_set_sync_size (texture, g_value_get_boolean (value));
830 clutter_texture_set_repeat (texture,
831 g_value_get_boolean (value),
836 clutter_texture_set_repeat (texture,
838 g_value_get_boolean (value));
841 case PROP_FILTER_QUALITY:
842 clutter_texture_set_filter_quality (texture,
843 g_value_get_enum (value));
846 case PROP_COGL_TEXTURE:
848 CoglHandle hnd = g_value_get_boxed (value);
850 clutter_texture_set_cogl_texture (texture, hnd);
854 case PROP_COGL_MATERIAL:
856 CoglHandle hnd = g_value_get_boxed (value);
858 clutter_texture_set_cogl_material (texture, hnd);
863 clutter_texture_set_from_file (texture,
864 g_value_get_string (value),
869 priv->no_slice = g_value_get_boolean (value);
872 case PROP_KEEP_ASPECT_RATIO:
873 clutter_texture_set_keep_aspect_ratio (texture,
874 g_value_get_boolean (value));
877 case PROP_LOAD_DATA_ASYNC:
878 clutter_texture_set_load_data_async (texture,
879 g_value_get_boolean (value));
882 case PROP_LOAD_ASYNC:
883 clutter_texture_set_load_async (texture, g_value_get_boolean (value));
886 case PROP_PICK_WITH_ALPHA:
887 clutter_texture_set_pick_with_alpha (texture,
888 g_value_get_boolean (value));
892 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
898 clutter_texture_get_property (GObject *object,
903 ClutterTexture *texture;
904 ClutterTexturePrivate *priv;
906 texture = CLUTTER_TEXTURE(object);
907 priv = texture->priv;
911 case PROP_PIXEL_FORMAT:
912 g_value_set_enum (value, clutter_texture_get_pixel_format (texture));
915 case PROP_MAX_TILE_WASTE:
916 g_value_set_int (value, clutter_texture_get_max_tile_waste (texture));
920 g_value_set_boolean (value, priv->sync_actor_size);
924 g_value_set_boolean (value, priv->repeat_x);
928 g_value_set_boolean (value, priv->repeat_y);
931 case PROP_FILTER_QUALITY:
932 g_value_set_enum (value, clutter_texture_get_filter_quality (texture));
935 case PROP_COGL_TEXTURE:
936 g_value_set_boxed (value, clutter_texture_get_cogl_texture (texture));
939 case PROP_COGL_MATERIAL:
940 g_value_set_boxed (value, clutter_texture_get_cogl_material (texture));
944 g_value_set_boolean (value, priv->no_slice);
947 case PROP_KEEP_ASPECT_RATIO:
948 g_value_set_boolean (value, priv->keep_aspect_ratio);
951 case PROP_PICK_WITH_ALPHA:
952 g_value_set_boolean (value, priv->pick_with_alpha);
956 g_value_set_string (value, priv->filename);
960 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
966 clutter_texture_class_init (ClutterTextureClass *klass)
968 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
969 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
972 g_type_class_add_private (klass, sizeof (ClutterTexturePrivate));
974 actor_class->paint = clutter_texture_paint;
975 actor_class->pick = clutter_texture_pick;
976 actor_class->get_paint_volume = clutter_texture_get_paint_volume;
977 actor_class->realize = clutter_texture_realize;
978 actor_class->unrealize = clutter_texture_unrealize;
979 actor_class->has_overlaps = clutter_texture_has_overlaps;
981 actor_class->get_preferred_width = clutter_texture_get_preferred_width;
982 actor_class->get_preferred_height = clutter_texture_get_preferred_height;
983 actor_class->allocate = clutter_texture_allocate;
985 gobject_class->dispose = clutter_texture_dispose;
986 gobject_class->finalize = clutter_texture_finalize;
987 gobject_class->set_property = clutter_texture_set_property;
988 gobject_class->get_property = clutter_texture_get_property;
990 pspec = g_param_spec_boolean ("sync-size",
991 P_("Sync size of actor"),
992 P_("Auto sync size of actor to underlying pixbuf dimensions"),
994 CLUTTER_PARAM_READWRITE);
995 obj_props[PROP_SYNC_SIZE] = pspec;
996 g_object_class_install_property (gobject_class, PROP_SYNC_SIZE, pspec);
998 pspec = g_param_spec_boolean ("disable-slicing",
999 P_("Disable Slicing"),
1000 P_("Forces the underlying texture to be singular and not made of smaller space saving "
1001 "individual textures"),
1003 G_PARAM_CONSTRUCT_ONLY |
1004 CLUTTER_PARAM_READWRITE);
1005 obj_props[PROP_NO_SLICE] = pspec;
1006 g_object_class_install_property (gobject_class, PROP_NO_SLICE, pspec);
1008 pspec = g_param_spec_int ("tile-waste",
1010 P_("Maximum waste area of a sliced texture"),
1012 COGL_TEXTURE_MAX_WASTE,
1013 CLUTTER_PARAM_READABLE);
1014 obj_props[PROP_MAX_TILE_WASTE] = pspec;
1015 g_object_class_install_property (gobject_class, PROP_MAX_TILE_WASTE, pspec);
1017 pspec = g_param_spec_boolean ("repeat-x",
1018 P_("Horizontal repeat"),
1019 P_("Repeat the contents rather than scaling them horizontally"),
1021 CLUTTER_PARAM_READWRITE);
1022 obj_props[PROP_REPEAT_X] = pspec;
1023 g_object_class_install_property (gobject_class, PROP_REPEAT_X, pspec);
1025 pspec = g_param_spec_boolean ("repeat-y",
1026 P_("Vertical repeat"),
1027 P_("Repeat the contents rather than scaling them vertically"),
1029 CLUTTER_PARAM_READWRITE);
1030 obj_props[PROP_REPEAT_Y] = pspec;
1031 g_object_class_install_property (gobject_class, PROP_REPEAT_Y, pspec);
1033 pspec = g_param_spec_enum ("filter-quality",
1034 P_("Filter Quality"),
1035 P_("Rendering quality used when drawing the texture"),
1036 CLUTTER_TYPE_TEXTURE_QUALITY,
1037 CLUTTER_TEXTURE_QUALITY_MEDIUM,
1038 G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
1039 obj_props[PROP_FILTER_QUALITY] = pspec;
1040 g_object_class_install_property (gobject_class, PROP_FILTER_QUALITY, pspec);
1042 pspec = g_param_spec_enum ("pixel-format",
1044 P_("The Cogl pixel format to use"),
1045 COGL_TYPE_PIXEL_FORMAT,
1046 COGL_PIXEL_FORMAT_RGBA_8888,
1047 CLUTTER_PARAM_READABLE);
1048 obj_props[PROP_PIXEL_FORMAT] = pspec;
1049 g_object_class_install_property (gobject_class, PROP_PIXEL_FORMAT, pspec);
1051 pspec = g_param_spec_boxed ("cogl-texture",
1053 P_("The underlying Cogl texture handle used to draw this actor"),
1055 CLUTTER_PARAM_READWRITE);
1056 obj_props[PROP_COGL_TEXTURE] = pspec;
1057 g_object_class_install_property (gobject_class, PROP_COGL_TEXTURE, pspec);
1059 pspec = g_param_spec_boxed ("cogl-material",
1060 P_("Cogl Material"),
1061 P_("The underlying Cogl material handle used to draw this actor"),
1063 CLUTTER_PARAM_READWRITE);
1064 obj_props[PROP_COGL_MATERIAL] = pspec;
1065 g_object_class_install_property (gobject_class, PROP_COGL_MATERIAL, pspec);
1068 * ClutterTexture:filename:
1070 * The path of the file containing the image data to be displayed by
1073 * This property is unset when using the clutter_texture_set_from_*_data()
1074 * family of functions.
1076 pspec = g_param_spec_string ("filename",
1078 P_("The path of the file containing the image data"),
1080 CLUTTER_PARAM_READWRITE);
1081 obj_props[PROP_FILENAME] = pspec;
1082 g_object_class_install_property (gobject_class, PROP_FILENAME, pspec);
1084 pspec = g_param_spec_boolean ("keep-aspect-ratio",
1085 P_("Keep Aspect Ratio"),
1086 P_("Keep the aspect ratio of the texture when requesting the preferred width or height"),
1088 CLUTTER_PARAM_READWRITE);
1089 obj_props[PROP_KEEP_ASPECT_RATIO] = pspec;
1090 g_object_class_install_property (gobject_class, PROP_KEEP_ASPECT_RATIO, pspec);
1093 * ClutterTexture:load-async:
1095 * Tries to load a texture from a filename by using a local thread to perform
1096 * the read operations. The initially created texture has dimensions 0x0 when
1097 * the true size becomes available the #ClutterTexture::size-change signal is
1098 * emitted and when the image has completed loading the
1099 * #ClutterTexture::load-finished signal is emitted.
1101 * Threading is only enabled if g_thread_init() has been called prior to
1102 * clutter_init(), otherwise #ClutterTexture will use the main loop to load
1105 * The upload of the texture data on the GL pipeline is not asynchronous, as
1106 * it must be performed from within the same thread that called
1111 pspec = g_param_spec_boolean ("load-async",
1112 P_("Load asynchronously"),
1113 P_("Load files inside a thread to avoid blocking when loading images from disk"),
1115 CLUTTER_PARAM_WRITABLE);
1116 obj_props[PROP_LOAD_ASYNC] = pspec;
1117 g_object_class_install_property (gobject_class, PROP_LOAD_ASYNC, pspec);
1121 * ClutterTexture:load-data-async:
1123 * Like #ClutterTexture:load-async but loads the width and height
1124 * synchronously causing some blocking.
1128 pspec = g_param_spec_boolean ("load-data-async",
1129 P_("Load data asynchronously"),
1130 P_("Decode image data files inside a thread to reduce blocking when loading images from disk"),
1132 CLUTTER_PARAM_WRITABLE);
1133 obj_props[PROP_LOAD_DATA_ASYNC] = pspec;
1134 g_object_class_install_property (gobject_class, PROP_LOAD_DATA_ASYNC, pspec);
1137 * ClutterTexture::pick-with-alpha:
1139 * Determines whether a #ClutterTexture should have it's shape defined
1140 * by its alpha channel when picking.
1142 * Be aware that this is a bit more costly than the default picking
1143 * due to the texture lookup, extra test against the alpha value and
1144 * the fact that it will also interrupt the batching of geometry
1147 * Also there is currently no control over the threshold used to
1148 * determine what value of alpha is considered pickable, and so
1149 * only fully opaque parts of the texture will react to picking.
1153 pspec = g_param_spec_boolean ("pick-with-alpha",
1154 P_("Pick With Alpha"),
1155 P_("Shape actor with alpha channel when picking"),
1157 CLUTTER_PARAM_READWRITE);
1158 obj_props[PROP_PICK_WITH_ALPHA] = pspec;
1159 g_object_class_install_property (gobject_class, PROP_PICK_WITH_ALPHA, pspec);
1162 * ClutterTexture::size-change:
1163 * @texture: the texture which received the signal
1164 * @width: the width of the new texture
1165 * @height: the height of the new texture
1167 * The ::size-change signal is emitted each time the size of the
1168 * pixbuf used by @texture changes. The new size is given as
1169 * argument to the callback.
1171 texture_signals[SIZE_CHANGE] =
1172 g_signal_new ("size-change",
1173 G_TYPE_FROM_CLASS (gobject_class),
1175 G_STRUCT_OFFSET (ClutterTextureClass, size_change),
1177 _clutter_marshal_VOID__INT_INT,
1182 * ClutterTexture::pixbuf-change:
1183 * @texture: the texture which received the signal
1185 * The ::pixbuf-change signal is emitted each time the pixbuf
1186 * used by @texture changes.
1188 texture_signals[PIXBUF_CHANGE] =
1189 g_signal_new ("pixbuf-change",
1190 G_TYPE_FROM_CLASS (gobject_class),
1192 G_STRUCT_OFFSET (ClutterTextureClass, pixbuf_change),
1194 _clutter_marshal_VOID__VOID,
1198 * ClutterTexture::load-finished:
1199 * @texture: the texture which received the signal
1200 * @error: A set error, or %NULL
1202 * The ::load-finished signal is emitted when a texture load has
1203 * completed. If there was an error during loading, @error will
1204 * be set, otherwise it will be %NULL
1208 texture_signals[LOAD_FINISHED] =
1209 g_signal_new (I_("load-finished"),
1210 G_TYPE_FROM_CLASS (gobject_class),
1212 G_STRUCT_OFFSET (ClutterTextureClass, load_finished),
1214 _clutter_marshal_VOID__BOXED,
1220 static ClutterScriptableIface *parent_scriptable_iface = NULL;
1223 clutter_texture_set_custom_property (ClutterScriptable *scriptable,
1224 ClutterScript *script,
1226 const GValue *value)
1228 ClutterTexture *texture = CLUTTER_TEXTURE (scriptable);
1230 if (strcmp ("filename", name) == 0)
1232 const gchar *str = g_value_get_string (value);
1236 path = clutter_script_lookup_filename (script, str);
1237 if (G_UNLIKELY (!path))
1239 g_warning ("Unable to find image %s", str);
1244 clutter_texture_set_from_file (texture, path, &error);
1247 g_warning ("Unable to open image path at '%s': %s",
1250 g_error_free (error);
1258 if (parent_scriptable_iface->set_custom_property)
1259 parent_scriptable_iface->set_custom_property (scriptable, script,
1266 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
1268 parent_scriptable_iface = g_type_interface_peek_parent (iface);
1270 if (!parent_scriptable_iface)
1271 parent_scriptable_iface = g_type_default_interface_peek
1272 (CLUTTER_TYPE_SCRIPTABLE);
1274 iface->set_custom_property = clutter_texture_set_custom_property;
1278 clutter_texture_init (ClutterTexture *self)
1280 ClutterTexturePrivate *priv;
1282 self->priv = priv = CLUTTER_TEXTURE_GET_PRIVATE (self);
1284 priv->repeat_x = FALSE;
1285 priv->repeat_y = FALSE;
1286 priv->sync_actor_size = TRUE;
1287 priv->fbo_handle = NULL;
1288 priv->pick_pipeline = NULL;
1289 priv->keep_aspect_ratio = FALSE;
1290 priv->pick_with_alpha = FALSE;
1291 priv->pick_with_alpha_supported = TRUE;
1292 priv->seen_create_pick_pipeline_warning = FALSE;
1294 if (G_UNLIKELY (texture_template_pipeline == NULL))
1296 CoglPipeline *pipeline;
1298 clutter_backend_get_cogl_context (clutter_get_default_backend ());
1300 texture_template_pipeline = cogl_pipeline_new (ctx);
1301 pipeline = COGL_PIPELINE (texture_template_pipeline);
1302 cogl_pipeline_set_layer_null_texture (pipeline,
1303 0, /* layer_index */
1304 COGL_TEXTURE_TYPE_2D);
1307 g_assert (texture_template_pipeline != NULL);
1308 priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1312 * clutter_texture_get_cogl_material:
1313 * @texture: A #ClutterTexture
1315 * Returns a handle to the underlying COGL material used for drawing
1318 * Return value: (transfer none): a handle for a #CoglMaterial. The
1319 * material is owned by the #ClutterTexture and it should not be
1325 clutter_texture_get_cogl_material (ClutterTexture *texture)
1327 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1329 return texture->priv->pipeline;
1333 * clutter_texture_set_cogl_material:
1334 * @texture: A #ClutterTexture
1335 * @cogl_material: A CoglHandle for a material
1337 * Replaces the underlying Cogl material drawn by this actor with
1338 * @cogl_material. A reference to the material is taken so if the
1339 * handle is no longer needed it should be deref'd with
1340 * cogl_handle_unref. Texture data is attached to the material so
1341 * calling this function also replaces the Cogl
1342 * texture. #ClutterTexture requires that the material have a texture
1343 * layer so you should set one on the material before calling this
1350 clutter_texture_set_cogl_material (ClutterTexture *texture,
1351 CoglHandle cogl_material)
1353 CoglPipeline *cogl_pipeline = cogl_material;
1354 CoglHandle cogl_texture;
1356 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1358 cogl_object_ref (cogl_pipeline);
1360 if (texture->priv->pipeline)
1361 cogl_object_unref (texture->priv->pipeline);
1363 texture->priv->pipeline = cogl_pipeline;
1365 /* XXX: We are re-asserting the first layer of the new pipeline to ensure the
1366 * priv state is in sync with the contents of the pipeline. */
1367 cogl_texture = clutter_texture_get_cogl_texture (texture);
1368 clutter_texture_set_cogl_texture (texture, cogl_texture);
1369 /* XXX: If we add support for more pipeline layers, this will need
1373 typedef struct _GetLayerState
1380 layer_cb (CoglPipeline *pipeline, int layer, void *user_data)
1382 GetLayerState *state = user_data;
1384 state->has_layer = TRUE;
1385 state->first_layer = layer;
1387 /* We only care about the first layer. */
1392 get_first_layer_index (CoglPipeline *pipeline, int *layer_index)
1394 GetLayerState state = { FALSE };
1395 cogl_pipeline_foreach_layer (pipeline,
1398 if (state.has_layer)
1399 *layer_index = state.first_layer;
1401 return state.has_layer;
1405 * clutter_texture_get_cogl_texture:
1406 * @texture: A #ClutterTexture
1408 * Retrieves the handle to the underlying COGL texture used for drawing
1409 * the actor. No extra reference is taken so if you need to keep the
1410 * handle then you should call cogl_handle_ref() on it.
1412 * The texture handle returned is the first layer of the material
1413 * handle used by the #ClutterTexture. If you need to access the other
1414 * layers you should use clutter_texture_get_cogl_material() instead
1415 * and use the #CoglMaterial API.
1417 * Return value: (transfer none): a #CoglHandle for the texture. The returned
1418 * handle is owned by the #ClutterTexture and it should not be unreferenced
1423 clutter_texture_get_cogl_texture (ClutterTexture *texture)
1425 ClutterTexturePrivate *priv;
1428 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1430 priv = texture->priv;
1432 if (get_first_layer_index (priv->pipeline, &layer_index))
1433 return cogl_pipeline_get_layer_texture (priv->pipeline, layer_index);
1439 * clutter_texture_set_cogl_texture:
1440 * @texture: A #ClutterTexture
1441 * @cogl_tex: A CoglHandle for a texture
1443 * Replaces the underlying COGL texture drawn by this actor with
1444 * @cogl_tex. A reference to the texture is taken so if the handle is
1445 * no longer needed it should be deref'd with cogl_handle_unref.
1450 clutter_texture_set_cogl_texture (ClutterTexture *texture,
1451 CoglHandle cogl_tex)
1453 ClutterTexturePrivate *priv;
1454 gboolean size_changed;
1455 guint width, height;
1457 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1458 g_return_if_fail (cogl_is_texture (cogl_tex));
1460 /* This function can set the texture without the actor being
1461 realized. This is ok because Clutter requires that the GL context
1462 always be current so there is no point in waiting to realization
1463 to set the texture. */
1465 priv = texture->priv;
1467 width = cogl_texture_get_width (cogl_tex);
1468 height = cogl_texture_get_height (cogl_tex);
1470 /* Reference the new texture now in case it is the same one we are
1472 cogl_object_ref (cogl_tex);
1474 /* Remove FBO if exisiting */
1475 if (priv->fbo_source)
1476 texture_fbo_free_resources (texture);
1478 /* Remove old texture */
1479 texture_free_gl_resources (texture);
1481 /* Use the new texture */
1482 if (priv->pipeline == NULL)
1483 priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1485 g_assert (priv->pipeline != NULL);
1486 cogl_pipeline_set_layer_texture (priv->pipeline, 0, cogl_tex);
1488 /* The pipeline now holds a reference to the texture so we can
1489 safely release the reference we claimed above */
1490 cogl_object_unref (cogl_tex);
1492 size_changed = (width != priv->image_width || height != priv->image_height);
1493 priv->image_width = width;
1494 priv->image_height = height;
1496 CLUTTER_NOTE (TEXTURE, "set size (w:%d, h:%d)",
1498 priv->image_height);
1502 g_signal_emit (texture, texture_signals[SIZE_CHANGE], 0,
1504 priv->image_height);
1506 if (priv->sync_actor_size)
1508 ClutterActor *actor = CLUTTER_ACTOR (texture);
1510 /* we have been requested to keep the actor size in
1511 * sync with the texture data; if we also want to
1512 * maintain the aspect ratio we want to change the
1513 * requisition mode depending on the orientation of
1514 * the texture, so that the parent container can do
1517 if (priv->keep_aspect_ratio)
1519 ClutterRequestMode request;
1521 if (priv->image_width >= priv->image_height)
1522 request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
1524 request = CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
1526 clutter_actor_set_request_mode (actor, request);
1529 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
1534 g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
1536 /* If resized actor may need resizing but paint() will do this */
1537 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
1539 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_COGL_TEXTURE]);
1543 clutter_texture_set_from_data (ClutterTexture *texture,
1545 CoglPixelFormat source_format,
1552 ClutterTexturePrivate *priv = texture->priv;
1553 CoglHandle new_texture = NULL;
1554 CoglTextureFlags flags = COGL_TEXTURE_NONE;
1557 flags |= COGL_TEXTURE_NO_SLICING;
1559 /* FIXME if we are not realized, we should store the data
1560 * for future use, instead of creating the texture.
1562 new_texture = cogl_texture_new_from_data (width, height,
1565 COGL_PIXEL_FORMAT_ANY,
1569 if (G_UNLIKELY (new_texture == NULL))
1571 GError *inner_error = NULL;
1573 g_set_error (&inner_error, CLUTTER_TEXTURE_ERROR,
1574 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1575 _("Failed to load the image data"));
1577 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, inner_error);
1580 g_propagate_error (error, inner_error);
1582 g_error_free (inner_error);
1587 g_free (priv->filename);
1588 priv->filename = NULL;
1590 clutter_texture_set_cogl_texture (texture, new_texture);
1592 cogl_object_unref (new_texture);
1594 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
1599 static inline gboolean
1600 get_pixel_format_from_texture_flags (gint bpp,
1602 ClutterTextureFlags flags,
1603 CoglPixelFormat *source_format)
1605 /* Convert the flags to a CoglPixelFormat */
1608 if (G_UNLIKELY (bpp != 4))
1610 g_warning ("Unsupported bytes per pixel value '%d': "
1611 "Clutter supports only a value of 4 "
1617 *source_format = COGL_PIXEL_FORMAT_RGBA_8888;
1621 if (G_UNLIKELY (bpp != 3))
1623 g_warning ("Unsupported bytes per pixel value '%d': "
1624 "Clutter supports only a BPP value of 3 "
1630 *source_format = COGL_PIXEL_FORMAT_RGB_888;
1633 if ((flags & CLUTTER_TEXTURE_RGB_FLAG_BGR))
1634 *source_format |= COGL_BGR_BIT;
1636 if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT))
1637 *source_format |= COGL_PREMULT_BIT;
1643 * clutter_texture_set_from_rgb_data:
1644 * @texture: a #ClutterTexture
1645 * @data: (array): image data in RGBA type colorspace.
1646 * @has_alpha: set to %TRUE if image data has an alpha channel.
1647 * @width: width in pixels of image data.
1648 * @height: height in pixels of image data
1649 * @rowstride: distance in bytes between row starts.
1650 * @bpp: bytes per pixel (currently only 3 and 4 supported, depending
1651 * on the value of @has_alpha)
1652 * @flags: #ClutterTextureFlags
1653 * @error: return location for a #GError, or %NULL.
1655 * Sets #ClutterTexture image data.
1657 * Return value: %TRUE on success, %FALSE on failure.
1662 clutter_texture_set_from_rgb_data (ClutterTexture *texture,
1669 ClutterTextureFlags flags,
1672 CoglPixelFormat source_format;
1674 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1676 if (!get_pixel_format_from_texture_flags (bpp,
1684 return clutter_texture_set_from_data (texture, data,
1692 * clutter_texture_set_from_yuv_data:
1693 * @texture: A #ClutterTexture
1694 * @data: (array): Image data in YUV type colorspace.
1695 * @width: Width in pixels of image data.
1696 * @height: Height in pixels of image data
1697 * @flags: #ClutterTextureFlags
1698 * @error: Return location for a #GError, or %NULL.
1700 * Sets a #ClutterTexture from YUV image data. If an error occurred,
1701 * %FALSE is returned and @error is set.
1703 * The YUV support depends on the driver; the format supported by the
1704 * few drivers exposing this capability are not really useful.
1706 * The proper way to convert image data in any YUV colorspace to any
1707 * RGB colorspace is to use a fragment shader associated with the
1708 * #ClutterTexture material.
1710 * Return value: %TRUE if the texture was successfully updated
1714 * Deprecated: 1.10: Use clutter_texture_get_cogl_material() and
1715 * the Cogl API to install a fragment shader for decoding YUV
1716 * formats on the GPU
1719 clutter_texture_set_from_yuv_data (ClutterTexture *texture,
1723 ClutterTextureFlags flags,
1726 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1728 if (!clutter_feature_available (CLUTTER_FEATURE_TEXTURE_YUV))
1730 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1731 CLUTTER_TEXTURE_ERROR_NO_YUV,
1732 _("YUV textures are not supported"));
1736 /* Convert the flags to a CoglPixelFormat */
1737 if ((flags & CLUTTER_TEXTURE_YUV_FLAG_YUV2))
1739 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1740 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1741 _("YUV2 textues are not supported"));
1745 return clutter_texture_set_from_data (texture, data,
1746 COGL_PIXEL_FORMAT_YUV,
1753 * clutter_texture_async_load_complete:
1754 * @self: a #ClutterTexture
1755 * @bitmap: a handle to a CoglBitmap
1756 * @error: load error
1758 * If @error is %NULL, loads @bitmap into a #CoglTexture.
1760 * This function emits the ::load-finished signal on @self.
1763 clutter_texture_async_load_complete (ClutterTexture *self,
1765 const GError *error)
1767 ClutterTexturePrivate *priv = self->priv;
1768 CoglTextureFlags flags = COGL_TEXTURE_NONE;
1771 priv->async_data = NULL;
1776 flags |= COGL_TEXTURE_NO_SLICING;
1778 handle = cogl_texture_new_from_bitmap (bitmap,
1780 COGL_PIXEL_FORMAT_ANY);
1781 clutter_texture_set_cogl_texture (self, handle);
1783 if (priv->load_size_async)
1785 g_signal_emit (self, texture_signals[SIZE_CHANGE], 0,
1786 cogl_texture_get_width (handle),
1787 cogl_texture_get_height (handle));
1790 cogl_object_unref (handle);
1793 g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, error);
1795 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
1799 texture_repaint_upload_func (gpointer user_data)
1801 g_mutex_lock (&upload_list_mutex);
1803 if (upload_list != NULL)
1805 gint64 start_time = g_get_monotonic_time ();
1807 /* continue uploading textures as long as we havent spent more
1808 * then 5ms doing so this stage redraw cycle.
1812 ClutterTextureAsyncData *async_data = upload_list->data;
1814 clutter_texture_async_data_lock (async_data);
1816 if (async_data->state & ASYNC_STATE_QUEUED)
1818 CLUTTER_NOTE (TEXTURE, "[async] operation complete for '%s'",
1819 async_data->load_filename);
1821 clutter_texture_async_load_complete (async_data->texture,
1822 async_data->load_bitmap,
1823 async_data->load_error);
1826 CLUTTER_NOTE (TEXTURE, "[async] operation cancelled for '%s'",
1827 async_data->load_filename);
1829 clutter_texture_async_data_unlock (async_data);
1831 upload_list = g_list_remove (upload_list, async_data);
1832 clutter_texture_async_data_free (async_data);
1834 while (upload_list != NULL &&
1835 g_get_monotonic_time () < start_time + 5 * 1000L);
1838 if (upload_list != NULL)
1840 ClutterMasterClock *master_clock;
1842 master_clock = _clutter_master_clock_get_default ();
1843 _clutter_master_clock_ensure_next_iteration (master_clock);
1846 g_mutex_unlock (&upload_list_mutex);
1852 clutter_texture_thread_load (gpointer user_data,
1855 ClutterTextureAsyncData *async_data = user_data;
1856 ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
1858 clutter_texture_async_data_lock (async_data);
1860 if (~async_data->state & ASYNC_STATE_CANCELLED)
1862 CLUTTER_NOTE (TEXTURE, "[async] loading bitmap from file '%s'",
1863 async_data->load_filename);
1865 async_data->load_bitmap =
1866 cogl_bitmap_new_from_file (async_data->load_filename,
1867 &async_data->load_error);
1869 g_mutex_lock (&upload_list_mutex);
1871 if (repaint_upload_func == 0)
1873 repaint_upload_func =
1874 clutter_threads_add_repaint_func (texture_repaint_upload_func,
1878 upload_list = g_list_append (upload_list, async_data);
1879 async_data->state |= ASYNC_STATE_QUEUED;
1881 CLUTTER_NOTE (TEXTURE, "[async] operation queued");
1883 g_mutex_unlock (&upload_list_mutex);
1887 clutter_texture_async_data_unlock (async_data);
1888 clutter_texture_async_data_free (async_data);
1893 clutter_texture_async_data_unlock (async_data);
1895 _clutter_master_clock_ensure_next_iteration (master_clock);
1899 clutter_texture_idle_load (gpointer data)
1901 ClutterTextureAsyncData *async_data = data;
1903 async_data->load_bitmap =
1904 cogl_bitmap_new_from_file (async_data->load_filename,
1905 &async_data->load_error);
1907 clutter_texture_async_load_complete (async_data->texture,
1908 async_data->load_bitmap,
1909 async_data->load_error);
1911 clutter_texture_async_data_free (async_data);
1917 * clutter_texture_async_load:
1918 * @self: a #ClutterTExture
1919 * @filename: name of the file to load
1920 * @error: return location for a #GError
1922 * Starts an asynchronous load of the file name stored inside
1923 * the load_filename member of @data.
1925 * If threading is enabled we use a GThread to perform the actual
1926 * I/O; if threading is not enabled, we use an idle GSource.
1928 * The I/O is the only bit done in a thread -- uploading the
1929 * texture data to the GL pipeline must be done from within the
1930 * same thread that called clutter_main(). Threaded upload should
1931 * be part of the GL implementation.
1933 * This function will block until we get a size from the file
1934 * so that we can effectively get the size the texture actor after
1935 * clutter_texture_set_from_file().
1937 * Return value: %TRUE if the asynchronous loading was successfully
1938 * initiated, %FALSE otherwise
1941 clutter_texture_async_load (ClutterTexture *self,
1942 const gchar *filename,
1945 ClutterTexturePrivate *priv = self->priv;
1946 ClutterTextureAsyncData *data;
1950 /* ask the file for a size; if we cannot get the size then
1951 * there's no point in even continuing the asynchronous
1952 * loading, so we just stop there
1955 if (priv->load_size_async)
1962 res = cogl_bitmap_get_size_from_file (filename, &width, &height);
1966 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1967 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1968 _("Failed to load the image data"));
1973 priv->image_width = width;
1974 priv->image_height = height;
1977 clutter_texture_async_load_cancel (self);
1979 data = g_slice_new0 (ClutterTextureAsyncData);
1981 data->texture = self;
1982 data->load_filename = g_strdup (filename);
1984 priv->async_data = data;
1988 if (G_UNLIKELY (async_thread_pool == NULL))
1990 /* This apparently can't fail if exclusive == FALSE */
1992 g_thread_pool_new (clutter_texture_thread_load, NULL,
1998 g_thread_pool_push (async_thread_pool, data, NULL);
2003 clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
2004 clutter_texture_idle_load,
2013 * clutter_texture_set_from_file:
2014 * @texture: A #ClutterTexture
2015 * @filename: The filename of the image in GLib file name encoding
2016 * @error: Return location for a #GError, or %NULL
2018 * Sets the #ClutterTexture image data from an image file. In case of
2019 * failure, %FALSE is returned and @error is set.
2021 * If #ClutterTexture:load-async is set to %TRUE, this function
2022 * will return as soon as possible, and the actual image loading
2023 * from disk will be performed asynchronously. #ClutterTexture::size-change
2024 * will be emitten when the size of the texture is available and
2025 * #ClutterTexture::load-finished will be emitted when the image has been
2026 * loaded or if an error occurred.
2028 * Return value: %TRUE if the image was successfully loaded and set
2033 clutter_texture_set_from_file (ClutterTexture *texture,
2034 const gchar *filename,
2037 ClutterTexturePrivate *priv;
2038 CoglHandle new_texture = NULL;
2039 GError *internal_error = NULL;
2040 CoglTextureFlags flags = COGL_TEXTURE_NONE;
2042 priv = texture->priv;
2044 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2046 if (priv->load_data_async)
2047 return clutter_texture_async_load (texture, filename, error);
2050 flags |= COGL_TEXTURE_NO_SLICING;
2052 new_texture = cogl_texture_new_from_file (filename,
2054 COGL_PIXEL_FORMAT_ANY,
2057 /* If COGL didn't give an error then make one up */
2058 if (internal_error == NULL && new_texture == NULL)
2060 g_set_error (&internal_error, CLUTTER_TEXTURE_ERROR,
2061 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2062 _("Failed to load the image data"));
2065 if (internal_error != NULL)
2067 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0,
2070 g_propagate_error (error, internal_error);
2075 g_free (priv->filename);
2076 priv->filename = g_strdup (filename);
2078 clutter_texture_set_cogl_texture (texture, new_texture);
2080 cogl_object_unref (new_texture);
2082 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
2084 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILENAME]);
2090 * clutter_texture_set_filter_quality:
2091 * @texture: a #ClutterTexture
2092 * @filter_quality: new filter quality value
2094 * Sets the filter quality when scaling a texture. The quality is an
2095 * enumeration currently the following values are supported:
2096 * %CLUTTER_TEXTURE_QUALITY_LOW which is fast but only uses nearest neighbour
2097 * interpolation. %CLUTTER_TEXTURE_QUALITY_MEDIUM which is computationally a
2098 * bit more expensive (bilinear interpolation), and
2099 * %CLUTTER_TEXTURE_QUALITY_HIGH which uses extra texture memory resources to
2100 * improve scaled down rendering as well (by using mipmaps). The default value
2101 * is %CLUTTER_TEXTURE_QUALITY_MEDIUM.
2106 clutter_texture_set_filter_quality (ClutterTexture *texture,
2107 ClutterTextureQuality filter_quality)
2109 ClutterTexturePrivate *priv;
2110 ClutterTextureQuality old_quality;
2112 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2114 priv = texture->priv;
2116 old_quality = clutter_texture_get_filter_quality (texture);
2118 if (filter_quality != old_quality)
2120 gint min_filter, mag_filter;
2122 min_filter = mag_filter = COGL_PIPELINE_FILTER_LINEAR;
2123 clutter_texture_quality_to_filters (filter_quality,
2127 cogl_pipeline_set_layer_filters (priv->pipeline, 0,
2128 min_filter, mag_filter);
2130 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2132 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILTER_QUALITY]);
2137 * clutter_texture_get_filter_quality:
2138 * @texture: A #ClutterTexture
2140 * Gets the filter quality used when scaling a texture.
2142 * Return value: The filter quality value.
2146 ClutterTextureQuality
2147 clutter_texture_get_filter_quality (ClutterTexture *texture)
2149 ClutterTexturePrivate *priv;
2151 CoglPipelineFilter min_filter, mag_filter;
2154 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2156 priv = texture->priv;
2158 if (get_first_layer_index (priv->pipeline, &layer_index))
2160 min_filter = cogl_pipeline_get_layer_min_filter (priv->pipeline,
2162 mag_filter = cogl_pipeline_get_layer_mag_filter (priv->pipeline,
2166 return CLUTTER_TEXTURE_QUALITY_MEDIUM;
2168 for (i = 0; i < G_N_ELEMENTS (clutter_texture_quality_filters); i++)
2169 if (clutter_texture_quality_filters[i].min_filter == min_filter
2170 && clutter_texture_quality_filters[i].mag_filter == mag_filter)
2173 /* Unknown filter combination */
2174 return CLUTTER_TEXTURE_QUALITY_LOW;
2178 * clutter_texture_get_max_tile_waste:
2179 * @texture: A #ClutterTexture
2181 * Gets the maximum waste that will be used when creating a texture or
2182 * -1 if slicing is disabled.
2184 * Return value: The maximum waste or -1 if the texture waste is
2190 clutter_texture_get_max_tile_waste (ClutterTexture *texture)
2192 ClutterTexturePrivate *priv;
2193 CoglHandle cogl_texture;
2195 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2197 priv = texture->priv;
2199 cogl_texture = clutter_texture_get_cogl_texture (texture);
2201 if (cogl_texture == NULL)
2202 return priv->no_slice ? -1 : COGL_TEXTURE_MAX_WASTE;
2204 return cogl_texture_get_max_waste (cogl_texture);
2208 * clutter_texture_new_from_file:
2209 * @filename: The name of an image file to load.
2210 * @error: Return locatoin for an error.
2212 * Creates a new ClutterTexture actor to display the image contained a
2213 * file. If the image failed to load then NULL is returned and @error
2216 * Return value: A newly created #ClutterTexture object or NULL on
2222 clutter_texture_new_from_file (const gchar *filename,
2225 ClutterActor *texture = clutter_texture_new ();
2227 if (!clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
2230 g_object_ref_sink (texture);
2231 g_object_unref (texture);
2240 * clutter_texture_new:
2242 * Creates a new empty #ClutterTexture object.
2244 * Return value: A newly created #ClutterTexture object.
2247 clutter_texture_new (void)
2249 return g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
2253 * clutter_texture_get_base_size:
2254 * @texture: a #ClutterTexture
2255 * @width: (out): return location for the width, or %NULL
2256 * @height: (out): return location for the height, or %NULL
2258 * Gets the size in pixels of the untransformed underlying image
2261 clutter_texture_get_base_size (ClutterTexture *texture,
2265 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2268 *width = texture->priv->image_width;
2271 *height = texture->priv->image_height;
2275 * clutter_texture_set_area_from_rgb_data:
2276 * @texture: A #ClutterTexture
2277 * @data: (array): Image data in RGB type colorspace.
2278 * @has_alpha: Set to TRUE if image data has an alpha channel.
2279 * @x: X coordinate of upper left corner of region to update.
2280 * @y: Y coordinate of upper left corner of region to update.
2281 * @width: Width in pixels of region to update.
2282 * @height: Height in pixels of region to update.
2283 * @rowstride: Distance in bytes between row starts on source buffer.
2284 * @bpp: bytes per pixel (Currently only 3 and 4 supported,
2285 * depending on @has_alpha)
2286 * @flags: #ClutterTextureFlags
2287 * @error: return location for a #GError, or %NULL
2289 * Updates a sub-region of the pixel data in a #ClutterTexture.
2291 * Return value: %TRUE on success, %FALSE on failure.
2296 clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
2305 ClutterTextureFlags flags,
2308 CoglPixelFormat source_format;
2309 CoglHandle cogl_texture;
2311 if (!get_pixel_format_from_texture_flags (bpp, has_alpha, flags,
2317 /* attempt to realize ... */
2318 if (!CLUTTER_ACTOR_IS_REALIZED (texture) &&
2319 clutter_actor_get_stage (CLUTTER_ACTOR (texture)) != NULL)
2321 clutter_actor_realize (CLUTTER_ACTOR (texture));
2324 /* due to the fudging of clutter_texture_set_cogl_texture()
2325 * which allows setting a texture pre-realize, we may end
2326 * up having a texture even if we couldn't realize yet.
2328 cogl_texture = clutter_texture_get_cogl_texture (texture);
2329 if (cogl_texture == NULL)
2331 g_warning ("Failed to realize actor '%s'",
2332 _clutter_actor_get_debug_name (CLUTTER_ACTOR (texture)));
2336 if (!cogl_texture_set_region (cogl_texture,
2338 x, y, width, height,
2344 g_set_error (error, CLUTTER_TEXTURE_ERROR,
2345 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2346 _("Failed to load the image data"));
2350 g_free (texture->priv->filename);
2351 texture->priv->filename = NULL;
2354 g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
2356 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2362 on_fbo_source_size_change (GObject *object,
2363 GParamSpec *param_spec,
2364 ClutterTexture *texture)
2366 ClutterTexturePrivate *priv = texture->priv;
2368 ClutterActorBox box;
2371 status = clutter_actor_get_paint_box (priv->fbo_source, &box);
2373 clutter_actor_box_get_size (&box, &w, &h);
2375 /* In the end we will size the framebuffer according to the paint
2376 * box, but for code that does:
2377 * tex = clutter_texture_new_from_actor (src);
2378 * clutter_actor_get_size (tex, &width, &height);
2379 * it seems more helpfull to return the src actor size if it has a
2380 * degenerate paint box. The most likely reason it will have a
2381 * degenerate paint box is simply that the src currently has no
2383 if (status == FALSE || w == 0 || h == 0)
2384 clutter_actor_get_size (priv->fbo_source, &w, &h);
2386 /* We can't create a texture with a width or height of 0... */
2390 if (w != priv->image_width || h != priv->image_height)
2392 CoglTextureFlags flags = COGL_TEXTURE_NONE;
2395 /* tear down the FBO */
2396 if (priv->fbo_handle != NULL)
2397 cogl_object_unref (priv->fbo_handle);
2399 texture_free_gl_resources (texture);
2401 priv->image_width = w;
2402 priv->image_height = h;
2404 flags |= COGL_TEXTURE_NO_SLICING;
2406 tex = cogl_texture_new_with_size (MAX (priv->image_width, 1),
2407 MAX (priv->image_height, 1),
2409 COGL_PIXEL_FORMAT_RGBA_8888_PRE);
2411 cogl_pipeline_set_layer_texture (priv->pipeline, 0, tex);
2413 priv->fbo_handle = cogl_offscreen_new_to_texture (tex);
2415 /* The pipeline now has a reference to the texture so it will
2417 cogl_object_unref (tex);
2419 if (priv->fbo_handle == NULL)
2421 g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
2425 clutter_actor_set_size (CLUTTER_ACTOR (texture), w, h);
2430 on_fbo_parent_change (ClutterActor *actor,
2431 ClutterActor *old_parent,
2432 ClutterTexture *texture)
2434 ClutterActor *parent = CLUTTER_ACTOR(texture);
2436 while ((parent = clutter_actor_get_parent (parent)) != NULL)
2438 if (parent == actor)
2440 g_warning ("Offscreen texture is ancestor of source!");
2441 /* Desperate but will avoid infinite loops */
2442 clutter_actor_remove_child (parent, actor);
2448 fbo_source_queue_redraw_cb (ClutterActor *source,
2449 ClutterActor *origin,
2450 ClutterTexture *texture)
2452 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2456 fbo_source_queue_relayout_cb (ClutterActor *source,
2457 ClutterTexture *texture)
2459 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2463 * clutter_texture_new_from_actor:
2464 * @actor: A source #ClutterActor
2466 * Creates a new #ClutterTexture object with its source a prexisting
2467 * actor (and associated children). The textures content will contain
2468 * 'live' redirected output of the actors scene.
2470 * Note this function is intented as a utility call for uniformly applying
2471 * shaders to groups and other potential visual effects. It requires that
2472 * the %CLUTTER_FEATURE_OFFSCREEN feature is supported by the current backend
2473 * and the target system.
2475 * Some tips on usage:
2479 * <para>The source actor must be made visible (i.e by calling
2480 * #clutter_actor_show).</para>
2483 * <para>The source actor must have a parent in order for it to be
2484 * allocated a size from the layouting mechanism. If the source
2485 * actor does not have a parent when this function is called then
2486 * the ClutterTexture will adopt it and allocate it at its
2487 * preferred size. Using this you can clone an actor that is
2488 * otherwise not displayed. Because of this feature if you do
2489 * intend to display the source actor then you must make sure that
2490 * the actor is parented before calling
2491 * clutter_texture_new_from_actor() or that you unparent it before
2492 * adding it to a container.</para>
2495 * <para>When getting the image for the clone texture, Clutter
2496 * will attempt to render the source actor exactly as it would
2497 * appear if it was rendered on screen. The source actor's parent
2498 * transformations are taken into account. Therefore if your
2499 * source actor is rotated along the X or Y axes so that it has
2500 * some depth, the texture will appear differently depending on
2501 * the on-screen location of the source actor. While painting the
2502 * source actor, Clutter will set up a temporary asymmetric
2503 * perspective matrix as the projection matrix so that the source
2504 * actor will be projected as if a small section of the screen was
2505 * being viewed. Before version 0.8.2, an orthogonal identity
2506 * projection was used which meant that the source actor would be
2507 * clipped if any part of it was not on the zero Z-plane.</para>
2510 * <para>Avoid reparenting the source with the created texture.</para>
2513 * <para>A group can be padded with a transparent rectangle as to
2514 * provide a border to contents for shader output (blurring text
2515 * for example).</para>
2518 * <para>The texture will automatically resize to contain a further
2519 * transformed source. However, this involves overhead and can be
2520 * avoided by placing the source actor in a bounding group
2521 * sized large enough to contain any child tranformations.</para>
2524 * <para>Uploading pixel data to the texture (e.g by using
2525 * clutter_texture_set_from_file()) will destroy the offscreen texture
2526 * data and end redirection.</para>
2529 * <para>cogl_texture_get_data() with the handle returned by
2530 * clutter_texture_get_cogl_texture() can be used to read the
2531 * offscreen texture pixels into a pixbuf.</para>
2535 * Return value: A newly created #ClutterTexture object, or %NULL on failure.
2537 * Deprecated: 1.8: Use the #ClutterOffscreenEffect and #ClutterShaderEffect
2538 * directly on the intended #ClutterActor to replace the functionality of
2544 clutter_texture_new_from_actor (ClutterActor *actor)
2546 ClutterTexture *texture;
2547 ClutterTexturePrivate *priv;
2549 ClutterActorBox box;
2552 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2554 if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE)
2557 if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2559 clutter_actor_realize (actor);
2561 if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2565 status = clutter_actor_get_paint_box (actor, &box);
2567 clutter_actor_box_get_size (&box, &w, &h);
2569 /* In the end we will size the framebuffer according to the paint
2570 * box, but for code that does:
2571 * tex = clutter_texture_new_from_actor (src);
2572 * clutter_actor_get_size (tex, &width, &height);
2573 * it seems more helpfull to return the src actor size if it has a
2574 * degenerate paint box. The most likely reason it will have a
2575 * degenerate paint box is simply that the src currently has no
2577 if (status == FALSE || w == 0 || h == 0)
2578 clutter_actor_get_size (actor, &w, &h);
2580 /* We can't create a 0x0 fbo so always bump the size up to at least
2585 /* Hopefully now were good.. */
2586 texture = g_object_new (CLUTTER_TYPE_TEXTURE,
2587 "disable-slicing", TRUE,
2590 priv = texture->priv;
2592 priv->fbo_source = g_object_ref_sink (actor);
2594 /* If the actor doesn't have a parent then claim it so that it will
2595 get a size allocation during layout */
2596 if (clutter_actor_get_parent (actor) == NULL)
2597 clutter_actor_add_child (CLUTTER_ACTOR (texture), actor);
2599 /* Connect up any signals which could change our underlying size */
2600 g_signal_connect (actor,
2602 G_CALLBACK(on_fbo_source_size_change),
2604 g_signal_connect (actor,
2606 G_CALLBACK(on_fbo_source_size_change),
2608 g_signal_connect (actor,
2610 G_CALLBACK(on_fbo_source_size_change),
2612 g_signal_connect (actor,
2614 G_CALLBACK(on_fbo_source_size_change),
2616 g_signal_connect (actor,
2617 "notify::rotation-angle-x",
2618 G_CALLBACK(on_fbo_source_size_change),
2620 g_signal_connect (actor,
2621 "notify::rotation-angle-y",
2622 G_CALLBACK(on_fbo_source_size_change),
2624 g_signal_connect (actor,
2625 "notify::rotation-angle-z",
2626 G_CALLBACK(on_fbo_source_size_change),
2629 g_signal_connect (actor, "queue-relayout",
2630 G_CALLBACK (fbo_source_queue_relayout_cb), texture);
2631 g_signal_connect (actor, "queue-redraw",
2632 G_CALLBACK (fbo_source_queue_redraw_cb), texture);
2634 /* And a warning if the source becomes a child of the texture */
2635 g_signal_connect (actor,
2637 G_CALLBACK(on_fbo_parent_change),
2640 priv->image_width = w;
2641 priv->image_height = h;
2643 clutter_actor_set_size (CLUTTER_ACTOR (texture),
2645 priv->image_height);
2647 return CLUTTER_ACTOR (texture);
2651 texture_fbo_free_resources (ClutterTexture *texture)
2653 ClutterTexturePrivate *priv;
2655 priv = texture->priv;
2657 if (priv->fbo_source != NULL)
2659 ClutterActor *parent;
2661 parent = clutter_actor_get_parent (priv->fbo_source);
2663 /* If we parented the texture then unparent it again so that it
2664 will lose the reference */
2665 if (parent == CLUTTER_ACTOR (texture))
2666 clutter_actor_remove_child (parent, priv->fbo_source);
2668 g_signal_handlers_disconnect_by_func
2670 G_CALLBACK(on_fbo_parent_change),
2673 g_signal_handlers_disconnect_by_func
2675 G_CALLBACK(on_fbo_source_size_change),
2678 g_signal_handlers_disconnect_by_func
2680 G_CALLBACK(fbo_source_queue_relayout_cb),
2683 g_signal_handlers_disconnect_by_func
2685 G_CALLBACK(fbo_source_queue_redraw_cb),
2688 g_object_unref (priv->fbo_source);
2690 priv->fbo_source = NULL;
2693 if (priv->fbo_handle != NULL)
2695 cogl_object_unref (priv->fbo_handle);
2696 priv->fbo_handle = NULL;
2701 * clutter_texture_set_sync_size:
2702 * @texture: a #ClutterTexture
2703 * @sync_size: %TRUE if the texture should have the same size of the
2704 * underlying image data
2706 * Sets whether @texture should have the same preferred size as the
2707 * underlying image data.
2712 clutter_texture_set_sync_size (ClutterTexture *texture,
2715 ClutterTexturePrivate *priv;
2717 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2719 priv = texture->priv;
2721 if (priv->sync_actor_size != sync_size)
2723 priv->sync_actor_size = sync_size;
2725 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2727 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_SYNC_SIZE]);
2732 * clutter_texture_get_sync_size:
2733 * @texture: a #ClutterTexture
2735 * Retrieves the value set with clutter_texture_set_sync_size()
2737 * Return value: %TRUE if the #ClutterTexture should have the same
2738 * preferred size of the underlying image data
2743 clutter_texture_get_sync_size (ClutterTexture *texture)
2745 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2747 return texture->priv->sync_actor_size;
2751 * clutter_texture_set_repeat:
2752 * @texture: a #ClutterTexture
2753 * @repeat_x: %TRUE if the texture should repeat horizontally
2754 * @repeat_y: %TRUE if the texture should repeat vertically
2756 * Sets whether the @texture should repeat horizontally or
2757 * vertically when the actor size is bigger than the image size
2762 clutter_texture_set_repeat (ClutterTexture *texture,
2766 ClutterTexturePrivate *priv;
2767 gboolean changed = FALSE;
2769 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2771 priv = texture->priv;
2773 g_object_freeze_notify (G_OBJECT (texture));
2775 if (priv->repeat_x != repeat_x)
2777 priv->repeat_x = repeat_x;
2779 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_X]);
2784 if (priv->repeat_y != repeat_y)
2786 priv->repeat_y = repeat_y;
2788 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_Y]);
2794 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2796 g_object_thaw_notify (G_OBJECT (texture));
2800 * clutter_texture_get_repeat:
2801 * @texture: a #ClutterTexture
2802 * @repeat_x: (out): return location for the horizontal repeat
2803 * @repeat_y: (out): return location for the vertical repeat
2805 * Retrieves the horizontal and vertical repeat values set
2806 * using clutter_texture_set_repeat()
2811 clutter_texture_get_repeat (ClutterTexture *texture,
2815 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2817 if (repeat_x != NULL)
2818 *repeat_x = texture->priv->repeat_x;
2820 if (repeat_y != NULL)
2821 *repeat_y = texture->priv->repeat_y;
2825 * clutter_texture_get_pixel_format:
2826 * @texture: a #ClutterTexture
2828 * Retrieves the pixel format used by @texture. This is
2832 * handle = clutter_texture_get_pixel_format (texture);
2834 * if (handle != COGL_INVALID_HANDLE)
2835 * format = cogl_texture_get_format (handle);
2838 * Return value: a #CoglPixelFormat value
2843 clutter_texture_get_pixel_format (ClutterTexture *texture)
2845 CoglHandle cogl_texture;
2847 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), COGL_PIXEL_FORMAT_ANY);
2849 cogl_texture = clutter_texture_get_cogl_texture (texture);
2850 if (cogl_texture == NULL)
2851 return COGL_PIXEL_FORMAT_ANY;
2853 return cogl_texture_get_format (cogl_texture);
2857 * clutter_texture_set_keep_aspect_ratio:
2858 * @texture: a #ClutterTexture
2859 * @keep_aspect: %TRUE to maintain aspect ratio
2861 * Sets whether @texture should have a preferred size maintaining
2862 * the aspect ratio of the underlying image
2867 clutter_texture_set_keep_aspect_ratio (ClutterTexture *texture,
2868 gboolean keep_aspect)
2870 ClutterTexturePrivate *priv;
2872 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2874 priv = texture->priv;
2876 if (priv->keep_aspect_ratio != keep_aspect)
2878 priv->keep_aspect_ratio = keep_aspect;
2880 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2882 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_KEEP_ASPECT_RATIO]);
2887 * clutter_texture_get_keep_aspect_ratio:
2888 * @texture: a #ClutterTexture
2890 * Retrieves the value set using clutter_texture_set_keep_aspect_ratio()
2892 * Return value: %TRUE if the #ClutterTexture should maintain the
2893 * aspect ratio of the underlying image
2898 clutter_texture_get_keep_aspect_ratio (ClutterTexture *texture)
2900 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2902 return texture->priv->keep_aspect_ratio;
2906 * clutter_texture_set_load_async:
2907 * @texture: a #ClutterTexture
2908 * @load_async: %TRUE if the texture should asynchronously load data
2911 * Sets whether @texture should use a worker thread to load the data
2912 * from disk asynchronously. Setting @load_async to %TRUE will make
2913 * clutter_texture_set_from_file() return immediately.
2915 * See the #ClutterTexture:load-async property documentation, and
2916 * clutter_texture_set_load_data_async().
2921 clutter_texture_set_load_async (ClutterTexture *texture,
2922 gboolean load_async)
2924 ClutterTexturePrivate *priv;
2926 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2928 priv = texture->priv;
2930 load_async = !!load_async;
2932 if (priv->load_async_set != load_async)
2934 priv->load_data_async = load_async;
2935 priv->load_size_async = load_async;
2937 priv->load_async_set = load_async;
2939 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_ASYNC]);
2940 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_DATA_ASYNC]);
2945 * clutter_texture_get_load_async:
2946 * @texture: a #ClutterTexture
2948 * Retrieves the value set using clutter_texture_set_load_async()
2950 * Return value: %TRUE if the #ClutterTexture should load the data from
2951 * disk asynchronously
2956 clutter_texture_get_load_async (ClutterTexture *texture)
2958 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2960 return texture->priv->load_async_set;
2964 * clutter_texture_set_load_data_async:
2965 * @texture: a #ClutterTexture
2966 * @load_async: %TRUE if the texture should asynchronously load data
2969 * Sets whether @texture should use a worker thread to load the data
2970 * from disk asynchronously. Setting @load_async to %TRUE will make
2971 * clutter_texture_set_from_file() block until the #ClutterTexture has
2972 * determined the width and height of the image data.
2974 * See the #ClutterTexture:load-async property documentation, and
2975 * clutter_texture_set_load_async().
2980 clutter_texture_set_load_data_async (ClutterTexture *texture,
2981 gboolean load_async)
2983 ClutterTexturePrivate *priv;
2985 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2987 priv = texture->priv;
2989 if (priv->load_data_async != load_async)
2991 /* load-data-async always unsets load-size-async */
2992 priv->load_data_async = load_async;
2993 priv->load_size_async = FALSE;
2995 priv->load_async_set = load_async;
2997 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_ASYNC]);
2998 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_LOAD_DATA_ASYNC]);
3003 * clutter_texture_get_load_data_async:
3004 * @texture: a #ClutterTexture
3006 * Retrieves the value set by clutter_texture_set_load_data_async()
3008 * Return value: %TRUE if the #ClutterTexture should load the image
3009 * data from a file asynchronously
3014 clutter_texture_get_load_data_async (ClutterTexture *texture)
3016 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3018 return texture->priv->load_async_set &&
3019 texture->priv->load_data_async;
3023 * clutter_texture_set_pick_with_alpha:
3024 * @texture: a #ClutterTexture
3025 * @pick_with_alpha: %TRUE if the alpha channel should affect the
3028 * Sets whether @texture should have it's shape defined by the alpha
3029 * channel when picking.
3031 * Be aware that this is a bit more costly than the default picking
3032 * due to the texture lookup, extra test against the alpha value and
3033 * the fact that it will also interrupt the batching of geometry done
3036 * Also there is currently no control over the threshold used to
3037 * determine what value of alpha is considered pickable, and so only
3038 * fully opaque parts of the texture will react to picking.
3043 clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
3044 gboolean pick_with_alpha)
3046 ClutterTexturePrivate *priv;
3048 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
3050 priv = texture->priv;
3052 if (priv->pick_with_alpha == pick_with_alpha)
3055 if (!pick_with_alpha && priv->pick_pipeline != NULL)
3057 cogl_object_unref (priv->pick_pipeline);
3058 priv->pick_pipeline = NULL;
3061 /* NB: the pick pipeline is created lazily when we first pick */
3062 priv->pick_with_alpha = pick_with_alpha;
3064 /* NB: actors are expected to call clutter_actor_queue_redraw when
3065 * ever some state changes that will affect painting *or picking...
3067 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
3071 * clutter_texture_get_pick_with_alpha:
3072 * @texture: a #ClutterTexture
3074 * Retrieves the value set by clutter_texture_set_load_data_async()
3076 * Return value: %TRUE if the #ClutterTexture should define its shape
3077 * using the alpha channel when picking.
3082 clutter_texture_get_pick_with_alpha (ClutterTexture *texture)
3084 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3086 return texture->priv->pick_with_alpha ? TRUE : FALSE;