1#include <stdint.h> 2#include <stdio.h> 3#include <stdlib.h> 4 5#include <X11/Xutil.h> /* for XDestroyImage */ 6#include <pixman.h> /* for pixman blt functions */ 7 8#include "test.h" 9 10enum trapezoid { 11 RECT_ALIGN, 12 RECT_UNALIGN, 13 GENERAL 14}; 15 16static const uint8_t ops[] = { 17 PictOpClear, 18 PictOpSrc, 19 PictOpDst, 20}; 21 22static XRenderPictFormat *mask_format(Display *dpy, enum mask mask) 23{ 24 switch (mask) { 25 default: 26 case MASK_NONE: return NULL; 27 case MASK_A1: return XRenderFindStandardFormat(dpy, PictStandardA1); 28 case MASK_A8: return XRenderFindStandardFormat(dpy, PictStandardA8); 29 } 30} 31 32static const char *mask_name(enum mask mask) 33{ 34 switch (mask) { 35 default: 36 case MASK_NONE: return "none"; 37 case MASK_A1: return "a1"; 38 case MASK_A8: return "a8"; 39 } 40} 41 42static const char *trapezoid_name(enum trapezoid trapezoid) 43{ 44 switch (trapezoid) { 45 default: 46 case RECT_ALIGN: return "pixel-aligned"; 47 case RECT_UNALIGN: return "rectilinear"; 48 case GENERAL: return "general"; 49 } 50} 51 52static void fill_rect(struct test_display *dpy, Picture p, uint8_t op, 53 int x, int y, int w, int h, 54 int dx, int dy, enum mask mask, 55 uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) 56{ 57 XRenderColor render_color; 58 XTrapezoid trap; 59 Picture src; 60 61 render_color.red = red * alpha; 62 render_color.green = green * alpha; 63 render_color.blue = blue * alpha; 64 render_color.alpha = alpha << 8; 65 66 trap.left.p1.x = trap.left.p2.x = (x << 16) + dx; 67 trap.top = trap.left.p1.y = trap.right.p1.y = (y << 16) + dy; 68 trap.right.p1.x = trap.right.p2.x = ((x + w) << 16) + dx; 69 trap.bottom = trap.left.p2.y = trap.right.p2.y = ((y + h) << 16) + dy; 70 71 src = XRenderCreateSolidFill(dpy->dpy, &render_color); 72 XRenderCompositeTrapezoids(dpy->dpy, 73 op, src, p, mask_format(dpy->dpy, mask), 74 0, 0, &trap, 1); 75 XRenderFreePicture(dpy->dpy, src); 76} 77 78static void pixel_tests(struct test *t, int reps, int sets, enum target target) 79{ 80 struct test_target tt; 81 XImage image; 82 uint32_t *cells = malloc(t->out.width*t->out.height*4); 83 struct { 84 uint16_t x, y; 85 } *pixels = malloc(reps*sizeof(*pixels)); 86 int r, s; 87 88 printf("Testing setting of single pixels (%s): ", test_target_name(target)); 89 fflush(stdout); 90 91 test_target_create_render(&t->out, target, &tt); 92 93 for (s = 0; s < sets; s++) { 94 for (r = 0; r < reps; r++) { 95 int x = rand() % (tt.width - 1); 96 int y = rand() % (tt.height - 1); 97 int red = rand() % 0xff; 98 int green = rand() % 0xff; 99 int blue = rand() % 0xff; 100 int alpha = rand() % 0xff; 101 102 fill_rect(&t->out, tt.picture, PictOpSrc, 103 x, y, 1, 1, 104 0, 0, MASK_NONE, 105 red, green, blue, alpha); 106 107 pixels[r].x = x; 108 pixels[r].y = y; 109 cells[y*t->out.width+x] = color(red, green, blue, alpha); 110 } 111 112 test_init_image(&image, &t->out.shm, tt.format, 1, 1); 113 114 for (r = 0; r < reps; r++) { 115 uint32_t result; 116 uint32_t x = pixels[r].x; 117 uint32_t y = pixels[r].y; 118 119 XShmGetImage(t->out.dpy, tt.draw, &image, 120 x, y, AllPlanes); 121 122 result = *(uint32_t *)image.data; 123 if (!pixel_equal(image.depth, result, 124 cells[y*tt.width+x])) { 125 uint32_t mask = depth_mask(image.depth); 126 die("failed to set pixel (%d,%d) to %08x [%08x], found %08x [%08x] instead\n", 127 x, y, 128 cells[y*tt.width+x] & mask, 129 cells[y*tt.width+x], 130 result & mask, 131 result); 132 } 133 } 134 } 135 printf("passed [%d iterations x %d]\n", reps, sets); 136 137 test_target_destroy_render(&t->out, &tt); 138 139 free(pixels); 140 free(cells); 141} 142 143static void clear(struct test_display *dpy, struct test_target *tt) 144{ 145 XRenderColor render_color = {0}; 146 XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, &render_color, 147 0, 0, tt->width, tt->height); 148} 149 150static void area_tests(struct test *t, int reps, int sets, enum target target) 151{ 152 struct test_target tt; 153 XImage image; 154 uint32_t *cells = calloc(sizeof(uint32_t), t->out.width*t->out.height); 155 int r, s, x, y; 156 157 printf("Testing area sets (%s): ", test_target_name(target)); 158 fflush(stdout); 159 160 test_target_create_render(&t->out, target, &tt); 161 clear(&t->out, &tt); 162 163 test_init_image(&image, &t->out.shm, tt.format, tt.width, tt.height); 164 165 for (s = 0; s < sets; s++) { 166 for (r = 0; r < reps; r++) { 167 int w = rand() % tt.width; 168 int h = rand() % tt.height; 169 int red = rand() % 0xff; 170 int green = rand() % 0xff; 171 int blue = rand() % 0xff; 172 int alpha = rand() % 0xff; 173 174 x = rand() % (2*tt.width) - tt.width; 175 y = rand() % (2*tt.height) - tt.height; 176 177 fill_rect(&t->out, tt.picture, PictOpSrc, 178 x, y, w, h, 179 0, 0, MASK_NONE, 180 red, green, blue, alpha); 181 182 if (x < 0) 183 w += x, x = 0; 184 if (y < 0) 185 h += y, y = 0; 186 if (x >= tt.width || y >= tt.height) 187 continue; 188 189 if (x + w > tt.width) 190 w = tt.width - x; 191 if (y + h > tt.height) 192 h = tt.height - y; 193 if (w <= 0 || h <= 0) 194 continue; 195 196 pixman_fill(cells, tt.width, 32, x, y, w, h, 197 color(red, green, blue, alpha)); 198 } 199 200 XShmGetImage(t->out.dpy, tt.draw, &image, 0, 0, AllPlanes); 201 202 for (y = 0; y < tt.height; y++) { 203 for (x = 0; x < tt.width; x++) { 204 uint32_t result = 205 *(uint32_t *)(image.data + 206 y*image.bytes_per_line + 207 image.bits_per_pixel*x/8); 208 if (!pixel_equal(image.depth, result, cells[y*tt.width+x])) { 209 uint32_t mask = depth_mask(image.depth); 210 211 die("failed to set pixel (%d,%d) to %08x [%08x], found %08x [%08x] instead\n", 212 x, y, 213 cells[y*tt.width+x] & mask, 214 cells[y*tt.width+x], 215 result & mask, 216 result); 217 } 218 } 219 } 220 } 221 222 printf("passed [%d iterations x %d]\n", reps, sets); 223 224 test_target_destroy_render(&t->out, &tt); 225 free(cells); 226} 227 228static void rect_tests(struct test *t, 229 int dx, int dy, 230 enum mask mask, 231 int reps, int sets, 232 enum target target) 233{ 234 struct test_target out, ref; 235 int r, s; 236 237 printf("Testing area fills (offset %dx%d, mask %s) (%s): ", 238 dx, dy, mask_name(mask), test_target_name(target)); 239 fflush(stdout); 240 241 test_target_create_render(&t->out, target, &out); 242 clear(&t->out, &out); 243 244 test_target_create_render(&t->ref, target, &ref); 245 clear(&t->ref, &ref); 246 247 for (s = 0; s < sets; s++) { 248 for (r = 0; r < reps; r++) { 249 int x = rand() % (2*out.width) - out.width; 250 int y = rand() % (2*out.height) - out.height; 251 int w = rand() % out.width; 252 int h = rand() % out.height; 253 int op = ops[rand() % sizeof(ops)]; 254 int red = rand() % 0xff; 255 int green = rand() % 0xff; 256 int blue = rand() % 0xff; 257 int alpha = rand() % 0xff; 258 259 fill_rect(&t->out, out.picture, op, 260 x, y, w, h, dx, dy, mask, 261 red, green, blue, alpha); 262 fill_rect(&t->ref, ref.picture, op, 263 x, y, w, h, dx, dy, mask, 264 red, green, blue, alpha); 265 } 266 267 test_compare(t, 268 out.draw, out.format, 269 ref.draw, ref.format, 270 0, 0, out.width, out.height, 271 ""); 272 } 273 274 printf("passed [%d iterations x %d]\n", reps, sets); 275 276 test_target_destroy_render(&t->out, &out); 277 test_target_destroy_render(&t->ref, &ref); 278} 279 280static void random_trapezoid(XTrapezoid *trap, enum trapezoid trapezoid, 281 int x1, int y1, int x2, int y2) 282{ 283 switch (trapezoid) { 284 case RECT_ALIGN: 285 x1 = x1 + rand() % (x2 - x1); 286 x2 = x1 + rand() % (x2 - x1); 287 y1 = y1 + rand() % (y2 - y1); 288 y2 = y1 + rand() % (y2 - y1); 289 290 trap->left.p1.x = trap->left.p2.x = x1 << 16; 291 trap->top = trap->left.p1.y = trap->right.p1.y = y1 << 16; 292 trap->right.p1.x = trap->right.p2.x = x2 << 16; 293 trap->bottom = trap->left.p2.y = trap->right.p2.y = y2 << 16; 294 break; 295 296 case RECT_UNALIGN: 297 x1 <<= 16; x2 <<= 16; 298 y1 <<= 16; y2 <<= 16; 299 300 x1 = x1 + rand() % (x2 - x1); 301 x2 = x1 + rand() % (x2 - x1); 302 y1 = y1 + rand() % (y2 - y1); 303 y2 = y1 + rand() % (y2 - y1); 304 305 trap->left.p1.x = trap->left.p2.x = x1; 306 trap->top = trap->left.p1.y = trap->right.p1.y = y1; 307 trap->right.p1.x = trap->right.p2.x = x2; 308 trap->bottom = trap->left.p2.y = trap->right.p2.y = y2; 309 break; 310 311 case GENERAL: 312 x1 <<= 16; x2 <<= 16; 313 y1 <<= 16; y2 <<= 16; 314 315 trap->top = y1 + rand() % (y2 - y1); 316 trap->bottom = y1 + rand() % (y2 - y1); 317 318 trap->left.p1.x = x1 + rand() % (x2 - x1); 319 trap->left.p2.x = x1 + rand() % (x2 - x1); 320 321 trap->right.p1.x = x1 + rand() % (x2 - x1); 322 trap->right.p2.x = x1 + rand() % (x2 - x1); 323 break; 324 325 } 326} 327 328static void trap_tests(struct test *t, 329 enum mask mask, 330 enum trapezoid trapezoid, 331 int reps, int sets, 332 enum target target) 333{ 334 struct test_target out, ref; 335 XTrapezoid *traps; 336 int max_traps = 65536; 337 int r, s, n; 338 339 traps = malloc(sizeof(*traps) * max_traps); 340 if (traps == NULL) 341 return; 342 343 printf("Testing trapezoids (%s with mask %s) (%s): ", 344 trapezoid_name(trapezoid), 345 mask_name(mask), 346 test_target_name(target)); 347 fflush(stdout); 348 349 test_target_create_render(&t->out, target, &out); 350 clear(&t->out, &out); 351 352 test_target_create_render(&t->ref, target, &ref); 353 clear(&t->ref, &ref); 354 355 for (s = 0; s < sets; s++) { 356 for (r = 0; r < reps; r++) { 357 XRenderColor render_color; 358 int op = ops[rand() % sizeof(ops)]; 359 int red = rand() % 0xff; 360 int green = rand() % 0xff; 361 int blue = rand() % 0xff; 362 int alpha = rand() % 0xff; 363 int num_traps = rand() % max_traps; 364 Picture src; 365 366 for (n = 0; n < num_traps; n++) 367 random_trapezoid(&traps[n], 0, 368 0, 0, out.width, out.height); 369 370 render_color.red = red * alpha; 371 render_color.green = green * alpha; 372 render_color.blue = blue * alpha; 373 render_color.alpha = alpha << 8; 374 375 src = XRenderCreateSolidFill(t->out.dpy, 376 &render_color); 377 XRenderCompositeTrapezoids(t->out.dpy, 378 op, src, out.picture, 379 mask_format(t->out.dpy, mask), 380 0, 0, traps, num_traps); 381 XRenderFreePicture(t->out.dpy, src); 382 383 src = XRenderCreateSolidFill(t->ref.dpy, 384 &render_color); 385 XRenderCompositeTrapezoids(t->ref.dpy, 386 op, src, ref.picture, 387 mask_format(t->ref.dpy, mask), 388 0, 0, traps, num_traps); 389 XRenderFreePicture(t->ref.dpy, src); 390 } 391 392 test_compare(t, 393 out.draw, out.format, 394 ref.draw, ref.format, 395 0, 0, out.width, out.height, 396 ""); 397 } 398 399 printf("passed [%d iterations x %d]\n", reps, sets); 400 401 test_target_destroy_render(&t->out, &out); 402 test_target_destroy_render(&t->ref, &ref); 403 free(traps); 404} 405 406enum edge { 407 EDGE_SHARP = PolyEdgeSharp, 408 EDGE_SMOOTH, 409}; 410 411static const char *edge_name(enum edge edge) 412{ 413 switch (edge) { 414 default: 415 case EDGE_SHARP: return "sharp"; 416 case EDGE_SMOOTH: return "smooth"; 417 } 418} 419 420static void set_edge(Display *dpy, Picture p, enum edge edge) 421{ 422 XRenderPictureAttributes a; 423 424 a.poly_edge = edge; 425 XRenderChangePicture(dpy, p, CPPolyEdge, &a); 426} 427 428static void edge_test(struct test *t, 429 enum mask mask, 430 enum edge edge, 431 enum target target) 432{ 433 struct test_target out, ref; 434 XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; 435 Picture src_ref, src_out; 436 XTrapezoid trap; 437 int left_or_right, p; 438 439 test_target_create_render(&t->out, target, &out); 440 set_edge(t->out.dpy, out.picture, edge); 441 src_out = XRenderCreateSolidFill(t->out.dpy, &white); 442 443 test_target_create_render(&t->ref, target, &ref); 444 set_edge(t->ref.dpy, ref.picture, edge); 445 src_ref = XRenderCreateSolidFill(t->ref.dpy, &white); 446 447 printf("Testing edges (with mask %s and %s edges) (%s): ", 448 mask_name(mask), 449 edge_name(edge), 450 test_target_name(target)); 451 fflush(stdout); 452 453 for (left_or_right = 0; left_or_right <= 1; left_or_right++) { 454 for (p = -64; p <= out.width + 64; p++) { 455 char buf[80]; 456 457 if (left_or_right) { 458 trap.left.p1.x = 0; 459 trap.left.p1.y = 0; 460 trap.left.p2.x = 0; 461 trap.left.p2.y = out.height << 16; 462 463 trap.right.p1.x = p << 16; 464 trap.right.p1.y = 0; 465 trap.right.p2.x = out.width << 16; 466 trap.right.p2.y = out.height << 16; 467 } else { 468 trap.right.p1.x = out.width << 16; 469 trap.right.p1.y = 0; 470 trap.right.p2.x = out.width << 16; 471 trap.right.p2.y = out.height << 16; 472 473 trap.left.p1.x = 0; 474 trap.left.p1.y = 0; 475 trap.left.p2.x = p << 16; 476 trap.left.p2.y = out.height << 16; 477 } 478 479 trap.top = 0; 480 trap.bottom = out.height << 16; 481 482 sprintf(buf, 483 "trap=((%d, %d), (%d, %d)), ((%d, %d), (%d, %d))\n", 484 trap.left.p1.x >> 16, trap.left.p1.y >> 16, 485 trap.left.p2.x >> 16, trap.left.p2.y >> 16, 486 trap.right.p1.x >> 16, trap.right.p1.y >> 16, 487 trap.right.p2.x >> 16, trap.right.p2.y >> 16); 488 489 clear(&t->out, &out); 490 XRenderCompositeTrapezoids(t->out.dpy, 491 PictOpSrc, 492 src_out, 493 out.picture, 494 mask_format(t->out.dpy, mask), 495 0, 0, 496 &trap, 1); 497 498 clear(&t->ref, &ref); 499 XRenderCompositeTrapezoids(t->ref.dpy, 500 PictOpSrc, 501 src_ref, 502 ref.picture, 503 mask_format(t->ref.dpy, mask), 504 0, 0, 505 &trap, 1); 506 507 test_compare(t, 508 out.draw, out.format, 509 ref.draw, ref.format, 510 0, 0, out.width, out.height, 511 buf); 512 } 513 } 514 515 XRenderFreePicture(t->out.dpy, src_out); 516 test_target_destroy_render(&t->out, &out); 517 518 XRenderFreePicture(t->ref.dpy, src_ref); 519 test_target_destroy_render(&t->ref, &ref); 520 521 printf("pass\n"); 522} 523 524int main(int argc, char **argv) 525{ 526 struct test test; 527 int i, dx, dy; 528 enum target target; 529 enum mask mask; 530 enum edge edge; 531 enum trapezoid trapezoid; 532 533 test_init(&test, argc, argv); 534 535 for (target = TARGET_FIRST; target <= TARGET_LAST; target++) { 536 for (mask = MASK_NONE; mask <= MASK_A8; mask++) 537 for (edge = EDGE_SHARP; edge <= EDGE_SMOOTH; edge++) 538 edge_test(&test, mask, edge, target); 539 } 540 541 for (i = 0; i <= DEFAULT_ITERATIONS; i++) { 542 int reps = REPS(i), sets = SETS(i); 543 544 for (target = TARGET_FIRST; target <= TARGET_LAST; target++) { 545 pixel_tests(&test, reps, sets, target); 546 area_tests(&test, reps, sets, target); 547 for (dy = 0; dy < 1 << 16; dy += 1 << 14) 548 for (dx = 0; dx < 1 << 16; dx += 1 << 14) 549 for (mask = MASK_NONE; mask <= MASK_A8; mask++) 550 rect_tests(&test, dx, dy, mask, reps, sets, target); 551 for (trapezoid = RECT_ALIGN; trapezoid <= GENERAL; trapezoid++) 552 trap_tests(&test, mask, trapezoid, reps, sets, target); 553 } 554 } 555 556 return 0; 557} 558