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