2008-02-01 Matthew Allum <mallum@openedhand.com>

* clutter/clutter-feature.h:
        * clutter/clutter-texture.c:
        * clutter/clutter-texture.h:
        * clutter/cogl/cogl.h:
        * clutter/cogl/gl/cogl.c:
        * clutter/cogl/gles/cogl.c:
        * tests/Makefile.am:
        * tests/test.fbo.c:
        Add initial support for FBO's in Clutter (OpenGL only so far).
        See new clutter_texture_new_from_actor()
        Initial implementation, needs work.

        * clutter/x11/clutter-stage-x11.c:
        (clutter_stage_x11_set_cursor_visible):
        Fall back to again not relying on xfixes to hide cursor. *sigh*

        * clutter/clutter-deprecated.h:
        Add clutter_group_find_child_by_id
This commit is contained in:
Matthew Allum 2008-02-01 15:29:00 +00:00
parent 6bb998f25f
commit 8366a906e2
11 changed files with 438 additions and 6 deletions

View File

@ -1,3 +1,24 @@
2008-02-01 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-feature.h:
* clutter/clutter-texture.c:
* clutter/clutter-texture.h:
* clutter/cogl/cogl.h:
* clutter/cogl/gl/cogl.c:
* clutter/cogl/gles/cogl.c:
* tests/Makefile.am:
* tests/test.fbo.c:
Add initial support for FBO's in Clutter (OpenGL only so far).
See new clutter_texture_new_from_actor()
Initial implementation, needs work.
* clutter/x11/clutter-stage-x11.c:
(clutter_stage_x11_set_cursor_visible):
Fall back to again not relying on xfixes to hide cursor. *sigh*
* clutter/clutter-deprecated.h:
Add clutter_group_find_child_by_id
2008-02-01 Øyvind Kolås <pippin@o-hand.com>
* clutter/cogl/gles/cogl.c: (cogl_rectangle_internal): increase size

View File

@ -11,6 +11,8 @@
* new functionality should giver errors containing _DEPRECATED_BY_.
*/
#define clutter_group_find_child_by_id clutter_group_find_child_by_id_REPLACED_BY_clutter_container_find_child_by_name
#define clutter_behaviour_ellipse_set_angle_begin clutter_behaviour_ellipse_set_angle_begin_REPLACED_BY_clutter_behaviour_set_angle_start
#define clutter_behaviour_ellipse_set_angle_beginx clutter_behaviour_ellipse_set_angle_beginx_REPLACED_BY_clutter_behaviour_set_angle_startx
#define clutter_behaviour_ellipse_get_angle_begin clutter_behaviour_ellipse_get_angle_begin_REPLACED_BY_clutter_behaviour_get_angle_start

View File

@ -47,6 +47,7 @@ G_BEGIN_DECLS
* @CLUTTER_FEATURE_STAGE_USER_RESIZE: Set if stage is able to be user resized.
* @CLUTTER_FEATURE_STAGE_CURSOR: Set if stage has a graphical cursor.
* @CLUTTER_FEATURE_SHADERS_GLSL: Set if the backend supports GLSL shaders.
* @CLUTTER_FEATURE_OFFSCREEN: Set if the backend supports offscreen rendering.
*
* Runtime flags indicating specific features available via Clutter window
* sysytem and graphics backend.
@ -62,7 +63,8 @@ typedef enum
CLUTTER_FEATURE_STAGE_STATIC = (1 << 5),
CLUTTER_FEATURE_STAGE_USER_RESIZE = (1 << 6),
CLUTTER_FEATURE_STAGE_CURSOR = (1 << 7),
CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8)
CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8),
CLUTTER_FEATURE_OFFSCREEN = (1 << 9)
} ClutterFeatureFlags;
gboolean clutter_feature_available (ClutterFeatureFlags feature);

View File

