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