+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
* 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
* @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.
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);
gint n_x_tiles;
gint n_y_tiles;
COGLuint *tiles;
+
+ ClutterActor *fbo_source;
+ COGLuint fbo_handle;
};
enum
texture = CLUTTER_TEXTURE(actor);
priv = texture->priv;
+ if (priv->fbo_handle)
+ return; /* handle better */
+
CLUTTER_MARK();
if (priv->local_pixbuf != NULL)
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)
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);
+}
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,
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
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
{
glDepthFunc (GL_LEQUAL);
cogl_enable (CGL_ENABLE_BLEND);
-
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
}
#endif
+#if 0
if (flags & CGL_ENABLE_ALPHA_TEST)
{
if (!(__enable_flags & CGL_ENABLE_ALPHA_TEST))
glDisable (GL_ALPHA_TEST);
__enable_flags &= ~CGL_ENABLE_ALPHA_TEST;
}
+#endif
}
void
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;
}
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...) \
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)
{
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);
}
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;
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
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
--- /dev/null
+/*#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 (¬hing);
+ 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 ();
+}