@ -96,6 +96,9 @@ struct _ClutterTexturePrivate
gint n_x_tiles;
gint n_y_tiles;
COGLuint *tiles;
ClutterActor *fbo_source;
COGLuint fbo_handle;
};
enum
@ -668,6 +671,9 @@ clutter_texture_realize (ClutterActor *actor)
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
if (priv->fbo_handle)
return; /* handle better */
CLUTTER_MARK();
if (priv->local_pixbuf != NULL)
@ -751,6 +757,19 @@ clutter_texture_paint (ClutterActor *self)
return;
}
if (priv->fbo_handle)
{
cogl_offscreen_redirect_start (priv->fbo_handle,
priv->width, priv->height);
clutter_actor_paint (priv->fbo_source);
cogl_offscreen_redirect_end (priv->fbo_handle,
CLUTTER_STAGE_WIDTH(),
CLUTTER_STAGE_HEIGHT());
glBindTexture(CGL_TEXTURE_RECTANGLE_ARB, priv->tiles[0]);
}
CLUTTER_NOTE (PAINT,
"painting texture '%s'",
clutter_actor_get_name (self) ? clutter_actor_get_name (self)
@ -2083,3 +2102,90 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
return TRUE;
}
/**
* clutter_texture_new_from_actor:
* @actor: A #ClutterActor
*
* Creates a new #ClutterTexture object with its source a prexisting
* actor (and associated children).
*
* Note this function is intented as a utility call for uniformly applying
* shaders to groups and other potentail visual effects. It requires the
* #CLUTTER_FEATURE_TEXTURE_RECTANGLE & #CLUTTER_FEATURE_OFFSCREEN features
* are supported by both the current backend and target system
*
* Return value: A newly created #ClutterTexture object or NULL on fail.
**/
ClutterActor *
clutter_texture_new_from_actor (ClutterActor *actor)
{
ClutterTexture *texture;
ClutterTexturePrivate *priv;
guint w, h;
/* TODO (before 0.6 release):
*
* - Figure out getting source actor size correctly.
* - Figure out refing/reparenting source actor.
* - Handle source actor resizing.
* - Handle failure better.
* - Handle cleanup on destruction.
* - Beef up test-fbo.
* - Have the source actor as a prop?
*/
if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE) == FALSE)
return NULL;
if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE)
return NULL;
texture = g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
priv = texture->priv;
priv->fbo_source = actor;
if (!CLUTTER_ACTOR_IS_REALIZED (priv->fbo_source))
clutter_actor_realize (priv->fbo_source);
/* FIXME: just ref ? */
clutter_actor_set_parent (actor, CLUTTER_ACTOR(texture));
/* FIXME abs size */
/*clutter_actor_get_abs_size (priv->fbo_source, &w, &h);*/
clutter_actor_get_size (actor, &w, &h);
/* FIXME: Check we can actually create a texture this large */
priv->width = w;
priv->height = h;
priv->target_type = CGL_TEXTURE_RECTANGLE_ARB;
priv->pixel_format = CGL_RGBA;
priv->pixel_type = PIXEL_TYPE;
priv->is_tiled = 0;
priv->tiles = g_new (COGLuint, 1);
/* FIXME: needs a cogl wrapper */
glGenTextures (1, priv->tiles);
cogl_texture_bind (priv->target_type, priv->tiles[0]);
cogl_texture_image_2d (priv->target_type,
CGL_RGBA,
w,
h,
priv->pixel_format,
priv->pixel_type,
NULL);
priv->fbo_handle = cogl_offscreen_create (priv->tiles[0]);
clutter_actor_set_size (actor, w, h);
return CLUTTER_ACTOR(texture);
}

View File

@ -119,7 +119,7 @@ gboolean clutter_texture_set_from_rgb_data (ClutterTexture *texture,
gint bpp,
ClutterTextureFlags flags,
GError **error);
ClutterActor *clutter_texture_new_from_actor (ClutterActor *actor);
gboolean clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
const guchar *data,
gboolean has_alpha,

View File

@ -264,6 +264,23 @@ void
cogl_program_uniform_1f (COGLint uniform_no,
gfloat value);
/* Offscreen - FBO support */
COGLuint
cogl_offscreen_create (COGLuint target_texture);
void
cogl_offscreen_destroy (COGLuint offscreen_handle);
void
cogl_offscreen_redirect_start (COGLuint offscreen_handle,
gint width,
gint height);
void
cogl_offscreen_redirect_end (COGLuint offscreen_handle,
gint width,
gint height);
G_END_DECLS

View File

