drawtest.c (23510B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 #include <memdraw.h> 6 7 #define DBG if(0) 8 #define RGB2K(r,g,b) ((299*((u32int)(r))+587*((u32int)(g))+114*((u32int)(b)))/1000) 9 10 /* 11 * This program tests the 'memimagedraw' primitive stochastically. 12 * It tests the combination aspects of it thoroughly, but since the 13 * three images it uses are disjoint, it makes no check of the 14 * correct behavior when images overlap. That is, however, much 15 * easier to get right and to test. 16 */ 17 18 void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point); 19 void verifyone(void); 20 void verifyline(void); 21 void verifyrect(void); 22 void verifyrectrepl(int, int); 23 void putpixel(Memimage *img, Point pt, u32int nv); 24 u32int rgbatopix(uchar, uchar, uchar, uchar); 25 26 char *dchan, *schan, *mchan; 27 int dbpp, sbpp, mbpp; 28 29 int drawdebug=0; 30 int seed; 31 int niters = 100; 32 int dbpp; /* bits per pixel in destination */ 33 int sbpp; /* bits per pixel in src */ 34 int mbpp; /* bits per pixel in mask */ 35 int dpm; /* pixel mask at high part of byte, in destination */ 36 int nbytes; /* in destination */ 37 38 int Xrange = 64; 39 int Yrange = 8; 40 41 Memimage *dst; 42 Memimage *src; 43 Memimage *mask; 44 Memimage *stmp; 45 Memimage *mtmp; 46 Memimage *ones; 47 uchar *dstbits; 48 uchar *srcbits; 49 uchar *maskbits; 50 u32int *savedstbits; 51 52 void 53 rdb(void) 54 { 55 } 56 57 int 58 iprint(char *fmt, ...) 59 { 60 int n; 61 va_list va; 62 char buf[1024]; 63 64 va_start(va, fmt); 65 n = vseprint(buf, buf+sizeof buf, fmt, va) - buf; 66 va_end(va); 67 68 write(1,buf,n); 69 return 1; 70 } 71 72 void 73 main(int argc, char *argv[]) 74 { 75 memimageinit(); 76 seed = time(0); 77 78 ARGBEGIN{ 79 case 'x': 80 Xrange = atoi(ARGF()); 81 break; 82 case 'y': 83 Yrange = atoi(ARGF()); 84 break; 85 case 'n': 86 niters = atoi(ARGF()); 87 break; 88 case 's': 89 seed = atoi(ARGF()); 90 break; 91 }ARGEND 92 93 dchan = "r8g8b8"; 94 schan = "r8g8b8"; 95 mchan = "r8g8b8"; 96 switch(argc){ 97 case 3: mchan = argv[2]; 98 case 2: schan = argv[1]; 99 case 1: dchan = argv[0]; 100 case 0: break; 101 default: goto Usage; 102 Usage: 103 fprint(2, "usage: dtest [dchan [schan [mchan]]]\n"); 104 exits("usage"); 105 } 106 107 fprint(2, "%s -x %d -y %d -s 0x%x %s %s %s\n", argv0, Xrange, Yrange, seed, dchan, schan, mchan); 108 srand(seed); 109 110 dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan)); 111 src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); 112 mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); 113 stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); 114 mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); 115 ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); 116 /* print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan); */ 117 if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) { 118 Alloc: 119 fprint(2, "dtest: allocation failed: %r\n"); 120 exits("alloc"); 121 } 122 nbytes = (4*Xrange+4)*Yrange; 123 srcbits = malloc(nbytes); 124 dstbits = malloc(nbytes); 125 maskbits = malloc(nbytes); 126 savedstbits = malloc(nbytes); 127 if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0) 128 goto Alloc; 129 dbpp = dst->depth; 130 sbpp = src->depth; 131 mbpp = mask->depth; 132 dpm = 0xFF ^ (0xFF>>dbpp); 133 memset(ones->data->bdata, 0xFF, ones->width*sizeof(u32int)*Yrange); 134 135 136 fprint(2, "dtest: verify single pixel operation\n"); 137 verifyone(); 138 139 fprint(2, "dtest: verify full line non-replicated\n"); 140 verifyline(); 141 142 fprint(2, "dtest: verify full rectangle non-replicated\n"); 143 verifyrect(); 144 145 fprint(2, "dtest: verify full rectangle source replicated\n"); 146 verifyrectrepl(1, 0); 147 148 fprint(2, "dtest: verify full rectangle mask replicated\n"); 149 verifyrectrepl(0, 1); 150 151 fprint(2, "dtest: verify full rectangle source and mask replicated\n"); 152 verifyrectrepl(1, 1); 153 154 exits(0); 155 } 156 157 /* 158 * Dump out an ASCII representation of an image. The label specifies 159 * a list of characters to put at various points in the picture. 160 */ 161 static void 162 Bprintr5g6b5(Biobuf *bio, char* _, u32int v) 163 { 164 int r,g,b; 165 r = (v>>11)&31; 166 g = (v>>5)&63; 167 b = v&31; 168 Bprint(bio, "%.2x%.2x%.2x", r,g,b); 169 } 170 171 static void 172 Bprintr5g5b5a1(Biobuf *bio, char* _, u32int v) 173 { 174 int r,g,b,a; 175 r = (v>>11)&31; 176 g = (v>>6)&31; 177 b = (v>>1)&31; 178 a = v&1; 179 Bprint(bio, "%.2x%.2x%.2x%.2x", r,g,b,a); 180 } 181 182 void 183 dumpimage(char *name, Memimage *img, void *vdata, Point labelpt) 184 { 185 Biobuf b; 186 uchar *data; 187 uchar *p; 188 char *arg; 189 void (*fmt)(Biobuf*, char*, u32int); 190 int npr, x, y, nb, bpp; 191 u32int v, mask; 192 Rectangle r; 193 194 fmt = nil; 195 arg = nil; 196 switch(img->depth){ 197 case 1: 198 case 2: 199 case 4: 200 fmt = (void(*)(Biobuf*,char*,u32int))Bprint; 201 arg = "%.1ux"; 202 break; 203 case 8: 204 fmt = (void(*)(Biobuf*,char*,u32int))Bprint; 205 arg = "%.2ux"; 206 break; 207 case 16: 208 arg = nil; 209 if(img->chan == RGB16) 210 fmt = Bprintr5g6b5; 211 else{ 212 fmt = (void(*)(Biobuf*,char*,u32int))Bprint; 213 arg = "%.4ux"; 214 } 215 break; 216 case 24: 217 fmt = (void(*)(Biobuf*,char*,u32int))Bprint; 218 arg = "%.6lux"; 219 break; 220 case 32: 221 fmt = (void(*)(Biobuf*,char*,u32int))Bprint; 222 arg = "%.8lux"; 223 break; 224 } 225 if(fmt == nil){ 226 fprint(2, "bad format\n"); 227 abort(); 228 } 229 230 r = img->r; 231 Binit(&b, 2, OWRITE); 232 data = vdata; 233 bpp = img->depth; 234 Bprint(&b, "%s\t%d\tr %R clipr %R repl %d data %p *%P\n", name, r.min.x, r, img->clipr, (img->flags&Frepl) ? 1 : 0, vdata, labelpt); 235 mask = (1ULL<<bpp)-1; 236 /* for(y=r.min.y; y<r.max.y; y++){ */ 237 for(y=0; y<Yrange; y++){ 238 nb = 0; 239 v = 0; 240 p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata); 241 Bprint(&b, "%-4d\t", y); 242 /* for(x=r.min.x; x<r.max.x; x++){ */ 243 for(x=0; x<Xrange; x++){ 244 if(x==0) 245 Bprint(&b, "\t"); 246 247 if(x != 0 && (x%8)==0) 248 Bprint(&b, " "); 249 250 npr = 0; 251 if(x==labelpt.x && y==labelpt.y){ 252 Bprint(&b, "*"); 253 npr++; 254 } 255 if(npr == 0) 256 Bprint(&b, " "); 257 258 while(nb < bpp){ 259 v &= (1<<nb)-1; 260 v |= (u32int)(*p++) << nb; 261 nb += 8; 262 } 263 nb -= bpp; 264 /* print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v, mask, nb); */ 265 fmt(&b, arg, (v>>nb)&mask); 266 } 267 Bprint(&b, "\n"); 268 } 269 Bterm(&b); 270 } 271 272 /* 273 * Verify that the destination pixel has the specified value. 274 * The value is in the high bits of v, suitably masked, but must 275 * be extracted from the destination Memimage. 276 */ 277 void 278 checkone(Point p, Point sp, Point mp) 279 { 280 int delta; 281 uchar *dp, *sdp; 282 283 delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata; 284 dp = (uchar*)dst->data->bdata+delta; 285 sdp = (uchar*)savedstbits+delta; 286 287 if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) { 288 fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp); 289 fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n", 290 dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]); 291 fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp)); 292 dumpimage("src", src, src->data->bdata, sp); 293 dumpimage("mask", mask, mask->data->bdata, mp); 294 dumpimage("origdst", dst, dstbits, p); 295 dumpimage("dst", dst, dst->data->bdata, p); 296 dumpimage("gooddst", dst, savedstbits, p); 297 abort(); 298 } 299 } 300 301 /* 302 * Verify that the destination line has the same value as the saved line. 303 */ 304 #define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y 305 void 306 checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp) 307 { 308 u32int *dp; 309 int nb; 310 u32int *saved; 311 312 dp = wordaddr(dst, Pt(0, y)); 313 saved = savedstbits + y*dst->width; 314 if(dst->depth < 8) 315 nb = Xrange/(8/dst->depth); 316 else 317 nb = Xrange*(dst->depth/8); 318 if(memcmp(dp, saved, nb) != 0){ 319 fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp); 320 fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp); 321 dumpimage("src", src, src->data->bdata, sp); 322 if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp); 323 dumpimage("mask", mask, mask->data->bdata, mp); 324 if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp); 325 dumpimage("origdst", dst, dstbits, r.min); 326 dumpimage("dst", dst, dst->data->bdata, r.min); 327 dumpimage("gooddst", dst, savedstbits, r.min); 328 abort(); 329 } 330 } 331 332 /* 333 * Fill the bits of an image with random data. 334 * The Memimage parameter is used only to make sure 335 * the data is well formatted: only ucbits is written. 336 */ 337 void 338 fill(Memimage *img, uchar *ucbits) 339 { 340 int i, x, y; 341 ushort *up; 342 uchar alpha, r, g, b; 343 void *data; 344 345 if((img->flags&Falpha) == 0){ 346 up = (ushort*)ucbits; 347 for(i=0; i<nbytes/2; i++) 348 *up++ = lrand() >> 7; 349 if(i+i != nbytes) 350 *(uchar*)up = lrand() >> 7; 351 }else{ 352 data = img->data->bdata; 353 img->data->bdata = ucbits; 354 355 for(x=img->r.min.x; x<img->r.max.x; x++) 356 for(y=img->r.min.y; y<img->r.max.y; y++){ 357 alpha = rand() >> 4; 358 r = rand()%(alpha+1); 359 g = rand()%(alpha+1); 360 b = rand()%(alpha+1); 361 putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha)); 362 } 363 img->data->bdata = data; 364 } 365 366 } 367 368 /* 369 * Mask is preset; do the rest 370 */ 371 void 372 verifyonemask(void) 373 { 374 Point dp, sp, mp; 375 376 fill(dst, dstbits); 377 fill(src, srcbits); 378 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 379 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); 380 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 381 382 dp.x = nrand(Xrange); 383 dp.y = nrand(Yrange); 384 385 sp.x = nrand(Xrange); 386 sp.y = nrand(Yrange); 387 388 mp.x = nrand(Xrange); 389 mp.y = nrand(Yrange); 390 391 drawonepixel(dst, dp, src, sp, mask, mp); 392 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 393 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); 394 395 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 396 memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD); 397 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 398 399 checkone(dp, sp, mp); 400 } 401 402 void 403 verifyone(void) 404 { 405 int i; 406 407 /* mask all zeros */ 408 memset(maskbits, 0, nbytes); 409 for(i=0; i<niters; i++) 410 verifyonemask(); 411 412 /* mask all ones */ 413 memset(maskbits, 0xFF, nbytes); 414 for(i=0; i<niters; i++) 415 verifyonemask(); 416 417 /* random mask */ 418 for(i=0; i<niters; i++){ 419 fill(mask, maskbits); 420 verifyonemask(); 421 } 422 } 423 424 /* 425 * Mask is preset; do the rest 426 */ 427 void 428 verifylinemask(void) 429 { 430 Point sp, mp, tp, up; 431 Rectangle dr; 432 int x; 433 434 fill(dst, dstbits); 435 fill(src, srcbits); 436 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 437 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); 438 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 439 440 dr.min.x = nrand(Xrange-1); 441 dr.min.y = nrand(Yrange-1); 442 dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); 443 dr.max.y = dr.min.y + 1; 444 445 sp.x = nrand(Xrange); 446 sp.y = nrand(Yrange); 447 448 mp.x = nrand(Xrange); 449 mp.y = nrand(Yrange); 450 451 tp = sp; 452 up = mp; 453 for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++) 454 memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp, mask, up, SoverD); 455 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); 456 457 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 458 459 memimagedraw(dst, dr, src, sp, mask, mp, SoverD); 460 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil); 461 } 462 463 void 464 verifyline(void) 465 { 466 int i; 467 468 /* mask all ones */ 469 memset(maskbits, 0xFF, nbytes); 470 for(i=0; i<niters; i++) 471 verifylinemask(); 472 473 /* mask all zeros */ 474 memset(maskbits, 0, nbytes); 475 for(i=0; i<niters; i++) 476 verifylinemask(); 477 478 /* random mask */ 479 for(i=0; i<niters; i++){ 480 fill(mask, maskbits); 481 verifylinemask(); 482 } 483 } 484 485 /* 486 * Mask is preset; do the rest 487 */ 488 void 489 verifyrectmask(void) 490 { 491 Point sp, mp, tp, up; 492 Rectangle dr; 493 int x, y; 494 495 fill(dst, dstbits); 496 fill(src, srcbits); 497 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 498 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); 499 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 500 501 dr.min.x = nrand(Xrange-1); 502 dr.min.y = nrand(Yrange-1); 503 dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); 504 dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y); 505 506 sp.x = nrand(Xrange); 507 sp.y = nrand(Yrange); 508 509 mp.x = nrand(Xrange); 510 mp.y = nrand(Yrange); 511 512 tp = sp; 513 up = mp; 514 for(y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++){ 515 for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++) 516 memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask, up, SoverD); 517 tp.x = sp.x; 518 up.x = mp.x; 519 } 520 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); 521 522 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 523 524 memimagedraw(dst, dr, src, sp, mask, mp, SoverD); 525 for(y=0; y<Yrange; y++) 526 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, nil, nil); 527 } 528 529 void 530 verifyrect(void) 531 { 532 int i; 533 534 /* mask all zeros */ 535 memset(maskbits, 0, nbytes); 536 for(i=0; i<niters; i++) 537 verifyrectmask(); 538 539 /* mask all ones */ 540 memset(maskbits, 0xFF, nbytes); 541 for(i=0; i<niters; i++) 542 verifyrectmask(); 543 544 /* random mask */ 545 for(i=0; i<niters; i++){ 546 fill(mask, maskbits); 547 verifyrectmask(); 548 } 549 } 550 551 Rectangle 552 randrect(void) 553 { 554 Rectangle r; 555 556 r.min.x = nrand(Xrange-1); 557 r.min.y = nrand(Yrange-1); 558 r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x); 559 r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y); 560 return r; 561 } 562 563 /* 564 * Return coordinate corresponding to x withing range [minx, maxx) 565 */ 566 int 567 tilexy(int minx, int maxx, int x) 568 { 569 int sx; 570 571 sx = (x-minx) % (maxx-minx); 572 if(sx < 0) 573 sx += maxx-minx; 574 return sx+minx; 575 } 576 577 void 578 replicate(Memimage *i, Memimage *tmp) 579 { 580 Rectangle r, r1; 581 int x, y, nb; 582 583 /* choose the replication window (i->r) */ 584 r.min.x = nrand(Xrange-1); 585 r.min.y = nrand(Yrange-1); 586 /* make it trivial more often than pure chance allows */ 587 switch(lrand()&0){ 588 case 1: 589 r.max.x = r.min.x + 2; 590 r.max.y = r.min.y + 2; 591 if(r.max.x < Xrange && r.max.y < Yrange) 592 break; 593 /* fall through */ 594 case 0: 595 r.max.x = r.min.x + 1; 596 r.max.y = r.min.y + 1; 597 break; 598 default: 599 if(r.min.x+3 >= Xrange) 600 r.max.x = Xrange; 601 else 602 r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3)); 603 604 if(r.min.y+3 >= Yrange) 605 r.max.y = Yrange; 606 else 607 r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3)); 608 } 609 assert(r.min.x >= 0); 610 assert(r.max.x <= Xrange); 611 assert(r.min.y >= 0); 612 assert(r.max.y <= Yrange); 613 /* copy from i to tmp so we have just the replicated bits */ 614 nb = tmp->width*sizeof(u32int)*Yrange; 615 memset(tmp->data->bdata, 0, nb); 616 memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD); 617 memmove(i->data->bdata, tmp->data->bdata, nb); 618 /* i is now a non-replicated instance of the replication */ 619 /* replicate it by hand through tmp */ 620 memset(tmp->data->bdata, 0, nb); 621 x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x); 622 for(; x<Xrange; x+=Dx(r)){ 623 y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y); 624 for(; y<Yrange; y+=Dy(r)){ 625 /* set r1 to instance of tile by translation */ 626 r1.min.x = x; 627 r1.min.y = y; 628 r1.max.x = r1.min.x+Dx(r); 629 r1.max.y = r1.min.y+Dy(r); 630 memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD); 631 } 632 } 633 i->flags |= Frepl; 634 i->r = r; 635 i->clipr = randrect(); 636 /* fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y, */ 637 /* i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); */ 638 tmp->clipr = i->clipr; 639 } 640 641 /* 642 * Mask is preset; do the rest 643 */ 644 void 645 verifyrectmaskrepl(int srcrepl, int maskrepl) 646 { 647 Point sp, mp, tp, up; 648 Rectangle dr; 649 int x, y; 650 Memimage *s, *m; 651 652 /* print("verfrect %d %d\n", srcrepl, maskrepl); */ 653 src->flags &= ~Frepl; 654 src->r = Rect(0, 0, Xrange, Yrange); 655 src->clipr = src->r; 656 stmp->flags &= ~Frepl; 657 stmp->r = Rect(0, 0, Xrange, Yrange); 658 stmp->clipr = src->r; 659 mask->flags &= ~Frepl; 660 mask->r = Rect(0, 0, Xrange, Yrange); 661 mask->clipr = mask->r; 662 mtmp->flags &= ~Frepl; 663 mtmp->r = Rect(0, 0, Xrange, Yrange); 664 mtmp->clipr = mask->r; 665 666 fill(dst, dstbits); 667 fill(src, srcbits); 668 669 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 670 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); 671 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); 672 673 if(srcrepl){ 674 replicate(src, stmp); 675 s = stmp; 676 }else 677 s = src; 678 if(maskrepl){ 679 replicate(mask, mtmp); 680 m = mtmp; 681 }else 682 m = mask; 683 684 dr = randrect(); 685 686 sp.x = nrand(Xrange); 687 sp.y = nrand(Yrange); 688 689 mp.x = nrand(Xrange); 690 mp.y = nrand(Yrange); 691 692 DBG print("smalldraws\n"); 693 for(tp.y=sp.y,up.y=mp.y,y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++) 694 for(tp.x=sp.x,up.x=mp.x,x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++) 695 memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, SoverD); 696 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); 697 698 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); 699 700 DBG print("bigdraw\n"); 701 memimagedraw(dst, dr, src, sp, mask, mp, SoverD); 702 for(y=0; y<Yrange; y++) 703 checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil); 704 } 705 706 void 707 verifyrectrepl(int srcrepl, int maskrepl) 708 { 709 int i; 710 711 /* mask all ones */ 712 memset(maskbits, 0xFF, nbytes); 713 for(i=0; i<niters; i++) 714 verifyrectmaskrepl(srcrepl, maskrepl); 715 716 /* mask all zeros */ 717 memset(maskbits, 0, nbytes); 718 for(i=0; i<niters; i++) 719 verifyrectmaskrepl(srcrepl, maskrepl); 720 721 /* random mask */ 722 for(i=0; i<niters; i++){ 723 fill(mask, maskbits); 724 verifyrectmaskrepl(srcrepl, maskrepl); 725 } 726 } 727 728 /* 729 * Trivial draw implementation. 730 * Color values are passed around as u32ints containing ααRRGGBB 731 */ 732 733 /* 734 * Convert v, which is nhave bits wide, into its nwant bits wide equivalent. 735 * Replicates to widen the value, truncates to narrow it. 736 */ 737 u32int 738 replbits(u32int v, int nhave, int nwant) 739 { 740 v &= (1<<nhave)-1; 741 for(; nhave<nwant; nhave*=2) 742 v |= v<<nhave; 743 v >>= (nhave-nwant); 744 return v & ((1<<nwant)-1); 745 } 746 747 /* 748 * Decode a pixel into the uchar* values. 749 */ 750 void 751 pixtorgba(u32int v, uchar *r, uchar *g, uchar *b, uchar *a) 752 { 753 *a = v>>24; 754 *r = v>>16; 755 *g = v>>8; 756 *b = v; 757 } 758 759 /* 760 * Convert uchar channels into u32int pixel. 761 */ 762 u32int 763 rgbatopix(uchar r, uchar g, uchar b, uchar a) 764 { 765 return (a<<24)|(r<<16)|(g<<8)|b; 766 } 767 768 /* 769 * Retrieve the pixel value at pt in the image. 770 */ 771 u32int 772 getpixel(Memimage *img, Point pt) 773 { 774 uchar r, g, b, a, *p; 775 int nbits, npack, bpp; 776 u32int v, c, rbits, bits; 777 778 r = g = b = 0; 779 a = ~0; /* default alpha is full */ 780 781 p = byteaddr(img, pt); 782 v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); 783 bpp = img->depth; 784 if(bpp<8){ 785 /* 786 * Sub-byte greyscale pixels. 787 * 788 * We want to throw away the top pt.x%npack pixels and then use the next bpp bits 789 * in the bottom byte of v. This madness is due to having big endian bits 790 * but little endian bytes. 791 */ 792 npack = 8/bpp; 793 v >>= 8 - bpp*(pt.x%npack+1); 794 v &= (1<<bpp)-1; 795 r = g = b = replbits(v, bpp, 8); 796 }else{ 797 /* 798 * General case. We need to parse the channel descriptor and do what it says. 799 * In all channels but the color map, we replicate to 8 bits because that's the 800 * precision that all calculations are done at. 801 * 802 * In the case of the color map, we leave the bits alone, in case a color map 803 * with less than 8 bits of index is used. This is currently disallowed, so it's 804 * sort of silly. 805 */ 806 807 for(c=img->chan; c; c>>=8){ 808 nbits = NBITS(c); 809 bits = v & ((1<<nbits)-1); 810 rbits = replbits(bits, nbits, 8); 811 v >>= nbits; 812 switch(TYPE(c)){ 813 case CRed: 814 r = rbits; 815 break; 816 case CGreen: 817 g = rbits; 818 break; 819 case CBlue: 820 b = rbits; 821 break; 822 case CGrey: 823 r = g = b = rbits; 824 break; 825 case CAlpha: 826 a = rbits; 827 break; 828 case CMap: 829 p = img->cmap->cmap2rgb + 3*bits; 830 r = p[0]; 831 g = p[1]; 832 b = p[2]; 833 break; 834 case CIgnore: 835 break; 836 default: 837 fprint(2, "unknown channel type %lud\n", TYPE(c)); 838 abort(); 839 } 840 } 841 } 842 return rgbatopix(r, g, b, a); 843 } 844 845 /* 846 * Return the greyscale equivalent of a pixel. 847 */ 848 uchar 849 getgrey(Memimage *img, Point pt) 850 { 851 uchar r, g, b, a; 852 pixtorgba(getpixel(img, pt), &r, &g, &b, &a); 853 return RGB2K(r, g, b); 854 } 855 856 /* 857 * Return the value at pt in image, if image is interpreted 858 * as a mask. This means the alpha channel if present, else 859 * the greyscale or its computed equivalent. 860 */ 861 uchar 862 getmask(Memimage *img, Point pt) 863 { 864 if(img->flags&Falpha) 865 return getpixel(img, pt)>>24; 866 else 867 return getgrey(img, pt); 868 } 869 #undef DBG 870 871 #define DBG if(0) 872 /* 873 * Write a pixel to img at point pt. 874 * 875 * We do this by reading a 32-bit little endian 876 * value from p and then writing it back 877 * after tweaking the appropriate bits. Because 878 * the data is little endian, we don't have to worry 879 * about what the actual depth is, as long as it is 880 * less than 32 bits. 881 */ 882 void 883 putpixel(Memimage *img, Point pt, u32int nv) 884 { 885 uchar r, g, b, a, *p, *q; 886 u32int c, mask, bits, v; 887 int bpp, sh, npack, nbits; 888 889 pixtorgba(nv, &r, &g, &b, &a); 890 891 p = byteaddr(img, pt); 892 v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); 893 bpp = img->depth; 894 DBG print("v %.8lux...", v); 895 if(bpp < 8){ 896 /* 897 * Sub-byte greyscale pixels. We need to skip the leftmost pt.x%npack pixels, 898 * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels. 899 */ 900 npack = 8/bpp; 901 sh = bpp*(npack - pt.x%npack - 1); 902 bits = RGB2K(r,g,b); 903 DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp)); 904 bits = replbits(bits, 8, bpp); 905 mask = (1<<bpp)-1; 906 DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); 907 mask <<= sh; 908 bits <<= sh; 909 DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask); 910 v = (v & ~mask) | (bits & mask); 911 } else { 912 /* 913 * General case. We need to parse the channel descriptor again. 914 */ 915 sh = 0; 916 for(c=img->chan; c; c>>=8){ 917 nbits = NBITS(c); 918 switch(TYPE(c)){ 919 case CRed: 920 bits = r; 921 break; 922 case CGreen: 923 bits = g; 924 break; 925 case CBlue: 926 bits = b; 927 break; 928 case CGrey: 929 bits = RGB2K(r, g, b); 930 break; 931 case CAlpha: 932 bits = a; 933 break; 934 case CIgnore: 935 bits = 0; 936 break; 937 case CMap: 938 q = img->cmap->rgb2cmap; 939 bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)]; 940 break; 941 default: 942 SET(bits); 943 fprint(2, "unknown channel type %lud\n", TYPE(c)); 944 abort(); 945 } 946 947 DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits)); 948 if(TYPE(c) != CMap) 949 bits = replbits(bits, 8, nbits); 950 mask = (1<<nbits)-1; 951 DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); 952 bits <<= sh; 953 mask <<= sh; 954 v = (v & ~mask) | (bits & mask); 955 sh += nbits; 956 } 957 } 958 DBG print("v %.8lux\n", v); 959 p[0] = v; 960 p[1] = v>>8; 961 p[2] = v>>16; 962 p[3] = v>>24; 963 } 964 #undef DBG 965 966 #define DBG if(0) 967 void 968 drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp) 969 { 970 uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk; 971 972 pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da); 973 pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa); 974 m = getmask(mask, mp); 975 M = 255-(sa*m + 127)/255; 976 977 DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m); 978 if(dst->flags&Fgrey){ 979 /* 980 * We need to do the conversion to grey before the alpha calculation 981 * because the draw operator does this, and we need to be operating 982 * at the same precision so we get exactly the same answers. 983 */ 984 sk = RGB2K(sr, sg, sb); 985 dk = RGB2K(dr, dg, db); 986 dk = (sk*m + dk*M + 127)/255; 987 dr = dg = db = dk; 988 da = (sa*m + da*M + 127)/255; 989 }else{ 990 /* 991 * True color alpha calculation treats all channels (including alpha) 992 * the same. It might have been nice to use an array, but oh well. 993 */ 994 dr = (sr*m + dr*M + 127)/255; 995 dg = (sg*m + dg*M + 127)/255; 996 db = (sb*m + db*M + 127)/255; 997 da = (sa*m + da*M + 127)/255; 998 } 999 1000 DBG print("%x %x %x %x\n", dr,dg,db,da); 1001 putpixel(dst, dp, rgbatopix(dr, dg, db, da)); 1002 }