From 1cbafc99d6a05c223cf20a3857966f9eae6f4459 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 16:25:20 +0000 Subject: [PATCH 1/7] * tests/conform/test-mesh-mutability.c: * tests/conform/test-mesh-interleved.c: * tests/conform/test-mesh-contiguous.c: Remove the idle source after the test is complete so that it won't interfere with other tests. --- ChangeLog | 8 ++++++++ tests/conform/test-mesh-contiguous.c | 5 ++++- tests/conform/test-mesh-interleved.c | 5 ++++- tests/conform/test-mesh-mutability.c | 5 ++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index a992b6f2c..a350dd1ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-11-28 Neil Roberts + + * tests/conform/test-mesh-mutability.c: + * tests/conform/test-mesh-interleved.c: + * tests/conform/test-mesh-contiguous.c: Remove the idle source + after the test is complete so that it won't interfere with other + tests. + 2008-11-26 Emmanuele Bassi * clutter/clutter-group.c: diff --git a/tests/conform/test-mesh-contiguous.c b/tests/conform/test-mesh-contiguous.c index f411b755f..b5d940ad7 100644 --- a/tests/conform/test-mesh-contiguous.c +++ b/tests/conform/test-mesh-contiguous.c @@ -128,6 +128,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture, ClutterActor *stage; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterActor *group; + guint idle_source; state.frame = 0; @@ -145,7 +146,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture, /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ - g_idle_add (queue_redraw, stage); + idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); @@ -186,6 +187,8 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture, cogl_mesh_unref (state.mesh); + g_source_remove (idle_source); + if (g_test_verbose ()) g_print ("OK\n"); } diff --git a/tests/conform/test-mesh-interleved.c b/tests/conform/test-mesh-interleved.c index ea4cafdc3..5cd3a2b1b 100644 --- a/tests/conform/test-mesh-interleved.c +++ b/tests/conform/test-mesh-interleved.c @@ -99,6 +99,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture, ClutterActor *stage; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterActor *group; + guint idle_source; state.frame = 0; @@ -116,7 +117,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture, /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ - g_idle_add (queue_redraw, stage); + idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); @@ -164,6 +165,8 @@ test_mesh_interleved (TestConformSimpleFixture *fixture, cogl_mesh_unref (state.mesh); + g_source_remove (idle_source); + if (g_test_verbose ()) g_print ("OK\n"); } diff --git a/tests/conform/test-mesh-mutability.c b/tests/conform/test-mesh-mutability.c index 08bd88abc..af7ec80f5 100644 --- a/tests/conform/test-mesh-mutability.c +++ b/tests/conform/test-mesh-mutability.c @@ -139,6 +139,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture, ClutterActor *stage; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterActor *group; + guint idle_source; state.frame = 0; @@ -156,7 +157,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture, /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ - g_idle_add (queue_redraw, stage); + idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); @@ -197,6 +198,8 @@ test_mesh_mutability (TestConformSimpleFixture *fixture, cogl_mesh_unref (state.mesh); + g_source_remove (idle_source); + if (g_test_verbose ()) g_print ("OK\n"); } From 72f2fc22adff4fb3e85f9a17644275b1add4a703 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 17:36:37 +0000 Subject: [PATCH 2/7] * tests/conform/test-backface-culling.c: New test for backface culling * tests/conform/test-conform-main.c (main): Add /texture/test_backface_culing * tests/conform/Makefile.am (test_conformance_SOURCES): Add test-backface-culling.c --- ChangeLog | 11 ++ tests/conform/Makefile.am | 3 +- tests/conform/test-backface-culling.c | 266 ++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 2 + 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 tests/conform/test-backface-culling.c diff --git a/ChangeLog b/ChangeLog index a350dd1ef..f71882749 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-11-28 Neil Roberts + + * tests/conform/test-backface-culling.c: New test for backface + culling + + * tests/conform/test-conform-main.c (main): Add + /texture/test_backface_culing + + * tests/conform/Makefile.am (test_conformance_SOURCES): Add + test-backface-culling.c + 2008-11-28 Neil Roberts * tests/conform/test-mesh-mutability.c: diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index c71242c71..9706b8a03 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -19,7 +19,8 @@ test_conformance_SOURCES = \ test-clutter-rectangle.c \ test-clutter-fixed.c \ test-actor-invariants.c \ - test-paint-opacity.c + test-paint-opacity.c \ + test-backface-culling.c # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c new file mode 100644 index 000000000..3a18d8916 --- /dev/null +++ b/tests/conform/test-backface-culling.c @@ -0,0 +1,266 @@ + +#include +#include +#include + +#include "test-conform-common.h" + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +/* Size the texture so that it is just off a power of two to enourage + it so use software tiling when NPOTs aren't available */ +#define TEXTURE_SIZE 33 + +/* Amount of pixels to skip off the top, bottom, left and right of the + texture when reading back the stage */ +#define TEST_INSET 4 + +typedef struct _TestState +{ + guint frame; + CoglHandle texture; +} TestState; + +static gboolean +validate_part (int xnum, int ynum, gboolean shown) +{ + guchar *pixels, *p; + ClutterActor *stage = clutter_stage_get_default (); + gboolean ret = TRUE; + + /* Read the appropriate part but skip out a few pixels around the + edges */ + pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage), + xnum * TEXTURE_SIZE + TEST_INSET, + ynum * TEXTURE_SIZE + TEST_INSET, + TEXTURE_SIZE - TEST_INSET * 2, + TEXTURE_SIZE - TEST_INSET * 2); + + /* Make sure every pixels is the appropriate color */ + for (p = pixels; + p < pixels + ((TEXTURE_SIZE - TEST_INSET * 2) + * (TEXTURE_SIZE - TEST_INSET * 2)); + p += 4) + { + if (p[0] != (shown ? 255 : 0)) + ret = FALSE; + if (p[1] != 0) + ret = FALSE; + if (p[2] != 0) + ret = FALSE; + } + + g_free (pixels); + + return ret; +} + +static void +validate_result (TestState *state) +{ + /* Front-facing texture */ + g_assert (validate_part (0, 0, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (1, 0, FALSE)); + /* Front-facing texture polygon */ + g_assert (validate_part (2, 0, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (3, 0, FALSE)); + /* Regular rectangle */ + g_assert (validate_part (4, 0, TRUE)); + + /* Backface culling disabled - everything should be shown */ + + /* Front-facing texture */ + g_assert (validate_part (0, 1, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (1, 1, TRUE)); + /* Front-facing texture polygon */ + g_assert (validate_part (2, 1, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (3, 1, TRUE)); + /* Regular rectangle */ + g_assert (validate_part (4, 1, TRUE)); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int i; + int frame_num; + + cogl_enable_backface_culling (TRUE); + + cogl_push_matrix (); + + /* Render the scene twice - once with backface culling enabled and + once without. The second time is translated so that it is below + the first */ + for (i = 0; i < 2; i++) + { + CoglFixed x1 = 0, x2, y1 = 0, y2 = COGL_FIXED_FROM_INT (TEXTURE_SIZE); + CoglTextureVertex verts[4]; + + memset (verts, 0, sizeof (verts)); + + /* Set the color to white so that all the textures will be drawn + at their own color */ + cogl_set_source_color4x (COGL_FIXED_1, COGL_FIXED_1, + COGL_FIXED_1, COGL_FIXED_1); + + x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE); + + /* Draw a front-facing texture */ + cogl_texture_rectangle (state->texture, + x1, y1, x2, y2, + 0, 0, COGL_FIXED_1, COGL_FIXED_1); + + x1 = x2; + x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE); + + /* Draw a back-facing texture */ + cogl_texture_rectangle (state->texture, + x2, y1, x1, y2, + 0, 0, COGL_FIXED_1, COGL_FIXED_1); + + x1 = x2; + x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE); + + /* Draw a front-facing texture polygon */ + verts[0].x = x1; verts[0].y = y2; + verts[1].x = x2; verts[1].y = y2; + verts[2].x = x2; verts[2].y = y1; + verts[3].x = x1; verts[3].y = y1; + verts[0].tx = 0; verts[0].ty = 0; + verts[1].tx = COGL_FIXED_1; verts[1].ty = 0; + verts[2].tx = COGL_FIXED_1; verts[2].ty = COGL_FIXED_1; + verts[3].tx = 0; verts[3].ty = COGL_FIXED_1; + cogl_texture_polygon (state->texture, 4, + verts, FALSE); + + x1 = x2; + x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE); + + /* Draw a back-facing texture polygon */ + verts[0].x = x1; verts[0].y = y1; + verts[1].x = x2; verts[1].y = y1; + verts[2].x = x2; verts[2].y = y2; + verts[3].x = x1; verts[3].y = y2; + verts[0].tx = 0; verts[0].ty = 0; + verts[1].tx = COGL_FIXED_1; verts[1].ty = 0; + verts[2].tx = COGL_FIXED_1; verts[2].ty = COGL_FIXED_1; + verts[3].tx = 0; verts[3].ty = COGL_FIXED_1; + cogl_texture_polygon (state->texture, 4, + verts, FALSE); + + x1 = x2; + x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE); + + /* Draw a regular rectangle (this should always show) */ + cogl_set_source_color4x (COGL_FIXED_1, 0, 0, COGL_FIXED_1); + cogl_rectangle (COGL_FIXED_TO_INT (x1), COGL_FIXED_TO_INT (y1), + COGL_FIXED_TO_INT (x2 - x1), COGL_FIXED_TO_INT (y2 - y1)); + + /* The second time round draw beneath the first with backface + culling disabled */ + cogl_translate (0, TEXTURE_SIZE, 0); + cogl_enable_backface_culling (FALSE); + } + + cogl_pop_matrix (); + + /* XXX: Experiments have shown that for some buggy drivers, when using + * glReadPixels there is some kind of race, so we delay our test for a + * few frames and a few seconds: + */ + /* Need to increment frame first because clutter_stage_read_pixels + fires a redraw */ + frame_num = state->frame++; + if (frame_num == 2) + validate_result (state); + else if (frame_num < 2) + g_usleep (G_USEC_PER_SEC); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +static CoglHandle +make_texture (void) +{ + guchar *tex_data, *p; + CoglHandle tex; + + tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); + + for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;) + { + *(--p) = 255; + *(--p) = 0; + *(--p) = 0; + *(--p) = 255; + } + + tex = cogl_texture_new_from_data (TEXTURE_SIZE, + TEXTURE_SIZE, + 8, + FALSE, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_ANY, + TEXTURE_SIZE * 4, + tex_data); + + g_free (tex_data); + + return tex; +} + +void +test_backface_culling (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + ClutterActor *stage; + ClutterActor *group; + guint idle_source; + + state.frame = 0; + + state.texture = make_texture (); + + stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + group = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + + g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (stage); + + clutter_main (); + + g_source_remove (idle_source); + + cogl_texture_unref (state.texture); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index b70ab1caf..1f6e2c811 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -103,5 +103,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/opacity", test_rectangle_opacity); TEST_CONFORM_SIMPLE ("/opacity", test_paint_opacity); + TEST_CONFORM_SIMPLE ("/texture", test_backface_culling); + return g_test_run (); } From f052335bb4a5ef25042d16832f05ef7b0058cd80 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 28 Nov 2008 17:45:54 +0000 Subject: [PATCH 3/7] * tests/conform/test-backface-culling.c (TEXTURE_SIZE): Don't set to a funny size on GLES because it will break cogl_texture_polygon --- ChangeLog | 5 +++++ tests/conform/test-backface-culling.c | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/ChangeLog b/ChangeLog index f71882749..1b2b02a66 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2008-11-28 Neil Roberts + + * tests/conform/test-backface-culling.c (TEXTURE_SIZE): Don't set + to a funny size on GLES because it will break cogl_texture_polygon + 2008-11-28 Neil Roberts * tests/conform/test-backface-culling.c: New test for backface diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c index 3a18d8916..3b7948e96 100644 --- a/tests/conform/test-backface-culling.c +++ b/tests/conform/test-backface-culling.c @@ -7,10 +7,21 @@ static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; +#ifdef CLUTTER_COGL_HAS_GL + /* Size the texture so that it is just off a power of two to enourage it so use software tiling when NPOTs aren't available */ #define TEXTURE_SIZE 33 +#else /* CLUTTER_COGL_HAS_GL */ + +/* We can't use the funny-sized texture on GL ES because it will break + cogl_texture_polygon. However there is only one code path for + rendering quads so there is no need */ +#define TEXTURE_SIZE 32 + +#endif /* CLUTTER_COGL_HAS_GL */ + /* Amount of pixels to skip off the top, bottom, left and right of the texture when reading back the stage */ #define TEST_INSET 4 From 55cec1f65170fea67cb889f70c979a50eada0d48 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 1 Dec 2008 16:27:54 +0000 Subject: [PATCH 4/7] Bug 1305 - NPOT textures unaligned to a pixel sometimes have border artifacts * clutter/cogl/gl/cogl-texture.c: Set the wrap mode of a texture on demand Instead of setting the wrap mode once per texture at creation, it is now changed whenever the texture is drawn. The previous value is cached so that it isn't changed if the value is the same. This is used in _cogl_texture_quad_hw to only enable GL_REPEAT mode when the coordinates are not in the range [0,1]. Otherwise it can pull in pixels from the other edge when the texture is rendered off-pixel. --- ChangeLog | 17 ++++++ clutter/cogl/gl/cogl-texture.c | 102 ++++++++++++++++----------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1b2b02a66..46109caf9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-12-01 Neil Roberts + + Bug 1305 - NPOT textures unaligned to a pixel sometimes have + border artifacts + + * clutter/cogl/gl/cogl-texture.c: Set the wrap mode of a texture + on demand + + Instead of setting the wrap mode once per texture at creation, it + is now changed whenever the texture is drawn. The previous value + is cached so that it isn't changed if the value is the same. + + This is used in _cogl_texture_quad_hw to only enable GL_REPEAT + mode when the coordinates are not in the range [0,1]. Otherwise it + can pull in pixels from the other edge when the texture is + rendered off-pixel. + 2008-11-28 Neil Roberts * tests/conform/test-backface-culling.c (TEXTURE_SIZE): Don't set diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index bf2eef304..a1c5e7d75 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -763,6 +763,29 @@ _cogl_texture_size_supported (GLenum gl_target, } } +static void +_cogl_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex->wrap_mode != wrap_mode) + { + int i; + + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GLuint texnum = g_array_index (tex->slice_gl_handles, GLuint, i); + + GE( glBindTexture (tex->gl_target, texnum) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); + } + + tex->wrap_mode = wrap_mode; + } +} + static gboolean _cogl_texture_slices_create (CoglTexture *tex) { @@ -890,15 +913,10 @@ _cogl_texture_slices_create (CoglTexture *tex) n_slices); g_array_set_size (tex->slice_gl_handles, n_slices); - - - /* Hardware repeated tiling if supported, else tile in software*/ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && n_slices == 1) - tex->wrap_mode = GL_REPEAT; - else - tex->wrap_mode = GL_CLAMP_TO_EDGE; - + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + /* Generate a "working set" of GL texture objects * (some implementations might supported faster * re-binding between textures inside a set) */ @@ -930,11 +948,6 @@ _cogl_texture_slices_create (CoglTexture *tex) GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, - tex->wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, - tex->wrap_mode) ); - if (tex->auto_mipmap) GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); @@ -1507,7 +1520,10 @@ cogl_texture_new_from_foreign (GLuint gl_handle, tex->min_filter = gl_min_filter; tex->mag_filter = gl_mag_filter; tex->max_waste = 0; - + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + /* Create slice arrays */ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, @@ -1533,24 +1549,7 @@ cogl_texture_new_from_foreign (GLuint gl_handle, g_array_append_val (tex->slice_y_spans, y_span); g_array_append_val (tex->slice_gl_handles, gl_handle); - - /* Force appropriate wrap parameter */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && - gl_target == GL_TEXTURE_2D) - { - /* Hardware repeated tiling */ - tex->wrap_mode = GL_REPEAT; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) ); - } - else - { - /* Any tiling will be done in software */ - tex->wrap_mode = GL_CLAMP_TO_EDGE; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); - } - + return _cogl_texture_handle_new (tex); } @@ -1952,6 +1951,10 @@ _cogl_texture_quad_sw (CoglTexture *tex, cogl_enable (enable_flags); + /* We can't use hardware repeat so we need to set clamp to edge + otherwise it might pull in edge pixels from the other side */ + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be flipped but we can still use the same algorithm to iterate the @@ -2129,7 +2132,19 @@ _cogl_texture_quad_hw (CoglTexture *tex, enable_flags |= COGL_ENABLE_BACKFACE_CULLING; cogl_enable (enable_flags); - + + + /* If the texture coords are all in the range [0,1] then we want to + clamp the coords to the edge otherwise it can pull in edge pixels + from the wrong side when scaled */ + if (tx1 >= 0 && tx1 <= COGL_FIXED_1 + && tx2 >= 0 && tx2 <= COGL_FIXED_1 + && ty1 >= 0 && ty1 <= COGL_FIXED_1 + && ty2 >= 0 && ty2 <= COGL_FIXED_1) + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + else + _cogl_texture_set_wrap_mode_parameter (tex, GL_REPEAT); + GE( glTexCoordPointer (2, GL_FLOAT, 0, tex_coords) ); GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords) ); @@ -2295,15 +2310,7 @@ cogl_texture_polygon (CoglHandle handle, /* Temporarily change the wrapping mode on all of the slices to use a transparent border */ - for (i = 0; i < tex->slice_gl_handles->len; i++) - { - GE( glBindTexture (tex->gl_target, - g_array_index (tex->slice_gl_handles, GLuint, i)) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, - GL_CLAMP_TO_BORDER) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, - GL_CLAMP_TO_BORDER) ); - } + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_BORDER); tex_num = 0; @@ -2350,13 +2357,4 @@ cogl_texture_polygon (CoglHandle handle, GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); } } - - /* Restore the wrapping mode */ - for (i = 0; i < tex->slice_gl_handles->len; i++) - { - GE( glBindTexture (tex->gl_target, - g_array_index (tex->slice_gl_handles, GLuint, i)) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, tex->wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, tex->wrap_mode) ); - } } From bb2272992b23abb387e8469607c2299a4104ffe4 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 3 Dec 2008 18:00:05 +0000 Subject: [PATCH 5/7] Bug 1303 - clutter_glx_texture_pixmap_using_extension doesn't check if fallbacks are being used * glx/clutter-glx-texture-pixmap.c: clutter_glx_texture_pixmap_using_extension now checks to see if priv->use_fallback is TRUE not just that the tfp extension is available. --- ChangeLog | 10 ++++++++++ clutter/glx/clutter-glx-texture-pixmap.c | 5 ++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 46109caf9..fa2c16e52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2008-12-03 Robert Bragg + + Bug 1303 - clutter_glx_texture_pixmap_using_extension doesn't check if + fallbacks are being used + + * glx/clutter-glx-texture-pixmap.c: + clutter_glx_texture_pixmap_using_extension now checks to see if + priv->use_fallback is TRUE not just that the tfp extension is + available. + 2008-12-01 Neil Roberts Bug 1305 - NPOT textures unaligned to a pixel sometimes have diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index a76ff30d9..169ee209e 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -524,8 +524,7 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) dpy = clutter_x11_get_default_display (); - if (priv->use_fallback == TRUE - || !clutter_glx_texture_pixmap_using_extension (texture)) + if (!clutter_glx_texture_pixmap_using_extension (texture)) goto cleanup; priv->use_fallback = FALSE; @@ -744,7 +743,7 @@ clutter_glx_texture_pixmap_using_extension (ClutterGLXTexturePixmap *texture) priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; - return (_have_tex_from_pixmap_ext); + return (_have_tex_from_pixmap_ext && !priv->use_fallback); /* Assume NPOT TFP's are supported even if regular NPOT isn't advertised * but tfp is. Seemingly some Intel drivers do this ? */ From 89e7552ca376b83df9fdfd9c1a70b3a0fc678397 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 4 Dec 2008 13:45:09 +0000 Subject: [PATCH 6/7] Bug 1172 - Disjoint paths and clip to path * clutter/cogl/cogl-path.h: * clutter/cogl/common/cogl-primitives.c: * clutter/cogl/common/cogl-primitives.h: * clutter/cogl/gl/cogl-primitives.c: * clutter/cogl/gles/cogl-primitives.c: Changed the semantics of cogl_path_move_to. Previously this always started a new path but now it instead starts a new disjoint sub path. The path isn't cleared until you call either cogl_path_stroke, cogl_path_fill or cogl_path_new. There are also cogl_path_stroke_preserve and cogl_path_fill_preserve functions. * clutter/cogl/gl/cogl-context.c: * clutter/cogl/gl/cogl-context.h: * clutter/cogl/gles/cogl-context.c: * clutter/cogl/gles/cogl-context.h: Convert the path nodes array to a GArray. * clutter/cogl/gl/cogl-texture.c: * clutter/cogl/gles/cogl-texture.c: Call cogl_clip_ensure * clutter/cogl/common/cogl-clip-stack.c: * clutter/cogl/common/cogl-clip-stack.h: Simplified the clip stack code quite a bit to make it more maintainable. Previously whenever you added a new clip it would go through a separate route to immediately intersect with the current clip and when you removed it again it would immediately rebuild the entire clip. Now when you add or remove a clip it doesn't do anything immediately but just sets a dirty flag instead. * clutter/cogl/gl/cogl.c: * clutter/cogl/gles/cogl.c: Taken away the code to intersect stencil clips when there is exactly one stencil bit. It won't work with path clips and I don't know of any platform that doesn't have eight or zero stencil bits. It needs at least three bits to intersect a path with an existing clip. cogl_features_init now just decides you don't have a stencil buffer at all if you have less than three bits. * clutter/cogl/cogl.h.in: New functions and documentation. * tests/interactive/test-clip.c: Replaced with a different test that lets you add and remove clips. The three different mouse buttons add clips in different shapes. This makes it easier to test multiple levels of clipping. * tests/interactive/test-cogl-primitives.c: Use cogl_path_stroke_preserve when using the same path again. * doc/reference/cogl/cogl-sections.txt: Document the new functions. --- ChangeLog | 55 +++ clutter/cogl/cogl-path.h | 88 ++-- clutter/cogl/cogl.h.in | 43 +- clutter/cogl/common/cogl-clip-stack.c | 356 +++++++++----- clutter/cogl/common/cogl-clip-stack.h | 15 +- clutter/cogl/common/cogl-primitives.c | 63 ++- clutter/cogl/common/cogl-primitives.h | 22 +- clutter/cogl/gl/cogl-context.c | 13 +- clutter/cogl/gl/cogl-context.h | 10 +- clutter/cogl/gl/cogl-primitives.c | 216 ++++++--- clutter/cogl/gl/cogl-texture.c | 4 + clutter/cogl/gl/cogl.c | 161 +------ clutter/cogl/gles/cogl-context.c | 14 +- clutter/cogl/gles/cogl-context.h | 10 +- clutter/cogl/gles/cogl-primitives.c | 563 ++++++++++++++--------- clutter/cogl/gles/cogl-texture.c | 4 + clutter/cogl/gles/cogl.c | 154 +------ doc/reference/cogl/cogl-sections.txt | 6 + tests/interactive/test-clip.c | 386 ++++++++++++---- tests/interactive/test-cogl-primitives.c | 6 +- 20 files changed, 1339 insertions(+), 850 deletions(-) diff --git a/ChangeLog b/ChangeLog index fa2c16e52..8366bfbd1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,58 @@ +2008-12-04 Neil Roberts + + Bug 1172 - Disjoint paths and clip to path + + * clutter/cogl/cogl-path.h: + * clutter/cogl/common/cogl-primitives.c: + * clutter/cogl/common/cogl-primitives.h: + * clutter/cogl/gl/cogl-primitives.c: + * clutter/cogl/gles/cogl-primitives.c: Changed the semantics of + cogl_path_move_to. Previously this always started a new path but + now it instead starts a new disjoint sub path. The path isn't + cleared until you call either cogl_path_stroke, cogl_path_fill or + cogl_path_new. There are also cogl_path_stroke_preserve and + cogl_path_fill_preserve functions. + + * clutter/cogl/gl/cogl-context.c: + * clutter/cogl/gl/cogl-context.h: + * clutter/cogl/gles/cogl-context.c: + * clutter/cogl/gles/cogl-context.h: Convert the path nodes array + to a GArray. + + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: Call cogl_clip_ensure + + * clutter/cogl/common/cogl-clip-stack.c: + * clutter/cogl/common/cogl-clip-stack.h: Simplified the clip + stack code quite a bit to make it more maintainable. Previously + whenever you added a new clip it would go through a separate route + to immediately intersect with the current clip and when you + removed it again it would immediately rebuild the entire clip. Now + when you add or remove a clip it doesn't do anything immediately + but just sets a dirty flag instead. + + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gles/cogl.c: Taken away the code to intersect + stencil clips when there is exactly one stencil bit. It won't work + with path clips and I don't know of any platform that doesn't have + eight or zero stencil bits. It needs at least three bits to + intersect a path with an existing clip. cogl_features_init now + just decides you don't have a stencil buffer at all if you have + less than three bits. + + * clutter/cogl/cogl.h.in: New functions and documentation. + + * tests/interactive/test-clip.c: Replaced with a different test + that lets you add and remove clips. The three different mouse + buttons add clips in different shapes. This makes it easier to + test multiple levels of clipping. + + * tests/interactive/test-cogl-primitives.c: Use + cogl_path_stroke_preserve when using the same path again. + + * doc/reference/cogl/cogl-sections.txt: Document the new + functions. + 2008-12-03 Robert Bragg Bug 1303 - clutter_glx_texture_pixmap_using_extension doesn't check if diff --git a/clutter/cogl/cogl-path.h b/clutter/cogl/cogl-path.h index 01867d144..20632da37 100644 --- a/clutter/cogl/cogl-path.h +++ b/clutter/cogl/cogl-path.h @@ -82,28 +82,60 @@ void cogl_rectanglex (CoglFixed x, /** * cogl_path_fill: * - * Fills the constructed shape using the current drawing color. + * Fills the constructed shape using the current drawing color. The + * current path is then cleared. To use the path again, call + * cogl_path_fill_preserve() instead. **/ -void cogl_path_fill (void); +void cogl_path_fill (void); + +/** + * cogl_path_fill_preserve: + * + * Fills the constructed shape using the current drawing color and + * preserves the path to be used again. + * + * Since: 1.0 + **/ +void cogl_path_fill_preserve (void); /** * cogl_path_stroke: * - * Strokes the constructed shape using the current drawing color - * and a width of 1 pixel (regardless of the current transformation - * matrix). + * Strokes the constructed shape using the current drawing color and a + * width of 1 pixel (regardless of the current transformation + * matrix). To current path is then cleared. To use the path again, + * call cogl_path_stroke_preserve() instead. **/ -void cogl_path_stroke (void); +void cogl_path_stroke (void); +/** + * cogl_path_stroke_preserve: + * + * Strokes the constructed shape using the current drawing color and + * preserves the path to be used again. + * + * Since: 1.0 + **/ +void cogl_path_stroke_preserve (void); + +/** + * cogl_path_new: + * + * Clears the current path and starts a new one. + * + * Since: 1.0 + */ +void cogl_path_new (void); + /** * cogl_path_move_to: * @x: X coordinate of the pen location to move to. * @y: Y coordinate of the pen location to move to. * - * Clears the previously constructed shape and begins a new path - * contour by moving the pen to the given coordinates. - **/ + * Moves the pen to the given location. If there is an existing path + * this will start a new disjoint subpath. + **/ void cogl_path_move_to (CoglFixed x, CoglFixed y); @@ -113,9 +145,9 @@ void cogl_path_move_to (CoglFixed x, * @x: X offset from the current pen location to move the pen to. * @y: Y offset from the current pen location to move the pen to. * - * Clears the previously constructed shape and begins a new path - * contour by moving the pen to the given coordinates relative - * to the current pen location. + * Moves the pen to the given offset relative to the current pen + * location. If there is an existing path this will start a new + * disjoint subpath. **/ void cogl_path_rel_move_to (CoglFixed x, CoglFixed y); @@ -222,8 +254,9 @@ void cogl_path_close (void); * @x2: X coordinate of the end line vertex * @y2: Y coordinate of the end line vertex * - * Clears the previously constructed shape and constructs a straight - * line shape start and ending at the given coordinates. + * Constructs a straight line shape starting and ending at the given + * coordinates. If there is an existing path this will start a new + * disjoint sub-path. **/ void cogl_path_line (CoglFixed x1, CoglFixed y1, @@ -236,10 +269,11 @@ void cogl_path_line (CoglFixed x1, * values that specify the vertex coordinates. * @num_points: The total number of vertices. * - * Clears the previously constructed shape and constructs a series of straight - * line segments, starting from the first given vertex coordinate. Each - * subsequent segment stars where the previous one ended and ends at the next - * given vertex coordinate. + * Constructs a series of straight line segments, starting from the + * first given vertex coordinate. If there is an existing path this + * will start a new disjoint sub-path. Each subsequent segment starts + * where the previous one ended and ends at the next given vertex + * coordinate. * * The coords array must contain 2 * num_points values. The first value * represents the X coordinate of the first vertex, the second value @@ -257,8 +291,8 @@ void cogl_path_polyline (CoglFixed *coords, * values that specify the vertex coordinates. * @num_points: The total number of vertices. * - * Clears the previously constructed shape and constructs a polygonal - * shape of the given number of vertices. + * Constructs a polygonal shape of the given number of vertices. If + * there is an existing path this will start a new disjoint sub-path. * * The coords array must contain 2 * num_points values. The first value * represents the X coordinate of the first vertex, the second value @@ -276,8 +310,8 @@ void cogl_path_polygon (CoglFixed *coords, * @width: Rectangle width. * @height: Rectangle height. * - * Clears the previously constructed shape and constructs a rectangular - * shape at the given coordinates. + * Constructs a rectangular shape at the given coordinates. If there + * is an existing path this will start a new disjoint sub-path. **/ void cogl_path_rectangle (CoglFixed x, CoglFixed y, @@ -291,8 +325,8 @@ void cogl_path_rectangle (CoglFixed x, * @radius_x: X radius of the ellipse * @radius_y: Y radius of the ellipse * - * Clears the previously constructed shape and constructs an ellipse - * shape. + * Constructs an ellipse shape. If there is an existing path this will + * start a new disjoint sub-path. **/ void cogl_path_ellipse (CoglFixed center_x, CoglFixed center_y, @@ -309,9 +343,9 @@ void cogl_path_ellipse (CoglFixed center_x, * @arc_step: Angle increment resolution for subdivision of * the corner arcs. * - * Clears the previously constructed shape and constructs a rectangular - * shape with rounded corners. - **/ + * Constructs a rectangular shape with rounded corners. If there is an + * existing path this will start a new disjoint sub-path. + **/ void cogl_path_round_rectangle (CoglFixed x, CoglFixed y, CoglFixed width, diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in index 224a1fac4..ea81c7b2c 100644 --- a/clutter/cogl/cogl.h.in +++ b/clutter/cogl/cogl.h.in @@ -332,20 +332,59 @@ void cogl_get_viewport (CoglFixed v[4]); * will be clipped so that only the portion inside the rectangle will * be displayed. The rectangle dimensions are transformed by the * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_unset(). */ void cogl_clip_set (CoglFixed x_offset, CoglFixed y_offset, CoglFixed width, CoglFixed height); +/** + * cogl_clip_set_from_path: + * + * Sets a new clipping area using the current path. The current path + * is then cleared. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * cogl_clip_unset(). + * + * Since: 1.0 + */ +void cogl_clip_set_from_path (void); + +/** + * cogl_clip_set_from_path_preserve: + * + * Sets a new clipping area using the current path. The current path + * is then cleared. The clipping area is intersected with the previous + * clipping area. To restore the previous clipping area, call + * cogl_clip_unset(). + * + * Since: 1.0 + */ +void cogl_clip_set_from_path_preserve (void); + /** * cogl_clip_unset: * - * Removes the current clipping rectangle so that all drawing - * operations extend to full size of the viewport again. + * Reverts the clipping region to the state before the last call to + * cogl_clip_set(). */ void cogl_clip_unset (void); +/** + * cogl_clip_ensure: + * + * Ensures that the current clipping region has been set in GL. This + * will automatically be called before any Cogl primitives but it + * maybe be neccessary to call if you are using raw GL calls with + * clipping. + * + * Since: 1.0 + */ +void cogl_clip_ensure (void); + /** * cogl_clip_stack_save: * diff --git a/clutter/cogl/common/cogl-clip-stack.c b/clutter/cogl/common/cogl-clip-stack.c index 66d844bed..c533b0b4c 100644 --- a/clutter/cogl/common/cogl-clip-stack.c +++ b/clutter/cogl/common/cogl-clip-stack.c @@ -27,8 +27,11 @@ #include "config.h" #endif +#include #include "cogl.h" #include "cogl-clip-stack.h" +#include "cogl-primitives.h" +#include "cogl-context.h" /* These are defined in the particular backend (float in GL vs fixed in GL ES) */ @@ -36,58 +39,64 @@ void _cogl_set_clip_planes (CoglFixed x, CoglFixed y, CoglFixed width, CoglFixed height); -void _cogl_init_stencil_buffer (void); void _cogl_add_stencil_clip (CoglFixed x, CoglFixed y, CoglFixed width, CoglFixed height, gboolean first); +void _cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min, + CoglFixedVec2 nodes_max, + guint path_size, + CoglPathNode *path, + gboolean merge); +void _cogl_enable_clip_planes (void); void _cogl_disable_clip_planes (void); void _cogl_disable_stencil_buffer (void); void _cogl_set_matrix (const CoglFixed *matrix); -typedef struct _CoglClipStackEntry CoglClipStackEntry; +typedef struct _CoglClipStack CoglClipStack; -struct _CoglClipStackEntry +typedef struct _CoglClipStackEntryRect CoglClipStackEntryRect; +typedef struct _CoglClipStackEntryPath CoglClipStackEntryPath; + +typedef enum + { + COGL_CLIP_STACK_RECT, + COGL_CLIP_STACK_PATH + } CoglClipStackEntryType; + +struct _CoglClipStack { - /* If this is set then this entry clears the clip stack. This is - used to clear the stack when drawing an FBO put to keep the - entries so they can be restored when the FBO drawing is - completed */ - gboolean clear; - - /* The rectangle for this clip */ - CoglFixed x_offset; - CoglFixed y_offset; - CoglFixed width; - CoglFixed height; - - /* The matrix that was current when the clip was set */ - CoglFixed matrix[16]; + GList *stack_top; }; -static GList *cogl_clip_stack_top = NULL; -static int cogl_clip_stack_depth = 0; - -static void -_cogl_clip_stack_add (const CoglClipStackEntry *entry, int depth) +struct _CoglClipStackEntryRect { - int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + CoglClipStackEntryType type; - /* If this is the first entry and we support clip planes then use - that instead */ - if (depth == 1 && has_clip_planes) - _cogl_set_clip_planes (entry->x_offset, - entry->y_offset, - entry->width, - entry->height); - else - _cogl_add_stencil_clip (entry->x_offset, - entry->y_offset, - entry->width, - entry->height, - depth == (has_clip_planes ? 2 : 1)); -} + /* The rectangle for this clip */ + CoglFixed x_offset; + CoglFixed y_offset; + CoglFixed width; + CoglFixed height; + + /* The matrix that was current when the clip was set */ + CoglFixed matrix[16]; +}; + +struct _CoglClipStackEntryPath +{ + CoglClipStackEntryType type; + + /* The matrix that was current when the clip was set */ + CoglFixed matrix[16]; + + CoglFixedVec2 path_nodes_min; + CoglFixedVec2 path_nodes_max; + + guint path_size; + CoglPathNode path[1]; +}; void cogl_clip_set (CoglFixed x_offset, @@ -95,10 +104,17 @@ cogl_clip_set (CoglFixed x_offset, CoglFixed width, CoglFixed height) { - CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry); + CoglClipStackEntryRect *entry; + CoglClipStack *stack; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + stack = (CoglClipStack *) ctx->clip.stacks->data; + + entry = g_slice_new (CoglClipStackEntryRect); /* Make a new entry */ - entry->clear = FALSE; + entry->type = COGL_CLIP_STACK_RECT; entry->x_offset = x_offset; entry->y_offset = y_offset; entry->width = width; @@ -106,127 +122,231 @@ cogl_clip_set (CoglFixed x_offset, cogl_get_modelview_matrix (entry->matrix); - /* Add the entry to the current clip */ - _cogl_clip_stack_add (entry, ++cogl_clip_stack_depth); + /* Store it in the stack */ + stack->stack_top = g_list_prepend (stack->stack_top, entry); + + ctx->clip.stack_dirty = TRUE; +} + +void +cogl_clip_set_from_path_preserve (void) +{ + CoglClipStackEntryPath *entry; + CoglClipStack *stack; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + stack = (CoglClipStack *) ctx->clip.stacks->data; + + entry = g_malloc (sizeof (CoglClipStackEntryPath) + + sizeof (CoglPathNode) * (ctx->path_nodes->len - 1)); + + entry->type = COGL_CLIP_STACK_PATH; + entry->path_nodes_min = ctx->path_nodes_min; + entry->path_nodes_max = ctx->path_nodes_max; + entry->path_size = ctx->path_nodes->len; + memcpy (entry->path, ctx->path_nodes->data, + sizeof (CoglPathNode) * ctx->path_nodes->len); + + cogl_get_modelview_matrix (entry->matrix); /* Store it in the stack */ - cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry); + stack->stack_top = g_list_prepend (stack->stack_top, entry); + + ctx->clip.stack_dirty = TRUE; +} + +void +cogl_clip_set_from_path (void) +{ + cogl_clip_set_from_path_preserve (); + + cogl_path_new (); } void cogl_clip_unset (void) { - g_return_if_fail (cogl_clip_stack_top != NULL); + gpointer entry; + CoglClipStack *stack; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + stack = (CoglClipStack *) ctx->clip.stacks->data; + + g_return_if_fail (stack->stack_top != NULL); + + entry = stack->stack_top->data; /* Remove the top entry from the stack */ - g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data); - cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top, - cogl_clip_stack_top); - cogl_clip_stack_depth--; + if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_RECT) + g_slice_free (CoglClipStackEntryRect, entry); + else + g_free (entry); - /* Rebuild the clip */ - _cogl_clip_stack_rebuild (FALSE); + stack->stack_top = g_list_delete_link (stack->stack_top, + stack->stack_top); + + ctx->clip.stack_dirty = TRUE; } void -_cogl_clip_stack_rebuild (gboolean just_stencil) +_cogl_clip_stack_rebuild (void) { int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + gboolean using_clip_planes = FALSE; + gboolean using_stencil_buffer = FALSE; GList *node; - int depth = 0; + CoglClipStack *stack; - /* Disable clip planes if the stack is empty */ - if (has_clip_planes && cogl_clip_stack_depth < 1) - _cogl_disable_clip_planes (); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Disable the stencil buffer if there isn't enough entries */ - if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1)) - _cogl_disable_stencil_buffer (); + stack = (CoglClipStack *) ctx->clip.stacks->data; + + ctx->clip.stack_dirty = FALSE; + ctx->clip.stencil_used = FALSE; + + _cogl_disable_clip_planes (); + _cogl_disable_stencil_buffer (); + + /* If the stack is empty then there's nothing else to do */ + if (stack->stack_top == NULL) + return; /* Find the bottom of the stack */ - for (node = cogl_clip_stack_top; depth < cogl_clip_stack_depth - 1; - node = node->next) - depth++; + for (node = stack->stack_top; node->next; node = node->next); /* Re-add every entry from the bottom of the stack up */ - depth = 1; - for (; depth <= cogl_clip_stack_depth; node = node->prev, depth++) - if (!just_stencil || !has_clip_planes || depth > 1) - { - const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data; - cogl_push_matrix (); - _cogl_set_matrix (entry->matrix); - _cogl_clip_stack_add (entry, depth); - cogl_pop_matrix (); - } + for (; node; node = node->prev) + { + gpointer entry = node->data; + + if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_PATH) + { + CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry; + + cogl_push_matrix (); + _cogl_set_matrix (path->matrix); + + _cogl_add_path_to_stencil_buffer (path->path_nodes_min, + path->path_nodes_max, + path->path_size, + path->path, + using_stencil_buffer); + + cogl_pop_matrix (); + + using_stencil_buffer = TRUE; + + /* We can't use clip planes any more */ + has_clip_planes = FALSE; + } + else + { + CoglClipStackEntryRect *rect = (CoglClipStackEntryRect *) entry; + + cogl_push_matrix (); + _cogl_set_matrix (rect->matrix); + + /* If this is the first entry and we support clip planes then use + that instead */ + if (has_clip_planes) + { + _cogl_set_clip_planes (rect->x_offset, + rect->y_offset, + rect->width, + rect->height); + using_clip_planes = TRUE; + /* We can't use clip planes a second time */ + has_clip_planes = FALSE; + } + else + { + _cogl_add_stencil_clip (rect->x_offset, + rect->y_offset, + rect->width, + rect->height, + !using_stencil_buffer); + using_stencil_buffer = TRUE; + } + + cogl_pop_matrix (); + } + } + + /* Enabling clip planes is delayed to now so that they won't affect + setting up the stencil buffer */ + if (using_clip_planes) + _cogl_enable_clip_planes (); + + ctx->clip.stencil_used = using_stencil_buffer; } void -_cogl_clip_stack_merge (void) +cogl_clip_ensure (void) { - GList *node = cogl_clip_stack_top; - int i; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Merge the current clip stack on top of whatever is in the stencil - buffer */ - if (cogl_clip_stack_depth) - { - for (i = 0; i < cogl_clip_stack_depth - 1; i++) - node = node->next; - - /* Skip the first entry if we have clipping planes */ - if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) - node = node->prev; - - while (node) - { - const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data; - cogl_push_matrix (); - _cogl_set_matrix (entry->matrix); - _cogl_clip_stack_add (entry, 3); - cogl_pop_matrix (); - - node = node->prev; - } - } + if (ctx->clip.stack_dirty) + _cogl_clip_stack_rebuild (); } void cogl_clip_stack_save (void) { - CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry); + CoglClipStack *stack; - /* Push an entry into the stack to mark that it should be cleared */ - entry->clear = TRUE; - cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Reset the depth to zero */ - cogl_clip_stack_depth = 0; + stack = g_slice_new (CoglClipStack); + stack->stack_top = NULL; - /* Rebuilding the stack will now disabling all clipping */ - _cogl_clip_stack_rebuild (FALSE); + ctx->clip.stacks = g_slist_prepend (ctx->clip.stacks, stack); + + ctx->clip.stack_dirty = TRUE; } void cogl_clip_stack_restore (void) { - GList *node; + CoglClipStack *stack; - /* The top of the stack should be a clear marker */ - g_assert (cogl_clip_stack_top); - g_assert (((CoglClipStackEntry *) cogl_clip_stack_top->data)->clear); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Remove the top entry */ - g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data); - cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top, - cogl_clip_stack_top); + g_return_if_fail (ctx->clip.stacks != NULL); - /* Recalculate the depth of the stack */ - cogl_clip_stack_depth = 0; - for (node = cogl_clip_stack_top; - node && !((CoglClipStackEntry *) node->data)->clear; - node = node->next) - cogl_clip_stack_depth++; + stack = (CoglClipStack *) ctx->clip.stacks->data; - _cogl_clip_stack_rebuild (FALSE); + /* Empty the current stack */ + while (stack->stack_top) + cogl_clip_unset (); + + /* Revert to an old stack */ + g_slice_free (CoglClipStack, stack); + ctx->clip.stacks = g_slist_delete_link (ctx->clip.stacks, + ctx->clip.stacks); + + ctx->clip.stack_dirty = TRUE; +} + +void +_cogl_clip_stack_state_init (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + ctx->clip.stacks = NULL; + ctx->clip.stack_dirty = TRUE; + + /* Add an intial stack */ + cogl_clip_stack_save (); +} + +void +_cogl_clip_stack_state_destroy (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Destroy all of the stacks */ + while (ctx->clip.stacks) + cogl_clip_stack_restore (); } diff --git a/clutter/cogl/common/cogl-clip-stack.h b/clutter/cogl/common/cogl-clip-stack.h index 04be889ae..cbabc7381 100644 --- a/clutter/cogl/common/cogl-clip-stack.h +++ b/clutter/cogl/common/cogl-clip-stack.h @@ -26,7 +26,20 @@ #ifndef __COGL_CLIP_STACK_H #define __COGL_CLIP_STACK_H -void _cogl_clip_stack_rebuild (gboolean just_stencil); +typedef struct _CoglClipStackState CoglClipStackState; + +struct _CoglClipStackState +{ + /* Stack of stacks */ + GSList *stacks; + + gboolean stack_dirty; + gboolean stencil_used; +}; + +void _cogl_clip_stack_state_init (void); +void _cogl_clip_stack_state_destroy (void); +void _cogl_clip_stack_rebuild (void); void _cogl_clip_stack_merge (void); #endif /* __COGL_CLIP_STACK_H */ diff --git a/clutter/cogl/common/cogl-primitives.c b/clutter/cogl/common/cogl-primitives.c index 38499e6fc..a063331f2 100644 --- a/clutter/cogl/common/cogl-primitives.c +++ b/clutter/cogl/common/cogl-primitives.c @@ -37,8 +37,8 @@ #define _COGL_MAX_BEZ_RECURSE_DEPTH 16 /* these are defined in the particular backend(float in gl vs fixed in gles)*/ -void _cogl_path_clear_nodes (); -void _cogl_path_add_node (CoglFixed x, +void _cogl_path_add_node (gboolean new_sub_path, + CoglFixed x, CoglFixed y); void _cogl_path_fill_nodes (); void _cogl_path_stroke_nodes (); @@ -56,6 +56,8 @@ cogl_rectangle (gint x, guint width, guint height) { + cogl_clip_ensure (); + _cogl_rectangle (x, y, width, height); } @@ -65,27 +67,49 @@ cogl_rectanglex (CoglFixed x, CoglFixed width, CoglFixed height) { + cogl_clip_ensure (); + _cogl_rectanglex (x, y, width, height); } void cogl_path_fill (void) +{ + cogl_path_fill_preserve (); + + cogl_path_new (); +} + +void +cogl_path_fill_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_clip_ensure (); - if (ctx->path_nodes_size == 0) - return; - - _cogl_path_fill_nodes(); + if (ctx->path_nodes->len == 0) + return; + + _cogl_path_fill_nodes (); } void cogl_path_stroke (void) +{ + cogl_path_stroke_preserve (); + + cogl_path_new (); +} + +void +cogl_path_stroke_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (ctx->path_nodes_size == 0) + cogl_clip_ensure (); + + if (ctx->path_nodes->len == 0) return; _cogl_path_stroke_nodes(); @@ -99,11 +123,7 @@ cogl_path_move_to (CoglFixed x, /* FIXME: handle multiple contours maybe? */ - /* at the moment, a move_to is an implicit instruction to create - * a new path. - */ - _cogl_path_clear_nodes (); - _cogl_path_add_node (x, y); + _cogl_path_add_node (TRUE, x, y); ctx->path_start.x = x; ctx->path_start.y = y; @@ -127,7 +147,7 @@ cogl_path_line_to (CoglFixed x, { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_path_add_node (x, y); + _cogl_path_add_node (FALSE, x, y); ctx->path_pen.x = x; ctx->path_pen.y = y; @@ -148,10 +168,17 @@ cogl_path_close (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); + _cogl_path_add_node (FALSE, ctx->path_start.x, ctx->path_start.y); ctx->path_pen = ctx->path_start; } +void +cogl_path_new (void) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_array_set_size (ctx->path_nodes, 0); +} void cogl_path_line (CoglFixed x1, @@ -427,7 +454,7 @@ _cogl_path_bezier3_sub (CoglBezCubic *cubic) if (cindex == 0) return; - _cogl_path_add_node (c->p4.x, c->p4.y); + _cogl_path_add_node (FALSE, c->p4.x, c->p4.y); --cindex; @@ -492,7 +519,7 @@ cogl_path_curve_to (CoglFixed x1, _cogl_path_bezier3_sub (&cubic); /* Add last point */ - _cogl_path_add_node (cubic.p4.x, cubic.p4.y); + _cogl_path_add_node (FALSE, cubic.p4.x, cubic.p4.y); ctx->path_pen = cubic.p4; } @@ -559,7 +586,7 @@ _cogl_path_bezier2_sub (CoglBezQuad *quad) { /* Add subdivision point (skip last) */ if (qindex == 0) return; - _cogl_path_add_node (q->p3.x, q->p3.y); + _cogl_path_add_node (FALSE, q->p3.x, q->p3.y); --qindex; continue; } @@ -607,7 +634,7 @@ cogl_path_curve2_to (CoglFixed x1, _cogl_path_bezier2_sub (&quad); /* Add last point */ - _cogl_path_add_node (quad.p3.x, quad.p3.y); + _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y); ctx->path_pen = quad.p3; } diff --git a/clutter/cogl/common/cogl-primitives.h b/clutter/cogl/common/cogl-primitives.h index f2d3ed173..01905d611 100644 --- a/clutter/cogl/common/cogl-primitives.h +++ b/clutter/cogl/common/cogl-primitives.h @@ -29,6 +29,7 @@ typedef struct _CoglFixedVec2 CoglFixedVec2; typedef struct _CoglBezQuad CoglBezQuad; typedef struct _CoglBezCubic CoglBezCubic; +typedef struct _CoglPathNode CoglPathNode; struct _CoglFixedVec2 { @@ -37,13 +38,32 @@ struct _CoglFixedVec2 }; #ifdef CLUTTER_COGL_HAS_GL + typedef struct _CoglFloatVec2 CoglFloatVec2; + struct _CoglFloatVec2 { GLfloat x; GLfloat y; }; -#endif + +struct _CoglPathNode +{ + GLfloat x; + GLfloat y; + guint path_size; +}; + +#else /* CLUTTER_COGL_HAS_GL */ + +struct _CoglPathNode +{ + GLfixed x; + GLfixed y; + guint path_size; +}; + +#endif /* CLUTTER_COGL_HAS_GL */ struct _CoglBezQuad { diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c index a9240a102..d75086fbc 100644 --- a/clutter/cogl/gl/cogl-context.c +++ b/clutter/cogl/gl/cogl-context.c @@ -52,9 +52,8 @@ cogl_create_context () _context->enable_flags = 0; _context->color_alpha = 255; - _context->path_nodes = NULL; - _context->path_nodes_cap = 0; - _context->path_nodes_size = 0; + _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); + _context->last_path = 0; _context->texture_handles = NULL; _context->texture_vertices_size = 0; @@ -122,6 +121,9 @@ cogl_create_context () GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); cogl_enable (0); + + /* Initialise the clip stack */ + _cogl_clip_stack_state_init (); return TRUE; } @@ -132,6 +134,11 @@ cogl_destroy_context () if (_context == NULL) return; + _cogl_clip_stack_state_destroy (); + + if (_context->path_nodes) + g_array_free (_context->path_nodes, TRUE); + if (_context->texture_handles) g_array_free (_context->texture_handles, TRUE); if (_context->fbo_handles) diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 5ab583862..df62a53e8 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -27,6 +27,7 @@ #define __COGL_CONTEXT_H #include "cogl-primitives.h" +#include "cogl-clip-stack.h" typedef struct { @@ -40,7 +41,6 @@ typedef struct /* Features cache */ CoglFeatureFlags feature_flags; gboolean features_cached; - GLint num_stencil_bits; /* Enable cache */ gulong enable_flags; @@ -53,9 +53,8 @@ typedef struct /* Primitives */ CoglFixedVec2 path_start; CoglFixedVec2 path_pen; - CoglFloatVec2 *path_nodes; - guint path_nodes_cap; - guint path_nodes_size; + GArray *path_nodes; + guint last_path; CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_max; @@ -76,6 +75,9 @@ typedef struct /* Programs */ GArray *program_handles; + + /* Clip stack */ + CoglClipStackState clip; /* Mesh */ GArray *mesh_handles; diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c index 950a46106..ca46d85ac 100644 --- a/clutter/cogl/gl/cogl-primitives.c +++ b/clutter/cogl/gl/cogl-primitives.c @@ -70,43 +70,26 @@ _cogl_rectanglex (CoglFixed x, } void -_cogl_path_clear_nodes () -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->path_nodes) - g_free(ctx->path_nodes); - - ctx->path_nodes = (CoglFloatVec2*) g_malloc (2 * sizeof(CoglFloatVec2)); - ctx->path_nodes_size = 0; - ctx->path_nodes_cap = 2; -} - -void -_cogl_path_add_node (CoglFixed x, +_cogl_path_add_node (gboolean new_sub_path, + CoglFixed x, CoglFixed y) { - CoglFloatVec2 *new_nodes = NULL; + CoglPathNode new_node; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->path_nodes_size == ctx->path_nodes_cap) - { - new_nodes = g_realloc (ctx->path_nodes, - 2 * ctx->path_nodes_cap - * sizeof (CoglFloatVec2)); - - if (new_nodes == NULL) return; - ctx->path_nodes = new_nodes; - ctx->path_nodes_cap *= 2; - } - - ctx->path_nodes [ctx->path_nodes_size] .x = COGL_FIXED_TO_FLOAT (x); - ctx->path_nodes [ctx->path_nodes_size] .y = COGL_FIXED_TO_FLOAT (y); - ctx->path_nodes_size++; - - if (ctx->path_nodes_size == 1) + new_node.x = COGL_FIXED_TO_FLOAT (x); + new_node.y = COGL_FIXED_TO_FLOAT (y); + new_node.path_size = 0; + + if (new_sub_path || ctx->path_nodes->len == 0) + ctx->last_path = ctx->path_nodes->len; + + g_array_append_val (ctx->path_nodes, new_node); + + g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++; + + if (ctx->path_nodes->len == 1) { ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; @@ -123,55 +106,158 @@ _cogl_path_add_node (CoglFixed x, void _cogl_path_stroke_nodes () { + guint path_start = 0; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); cogl_enable (COGL_ENABLE_VERTEX_ARRAY | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); + + while (path_start < ctx->path_nodes->len) + { + CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, + path_start); + + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( glDrawArrays (GL_LINE_STRIP, 0, path->path_size) ); + + path_start += path->path_size; + } +} + +static void +_cogl_path_get_bounds (CoglFixedVec2 nodes_min, + CoglFixedVec2 nodes_max, + gint *bounds_x, + gint *bounds_y, + guint *bounds_w, + guint *bounds_h) +{ + *bounds_x = COGL_FIXED_FLOOR (nodes_min.x); + *bounds_y = COGL_FIXED_FLOOR (nodes_min.y); + *bounds_w = COGL_FIXED_CEIL (nodes_max.x + - COGL_FIXED_FROM_INT (*bounds_x)); + *bounds_h = COGL_FIXED_CEIL (nodes_max.y + - COGL_FIXED_FROM_INT (*bounds_y)); +} + +void +_cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min, + CoglFixedVec2 nodes_max, + guint path_size, + CoglPathNode *path, + gboolean merge) +{ + guint path_start = 0; + guint sub_path_num = 0; + gint bounds_x; + gint bounds_y; + guint bounds_w; + guint bounds_h; + + _cogl_path_get_bounds (nodes_min, nodes_max, + &bounds_x, &bounds_y, &bounds_w, &bounds_h); + + if (merge) + { + GE( glStencilMask (2) ); + GE( glStencilFunc (GL_LEQUAL, 0x2, 0x6) ); + } + else + { + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glStencilMask (1) ); + GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); + } + + GE( glEnable (GL_STENCIL_TEST) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + + GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); + GE( glDepthMask (FALSE) ); - GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); - GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); + while (path_start < path_size) + { + cogl_enable (COGL_ENABLE_VERTEX_ARRAY); + + GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size) ); + + if (sub_path_num > 0) + { + /* Union the two stencil buffers bits into the least + significant bit */ + GE( glStencilMask (merge ? 6 : 3) ); + GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); + cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + } + + GE( glStencilMask (merge ? 4 : 2) ); + + path_start += path->path_size; + path += path->path_size; + sub_path_num++; + } + + if (merge) + { + /* Now we have the new stencil buffer in bit 1 and the old + stencil buffer in bit 0 so we need to intersect them */ + GE( glStencilMask (3) ); + GE( glStencilFunc (GL_NEVER, 0x2, 0x3) ); + GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); + /* Decrement all of the bits twice so that only pixels where the + value is 3 will remain */ + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + GE( glRecti (-1, 1, 1, -1) ); + GE( glRecti (-1, 1, 1, -1) ); + GE( glPopMatrix () ); + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPopMatrix () ); + } + + GE( glStencilMask (~(GLuint) 0) ); + GE( glDepthMask (TRUE) ); + GE( glColorMask (TRUE, TRUE, TRUE, TRUE) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); } void _cogl_path_fill_nodes () { - guint bounds_x; - guint bounds_y; + gint bounds_x; + gint bounds_y; guint bounds_w; guint bounds_h; - + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glEnable (GL_STENCIL_TEST) ); - GE( glStencilFunc (GL_NEVER, 0x0, 0x1) ); - GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min, + ctx->path_nodes_max, + ctx->path_nodes->len, + &g_array_index (ctx->path_nodes, + CoglPathNode, 0), + ctx->clip.stencil_used); - GE( glStencilMask (1) ); - - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - - GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); - GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); - - GE( glStencilMask (~(GLuint) 0) ); - - /* Merge the stencil buffer with any clipping rectangles */ - _cogl_clip_stack_merge (); - - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max, + &bounds_x, &bounds_y, &bounds_w, &bounds_h); - bounds_x = COGL_FIXED_FLOOR (ctx->path_nodes_min.x); - bounds_y = COGL_FIXED_FLOOR (ctx->path_nodes_min.y); - bounds_w = COGL_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); - bounds_h = COGL_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); - cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); - - /* Rebuild the stencil clip */ - _cogl_clip_stack_rebuild (TRUE); + + /* The stencil buffer now contains garbage so the clip area needs to + be rebuilt */ + ctx->clip.stack_dirty = TRUE; } diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index a1c5e7d75..b1b654c58 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -2197,6 +2197,8 @@ cogl_texture_rectangle (CoglHandle handle, if (!cogl_is_texture (handle)) return; + cogl_clip_ensure (); + tex = _cogl_texture_pointer_from_handle (handle); /* Make sure we got stuff to draw */ @@ -2242,6 +2244,8 @@ cogl_texture_polygon (CoglHandle handle, if (!cogl_is_texture (handle)) return; + cogl_clip_ensure (); + tex = _cogl_texture_pointer_from_handle (handle); /* The polygon will have artifacts where the slices join if the wrap diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index 1ce1a3c82..9bd14d113 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -457,8 +457,6 @@ set_clip_plane (GLint plane_num, GE( glClipPlane (plane_num, plane) ); GE( glPopMatrix () ); - - GE( glEnable (plane_num) ); } void @@ -513,15 +511,6 @@ _cogl_set_clip_planes (CoglFixed x_offset, set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); } -static int -compare_y_coordinate (const void *a, const void *b) -{ - GLfloat ay = ((const GLfloat *) a)[1]; - GLfloat by = ((const GLfloat *) b)[1]; - - return ay < by ? -1 : ay > by ? 1 : 0; -} - void _cogl_add_stencil_clip (CoglFixed x_offset, CoglFixed y_offset, @@ -529,19 +518,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset, CoglFixed height, gboolean first) { - gboolean has_clip_planes - = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (has_clip_planes) - { - GE( glDisable (GL_CLIP_PLANE3) ); - GE( glDisable (GL_CLIP_PLANE2) ); - GE( glDisable (GL_CLIP_PLANE1) ); - GE( glDisable (GL_CLIP_PLANE0) ); - } - if (first) { GE( glEnable (GL_STENCIL_TEST) ); @@ -558,7 +536,7 @@ _cogl_add_stencil_clip (CoglFixed x_offset, COGL_FIXED_TO_FLOAT (x_offset + width), COGL_FIXED_TO_FLOAT (y_offset + height)) ); } - else if (ctx->num_stencil_bits > 1) + else { /* Add one to every pixel of the stencil buffer in the rectangle */ @@ -583,127 +561,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset, GE( glMatrixMode (GL_MODELVIEW) ); GE( glPopMatrix () ); } - else - { - /* Slower fallback if there is exactly one stencil bit. This - tries to draw enough triangles to tessalate around the - rectangle so that it can subtract from the stencil buffer for - every pixel in the screen except those in the rectangle */ - GLfloat modelview[16], projection[16]; - GLfloat temp_point[4]; - GLfloat left_edge, right_edge, bottom_edge, top_edge; - int i; - GLfloat points[16] = - { - COGL_FIXED_TO_FLOAT (x_offset), - COGL_FIXED_TO_FLOAT (y_offset), - 0, 1, - COGL_FIXED_TO_FLOAT (x_offset + width), - COGL_FIXED_TO_FLOAT (y_offset), - 0, 1, - COGL_FIXED_TO_FLOAT (x_offset), - COGL_FIXED_TO_FLOAT (y_offset + height), - 0, 1, - COGL_FIXED_TO_FLOAT (x_offset + width), - COGL_FIXED_TO_FLOAT (y_offset + height), - 0, 1 - }; - - GE( glGetFloatv (GL_MODELVIEW_MATRIX, modelview) ); - GE( glGetFloatv (GL_PROJECTION_MATRIX, projection) ); - - /* Project all of the vertices into screen coordinates */ - for (i = 0; i < 4; i++) - project_vertex (modelview, projection, points + i * 4); - - /* Sort the points by y coordinate */ - qsort (points, 4, sizeof (GLfloat) * 4, compare_y_coordinate); - - /* Put the bottom two pairs and the top two pairs in - left-right order */ - if (points[0] > points[4]) - { - memcpy (temp_point, points, sizeof (GLfloat) * 4); - memcpy (points, points + 4, sizeof (GLfloat) * 4); - memcpy (points + 4, temp_point, sizeof (GLfloat) * 4); - } - if (points[8] > points[12]) - { - memcpy (temp_point, points + 8, sizeof (GLfloat) * 4); - memcpy (points + 8, points + 12, sizeof (GLfloat) * 4); - memcpy (points + 12, temp_point, sizeof (GLfloat) * 4); - } - - /* If the clip rect goes outside of the screen then use the - extents of the rect instead */ - left_edge = MIN (-1.0f, MIN (points[0], points[8])); - right_edge = MAX ( 1.0f, MAX (points[4], points[12])); - bottom_edge = MIN (-1.0f, MIN (points[1], points[5])); - top_edge = MAX ( 1.0f, MAX (points[9], points[13])); - - /* Using the identity matrix for the projection and - modelview matrix, draw the triangles around the inner - rectangle */ - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); - GE( glPushMatrix () ); - GE( glLoadIdentity () ); - GE( glMatrixMode (GL_PROJECTION) ); - GE( glPushMatrix () ); - GE( glLoadIdentity () ); - - /* Clear the left side */ - glBegin (GL_TRIANGLE_STRIP); - glVertex2f (left_edge, bottom_edge); - glVertex2fv (points); - glVertex2f (left_edge, points[1]); - glVertex2fv (points + 8); - glVertex2f (left_edge, points[9]); - glVertex2f (left_edge, top_edge); - glEnd (); - - /* Clear the right side */ - glBegin (GL_TRIANGLE_STRIP); - glVertex2f (right_edge, top_edge); - glVertex2fv (points + 12); - glVertex2f (right_edge, points[13]); - glVertex2fv (points + 4); - glVertex2f (right_edge, points[5]); - glVertex2f (right_edge, bottom_edge); - glEnd (); - - /* Clear the top side */ - glBegin (GL_TRIANGLE_STRIP); - glVertex2f (left_edge, top_edge); - glVertex2fv (points + 8); - glVertex2f (points[8], top_edge); - glVertex2fv (points + 12); - glVertex2f (points[12], top_edge); - glVertex2f (right_edge, top_edge); - glEnd (); - - /* Clear the bottom side */ - glBegin (GL_TRIANGLE_STRIP); - glVertex2f (left_edge, bottom_edge); - glVertex2fv (points); - glVertex2f (points[0], bottom_edge); - glVertex2fv (points + 4); - glVertex2f (points[4], bottom_edge); - glVertex2f (right_edge, bottom_edge); - glEnd (); - - GE( glPopMatrix () ); - GE( glMatrixMode (GL_MODELVIEW) ); - GE( glPopMatrix () ); - } - - if (has_clip_planes) - { - GE( glEnable (GL_CLIP_PLANE0) ); - GE( glEnable (GL_CLIP_PLANE1) ); - GE( glEnable (GL_CLIP_PLANE2) ); - GE( glEnable (GL_CLIP_PLANE3) ); - } /* Restore the stencil mode */ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); @@ -729,6 +586,15 @@ _cogl_disable_stencil_buffer (void) GE( glDisable (GL_STENCIL_TEST) ); } +void +_cogl_enable_clip_planes (void) +{ + GE( glEnable (GL_CLIP_PLANE0) ); + GE( glEnable (GL_CLIP_PLANE1) ); + GE( glEnable (GL_CLIP_PLANE2) ); + GE( glEnable (GL_CLIP_PLANE3) ); +} + void _cogl_disable_clip_planes (void) { @@ -945,6 +811,7 @@ _cogl_features_init () CoglFeatureFlags flags = 0; const gchar *gl_extensions; GLint max_clip_planes = 0; + GLint num_stencil_bits = 0; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1214,9 +1081,9 @@ _cogl_features_init () flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; } - ctx->num_stencil_bits = 0; - GE( glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) ); - if (ctx->num_stencil_bits > 0) + GE( glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) ); + /* We need at least three stencil bits to combine clips */ + if (num_stencil_bits > 2) flags |= COGL_FEATURE_STENCIL_BUFFER; GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c index f70160659..c7c768dec 100644 --- a/clutter/cogl/gles/cogl-context.c +++ b/clutter/cogl/gles/cogl-context.c @@ -55,10 +55,9 @@ cogl_create_context () _context->enable_flags = 0; _context->color_alpha = 255; - _context->path_nodes = NULL; - _context->path_nodes_cap = 0; - _context->path_nodes_size = 0; - + _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); + _context->last_path = 0; + _context->texture_handles = NULL; _context->texture_vertices_size = 0; _context->texture_vertices = NULL; @@ -83,6 +82,8 @@ cogl_create_context () GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); cogl_enable (0); + + _cogl_clip_stack_state_init (); return TRUE; } @@ -93,6 +94,11 @@ cogl_destroy_context () if (_context == NULL) return; + _cogl_clip_stack_state_destroy (); + + if (_context->path_nodes) + g_array_free (_context->path_nodes, TRUE); + #ifdef HAVE_COGL_GLES2 cogl_gles2_wrapper_deinit (&_context->gles2); #endif diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index 9504fa6f4..b62903efc 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -27,6 +27,7 @@ #define __COGL_CONTEXT_H #include "cogl-primitives.h" +#include "cogl-clip-stack.h" #include "cogl-gles2-wrapper.h" @@ -42,7 +43,6 @@ typedef struct /* Features cache */ CoglFeatureFlags feature_flags; gboolean features_cached; - GLint num_stencil_bits; /* Enable cache */ gulong enable_flags; @@ -55,9 +55,8 @@ typedef struct /* Primitives */ CoglFixedVec2 path_start; CoglFixedVec2 path_pen; - CoglFixedVec2 *path_nodes; - guint path_nodes_cap; - guint path_nodes_size; + GArray *path_nodes; + guint last_path; CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_max; @@ -80,6 +79,9 @@ typedef struct /* Mesh */ GArray *mesh_handles; + /* Clip stack */ + CoglClipStackState clip; + #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; diff --git a/clutter/cogl/gles/cogl-primitives.c b/clutter/cogl/gles/cogl-primitives.c index 8be70784d..f6248ea9e 100644 --- a/clutter/cogl/gles/cogl-primitives.c +++ b/clutter/cogl/gles/cogl-primitives.c @@ -87,45 +87,27 @@ _cogl_rectanglex (CoglFixed x, } - void -_cogl_path_clear_nodes () -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->path_nodes) - g_free(ctx->path_nodes); - - ctx->path_nodes = (CoglFixedVec2*) g_malloc (2 * sizeof(CoglFixedVec2)); - ctx->path_nodes_size = 0; - ctx->path_nodes_cap = 2; -} - -void -_cogl_path_add_node (CoglFixed x, +_cogl_path_add_node (gboolean new_sub_path, + CoglFixed x, CoglFixed y) { - CoglFixedVec2 *new_nodes = NULL; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->path_nodes_size == ctx->path_nodes_cap) - { - new_nodes = g_realloc (ctx->path_nodes, - 2 * ctx->path_nodes_cap - * sizeof (CoglFixedVec2)); - - if (new_nodes == NULL) return; + CoglPathNode new_node; - ctx->path_nodes = new_nodes; - ctx->path_nodes_cap *= 2; - } - - ctx->path_nodes [ctx->path_nodes_size].x = x; - ctx->path_nodes [ctx->path_nodes_size].y = y; - ctx->path_nodes_size++; - - if (ctx->path_nodes_size == 1) + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + new_node.x = x; + new_node.y = y; + new_node.path_size = 0; + + if (new_sub_path || ctx->path_nodes->len == 0) + ctx->last_path = ctx->path_nodes->len; + + g_array_append_val (ctx->path_nodes, new_node); + + g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++; + + if (ctx->path_nodes->len == 1) { ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; @@ -142,14 +124,42 @@ _cogl_path_add_node (CoglFixed x, void _cogl_path_stroke_nodes () { + guint path_start = 0; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - + cogl_enable (COGL_ENABLE_VERTEX_ARRAY | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - - GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); - GE( cogl_wrap_glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); + + while (path_start < ctx->path_nodes->len) + { + CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, + path_start); + + GE( cogl_wrap_glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( cogl_wrap_glDrawArrays (GL_LINE_STRIP, 0, path->path_size) ); + + path_start += path->path_size; + } +} + +static void +_cogl_path_get_bounds (CoglFixedVec2 nodes_min, + CoglFixedVec2 nodes_max, + gint *bounds_x, + gint *bounds_y, + guint *bounds_w, + guint *bounds_h) +{ + *bounds_x = COGL_FIXED_FLOOR (nodes_min.x); + *bounds_y = COGL_FIXED_FLOOR (nodes_min.y); + *bounds_w = COGL_FIXED_CEIL (nodes_max.x + - COGL_FIXED_FROM_INT (*bounds_x)); + *bounds_h = COGL_FIXED_CEIL (nodes_max.y + - COGL_FIXED_FROM_INT (*bounds_y)); } static gint compare_ints (gconstpointer a, @@ -158,209 +168,314 @@ static gint compare_ints (gconstpointer a, return GPOINTER_TO_INT(a)-GPOINTER_TO_INT(b); } +void +_cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min, + CoglFixedVec2 nodes_max, + guint path_size, + CoglPathNode *path, + gboolean merge) +{ + guint path_start = 0; + guint sub_path_num = 0; + gint bounds_x; + gint bounds_y; + guint bounds_w; + guint bounds_h; + + _cogl_path_get_bounds (nodes_min, nodes_max, + &bounds_x, &bounds_y, &bounds_w, &bounds_h); + + if (merge) + { + GE( glStencilMask (2) ); + GE( glStencilFunc (GL_LEQUAL, 0x2, 0x6) ); + } + else + { + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glStencilMask (1) ); + GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); + } + + GE( cogl_wrap_glEnable (GL_STENCIL_TEST) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + + GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); + GE( glDepthMask (FALSE) ); + + while (path_start < path_size) + { + cogl_enable (COGL_ENABLE_VERTEX_ARRAY); + + GE( cogl_wrap_glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode), + (guchar *) path + + G_STRUCT_OFFSET (CoglPathNode, x)) ); + GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size) ); + + if (sub_path_num > 0) + { + /* Union the two stencil buffers bits into the least + significant bit */ + GE( glStencilMask (merge ? 6 : 3) ); + GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); + cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + } + + GE( glStencilMask (merge ? 4 : 2) ); + + path_start += path->path_size; + path += path->path_size; + sub_path_num++; + } + + if (merge) + { + /* Now we have the new stencil buffer in bit 1 and the old + stencil buffer in bit 0 so we need to intersect them */ + GE( glStencilMask (3) ); + GE( glStencilFunc (GL_NEVER, 0x2, 0x3) ); + GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); + /* Decrement all of the bits twice so that only pixels where the + value is 3 will remain */ + GE( cogl_wrap_glPushMatrix () ); + GE( cogl_wrap_glLoadIdentity () ); + GE( cogl_wrap_glMatrixMode (GL_PROJECTION) ); + GE( cogl_wrap_glPushMatrix () ); + GE( cogl_wrap_glLoadIdentity () ); + cogl_rectanglex (-COGL_FIXED_1, -COGL_FIXED_1, + COGL_FIXED_FROM_INT (2), + COGL_FIXED_FROM_INT (2)); + cogl_rectanglex (-COGL_FIXED_1, -COGL_FIXED_1, + COGL_FIXED_FROM_INT (2), + COGL_FIXED_FROM_INT (2)); + GE( cogl_wrap_glPopMatrix () ); + GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); + GE( cogl_wrap_glPopMatrix () ); + } + + GE( glStencilMask (~(GLuint) 0) ); + GE( glDepthMask (TRUE) ); + GE( glColorMask (TRUE, TRUE, TRUE, TRUE) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); +} + +static void +_cogl_path_fill_nodes_scanlines (CoglPathNode *path, + guint path_size, + gint bounds_x, + gint bounds_y, + guint bounds_w, + guint bounds_h) +{ + /* This is our edge list it stores intersections between our + * curve and scanlines, it should probably be implemented with a + * data structure that has smaller overhead for inserting the + * curve/scanline intersections. + */ + GSList *scanlines[bounds_h]; + + gint i; + gint prev_x; + gint prev_y; + gint first_x; + gint first_y; + gint lastdir=-2; /* last direction we vere moving */ + gint lastline=-1; /* the previous scanline we added to */ + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* clear scanline intersection lists */ + for (i=0; i < bounds_h; i++) + scanlines[i]=NULL; + + first_x = prev_x = COGL_FIXED_TO_INT (path->x); + first_y = prev_y = COGL_FIXED_TO_INT (path->y); + + /* create scanline intersection list */ + for (i=1; i < path_size; i++) + { + gint dest_x = COGL_FIXED_TO_INT (path[i].x); + gint dest_y = COGL_FIXED_TO_INT (path[i].y); + gint ydir; + gint dx; + gint dy; + gint y; + + fill_close: + dx = dest_x - prev_x; + dy = dest_y - prev_y; + + if (dy < 0) + ydir = -1; + else if (dy > 0) + ydir = 1; + else + ydir = 0; + + /* do linear interpolation between vertexes */ + for (y=prev_y; y!= dest_y; y += ydir) + { + + /* only add a point if the scanline has changed and we're + * within bounds. + */ + if (y-bounds_y >= 0 && + y-bounds_y < bounds_h && + lastline != y) + { + gint x = prev_x + (dx * (y-prev_y)) / dy; + + scanlines[ y - bounds_y ]= + g_slist_insert_sorted (scanlines[ y - bounds_y], + GINT_TO_POINTER(x), + compare_ints); + + if (ydir != lastdir && /* add a double entry when changing */ + lastdir!=-2) /* vertical direction */ + scanlines[ y - bounds_y ]= + g_slist_insert_sorted (scanlines[ y - bounds_y], + GINT_TO_POINTER(x), + compare_ints); + lastdir = ydir; + lastline = y; + } + } + + prev_x = dest_x; + prev_y = dest_y; + + /* if we're on the last knot, fake the first vertex being a + next one */ + if (path_size == i+1) + { + dest_x = first_x; + dest_y = first_y; + i++; /* to make the loop finally end */ + goto fill_close; + } + } + + { + gint spans = 0; + gint span_no; + GLfixed *coords; + + /* count number of spans */ + for (i=0; i < bounds_h; i++) + { + GSList *iter = scanlines[i]; + while (iter) + { + GSList *next = iter->next; + if (!next) + { + break; + } + /* draw the segments that should be visible */ + spans ++; + iter = next->next; + } + } + coords = g_malloc0 (spans * sizeof (GLfixed) * 3 * 2 * 2); + + span_no = 0; + /* build list of triangles */ + for (i=0; i < bounds_h; i++) + { + GSList *iter = scanlines[i]; + while (iter) + { + GSList *next = iter->next; + GLfixed x0, x1; + GLfixed y0, y1; + if (!next) + break; + + x0 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (iter->data)); + x1 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (next->data)); + y0 = COGL_FIXED_FROM_INT (bounds_y + i); + y1 = COGL_FIXED_FROM_INT (bounds_y + i + 1) + 2048; + /* render scanlines 1.0625 high to avoid gaps when + transformed */ + + coords[span_no * 12 + 0] = x0; + coords[span_no * 12 + 1] = y0; + coords[span_no * 12 + 2] = x1; + coords[span_no * 12 + 3] = y0; + coords[span_no * 12 + 4] = x1; + coords[span_no * 12 + 5] = y1; + coords[span_no * 12 + 6] = x0; + coords[span_no * 12 + 7] = y0; + coords[span_no * 12 + 8] = x0; + coords[span_no * 12 + 9] = y1; + coords[span_no * 12 + 10] = x1; + coords[span_no * 12 + 11] = y1; + span_no ++; + iter = next->next; + } + } + for (i=0; i < bounds_h; i++) + { + g_slist_free (scanlines[i]); + } + + /* render triangles */ + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); + GE ( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, coords ) ); + GE ( cogl_wrap_glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3)); + g_free (coords); + } +} + void _cogl_path_fill_nodes () { - guint bounds_x; - guint bounds_y; + gint bounds_x; + gint bounds_y; guint bounds_w; guint bounds_h; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - bounds_x = COGL_FIXED_FLOOR (ctx->path_nodes_min.x); - bounds_y = COGL_FIXED_FLOOR (ctx->path_nodes_min.y); - bounds_w = COGL_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); - bounds_h = COGL_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); + + _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max, + &bounds_x, &bounds_y, &bounds_w, &bounds_h); if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min, + ctx->path_nodes_max, + ctx->path_nodes->len, + &g_array_index (ctx->path_nodes, + CoglPathNode, 0), + ctx->clip.stencil_used); - GE( cogl_wrap_glEnable (GL_STENCIL_TEST) ); - GE( glStencilFunc (GL_NEVER, 0x0, 0x1) ); - GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); - - GE( glStencilMask (1) ); - - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - - GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); - GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); - - GE( glStencilMask (~(GLuint) 0) ); - - /* Merge the stencil buffer with any clipping rectangles */ - _cogl_clip_stack_merge (); - - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); - cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); - - /* Rebuild the stencil clip */ - _cogl_clip_stack_rebuild (TRUE); + + /* The stencil buffer now contains garbage so the clip area needs to + be rebuilt */ + ctx->clip.stack_dirty = TRUE; } else { - /* This is our edge list it stores intersections between our - * curve and scanlines, it should probably be implemented with a - * data structure that has smaller overhead for inserting the - * curve/scanline intersections. - */ - GSList *scanlines[bounds_h]; + guint path_start = 0; - gint i; - gint prev_x; - gint prev_y; - gint first_x; - gint first_y; - gint lastdir=-2; /* last direction we vere moving */ - gint lastline=-1; /* the previous scanline we added to */ + while (path_start < ctx->path_nodes->len) + { + CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, + path_start); - /* clear scanline intersection lists */ - for (i=0; i < bounds_h; i++) - scanlines[i]=NULL; + _cogl_path_fill_nodes_scanlines (path, + path->path_size, + bounds_x, bounds_y, + bounds_w, bounds_h); - first_x = prev_x = COGL_FIXED_TO_INT (ctx->path_nodes[0].x); - first_y = prev_y = COGL_FIXED_TO_INT (ctx->path_nodes[0].y); - - /* create scanline intersection list */ - for (i=1; ipath_nodes_size; i++) - { - gint dest_x = COGL_FIXED_TO_INT (ctx->path_nodes[i].x); - gint dest_y = COGL_FIXED_TO_INT (ctx->path_nodes[i].y); - gint ydir; - gint dx; - gint dy; - gint y; - - fill_close: - dx = dest_x - prev_x; - dy = dest_y - prev_y; - - if (dy < 0) - ydir = -1; - else if (dy > 0) - ydir = 1; - else - ydir = 0; - - /* do linear interpolation between vertexes */ - for (y=prev_y; y!= dest_y; y += ydir) - { - - /* only add a point if the scanline has changed and we're - * within bounds. - */ - if (y-bounds_y >= 0 && - y-bounds_y < bounds_h && - lastline != y) - { - gint x = prev_x + (dx * (y-prev_y)) / dy; - - scanlines[ y - bounds_y ]= - g_slist_insert_sorted (scanlines[ y - bounds_y], - GINT_TO_POINTER(x), - compare_ints); - - if (ydir != lastdir && /* add a double entry when changing */ - lastdir!=-2) /* vertical direction */ - scanlines[ y - bounds_y ]= - g_slist_insert_sorted (scanlines[ y - bounds_y], - GINT_TO_POINTER(x), - compare_ints); - lastdir = ydir; - lastline = y; - } - } - - prev_x = dest_x; - prev_y = dest_y; - - /* if we're on the last knot, fake the first vertex being a - next one */ - if (ctx->path_nodes_size == i+1) - { - dest_x = first_x; - dest_y = first_y; - i++; /* to make the loop finally end */ - goto fill_close; - } - } - - { - gint spans = 0; - gint span_no; - GLfixed *coords; - - /* count number of spans */ - for (i=0; i < bounds_h; i++) - { - GSList *iter = scanlines[i]; - while (iter) - { - GSList *next = iter->next; - if (!next) - { - break; - } - /* draw the segments that should be visible */ - spans ++; - iter = next->next; - } - } - coords = g_malloc0 (spans * sizeof (GLfixed) * 3 * 2 * 2); - - span_no = 0; - /* build list of triangles */ - for (i=0; i < bounds_h; i++) - { - GSList *iter = scanlines[i]; - while (iter) - { - GSList *next = iter->next; - GLfixed x0, x1; - GLfixed y0, y1; - if (!next) - break; - - x0 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (iter->data)); - x1 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (next->data)); - y0 = COGL_FIXED_FROM_INT (bounds_y + i); - y1 = COGL_FIXED_FROM_INT (bounds_y + i + 1) + 2048; - /* render scanlines 1.0625 high to avoid gaps when - transformed */ - - coords[span_no * 12 + 0] = x0; - coords[span_no * 12 + 1] = y0; - coords[span_no * 12 + 2] = x1; - coords[span_no * 12 + 3] = y0; - coords[span_no * 12 + 4] = x1; - coords[span_no * 12 + 5] = y1; - coords[span_no * 12 + 6] = x0; - coords[span_no * 12 + 7] = y0; - coords[span_no * 12 + 8] = x0; - coords[span_no * 12 + 9] = y1; - coords[span_no * 12 + 10] = x1; - coords[span_no * 12 + 11] = y1; - span_no ++; - iter = next->next; - } - } - for (i=0; i < bounds_h; i++) - { - g_slist_free (scanlines[i]); - } - - /* render triangles */ - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - GE ( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, coords ) ); - GE ( cogl_wrap_glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3)); - g_free (coords); - } + path_start += path->path_size; + } } } diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index 21d1575b3..5a75eb389 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -2324,6 +2324,8 @@ cogl_texture_rectangle (CoglHandle handle, /* Check if valid texture */ if (!cogl_is_texture (handle)) return; + + cogl_clip_ensure (); tex = _cogl_texture_pointer_from_handle (handle); @@ -2379,6 +2381,8 @@ cogl_texture_polygon (CoglHandle handle, if (!cogl_is_texture (handle)) return; + cogl_clip_ensure (); + tex = _cogl_texture_pointer_from_handle (handle); /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index 45bac331a..bb4a0ea76 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -389,8 +389,6 @@ set_clip_plane (GLint plane_num, GE( cogl_wrap_glClipPlanex (plane_num, plane) ); GE( cogl_wrap_glPopMatrix () ); - - GE( cogl_wrap_glEnable (plane_num) ); } void @@ -438,15 +436,6 @@ _cogl_set_clip_planes (CoglFixed x_offset, set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); } -static int -compare_y_coordinate (const void *a, const void *b) -{ - GLfixed ay = ((const GLfixed *) a)[1]; - GLfixed by = ((const GLfixed *) b)[1]; - - return ay < by ? -1 : ay > by ? 1 : 0; -} - void _cogl_add_stencil_clip (CoglFixed x_offset, CoglFixed y_offset, @@ -454,19 +443,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset, CoglFixed height, gboolean first) { - gboolean has_clip_planes - = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (has_clip_planes) - { - GE( cogl_wrap_glDisable (GL_CLIP_PLANE3) ); - GE( cogl_wrap_glDisable (GL_CLIP_PLANE2) ); - GE( cogl_wrap_glDisable (GL_CLIP_PLANE1) ); - GE( cogl_wrap_glDisable (GL_CLIP_PLANE0) ); - } - if (first) { GE( cogl_wrap_glEnable (GL_STENCIL_TEST) ); @@ -481,7 +459,7 @@ _cogl_add_stencil_clip (CoglFixed x_offset, cogl_rectanglex (x_offset, y_offset, width, height); } - else if (ctx->num_stencil_bits > 1) + else { /* Add one to every pixel of the stencil buffer in the rectangle */ @@ -505,120 +483,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset, GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); GE( cogl_wrap_glPopMatrix () ); } - else - { - /* Slower fallback if there is exactly one stencil bit. This - tries to draw enough triangles to tessalate around the - rectangle so that it can subtract from the stencil buffer for - every pixel in the screen except those in the rectangle */ - GLfixed modelview[16], projection[16]; - GLfixed temp_point[4]; - GLfixed left_edge, right_edge, bottom_edge, top_edge; - int i; - GLfixed points[16] = - { - x_offset, y_offset, 0, COGL_FIXED_1, - x_offset + width, y_offset, 0, COGL_FIXED_1, - x_offset, y_offset + height, 0, COGL_FIXED_1, - x_offset + width, y_offset + height, 0, COGL_FIXED_1 - }; - GLfixed draw_points[12]; - - GE( cogl_wrap_glGetFixedv (GL_MODELVIEW_MATRIX, modelview) ); - GE( cogl_wrap_glGetFixedv (GL_PROJECTION_MATRIX, projection) ); - - /* Project all of the vertices into screen coordinates */ - for (i = 0; i < 4; i++) - project_vertex (modelview, projection, points + i * 4); - - /* Sort the points by y coordinate */ - qsort (points, 4, sizeof (GLfixed) * 4, compare_y_coordinate); - - /* Put the bottom two pairs and the top two pairs in - left-right order */ - if (points[0] > points[4]) - { - memcpy (temp_point, points, sizeof (GLfixed) * 4); - memcpy (points, points + 4, sizeof (GLfixed) * 4); - memcpy (points + 4, temp_point, sizeof (GLfixed) * 4); - } - if (points[8] > points[12]) - { - memcpy (temp_point, points + 8, sizeof (GLfixed) * 4); - memcpy (points + 8, points + 12, sizeof (GLfixed) * 4); - memcpy (points + 12, temp_point, sizeof (GLfixed) * 4); - } - - /* If the clip rect goes outside of the screen then use the - extents of the rect instead */ - left_edge = MIN (-COGL_FIXED_1, MIN (points[0], points[8])); - right_edge = MAX ( COGL_FIXED_1, MAX (points[4], points[12])); - bottom_edge = MIN (-COGL_FIXED_1, MIN (points[1], points[5])); - top_edge = MAX ( COGL_FIXED_1, MAX (points[9], points[13])); - - /* Using the identity matrix for the projection and - modelview matrix, draw the triangles around the inner - rectangle */ - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); - GE( cogl_wrap_glPushMatrix () ); - GE( cogl_wrap_glLoadIdentity () ); - GE( cogl_wrap_glMatrixMode (GL_PROJECTION) ); - GE( cogl_wrap_glPushMatrix () ); - GE( cogl_wrap_glLoadIdentity () ); - - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); - GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, draw_points) ); - - /* Clear the left side */ - draw_points[0] = left_edge; draw_points[1] = bottom_edge; - draw_points[2] = points[0]; draw_points[3] = points[1]; - draw_points[4] = left_edge; draw_points[5] = points[1]; - draw_points[6] = points[8]; draw_points[7] = points[9]; - draw_points[8] = left_edge; draw_points[9] = points[9]; - draw_points[10] = left_edge; draw_points[11] = top_edge; - GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) ); - - /* Clear the right side */ - draw_points[0] = right_edge; draw_points[1] = top_edge; - draw_points[2] = points[12]; draw_points[3] = points[13]; - draw_points[4] = right_edge; draw_points[5] = points[13]; - draw_points[6] = points[4]; draw_points[7] = points[5]; - draw_points[8] = right_edge; draw_points[9] = points[5]; - draw_points[10] = right_edge; draw_points[11] = bottom_edge; - GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) ); - - /* Clear the top side */ - draw_points[0] = left_edge; draw_points[1] = top_edge; - draw_points[2] = points[8]; draw_points[3] = points[9]; - draw_points[4] = points[8]; draw_points[5] = top_edge; - draw_points[6] = points[12]; draw_points[7] = points[13]; - draw_points[8] = points[12]; draw_points[9] = top_edge; - draw_points[10] = right_edge; draw_points[11] = top_edge; - GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) ); - - /* Clear the bottom side */ - draw_points[0] = left_edge; draw_points[1] = bottom_edge; - draw_points[2] = points[0]; draw_points[3] = points[1]; - draw_points[4] = points[0]; draw_points[5] = bottom_edge; - draw_points[6] = points[4]; draw_points[7] = points[5]; - draw_points[8] = points[4]; draw_points[9] = bottom_edge; - draw_points[10] = right_edge; draw_points[11] = bottom_edge; - GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) ); - - GE( cogl_wrap_glPopMatrix () ); - GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); - GE( cogl_wrap_glPopMatrix () ); - } - - if (has_clip_planes) - { - GE( cogl_wrap_glEnable (GL_CLIP_PLANE0) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE1) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE2) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE3) ); - } /* Restore the stencil mode */ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); @@ -638,6 +502,15 @@ _cogl_disable_stencil_buffer (void) GE( cogl_wrap_glDisable (GL_STENCIL_TEST) ); } +void +_cogl_enable_clip_planes (void) +{ + GE( cogl_wrap_glEnable (GL_CLIP_PLANE0) ); + GE( cogl_wrap_glEnable (GL_CLIP_PLANE1) ); + GE( cogl_wrap_glEnable (GL_CLIP_PLANE2) ); + GE( cogl_wrap_glEnable (GL_CLIP_PLANE3) ); +} + void _cogl_disable_clip_planes (void) { @@ -818,12 +691,13 @@ _cogl_features_init () { CoglFeatureFlags flags = 0; int max_clip_planes = 0; + GLint num_stencil_bits = 0; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - ctx->num_stencil_bits = 0; - GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) ); - if (ctx->num_stencil_bits > 0) + GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) ); + /* We need at least three stencil bits to combine clips */ + if (num_stencil_bits > 2) flags |= COGL_FEATURE_STENCIL_BUFFER; GE( cogl_wrap_glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 3ff906e90..03247ff3c 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -29,9 +29,12 @@ cogl_rotatex cogl_rotate cogl_clip_set +cogl_clip_set_from_path +cogl_clip_set_from_path_preserve cogl_clip_unset cogl_clip_stack_save cogl_clip_stack_restore +cogl_clip_ensure cogl_enable_depth_test cogl_enable_backface_culling @@ -63,6 +66,7 @@ cogl_util_next_p2
cogl-primitives Primitives +cogl_path_new cogl_path_move_to cogl_path_close cogl_path_line_to @@ -80,7 +84,9 @@ cogl_path_ellipse cogl_path_fill +cogl_path_fill_preserve cogl_path_stroke +cogl_path_stroke_preserve cogl_set_source_color cogl_set_source_color4ub cogl_set_source_color4x diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 0db145931..3b52fdb81 100644 --- a/tests/interactive/test-clip.c +++ b/tests/interactive/test-clip.c @@ -1,128 +1,336 @@ #include -#include -#include -#include #include -#define TL_SCALE 5.0f - typedef struct _CallbackData CallbackData; +typedef struct _Clip Clip; + +typedef enum + { + CLIP_NONE, + CLIP_RECTANGLE, + CLIP_ELLIPSE, + CLIP_SHAPES + } ClipType; + +struct _Clip +{ + ClipType type; + gint x1, y1, x2, y2; +}; struct _CallbackData { - ClutterActor *stage, *group, *rect, *hand; + ClutterActor *stage; + CoglHandle hand; + + Clip current_clip; + + GSList *clips; }; +static const char +instructions[] = + "Press and drag any of the three mouse buttons to add a clip with different " + "shapes. Press 'r' to reset or 'u' to undo the last clip."; + static void -on_new_frame (ClutterTimeline *tl, int frame_num, CallbackData *data) +path_shapes (gint x, gint y, gint width, gint height) +{ + cogl_path_move_to (CLUTTER_INT_TO_FIXED (x), CLUTTER_INT_TO_FIXED (y)); + cogl_path_line_to (CLUTTER_INT_TO_FIXED (x), + CLUTTER_INT_TO_FIXED (y + height * 4 / 5)); + cogl_path_line_to (CLUTTER_INT_TO_FIXED (x + width * 4 / 15), + CLUTTER_INT_TO_FIXED (y + height * 4 / 5)); + cogl_path_close (); + + cogl_path_rectangle (CLUTTER_INT_TO_FIXED (x + width / 3), + CLUTTER_INT_TO_FIXED (y), + CLUTTER_INT_TO_FIXED (width * 4 / 15), + CLUTTER_INT_TO_FIXED (height * 4 / 5)); + + cogl_path_ellipse (CLUTTER_INT_TO_FIXED (x + width * 4 / 5), + CLUTTER_INT_TO_FIXED (y + height * 2 / 5), + CLUTTER_INT_TO_FIXED (width * 2 / 15), + CLUTTER_INT_TO_FIXED (height * 2 / 5)); +} + +static void +draw_shapes (gint x, gint y) +{ + path_shapes (x, y, 300, 100); + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_path_fill_preserve (); + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_path_stroke (); +} + +static void +make_clip_path (Clip *clip) +{ + switch (clip->type) + { + case CLIP_NONE: + break; + + case CLIP_RECTANGLE: + cogl_path_rectangle (CLUTTER_INT_TO_FIXED (clip->x1), + CLUTTER_INT_TO_FIXED (clip->y1), + CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1), + CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1)); + break; + + case CLIP_ELLIPSE: + cogl_path_ellipse (CLUTTER_INT_TO_FIXED (clip->x1 + clip->x2) / 2, + CLUTTER_INT_TO_FIXED (clip->y1 + clip->y2) / 2, + CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1) / 2, + CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1) / 2); + break; + + case CLIP_SHAPES: + { + int x, y, width, height; + + if (clip->x1 < clip->x2) + { + x = clip->x1; + width = clip->x2 - x; + } + else + { + x = clip->x2; + width = clip->x1 - x; + } + if (clip->y1 < clip->y2) + { + y = clip->y1; + height = clip->y2 - y; + } + else + { + y = clip->y2; + height = clip->y1 - y; + } + + path_shapes (x, y, width, height); + } + break; + } +} + +static void +on_paint (ClutterActor *actor, CallbackData *data) { int i; - int stage_width = clutter_actor_get_width (data->stage); - int stage_height = clutter_actor_get_height (data->stage); - gdouble progress = clutter_timeline_get_progress (tl); - gdouble angle = progress * 2 * M_PI * TL_SCALE; - gdouble rotation[3]; + ClutterGeometry stage_size; + guint hand_width, hand_height; + GSList *node; - gdouble xpos = stage_width * 0.45 * sin (angle) + stage_width / 8; - gdouble ypos = stage_height * 0.45 * sin (angle) + stage_height / 8; - gdouble zpos = stage_width * cos (angle) - stage_width / 2; + clutter_actor_get_allocation_geometry (data->stage, &stage_size); - clutter_actor_set_position (data->hand, xpos, ypos); - clutter_actor_set_depth (data->hand, zpos); - clutter_actor_set_rotation (data->hand, CLUTTER_Y_AXIS, - angle / M_PI * 180.0 * 3, - clutter_actor_get_width (data->hand) / 2, - clutter_actor_get_height (data->hand) / 2, - 0); + hand_width = cogl_texture_get_width (data->hand); + hand_height = cogl_texture_get_height (data->hand); - memset (rotation, 0, sizeof (rotation)); - - if (progress < 1 / 3.0) - rotation[2] = 360 * progress * 3; - else if (progress < 2 / 3.0) - rotation[1] = 360 * progress * 3; - else - rotation[0] = 360 * progress * 3; - - for (i = 0; i < 3; i++) + /* Setup the clipping */ + for (node = data->clips; node; node = node->next) { - clutter_actor_set_rotation (data->group, i, - rotation[i], - clutter_actor_get_width (data->rect) / 2, - clutter_actor_get_height (data->rect) / 2, - 0); - clutter_actor_set_rotation (data->rect, i, - rotation[i], - clutter_actor_get_width (data->rect) / 2, - clutter_actor_get_height (data->rect) / 2, - 0); + Clip *clip = (Clip *) node->data; + + if (clip->type == CLIP_RECTANGLE) + cogl_clip_set (CLUTTER_INT_TO_FIXED (clip->x1), + CLUTTER_INT_TO_FIXED (clip->y1), + CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1), + CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1)); + else + { + make_clip_path (clip); + cogl_clip_set_from_path (); + } } + + /* Draw a rectangle filling the entire stage */ + cogl_set_source_color4ub (0x80, 0x80, 0xff, 0xff); + cogl_rectangle (0, 0, stage_size.width, stage_size.height); + + draw_shapes (10, 10); + + /* Draw the hand at different rotations */ + for (i = -2; i <= 2; i++) + { + cogl_push_matrix (); + + cogl_translate (stage_size.width / 2 + stage_size.width / 6 * i, + stage_size.height / 2, 0); + + cogl_rotate (i * 40, 0, 1, 0); + + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + + cogl_texture_rectangle (data->hand, + CLUTTER_INT_TO_FIXED (-hand_width / 2), + CLUTTER_INT_TO_FIXED (-hand_height / 2), + CLUTTER_INT_TO_FIXED (hand_width / 2), + CLUTTER_INT_TO_FIXED (hand_height / 2), + 0, 0, CFX_ONE, CFX_ONE); + + cogl_pop_matrix (); + } + + draw_shapes (stage_size.width - 310, stage_size.height - 110); + + /* Remove all of the clipping */ + g_slist_foreach (data->clips, (GFunc) cogl_clip_unset, NULL); + + /* Draw the bounding box for each of the clips */ + for (node = data->clips; node; node = node->next) + { + Clip *clip = (Clip *) node->data; + + make_clip_path (clip); + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_path_stroke (); + } + + /* Draw the bounding box for the pending new clip */ + if (data->current_clip.type != CLIP_NONE) + { + make_clip_path (&data->current_clip); + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_path_stroke (); + } +} + +static gboolean +on_button_press (ClutterActor *stage, ClutterButtonEvent *event, + CallbackData *data) +{ + data->current_clip.x1 = data->current_clip.x2 = event->x; + data->current_clip.y1 = data->current_clip.y2 = event->y; + + data->current_clip.type + = event->button == 1 ? CLIP_RECTANGLE + : event->button == 2 ? CLIP_SHAPES + : CLIP_ELLIPSE; + + clutter_actor_queue_redraw (stage); + + return FALSE; +} + +static gboolean +on_button_release (ClutterActor *stage, ClutterButtonEvent *event, + CallbackData *data) +{ + if (data->current_clip.type != CLIP_NONE) + { + data->clips = g_slist_prepend (data->clips, + g_slice_copy (sizeof (Clip), + &data->current_clip)); + + data->current_clip.type = CLIP_NONE; + } + + clutter_actor_queue_redraw (stage); + + return FALSE; +} + +static gboolean +on_motion (ClutterActor *stage, ClutterMotionEvent *event, + CallbackData *data) +{ + if (data->current_clip.type != CLIP_NONE) + { + data->current_clip.x2 = event->x; + data->current_clip.y2 = event->y; + + clutter_actor_queue_redraw (stage); + } + + return FALSE; +} + +static void +free_clips (CallbackData *data) +{ + GSList *node; + + for (node = data->clips; node; node = node->next) + g_slice_free (Clip, node->data); + + g_slist_free (data->clips); + + data->clips = NULL; +} + +static gpointer +on_key_press (ClutterActor *stage, ClutterKeyEvent *event, + CallbackData *data) +{ + switch (event->keyval) + { + case CLUTTER_r: + free_clips (data); + clutter_actor_queue_redraw (stage); + break; + + case CLUTTER_u: + if (data->clips) + { + g_slice_free (Clip, data->clips->data); + data->clips = g_slist_delete_link (data->clips, data->clips); + clutter_actor_queue_redraw (stage); + } + break; + } + + return FALSE; } G_MODULE_EXPORT int test_clip_main (int argc, char **argv) { - ClutterGeometry geom; - ClutterTimeline *tl; - ClutterColor blue = { 0x40, 0x40, 0xff, 0xff }; CallbackData data; - ClutterActor *other_hand; - int x, y; + ClutterActor *stub_actor, *label; clutter_init (&argc, &argv); + data.current_clip.type = CLIP_NONE; + data.clips = NULL; + data.stage = clutter_stage_get_default (); - data.group = clutter_group_new (); + stub_actor = clutter_rectangle_new (); + clutter_container_add (CLUTTER_CONTAINER (data.stage), stub_actor, NULL); - clutter_actor_get_geometry (data.stage, &geom); - geom.x = geom.width / 4; - geom.y = geom.height / 4; - geom.width /= 2; - geom.height /= 2; - clutter_actor_set_geometry (data.group, &geom); + data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE, + COGL_PIXEL_FORMAT_ANY, NULL); - data.rect = clutter_rectangle_new_with_color (&blue); - clutter_actor_set_geometry (data.rect, &geom); - clutter_container_add (CLUTTER_CONTAINER (data.stage), data.rect, NULL); + label = clutter_label_new_with_text ("Sans 12px", instructions); + clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE); + clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310); + clutter_actor_set_y (label, + clutter_actor_get_height (data.stage) + - clutter_actor_get_height (label)); + clutter_container_add (CLUTTER_CONTAINER (data.stage), label, NULL); - clutter_container_add (CLUTTER_CONTAINER (data.stage), data.group, NULL); + g_signal_connect (stub_actor, "paint", G_CALLBACK (on_paint), &data); - clutter_actor_set_clip (data.group, 0, 0, geom.width, geom.height); - - data.hand = clutter_texture_new_from_file ("redhand.png", NULL); - if (data.hand == NULL) - { - g_critical ("pixbuf loading failed"); - exit (1); - } - clutter_container_add (CLUTTER_CONTAINER (data.group), data.hand, NULL); - - /* Add a hand at each of the four corners of the group */ - for (y = 0; y < 2; y++) - for (x = 0; x < 2; x++) - { - other_hand = clutter_clone_texture_new (CLUTTER_TEXTURE (data.hand)); - clutter_actor_set_anchor_point_from_gravity - (other_hand, CLUTTER_GRAVITY_CENTER); - clutter_actor_set_position (other_hand, - x * geom.width, - y * geom.height); - clutter_container_add (CLUTTER_CONTAINER (data.group), - other_hand, NULL); - } - - clutter_actor_raise_top (data.hand); - - tl = clutter_timeline_new (360 * TL_SCALE, 60); - clutter_timeline_start (tl); - clutter_timeline_set_loop (tl, TRUE); - - g_signal_connect (tl, "new-frame", G_CALLBACK (on_new_frame), &data); + g_signal_connect (data.stage, "button-press-event", + G_CALLBACK (on_button_press), &data); + g_signal_connect (data.stage, "button-release-event", + G_CALLBACK (on_button_release), &data); + g_signal_connect (data.stage, "motion-event", + G_CALLBACK (on_motion), &data); + g_signal_connect (data.stage, "key-press-event", + G_CALLBACK (on_key_press), &data); clutter_actor_show (data.stage); clutter_main (); + cogl_texture_unref (data.hand); + + free_clips (&data); + return 0; } diff --git a/tests/interactive/test-cogl-primitives.c b/tests/interactive/test-cogl-primitives.c index 09ac8a6bd..7fbc37676 100644 --- a/tests/interactive/test-cogl-primitives.c +++ b/tests/interactive/test-cogl-primitives.c @@ -204,11 +204,11 @@ test_coglbox_paint(ClutterActor *self) cogl_push_matrix (); paint_func[paint_index] (); - + cogl_translate (100, 100, 0); cogl_set_source_color4ub (0, 160, 0, 255); - cogl_path_stroke (); - + cogl_path_stroke_preserve (); + cogl_translate (150, 0, 0); cogl_set_source_color4ub (200, 0, 0, 255); cogl_path_fill (); From 98035e4d8a418f1f15dfdf56b2883b081a0401b2 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 4 Dec 2008 17:24:33 +0000 Subject: [PATCH 7/7] Bug 1297 - Bring back support for GL_ARB_texture_rectangle * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_foreign, (_cogl_texture_quad_hw, cogl_texture_polygon), (_cogl_texture_quad_sw): Support GL_ARB_texture_rectangle textures * clutter/glx/clutter-glx-texture-pixmap.c: Use rectangle textures when NPOTs are not available or it is forced by the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable. * clutter/cogl/gl/cogl.c (cogl_enable): Allow enabling GL_TEXTURE_RECTANGLE_ARB. --- ChangeLog | 15 +++ clutter/cogl/gl/cogl-texture.c | 109 +++++++++++------ clutter/cogl/gl/cogl.c | 8 +- clutter/glx/clutter-glx-texture-pixmap.c | 143 +++++++++++++++++++++-- 4 files changed, 229 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8366bfbd1..a2c59f83e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-12-04 Neil Roberts + + Bug 1297 - Bring back support for GL_ARB_texture_rectangle + + * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_foreign, + (_cogl_texture_quad_hw, cogl_texture_polygon), + (_cogl_texture_quad_sw): Support GL_ARB_texture_rectangle textures + + * clutter/glx/clutter-glx-texture-pixmap.c: Use rectangle textures + when NPOTs are not available or it is forced by the + CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable. + + * clutter/cogl/gl/cogl.c (cogl_enable): Allow enabling + GL_TEXTURE_RECTANGLE_ARB. + 2008-12-04 Neil Roberts Bug 1172 - Disjoint paths and clip to path diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index b1b654c58..5a202ae6b 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -1427,16 +1427,22 @@ cogl_texture_new_from_foreign (GLuint gl_handle, CoglTexture *tex; CoglTexSliceSpan x_span; CoglTexSliceSpan y_span; - - /* Allow 2-dimensional textures only */ - if (gl_target != GL_TEXTURE_2D) + + /* GL_ARB_texture_rectangle textures are supported if they are + created from foreign because some chipsets have trouble with + GL_ARB_texture_non_power_of_two. There is no Cogl call to create + them directly to emphasize the fact that they don't work fully + (for example, no mipmapping and complicated shader support) */ + + /* Allow 2-dimensional or rectangle textures only */ + if (gl_target != GL_TEXTURE_2D && gl_target != CGL_TEXTURE_RECTANGLE_ARB) return COGL_INVALID_HANDLE; - + /* Make sure it is a valid GL texture object */ gl_istexture = glIsTexture (gl_handle); if (gl_istexture == GL_FALSE) return COGL_INVALID_HANDLE; - + /* Make sure binding succeeds */ gl_error = glGetError (); glBindTexture (gl_target, gl_handle); @@ -1928,8 +1934,7 @@ _cogl_texture_quad_sw (CoglTexture *tex, GLfloat tex_coords[8]; GLfloat quad_coords[8]; GLuint gl_handle; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1937,9 +1942,13 @@ _cogl_texture_quad_sw (CoglTexture *tex, #if COGL_DEBUG printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif - - + /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) { @@ -2020,17 +2029,19 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); - + /* Localize slice texture coordinates */ slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_ty1 /= iter_y.span->size; - slice_ty2 /= iter_y.span->size; - - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + } + /* Iterate until whole quad width covered */ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, first_tx, tx1, tx2) ; @@ -2050,12 +2061,15 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_tx1 /= iter_x.span->size; - slice_tx2 /= iter_x.span->size; - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + } + #if COGL_DEBUG printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("qx1: %f\n", COGL_FIXED_TO_FLOAT (slice_qx1)); @@ -2111,8 +2125,7 @@ _cogl_texture_quad_hw (CoglTexture *tex, GLuint gl_handle; CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY); #if COGL_DEBUG @@ -2122,6 +2135,11 @@ _cogl_texture_quad_hw (CoglTexture *tex, _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) { @@ -2162,8 +2180,17 @@ _cogl_texture_quad_hw (CoglTexture *tex, ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx1 *= x_span->size; + tx2 *= x_span->size; + ty1 *= y_span->size; + ty2 *= y_span->size; + } + #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) - + /* Draw textured quad */ tex_coords[0] = CFX_F(tx1); tex_coords[1] = CFX_F(ty2); tex_coords[2] = CFX_F(tx2); tex_coords[3] = CFX_F(ty2); @@ -2215,7 +2242,8 @@ cogl_texture_rectangle (CoglHandle handle, (no waste) or all of the coordinates are in the range [0,1] then we can use hardware tiling */ if (tex->slice_gl_handles->len == 1 - && (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) || (tx1 >= 0 && tx1 <= COGL_FIXED_1 && tx2 >= 0 && tx2 <= COGL_FIXED_1 && ty1 >= 0 && ty1 <= COGL_FIXED_1 @@ -2289,11 +2317,14 @@ cogl_texture_polygon (CoglHandle handle, } /* Prepare GL state */ - enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY | COGL_ENABLE_BLEND); + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; if (ctx->enable_backface_culling) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; @@ -2335,19 +2366,31 @@ cogl_texture_polygon (CoglHandle handle, OpenGL */ for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) { + CoglFixed tx, ty; + #define CFX_F COGL_FIXED_TO_FLOAT + tx = ((vertices[i].tx + - (COGL_FIXED_FROM_INT (x_span->start) + / tex->bitmap.width)) + * tex->bitmap.width / x_span->size); + ty = ((vertices[i].ty + - (COGL_FIXED_FROM_INT (y_span->start) + / tex->bitmap.height)) + * tex->bitmap.height / y_span->size); + + /* Scale the coordinates up for rectangle textures */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + { + tx *= x_span->size; + ty *= y_span->size; + } + p->v[0] = CFX_F(vertices[i].x); p->v[1] = CFX_F(vertices[i].y); p->v[2] = CFX_F(vertices[i].z); - p->t[0] = CFX_F((vertices[i].tx - - (COGL_FIXED_FROM_INT (x_span->start) - / tex->bitmap.width)) - * tex->bitmap.width / x_span->size); - p->t[1] = CFX_F((vertices[i].ty - - (COGL_FIXED_FROM_INT (y_span->start) - / tex->bitmap.height)) - * tex->bitmap.height / y_span->size); + p->t[0] = CFX_F(tx); + p->t[1] = CFX_F(ty); p->c[0] = cogl_color_get_red_byte(&vertices[i].color); p->c[1] = cogl_color_get_green_byte(&vertices[i].color); p->c[2] = cogl_color_get_blue_byte(&vertices[i].color); diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index 9bd14d113..64bd6faf3 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -318,7 +318,13 @@ cogl_enable (gulong flags) cogl_toggle_flag (ctx, flags, COGL_ENABLE_BACKFACE_CULLING, GL_CULL_FACE); - + +#ifdef GL_TEXTURE_RECTANGLE_ARB + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_RECT, + GL_TEXTURE_RECTANGLE_ARB); +#endif + cogl_toggle_client_flag (ctx, flags, COGL_ENABLE_VERTEX_ARRAY, GL_VERTEX_ARRAY); diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index 169ee209e..4c5221cac 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -40,6 +40,13 @@ * * The class requires the GLX_EXT_texture_from_pixmap OpenGL extension * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt) + * + * The GL_ARB_texture_non_power_of_two extension will be used if it is + * available. Otherwise it will try to use the + * GL_ARB_texture_rectangle extension. If both are available you can + * force it to prefer the rectangle extension by setting the + * CLUTTER_PIXMAP_TEXTURE_RECTANGLE to 'force'. To prevent it ever + * using the rectangle extension you can set it to 'disable'. */ @@ -48,6 +55,8 @@ #include "config.h" #endif +#include + #include "../x11/clutter-x11-texture-pixmap.h" #include "clutter-glx-texture-pixmap.h" #include "clutter-glx.h" @@ -68,12 +77,20 @@ typedef void (*ReleaseTexImage) (Display *display, typedef void (*GenerateMipmap) (GLenum target); +typedef enum +{ + CLUTTER_GLX_RECTANGLE_DISALLOW, + CLUTTER_GLX_RECTANGLE_ALLOW, + CLUTTER_GLX_RECTANGLE_FORCE +} RectangleState; static BindTexImage _gl_bind_tex_image = NULL; static ReleaseTexImage _gl_release_tex_image = NULL; static GenerateMipmap _gl_generate_mipmap = NULL; static gboolean _have_tex_from_pixmap_ext = FALSE; static gboolean _ext_check_done = FALSE; +static gboolean _have_tex_rectangle = FALSE; +static RectangleState _rectangle_state = CLUTTER_GLX_RECTANGLE_ALLOW; struct _ClutterGLXTexturePixmapPrivate { @@ -86,6 +103,8 @@ struct _ClutterGLXTexturePixmapPrivate gboolean bound; gint can_mipmap; gint mipmap_generate_queued; + + gboolean using_rectangle; }; static void @@ -170,7 +189,9 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) if (_ext_check_done == FALSE) { + const char *gl_extensions = NULL; const gchar *glx_extensions = NULL; + const char *rect_env; glx_extensions = glXQueryExtensionsString (clutter_x11_get_default_display (), @@ -191,16 +212,59 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) _gl_generate_mipmap = (GenerateMipmap)cogl_get_proc_address ("glGenerateMipmapEXT"); + gl_extensions = (char *) glGetString (GL_EXTENSIONS); + _have_tex_rectangle = cogl_check_extension ("GL_ARB_texture_rectangle", + gl_extensions); + + if ((rect_env = g_getenv ("CLUTTER_PIXMAP_TEXTURE_RECTANGLE"))) + { + if (strcasecmp (rect_env, "force") == 0) + _rectangle_state = CLUTTER_GLX_RECTANGLE_FORCE; + else if (strcasecmp (rect_env, "disable") == 0) + _rectangle_state = CLUTTER_GLX_RECTANGLE_DISALLOW; + else if (rect_env[0]) + g_warning ("Unknown value for CLUTTER_PIXMAP_TEXTURE_RECTANGLE, " + "should be 'force' or 'disable'"); + } + _ext_check_done = TRUE; } } +static void +clutter_glx_texture_pixmap_free_rectangle (ClutterGLXTexturePixmap *texture) +{ + ClutterGLXTexturePixmapPrivate *priv = texture->priv; + CoglHandle cogl_tex; + + /* Cogl won't free the GL texture resource if it was created with + new_from_foreign so we need to free it manually */ + if (priv->using_rectangle) + { + cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); + + if (cogl_tex != COGL_INVALID_HANDLE) + { + GLuint gl_handle; + GLenum gl_target; + + cogl_texture_get_gl_texture (cogl_tex, &gl_handle, &gl_target); + + if (gl_target == CGL_TEXTURE_RECTANGLE_ARB) + glDeleteTextures (1, &gl_handle); + } + + priv->using_rectangle = FALSE; + } +} + static void clutter_glx_texture_pixmap_dispose (GObject *object) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); + ClutterGLXTexturePixmapPrivate *priv = texture->priv; - priv = CLUTTER_GLX_TEXTURE_PIXMAP (object)->priv; + clutter_glx_texture_pixmap_free_rectangle (texture); if (priv->glx_pixmap != None) { @@ -228,20 +292,70 @@ clutter_glx_texture_pixmap_notify (GObject *object, GParamSpec *pspec) } } +static gboolean +should_use_rectangle (void) +{ + /* Use the rectangle only if it is available and either: + + the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable is + set to 'force' + + *or* + + the env var is set to 'allow' (which is the default) and NPOTs + textures are not available */ + return _have_tex_rectangle + && ((_rectangle_state == CLUTTER_GLX_RECTANGLE_ALLOW + && !clutter_feature_available (CLUTTER_FEATURE_TEXTURE_NPOT)) + || _rectangle_state == CLUTTER_GLX_RECTANGLE_FORCE); +} + static gboolean create_cogl_texture (ClutterTexture *texture, guint width, guint height) { + ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); + ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; CoglHandle handle; + gboolean using_rectangle; - handle - = cogl_texture_new_with_size (width, height, - -1, FALSE, - COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); + /* We want to use the GL_ARB_texture_rectangle extension on some + chipsets because GL_ARB_texture_non_power_of_two is not always + supported or might be buggy */ + if (should_use_rectangle ()) + { + GLuint tex = 0; + + using_rectangle = TRUE; + + glGenTextures (1, &tex); + glBindTexture (CGL_TEXTURE_RECTANGLE_ARB, tex); + glTexImage2D (CGL_TEXTURE_RECTANGLE_ARB, 0, + GL_RGBA, width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB, + width, height, + 0, 0, + COGL_PIXEL_FORMAT_RGBA_8888 + | COGL_BGR_BIT); + } + else + { + handle + = cogl_texture_new_with_size (width, height, + -1, FALSE, + COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); + + using_rectangle = FALSE; + } if (handle) { + clutter_glx_texture_pixmap_free_rectangle (texture_glx); + priv->using_rectangle = using_rectangle; + clutter_texture_set_cogl_texture (texture, handle); CLUTTER_ACTOR_SET_FLAGS (texture, CLUTTER_ACTOR_REALIZED); @@ -301,12 +415,14 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor) static void clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); + ClutterGLXTexturePixmapPrivate *priv = texture->priv; Display *dpy; - priv = CLUTTER_GLX_TEXTURE_PIXMAP (actor)->priv; dpy = clutter_x11_get_default_display(); + clutter_glx_texture_pixmap_free_rectangle (texture); + if (!_have_tex_from_pixmap_ext) { CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> @@ -575,7 +691,8 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) attribs[i++] = GLX_TEXTURE_TARGET_EXT; - attribs[i++] = GLX_TEXTURE_2D_EXT; + attribs[i++] = (should_use_rectangle () + ? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT); attribs[i++] = None; @@ -637,13 +754,12 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint width, gint height) { - ClutterGLXTexturePixmapPrivate *priv; + ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); + ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; Display *dpy; - CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); - priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; dpy = clutter_x11_get_default_display(); if (!CLUTTER_ACTOR_IS_REALIZED (texture)) @@ -652,6 +768,9 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, if (priv->use_fallback) { CLUTTER_NOTE (TEXTURE, "Falling back to X11"); + + clutter_glx_texture_pixmap_free_rectangle (texture_glx); + parent_class->update_area (texture, x, y, width, height);