mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 00:20:42 -05:00
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.
This commit is contained in:
parent
05a7419e11
commit
c3aa4d24bf
@ -476,6 +476,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;
|
||||
|
||||
/* NB: This volume isn't relative to this actor, it is in eye
|
||||
* coordinates so that it can remain valid after the actor changes.
|
||||
*/
|
||||
@ -2342,60 +2347,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.
|
||||
@ -2780,8 +2731,6 @@ clutter_actor_paint (ClutterActor *self)
|
||||
|
||||
if (pick_mode == CLUTTER_PICK_NONE)
|
||||
{
|
||||
gboolean effect_painted = FALSE;
|
||||
|
||||
CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter);
|
||||
|
||||
/* We save the current paint volume so that the next time the
|
||||
@ -2822,17 +2771,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))
|
||||
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))
|
||||
@ -2863,6 +2815,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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
* {
|
||||
* /* Set up initialisation of the paint such as binding a
|
||||
* CoglOffscreen or other operations */
|
||||
*
|
||||
* /* 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. */
|
||||
* ClutterActor *actor =
|
||||
* clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
|
||||
* clutter_actor_continue_paint (actor);
|
||||
*
|
||||
* /* perform any cleanup of state, such as popping the
|
||||
* CoglOffscreen */
|
||||
* }
|
||||
* </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;
|
||||
*
|
||||
* /* If we were disabled we don't need to paint anything */
|
||||
* if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
|
||||
* return FALSE;
|
||||
*
|
||||
* clutter_actor_get_size (self->actor, &width, &height);
|
||||
*
|
||||
* /* Paint the first rectangle in the upper left quadrant */
|
||||
* cogl_set_source (self->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->actor, &width, &height);
|
||||
* /* Continue to the rest of the paint sequence */
|
||||
* clutter_actor_continue_paint (self->actor);
|
||||
*
|
||||
* /* Paint the second rectangle in the lower right quadrant */
|
||||
* cogl_set_source (self->rect_2);
|
||||
@ -154,8 +182,7 @@
|
||||
*
|
||||
* meta_class->set_actor = my_effect_set_actor;
|
||||
*
|
||||
* klass->pre_paint = my_effect_pre_paint;
|
||||
* klass->post_paint = my_effect_post_paint;
|
||||
* klass->run = my_effect_run;
|
||||
* }
|
||||
* </programlisting>
|
||||
* </example>
|
||||
@ -199,6 +226,27 @@ clutter_effect_real_get_paint_volume (ClutterEffect *effect,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -42,6 +42,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:
|
||||
*
|
||||
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user