@ -41,6 +41,21 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
static gulong __enable_flags = 0;
/* FBO Procs */
typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids);
typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
GLenum textarget, GLuint texture,
GLint level);
typedef GLenum (*CheckFramebufferStatus)(GLenum target);
typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers);
static GenFramebuffers _gen_framebuffers = NULL;
static BindFramebuffer _bind_framebuffer = NULL;
static FramebufferTexture2D _framebuffer_texture_2d = NULL;
static CheckFramebufferStatus _check_framebuffer_status = NULL;
static DeleteFramebuffers _delete_framebuffers = NULL;
#if COGL_DEBUG
struct token_string
{
@ -189,7 +204,6 @@ cogl_paint_init (const ClutterColor *color)
glDepthFunc (GL_LEQUAL);
cogl_enable (CGL_ENABLE_BLEND);
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
@ -295,6 +309,7 @@ cogl_enable (gulong flags)
}
#endif
#if 0
if (flags & CGL_ENABLE_ALPHA_TEST)
{
if (!(__enable_flags & CGL_ENABLE_ALPHA_TEST))
@ -308,6 +323,7 @@ cogl_enable (gulong flags)
glDisable (GL_ALPHA_TEST);
__enable_flags &= ~CGL_ENABLE_ALPHA_TEST;
}
#endif
}
void
@ -648,6 +664,35 @@ cogl_get_features ()
flags |= CLUTTER_FEATURE_SHADERS_GLSL;
}
if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) ||
cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions))
{
_gen_framebuffers =
(GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT");
_bind_framebuffer =
(BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT");
_framebuffer_texture_2d =
(FramebufferTexture2D)
cogl_get_proc_address ("glFramebufferTexture2DEXT");
_check_framebuffer_status =
(CheckFramebufferStatus)
cogl_get_proc_address ("glCheckFramebufferStatusEXT");
_delete_framebuffers =
(DeleteFramebuffers)
cogl_get_proc_address ("glDeleteFramebuffersEXT");
if (_gen_framebuffers
&& _bind_framebuffer
&& _framebuffer_texture_2d
&& _check_framebuffer_status
&& _delete_framebuffers)
flags |= CLUTTER_FEATURE_OFFSCREEN;
}
return flags;
}
@ -774,6 +819,112 @@ cogl_fog_set (const ClutterColor *fog_color,
glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop));
}
/* FBOs - offscreen */
COGLuint
cogl_offscreen_create (COGLuint target_texture)
{
#ifdef GL_FRAMEBUFFER_EXT
COGLuint handle;
GLenum status;
if (_gen_framebuffers == NULL
|| _bind_framebuffer == NULL
|| _framebuffer_texture_2d == NULL
|| _check_framebuffer_status == NULL)
{
/* tmp warning - need error reporting */
g_warning("Missing GL_FRAMEBUFFER_EXT API\n");
return 0;
}
_gen_framebuffers (1, &handle);
_bind_framebuffer (GL_FRAMEBUFFER_EXT, handle);
_framebuffer_texture_2d (GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_RECTANGLE_ARB,
target_texture,
0);
status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
_delete_framebuffers (1, &handle);
return 0;
}
_bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
return handle;
#else
/* tmp warning - need error reporting */
g_warning("No GL_FRAMEBUFFER_EXT\n");
return 0;
#endif
}
void
cogl_offscreen_destroy (COGLuint offscreen_handle)
{
if (_delete_framebuffers)
_delete_framebuffers (1, &offscreen_handle);
}
void
cogl_offscreen_redirect_start (COGLuint offscreen_handle,
gint width,
gint height)
{
/* FIXME: silly we need to pass width / height to init viewport */
#ifdef GL_FRAMEBUFFER_EXT
if (_bind_framebuffer == NULL)
return;
_bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle);
glViewport (0, 0, width, height);
glMatrixMode (GL_PROJECTION);
glPushMatrix();
glLoadIdentity ();
glMatrixMode (GL_MODELVIEW);
glPushMatrix();
glLoadIdentity ();
glTranslatef (-1.0f, -1.0f, 0.0f);
glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f);
#endif
}
void
cogl_offscreen_redirect_end (COGLuint offscreen_handle,
gint width,
gint height)
{
/* FIXME: silly we need to pass width / height to reset to */
if (_bind_framebuffer == NULL)
return;
#ifdef GL_FRAMEBUFFER_EXT
glViewport (0, 0, width, height);
glMatrixMode (GL_PROJECTION);
glPopMatrix();
glMatrixMode (GL_MODELVIEW);
glPopMatrix();
_bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
#endif
}
/* Shader Magic follows */
#ifdef __GNUC__
#define PROC(rettype, retval, procname, args...) \

View File

