/*
 * Copyright © 2004 Eric Anholt
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Eric Anholt not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Eric Anholt makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "rendercheck.h"

static XRenderPictFormat **format_list;
static int nformats;
static int argb32index;

/* Note: changing the order of these colors may disrupt tests that depend on
 * specific colors.  Just add to the end if you need.  These are
 * not premultiplied, but will be in main().
 */
color4d colors[] = {
	{1.0, 1.0, 1.0, 1.0},
	{1.0, 0, 0, 1.0},
	{0, 1.0, 0, 1.0},
	{0, 0, 1.0, 1.0},
	{0.5, 0, 0, .5},
};

/* Convenience pointers to 1x1 repeating colors */
picture_info *argb32white, *argb32red, *argb32green, *argb32blue;

int num_colors = sizeof(colors) / sizeof(colors[0]);

struct op_info ops[] = {
	{PictOpClear, "Clear"},
	{PictOpSrc, "Src"},
	{PictOpDst, "Dst"},
	{PictOpOver, "Over"},
	{PictOpOverReverse, "OverReverse"},
	{PictOpIn, "In"},
	{PictOpInReverse, "InReverse"},
	{PictOpOut, "Out"},
	{PictOpOutReverse, "OutReverse"},
	{PictOpAtop, "Atop"},
	{PictOpAtopReverse, "AtopReverse"},
	{PictOpXor, "Xor"},
	{PictOpAdd, "Add"},
	{PictOpSaturate, "Saturate"},
	{PictOpDisjointClear, "DisjointClear"},
	{PictOpDisjointSrc, "DisjointSrc"},
	{PictOpDisjointDst, "DisjointDst"},
	{PictOpDisjointOver, "DisjointOver"},
	{PictOpDisjointOverReverse, "DisjointOverReverse"},
	{PictOpDisjointIn, "DisjointIn"},
	{PictOpDisjointInReverse, "DisjointInReverse"},
	{PictOpDisjointOut, "DisjointOut"},
	{PictOpDisjointOutReverse, "DisjointOutReverse"},
	{PictOpDisjointAtop, "DisjointAtop"},
	{PictOpDisjointAtopReverse, "DisjointAtopReverse"},
	{PictOpDisjointXor, "DisjointXor"},
	{PictOpConjointClear, "ConjointClear"},
	{PictOpConjointSrc, "ConjointSrc"},
	{PictOpConjointDst, "ConjointDst"},
	{PictOpConjointOver, "ConjointOver"},
	{PictOpConjointOverReverse, "ConjointOverReverse"},
	{PictOpConjointIn, "ConjointIn"},
	{PictOpConjointInReverse, "ConjointInReverse"},
	{PictOpConjointOut, "ConjointOut"},
	{PictOpConjointOutReverse, "ConjointOutReverse"},
	{PictOpConjointAtop, "ConjointAtop"},
	{PictOpConjointAtopReverse, "ConjointAtopReverse"},
	{PictOpConjointXor, "ConjointXor"},
};

int num_ops = sizeof(ops) / sizeof(ops[0]);

#define round_pix(pix, mask) \
	((double)((int)(pix * (mask ) + .5)) / (double)(mask))

void
color_correct(picture_info *pi, color4d *color)
{
	if (!pi->format->direct.redMask) {
		color->r = 0.0;
		color->g = 0.0;
		color->b = 0.0;
	} else {
		color->r = round_pix(color->r, pi->format->direct.redMask);
		color->g = round_pix(color->g, pi->format->direct.greenMask);
		color->b = round_pix(color->b, pi->format->direct.blueMask);
	}
	if (!pi->format->direct.alphaMask)
		color->a = 1.0;
	else
		color->a = round_pix(color->a, pi->format->direct.alphaMask);
}

void
get_pixel_from_image(XImage *image,
		     const picture_info *pi,
		     int x, int y,
		     color4d *color)
{
	unsigned long val;
	unsigned long rm, gm, bm, am;
	XRenderDirectFormat *layout = &pi->format->direct;

	val = XGetPixel(image, x, y);

	rm = (unsigned long)layout->redMask << layout->red;
	gm = (unsigned long)layout->greenMask << layout->green;
	bm = (unsigned long)layout->blueMask << layout->blue;
	am = (unsigned long)layout->alphaMask << layout->alpha;
	if (am != 0)
		color->a = (double)(val & am) / (double)am;
	else
		color->a = 1.0;
	if (rm != 0) {
		color->r = (double)(val & rm) / (double)rm;
		color->g = (double)(val & gm) / (double)gm;
		color->b = (double)(val & bm) / (double)bm;
	} else {
		color->r = 0.0;
		color->g = 0.0;
		color->b = 0.0;
	}
}

