plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }