mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 08:30:42 -05:00
3540d222e1
This is a fairly extensive second pass at exposing paint volumes for actors. The API has changed to allow clutter_actor_get_paint_volume to fail since there are times - such as when an actor isn't a descendent of the stage - when the volume can't be determined. Another example is when something has connected to the "paint" signal of the actor and we simply have no way of knowing what might be drawn in that handler. The API has also be changed to return a const ClutterPaintVolume pointer (transfer none) so we can avoid having to dynamically allocate the volumes in the most common/performance critical code paths. Profiling was showing the slice allocation of volumes taking about 1% of an apps time, for some fairly basic tests. Most volumes can now simply be allocated on the stack; for clutter_actor_get_paint_volume we return a pointer to &priv->paint_volume and if we need a more dynamic allocation there is now a _clutter_stage_paint_volume_stack_allocate() mechanism which lets us allocate data which expires at the start of the next frame. The API has been extended to make it easier to implement get_paint_volume for containers by using clutter_actor_get_transformed_paint_volume and clutter_paint_volume_union. The first allows you to query the paint volume of a child but transformed into parent actor coordinates. The second lets you combine volumes together so you can union all the volumes for a container's children and report that as the container's own volume. The representation of paint volumes has been updated to consider that 2D actors are the most common. The effect apis, clutter-texture and clutter-group have been update accordingly.
333 lines
9.7 KiB
C
333 lines
9.7 KiB
C
/*
|
|
* 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/>.
|
|
*
|
|
* Author:
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-blur-effect
|
|
* @short_description: A blur effect
|
|
* @see_also: #ClutterEffect, #ClutterOffscreenEffect
|
|
*
|
|
* #ClutterBlurEffect is a sub-class of #ClutterEffect that allows blurring a
|
|
* actor and its contents.
|
|
*
|
|
* #ClutterBlurEffect is available since Clutter 1.4
|
|
*/
|
|
|
|
#define CLUTTER_BLUR_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass))
|
|
#define CLUTTER_IS_BLUR_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BLUR_EFFECT))
|
|
#define CLUTTER_BLUR_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass))
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "clutter-blur-effect.h"
|
|
|
|
#include "cogl/cogl.h"
|
|
|
|
#include "clutter-debug.h"
|
|
#include "clutter-offscreen-effect.h"
|
|
#include "clutter-private.h"
|
|
|
|
#define BLUR_PADDING 2
|
|
|
|
typedef struct _ClutterBlurEffectClass ClutterBlurEffectClass;
|
|
|
|
/* FIXME - lame shader; we should really have a decoupled
|
|
* horizontal/vertical two pass shader for the gaussian blur
|
|
*/
|
|
static const gchar *box_blur_glsl_shader =
|
|
"#version 110\n"
|
|
"uniform sampler2D tex;\n"
|
|
"uniform float x_step, y_step;\n"
|
|
"\n"
|
|
"vec4 get_rgba_rel (sampler2D source, float dx, float dy)\n"
|
|
"{\n"
|
|
" return texture2D (tex, gl_TexCoord[0].st + vec2 (dx, dy) * 2.0);\n"
|
|
"}\n"
|
|
"\n"
|
|
"void main ()\n"
|
|
"{\n"
|
|
" vec4 color = gl_Color * texture2D (tex, vec2 (gl_TexCoord[0].xy));\n"
|
|
" color += get_rgba_rel (tex, -x_step, -y_step);\n"
|
|
" color += get_rgba_rel (tex, 0.0, -y_step);\n"
|
|
" color += get_rgba_rel (tex, x_step, -y_step);\n"
|
|
" color += get_rgba_rel (tex, -x_step, 0.0);\n"
|
|
" color += get_rgba_rel (tex, 0.0, 0.0);\n"
|
|
" color += get_rgba_rel (tex, x_step, 0.0);\n"
|
|
" color += get_rgba_rel (tex, -x_step, y_step);\n"
|
|
" color += get_rgba_rel (tex, 0.0, y_step);\n"
|
|
" color += get_rgba_rel (tex, x_step, y_step);\n"
|
|
" gl_FragColor = color / 9.0;\n"
|
|
"}";
|
|
|
|
struct _ClutterBlurEffect
|
|
{
|
|
ClutterOffscreenEffect parent_instance;
|
|
|
|
/* a back pointer to our actor, so that we can query it */
|
|
ClutterActor *actor;
|
|
|
|
/* the parameters; x_step and y_step depend on
|
|
* the actor's allocation
|
|
*/
|
|
gfloat x_step;
|
|
gfloat y_step;
|
|
|
|
CoglHandle shader;
|
|
CoglHandle program;
|
|
|
|
gint tex_uniform;
|
|
gint x_step_uniform;
|
|
gint y_step_uniform;
|
|
|
|
guint is_compiled : 1;
|
|
};
|
|
|
|
struct _ClutterBlurEffectClass
|
|
{
|
|
ClutterOffscreenEffectClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (ClutterBlurEffect,
|
|
clutter_blur_effect,
|
|
CLUTTER_TYPE_OFFSCREEN_EFFECT);
|
|
|
|
static int
|
|
next_p2 (int a)
|
|
{
|
|
int rval = 1;
|
|
|
|
while (rval < a)
|
|
rval <<= 1;
|
|
|
|
return rval;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_blur_effect_pre_paint (ClutterEffect *effect)
|
|
{
|
|
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
|
|
ClutterEffectClass *parent_class;
|
|
ClutterActorBox allocation;
|
|
gfloat width, height;
|
|
|
|
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
|
|
return FALSE;
|
|
|
|
self->actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
|
|
if (self->actor == NULL)
|
|
return FALSE;
|
|
|
|
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
|
|
{
|
|
/* if we don't have support for GLSL shaders then we
|
|
* forcibly disable the ActorMeta
|
|
*/
|
|
g_warning ("Unable to use the ShaderEffect: the graphics hardware "
|
|
"or the current GL driver does not implement support "
|
|
"for the GLSL shading language.");
|
|
clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
clutter_actor_get_allocation_box (self->actor, &allocation);
|
|
clutter_actor_box_get_size (&allocation, &width, &height);
|
|
|
|
self->x_step = 1.0f / (float) next_p2 (width);
|
|
self->y_step = 1.0f / (float) next_p2 (height);
|
|
|
|
if (self->shader == COGL_INVALID_HANDLE)
|
|
{
|
|
self->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
|
|
cogl_shader_source (self->shader, box_blur_glsl_shader);
|
|
|
|
self->is_compiled = FALSE;
|
|
self->tex_uniform = -1;
|
|
self->x_step_uniform = -1;
|
|
self->y_step_uniform = -1;
|
|
}
|
|
|
|
if (self->program == COGL_INVALID_HANDLE)
|
|
self->program = cogl_create_program ();
|
|
|
|
if (!self->is_compiled)
|
|
{
|
|
g_assert (self->shader != COGL_INVALID_HANDLE);
|
|
g_assert (self->program != COGL_INVALID_HANDLE);
|
|
|
|
cogl_shader_compile (self->shader);
|
|
if (!cogl_shader_is_compiled (self->shader))
|
|
{
|
|
gchar *log_buf = cogl_shader_get_info_log (self->shader);
|
|
|
|
g_warning (G_STRLOC ": Unable to compile the box blur shader: %s",
|
|
log_buf);
|
|
g_free (log_buf);
|
|
|
|
cogl_handle_unref (self->shader);
|
|
cogl_handle_unref (self->program);
|
|
|
|
self->shader = COGL_INVALID_HANDLE;
|
|
self->program = COGL_INVALID_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
cogl_program_attach_shader (self->program, self->shader);
|
|
cogl_program_link (self->program);
|
|
|
|
cogl_handle_unref (self->shader);
|
|
|
|
self->is_compiled = TRUE;
|
|
|
|
self->tex_uniform =
|
|
cogl_program_get_uniform_location (self->program, "tex");
|
|
self->x_step_uniform =
|
|
cogl_program_get_uniform_location (self->program, "x_step");
|
|
self->y_step_uniform =
|
|
cogl_program_get_uniform_location (self->program, "y_step");
|
|
}
|
|
}
|
|
|
|
parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
|
|
return parent_class->pre_paint (effect);
|
|
}
|
|
|
|
static CoglHandle
|
|
clutter_blur_effect_create_texture (ClutterOffscreenEffect *effect,
|
|
gfloat min_width,
|
|
gfloat min_height)
|
|
{
|
|
return cogl_texture_new_with_size (min_width + (2 * BLUR_PADDING),
|
|
min_height + (2 * BLUR_PADDING),
|
|
COGL_TEXTURE_NO_SLICING,
|
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
|
|
}
|
|
|
|
static void
|
|
clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect)
|
|
{
|
|
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
|
|
ClutterOffscreenEffectClass *parent;
|
|
CoglHandle material;
|
|
|
|
if (self->program == COGL_INVALID_HANDLE)
|
|
goto out;
|
|
|
|
if (self->tex_uniform > -1)
|
|
cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
|
|
|
|
if (self->x_step_uniform > -1)
|
|
cogl_program_set_uniform_1f (self->program,
|
|
self->x_step_uniform,
|
|
self->x_step);
|
|
|
|
if (self->y_step_uniform > -1)
|
|
cogl_program_set_uniform_1f (self->program,
|
|
self->y_step_uniform,
|
|
self->y_step);
|
|
|
|
material = clutter_offscreen_effect_get_target (effect);
|
|
cogl_material_set_user_program (material, self->program);
|
|
|
|
out:
|
|
parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_blur_effect_parent_class);
|
|
parent->paint_target (effect);
|
|
}
|
|
|
|
static gboolean
|
|
clutter_blur_effect_get_paint_volume (ClutterEffect *effect,
|
|
ClutterPaintVolume *volume)
|
|
{
|
|
gfloat cur_width, cur_height;
|
|
ClutterVertex origin;
|
|
|
|
clutter_paint_volume_get_origin (volume, &origin);
|
|
cur_width = clutter_paint_volume_get_width (volume);
|
|
cur_height = clutter_paint_volume_get_height (volume);
|
|
|
|
origin.x -= BLUR_PADDING;
|
|
origin.y -= BLUR_PADDING;
|
|
cur_width += BLUR_PADDING;
|
|
cur_height += BLUR_PADDING;
|
|
clutter_paint_volume_set_origin (volume, &origin);
|
|
clutter_paint_volume_set_width (volume, cur_width);
|
|
clutter_paint_volume_set_height (volume, cur_height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clutter_blur_effect_dispose (GObject *gobject)
|
|
{
|
|
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject);
|
|
|
|
if (self->program != COGL_INVALID_HANDLE)
|
|
{
|
|
cogl_handle_unref (self->program);
|
|
|
|
self->program = COGL_INVALID_HANDLE;
|
|
self->shader = COGL_INVALID_HANDLE;
|
|
}
|
|
|
|
G_OBJECT_CLASS (clutter_blur_effect_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_blur_effect_class_init (ClutterBlurEffectClass *klass)
|
|
{
|
|
ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterOffscreenEffectClass *offscreen_class;
|
|
|
|
gobject_class->dispose = clutter_blur_effect_dispose;
|
|
|
|
effect_class->pre_paint = clutter_blur_effect_pre_paint;
|
|
effect_class->get_paint_volume = clutter_blur_effect_get_paint_volume;
|
|
|
|
offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
|
|
offscreen_class->create_texture = clutter_blur_effect_create_texture;
|
|
offscreen_class->paint_target = clutter_blur_effect_paint_target;
|
|
}
|
|
|
|
static void
|
|
clutter_blur_effect_init (ClutterBlurEffect *self)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* clutter_blur_effect_new:
|
|
*
|
|
* Creates a new #ClutterBlurEffect to be used with
|
|
* clutter_actor_add_effect()
|
|
*
|
|
* Return value: the newly created #ClutterBlurEffect or %NULL
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterEffect *
|
|
clutter_blur_effect_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_BLUR_EFFECT, NULL);
|
|
}
|