@ -645,6 +645,36 @@ cogl_fog_set (const ClutterColor *fog_color,
glFogx (GL_FOG_END, (GLfixed) z_far);
}
/* Offscreen - TODO: possible support from FBO's */
COGLuint
cogl_offscreen_create (COGLuint target_texture)
{
return 0;
}
void
cogl_offscreen_destroy (COGLuint offscreen_handle)
{
}
void
cogl_offscreen_redirect_start (COGLuint offscreen_handle,
gint width,
gint height)
{
}
void
cogl_offscreen_redirect_end (COGLuint offscreen_handle,
gint width,
gint height)
{
}
/* Shaders, no support on regular OpenGL 1.1 */
COGLhandle
cogl_create_program (void)
{

View File

@ -308,7 +308,7 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
if (show_cursor)
{
#if HAVE_XFIXES
#if 0 /* HAVE_XFIXES - seems buggy/unreliable */
XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin);
#else
XUndefineCursor (stage_x11->xdpy, stage_x11->xwin);
@ -316,7 +316,9 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
}
else
{
#if HAVE_XFIXES
#if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox
* loading page after hiding.
*/
XFixesHideCursor (stage_x11->xdpy, stage_x11->xwin);
#else
XColor col;

View File

@ -3,7 +3,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
test-perspective test-rotate test-depth \
test-threads test-timeline test-score test-script \
test-model test-grab test-effects test-fullscreen \
test-shader test-unproject test-viewport
test-shader test-unproject test-viewport test-fbo
INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@ -33,5 +33,6 @@ test_model_SOURCES = test-model.c
test_effects_SOURCES = test-effects.c
test_fullscreen_SOURCES = test-fullscreen.c
test_viewport_SOURCES = test-viewport.c
test_fbo_SOURCES = test-fbo.c
EXTRA_DIST = redhand.png test-script.json

100
tests/test-fbo.c Normal file
View File

@ -0,0 +1,100 @@
/*#define TEST_GROUP */
#include <clutter/clutter.h>
#include <errno.h>
#include <stdlib.h>
#include <glib.h>
gint
main (gint argc,
gchar *argv[])
{
ClutterColor color={0x33, 0x44, 0x55, 0xff};
ClutterActor *fbo;
ClutterActor *actor;
ClutterActor *actor2;
ClutterActor *group;
ClutterShader *shader;
ClutterActor *stage;
ClutterActor *rectangle;
ClutterActor *clone;
GdkPixbuf *pixbuf;
GError *error = NULL;
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &color);
pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error);
if (!pixbuf)
g_error("pixbuf load failed: %s", error ? error->message : "Unknown");
/* actor = clutter_texture_new_from_pixbuf (pixbuf);*/
group = clutter_group_new ();
{
ClutterColor nothing = {0, 0,0,0};
rectangle = clutter_rectangle_new_with_color (&nothing);
clutter_actor_set_size (rectangle, 800, 270);
}
actor2 = clutter_texture_new_from_pixbuf (pixbuf);
clutter_container_add_actor (CLUTTER_CONTAINER (group), actor2);
{
ClutterColor yellow = {0xff, 0xff, 0x00, 0xff};
actor = clutter_label_new_with_text ("Sans 50px", "Hello hadyness");
clutter_label_set_color (CLUTTER_LABEL (actor), &yellow);
}
clutter_container_add_actor (CLUTTER_CONTAINER (group), actor);
clutter_container_add_actor (CLUTTER_CONTAINER (group), rectangle);
clutter_actor_set_position (actor, 0, 15);
clutter_actor_show_all (group);
fbo = clutter_texture_new_from_actor (group);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), fbo);
clutter_actor_set_position (fbo, 20, 120);
clutter_actor_set_position (actor2, 130, 20);
shader = clutter_shader_new ();
clutter_shader_set_fragment_source (shader,
"uniform float radius ;"
"uniform sampler2DRect rectTexture;"
""
"void main()"
"{"
" vec4 color = texture2DRect(rectTexture, gl_TexCoord[0].st);"
" float u;"
" float v;"
" int count = 1;"
" for (u=-radius;u<radius;u++)"
" for (v=-radius;v<radius;v++)"
" {"
" color += texture2DRect(rectTexture, vec2(gl_TexCoord[0].s + u * 2.0, gl_TexCoord[0].t +v * 2.0));"
" count ++;"
" }"
""
" gl_FragColor = color / float(count);"
"}",
-1
);
clone = clutter_clone_texture_new (fbo);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), clone);
clutter_actor_set_position (clone, 40, 300);
if(1)clutter_actor_apply_shader (clone, shader);
if(1)clutter_actor_set_shader_param (clone, "radius", 2.0);
clutter_actor_show_all (stage);
clutter_main ();
}