xref: /plan9/sys/src/cmd/unix/drawterm/libmemdraw/drawtest.c (revision 8ccd4a6360d974db7bd7bbd4f37e7018419ea908)
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
rdb(void)53 rdb(void)
54 {
55 }
56 
57 int
iprint(char * fmt,...)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
main(int argc,char * argv[])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
Bprintr5g6b5(Biobuf * bio,char *,ulong v)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
Bprintr5g5b5a1(Biobuf * bio,char *,ulong v)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
dumpimage(char * name,Memimage * img,void * vdata,Point labelpt)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
checkone(Point p,Point sp,Point mp)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
checkline(Rectangle r,Point sp,Point mp,int y,Memimage * stmp,Memimage * mtmp)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
fill(Memimage * img,uchar * ucbits)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
verifyonemask(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
verifyone(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
verifylinemask(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
verifyline(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
verifyrectmask(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
verifyrect(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
randrect(void)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
tilexy(int minx,int maxx,int x)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
replicate(Memimage * i,Memimage * tmp)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
verifyrectmaskrepl(int srcrepl,int maskrepl)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
verifyrectrepl(int srcrepl,int maskrepl)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
replbits(ulong v,int nhave,int nwant)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
pixtorgba(ulong v,uchar * r,uchar * g,uchar * b,uchar * a)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
rgbatopix(uchar r,uchar g,uchar b,uchar a)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
getpixel(Memimage * img,Point pt)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
getgrey(Memimage * img,Point pt)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
getmask(Memimage * img,Point pt)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
putpixel(Memimage * img,Point pt,ulong nv)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
drawonepixel(Memimage * dst,Point dp,Memimage * src,Point sp,Memimage * mask,Point mp)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