Merge branch 'generic-actor-clone'
authorEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 27 Jan 2009 16:12:30 +0000 (16:12 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 27 Jan 2009 16:14:35 +0000 (16:14 +0000)
* generic-actor-clone:
  Remove CloneTexture from the API
  [tests] Clean up the Clone interactive test
  Rename ActorClone to Clone/2
  Rename ActorClone to Clone/1
  Improves the unit test to verify more awkward scaling and some corresponding fixes
  Implements a generic ClutterActorClone that doesn't need fbos.

1  2 
clutter/Makefile.am
clutter/clutter-actor.c
clutter/clutter-clone.c
clutter/clutter-private.h
doc/reference/clutter/clutter-sections.txt
tests/interactive/Makefile.am
tests/interactive/test-actor-clone.c
tests/interactive/test-actors.c
tests/interactive/test-layout.c
tests/interactive/test-paint-wrapper.c

Simple merge
@@@ -7787,3 -7713,26 +7797,26 @@@ clutter_actor_create_pango_context (Clu
  
    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;
+ }
index 0000000,daeec15..0f76b4d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,326 +1,325 @@@
 -  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;
+ }
Simple merge
Simple merge
index 0000000,17f01d5..fc595ab
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,279 +1,288 @@@
 -  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;
+ }
@@@ -164,29 -153,32 +163,31 @@@ test_actors_main (int argc, char *argv[
    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();
Simple merge