xref: /inferno-os/libmemdraw/drawtest.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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