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);
561 /* Render the actor to the fbo */
562 clutter_actor_paint (priv->fbo_source);
564 /* Restore drawing to the previous framebuffer */
565 cogl_pop_framebuffer ();
567 /* If there is a shader on top of the shader stack, turn it back on. */
569 clutter_shader_set_is_enabled (shader, TRUE);
573 gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self)
575 ClutterTexture *texture = CLUTTER_TEXTURE (self);
576 ClutterTexturePrivate *priv = texture->priv;
580 clutter_actor_get_allocation_box (self, &box);
582 if (priv->repeat_x && priv->image_width > 0)
583 t_w = (box.x2 - box.x1) / (float) priv->image_width;
587 if (priv->repeat_y && priv->image_height > 0)
588 t_h = (box.y2 - box.y1) / (float) priv->image_height;
592 cogl_rectangle_with_texture_coords (0, 0,
598 static CoglPipeline *
599 create_pick_pipeline (ClutterActor *self)
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;
606 if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
608 " MODULATE (CONSTANT, TEXTURE[A])",
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);
620 cogl_pipeline_set_blend (pick_pipeline,
621 "RGBA = ADD (SRC_COLOR[RGBA], 0)",
624 cogl_pipeline_set_alpha_test_function (pick_pipeline,
625 COGL_PIPELINE_ALPHA_FUNC_EQUAL,
628 return pick_pipeline;
632 clutter_texture_pick (ClutterActor *self,
633 const ClutterColor *color)
635 ClutterTexture *texture = CLUTTER_TEXTURE (self);
636 ClutterTexturePrivate *priv = texture->priv;
638 if (!clutter_actor_should_pick_paint (self))
641 if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
643 CoglColor pick_color;
645 if (priv->pick_pipeline == NULL)
646 priv->pick_pipeline = create_pick_pipeline (self);
648 if (priv->pick_pipeline == NULL)
650 priv->pick_with_alpha_supported = FALSE;
651 CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
656 if (priv->fbo_handle != NULL)
659 cogl_color_init_from_4ub (&pick_color,
664 cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
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);
672 CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
676 clutter_texture_paint (ClutterActor *self)
678 ClutterTexture *texture = CLUTTER_TEXTURE (self);
679 ClutterTexturePrivate *priv = texture->priv;
680 guint8 paint_opacity = clutter_actor_get_paint_opacity (self);
683 "painting texture '%s'",
684 clutter_actor_get_name (self) ? clutter_actor_get_name (self)
687 if (priv->fbo_handle != NULL)
690 cogl_pipeline_set_color4ub (priv->pipeline,
695 cogl_set_source (priv->pipeline);
697 gen_texcoords_and_draw_cogl_rectangle (self);
701 clutter_texture_get_paint_volume (ClutterActor *self,
702 ClutterPaintVolume *volume)
704 ClutterTexturePrivate *priv;
706 priv = CLUTTER_TEXTURE (self)->priv;
708 if (priv->pipeline == NULL)
711 if (priv->image_width == 0 || priv->image_height == 0)
714 return _clutter_actor_set_default_paint_volume (self,
715 CLUTTER_TYPE_TEXTURE,
720 clutter_texture_async_data_free (ClutterTextureAsyncData *data)
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);
728 if (data->load_bitmap != NULL)
729 cogl_object_unref (data->load_bitmap);
731 if (data->load_error != NULL)
732 g_error_free (data->load_error);
734 g_slice_free (ClutterTextureAsyncData, data);
738 * clutter_texture_async_load_cancel:
739 * @texture: a #ClutterTexture
741 * Cancels an asynchronous loading operation, whether done
742 * with threads enabled or just using the main loop
745 clutter_texture_async_load_cancel (ClutterTexture *texture)
747 ClutterTexturePrivate *priv = texture->priv;
749 if (priv->async_data != NULL)
751 ClutterTextureAsyncData *async_data = priv->async_data;
753 priv->async_data = NULL;
755 if (async_data->load_idle != 0)
757 g_source_remove (async_data->load_idle);
758 async_data->load_idle = 0;
760 clutter_texture_async_data_free (async_data);
764 clutter_texture_async_data_lock (async_data);
766 CLUTTER_NOTE (TEXTURE, "[async] cancelling operation for '%s'",
767 async_data->load_filename);
769 async_data->state |= ASYNC_STATE_CANCELLED;
771 clutter_texture_async_data_unlock (async_data);
777 clutter_texture_dispose (GObject *object)
779 ClutterTexture *texture = CLUTTER_TEXTURE (object);
780 ClutterTexturePrivate *priv = texture->priv;
782 texture_free_gl_resources (texture);
783 texture_fbo_free_resources (texture);
785 clutter_texture_async_load_cancel (texture);
787 if (priv->pipeline != NULL)
789 cogl_object_unref (priv->pipeline);
790 priv->pipeline = NULL;
793 if (priv->pick_pipeline != NULL)
795 cogl_object_unref (priv->pick_pipeline);
796 priv->pick_pipeline = NULL;
799 G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
803 clutter_texture_finalize (GObject *object)
805 ClutterTexturePrivate *priv = CLUTTER_TEXTURE (object)->priv;
807 g_free (priv->filename);
809 G_OBJECT_CLASS (clutter_texture_parent_class)->finalize (object);
813 clutter_texture_set_property (GObject *object,
818 ClutterTexture *texture;
819 ClutterTexturePrivate *priv;
821 texture = CLUTTER_TEXTURE (object);
822 priv = texture->priv;
827 clutter_texture_set_sync_size (texture, g_value_get_boolean (value));
831 clutter_texture_set_repeat (texture,
832 g_value_get_boolean (value),
837 clutter_texture_set_repeat (texture,
839 g_value_get_boolean (value));
842 case PROP_FILTER_QUALITY:
843 clutter_texture_set_filter_quality (texture,
844 g_value_get_enum (value));
847 case PROP_COGL_TEXTURE:
849 CoglHandle hnd = g_value_get_boxed (value);
851 clutter_texture_set_cogl_texture (texture, hnd);
855 case PROP_COGL_MATERIAL:
857 CoglHandle hnd = g_value_get_boxed (value);
859 clutter_texture_set_cogl_material (texture, hnd);
864 clutter_texture_set_from_file (texture,
865 g_value_get_string (value),
870 priv->no_slice = g_value_get_boolean (value);
873 case PROP_KEEP_ASPECT_RATIO:
874 clutter_texture_set_keep_aspect_ratio (texture,
875 g_value_get_boolean (value));
878 case PROP_LOAD_DATA_ASYNC:
879 clutter_texture_set_load_data_async (texture,
880 g_value_get_boolean (value));
883 case PROP_LOAD_ASYNC:
884 clutter_texture_set_load_async (texture, g_value_get_boolean (value));
887 case PROP_PICK_WITH_ALPHA:
888 clutter_texture_set_pick_with_alpha (texture,
889 g_value_get_boolean (value));
893 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
899 clutter_texture_get_property (GObject *object,
904 ClutterTexture *texture;
905 ClutterTexturePrivate *priv;
907 texture = CLUTTER_TEXTURE(object);
908 priv = texture->priv;
912 case PROP_PIXEL_FORMAT:
913 g_value_set_enum (value, clutter_texture_get_pixel_format (texture));
916 case PROP_MAX_TILE_WASTE:
917 g_value_set_int (value, clutter_texture_get_max_tile_waste (texture));
921 g_value_set_boolean (value, priv->sync_actor_size);
925 g_value_set_boolean (value, priv->repeat_x);
929 g_value_set_boolean (value, priv->repeat_y);
932 case PROP_FILTER_QUALITY:
933 g_value_set_enum (value, clutter_texture_get_filter_quality (texture));
936 case PROP_COGL_TEXTURE:
937 g_value_set_boxed (value, clutter_texture_get_cogl_texture (texture));
940 case PROP_COGL_MATERIAL:
941 g_value_set_boxed (value, clutter_texture_get_cogl_material (texture));
945 g_value_set_boolean (value, priv->no_slice);
948 case PROP_KEEP_ASPECT_RATIO:
949 g_value_set_boolean (value, priv->keep_aspect_ratio);
952 case PROP_PICK_WITH_ALPHA:
953 g_value_set_boolean (value, priv->pick_with_alpha);
957 g_value_set_string (value, priv->filename);
961 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
967 clutter_texture_class_init (ClutterTextureClass *klass)
969 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
970 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
973 g_type_class_add_private (klass, sizeof (ClutterTexturePrivate));
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;
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;
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;
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"),
995 CLUTTER_PARAM_READWRITE);
996 obj_props[PROP_SYNC_SIZE] = pspec;
997 g_object_class_install_property (gobject_class, PROP_SYNC_SIZE, pspec);
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"),
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);
1009 pspec = g_param_spec_int ("tile-waste",
1011 P_("Maximum waste area of a sliced texture"),
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);
1018 pspec = g_param_spec_boolean ("repeat-x",
1019 P_("Horizontal repeat"),
1020 P_("Repeat the contents rather than scaling them horizontally"),
1022 CLUTTER_PARAM_READWRITE);
1023 obj_props[PROP_REPEAT_X] = pspec;
1024 g_object_class_install_property (gobject_class, PROP_REPEAT_X, pspec);
1026 pspec = g_param_spec_boolean ("repeat-y",
1027 P_("Vertical repeat"),
1028 P_("Repeat the contents rather than scaling them vertically"),
1030 CLUTTER_PARAM_READWRITE);
1031 obj_props[PROP_REPEAT_Y] = pspec;
1032 g_object_class_install_property (gobject_class, PROP_REPEAT_Y, pspec);
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);
1043 pspec = g_param_spec_enum ("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);
1052 pspec = g_param_spec_boxed ("cogl-texture",
1054 P_("The underlying Cogl texture handle used to draw this actor"),
1056 CLUTTER_PARAM_READWRITE);
1057 obj_props[PROP_COGL_TEXTURE] = pspec;
1058 g_object_class_install_property (gobject_class, PROP_COGL_TEXTURE, pspec);
1060 pspec = g_param_spec_boxed ("cogl-material",
1061 P_("Cogl Material"),
1062 P_("The underlying Cogl material handle used to draw this actor"),
1064 CLUTTER_PARAM_READWRITE);
1065 obj_props[PROP_COGL_MATERIAL] = pspec;
1066 g_object_class_install_property (gobject_class, PROP_COGL_MATERIAL, pspec);
1069 * ClutterTexture:filename:
1071 * The path of the file containing the image data to be displayed by
1074 * This property is unset when using the clutter_texture_set_from_*_data()
1075 * family of functions.
1077 pspec = g_param_spec_string ("filename",
1079 P_("The path of the file containing the image data"),
1081 CLUTTER_PARAM_READWRITE);
1082 obj_props[PROP_FILENAME] = pspec;
1083 g_object_class_install_property (gobject_class, PROP_FILENAME, pspec);
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"),
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);
1094 * ClutterTexture:load-async:
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.
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
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
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"),
1116 CLUTTER_PARAM_WRITABLE);
1117 obj_props[PROP_LOAD_ASYNC] = pspec;
1118 g_object_class_install_property (gobject_class, PROP_LOAD_ASYNC, pspec);
1122 * ClutterTexture:load-data-async:
1124 * Like #ClutterTexture:load-async but loads the width and height
1125 * synchronously causing some blocking.
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"),
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);
1138 * ClutterTexture::pick-with-alpha:
1140 * Determines whether a #ClutterTexture should have it's shape defined
1141 * by its alpha channel when picking.
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
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.
1154 pspec = g_param_spec_boolean ("pick-with-alpha",
1155 P_("Pick With Alpha"),
1156 P_("Shape actor with alpha channel when picking"),
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);
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
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.
1172 texture_signals[SIZE_CHANGE] =
1173 g_signal_new ("size-change",
1174 G_TYPE_FROM_CLASS (gobject_class),
1176 G_STRUCT_OFFSET (ClutterTextureClass, size_change),
1178 _clutter_marshal_VOID__INT_INT,
1183 * ClutterTexture::pixbuf-change:
1184 * @texture: the texture which received the signal
1186 * The ::pixbuf-change signal is emitted each time the pixbuf
1187 * used by @texture changes.
1189 texture_signals[PIXBUF_CHANGE] =
1190 g_signal_new ("pixbuf-change",
1191 G_TYPE_FROM_CLASS (gobject_class),
1193 G_STRUCT_OFFSET (ClutterTextureClass, pixbuf_change),
1195 _clutter_marshal_VOID__VOID,
1199 * ClutterTexture::load-finished:
1200 * @texture: the texture which received the signal
1201 * @error: A set error, or %NULL
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
1209 texture_signals[LOAD_FINISHED] =
1210 g_signal_new (I_("load-finished"),
1211 G_TYPE_FROM_CLASS (gobject_class),
1213 G_STRUCT_OFFSET (ClutterTextureClass, load_finished),
1215 _clutter_marshal_VOID__BOXED,
1221 static ClutterScriptableIface *parent_scriptable_iface = NULL;
1224 clutter_texture_set_custom_property (ClutterScriptable *scriptable,
1225 ClutterScript *script,
1227 const GValue *value)
1229 ClutterTexture *texture = CLUTTER_TEXTURE (scriptable);
1231 if (strcmp ("filename", name) == 0)
1233 const gchar *str = g_value_get_string (value);
1237 path = clutter_script_lookup_filename (script, str);
1238 if (G_UNLIKELY (!path))
1240 g_warning ("Unable to find image %s", str);
1245 clutter_texture_set_from_file (texture, path, &error);
1248 g_warning ("Unable to open image path at '%s': %s",
1251 g_error_free (error);
1259 if (parent_scriptable_iface->set_custom_property)
1260 parent_scriptable_iface->set_custom_property (scriptable, script,
1267 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
1269 parent_scriptable_iface = g_type_interface_peek_parent (iface);
1271 if (!parent_scriptable_iface)
1272 parent_scriptable_iface = g_type_default_interface_peek
1273 (CLUTTER_TYPE_SCRIPTABLE);
1275 iface->set_custom_property = clutter_texture_set_custom_property;
1279 clutter_texture_init (ClutterTexture *self)
1281 ClutterTexturePrivate *priv;
1283 self->priv = priv = CLUTTER_TEXTURE_GET_PRIVATE (self);
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;
1295 if (G_UNLIKELY (texture_template_pipeline == NULL))
1297 CoglPipeline *pipeline;
1299 clutter_backend_get_cogl_context (clutter_get_default_backend ());
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);
1308 g_assert (texture_template_pipeline != NULL);
1309 priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1313 * clutter_texture_get_cogl_material:
1314 * @texture: A #ClutterTexture
1316 * Returns a handle to the underlying COGL material used for drawing
1319 * Return value: (transfer none): a handle for a #CoglMaterial. The
1320 * material is owned by the #ClutterTexture and it should not be
1326 clutter_texture_get_cogl_material (ClutterTexture *texture)
1328 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1330 return texture->priv->pipeline;
1334 * clutter_texture_set_cogl_material:
1335 * @texture: A #ClutterTexture
1336 * @cogl_material: A CoglHandle for a material
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
1351 clutter_texture_set_cogl_material (ClutterTexture *texture,
1352 CoglHandle cogl_material)
1354 CoglPipeline *cogl_pipeline = cogl_material;
1355 CoglHandle cogl_texture;
1357 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1359 cogl_object_ref (cogl_pipeline);
1361 if (texture->priv->pipeline)
1362 cogl_object_unref (texture->priv->pipeline);
1364 texture->priv->pipeline = cogl_pipeline;
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
1374 typedef struct _GetLayerState
1381 layer_cb (CoglPipeline *pipeline, int layer, void *user_data)
1383 GetLayerState *state = user_data;
1385 state->has_layer = TRUE;
1386 state->first_layer = layer;
1388 /* We only care about the first layer. */
1393 get_first_layer_index (CoglPipeline *pipeline, int *layer_index)
1395 GetLayerState state = { FALSE };
1396 cogl_pipeline_foreach_layer (pipeline,
1399 if (state.has_layer)
1400 *layer_index = state.first_layer;
1402 return state.has_layer;
1406 * clutter_texture_get_cogl_texture:
1407 * @texture: A #ClutterTexture
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.
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.
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
1424 clutter_texture_get_cogl_texture (ClutterTexture *texture)
1426 ClutterTexturePrivate *priv;
1429 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), NULL);
1431 priv = texture->priv;
1433 if (get_first_layer_index (priv->pipeline, &layer_index))
1434 return cogl_pipeline_get_layer_texture (priv->pipeline, layer_index);
1440 * clutter_texture_set_cogl_texture
1441 * @texture: A #ClutterTexture
1442 * @cogl_tex: A CoglHandle for a texture
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.
1451 clutter_texture_set_cogl_texture (ClutterTexture *texture,
1452 CoglHandle cogl_tex)
1454 ClutterTexturePrivate *priv;
1455 gboolean size_changed;
1456 guint width, height;
1458 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
1459 g_return_if_fail (cogl_is_texture (cogl_tex));
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. */
1466 priv = texture->priv;
1468 width = cogl_texture_get_width (cogl_tex);
1469 height = cogl_texture_get_height (cogl_tex);
1471 /* Reference the new texture now in case it is the same one we are
1473 cogl_object_ref (cogl_tex);
1475 /* Remove FBO if exisiting */
1476 if (priv->fbo_source)
1477 texture_fbo_free_resources (texture);
1479 /* Remove old texture */
1480 texture_free_gl_resources (texture);
1482 /* Use the new texture */
1483 if (priv->pipeline == NULL)
1484 priv->pipeline = cogl_pipeline_copy (texture_template_pipeline);
1486 g_assert (priv->pipeline != NULL);
1487 cogl_pipeline_set_layer_texture (priv->pipeline, 0, cogl_tex);
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);
1493 size_changed = (width != priv->image_width || height != priv->image_height);
1494 priv->image_width = width;
1495 priv->image_height = height;
1497 CLUTTER_NOTE (TEXTURE, "set size (w:%d, h:%d)",
1499 priv->image_height);
1503 g_signal_emit (texture, texture_signals[SIZE_CHANGE], 0,
1505 priv->image_height);
1507 if (priv->sync_actor_size)
1509 ClutterActor *actor = CLUTTER_ACTOR (texture);
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
1518 if (priv->keep_aspect_ratio)
1520 ClutterRequestMode request;
1522 if (priv->image_width >= priv->image_height)
1523 request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
1525 request = CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
1527 clutter_actor_set_request_mode (actor, request);
1530 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
1535 g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
1537 /* If resized actor may need resizing but paint() will do this */
1538 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
1540 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_COGL_TEXTURE]);
1544 clutter_texture_set_from_data (ClutterTexture *texture,
1546 CoglPixelFormat source_format,
1553 ClutterTexturePrivate *priv = texture->priv;
1554 CoglHandle new_texture = NULL;
1555 CoglTextureFlags flags = COGL_TEXTURE_NONE;
1558 flags |= COGL_TEXTURE_NO_SLICING;
1560 /* FIXME if we are not realized, we should store the data
1561 * for future use, instead of creating the texture.
1563 new_texture = cogl_texture_new_from_data (width, height,
1566 COGL_PIXEL_FORMAT_ANY,
1570 if (G_UNLIKELY (new_texture == NULL))
1572 GError *inner_error = NULL;
1574 g_set_error (&inner_error, CLUTTER_TEXTURE_ERROR,
1575 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1576 _("Failed to load the image data"));
1578 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, inner_error);
1581 g_propagate_error (error, inner_error);
1583 g_error_free (inner_error);
1588 g_free (priv->filename);
1589 priv->filename = NULL;
1591 clutter_texture_set_cogl_texture (texture, new_texture);
1593 cogl_object_unref (new_texture);
1595 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
1600 static inline gboolean
1601 get_pixel_format_from_texture_flags (gint bpp,
1603 ClutterTextureFlags flags,
1604 CoglPixelFormat *source_format)
1606 /* Convert the flags to a CoglPixelFormat */
1609 if (G_UNLIKELY (bpp != 4))
1611 g_warning ("Unsupported bytes per pixel value '%d': "
1612 "Clutter supports only a value of 4 "
1618 *source_format = COGL_PIXEL_FORMAT_RGBA_8888;
1622 if (G_UNLIKELY (bpp != 3))
1624 g_warning ("Unsupported bytes per pixel value '%d': "
1625 "Clutter supports only a BPP value of 3 "
1631 *source_format = COGL_PIXEL_FORMAT_RGB_888;
1634 if ((flags & CLUTTER_TEXTURE_RGB_FLAG_BGR))
1635 *source_format |= COGL_BGR_BIT;
1637 if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT))
1638 *source_format |= COGL_PREMULT_BIT;
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.
1656 * Sets #ClutterTexture image data.
1658 * Return value: %TRUE on success, %FALSE on failure.
1663 clutter_texture_set_from_rgb_data (ClutterTexture *texture,
1670 ClutterTextureFlags flags,
1673 CoglPixelFormat source_format;
1675 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1677 if (!get_pixel_format_from_texture_flags (bpp,
1685 return clutter_texture_set_from_data (texture, data,
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.
1701 * Sets a #ClutterTexture from YUV image data. If an error occurred,
1702 * %FALSE is returned and @error is set.
1704 * The YUV support depends on the driver; the format supported by the
1705 * few drivers exposing this capability are not really useful.
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.
1711 * Return value: %TRUE if the texture was successfully updated
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
1720 clutter_texture_set_from_yuv_data (ClutterTexture *texture,
1724 ClutterTextureFlags flags,
1727 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
1729 if (!clutter_feature_available (CLUTTER_FEATURE_TEXTURE_YUV))
1731 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1732 CLUTTER_TEXTURE_ERROR_NO_YUV,
1733 _("YUV textures are not supported"));
1737 /* Convert the flags to a CoglPixelFormat */
1738 if ((flags & CLUTTER_TEXTURE_YUV_FLAG_YUV2))
1740 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1741 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1742 _("YUV2 textues are not supported"));
1746 return clutter_texture_set_from_data (texture, data,
1747 COGL_PIXEL_FORMAT_YUV,
1754 * clutter_texture_async_load_complete:
1755 * @self: a #ClutterTexture
1756 * @bitmap: a handle to a CoglBitmap
1757 * @error: load error
1759 * If @error is %NULL, loads @bitmap into a #CoglTexture.
1761 * This function emits the ::load-finished signal on @self.
1764 clutter_texture_async_load_complete (ClutterTexture *self,
1766 const GError *error)
1768 ClutterTexturePrivate *priv = self->priv;
1769 CoglTextureFlags flags = COGL_TEXTURE_NONE;
1772 priv->async_data = NULL;
1777 flags |= COGL_TEXTURE_NO_SLICING;
1779 handle = cogl_texture_new_from_bitmap (bitmap,
1781 COGL_PIXEL_FORMAT_ANY);
1782 clutter_texture_set_cogl_texture (self, handle);
1784 if (priv->load_size_async)
1786 g_signal_emit (self, texture_signals[SIZE_CHANGE], 0,
1787 cogl_texture_get_width (handle),
1788 cogl_texture_get_height (handle));
1791 cogl_object_unref (handle);
1794 g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, error);
1796 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
1800 texture_repaint_upload_func (gpointer user_data)
1802 g_mutex_lock (&upload_list_mutex);
1804 if (upload_list != NULL)
1806 gint64 start_time = g_get_monotonic_time ();
1808 /* continue uploading textures as long as we havent spent more
1809 * then 5ms doing so this stage redraw cycle.
1813 ClutterTextureAsyncData *async_data = upload_list->data;
1815 clutter_texture_async_data_lock (async_data);
1817 if (async_data->state & ASYNC_STATE_QUEUED)
1819 CLUTTER_NOTE (TEXTURE, "[async] operation complete for '%s'",
1820 async_data->load_filename);
1822 clutter_texture_async_load_complete (async_data->texture,
1823 async_data->load_bitmap,
1824 async_data->load_error);
1827 CLUTTER_NOTE (TEXTURE, "[async] operation cancelled for '%s'",
1828 async_data->load_filename);
1830 clutter_texture_async_data_unlock (async_data);
1832 upload_list = g_list_remove (upload_list, async_data);
1833 clutter_texture_async_data_free (async_data);
1835 while (upload_list != NULL &&
1836 g_get_monotonic_time () < start_time + 5 * 1000L);
1839 if (upload_list != NULL)
1841 ClutterMasterClock *master_clock;
1843 master_clock = _clutter_master_clock_get_default ();
1844 _clutter_master_clock_ensure_next_iteration (master_clock);
1847 g_mutex_unlock (&upload_list_mutex);
1853 clutter_texture_thread_load (gpointer user_data,
1856 ClutterTextureAsyncData *async_data = user_data;
1857 ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
1859 clutter_texture_async_data_lock (async_data);
1861 if (~async_data->state & ASYNC_STATE_CANCELLED)
1863 CLUTTER_NOTE (TEXTURE, "[async] loading bitmap from file '%s'",
1864 async_data->load_filename);
1866 async_data->load_bitmap =
1867 cogl_bitmap_new_from_file (async_data->load_filename,
1868 &async_data->load_error);
1870 g_mutex_lock (&upload_list_mutex);
1872 if (repaint_upload_func == 0)
1874 repaint_upload_func =
1875 clutter_threads_add_repaint_func (texture_repaint_upload_func,
1879 upload_list = g_list_append (upload_list, async_data);
1880 async_data->state |= ASYNC_STATE_QUEUED;
1882 CLUTTER_NOTE (TEXTURE, "[async] operation queued");
1884 g_mutex_unlock (&upload_list_mutex);
1888 clutter_texture_async_data_unlock (async_data);
1889 clutter_texture_async_data_free (async_data);
1894 clutter_texture_async_data_unlock (async_data);
1896 _clutter_master_clock_ensure_next_iteration (master_clock);
1900 clutter_texture_idle_load (gpointer data)
1902 ClutterTextureAsyncData *async_data = data;
1904 async_data->load_bitmap =
1905 cogl_bitmap_new_from_file (async_data->load_filename,
1906 &async_data->load_error);
1908 clutter_texture_async_load_complete (async_data->texture,
1909 async_data->load_bitmap,
1910 async_data->load_error);
1912 clutter_texture_async_data_free (async_data);
1918 * clutter_texture_async_load:
1919 * @self: a #ClutterTExture
1920 * @filename: name of the file to load
1921 * @error: return location for a #GError
1923 * Starts an asynchronous load of the file name stored inside
1924 * the load_filename member of @data.
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.
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.
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().
1938 * Return value: %TRUE if the asynchronous loading was successfully
1939 * initiated, %FALSE otherwise
1942 clutter_texture_async_load (ClutterTexture *self,
1943 const gchar *filename,
1946 ClutterTexturePrivate *priv = self->priv;
1947 ClutterTextureAsyncData *data;
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
1956 if (priv->load_size_async)
1963 res = cogl_bitmap_get_size_from_file (filename, &width, &height);
1967 g_set_error (error, CLUTTER_TEXTURE_ERROR,
1968 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
1969 _("Failed to load the image data"));
1974 priv->image_width = width;
1975 priv->image_height = height;
1978 clutter_texture_async_load_cancel (self);
1980 data = g_slice_new0 (ClutterTextureAsyncData);
1982 data->texture = self;
1983 data->load_filename = g_strdup (filename);
1985 priv->async_data = data;
1989 if (G_UNLIKELY (async_thread_pool == NULL))
1991 /* This apparently can't fail if exclusive == FALSE */
1993 g_thread_pool_new (clutter_texture_thread_load, NULL,
1999 g_thread_pool_push (async_thread_pool, data, NULL);
2004 clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
2005 clutter_texture_idle_load,
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
2019 * Sets the #ClutterTexture image data from an image file. In case of
2020 * failure, %FALSE is returned and @error is set.
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.
2029 * Return value: %TRUE if the image was successfully loaded and set
2034 clutter_texture_set_from_file (ClutterTexture *texture,
2035 const gchar *filename,
2038 ClutterTexturePrivate *priv;
2039 CoglHandle new_texture = NULL;
2040 GError *internal_error = NULL;
2041 CoglTextureFlags flags = COGL_TEXTURE_NONE;
2043 priv = texture->priv;
2045 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2047 if (priv->load_data_async)
2048 return clutter_texture_async_load (texture, filename, error);
2051 flags |= COGL_TEXTURE_NO_SLICING;
2053 new_texture = cogl_texture_new_from_file (filename,
2055 COGL_PIXEL_FORMAT_ANY,
2058 /* If COGL didn't give an error then make one up */
2059 if (internal_error == NULL && new_texture == NULL)
2061 g_set_error (&internal_error, CLUTTER_TEXTURE_ERROR,
2062 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2063 _("Failed to load the image data"));
2066 if (internal_error != NULL)
2068 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0,
2071 g_propagate_error (error, internal_error);
2076 g_free (priv->filename);
2077 priv->filename = g_strdup (filename);
2079 clutter_texture_set_cogl_texture (texture, new_texture);
2081 cogl_object_unref (new_texture);
2083 g_signal_emit (texture, texture_signals[LOAD_FINISHED], 0, NULL);
2085 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILENAME]);
2091 * clutter_texture_set_filter_quality:
2092 * @texture: a #ClutterTexture
2093 * @filter_quality: new filter quality value
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.
2107 clutter_texture_set_filter_quality (ClutterTexture *texture,
2108 ClutterTextureQuality filter_quality)
2110 ClutterTexturePrivate *priv;
2111 ClutterTextureQuality old_quality;
2113 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2115 priv = texture->priv;
2117 old_quality = clutter_texture_get_filter_quality (texture);
2119 if (filter_quality != old_quality)
2121 gint min_filter, mag_filter;
2123 min_filter = mag_filter = COGL_PIPELINE_FILTER_LINEAR;
2124 clutter_texture_quality_to_filters (filter_quality,
2128 cogl_pipeline_set_layer_filters (priv->pipeline, 0,
2129 min_filter, mag_filter);
2131 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2133 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_FILTER_QUALITY]);
2138 * clutter_texture_get_filter_quality
2139 * @texture: A #ClutterTexture
2141 * Gets the filter quality used when scaling a texture.
2143 * Return value: The filter quality value.
2147 ClutterTextureQuality
2148 clutter_texture_get_filter_quality (ClutterTexture *texture)
2150 ClutterTexturePrivate *priv;
2152 CoglPipelineFilter min_filter, mag_filter;
2155 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2157 priv = texture->priv;
2159 if (get_first_layer_index (priv->pipeline, &layer_index))
2161 min_filter = cogl_pipeline_get_layer_min_filter (priv->pipeline,
2163 mag_filter = cogl_pipeline_get_layer_mag_filter (priv->pipeline,
2167 return CLUTTER_TEXTURE_QUALITY_MEDIUM;
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)
2174 /* Unknown filter combination */
2175 return CLUTTER_TEXTURE_QUALITY_LOW;
2179 * clutter_texture_get_max_tile_waste
2180 * @texture: A #ClutterTexture
2182 * Gets the maximum waste that will be used when creating a texture or
2183 * -1 if slicing is disabled.
2185 * Return value: The maximum waste or -1 if the texture waste is
2191 clutter_texture_get_max_tile_waste (ClutterTexture *texture)
2193 ClutterTexturePrivate *priv;
2194 CoglHandle cogl_texture;
2196 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0);
2198 priv = texture->priv;
2200 cogl_texture = clutter_texture_get_cogl_texture (texture);
2202 if (cogl_texture == NULL)
2203 return priv->no_slice ? -1 : COGL_TEXTURE_MAX_WASTE;
2205 return cogl_texture_get_max_waste (cogl_texture);
2209 * clutter_texture_new_from_file:
2210 * @filename: The name of an image file to load.
2211 * @error: Return locatoin for an error.
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
2217 * Return value: A newly created #ClutterTexture object or NULL on
2223 clutter_texture_new_from_file (const gchar *filename,
2226 ClutterActor *texture = clutter_texture_new ();
2228 if (!clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
2231 g_object_ref_sink (texture);
2232 g_object_unref (texture);
2241 * clutter_texture_new:
2243 * Creates a new empty #ClutterTexture object.
2245 * Return value: A newly created #ClutterTexture object.
2248 clutter_texture_new (void)
2250 return g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
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
2259 * Gets the size in pixels of the untransformed underlying image
2262 clutter_texture_get_base_size (ClutterTexture *texture,
2266 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2269 *width = texture->priv->image_width;
2272 *height = texture->priv->image_height;
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
2290 * Updates a sub-region of the pixel data in a #ClutterTexture.
2292 * Return value: %TRUE on success, %FALSE on failure.
2297 clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
2306 ClutterTextureFlags flags,
2309 CoglPixelFormat source_format;
2310 CoglHandle cogl_texture;
2312 if (!get_pixel_format_from_texture_flags (bpp, has_alpha, flags,
2318 /* attempt to realize ... */
2319 if (!CLUTTER_ACTOR_IS_REALIZED (texture) &&
2320 clutter_actor_get_stage (CLUTTER_ACTOR (texture)) != NULL)
2322 clutter_actor_realize (CLUTTER_ACTOR (texture));
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.
2329 cogl_texture = clutter_texture_get_cogl_texture (texture);
2330 if (cogl_texture == NULL)
2332 g_warning ("Failed to realize actor '%s'",
2333 _clutter_actor_get_debug_name (CLUTTER_ACTOR (texture)));
2337 if (!cogl_texture_set_region (cogl_texture,
2339 x, y, width, height,
2345 g_set_error (error, CLUTTER_TEXTURE_ERROR,
2346 CLUTTER_TEXTURE_ERROR_BAD_FORMAT,
2347 _("Failed to load the image data"));
2351 g_free (texture->priv->filename);
2352 texture->priv->filename = NULL;
2355 g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0);
2357 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2363 on_fbo_source_size_change (GObject *object,
2364 GParamSpec *param_spec,
2365 ClutterTexture *texture)
2367 ClutterTexturePrivate *priv = texture->priv;
2369 ClutterActorBox box;
2372 status = clutter_actor_get_paint_box (priv->fbo_source, &box);
2374 clutter_actor_box_get_size (&box, &w, &h);
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
2384 if (status == FALSE || w == 0 || h == 0)
2385 clutter_actor_get_size (priv->fbo_source, &w, &h);
2387 /* We can't create a texture with a width or height of 0... */
2391 if (w != priv->image_width || h != priv->image_height)
2393 CoglTextureFlags flags = COGL_TEXTURE_NONE;
2396 /* tear down the FBO */
2397 if (priv->fbo_handle != NULL)
2398 cogl_object_unref (priv->fbo_handle);
2400 texture_free_gl_resources (texture);
2402 priv->image_width = w;
2403 priv->image_height = h;
2405 flags |= COGL_TEXTURE_NO_SLICING;
2407 tex = cogl_texture_new_with_size (MAX (priv->image_width, 1),
2408 MAX (priv->image_height, 1),
2410 COGL_PIXEL_FORMAT_RGBA_8888_PRE);
2412 cogl_pipeline_set_layer_texture (priv->pipeline, 0, tex);
2414 priv->fbo_handle = cogl_offscreen_new_to_texture (tex);
2416 /* The pipeline now has a reference to the texture so it will
2418 cogl_object_unref (tex);
2420 if (priv->fbo_handle == NULL)
2422 g_warning ("%s: Offscreen texture creation failed", G_STRLOC);
2426 clutter_actor_set_size (CLUTTER_ACTOR (texture), w, h);
2431 on_fbo_parent_change (ClutterActor *actor,
2432 ClutterActor *old_parent,
2433 ClutterTexture *texture)
2435 ClutterActor *parent = CLUTTER_ACTOR(texture);
2437 while ((parent = clutter_actor_get_parent (parent)) != NULL)
2439 if (parent == actor)
2441 g_warning ("Offscreen texture is ancestor of source!");
2442 /* Desperate but will avoid infinite loops */
2443 clutter_actor_remove_child (parent, actor);
2449 fbo_source_queue_redraw_cb (ClutterActor *source,
2450 ClutterActor *origin,
2451 ClutterTexture *texture)
2453 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2457 fbo_source_queue_relayout_cb (ClutterActor *source,
2458 ClutterTexture *texture)
2460 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2464 * clutter_texture_new_from_actor:
2465 * @actor: A source #ClutterActor
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.
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.
2476 * Some tips on usage:
2480 * <para>The source actor must be made visible (i.e by calling
2481 * #clutter_actor_show).</para>
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>
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>
2511 * <para>Avoid reparenting the source with the created texture.</para>
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>
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>
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>
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>
2536 * Return value: A newly created #ClutterTexture object, or %NULL on failure.
2538 * Deprecated: 1.8: Use the #ClutterOffscreenEffect and #ClutterShaderEffect
2539 * directly on the intended #ClutterActor to replace the functionality of
2545 clutter_texture_new_from_actor (ClutterActor *actor)
2547 ClutterTexture *texture;
2548 ClutterTexturePrivate *priv;
2550 ClutterActorBox box;
2553 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2555 if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE)
2558 if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2560 clutter_actor_realize (actor);
2562 if (!CLUTTER_ACTOR_IS_REALIZED (actor))
2566 status = clutter_actor_get_paint_box (actor, &box);
2568 clutter_actor_box_get_size (&box, &w, &h);
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
2578 if (status == FALSE || w == 0 || h == 0)
2579 clutter_actor_get_size (actor, &w, &h);
2581 /* We can't create a 0x0 fbo so always bump the size up to at least
2586 /* Hopefully now were good.. */
2587 texture = g_object_new (CLUTTER_TYPE_TEXTURE,
2588 "disable-slicing", TRUE,
2591 priv = texture->priv;
2593 priv->fbo_source = g_object_ref_sink (actor);
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);
2600 /* Connect up any signals which could change our underlying size */
2601 g_signal_connect (actor,
2603 G_CALLBACK(on_fbo_source_size_change),
2605 g_signal_connect (actor,
2607 G_CALLBACK(on_fbo_source_size_change),
2609 g_signal_connect (actor,
2611 G_CALLBACK(on_fbo_source_size_change),
2613 g_signal_connect (actor,
2615 G_CALLBACK(on_fbo_source_size_change),
2617 g_signal_connect (actor,
2618 "notify::rotation-angle-x",
2619 G_CALLBACK(on_fbo_source_size_change),
2621 g_signal_connect (actor,
2622 "notify::rotation-angle-y",
2623 G_CALLBACK(on_fbo_source_size_change),
2625 g_signal_connect (actor,
2626 "notify::rotation-angle-z",
2627 G_CALLBACK(on_fbo_source_size_change),
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);
2635 /* And a warning if the source becomes a child of the texture */
2636 g_signal_connect (actor,
2638 G_CALLBACK(on_fbo_parent_change),
2641 priv->image_width = w;
2642 priv->image_height = h;
2644 clutter_actor_set_size (CLUTTER_ACTOR (texture),
2646 priv->image_height);
2648 return CLUTTER_ACTOR (texture);
2652 texture_fbo_free_resources (ClutterTexture *texture)
2654 ClutterTexturePrivate *priv;
2656 priv = texture->priv;
2658 if (priv->fbo_source != NULL)
2660 ClutterActor *parent;
2662 parent = clutter_actor_get_parent (priv->fbo_source);
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);
2669 g_signal_handlers_disconnect_by_func
2671 G_CALLBACK(on_fbo_parent_change),
2674 g_signal_handlers_disconnect_by_func
2676 G_CALLBACK(on_fbo_source_size_change),
2679 g_signal_handlers_disconnect_by_func
2681 G_CALLBACK(fbo_source_queue_relayout_cb),
2684 g_signal_handlers_disconnect_by_func
2686 G_CALLBACK(fbo_source_queue_redraw_cb),
2689 g_object_unref (priv->fbo_source);
2691 priv->fbo_source = NULL;
2694 if (priv->fbo_handle != NULL)
2696 cogl_object_unref (priv->fbo_handle);
2697 priv->fbo_handle = NULL;
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
2707 * Sets whether @texture should have the same preferred size as the
2708 * underlying image data.
2713 clutter_texture_set_sync_size (ClutterTexture *texture,
2716 ClutterTexturePrivate *priv;
2718 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2720 priv = texture->priv;
2722 if (priv->sync_actor_size != sync_size)
2724 priv->sync_actor_size = sync_size;
2726 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2728 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_SYNC_SIZE]);
2733 * clutter_texture_get_sync_size:
2734 * @texture: a #ClutterTexture
2736 * Retrieves the value set with clutter_texture_set_sync_size()
2738 * Return value: %TRUE if the #ClutterTexture should have the same
2739 * preferred size of the underlying image data
2744 clutter_texture_get_sync_size (ClutterTexture *texture)
2746 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2748 return texture->priv->sync_actor_size;
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
2757 * Sets whether the @texture should repeat horizontally or
2758 * vertically when the actor size is bigger than the image size
2763 clutter_texture_set_repeat (ClutterTexture *texture,
2767 ClutterTexturePrivate *priv;
2768 gboolean changed = FALSE;
2770 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2772 priv = texture->priv;
2774 g_object_freeze_notify (G_OBJECT (texture));
2776 if (priv->repeat_x != repeat_x)
2778 priv->repeat_x = repeat_x;
2780 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_X]);
2785 if (priv->repeat_y != repeat_y)
2787 priv->repeat_y = repeat_y;
2789 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_REPEAT_Y]);
2795 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
2797 g_object_thaw_notify (G_OBJECT (texture));
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
2806 * Retrieves the horizontal and vertical repeat values set
2807 * using clutter_texture_set_repeat()
2812 clutter_texture_get_repeat (ClutterTexture *texture,
2816 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2818 if (repeat_x != NULL)
2819 *repeat_x = texture->priv->repeat_x;
2821 if (repeat_y != NULL)
2822 *repeat_y = texture->priv->repeat_y;
2826 * clutter_texture_get_pixel_format:
2827 * @texture: a #ClutterTexture
2829 * Retrieves the pixel format used by @texture. This is
2833 * handle = clutter_texture_get_pixel_format (texture);
2835 * if (handle != COGL_INVALID_HANDLE)
2836 * format = cogl_texture_get_format (handle);
2839 * Return value: a #CoglPixelFormat value
2844 clutter_texture_get_pixel_format (ClutterTexture *texture)
2846 CoglHandle cogl_texture;
2848 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), COGL_PIXEL_FORMAT_ANY);
2850 cogl_texture = clutter_texture_get_cogl_texture (texture);
2851 if (cogl_texture == NULL)
2852 return COGL_PIXEL_FORMAT_ANY;
2854 return cogl_texture_get_format (cogl_texture);
2858 * clutter_texture_set_keep_aspect_ratio:
2859 * @texture: a #ClutterTexture
2860 * @keep_aspect: %TRUE to maintain aspect ratio
2862 * Sets whether @texture should have a preferred size maintaining
2863 * the aspect ratio of the underlying image
2868 clutter_texture_set_keep_aspect_ratio (ClutterTexture *texture,
2869 gboolean keep_aspect)
2871 ClutterTexturePrivate *priv;
2873 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2875 priv = texture->priv;
2877 if (priv->keep_aspect_ratio != keep_aspect)
2879 priv->keep_aspect_ratio = keep_aspect;
2881 clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
2883 g_object_notify_by_pspec (G_OBJECT (texture), obj_props[PROP_KEEP_ASPECT_RATIO]);
2888 * clutter_texture_get_keep_aspect_ratio:
2889 * @texture: a #ClutterTexture
2891 * Retrieves the value set using clutter_texture_set_keep_aspect_ratio()
2893 * Return value: %TRUE if the #ClutterTexture should maintain the
2894 * aspect ratio of the underlying image
2899 clutter_texture_get_keep_aspect_ratio (ClutterTexture *texture)
2901 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2903 return texture->priv->keep_aspect_ratio;
2907 * clutter_texture_set_load_async:
2908 * @texture: a #ClutterTexture
2909 * @load_async: %TRUE if the texture should asynchronously load data
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.
2916 * See the #ClutterTexture:load-async property documentation, and
2917 * clutter_texture_set_load_data_async().
2922 clutter_texture_set_load_async (ClutterTexture *texture,
2923 gboolean load_async)
2925 ClutterTexturePrivate *priv;
2927 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2929 priv = texture->priv;
2931 load_async = !!load_async;
2933 if (priv->load_async_set != load_async)
2935 priv->load_data_async = load_async;
2936 priv->load_size_async = load_async;
2938 priv->load_async_set = load_async;
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]);
2946 * clutter_texture_get_load_async:
2947 * @texture: a #ClutterTexture
2949 * Retrieves the value set using clutter_texture_set_load_async()
2951 * Return value: %TRUE if the #ClutterTexture should load the data from
2952 * disk asynchronously
2957 clutter_texture_get_load_async (ClutterTexture *texture)
2959 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
2961 return texture->priv->load_async_set;
2965 * clutter_texture_set_load_data_async:
2966 * @texture: a #ClutterTexture
2967 * @load_async: %TRUE if the texture should asynchronously load data
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.
2975 * See the #ClutterTexture:load-async property documentation, and
2976 * clutter_texture_set_load_async().
2981 clutter_texture_set_load_data_async (ClutterTexture *texture,
2982 gboolean load_async)
2984 ClutterTexturePrivate *priv;
2986 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
2988 priv = texture->priv;
2990 if (priv->load_data_async != load_async)
2992 /* load-data-async always unsets load-size-async */
2993 priv->load_data_async = load_async;
2994 priv->load_size_async = FALSE;
2996 priv->load_async_set = load_async;
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]);
3004 * clutter_texture_get_load_data_async:
3005 * @texture: a #ClutterTexture
3007 * Retrieves the value set by clutter_texture_set_load_data_async()
3009 * Return value: %TRUE if the #ClutterTexture should load the image
3010 * data from a file asynchronously
3015 clutter_texture_get_load_data_async (ClutterTexture *texture)
3017 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3019 return texture->priv->load_async_set &&
3020 texture->priv->load_data_async;
3024 * clutter_texture_set_pick_with_alpha:
3025 * @texture: a #ClutterTexture
3026 * @pick_with_alpha: %TRUE if the alpha channel should affect the
3029 * Sets whether @texture should have it's shape defined by the alpha
3030 * channel when picking.
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
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.
3044 clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
3045 gboolean pick_with_alpha)
3047 ClutterTexturePrivate *priv;
3049 g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
3051 priv = texture->priv;
3053 if (priv->pick_with_alpha == pick_with_alpha)
3056 if (!pick_with_alpha && priv->pick_pipeline != NULL)
3058 cogl_object_unref (priv->pick_pipeline);
3059 priv->pick_pipeline = NULL;
3062 /* NB: the pick pipeline is created lazily when we first pick */
3063 priv->pick_with_alpha = pick_with_alpha;
3065 /* NB: actors are expected to call clutter_actor_queue_redraw when
3066 * ever some state changes that will affect painting *or picking...
3068 clutter_actor_queue_redraw (CLUTTER_ACTOR (texture));
3072 * clutter_texture_get_pick_with_alpha:
3073 * @texture: a #ClutterTexture
3075 * Retrieves the value set by clutter_texture_set_load_data_async()
3077 * Return value: %TRUE if the #ClutterTexture should define its shape
3078 * using the alpha channel when picking.
3083 clutter_texture_get_pick_with_alpha (ClutterTexture *texture)
3085 g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
3087 return texture->priv->pick_with_alpha ? TRUE : FALSE;