return retval;
}
- * opacity. Used by ClutterActorClone. */
+
+ /* Allows overriding the parent traversed when querying an actors paint
- * a paint. Used by ClutterActorClone. */
++ * opacity. Used by ClutterClone. */
+ void
+ _clutter_actor_set_opacity_parent (ClutterActor *self,
+ ClutterActor *parent)
+ {
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ self->priv->opacity_parent = parent;
+ }
+
+ /* Allows you to disable applying the actors model view transform during
++ * a paint. Used by ClutterClone. */
+ void
+ _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
+ gboolean enable)
+ {
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ self->priv->enable_model_view_transform = enable;
+ }
+
--- /dev/null
- cogl_scale (COGL_FIXED_FROM_FLOAT (x_scale),
- COGL_FIXED_FROM_FLOAT (y_scale));
+ /*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2008 Intel Corporation.
+ *
+ * Authored By: Robert Bragg <robert@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ /**
+ * SECTION:clutterclone
+ * @short_description: An actor that displays a clone of a source actor
+ *
+ * #ClutterClone is a #ClutterActor which draws with the paint
+ * function of another actor, scaled to fit its own allocation.
+ *
+ * #ClutterClone can be used to efficiently clone any other actor.
+ *
+ * <note><para>This is different from clutter_texture_new_from_actor()
+ * which requires support for FBOs in the underlying GL
+ * implementation.</para></note>
+ *
+ * #ClutterClone is available since Clutter 1.0
+ */
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include "clutter-color.h"
+ #include "clutter-clone.h"
+ #include "clutter-debug.h"
+ #include "clutter-main.h"
+ #include "clutter-private.h"
+
+ #include "cogl/cogl.h"
+
+ G_DEFINE_TYPE (ClutterClone, clutter_clone, CLUTTER_TYPE_ACTOR);
+
+ enum
+ {
+ PROP_0,
+
+ PROP_SOURCE
+ };
+
+ #define CLUTTER_CLONE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_CLONE, ClutterClonePrivate))
+
+ struct _ClutterClonePrivate
+ {
+ ClutterActor *clone_source;
+ };
+
+ static void
+ clutter_clone_get_preferred_width (ClutterActor *self,
+ ClutterUnit for_height,
+ ClutterUnit *min_width_p,
+ ClutterUnit *natural_width_p)
+ {
+ ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
+ ClutterActor *clone_source = priv->clone_source;
+
+ if (G_UNLIKELY (clone_source == NULL))
+ {
+ if (min_width_p)
+ *min_width_p = 0;
+
+ if (natural_width_p)
+ *natural_width_p = 0;
+ }
+ else
+ clutter_actor_get_preferred_width (clone_source,
+ for_height,
+ min_width_p,
+ natural_width_p);
+ }
+
+ static void
+ clutter_clone_get_preferred_height (ClutterActor *self,
+ ClutterUnit for_width,
+ ClutterUnit *min_height_p,
+ ClutterUnit *natural_height_p)
+ {
+ ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
+ ClutterActor *clone_source = priv->clone_source;
+
+ if (G_UNLIKELY (clone_source == NULL))
+ {
+ if (min_height_p)
+ *min_height_p = 0;
+
+ if (natural_height_p)
+ *natural_height_p = 0;
+ }
+ else
+ clutter_actor_get_preferred_height (clone_source,
+ for_width,
+ min_height_p,
+ natural_height_p);
+ }
+
+ static void
+ clutter_clone_paint (ClutterActor *self)
+ {
+ ClutterClone *clone = CLUTTER_CLONE (self);
+ ClutterClonePrivate *priv = clone->priv;
+ ClutterGeometry geom;
+ ClutterGeometry clone_source_geom;
+ gfloat x_scale, y_scale;
+
+ if (G_UNLIKELY (priv->clone_source == NULL))
+ return;
+
+ CLUTTER_NOTE (PAINT,
+ "painting clone actor '%s'",
+ clutter_actor_get_name (self) ? clutter_actor_get_name (self)
+ : "unknown");
+
+ clutter_actor_get_allocation_geometry (self, &geom);
+ clutter_actor_get_allocation_geometry (priv->clone_source,
+ &clone_source_geom);
+
+ /* We need to scale what the clone-source actor paints to fill our own
+ * allocation... */
+
+ x_scale = (gfloat) geom.width / clone_source_geom.width;
+ y_scale = (gfloat) geom.height / clone_source_geom.height;
+
++ cogl_scale (x_scale, y_scale, 1.0);
+
+ /* The final bits of magic:
+ * - We need to make sure that when the clone-source actor's paint method
+ * calls clutter_actor_get_paint_opacity, it traverses our parent not it's
+ * real parent.
+ * - We need to stop clutter_actor_paint applying the model view matrix of
+ * the clone source actor.
+ */
+ _clutter_actor_set_opacity_parent (priv->clone_source,
+ clutter_actor_get_parent (self));
+ _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE);
+
+ clutter_actor_paint (priv->clone_source);
+
+ _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE);
+ _clutter_actor_set_opacity_parent (priv->clone_source, NULL);
+ }
+
+ static void
+ clutter_clone_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+ {
+ ClutterClone *clone = CLUTTER_CLONE (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_SOURCE:
+ clutter_clone_set_source (clone, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+ }
+
+ static void
+ clutter_clone_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+ {
+ ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_SOURCE:
+ g_value_set_object (value, priv->clone_source);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+ }
+
+ static void
+ clutter_clone_dispose (GObject *gobject)
+ {
+ ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv;
+
+ if (priv->clone_source)
+ {
+ g_object_unref (priv->clone_source);
+ priv->clone_source = NULL;
+ }
+
+ G_OBJECT_CLASS (clutter_clone_parent_class)->dispose (gobject);
+ }
+
+ static void
+ clutter_clone_class_init (ClutterCloneClass *klass)
+ {
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ GParamSpec *pspec = NULL;
+
+ g_type_class_add_private (gobject_class, sizeof (ClutterClonePrivate));
+
+ actor_class->paint = clutter_clone_paint;
+ actor_class->get_preferred_width = clutter_clone_get_preferred_width;
+ actor_class->get_preferred_height = clutter_clone_get_preferred_height;
+
+ gobject_class->dispose = clutter_clone_dispose;
+ gobject_class->set_property = clutter_clone_set_property;
+ gobject_class->get_property = clutter_clone_get_property;
+
+ /**
+ * ClutterClone:source:
+ *
+ * This property specifies the source actor being cloned.
+ *
+ * Since: 1.0
+ */
+ pspec = g_param_spec_object ("source",
+ "Source",
+ "Specifies the actor to be cloned",
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_CONSTRUCT_ONLY |
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_SOURCE, pspec);
+ }
+
+ static void
+ clutter_clone_init (ClutterClone *self)
+ {
+ ClutterClonePrivate *priv;
+
+ self->priv = priv = CLUTTER_CLONE_GET_PRIVATE (self);
+
+ priv->clone_source = NULL;
+ }
+
+ /**
+ * clutter_clone_new:
+ * @source: a #ClutterActor, or %NULL
+ *
+ * Creates a new #ClutterActor which clones @source/
+ *
+ * Return value: the newly created #ClutterClone
+ *
+ * Since: 1.0
+ */
+ ClutterActor *
+ clutter_clone_new (ClutterActor *source)
+ {
+ return g_object_new (CLUTTER_TYPE_CLONE, "source", source, NULL);
+ }
+
+ /**
+ * clutter_clone_set_source:
+ * @clone: a #ClutterClone
+ * @source: a #ClutterActor, or %NULL
+ *
+ * Sets @source as the source actor to be cloned by @clone.
+ *
+ * Since: 1.0
+ */
+ void
+ clutter_clone_set_source (ClutterClone *clone,
+ ClutterActor *source)
+ {
+ ClutterClonePrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_CLONE (clone));
+ g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
+
+ priv = clone->priv;
+
+ if (priv->clone_source)
+ {
+ g_object_unref (priv->clone_source);
+ priv->clone_source = NULL;
+ }
+
+ if (source)
+ priv->clone_source = g_object_ref (source);
+
+ g_object_notify (G_OBJECT (clone), "source");
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (clone));
+ }
+
+ /**
+ * clutter_clone_get_source:
+ * @clone: a #ClutterClone
+ *
+ * Retrieves the source #ClutterActor being cloned by @clone
+ *
+ * Return value: the actor source for the clone
+ *
+ * Since: 1.0
+ */
+ ClutterActor *
+ clutter_clone_get_clone_source (ClutterClone *clone)
+ {
+ g_return_val_if_fail (CLUTTER_IS_CLONE (clone), NULL);
+
+ return clone->priv->clone_source;
+ }
--- /dev/null
- alpha = clutter_alpha_new_with_func (oh->timeline, clutter_sine_func,
- NULL, NULL);
+ #include <clutter/clutter.h>
+
+ #include <math.h>
+ #include <errno.h>
+ #include <stdlib.h>
+ #include <glib.h>
+ #include <gmodule.h>
+
+ #define NHANDS 6
+
+ typedef struct SuperOH
+ {
+ ClutterActor **hand, *bgtex;
+ ClutterActor *real_hand;
+ ClutterActor *group;
+ ClutterActor *stage;
+
+ gint stage_width;
+ gint stage_height;
+ gfloat radius;
+
+ ClutterBehaviour *scaler_1;
+ ClutterBehaviour *scaler_2;
+ ClutterTimeline *timeline;
+ } SuperOH;
+
+ static gint n_hands = NHANDS;
+
+ static GOptionEntry super_oh_entries[] = {
+ {
+ "num-hands", 'n',
+ 0,
+ G_OPTION_ARG_INT, &n_hands,
+ "Number of hands", "HANDS"
+ },
+ { NULL }
+ };
+
+ /* input handler */
+ static gboolean
+ input_cb (ClutterActor *stage,
+ ClutterEvent *event,
+ gpointer data)
+ {
+ SuperOH *oh = data;
+
+ if (event->type == CLUTTER_BUTTON_PRESS)
+ {
+ ClutterButtonEvent *button_event;
+ ClutterActor *e;
+ gint x, y;
+
+ clutter_event_get_coords (event, &x, &y);
+
+ button_event = (ClutterButtonEvent *) event;
+ g_print ("*** button press event (button:%d) at %d, %d ***\n",
+ button_event->button,
+ x, y);
+
+ e = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage), x, y);
+
+ /* only allow hiding the clones */
+ if (e && CLUTTER_IS_CLONE (e))
+ {
+ clutter_actor_hide (e);
+ return TRUE;
+ }
+ }
+ else if (event->type == CLUTTER_KEY_RELEASE)
+ {
+ ClutterKeyEvent *kev = (ClutterKeyEvent *) event;
+
+ g_print ("*** key press event (key:%c) ***\n",
+ clutter_key_event_symbol (kev));
+
+ if (clutter_key_event_symbol (kev) == CLUTTER_q)
+ {
+ clutter_main_quit ();
+ return TRUE;
+ }
+ else if (clutter_key_event_symbol (kev) == CLUTTER_r)
+ {
+ gint i;
+
+ for (i = 0; i < n_hands; i++)
+ clutter_actor_show (oh->hand[i]);
+
+ clutter_actor_show (oh->real_hand);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+
+ /* Timeline handler */
+ static void
+ frame_cb (ClutterTimeline *timeline,
+ gint frame_num,
+ gpointer data)
+ {
+ SuperOH *oh = data;
+ gint i;
+
+ /* Rotate everything clockwise about stage center*/
+
+ clutter_actor_set_rotation (oh->group,
+ CLUTTER_Z_AXIS,
+ frame_num,
+ oh->stage_width / 2,
+ oh->stage_height / 2,
+ 0);
+
+ for (i = 0; i < n_hands; i++)
+ {
+ gdouble scale_x, scale_y;
+
+ clutter_actor_get_scale (oh->hand[i], &scale_x, &scale_y);
+
+ /* Rotate each hand around there centers - to get this we need
+ * to take into account any scaling.
+ */
+ clutter_actor_set_rotation (oh->hand[i],
+ CLUTTER_Z_AXIS,
+ -6.0 * frame_num,
+ 0, 0, 0);
+ }
+ }
+
++static gdouble
++my_sine_wave (ClutterAlpha *alpha,
++ gpointer dummy G_GNUC_UNUSED)
++{
++ ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
++ gdouble progress = clutter_timeline_get_progress (timeline);
++
++ return sin (progress * G_PI);
++}
++
+ G_MODULE_EXPORT int
+ test_actor_clone_main (int argc, char *argv[])
+ {
+ ClutterAlpha *alpha;
+ ClutterActor *stage;
+ ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff };
+ SuperOH *oh;
+ gint i;
+ GError *error;
+ ClutterActor *real_hand, *tmp;
+ ClutterColor clr = { 0xff, 0xff, 0x00, 0xff };
+
+ error = NULL;
+
+ clutter_init_with_args (&argc, &argv,
+ NULL,
+ super_oh_entries,
+ NULL,
+ &error);
+ if (error)
+ {
+ g_warning ("Unable to initialise Clutter:\n%s",
+ error->message);
+ g_error_free (error);
+
+ return EXIT_FAILURE;
+ }
+
+ stage = clutter_stage_get_default ();
+ clutter_actor_set_size (stage, 800, 600);
+
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Clone Test");
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ oh = g_new (SuperOH, 1);
+
+ /* Create a timeline to manage animation */
+ oh->timeline = clutter_timeline_new (360, 60);
+ clutter_timeline_set_loop (oh->timeline, TRUE);
+
+ /* fire a callback for frame change */
+ g_signal_connect (oh->timeline, "new-frame", G_CALLBACK (frame_cb), oh);
+
+ /* Set up some behaviours to handle scaling */
++ alpha = clutter_alpha_new_with_func (oh->timeline, my_sine_wave, NULL, NULL);
+
+ oh->scaler_1 = clutter_behaviour_scale_new (alpha, 0.5, 0.5, 1.0, 1.0);
+ oh->scaler_2 = clutter_behaviour_scale_new (alpha, 1.0, 1.0, 0.5, 0.5);
+
+ tmp = clutter_texture_new_from_file ("redhand.png", &error);
+ if (tmp == NULL)
+ {
+ g_error ("image load failed: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ clutter_actor_set_size (tmp, 300, 500);
+
+ real_hand = clutter_group_new ();
+ clutter_container_add_actor (CLUTTER_CONTAINER (real_hand), tmp);
+ tmp = clutter_rectangle_new_with_color (&clr);
+ clutter_actor_set_size (tmp, 100, 100);
+ clutter_container_add_actor (CLUTTER_CONTAINER (real_hand), tmp);
+ clutter_actor_set_scale (real_hand, 0.5, 0.5);
+ oh->real_hand = real_hand;
+
+ /* Now stick the group we want to clone into another group with a custom
+ * opacity to verify that the clones don't traverse this parent when
+ * calculating their opacity. */
+ tmp = clutter_group_new ();
+ clutter_actor_set_opacity (tmp, 0x80);
+ clutter_container_add_actor (CLUTTER_CONTAINER (tmp), real_hand);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), tmp);
+
+ /* create a new group to hold multiple actors in a group */
+ oh->group = clutter_group_new();
+
+ oh->hand = g_new (ClutterActor*, n_hands);
+
+ oh->stage_width = clutter_actor_get_width (stage);
+ oh->stage_height = clutter_actor_get_height (stage);
+ oh->radius = (oh->stage_width + oh->stage_height)
+ / n_hands;
+
+ for (i = 0; i < n_hands; i++)
+ {
+ gint x, y, w, h;
+
+ /* Create a texture from file, then clone in to same resources */
+ oh->hand[i] = clutter_clone_new (real_hand);
+ clutter_actor_set_size (oh->hand[i], 200, 213);
+
+ /* Place around a circle */
+ w = clutter_actor_get_width (oh->hand[0]);
+ h = clutter_actor_get_height (oh->hand[0]);
+
+ x = oh->stage_width / 2
+ + oh->radius
+ * cos (i * G_PI / (n_hands / 2))
+ - w / 2;
+
+ y = oh->stage_height / 2
+ + oh->radius
+ * sin (i * G_PI / (n_hands / 2))
+ - h / 2;
+
+ clutter_actor_set_position (oh->hand[i], x, y);
+
+ clutter_actor_move_anchor_point_from_gravity (oh->hand[i],
+ CLUTTER_GRAVITY_CENTER);
+
+ /* Add to our group group */
+ clutter_container_add_actor (CLUTTER_CONTAINER (oh->group), oh->hand[i]);
+
+ if (i % 2)
+ clutter_behaviour_apply (oh->scaler_1, oh->hand[i]);
+ else
+ clutter_behaviour_apply (oh->scaler_2, oh->hand[i]);
+ }
+
+ /* Add the group to the stage */
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), oh->group);
+
+ /* Show everying */
+ clutter_actor_show (stage);
+
+ g_signal_connect (stage, "button-press-event",
+ G_CALLBACK (input_cb),
+ oh);
+ g_signal_connect (stage, "key-release-event",
+ G_CALLBACK (input_cb),
+ oh);
+
+ /* and start it */
+ clutter_timeline_start (oh->timeline);
+
+ clutter_main ();
+
+ /* clean up */
+ g_object_unref (oh->scaler_1);
+ g_object_unref (oh->scaler_2);
+ g_object_unref (oh->timeline);
+ g_free (oh->hand);
+ g_free (oh);
+
+ return EXIT_SUCCESS;
+ }
stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 800, 600);
- clutter_stage_set_title (CLUTTER_STAGE (stage), "Actors Test");
- clutter_stage_set_color (CLUTTER_STAGE (stage),
- &stage_color);
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Clone Test");
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
- oh = g_new(SuperOH, 1);
+ oh = g_new (SuperOH, 1);
+ oh->stage = stage;
/* Create a timeline to manage animation */
- timeline = clutter_timeline_new (360, 60); /* num frames, fps */
- g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */
+ oh->timeline = clutter_timeline_new (360, 60);
+ clutter_timeline_set_loop (oh->timeline, TRUE);
/* fire a callback for frame change */
- g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), oh);
+ g_signal_connect (oh->timeline, "new-frame", G_CALLBACK (frame_cb), oh);
/* Set up some behaviours to handle scaling */
- alpha = clutter_alpha_new_with_func (timeline, my_sine_wave, NULL, NULL);
- alpha = clutter_alpha_new_with_func (oh->timeline, clutter_sine_func,
- NULL, NULL);
++ alpha = clutter_alpha_new_with_func (oh->timeline, my_sine_wave, NULL, NULL);
- scaler_1 = clutter_behaviour_scale_new (alpha,
- 0.5, 0.5,
- 1.0, 1.0);
+ oh->scaler_1 = clutter_behaviour_scale_new (alpha, 0.5, 0.5, 1.0, 1.0);
+ oh->scaler_2 = clutter_behaviour_scale_new (alpha, 1.0, 1.0, 0.5, 0.5);
- scaler_2 = clutter_behaviour_scale_new (alpha,
- 1.0, 1.0,
- 0.5, 0.5);
+ real_hand = clutter_texture_new_from_file ("redhand.png", &error);
+ if (real_hand == NULL)
+ {
+ g_error ("image load failed: %s", error->message);
+ return EXIT_FAILURE;
+ }
/* create a new group to hold multiple actors in a group */
oh->group = clutter_group_new();