2010-05-17 10:39:27 +00:00
|
|
|
/*
|
|
|
|
* Clutter.
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Intel Corporation.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2010-04-16 11:33:20 +00:00
|
|
|
* Authors:
|
2010-05-17 10:39:27 +00:00
|
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
2010-04-16 11:33:20 +00:00
|
|
|
* Robert Bragg <robert@linux.intel.com>
|
2010-05-17 10:39:27 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:clutter-offscreen-effect
|
|
|
|
* @short_description: Base class for effects using offscreen buffers
|
|
|
|
* @see_also: #ClutterBlurEffect, #ClutterEffect
|
|
|
|
*
|
|
|
|
* #ClutterOffscreenEffect is an abstract class that can be used by
|
|
|
|
* #ClutterEffect sub-classes requiring access to an offscreen buffer.
|
|
|
|
*
|
|
|
|
* Some effects, like the fragment shader based effects, can only use GL
|
|
|
|
* textures, and in order to apply those effects to any kind of actor they
|
|
|
|
* require that all drawing operations are applied to an offscreen framebuffer
|
|
|
|
* that gets redirected to a texture.
|
|
|
|
*
|
|
|
|
* #ClutterOffscreenEffect provides all the heavy-lifting for creating the
|
|
|
|
* offscreen framebuffer, the redirection and the final paint of the texture on
|
|
|
|
* the desired stage.
|
|
|
|
*
|
|
|
|
* #ClutterOffscreenEffect is available since Clutter 1.4
|
2014-03-17 23:07:58 +00:00
|
|
|
*
|
|
|
|
* ## Implementing a ClutterOffscreenEffect
|
|
|
|
*
|
|
|
|
* Creating a sub-class of #ClutterOffscreenEffect requires, in case
|
|
|
|
* of overriding the #ClutterEffect virtual functions, to chain up to the
|
|
|
|
* #ClutterOffscreenEffect's implementation.
|
|
|
|
*
|
|
|
|
* On top of the #ClutterEffect's virtual functions,
|
|
|
|
* #ClutterOffscreenEffect also provides a #ClutterOffscreenEffectClass.paint_target()
|
|
|
|
* function, which encapsulates the effective painting of the texture that
|
|
|
|
* contains the result of the offscreen redirection.
|
|
|
|
*
|
|
|
|
* The size of the target material is defined to be as big as the
|
|
|
|
* transformed size of the #ClutterActor using the offscreen effect.
|
|
|
|
* Sub-classes of #ClutterOffscreenEffect can change the texture creation
|
|
|
|
* code to provide bigger textures by overriding the
|
|
|
|
* #ClutterOffscreenEffectClass.create_texture() virtual function; no chain up
|
|
|
|
* to the #ClutterOffscreenEffect implementation is required in this
|
|
|
|
* case.
|
2010-05-17 10:39:27 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2012-02-21 13:22:17 +00:00
|
|
|
#define CLUTTER_ENABLE_EXPERIMENTAL_API
|
|
|
|
|
2010-05-17 10:39:27 +00:00
|
|
|
#include "clutter-offscreen-effect.h"
|
|
|
|
|
|
|
|
#include "cogl/cogl.h"
|
|
|
|
|
2010-10-21 11:16:05 +00:00
|
|
|
#include "clutter-actor-private.h"
|
2010-05-17 10:39:27 +00:00
|
|
|
#include "clutter-debug.h"
|
|
|
|
#include "clutter-private.h"
|
2010-10-21 10:29:09 +00:00
|
|
|
#include "clutter-stage-private.h"
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
struct _ClutterOffscreenEffectPrivate
|
|
|
|
{
|
|
|
|
CoglHandle offscreen;
|
2012-02-21 13:22:17 +00:00
|
|
|
CoglPipeline *target;
|
2011-11-28 11:53:54 +00:00
|
|
|
CoglHandle texture;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
ClutterActor *actor;
|
2010-04-16 11:33:20 +00:00
|
|
|
ClutterActor *stage;
|
|
|
|
|
|
|
|
gfloat x_offset;
|
|
|
|
gfloat y_offset;
|
|
|
|
|
2011-11-28 13:08:57 +00:00
|
|
|
/* This is the calculated size of the fbo before being passed
|
|
|
|
through create_texture(). This needs to be tracked separately so
|
|
|
|
that we can detect when a different size is calculated and
|
|
|
|
regenerate the fbo */
|
|
|
|
int fbo_width;
|
|
|
|
int fbo_height;
|
|
|
|
|
2011-03-03 16:46:26 +00:00
|
|
|
gint old_opacity_override;
|
2011-02-25 11:24:23 +00:00
|
|
|
|
|
|
|
/* The matrix that was current the last time the fbo was updated. We
|
|
|
|
need to keep track of this to detect when we can reuse the
|
|
|
|
contents of the fbo without redrawing the actor. We need the
|
|
|
|
actual matrix rather than just detecting queued redraws on the
|
|
|
|
actor because any change in the parent hierarchy (even just a
|
|
|
|
translation) could cause the actor to look completely different
|
|
|
|
and it won't cause a redraw to be queued on the parent's
|
|
|
|
children. */
|
|
|
|
CoglMatrix last_matrix_drawn;
|
2010-05-17 10:39:27 +00:00
|
|
|
};
|
|
|
|
|
2013-07-03 13:14:01 +00:00
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterOffscreenEffect,
|
|
|
|
clutter_offscreen_effect,
|
|
|
|
CLUTTER_TYPE_EFFECT)
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_set_actor (ClutterActorMeta *meta,
|
|
|
|
ClutterActor *actor)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (meta);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
|
|
|
ClutterActorMetaClass *meta_class;
|
|
|
|
|
|
|
|
meta_class = CLUTTER_ACTOR_META_CLASS (clutter_offscreen_effect_parent_class);
|
|
|
|
meta_class->set_actor (meta, actor);
|
|
|
|
|
|
|
|
/* clear out the previous state */
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->offscreen != NULL)
|
2010-05-17 10:39:27 +00:00
|
|
|
{
|
|
|
|
cogl_handle_unref (priv->offscreen);
|
2012-02-21 15:55:51 +00:00
|
|
|
priv->offscreen = NULL;
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* we keep a back pointer here, to avoid going through the ActorMeta */
|
|
|
|
priv->actor = clutter_actor_meta_get_actor (meta);
|
2010-04-16 11:33:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static CoglHandle
|
2010-09-01 16:56:15 +00:00
|
|
|
clutter_offscreen_effect_real_create_texture (ClutterOffscreenEffect *effect,
|
|
|
|
gfloat width,
|
|
|
|
gfloat height)
|
2010-04-16 11:33:20 +00:00
|
|
|
{
|
2013-09-02 16:06:49 +00:00
|
|
|
return cogl_texture_new_with_size (MAX (width, 1), MAX (height, 1),
|
|
|
|
COGL_TEXTURE_NO_SLICING,
|
|
|
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
|
2010-04-16 11:33:20 +00:00
|
|
|
}
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
static gboolean
|
2010-09-07 17:04:19 +00:00
|
|
|
update_fbo (ClutterEffect *effect, int fbo_width, int fbo_height)
|
2010-04-16 11:33:20 +00:00
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
|
|
|
|
|
|
|
priv->stage = clutter_actor_get_stage (priv->actor);
|
|
|
|
if (priv->stage == NULL)
|
2010-05-17 10:39:27 +00:00
|
|
|
{
|
|
|
|
CLUTTER_NOTE (MISC, "The actor '%s' is not part of a stage",
|
2010-04-16 11:33:20 +00:00
|
|
|
clutter_actor_get_name (priv->actor) == NULL
|
|
|
|
? G_OBJECT_TYPE_NAME (priv->actor)
|
|
|
|
: clutter_actor_get_name (priv->actor));
|
|
|
|
return FALSE;
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 13:08:57 +00:00
|
|
|
if (priv->fbo_width == fbo_width &&
|
|
|
|
priv->fbo_height == fbo_height &&
|
2012-02-21 15:55:51 +00:00
|
|
|
priv->offscreen != NULL)
|
2010-04-16 11:33:20 +00:00
|
|
|
return TRUE;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2012-02-21 13:22:17 +00:00
|
|
|
if (priv->target == NULL)
|
2011-03-15 16:20:34 +00:00
|
|
|
{
|
2012-02-21 13:22:17 +00:00
|
|
|
CoglContext *ctx =
|
|
|
|
clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
|
|
|
|
|
|
|
priv->target = cogl_pipeline_new (ctx);
|
2011-03-15 16:20:34 +00:00
|
|
|
|
|
|
|
/* We're always going to render the texture at a 1:1 texel:pixel
|
|
|
|
ratio so we can use 'nearest' filtering to decrease the
|
|
|
|
effects of rounding errors in the geometry calculation */
|
2012-02-21 13:22:17 +00:00
|
|
|
cogl_pipeline_set_layer_filters (priv->target,
|
2011-03-15 16:20:34 +00:00
|
|
|
0, /* layer_index */
|
2012-02-21 13:22:17 +00:00
|
|
|
COGL_PIPELINE_FILTER_NEAREST,
|
|
|
|
COGL_PIPELINE_FILTER_NEAREST);
|
2011-03-15 16:20:34 +00:00
|
|
|
}
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->texture != NULL)
|
2011-11-28 11:53:54 +00:00
|
|
|
{
|
|
|
|
cogl_handle_unref (priv->texture);
|
2012-02-21 15:55:51 +00:00
|
|
|
priv->texture = NULL;
|
2011-11-28 11:53:54 +00:00
|
|
|
}
|
2010-04-16 11:33:20 +00:00
|
|
|
|
2011-11-28 11:53:54 +00:00
|
|
|
priv->texture =
|
|
|
|
clutter_offscreen_effect_create_texture (self, fbo_width, fbo_height);
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->texture == NULL)
|
2011-11-28 11:53:54 +00:00
|
|
|
return FALSE;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2012-02-21 13:22:17 +00:00
|
|
|
cogl_pipeline_set_layer_texture (priv->target, 0, priv->texture);
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2011-11-28 13:08:57 +00:00
|
|
|
priv->fbo_width = fbo_width;
|
|
|
|
priv->fbo_height = fbo_height;
|
|
|
|
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->offscreen != NULL)
|
2010-11-25 10:56:50 +00:00
|
|
|
cogl_handle_unref (priv->offscreen);
|
|
|
|
|
2011-11-28 11:53:54 +00:00
|
|
|
priv->offscreen = cogl_offscreen_new_to_texture (priv->texture);
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->offscreen == NULL)
|
2010-05-17 10:39:27 +00:00
|
|
|
{
|
|
|
|
g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC);
|
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
cogl_handle_unref (priv->target);
|
2012-02-21 13:22:17 +00:00
|
|
|
priv->target = NULL;
|
2010-04-16 11:33:20 +00:00
|
|
|
|
2011-11-28 13:08:57 +00:00
|
|
|
priv->fbo_width = 0;
|
|
|
|
priv->fbo_height = 0;
|
2010-04-16 11:33:20 +00:00
|
|
|
|
|
|
|
return FALSE;
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
static gboolean
|
|
|
|
clutter_offscreen_effect_pre_paint (ClutterEffect *effect)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
2010-09-07 17:04:19 +00:00
|
|
|
ClutterActorBox box;
|
2013-05-04 16:37:33 +00:00
|
|
|
ClutterActor *stage;
|
2010-09-16 11:58:42 +00:00
|
|
|
CoglMatrix projection;
|
2010-04-16 11:33:20 +00:00
|
|
|
CoglColor transparent;
|
2013-05-04 16:37:33 +00:00
|
|
|
gfloat stage_width, stage_height;
|
|
|
|
gfloat fbo_width = -1, fbo_height = -1;
|
2010-04-16 11:33:20 +00:00
|
|
|
gfloat width, height;
|
2011-02-11 15:18:08 +00:00
|
|
|
gfloat xexpand, yexpand;
|
2011-11-28 11:53:54 +00:00
|
|
|
int texture_width, texture_height;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
|
|
|
|
return FALSE;
|
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
if (priv->actor == NULL)
|
|
|
|
return FALSE;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2013-05-04 16:37:33 +00:00
|
|
|
stage = _clutter_actor_get_stage_internal (priv->actor);
|
|
|
|
clutter_actor_get_size (stage, &stage_width, &stage_height);
|
|
|
|
|
2010-09-07 17:04:19 +00:00
|
|
|
/* The paint box is the bounding box of the actor's paint volume in
|
|
|
|
* stage coordinates. This will give us the size for the framebuffer
|
|
|
|
* we need to redirect its rendering offscreen and its position will
|
|
|
|
* be used to setup an offset viewport */
|
|
|
|
if (clutter_actor_get_paint_box (priv->actor, &box))
|
2011-03-04 17:04:02 +00:00
|
|
|
{
|
|
|
|
clutter_actor_box_get_size (&box, &fbo_width, &fbo_height);
|
|
|
|
clutter_actor_box_get_origin (&box, &priv->x_offset, &priv->y_offset);
|
2013-05-04 16:37:33 +00:00
|
|
|
|
|
|
|
fbo_width = MIN (fbo_width, stage_width);
|
|
|
|
fbo_height = MIN (fbo_height, stage_height);
|
2011-03-04 17:04:02 +00:00
|
|
|
}
|
2010-09-07 17:04:19 +00:00
|
|
|
else
|
|
|
|
{
|
2013-05-04 16:37:33 +00:00
|
|
|
fbo_width = stage_width;
|
|
|
|
fbo_height = stage_height;
|
2010-09-07 17:04:19 +00:00
|
|
|
}
|
|
|
|
|
2013-05-04 16:37:33 +00:00
|
|
|
if (fbo_width == stage_width)
|
|
|
|
priv->x_offset = 0.0f;
|
|
|
|
if (fbo_height == stage_height)
|
|
|
|
priv->y_offset = 0.0f;
|
|
|
|
|
2010-09-07 17:04:19 +00:00
|
|
|
/* First assert that the framebuffer is the right size... */
|
|
|
|
if (!update_fbo (effect, fbo_width, fbo_height))
|
2010-04-16 11:33:20 +00:00
|
|
|
return FALSE;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2011-11-28 11:53:54 +00:00
|
|
|
texture_width = cogl_texture_get_width (priv->texture);
|
|
|
|
texture_height = cogl_texture_get_height (priv->texture);
|
|
|
|
|
2011-09-21 15:36:30 +00:00
|
|
|
/* get the current modelview matrix so that we can copy it to the
|
|
|
|
* framebuffer. We also store the matrix that was last used when we
|
|
|
|
* updated the FBO so that we can detect when we don't need to
|
|
|
|
* update the FBO to paint a second time */
|
|
|
|
cogl_get_modelview_matrix (&priv->last_matrix_drawn);
|
2011-02-25 11:24:23 +00:00
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
/* let's draw offscreen */
|
|
|
|
cogl_push_framebuffer (priv->offscreen);
|
|
|
|
|
2011-09-21 15:36:30 +00:00
|
|
|
/* Copy the modelview that would have been used if rendering onscreen */
|
|
|
|
cogl_set_modelview_matrix (&priv->last_matrix_drawn);
|
|
|
|
|
2010-09-07 17:04:19 +00:00
|
|
|
/* Set up the viewport so that it has the same size as the stage,
|
|
|
|
* but offset it so that the actor of interest lands on our
|
|
|
|
* framebuffer. */
|
2010-09-16 11:58:42 +00:00
|
|
|
clutter_actor_get_size (priv->stage, &width, &height);
|
2011-02-11 15:18:08 +00:00
|
|
|
|
|
|
|
/* Expand the viewport if the actor is partially off-stage,
|
|
|
|
* otherwise the actor will end up clipped to the stage viewport
|
|
|
|
*/
|
|
|
|
xexpand = 0.f;
|
|
|
|
if (priv->x_offset < 0.f)
|
|
|
|
xexpand = -priv->x_offset;
|
2011-11-28 11:53:54 +00:00
|
|
|
if (priv->x_offset + texture_width > width)
|
|
|
|
xexpand = MAX (xexpand, (priv->x_offset + texture_width) - width);
|
2011-02-11 15:18:08 +00:00
|
|
|
|
|
|
|
yexpand = 0.f;
|
|
|
|
if (priv->y_offset < 0.f)
|
|
|
|
yexpand = -priv->y_offset;
|
2011-11-28 11:53:54 +00:00
|
|
|
if (priv->y_offset + texture_height > height)
|
|
|
|
yexpand = MAX (yexpand, (priv->y_offset + texture_height) - height);
|
2011-02-11 15:18:08 +00:00
|
|
|
|
|
|
|
/* Set the viewport */
|
|
|
|
cogl_set_viewport (-(priv->x_offset + xexpand), -(priv->y_offset + yexpand),
|
|
|
|
width + (2 * xexpand), height + (2 * yexpand));
|
2010-09-16 11:58:42 +00:00
|
|
|
|
|
|
|
/* Copy the stage's projection matrix across to the framebuffer */
|
|
|
|
_clutter_stage_get_projection_matrix (CLUTTER_STAGE (priv->stage),
|
|
|
|
&projection);
|
|
|
|
|
2011-09-21 15:36:30 +00:00
|
|
|
/* If we've expanded the viewport, make sure to scale the projection
|
2011-02-11 15:18:08 +00:00
|
|
|
* matrix accordingly (as it's been initialised to work with the
|
|
|
|
* original viewport and not our expanded one).
|
|
|
|
*/
|
|
|
|
if (xexpand > 0.f || yexpand > 0.f)
|
|
|
|
{
|
|
|
|
gfloat new_width, new_height;
|
|
|
|
|
|
|
|
new_width = width + (2 * xexpand);
|
|
|
|
new_height = height + (2 * yexpand);
|
|
|
|
|
2011-09-21 15:36:30 +00:00
|
|
|
cogl_matrix_scale (&projection,
|
2011-02-11 15:18:08 +00:00
|
|
|
width / new_width,
|
|
|
|
height / new_height,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
|
2011-09-21 15:36:30 +00:00
|
|
|
cogl_set_projection_matrix (&projection);
|
2010-04-16 11:33:20 +00:00
|
|
|
|
2010-09-03 15:55:12 +00:00
|
|
|
cogl_color_init_from_4ub (&transparent, 0, 0, 0, 0);
|
2010-04-16 11:33:20 +00:00
|
|
|
cogl_clear (&transparent,
|
|
|
|
COGL_BUFFER_BIT_COLOR |
|
|
|
|
COGL_BUFFER_BIT_DEPTH);
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
cogl_push_matrix ();
|
|
|
|
|
2011-02-03 14:36:55 +00:00
|
|
|
/* Override the actor's opacity to fully opaque - we paint the offscreen
|
|
|
|
* texture with the actor's paint opacity, so we need to do this to avoid
|
|
|
|
* multiplying the opacity twice.
|
|
|
|
*/
|
2011-03-03 16:46:26 +00:00
|
|
|
priv->old_opacity_override =
|
|
|
|
_clutter_actor_get_opacity_override (priv->actor);
|
2011-02-03 14:36:55 +00:00
|
|
|
_clutter_actor_set_opacity_override (priv->actor, 0xff);
|
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
return TRUE;
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffectPrivate *priv = effect->priv;
|
|
|
|
guint8 paint_opacity;
|
|
|
|
|
|
|
|
paint_opacity = clutter_actor_get_paint_opacity (priv->actor);
|
|
|
|
|
2012-02-21 13:22:17 +00:00
|
|
|
cogl_pipeline_set_color4ub (priv->target,
|
2010-05-17 10:39:27 +00:00
|
|
|
paint_opacity,
|
|
|
|
paint_opacity,
|
|
|
|
paint_opacity,
|
|
|
|
paint_opacity);
|
|
|
|
cogl_set_source (priv->target);
|
2010-04-16 11:33:20 +00:00
|
|
|
|
2010-09-07 17:04:19 +00:00
|
|
|
/* At this point we are in stage coordinates translated so if
|
|
|
|
* we draw our texture using a textured quad the size of the paint
|
|
|
|
* box then we will overlay where the actor would have drawn if it
|
|
|
|
* hadn't been redirected offscreen.
|
2010-04-16 11:33:20 +00:00
|
|
|
*/
|
2010-06-10 16:35:56 +00:00
|
|
|
cogl_rectangle_with_texture_coords (0, 0,
|
2011-11-28 11:53:54 +00:00
|
|
|
cogl_texture_get_width (priv->texture),
|
|
|
|
cogl_texture_get_height (priv->texture),
|
2010-05-17 10:39:27 +00:00
|
|
|
0.0, 0.0,
|
|
|
|
1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-02-25 11:24:23 +00:00
|
|
|
clutter_offscreen_effect_paint_texture (ClutterOffscreenEffect *effect)
|
2010-05-17 10:39:27 +00:00
|
|
|
{
|
2011-02-25 11:24:23 +00:00
|
|
|
ClutterOffscreenEffectPrivate *priv = effect->priv;
|
2010-09-16 11:58:42 +00:00
|
|
|
CoglMatrix modelview;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
cogl_push_matrix ();
|
|
|
|
|
2010-09-16 11:58:42 +00:00
|
|
|
/* Now reset the modelview to put us in stage coordinates so
|
|
|
|
* we can drawn the result of our offscreen render as a textured
|
|
|
|
* quad... */
|
|
|
|
|
|
|
|
cogl_matrix_init_identity (&modelview);
|
|
|
|
_clutter_actor_apply_modelview_transform (priv->stage, &modelview);
|
|
|
|
cogl_matrix_translate (&modelview, priv->x_offset, priv->y_offset, 0.0f);
|
|
|
|
cogl_set_modelview_matrix (&modelview);
|
2010-06-10 16:35:56 +00:00
|
|
|
|
2010-04-16 11:33:20 +00:00
|
|
|
/* paint the target material; this is virtualized for
|
|
|
|
* sub-classes that require special hand-holding
|
|
|
|
*/
|
2011-02-25 11:24:23 +00:00
|
|
|
clutter_offscreen_effect_paint_target (effect);
|
2010-04-16 11:33:20 +00:00
|
|
|
|
|
|
|
cogl_pop_matrix ();
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
2011-02-25 11:24:23 +00:00
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_post_paint (ClutterEffect *effect)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
|
|
|
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->offscreen == NULL ||
|
2012-02-21 13:22:17 +00:00
|
|
|
priv->target == NULL ||
|
2011-02-25 11:24:23 +00:00
|
|
|
priv->actor == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Restore the previous opacity override */
|
|
|
|
_clutter_actor_set_opacity_override (priv->actor, priv->old_opacity_override);
|
|
|
|
|
|
|
|
cogl_pop_matrix ();
|
|
|
|
cogl_pop_framebuffer ();
|
|
|
|
|
|
|
|
clutter_offscreen_effect_paint_texture (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-06-13 14:58:46 +00:00
|
|
|
clutter_offscreen_effect_paint (ClutterEffect *effect,
|
|
|
|
ClutterEffectPaintFlags flags)
|
2011-02-25 11:24:23 +00:00
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
|
|
|
CoglMatrix matrix;
|
|
|
|
|
|
|
|
cogl_get_modelview_matrix (&matrix);
|
|
|
|
|
|
|
|
/* If we've already got a cached image for the same matrix and the
|
|
|
|
actor hasn't been redrawn then we can just use the cached image
|
|
|
|
in the fbo */
|
|
|
|
if (priv->offscreen == NULL ||
|
2011-06-13 14:58:46 +00:00
|
|
|
(flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) ||
|
2011-02-25 11:24:23 +00:00
|
|
|
!cogl_matrix_equal (&matrix, &priv->last_matrix_drawn))
|
|
|
|
{
|
2011-06-06 06:44:11 +00:00
|
|
|
/* Chain up to the parent paint method which will call the pre and
|
2011-02-25 11:24:23 +00:00
|
|
|
post paint functions to update the image */
|
|
|
|
CLUTTER_EFFECT_CLASS (clutter_offscreen_effect_parent_class)->
|
2011-06-06 06:44:11 +00:00
|
|
|
paint (effect, flags);
|
2011-02-25 11:24:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
clutter_offscreen_effect_paint_texture (self);
|
|
|
|
}
|
|
|
|
|
2010-05-17 10:39:27 +00:00
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_finalize (GObject *gobject)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (gobject);
|
|
|
|
ClutterOffscreenEffectPrivate *priv = self->priv;
|
|
|
|
|
|
|
|
if (priv->offscreen)
|
|
|
|
cogl_handle_unref (priv->offscreen);
|
|
|
|
|
|
|
|
if (priv->target)
|
|
|
|
cogl_handle_unref (priv->target);
|
|
|
|
|
2011-11-28 11:53:54 +00:00
|
|
|
if (priv->texture)
|
|
|
|
cogl_handle_unref (priv->texture);
|
|
|
|
|
2010-05-17 10:39:27 +00:00
|
|
|
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->finalize (gobject);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_class_init (ClutterOffscreenEffectClass *klass)
|
|
|
|
{
|
|
|
|
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
|
|
|
|
ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2010-09-01 16:56:15 +00:00
|
|
|
klass->create_texture = clutter_offscreen_effect_real_create_texture;
|
2010-05-17 10:39:27 +00:00
|
|
|
klass->paint_target = clutter_offscreen_effect_real_paint_target;
|
|
|
|
|
|
|
|
meta_class->set_actor = clutter_offscreen_effect_set_actor;
|
|
|
|
|
|
|
|
effect_class->pre_paint = clutter_offscreen_effect_pre_paint;
|
|
|
|
effect_class->post_paint = clutter_offscreen_effect_post_paint;
|
2011-06-06 06:44:11 +00:00
|
|
|
effect_class->paint = clutter_offscreen_effect_paint;
|
2010-05-17 10:39:27 +00:00
|
|
|
|
|
|
|
gobject_class->finalize = clutter_offscreen_effect_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_offscreen_effect_init (ClutterOffscreenEffect *self)
|
|
|
|
{
|
2013-07-03 13:14:01 +00:00
|
|
|
self->priv = clutter_offscreen_effect_get_instance_private (self);
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 11:53:54 +00:00
|
|
|
/**
|
|
|
|
* clutter_offscreen_effect_get_texture:
|
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
*
|
|
|
|
* Retrieves the texture used as a render target for the offscreen
|
|
|
|
* buffer created by @effect
|
|
|
|
*
|
|
|
|
* You should only use the returned texture when painting. The texture
|
|
|
|
* may change after ClutterEffect::pre_paint is called so the effect
|
|
|
|
* implementation should update any references to the texture after
|
|
|
|
* chaining-up to the parent's pre_paint implementation. This can be
|
|
|
|
* used instead of clutter_offscreen_effect_get_target() when the
|
|
|
|
* effect subclass wants to paint using its own material.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): a #CoglHandle or %COGL_INVALID_HANDLE. The
|
|
|
|
* returned texture is owned by Clutter and it should not be
|
|
|
|
* modified or freed
|
|
|
|
*
|
|
|
|
* Since: 1.10
|
|
|
|
*/
|
|
|
|
CoglHandle
|
|
|
|
clutter_offscreen_effect_get_texture (ClutterOffscreenEffect *effect)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
|
2012-02-21 15:55:51 +00:00
|
|
|
NULL);
|
2011-11-28 11:53:54 +00:00
|
|
|
|
|
|
|
return effect->priv->texture;
|
|
|
|
}
|
|
|
|
|
2010-05-17 10:39:27 +00:00
|
|
|
/**
|
|
|
|
* clutter_offscreen_effect_get_target:
|
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
*
|
|
|
|
* Retrieves the material used as a render target for the offscreen
|
|
|
|
* buffer created by @effect
|
|
|
|
*
|
2010-09-01 16:56:15 +00:00
|
|
|
* You should only use the returned #CoglMaterial when painting. The
|
|
|
|
* returned material might change between different frames.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): a #CoglMaterial or %NULL. The
|
|
|
|
* returned material is owned by Clutter and it should not be
|
|
|
|
* modified or freed
|
2010-05-17 10:39:27 +00:00
|
|
|
*
|
|
|
|
* Since: 1.4
|
|
|
|
*/
|
2010-09-01 16:56:15 +00:00
|
|
|
CoglMaterial *
|
2010-05-17 10:39:27 +00:00
|
|
|
clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
|
2012-02-21 13:22:17 +00:00
|
|
|
NULL);
|
2010-05-17 10:39:27 +00:00
|
|
|
|
2012-02-21 13:22:17 +00:00
|
|
|
return (CoglMaterial *)effect->priv->target;
|
2010-05-17 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_offscreen_effect_paint_target:
|
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
*
|
|
|
|
* Calls the paint_target() virtual function of the @effect
|
|
|
|
*
|
|
|
|
* Since: 1.4
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_offscreen_effect_paint_target (ClutterOffscreenEffect *effect)
|
|
|
|
{
|
|
|
|
g_return_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect));
|
|
|
|
|
|
|
|
CLUTTER_OFFSCREEN_EFFECT_GET_CLASS (effect)->paint_target (effect);
|
|
|
|
}
|
2010-04-16 11:33:20 +00:00
|
|
|
|
|
|
|
/**
|
2010-09-01 16:56:15 +00:00
|
|
|
* clutter_offscreen_effect_create_texture:
|
2010-04-16 11:33:20 +00:00
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
* @width: the minimum width of the target texture
|
|
|
|
* @height: the minimum height of the target texture
|
|
|
|
*
|
2010-09-01 16:56:15 +00:00
|
|
|
* Calls the create_texture() virtual function of the @effect
|
2010-04-16 11:33:20 +00:00
|
|
|
*
|
2010-09-06 15:11:46 +00:00
|
|
|
* Return value: (transfer full): a handle to a Cogl texture, or
|
|
|
|
* %COGL_INVALID_HANDLE. The returned handle has its reference
|
|
|
|
* count increased.
|
2010-04-16 11:33:20 +00:00
|
|
|
*
|
|
|
|
* Since: 1.4
|
|
|
|
*/
|
|
|
|
CoglHandle
|
2010-09-01 16:56:15 +00:00
|
|
|
clutter_offscreen_effect_create_texture (ClutterOffscreenEffect *effect,
|
|
|
|
gfloat width,
|
|
|
|
gfloat height)
|
2010-04-16 11:33:20 +00:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
|
2012-02-21 15:55:51 +00:00
|
|
|
NULL);
|
2010-04-16 11:33:20 +00:00
|
|
|
|
2010-09-01 16:56:15 +00:00
|
|
|
return CLUTTER_OFFSCREEN_EFFECT_GET_CLASS (effect)->create_texture (effect,
|
|
|
|
width,
|
|
|
|
height);
|
2010-04-16 11:33:20 +00:00
|
|
|
}
|
2011-02-19 09:15:34 +00:00
|
|
|
|
2011-02-28 14:31:59 +00:00
|
|
|
/**
|
|
|
|
* clutter_offscreen_effect_get_target_size:
|
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
* @width: (out): return location for the target width, or %NULL
|
|
|
|
* @height: (out): return location for the target height, or %NULL
|
|
|
|
*
|
|
|
|
* Retrieves the size of the offscreen buffer used by @effect to
|
|
|
|
* paint the actor to which it has been applied.
|
|
|
|
*
|
|
|
|
* This function should only be called by #ClutterOffscreenEffect
|
2014-03-17 23:07:58 +00:00
|
|
|
* implementations, from within the #ClutterOffscreenEffectClass.paint_target()
|
2011-02-28 14:31:59 +00:00
|
|
|
* virtual function.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the offscreen buffer has a valid size,
|
|
|
|
* and %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
2013-02-21 13:01:58 +00:00
|
|
|
*
|
|
|
|
* Deprecated: 1.14: Use clutter_offscreen_effect_get_target_rect() instead
|
2011-02-28 14:31:59 +00:00
|
|
|
*/
|
2011-02-19 09:15:34 +00:00
|
|
|
gboolean
|
2011-02-28 14:31:59 +00:00
|
|
|
clutter_offscreen_effect_get_target_size (ClutterOffscreenEffect *effect,
|
|
|
|
gfloat *width,
|
|
|
|
gfloat *height)
|
2011-02-19 09:15:34 +00:00
|
|
|
{
|
2011-02-28 14:31:59 +00:00
|
|
|
ClutterOffscreenEffectPrivate *priv;
|
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), FALSE);
|
2011-11-28 11:53:54 +00:00
|
|
|
|
2011-02-28 14:31:59 +00:00
|
|
|
priv = effect->priv;
|
2011-02-19 09:15:34 +00:00
|
|
|
|
2012-02-21 15:55:51 +00:00
|
|
|
if (priv->texture == NULL)
|
2011-02-19 09:15:34 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (width)
|
2011-11-28 11:53:54 +00:00
|
|
|
*width = cogl_texture_get_width (priv->texture);
|
2011-02-19 09:15:34 +00:00
|
|
|
|
|
|
|
if (height)
|
2011-11-28 11:53:54 +00:00
|
|
|
*height = cogl_texture_get_height (priv->texture);
|
2011-02-19 09:15:34 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-02-21 13:01:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_offscreen_effect_get_target_rect:
|
|
|
|
* @effect: a #ClutterOffscreenEffect
|
|
|
|
* @rect: (out caller-allocates): return location for the target area
|
|
|
|
*
|
|
|
|
* Retrieves the origin and size of the offscreen buffer used by @effect to
|
|
|
|
* paint the actor to which it has been applied.
|
|
|
|
*
|
|
|
|
* This function should only be called by #ClutterOffscreenEffect
|
2014-03-17 23:07:58 +00:00
|
|
|
* implementations, from within the #ClutterOffscreenEffectClass.paint_target()
|
2013-02-21 13:01:58 +00:00
|
|
|
* virtual function.
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the offscreen buffer has a valid rectangle,
|
|
|
|
* and %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.14
|
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
clutter_offscreen_effect_get_target_rect (ClutterOffscreenEffect *effect,
|
|
|
|
ClutterRect *rect)
|
|
|
|
{
|
|
|
|
ClutterOffscreenEffectPrivate *priv;
|
|
|
|
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), FALSE);
|
|
|
|
g_return_val_if_fail (rect != NULL, FALSE);
|
|
|
|
|
|
|
|
priv = effect->priv;
|
|
|
|
|
|
|
|
if (priv->texture == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
clutter_rect_init (rect,
|
|
|
|
priv->x_offset,
|
|
|
|
priv->y_offset,
|
|
|
|
cogl_texture_get_width (priv->texture),
|
|
|
|
cogl_texture_get_height (priv->texture));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|