4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 * Emmanuele Bassi <ebassi@linux.intel.com>
26 * SECTION:clutter-effect
27 * @short_description: Base class for actor effects
29 * The #ClutterEffect class provides a default type and API for creating
30 * effects for generic actors.
32 * Effects are a #ClutterActorMeta sub-class that modify the way an actor
33 * is painted in a way that is not part of the actor's implementation.
35 * Effects should be the preferred way to affect the paint sequence of an
36 * actor without sub-classing the actor itself and overriding the
37 * #ClutterActorClass.paint()_ virtual function.
39 * <refsect2 id="ClutterEffect-implementation">
40 * <title>Implementing a ClutterEffect</title>
42 * Creating a sub-class of #ClutterEffect requires overriding the
43 * ‘paint’ method. The implementation of the function should look
44 * something like this:
47 * void effect_paint (ClutterEffect *effect, ClutterEffectPaintFlags flags)
49 * /* Set up initialisation of the paint such as binding a
50 * CoglOffscreen or other operations */
52 * /* Chain to the next item in the paint sequence. This will either call
53 * ‘paint’ on the next effect or just paint the actor if this is
54 * the last effect. */
55 * ClutterActor *actor =
56 * clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
57 * clutter_actor_continue_paint (actor);
59 * /* perform any cleanup of state, such as popping the
60 * CoglOffscreen */
64 * The effect can optionally avoid calling
65 * clutter_actor_continue_paint() to skip any further stages of
66 * the paint sequence. This is useful for example if the effect
67 * contains a cached image of the actor. In that case it can
68 * optimise painting by avoiding the actor paint and instead
69 * painting the cached image. The %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY
70 * flag is useful in this case. Clutter will set this flag when a
71 * redraw has been queued on the actor since it was last
72 * painted. The effect can use this information to decide if the
73 * cached image is still valid.
76 * The ‘paint’ virtual was added in Clutter 1.8. Prior to that there
77 * were two separate functions as follows.
80 * <listitem><simpara><function>pre_paint()</function>, which is called
81 * before painting the #ClutterActor.</simpara></listitem>
82 * <listitem><simpara><function>post_paint()</function>, which is called
83 * after painting the #ClutterActor.</simpara></listitem>
85 * <para>The <function>pre_paint()</function> function was used to set
86 * up the #ClutterEffect right before the #ClutterActor's paint
87 * sequence. This function can fail, and return %FALSE; in that case, no
88 * <function>post_paint()</function> invocation will follow.</para>
89 * <para>The <function>post_paint()</function> function was called after the
90 * #ClutterActor's paint sequence.</para>
92 * With these two functions it is not possible to skip the rest of
93 * the paint sequence. The default implementation of the ‘paint’
94 * virtual calls #ClutterEffectClass.pre_paint(), clutter_actor_continue_paint()
95 * and then #ClutterEffectClass.post_paint() so that existing actors that aren't
96 * using the #ClutterEffectClass.paint() virtual will continue to work. New
97 * effects using the #ClutterEffectClass.paint() virtual do not need to implement
100 * <example id="ClutterEffect-example">
101 * <title>A simple ClutterEffect implementation</title>
102 * <para>The example below creates two rectangles: one will be
103 * painted "behind" the actor, while another will be painted "on
104 * top" of the actor. The #ClutterActorMetaClass.set_actor()
105 * implementation will create the two materials used for the two
106 * different rectangles; the #ClutterEffectClass.paint() implementation
107 * will paint the first material using cogl_rectangle(), before
108 * continuing and then it will paint paint the second material
112 * ClutterEffect parent_instance;
118 * typedef struct _ClutterEffectClass MyEffectClass;
120 * G_DEFINE_TYPE (MyEffect, my_effect, CLUTTER_TYPE_EFFECT);
123 * my_effect_set_actor (ClutterActorMeta *meta,
124 * ClutterActor *actor)
126 * MyEffect *self = MY_EFFECT (meta);
128 * /* Clear the previous state */
129 * if (self->rect_1)
131 * cogl_handle_unref (self->rect_1);
132 * self->rect_1 = NULL;
135 * if (self->rect_2)
137 * cogl_handle_unref (self->rect_2);
138 * self->rect_2 = NULL;
141 * /* Maintain a pointer to the actor *
142 * self->actor = actor;
144 * /* If we've been detached by the actor then we should
145 * * just bail out here
147 * if (self->actor == NULL)
150 * /* Create a red material */
151 * self->rect_1 = cogl_material_new ();
152 * cogl_material_set_color4f (self->rect_1, 1.0, 0.0, 0.0, 1.0);
154 * /* Create a green material */
155 * self->rect_2 = cogl_material_new ();
156 * cogl_material_set_color4f (self->rect_2, 0.0, 1.0, 0.0, 1.0);
160 * my_effect_paint (ClutterEffect *effect)
162 * MyEffect *self = MY_EFFECT (effect);
163 * gfloat width, height;
165 * clutter_actor_get_size (self->actor, &width, &height);
167 * /* Paint the first rectangle in the upper left quadrant */
168 * cogl_set_source (self->rect_1);
169 * cogl_rectangle (0, 0, width / 2, height / 2);
171 * /* Continue to the rest of the paint sequence */
172 * clutter_actor_continue_paint (self->actor);
174 * /* Paint the second rectangle in the lower right quadrant */
175 * cogl_set_source (self->rect_2);
176 * cogl_rectangle (width / 2, height / 2, width, height);
180 * my_effect_class_init (MyEffectClass *klass)
182 * ClutterActorMetaClas *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
184 * meta_class->set_actor = my_effect_set_actor;
186 * klass->paint = my_effect_paint;
192 * #ClutterEffect is available since Clutter 1.4
199 #include "clutter-effect.h"
201 #include "clutter-actor-meta-private.h"
202 #include "clutter-debug.h"
203 #include "clutter-effect-private.h"
204 #include "clutter-enum-types.h"
205 #include "clutter-marshal.h"
206 #include "clutter-private.h"
207 #include "clutter-actor-private.h"
209 G_DEFINE_ABSTRACT_TYPE (ClutterEffect,
211 CLUTTER_TYPE_ACTOR_META);
214 clutter_effect_real_pre_paint (ClutterEffect *effect)
220 clutter_effect_real_post_paint (ClutterEffect *effect)
225 clutter_effect_real_get_paint_volume (ClutterEffect *effect,
226 ClutterPaintVolume *volume)
232 clutter_effect_real_paint (ClutterEffect *effect,
233 ClutterEffectPaintFlags flags)
235 ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect);
237 gboolean pre_paint_succeeded;
239 /* The default implementation provides a compatibility wrapper for
240 effects that haven't migrated to use the 'paint' virtual yet. This
241 just calls the old pre and post virtuals before chaining on */
243 pre_paint_succeeded = _clutter_effect_pre_paint (effect);
245 actor = clutter_actor_meta_get_actor (actor_meta);
246 clutter_actor_continue_paint (actor);
248 if (pre_paint_succeeded)
249 _clutter_effect_post_paint (effect);
253 clutter_effect_real_pick (ClutterEffect *effect,
254 ClutterEffectPaintFlags flags)
256 ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect);
259 actor = clutter_actor_meta_get_actor (actor_meta);
260 clutter_actor_continue_paint (actor);
264 clutter_effect_notify (GObject *gobject,
267 if (strcmp (pspec->name, "enabled") == 0)
269 ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
270 ClutterActor *actor = clutter_actor_meta_get_actor (meta);
273 clutter_actor_queue_redraw (actor);
276 if (G_OBJECT_CLASS (clutter_effect_parent_class)->notify != NULL)
277 G_OBJECT_CLASS (clutter_effect_parent_class)->notify (gobject, pspec);
281 clutter_effect_class_init (ClutterEffectClass *klass)
283 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
285 gobject_class->notify = clutter_effect_notify;
287 klass->pre_paint = clutter_effect_real_pre_paint;
288 klass->post_paint = clutter_effect_real_post_paint;
289 klass->get_paint_volume = clutter_effect_real_get_paint_volume;
290 klass->paint = clutter_effect_real_paint;
291 klass->pick = clutter_effect_real_pick;
295 clutter_effect_init (ClutterEffect *self)
300 _clutter_effect_pre_paint (ClutterEffect *effect)
302 g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE);
304 return CLUTTER_EFFECT_GET_CLASS (effect)->pre_paint (effect);
308 _clutter_effect_post_paint (ClutterEffect *effect)
310 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
312 CLUTTER_EFFECT_GET_CLASS (effect)->post_paint (effect);
316 _clutter_effect_paint (ClutterEffect *effect,
317 ClutterEffectPaintFlags flags)
319 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
321 CLUTTER_EFFECT_GET_CLASS (effect)->paint (effect, flags);
325 _clutter_effect_pick (ClutterEffect *effect,
326 ClutterEffectPaintFlags flags)
328 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
330 CLUTTER_EFFECT_GET_CLASS (effect)->pick (effect, flags);
334 _clutter_effect_get_paint_volume (ClutterEffect *effect,
335 ClutterPaintVolume *volume)
337 g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE);
338 g_return_val_if_fail (volume != NULL, FALSE);
340 return CLUTTER_EFFECT_GET_CLASS (effect)->get_paint_volume (effect, volume);
344 * clutter_effect_queue_repaint:
345 * @effect: A #ClutterEffect which needs redrawing
347 * Queues a repaint of the effect. The effect can detect when the ‘paint’
348 * method is called as a result of this function because it will not
349 * have the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY flag set. In that case the
350 * effect is free to assume that the actor has not changed its
351 * appearance since the last time it was painted so it doesn't need to
352 * call clutter_actor_continue_paint() if it can draw a cached
353 * image. This is mostly intended for effects that are using a
354 * %CoglOffscreen to redirect the actor (such as
355 * %ClutterOffscreenEffect). In that case the effect can save a bit of
356 * rendering time by painting the cached texture without causing the
357 * entire actor to be painted.
359 * This function can be used by effects that have their own animatable
360 * parameters. For example, an effect which adds a varying degree of a
361 * red tint to an actor by redirecting it through a CoglOffscreen
362 * might have a property to specify the level of tint. When this value
363 * changes, the underlying actor doesn't need to be redrawn so the
364 * effect can call clutter_effect_queue_repaint() to make sure the
365 * effect is repainted.
367 * Note however that modifying the position of the parent of an actor
368 * may change the appearance of the actor because its transformation
369 * matrix would change. In this case a redraw wouldn't be queued on
370 * the actor itself so the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY would still
371 * not be set. The effect can detect this case by keeping track of the
372 * last modelview matrix that was used to render the actor and
373 * veryifying that it remains the same in the next paint.
375 * Any other effects that are layered on top of the passed in effect
376 * will still be passed the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY flag. If
377 * anything queues a redraw on the actor without specifying an effect
378 * or with an effect that is lower in the chain of effects than this
379 * one then that will override this call. In that case this effect
380 * will instead be called with the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY
386 clutter_effect_queue_repaint (ClutterEffect *effect)
390 g_return_if_fail (CLUTTER_IS_EFFECT (effect));
392 actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
394 /* If the effect has no actor then nothing needs to be done */
396 _clutter_actor_queue_redraw_full (actor,
398 NULL, /* clip volume */
399 effect /* effect */);