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