Blob Blame History Raw
<!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>