diff --git a/clutter/cogl/cogl/cogl-sub-texture-private.h b/clutter/cogl/cogl/cogl-sub-texture-private.h index 16513425e..40ce68812 100644 --- a/clutter/cogl/cogl/cogl-sub-texture-private.h +++ b/clutter/cogl/cogl/cogl-sub-texture-private.h @@ -35,9 +35,22 @@ struct _CoglSubTexture { CoglTexture _parent; + /* This is the texture that was passed in to + _cogl_sub_texture_new. If this is also a sub texture then we will + use the full texture from that to render instead of making a + chain. However we want to preserve the next texture in case the + user is expecting us to keep a reference and also so that we can + later add a cogl_sub_texture_get_full_texture() function. */ + CoglHandle next_texture; + /* This is the texture that will actually be used to draw. It will + point to the end of the chain if a sub texture of a sub texture + is created */ CoglHandle full_texture; - /* The region represented by this sub-texture */ + /* The region represented by this sub-texture. This is the region of + full_texture which won't necessarily be the same as the region + passed to _cogl_sub_texture_new if next_texture is actually + already a sub texture */ int sub_x; int sub_y; int sub_width; @@ -48,7 +61,7 @@ GQuark _cogl_handle_sub_texture_get_type (void); CoglHandle -_cogl_sub_texture_new (CoglHandle full_texture, +_cogl_sub_texture_new (CoglHandle next_texture, int sub_x, int sub_y, int sub_width, diff --git a/clutter/cogl/cogl/cogl-sub-texture.c b/clutter/cogl/cogl/cogl-sub-texture.c index d28bcf850..b63006379 100644 --- a/clutter/cogl/cogl/cogl-sub-texture.c +++ b/clutter/cogl/cogl/cogl-sub-texture.c @@ -235,34 +235,51 @@ _cogl_sub_texture_set_wrap_mode_parameter (CoglTexture *tex, static void _cogl_sub_texture_free (CoglSubTexture *sub_tex) { + cogl_handle_unref (sub_tex->next_texture); cogl_handle_unref (sub_tex->full_texture); g_free (sub_tex); } CoglHandle -_cogl_sub_texture_new (CoglHandle full_texture, +_cogl_sub_texture_new (CoglHandle next_texture, int sub_x, int sub_y, int sub_width, int sub_height) { + CoglHandle full_texture; CoglSubTexture *sub_tex; CoglTexture *tex; - unsigned int full_width, full_height; + unsigned int next_width, next_height; - full_width = cogl_texture_get_width (full_texture); - full_height = cogl_texture_get_height (full_texture); + next_width = cogl_texture_get_width (next_texture); + next_height = cogl_texture_get_height (next_texture); /* The region must specify a non-zero subset of the full texture */ g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, COGL_INVALID_HANDLE); g_return_val_if_fail (sub_width > 0 && sub_height > 0, COGL_INVALID_HANDLE); - g_return_val_if_fail (sub_x + sub_width <= full_width, COGL_INVALID_HANDLE); - g_return_val_if_fail (sub_y + sub_height <= full_height, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_x + sub_width <= next_width, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_y + sub_height <= next_height, COGL_INVALID_HANDLE); sub_tex = g_new (CoglSubTexture, 1); tex = COGL_TEXTURE (sub_tex); tex->vtable = &cogl_sub_texture_vtable; + /* If the next texture is also a sub texture we can avoid one level + of indirection by referencing the full texture of that texture + instead. */ + if (cogl_is_sub_texture (next_texture)) + { + CoglSubTexture *other_sub_tex = + _cogl_sub_texture_pointer_from_handle (next_texture); + full_texture = other_sub_tex->full_texture; + sub_x += other_sub_tex->sub_x; + sub_y += other_sub_tex->sub_y; + } + else + full_texture = next_texture; + + sub_tex->next_texture = cogl_handle_ref (next_texture); sub_tex->full_texture = cogl_handle_ref (full_texture); sub_tex->sub_x = sub_x; diff --git a/clutter/cogl/cogl/cogl-texture.h b/clutter/cogl/cogl/cogl-texture.h index 4f9a40ce8..44644059b 100644 --- a/clutter/cogl/cogl/cogl-texture.h +++ b/clutter/cogl/cogl/cogl-texture.h @@ -348,6 +348,10 @@ cogl_texture_set_region (CoglHandle handle, * of the range [0,1] are used. They also do not work with * CoglVertexBuffers. * + * The sub texture will keep a reference to the full texture so you do + * not need to keep one separately if you only want to use the sub + * texture. + * * Return value: a #CoglHandle to the new texture. * * Since: 1.2 diff --git a/tests/conform/test-cogl-sub-texture.c b/tests/conform/test-cogl-sub-texture.c index ea0f6123a..49a278c49 100644 --- a/tests/conform/test-cogl-sub-texture.c +++ b/tests/conform/test-cogl-sub-texture.c @@ -67,10 +67,41 @@ create_source (void) data); } +static CoglHandle +create_test_texture (void) +{ + CoglHandle tex; + guint8 *data = g_malloc (256 * 256 * 4), *p = data; + int x, y; + + /* Create a texture that is 256x256 where the red component ranges + from 0->255 along the x axis and the green component ranges from + 0->255 along the y axis. The blue and alpha components are all + 255 */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = x; + *(p++) = y; + *(p++) = 255; + *(p++) = 255; + } + + tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + 256 * 4, + data); + + g_free (data); + + return tex; +} + static void draw_frame (TestState *state) { - CoglHandle sub_texture; + CoglHandle full_texture, sub_texture, sub_sub_texture; /* Create a sub texture of the bottom right quarter of the texture */ sub_texture = cogl_texture_new_from_sub_texture (state->tex, @@ -98,6 +129,19 @@ draw_frame (TestState *state) 0.0f, 0.0f, 2.0f, 1.0f); cogl_handle_unref (sub_texture); + + /* Create a sub texture of a sub texture */ + full_texture = create_test_texture (); + sub_texture = cogl_texture_new_from_sub_texture (full_texture, + 20, 10, 30, 20); + sub_sub_texture = cogl_texture_new_from_sub_texture (sub_texture, + 20, 10, 10, 10); + cogl_set_source_texture (sub_sub_texture); + cogl_rectangle (0.0f, SOURCE_SIZE * 2.0f, + 10.0f, SOURCE_SIZE * 2.0f + 10.0f); + cogl_handle_unref (sub_sub_texture); + cogl_handle_unref (sub_texture); + cogl_handle_unref (full_texture); } static gboolean @@ -131,37 +175,6 @@ validate_part (TestState *state, return pass; } -static CoglHandle -create_test_texture (void) -{ - CoglHandle tex; - guint8 *data = g_malloc (256 * 256 * 4), *p = data; - int x, y; - - /* Create a texture that is 256x256 where the red component ranges - from 0->255 along the x axis and the green component ranges from - 0->255 along the y axis. The blue and alpha components are all - 255 */ - for (y = 0; y < 256; y++) - for (x = 0; x < 256; x++) - { - *(p++) = x; - *(p++) = y; - *(p++) = 255; - *(p++) = 255; - } - - tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE, - COGL_PIXEL_FORMAT_RGBA_8888_PRE, - COGL_PIXEL_FORMAT_ANY, - 256 * 4, - data); - - g_free (data); - - return tex; -} - static guint8 * create_update_data (void) { @@ -207,6 +220,18 @@ validate_result (TestState *state) DIVISION_WIDTH, DIVISION_HEIGHT, corner_colors + division_num)); + /* Sub sub texture */ + p = texture_data = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), + 0, SOURCE_SIZE * 2, 10, 10); + for (y = 0; y < 10; y++) + for (x = 0; x < 10; x++) + { + g_assert (*(p++) == x + 40); + g_assert (*(p++) == y + 20); + p += 2; + } + g_free (texture_data); + /* Try reading back the texture data */ sub_texture = cogl_texture_new_from_sub_texture (state->tex, SOURCE_SIZE / 4,