|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <draw.h> | 
|  | #include <memdraw.h> | 
|  |  | 
|  | #define DBG if(0) | 
|  | #define RGB2K(r,g,b)	((299*((u32int)(r))+587*((u32int)(g))+114*((u32int)(b)))/1000) | 
|  |  | 
|  | /* | 
|  | * This program tests the 'memimagedraw' primitive stochastically. | 
|  | * It tests the combination aspects of it thoroughly, but since the | 
|  | * three images it uses are disjoint, it makes no check of the | 
|  | * correct behavior when images overlap.  That is, however, much | 
|  | * easier to get right and to test. | 
|  | */ | 
|  |  | 
|  | void	drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point); | 
|  | void	verifyone(void); | 
|  | void	verifyline(void); | 
|  | void	verifyrect(void); | 
|  | void	verifyrectrepl(int, int); | 
|  | void putpixel(Memimage *img, Point pt, u32int nv); | 
|  | u32int rgbatopix(uchar, uchar, uchar, uchar); | 
|  |  | 
|  | char *dchan, *schan, *mchan; | 
|  | int dbpp, sbpp, mbpp; | 
|  |  | 
|  | int drawdebug=0; | 
|  | int	seed; | 
|  | int	niters = 100; | 
|  | int	dbpp;	/* bits per pixel in destination */ | 
|  | int	sbpp;	/* bits per pixel in src */ | 
|  | int	mbpp;	/* bits per pixel in mask */ | 
|  | int	dpm;	/* pixel mask at high part of byte, in destination */ | 
|  | int	nbytes;	/* in destination */ | 
|  |  | 
|  | int	Xrange	= 64; | 
|  | int	Yrange	= 8; | 
|  |  | 
|  | Memimage	*dst; | 
|  | Memimage	*src; | 
|  | Memimage	*mask; | 
|  | Memimage	*stmp; | 
|  | Memimage	*mtmp; | 
|  | Memimage	*ones; | 
|  | uchar	*dstbits; | 
|  | uchar	*srcbits; | 
|  | uchar	*maskbits; | 
|  | u32int	*savedstbits; | 
|  |  | 
|  | void | 
|  | rdb(void) | 
|  | { | 
|  | } | 
|  |  | 
|  | int | 
|  | iprint(char *fmt, ...) | 
|  | { | 
|  | int n; | 
|  | va_list va; | 
|  | char buf[1024]; | 
|  |  | 
|  | va_start(va, fmt); | 
|  | n = vseprint(buf, buf+sizeof buf, fmt, va) - buf; | 
|  | va_end(va); | 
|  |  | 
|  | write(1,buf,n); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | memimageinit(); | 
|  | seed = time(0); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'x': | 
|  | Xrange = atoi(ARGF()); | 
|  | break; | 
|  | case 'y': | 
|  | Yrange = atoi(ARGF()); | 
|  | break; | 
|  | case 'n': | 
|  | niters = atoi(ARGF()); | 
|  | break; | 
|  | case 's': | 
|  | seed = atoi(ARGF()); | 
|  | break; | 
|  | }ARGEND | 
|  |  | 
|  | dchan = "r8g8b8"; | 
|  | schan = "r8g8b8"; | 
|  | mchan = "r8g8b8"; | 
|  | switch(argc){ | 
|  | case 3:	mchan = argv[2]; | 
|  | case 2:	schan = argv[1]; | 
|  | case 1:	dchan = argv[0]; | 
|  | case 0:	break; | 
|  | default:	goto Usage; | 
|  | Usage: | 
|  | fprint(2, "usage: dtest [dchan [schan [mchan]]]\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | fprint(2, "%s -x %d -y %d -s 0x%x %s %s %s\n", argv0, Xrange, Yrange, seed, dchan, schan, mchan); | 
|  | srand(seed); | 
|  |  | 
|  | dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan)); | 
|  | src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); | 
|  | mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); | 
|  | stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); | 
|  | mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); | 
|  | ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); | 
|  | /*	print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan); */ | 
|  | if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) { | 
|  | Alloc: | 
|  | fprint(2, "dtest: allocation failed: %r\n"); | 
|  | exits("alloc"); | 
|  | } | 
|  | nbytes = (4*Xrange+4)*Yrange; | 
|  | srcbits = malloc(nbytes); | 
|  | dstbits = malloc(nbytes); | 
|  | maskbits = malloc(nbytes); | 
|  | savedstbits = malloc(nbytes); | 
|  | if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0) | 
|  | goto Alloc; | 
|  | dbpp = dst->depth; | 
|  | sbpp = src->depth; | 
|  | mbpp = mask->depth; | 
|  | dpm = 0xFF ^ (0xFF>>dbpp); | 
|  | memset(ones->data->bdata, 0xFF, ones->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  |  | 
|  | fprint(2, "dtest: verify single pixel operation\n"); | 
|  | verifyone(); | 
|  |  | 
|  | fprint(2, "dtest: verify full line non-replicated\n"); | 
|  | verifyline(); | 
|  |  | 
|  | fprint(2, "dtest: verify full rectangle non-replicated\n"); | 
|  | verifyrect(); | 
|  |  | 
|  | fprint(2, "dtest: verify full rectangle source replicated\n"); | 
|  | verifyrectrepl(1, 0); | 
|  |  | 
|  | fprint(2, "dtest: verify full rectangle mask replicated\n"); | 
|  | verifyrectrepl(0, 1); | 
|  |  | 
|  | fprint(2, "dtest: verify full rectangle source and mask replicated\n"); | 
|  | verifyrectrepl(1, 1); | 
|  |  | 
|  | exits(0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Dump out an ASCII representation of an image.  The label specifies | 
|  | * a list of characters to put at various points in the picture. | 
|  | */ | 
|  | static void | 
|  | Bprintr5g6b5(Biobuf *bio, char* _, u32int v) | 
|  | { | 
|  | int r,g,b; | 
|  | r = (v>>11)&31; | 
|  | g = (v>>5)&63; | 
|  | b = v&31; | 
|  | Bprint(bio, "%.2x%.2x%.2x", r,g,b); | 
|  | } | 
|  |  | 
|  | static void | 
|  | Bprintr5g5b5a1(Biobuf *bio, char* _, u32int v) | 
|  | { | 
|  | int r,g,b,a; | 
|  | r = (v>>11)&31; | 
|  | g = (v>>6)&31; | 
|  | b = (v>>1)&31; | 
|  | a = v&1; | 
|  | Bprint(bio, "%.2x%.2x%.2x%.2x", r,g,b,a); | 
|  | } | 
|  |  | 
|  | void | 
|  | dumpimage(char *name, Memimage *img, void *vdata, Point labelpt) | 
|  | { | 
|  | Biobuf b; | 
|  | uchar *data; | 
|  | uchar *p; | 
|  | char *arg; | 
|  | void (*fmt)(Biobuf*, char*, u32int); | 
|  | int npr, x, y, nb, bpp; | 
|  | u32int v, mask; | 
|  | Rectangle r; | 
|  |  | 
|  | fmt = nil; | 
|  | arg = nil; | 
|  | switch(img->depth){ | 
|  | case 1: | 
|  | case 2: | 
|  | case 4: | 
|  | fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | 
|  | arg = "%.1ux"; | 
|  | break; | 
|  | case 8: | 
|  | fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | 
|  | arg = "%.2ux"; | 
|  | break; | 
|  | case 16: | 
|  | arg = nil; | 
|  | if(img->chan == RGB16) | 
|  | fmt = Bprintr5g6b5; | 
|  | else{ | 
|  | fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | 
|  | arg = "%.4ux"; | 
|  | } | 
|  | break; | 
|  | case 24: | 
|  | fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | 
|  | arg = "%.6lux"; | 
|  | break; | 
|  | case 32: | 
|  | fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | 
|  | arg = "%.8lux"; | 
|  | break; | 
|  | } | 
|  | if(fmt == nil){ | 
|  | fprint(2, "bad format\n"); | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | r  = img->r; | 
|  | Binit(&b, 2, OWRITE); | 
|  | data = vdata; | 
|  | bpp = img->depth; | 
|  | 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); | 
|  | mask = (1ULL<<bpp)-1; | 
|  | /*	for(y=r.min.y; y<r.max.y; y++){ */ | 
|  | for(y=0; y<Yrange; y++){ | 
|  | nb = 0; | 
|  | v = 0; | 
|  | p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata); | 
|  | Bprint(&b, "%-4d\t", y); | 
|  | /*		for(x=r.min.x; x<r.max.x; x++){ */ | 
|  | for(x=0; x<Xrange; x++){ | 
|  | if(x==0) | 
|  | Bprint(&b, "\t"); | 
|  |  | 
|  | if(x != 0 && (x%8)==0) | 
|  | Bprint(&b, " "); | 
|  |  | 
|  | npr = 0; | 
|  | if(x==labelpt.x && y==labelpt.y){ | 
|  | Bprint(&b, "*"); | 
|  | npr++; | 
|  | } | 
|  | if(npr == 0) | 
|  | Bprint(&b, " "); | 
|  |  | 
|  | while(nb < bpp){ | 
|  | v &= (1<<nb)-1; | 
|  | v |= (u32int)(*p++) << nb; | 
|  | nb += 8; | 
|  | } | 
|  | nb -= bpp; | 
|  | /*			print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v, mask, nb); */ | 
|  | fmt(&b, arg, (v>>nb)&mask); | 
|  | } | 
|  | Bprint(&b, "\n"); | 
|  | } | 
|  | Bterm(&b); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Verify that the destination pixel has the specified value. | 
|  | * The value is in the high bits of v, suitably masked, but must | 
|  | * be extracted from the destination Memimage. | 
|  | */ | 
|  | void | 
|  | checkone(Point p, Point sp, Point mp) | 
|  | { | 
|  | int delta; | 
|  | uchar *dp, *sdp; | 
|  |  | 
|  | delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata; | 
|  | dp = (uchar*)dst->data->bdata+delta; | 
|  | sdp = (uchar*)savedstbits+delta; | 
|  |  | 
|  | if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) { | 
|  | fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp); | 
|  | fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n", | 
|  | dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]); | 
|  | fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp)); | 
|  | dumpimage("src", src, src->data->bdata, sp); | 
|  | dumpimage("mask", mask, mask->data->bdata, mp); | 
|  | dumpimage("origdst", dst, dstbits, p); | 
|  | dumpimage("dst", dst, dst->data->bdata, p); | 
|  | dumpimage("gooddst", dst, savedstbits, p); | 
|  | abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Verify that the destination line has the same value as the saved line. | 
|  | */ | 
|  | #define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y | 
|  | void | 
|  | checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp) | 
|  | { | 
|  | u32int *dp; | 
|  | int nb; | 
|  | u32int *saved; | 
|  |  | 
|  | dp = wordaddr(dst, Pt(0, y)); | 
|  | saved = savedstbits + y*dst->width; | 
|  | if(dst->depth < 8) | 
|  | nb = Xrange/(8/dst->depth); | 
|  | else | 
|  | nb = Xrange*(dst->depth/8); | 
|  | if(memcmp(dp, saved, nb) != 0){ | 
|  | fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp); | 
|  | fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp); | 
|  | dumpimage("src", src, src->data->bdata, sp); | 
|  | if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp); | 
|  | dumpimage("mask", mask, mask->data->bdata, mp); | 
|  | if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp); | 
|  | dumpimage("origdst", dst, dstbits, r.min); | 
|  | dumpimage("dst", dst, dst->data->bdata, r.min); | 
|  | dumpimage("gooddst", dst, savedstbits, r.min); | 
|  | abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fill the bits of an image with random data. | 
|  | * The Memimage parameter is used only to make sure | 
|  | * the data is well formatted: only ucbits is written. | 
|  | */ | 
|  | void | 
|  | fill(Memimage *img, uchar *ucbits) | 
|  | { | 
|  | int i, x, y; | 
|  | ushort *up; | 
|  | uchar alpha, r, g, b; | 
|  | void *data; | 
|  |  | 
|  | if((img->flags&Falpha) == 0){ | 
|  | up = (ushort*)ucbits; | 
|  | for(i=0; i<nbytes/2; i++) | 
|  | *up++ = lrand() >> 7; | 
|  | if(i+i != nbytes) | 
|  | *(uchar*)up = lrand() >> 7; | 
|  | }else{ | 
|  | data = img->data->bdata; | 
|  | img->data->bdata = ucbits; | 
|  |  | 
|  | for(x=img->r.min.x; x<img->r.max.x; x++) | 
|  | for(y=img->r.min.y; y<img->r.max.y; y++){ | 
|  | alpha = rand() >> 4; | 
|  | r = rand()%(alpha+1); | 
|  | g = rand()%(alpha+1); | 
|  | b = rand()%(alpha+1); | 
|  | putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha)); | 
|  | } | 
|  | img->data->bdata = data; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mask is preset; do the rest | 
|  | */ | 
|  | void | 
|  | verifyonemask(void) | 
|  | { | 
|  | Point dp, sp, mp; | 
|  |  | 
|  | fill(dst, dstbits); | 
|  | fill(src, srcbits); | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  | memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | dp.x = nrand(Xrange); | 
|  | dp.y = nrand(Yrange); | 
|  |  | 
|  | sp.x = nrand(Xrange); | 
|  | sp.y = nrand(Yrange); | 
|  |  | 
|  | mp.x = nrand(Xrange); | 
|  | mp.y = nrand(Yrange); | 
|  |  | 
|  | drawonepixel(dst, dp, src, sp, mask, mp); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  | memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  | memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | checkone(dp, sp, mp); | 
|  | } | 
|  |  | 
|  | void | 
|  | verifyone(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* mask all zeros */ | 
|  | memset(maskbits, 0, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyonemask(); | 
|  |  | 
|  | /* mask all ones */ | 
|  | memset(maskbits, 0xFF, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyonemask(); | 
|  |  | 
|  | /* random mask */ | 
|  | for(i=0; i<niters; i++){ | 
|  | fill(mask, maskbits); | 
|  | verifyonemask(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mask is preset; do the rest | 
|  | */ | 
|  | void | 
|  | verifylinemask(void) | 
|  | { | 
|  | Point sp, mp, tp, up; | 
|  | Rectangle dr; | 
|  | int x; | 
|  |  | 
|  | fill(dst, dstbits); | 
|  | fill(src, srcbits); | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  | memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | dr.min.x = nrand(Xrange-1); | 
|  | dr.min.y = nrand(Yrange-1); | 
|  | dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); | 
|  | dr.max.y = dr.min.y + 1; | 
|  |  | 
|  | sp.x = nrand(Xrange); | 
|  | sp.y = nrand(Yrange); | 
|  |  | 
|  | mp.x = nrand(Xrange); | 
|  | mp.y = nrand(Yrange); | 
|  |  | 
|  | tp = sp; | 
|  | up = mp; | 
|  | for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++) | 
|  | memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp, mask, up, SoverD); | 
|  | memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | 
|  | checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | verifyline(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* mask all ones */ | 
|  | memset(maskbits, 0xFF, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifylinemask(); | 
|  |  | 
|  | /* mask all zeros */ | 
|  | memset(maskbits, 0, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifylinemask(); | 
|  |  | 
|  | /* random mask */ | 
|  | for(i=0; i<niters; i++){ | 
|  | fill(mask, maskbits); | 
|  | verifylinemask(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mask is preset; do the rest | 
|  | */ | 
|  | void | 
|  | verifyrectmask(void) | 
|  | { | 
|  | Point sp, mp, tp, up; | 
|  | Rectangle dr; | 
|  | int x, y; | 
|  |  | 
|  | fill(dst, dstbits); | 
|  | fill(src, srcbits); | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  | memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | dr.min.x = nrand(Xrange-1); | 
|  | dr.min.y = nrand(Yrange-1); | 
|  | dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); | 
|  | dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y); | 
|  |  | 
|  | sp.x = nrand(Xrange); | 
|  | sp.y = nrand(Yrange); | 
|  |  | 
|  | mp.x = nrand(Xrange); | 
|  | mp.y = nrand(Yrange); | 
|  |  | 
|  | tp = sp; | 
|  | up = mp; | 
|  | for(y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++){ | 
|  | for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++) | 
|  | memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask, up, SoverD); | 
|  | tp.x = sp.x; | 
|  | up.x = mp.x; | 
|  | } | 
|  | memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | 
|  | for(y=0; y<Yrange; y++) | 
|  | checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, nil, nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | verifyrect(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* mask all zeros */ | 
|  | memset(maskbits, 0, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyrectmask(); | 
|  |  | 
|  | /* mask all ones */ | 
|  | memset(maskbits, 0xFF, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyrectmask(); | 
|  |  | 
|  | /* random mask */ | 
|  | for(i=0; i<niters; i++){ | 
|  | fill(mask, maskbits); | 
|  | verifyrectmask(); | 
|  | } | 
|  | } | 
|  |  | 
|  | Rectangle | 
|  | randrect(void) | 
|  | { | 
|  | Rectangle r; | 
|  |  | 
|  | r.min.x = nrand(Xrange-1); | 
|  | r.min.y = nrand(Yrange-1); | 
|  | r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x); | 
|  | r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return coordinate corresponding to x withing range [minx, maxx) | 
|  | */ | 
|  | int | 
|  | tilexy(int minx, int maxx, int x) | 
|  | { | 
|  | int sx; | 
|  |  | 
|  | sx = (x-minx) % (maxx-minx); | 
|  | if(sx < 0) | 
|  | sx += maxx-minx; | 
|  | return sx+minx; | 
|  | } | 
|  |  | 
|  | void | 
|  | replicate(Memimage *i, Memimage *tmp) | 
|  | { | 
|  | Rectangle r, r1; | 
|  | int x, y, nb; | 
|  |  | 
|  | /* choose the replication window (i->r) */ | 
|  | r.min.x = nrand(Xrange-1); | 
|  | r.min.y = nrand(Yrange-1); | 
|  | /* make it trivial more often than pure chance allows */ | 
|  | switch(lrand()&0){ | 
|  | case 1: | 
|  | r.max.x = r.min.x + 2; | 
|  | r.max.y = r.min.y + 2; | 
|  | if(r.max.x < Xrange && r.max.y < Yrange) | 
|  | break; | 
|  | /* fall through */ | 
|  | case 0: | 
|  | r.max.x = r.min.x + 1; | 
|  | r.max.y = r.min.y + 1; | 
|  | break; | 
|  | default: | 
|  | if(r.min.x+3 >= Xrange) | 
|  | r.max.x = Xrange; | 
|  | else | 
|  | r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3)); | 
|  |  | 
|  | if(r.min.y+3 >= Yrange) | 
|  | r.max.y = Yrange; | 
|  | else | 
|  | r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3)); | 
|  | } | 
|  | assert(r.min.x >= 0); | 
|  | assert(r.max.x <= Xrange); | 
|  | assert(r.min.y >= 0); | 
|  | assert(r.max.y <= Yrange); | 
|  | /* copy from i to tmp so we have just the replicated bits */ | 
|  | nb = tmp->width*sizeof(u32int)*Yrange; | 
|  | memset(tmp->data->bdata, 0, nb); | 
|  | memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD); | 
|  | memmove(i->data->bdata, tmp->data->bdata, nb); | 
|  | /* i is now a non-replicated instance of the replication */ | 
|  | /* replicate it by hand through tmp */ | 
|  | memset(tmp->data->bdata, 0, nb); | 
|  | x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x); | 
|  | for(; x<Xrange; x+=Dx(r)){ | 
|  | y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y); | 
|  | for(; y<Yrange; y+=Dy(r)){ | 
|  | /* set r1 to instance of tile by translation */ | 
|  | r1.min.x = x; | 
|  | r1.min.y = y; | 
|  | r1.max.x = r1.min.x+Dx(r); | 
|  | r1.max.y = r1.min.y+Dy(r); | 
|  | memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD); | 
|  | } | 
|  | } | 
|  | i->flags |= Frepl; | 
|  | i->r = r; | 
|  | i->clipr = randrect(); | 
|  | /*	fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y, */ | 
|  | /*		i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); */ | 
|  | tmp->clipr = i->clipr; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mask is preset; do the rest | 
|  | */ | 
|  | void | 
|  | verifyrectmaskrepl(int srcrepl, int maskrepl) | 
|  | { | 
|  | Point sp, mp, tp, up; | 
|  | Rectangle dr; | 
|  | int x, y; | 
|  | Memimage *s, *m; | 
|  |  | 
|  | /*	print("verfrect %d %d\n", srcrepl, maskrepl); */ | 
|  | src->flags &= ~Frepl; | 
|  | src->r = Rect(0, 0, Xrange, Yrange); | 
|  | src->clipr = src->r; | 
|  | stmp->flags &= ~Frepl; | 
|  | stmp->r = Rect(0, 0, Xrange, Yrange); | 
|  | stmp->clipr = src->r; | 
|  | mask->flags &= ~Frepl; | 
|  | mask->r = Rect(0, 0, Xrange, Yrange); | 
|  | mask->clipr = mask->r; | 
|  | mtmp->flags &= ~Frepl; | 
|  | mtmp->r = Rect(0, 0, Xrange, Yrange); | 
|  | mtmp->clipr = mask->r; | 
|  |  | 
|  | fill(dst, dstbits); | 
|  | fill(src, srcbits); | 
|  |  | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  | memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | 
|  | memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | if(srcrepl){ | 
|  | replicate(src, stmp); | 
|  | s = stmp; | 
|  | }else | 
|  | s = src; | 
|  | if(maskrepl){ | 
|  | replicate(mask, mtmp); | 
|  | m = mtmp; | 
|  | }else | 
|  | m = mask; | 
|  |  | 
|  | dr = randrect(); | 
|  |  | 
|  | sp.x = nrand(Xrange); | 
|  | sp.y = nrand(Yrange); | 
|  |  | 
|  | mp.x = nrand(Xrange); | 
|  | mp.y = nrand(Yrange); | 
|  |  | 
|  | DBG	print("smalldraws\n"); | 
|  | 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++) | 
|  | 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++) | 
|  | memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, SoverD); | 
|  | memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | 
|  |  | 
|  | DBG	print("bigdraw\n"); | 
|  | memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | 
|  | for(y=0; y<Yrange; y++) | 
|  | checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil); | 
|  | } | 
|  |  | 
|  | void | 
|  | verifyrectrepl(int srcrepl, int maskrepl) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* mask all ones */ | 
|  | memset(maskbits, 0xFF, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyrectmaskrepl(srcrepl, maskrepl); | 
|  |  | 
|  | /* mask all zeros */ | 
|  | memset(maskbits, 0, nbytes); | 
|  | for(i=0; i<niters; i++) | 
|  | verifyrectmaskrepl(srcrepl, maskrepl); | 
|  |  | 
|  | /* random mask */ | 
|  | for(i=0; i<niters; i++){ | 
|  | fill(mask, maskbits); | 
|  | verifyrectmaskrepl(srcrepl, maskrepl); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Trivial draw implementation. | 
|  | * Color values are passed around as u32ints containing ααRRGGBB | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Convert v, which is nhave bits wide, into its nwant bits wide equivalent. | 
|  | * Replicates to widen the value, truncates to narrow it. | 
|  | */ | 
|  | u32int | 
|  | replbits(u32int v, int nhave, int nwant) | 
|  | { | 
|  | v &= (1<<nhave)-1; | 
|  | for(; nhave<nwant; nhave*=2) | 
|  | v |= v<<nhave; | 
|  | v >>= (nhave-nwant); | 
|  | return v & ((1<<nwant)-1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Decode a pixel into the uchar* values. | 
|  | */ | 
|  | void | 
|  | pixtorgba(u32int v, uchar *r, uchar *g, uchar *b, uchar *a) | 
|  | { | 
|  | *a = v>>24; | 
|  | *r = v>>16; | 
|  | *g = v>>8; | 
|  | *b = v; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert uchar channels into u32int pixel. | 
|  | */ | 
|  | u32int | 
|  | rgbatopix(uchar r, uchar g, uchar b, uchar a) | 
|  | { | 
|  | return (a<<24)|(r<<16)|(g<<8)|b; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Retrieve the pixel value at pt in the image. | 
|  | */ | 
|  | u32int | 
|  | getpixel(Memimage *img, Point pt) | 
|  | { | 
|  | uchar r, g, b, a, *p; | 
|  | int nbits, npack, bpp; | 
|  | u32int v, c, rbits, bits; | 
|  |  | 
|  | r = g = b = 0; | 
|  | a = ~0;	/* default alpha is full */ | 
|  |  | 
|  | p = byteaddr(img, pt); | 
|  | v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); | 
|  | bpp = img->depth; | 
|  | if(bpp<8){ | 
|  | /* | 
|  | * Sub-byte greyscale pixels. | 
|  | * | 
|  | * We want to throw away the top pt.x%npack pixels and then use the next bpp bits | 
|  | * in the bottom byte of v.  This madness is due to having big endian bits | 
|  | * but little endian bytes. | 
|  | */ | 
|  | npack = 8/bpp; | 
|  | v >>= 8 - bpp*(pt.x%npack+1); | 
|  | v &= (1<<bpp)-1; | 
|  | r = g = b = replbits(v, bpp, 8); | 
|  | }else{ | 
|  | /* | 
|  | * General case.  We need to parse the channel descriptor and do what it says. | 
|  | * In all channels but the color map, we replicate to 8 bits because that's the | 
|  | * precision that all calculations are done at. | 
|  | * | 
|  | * In the case of the color map, we leave the bits alone, in case a color map | 
|  | * with less than 8 bits of index is used.  This is currently disallowed, so it's | 
|  | * sort of silly. | 
|  | */ | 
|  |  | 
|  | for(c=img->chan; c; c>>=8){ | 
|  | nbits = NBITS(c); | 
|  | bits = v & ((1<<nbits)-1); | 
|  | rbits = replbits(bits, nbits, 8); | 
|  | v >>= nbits; | 
|  | switch(TYPE(c)){ | 
|  | case CRed: | 
|  | r = rbits; | 
|  | break; | 
|  | case CGreen: | 
|  | g = rbits; | 
|  | break; | 
|  | case CBlue: | 
|  | b = rbits; | 
|  | break; | 
|  | case CGrey: | 
|  | r = g = b = rbits; | 
|  | break; | 
|  | case CAlpha: | 
|  | a = rbits; | 
|  | break; | 
|  | case CMap: | 
|  | p = img->cmap->cmap2rgb + 3*bits; | 
|  | r = p[0]; | 
|  | g = p[1]; | 
|  | b = p[2]; | 
|  | break; | 
|  | case CIgnore: | 
|  | break; | 
|  | default: | 
|  | fprint(2, "unknown channel type %lud\n", TYPE(c)); | 
|  | abort(); | 
|  | } | 
|  | } | 
|  | } | 
|  | return rgbatopix(r, g, b, a); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return the greyscale equivalent of a pixel. | 
|  | */ | 
|  | uchar | 
|  | getgrey(Memimage *img, Point pt) | 
|  | { | 
|  | uchar r, g, b, a; | 
|  | pixtorgba(getpixel(img, pt), &r, &g, &b, &a); | 
|  | return RGB2K(r, g, b); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return the value at pt in image, if image is interpreted | 
|  | * as a mask.  This means the alpha channel if present, else | 
|  | * the greyscale or its computed equivalent. | 
|  | */ | 
|  | uchar | 
|  | getmask(Memimage *img, Point pt) | 
|  | { | 
|  | if(img->flags&Falpha) | 
|  | return getpixel(img, pt)>>24; | 
|  | else | 
|  | return getgrey(img, pt); | 
|  | } | 
|  | #undef DBG | 
|  |  | 
|  | #define DBG if(0) | 
|  | /* | 
|  | * Write a pixel to img at point pt. | 
|  | * | 
|  | * We do this by reading a 32-bit little endian | 
|  | * value from p and then writing it back | 
|  | * after tweaking the appropriate bits.  Because | 
|  | * the data is little endian, we don't have to worry | 
|  | * about what the actual depth is, as long as it is | 
|  | * less than 32 bits. | 
|  | */ | 
|  | void | 
|  | putpixel(Memimage *img, Point pt, u32int nv) | 
|  | { | 
|  | uchar r, g, b, a, *p, *q; | 
|  | u32int c, mask, bits, v; | 
|  | int bpp, sh, npack, nbits; | 
|  |  | 
|  | pixtorgba(nv, &r, &g, &b, &a); | 
|  |  | 
|  | p = byteaddr(img, pt); | 
|  | v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); | 
|  | bpp = img->depth; | 
|  | DBG print("v %.8lux...", v); | 
|  | if(bpp < 8){ | 
|  | /* | 
|  | * Sub-byte greyscale pixels.  We need to skip the leftmost pt.x%npack pixels, | 
|  | * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels. | 
|  | */ | 
|  | npack = 8/bpp; | 
|  | sh = bpp*(npack - pt.x%npack - 1); | 
|  | bits = RGB2K(r,g,b); | 
|  | DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp)); | 
|  | bits = replbits(bits, 8, bpp); | 
|  | mask = (1<<bpp)-1; | 
|  | DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); | 
|  | mask <<= sh; | 
|  | bits <<= sh; | 
|  | DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask); | 
|  | v = (v & ~mask) | (bits & mask); | 
|  | } else { | 
|  | /* | 
|  | * General case.  We need to parse the channel descriptor again. | 
|  | */ | 
|  | sh = 0; | 
|  | for(c=img->chan; c; c>>=8){ | 
|  | nbits = NBITS(c); | 
|  | switch(TYPE(c)){ | 
|  | case CRed: | 
|  | bits = r; | 
|  | break; | 
|  | case CGreen: | 
|  | bits = g; | 
|  | break; | 
|  | case CBlue: | 
|  | bits = b; | 
|  | break; | 
|  | case CGrey: | 
|  | bits = RGB2K(r, g, b); | 
|  | break; | 
|  | case CAlpha: | 
|  | bits = a; | 
|  | break; | 
|  | case CIgnore: | 
|  | bits = 0; | 
|  | break; | 
|  | case CMap: | 
|  | q = img->cmap->rgb2cmap; | 
|  | bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)]; | 
|  | break; | 
|  | default: | 
|  | SET(bits); | 
|  | fprint(2, "unknown channel type %lud\n", TYPE(c)); | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits)); | 
|  | if(TYPE(c) != CMap) | 
|  | bits = replbits(bits, 8, nbits); | 
|  | mask = (1<<nbits)-1; | 
|  | DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); | 
|  | bits <<= sh; | 
|  | mask <<= sh; | 
|  | v = (v & ~mask) | (bits & mask); | 
|  | sh += nbits; | 
|  | } | 
|  | } | 
|  | DBG print("v %.8lux\n", v); | 
|  | p[0] = v; | 
|  | p[1] = v>>8; | 
|  | p[2] = v>>16; | 
|  | p[3] = v>>24; | 
|  | } | 
|  | #undef DBG | 
|  |  | 
|  | #define DBG if(0) | 
|  | void | 
|  | drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp) | 
|  | { | 
|  | uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk; | 
|  |  | 
|  | pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da); | 
|  | pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa); | 
|  | m = getmask(mask, mp); | 
|  | M = 255-(sa*m + 127)/255; | 
|  |  | 
|  | DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m); | 
|  | if(dst->flags&Fgrey){ | 
|  | /* | 
|  | * We need to do the conversion to grey before the alpha calculation | 
|  | * because the draw operator does this, and we need to be operating | 
|  | * at the same precision so we get exactly the same answers. | 
|  | */ | 
|  | sk = RGB2K(sr, sg, sb); | 
|  | dk = RGB2K(dr, dg, db); | 
|  | dk = (sk*m + dk*M + 127)/255; | 
|  | dr = dg = db = dk; | 
|  | da = (sa*m + da*M + 127)/255; | 
|  | }else{ | 
|  | /* | 
|  | * True color alpha calculation treats all channels (including alpha) | 
|  | * the same.  It might have been nice to use an array, but oh well. | 
|  | */ | 
|  | dr = (sr*m + dr*M + 127)/255; | 
|  | dg = (sg*m + dg*M + 127)/255; | 
|  | db = (sb*m + db*M + 127)/255; | 
|  | da = (sa*m + da*M + 127)/255; | 
|  | } | 
|  |  | 
|  | DBG print("%x %x %x %x\n", dr,dg,db,da); | 
|  | putpixel(dst, dp, rgbatopix(dr, dg, db, da)); | 
|  | } |