537 lines
12 KiB
C
537 lines
12 KiB
C
/*
|
|
* Copyright © 2005 Eric Anholt
|
|
* Copyright © 2009 Chris Wilson
|
|
* Copyright © 2010 Soeren Sandmann
|
|
* Copyright © 2010 Red Hat, Inc.
|
|
*
|
|
* 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> /* abort() */
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include "utils.h"
|
|
|
|
typedef struct image_t image_t;
|
|
|
|
static const color_t colors[] =
|
|
{
|
|
{ 1.0, 1.0, 1.0, 1.0 },
|
|
{ 1.0, 1.0, 1.0, 0.0 },
|
|
{ 0.0, 0.0, 0.0, 1.0 },
|
|
{ 0.0, 0.0, 0.0, 0.0 },
|
|
{ 1.0, 0.0, 0.0, 1.0 },
|
|
{ 0.0, 1.0, 0.0, 1.0 },
|
|
{ 0.0, 0.0, 1.0, 1.0 },
|
|
{ 0.5, 0.0, 0.0, 0.5 },
|
|
};
|
|
|
|
static uint16_t
|
|
_color_double_to_short (double d)
|
|
{
|
|
uint32_t i;
|
|
|
|
i = (uint32_t) (d * 65536);
|
|
i -= (i >> 16);
|
|
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
compute_pixman_color (const color_t *color,
|
|
pixman_color_t *out)
|
|
{
|
|
out->red = _color_double_to_short (color->r);
|
|
out->green = _color_double_to_short (color->g);
|
|
out->blue = _color_double_to_short (color->b);
|
|
out->alpha = _color_double_to_short (color->a);
|
|
}
|
|
|
|
#define REPEAT 0x01000000
|
|
#define FLAGS 0xff000000
|
|
|
|
static const int sizes[] =
|
|
{
|
|
0,
|
|
1,
|
|
1 | REPEAT,
|
|
10
|
|
};
|
|
|
|
static const pixman_format_code_t formats[] =
|
|
{
|
|
/* 32 bpp formats */
|
|
PIXMAN_a8r8g8b8,
|
|
PIXMAN_x8r8g8b8,
|
|
PIXMAN_a8b8g8r8,
|
|
PIXMAN_x8b8g8r8,
|
|
PIXMAN_b8g8r8a8,
|
|
PIXMAN_b8g8r8x8,
|
|
PIXMAN_r8g8b8a8,
|
|
PIXMAN_r8g8b8x8,
|
|
PIXMAN_x2r10g10b10,
|
|
PIXMAN_x2b10g10r10,
|
|
PIXMAN_a2r10g10b10,
|
|
PIXMAN_a2b10g10r10,
|
|
|
|
/* sRGB formats */
|
|
PIXMAN_a8r8g8b8_sRGB,
|
|
|
|
/* 24 bpp formats */
|
|
PIXMAN_r8g8b8,
|
|
PIXMAN_b8g8r8,
|
|
PIXMAN_r5g6b5,
|
|
PIXMAN_b5g6r5,
|
|
|
|
/* 16 bpp formats */
|
|
PIXMAN_x1r5g5b5,
|
|
PIXMAN_x1b5g5r5,
|
|
PIXMAN_a1r5g5b5,
|
|
PIXMAN_a1b5g5r5,
|
|
PIXMAN_a4b4g4r4,
|
|
PIXMAN_x4b4g4r4,
|
|
PIXMAN_a4r4g4b4,
|
|
PIXMAN_x4r4g4b4,
|
|
|
|
/* 8 bpp formats */
|
|
PIXMAN_a8,
|
|
PIXMAN_r3g3b2,
|
|
PIXMAN_b2g3r3,
|
|
PIXMAN_a2r2g2b2,
|
|
PIXMAN_a2b2g2r2,
|
|
PIXMAN_x4a4,
|
|
|
|
/* 4 bpp formats */
|
|
PIXMAN_a4,
|
|
PIXMAN_r1g2b1,
|
|
PIXMAN_b1g2r1,
|
|
PIXMAN_a1r1g1b1,
|
|
PIXMAN_a1b1g1r1,
|
|
|
|
/* 1 bpp formats */
|
|
PIXMAN_a1,
|
|
};
|
|
|
|
struct image_t
|
|
{
|
|
pixman_image_t *image;
|
|
pixman_format_code_t format;
|
|
const color_t *color;
|
|
pixman_repeat_t repeat;
|
|
int size;
|
|
};
|
|
|
|
static const pixman_op_t operators[] =
|
|
{
|
|
PIXMAN_OP_CLEAR,
|
|
PIXMAN_OP_SRC,
|
|
PIXMAN_OP_DST,
|
|
PIXMAN_OP_OVER,
|
|
PIXMAN_OP_OVER_REVERSE,
|
|
PIXMAN_OP_IN,
|
|
PIXMAN_OP_IN_REVERSE,
|
|
PIXMAN_OP_OUT,
|
|
PIXMAN_OP_OUT_REVERSE,
|
|
PIXMAN_OP_ATOP,
|
|
PIXMAN_OP_ATOP_REVERSE,
|
|
PIXMAN_OP_XOR,
|
|
PIXMAN_OP_ADD,
|
|
PIXMAN_OP_SATURATE,
|
|
|
|
PIXMAN_OP_DISJOINT_CLEAR,
|
|
PIXMAN_OP_DISJOINT_SRC,
|
|
PIXMAN_OP_DISJOINT_DST,
|
|
PIXMAN_OP_DISJOINT_OVER,
|
|
PIXMAN_OP_DISJOINT_OVER_REVERSE,
|
|
PIXMAN_OP_DISJOINT_IN,
|
|
PIXMAN_OP_DISJOINT_IN_REVERSE,
|
|
PIXMAN_OP_DISJOINT_OUT,
|
|
PIXMAN_OP_DISJOINT_OUT_REVERSE,
|
|
PIXMAN_OP_DISJOINT_ATOP,
|
|
PIXMAN_OP_DISJOINT_ATOP_REVERSE,
|
|
PIXMAN_OP_DISJOINT_XOR,
|
|
|
|
PIXMAN_OP_CONJOINT_CLEAR,
|
|
PIXMAN_OP_CONJOINT_SRC,
|
|
PIXMAN_OP_CONJOINT_DST,
|
|
PIXMAN_OP_CONJOINT_OVER,
|
|
PIXMAN_OP_CONJOINT_OVER_REVERSE,
|
|
PIXMAN_OP_CONJOINT_IN,
|
|
PIXMAN_OP_CONJOINT_IN_REVERSE,
|
|
PIXMAN_OP_CONJOINT_OUT,
|
|
PIXMAN_OP_CONJOINT_OUT_REVERSE,
|
|
PIXMAN_OP_CONJOINT_ATOP,
|
|
PIXMAN_OP_CONJOINT_ATOP_REVERSE,
|
|
PIXMAN_OP_CONJOINT_XOR,
|
|
};
|
|
|
|
static uint32_t
|
|
get_value (pixman_image_t *image)
|
|
{
|
|
uint32_t value = *(uint32_t *)pixman_image_get_data (image);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
{
|
|
pixman_format_code_t format = pixman_image_get_format (image);
|
|
value >>= 8 * sizeof(value) - PIXMAN_FORMAT_BPP (format);
|
|
}
|
|
#endif
|
|
|
|
return value;
|
|
}
|
|
|
|
static char *
|
|
describe_image (image_t *info, char *buf)
|
|
{
|
|
if (info->size)
|
|
{
|
|
sprintf (buf, "%s, %dx%d%s",
|
|
format_name (info->format),
|
|
info->size, info->size,
|
|
info->repeat ? " R" :"");
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "solid");
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *
|
|
describe_color (const color_t *color, char *buf)
|
|
{
|
|
sprintf (buf, "%.3f %.3f %.3f %.3f",
|
|
color->r, color->g, color->b, color->a);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static pixman_bool_t
|
|
composite_test (image_t *dst,
|
|
pixman_op_t op,
|
|
image_t *src,
|
|
image_t *mask,
|
|
pixman_bool_t component_alpha,
|
|
int testno)
|
|
{
|
|
color_t expected, tdst, tsrc, tmsk;
|
|
pixel_checker_t checker;
|
|
|
|
if (mask)
|
|
{
|
|
pixman_image_set_component_alpha (mask->image, component_alpha);
|
|
|
|
pixman_image_composite (op, src->image, mask->image, dst->image,
|
|
0, 0, 0, 0, 0, 0, dst->size, dst->size);
|
|
}
|
|
else
|
|
{
|
|
pixman_image_composite (op, src->image, NULL, dst->image,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
dst->size, dst->size);
|
|
}
|
|
|
|
tdst = *dst->color;
|
|
tsrc = *src->color;
|
|
|
|
if (mask)
|
|
{
|
|
tmsk = *mask->color;
|
|
}
|
|
|
|
/* It turns out that by construction all source, mask etc. colors are
|
|
* linear because they are made from fills, and fills are always in linear
|
|
* color space. However, if they have been converted to bitmaps, we need
|
|
* to simulate the sRGB approximation to pass the test cases.
|
|
*/
|
|
if (src->size)
|
|
{
|
|
if (PIXMAN_FORMAT_TYPE (src->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tsrc.r = convert_linear_to_srgb (tsrc.r);
|
|
tsrc.g = convert_linear_to_srgb (tsrc.g);
|
|
tsrc.b = convert_linear_to_srgb (tsrc.b);
|
|
round_color (src->format, &tsrc);
|
|
tsrc.r = convert_srgb_to_linear (tsrc.r);
|
|
tsrc.g = convert_srgb_to_linear (tsrc.g);
|
|
tsrc.b = convert_srgb_to_linear (tsrc.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (src->format, &tsrc);
|
|
}
|
|
}
|
|
|
|
if (mask && mask->size)
|
|
{
|
|
if (PIXMAN_FORMAT_TYPE (mask->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tmsk.r = convert_linear_to_srgb (tmsk.r);
|
|
tmsk.g = convert_linear_to_srgb (tmsk.g);
|
|
tmsk.b = convert_linear_to_srgb (tmsk.b);
|
|
round_color (mask->format, &tmsk);
|
|
tmsk.r = convert_srgb_to_linear (tmsk.r);
|
|
tmsk.g = convert_srgb_to_linear (tmsk.g);
|
|
tmsk.b = convert_srgb_to_linear (tmsk.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (mask->format, &tmsk);
|
|
}
|
|
}
|
|
|
|
if (mask)
|
|
{
|
|
if (component_alpha && PIXMAN_FORMAT_R (mask->format) == 0)
|
|
{
|
|
/* Ax component-alpha masks expand alpha into
|
|
* all color channels.
|
|
*/
|
|
tmsk.r = tmsk.g = tmsk.b = tmsk.a;
|
|
}
|
|
}
|
|
|
|
if (PIXMAN_FORMAT_TYPE (dst->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tdst.r = convert_linear_to_srgb (tdst.r);
|
|
tdst.g = convert_linear_to_srgb (tdst.g);
|
|
tdst.b = convert_linear_to_srgb (tdst.b);
|
|
round_color (dst->format, &tdst);
|
|
tdst.r = convert_srgb_to_linear (tdst.r);
|
|
tdst.g = convert_srgb_to_linear (tdst.g);
|
|
tdst.b = convert_srgb_to_linear (tdst.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (dst->format, &tdst);
|
|
}
|
|
|
|
do_composite (op,
|
|
&tsrc,
|
|
mask? &tmsk : NULL,
|
|
&tdst,
|
|
&expected,
|
|
component_alpha);
|
|
|
|
pixel_checker_init (&checker, dst->format);
|
|
|
|
if (!pixel_checker_check (&checker, get_value (dst->image), &expected))
|
|
{
|
|
char buf[40], buf2[40];
|
|
int a, r, g, b;
|
|
uint32_t pixel;
|
|
|
|
printf ("---- Test %d failed ----\n", testno);
|
|
printf ("Operator: %s %s\n",
|
|
operator_name (op), component_alpha ? "CA" : "");
|
|
|
|
printf ("Source: %s\n", describe_image (src, buf));
|
|
if (mask != NULL)
|
|
printf ("Mask: %s\n", describe_image (mask, buf));
|
|
|
|
printf ("Destination: %s\n\n", describe_image (dst, buf));
|
|
printf (" R G B A Rounded\n");
|
|
printf ("Source color: %s %s\n",
|
|
describe_color (src->color, buf),
|
|
describe_color (&tsrc, buf2));
|
|
if (mask)
|
|
{
|
|
printf ("Mask color: %s %s\n",
|
|
describe_color (mask->color, buf),
|
|
describe_color (&tmsk, buf2));
|
|
}
|
|
printf ("Dest. color: %s %s\n",
|
|
describe_color (dst->color, buf),
|
|
describe_color (&tdst, buf2));
|
|
|
|
pixel = get_value (dst->image);
|
|
|
|
printf ("Expected: %s\n", describe_color (&expected, buf));
|
|
|
|
pixel_checker_split_pixel (&checker, pixel, &a, &r, &g, &b);
|
|
|
|
printf ("Got: %5d %5d %5d %5d [pixel: 0x%08x]\n", r, g, b, a, pixel);
|
|
pixel_checker_get_min (&checker, &expected, &a, &r, &g, &b);
|
|
printf ("Min accepted: %5d %5d %5d %5d\n", r, g, b, a);
|
|
pixel_checker_get_max (&checker, &expected, &a, &r, &g, &b);
|
|
printf ("Max accepted: %5d %5d %5d %5d\n", r, g, b, a);
|
|
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
image_init (image_t *info,
|
|
int color,
|
|
int format,
|
|
int size)
|
|
{
|
|
pixman_color_t fill;
|
|
|
|
info->color = &colors[color];
|
|
compute_pixman_color (info->color, &fill);
|
|
|
|
info->format = formats[format];
|
|
info->size = sizes[size] & ~FLAGS;
|
|
info->repeat = PIXMAN_REPEAT_NONE;
|
|
|
|
if (info->size)
|
|
{
|
|
pixman_image_t *solid;
|
|
|
|
info->image = pixman_image_create_bits (info->format,
|
|
info->size, info->size,
|
|
NULL, 0);
|
|
|
|
solid = pixman_image_create_solid_fill (&fill);
|
|
pixman_image_composite32 (PIXMAN_OP_SRC, solid, NULL, info->image,
|
|
0, 0, 0, 0, 0, 0, info->size, info->size);
|
|
pixman_image_unref (solid);
|
|
|
|
if (sizes[size] & REPEAT)
|
|
{
|
|
pixman_image_set_repeat (info->image, PIXMAN_REPEAT_NORMAL);
|
|
info->repeat = PIXMAN_REPEAT_NORMAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
info->image = pixman_image_create_solid_fill (&fill);
|
|
}
|
|
}
|
|
|
|
static void
|
|
image_fini (image_t *info)
|
|
{
|
|
pixman_image_unref (info->image);
|
|
}
|
|
|
|
static int
|
|
random_size (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (sizes));
|
|
}
|
|
|
|
static int
|
|
random_color (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (colors));
|
|
}
|
|
|
|
static int
|
|
random_format (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (formats));
|
|
}
|
|
|
|
static pixman_bool_t
|
|
run_test (uint32_t seed)
|
|
{
|
|
image_t src, mask, dst;
|
|
pixman_op_t op;
|
|
int ca;
|
|
int ok;
|
|
|
|
prng_srand (seed);
|
|
|
|
image_init (&dst, random_color(), random_format(), 1);
|
|
image_init (&src, random_color(), random_format(), random_size());
|
|
image_init (&mask, random_color(), random_format(), random_size());
|
|
|
|
op = operators [prng_rand_n (ARRAY_LENGTH (operators))];
|
|
|
|
ca = prng_rand_n (3);
|
|
|
|
switch (ca)
|
|
{
|
|
case 0:
|
|
ok = composite_test (&dst, op, &src, NULL, FALSE, seed);
|
|
break;
|
|
case 1:
|
|
ok = composite_test (&dst, op, &src, &mask, FALSE, seed);
|
|
break;
|
|
case 2:
|
|
ok = composite_test (&dst, op, &src, &mask,
|
|
mask.size? TRUE : FALSE, seed);
|
|
break;
|
|
default:
|
|
ok = FALSE;
|
|
break;
|
|
}
|
|
|
|
image_fini (&src);
|
|
image_fini (&mask);
|
|
image_fini (&dst);
|
|
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
#define N_TESTS (8 * 1024 * 1024)
|
|
int result = 0;
|
|
uint32_t seed;
|
|
int32_t i;
|
|
|
|
if (argc > 1)
|
|
{
|
|
char *end;
|
|
|
|
i = strtol (argv[1], &end, 0);
|
|
|
|
if (end != argv[1])
|
|
{
|
|
if (!run_test (i))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
printf ("Usage:\n\n %s <number>\n\n", argv[0]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (getenv ("PIXMAN_RANDOMIZE_TESTS"))
|
|
seed = get_random_seed();
|
|
else
|
|
seed = 1;
|
|
|
|
#ifdef USE_OPENMP
|
|
# pragma omp parallel for default(none) shared(result, argv, seed)
|
|
#endif
|
|
for (i = 0; i <= N_TESTS; ++i)
|
|
{
|
|
if (!result && !run_test (i + seed))
|
|
{
|
|
printf ("Test 0x%08X failed.\n", seed + i);
|
|
|
|
result = seed + i;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|