ff1d9cf090
Otherwise the paint handler will still be run for the subsequent tests. This ends up writing to the ‘state’ variable which used to be on the stack so it will end up corrupting some stack variable. This was causing test-cogl-premult to fail.
260 lines
7.8 KiB
C
260 lines
7.8 KiB
C
|
|
#include <clutter/clutter.h>
|
|
#include <cogl/cogl.h>
|
|
|
|
#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)
|
|
|
|
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;
|
|
} TestState;
|
|
|
|
static ClutterActor *
|
|
create_source (void)
|
|
{
|
|
int x, y;
|
|
ClutterActor *group = clutter_group_new ();
|
|
|
|
/* Create a group with a different coloured rectangle at each
|
|
corner */
|
|
for (y = 0; y < SOURCE_DIVISIONS_Y; y++)
|
|
for (x = 0; x < SOURCE_DIVISIONS_X; x++)
|
|
{
|
|
ClutterActor *rect = clutter_rectangle_new ();
|
|
clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT);
|
|
clutter_actor_set_position (rect,
|
|
DIVISION_WIDTH * x,
|
|
DIVISION_HEIGHT * y);
|
|
clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect),
|
|
corner_colors +
|
|
(y * SOURCE_DIVISIONS_X + x));
|
|
clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL);
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
static void
|
|
pre_paint_clip_cb (void)
|
|
{
|
|
/* Generate a clip path that clips out the top left division */
|
|
cogl_path_move_to (DIVISION_WIDTH, 0);
|
|
cogl_path_line_to (SOURCE_SIZE, 0);
|
|
cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE);
|
|
cogl_path_line_to (0, SOURCE_SIZE);
|
|
cogl_path_line_to (0, DIVISION_HEIGHT);
|
|
cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT);
|
|
cogl_path_close ();
|
|
cogl_clip_push_from_path ();
|
|
}
|
|
|
|
static void
|
|
post_paint_clip_cb (void)
|
|
{
|
|
cogl_clip_pop ();
|
|
}
|
|
|
|
static gboolean
|
|
validate_part (TestState *state,
|
|
int xpos, int ypos,
|
|
int clip_flags)
|
|
{
|
|
int x, y;
|
|
gboolean pass = TRUE;
|
|
|
|
/* Check whether the center of each division is the right color */
|
|
for (y = 0; y < SOURCE_DIVISIONS_Y; y++)
|
|
for (x = 0; x < SOURCE_DIVISIONS_X; x++)
|
|
{
|
|
guchar *pixels;
|
|
const ClutterColor *correct_color;
|
|
|
|
/* Read the center pixels of this division */
|
|
pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
|
|
x * DIVISION_WIDTH +
|
|
DIVISION_WIDTH / 2 + xpos,
|
|
y * DIVISION_HEIGHT +
|
|
DIVISION_HEIGHT / 2 + ypos,
|
|
1, 1);
|
|
|
|
/* If this division is clipped then it should be the stage
|
|
color */
|
|
if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x))))
|
|
correct_color = &stage_color;
|
|
else
|
|
/* Otherwise it should be the color for this division */
|
|
correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x;
|
|
|
|
if (pixels == NULL ||
|
|
pixels[0] != correct_color->red ||
|
|
pixels[1] != correct_color->green ||
|
|
pixels[2] != correct_color->blue)
|
|
pass = FALSE;
|
|
|
|
g_free (pixels);
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
static void
|
|
validate_result (TestState *state)
|
|
{
|
|
int ypos = 0;
|
|
|
|
if (g_test_verbose ())
|
|
g_print ("Testing onscreen clone...\n");
|
|
g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0));
|
|
ypos++;
|
|
|
|
#if 0 /* this doesn't work */
|
|
if (g_test_verbose ())
|
|
g_print ("Testing offscreen clone...\n");
|
|
g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0));
|
|
#endif
|
|
ypos++;
|
|
|
|
if (g_test_verbose ())
|
|
g_print ("Testing onscreen clone with rectangular clip...\n");
|
|
g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1));
|
|
ypos++;
|
|
|
|
if (g_test_verbose ())
|
|
g_print ("Testing onscreen clone with path clip...\n");
|
|
g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1));
|
|
ypos++;
|
|
|
|
/* 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 frame_num;
|
|
|
|
/* 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_texture_fbo (TestConformSimpleFixture *fixture,
|
|
gconstpointer data)
|
|
{
|
|
TestState state;
|
|
guint idle_source;
|
|
gulong paint_handler;
|
|
ClutterActor *actor;
|
|
int ypos = 0;
|
|
|
|
state.frame = 0;
|
|
|
|
state.stage = clutter_stage_get_default ();
|
|
|
|
clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
|
|
|
|
/* Onscreen source with clone next to it */
|
|
actor = create_source ();
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
|
|
actor = clutter_texture_new_from_actor (actor);
|
|
clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
ypos++;
|
|
|
|
/* Offscreen source with clone */
|
|
#if 0 /* this doesn't work */
|
|
actor = create_source ();
|
|
actor = clutter_texture_new_from_actor (actor);
|
|
clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
#endif
|
|
ypos++;
|
|
|
|
/* Source clipped to the top left division */
|
|
actor = create_source ();
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
|
|
clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT);
|
|
actor = clutter_texture_new_from_actor (actor);
|
|
clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
ypos++;
|
|
|
|
/* Source clipped to everything but top left division using a
|
|
path */
|
|
actor = create_source ();
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
|
|
g_signal_connect (actor, "paint",
|
|
G_CALLBACK (pre_paint_clip_cb), NULL);
|
|
g_signal_connect_after (actor, "paint",
|
|
G_CALLBACK (post_paint_clip_cb), NULL);
|
|
actor = clutter_texture_new_from_actor (actor);
|
|
clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
|
|
clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
|
|
ypos++;
|
|
|
|
/* 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_signal_handler_disconnect (state.stage, paint_handler);
|
|
|
|
g_source_remove (idle_source);
|
|
|
|
/* 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");
|
|
}
|
|
|