Chapter 9. Effects

Don't wake me for the end of the world unless it has very good special effects

Roger Zelazny, from Prince of Chaos

1. Introduction

Effects modify an actor's appearance, such as how it is positioned, colored and textured.

The Clutter API for effects contains several abstract classes you can subclass to create your own effects. It also contains several built-in effects you can use to modify the visual appearance of actors in a variety of ways.

The recipes in this section of the cookbook cover how to create your own effects as well as how to apply Clutter's effects.

1.1. Creating effects using the abstract effect classes

Tip

One of the original design goals of Clutter was to abstract the complexity of GL. However, the effects API partially circumvents these abstractions, to give you finer-grained access to the graphics pipeline. Therefore, if you want to write your own effects, some understanding of Cogl, OpenGL, and general graphics programming is essential.

Each abstract effect class is tailored to modifying different aspects of an actor, as explained below:

  • ClutterEffectIf you're just using the Clutter and Cogl APIs to decorate an actor, this is simplest type of effect to implement.

    Subclassing ClutterEffect enables you to "wrap" how an actor is painted, by injecting some code before and/or after the actor's own paint() implementation.

    Note

    This is the preferred way to modify how an actor is painted, short of creating your own actor subclass.

    Subclasses of ClutterEffect:

    • ClutterOffscreenEffectUse this class as a basis if you need GL textures for your effect.

      GL textures are required for effects which need an offscreen framebuffer. The offscreen framebuffer is used to store a modified rendering of an actor (e.g. with its colors altered or with deformed geometry). This buffer is then redirected to a texture in the stage window.

      An example is ClutterBlurEffect, which uses a GLSL fragment shader to blur an actor's appearance in an offscreen framebuffer.

      Subclasses of ClutterOffscreenEffect:

      • ClutterDeformEffectUse this base class if you want to modify an actor's geometry, at the level of individual vertices.

        ClutterDeformEffect removes the complexity of dealing with vertex-based deformations at the OpenGL level, instead enabling you to easily plug a deformation callback into the graphics pipeline.

        If you are writing your own deform effects, a good example to work from is ClutterPageTurnEffect.

        There is also a recipe which explains how to implement a simple custom deform effect (a page fold).

      • ClutterShaderEffectUse this if you want to apply custom GLSL vertex or fragment shaders to your actors.

        Writing ClutterShaderEffects gives you very fine-grained control over the GL pipeline. However, this makes them the most complex effects to implement.

        Tip

        If you want to write your own GLSL shaders, the GLSL specification is a good starting point.

1.2. Using the built-in effects

Clutter comes with a number of built-in effects which can easily be applied to your actors. This section explains how to do this.

First, create an actor. For this example, we use a texture loaded with an image:

/* filename could be set from command line or constant */
gchar *filename;

/* create a texture */
ClutterActor *texture = clutter_texture_new ();

/* ...set texture size, keep aspect ratio etc... */

/* NB ignoring missing file errors here for brevity */
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
                               filename,
                               NULL);

/* ...add texture to the stage... */

Next, create an instance of an effect; here, we're creating a ClutterColorizeEffect with a pink tint:

ClutterColor *pink = clutter_color_new (230, 187, 210, 255);
ClutterEffect *effect = clutter_colorize_effect_new (pink);

Finally, apply the effect to the actor:

clutter_actor_add_effect (texture, effect);

The result in this case is an image colorized with a pink tint, like this:

Applying a ClutterColorizeEffect to a texture loaded with an image (drawing by Madeleine Smith)

The same set of steps applies for any of the built-in Clutter effects. Your own custom effects classes should also behave in a similar way: constructors should return ClutterEffect instances so your effect can be added to an actor through the standard API.

One further thing worth mentioning is that because an effect is a GObject, any properties you expose for your effect can be animated via implicit animations, ClutterAnimator or ClutterState. For example, the ClutterPageTurnEffect can be animated by manipulating its period property. An example of how to do this for your own effect is given in the custom deform effect recipe.

The full code for the ClutterColorizeEffect example is below.

Example 9.1. Applying a ClutterColorizeEffect to a texture loaded with an image

#include <clutter/clutter.h>

int
main (int   argc,
      char *argv[])
{
  ClutterActor *stage;
  ClutterActor *texture;
  ClutterConstraint *constraint_x;
  ClutterConstraint *constraint_y;
  ClutterColor *pink;
  ClutterEffect *effect;
  gchar *filename;

  if (argc < 2)
    {
      g_print ("Usage: %s <path to image file>\n", argv[0]);
      return 1;
    }

  filename = argv[1];

  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
    return 1;

  stage = clutter_stage_new ();
  clutter_actor_set_size (stage, 400, 400);
  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

  texture = clutter_texture_new ();
  clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
  clutter_actor_set_width (texture, 300);

  /* NB ignoring missing file errors here for brevity */
  clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
                                 filename,
                                 NULL);

  /* align the texture on the x and y axes */
  constraint_x = clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5);
  constraint_y = clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5);
  clutter_actor_add_constraint (texture, constraint_x);
  clutter_actor_add_constraint (texture, constraint_y);

  /* create a colorize effect with pink tint */
  pink = clutter_color_new (230, 187, 210, 255);
  effect = clutter_colorize_effect_new (pink);

  /* apply the effect to the texture */
  clutter_actor_add_effect (texture, effect);

  clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);

  clutter_actor_show (stage);

  clutter_main ();

  return 0;
}