diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 1b7171020..476a38d2d 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -175,6 +175,7 @@ source_c = \
$(srcdir)/clutter-feature.c \
$(srcdir)/clutter-fixed.c \
$(srcdir)/clutter-fixed-layout.c \
+ $(srcdir)/clutter-flatten-effect.c \
$(srcdir)/clutter-flow-layout.c \
$(srcdir)/clutter-frame-source.c \
$(srcdir)/clutter-group.c \
@@ -227,6 +228,7 @@ source_h_priv = \
$(srcdir)/clutter-effect-private.h \
$(srcdir)/clutter-event-translator.h \
$(srcdir)/clutter-event-private.h \
+ $(srcdir)/clutter-flatten-effect.h \
$(srcdir)/clutter-id-pool.h \
$(srcdir)/clutter-master-clock.h \
$(srcdir)/clutter-model-private.h \
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index b9e1e62fa..e7d0a7d65 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -299,6 +299,7 @@
#include "clutter-enum-types.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
+#include "clutter-flatten-effect.h"
#include "clutter-paint-volume-private.h"
#include "clutter-private.h"
#include "clutter-profile.h"
@@ -442,6 +443,12 @@ struct _ClutterActorPrivate
guint8 opacity;
gint opacity_override;
+ ClutterOffscreenRedirect offscreen_redirect;
+
+ /* This is an internal effect used to implement the
+ offscreen-redirect property */
+ ClutterEffect *flatten_effect;
+
ClutterActor *parent_actor;
GList *children;
gint n_children;
@@ -545,6 +552,8 @@ enum
PROP_OPACITY,
+ PROP_OFFSCREEN_REDIRECT,
+
PROP_VISIBLE,
PROP_MAPPED,
PROP_REALIZED,
@@ -3017,6 +3026,10 @@ clutter_actor_set_property (GObject *object,
clutter_actor_set_opacity (actor, g_value_get_uint (value));
break;
+ case PROP_OFFSCREEN_REDIRECT:
+ clutter_actor_set_offscreen_redirect (actor, g_value_get_enum (value));
+ break;
+
case PROP_NAME:
clutter_actor_set_name (actor, g_value_get_string (value));
break;
@@ -3308,6 +3321,10 @@ clutter_actor_get_property (GObject *object,
g_value_set_uint (value, priv->opacity);
break;
+ case PROP_OFFSCREEN_REDIRECT:
+ g_value_set_enum (value, priv->offscreen_redirect);
+ break;
+
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
@@ -3544,6 +3561,12 @@ clutter_actor_dispose (GObject *object)
priv->effects = NULL;
}
+ if (priv->flatten_effect != NULL)
+ {
+ g_object_unref (priv->flatten_effect);
+ priv->flatten_effect = NULL;
+ }
+
g_signal_emit (self, actor_signals[DESTROY], 0);
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
@@ -4016,6 +4039,26 @@ clutter_actor_class_init (ClutterActorClass *klass)
obj_props[PROP_OPACITY] = pspec;
g_object_class_install_property (object_class, PROP_OPACITY, pspec);
+ /**
+ * ClutterActor:offscreen-redirect:
+ *
+ * Whether to flatten the actor into a single image. See
+ * clutter_actor_set_offscreen_redirect() for details.
+ *
+ * Since: 1.8
+ */
+ pspec = g_param_spec_enum ("offscreen-redirect",
+ P_("Offscreen redirect"),
+ P_("Whether to flatten the actor into a "
+ "single image"),
+ CLUTTER_TYPE_OFFSCREEN_REDIRECT,
+ CLUTTER_OFFSCREEN_REDIRECT_NEVER,
+ CLUTTER_PARAM_READWRITE);
+ obj_props[PROP_OFFSCREEN_REDIRECT] = pspec;
+ g_object_class_install_property (object_class,
+ PROP_OFFSCREEN_REDIRECT,
+ pspec);
+
/**
* ClutterActor:visible:
*
@@ -5061,6 +5104,7 @@ clutter_actor_init (ClutterActor *self)
priv->parent_actor = NULL;
priv->has_clip = FALSE;
priv->opacity = 0xff;
+ priv->offscreen_redirect = CLUTTER_OFFSCREEN_REDIRECT_NEVER;
priv->id = _clutter_context_acquire_id (self);
priv->pick_id = -1;
priv->scale_x = 1.0;
@@ -7259,7 +7303,16 @@ clutter_actor_set_opacity (ClutterActor *self,
{
priv->opacity = opacity;
- clutter_actor_queue_redraw (self);
+ /* Queue a redraw from the flatten 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. If there
+ is no flatten effect yet then this is equivalent to queueing
+ a full redraw */
+ _clutter_actor_queue_redraw_full (self,
+ 0, /* flags */
+ NULL, /* clip */
+ priv->flatten_effect);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
}
@@ -7350,6 +7403,131 @@ clutter_actor_get_opacity (ClutterActor *self)
return self->priv->opacity;
}
+/**
+ * clutter_actor_set_offscreen_redirect:
+ * @self: A #ClutterActor
+ * @redirect: New offscreen redirect value for the actor.
+ *
+ * Sets whether to redirect the actor into an offscreen image. The
+ * offscreen image is used to flatten the actor into a single image
+ * while painting for two main reasons. Firstly, when the actor is
+ * painted a second time without any of its contents changing it can
+ * simply repaint the cached image without descending further down the
+ * actor hierarchy. Secondly, it will make the opacity look correct
+ * even if there are overlapping primitives in the actor.
+ *
+ * Caching the actor could in some cases be a performance win and in
+ * some cases be a performance lose so it is important to determine
+ * 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
+ * a single texture (such as a ClutterTexture) because it is
+ * effectively already cached in an image so the offscreen would be
+ * redundant. Also if the actor contains primitives that are far apart
+ * with a large transparent area in the middle (such as a large
+ * CluterGroup with a small actor in the top left and a small actor in
+ * the bottom right) then the cached image will contain the entire
+ * image of the large area and the paint will waste time blending all
+ * of the transparent pixels in the middle.
+ *
+ * The default method of implementing opacity on a container simply
+ * 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
+ * objects and there will be a break in the color where they
+ * overlap. By setting the offscreen-redirect to
+ * %CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY it will be as if the two
+ * opaque objects are combined into one and then made transparent
+ * which is usually what is expected.
+ *
+ * The image below demonstrates the difference between the fast
+ * default opacity and the correct but inefficient opacity achieved
+ * through the offscreen redirect. The image shows two Clutter groups,
+ * each containing a red and a green rectangle which overlap. The
+ * opacity on the group is set to 128 (which is 50%). When the
+ * offscreen redirect is not used, the red rectangle can be seen
+ * through the blue rectangle as if the two rectangles were separately
+ * transparent. When the redirect is used the group as a whole is
+ * transparent instead so the red rectangle is not visible where they
+ * overlap.
+ *
+ *
+ *
+ * The default value is %CLUTTER_OFFSCREEN_REDIRECT_NEVER.
+ *
+ * Since: 1.8
+ */
+void
+clutter_actor_set_offscreen_redirect (ClutterActor *self,
+ ClutterOffscreenRedirect redirect)
+{
+ ClutterActorPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+ priv = self->priv;
+
+ if (priv->offscreen_redirect != redirect)
+ {
+ priv->offscreen_redirect = redirect;
+
+ 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 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),
+ obj_props[PROP_OFFSCREEN_REDIRECT]);
+ }
+}
+
+/**
+ * clutter_actor_get_offscreen_redirect:
+ * @self: a #ClutterActor
+ *
+ * Retrieves whether to redirect the actor to an offscreen buffer, as
+ * set by clutter_actor_set_offscreen_redirect().
+ *
+ * Return value: the value of the offscreen-redirect property of the actor
+ *
+ * Since: 1.8
+ */
+ClutterOffscreenRedirect
+clutter_actor_get_offscreen_redirect (ClutterActor *self)
+{
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
+
+ return self->priv->offscreen_redirect;
+}
+
/**
* clutter_actor_set_name:
* @self: A #ClutterActor
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index 32096175f..06e1ae73a 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -116,6 +116,26 @@ typedef enum
CLUTTER_ACTOR_NO_LAYOUT = 1 << 5
} ClutterActorFlags;
+/**
+ * ClutterOffscreenRedirect:
+ * @CLUTTER_OFFSCREEN_REDIRECT_NEVER: Never redirect the actor to an
+ * offscreen buffer.
+ * @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an
+ * offscreen buffer.
+ * @CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY: Only redirect the actor if
+ * it is semi-transparent.
+ *
+ * Possible values to pass to clutter_actor_set_offscreen_redirect().
+ *
+ * Since: 1.8
+ */
+typedef enum
+{
+ CLUTTER_OFFSCREEN_REDIRECT_NEVER,
+ CLUTTER_OFFSCREEN_REDIRECT_ALWAYS,
+ CLUTTER_OFFSCREEN_REDIRECT_OPACITY_ONLY
+} ClutterOffscreenRedirect;
+
/**
* ClutterAllocationFlags:
* @CLUTTER_ALLOCATION_NONE: No flag set
@@ -424,6 +444,10 @@ guint8 clutter_actor_get_opacity (ClutterActor
guint8 clutter_actor_get_paint_opacity (ClutterActor *self);
gboolean clutter_actor_get_paint_visibility (ClutterActor *self);
+void clutter_actor_set_offscreen_redirect (ClutterActor *self,
+ ClutterOffscreenRedirect redirect);
+ClutterOffscreenRedirect
+ clutter_actor_get_offscreen_redirect (ClutterActor *self);
void clutter_actor_set_name (ClutterActor *self,
const gchar *name);
diff --git a/clutter/clutter-flatten-effect.c b/clutter/clutter-flatten-effect.c
new file mode 100644
index 000000000..d844299a4
--- /dev/null
+++ b/clutter/clutter-flatten-effect.c
@@ -0,0 +1,151 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * 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 .
+ *
+ * Authors:
+ * Neil Roberts
+ */
+
+/* This is an internal-only effect used to implement the
+ 'flatness' property of ClutterActor */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-flatten-effect.h"
+#include "clutter-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,
+ _clutter_flatten_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
+_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
+_clutter_flatten_effect_init (ClutterFlattenEffect *self)
+{
+ self->priv = CLUTTER_FLATTEN_EFFECT_GET_PRIVATE (self);
+}
+
+ClutterEffect *
+_clutter_flatten_effect_new (void)
+{
+ 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;
+ }
+}
diff --git a/clutter/clutter-flatten-effect.h b/clutter/clutter-flatten-effect.h
new file mode 100644
index 000000000..7508f8ce8
--- /dev/null
+++ b/clutter/clutter-flatten-effect.h
@@ -0,0 +1,75 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * 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 .
+ *
+ * Authors:
+ * Neil Roberts
+ */
+
+#ifndef __CLUTTER_FLATTEN_EFFECT_H__
+#define __CLUTTER_FLATTEN_EFFECT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_FLATTEN_EFFECT \
+ (_clutter_flatten_effect_get_type())
+#define CLUTTER_FLATTEN_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CLUTTER_TYPE_FLATTEN_EFFECT, \
+ ClutterFlattenEffect))
+#define CLUTTER_FLATTEN_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ CLUTTER_TYPE_FLATTEN_EFFECT, \
+ ClutterFlattenEffectClass))
+#define CLUTTER_IS_FLATTEN_EFFECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ CLUTTER_TYPE_FLATTEN_EFFECT))
+#define CLUTTER_IS_FLATTEN_EFFECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ CLUTTER_TYPE_FLATTEN_EFFECT))
+#define CLUTTER_FLATTEN_EFFECT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ CLUTTER_FLATTEN_EFFECT, \
+ ClutterFlattenEffectClass))
+
+typedef struct _ClutterFlattenEffect ClutterFlattenEffect;
+typedef struct _ClutterFlattenEffectClass ClutterFlattenEffectClass;
+typedef struct _ClutterFlattenEffectPrivate ClutterFlattenEffectPrivate;
+
+struct _ClutterFlattenEffectClass
+{
+ ClutterOffscreenEffectClass parent_class;
+};
+
+struct _ClutterFlattenEffect
+{
+ ClutterOffscreenEffect parent;
+
+ ClutterFlattenEffectPrivate *priv;
+};
+
+GType _clutter_flatten_effect_get_type (void) G_GNUC_CONST;
+
+ClutterEffect *_clutter_flatten_effect_new (void);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_FLATTEN_EFFECT_H__ */
diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am
index 82b8a83f3..648013bc0 100644
--- a/doc/reference/clutter/Makefile.am
+++ b/doc/reference/clutter/Makefile.am
@@ -122,7 +122,8 @@ HTML_IMAGES=\
event-flow.png \
flow-layout-horizontal.png \
flow-layout-vertical.png \
- path-alpha-func.png
+ path-alpha-func.png \
+ offscreen-redirect.png
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
@@ -179,4 +180,5 @@ EXTRA_DIST += \
event-flow.png \
flow-layout-horizontal.png \
flow-layout-vertical.png \
- path-alpha-func.png
+ path-alpha-func.png \
+ offscreen-redirect.png
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 93c09d437..844b08ea8 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -341,6 +341,9 @@ clutter_actor_get_z_rotation_gravity
clutter_actor_is_rotated
clutter_actor_set_opacity
clutter_actor_get_opacity
+ClutterOffscreenRedirect
+clutter_actor_set_offscreen_redirect
+clutter_actor_get_offscreen_redirect
clutter_actor_set_name
clutter_actor_get_name
clutter_actor_get_gid
diff --git a/doc/reference/clutter/offscreen-redirect.png b/doc/reference/clutter/offscreen-redirect.png
new file mode 100644
index 000000000..ff36c8082
Binary files /dev/null and b/doc/reference/clutter/offscreen-redirect.png differ