clutter-actor: Add a 'has_overlaps' virtual

This adds a virtual to ClutterActor so that an actor subclass can
report whether it has overlapping primitives. ClutterActor uses this
to determine whether it needs to use ClutterFlattenEffect to implement
the opacity property. The default implementation of the virtual
returns TRUE which means that most actors will end up being redirected
offscreen when the opacity != 255. ClutterTexture and ClutterRectangle
override this to return FALSE because they should never need to be
redirected. ClutterClone overrides it to divert to the source.

The values for the ClutterOffscreenRedirect enum have changed to:

AUTOMATIC_FOR_OPACITY

 The actor will only be redirected if has_overlaps returns TRUE and
 the opacity is < 255

ALWAYS_FOR_OPACITY

 The actor will always be redirected if the opacity < 255 regardless
 of the return value of has_overlaps

ALWAYS

 The actor will always be redirected offscreen.

This means that the property can't be used to prevent the actor from
being redirected but only to increase the likelihood that it will be
redirected.

ClutterActor now adds and removes the flatten effect depending on
whether flattening is needed directly in clutter_actor_paint(). There
are new internal versions of add/remove_effect that don't queue a
redraw. This means that ClutterFlattenEffect is now just a no-op
subclass of ClutterOffscreen. It is only needed because
ClutterOffscreen is abstract. Removing the effect also makes it so
that the cached image will be freed as soon as an actor is repainted
without being flattened.
This commit is contained in:
Neil Roberts 2011-03-14 16:30:53 +00:00
parent 2a09a04c2a
commit 701440efd8
9 changed files with 272 additions and 183 deletions

View File

