[clutter/wip/correct-opacity: 3/8] clutter-effect: Add clutter_effect_queue_rerun



commit 00b733a85ad4c7781166ffce61c44f15116fa6ba
Author: Neil Roberts <neil linux intel com>
Date:   Mon Feb 28 12:16:55 2011 +0000

    clutter-effect: Add clutter_effect_queue_rerun
    
    This adds a new public function to queue a rerun of an effect. If
    nothing else queues a redraw then when the effect's actor is painted
    the effect will be run without the CLUTTER_EFFECT_RUN_ACTOR_DIRTY
    flag. This allows parametrised offscreen effects to report that they
    need to redraw the image without having to redraw the underlying
    actor. This will be used to implement the 'transparency' effect of
    ClutterActor.
    
    If multiple redraws are queued with different effects then redrawing
    is started from the one that occurs last in the list of effects.
    
    Internally the function is a wrapper around the new function
    _clutter_actor_queue_redraw_full. This is intended to be the sole
    point of code for queuing redraws on an actor. It has parameters for
    the clip and the effect. The other two existing functions to queue a
    redraw (one with a clip and one without) now wrap around this function
    by passing a NULL effect.

 clutter/clutter-actor-private.h            |    5 +
 clutter/clutter-actor.c                    |  310 +++++++++++++++++-----------
 clutter/clutter-effect.c                   |   60 ++++++
 clutter/clutter-effect.h                   |    2 +
 doc/reference/clutter/clutter-sections.txt |    1 +
 5 files changed, 261 insertions(+), 117 deletions(-)
---
diff --git a/clutter/clutter-actor-private.h b/clutter/clutter-actor-private.h
index 372f729..e96540d 100644
--- a/clutter/clutter-actor-private.h
+++ b/clutter/clutter-actor-private.h
@@ -147,6 +147,11 @@ void _clutter_actor_set_has_pointer (ClutterActor *self,
 void _clutter_actor_queue_redraw_with_clip   (ClutterActor              *self,
                                               ClutterRedrawFlags         flags,
                                               ClutterPaintVolume        *clip_volume);
+void _clutter_actor_queue_redraw_full        (ClutterActor              *self,
+                                              ClutterRedrawFlags         flags,
+                                              ClutterPaintVolume        *volume,
+                                              ClutterEffect             *effect);
+
 ClutterPaintVolume *_clutter_actor_get_queue_redraw_clip (ClutterActor *self);
 void _clutter_actor_set_queue_redraw_clip     (ClutterActor             *self,
                                                ClutterPaintVolume *clip_volume);
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 5395faf..7dcaf7e 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -472,7 +472,14 @@ struct _ClutterActorPrivate
   ClutterMetaGroup *effects;
 
   /* used when painting, to update the paint volume */
-  ClutterActorMeta *current_effect;
+  ClutterEffect *current_effect;
+
+  /* This is used to store an effect which needs to be redrawn. A
+     redraw can be queued to start from a particular effect. This is
+     used by parametrised effects that can cache an image of the
+     actor. If a parameter of the effect changes then it only needs to
+     redraw the cached image, not the actual actor */
+  ClutterEffect *effect_to_redraw;
 
   ClutterPaintVolume paint_volume;
 
@@ -1910,6 +1917,14 @@ clutter_actor_real_queue_redraw (ClutterActor *self,
 
   self->priv->propagated_one_redraw = TRUE;
 
+  /* If the queue redraw is coming from a child actor then we'll
+     assume the queued effect is no longer valid. If this actor has
+     had a redraw queued then that will mean it will instead redraw
+     the whole actor. If it hasn't had a redraw queued then it will
+     stay that way */
+  if (self != origin)
+    self->priv->effect_to_redraw = NULL;
+
   /* notify parents, if they are all visible eventually we'll
    * queue redraw on the stage, which queues the redraw idle.
    */
@@ -2854,7 +2869,7 @@ clutter_actor_continue_paint (ClutterActor *self)
     }
   else
     {
-      ClutterActorMeta *old_current_effect;
+      ClutterEffect *old_current_effect;
       ClutterEffectRunFlags run_flags = 0;
 
       /* Cache the current effect so that we can put it back before
@@ -2865,9 +2880,18 @@ clutter_actor_continue_paint (ClutterActor *self)
       priv->next_effect_to_paint = priv->next_effect_to_paint->next;
 
       if (priv->propagated_one_redraw)
-        run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
+        {
+          /* If there's an effect queued with this redraw then all
+             effects up to that one will be considered dirty. It is
+             expected the queued effect will paint the cached image
+             and not call clutter_actor_continue_paint again (although
+             it should work ok if it does) */
+          if (priv->effect_to_redraw == NULL ||
+              priv->current_effect != priv->effect_to_redraw)
+            run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
+        }
 
-      _clutter_effect_run (CLUTTER_EFFECT (priv->current_effect), run_flags);
+      _clutter_effect_run (priv->current_effect, run_flags);
 
       priv->current_effect = old_current_effect;
     }
