Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <memdraw.h>
7 #define DBG if(0)
8 #define RGB2K(r,g,b) ((299*((u32int)(r))+587*((u32int)(g))+114*((u32int)(b)))/1000)
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 */
18 void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point);
19 void verifyone(void);
20 void verifyline(void);
21 void verifyrect(void);
22 void verifyrectrepl(int, int);
23 void putpixel(Memimage *img, Point pt, u32int nv);
24 u32int rgbatopix(uchar, uchar, uchar, uchar);
26 char *dchan, *schan, *mchan;
27 int dbpp, sbpp, mbpp;
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 */
38 int Xrange = 64;
39 int Yrange = 8;
41 Memimage *dst;
42 Memimage *src;
43 Memimage *mask;
44 Memimage *stmp;
45 Memimage *mtmp;
46 Memimage *ones;
47 uchar *dstbits;
48 uchar *srcbits;
49 uchar *maskbits;
50 u32int *savedstbits;
52 void
53 rdb(void)
54 {
55 }
57 int
58 iprint(char *fmt, ...)
59 {
60 int n;
61 va_list va;
62 char buf[1024];
64 va_start(va, fmt);
65 n = doprint(buf, buf+sizeof buf, fmt, va) - buf;
66 va_end(va);
68 write(1,buf,n);
69 return 1;
70 }
72 void
73 main(int argc, char *argv[])
74 {
75 memimageinit();
76 seed = time(0);
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
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");
107 fmtinstall('b', numbconv); /* binary! */
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);
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");
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(u32int)*Yrange);
138 fprint(2, "dtest: verify single pixel operation\n");
139 verifyone();
141 fprint(2, "dtest: verify full line non-replicated\n");
142 verifyline();
144 fprint(2, "dtest: verify full rectangle non-replicated\n");
145 verifyrect();
147 fprint(2, "dtest: verify full rectangle source replicated\n");
148 verifyrectrepl(1, 0);
150 fprint(2, "dtest: verify full rectangle mask replicated\n");
151 verifyrectrepl(0, 1);
153 fprint(2, "dtest: verify full rectangle source and mask replicated\n");
154 verifyrectrepl(1, 1);
156 exits(0);
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*, u32int v)
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);
173 static void
174 Bprintr5g5b5a1(Biobuf *bio, char*, u32int v)
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);
184 void
185 dumpimage(char *name, Memimage *img, void *vdata, Point labelpt)
187 Biobuf b;
188 uchar *data;
189 uchar *p;
190 char *arg;
191 void (*fmt)(Biobuf*, char*, u32int);
192 int npr, x, y, nb, bpp;
193 u32int v, mask;
194 Rectangle r;
196 fmt = nil;
197 arg = nil;
198 switch(img->depth){
199 case 1:
200 case 2:
201 case 4:
202 fmt = (void(*)(Biobuf*,char*,u32int))Bprint;
203 arg = "%.1ux";
204 break;
205 case 8:
206 fmt = (void(*)(Biobuf*,char*,u32int))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*,u32int))Bprint;
215 arg = "%.4ux";
217 break;
218 case 24:
219 fmt = (void(*)(Biobuf*,char*,u32int))Bprint;
220 arg = "%.6lux";
221 break;
222 case 32:
223 fmt = (void(*)(Biobuf*,char*,u32int))Bprint;
224 arg = "%.8lux";
225 break;
227 if(fmt == nil){
228 fprint(2, "bad format\n");
229 abort();
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");
249 if(x != 0 && (x%8)==0)
250 Bprint(&b, " ");
252 npr = 0;
253 if(x==labelpt.x && y==labelpt.y){
254 Bprint(&b, "*");
255 npr++;
257 if(npr == 0)
258 Bprint(&b, " ");
260 while(nb < bpp){
261 v &= (1<<nb)-1;
262 v |= (u32int)(*p++) << nb;
263 nb += 8;
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);
269 Bprint(&b, "\n");
271 Bterm(&b);
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)
282 int delta;
283 uchar *dp, *sdp;
285 delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata;
286 dp = (uchar*)dst->data->bdata+delta;
287 sdp = (uchar*)savedstbits+delta;
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();
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)
310 u32int *dp;
311 int nb;
312 u32int *saved;
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();
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)
342 int i, x, y;
343 ushort *up;
344 uchar alpha, r, g, b;
345 void *data;
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;
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));
365 img->data->bdata = data;
370 /*
371 * Mask is preset; do the rest
372 */
373 void
374 verifyonemask(void)
376 Point dp, sp, mp;
378 fill(dst, dstbits);
379 fill(src, srcbits);
380 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
381 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
382 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
384 dp.x = nrand(Xrange);
385 dp.y = nrand(Yrange);
387 sp.x = nrand(Xrange);
388 sp.y = nrand(Yrange);
390 mp.x = nrand(Xrange);
391 mp.y = nrand(Yrange);
393 drawonepixel(dst, dp, src, sp, mask, mp);
394 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
395 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
397 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*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(u32int)*Yrange);
401 checkone(dp, sp, mp);
404 void
405 verifyone(void)
407 int i;
409 /* mask all zeros */
410 memset(maskbits, 0, nbytes);
411 for(i=0; i<niters; i++)
412 verifyonemask();
414 /* mask all ones */
415 memset(maskbits, 0xFF, nbytes);
416 for(i=0; i<niters; i++)
417 verifyonemask();
419 /* random mask */
420 for(i=0; i<niters; i++){
421 fill(mask, maskbits);
422 verifyonemask();
426 /*
427 * Mask is preset; do the rest
428 */
429 void
430 verifylinemask(void)
432 Point sp, mp, tp, up;
433 Rectangle dr;
434 int x;
436 fill(dst, dstbits);
437 fill(src, srcbits);
438 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
439 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
440 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
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;
447 sp.x = nrand(Xrange);
448 sp.y = nrand(Yrange);
450 mp.x = nrand(Xrange);
451 mp.y = nrand(Yrange);
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(u32int)*Yrange);
459 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
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);
465 void
466 verifyline(void)
468 int i;
470 /* mask all ones */
471 memset(maskbits, 0xFF, nbytes);
472 for(i=0; i<niters; i++)
473 verifylinemask();
475 /* mask all zeros */
476 memset(maskbits, 0, nbytes);
477 for(i=0; i<niters; i++)
478 verifylinemask();
480 /* random mask */
481 for(i=0; i<niters; i++){
482 fill(mask, maskbits);
483 verifylinemask();
487 /*
488 * Mask is preset; do the rest
489 */
490 void
491 verifyrectmask(void)
493 Point sp, mp, tp, up;
494 Rectangle dr;
495 int x, y;
497 fill(dst, dstbits);
498 fill(src, srcbits);
499 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
500 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
501 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
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);
508 sp.x = nrand(Xrange);
509 sp.y = nrand(Yrange);
511 mp.x = nrand(Xrange);
512 mp.y = nrand(Yrange);
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;
522 memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
524 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
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);
531 void
532 verifyrect(void)
534 int i;
536 /* mask all zeros */
537 memset(maskbits, 0, nbytes);
538 for(i=0; i<niters; i++)
539 verifyrectmask();
541 /* mask all ones */
542 memset(maskbits, 0xFF, nbytes);
543 for(i=0; i<niters; i++)
544 verifyrectmask();
546 /* random mask */
547 for(i=0; i<niters; i++){
548 fill(mask, maskbits);
549 verifyrectmask();
553 Rectangle
554 randrect(void)
556 Rectangle r;
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;
565 /*
566 * Return coordinate corresponding to x withing range [minx, maxx)
567 */
568 int
569 tilexy(int minx, int maxx, int x)
571 int sx;
573 sx = (x-minx) % (maxx-minx);
574 if(sx < 0)
575 sx += maxx-minx;
576 return sx+minx;
579 void
580 replicate(Memimage *i, Memimage *tmp)
582 Rectangle r, r1;
583 int x, y, nb;
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));
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));
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(u32int)*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);
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;
643 /*
644 * Mask is preset; do the rest
645 */
646 void
647 verifyrectmaskrepl(int srcrepl, int maskrepl)
649 Point sp, mp, tp, up;
650 Rectangle dr;
651 int x, y;
652 Memimage *s, *m;
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;
668 fill(dst, dstbits);
669 fill(src, srcbits);
671 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
672 memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
673 memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
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;
686 dr = randrect();
688 sp.x = nrand(Xrange);
689 sp.y = nrand(Yrange);
691 mp.x = nrand(Xrange);
692 mp.y = nrand(Yrange);
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(u32int)*Yrange);
700 memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
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);
708 void
709 verifyrectrepl(int srcrepl, int maskrepl)
711 int i;
713 /* mask all ones */
714 memset(maskbits, 0xFF, nbytes);
715 for(i=0; i<niters; i++)
716 verifyrectmaskrepl(srcrepl, maskrepl);
718 /* mask all zeros */
719 memset(maskbits, 0, nbytes);
720 for(i=0; i<niters; i++)
721 verifyrectmaskrepl(srcrepl, maskrepl);
723 /* random mask */
724 for(i=0; i<niters; i++){
725 fill(mask, maskbits);
726 verifyrectmaskrepl(srcrepl, maskrepl);
730 /*
731 * Trivial draw implementation.
732 * Color values are passed around as u32ints containing ααRRGGBB
733 */
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 u32int
740 replbits(u32int v, int nhave, int nwant)
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);
749 /*
750 * Decode a pixel into the uchar* values.
751 */
752 void
753 pixtorgba(u32int v, uchar *r, uchar *g, uchar *b, uchar *a)
755 *a = v>>24;
756 *r = v>>16;
757 *g = v>>8;
758 *b = v;
761 /*
762 * Convert uchar channels into u32int pixel.
763 */
764 u32int
765 rgbatopix(uchar r, uchar g, uchar b, uchar a)
767 return (a<<24)|(r<<16)|(g<<8)|b;
770 /*
771 * Retrieve the pixel value at pt in the image.
772 */
773 u32int
774 getpixel(Memimage *img, Point pt)
776 uchar r, g, b, a, *p;
777 int nbits, npack, bpp;
778 u32int v, c, rbits, bits;
780 r = g = b = 0;
781 a = ~0; /* default alpha is full */
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.
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.
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 */
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();
844 return rgbatopix(r, g, b, a);
847 /*
848 * Return the greyscale equivalent of a pixel.
849 */
850 uchar
851 getgrey(Memimage *img, Point pt)
853 uchar r, g, b, a;
854 pixtorgba(getpixel(img, pt), &r, &g, &b, &a);
855 return RGB2K(r, g, b);
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)
866 if(img->flags&Falpha)
867 return getpixel(img, pt)>>24;
868 else
869 return getgrey(img, pt);
871 #undef DBG
873 #define DBG if(0)
874 /*
875 * Write a pixel to img at point pt.
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, u32int nv)
887 uchar r, g, b, a, *p, *q;
888 u32int c, mask, bits, v;
889 int bpp, sh, npack, nbits;
891 pixtorgba(nv, &r, &g, &b, &a);
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();
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;
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;
966 #undef DBG
968 #define DBG if(0)
969 void
970 drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp)
972 uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk;
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;
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;
1002 DBG print("%x %x %x %x\n", dr,dg,db,da);
1003 putpixel(dst, dp, rgbatopix(dr, dg, db, da));