From 27c4eb483d0ce219d80000613357c20fc25724e3 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 27 Nov 2009 16:40:31 +0000 Subject: [PATCH] cogl: Add a conformance test for sub textures This tests creating a sub texture from a larger texture using various different texture coordinates. It also tries to read back the texture data using cogl_texture_get_data. --- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-sub-texture.c | 388 ++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 3 files changed, 390 insertions(+) create mode 100644 tests/conform/test-cogl-sub-texture.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index b1bb81ec3..f61682103 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -39,6 +39,7 @@ test_conformance_SOURCES = \ test-group.c \ test-actor-size.c \ test-texture-fbo.c \ + test-cogl-sub-texture.c \ test-script-parser.c \ $(NULL) diff --git a/tests/conform/test-cogl-sub-texture.c b/tests/conform/test-cogl-sub-texture.c new file mode 100644 index 000000000..45449b9e5 --- /dev/null +++ b/tests/conform/test-cogl-sub-texture.c @@ -0,0 +1,388 @@ + +#include +#include +#include + +#include "test-conform-common.h" + +#define SOURCE_SIZE 32 +#define SOURCE_DIVISIONS_X 2 +#define SOURCE_DIVISIONS_Y 2 +#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) +#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) + +#define TEST_INSET 1 + +static const ClutterColor +corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = + { + { 0xff, 0x00, 0x00, 0xff }, /* red top left */ + { 0x00, 0xff, 0x00, 0xff }, /* green top right */ + { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */ + { 0xff, 0x00, 0xff, 0xff } /* purple bottom right */ + }; + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; + guint frame; + + CoglHandle tex; +} TestState; + +static CoglHandle +create_source (void) +{ + int dx, dy; + guchar *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4); + + /* Create a texture with a different coloured rectangle at each + corner */ + for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++) + for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++) + { + guchar *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 + + dx * DIVISION_WIDTH * 4); + int x, y; + + for (y = 0; y < DIVISION_HEIGHT; y++) + { + for (x = 0; x < DIVISION_WIDTH; x++) + { + memcpy (p, corner_colors + dx + dy * SOURCE_DIVISIONS_X, 4); + p += 4; + } + + p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4; + } + } + + return cogl_texture_new_from_data (SOURCE_SIZE, SOURCE_SIZE, + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888, + COGL_PIXEL_FORMAT_ANY, + SOURCE_SIZE * 4, + data); +} + +static void +draw_frame (TestState *state) +{ + CoglHandle sub_texture; + + /* Create a sub texture of the bottom right quarter of the texture */ + sub_texture = cogl_texture_new_from_sub_texture (state->tex, + 1.0f / SOURCE_DIVISIONS_X, + 1.0f / SOURCE_DIVISIONS_Y, + 1.0f, 1.0f); + + /* Paint it */ + cogl_set_source_texture (sub_texture); + cogl_rectangle (0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT); + + cogl_handle_unref (sub_texture); + + /* Repeat a sub texture of the top half of the full texture */ + sub_texture = cogl_texture_new_from_sub_texture (state->tex, + 0.0f, 0.0f, 1.0f, 0.5f); + cogl_set_source_texture (sub_texture); + cogl_rectangle_with_texture_coords (0.0f, SOURCE_SIZE, + SOURCE_SIZE * 2.0f, SOURCE_SIZE * 1.5f, + 0.0f, 0.0f, + 2.0f, 1.0f); + cogl_handle_unref (sub_texture); + + /* Create a texture that repeats the source texture twice */ + sub_texture = cogl_texture_new_from_sub_texture (state->tex, + 0.0f, 0.0f, 2.0f, 2.0f); + cogl_set_source_texture (sub_texture); + cogl_rectangle (0.0f, SOURCE_SIZE * 2, SOURCE_SIZE * 2, SOURCE_SIZE * 4); + cogl_handle_unref (sub_texture); +} + +static gboolean +validate_part (TestState *state, + int xpos, int ypos, + int width, int height, + const ClutterColor *color) +{ + int x, y; + gboolean pass = TRUE; + guchar *pixels, *p; + + p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), + xpos + TEST_INSET, + ypos + TEST_INSET, + width - TEST_INSET - 2, + height - TEST_INSET - 2); + + /* Check whether the center of each division is the right color */ + for (y = 0; y < height - TEST_INSET - 2; y++) + for (x = 0; x < width - TEST_INSET - 2; x++) + { + if (p[0] != color->red || + p[1] != color->green || + p[2] != color->blue) + pass = FALSE; + + p += 4; + } + + 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) +{ + guint8 *data = g_malloc (256 * 256 * 4), *p = data; + int x, y; + + /* Create some image data that is 256x256 where the blue component + ranges from 0->255 along the x axis and the alpha component + ranges from 0->255 along the y axis. The red and green components + are all zero */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + *(p++) = 0; + *(p++) = 0; + *(p++) = x; + *(p++) = y; + } + + return data; +} + +static void +validate_result (TestState *state) +{ + int i, division_num, x, y; + CoglHandle sub_texture, test_tex; + guchar *texture_data, *p; + gint tex_width, tex_height; + + /* Sub texture of the bottom right corner of the texture */ + g_assert (validate_part (state, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT, + corner_colors + + (SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X + + SOURCE_DIVISIONS_X - 1)); + + /* Sub texture of the top half repeated horizontally */ + for (i = 0; i < 2; i++) + for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++) + g_assert (validate_part (state, + i * SOURCE_SIZE + division_num * DIVISION_WIDTH, + SOURCE_SIZE, + DIVISION_WIDTH, DIVISION_HEIGHT, + corner_colors + division_num)); + + /* Sub texture that repeats the texture (the opposite of a sub-texture?) */ + for (y = 0; y < SOURCE_DIVISIONS_Y * 2; y++) + for (x = 0; x < SOURCE_DIVISIONS_X * 2; x++) + { + guint color_num = (y % SOURCE_DIVISIONS_Y * SOURCE_DIVISIONS_X + + x % SOURCE_DIVISIONS_X); + g_assert (validate_part (state, + x * DIVISION_WIDTH, + y * DIVISION_WIDTH + SOURCE_SIZE * 2, + DIVISION_WIDTH, DIVISION_HEIGHT, + corner_colors + color_num)); + } + + /* Try reading back the texture data */ + sub_texture = cogl_texture_new_from_sub_texture (state->tex, + 0.25f, 0.25f, 0.75f, 0.75f); + tex_width = cogl_texture_get_width (sub_texture); + tex_height = cogl_texture_get_height (sub_texture); + p = texture_data = g_malloc (tex_width * tex_height * 4); + cogl_texture_get_data (sub_texture, COGL_PIXEL_FORMAT_RGBA_8888, + tex_width * 4, + texture_data); + for (y = 0; y < tex_height; y++) + for (x = 0; x < tex_width; x++) + { + int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) / + DIVISION_WIDTH); + int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) / + DIVISION_HEIGHT); + const ClutterColor *color = (corner_colors + div_x + + div_y * SOURCE_DIVISIONS_X); + g_assert (p[0] == color->red); + g_assert (p[1] == color->green); + g_assert (p[2] == color->blue); + p += 4; + } + g_free (texture_data); + cogl_handle_unref (sub_texture); + + /* Try reading back the repeated texture data */ + sub_texture = cogl_texture_new_from_sub_texture (state->tex, + 0.0f, 0.0f, 2.0f, 2.0f); + tex_width = cogl_texture_get_width (sub_texture); + tex_height = cogl_texture_get_height (sub_texture); + p = texture_data = g_malloc (tex_width * tex_height * 4); + cogl_texture_get_data (sub_texture, COGL_PIXEL_FORMAT_RGBA_8888, + tex_width * 4, + texture_data); + for (y = 0; y < tex_height; y++) + for (x = 0; x < tex_width; x++) + { + int div_x = x / DIVISION_WIDTH % SOURCE_DIVISIONS_X; + int div_y = y / DIVISION_HEIGHT % SOURCE_DIVISIONS_Y; + const ClutterColor *color = (corner_colors + div_x + + div_y * SOURCE_DIVISIONS_X); + g_assert (p[0] == color->red); + g_assert (p[1] == color->green); + g_assert (p[2] == color->blue); + p += 4; + } + g_free (texture_data); + cogl_handle_unref (sub_texture); + + /* Create a 256x256 test texture */ + test_tex = create_test_texture (); + /* Create a sub texture the views the bottom right and top left of + the texture by wrapping around */ + sub_texture = cogl_texture_new_from_sub_texture (test_tex, + 0.5f, 0.5f, 1.5f, 1.5f); + /* Update the center of the sub texture */ + texture_data = create_update_data (); + cogl_texture_set_region (sub_texture, 0, 0, 64, 64, 128, 128, 256, 256, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4, + texture_data); + g_free (texture_data); + cogl_handle_unref (sub_texture); + /* Get the texture data */ + p = texture_data = g_malloc (256 * 256 * 4); + cogl_texture_get_data (test_tex, COGL_PIXEL_FORMAT_RGBA_8888, + 256 * 4, texture_data); + + /* Verify the texture data */ + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) + { + /* If we're in the center of the subregion.. */ + if ((x < 64 || x >= 192) && (y < 64 || y >= 192)) + { + g_assert ((*p++) == 0); + g_assert ((*p++) == 0); + g_assert ((*p++) == ((x + 64) & 0xff)); + g_assert ((*p++) == ((y + 64) & 0xff)); + } + else + { + g_assert ((*p++) == x); + g_assert ((*p++) == y); + g_assert ((*p++) == 255); + g_assert ((*p++) == 255); + } + } + g_free (texture_data); + cogl_handle_unref (test_tex); + + /* Comment this out to see what the test paints */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int frame_num; + + draw_frame (state); + + /* 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; +} + +void +test_cogl_sub_texture (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + guint idle_source; + guint paint_handler; + + state.frame = 0; + + state.stage = clutter_stage_get_default (); + state.tex = create_source (); + + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* 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, state.stage); + + paint_handler = g_signal_connect_after (state.stage, "paint", + G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_source_remove (idle_source); + g_signal_handler_disconnect (state.stage, paint_handler); + + cogl_handle_unref (state.tex); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (state.stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + 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 be3929b33..4a541487e 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -145,6 +145,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/opacity", test_paint_opacity); TEST_CONFORM_SIMPLE ("/texture", test_texture_fbo); + TEST_CONFORM_SIMPLE ("/texture", test_cogl_sub_texture); TEST_CONFORM_SIMPLE ("/path", test_path);