[clutter/wip/correct-opacity: 1/9] clutter-effect: Add a 'run' virtual



commit 9228dff2dba4648e2cfcb9ff67e08c4418132947
Author: Neil Roberts <neil linux intel com>
Date:   Wed Mar 2 16:55:10 2011 +0000

    clutter-effect: Add a 'run' virtual
    
    This adds a new virtual to ClutterEffect which is intended to be a
    more flexible replacement for the pre and post_paint functions. The
    implementation of a run virtual would look something like this:
    
    void
    effect_run (ClutterEffect *effect,
                ClutterEffectRunFlags flags)
    {
      /* Set up state */
      /* ... */
    
      /* Chain to the next item in the paint sequence */
      clutter_actor_continue_paint (priv->actor);
    
      /* Clean up state */
      /* ... */
    }
    
    ClutterActor now just calls this virtual instead of the pre_paint and
    post_paint functions. It keeps track of the next effect in the list so
    that it knows what to do when clutter_actor_continue_paint is
    called. clutter_actor_continue_paint is a new function added just for
    implementing effects.
    
    The default implementation of the run virtual just calls pre_paint and
    post_paint so that existing effects will continue to work.
    
    An effect is allowed to conditionally skip calling
    clutter_actor_continue_paint(). This is useful to implement effects
    that cache the image of an actor. The flags parameter can be used to
    determine if the actor is dirty since the last paint. ClutterActor
    sets this flag whenever propagated_one_redraw is TRUE which means that
    a redraw for this actor or one of its children was queued.

 clutter/clutter-actor.c                    |  139 +++++++++++++++-------------
 clutter/clutter-actor.h                    |    1 +
 clutter/clutter-effect-private.h           |    2 +
 clutter/clutter-effect.c                   |  124 ++++++++++++++++++-------
 clutter/clutter-effect.h                   |   19 ++++-
 doc/reference/clutter/clutter-sections.txt |    2 +
 6 files changed, 189 insertions(+), 98 deletions(-)
---
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 92990bb..cff890f 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -471,6 +471,11 @@ struct _ClutterActorPrivate
 
   ClutterPaintVolume paint_volume;
 
+  /* This is used when painting effects to implement the
+     clutter_actor_continue_paint() function. It points to the node in
+     the list of effects that is next in the chain */
+  const GList *next_effect_to_paint;
+
   ClutterActorBox last_paint_box;
 
   ClutterStageQueueRedrawEntry *queue_redraw_entry;
@@ -2300,60 +2305,6 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self,
   CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix);
 }
 
