| #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)); |
| } |