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