#include #include #include "test-conform-common.h" #define BLOCK_SIZE 16 /* Number of pixels at the border of a block to skip when verifying */ #define TEST_INSET 1 static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; typedef enum { /* The first frame is drawn using clutter_cairo_texture_create. The second frame is an update of the first frame using clutter_cairo_texture_create_region. The states are stored like this because the cairo drawing is done on idle and the validation is done during paint and we need to synchronize the two */ TEST_BEFORE_DRAW_FIRST_FRAME, TEST_BEFORE_VALIDATE_FIRST_FRAME, TEST_BEFORE_DRAW_SECOND_FRAME, TEST_BEFORE_VALIDATE_SECOND_FRAME, TEST_DONE } TestProgress; typedef struct _TestState { ClutterActor *stage; ClutterActor *ct; guint frame; TestProgress progress; } TestState; static void validate_part (int block_x, int block_y, const ClutterColor *color) { guint8 data[BLOCK_SIZE * BLOCK_SIZE * 4]; int x, y; cogl_read_pixels (block_x * BLOCK_SIZE, block_y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); for (x = 0; x < BLOCK_SIZE - TEST_INSET * 2; x++) for (y = 0; y < BLOCK_SIZE - TEST_INSET * 2; y++) { const guint8 *p = data + ((x + TEST_INSET) * 4 + (y + TEST_INSET) * BLOCK_SIZE * 4); g_assert_cmpint (p[0], ==, color->red); g_assert_cmpint (p[1], ==, color->green); g_assert_cmpint (p[2], ==, color->blue); } } static void paint_cb (ClutterActor *actor, TestState *state) { static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff }; static const ClutterColor green = { 0x00, 0xff, 0x00, 0xff }; static const ClutterColor blue = { 0x00, 0x00, 0xff, 0xff }; if (state->frame++ < 2) return; switch (state->progress) { case TEST_BEFORE_DRAW_FIRST_FRAME: case TEST_BEFORE_DRAW_SECOND_FRAME: case TEST_DONE: /* Handled by the idle callback */ break; case TEST_BEFORE_VALIDATE_FIRST_FRAME: /* In the first frame there is a red rectangle next to a green rectangle */ validate_part (0, 0, &red); validate_part (1, 0, &green); state->progress = TEST_BEFORE_DRAW_SECOND_FRAME; break; case TEST_BEFORE_VALIDATE_SECOND_FRAME: /* The second frame is the same except the green rectangle is replaced with a blue one */ validate_part (0, 0, &red); validate_part (1, 0, &blue); state->progress = TEST_DONE; break; } } static gboolean idle_cb (gpointer data) { TestState *state = data; cairo_t *cr; if (state->frame < 2) clutter_actor_queue_redraw (CLUTTER_ACTOR (state->stage)); else switch (state->progress) { case TEST_BEFORE_DRAW_FIRST_FRAME: /* Draw two different colour rectangles */ cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (state->ct)); cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_save (cr); cairo_rectangle (cr, 0, 0, BLOCK_SIZE, BLOCK_SIZE); cairo_clip (cr); cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); cairo_paint (cr); cairo_restore (cr); cairo_rectangle (cr, BLOCK_SIZE, 0, BLOCK_SIZE, BLOCK_SIZE); cairo_clip (cr); cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); cairo_paint (cr); cairo_restore (cr); cairo_destroy (cr); state->progress = TEST_BEFORE_VALIDATE_FIRST_FRAME; break; case TEST_BEFORE_DRAW_SECOND_FRAME: /* Replace the second rectangle with a blue one */ cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (state->ct)); cairo_rectangle (cr, BLOCK_SIZE, 0, BLOCK_SIZE, BLOCK_SIZE); cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); cairo_fill (cr); cairo_destroy (cr); state->progress = TEST_BEFORE_VALIDATE_SECOND_FRAME; break; case TEST_BEFORE_VALIDATE_FIRST_FRAME: case TEST_BEFORE_VALIDATE_SECOND_FRAME: /* Handled by the paint callback */ break; case TEST_DONE: clutter_main_quit (); break; } return G_SOURCE_CONTINUE; } void test_clutter_cairo_texture (TestConformSimpleFixture *fixture, gconstpointer data) { TestState state; unsigned int idle_source; unsigned int paint_handler; state.frame = 0; state.stage = clutter_stage_new (); state.progress = TEST_BEFORE_DRAW_FIRST_FRAME; state.ct = clutter_cairo_texture_new (BLOCK_SIZE * 2, BLOCK_SIZE); clutter_container_add_actor (CLUTTER_CONTAINER (state.stage), state.ct); 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 = clutter_threads_add_idle (idle_cb, &state); paint_handler = g_signal_connect_after (state.stage, "paint", G_CALLBACK (paint_cb), &state); clutter_actor_show (state.stage); clutter_main (); g_signal_handler_disconnect (state.stage, paint_handler); g_source_remove (idle_source); if (g_test_verbose ()) g_print ("OK\n"); clutter_actor_destroy (state.stage); }