-static gboolean
-_clutter_actor_effects_pre_paint (ClutterActor *self)
-{
-  ClutterActorPrivate *priv = self->priv;
-  const GList *effects, *l;
-  gboolean was_pre_painted = FALSE;
-
-  priv->current_effect = NULL;
-
-  effects = _clutter_meta_group_peek_metas (priv->effects);
-  for (l = effects; l != NULL; l = l->next)
-    {
-      ClutterEffect *effect = l->data;
-      ClutterActorMeta *meta = l->data;
-
-      if (!clutter_actor_meta_get_enabled (meta))
-        continue;
-
-      priv->current_effect = l->data;
-
-      was_pre_painted |= _clutter_effect_pre_paint (effect);
-    }
-
-  priv->current_effect = NULL;
-
-  return was_pre_painted;
-}
-
-static void
-_clutter_actor_effects_post_paint (ClutterActor *self)
-{
-  ClutterActorPrivate *priv = self->priv;
-  const GList *effects, *l;
-
-  priv->current_effect = NULL;
-
-  /* we walk the list backwards, to unwind the post-paint order */
-  effects = _clutter_meta_group_peek_metas (priv->effects);
-  for (l = g_list_last ((GList *) effects); l != NULL; l = l->prev)
-    {
-      ClutterEffect *effect = l->data;
-      ClutterActorMeta *meta = l->data;
-
-      if (!clutter_actor_meta_get_enabled (meta))
-        continue;
-
-      priv->current_effect = l->data;
-
-      _clutter_effect_post_paint (effect);
-    }
-
-  priv->current_effect = NULL;
-}
-
 /* Recursively applies the transforms associated with this actor and
  * its ancestors to the given matrix. Use NULL if you want this
  * to go all the way down to the stage.
@@ -2631,7 +2582,6 @@ clutter_actor_paint (ClutterActor *self)
 
   if (pick_mode == CLUTTER_PICK_NONE)
     {
-      gboolean effect_painted = FALSE;
       gboolean need_paint_box;
 
       CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter);
@@ -2685,17 +2635,20 @@ clutter_actor_paint (ClutterActor *self)
 	    goto done;
 	}
 
-      if (priv->effects != NULL)
-        effect_painted = _clutter_actor_effects_pre_paint (self);
-      else if (actor_has_shader_data (self))
-        clutter_actor_shader_pre_paint (self, FALSE);
+      if (priv->effects == NULL)
+        {
+          if (actor_has_shader_data (self))
+            clutter_actor_shader_pre_paint (self, FALSE);
+          priv->next_effect_to_paint = NULL;
+        }
+      else
+        priv->next_effect_to_paint =
+          _clutter_meta_group_peek_metas (priv->effects);
 
-      priv->propagated_one_redraw = FALSE;
-      g_signal_emit (self, actor_signals[PAINT], 0);
+      clutter_actor_continue_paint (self);
 
-      if (effect_painted)
-        _clutter_actor_effects_post_paint (self);
-      else if (actor_has_shader_data (self))
+      if (priv->effects == NULL &&
+          actor_has_shader_data (self))
         clutter_actor_shader_post_paint (self);
 
       if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
@@ -2726,6 +2679,64 @@ done:
   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
 }
 
+/**
+ * clutter_actor_continue_paint:
+ * @self: A #ClutterActor
+ *
+ * Run the next stage of the paint sequence. This function should only
+ * be called within the implementation of the â??runâ?? virtual of a
+ * #ClutterEffect. It will cause the run method of the next effect to
+ * be applied, or it will paint the actual actor if the current effect
+ * is the last effect in the chain.
+ *
+ * Since: 1.8
+ */
+void
+clutter_actor_continue_paint (ClutterActor *self)
+{
+  ClutterActorPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+  /* This should only be called from with in the â??runâ?? implementation
+     of a ClutterEffect */
+  g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self));
+
+  priv = self->priv;
+
+  /* Skip any effects that are disabled */
+  while (priv->next_effect_to_paint &&
+         !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
+    priv->next_effect_to_paint = priv->next_effect_to_paint->next;
+
+  /* If this has come from the last effect then we'll just paint the
+     actual actor */
+  if (priv->next_effect_to_paint == NULL)
+    {
+      priv->propagated_one_redraw = FALSE;
+
+      g_signal_emit (self, actor_signals[PAINT], 0);
+    }
+  else
+    {
+      ClutterActorMeta *old_current_effect;
+      ClutterEffectRunFlags run_flags = 0;
+
+      /* Cache the current effect so that we can put it back before
+         returning */
+      old_current_effect = priv->current_effect;
+
+      priv->current_effect = priv->next_effect_to_paint->data;
+      priv->next_effect_to_paint = priv->next_effect_to_paint->next;
+
+      if (priv->propagated_one_redraw)
+        run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
+
+      _clutter_effect_run (CLUTTER_EFFECT (priv->current_effect), run_flags);
+
+      priv->current_effect = old_current_effect;
+    }
+}
+
 /* internal helper function set the rotation angle without affecting
    the center point
  */
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index 6b05d13..d2697dc 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -314,6 +314,7 @@ void                  clutter_actor_unrealize                 (ClutterActor
 void                  clutter_actor_map                       (ClutterActor          *self);
 void                  clutter_actor_unmap                     (ClutterActor          *self);
 void                  clutter_actor_paint                     (ClutterActor          *self);
+void                  clutter_actor_continue_paint            (ClutterActor          *self);
 void                  clutter_actor_queue_redraw              (ClutterActor          *self);
 
 void                  clutter_actor_queue_relayout            (ClutterActor          *self);
diff --git a/clutter/clutter-effect-private.h b/clutter/clutter-effect-private.h
index 8b0153c..94a69ff 100644
--- a/clutter/clutter-effect-private.h
+++ b/clutter/clutter-effect-private.h
@@ -9,6 +9,8 @@ gboolean        _clutter_effect_pre_paint               (ClutterEffect      *eff
 void            _clutter_effect_post_paint              (ClutterEffect      *effect);
 gboolean        _clutter_effect_get_paint_volume        (ClutterEffect      *effect,
                                                          ClutterPaintVolume *volume);
+void            _clutter_effect_run                     (ClutterEffect      *effect,
+                                                         ClutterEffectRunFlags flags);
 
 G_END_DECLS
 
diff --git a/clutter/clutter-effect.c b/clutter/clutter-effect.c
index cf8775d..95550d0 100644
--- a/clutter/clutter-effect.c
+++ b/clutter/clutter-effect.c
@@ -38,34 +38,74 @@
  *
  * <refsect2 id="ClutterEffect-implementation">
  *   <title>Implementing a ClutterEffect</title>
- *   <para>Creating a sub-class of #ClutterEffect requires the implementation
- *   of two virtual functions:</para>
+ *   <para>
+ *     Creating a sub-class of #ClutterEffect requires overriding the
+ *     â??runâ?? method. The implementation of the function should look
+ *     something like this:
+ *   </para>
+ *   <programlisting>
+ * void effect_run (ClutterEffect *effect, ClutterEffectRunFlags flags)
+ * {
+ *   /&ast; Set up initialisation of the paint such as binding a
+ *      CoglOffscreen or other operations &ast;/
+ *
+ *   /&ast; Chain to the next item in the paint sequence. This will either call
+ *      â??runâ?? on the next effect or just paint the actor if this is
+ *      the last effect. &ast;/
+ *   ClutterActor *actor =
+ *     clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+ *   clutter_actor_continue_paint (actor);
+ *
+ *   /&ast; perform any cleanup of state, such as popping the
+ *      CoglOffscreen &ast;/
+ * }
+ *   </programlisting>
+ *   <para>
+ *     The effect can optionally avoid calling
+ *     clutter_actor_continue_paint() to skip any further stages of
+ *     the paint sequence. This is useful for example if the effect
+ *     contains a cached image of the actor. In that case it can
+ *     optimise painting by avoiding the actor paint and instead
+ *     painting the cached image. The %CLUTTER_EFFECT_RUN_ACTOR_DIRTY
+ *     flag is useful in this case. Clutter will set this flag when a
+ *     redraw has been queued on the actor since it was last
+ *     painted. The effect can use this information to decide if the
+ *     cached image is still valid.
+ *   </para>
+ *   <para>
+ *     The â??runâ?? virtual was added in Clutter 1.8. Prior to that there
+ *     were two separate functions as follows.
+ *   </para>
  *   <itemizedlist>
  *     <listitem><simpara><function>pre_paint()</function>, which is called
  *     before painting the #ClutterActor.</simpara></listitem>
  *     <listitem><simpara><function>post_paint()</function>, which is called
  *     after painting the #ClutterActor.</simpara></listitem>
  *   </itemizedlist>
- *   <para>The <function>pre_paint()</function> function should be used to set
+ *   <para>The <function>pre_paint()</function> function was used to set
  *   up the #ClutterEffect right before the #ClutterActor's paint
  *   sequence. This function can fail, and return %FALSE; in that case, no
  *   <function>post_paint()</function> invocation will follow.</para>
- *   <para>The <function>post_paint()</function> function is called after the
+ *   <para>The <function>post_paint()</function> function was called after the
  *   #ClutterActor's paint sequence.</para>
- *   <para>The <function>pre_paint()</function> phase could be seen as a custom
- *   handler to the #ClutterActor::paint signal, while the
- *   <function>post_paint()</function> phase could be seen as a custom handler
- *   to the #ClutterActor::paint signal connected using
- *   g_signal_connect_after().</para>
+ *   <para>
+ *     With these two functions it is not possible to skip the rest of
+ *     the paint sequence. The default implementation of the â??runâ??
+ *     virtual calls pre_paint(), clutter_actor_continue_paint() and
+ *     then post_paint() so that existing actors that aren't using the
+ *     run virtual will continue to work. New actors using the run
+ *     virtual do not need to implement pre or post paint.
+ *   </para>
  *   <example id="ClutterEffect-example">
  *     <title>A simple ClutterEffect implementation</title>
- *     <para>The example below creates two rectangles: one will be painted
- *     "behind" the actor, while another will be painted "on top" of the actor.
- *     The <function>set_actor()</function> implementation will create the two
- *     materials used for the two different rectangles; the
- *     <function>pre_paint()</function> function will paint the first material
- *     using cogl_rectangle(), while the <function>post_paint()</function>
- *     phase will paint the second material.</para>
+ *     <para>The example below creates two rectangles: one will be
+ *     painted "behind" the actor, while another will be painted "on
+ *     top" of the actor.  The <function>set_actor()</function>
+ *     implementation will create the two materials used for the two
+ *     different rectangles; the <function>run()</function> function
+ *     will paint the first material using cogl_rectangle(), before
+ *     continuing and then it will paint paint the second material
+ *     after.</para>
  *     <programlisting>
  *  typedef struct {
  *    ClutterEffect parent_instance;
@@ -116,31 +156,19 @@
  *  }
  *
  *  static gboolean
- *  my_effect_pre_paint (ClutterEffect *effect)
+ *  my_effect_run (ClutterEffect *effect)
  *  {
  *    MyEffect *self = MY_EFFECT (effect);
  *    gfloat width, height;
  *
- *    /&ast; If we were disabled we don't need to paint anything &ast;/
- *    if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
- *      return FALSE;
- *
  *    clutter_actor_get_size (self-&gt;actor, &amp;width, &amp;height);
  *
  *    /&ast; Paint the first rectangle in the upper left quadrant &ast;/
  *    cogl_set_source (self-&gt;rect_1);
  *    cogl_rectangle (0, 0, width / 2, height / 2);
  *
- *    return TRUE;
- *  }
- *
- *  static void
- *  my_effect_post_paint (ClutterEffect *effect)
- *  {
- *    MyEffect *self = MY_EFFECT (effect);
- *    gfloat width, height;
- *
- *    clutter_actor_get_size (self-&gt;actor, &amp;width, &amp;height);
+ *    /&ast; Continue to the rest of the paint sequence &ast;/
+ *    clutter_actor_continue_paint (self-&gt;actor);
  *
  *    /&ast; Paint the second rectangle in the lower right quadrant &ast;/
  *    cogl_set_source (self-&gt;rect_2);
@@ -154,8 +182,7 @@
  *
  *    meta_class-&gt;set_actor = my_effect_set_actor;
  *
- *    klass-&gt;pre_paint = my_effect_pre_paint;
- *    klass-&gt;post_paint = my_effect_post_paint;
+ *    klass-&gt;run = my_effect_run;
  *  }
  *     </programlisting>
  *   </example>
@@ -200,6 +227,27 @@ clutter_effect_real_get_paint_volume (ClutterEffect      *effect,
 }
 
 static void
+clutter_effect_real_run (ClutterEffect         *effect,
+                         ClutterEffectRunFlags  flags)
+{
+  ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect);
+  ClutterActor *actor;
+  gboolean pre_paint_succeeded;
+
+  /* The default implementation provides a compatibility wrapper for
+     effects that haven't migrated to use the 'run' virtual yet. This
+     just calls the old pre and post virtuals before chaining on */
+
+  pre_paint_succeeded = _clutter_effect_pre_paint (effect);
+
+  actor = clutter_actor_meta_get_actor (actor_meta);
+  clutter_actor_continue_paint (actor);
+
+  if (pre_paint_succeeded)
+    _clutter_effect_post_paint (effect);
+}
+
+static void
 clutter_effect_notify (GObject    *gobject,
                        GParamSpec *pspec)
 {
@@ -226,6 +274,7 @@ clutter_effect_class_init (ClutterEffectClass *klass)
   klass->pre_paint = clutter_effect_real_pre_paint;
   klass->post_paint = clutter_effect_real_post_paint;
   klass->get_paint_volume = clutter_effect_real_get_paint_volume;
+  klass->run = clutter_effect_real_run;
 }
 
 static void
@@ -249,6 +298,15 @@ _clutter_effect_post_paint (ClutterEffect *effect)
   CLUTTER_EFFECT_GET_CLASS (effect)->post_paint (effect);
 }
 
