]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libmemdraw/drawtest.c
libmemdraw: apply erik quanstros fix for sign preserving in byteaddr()
[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 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
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 //      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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }