diff --git a/doc/cookbook/Makefile.am b/doc/cookbook/Makefile.am
index 6fad1b4db..c567cbdeb 100644
--- a/doc/cookbook/Makefile.am
+++ b/doc/cookbook/Makefile.am
@@ -16,6 +16,7 @@ XML_FILES = \
$(srcdir)/textures.xml \
$(srcdir)/layouts.xml \
$(srcdir)/script.xml \
+ $(srcdir)/effects.xml \
$(NULL)
XSLTOPTS = \
@@ -36,6 +37,9 @@ HTML_FILES = $(top_builddir)/doc/cookbook/html/*.html
CSS_FILES = $(top_builddir)/doc/cookbook/html/*.css
IMAGE_FILES = \
$(srcdir)/images/clutter-logo.png \
+ $(srcdir)/images/effects-built-in.png \
+ $(srcdir)/images/effects-custom-deform.png \
+ $(srcdir)/images/effects-custom-deform-back-material.png \
$(srcdir)/images/textures-reflection.png \
$(srcdir)/images/actors-opacity.png \
$(srcdir)/images/actors-opacity-container-affects-opacity.png \
@@ -59,6 +63,7 @@ VIDEO_FILES = \
$(srcdir)/videos/animations-rotating-y-centered.ogv \
$(srcdir)/videos/animations-rotating-z-centered.ogv \
$(srcdir)/videos/animations-rotating-container-reverses-direction.ogv \
+ $(srcdir)/videos/effects-custom-deform.ogv \
$(srcdir)/videos/textures-split-go.ogv \
$(srcdir)/videos/events-mouse-scroll.ogv \
$(srcdir)/videos/textures-crossfade-two-textures.ogv \
diff --git a/doc/cookbook/clutter-cookbook.xml.in b/doc/cookbook/clutter-cookbook.xml.in
index 83e16476b..96953fa51 100644
--- a/doc/cookbook/clutter-cookbook.xml.in
+++ b/doc/cookbook/clutter-cookbook.xml.in
@@ -46,6 +46,7 @@
+ Contributing to this document
diff --git a/doc/cookbook/effects.xml b/doc/cookbook/effects.xml
new file mode 100644
index 000000000..2688bc941
--- /dev/null
+++ b/doc/cookbook/effects.xml
@@ -0,0 +1,769 @@
+
+
+
+ Effects
+
+
+ Roger Zelazny, from Prince of Chaos
+
+ Don't wake me for the end of the world unless it has very
+ good special effects
+
+
+
+ Introduction
+
+ Effects modify an actor's appearance, such
+ as how it is positioned, colored and textured.
+
+ The Clutter API for effects contains several
+ abstract classes you can subclass to create your own effects.
+ It also contains several built-in effects you can use to
+ modify the visual appearance of actors in a variety of ways.
+
+ The recipes in this section of the cookbook cover how to create
+ your own effects as well as how to apply Clutter's effects.
+
+
+ Creating effects using the abstract effect classes
+
+
+ One of the original design goals of Clutter was to abstract
+ the complexity of GL. However, the effects API partially circumvents
+ these abstractions, to give you finer-grained access to the
+ graphics pipeline. Therefore, if you want to write your own effects,
+ some understanding of Cogl, OpenGL, and general graphics programming
+ is essential.
+
+
+ Each abstract effect class is tailored to modifying different
+ aspects of an actor, as explained below:
+
+
+
+
+
+ ClutterEffect
+ If you're just using the Clutter and Cogl APIs to
+ decorate an actor, this is simplest type of effect to
+ implement.
+
+
+ Subclassing ClutterEffect enables you to
+ "wrap" how an actor is painted, by injecting some code before
+ and/or after the actor's own paint()
+ implementation.
+
+
+ This is the preferred way to modify how an actor is
+ painted, short of creating your own actor subclass.
+
+
+ Subclasses of
+ ClutterEffect:
+
+
+
+
+
+ ClutterOffscreenEffect
+
+ Use this class as a basis if you need GL textures
+ for your effect.
+
+
+ GL textures are required for effects which need
+ an offscreen framebuffer. The offscreen framebuffer is
+ used to store a modified rendering of an actor (e.g.
+ with its colors altered or with deformed geometry).
+ This buffer is then redirected to a texture in the
+ stage window.
+
+ An example is ClutterBlurEffect,
+ which uses a GLSL fragment shader to blur an
+ actor's appearance in an offscreen framebuffer.
+
+ Subclasses of
+ ClutterOffscreenEffect:
+
+
+
+
+
+
+ ClutterDeformEffect
+
+ Use this base class if you want to modify
+ an actor's geometry, at the level of individual
+ vertices.
+
+
+ ClutterDeformEffect removes the
+ complexity of dealing with vertex-based deformations
+ at the OpenGL level, instead enabling you to easily plug
+ a deformation callback into the graphics pipeline.
+
+ If you are writing your own deform effects,
+ a good example to work from is
+ ClutterPageTurnEffect.
+
+ There is also a
+ recipe which
+ explains how to implement a simple custom deform
+ effect (a page fold).
+
+
+
+
+
+ ClutterShaderEffect
+
+ Use this if you want to apply custom
+ GLSL vertex or fragment shaders to your actors.
+
+
+ Writing ClutterShaderEffects gives
+ you very fine-grained control over the GL pipeline.
+ However, this makes them the most complex
+ effects to implement.
+
+
+ If you want to write your own GLSL shaders, the
+ GLSL
+ specification is a good starting point.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Using the built-in effects
+
+ Clutter comes with a number of built-in effects
+ which can easily be applied to your actors. This section
+ explains how to do this.
+
+ First, create an actor. For this
+ example, we use a texture loaded with an image:
+
+
+
+/* filename could be set from command line or constant */
+gchar *filename;
+
+/* create a texture */
+ClutterActor *texture = clutter_texture_new ();
+
+/* ...set texture size, keep aspect ratio etc... */
+
+/* NB ignoring missing file errors here for brevity */
+clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
+ filename,
+ NULL);
+
+/* ...add texture to the stage... */
+
+
+
+ Next, create an instance of an effect; here, we're
+ creating a ClutterColorizeEffect with a pink tint:
+
+
+
+ClutterColor *pink = clutter_color_new (230, 187, 210, 255);
+ClutterEffect *effect = clutter_colorize_effect_new (pink);
+
+
+
+ Finally, apply the effect to the actor:
+
+
+
+clutter_actor_add_effect (texture, effect);
+
+
+
+ The result in this case is an image colorized with
+ a pink tint, like this:
+
+
+
+
+
+
+
+ Applying a ClutterColorizeEffect
+ to a texture loaded with an image (drawing by
+ Madeleine Smith)
+
+
+
+
+ The same set of steps applies for any of the built-in
+ Clutter effects. Your own custom effects classes should also
+ behave in a similar way: constructors should return
+ ClutterEffect instances so your effect can
+ be added to an actor through the standard API.
+
+ One further thing worth mentioning is that because an
+ effect is a GObject, any properties you expose for your effect
+ can be animated via implicit animations,
+ ClutterAnimator or ClutterState. For
+ example, the ClutterPageTurnEffect can be animated
+ by manipulating its period property. An example
+ of how to do this for your own effect is given in the
+ custom deform effect
+ recipe.
+
+ The full code for the ClutterColorizeEffect
+ example is below.
+
+
+ Applying a ClutterColorizeEffect to
+ a texture loaded with an image
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+
+
+
+
+ Creating and animating a custom ClutterDeformEffect
+
+
+ Problem
+
+ You want to deform an actor's geometry: for example,
+ to make it appear stretched, twisted or folded.
+
+ This recipe demonstrates how to do this with a simple page
+ fold effect, which folds one half of the actor over its other half.
+
+
+
+ Solution
+
+ Subclass ClutterDeformEffect and
+ implement a deform_vertex() function
+ to modify the actor's vertices.
+
+ The signature for deform_vertex()
+ is:
+
+
+
+void
+deform_vertex (ClutterDeformEffect *effect,
+ gfloat width,
+ gfloat height,
+ CoglTextureVertex *vertex);
+
+
+
+ The width and height
+ are the width and height of the target material, stored in
+ the offscreen buffer. Usually the target material's size will
+ match the actor's transformed size; however, if the effect
+ implements create_texture(), the target
+ material's size may differ from the actor's transformed size.
+
+ The vertex contains the position
+ and color of a vertex, to be deformed by your effect.
+ Your deform_vertex()
+ function should modify the member variables of this
+ CoglTextureVertex in place. Usually, this will
+ mean modifying the x, y
+ and y member variables of the vertex,
+ which describe its position in 3D space.
+
+ The example function below, taken from
+ the
+ full example, applies a transformation to vertices falling
+ in the "right-hand" half of the actor (i.e. vertices with an
+ x value greater than or equal to half the
+ width of the actor).
+
+
+
+static void
+cb_page_fold_effect_deform_vertex (ClutterDeformEffect *effect,
+ gfloat width,
+ gfloat height,
+ CoglTextureVertex *vertex)
+{
+ CbPageFoldEffectPrivate *priv = CB_PAGE_FOLD_EFFECT (effect)->priv;
+
+ /* the rotation angle is modified by the percentage progress of the fold,
+ * as represented by the period variable
+ */
+ gfloat radians = (priv->angle * priv->period) / (180.0f / G_PI);
+
+ /* rotate from the center of the actor on the y axis */
+ gfloat adjusted_x = vertex->x - (width / 2);
+
+ /* only rotate vertices to the right of the middle of the actor */
+ if (adjusted_x >= 0.0)
+ {
+ vertex->x = (vertex->z * sin (radians))
+ + (adjusted_x * cos (radians))
+ + width / 2;
+
+ /* NB add 1 to z to prevent "z fighting"; otherwise, when fully-folded
+ * the image has "stripes" where vertices from the folded part
+ * of the actor interfere with vertices from the unfolded part
+ */
+ vertex->z = (vertex->z * cos (radians))
+ + (adjusted_x * sin (radians))
+ + 1;
+ }
+
+ /* adjust depth of all vertices so they fit inside the actor while folding;
+ * this has the effect of making the image smaller within the texture,
+ * but does produce a cleaner fold animation
+ */
+ vertex->z -= width / 2;
+}
+
+
+
+ Note that this effect has two properties set in its
+ constructor or through setters:
+
+
+
+ angle, representing the angle of
+ the full fold; for the actor to fully fold in half, this
+ would be set to 180.0
+
+
+
+ period, representing the percentage
+ of the fold to apply
+
+
+
+ As well as rotating the vertex, the
+ deform_vertex() function also shifts
+ the z coordinate "up" by 1
+ (towards the viewpoint) for vertices on the right-hand side of the
+ actor. This is so that the "folded over" vertices
+ are above vertices on the left-hand side. Without this small
+ shift, the vertices interfere with each other, which can cause striping
+ artefacts.
+
+ All vertices are also shifted "down",
+ so that the the folding part of the actor remains within the texture.
+ Otherwise the part which is folding may be clipped to the allocation of
+ the actor.
+
+ This effect can now be applied to an actor, using the
+ approach
+ outlined
+ in the introduction. The result looks like this when
+ period is set to 0.25 and angle
+ to 180.0 (i.e. the page is folded by 45 degrees):
+
+
+
+
+
+
+
+ Applying a custom ClutterDeformEffect
+ to a texture loaded with an image
+
+
+
+
+ Because the effect is a GObject which exposes its
+ properties, it can easily be animated, as described in
+ the
+ discussion section.
+
+
+
+
+ Discussion
+
+ A deform effect processes an actor as follows:
+
+
+
+
+ The actor is divided into a series of
+ triangular tiles. The number of
+ horizontal and vertical tiles is configurable;
+ more tiles implies more vertices. See
+ this
+ section for more details about tiles.
+
+
+
+ The position of each vertex of each
+ tile is then modified (or not) by the
+ deform_vertex() function. In this
+ function, you can change the vertex's position
+ (x, y,
+ z coordinates). You can also
+ modify the color at the vertex if desired.
+
+ The resulting deformed vertices are stored
+ in an offscreen buffer.
+
+
+
+ Once the deformation has been applied to
+ all vertices, the content of the offscreen buffer
+ is painted at the onscreen position of the actor.
+
+
+
+
+ You may find it useful to visualise this process by imagining
+ your actor's surface as a net, composed of triangles. (Something
+ like a fishing net, not a mathematical one.) At each corner of
+ each triangle is a marble; and between each pair of corners
+ is an infinitely flexible length of elastic. Moving a marble
+ doesn't change the position of its neighbours; it just stretches
+ or relaxes the elastic.
+
+ In this analogy, the marbles are the vertices; and the
+ surfaces between the marbles, bordered by triangles of
+ elastic, are the tiles. More triangles (tiles) means more
+ marbles (vertices).
+
+ When you create a ClutterDeformEffect,
+ think of it as specifying movements of marbles in the net.
+ Changing the position of a vertex corresponds to moving a marble
+ up/down (-/+ y position), left/right
+ (-/+ x position) or away/towards
+ you (-/+ z position) (ignoring color for the
+ moment).
+
+ Now imagine that you are asked to fold the whole net of
+ marbles; but you can't just grab the edge of the net and pull
+ it over: you can only move one marble at a time. However, once moved,
+ each marble magically stays where you put it in 3D space.
+
+ To do this, you could project where each marble would be if
+ you could fold the whole sheet in one go; then move the
+ marbles one by one to their projected positions. Even though
+ you'd be moving the marbles one at a time, it would eventually
+ look as though you'd folded the whole net with a single movement.
+
+ When you write a ClutterDeformEffect, you have
+ to accomplish a similar feat: change the shape of an actor
+ by individually modifying the positions of points on its surface. In
+ most cases, your deform_vertex() implementation
+ can take advantage of an existing geometric transformation
+ method to achieve this. (For example, the page fold in this recipe
+ is based on equations from p.412 of Computer
+ Graphics (C Version), 2nd Edition by Hearn and
+ Baker, 1996.)
+
+
+ Customising the back material
+
+ When you set up a deform effect, you
+ can optionally specify a material to use for the "back" of
+ any actor it is applied to.
+
+ If you think of an actor as a sheet of paper with a
+ picture on it, specifying a back is similar to turning the
+ sheet of paper over (rotating it around the
+ y axis) and drawing another picture on
+ the other side. If you then folded or twisted the paper,
+ you would be able to see parts of the pictures on both the
+ front and back of the paper.
+
+ Similarly, during deformation of an actor, if any
+ vertices of the actor are deformed such that the actor's surface
+ is folded or twisted over itself, parts of its back
+ become visible. If you set a back material, you will see parts
+ of that where the surface is folded over. If you don't set a back
+ material, you will instead see mirror images of parts of the actor's
+ front: as if the actor was flexible stained glass, rather than paper.
+ You can see this if you watch the animation in
+ this
+ section.
+
+ The back material should be an instance of
+ CoglMaterial. You can either create this via
+ the Cogl API directly; or indirectly through the Clutter API
+ (for example, by getting the material from a
+ ClutterTexture). The code below gives an example
+ of how to do the latter:
+
+
+
+
+
+
+
+ See the ClutterDeformEffect API reference
+ for more details about back materials.
+
+ Here's a screenshot of the
+ example
+ with the addition of a back material, folded at an angle
+ of 60 degrees:
+
+
+
+
+
+
+
+ Applying a custom ClutterDeformEffect
+ to a texture loaded with an image
+
+
+
+
+
+
+
+ Animating a custom deform effect
+
+ Clutter's animation API can animate any GObject which
+ exposes its properties. In the case of the page fold effect,
+ we can expose the period property using
+ standard GObject property installation:
+
+
+
+/* GObject class init */
+static void
+cb_page_fold_effect_class_init (CbPageFoldEffectClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ /* ...other class setup code... */
+
+ /* expose the period as a GObject property */
+ pspec = g_param_spec_double ("period",
+ "Period",
+ "The period of the page fold",
+ 0.0, 1.0,
+ 0.0,
+ G_PARAM_READWRITE);
+ obj_props[PROP_PERIOD] = pspec;
+ g_object_class_install_property (gobject_class, PROP_PERIOD, pspec);
+
+ /* ...install other properties... */
+}
+
+
+
+ We also add a get_property()
+ implementation, as well as a setter (see
+ the full
+ GObject implementation for details).
+
+ Then set up an animation for the property; in this case,
+ using a ClutterState:
+
+
+
+ClutterEffect *effect = cb_page_fold_effect_new (180.0, 0.0);
+
+ClutterState *transitions = clutter_state_new ();
+clutter_state_set_duration (transitions, NULL, NULL, 500);
+
+clutter_state_set (transitions, NULL, "unfolded",
+ effect, "period", CLUTTER_LINEAR, 0.0,
+ NULL);
+
+clutter_state_set (transitions, NULL, "folded",
+ effect, "period", CLUTTER_LINEAR, 1.0,
+ NULL);
+
+
+
+ To start the animation, warp the ClutterState
+ into its "unfolded" state, then set it to
+ "folded":
+
+
+
+/* this changes state instantaneously */
+clutter_state_warp_to_state (transitions, "unfolded");
+
+/* this starts an animation to the state */
+clutter_state_set_state (transitions, "folded");
+
+
+
+ Note that the
+ full code
+ sample is slightly more complex, as it triggers state
+ changes when a mouse button is pressed on the texture. There is
+ also a third "partially folded" state (used to create
+ the screenshot for the
+ previous
+ section).
+
+ Here's what the resulting animation looks like:
+
+
+
+
+
+
+ Video showing animation of a custom deform effect
+ on a texture
+
+
+
+
+
+
+ Tiles
+
+ A ClutterDeformEffect divides the actor
+ being deformed into a number of tiles: the larger the number
+ of tiles, the larger the number of vertices to be manipulated
+ by the effect. Increasing the number of tiles increases the number of
+ vertex computations required, which can slow down animations;
+ at the same time, finer-grained tiles can make an effect appear
+ smoother, particularly when animated.
+
+ Most of the time, the default number
+ of tiles in the x and y
+ axes should suffice. You can get the current number of
+ tiles associated with an effect with:
+
+
+
+
+
+
+
+ However, if an effect produces jerky or fragmented output,
+ you want to tweak the number of tiles. Use the
+ clutter_deform_effect_set_n_tiles() function
+ to do this:
+
+
+
+/* 64 tiles in both axes */
+guint x_tiles = 64;
+guint y_tiles = 64;
+
+clutter_deform_effect_set_n_tiles (CLUTTER_DEFORM_EFFECT (effect),
+ x_tiles,
+ y_tiles);
+
+
+
+
+
+
+
+
+ Full example
+
+ This example consists of three files:
+
+
+
+ A header
+ file for the CbPageFoldEffect GObject.
+
+
+ The
+ code file implementing CbPageFoldEffect.
+
+
+ A short
+ sample application which applies a CbPageFoldEffect
+ instance to an actor and animates the fold when the actor is
+ clicked.
+
+
+
+ As Clutter effect subclasses are written using GObject,
+ you might find this recipe
+ (which goes into GObject in more detail) a useful introduction.
+
+
+ cb-page-fold-effect.h (header file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+ cb-page-fold-effect.c (code file)
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+ Application which uses CbPageFoldEffect
+ to do animated folding of a ClutterTexture
+
+
+ a code sample should be here... but isn't
+
+
+
+
+
+
+
+
+
diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am
index 2e4e28acd..fa7a8781a 100644
--- a/doc/cookbook/examples/Makefile.am
+++ b/doc/cookbook/examples/Makefile.am
@@ -18,6 +18,8 @@ noinst_PROGRAMS = \
animations-rotating \
animations-scaling \
animations-scaling-zoom \
+ effects-built-in \
+ effects-custom-deform \
text-shadow \
textures-reflection \
textures-split-go \
@@ -81,6 +83,8 @@ animations_reuse_SOURCES = animations-reuse.c
animations_rotating_SOURCES = animations-rotating.c
animations_scaling_SOURCES = animations-scaling.c
animations_scaling_zoom_SOURCES = animations-scaling-zoom.c
+effects_built_in_SOURCES = effects-built-in.c
+effects_custom_deform_SOURCES = cb-page-fold-effect.c cb-page-fold-effect.h effects-custom-deform.c
text_shadow_SOURCES = text-shadow.c
textures_reflection_SOURCES = textures-reflection.c
textures_split_go_SOURCES = textures-split-go.c
diff --git a/doc/cookbook/examples/cb-page-fold-effect.c b/doc/cookbook/examples/cb-page-fold-effect.c
new file mode 100644
index 000000000..272703503
--- /dev/null
+++ b/doc/cookbook/examples/cb-page-fold-effect.c
@@ -0,0 +1,250 @@
+#include
+#include "cb-page-fold-effect.h"
+
+G_DEFINE_TYPE (CbPageFoldEffect, cb_page_fold_effect, CLUTTER_TYPE_DEFORM_EFFECT);
+
+#define CB_PAGE_FOLD_EFFECT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ CB_TYPE_PAGE_FOLD_EFFECT, \
+ CbPageFoldEffectPrivate))
+
+struct _CbPageFoldEffectPrivate
+{
+ gdouble angle;
+ gdouble period;
+};
+
+enum {
+ PROP_0,
+
+ PROP_PERIOD,
+ PROP_ANGLE,
+
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+/* ClutterDeformEffect implementation */
+static void
+cb_page_fold_effect_deform_vertex (ClutterDeformEffect *effect,
+ gfloat width,
+ gfloat height,
+ CoglTextureVertex *vertex)
+{
+ CbPageFoldEffectPrivate *priv = CB_PAGE_FOLD_EFFECT (effect)->priv;
+
+ gfloat radians = (priv->angle * priv->period) / (180.0f / G_PI);
+
+ /* rotate from the center of the actor on the y axis */
+ gfloat adjusted_x = vertex->x - (width / 2);
+
+ /* only rotate vertices to the right of the middle of the actor */
+ if (adjusted_x >= 0.0)
+ {
+ vertex->x = (vertex->z * sin (radians))
+ + (adjusted_x * cos (radians))
+ + width / 2;
+
+ /* NB add 1 to z to prevent "z fighting"; otherwise, when fully-folded
+ * the image has "stripes" where vertices from the folded part
+ * of the actor interfere with vertices from the unfolded part
+ */
+ vertex->z = (vertex->z * cos (radians))
+ + (adjusted_x * sin (radians))
+ + 1;
+ }
+
+ /* adjust depth of all vertices so they fit inside the actor while folding;
+ * this has the effect of making the image smaller within the texture,
+ * but does produce a cleaner fold animation
+ */
+ vertex->z -= width / 2;
+}
+
+/* GObject implementation */
+static void
+cb_page_fold_effect_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CbPageFoldEffect *effect = CB_PAGE_FOLD_EFFECT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_PERIOD:
+ cb_page_fold_effect_set_period (effect, g_value_get_double (value));
+ break;
+
+ case PROP_ANGLE:
+ cb_page_fold_effect_set_angle (effect, g_value_get_double (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+cb_page_fold_effect_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CbPageFoldEffectPrivate *priv = CB_PAGE_FOLD_EFFECT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_PERIOD:
+ g_value_set_double (value, priv->period);
+ break;
+
+ case PROP_ANGLE:
+ g_value_set_double (value, priv->angle);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+/* GObject class and instance init */
+static void
+cb_page_fold_effect_class_init (CbPageFoldEffectClass *klass)
+{
+ GParamSpec *pspec;
+ ClutterDeformEffectClass *effect_class = CLUTTER_DEFORM_EFFECT_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ effect_class->deform_vertex = cb_page_fold_effect_deform_vertex;
+
+ gobject_class->set_property = cb_page_fold_effect_set_property;
+ gobject_class->get_property = cb_page_fold_effect_get_property;
+
+ g_type_class_add_private (klass, sizeof (CbPageFoldEffectPrivate));
+
+ /**
+ * CbPageFoldEffect:period:
+ *
+ * The period of the page fold, between 0.0 (no fold) and
+ * 1.0 (fully folded)
+ */
+ pspec = g_param_spec_double ("period",
+ "Period",
+ "The period of the page fold",
+ 0.0, 1.0,
+ 0.0,
+ G_PARAM_READWRITE);
+ obj_props[PROP_PERIOD] = pspec;
+ g_object_class_install_property (gobject_class, PROP_PERIOD, pspec);
+
+ /**
+ * CbPageFoldEffect:angle:
+ *
+ * The angle of the page fold, in degrees, between 0.0 and 180.0
+ */
+ pspec = g_param_spec_double ("angle",
+ "Angle",
+ "The angle of the page fold, in degrees",
+ 0.0, 180.0,
+ 0.0,
+ G_PARAM_READWRITE);
+ obj_props[PROP_ANGLE] = pspec;
+ g_object_class_install_property (gobject_class, PROP_ANGLE, pspec);
+}
+
+static void
+cb_page_fold_effect_init (CbPageFoldEffect *self)
+{
+ CbPageFoldEffectPrivate *priv;
+
+ priv = self->priv = CB_PAGE_FOLD_EFFECT_GET_PRIVATE (self);
+
+ priv->period = 0.0;
+ priv->angle = 0.0;
+}
+
+/* public API */
+ClutterEffect *
+cb_page_fold_effect_new (gdouble angle,
+ gdouble period)
+{
+ return g_object_new (CB_TYPE_PAGE_FOLD_EFFECT,
+ "angle", angle,
+ "period", period,
+ NULL);
+}
+
+/**
+ * cb_page_fold_effect_set_period:
+ * @effect: a #CbPageFoldEffect
+ * @period: the period of the page fold, between 0.0 and 1.0
+ *
+ * Sets the period of the page fold, between 0.0 (no fold)
+ * and 1.0 (fully folded)
+ */
+void
+cb_page_fold_effect_set_period (CbPageFoldEffect *effect,
+ gdouble period)
+{
+ g_return_if_fail (CB_IS_PAGE_FOLD_EFFECT (effect));
+ g_return_if_fail (period >= 0.0 && period <= 1.0);
+
+ effect->priv->period = period;
+
+ clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (effect));
+}
+
+/**
+ * cb_page_fold_effect_get_period:
+ * @effect: a #CbPageFoldEffect
+ *
+ * Retrieves the value set using cb_page_fold_effect_get_period()
+ *
+ * Return value: the period of the page fold
+ */
+gdouble
+cb_page_fold_effect_get_period (CbPageFoldEffect *effect)
+{
+ g_return_val_if_fail (CB_IS_PAGE_FOLD_EFFECT (effect), 0.0);
+
+ return effect->priv->period;
+}
+
+/**
+ * cb_page_fold_effect_set_angle:
+ * @effect: #CbPageFoldEffect
+ * @angle: the angle of the page fold, in degrees
+ *
+ * Sets the angle of the page fold, in degrees; must be a value between
+ * 0.0 and 180.0
+ */
+void
+cb_page_fold_effect_set_angle (CbPageFoldEffect *effect,
+ gdouble angle)
+{
+ g_return_if_fail (CB_IS_PAGE_FOLD_EFFECT (effect));
+ g_return_if_fail (angle >= 0.0 && angle <= 180.0);
+
+ effect->priv->angle = angle;
+
+ clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (effect));
+}
+
+/**
+ * cb_page_fold_effect_get_angle:
+ * @effect: a #CbPageFoldEffect:
+ *
+ * Retrieves the angle of the page fold, in degrees
+ *
+ * Return value: the angle of the page fold
+ */
+gdouble
+cb_page_fold_effect_get_angle (CbPageFoldEffect *effect)
+{
+ g_return_val_if_fail (CB_IS_PAGE_FOLD_EFFECT (effect), 0.0);
+
+ return effect->priv->angle;
+}
diff --git a/doc/cookbook/examples/cb-page-fold-effect.h b/doc/cookbook/examples/cb-page-fold-effect.h
new file mode 100644
index 000000000..1fcb8d746
--- /dev/null
+++ b/doc/cookbook/examples/cb-page-fold-effect.h
@@ -0,0 +1,49 @@
+#ifndef __CB_PAGE_FOLD_EFFECT_H__
+#define __CB_PAGE_FOLD_EFFECT_H__
+
+#include
+
+GType cb_page_fold_effect_get_type (void);
+
+#define CB_TYPE_PAGE_FOLD_EFFECT (cb_page_fold_effect_get_type ())
+#define CB_PAGE_FOLD_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CB_TYPE_PAGE_FOLD_EFFECT, \
+ CbPageFoldEffect))
+#define CB_IS_PAGE_FOLD_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ CB_TYPE_PAGE_FOLD_EFFECT))
+#define CB_PAGE_FOLD_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ CB_TYPE_PAGE_FOLD_EFFECT, \
+ CbPageFoldEffectClass))
+#define CB_IS_PAGE_FOLD_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ CB_TYPE_PAGE_FOLD_EFFECT))
+#define CB_PAGE_FOLD_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ CB_TYPE_PAGE_FOLD_EFFECT, \
+ CbPageFoldEffectClass))
+
+typedef struct _CbPageFoldEffectPrivate CbPageFoldEffectPrivate;
+typedef struct _CbPageFoldEffect CbPageFoldEffect;
+typedef struct _CbPageFoldEffectClass CbPageFoldEffectClass;
+
+/* object */
+struct _CbPageFoldEffect
+{
+ ClutterDeformEffect parent_instance;
+ CbPageFoldEffectPrivate *priv;
+};
+
+/* class */
+struct _CbPageFoldEffectClass
+{
+ ClutterDeformEffectClass parent_class;
+};
+
+ClutterEffect *cb_page_fold_effect_new (gdouble angle,
+ gdouble period);
+void cb_page_fold_effect_set_angle (CbPageFoldEffect *effect,
+ gdouble angle);
+void cb_page_fold_effect_set_period (CbPageFoldEffect *effect,
+ gdouble period);
+gdouble cb_page_fold_effect_get_period (CbPageFoldEffect *effect);
+gdouble cb_page_fold_effect_get_angle (CbPageFoldEffect *effect);
+
+#endif /* __CB_PAGE_FOLD_EFFECT_H__ */
diff --git a/doc/cookbook/examples/effects-built-in.c b/doc/cookbook/examples/effects-built-in.c
new file mode 100644
index 000000000..a7eb3ceef
--- /dev/null
+++ b/doc/cookbook/examples/effects-built-in.c
@@ -0,0 +1,58 @@
+#include
+
+int
+main (int argc,
+ char *argv[])
+{
+ ClutterActor *stage;
+ ClutterActor *texture;
+ ClutterConstraint *constraint_x;
+ ClutterConstraint *constraint_y;
+ ClutterColor *pink;
+ ClutterEffect *effect;
+ gchar *filename;
+
+ if (argc < 2)
+ {
+ g_print ("Usage: %s \n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+
+ clutter_init (&argc, &argv);
+
+ stage = clutter_stage_new ();
+ clutter_actor_set_size (stage, 400, 400);
+ g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+
+ texture = clutter_texture_new ();
+ clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
+ clutter_actor_set_width (texture, 300);
+
+ /* NB ignoring missing file errors here for brevity */
+ clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
+ filename,
+ NULL);
+
+ /* align the texture on the x and y axes */
+ constraint_x = clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5);
+ constraint_y = clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5);
+ clutter_actor_add_constraint (texture, constraint_x);
+ clutter_actor_add_constraint (texture, constraint_y);
+
+ /* create a colorize effect with pink tint */
+ pink = clutter_color_new (230, 187, 210, 255);
+ effect = clutter_colorize_effect_new (pink);
+
+ /* apply the effect to the texture */
+ clutter_actor_add_effect (texture, effect);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ return 0;
+}
diff --git a/doc/cookbook/examples/effects-custom-deform.c b/doc/cookbook/examples/effects-custom-deform.c
new file mode 100644
index 000000000..55094bfd4
--- /dev/null
+++ b/doc/cookbook/examples/effects-custom-deform.c
@@ -0,0 +1,119 @@
+/* Example of using a custom CbPageFoldEffect to do
+ * an animated fold of a texture containing an image
+ *
+ * Pass the full path to the image on the command line;
+ * click on the texture to trigger the folding animation
+ */
+#include
+#include
+
+#include "cb-page-fold-effect.h"
+
+static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
+
+static gboolean
+button_pressed_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer user_data)
+{
+ ClutterState *transitions = CLUTTER_STATE (user_data);
+
+ if (g_strcmp0 (clutter_state_get_state (transitions), "folded") == 0)
+ clutter_state_set_state (transitions, "unfolded");
+ else
+ clutter_state_set_state (transitions, "folded");
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ ClutterActor *stage;
+ ClutterActor *texture;
+ ClutterEffect *effect;
+ ClutterState *transitions;
+ GError *error = NULL;
+
+ gchar *filename;
+
+ if (argc < 2)
+ {
+ g_print ("Usage: %s \n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ filename = argv[1];
+
+ clutter_init (&argc, &argv);
+
+ stage = clutter_stage_new ();
+ clutter_actor_set_size (stage, 400, 300);
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+ g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+
+ texture = clutter_texture_new ();
+ clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
+ clutter_actor_set_width (texture, 400);
+ clutter_actor_set_reactive (texture, TRUE);
+ clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
+ filename,
+ &error);
+
+ if (error != NULL)
+ {
+ g_critical ("Error loading texture from file %s; error was:\n%s",
+ filename,
+ error->message);
+ return EXIT_FAILURE;
+ }
+
+ /* create the page fold effect instance with destination fold angle
+ * of 180 degrees and starting period of 0 (no folding)
+ */
+ effect = cb_page_fold_effect_new (180.0, 0.0);
+
+ /* add the effect to the texture actor */
+ clutter_actor_add_effect (texture, effect);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);
+
+ /* animation for the period property of the effect,
+ * to animate its value between 0.0 and 1.0 and back
+ */
+ transitions = clutter_state_new ();
+ clutter_state_set_duration (transitions, NULL, NULL, 500);
+
+ clutter_state_set_duration (transitions,
+ "partially-folded",
+ "folded",
+ 375);
+
+ clutter_state_set (transitions, NULL, "folded",
+ effect, "period", CLUTTER_LINEAR, 1.0,
+ NULL);
+
+ clutter_state_set (transitions, NULL, "partially-folded",
+ effect, "period", CLUTTER_LINEAR, 0.25,
+ NULL);
+
+ clutter_state_set (transitions, NULL, "unfolded",
+ effect, "period", CLUTTER_LINEAR, 0.0,
+ NULL);
+
+ clutter_state_warp_to_state (transitions, "partially-folded");
+
+ g_signal_connect (texture,
+ "button-press-event",
+ G_CALLBACK (button_pressed_cb),
+ transitions);
+
+ clutter_actor_show (stage);
+
+ clutter_main ();
+
+ g_object_unref (transitions);
+
+ return EXIT_SUCCESS;
+}
diff --git a/doc/cookbook/images/effects-built-in.png b/doc/cookbook/images/effects-built-in.png
new file mode 100644
index 000000000..81606023d
Binary files /dev/null and b/doc/cookbook/images/effects-built-in.png differ
diff --git a/doc/cookbook/images/effects-custom-deform-back-material.png b/doc/cookbook/images/effects-custom-deform-back-material.png
new file mode 100644
index 000000000..975e227eb
Binary files /dev/null and b/doc/cookbook/images/effects-custom-deform-back-material.png differ
diff --git a/doc/cookbook/images/effects-custom-deform.png b/doc/cookbook/images/effects-custom-deform.png
new file mode 100644
index 000000000..83dc9dff3
Binary files /dev/null and b/doc/cookbook/images/effects-custom-deform.png differ
diff --git a/doc/cookbook/videos/effects-custom-deform.ogv b/doc/cookbook/videos/effects-custom-deform.ogv
new file mode 100644
index 000000000..bf7f95978
Binary files /dev/null and b/doc/cookbook/videos/effects-custom-deform.ogv differ