void
get_pixel(Display *dpy,
	  const picture_info *pi,
	  int x, int y,
	  color4d *color)
{
	XImage *image;

	image = XGetImage(dpy, pi->d, x, y, 1, 1, 0xffffffff, ZPixmap);
	get_pixel_from_image(image, pi, 0, 0, color);
	XDestroyImage(image);
}

void
accuracy(XRenderDirectFormat *result,
	 const XRenderDirectFormat *a,
	 const XRenderDirectFormat *b)
{
    result->redMask = min(a->redMask, b->redMask);
    result->greenMask = min(a->greenMask, b->greenMask);
    result->blueMask = min(a->blueMask, b->blueMask);
    result->alphaMask = min(a->alphaMask, b->alphaMask);
}

double
eval_diff(const XRenderDirectFormat *format,
	  const color4d *expected,
	  const color4d *test)
{
	double rdiff, gdiff, bdiff, adiff;

	rdiff = fabs(test->r - expected->r) * format->redMask;
	bdiff = fabs(test->g - expected->g) * format->greenMask;
	gdiff = fabs(test->b - expected->b) * format->blueMask;
	adiff = fabs(test->a - expected->a) * format->alphaMask;

#if 0
	rdiff = log2(1.0 + rdiff);
	gdiff = log2(1.0 + gdiff);
	bdiff = log2(1.0 + bdiff);
	adiff = log2(1.0 + adiff);
#endif

	return max(max(max(rdiff, gdiff), bdiff), adiff);
}

void print_fail(const char *name,
		const color4d *expected,
		const color4d *test,
		int x, int y,
		double d)
{
    printf("%s test error of %.4f at (%d, %d) --\n"
	   "           R     G     B     A\n"
	   "got:       %.3f %.3f %.3f %.3f\n"
	   "expected:  %.3f %.3f %.3f %.3f\n",
	   name, d, x, y,
	   test->r, test->g, test->b, test->a,
	   expected->r, expected->g, expected->b, expected->a);
}

void print_pass(const char *name,
		const color4d *expected,
		int x, int y,
		double d)
{
    printf("%s test succeeded at (%d, %d) with %.4f: "
	   "%.2f %.2f %.2f %.2f\n",
	   name, x, y, d,
	   expected->r, expected->g, expected->b, expected->a);
}

