/* * 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; }