57a5f2296a
pre_paint() and post_paint() implementations don't need to check whether an effect is disabled: Clutter will not apply an effect unless it is enabled. So remove code which checks whether the effect is enabled or disabled from the example applications and the documentation.
1300 lines
47 KiB
XML
1300 lines
47 KiB
XML
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
<chapter id="effects" xmlns:xi="http://www.w3.org/2003/XInclude">
|
|
<title>Effects</title>
|
|
|
|
<epigraph>
|
|
<attribution>Roger Zelazny, from <citetitle>Prince of Chaos</citetitle>
|
|
</attribution>
|
|
<para>Don't wake me for the end of the world unless it has very
|
|
good special effects</para>
|
|
</epigraph>
|
|
|
|
<section id="effects-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>Effects modify an actor's appearance, such
|
|
as how it is positioned, colored and textured.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>The recipes in this section of the cookbook cover how to create
|
|
your own effects as well as how to apply Clutter's effects.</para>
|
|
|
|
<section>
|
|
<title>Creating effects using the abstract effect classes</title>
|
|
|
|
<tip>
|
|
<para>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.</para>
|
|
</tip>
|
|
|
|
<para>Each abstract effect class is tailored to modifying different
|
|
aspects of an actor, as explained below:</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
<formalpara>
|
|
<title><type>ClutterEffect</type></title>
|
|
<para>If you're just using the Clutter and Cogl APIs to
|
|
decorate an actor, this is simplest type of effect to
|
|
implement.</para>
|
|
</formalpara>
|
|
|
|
<para>Subclassing <type>ClutterEffect</type> enables you to
|
|
"wrap" how an actor is painted, by injecting some code before
|
|
and/or after the actor's own <function>paint()</function>
|
|
implementation.</para>
|
|
|
|
<note>
|
|
<para>This is the preferred way to modify how an actor is
|
|
painted, short of creating your own actor subclass.</para>
|
|
</note>
|
|
|
|
<para><emphasis>Subclasses of
|
|
<type>ClutterEffect</type></emphasis>:</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
<formalpara>
|
|
<title><type>ClutterOffscreenEffect</type></title>
|
|
|
|
<para>Use this class as a basis if you need GL textures
|
|
for your effect.</para>
|
|
</formalpara>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>An example is <type>ClutterBlurEffect</type>,
|
|
which uses a GLSL fragment shader to blur an
|
|
actor's appearance in an offscreen framebuffer.</para>
|
|
|
|
<para><emphasis>Subclasses of
|
|
<type>ClutterOffscreenEffect</type></emphasis>:</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
|
<title><type>ClutterDeformEffect</type></title>
|
|
|
|
<para>Use this base class if you want to modify
|
|
an actor's geometry, at the level of individual
|
|
vertices.</para>
|
|
</formalpara>
|
|
|
|
<para><type>ClutterDeformEffect</type> 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.</para>
|
|
|
|
<para>If you are writing your own deform effects,
|
|
a good example to work from is
|
|
<type>ClutterPageTurnEffect</type>.</para>
|
|
|
|
<para>There is also a
|
|
<link linkend="effects-custom-deform">recipe which
|
|
explains how to implement a simple custom deform
|
|
effect</link> (a page fold).</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<formalpara>
|
|
<title><type>ClutterShaderEffect</type></title>
|
|
|
|
<para>Use this if you want to apply custom
|
|
GLSL vertex or fragment shaders to your actors.</para>
|
|
</formalpara>
|
|
|
|
<para>Writing <type>ClutterShaderEffects</type> gives
|
|
you very fine-grained control over the GL pipeline.
|
|
However, this makes them the most complex
|
|
effects to implement.</para>
|
|
|
|
<tip>
|
|
<para>If you want to write your own GLSL shaders, the
|
|
<ulink url="http://www.opengl.org/documentation/glsl/">GLSL
|
|
specification</ulink> is a good starting point.</para>
|
|
</tip>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
<section id="effects-introduction-using-the-built-in-effects">
|
|
<title>Using the built-in effects</title>
|
|
|
|
<para>Clutter comes with a number of built-in effects
|
|
which can easily be applied to your actors. This section
|
|
explains how to do this.</para>
|
|
|
|
<para>First, create an actor. For this
|
|
example, we use a texture loaded with an image:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* 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... */
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Next, create an instance of an effect; here, we're
|
|
creating a <type>ClutterColorizeEffect</type> with a pink tint:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterColor *pink = clutter_color_new (230, 187, 210, 255);
|
|
ClutterEffect *effect = clutter_colorize_effect_new (pink);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Finally, apply the effect to the actor:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_actor_add_effect (texture, effect);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>The result in this case is an image colorized with
|
|
a pink tint, like this:</para>
|
|
|
|
<screenshot>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata format="PNG"
|
|
fileref="images/effects-built-in.png" />
|
|
</imageobject>
|
|
<alt>
|
|
<para>Applying a <type>ClutterColorizeEffect</type>
|
|
to a texture loaded with an image (drawing by
|
|
Madeleine Smith)</para>
|
|
</alt>
|
|
</mediaobject>
|
|
</screenshot>
|
|
|
|
<para>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
|
|
<type>ClutterEffect</type> instances so your effect can
|
|
be added to an actor through the standard API.</para>
|
|
|
|
<para>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,
|
|
<type>ClutterAnimator</type> or <type>ClutterState</type>. For
|
|
example, the <type>ClutterPageTurnEffect</type> can be animated
|
|
by manipulating its <varname>period</varname> property. An example
|
|
of how to do this for your own effect is given in the
|
|
<link linkend="effects-custom-deform">custom deform effect
|
|
recipe</link>.</para>
|
|
|
|
<para>The full code for the <type>ClutterColorizeEffect</type>
|
|
example is below.</para>
|
|
|
|
<example id="effects-introduction-example-1">
|
|
<title>Applying a <type>ClutterColorizeEffect</type> to
|
|
a texture loaded with an image</title>
|
|
<programlisting>
|
|
<xi:include href="examples/effects-built-in.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="effects-basic">
|
|
<title>Changing an actor's paint sequence using
|
|
<type>ClutterEffect</type></title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to paint on top of or under an actor in a generic
|
|
way, without editing the actor's <function>paint()</function>
|
|
implementation. Example use cases are:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Adding a border on top of an actor.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>Drawing a background for an actor.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>A quick way to achieve the same thing (though not
|
|
readily portable between actors) is to connect a callback
|
|
before or after an actor's <emphasis>paint</emphasis> signal.
|
|
See <link linkend="actors-paint-wrappers">this recipe</link> for
|
|
more details. However, using a <type>ClutterEffect</type>
|
|
implementation, as explained in this recipe, is the preferred
|
|
approach.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution</title>
|
|
|
|
<para>Create a subclass of the <type>ClutterEffect</type> abstract
|
|
class; then implement the <function>pre_paint()</function> and/or
|
|
<function>post_paint()</function> virtual functions. When the
|
|
effect is applied to an actor, these functions will paint
|
|
before and after the actor's own <function>paint()</function>
|
|
implementation.</para>
|
|
|
|
<note>
|
|
<para>For this solution, we implement a simple
|
|
<type>CbBackgroundEffect</type> which draws a gray rectangle
|
|
under an actor. The full source is in
|
|
<link linkend="effects-basic-example-cbbackgroundeffect">this
|
|
section</link>. To keep it simple, the effect has no properties
|
|
and isn't configurable (the background is always gray); see the
|
|
<link linkend="effects-basic-example-cbbordereffect">border
|
|
effect</link> for a more detailed implementation with GObject
|
|
trimmings.</para>
|
|
</note>
|
|
|
|
<para>First, create a <type>ClutterEffect</type> subclass. This
|
|
requires the trappings of a GObject class; in particular,
|
|
it needs a private struct to hold the effect's state. This
|
|
should include any <type>CoglMaterials</type>,
|
|
<type>CoglColors</type> or other private member variables
|
|
you intend to use to draw the effect.</para>
|
|
|
|
<para>In the case of the background effect, we have a background
|
|
<type>CoglMaterial</type> and a <type>CoglColor</type> for that
|
|
material:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
struct _CbBackgroundEffectPrivate
|
|
{
|
|
CoglMaterial *background;
|
|
CoglColor *color;
|
|
};
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>In the <function>init()</function> function for objects of
|
|
your class, create any Cogl resources which you need to draw the
|
|
effect. In the case of the background effect,
|
|
we need to create the <type>CoglMaterial</type> and
|
|
<type>CoglColor</type> for the private struct:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
static void
|
|
cb_background_effect_init (CbBackgroundEffect *self)
|
|
{
|
|
/* get the private struct for the object
|
|
CbBackgroundEffectPrivate *priv;
|
|
priv = self->priv = CB_BACKGROUND_EFFECT_GET_PRIVATE (self);
|
|
|
|
/* create the background material */
|
|
priv->background = cogl_material_new ();
|
|
|
|
/* gray color for filling the background material */
|
|
priv->color = cogl_color_new ();
|
|
cogl_color_init_from_4ub (priv->color, 122, 122, 122, 255);
|
|
|
|
/* set the color on the material; NB this isn't configurable
|
|
* for this effect, and is always gray
|
|
*/
|
|
cogl_material_set_color (priv->background, priv->color);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Optionally, you can create GObject properties for
|
|
the class, if you want a configurable effect: see
|
|
<link linkend="effects-basic-discussion-properties">this
|
|
section</link> for details.</para>
|
|
|
|
<para>The <function>dispose()</function> function for your effect
|
|
should clean up any Cogl resources:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
static void
|
|
cb_background_effect_dispose (GObject *gobject)
|
|
{
|
|
CbBackgroundEffectPrivate *priv = CB_BACKGROUND_EFFECT (gobject)->priv;
|
|
|
|
if (priv->background != COGL_INVALID_HANDLE)
|
|
{
|
|
cogl_handle_unref (priv->background);
|
|
priv->background = COGL_INVALID_HANDLE;
|
|
}
|
|
|
|
if (priv->color != NULL)
|
|
{
|
|
cogl_color_free (priv->color);
|
|
priv->color = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (cb_background_effect_parent_class)->dispose (gobject);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Now, the important part: implement <function>pre_paint()</function>
|
|
and/or <function>post_paint()</function>, using Cogl to draw on the
|
|
material(s) set up for the effect.</para>
|
|
|
|
<para>For the background effect, we implement <function>pre_paint()</function>,
|
|
to draw a gray rectangle under the actor:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* note that if pre_paint() returns FALSE
|
|
* any post_paint() defined for the effect will not be called
|
|
*/
|
|
static gboolean
|
|
cb_background_effect_pre_paint (ClutterEffect *self)
|
|
{
|
|
ClutterActor *actor;
|
|
gfloat width;
|
|
gfloat height;
|
|
CbBackgroundEffectPrivate *priv;
|
|
|
|
priv = CB_BACKGROUND_EFFECT (self)->priv;
|
|
|
|
/* get the associated actor's dimensions */
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
|
|
clutter_actor_get_size (actor, &width, &height);
|
|
|
|
/* draw a Cogl rectangle in the background using the default color */
|
|
cogl_set_source (priv->background);
|
|
|
|
/* the rectangle has the same dimensions as the actor */
|
|
cogl_rectangle (0, 0, width, height);
|
|
|
|
return TRUE;
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Now, in the <function>init()</function> function for the
|
|
effect <emphasis>class</emphasis>, assign your implementations to the
|
|
virtual methods of the <type>ClutterEffect</type> abstract class:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
static void
|
|
cb_background_effect_class_init (CbBackgroundEffectClass *klass)
|
|
{
|
|
ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
effect_class->pre_paint = cb_background_effect_pre_paint;
|
|
gobject_class->dispose = cb_background_effect_dispose;
|
|
|
|
g_type_class_add_private (klass, sizeof (CbBackgroundEffectPrivate));
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>If you intend to make your effect reusable, provide
|
|
a public constructor (as is done for the example effects in this
|
|
recipe):</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterEffect *
|
|
cb_background_effect_new ()
|
|
{
|
|
return g_object_new (CB_TYPE_BACKGROUND_EFFECT,
|
|
NULL);
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>The effect is now ready to be used. The application code
|
|
for applying your effect to an actor is the same as for any
|
|
other effect:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterActor *texture;
|
|
ClutterEffect *background_effect;
|
|
|
|
/* ...initialize texture, load image file etc... */
|
|
|
|
/* create a gray background effect */
|
|
background_effect = cb_background_effect_new ();
|
|
|
|
/* apply the effect to the actor */
|
|
clutter_actor_add_effect (texture, background_effect);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Below is an example of applying this effect to a texture loaded
|
|
with an image; the image has a transparent background, so the
|
|
background is visible through it. The screenshot is from
|
|
the <link linkend="effects-basic-example-5">example
|
|
application</link>:</para>
|
|
|
|
<screenshot>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata format="PNG"
|
|
fileref="images/effects-basic-background.png" />
|
|
</imageobject>
|
|
<alt>
|
|
<para>Applying <type>CbBackgroundEffect</type>
|
|
to a texture loaded with an image that has a transparent
|
|
background</para>
|
|
</alt>
|
|
</mediaobject>
|
|
</screenshot>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<para>A basic <type>ClutterEffect</type> is particularly useful for
|
|
amending the appearance of an actor on the fly: for example,
|
|
to highlight an actor in response to a button presses. This
|
|
<emphasis>could</emphasis> be done by creating a custom widget
|
|
whose appearance could be toggled. But what if you wanted to make
|
|
an arbitrary actor's appearance "togglable"? A generic effect
|
|
in the style of the border effect in this recipe can be applied
|
|
to any actor, and easily toggled by enabling/disabling the
|
|
effect.</para>
|
|
|
|
<para><type>ClutterEffect</type> works best where
|
|
you want to overlay or underlay the actor with Cogl paths or
|
|
primitives, without changing the actor's geometry. If you want
|
|
to do complicated geometry transformations, or other subtle
|
|
manipulations of an actor's appearance, it is better to use
|
|
a <type>ClutterEffect</type> subclass like
|
|
<type>ClutterOffscreenEffect</type>, <type>ClutterDeformEffect</type>,
|
|
or <type>ClutterShaderEffect</type>.</para>
|
|
|
|
<para>In a similar vein, when a <type>ClutterEffect</type> is
|
|
applied to an actor, the effect shouldn't paint outside the actor's
|
|
allocation. However, if the effect provides a
|
|
<function>get_paint_volume()</function> implementation which
|
|
returns a volume larger than the actor's allocation, the effect
|
|
<emphasis>can</emphasis> paint anywhere within that volume. Though
|
|
in most cases, creating a custom paint volume is only going to be
|
|
useful for offscreen effects, where you are changing the
|
|
actor's geometry.</para>
|
|
|
|
<section id="effects-basic-discussion-properties">
|
|
<title>Effect properties</title>
|
|
|
|
<para>If your effect has GObject properties, you should
|
|
ensure that an actor associated with the effect is queued
|
|
for a redraw when those properties change. (You only need to
|
|
do this for properties which change the effect's appearance;
|
|
but this is likely to include most of an effect's properties.)</para>
|
|
|
|
<para>In most cases, you're likely define standard GObject
|
|
properties for the class; for example,
|
|
<link linkend="effects-basic-example-2"><type>CbBorderEffect</type></link>
|
|
defines a <varname>width</varname> property like this:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
static void
|
|
cb_border_effect_class_init (CbBorderEffectClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
/* ...more class initialization code here... */
|
|
|
|
pspec = g_param_spec_float ("width",
|
|
"Width",
|
|
"The width of the border (in pixels)",
|
|
1.0, 100.0,
|
|
10.0,
|
|
G_PARAM_READWRITE);
|
|
obj_props[PROP_WIDTH] = pspec;
|
|
g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
|
|
|
|
/* ...more property definitions...*/
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>It also defines a standard GObject
|
|
<function>set_property()</function> function for
|
|
<varname>width</varname>:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
static void
|
|
cb_border_effect_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CbBorderEffect *effect = CB_BORDER_EFFECT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
/* ...other cases here ... */
|
|
|
|
case PROP_WIDTH:
|
|
cb_border_effect_set_width (effect, g_value_get_float (value));
|
|
break;
|
|
|
|
/* ...default case ... */
|
|
}
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note that this calls
|
|
<function>cb_border_effect_set_width()</function>, which is
|
|
also exposed in the public API. This is where the
|
|
<varname>width</varname> member variable is actually set in
|
|
the private struct; and also where the redraw for the actor
|
|
associated with the effect should be queued:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* queues a redraw of the actor associated with the effect, if there is one */
|
|
static void
|
|
cb_border_effect_update (CbBorderEffect *self)
|
|
{
|
|
ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
|
|
|
|
/* this guard is necessary as an effect's properties can be manipulated
|
|
* before it has an actor associated with it
|
|
*/
|
|
if (actor != NULL)
|
|
clutter_actor_queue_redraw (actor);
|
|
}
|
|
|
|
/* public setter for the width property, which calls the update function */
|
|
void
|
|
cb_border_effect_set_width (CbBorderEffect *self,
|
|
gfloat width)
|
|
{
|
|
CbBorderEffectPrivate *priv;
|
|
|
|
g_return_if_fail (CB_IS_BORDER_EFFECT (self));
|
|
|
|
priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
|
|
|
|
priv->width = width;
|
|
|
|
/* the property has been updated, so queue a redraw of the actor (if set) */
|
|
cb_border_effect_update (self);
|
|
}
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Any other property setters which affect the associated
|
|
actor's appearance (i.e. color in the case of
|
|
<type>CbBorderEffect</type>) should also call the update
|
|
function after setting the property.</para>
|
|
|
|
<tip>
|
|
<para>If your effect exposes GObject properties in this way,
|
|
it can also be animated with the Clutter animation API as usual.
|
|
For example, you could animate the border effect in this recipe
|
|
so that the border gradually becomes thinner or thicker.</para>
|
|
</tip>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="effects-basic-example">
|
|
<title>Full example</title>
|
|
|
|
<para>The example application applies two effects to a
|
|
group of <type>ClutterTextures</type>:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>A <type>CbBackgroundEffect</type> which draws a gray
|
|
background under each actor. The effect is implemented in
|
|
<link linkend="effects-basic-example-1">a header
|
|
file</link> and <link linkend="effects-basic-example-2">a C
|
|
code file</link>.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>A <type>CbBorderEffect</type> which draws a
|
|
red border on top of an actor; this is toggled by clicking
|
|
on the actor. The effect is implemented in
|
|
<link linkend="effects-basic-example-3">a header
|
|
file</link> and <link linkend="effects-basic-example-4">a C
|
|
code file</link>.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The <link linkend="effects-basic-example-5">application</link>
|
|
creates textures from the file paths specified
|
|
on the command line then applies both of these effects to
|
|
each texture. In the case of the <type>CbBorderEffect</type>,
|
|
a 5 pixel red border is applied; this is also disabled by default,
|
|
and enabled when a texture is clicked.</para>
|
|
|
|
<para>Here is an example of the output when the application is loaded
|
|
with four images:</para>
|
|
|
|
<screenshot>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata format="PNG"
|
|
fileref="images/effects-basic.png" />
|
|
</imageobject>
|
|
<alt>
|
|
<para>Applying <type>CbBackgroundEffect</type>
|
|
and a togglable <type>CbBorderEffect</type>
|
|
to a several textures</para>
|
|
</alt>
|
|
</mediaobject>
|
|
</screenshot>
|
|
|
|
<section id="effects-basic-example-cbbackgroundeffect">
|
|
<title><type>CbBackgroundEffect</type></title>
|
|
|
|
<example id="effects-basic-example-1">
|
|
<title><filename>cb-background-effect.h</filename> (header file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-background-effect.h" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="effects-basic-example-2">
|
|
<title><filename>cb-background-effect.c</filename> (code file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-background-effect.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="effects-basic-example-cbbordereffect">
|
|
<title><type>CbBorderEffect</type></title>
|
|
|
|
<para>This is a more sophisticated effect with configurable
|
|
border color and width.</para>
|
|
|
|
<example id="effects-basic-example-3">
|
|
<title><filename>cb-border-effect.h</filename> (header file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-border-effect.h" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="effects-basic-example-4">
|
|
<title><filename>cb-border-effect.c</filename> (code file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-border-effect.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="effects-basic-example-application">
|
|
<title>Application</title>
|
|
|
|
<example id="effects-basic-example-5">
|
|
<title>Application which applies <type>CbBorderEffect</type>
|
|
and <type>CbBackgroundEffect</type> to a group of
|
|
<type>ClutterTextures</type>.</title>
|
|
<programlisting>
|
|
<xi:include href="examples/effects-basic.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="effects-custom-deform">
|
|
<title>Creating and animating a custom <type>ClutterDeformEffect</type></title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to deform an actor's geometry: for example,
|
|
to make it appear stretched, twisted or folded.</para>
|
|
|
|
<para>This recipe demonstrates how to do this with a simple page
|
|
fold effect, which folds one half of the actor over its other half.</para>
|
|
</section>
|
|
|
|
<section id="effects-custom-deform-solution">
|
|
<title>Solution</title>
|
|
|
|
<para>Subclass <type>ClutterDeformEffect</type> and
|
|
implement a <function>deform_vertex()</function> function
|
|
to modify the actor's vertices.</para>
|
|
|
|
<para>The signature for <function>deform_vertex()</function>
|
|
is:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
void
|
|
deform_vertex (ClutterDeformEffect *effect,
|
|
gfloat width,
|
|
gfloat height,
|
|
CoglTextureVertex *vertex);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>The <varname>width</varname> and <varname>height</varname>
|
|
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 <function>create_texture()</function>, the target
|
|
material's size may differ from the actor's transformed size.</para>
|
|
|
|
<para>The <varname>vertex</varname> contains the position
|
|
and color of a vertex, to be deformed by your effect.
|
|
Your <function>deform_vertex()</function>
|
|
function should modify the member variables of this
|
|
<type>CoglTextureVertex</type> in place. Usually, this will
|
|
mean modifying the <varname>x</varname>, <varname>y</varname>
|
|
and <varname>y</varname> member variables of the vertex,
|
|
which describe its position in 3D space.</para>
|
|
|
|
<para>The example function below, taken from
|
|
<link linkend="effects-custom-deform-example-2">the
|
|
full example</link>, applies a transformation to vertices falling
|
|
in the "right-hand" half of the actor (i.e. vertices with an
|
|
<varname>x</varname> value greater than or equal to half the
|
|
width of the actor).</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
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;
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note that this effect has two properties set in its
|
|
constructor or through setters:</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para><varname>angle</varname>, representing the angle of
|
|
the full fold; for the actor to fully fold in half, this
|
|
would be set to 180.0</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><varname>period</varname>, representing the percentage
|
|
of the fold to apply</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>As well as rotating the vertex, the
|
|
<function>deform_vertex()</function> function also shifts
|
|
the <varname>z</varname> 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.</para>
|
|
|
|
<para><emphasis>All</emphasis> 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.</para>
|
|
|
|
<para>This effect can now be applied to an actor, using the
|
|
approach
|
|
<link linkend="effects-introduction-using-the-built-in-effects">outlined
|
|
in the introduction</link>. The result looks like this when
|
|
<varname>period</varname> is set to 0.25 and <varname>angle</varname>
|
|
to 180.0 (i.e. the page is folded by 45 degrees):</para>
|
|
|
|
<screenshot>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata format="PNG"
|
|
fileref="images/effects-custom-deform.png" />
|
|
</imageobject>
|
|
<alt>
|
|
<para>Applying a custom <type>ClutterDeformEffect</type>
|
|
to a texture loaded with an image</para>
|
|
</alt>
|
|
</mediaobject>
|
|
</screenshot>
|
|
|
|
<para>Because the effect is a GObject which exposes its
|
|
properties, it can easily be animated, as described in
|
|
<link linkend="effects-custom-deform-discussion-animating">the
|
|
discussion section</link>.</para>
|
|
|
|
</section>
|
|
|
|
<section id="effects-custom-deform-discussion">
|
|
<title>Discussion</title>
|
|
|
|
<para>A deform effect processes an actor as follows:</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
<para>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
|
|
<link linkend="effects-custom-deform-discussion-tiles">this
|
|
section</link> for more details about tiles.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The position of each vertex of each
|
|
tile is then modified (or not) by the
|
|
<function>deform_vertex()</function> function. In this
|
|
function, you can change the vertex's position
|
|
(<varname>x</varname>, <varname>y</varname>,
|
|
<varname>z</varname> coordinates). You can also
|
|
modify the color at the vertex if desired.</para>
|
|
|
|
<para>The resulting deformed vertices are stored
|
|
in an offscreen buffer.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Once the deformation has been applied to
|
|
all vertices, the content of the offscreen buffer
|
|
is painted at the onscreen position of the actor.</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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).</para>
|
|
|
|
<para>When you create a <type>ClutterDeformEffect</type>,
|
|
think of it as specifying movements of marbles in the net.
|
|
Changing the position of a vertex corresponds to moving a marble
|
|
up/down (-/+ <varname>y</varname> position), left/right
|
|
(-/+ <varname>x</varname> position) or away/towards
|
|
you (-/+ <varname>z</varname> position) (ignoring color for the
|
|
moment).</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>When you write a <type>ClutterDeformEffect</type>, 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 <function>deform_vertex()</function> 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 <citetitle pubwork="book">Computer
|
|
Graphics (C Version), 2nd Edition</citetitle> by Hearn and
|
|
Baker, 1996.)</para>
|
|
|
|
<section>
|
|
<title>Customising the back material</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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
|
|
<varname>y</varname> 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.</para>
|
|
|
|
<para>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
|
|
<link linkend="effects-custom-deform-discussion-animating">this
|
|
section</link>.</para>
|
|
|
|
<para>The back material should be an instance of
|
|
<type>CoglMaterial</type>. You can either create this via
|
|
the Cogl API directly; or indirectly through the Clutter API
|
|
(for example, by getting the material from a
|
|
<type>ClutterTexture</type>). The code below gives an example
|
|
of how to do the latter:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* create a texture */
|
|
ClutterActor *back = clutter_texture_new ();
|
|
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (back), TRUE);
|
|
clutter_actor_set_width (back, 400);
|
|
|
|
/* load image into texture (ignoring errors for brevity) */
|
|
clutter_texture_set_from_file (CLUTTER_TEXTURE (back),
|
|
back_image_file,
|
|
NULL);
|
|
|
|
/* get a handle to the texture's Cogl material */
|
|
CoglHandle material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (back));
|
|
|
|
/* cast the effect to ClutterDeformEffect and set its back material
|
|
* to the handle
|
|
*/
|
|
clutter_deform_effect_set_back_material (CLUTTER_DEFORM_EFFECT (effect),
|
|
material);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>See the <type>ClutterDeformEffect</type> API reference
|
|
for more details about back materials.</para>
|
|
|
|
<para>Here's a screenshot of the
|
|
<link linkend="effects-custom-deform-example-3">example</link>
|
|
with the addition of a back material, folded at an angle
|
|
of 60 degrees:</para>
|
|
|
|
<screenshot>
|
|
<mediaobject>
|
|
<imageobject>
|
|
<imagedata format="PNG"
|
|
fileref="images/effects-custom-deform-back-material.png" />
|
|
</imageobject>
|
|
<alt>
|
|
<para>Applying a custom <type>ClutterDeformEffect</type>
|
|
to a texture loaded with an image</para>
|
|
</alt>
|
|
</mediaobject>
|
|
</screenshot>
|
|
|
|
</section>
|
|
|
|
<section id="effects-custom-deform-discussion-animating">
|
|
<title>Animating a custom deform effect</title>
|
|
|
|
<para>Clutter's animation API can animate any GObject which
|
|
exposes its properties. In the case of the page fold effect,
|
|
we can expose the <varname>period</varname> property using
|
|
standard GObject property installation:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* 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... */
|
|
}
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>We also add a <function>get_property()</function>
|
|
implementation, as well as a setter (see
|
|
<link linkend="effects-custom-deform-example-2">the full
|
|
GObject implementation</link> for details).</para>
|
|
|
|
<para>Then set up an animation for the property; in this case,
|
|
using a <type>ClutterState</type>:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
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);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>To start the animation, warp the <type>ClutterState</type>
|
|
into its <emphasis>"unfolded"</emphasis> state, then set it to
|
|
<emphasis>"folded"</emphasis>:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* this changes state instantaneously */
|
|
clutter_state_warp_to_state (transitions, "unfolded");
|
|
|
|
/* this starts an animation to the state */
|
|
clutter_state_set_state (transitions, "folded");
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note that the
|
|
<link linkend="effects-custom-deform-example-3">full code
|
|
sample</link> 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
|
|
<link linkend="effects-custom-deform-solution">previous
|
|
section</link>).</para>
|
|
|
|
<para>Here's what the resulting animation looks like:</para>
|
|
|
|
<inlinemediaobject>
|
|
<videoobject>
|
|
<videodata fileref="videos/effects-custom-deform.ogv"/>
|
|
</videoobject>
|
|
<alt>
|
|
<para>Video showing animation of a custom deform effect
|
|
on a texture</para>
|
|
</alt>
|
|
</inlinemediaobject>
|
|
|
|
</section>
|
|
|
|
<section id="effects-custom-deform-discussion-tiles">
|
|
<title>Tiles</title>
|
|
|
|
<para>A <type>ClutterDeformEffect</type> 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.</para>
|
|
|
|
<para>Most of the time, the default number
|
|
of tiles in the <varname>x</varname> and <varname>y</varname>
|
|
axes should suffice. You can get the current number of
|
|
tiles associated with an effect with:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
guint x_tiles;
|
|
guint y_tiles;
|
|
|
|
/* effect must be a subclass of ClutterDeformEffect */
|
|
clutter_deform_effect_get_n_tiles (CLUTTER_DEFORM_EFFECT (effect),
|
|
&x_tiles,
|
|
&y_tiles);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>However, if an effect produces jerky or fragmented output,
|
|
you want to tweak the number of tiles. Use the
|
|
<function>clutter_deform_effect_set_n_tiles()</function> function
|
|
to do this:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* 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);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Full example</title>
|
|
|
|
<para>This example consists of three files:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><link linkend="effects-custom-deform-example-1">A header
|
|
file</link> for the <type>CbPageFoldEffect</type> GObject.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><link linkend="effects-custom-deform-example-2">The
|
|
code file</link> implementing <type>CbPageFoldEffect</type>.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><link linkend="effects-custom-deform-example-3">A short
|
|
sample application</link> which applies a <type>CbPageFoldEffect</type>
|
|
instance to an actor and animates the fold when the actor is
|
|
clicked.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>As Clutter effect subclasses are written using GObject,
|
|
you might find <link linkend="actors-composite">this recipe</link>
|
|
(which goes into GObject in more detail) a useful introduction.</para>
|
|
|
|
<example id="effects-custom-deform-example-1">
|
|
<title><filename>cb-page-fold-effect.h</filename> (header file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-page-fold-effect.h" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="effects-custom-deform-example-2">
|
|
<title><filename>cb-page-fold-effect.c</filename> (code file)</title>
|
|
<programlisting>
|
|
<xi:include href="examples/cb-page-fold-effect.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="effects-custom-deform-example-3">
|
|
<title>Application which uses <type>CbPageFoldEffect</type>
|
|
to do animated folding of a <type>ClutterTexture</type></title>
|
|
<programlisting>
|
|
<xi:include href="examples/effects-custom-deform.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</chapter>
|