#include #include #include "utils.h" #include #if 0 #define fence_malloc malloc #define fence_free free #define make_random_bytes malloc #endif static const pixman_format_code_t image_formats[] = { PIXMAN_a8r8g8b8, PIXMAN_x8r8g8b8, PIXMAN_r5g6b5, PIXMAN_r3g3b2, PIXMAN_a8, PIXMAN_a8b8g8r8, PIXMAN_x8b8g8r8, PIXMAN_b8g8r8a8, PIXMAN_b8g8r8x8, PIXMAN_r8g8b8a8, PIXMAN_r8g8b8x8, PIXMAN_x14r6g6b6, PIXMAN_r8g8b8, PIXMAN_b8g8r8, PIXMAN_a8r8g8b8_sRGB, PIXMAN_r5g6b5, PIXMAN_b5g6r5, PIXMAN_x2r10g10b10, PIXMAN_a2r10g10b10, PIXMAN_x2b10g10r10, PIXMAN_a2b10g10r10, PIXMAN_a1r5g5b5, PIXMAN_x1r5g5b5, PIXMAN_a1b5g5r5, PIXMAN_x1b5g5r5, PIXMAN_a4r4g4b4, PIXMAN_x4r4g4b4, PIXMAN_a4b4g4r4, PIXMAN_x4b4g4r4, PIXMAN_a8, PIXMAN_r3g3b2, PIXMAN_b2g3r3, PIXMAN_a2r2g2b2, PIXMAN_a2b2g2r2, PIXMAN_c8, PIXMAN_g8, PIXMAN_x4c4, PIXMAN_x4g4, PIXMAN_c4, PIXMAN_g4, PIXMAN_g1, PIXMAN_x4a4, PIXMAN_a4, PIXMAN_r1g2b1, PIXMAN_b1g2r1, PIXMAN_a1r1g1b1, PIXMAN_a1b1g1r1, PIXMAN_a1 }; static pixman_filter_t filters[] = { PIXMAN_FILTER_NEAREST, PIXMAN_FILTER_BILINEAR, PIXMAN_FILTER_FAST, PIXMAN_FILTER_GOOD, PIXMAN_FILTER_BEST, PIXMAN_FILTER_CONVOLUTION }; static int get_size (void) { switch (prng_rand_n (28)) { case 0: return 1; case 1: return 2; default: case 2: return prng_rand_n (100); case 4: return prng_rand_n (2000) + 1000; case 5: return 65535; case 6: return 65536; case 7: return prng_rand_n (64000) + 63000; } } static void destroy (pixman_image_t *image, void *data) { if (image->type == BITS && image->bits.free_me != image->bits.bits) { uint32_t *bits; if (image->bits.bits != (void *)0x01) { bits = image->bits.bits; if (image->bits.rowstride < 0) bits -= (- image->bits.rowstride * (image->bits.height - 1)); fence_free (bits); } } free (data); } static uint32_t real_reader (const void *src, int size) { switch (size) { case 1: return *(uint8_t *)src; case 2: return *(uint16_t *)src; case 4: return *(uint32_t *)src; default: assert (0); return 0; /* silence MSVC */ } } static void real_writer (void *src, uint32_t value, int size) { switch (size) { case 1: *(uint8_t *)src = value; break; case 2: *(uint16_t *)src = value; break; case 4: *(uint32_t *)src = value; break; default: assert (0); break; } } static uint32_t fake_reader (const void *src, int size) { uint32_t r = prng_rand (); assert (size == 1 || size == 2 || size == 4); return r >> (32 - (size * 8)); } static void fake_writer (void *src, uint32_t value, int size) { assert (size == 1 || size == 2 || size == 4); } static int32_t log_rand (void) { uint32_t mask; mask = (1 << prng_rand_n (10)) - 1; return (prng_rand () & mask) - (mask >> 1); } static int32_t rand_x (pixman_image_t *image) { if (image->type == BITS) return prng_rand_n (image->bits.width); else return log_rand (); } static int32_t rand_y (pixman_image_t *image) { if (image->type == BITS) return prng_rand_n (image->bits.height); else return log_rand (); } typedef enum { DONT_CARE, PREFER_ALPHA, REQUIRE_ALPHA } alpha_preference_t; static pixman_format_code_t random_format (alpha_preference_t alpha) { pixman_format_code_t format; int n = prng_rand_n (ARRAY_LENGTH (image_formats)); if (alpha >= PREFER_ALPHA && (alpha == REQUIRE_ALPHA || prng_rand_n (4) != 0)) { do { format = image_formats[n++ % ARRAY_LENGTH (image_formats)]; } while (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_A); } else { format = image_formats[n]; } return format; } static pixman_image_t * create_random_bits_image (alpha_preference_t alpha_preference) { pixman_format_code_t format; pixman_indexed_t *indexed; pixman_image_t *image; int width, height, stride; uint32_t *bits; pixman_read_memory_func_t read_func = NULL; pixman_write_memory_func_t write_func = NULL; pixman_filter_t filter; pixman_fixed_t *coefficients = NULL; int n_coefficients = 0; /* format */ format = random_format (alpha_preference); indexed = NULL; if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_COLOR) { indexed = malloc (sizeof (pixman_indexed_t)); initialize_palette (indexed, PIXMAN_FORMAT_BPP (format), TRUE); } else if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_GRAY) { indexed = malloc (sizeof (pixman_indexed_t)); initialize_palette (indexed, PIXMAN_FORMAT_BPP (format), FALSE); } else { indexed = NULL; } /* size */ width = get_size (); height = get_size (); while ((uint64_t)width * height > 200000) { if (prng_rand_n(2) == 0) height = 200000 / width; else width = 200000 / height; } if (height == 0) height = 1; if (width == 0) width = 1; /* bits */ switch (prng_rand_n (7)) { default: case 0: stride = width * PIXMAN_FORMAT_BPP (format) + prng_rand_n (17); stride = (stride + 3) & (~3); bits = (uint32_t *)make_random_bytes (height * stride); break; case 1: stride = 0; bits = NULL; break; case 2: /* Zero-filled */ stride = width * PIXMAN_FORMAT_BPP (format) + prng_rand_n (17); stride = (stride + 3) & (~3); bits = fence_malloc (height * stride); if (!bits) return NULL; memset (bits, 0, height * stride); break; case 3: /* Filled with 0xFF */ stride = width * PIXMAN_FORMAT_BPP (format) + prng_rand_n (17); stride = (stride + 3) & (~3); bits = fence_malloc (height * stride); if (!bits) return NULL; memset (bits, 0xff, height * stride); break; case 4: /* bits is a bad pointer, has read/write functions */ stride = 232; bits = (void *)0x01; read_func = fake_reader; write_func = fake_writer; break; case 5: /* bits is a real pointer, has read/write functions */ stride = width * PIXMAN_FORMAT_BPP (format) + prng_rand_n (17); stride = (stride + 3) & (~3); bits = fence_malloc (height * stride); if (!bits) return NULL; memset (bits, 0xff, height * stride); read_func = real_reader; write_func = real_writer; break; case 6: /* bits is a real pointer, stride is negative */ stride = (width * PIXMAN_FORMAT_BPP (format) + prng_rand_n (17)); stride = (stride + 3) & (~3); bits = (uint32_t *)make_random_bytes (height * stride); if (!bits) return NULL; bits += ((height - 1) * stride) / 4; stride = - stride; break; } /* Filter */ filter = filters[prng_rand_n (ARRAY_LENGTH (filters))]; if (filter == PIXMAN_FILTER_CONVOLUTION) { int width = prng_rand_n (3); int height = prng_rand_n (4); n_coefficients = width * height + 2; coefficients = malloc (n_coefficients * sizeof (pixman_fixed_t)); if (coefficients) { int i; for (i = 0; i < width * height; ++i) coefficients[i + 2] = prng_rand(); coefficients[0] = width << 16; coefficients[1] = height << 16; } else { filter = PIXMAN_FILTER_BEST; } } /* Finally create the image */ image = pixman_image_create_bits (format, width, height, bits, stride); if (!image) return NULL; pixman_image_set_indexed (image, indexed); pixman_image_set_destroy_function (image, destroy, indexed); pixman_image_set_accessors (image, read_func, write_func); pixman_image_set_filter (image, filter, coefficients, n_coefficients); return image; } static pixman_repeat_t repeats[] = { PIXMAN_REPEAT_NONE, PIXMAN_REPEAT_NORMAL, PIXMAN_REPEAT_REFLECT, PIXMAN_REPEAT_PAD }; static uint32_t absolute (int32_t i) { return i < 0? -i : i; } static void set_general_properties (pixman_image_t *image, pixman_bool_t allow_alpha_map) { pixman_repeat_t repeat; /* Set properties that are generic to all images */ /* Repeat */ repeat = repeats[prng_rand_n (ARRAY_LENGTH (repeats))]; pixman_image_set_repeat (image, repeat); /* Alpha map */ if (allow_alpha_map && prng_rand_n (4) == 0) { pixman_image_t *alpha_map; int16_t x, y; alpha_map = create_random_bits_image (DONT_CARE); if (alpha_map) { set_general_properties (alpha_map, FALSE); x = rand_x (image) - image->bits.width / 2; y = rand_y (image) - image->bits.height / 2; pixman_image_set_alpha_map (image, alpha_map, x, y); pixman_image_unref (alpha_map); } } /* Component alpha */ pixman_image_set_component_alpha (image, prng_rand_n (3) == 0); /* Clip region */ if (prng_rand_n (8) < 2) { pixman_region32_t region; int i, n_rects; pixman_region32_init (®ion); switch (prng_rand_n (12)) { case 0: n_rects = 0; break; case 1: case 2: case 3: n_rects = 1; break; case 4: case 5: n_rects = 2; break; case 6: case 7: n_rects = 3; break; default: n_rects = prng_rand_n (100); break; } for (i = 0; i < n_rects; ++i) { uint32_t width, height; int x, y; x = log_rand(); y = log_rand(); width = absolute (log_rand ()) + 1; height = absolute (log_rand ()) + 1; pixman_region32_union_rect ( ®ion, ®ion, x, y, width, height); } if (image->type == BITS && prng_rand_n (8) != 0) { uint32_t width, height; int x, y; int i; /* Also add a couple of clip rectangles inside the image * so that compositing will actually take place. */ for (i = 0; i < 5; ++i) { x = prng_rand_n (2 * image->bits.width) - image->bits.width; y = prng_rand_n (2 * image->bits.height) - image->bits.height; width = prng_rand_n (image->bits.width) - x + 10; height = prng_rand_n (image->bits.height) - y + 10; if (width + x < x) width = INT32_MAX - x; if (height + y < y) height = INT32_MAX - y; pixman_region32_union_rect ( ®ion, ®ion, x, y, width, height); } } pixman_image_set_clip_region32 (image, ®ion); pixman_region32_fini (®ion); } /* Whether source clipping is enabled */ pixman_image_set_source_clipping (image, !!prng_rand_n (2)); /* Client clip */ pixman_image_set_has_client_clip (image, !!prng_rand_n (2)); /* Transform */ if (prng_rand_n (5) < 2) { pixman_transform_t xform; int i, j, k; uint32_t tx, ty, sx, sy; uint32_t c, s; memset (&xform, 0, sizeof xform); xform.matrix[0][0] = pixman_fixed_1; xform.matrix[1][1] = pixman_fixed_1; xform.matrix[2][2] = pixman_fixed_1; for (k = 0; k < 3; ++k) { switch (prng_rand_n (4)) { case 0: /* rotation */ c = prng_rand_n (2 * 65536) - 65536; s = prng_rand_n (2 * 65536) - 65536; pixman_transform_rotate (&xform, NULL, c, s); break; case 1: /* translation */ tx = prng_rand(); ty = prng_rand(); pixman_transform_translate (&xform, NULL, tx, ty); break; case 2: /* scale */ sx = prng_rand(); sy = prng_rand(); pixman_transform_scale (&xform, NULL, sx, sy); break; case 3: if (prng_rand_n (16) == 0) { /* random */ for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) xform.matrix[i][j] = prng_rand(); break; } else if (prng_rand_n (16) == 0) { /* zero */ memset (&xform, 0, sizeof xform); } break; } } pixman_image_set_transform (image, &xform); } } static pixman_color_t random_color (void) { pixman_color_t color = { prng_rand() & 0xffff, prng_rand() & 0xffff, prng_rand() & 0xffff, prng_rand() & 0xffff, }; return color; } static pixman_image_t * create_random_solid_image (void) { pixman_color_t color = random_color(); pixman_image_t *image = pixman_image_create_solid_fill (&color); return image; } static pixman_gradient_stop_t * create_random_stops (int *n_stops) { pixman_fixed_t step; pixman_fixed_t s; int i; pixman_gradient_stop_t *stops; *n_stops = prng_rand_n (50) + 1; step = pixman_fixed_1 / *n_stops; stops = malloc (*n_stops * sizeof (pixman_gradient_stop_t)); s = 0; for (i = 0; i < (*n_stops) - 1; ++i) { stops[i].x = s; stops[i].color = random_color(); s += step; } stops[*n_stops - 1].x = pixman_fixed_1; stops[*n_stops - 1].color = random_color(); return stops; } static pixman_point_fixed_t create_random_point (void) { pixman_point_fixed_t p; p.x = log_rand (); p.y = log_rand (); return p; } static pixman_image_t * create_random_linear_image (void) { int n_stops; pixman_gradient_stop_t *stops; pixman_point_fixed_t p1, p2; pixman_image_t *result; stops = create_random_stops (&n_stops); if (!stops) return NULL; p1 = create_random_point (); p2 = create_random_point (); result = pixman_image_create_linear_gradient (&p1, &p2, stops, n_stops); free (stops); return result; } static pixman_image_t * create_random_radial_image (void) { int n_stops; pixman_gradient_stop_t *stops; pixman_point_fixed_t inner_c, outer_c; pixman_fixed_t inner_r, outer_r; pixman_image_t *result; inner_c = create_random_point(); outer_c = create_random_point(); inner_r = prng_rand(); outer_r = prng_rand(); stops = create_random_stops (&n_stops); if (!stops) return NULL; result = pixman_image_create_radial_gradient ( &inner_c, &outer_c, inner_r, outer_r, stops, n_stops); free (stops); return result; } static pixman_image_t * create_random_conical_image (void) { pixman_gradient_stop_t *stops; int n_stops; pixman_point_fixed_t c; pixman_fixed_t angle; pixman_image_t *result; c = create_random_point(); angle = prng_rand(); stops = create_random_stops (&n_stops); if (!stops) return NULL; result = pixman_image_create_conical_gradient (&c, angle, stops, n_stops); free (stops); return result; } static pixman_image_t * create_random_image (void) { pixman_image_t *result; switch (prng_rand_n (5)) { default: case 0: result = create_random_bits_image (DONT_CARE); break; case 1: result = create_random_solid_image (); break; case 2: result = create_random_linear_image (); break; case 3: result = create_random_radial_image (); break; case 4: result = create_random_conical_image (); break; } if (result) set_general_properties (result, TRUE); return result; } static void random_line (pixman_line_fixed_t *line, int width, int height) { line->p1.x = prng_rand_n (width) << 16; line->p1.y = prng_rand_n (height) << 16; line->p2.x = prng_rand_n (width) << 16; line->p2.y = prng_rand_n (height) << 16; } static pixman_trapezoid_t * create_random_trapezoids (int *n_traps, int height, int width) { pixman_trapezoid_t *trapezoids; int i; *n_traps = prng_rand_n (16) + 1; trapezoids = malloc (sizeof (pixman_trapezoid_t) * *n_traps); for (i = 0; i < *n_traps; ++i) { pixman_trapezoid_t *t = &(trapezoids[i]); t->top = prng_rand_n (height) << 16; t->bottom = prng_rand_n (height) << 16; random_line (&t->left, height, width); random_line (&t->right, height, width); } return trapezoids; } static const pixman_op_t op_list[] = { PIXMAN_OP_SRC, PIXMAN_OP_OVER, PIXMAN_OP_ADD, 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, PIXMAN_OP_MULTIPLY, PIXMAN_OP_SCREEN, PIXMAN_OP_OVERLAY, PIXMAN_OP_DARKEN, PIXMAN_OP_LIGHTEN, PIXMAN_OP_COLOR_DODGE, PIXMAN_OP_COLOR_BURN, PIXMAN_OP_HARD_LIGHT, PIXMAN_OP_DIFFERENCE, PIXMAN_OP_EXCLUSION, PIXMAN_OP_SOFT_LIGHT, PIXMAN_OP_HSL_HUE, PIXMAN_OP_HSL_SATURATION, PIXMAN_OP_HSL_COLOR, PIXMAN_OP_HSL_LUMINOSITY, }; static void run_test (uint32_t seed, pixman_bool_t verbose, uint32_t mod) { pixman_image_t *source, *mask, *dest; pixman_op_t op; if (verbose) { if (mod == 0 || (seed % mod) == 0) printf ("Seed 0x%08x\n", seed); } source = mask = dest = NULL; prng_srand (seed); if (prng_rand_n (8) == 0) { int n_traps; pixman_trapezoid_t *trapezoids; int p = prng_rand_n (3); if (p == 0) dest = create_random_bits_image (DONT_CARE); else dest = create_random_bits_image (REQUIRE_ALPHA); if (!dest) goto out; set_general_properties (dest, TRUE); if (!(trapezoids = create_random_trapezoids ( &n_traps, dest->bits.width, dest->bits.height))) { goto out; } switch (p) { case 0: source = create_random_image (); if (source) { op = op_list [prng_rand_n (ARRAY_LENGTH (op_list))]; pixman_composite_trapezoids ( op, source, dest, random_format (REQUIRE_ALPHA), rand_x (source), rand_y (source), rand_x (dest), rand_y (dest), n_traps, trapezoids); } break; case 1: pixman_rasterize_trapezoid ( dest, &trapezoids[prng_rand_n (n_traps)], rand_x (dest), rand_y (dest)); break; case 2: pixman_add_trapezoids ( dest, rand_x (dest), rand_y (dest), n_traps, trapezoids); break; } free (trapezoids); } else { dest = create_random_bits_image (DONT_CARE); source = create_random_image (); mask = create_random_image (); if (source && mask && dest) { set_general_properties (dest, TRUE); op = op_list [prng_rand_n (ARRAY_LENGTH (op_list))]; pixman_image_composite32 (op, source, mask, dest, rand_x (source), rand_y (source), rand_x (mask), rand_y (mask), 0, 0, dest->bits.width, dest->bits.height); } } out: if (source) pixman_image_unref (source); if (mask) pixman_image_unref (mask); if (dest) pixman_image_unref (dest); } static pixman_bool_t get_int (char *s, uint32_t *i) { char *end; int p; p = strtol (s, &end, 0); if (end != s && *end == 0) { *i = p; return TRUE; } return FALSE; } int main (int argc, char **argv) { int verbose = FALSE; uint32_t seed = 1; uint32_t n_tests = 8000; uint32_t mod = 0; pixman_bool_t use_threads = TRUE; int32_t i; pixman_disable_out_of_bounds_workaround (); enable_divbyzero_exceptions(); if (getenv ("VERBOSE") != NULL) verbose = TRUE; for (i = 1; i < argc; ++i) { if (strcmp (argv[i], "-v") == 0) { verbose = TRUE; if (i + 1 < argc) { get_int (argv[i + 1], &mod); i++; } } else if (strcmp (argv[i], "-s") == 0 && i + 1 < argc) { get_int (argv[i + 1], &seed); use_threads = FALSE; i++; } else if (strcmp (argv[i], "-n") == 0 && i + 1 < argc) { get_int (argv[i + 1], &n_tests); i++; } else { if (strcmp (argv[i], "-h") != 0) printf ("Unknown option '%s'\n\n", argv[i]); printf ("Options:\n\n" "-n Number of tests to run\n" "-s Seed of first test (ignored if PIXMAN_RANDOMIZE_TESTS is set)\n" "-v Print out seeds\n" "-v Print out every n'th seed\n\n"); exit (-1); } } if (getenv ("PIXMAN_RANDOMIZE_TESTS")) { seed = get_random_seed(); printf ("First seed: 0x%08x\n", seed); } if (use_threads) { #ifdef USE_OPENMP # pragma omp parallel for default(none) shared(verbose, n_tests, mod, seed) #endif for (i = 0; i < (int32_t)n_tests; ++i) run_test (seed + i, verbose, mod); } else { for (i = 0; i < (int32_t)n_tests; ++i) run_test (seed + i, verbose, mod); } return 0; }