@@ -5172,38 +5196,60 @@ _clutter_actor_finish_queue_redraw (ClutterActor *self,
   priv->queue_redraw_entry = NULL;
 }
 
-/**
- * clutter_actor_queue_redraw:
- * @self: A #ClutterActor
- *
- * Queues up a redraw of an actor and any children. The redraw occurs
- * once the main loop becomes idle (after the current batch of events
- * has been processed, roughly).
- *
- * Applications rarely need to call this, as redraws are handled
- * automatically by modification functions.
- *
- * This function will not do anything if @self is not visible, or
- * if the actor is inside an invisible part of the scenegraph.
- *
- * Also be aware that painting is a NOP for actors with an opacity of
- * 0
- *
- * When you are implementing a custom actor you must queue a redraw
- * whenever some private state changes that will affect painting or
- * picking of your actor.
- */
+static void
+_clutter_actor_get_allocation_clip (ClutterActor *self,
+                                    ClutterActorBox *clip)
+{
+  ClutterActorBox allocation;
+
+  /* XXX: we don't care if we get an out of date allocation here
+   * because clutter_actor_queue_redraw_with_clip knows to ignore
+   * the clip if the actor's allocation is invalid.
+   *
+   * This is noted because clutter_actor_get_allocation_box does some
+   * unnecessary work to support buggy code with a comment suggesting
+   * that it could be changed later which would be good for this use
+   * case!
+   */
+  clutter_actor_get_allocation_box (self, &allocation);
+
+  /* NB: clutter_actor_queue_redraw_with_clip expects a box in the
+   * actor's own coordinate space but the allocation is in parent
+   * coordinates */
+  clip->x1 = 0;
+  clip->y1 = 0;
+  clip->x2 = allocation.x2 - allocation.x1;
+  clip->y2 = allocation.y2 - allocation.y1;
+}
+
 void