@ -2656,6 +2656,101 @@ _clutter_actor_get_pick_id (ClutterActor *self)
return self->priv->pick_id; return self->priv->pick_id;
} }
/* This is the same as clutter_actor_add_effect except that it doesn't
queue a redraw and it doesn't notify on the effect property */
static void
_clutter_actor_add_effect_internal (ClutterActor *self,
ClutterEffect *effect)
{
ClutterActorPrivate *priv = self->priv;
if (priv->effects == NULL)
{
priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
priv->effects->actor = self;
}
_clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
}
/* This is the same as clutter_actor_remove_effect except that it doesn't
queue a redraw and it doesn't notify on the effect property */
static void
_clutter_actor_remove_effect_internal (ClutterActor *self,
ClutterEffect *effect)
{
ClutterActorPrivate *priv = self->priv;
if (priv->effects == NULL)
return;
_clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
}
static gboolean
needs_flatten_effect (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
switch (priv->offscreen_redirect)
{
case CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY:
if (!clutter_actor_has_overlaps (self))
return FALSE;
/* flow through */
case CLUTTER_OFFSCREEN_REDIRECT_ALWAYS_FOR_OPACITY:
return clutter_actor_get_paint_opacity (self) < 255;
case CLUTTER_OFFSCREEN_REDIRECT_ALWAYS:
return TRUE;
}
g_assert_not_reached ();
}
static void
add_or_remove_flatten_effect (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
/* Add or remove the flatten effect depending on the
offscreen-redirect property. */
if (needs_flatten_effect (self))
{
if (priv->flatten_effect == NULL)
{
ClutterActorMeta *actor_meta;
gint priority;
priv->flatten_effect = _clutter_flatten_effect_new ();
/* Keep a reference to the effect so that we can queue
redraws from it */
g_object_ref_sink (priv->flatten_effect);
/* Set the priority of the effect to high so that it will
always be applied to the actor first. It uses an internal
priority so that it won't be visible to applications */
actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
_clutter_actor_meta_set_priority (actor_meta, priority);
/* This will add the effect without queueing a redraw */
_clutter_actor_add_effect_internal (self, priv->flatten_effect);
}
}
else
{
if (priv->flatten_effect != NULL)
{
/* Destroy the effect so that it will lose its fbo cache of
the actor */
_clutter_actor_remove_effect_internal (self, priv->flatten_effect);
g_object_unref (priv->flatten_effect);
priv->flatten_effect = NULL;
}
}
}
/** /**
* clutter_actor_paint: * clutter_actor_paint:
* @self: A #ClutterActor * @self: A #ClutterActor
@ -2757,6 +2852,12 @@ clutter_actor_paint (ClutterActor *self)
{ {
CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter); CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter);
/* We check whether we need to add the flatten effect before
each paint so that we can avoid having a mechanism for
applications to notify when the value of the
has_overlaps virtual changes. */
add_or_remove_flatten_effect (self);
/* We save the current paint volume so that the next time the /* We save the current paint volume so that the next time the
* actor queues a redraw we can constrain the redraw to just * actor queues a redraw we can constrain the redraw to just
* cover the union of the new bounding box and the old. * cover the union of the new bounding box and the old.
@ -3648,6 +3749,17 @@ clutter_actor_real_get_paint_volume (ClutterActor *self,
return FALSE; return FALSE;
} }
static gboolean
clutter_actor_real_has_overlaps (ClutterActor *self)
{
/* By default we'll assume that all actors need an offscreen
redirect to get the correct opacity. This effectively favours
accuracy over efficiency. Actors such as ClutterTexture that
would never need an offscreen redirect can override this to
return FALSE. */
return TRUE;
}
static void static void
clutter_actor_class_init (ClutterActorClass *klass) clutter_actor_class_init (ClutterActorClass *klass)
{ {
@ -4052,7 +4164,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
P_("Whether to flatten the actor into a " P_("Whether to flatten the actor into a "
"single image"), "single image"),
CLUTTER_TYPE_OFFSCREEN_REDIRECT, CLUTTER_TYPE_OFFSCREEN_REDIRECT,
CLUTTER_OFFSCREEN_REDIRECT_NEVER, CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY,
CLUTTER_PARAM_READWRITE); CLUTTER_PARAM_READWRITE);
obj_props[PROP_OFFSCREEN_REDIRECT] = pspec; obj_props[PROP_OFFSCREEN_REDIRECT] = pspec;
g_object_class_install_property (object_class, g_object_class_install_property (object_class,
@ -5092,6 +5204,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
klass->apply_transform = clutter_actor_real_apply_transform; klass->apply_transform = clutter_actor_real_apply_transform;
klass->get_accessible = clutter_actor_real_get_accessible; klass->get_accessible = clutter_actor_real_get_accessible;
klass->get_paint_volume = clutter_actor_real_get_paint_volume; klass->get_paint_volume = clutter_actor_real_get_paint_volume;
klass->has_overlaps = clutter_actor_real_has_overlaps;
} }
static void static void
@ -5104,7 +5217,7 @@ clutter_actor_init (ClutterActor *self)
priv->parent_actor = NULL; priv->parent_actor = NULL;
priv->has_clip = FALSE; priv->has_clip = FALSE;
priv->opacity = 0xff; priv->opacity = 0xff;
priv->offscreen_redirect = CLUTTER_OFFSCREEN_REDIRECT_NEVER; priv->offscreen_redirect = CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY;
priv->id = _clutter_context_acquire_id (self); priv->id = _clutter_context_acquire_id (self);
priv->pick_id = -1; priv->pick_id = -1;
priv->scale_x = 1.0; priv->scale_x = 1.0;
@ -7420,7 +7533,7 @@ clutter_actor_get_opacity (ClutterActor *self)
* some cases be a performance lose so it is important to determine * some cases be a performance lose so it is important to determine
* which value is right for an actor before modifying this value. For * which value is right for an actor before modifying this value. For
* example, there is never any reason to flatten an actor that is just * example, there is never any reason to flatten an actor that is just
* a single texture (such as a ClutterTexture) because it is * a single texture (such as a #ClutterTexture) because it is
* effectively already cached in an image so the offscreen would be * effectively already cached in an image so the offscreen would be
* redundant. Also if the actor contains primitives that are far apart * redundant. Also if the actor contains primitives that are far apart
* with a large transparent area in the middle (such as a large * with a large transparent area in the middle (such as a large
@ -7433,28 +7546,35 @@ clutter_actor_get_opacity (ClutterActor *self)
* forwards on the opacity to all of the children. If the children are * forwards on the opacity to all of the children. If the children are
* overlapping then it will appear as if they are two separate glassy * overlapping then it will appear as if they are two separate glassy
* objects and there will be a break in the color where they * objects and there will be a break in the color where they
* overlap. By setting the offscreen-redirect to * overlap. By redirecting to an offscreen buffer it will be as if the
* %CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY it will be as if the two * two opaque objects are combined into one and then made transparent
* opaque objects are combined into one and then made transparent
* which is usually what is expected. * which is usually what is expected.
* *
* The image below demonstrates the difference between the fast * The image below demonstrates the difference between redirecting and
* default opacity and the correct but inefficient opacity achieved * not. The image shows two Clutter groups, each containing a red and
* through the offscreen redirect. The image shows two Clutter groups, * a green rectangle which overlap. The opacity on the group is set to
* each containing a red and a green rectangle which overlap. The * 128 (which is 50%). When the offscreen redirect is not used, the
* opacity on the group is set to 128 (which is 50%). When the * red rectangle can be seen through the blue rectangle as if the two
* offscreen redirect is not used, the red rectangle can be seen * rectangles were separately transparent. When the redirect is used
* through the blue rectangle as if the two rectangles were separately * the group as a whole is transparent instead so the red rectangle is
* transparent. When the redirect is used the group as a whole is * not visible where they overlap.
* transparent instead so the red rectangle is not visible where they
* overlap.
* *
* <figure id="offscreen-redirect"> * <figure id="offscreen-redirect">
* <title>Sample of using an offscreen redirect for transparency</title> * <title>Sample of using an offscreen redirect for transparency</title>
* <graphic fileref="offscreen-redirect.png" format="PNG"/> * <graphic fileref="offscreen-redirect.png" format="PNG"/>
* </figure> * </figure>
* *
* The default value is %CLUTTER_OFFSCREEN_REDIRECT_NEVER. * The default behaviour is
* %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY. This will end up
* redirecting actors whenever they are semi-transparent unless their
* has_overlaps() virtual returns %FALSE. This should mean that
* generally all actors will be rendered with the correct opacity and
* certain actors that don't need the offscreen redirect (such as
* #ClutterTexture) will paint directly for efficiency.
*
* Custom actors that don't contain any overlapping primitives are
* recommended to override the has_overlaps() virtual to return %FALSE
* for maximum efficiency.
* *
* Since: 1.8 * Since: 1.8
*/ */
@ -7472,37 +7592,15 @@ clutter_actor_set_offscreen_redirect (ClutterActor *self,
{ {
priv->offscreen_redirect = redirect; priv->offscreen_redirect = redirect;
if (priv->flatten_effect == NULL) /* Queue a redraw from the effect so that it can use its cached
{ image if available instead of having to redraw the actual
ClutterActorMeta *actor_meta; actor. If it doesn't end up using the FBO then the effect is
gint priority; still able to continue the paint anyway. If there is no
effect then this is equivalent to queuing a full redraw */
priv->flatten_effect = _clutter_flatten_effect_new (); _clutter_actor_queue_redraw_full (self,
/* Keep a reference to the effect so that we can queue 0, /* flags */
redraws from it */ NULL, /* clip */
g_object_ref_sink (priv->flatten_effect); priv->flatten_effect);
/* Set the priority of the effect to high so that it will
always be applied to the actor first. It uses an internal
priority so that it won't be visible to applications */
actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
_clutter_actor_meta_set_priority (actor_meta, priority);
/* This will also queue a full redraw of the actor */
clutter_actor_add_effect (self, priv->flatten_effect);
}
else
{
/* Queue a redraw from the effect so that it can use its
cached image if available instead of having to redraw the
actual actor. If it doesn't end up using the FBO then the
effect is still able to continue the paint anyway */
_clutter_actor_queue_redraw_full (self,
0, /* flags */
NULL, /* clip */
priv->flatten_effect);
}
g_object_notify_by_pspec (G_OBJECT (self), g_object_notify_by_pspec (G_OBJECT (self),
obj_props[PROP_OFFSCREEN_REDIRECT]); obj_props[PROP_OFFSCREEN_REDIRECT]);
@ -11744,20 +11842,10 @@ void
clutter_actor_add_effect (ClutterActor *self, clutter_actor_add_effect (ClutterActor *self,
ClutterEffect *effect) ClutterEffect *effect)
{ {
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_EFFECT (effect)); g_return_if_fail (CLUTTER_IS_EFFECT (effect));
priv = self->priv; _clutter_actor_add_effect_internal (self, effect);
if (priv->effects == NULL)
{
priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
priv->effects->actor = self;
}
_clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
@ -11810,17 +11898,10 @@ void
clutter_actor_remove_effect (ClutterActor *self, clutter_actor_remove_effect (ClutterActor *self,
ClutterEffect *effect) ClutterEffect *effect)
{ {
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_EFFECT (effect)); g_return_if_fail (CLUTTER_IS_EFFECT (effect));
priv = self->priv; _clutter_actor_remove_effect_internal (self, effect);
if (priv->effects == NULL)
return;
_clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (self);
@ -12236,6 +12317,27 @@ clutter_actor_get_paint_box (ClutterActor *self,
return TRUE; return TRUE;
} }
/**
* clutter_actor_has_overlaps:
* @self: A #ClutterActor
*
* Return value: whether the actor may contain overlapping
* primitives. Clutter uses this to determine whether the painting
* should be redirected to an offscreen buffer to correctly implement
* the opacity property. Custom actors can override this by
* implementing the has_overlaps virtual. See
* clutter_actor_set_offscreen_redirect() for more information.
*
* Since: 1.8
*/
gboolean
clutter_actor_has_overlaps (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self);
}
gint gint
_clutter_actor_get_n_children (ClutterActor *self) _clutter_actor_get_n_children (ClutterActor *self)
{ {

View File

@ -118,12 +118,14 @@ typedef enum
/** /**
* ClutterOffscreenRedirect: * ClutterOffscreenRedirect:
* @CLUTTER_OFFSCREEN_REDIRECT_NEVER: Never redirect the actor to an * @CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY: Only redirect
* offscreen buffer. * the actor if it is semi-transparent and its has_overlaps()
* virtual returns %TRUE. This is the default.
* @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS_FOR_OPACITY: Always redirect the
* actor if it is semi-transparent regardless of the return value of
* its has_overlaps() virtual.
* @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an * @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an
* offscreen buffer. * offscreen buffer even if it is fully opaque.
* @CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY: Only redirect the actor if
* it is semi-transparent.
* *
* Possible values to pass to clutter_actor_set_offscreen_redirect(). * Possible values to pass to clutter_actor_set_offscreen_redirect().
* *
@ -131,9 +133,9 @@ typedef enum
*/ */
typedef enum typedef enum
{ {
CLUTTER_OFFSCREEN_REDIRECT_NEVER, CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY,
CLUTTER_OFFSCREEN_REDIRECT_ALWAYS, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS_FOR_OPACITY,
CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY CLUTTER_OFFSCREEN_REDIRECT_ALWAYS
} ClutterOffscreenRedirect; } ClutterOffscreenRedirect;
/** /**
@ -235,6 +237,10 @@ struct _ClutterActor
* describes the actor to an assistive technology. * describes the actor to an assistive technology.
* @get_paint_volume: virtual function, for sub-classes to define their * @get_paint_volume: virtual function, for sub-classes to define their
* #ClutterPaintVolume * #ClutterPaintVolume
* @has_overlaps: virtual function for
* sub-classes to advertise whether they need an offscreen redirect
* to get the correct opacity. See
* clutter_actor_set_offscreen_redirect() for details.
* *
* Base class for actors. * Base class for actors.
*/ */
@ -312,9 +318,11 @@ struct _ClutterActorClass
gboolean (* get_paint_volume) (ClutterActor *actor, gboolean (* get_paint_volume) (ClutterActor *actor,
ClutterPaintVolume *volume); ClutterPaintVolume *volume);
gboolean (* has_overlaps) (ClutterActor *self);
/*< private >*/ /*< private >*/
/* padding for future expansion */ /* padding for future expansion */
gpointer _padding_dummy[29]; gpointer _padding_dummy[28];
}; };
GType clutter_actor_get_type (void) G_GNUC_CONST; GType clutter_actor_get_type (void) G_GNUC_CONST;
@ -610,6 +618,8 @@ const ClutterPaintVolume *clutter_actor_get_transformed_paint_volume (ClutterAc
gboolean clutter_actor_get_paint_box (ClutterActor *self, gboolean clutter_actor_get_paint_box (ClutterActor *self,
ClutterActorBox *box); ClutterActorBox *box);
gboolean clutter_actor_has_overlaps (ClutterActor *self);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_ACTOR_H__ */ #endif /* __CLUTTER_ACTOR_H__ */

View File

@ -221,6 +221,19 @@ clutter_clone_get_paint_volume (ClutterActor *self,
return TRUE; return TRUE;
} }
static gboolean
clutter_clone_has_overlaps (ClutterActor *self)
{
ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
/* The clone has overlaps iff the source has overlaps */
if (priv->clone_source == NULL)
return FALSE;
return clutter_actor_has_overlaps (priv->clone_source);
}
static void static void
clutter_clone_allocate (ClutterActor *self, clutter_clone_allocate (ClutterActor *self,
const ClutterActorBox *box, const ClutterActorBox *box,
@ -309,6 +322,7 @@ clutter_clone_class_init (ClutterCloneClass *klass)
actor_class->get_preferred_width = clutter_clone_get_preferred_width; actor_class->get_preferred_width = clutter_clone_get_preferred_width;
actor_class->get_preferred_height = clutter_clone_get_preferred_height; actor_class->get_preferred_height = clutter_clone_get_preferred_height;
actor_class->allocate = clutter_clone_allocate; actor_class->allocate = clutter_clone_allocate;
actor_class->has_overlaps = clutter_clone_has_overlaps;
gobject_class->dispose = clutter_clone_dispose; gobject_class->dispose = clutter_clone_dispose;
gobject_class->set_property = clutter_clone_set_property; gobject_class->set_property = clutter_clone_set_property;

View File

@ -23,7 +23,9 @@
*/ */
/* This is an internal-only effect used to implement the /* This is an internal-only effect used to implement the
'flatness' property of ClutterActor */ 'offscreen-redirect' property of ClutterActor. It doesn't actually
need to do anything on top of the ClutterOffscreenEffect class so
it only exists because that class is abstract */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -33,50 +35,18 @@
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-actor-private.h" #include "clutter-actor-private.h"
static void
_clutter_flatten_effect_set_actor (ClutterActorMeta *meta,
ClutterActor *actor);
static void
_clutter_flatten_effect_run (ClutterEffect *effect,
ClutterEffectRunFlags flags);
G_DEFINE_TYPE (ClutterFlattenEffect, G_DEFINE_TYPE (ClutterFlattenEffect,
_clutter_flatten_effect, _clutter_flatten_effect,
CLUTTER_TYPE_OFFSCREEN_EFFECT); CLUTTER_TYPE_OFFSCREEN_EFFECT);
#define CLUTTER_FLATTEN_EFFECT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_FLATTEN_EFFECT, \
ClutterFlattenEffectPrivate))
struct _ClutterFlattenEffectPrivate
{
ClutterActor *actor;
/* This records whether the last paint went through the FBO or if it
was painted directly. We need to know this so we can force the
offscreen effect to clear its image when we switch from rendering
directly to rendering through the FBO */
gboolean last_paint_used_fbo;
};
static void static void
_clutter_flatten_effect_class_init (ClutterFlattenEffectClass *klass) _clutter_flatten_effect_class_init (ClutterFlattenEffectClass *klass)
{ {
ClutterActorMetaClass *actor_meta_class = (ClutterActorMetaClass *) klass;
ClutterEffectClass *effect_class = (ClutterEffectClass *) klass;
actor_meta_class->set_actor = _clutter_flatten_effect_set_actor;
effect_class->run = _clutter_flatten_effect_run;
g_type_class_add_private (klass, sizeof (ClutterFlattenEffectPrivate));
} }
static void static void
_clutter_flatten_effect_init (ClutterFlattenEffect *self) _clutter_flatten_effect_init (ClutterFlattenEffect *self)
{ {
self->priv = CLUTTER_FLATTEN_EFFECT_GET_PRIVATE (self);
} }
ClutterEffect * ClutterEffect *
@ -84,68 +54,3 @@ _clutter_flatten_effect_new (void)
{ {
return g_object_new (CLUTTER_TYPE_FLATTEN_EFFECT, NULL); return g_object_new (CLUTTER_TYPE_FLATTEN_EFFECT, NULL);
} }
static gboolean
_clutter_flatten_effect_is_using_fbo (ClutterFlattenEffect *opacity_effect)
{
ClutterFlattenEffectPrivate *priv = opacity_effect->priv;
switch (clutter_actor_get_offscreen_redirect (priv->actor))
{
case CLUTTER_OFFSCREEN_REDIRECT_NEVER:
return FALSE;
case CLUTTER_OFFSCREEN_REDIRECT_ALWAYS:
return TRUE;
case CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY:
return clutter_actor_get_paint_opacity (priv->actor) < 255;
}
g_assert_not_reached ();
}
static void
_clutter_flatten_effect_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
ClutterFlattenEffect *opacity_effect = CLUTTER_FLATTEN_EFFECT (meta);
ClutterFlattenEffectPrivate *priv = opacity_effect->priv;
CLUTTER_ACTOR_META_CLASS (_clutter_flatten_effect_parent_class)->
set_actor (meta, actor);
/* we keep a back pointer here, to avoid going through the ActorMeta */
priv->actor = clutter_actor_meta_get_actor (meta);
}
static void
_clutter_flatten_effect_run (ClutterEffect *effect,
ClutterEffectRunFlags flags)
{
ClutterFlattenEffect *opacity_effect = CLUTTER_FLATTEN_EFFECT (effect);
ClutterFlattenEffectPrivate *priv = opacity_effect->priv;
if (_clutter_flatten_effect_is_using_fbo (opacity_effect))
{
/* If the last paint bypassed the FBO then we'll pretend the
actor is dirty so that the offscreen will clear its image */
if (!priv->last_paint_used_fbo)
{
flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
priv->last_paint_used_fbo = TRUE;
}
/* Let the offscreen effect paint the actor through the FBO */
CLUTTER_EFFECT_CLASS (_clutter_flatten_effect_parent_class)->
run (effect, flags);
}
else
{
/* Just let the actor paint directly to the stage */
clutter_actor_continue_paint (priv->actor);
priv->last_paint_used_fbo = FALSE;
}
}

View File

@ -62,8 +62,6 @@ struct _ClutterFlattenEffectClass
struct _ClutterFlattenEffect struct _ClutterFlattenEffect
{ {
ClutterOffscreenEffect parent; ClutterOffscreenEffect parent;
ClutterFlattenEffectPrivate *priv;
}; };
GType _clutter_flatten_effect_get_type (void) G_GNUC_CONST; GType _clutter_flatten_effect_get_type (void) G_GNUC_CONST;

View File

@ -162,6 +162,14 @@ clutter_rectangle_get_paint_volume (ClutterActor *self,
volume); volume);
} }
static gboolean
clutter_rectangle_has_overlaps (ClutterActor *self)
{
/* Rectangles never need an offscreen redirect because there are
never any overlapping primitives */
return FALSE;
}
static void static void
clutter_rectangle_set_property (GObject *object, clutter_rectangle_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -243,6 +251,7 @@ clutter_rectangle_class_init (ClutterRectangleClass *klass)
actor_class->paint = clutter_rectangle_paint; actor_class->paint = clutter_rectangle_paint;
actor_class->get_paint_volume = clutter_rectangle_get_paint_volume; actor_class->get_paint_volume = clutter_rectangle_get_paint_volume;
actor_class->has_overlaps = clutter_rectangle_has_overlaps;
gobject_class->finalize = clutter_rectangle_finalize; gobject_class->finalize = clutter_rectangle_finalize;
gobject_class->dispose = clutter_rectangle_dispose; gobject_class->dispose = clutter_rectangle_dispose;

View File

@ -409,6 +409,14 @@ clutter_texture_allocate (ClutterActor *self,
clutter_actor_allocate_preferred_size (priv->fbo_source, flags); clutter_actor_allocate_preferred_size (priv->fbo_source, flags);
} }
static gboolean
clutter_texture_has_overlaps (ClutterActor *self)
{
/* Textures never need an offscreen redirect because there are never
any overlapping primitives */
return FALSE;
}
static void static void
set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source, set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source,
int viewport_width, int viewport_width,
@ -980,6 +988,7 @@ clutter_texture_class_init (ClutterTextureClass *klass)
actor_class->get_paint_volume = clutter_texture_get_paint_volume; actor_class->get_paint_volume = clutter_texture_get_paint_volume;
actor_class->realize = clutter_texture_realize; actor_class->realize = clutter_texture_realize;
actor_class->unrealize = clutter_texture_unrealize; actor_class->unrealize = clutter_texture_unrealize;
actor_class->has_overlaps = clutter_texture_has_overlaps;
actor_class->get_preferred_width = clutter_texture_get_preferred_width; actor_class->get_preferred_width = clutter_texture_get_preferred_width;
actor_class->get_preferred_height = clutter_texture_get_preferred_height; actor_class->get_preferred_height = clutter_texture_get_preferred_height;

View File

@ -299,6 +299,7 @@ clutter_actor_event
clutter_actor_should_pick_paint clutter_actor_should_pick_paint
clutter_actor_map clutter_actor_map
clutter_actor_unmap clutter_actor_unmap
clutter_actor_has_overlaps
<SUBSECTION> <SUBSECTION>
ClutterAllocationFlags ClutterAllocationFlags

View File

@ -33,7 +33,7 @@ GType foo_actor_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR); G_DEFINE_TYPE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR);
static void static void
foo_actor_class_paint (ClutterActor *actor) foo_actor_paint (ClutterActor *actor)
{ {
FooActor *foo_actor = (FooActor *) actor; FooActor *foo_actor = (FooActor *) actor;
ClutterActorBox allocation; ClutterActorBox allocation;
@ -55,19 +55,26 @@ foo_actor_class_paint (ClutterActor *actor)
} }
static gboolean static gboolean
foo_actor_class_get_paint_volume (ClutterActor *actor, foo_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume) ClutterPaintVolume *volume)
{ {
return clutter_paint_volume_set_from_allocation (volume, actor); return clutter_paint_volume_set_from_allocation (volume, actor);
} }
static gboolean
foo_actor_has_overlaps (ClutterActor *actor)
{
return FALSE;
}
static void static void
foo_actor_class_init (FooActorClass *klass) foo_actor_class_init (FooActorClass *klass)
{ {
ClutterActorClass *actor_class = (ClutterActorClass *) klass; ClutterActorClass *actor_class = (ClutterActorClass *) klass;
actor_class->paint = foo_actor_class_paint; actor_class->paint = foo_actor_paint;
actor_class->get_paint_volume = foo_actor_class_get_paint_volume; actor_class->get_paint_volume = foo_actor_get_paint_volume;
actor_class->has_overlaps = foo_actor_has_overlaps;
} }
static void static void
@ -75,6 +82,40 @@ foo_actor_init (FooActor *self)
{ {
} }
typedef struct _FooGroup FooGroup;
typedef struct _FooGroupClass FooGroupClass;
struct _FooGroupClass
{
ClutterGroupClass parent_class;
};
struct _FooGroup
{
ClutterGroup parent;
};
G_DEFINE_TYPE (FooGroup, foo_group, CLUTTER_TYPE_GROUP);
static gboolean
foo_group_has_overlaps (ClutterActor *actor)
{
return FALSE;
}
static void
foo_group_class_init (FooGroupClass *klass)
{
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
actor_class->has_overlaps = foo_group_has_overlaps;
}
static void
foo_group_init (FooGroup *self)
{
}
static void static void
verify_results (Data *data, verify_results (Data *data,
guint8 expected_color_red, guint8 expected_color_red,
@ -155,7 +196,7 @@ timeout_cb (gpointer user_data)
it needs to fill the cache first. It should be painted with full it needs to fill the cache first. It should be painted with full
opacity */ opacity */
clutter_actor_set_offscreen_redirect clutter_actor_set_offscreen_redirect
(data->container, CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY); (data->container, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS_FOR_OPACITY);
verify_results (data, verify_results (data,
255, 127, 127, 255, 127, 127,
1, 1,
@ -242,7 +283,7 @@ test_offscreen_redirect (TestConformSimpleFixture *fixture,
data.parent_container = clutter_group_new (); data.parent_container = clutter_group_new ();
data.container = clutter_group_new (); data.container = g_object_new (foo_group_get_type (), NULL);
data.foo_actor = g_object_new (foo_actor_get_type (), NULL); data.foo_actor = g_object_new (foo_actor_get_type (), NULL);
clutter_actor_set_size (CLUTTER_ACTOR (data.foo_actor), 100, 100); clutter_actor_set_size (CLUTTER_ACTOR (data.foo_actor), 100, 100);