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