-clutter_actor_queue_redraw (ClutterActor *self)
+_clutter_actor_queue_redraw_full (ClutterActor       *self,
+                                  ClutterRedrawFlags  flags,
+                                  ClutterPaintVolume *volume,
+                                  ClutterEffect      *effect)
 {
+  ClutterPaintVolume allocation_pv;
+  ClutterActorPrivate *priv;
+  ClutterPaintVolume *pv;
+  gboolean should_free_pv;
   ClutterActor *stage;
+  gboolean was_dirty;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+  priv = self->priv;
 
   /* Here's an outline of the actor queue redraw mechanism:
    *
-   * The process starts either here or in
-   * _clutter_actor_queue_redraw_with_clip.
+   * The process starts in one of the following two functions which
+   * are wrappers for this function:
+   * clutter_actor_queue_redraw
+   * _clutter_actor_queue_redraw_with_clip
+   *
+   * additionally, an effect can queue a redraw by wrapping this
+   * function in clutter_effect_queue_rerun
    *
-   * These functions queue an entry in a list associated with the
+   * This functions queues an entry in a list associated with the
    * stage which is a list of actors that queued a redraw while
    * updating the timelines, performing layouting and processing other
    * mainloop sources before the next paint starts.
@@ -5232,8 +5278,9 @@ clutter_actor_queue_redraw (ClutterActor *self)
    * difference to performance.
    *
    * So the control flow goes like this:
-   * clutter_actor_queue_redraw and
-   * _clutter_actor_queue_redraw_with_clip
+   * One of clutter_actor_queue_redraw,
+   *        _clutter_actor_queue_redraw_with_clip
+   *     or clutter_effect_queue_rerun
    *
    * then control moves to:
    *   _clutter_stage_queue_actor_redraw
@@ -5261,44 +5308,125 @@ clutter_actor_queue_redraw (ClutterActor *self)
    * paint.
    */
 
-  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+  stage = _clutter_actor_get_stage_internal (self);
 
   /* Ignore queuing a redraw for actors not descended from a stage */
-  stage = _clutter_actor_get_stage_internal (self);
   if (stage == NULL)
     return;
 
-  self->priv->queue_redraw_entry =
-    _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
-                                       self->priv->queue_redraw_entry,
-                                       self,
-                                       NULL);
-}
+  if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
+    {
+      ClutterActorBox allocation_clip;
+      ClutterVertex origin;
 
-static void
-_clutter_actor_get_allocation_clip (ClutterActor *self,
-                                    ClutterActorBox *clip)
-{
-  ClutterActorBox allocation;
+      /* If the actor doesn't have a valid allocation then we will
+       * queue a full stage redraw. */
+      if (priv->needs_allocation)
+        {
+          /* NB: NULL denotes an undefined clip which will result in a
+           * full redraw... */
+          _clutter_actor_set_queue_redraw_clip (self, NULL);
+          _clutter_actor_signal_queue_redraw (self, self);
+          return;
+        }
 
-  /* XXX: we don't care if we get an out of date allocation here
-   * because clutter_actor_queue_redraw_with_clip knows to ignore
-   * the clip if the actor's allocation is invalid.
-   *
-   * This is noted because clutter_actor_get_allocation_box does some
-   * unnecessary work to support buggy code with a comment suggesting
-   * that it could be changed later which would be good for this use
-   * case!
-   */
-  clutter_actor_get_allocation_box (self, &allocation);
+      _clutter_paint_volume_init_static (&allocation_pv, self);
+      pv = &allocation_pv;
 
-  /* NB: clutter_actor_queue_redraw_with_clip expects a box in the
-   * actor's own coordinate space but the allocation is in parent
-   * coordinates */
-  clip->x1 = 0;
-  clip->y1 = 0;
-  clip->x2 = allocation.x2 - allocation.x1;
-  clip->y2 = allocation.y2 - allocation.y1;
+      _clutter_actor_get_allocation_clip (self, &allocation_clip);
+
+      origin.x = allocation_clip.x1;
+      origin.y = allocation_clip.y1;
+      origin.z = 0;
+      clutter_paint_volume_set_origin (pv, &origin);
+      clutter_paint_volume_set_width (pv,
+                                      allocation_clip.x2 - allocation_clip.x1);
+      clutter_paint_volume_set_height (pv,
+                                       allocation_clip.y2 -
+                                       allocation_clip.y1);
+      should_free_pv = TRUE;
+    }
+  else
+    {
+      pv = volume;
+      should_free_pv = FALSE;
+    }
+
+  was_dirty = priv->queue_redraw_entry != NULL;
+
+  _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
+                                     priv->queue_redraw_entry,
+                                     self,
+                                     pv);
+
+  if (should_free_pv)
+    clutter_paint_volume_free (pv);
+
+  /* If this is the first redraw queued then we can directly use the
+     effect parameter */
+  if (!was_dirty)
+    priv->effect_to_redraw = effect;
+  /* Otherwise we need to merge it with the existing effect parameter */
+  else if (effect)
+    {
+      /* If there's already an effect then we need to use whichever is
+         later in the chain of actors. Otherwise a full redraw has
+         already been queued on the actor so we need to ignore the
+         effect parameter */
+      if (priv->effect_to_redraw)
+        {
+          if (priv->effects == NULL)
+            g_warning ("Redraw queued with an effect that is "
+                       "not applied to the actor");
+          else
+            {
+              const GList *l;
+
+              for (l = _clutter_meta_group_peek_metas (priv->effects);
+                   l != NULL;
+                   l = l->next)
+                {
+                  if (l->data == priv->effect_to_redraw ||
+                      l->data == effect)
+                    priv->effect_to_redraw = l->data;
+                }
+            }
+        }
+    }
+  else
+    /* If no effect is specified then we need to redraw the whole
+       actor */
+    priv->effect_to_redraw = NULL;
+}
+
+/**
+ * clutter_actor_queue_redraw:
+ * @self: A #ClutterActor
+ *
+ * Queues up a redraw of an actor and any children. The redraw occurs
+ * once the main loop becomes idle (after the current batch of events
+ * has been processed, roughly).
+ *
+ * Applications rarely need to call this, as redraws are handled
+ * automatically by modification functions.
+ *
+ * This function will not do anything if @self is not visible, or
+ * if the actor is inside an invisible part of the scenegraph.
+ *
+ * Also be aware that painting is a NOP for actors with an opacity of
+ * 0
+ *
+ * When you are implementing a custom actor you must queue a redraw
+ * whenever some private state changes that will affect painting or
+ * picking of your actor.
+ */
+void
+clutter_actor_queue_redraw (ClutterActor *self)
+{
+  _clutter_actor_queue_redraw_full (self,
+                                    0, /* flags */
+                                    NULL, /* clip volume */
+                                    NULL /* effect */);
 }
 
 /*
@@ -5343,62 +5471,10 @@ _clutter_actor_queue_redraw_with_clip (ClutterActor       *self,
                                        ClutterRedrawFlags  flags,
                                        ClutterPaintVolume *volume)
 {
-  ClutterPaintVolume allocation_pv;
-  ClutterPaintVolume *pv;
-  gboolean should_free_pv;
-  ClutterActor *stage;
-
-  g_return_if_fail (CLUTTER_IS_ACTOR (self));
-
-  if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
-    {
-      ClutterActorBox allocation_clip;
-      ClutterVertex origin;
-
-      /* If the actor doesn't have a valid allocation then we will
-       * queue a full stage redraw. */
-      if (self->priv->needs_allocation)
-        {
-          /* NB: NULL denotes an undefined clip which will result in a
-           * full redraw... */
-          _clutter_actor_set_queue_redraw_clip (self, NULL);
-          _clutter_actor_signal_queue_redraw (self, self);
-          return;
-        }
-
-      _clutter_paint_volume_init_static (&allocation_pv, self);
-      pv = &allocation_pv;
-
-      _clutter_actor_get_allocation_clip (self, &allocation_clip);
-
-      origin.x = allocation_clip.x1;
-      origin.y = allocation_clip.y1;
-      origin.z = 0;
-      clutter_paint_volume_set_origin (pv, &origin);
-      clutter_paint_volume_set_width (pv,
-                                      allocation_clip.x2 - allocation_clip.x1);
-      clutter_paint_volume_set_height (pv,
-                                       allocation_clip.y2 -
-                                       allocation_clip.y1);
-      should_free_pv = TRUE;
-    }
-  else
-    {
-      pv = volume;
-      should_free_pv = FALSE;
-    }
-
-  /* Ignore queuing a redraw for actors not descended from a stage */
-  stage = _clutter_actor_get_stage_internal (self);
-
-  if (stage != NULL)
-    _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
-                                       self->priv->queue_redraw_entry,
-                                       self,
-                                       pv);
-
-  if (should_free_pv)
-    clutter_paint_volume_free (pv);
+  _clutter_actor_queue_redraw_full (self,
+                                    flags, /* flags */
+                                    volume, /* clip volume */
+                                    NULL /* effect */);
 }
 
 static void