void
argb_fill(Display *dpy, picture_info *p, int x, int y, int w, int h, float a,
    float r, float g, float b)
{
	XRenderColor rendercolor;

	rendercolor.red = r * 65535;
	rendercolor.green = g * 65535;
	rendercolor.blue = b * 65535;
	rendercolor.alpha = a * 65535;

	XRenderFillRectangle(dpy, PictOpSrc, p->pict, &rendercolor, x, y, w, h);
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

/* Create a set of direct format XRenderPictFormats for later use.  This lets
 * us get more formats than just the standard required set, and lets us attach
 * names to them.
 */
static void
create_formats_list(Display *dpy)
{
    int i;
    int nformats_allocated = 5;
    XRenderPictFormat templ;

    memset(&templ, 0, sizeof(templ));
    templ.type = PictTypeDirect;

    format_list = malloc(sizeof(XRenderPictFormat *) * nformats_allocated);
    if (format_list == NULL)
	errx(1, "malloc error");
    nformats = 0;

    argb32index = -1;
    for (i = 0; ; i++) {
	char *name;
	int alphabits, redbits;

	if (nformats + 1 == nformats_allocated) {
	    nformats_allocated *= 2;
	    format_list = realloc(format_list, sizeof(XRenderPictFormat *) *
		nformats_allocated);
	    if (format_list == NULL)
		errx(1, "realloc error");
	}

	format_list[nformats] = XRenderFindFormat(dpy, PictFormatType, &templ,
	    i);
	if (format_list[nformats] == NULL)
	    break;

	alphabits = bit_count(format_list[nformats]->direct.alphaMask);
	redbits = bit_count(format_list[nformats]->direct.redMask);

	/* Our testing code isn't all that hot, so don't bother trying at
	 * the low depths yet.
	 */
	if ((redbits >= 1 && redbits <= 4) ||
	    (alphabits >= 1 && alphabits <= 4))
	{
	    continue;
	}

	describe_format(&name, NULL, format_list[nformats]);

	if (format_whitelist_len != 0) {
	    Bool ok = FALSE;
	    int j;

	    for (j = 0; j < format_whitelist_len; j++) {
		if (strcmp(format_whitelist[j], name) == 0) {
		    ok = TRUE;
		    break;
		}
	    }
	    if (!ok) {
		printf("Ignoring server-supported format: %s\n", name);
		continue;
	    }
	}

	if (format_list[nformats] == XRenderFindStandardFormat(dpy,
	    PictStandardARGB32))
	{
	    argb32index = nformats;
	}

	printf("Found server-supported format: %s\n", name);

	nformats++;
    }
    if (argb32index == -1) {
	errx(1, "required ARGB32 format not found");
    }
}

Bool
do_tests(Display *dpy, picture_info *win)
{
	int i, j, src;
	int num_dests;
	picture_info *dests, *pictures_1x1, *pictures_10x10, picture_3x3, *pictures_solid;
	int success_mask = 0, tests_passed = 0, tests_total = 0;
	int num_tests;
	int *test_ops;
	const picture_info **test_src, **test_mask, **test_dst;
	int num_test_ops = 0;
	int num_test_src = 0;
	int num_test_mask = 0;
	int num_test_dst = 0;

	create_formats_list(dpy);

	num_dests = nformats;
	dests = (picture_info *)malloc(num_dests * sizeof(dests[0]));
	if (dests == NULL)
		errx(1, "malloc error");

	for (i = 0; i < num_dests; i++) {
		dests[i].format = format_list[i];
		dests[i].d = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    win_width, win_height, dests[i].format->depth);
		dests[i].pict = XRenderCreatePicture(dpy, dests[i].d,
		    dests[i].format, 0, NULL);

		describe_format(&dests[i].name, NULL, dests[i].format);
	}

	pictures_1x1 = (picture_info *)malloc(num_colors * nformats *
	    sizeof(picture_info));
	if (pictures_1x1 == NULL)
		errx(1, "malloc error");

	for (i = 0; i < num_colors * nformats; i++) {
		XRenderPictureAttributes pa;
		color4d *c = &colors[i / nformats];

		/* The standard PictFormat numbers go from 0 to 4 */
		pictures_1x1[i].format = format_list[i % nformats];
		pictures_1x1[i].d = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    1, 1, pictures_1x1[i].format->depth);
		pa.repeat = TRUE;
		pictures_1x1[i].pict = XRenderCreatePicture(dpy,
		    pictures_1x1[i].d, pictures_1x1[i].format, CPRepeat, &pa);

		describe_format(&pictures_1x1[i].name, "1x1R ",
		    pictures_1x1[i].format);

		argb_fill(dpy, &pictures_1x1[i], 0, 0, 1, 1,
		    c->a, c->r, c->g, c->b);

		pictures_1x1[i].color = *c;
		color_correct(&pictures_1x1[i], &pictures_1x1[i].color);
	}
	argb32white = &pictures_1x1[0 * nformats + argb32index];
	argb32red = &pictures_1x1[1 * nformats + argb32index];
	argb32green = &pictures_1x1[2 * nformats + argb32index];
	argb32blue = &pictures_1x1[3 * nformats + argb32index];

	pictures_10x10 = (picture_info *)malloc(num_colors * nformats *
	    sizeof(picture_info));
	if (pictures_10x10 == NULL)
		errx(1, "malloc error");

	for (i = 0; i < num_colors * nformats; i++) {
		color4d *c = &colors[i / nformats];

		/* The standard PictFormat numbers go from 0 to 4 */
		pictures_10x10[i].format = format_list[i % nformats];
		pictures_10x10[i].d = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    10, 10, pictures_10x10[i].format->depth);
		pictures_10x10[i].pict = XRenderCreatePicture(dpy,
		    pictures_10x10[i].d, pictures_10x10[i].format, 0, NULL);

		describe_format(&pictures_10x10[i].name, "10x10 ",
		    pictures_10x10[i].format);

		argb_fill(dpy, &pictures_10x10[i], 0, 0, 10, 10,
		    c->a, c->r, c->g, c->b);

		pictures_10x10[i].color = *c;
		color_correct(&pictures_10x10[i], &pictures_10x10[i].color);
	}

	picture_3x3.d = XCreatePixmap(dpy, DefaultRootWindow(dpy), 3, 3, 32);
	picture_3x3.format = XRenderFindStandardFormat(dpy, PictStandardARGB32);
	picture_3x3.pict = XRenderCreatePicture(dpy, picture_3x3.d,
	    picture_3x3.format, 0, NULL);
	picture_3x3.name = (char *)"3x3 sample picture";
	for (i = 0; i < 9; i++) {
		int x = i % 3;
		int y = i / 3;
		color4d *c = &colors[i % num_colors];

		argb_fill(dpy, &picture_3x3, x, y, 1, 1, c->a, c->r, c->g, c->b);
	}

        pictures_solid = malloc(num_colors * sizeof(picture_info));
	for (i = 0; i < num_colors; i++) {
            XRenderColor c;
            pictures_solid[i].color = colors[i];
            c.alpha = (int)(colors[i].a*65535);
            c.red = (int)(colors[i].r*65535);
            c.green = (int)(colors[i].g*65535);
            c.blue = (int)(colors[i].b*65535);
            pictures_solid[i].pict = XRenderCreateSolidFill(dpy, &c);
            pictures_solid[i].format = format_list[argb32index];
            pictures_solid[i].name = (char *)"Solid";
        }

