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