+void
+_clutter_effect_run (ClutterEffect         *effect,
+                     ClutterEffectRunFlags  flags)
+{
+  g_return_if_fail (CLUTTER_IS_EFFECT (effect));
+
+  CLUTTER_EFFECT_GET_CLASS (effect)->run (effect, flags);
+}
+
 gboolean
 _clutter_effect_get_paint_volume (ClutterEffect      *effect,
                                   ClutterPaintVolume *volume)
diff --git a/clutter/clutter-effect.h b/clutter/clutter-effect.h
index 422c89a..1e2c124 100644
--- a/clutter/clutter-effect.h
+++ b/clutter/clutter-effect.h
@@ -43,6 +43,20 @@ G_BEGIN_DECLS
 typedef struct _ClutterEffectClass      ClutterEffectClass;
 
 /**
+ * ClutterEffectRunFlags:
+ * @CLUTTER_EFFECT_RUN_ACTOR_DIRTY: The actor or one of its children
+ * has queued a redraw before this paint. This implies that the effect
+ * should call clutter_actor_continue_paint() to chain to the next
+ * effect and can not cache any results from a previous paint.
+ *
+ * Flags passed to the â??runâ?? method of #ClutterEffect.
+ */
+typedef enum
+{
+  CLUTTER_EFFECT_RUN_ACTOR_DIRTY = (1 << 0)
+} ClutterEffectRunFlags;
+
+/**
  * ClutterEffect:
  *
  * The #ClutterEffect structure contains only private data and should
@@ -61,6 +75,7 @@ struct _ClutterEffect
  * @pre_paint: virtual function
  * @post_paint: virtual function
  * @get_paint_volume: virtual function
+ * @run: virtual function
  *
  * The #ClutterEffectClass structure contains only private data
  *
@@ -78,8 +93,10 @@ struct _ClutterEffectClass
   gboolean (* get_paint_volume) (ClutterEffect      *effect,
                                  ClutterPaintVolume *volume);
 
+  void     (* run)              (ClutterEffect      *effect,
+                                 ClutterEffectRunFlags flags);
+
   /*< private >*/
-  void (* _clutter_effect2) (void);
   void (* _clutter_effect3) (void);
   void (* _clutter_effect4) (void);
   void (* _clutter_effect5) (void);
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 3bad594..0c91b93 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -291,6 +291,7 @@ clutter_actor_hide_all
 clutter_actor_realize
 clutter_actor_unrealize
 clutter_actor_paint
+clutter_actor_continue_paint
 clutter_actor_queue_redraw
 clutter_actor_queue_relayout
 clutter_actor_destroy
@@ -2467,6 +2468,7 @@ ClutterClickActionPrivate
 <FILE>clutter-effect</FILE>
 ClutterEffect
 ClutterEffectClass
+ClutterEffectRunFlags
 <SUBSECTION Standard>
 CLUTTER_TYPE_EFFECT
 CLUTTER_EFFECT



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]