#define RECORD_RESULTS()					\
do {								\
	group_ok = group_ok && ok;				\
	if (ok)							\
		tests_passed++;					\
	tests_total++;						\
} while (0)

	num_tests = num_colors * nformats;

	test_ops = malloc(sizeof(int)*num_ops);
	test_src = malloc(sizeof(picture_info*)*(2*num_tests+num_colors));
	test_mask = malloc(sizeof(picture_info*)*2*num_tests);
	test_dst = malloc(sizeof(picture_info*)*(num_tests+num_colors));

	for (i = 0; i < num_ops; i++) {
	    if (ops[i].disabled)
		continue;

	    test_ops[num_test_ops++] = i;
	}

	for (i = 0; i < num_tests; i++) {
	    test_src[num_test_src++] = &pictures_1x1[i];
	    test_src[num_test_src++] = &pictures_10x10[i];
	    test_mask[num_test_mask++] = &pictures_1x1[i];
	    test_mask[num_test_mask++] = &pictures_10x10[i];
	    test_dst[num_test_dst++] = &pictures_1x1[i];
	}
	for (i = 0; i < num_colors; i++) {
	    test_src[num_test_src++] = &pictures_solid[i];
	    test_dst[num_test_dst++] = &pictures_solid[i];
	}

	if (enabled_tests & TEST_FILL) {
		Bool ok, group_ok = TRUE;

		printf("Beginning testing of filling of 1x1R pictures\n");
		for (i = 0; i < num_tests; i++) {
			ok = fill_test(dpy, win, &pictures_1x1[i]);
			RECORD_RESULTS();
		}

		printf("Beginning testing of filling of 10x10 pictures\n");
		for (i = 0; i < num_tests; i++) {
			ok = fill_test(dpy, win, &pictures_10x10[i]);
			RECORD_RESULTS();
		}
		if (group_ok)
			success_mask |= TEST_FILL;
	}

	if (enabled_tests & TEST_DSTCOORDS) {
		Bool ok, group_ok = TRUE;

		printf("Beginning dest coords test\n");
		for (i = 0; i < 2; i++) {
			ok = dstcoords_test(dpy, win,
			    i == 0 ? PictOpSrc : PictOpOver, win,
			    argb32white, argb32red);
			RECORD_RESULTS();
		}
		if (group_ok)
			success_mask |= TEST_DSTCOORDS;
	}

	if (enabled_tests & TEST_SRCCOORDS) {
		Bool ok, group_ok = TRUE;

		printf("Beginning src coords test\n");
		ok = srccoords_test(dpy, win, argb32white, FALSE);
		RECORD_RESULTS();
		if (group_ok)
			success_mask |= TEST_SRCCOORDS;
	}

	if (enabled_tests & TEST_MASKCOORDS) {
		Bool ok, group_ok = TRUE;

		printf("Beginning mask coords test\n");
		ok = srccoords_test(dpy, win, argb32white, TRUE);
		RECORD_RESULTS();
		if (group_ok)
			success_mask |= TEST_MASKCOORDS;
	}

	if (enabled_tests & TEST_TSRCCOORDS) {
		Bool ok, group_ok = TRUE;

		printf("Beginning transformed src coords test\n");
		ok = trans_coords_test(dpy, win, argb32white, FALSE);
		RECORD_RESULTS();

		printf("Beginning transformed src coords test 2\n");
		ok = trans_srccoords_test_2(dpy, win, argb32white, FALSE);
		RECORD_RESULTS();
		if (group_ok)
			success_mask |= TEST_TSRCCOORDS;
	}

	if (enabled_tests & TEST_TMASKCOORDS) {
		Bool ok, group_ok = TRUE;

		printf("Beginning transformed mask coords test\n");
		ok = trans_coords_test(dpy, win, argb32white, TRUE);
		RECORD_RESULTS();

		printf("Beginning transformed mask coords test 2\n");
		ok = trans_srccoords_test_2(dpy, win, argb32white, TRUE);
		RECORD_RESULTS();

		if (group_ok)
			success_mask |= TEST_TMASKCOORDS;
	}

	if (enabled_tests & TEST_BLEND) {
		Bool ok, group_ok = TRUE;

		for (j = 0; j <= num_dests; j++) {
		    picture_info *pi;

		    if (j != num_dests)
			pi = &dests[j];
		    else
			pi = win;

		    printf("Beginning blend test on %s\n", pi->name);

		    ok = blend_test(dpy, win, pi,
				    test_ops, num_test_ops,
				    test_src, num_test_src,
				    test_dst, num_test_dst);
		    RECORD_RESULTS();
		}
		if (group_ok)
			success_mask |= TEST_BLEND;
	}

	if (enabled_tests & TEST_COMPOSITE) {
		Bool ok, group_ok = TRUE;

		for (j = 0; j <= num_dests; j++) {
		    picture_info *pi;

		    if (j != num_dests)
			pi = &dests[j];
		    else
			pi = win;

		    printf("Beginning composite mask test on %s\n", pi->name);

		    ok = composite_test(dpy, win, pi,
					test_ops, num_test_ops,
					test_src, num_test_src,
					test_mask, num_test_mask,
					test_dst, num_test_dst,
					FALSE);
		    RECORD_RESULTS();
		}
		if (group_ok)
			success_mask |= TEST_COMPOSITE;
	}

	if (enabled_tests & TEST_CACOMPOSITE) {
		Bool ok, group_ok = TRUE;

		for (j = 0; j <= num_dests; j++) {
		    picture_info *pi;

		    if (j != num_dests)
			pi = &dests[j];
		    else
			pi = win;

		    printf("Beginning composite CA mask test on %s\n", pi->name);

		    ok = composite_test(dpy, win, pi,
					test_ops, num_test_ops,
					test_src, num_test_src,
					test_mask, num_test_mask,
					test_dst, num_test_dst,
					TRUE);
		    RECORD_RESULTS();
		}
		if (group_ok)
			success_mask |= TEST_CACOMPOSITE;
	}

        if (enabled_tests & TEST_GRADIENTS) {
	    Bool ok, group_ok = TRUE;

	    printf("Beginning render to linear gradient test\n");
	    ok = render_to_gradient_test(dpy, &pictures_1x1[0]);
	    RECORD_RESULTS();

            for (i = 0; i < num_ops; i++) {
		if (ops[i].disabled)
		    continue;

                for (j = 0; j <= num_dests; j++) {
                    picture_info *pi;
                    
                    if (j != num_dests)
                        pi = &dests[j];
                    else
                        pi = win;
                    printf("Beginning %s linear gradient test on %s\n",
                           ops[i].name, pi->name);
                    
                    for (src = 0; src < num_tests; src++) {
			ok = linear_gradient_test(dpy, win, pi, i,
						  &pictures_1x1[src]);
			RECORD_RESULTS();
                    }
                }
            }
	    if (group_ok)
		 success_mask |= TEST_GRADIENTS;
        }

        if (enabled_tests & TEST_REPEAT) {
	    Bool ok, group_ok = TRUE;

            for (i = 0; i < num_ops; i++) {
		if (ops[i].disabled)
		    continue;

                for (j = 0; j <= num_dests; j++) {
                    picture_info *pi;
                    
                    if (j != num_dests)
                        pi = &dests[j];
                    else
                        pi = win;
                    printf("Beginning %s src repeat test on %s\n",
                           ops[i].name, pi->name);
		    /* Test with white dest, and generated repeating src
		     * consisting of colors 1 and 2 (r, g).
		     */
		    ok = repeat_test(dpy, win, pi, i, argb32white, argb32red,
		        argb32green, FALSE);
		    RECORD_RESULTS();

                    printf("Beginning %s mask repeat test on %s\n",
                           ops[i].name, pi->name);
		    /* Test with white dest, translucent red src, and generated
		     * repeating mask consisting of colors 1 and 2 (r, g).
		     */
		    ok = repeat_test(dpy, win, pi, i, argb32white, argb32red,
		        argb32green, TRUE);
		    RECORD_RESULTS();
                }
            }
	    if (group_ok)
		success_mask |= TEST_REPEAT;
        }

	if (enabled_tests & TEST_TRIANGLES) {
	    Bool ok, group_ok = TRUE;

	    for (i = 0; i < num_ops; i++) {
		if (ops[i].disabled)
		    continue;

		for (j = 0; j <= num_dests; j++) {
			picture_info *pi;

			if (j != num_dests)
			    pi = &dests[j];
			else
			    pi = win;

			printf("Beginning %s Triangles test on %s\n",
			    ops[i].name, pi->name);
			ok = triangles_test(dpy, win, pi, i,
			    argb32red, argb32white);
			RECORD_RESULTS();

			printf("Beginning %s TriStrip test on %s\n",
			    ops[i].name, pi->name);
			ok = tristrip_test(dpy, win, pi, i,
			    argb32red, argb32white);
			RECORD_RESULTS();

			printf("Beginning %s TriFan test on %s\n",
			    ops[i].name, pi->name);
			ok = trifan_test(dpy, win, pi, i,
			    argb32red, argb32white);
			RECORD_RESULTS();
		}
	    }
	    if (group_ok)
		success_mask |= TEST_TRIANGLES;
	}

        if (enabled_tests & TEST_BUG7366) {
	    Bool ok, group_ok = TRUE;

	    ok = bug7366_test(dpy);
	    RECORD_RESULTS();

	    if (group_ok)
		success_mask |= TEST_BUG7366;
	}

        if (enabled_tests & TEST_GTK_ARGB_XBGR) {
		Bool ok, group_ok = TRUE;

		ok = gtk_argb_xbgr_test(dpy);
		RECORD_RESULTS();

		if (group_ok)
			success_mask |= TEST_GTK_ARGB_XBGR;
	}

        if (enabled_tests & TEST_LIBREOFFICE_XRGB) {
		Bool ok, group_ok = TRUE;

		ok = libreoffice_xrgb_test(dpy, FALSE);
		RECORD_RESULTS();

		ok = libreoffice_xrgb_test(dpy, TRUE);
		RECORD_RESULTS();

		if (group_ok)
			success_mask |= TEST_LIBREOFFICE_XRGB;
	}

	free(test_ops);
	free(test_src);
	free(test_mask);
	free(test_dst);

	printf("%d tests passed of %d total\n", tests_passed, tests_total);
	printf("Successful Groups:\n");
	print_tests(stdout, success_mask);

	return tests_passed == tests_total;
}

/**
 * \brief copies the contents of a picture to the window.
 *
 * This is used in tests so that the user sees flashing colors indicating that
 * rendercheck is really doing things.  The minimalrendering commandline option
 * indicates that this behavior should be disabled.
 */
void
copy_pict_to_win(Display *dpy, picture_info *pict, picture_info *win,
    int width, int height)
{
	if (pict == win || minimalrendering)
		return;

	XRenderComposite(dpy, PictOpSrc, pict->pict, 0, win->pict, 0, 0,
	    0, 0, 0, 0, width, height);
}