diff --git a/clutter/clutter-effect.c b/clutter/clutter-effect.c
index 95550d0..66acb59 100644
--- a/clutter/clutter-effect.c
+++ b/clutter/clutter-effect.c
@@ -203,6 +203,7 @@
 #include "clutter-enum-types.h"
 #include "clutter-marshal.h"
 #include "clutter-private.h"
+#include "clutter-actor-private.h"
 
 G_DEFINE_ABSTRACT_TYPE (ClutterEffect,
                         clutter_effect,
@@ -316,3 +317,62 @@ _clutter_effect_get_paint_volume (ClutterEffect      *effect,
 
   return CLUTTER_EFFECT_GET_CLASS (effect)->get_paint_volume (effect, volume);
 }
+
+/**
+ * clutter_effect_queue_rerun:
+ * @effect: A #ClutterEffect which needs redrawing
+ *
+ * Queues a rerun of the effect. The effect can detect when the â??runâ??
+ * method is called as a result of this function because it will not
+ * have the %CLUTTER_EFFECT_RUN_ACTOR_DIRTY flag set. In that case the
+ * effect is free to assume that the actor has not changed its
+ * appearance since the last time it was painted so it doesn't need to
+ * call clutter_actor_continue_paint() if it can draw a cached
+ * image. This is mostly intended for effects that are using a
+ * %CoglOffscreen to redirect the actor (such as
+ * %ClutterOffscreenEffect). In that case the effect can save a bit of
+ * rendering time by painting the cached texture without causing the
+ * entire actor to be painted.
+ *
+ * This function can be used by effects that have their own animatable
+ * parameters. For example, an effect which adds a varying degree of a
+ * red tint to an actor by redirecting it through a CoglOffscreen
+ * might have a property to specify the level of tint. When this value
+ * changes, the underlying actor doesn't need to be redrawn so the
+ * effect can call clutter_effect_queue_rerun() to make sure the
+ * effect is repainted.
+ *
+ * Note however that modifying the position of the parent of an actor
+ * may change the appearance of the actor because its transformation
+ * matrix would change. In this case a redraw wouldn't be queued on
+ * the actor itself so the %CLUTTER_EFFECT_RUN_ACTOR_DIRTY would still
+ * not be set. The effect can detect this case by keeping track of the
+ * last modelview matrix that was used to render the actor and
+ * veryifying that it remains the same in the next paint.
+ *
+ * Any other effects that are layered on top of the passed in effect
+ * will still be passed the %CLUTTER_EFFECT_RUN_ACTOR_DIRTY flag. If
+ * anything queues a redraw on the actor without specifying an effect
+ * or with an effect that is lower in the chain of effects than this
+ * one then that will override this call. In that case this effect
+ * will instead be called with the %CLUTTER_EFFECT_RUN_ACTOR_DIRTY
+ * flag set.
+ *
+ * Since: 1.8
+ */
+void
+clutter_effect_queue_rerun (ClutterEffect *effect)
+{
+  ClutterActor *actor;
+
+  g_return_if_fail (CLUTTER_IS_EFFECT (effect));
+
+  actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+
+  /* If the effect has no actor then nothing needs to be done */
+  if (actor != NULL)
+    _clutter_actor_queue_redraw_full (actor,
+                                      0, /* flags */
+                                      NULL, /* clip volume */
+                                      effect /* effect */);
+}
diff --git a/clutter/clutter-effect.h b/clutter/clutter-effect.h
index 1e2c124..f389830 100644
--- a/clutter/clutter-effect.h
+++ b/clutter/clutter-effect.h
@@ -105,6 +105,8 @@ struct _ClutterEffectClass
 
 GType clutter_effect_get_type (void) G_GNUC_CONST;
 
+void           clutter_effect_queue_rerun          (ClutterEffect *effect);
+
 /*
  * ClutterActor API
  */
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 0c91b93..93c09d4 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -2469,6 +2469,7 @@ ClutterClickActionPrivate
 ClutterEffect
 ClutterEffectClass
 ClutterEffectRunFlags
+clutter_effect_queue_rerun
 <SUBSECTION Standard>
 CLUTTER_TYPE_EFFECT
 CLUTTER_EFFECT



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