commit 324891a5579d6f504201a6107369c64dab245a98 from: rsc date: Sun Jun 25 18:58:06 2006 UTC separate out commit - 9361131304f39db29b1bec59d881e585035ec93c commit + 324891a5579d6f504201a6107369c64dab245a98 blob - /dev/null blob + b6ad61a73511c09d9c969629c292374b752e28bf (mode 644) --- /dev/null +++ src/libmemdraw/alloc.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include + +#define poolalloc(a, b) malloc(b) +#define poolfree(a, b) free(b) + +void +memimagemove(void *from, void *to) +{ + Memdata *md; + + md = *(Memdata**)to; + if(md->base != from){ + print("compacted data not right: #%p\n", md->base); + abort(); + } + md->base = to; + + /* if allocmemimage changes this must change too */ + md->bdata = (uchar*)md->base+sizeof(Memdata*)+sizeof(ulong); +} + +Memimage* +allocmemimaged(Rectangle r, u32int chan, Memdata *md, void *X) +{ + int d; + u32int l; + Memimage *i; + + if(Dx(r) <= 0 || Dy(r) <= 0){ + werrstr("bad rectangle %R", r); + return nil; + } + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + + i = mallocz(sizeof(Memimage), 1); + if(i == nil) + return nil; + + i->X = X; + i->data = md; + i->zero = sizeof(u32int)*l*r.min.y; + + if(r.min.x >= 0) + i->zero += (r.min.x*d)/8; + else + i->zero -= (-r.min.x*d+7)/8; + i->zero = -i->zero; + i->width = l; + i->r = r; + i->clipr = r; + i->flags = 0; + i->layer = nil; + i->cmap = memdefcmap; + if(memsetchan(i, chan) < 0){ + free(i); + return nil; + } + return i; +} + +Memimage* +_allocmemimage(Rectangle r, u32int chan) +{ + int d; + u32int l, nw; + uchar *p; + Memdata *md; + Memimage *i; + + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + nw = l*Dy(r); + md = malloc(sizeof(Memdata)); + if(md == nil) + return nil; + + md->ref = 1; + md->base = poolalloc(imagmem, sizeof(Memdata*)+(1+nw)*sizeof(ulong)); + if(md->base == nil){ + free(md); + return nil; + } + + p = (uchar*)md->base; + *(Memdata**)p = md; + p += sizeof(Memdata*); + + *(ulong*)p = getcallerpc(&r); + p += sizeof(ulong); + + /* if this changes, memimagemove must change too */ + md->bdata = p; + md->allocd = 1; + + i = allocmemimaged(r, chan, md, nil); + if(i == nil){ + poolfree(imagmem, md->base); + free(md); + return nil; + } + md->imref = i; + return i; +} + +void +_freememimage(Memimage *i) +{ + if(i == nil) + return; + if(i->data->ref-- == 1 && i->data->allocd){ + if(i->data->base) + poolfree(imagmem, i->data->base); + free(i->data); + } + free(i); +} + +/* + * Wordaddr is deprecated. + */ +u32int* +wordaddr(Memimage *i, Point p) +{ + return (u32int*) ((ulong)byteaddr(i, p) & ~(sizeof(u32int)-1)); +} + +uchar* +byteaddr(Memimage *i, Point p) +{ + uchar *a; + + a = i->data->bdata+i->zero+sizeof(u32int)*p.y*i->width; + + if(i->depth < 8){ + /* + * We need to always round down, + * but C rounds toward zero. + */ + int np; + np = 8/i->depth; + if(p.x < 0) + return a+(p.x-np+1)/np; + else + return a+p.x/np; + } + else + return a+p.x*(i->depth/8); +} + +int +memsetchan(Memimage *i, u32int chan) +{ + int d; + int t, j, k; + u32int cc; + int bytes; + + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor"); + return -1; + } + + i->depth = d; + i->chan = chan; + i->flags &= ~(Fgrey|Falpha|Fcmap|Fbytes); + bytes = 1; + for(cc=chan, j=0, k=0; cc; j+=NBITS(cc), cc>>=8, k++){ + t=TYPE(cc); + if(t < 0 || t >= NChan){ + werrstr("bad channel string"); + return -1; + } + if(t == CGrey) + i->flags |= Fgrey; + if(t == CAlpha) + i->flags |= Falpha; + if(t == CMap && i->cmap == nil){ + i->cmap = memdefcmap; + i->flags |= Fcmap; + } + + i->shift[t] = j; + i->mask[t] = (1<nbits[t] = NBITS(cc); + if(NBITS(cc) != 8) + bytes = 0; + } + i->nchan = k; + if(bytes) + i->flags |= Fbytes; + return 0; +} blob - /dev/null blob + df836a00e324c99a4c8f7dee7db0a0af37e81123 (mode 644) --- /dev/null +++ src/libmemdraw/arc.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +/* + * elarc(dst,c,a,b,t,src,sp,alpha,phi) + * draws the part of an ellipse between rays at angles alpha and alpha+phi + * measured counterclockwise from the positive x axis. other + * arguments are as for ellipse(dst,c,a,b,t,src,sp) + */ + +enum +{ + R, T, L, B /* right, top, left, bottom */ +}; + +static +Point corners[] = { + {1,1}, + {-1,1}, + {-1,-1}, + {1,-1} +}; + +static +Point p00; + +/* + * make a "wedge" mask covering the desired angle and contained in + * a surrounding square; draw a full ellipse; intersect that with the + * wedge to make a mask through which to copy src to dst. + */ +void +memarc(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int alpha, int phi, int op) +{ + int i, w, beta, tmp, c1, c2, m, m1; + Rectangle rect; + Point p, bnd[8]; + Memimage *wedge, *figure, *mask; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + w = t; + if(w < 0) + w = 0; + alpha = -alpha; /* compensate for upside-down coords */ + phi = -phi; + beta = alpha + phi; + if(phi < 0){ + tmp = alpha; + alpha = beta; + beta = tmp; + phi = -phi; + } + if(phi >= 360){ + memellipse(dst, c, a, b, t, src, sp, op); + return; + } + while(alpha < 0) + alpha += 360; + while(beta < 0) + beta += 360; + c1 = alpha/90 & 3; /* number of nearest corner */ + c2 = beta/90 & 3; + /* + * icossin returns point at radius ICOSSCALE. + * multiplying by m1 moves it outside the ellipse + */ + rect = Rect(-a-w, -b-w, a+w+1, b+w+1); + m = rect.max.x; /* inradius of bounding square */ + if(m < rect.max.y) + m = rect.max.y; + m1 = (m+ICOSSCALE-1) >> 10; + m = m1 << 10; /* assure m1*cossin is inside */ + i = 0; + bnd[i++] = Pt(0,0); + icossin(alpha, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + for(;;) { + bnd[i++] = mulpt(corners[c1], m); + if(c1==c2 && phi<180) + break; + c1 = (c1+1) & 3; + phi -= 90; + } + icossin(beta, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + + figure = nil; + mask = nil; + wedge = allocmemimage(rect, GREY1); + if(wedge == nil) + goto Return; + memfillcolor(wedge, DTransparent); + memfillpoly(wedge, bnd, i, ~0, memopaque, p00, S); + figure = allocmemimage(rect, GREY1); + if(figure == nil) + goto Return; + memfillcolor(figure, DTransparent); + memellipse(figure, p00, a, b, t, memopaque, p00, S); + mask = allocmemimage(rect, GREY1); + if(mask == nil) + goto Return; + memfillcolor(mask, DTransparent); + memimagedraw(mask, rect, figure, rect.min, wedge, rect.min, S); + c = subpt(c, dst->r.min); + memdraw(dst, dst->r, src, subpt(sp, c), mask, subpt(p00, c), op); + + Return: + freememimage(wedge); + freememimage(figure); + freememimage(mask); +} blob - /dev/null blob + b3a8c21119b67ad4a227fbe2caa4e497b1ed1350 (mode 644) --- /dev/null +++ src/libmemdraw/arctest.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +extern int drawdebug; +void +main(int argc, char **argv) +{ + char cc; + Memimage *x; + Point c = {208,871}; + int a = 441; + int b = 441; + int thick = 0; + Point sp = {0,0}; + int alpha = 51; + int phi = 3; + vlong t0, t1; + int i, n; + vlong del; + + memimageinit(); + + x = allocmemimage(Rect(0,0,1000,1000), CMAP8); + n = atoi(argv[1]); + + t0 = nsec(); + t0 = nsec(); + t0 = nsec(); + t1 = nsec(); + del = t1-t0; + t0 = nsec(); + for(i=0; i +#include +#include +#include + +int +_cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, bpl, c, cnt, offs; + uchar mem[NMEM], *memp, *omemp, *emem, *linep, *elinep, *u, *eu; + + if(!rectinrect(r, i->r)) + return -1; + bpl = bytesperline(r, i->depth); + u = data; + eu = data+ndata; + memp = mem; + emem = mem+NMEM; + y = r.min.y; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + for(;;){ + if(linep == elinep){ + if(++y == r.max.y) + break; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + } + if(u == eu){ /* buffer too small */ + return -1; + } + c = *u++; + if(c >= 128){ + for(cnt=c-128+1; cnt!=0 ;--cnt){ + if(u == eu){ /* buffer too small */ + return -1; + } + if(linep == elinep){ /* phase error */ + return -1; + } + *linep++ = *u; + *memp++ = *u++; + if(memp == emem) + memp = mem; + } + } + else{ + if(u == eu) /* short buffer */ + return -1; + offs = *u++ + ((c&3)<<8)+1; + if(memp-mem < offs) + omemp = memp+(NMEM-offs); + else + omemp = memp-offs; + for(cnt=(c>>2)+NMATCH; cnt!=0; --cnt){ + if(linep == elinep) /* phase error */ + return -1; + *linep++ = *omemp; + *memp++ = *omemp++; + if(omemp == emem) + omemp = mem; + if(memp == emem) + memp = mem; + } + } + } + return u-data; +} blob - /dev/null blob + 2561c3f1cd2e9309849728ab0f16e3c0fc00c504 (mode 644) --- /dev/null +++ src/libmemdraw/cmap.c @@ -0,0 +1,320 @@ +/* + * generated by mkcmap.c + */ +#include +#include +#include +#include + +static Memcmap def = { +/* cmap2rgb */ { + 0x00,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x88,0x00,0x00,0xcc,0x00,0x44,0x00,0x00, + 0x44,0x44,0x00,0x44,0x88,0x00,0x44,0xcc,0x00,0x88,0x00,0x00,0x88,0x44,0x00,0x88, + 0x88,0x00,0x88,0xcc,0x00,0xcc,0x00,0x00,0xcc,0x44,0x00,0xcc,0x88,0x00,0xcc,0xcc, + 0x00,0xdd,0xdd,0x11,0x11,0x11,0x00,0x00,0x55,0x00,0x00,0x99,0x00,0x00,0xdd,0x00, + 0x55,0x00,0x00,0x55,0x55,0x00,0x4c,0x99,0x00,0x49,0xdd,0x00,0x99,0x00,0x00,0x99, + 0x4c,0x00,0x99,0x99,0x00,0x93,0xdd,0x00,0xdd,0x00,0x00,0xdd,0x49,0x00,0xdd,0x93, + 0x00,0xee,0x9e,0x00,0xee,0xee,0x22,0x22,0x22,0x00,0x00,0x66,0x00,0x00,0xaa,0x00, + 0x00,0xee,0x00,0x66,0x00,0x00,0x66,0x66,0x00,0x55,0xaa,0x00,0x4f,0xee,0x00,0xaa, + 0x00,0x00,0xaa,0x55,0x00,0xaa,0xaa,0x00,0x9e,0xee,0x00,0xee,0x00,0x00,0xee,0x4f, + 0x00,0xff,0x55,0x00,0xff,0xaa,0x00,0xff,0xff,0x33,0x33,0x33,0x00,0x00,0x77,0x00, + 0x00,0xbb,0x00,0x00,0xff,0x00,0x77,0x00,0x00,0x77,0x77,0x00,0x5d,0xbb,0x00,0x55, + 0xff,0x00,0xbb,0x00,0x00,0xbb,0x5d,0x00,0xbb,0xbb,0x00,0xaa,0xff,0x00,0xff,0x00, + 0x44,0x00,0x44,0x44,0x00,0x88,0x44,0x00,0xcc,0x44,0x44,0x00,0x44,0x44,0x44,0x44, + 0x44,0x88,0x44,0x44,0xcc,0x44,0x88,0x00,0x44,0x88,0x44,0x44,0x88,0x88,0x44,0x88, + 0xcc,0x44,0xcc,0x00,0x44,0xcc,0x44,0x44,0xcc,0x88,0x44,0xcc,0xcc,0x44,0x00,0x00, + 0x55,0x00,0x00,0x55,0x00,0x55,0x4c,0x00,0x99,0x49,0x00,0xdd,0x55,0x55,0x00,0x55, + 0x55,0x55,0x4c,0x4c,0x99,0x49,0x49,0xdd,0x4c,0x99,0x00,0x4c,0x99,0x4c,0x4c,0x99, + 0x99,0x49,0x93,0xdd,0x49,0xdd,0x00,0x49,0xdd,0x49,0x49,0xdd,0x93,0x49,0xdd,0xdd, + 0x4f,0xee,0xee,0x66,0x00,0x00,0x66,0x00,0x66,0x55,0x00,0xaa,0x4f,0x00,0xee,0x66, + 0x66,0x00,0x66,0x66,0x66,0x55,0x55,0xaa,0x4f,0x4f,0xee,0x55,0xaa,0x00,0x55,0xaa, + 0x55,0x55,0xaa,0xaa,0x4f,0x9e,0xee,0x4f,0xee,0x00,0x4f,0xee,0x4f,0x4f,0xee,0x9e, + 0x55,0xff,0xaa,0x55,0xff,0xff,0x77,0x00,0x00,0x77,0x00,0x77,0x5d,0x00,0xbb,0x55, + 0x00,0xff,0x77,0x77,0x00,0x77,0x77,0x77,0x5d,0x5d,0xbb,0x55,0x55,0xff,0x5d,0xbb, + 0x00,0x5d,0xbb,0x5d,0x5d,0xbb,0xbb,0x55,0xaa,0xff,0x55,0xff,0x00,0x55,0xff,0x55, + 0x88,0x00,0x88,0x88,0x00,0xcc,0x88,0x44,0x00,0x88,0x44,0x44,0x88,0x44,0x88,0x88, + 0x44,0xcc,0x88,0x88,0x00,0x88,0x88,0x44,0x88,0x88,0x88,0x88,0x88,0xcc,0x88,0xcc, + 0x00,0x88,0xcc,0x44,0x88,0xcc,0x88,0x88,0xcc,0xcc,0x88,0x00,0x00,0x88,0x00,0x44, + 0x99,0x00,0x4c,0x99,0x00,0x99,0x93,0x00,0xdd,0x99,0x4c,0x00,0x99,0x4c,0x4c,0x99, + 0x4c,0x99,0x93,0x49,0xdd,0x99,0x99,0x00,0x99,0x99,0x4c,0x99,0x99,0x99,0x93,0x93, + 0xdd,0x93,0xdd,0x00,0x93,0xdd,0x49,0x93,0xdd,0x93,0x93,0xdd,0xdd,0x99,0x00,0x00, + 0xaa,0x00,0x00,0xaa,0x00,0x55,0xaa,0x00,0xaa,0x9e,0x00,0xee,0xaa,0x55,0x00,0xaa, + 0x55,0x55,0xaa,0x55,0xaa,0x9e,0x4f,0xee,0xaa,0xaa,0x00,0xaa,0xaa,0x55,0xaa,0xaa, + 0xaa,0x9e,0x9e,0xee,0x9e,0xee,0x00,0x9e,0xee,0x4f,0x9e,0xee,0x9e,0x9e,0xee,0xee, + 0xaa,0xff,0xff,0xbb,0x00,0x00,0xbb,0x00,0x5d,0xbb,0x00,0xbb,0xaa,0x00,0xff,0xbb, + 0x5d,0x00,0xbb,0x5d,0x5d,0xbb,0x5d,0xbb,0xaa,0x55,0xff,0xbb,0xbb,0x00,0xbb,0xbb, + 0x5d,0xbb,0xbb,0xbb,0xaa,0xaa,0xff,0xaa,0xff,0x00,0xaa,0xff,0x55,0xaa,0xff,0xaa, + 0xcc,0x00,0xcc,0xcc,0x44,0x00,0xcc,0x44,0x44,0xcc,0x44,0x88,0xcc,0x44,0xcc,0xcc, + 0x88,0x00,0xcc,0x88,0x44,0xcc,0x88,0x88,0xcc,0x88,0xcc,0xcc,0xcc,0x00,0xcc,0xcc, + 0x44,0xcc,0xcc,0x88,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0xcc,0x00,0x44,0xcc,0x00,0x88, + 0xdd,0x00,0x93,0xdd,0x00,0xdd,0xdd,0x49,0x00,0xdd,0x49,0x49,0xdd,0x49,0x93,0xdd, + 0x49,0xdd,0xdd,0x93,0x00,0xdd,0x93,0x49,0xdd,0x93,0x93,0xdd,0x93,0xdd,0xdd,0xdd, + 0x00,0xdd,0xdd,0x49,0xdd,0xdd,0x93,0xdd,0xdd,0xdd,0xdd,0x00,0x00,0xdd,0x00,0x49, + 0xee,0x00,0x4f,0xee,0x00,0x9e,0xee,0x00,0xee,0xee,0x4f,0x00,0xee,0x4f,0x4f,0xee, + 0x4f,0x9e,0xee,0x4f,0xee,0xee,0x9e,0x00,0xee,0x9e,0x4f,0xee,0x9e,0x9e,0xee,0x9e, + 0xee,0xee,0xee,0x00,0xee,0xee,0x4f,0xee,0xee,0x9e,0xee,0xee,0xee,0xee,0x00,0x00, + 0xff,0x00,0x00,0xff,0x00,0x55,0xff,0x00,0xaa,0xff,0x00,0xff,0xff,0x55,0x00,0xff, + 0x55,0x55,0xff,0x55,0xaa,0xff,0x55,0xff,0xff,0xaa,0x00,0xff,0xaa,0x55,0xff,0xaa, + 0xaa,0xff,0xaa,0xff,0xff,0xff,0x00,0xff,0xff,0x55,0xff,0xff,0xaa,0xff,0xff,0xff, +}, +/* rgb2cmap */ { + 0x00,0x00,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x22,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x22,0x22,0x22,0x33,0x33,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x22,0x22,0x33,0x33,0x33,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x33,0x33,0x33,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x33,0x33,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x4f,0x22,0x22,0x22,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x22,0x22,0x22,0x33,0x33,0x33,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x43,0x22,0x33,0x33,0x33,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x46,0x57,0x68, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x4a,0x5b,0x6c, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x5f, + 0x5c,0x5c,0x5c,0x4c,0x5d,0x5d,0x5d,0x4d,0x4d,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x6e,0x6e,0x5e,0x5e,0x5e,0x6f,0x6f,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x4f,0x4f,0x40,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x22,0x33,0x33,0x33,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x59,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x48,0x59,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5d,0x4d,0x5e,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x6d,0x6d,0x6d,0x5d,0x6e,0x6e,0x6e,0x5e,0x5e,0x6f,0x6f,0x70,0x5f,0x5f,0x60,0x60, + 0x7e,0x7e,0x7e,0x6e,0x6e,0x7f,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x70,0x60,0x60,0x71, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x33,0x33,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x66,0x49,0x49,0x49,0x78,0x78,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x69,0x69,0x69,0x59,0x6a,0x6a,0x6a,0x7b,0x5a,0x6b,0x6b,0x6b,0x7c,0x6c,0x6c,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x4c,0x7b,0x7b,0x7b,0x4d,0x6b,0x6b,0x7c,0x7c,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x7b,0x7b,0x4d,0x4d,0x5e,0x7c,0x7c,0x4e,0x5f,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x6e,0x4d,0x5e,0x5e,0x6f,0x4e,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x6e,0x5e,0x6f,0x6f,0x6f,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x6e,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x65,0x65,0x65,0x55,0x55,0x66,0x66,0x66,0x77,0x67,0x78,0x78,0x78,0x78,0x79,0x79, + 0x65,0x65,0x65,0x48,0x48,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x5b,0x79,0x79, + 0x76,0x76,0x76,0x48,0x59,0x59,0x77,0x77,0x77,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x77,0x5a,0x5a,0x6b,0x6b,0x5b,0x6c,0x6c,0x7d, + 0x69,0x69,0x69,0x6a,0x6a,0x6a,0x7b,0x7b,0x5a,0x6b,0x6b,0x7c,0x7c,0x6c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x6b,0x7c,0x7c,0x7c,0x7c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x5e,0x7c,0x7c,0x7c,0x5f,0x5f,0x7d, + 0x6d,0x6d,0x6d,0x5d,0x5d,0x6e,0x7b,0x5e,0x5e,0x6f,0x6f,0x7c,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x83,0x83,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x84,0x85,0x85,0x85,0x96,0x79, + 0x82,0x82,0x82,0x83,0x83,0x83,0x66,0x84,0x84,0x84,0x67,0x85,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x83,0x66,0x66,0x66,0x84,0x84,0x78,0x78,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x66,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x96,0x79,0x79, + 0x76,0x76,0x76,0x87,0x87,0x66,0x77,0x77,0x77,0x88,0x78,0x89,0x89,0x89,0x89,0x79, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x6b,0x89,0x89,0x9a,0x9a,0x7d, + 0x7a,0x7a,0x7a,0x87,0x6a,0x7b,0x7b,0x7b,0x88,0x6b,0x6b,0x7c,0x7c,0x9a,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x7b,0x7b,0x8c,0x8c,0x8c,0x7c,0x7c,0x8d,0x8d,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x7b,0x8c,0x8c,0x8c,0x7c,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0x7f,0x8c,0x9d,0x9d,0x70,0x70,0x9e,0x9e,0x9e,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x9d,0x70,0x70,0x70,0x9e,0x9e,0x71,0x71, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x95,0x85,0x85,0x85,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x83,0x94,0x84,0x84,0x84,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x83,0x94,0x94,0x77,0x77,0x77,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x9d,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x8b,0x9c,0x9c,0x9c,0x8c,0x9d,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xaf, + 0x9b,0x9b,0x9b,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0x9d,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xad,0x9d,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xaf, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x83,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x93,0x93,0x93,0x83,0x94,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x77,0x95,0x95,0xa6,0xa6,0x96,0xa7,0xa7,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0xa9,0xa9,0x8c,0x8c,0x8c,0xaa,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x9c,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xbc, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0xad,0x9d,0x9d,0x9d,0xae,0x8d,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0x9c,0xad,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0xaf,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xad,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xbf,0xaf,0xaf,0xb0, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0xa5,0xa5,0xa5,0xb6,0x95,0xa6,0xa6,0xa6,0xb7,0xa7,0xa7,0xb8, + 0xa4,0xa4,0xa4,0xa5,0xa5,0xa5,0xb6,0xb6,0x95,0xa6,0xa6,0xb7,0xb7,0xa7,0xb8,0xb8, + 0xb5,0xb5,0xb5,0x87,0x87,0xb6,0xb6,0xb6,0x88,0x99,0xa6,0xb7,0xb7,0x9a,0xb8,0xb8, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xa9,0xa9,0x99,0xaa,0xaa,0xaa,0xbb,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0x8c,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0x9c,0x9c,0xba,0xba,0xba,0x9d,0x9d,0xbb,0xbb,0xbb,0x9e,0x9e,0xbc, + 0xac,0xac,0xac,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xbe,0xbe,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xae,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xc2,0xc2,0xb2,0xb2,0xc3,0xc3,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xa5,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xa7,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xa5,0xb6,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xa5,0xb6,0xb6,0xb6,0xc3,0xa6,0xa6,0xb7,0xb7,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xa5,0xb6,0xb6,0xb6,0xb6,0xc3,0xa6,0xb7,0xb7,0xb7,0xb7,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xb6,0xb6,0xc7,0xc7,0xc7,0xb7,0xb7,0xc8,0xc8,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xaa,0xc8,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xc6,0xc6,0xa9,0xa9,0xc7,0xc7,0xaa,0xaa,0xaa,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0xaa,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xba,0xcb,0xaa,0xbb,0xbb,0xbb,0xcc,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xae,0xcc,0xcc,0xcc,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xbe,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xcc,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbf,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb2,0xc3,0xc3,0xc3,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xc2,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xd5, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb6,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xb6,0xb6,0xc3,0xc3,0xd4,0xb7,0xb7,0xc4,0xd5,0xd5,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xb6,0xb6,0xb6,0xb6,0xc3,0xd4,0xb7,0xb7,0xb7,0xd5,0xd5,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xb6,0xc7,0xc7,0xc7,0xb7,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xd8,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xd7,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xd7,0xd7,0xba,0xba,0xba,0xd8,0xd8,0xbb,0xbb,0xbb,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xbb,0xcc,0xcc,0xcc,0xdd,0xdd, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xb0, + 0xbd,0xbd,0xbd,0xdb,0xbe,0xbe,0xbe,0xdc,0xdc,0xbf,0xbf,0xbf,0xdd,0xdd,0xb0,0xb0, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xc3,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xd2,0xd2,0xd2,0xc2,0xd3,0xd3,0xd3,0xc3,0xc3,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xd3,0xc3,0xd4,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xe4,0xc3,0xd4,0xd4,0xe5,0xc4,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xd3,0xe4,0xb6,0xd4,0xd4,0xe5,0xe5,0xb7,0xd5,0xd5,0xe6,0xe6, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xd9, + 0xd6,0xd6,0xd6,0xc6,0xd7,0xd7,0xd7,0xc7,0xd8,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xea, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xe8,0xd8,0xd8,0xd8,0xe9,0xc8,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xe9,0xcc,0xcc,0xcc,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xe4,0xe4,0xd4,0xd4,0xd4,0xe5,0xe5,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xe4,0xe4,0xe4,0xd4,0xd4,0xe5,0xe5,0xf6,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xe4,0xd4,0xe5,0xe5,0xe5,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xf5,0xc7,0xd8,0xd8,0xf6,0xc8,0xd9,0xd9,0xd9,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xd8,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xe8,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xea,0xea,0xea, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xf9,0xf9,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xe8,0xf9,0xf9,0xf9,0xcb,0xe9,0xe9,0xfa,0xfa,0xcc,0xea,0xea,0xfb, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xe4,0xf5,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xf6,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xd8,0xf6,0xf6,0xf6,0xd9,0xd9,0xf7,0xf7, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xd8,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xea, + 0xf8,0xf8,0xf8,0xe8,0xe8,0xf9,0xf9,0xf9,0xe9,0xe9,0xfa,0xfa,0xfa,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xe9,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xdb,0xf9,0xf9,0xf9,0xdc,0xdc,0xfa,0xfa,0xfa,0xdd,0xdd,0xee,0xfb, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xed,0xfe,0xfe,0xfe,0xfe,0xee,0xff,0xff, +} +}; +Memcmap *memdefcmap = &def; +void _memmkcmap(void){} blob - /dev/null blob + 4fa5942a4bb9b578fd213cc7debafbc2230ba35b (mode 644) --- /dev/null +++ src/libmemdraw/cread.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +Memimage* +creadmemimage(int fd) +{ + char hdr[5*12+1]; + Rectangle r; + int m, nb, miny, maxy, new, ldepth, ncblock; + uchar *buf; + Memimage *i; + u32int chan; + + if(readn(fd, hdr, 5*12) != 5*12){ + werrstr("readmemimage: short header (2)"); + return nil; + } + + /* + * distinguish new channel descriptor from old ldepth. + * channel descriptors have letters as well as numbers, + * while ldepths are a single digit formatted as %-11d. + */ + new = 0; + for(m=0; m<10; m++){ + if(hdr[m] != ' '){ + new = 1; + break; + } + } + if(hdr[11] != ' '){ + werrstr("creadimage: bad format"); + return nil; + } + if(new){ + hdr[11] = '\0'; + if((chan = strtochan(hdr)) == 0){ + werrstr("creadimage: bad channel string %s", hdr); + return nil; + } + }else{ + ldepth = ((int)hdr[10])-'0'; + if(ldepth<0 || ldepth>3){ + werrstr("creadimage: bad ldepth %d", ldepth); + return nil; + } + chan = drawld2chan[ldepth]; + } + r.min.x=atoi(hdr+1*12); + r.min.y=atoi(hdr+2*12); + r.max.x=atoi(hdr+3*12); + r.max.y=atoi(hdr+4*12); + if(r.min.x>r.max.x || r.min.y>r.max.y){ + werrstr("creadimage: bad rectangle"); + return nil; + } + + i = allocmemimage(r, chan); + if(i == nil) + return nil; + ncblock = _compblocksize(r, i->depth); + buf = malloc(ncblock); + if(buf == nil) + goto Errout; + miny = r.min.y; + while(miny != r.max.y){ + if(readn(fd, hdr, 2*12) != 2*12){ + Shortread: + werrstr("readmemimage: short read"); + Errout: + freememimage(i); + free(buf); + return nil; + } + maxy = atoi(hdr+0*12); + nb = atoi(hdr+1*12); + if(maxy<=miny || r.max.y +#include +#include +#include + +Memsubfont* +getmemdefont(void) +{ + char *hdr, *p; + int n; + Fontchar *fc; + Memsubfont *f; + int ld; + Rectangle r; + Memdata *md; + Memimage *i; + + /* + * make sure data is word-aligned. this is true with Plan 9 compilers + * but not in general. the byte order is right because the data is + * declared as char*, not u32int*. + */ + p = (char*)defontdata; + n = (uintptr)p & 3; + if(n != 0){ + memmove(p+(4-n), p, sizeofdefont-n); + p += 4-n; + } + ld = atoi(p+0*12); + r.min.x = atoi(p+1*12); + r.min.y = atoi(p+2*12); + r.max.x = atoi(p+3*12); + r.max.y = atoi(p+4*12); + + md = mallocz(sizeof(Memdata), 1); + if(md == nil) + return nil; + + p += 5*12; + + md->base = nil; /* so freememimage doesn't free p */ + md->bdata = (uchar*)p; /* ick */ + md->ref = 1; + md->allocd = 1; /* so freememimage does free md */ + + i = allocmemimaged(r, drawld2chan[ld], md, nil); + if(i == nil){ + free(md); + return nil; + } + + hdr = p+Dy(r)*i->width*sizeof(u32int); + n = atoi(hdr); + p = hdr+3*12; + fc = malloc(sizeof(Fontchar)*(n+1)); + if(fc == 0){ + freememimage(i); + return 0; + } + _unpackinfo(fc, (uchar*)p, n); + f = allocmemsubfont("*default*", n, atoi(hdr+12), atoi(hdr+24), fc, i); + if(f == 0){ + freememimage(i); + free(fc); + return 0; + } + return f; +} blob - /dev/null blob + 2d8681e5ee156abfa91e72b41a88477921e13fa7 (mode 644) --- /dev/null +++ src/libmemdraw/draw.c @@ -0,0 +1,2489 @@ +#include +#include +#include +#include + +int drawdebug; +static int tablesbuilt; + +/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */ +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +/* + * for 0 ≤ x ≤ 255*255, (x*0x0101+0x100)>>16 is a perfect approximation. + * for 0 ≤ x < (1<<16), x/255 = ((x+1)*0x0101)>>16 is a perfect approximation. + * the last one is perfect for all up to 1<<16, avoids a multiply, but requires a rathole. + */ +/* #define DIV255(x) (((x)*257+256)>>16) */ +#define DIV255(x) ((((x)+1)*257)>>16) +/* #define DIV255(x) (tmp=(x)+1, (tmp+(tmp>>8))>>8) */ + +#define MUL(x, y, t) (t = (x)*(y)+128, (t+(t>>8))>>8) +#define MASK13 0xFF00FF00 +#define MASK02 0x00FF00FF +#define MUL13(a, x, t) (t = (a)*(((x)&MASK13)>>8)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL02(a, x, t) (t = (a)*(((x)&MASK02)>>0)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL0123(a, x, s, t) ((MUL13(a, x, s)<<8)|MUL02(a, x, t)) + +#define MUL2(u, v, x, y) (t = (u)*(v)+(x)*(y)+256, (t+(t>>8))>>8) + +static void mktables(void); +typedef int Subdraw(Memdrawparam*); +static Subdraw chardraw, alphadraw, memoptdraw; + +static Memimage* memones; +static Memimage* memzeros; +Memimage *memwhite; +Memimage *memblack; +Memimage *memtransparent; +Memimage *memopaque; + +int __ifmt(Fmt*); + +void +memimageinit(void) +{ + static int didinit = 0; + + if(didinit) + return; + + didinit = 1; + + mktables(); + _memmkcmap(); + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + fmtinstall('b', __ifmt); + + memones = allocmemimage(Rect(0,0,1,1), GREY1); + memones->flags |= Frepl; + memones->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memones, ZP) = ~0; + + memzeros = allocmemimage(Rect(0,0,1,1), GREY1); + memzeros->flags |= Frepl; + memzeros->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memzeros, ZP) = 0; + + if(memones == nil || memzeros == nil) + assert(0 /*cannot initialize memimage library */); /* RSC BUG */ + + memwhite = memones; + memblack = memzeros; + memopaque = memones; + memtransparent = memzeros; +} + +u32int _imgtorgba(Memimage*, u32int); +u32int _rgbatoimg(Memimage*, u32int); +u32int _pixelbits(Memimage*, Point); + +#define DBG if(drawdebug) +static Memdrawparam par; + +Memdrawparam* +_memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op) +{ + if(mask == nil) + mask = memopaque; + +DBG print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst, dst->chan, r, dst->data->bdata, src, src->chan, p0, mask, mask->chan, p1); + + if(drawclip(dst, &r, src, &p0, mask, &p1, &par.sr, &par.mr) == 0){ +/* if(drawdebug) */ +/* iprint("empty clipped rectangle\n"); */ + return nil; + } + + if(op < Clear || op > SoverD){ +/* if(drawdebug) */ +/* iprint("op out of range: %d\n", op); */ + return nil; + } + + par.op = op; + par.dst = dst; + par.r = r; + par.src = src; + /* par.sr set by drawclip */ + par.mask = mask; + /* par.mr set by drawclip */ + + par.state = 0; + if(src->flags&Frepl){ + par.state |= Replsrc; + if(Dx(src->r)==1 && Dy(src->r)==1){ + par.sval = pixelbits(src, src->r.min); + par.state |= Simplesrc; + par.srgba = _imgtorgba(src, par.sval); + par.sdval = _rgbatoimg(dst, par.srgba); + if((par.srgba&0xFF) == 0 && (op&DoutS)){ +/* if (drawdebug) iprint("fill with transparent source\n"); */ + return nil; /* no-op successfully handled */ + } + if((par.srgba&0xFF) == 0xFF) + par.state |= Fullsrc; + } + } + + if(mask->flags & Frepl){ + par.state |= Replmask; + if(Dx(mask->r)==1 && Dy(mask->r)==1){ + par.mval = pixelbits(mask, mask->r.min); + if(par.mval == 0 && (op&DoutS)){ +/* if(drawdebug) iprint("fill with zero mask\n"); */ + return nil; /* no-op successfully handled */ + } + par.state |= Simplemask; + if(par.mval == ~0) + par.state |= Fullmask; + par.mrgba = _imgtorgba(mask, par.mval); + } + } + +/* if(drawdebug) */ +/* iprint("dr %R sr %R mr %R...", r, par.sr, par.mr); */ +DBG print("draw dr %R sr %R mr %R %lux\n", r, par.sr, par.mr, par.state); + + return ∥ +} + +void +_memimagedraw(Memdrawparam *par) +{ + /* + * Now that we've clipped the parameters down to be consistent, we + * simply try sub-drawing routines in order until we find one that was able + * to handle us. If the sub-drawing routine returns zero, it means it was + * unable to satisfy the request, so we do not return. + */ + + /* + * Hardware support. Each video driver provides this function, + * which checks to see if there is anything it can help with. + * There could be an if around this checking to see if dst is in video memory. + */ +DBG print("test hwdraw\n"); + if(hwdraw(par)){ +/*if(drawdebug) iprint("hw handled\n"); */ +DBG print("hwdraw handled\n"); + return; + } + /* + * Optimizations using memmove and memset. + */ +DBG print("test memoptdraw\n"); + if(memoptdraw(par)){ +/*if(drawdebug) iprint("memopt handled\n"); */ +DBG print("memopt handled\n"); + return; + } + + /* + * Character drawing. + * Solid source color being painted through a boolean mask onto a high res image. + */ +DBG print("test chardraw\n"); + if(chardraw(par)){ +/*if(drawdebug) iprint("chardraw handled\n"); */ +DBG print("chardraw handled\n"); + return; + } + + /* + * General calculation-laden case that does alpha for each pixel. + */ +DBG print("do alphadraw\n"); + alphadraw(par); +/*if(drawdebug) iprint("alphadraw handled\n"); */ +DBG print("alphadraw handled\n"); +} +#undef DBG + +/* + * Clip the destination rectangle further based on the properties of the + * source and mask rectangles. Once the destination rectangle is properly + * clipped, adjust the source and mask rectangles to be the same size. + * Then if source or mask is replicated, move its clipped rectangle + * so that its minimum point falls within the repl rectangle. + * + * Return zero if the final rectangle is null. + */ +int +drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr) +{ + Point rmin, delta; + int splitcoords; + Rectangle omr; + + if(r->min.x>=r->max.x || r->min.y>=r->max.y) + return 0; + splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y); + /* clip to destination */ + rmin = r->min; + if(!rectclip(r, dst->r) || !rectclip(r, dst->clipr)) + return 0; + /* move mask point */ + p1->x += r->min.x-rmin.x; + p1->y += r->min.y-rmin.y; + /* move source point */ + p0->x += r->min.x-rmin.x; + p0->y += r->min.y-rmin.y; + /* map destination rectangle into source */ + sr->min = *p0; + sr->max.x = p0->x+Dx(*r); + sr->max.y = p0->y+Dy(*r); + /* sr is r in source coordinates; clip to source */ + if(!(src->flags&Frepl) && !rectclip(sr, src->r)) + return 0; + if(!rectclip(sr, src->clipr)) + return 0; + /* compute and clip rectangle in mask */ + if(splitcoords){ + /* move mask point with source */ + p1->x += sr->min.x-p0->x; + p1->y += sr->min.y-p0->y; + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + omr = *mr; + /* mr is now rectangle in mask; clip it */ + if(!(mask->flags&Frepl) && !rectclip(mr, mask->r)) + return 0; + if(!rectclip(mr, mask->clipr)) + return 0; + /* reflect any clips back to source */ + sr->min.x += mr->min.x-omr.min.x; + sr->min.y += mr->min.y-omr.min.y; + sr->max.x += mr->max.x-omr.max.x; + sr->max.y += mr->max.y-omr.max.y; + *p1 = mr->min; + }else{ + if(!(mask->flags&Frepl) && !rectclip(sr, mask->r)) + return 0; + if(!rectclip(sr, mask->clipr)) + return 0; + *p1 = sr->min; + } + + /* move source clipping back to destination */ + delta.x = r->min.x - p0->x; + delta.y = r->min.y - p0->y; + r->min.x = sr->min.x + delta.x; + r->min.y = sr->min.y + delta.y; + r->max.x = sr->max.x + delta.x; + r->max.y = sr->max.y + delta.y; + + /* move source rectangle so sr->min is in src->r */ + if(src->flags&Frepl) { + delta.x = drawreplxy(src->r.min.x, src->r.max.x, sr->min.x) - sr->min.x; + delta.y = drawreplxy(src->r.min.y, src->r.max.y, sr->min.y) - sr->min.y; + sr->min.x += delta.x; + sr->min.y += delta.y; + sr->max.x += delta.x; + sr->max.y += delta.y; + } + *p0 = sr->min; + + /* move mask point so it is in mask->r */ + *p1 = drawrepl(mask->r, *p1); + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + + assert(Dx(*sr) == Dx(*mr) && Dx(*mr) == Dx(*r)); + assert(Dy(*sr) == Dy(*mr) && Dy(*mr) == Dy(*r)); + assert(ptinrect(*p0, src->r)); + assert(ptinrect(*p1, mask->r)); + assert(ptinrect(r->min, dst->r)); + + return 1; +} + +/* + * Conversion tables. + */ +static uchar replbit[1+8][256]; /* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */ +static uchar conv18[256][8]; /* conv18[x][y] is the yth pixel in the depth-1 pixel x */ +static uchar conv28[256][4]; /* ... */ +static uchar conv48[256][2]; + +/* + * bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8. + * the X's are where to put the bottom (ones) bit of the n-bit pattern. + * only the top 8 bits of the result are actually used. + * (the lower 8 bits are needed to get bits in the right place + * when n is not a divisor of 8.) + * + * Should check to see if its easier to just refer to replmul than + * use the precomputed values in replbit. On PCs it may well + * be; on machines with slow multiply instructions it probably isn't. + */ +#define a ((((((((((((((((0 +#define X *2+1) +#define _ *2) +static int replmul[1+8] = { + 0, + a X X X X X X X X X X X X X X X X, + a _ X _ X _ X _ X _ X _ X _ X _ X, + a _ _ X _ _ X _ _ X _ _ X _ _ X _, + a _ _ _ X _ _ _ X _ _ _ X _ _ _ X, + a _ _ _ _ X _ _ _ _ X _ _ _ _ X _, + a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _, + a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _, + a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X, +}; +#undef a +#undef X +#undef _ + +static void +mktables(void) +{ + int i, j, mask, sh, small; + + if(tablesbuilt) + return; + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + tablesbuilt = 1; + + /* bit replication up to 8 bits */ + for(i=0; i<256; i++){ + for(j=0; j<=8; j++){ /* j <= 8 [sic] */ + small = i & ((1<>8; + } + } + + /* bit unpacking up to 8 bits, only powers of 2 */ + for(i=0; i<256; i++){ + for(j=0, sh=7, mask=1; j<8; j++, sh--) + conv18[i][j] = replbit[1][(i>>sh)&mask]; + + for(j=0, sh=6, mask=3; j<4; j++, sh-=2) + conv28[i][j] = replbit[2][(i>>sh)&mask]; + + for(j=0, sh=4, mask=15; j<2; j++, sh-=4) + conv48[i][j] = replbit[4][(i>>sh)&mask]; + } +} + +static uchar ones = 0xff; + +/* + * General alpha drawing case. Can handle anything. + */ +typedef struct Buffer Buffer; +struct Buffer { + /* used by most routines */ + uchar *red; + uchar *grn; + uchar *blu; + uchar *alpha; + uchar *grey; + u32int *rgba; + int delta; /* number of bytes to add to pointer to get next pixel to the right */ + + /* used by boolcalc* for mask data */ + uchar *m; /* ptr to mask data r.min byte; like p->bytermin */ + int mskip; /* no. of left bits to skip in *m */ + uchar *bm; /* ptr to mask data img->r.min byte; like p->bytey0s */ + int bmskip; /* no. of left bits to skip in *bm */ + uchar *em; /* ptr to mask data img->r.max.x byte; like p->bytey0e */ + int emskip; /* no. of right bits to skip in *em */ +}; + +typedef struct Param Param; +typedef Buffer Readfn(Param*, uchar*, int); +typedef void Writefn(Param*, uchar*, Buffer); +typedef Buffer Calcfn(Buffer, Buffer, Buffer, int, int, int); + +enum { + MAXBCACHE = 16 +}; + +/* giant rathole to customize functions with */ +struct Param { + Readfn *replcall; + Readfn *greymaskcall; + Readfn *convreadcall; + Writefn *convwritecall; + + Memimage *img; + Rectangle r; + int dx; /* of r */ + int needbuf; + int convgrey; + int alphaonly; + + uchar *bytey0s; /* byteaddr(Pt(img->r.min.x, img->r.min.y)) */ + uchar *bytermin; /* byteaddr(Pt(r.min.x, img->r.min.y)) */ + uchar *bytey0e; /* byteaddr(Pt(img->r.max.x, img->r.min.y)) */ + int bwidth; + + int replcache; /* if set, cache buffers */ + Buffer bcache[MAXBCACHE]; + u32int bfilled; + uchar *bufbase; + int bufoff; + int bufdelta; + + int dir; + + int convbufoff; + uchar *convbuf; + Param *convdpar; + int convdx; +}; + +static uchar *drawbuf; +static int ndrawbuf; +static int mdrawbuf; +static Param spar, mpar, dpar; /* easier on the stacks */ +static Readfn greymaskread, replread, readptr; +static Writefn nullwrite; +static Calcfn alphacalc0, alphacalc14, alphacalc2810, alphacalc3679, alphacalc5, alphacalc11, alphacalcS; +static Calcfn boolcalc14, boolcalc236789, boolcalc1011; + +static Readfn* readfn(Memimage*); +static Readfn* readalphafn(Memimage*); +static Writefn* writefn(Memimage*); + +static Calcfn* boolcopyfn(Memimage*, Memimage*); +static Readfn* convfn(Memimage*, Param*, Memimage*, Param*); + +static Calcfn *alphacalc[Ncomp] = +{ + alphacalc0, /* Clear */ + alphacalc14, /* DoutS */ + alphacalc2810, /* SoutD */ + alphacalc3679, /* DxorS */ + alphacalc14, /* DinS */ + alphacalc5, /* D */ + alphacalc3679, /* DatopS */ + alphacalc3679, /* DoverS */ + alphacalc2810, /* SinD */ + alphacalc3679, /* SatopD */ + alphacalc2810, /* S */ + alphacalc11, /* SoverD */ +}; + +static Calcfn *boolcalc[Ncomp] = +{ + alphacalc0, /* Clear */ + boolcalc14, /* DoutS */ + boolcalc236789, /* SoutD */ + boolcalc236789, /* DxorS */ + boolcalc14, /* DinS */ + alphacalc5, /* D */ + boolcalc236789, /* DatopS */ + boolcalc236789, /* DoverS */ + boolcalc236789, /* SinD */ + boolcalc236789, /* SatopD */ + boolcalc1011, /* S */ + boolcalc1011, /* SoverD */ +}; + +static int +allocdrawbuf(void) +{ + uchar *p; + + if(ndrawbuf > mdrawbuf){ + p = realloc(drawbuf, ndrawbuf); + if(p == nil){ + werrstr("memimagedraw out of memory"); + return -1; + } + drawbuf = p; + mdrawbuf = ndrawbuf; + } + return 0; +} + +static void +getparam(Param *p, Memimage *img, Rectangle r, int convgrey, int needbuf) +{ + int nbuf; + + memset(p, 0, sizeof *p); + + p->img = img; + p->r = r; + p->dx = Dx(r); + p->needbuf = needbuf; + p->convgrey = convgrey; + + assert(img->r.min.x <= r.min.x && r.min.x < img->r.max.x); + + p->bytey0s = byteaddr(img, Pt(img->r.min.x, img->r.min.y)); + p->bytermin = byteaddr(img, Pt(r.min.x, img->r.min.y)); + p->bytey0e = byteaddr(img, Pt(img->r.max.x, img->r.min.y)); + p->bwidth = sizeof(u32int)*img->width; + + assert(p->bytey0s <= p->bytermin && p->bytermin <= p->bytey0e); + + if(p->r.min.x == p->img->r.min.x) + assert(p->bytermin == p->bytey0s); + + nbuf = 1; + if((img->flags&Frepl) && Dy(img->r) <= MAXBCACHE && Dy(img->r) < Dy(r)){ + p->replcache = 1; + nbuf = Dy(img->r); + } + p->bufdelta = 4*p->dx; + p->bufoff = ndrawbuf; + ndrawbuf += p->bufdelta*nbuf; +} + +static void +clipy(Memimage *img, int *y) +{ + int dy; + + dy = Dy(img->r); + if(*y == dy) + *y = 0; + else if(*y == -1) + *y = dy-1; + assert(0 <= *y && *y < dy); +} + +static void +dumpbuf(char *s, Buffer b, int n) +{ + int i; + uchar *p; + + print("%s", s); + for(i=0; ir); + r = par->r; + dx = Dx(r); + dy = Dy(r); + + ndrawbuf = 0; + + src = par->src; + mask = par->mask; + dst = par->dst; + sr = par->sr; + mr = par->mr; + op = par->op; + + isgrey = dst->flags&Fgrey; + + /* + * Buffering when src and dst are the same bitmap is sufficient but not + * necessary. There are stronger conditions we could use. We could + * check to see if the rectangles intersect, and if simply moving in the + * correct y direction can avoid the need to buffer. + */ + needbuf = (src->data == dst->data); + + getparam(&spar, src, sr, isgrey, needbuf); + getparam(&dpar, dst, r, isgrey, needbuf); + getparam(&mpar, mask, mr, 0, needbuf); + + dir = (needbuf && byteaddr(dst, r.min) > byteaddr(src, sr.min)) ? -1 : 1; + spar.dir = mpar.dir = dpar.dir = dir; + + /* + * If the mask is purely boolean, we can convert from src to dst format + * when we read src, and then just copy it to dst where the mask tells us to. + * This requires a boolean (1-bit grey) mask and lack of a source alpha channel. + * + * The computation is accomplished by assigning the function pointers as follows: + * rdsrc - read and convert source into dst format in a buffer + * rdmask - convert mask to bytes, set pointer to it + * rddst - fill with pointer to real dst data, but do no reads + * calc - copy src onto dst when mask says to. + * wrdst - do nothing + * This is slightly sleazy, since things aren't doing exactly what their names say, + * but it avoids a fair amount of code duplication to make this a case here + * rather than have a separate booldraw. + */ +/*if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, mask->chan, GREY1, dst->depth); */ + if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8 && op == SoverD){ +/*if(drawdebug) iprint("boolcopy..."); */ + rdsrc = convfn(dst, &dpar, src, &spar); + rddst = readptr; + rdmask = readfn(mask); + calc = boolcopyfn(dst, mask); + wrdst = nullwrite; + }else{ + /* usual alphadraw parameter fetching */ + rdsrc = readfn(src); + rddst = readfn(dst); + wrdst = writefn(dst); + calc = alphacalc[op]; + + /* + * If there is no alpha channel, we'll ask for a grey channel + * and pretend it is the alpha. + */ + if(mask->flags&Falpha){ + rdmask = readalphafn(mask); + mpar.alphaonly = 1; + }else{ + mpar.greymaskcall = readfn(mask); + mpar.convgrey = 1; + rdmask = greymaskread; + + /* + * Should really be above, but then boolcopyfns would have + * to deal with bit alignment, and I haven't written that. + * + * This is a common case for things like ellipse drawing. + * When there's no alpha involved and the mask is boolean, + * we can avoid all the division and multiplication. + */ + if(mask->chan == GREY1 && !(src->flags&Falpha)) + calc = boolcalc[op]; + else if(op == SoverD && !(src->flags&Falpha)) + calc = alphacalcS; + } + } + + /* + * If the image has a small enough repl rectangle, + * we can just read each line once and cache them. + */ + if(spar.replcache){ + spar.replcall = rdsrc; + rdsrc = replread; + } + if(mpar.replcache){ + mpar.replcall = rdmask; + rdmask = replread; + } + + if(allocdrawbuf() < 0) + return 0; + + /* + * Before we were saving only offsets from drawbuf in the parameter + * structures; now that drawbuf has been grown to accomodate us, + * we can fill in the pointers. + */ + spar.bufbase = drawbuf+spar.bufoff; + mpar.bufbase = drawbuf+mpar.bufoff; + dpar.bufbase = drawbuf+dpar.bufoff; + spar.convbuf = drawbuf+spar.convbufoff; + + if(dir == 1){ + starty = 0; + endy = dy; + }else{ + starty = dy-1; + endy = -1; + } + + /* + * srcy, masky, and dsty are offsets from the top of their + * respective Rectangles. they need to be contained within + * the rectangles, so clipy can keep them there without division. + */ + srcy = (starty + sr.min.y - src->r.min.y)%Dy(src->r); + masky = (starty + mr.min.y - mask->r.min.y)%Dy(mask->r); + dsty = starty + r.min.y - dst->r.min.y; + + assert(0 <= srcy && srcy < Dy(src->r)); + assert(0 <= masky && masky < Dy(mask->r)); + assert(0 <= dsty && dsty < Dy(dst->r)); + + if(drawdebug) + print("alphadraw: rdsrc=%p rdmask=%p rddst=%p calc=%p wrdst=%p\n", + rdsrc, rdmask, rddst, calc, wrdst); + for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){ + clipy(src, &srcy); + clipy(dst, &dsty); + clipy(mask, &masky); + + bsrc = rdsrc(&spar, spar.bufbase, srcy); +DBG print("["); + bmask = rdmask(&mpar, mpar.bufbase, masky); +DBG print("]\n"); + bdst = rddst(&dpar, dpar.bufbase, dsty); +DBG dumpbuf("src", bsrc, dx); +DBG dumpbuf("mask", bmask, dx); +DBG dumpbuf("dst", bdst, dx); + bdst = calc(bdst, bsrc, bmask, dx, isgrey, op); +DBG dumpbuf("bdst", bdst, dx); + wrdst(&dpar, dpar.bytermin+dsty*dpar.bwidth, bdst); + } + + return 1; +} +#undef DBG + +static Buffer +alphacalc0(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op) +{ + USED(grey); + USED(op); + memset(bdst.rgba, 0, dx*bdst.delta); + return bdst; +} + +static Buffer +alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) +{ + Buffer obdst; + int fd, sadelta; + int i, sa, ma, q; + u32int s, t; + + obdst = bdst; + sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; + q = bsrc.delta == 4 && bdst.delta == 4; + + for(i=0; ibcache[y]; + if((p->bfilled & (1<bfilled |= 1<replcall(p, p->bufbase+y*p->bufdelta, y); + } + return *b; +} + +/* + * Alpha reading function that simply relabels the grey pointer. + */ +static Buffer +greymaskread(Param *p, uchar *buf, int y) +{ + Buffer b; + + b = p->greymaskcall(p, buf, y); + b.alpha = b.grey; + return b; +} + +#define DBG if(0) +static Buffer +readnbit(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + uchar *repl, *r, *w, *ow, bits; + int i, n, sh, depth, x, dx, npack, nbits; + + memset(&b, 0, sizeof b); + b.rgba = (u32int*)buf; + b.grey = w = buf; + b.red = b.blu = b.grn = w; + b.alpha = &ones; + b.delta = 1; + + dx = p->dx; + img = p->img; + depth = img->depth; + repl = &replbit[depth][0]; + npack = 8/depth; + sh = 8-depth; + + /* copy from p->r.min.x until end of repl rectangle */ + x = p->r.min.x; + n = dx; + if(n > p->img->r.max.x - x) + n = p->img->r.max.x - x; + + r = p->bytermin + y*p->bwidth; +DBG print("readnbit dx %d %p=%p+%d*%d, *r=%d fetch %d ", dx, r, p->bytermin, y, p->bwidth, *r, n); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ +DBG print("throwaway %d...", i); + bits <<= depth*i; + nbits -= depth*i; + } + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; + } + dx -= n; + if(dx == 0) + return b; + + assert(x+i == p->img->r.max.x); + + /* copy from beginning of repl rectangle until where we were before. */ + x = p->img->r.min.x; + n = dx; + if(n > p->r.min.x - x) + n = p->r.min.x - x; + + r = p->bytey0s + y*p->bwidth; +DBG print("x=%d r=%p...", x, r); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ + bits <<= depth*i; + nbits -= depth*i; + } +DBG print("nbits=%d...", nbits); + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; +DBG print("bits %x nbits %d...", bits, nbits); + } + dx -= n; + if(dx == 0) + return b; + + assert(dx > 0); + /* now we have exactly one full scan line: just replicate the buffer itself until we are done */ + ow = buf; + while(dx--) + *w++ = *ow++; + + return b; +} +#undef DBG + +#define DBG if(0) +static void +writenbit(Param *p, uchar *w, Buffer src) +{ + uchar *r; + u32int bits; + int i, sh, depth, npack, nbits, x, ex; + + assert(src.grey != nil && src.delta == 1); + + x = p->r.min.x; + ex = x+p->dx; + depth = p->img->depth; + npack = 8/depth; + + i=x&(npack-1); + bits = i ? (*w >> (8-depth*i)) : 0; + nbits = depth*i; + sh = 8-depth; + r = src.grey; + + for(; x> sh); + nbits += depth; + if(nbits == 8){ + *w++ = bits; + nbits = 0; + } + } + + if(nbits){ + sh = 8-nbits; + bits <<= sh; + bits |= *w & ((1<bytey0s + y*p->bwidth; + r = p->bytermin + y*p->bwidth; + end = p->bytey0e + y*p->bwidth; + cmap = p->img->cmap->cmap2rgb; + convgrey = p->convgrey; + copyalpha = (p->img->flags&Falpha) ? 1 : 0; + + w = buf; + dx = p->dx; + if(copyalpha){ + b.alpha = buf++; + a = p->img->shift[CAlpha]/8; + m = p->img->shift[CMap]/8; + for(i=0; iimg->cmap->rgb2cmap; + + delta = src.delta; + red= src.red; + grn = src.grn; + blu = src.blu; + + dx = p->dx; + for(i=0; i>4)*256+(*grn>>4)*16+(*blu>>4)]; +} + +#define DBG if(drawdebug) +static Buffer +readbyte(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb; + uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl; + uchar ured, ugrn, ublu; + u32int u; + + img = p->img; + begin = p->bytey0s + y*p->bwidth; + r = p->bytermin + y*p->bwidth; + end = p->bytey0e + y*p->bwidth; + + w = buf; + dx = p->dx; + nb = img->depth/8; + + convgrey = p->convgrey; /* convert rgb to grey */ + isgrey = img->flags&Fgrey; + alphaonly = p->alphaonly; + copyalpha = (img->flags&Falpha) ? 1 : 0; + + /* if we can, avoid processing everything */ + if(!(img->flags&Frepl) && !convgrey && (img->flags&Fbytes)){ + memset(&b, 0, sizeof b); + if(p->needbuf){ + memmove(buf, r, dx*nb); + r = buf; + } + b.rgba = (u32int*)r; + if(copyalpha) + b.alpha = r+img->shift[CAlpha]/8; + else + b.alpha = &ones; + if(isgrey){ + b.grey = r+img->shift[CGrey]/8; + b.red = b.grn = b.blu = b.grey; + }else{ + b.red = r+img->shift[CRed]/8; + b.grn = r+img->shift[CGreen]/8; + b.blu = r+img->shift[CBlue]/8; + } + b.delta = nb; + return b; + } + + rrepl = replbit[img->nbits[CRed]]; + grepl = replbit[img->nbits[CGreen]]; + brepl = replbit[img->nbits[CBlue]]; + arepl = replbit[img->nbits[CAlpha]]; + krepl = replbit[img->nbits[CGrey]]; + + for(i=0; i>img->shift[CAlpha]) & img->mask[CAlpha]]; + + if(isgrey) + *w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGrey]]; + else if(!alphaonly){ + ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + if(convgrey){ + *w++ = RGB2K(ured, ugrn, ublu); + }else{ + *w++ = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + *w++ = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + *w++ = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + } + } + r += nb; + if(r == end) + r = begin; + } + + b.alpha = copyalpha ? buf : &ones; + b.rgba = (u32int*)buf; + if(alphaonly){ + b.red = b.grn = b.blu = b.grey = nil; + if(!copyalpha) + b.rgba = nil; + b.delta = 1; + }else if(isgrey || convgrey){ + b.grey = buf+copyalpha; + b.red = b.grn = b.blu = buf+copyalpha; + b.delta = copyalpha+1; + }else{ + b.blu = buf+copyalpha; + b.grn = buf+copyalpha+1; + b.grey = nil; + b.red = buf+copyalpha+2; + b.delta = copyalpha+3; + } + return b; +} +#undef DBG + +#define DBG if(drawdebug) +static void +writebyte(Param *p, uchar *w, Buffer src) +{ + Memimage *img; + int i, isalpha, isgrey, nb, delta, dx, adelta; + uchar ff, *red, *grn, *blu, *grey, *alpha; + u32int u, mask; + + img = p->img; + + red = src.red; + grn = src.grn; + blu = src.blu; + alpha = src.alpha; + delta = src.delta; + grey = src.grey; + dx = p->dx; + + nb = img->depth/8; + mask = (nb==4) ? 0 : ~((1<depth)-1); + + isalpha = img->flags&Falpha; + isgrey = img->flags&Fgrey; + adelta = src.delta; + + if(isalpha && (alpha == nil || alpha == &ones)){ + ff = 0xFF; + alpha = &ff; + adelta = 0; + } + + for(i=0; i> (8-img->nbits[CGrey])) & img->mask[CGrey]) << img->shift[CGrey]; +DBG print("|grey %.8lux...", u); + grey += delta; + }else{ + u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]) << img->shift[CRed]; + u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGreen]) << img->shift[CGreen]; + u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlue]) << img->shift[CBlue]; + red += delta; + grn += delta; + blu += delta; +DBG print("|rgb %.8lux...", u); + } + + if(isalpha){ + u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[CAlpha]) << img->shift[CAlpha]; + alpha += adelta; +DBG print("|alpha %.8lux...", u); + } + + w[0] = u; + w[1] = u>>8; + w[2] = u>>16; + w[3] = u>>24; +DBG print("write back %.8lux...", u); + w += nb; + } +} +#undef DBG + +static Readfn* +readfn(Memimage *img) +{ + if(img->depth < 8) + return readnbit; + if(img->nbits[CMap] == 8) + return readcmap; + return readbyte; +} + +static Readfn* +readalphafn(Memimage *m) +{ + USED(m); + return readbyte; +} + +static Writefn* +writefn(Memimage *img) +{ + if(img->depth < 8) + return writenbit; + if(img->chan == CMAP8) + return writecmap; + return writebyte; +} + +static void +nullwrite(Param *p, uchar *s, Buffer b) +{ + USED(p); + USED(s); +} + +static Buffer +readptr(Param *p, uchar *s, int y) +{ + Buffer b; + uchar *q; + + USED(s); + memset(&b, 0, sizeof b); + q = p->bytermin + y*p->bwidth; + b.red = q; /* ptr to data */ + b.grn = b.blu = b.grey = b.alpha = nil; + b.rgba = (u32int*)q; + b.delta = p->img->depth/8; + return b; +} + +static Buffer +boolmemmove(Buffer bdst, Buffer bsrc, Buffer b1, int dx, int i, int o) +{ + USED(i); + USED(o); + memmove(bdst.red, bsrc.red, dx*bdst.delta); + return bdst; +} + +static Buffer +boolcopy8(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m, *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = bdst.red; + r = bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +boolcopy16(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + ushort *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = (ushort*)bdst.red; + r = (ushort*)bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +boolcopy24(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + uchar *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = bdst.red; + r = bsrc.red; + ew = w+dx*3; + while(w < ew){ + if(*m++){ + *w++ = *r++; + *w++ = *r++; + *w++ = *r++; + }else{ + w += 3; + r += 3; + } + } + return bdst; /* not used */ +} + +static Buffer +boolcopy32(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + u32int *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = (u32int*)bdst.red; + r = (u32int*)bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +genconv(Param *p, uchar *buf, int y) +{ + Buffer b; + int nb; + uchar *r, *w, *ew; + + /* read from source into RGB format in convbuf */ + b = p->convreadcall(p, p->convbuf, y); + + /* write RGB format into dst format in buf */ + p->convwritecall(p->convdpar, buf, b); + + if(p->convdx){ + nb = p->convdpar->img->depth/8; + r = buf; + w = buf+nb*p->dx; + ew = buf+nb*p->convdx; + while(wchan == src->chan && !(src->flags&Frepl)){ +/*if(drawdebug) iprint("readptr..."); */ + return readptr; + } + + if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan==GREY4)){ + /* cheat because we know the replicated value is exactly the color map entry. */ +/*if(drawdebug) iprint("Readnbit..."); */ + return readnbit; + } + + spar->convreadcall = readfn(src); + spar->convwritecall = writefn(dst); + spar->convdpar = dpar; + + /* allocate a conversion buffer */ + spar->convbufoff = ndrawbuf; + ndrawbuf += spar->dx*4; + + if(spar->dx > Dx(spar->img->r)){ + spar->convdx = spar->dx; + spar->dx = Dx(spar->img->r); + } + +/*if(drawdebug) iprint("genconv..."); */ + return genconv; +} + +/* + * Do NOT call this directly. pixelbits is a wrapper + * around this that fetches the bits from the X server + * when necessary. + */ +u32int +_pixelbits(Memimage *i, Point pt) +{ + uchar *p; + u32int val; + int off, bpp, npack; + + val = 0; + p = byteaddr(i, pt); + switch(bpp=i->depth){ + case 1: + case 2: + case 4: + npack = 8/bpp; + off = pt.x%npack; + val = p[0] >> bpp*(npack-1-off); + val &= (1<flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && pixelbits(mask, mask->r.min)==~0) + return boolmemmove; + + switch(img->depth){ + case 8: + return boolcopy8; + case 16: + return boolcopy16; + case 24: + return boolcopy24; + case 32: + return boolcopy32; + default: + assert(0 /* boolcopyfn */); + } + return 0; +} + +/* + * Optimized draw for filling and scrolling; uses memset and memmove. + */ +static void +memsets(void *vp, ushort val, int n) +{ + ushort *p, *ep; + + p = vp; + ep = p+n; + while(p>8; + c = val>>16; + while(pchan; chan; chan>>=8){ + nb = NBITS(chan); + ov = v = val&((1<>= nb; + + while(nb < 8){ + v |= v<>= (nb-8); + + switch(TYPE(chan)){ + case CRed: + r = v; + break; + case CGreen: + g = v; + break; + case CBlue: + b = v; + break; + case CAlpha: + a = v; + break; + case CGrey: + r = g = b = v; + break; + case CMap: + p = img->cmap->cmap2rgb+3*ov; + r = *p++; + g = *p++; + b = *p; + break; + } + } + return (r<<24)|(g<<16)|(b<<8)|a; +} + +u32int +_rgbatoimg(Memimage *img, u32int rgba) +{ + u32int chan; + int d, nb; + u32int v; + uchar *p, r, g, b, a, m; + + v = 0; + r = rgba>>24; + g = rgba>>16; + b = rgba>>8; + a = rgba; + d = 0; + for(chan=img->chan; chan; chan>>=8){ + nb = NBITS(chan); + switch(TYPE(chan)){ + case CRed: + v |= (r>>(8-nb))<>(8-nb))<>(8-nb))<>(8-nb))<cmap->rgb2cmap; + m = p[(r>>4)*256+(g>>4)*16+(b>>4)]; + v |= (m>>(8-nb))<>(8-nb))<r); + dy = Dy(par->r); + src = par->src; + dst = par->dst; + op = par->op; + +DBG print("state %lux mval %lux dd %d\n", par->state, par->mval, dst->depth); + /* + * If we have an opaque mask and source is one opaque pixel we can convert to the + * destination format and just replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if((par->state&m)==m && (par->srgba&0xFF) == 0xFF && (op ==S || op == SoverD)){ + uchar *dp, p[4]; + int d, dwid, ppb, np, nb; + uchar lm, rm; + +DBG print("memopt, dst %p, dst->data->bdata %p\n", dst, dst->data->bdata); + dwid = dst->width*sizeof(u32int); + dp = byteaddr(dst, par->r.min); + v = par->sdval; +DBG print("sdval %lud, depth %d\n", v, dst->depth); + switch(dst->depth){ + case 1: + case 2: + case 4: + for(d=dst->depth; d<8; d*=2) + v |= (v<depth; /* pixels per byte */ + m = ppb-1; + /* left edge */ + np = par->r.min.x&m; /* no. pixels unused on left side of word */ + dx -= (ppb-np); + nb = 8 - np * dst->depth; /* no. bits used on right side of word */ + lm = (1<r.min.x, nb, lm, ppb, m); + + /* right edge */ + np = par->r.max.x&m; /* no. pixels used on left side of word */ + dx -= np; + nb = 8 - np * dst->depth; /* no. bits unused on right side of word */ + rm = ~((1<r.max.x, nb, rm, ppb, m); + +DBG print("dx %d Dx %d\n", dx, Dx(par->r)); + /* lm, rm are masks that are 1 where we should touch the bits */ + if(dx < 0){ /* just one byte */ + lm &= rm; + for(y=0; y>8; + v = *(ushort*)p; +DBG print("dp=%p; dx=%d; for(y=0; y<%d; y++, dp+=%d)\nmemsets(dp, v, dx);\n", + dp, dx, dy, dwid); + for(y=0; y>8; + p[2] = v>>16; + p[3] = v>>24; + v = *(u32int*)p; + for(y=0; ystate&(m|Replsrc))==m && src->depth >= 8 + && src->chan == dst->chan && !(src->flags&Falpha) && (op == S || op == SoverD)){ + uchar *sp, *dp; + long swid, dwid, nb; + int dir; + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)) + dir = -1; + else + dir = 1; + + swid = src->width*sizeof(u32int); + dwid = dst->width*sizeof(u32int); + sp = byteaddr(src, par->sr.min); + dp = byteaddr(dst, par->r.min); + if(dir == -1){ + sp += (dy-1)*swid; + dp += (dy-1)*dwid; + swid = -swid; + dwid = -dwid; + } + nb = (dx*src->depth)/8; + for(y=0; ystate&(Simplemask|Simplesrc|Replmask|Replsrc))==0 + && dst->chan==GREY1 && src->chan==GREY1 && par->mask->chan==GREY1 + && (par->r.min.x&7)==(par->sr.min.x&7) && (par->r.min.x&7)==(par->mr.min.x&7)){ + uchar *sp, *dp, *mp; + uchar lm, rm; + long swid, dwid, mwid; + int i, x, dir; + + sp = byteaddr(src, par->sr.min); + dp = byteaddr(dst, par->r.min); + mp = byteaddr(par->mask, par->mr.min); + swid = src->width*sizeof(u32int); + dwid = dst->width*sizeof(u32int); + mwid = par->mask->width*sizeof(u32int); + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)){ + dir = -1; + }else + dir = 1; + + lm = 0xFF>>(par->r.min.x&7); + rm = 0xFF<<(8-(par->r.max.x&7)); + dx -= (8-(par->r.min.x&7)) + (par->r.max.x&7); + + if(dx < 0){ /* one byte wide */ + lm &= rm; + if(dir == -1){ + dp += dwid*(dy-1); + sp += swid*(dy-1); + mp += mwid*(dy-1); + dwid = -dwid; + swid = -swid; + mwid = -mwid; + } + for(y=0; ymask->flags, par->mask->depth, par->src->flags, + Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->data, par->src->data); + + mask = par->mask; + src = par->src; + dst = par->dst; + r = par->r; + mr = par->mr; + op = par->op; + + if((par->state&(Replsrc|Simplesrc|Fullsrc|Replmask)) != (Replsrc|Simplesrc|Fullsrc) + || mask->depth != 1 || dst->depth<8 || dst->data==src->data + || op != SoverD) + return 0; + +/*if(drawdebug) iprint("chardraw..."); */ + + depth = mask->depth; + maskwid = mask->width*sizeof(u32int); + rp = byteaddr(mask, mr.min); + npack = 8/depth; + bsh = (mr.min.x % npack) * depth; + + wp = byteaddr(dst, r.min); + dstwid = dst->width*sizeof(u32int); +DBG print("bsh %d\n", bsh); + dy = Dy(r); + dx = Dx(r); + + ddepth = dst->depth; + + /* + * for loop counts from bsh to bsh+dx + * + * we want the bottom bits to be the amount + * to shift the pixels down, so for n≡0 (mod 8) we want + * bottom bits 7. for n≡1, 6, etc. + * the bits come from -n-1. + */ + + bx = -bsh-1; + ex = -bsh-1-dx; + SET(bits); + v = par->sdval; + + /* make little endian */ + sp[0] = v; + sp[1] = v>>8; + sp[2] = v>>16; + sp[3] = v>>24; + +/*print("sp %x %x %x %x\n", sp[0], sp[1], sp[2], sp[3]); */ + for(y=0; yex; x--, wc++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *wc = v; + } + break; + case 16: + ws = (ushort*)wp; + v = *(ushort*)sp; + for(x=bx; x>ex; x--, ws++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *ws = v; + } + break; + case 24: + wc = wp; + for(x=bx; x>ex; x--, wc+=3){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1){ + wc[0] = sp[0]; + wc[1] = sp[1]; + wc[2] = sp[2]; + } + } + break; + case 32: + wl = (u32int*)wp; + v = *(u32int*)sp; + for(x=bx; x>ex; x--, wl++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG iprint("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *wl = v; + } + break; + } + } + +DBG print("\n"); + return 1; +} +#undef DBG + + +/* + * Fill entire byte with replicated (if necessary) copy of source pixel, + * assuming destination ldepth is >= source ldepth. + * + * This code is just plain wrong for >8bpp. + * +u32int +membyteval(Memimage *src) +{ + int i, val, bpp; + uchar uc; + + unloadmemimage(src, src->r, &uc, 1); + bpp = src->depth; + uc <<= (src->r.min.x&(7/src->depth))*src->depth; + uc &= ~(0xFF>>bpp); + * pixel value is now in high part of byte. repeat throughout byte + val = uc; + for(i=bpp; i<8; i<<=1) + val |= val>>i; + return val; +} + * + */ + +void +_memfillcolor(Memimage *i, u32int val) +{ + u32int bits; + int d, y; + uchar p[4]; + + if(val == DNofill) + return; + + bits = _rgbatoimg(i, val); + switch(i->depth){ + case 24: /* 24-bit images suck */ + for(y=i->r.min.y; yr.max.y; y++) + memset24(byteaddr(i, Pt(i->r.min.x, y)), bits, Dx(i->r)); + break; + default: /* 1, 2, 4, 8, 16, 32 */ + for(d=i->depth; d<32; d*=2) + bits = (bits << d) | bits; + p[0] = bits; /* make little endian */ + p[1] = bits>>8; + p[2] = bits>>16; + p[3] = bits>>24; + bits = *(u32int*)p; + memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r)); + break; + } +} + blob - /dev/null blob + 9d991782589c4659265a782c13fc3bd707e800f4 (mode 644) --- /dev/null +++ src/libmemdraw/drawtest.c @@ -0,0 +1,1004 @@ +#include +#include +#include +#include +#include + +#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 = doprint(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"); + } + + fmtinstall('b', numbconv); /* binary! */ + + 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<data->bdata); + Bprint(&b, "%-4d\t", y); +/* for(x=r.min.x; x>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> 7; + if(i+i != nbytes) + *(uchar*)up = lrand() >> 7; + }else{ + data = img->data->bdata; + img->data->bdata = ucbits; + + for(x=img->r.min.x; xr.max.x; x++) + for(y=img->r.min.y; yr.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; idata->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; xdata->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; idata->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; ydata->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; yr, sp), drawrepl(mask->r, mp), y, nil, nil); +} + +void +verifyrect(void) +{ + int i; + + /* mask all zeros */ + memset(maskbits, 0, nbytes); + for(i=0; ir) */ + 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(; xflags |= 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; ydata->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; yr, 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>= (nhave-nwant); + return v & ((1<>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<chan; c; c>>=8){ + nbits = NBITS(c); + bits = v & ((1<>= 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<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<>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)/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)/255; + dr = dg = db = dk; + da = (sa*m + da*M)/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)/255; + dg = (sg*m + dg*M)/255; + db = (sb*m + db*M)/255; + da = (sa*m + da*M)/255; + } + +DBG print("%x %x %x %x\n", dr,dg,db,da); + putpixel(dst, dp, rgbatopix(dr, dg, db, da)); +} blob - /dev/null blob + 7dc45cd6fee3dab9ea799c8b0dcdb62422018e20 (mode 644) --- /dev/null +++ src/libmemdraw/ellipse.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include + +/* + * ellipse(dst, c, a, b, t, src, sp) + * draws an ellipse centered at c with semiaxes a,b>=0 + * and semithickness t>=0, or filled if t<0. point sp + * in src maps to c in dst + * + * very thick skinny ellipses are brushed with circles (slow) + * others are approximated by filling between 2 ellipses + * criterion for very thick when b 0.5*x/(1-x) + * where x = b/a + */ + +typedef struct Param Param; +typedef struct State State; + +static void bellipse(int, State*, Param*); +static void erect(int, int, int, int, Param*); +static void eline(int, int, int, int, Param*); + +struct Param { + Memimage *dst; + Memimage *src; + Point c; + int t; + Point sp; + Memimage *disc; + int op; +}; + +/* + * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 + * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside + */ + +struct State { + int a; + int x; + vlong a2; /* a^2 */ + vlong b2; /* b^2 */ + vlong b2x; /* b^2 * x */ + vlong a2y; /* a^2 * y */ + vlong c1; + vlong c2; /* test criteria */ + vlong ee; /* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */ + vlong dxe; + vlong dye; + vlong d2xe; + vlong d2ye; +}; + +static +State* +newstate(State *s, int a, int b) +{ + s->x = 0; + s->a = a; + s->a2 = (vlong)(a*a); + s->b2 = (vlong)(b*b); + s->b2x = (vlong)0; + s->a2y = s->a2*(vlong)b; + s->c1 = -((s->a2>>2) + (vlong)(a&1) + s->b2); + s->c2 = -((s->b2>>2) + (vlong)(b&1)); + s->ee = -s->a2y; + s->dxe = (vlong)0; + s->dye = s->ee<<1; + s->d2xe = s->b2<<1; + s->d2ye = s->a2<<1; + return s; +} + +/* + * return x coord of rightmost pixel on next scan line + */ +static +int +step(State *s) +{ + while(s->x < s->a) { + if(s->ee+s->b2x <= s->c1 || /* e(x+1,y-1/2) <= 0 */ + s->ee+s->a2y <= s->c2) { /* e(x+1/2,y) <= 0 (rare) */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + s->x++; + continue; + } + s->dye += s->d2ye; + s->ee += s->dye; + s->a2y -= s->a2; + if(s->ee-s->a2y <= s->c2) { /* e(x+1/2,y-1) <= 0 */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + return s->x++; + } + break; + } + return s->x; +} + +void +memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int op) +{ + State in, out; + int y, inb, inx, outx, u; + Param p; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + p.dst = dst; + p.src = src; + p.c = c; + p.t = t; + p.sp = subpt(sp, c); + p.disc = nil; + p.op = op; + + u = (t<<1)*(a-b); + if(bb*b || aa*a) { +/* if(bb*b/a || aa*a/b) # very thick */ + bellipse(b, newstate(&in, a, b), &p); + return; + } + + if(t < 0) { + inb = -1; + newstate(&out, a, y = b); + } else { + inb = b - t; + newstate(&out, a+t, y = b+t); + } + if(t > 0) + newstate(&in, a-t, inb); + inx = 0; + for( ; y>=0; y--) { + outx = step(&out); + if(y > inb) { + erect(-outx, y, outx, y, &p); + if(y != 0) + erect(-outx, -y, outx, -y, &p); + continue; + } + if(t > 0) { + inx = step(&in); + if(y == inb) + inx = 0; + } else if(inx > outx) + inx = outx; + erect(inx, y, outx, y, &p); + if(y != 0) + erect(inx, -y, outx, -y, &p); + erect(-outx, y, -inx, y, &p); + if(y != 0) + erect(-outx, -y, -inx, -y, &p); + inx = outx + 1; + } +} + +static Point p00 = {0, 0}; + +/* + * a brushed ellipse + */ +static +void +bellipse(int y, State *s, Param *p) +{ + int t, ox, oy, x, nx; + + t = p->t; + p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1); + if(p->disc == nil) + return; + memfillcolor(p->disc, DTransparent); + memellipse(p->disc, p00, t, t, -1, memopaque, p00, p->op); + oy = y; + ox = 0; + nx = x = step(s); + do { + while(nx==x && y-->0) + nx = step(s); + y++; + eline(-x,-oy,-ox, -y, p); + eline(ox,-oy, x, -y, p); + eline(-x, y,-ox, oy, p); + eline(ox, y, x, oy, p); + ox = x+1; + x = nx; + y--; + oy = y; + } while(oy > 0); +} + +/* + * a rectangle with closed (not half-open) coordinates expressed + * relative to the center of the ellipse + */ +static +void +erect(int x0, int y0, int x1, int y1, Param *p) +{ + Rectangle r; + +/* print("R %d,%d %d,%d\n", x0, y0, x1, y1); */ + r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00, p->op); +} + +/* + * a brushed point similarly specified + */ +static +void +epoint(int x, int y, Param *p) +{ + Point p0; + Rectangle r; + +/* print("P%d %d,%d\n", p->t, x, y); */ + p0 = Pt(p->c.x+x, p->c.y+y); + r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max)); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min, p->op); +} + +/* + * a brushed horizontal or vertical line similarly specified + */ +static +void +eline(int x0, int y0, int x1, int y1, Param *p) +{ +/* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); */ + if(x1 > x0+1) + erect(x0+1, y0-p->t, x1-1, y1+p->t, p); + else if(y1 > y0+1) + erect(x0-p->t, y0+1, x1+p->t, y1-1, p); + epoint(x0, y0, p); + if(x1-x0 || y1-y0) + epoint(x1, y1, p); +} blob - /dev/null blob + fcacae51afdb6b43a5d33fdc4584964bfec86471 (mode 644) --- /dev/null +++ src/libmemdraw/fillpoly.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include + +typedef struct Seg Seg; + +struct Seg +{ + Point p0; + Point p1; + long num; + long den; + long dz; + long dzrem; + long z; + long zerr; + long d; +}; + +static void zsort(Seg **seg, Seg **ep); +static int ycompare(const void*, const void*); +static int xcompare(const void*, const void*); +static int zcompare(const void*, const void*); +static void xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int, int, int); +static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int); + +#if 0 +static void +fillcolor(Memimage *dst, int left, int right, int y, Memimage *src, Point p) +{ + int srcval; + + USED(src); + srcval = p.x; + p.x = left; + p.y = y; + memset(byteaddr(dst, p), srcval, right-left); +} +#endif + +static void +fillline(Memimage *dst, int left, int right, int y, Memimage *src, Point p, int op) +{ + Rectangle r; + + r.min.x = left; + r.min.y = y; + r.max.x = right; + r.max.y = y+1; + p.x += left; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +static void +fillpoint(Memimage *dst, int x, int y, Memimage *src, Point p, int op) +{ + Rectangle r; + + r.min.x = x; + r.min.y = y; + r.max.x = x+1; + r.max.y = y+1; + p.x += x; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +void +memfillpoly(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int op) +{ + _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op); +} + +void +_memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) +{ + Seg **seg, *segtab; + Point p0; + int i; + + if(nvert == 0) + return; + + seg = malloc((nvert+2)*sizeof(Seg*)); + if(seg == nil) + return; + segtab = malloc((nvert+1)*sizeof(Seg)); + if(segtab == nil) { + free(seg); + return; + } + + sp.x = (sp.x - vert[0].x) >> fixshift; + sp.y = (sp.y - vert[0].y) >> fixshift; + p0 = vert[nvert-1]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + for(i = 0; i < nvert; i++) { + segtab[i].p0 = p0; + p0 = vert[i]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + segtab[i].p1 = p0; + segtab[i].d = 1; + } + if(!fixshift) + fixshift = 1; + + xscan(dst, seg, segtab, nvert, w, src, sp, detail, fixshift, clipped, op); + if(detail) + yscan(dst, seg, segtab, nvert, w, src, sp, fixshift, op); + + free(seg); + free(segtab); +} + +static long +mod(long x, long y) +{ + long z; + + z = x%y; + if((long)(((ulong)z)^((ulong)y)) > 0 || z == 0) + return z; + return z + y; +} + +static long +sdiv(long x, long y) +{ + if((long)(((ulong)x)^((ulong)y)) >= 0 || x == 0) + return x/y; + + return (x+((y>>30)|1))/y-1; +} + +static long +smuldivmod(long x, long y, long z, long *mod) +{ + vlong vx; + + if(x == 0 || y == 0){ + *mod = 0; + return 0; + } + vx = x; + vx *= y; + *mod = vx % z; + if(*mod < 0) + *mod += z; /* z is always >0 */ + if((vx < 0) == (z < 0)) + return vx/z; + return -((-vx)/z); +} + +static void +xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) +{ + long y, maxy, x, x2, xerr, xden, onehalf; + Seg **ep, **next, **p, **q, *s; + long n, i, iy, cnt, ix, ix2, minx, maxx; + Point pt; + void (*fill)(Memimage*, int, int, int, Memimage*, Point, int); + + fill = fillline; +/* + * This can only work on 8-bit destinations, since fillcolor is + * just using memset on sp.x. + * + * I'd rather not even enable it then, since then if the general + * code is too slow, someone will come up with a better improvement + * than this sleazy hack. -rsc + * + if(clipped && (src->flags&Frepl) && src->depth==8 && Dx(src->r)==1 && Dy(src->r)==1) { + fill = fillcolor; + sp.x = membyteval(src); + } + * + */ + USED(clipped); + + + for(i=0, s=segtab, p=seg; ip0.y == s->p1.y) + continue; + if(s->p0.y > s->p1.y) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.x - s->p0.x; + s->den = s->p1.y - s->p0.y; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, p-seg , sizeof(Seg*), ycompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + minx = dst->clipr.min.x; + maxx = dst->clipr.max.x; + + y = seg[0]->p0.y; + if(y < (dst->clipr.min.y << fixshift)) + y = dst->clipr.min.y << fixshift; + iy = (y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + maxy = dst->clipr.max.y << fixshift; + + ep = next = seg; + + while(yp1.y < y) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld dzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.y >= y) + break; + if(s->p1.y < y) + continue; + s->z = s->p0.x; + s->z += smuldivmod(y - s->p0.y, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + iy = (next[0]->p0.y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + x = p[0]->z; + xerr = p[0]->zerr; + xden = p[0]->den; + ix = (x + onehalf) >> fixshift; + if(ix >= maxx) + break; + if(ix < minx) + ix = minx; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("xscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + x2 = p[0]->z; + ix2 = (x2 + onehalf) >> fixshift; + if(ix2 <= minx) + continue; + if(ix2 > maxx) + ix2 = maxx; + if(ix == ix2 && detail) { + if(xerr*p[0]->den + p[0]->zerr*xden > p[0]->den*xden) + x++; + ix = (x + x2) >> (fixshift+1); + ix2 = ix+1; + } + (*fill)(dst, ix, ix2, iy, src, sp, op); + } + y += (1<p0.x == s->p1.x) + continue; + if(s->p0.x > s->p1.x) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.y - s->p0.y; + s->den = s->p1.x - s->p0.x; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, n , sizeof(Seg*), xcompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + miny = dst->clipr.min.y; + maxy = dst->clipr.max.y; + + x = seg[0]->p0.x; + if(x < (dst->clipr.min.x << fixshift)) + x = dst->clipr.min.x << fixshift; + ix = (x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + maxx = dst->clipr.max.x << fixshift; + + ep = next = seg; + + while(xp1.x < x) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.x >= x) + break; + if(s->p1.x < x) + continue; + s->z = s->p0.y; + s->z += smuldivmod(x - s->p0.x, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + ix = (next[0]->p0.x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + y = p[0]->z; + yerr = p[0]->zerr; + yden = p[0]->den; + iy = (y + onehalf) >> fixshift; + if(iy >= maxy) + break; + if(iy < miny) + iy = miny; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("yscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + y2 = p[0]->z; + iy2 = (y2 + onehalf) >> fixshift; + if(iy2 <= miny) + continue; + if(iy2 > maxy) + iy2 = maxy; + if(iy == iy2) { + if(yerr*p[0]->den + p[0]->zerr*yden > p[0]->den*yden) + y++; + iy = (y + y2) >> (fixshift+1); + fillpoint(dst, ix, iy, src, sp, op); + } + } + x += (1<z > p[1]->z) { + s = p[0]; + p[0] = p[1]; + p[1] = s; + done = 0; + } + } + } while(!done); + } else { + q = ep-1; + for(p = seg; p < q; p++) { + if(p[0]->z > p[1]->z) { + qsort(seg, ep-seg, sizeof(Seg*), zcompare); + break; + } + } + } +} + +static int +ycompare(const void *a, const void *b) +{ + Seg **s0, **s1; + long y0, y1; + + s0 = (Seg**)a; + s1 = (Seg**)b; + y0 = (*s0)->p0.y; + y1 = (*s1)->p0.y; + + if(y0 < y1) + return -1; + if(y0 == y1) + return 0; + return 1; +} + +static int +xcompare(const void *a, const void *b) +{ + Seg **s0, **s1; + long x0, x1; + + s0 = (Seg**)a; + s1 = (Seg**)b; + x0 = (*s0)->p0.x; + x1 = (*s1)->p0.x; + + if(x0 < x1) + return -1; + if(x0 == x1) + return 0; + return 1; +} + +static int +zcompare(const void *a, const void *b) +{ + Seg **s0, **s1; + long z0, z1; + + s0 = (Seg**)a; + s1 = (Seg**)b; + z0 = (*s0)->z; + z1 = (*s1)->z; + + if(z0 < z1) + return -1; + if(z0 == z1) + return 0; + return 1; +} blob - /dev/null blob + 3f36250f22ec43c8bf6cb3d064bc2cc8e01333e5 (mode 644) --- /dev/null +++ src/libmemdraw/hwdraw.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +hwdraw(Memdrawparam *p) +{ + USED(p); + return 0; /* could not satisfy request */ +} + blob - /dev/null blob + 923b6b448c8e075b6c258e5777eb21a24de21cdd (mode 644) --- /dev/null +++ src/libmemdraw/iprint.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +iprint(char *fmt,...) +{ + USED(fmt); + return -1; +} + blob - /dev/null blob + c7042b9fc4f1ac4f9d1c6ffb105213f4b59a42a3 (mode 644) --- /dev/null +++ src/libmemdraw/line.c @@ -0,0 +1,486 @@ +#include +#include +#include +#include + +enum +{ + Arrow1 = 8, + Arrow2 = 10, + Arrow3 = 3 +}; + +/* +static +int +lmin(int a, int b) +{ + if(a < b) + return a; + return b; +} +*/ + +static +int +lmax(int a, int b) +{ + if(a > b) + return a; + return b; +} + +#ifdef NOTUSED +/* + * Rather than line clip, we run the Bresenham loop over the full line, + * and clip on each pixel. This is more expensive but means that + * lines look the same regardless of how the windowing has tiled them. + * For speed, we check for clipping outside the loop and make the + * test easy when possible. + */ + +static +void +horline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, dy, deltay, deltax, maxx; + int dd, easy, e, bpp, m, m0; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = dst->width*sizeof(u32int); + dy = 1; + if(deltay < 0){ + dd = -dd; + deltay = -deltay; + dy = -1; + } + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (p0.x&(7/dst->depth))*bpp; + easy = ptinrect(p0, clipr) && ptinrect(p1, clipr); + e = 2*deltay - deltax; + y = p0.y; + d = byteaddr(dst, p0); + deltay *= 2; + deltax = deltay - 2*deltax; + for(x=p0.x; x<=maxx; x++){ + if(easy || (clipr.min.x<=x && clipr.min.y<=y && y 0){ + y += dy; + d += dd; + e += deltax; + }else + e += deltay; + d++; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, deltay, deltax, maxy; + int easy, e, bpp, m, m0, dd; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = 1; + if(deltax < 0){ + dd = -1; + deltax = -deltax; + } + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (p0.x&(7/dst->depth))*bpp; + easy = ptinrect(p0, clipr) && ptinrect(p1, clipr); + e = 2*deltax - deltay; + x = p0.x; + d = byteaddr(dst, p0); + deltax *= 2; + deltay = deltax - 2*deltay; + for(y=p0.y; y<=maxy; y++){ + if(easy || (clipr.min.y<=y && clipr.min.x<=x && x 0){ + x += dd; + d += dd; + e += deltay; + }else + e += deltax; + d += dst->width*sizeof(u32int); + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +horliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sx = drawreplxy(src->r.min.x, src->r.max.x, p0.x+dsrc.x); + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltax/2)/deltax; + if(clipr.min.y<=y && yr.min.y, src->r.max.y, y+dsrc.y); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sx >= src->r.max.x) + sx = src->r.min.x; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sy = drawreplxy(src->r.min.y, src->r.max.y, p0.y+dsrc.y); + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + (deltax*(y-p0.y)+deltay/2)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + sx = drawreplxy(src->r.min.x, src->r.max.x, x+dsrc.x); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sy >= src->r.max.y) + sy = src->r.min.y; + } +} + +static +void +horline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltay/2)/deltax; + if(clipr.min.y<=y && y>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + deltax*(y-p0.y)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + s = byteaddr(src, addpt(dsrc, Pt(x, y))); + *d ^= (*d^*s) & m; + } + } +} +#endif /* NOTUSED */ + +static Memimage* +membrush(int radius) +{ + static Memimage *brush; + static int brushradius; + + if(brush==nil || brushradius!=radius){ + freememimage(brush); + brush = allocmemimage(Rect(0, 0, 2*radius+1, 2*radius+1), memopaque->chan); + if(brush != nil){ + memfillcolor(brush, DTransparent); /* zeros */ + memellipse(brush, Pt(radius, radius), radius, radius, -1, memopaque, Pt(radius, radius), S); + } + brushradius = radius; + } + return brush; +} + +static +void +discend(Point p, int radius, Memimage *dst, Memimage *src, Point dsrc, int op) +{ + Memimage *disc; + Rectangle r; + + disc = membrush(radius); + if(disc != nil){ + r.min.x = p.x - radius; + r.min.y = p.y - radius; + r.max.x = p.x + radius+1; + r.max.y = p.y + radius+1; + memdraw(dst, r, src, addpt(r.min, dsrc), disc, Pt(0,0), op); + } +} + +static +void +arrowend(Point tip, Point *pp, int end, int sin, int cos, int radius) +{ + int x1, x2, x3; + + /* before rotation */ + if(end == Endarrow){ + x1 = Arrow1; + x2 = Arrow2; + x3 = Arrow3; + }else{ + x1 = (end>>5) & 0x1FF; /* distance along line from end of line to tip */ + x2 = (end>>14) & 0x1FF; /* distance along line from barb to tip */ + x3 = (end>>23) & 0x1FF; /* distance perpendicular from edge of line to barb */ + } + + /* comments follow track of right-facing arrowhead */ + pp->x = tip.x+((2*radius+1)*sin/2-x1*cos); /* upper side of shaft */ + pp->y = tip.y-((2*radius+1)*cos/2+x1*sin); + pp++; + pp->x = tip.x+((2*radius+2*x3+1)*sin/2-x2*cos); /* upper barb */ + pp->y = tip.y-((2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x; + pp->y = tip.y; + pp++; + pp->x = tip.x+(-(2*radius+2*x3+1)*sin/2-x2*cos); /* lower barb */ + pp->y = tip.y-(-(2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x+(-(2*radius+1)*sin/2-x1*cos); /* lower side of shaft */ + pp->y = tip.y+((2*radius+1)*cos/2-x1*sin); +} + +void +_memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + /* + * BUG: We should really really pick off purely horizontal and purely + * vertical lines and handle them separately with calls to memimagedraw + * on rectangles. + */ + + int hor; + int sin, cos, dx, dy, t; + Rectangle oclipr, r; + Point q, pts[10], *pp, d; + + if(radius < 0) + return; + if(rectclip(&clipr, dst->r) == 0) + return; + if(rectclip(&clipr, dst->clipr) == 0) + return; + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + /* this means that only verline() handles degenerate lines (p0==p1) */ + hor = (abs(p1.x-p0.x) > abs(p1.y-p0.y)); + /* + * Clipping is a little peculiar. We can't use Sutherland-Cohen + * clipping because lines are wide. But this is probably just fine: + * we do all math with the original p0 and p1, but clip when deciding + * what pixels to draw. This means the layer code can call this routine, + * using clipr to define the region being written, and get the same set + * of pixels regardless of the dicing. + */ + if((hor && p0.x>p1.x) || (!hor && p0.y>p1.y)){ + q = p0; + p0 = p1; + p1 = q; + t = end0; + end0 = end1; + end1 = t; + } + + if((p0.x == p1.x || p0.y == p1.y) && (end0&0x1F) == Endsquare && (end1&0x1F) == Endsquare){ + r.min = p0; + r.max = p1; + if(p0.x == p1.x){ + r.min.x -= radius; + r.max.x += radius+1; + } + else{ + r.min.y -= radius; + r.max.y += radius+1; + } + oclipr = dst->clipr; + dst->clipr = clipr; + memimagedraw(dst, r, src, sp, memopaque, sp, op); + dst->clipr = oclipr; + return; + } + +/* Hard: */ + /* draw thick line using polygon fill */ + icossin2(p1.x-p0.x, p1.y-p0.y, &cos, &sin); + dx = (sin*(2*radius+1))/2; + dy = (cos*(2*radius+1))/2; + pp = pts; + oclipr = dst->clipr; + dst->clipr = clipr; + q.x = ICOSSCALE*p0.x+ICOSSCALE/2-cos/2; + q.y = ICOSSCALE*p0.y+ICOSSCALE/2-sin/2; + switch(end0 & 0x1F){ + case Enddisc: + discend(p0, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end0, -sin, -cos, radius); + _memfillpolysc(dst, pts, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + q.x = ICOSSCALE*p1.x+ICOSSCALE/2+cos/2; + q.y = ICOSSCALE*p1.y+ICOSSCALE/2+sin/2; + switch(end1 & 0x1F){ + case Enddisc: + discend(p1, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end1, sin, cos, radius); + _memfillpolysc(dst, pp, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + _memfillpolysc(dst, pts, pp-pts, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 0, 10, 1, op); + dst->clipr = oclipr; + return; +} + +void +memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} + +/* + * Simple-minded conservative code to compute bounding box of line. + * Result is probably a little larger than it needs to be. + */ +static +void +addbbox(Rectangle *r, Point p) +{ + if(r->min.x > p.x) + r->min.x = p.x; + if(r->min.y > p.y) + r->min.y = p.y; + if(r->max.x < p.x+1) + r->max.x = p.x+1; + if(r->max.y < p.y+1) + r->max.y = p.y+1; +} + +int +memlineendsize(int end) +{ + int x3; + + if((end&0x3F) != Endarrow) + return 0; + if(end == Endarrow) + x3 = Arrow3; + else + x3 = (end>>23) & 0x1FF; + return x3; +} + +Rectangle +memlinebbox(Point p0, Point p1, int end0, int end1, int radius) +{ + Rectangle r, r1; + int extra; + + r.min.x = 10000000; + r.min.y = 10000000; + r.max.x = -10000000; + r.max.y = -10000000; + extra = lmax(memlineendsize(end0), memlineendsize(end1)); + r1 = insetrect(canonrect(Rpt(p0, p1)), -(radius+extra)); + addbbox(&r, r1.min); + addbbox(&r, r1.max); + return r; +} blob - /dev/null blob + 6788fa90b3a78b55526c106beac8620ff9269e7b (mode 644) --- /dev/null +++ src/libmemdraw/load.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +int +_loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l, lpart, rpart, mx, m, mr; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + mx = 7/i->depth; + lpart = (r.min.x & mx) * i->depth; + rpart = (r.max.x & mx) * i->depth; + m = 0xFF >> lpart; + /* may need to do bit insertion on edges */ + if(l == 1){ /* all in one byte */ + if(rpart) + m ^= 0xFF >> rpart; + for(y=r.min.y; ywidth*sizeof(u32int); + data++; + } + return ndata; + } + if(lpart==0 && rpart==0){ /* easy case */ + for(y=r.min.y; ywidth*sizeof(u32int); + data += l; + } + return ndata; + } + mr = 0xFF ^ (0xFF >> rpart); + if(lpart!=0 && rpart==0){ + for(y=r.min.y; y 1) + memmove(q+1, data+1, l-1); + q += i->width*sizeof(u32int); + data += l; + } + return ndata; + } + if(lpart==0 && rpart!=0){ + for(y=r.min.y; y 1) + memmove(q, data, l-1); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(u32int); + data += l; + } + return ndata; + } + for(y=r.min.y; y 2) + memmove(q+1, data+1, l-2); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(u32int); + data += l; + } + return ndata; +} blob - /dev/null blob + e8d5efc394486da4f81a362839441ed54c1f5030 (mode 644) --- /dev/null +++ src/libmemdraw/mkcmap.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +/* +struct Memcmap +{ + uchar cmap2rgb[3*256]; + uchar rgb2cmap[16*16*16]; +}; +*/ + +static Memcmap* +mkcmap(void) +{ + static Memcmap def; + + int i, rgb, r, g, b; + + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + r = (rgb>>16)&0xff; + g = (rgb>>8)&0xff; + b = rgb&0xff; + def.cmap2rgb[3*i] = r; + def.cmap2rgb[3*i+1] = g; + def.cmap2rgb[3*i+2] = b; + } + + for(r=0; r<16; r++) + for(g=0; g<16; g++) + for(b=0; b<16; b++) + def.rgb2cmap[r*16*16+g*16+b] = rgb2cmap(r*0x11, g*0x11, b*0x11); + return &def; +} + +void +main(int argc, char **argv) +{ + Memcmap *c; + int i, j, inferno; + + inferno = 0; + ARGBEGIN{ + case 'i': + inferno = 1; + }ARGEND + + memimageinit(); + c = mkcmap(); + if(!inferno) + print("#include \n#include \n"); + else + print("#include \"lib9.h\"\n"); + print("#include \n"); + print("#include \n\n"); + print("static Memcmap def = {\n"); + print("/* cmap2rgb */ {\n"); + for(i=0; icmap2rgb); ){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->cmap2rgb[i]); + print("\n"); + } + print("},\n"); + print("/* rgb2cmap */ {\n"); + for(i=0; irgb2cmap);){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->rgb2cmap[i]); + print("\n"); + } + print("}\n"); + print("};\n"); + print("Memcmap *memdefcmap = &def;\n"); + print("void _memmkcmap(void){}\n"); + exits(0); +} blob - /dev/null blob + 4c99bd60ce0736834a450bfa7aeaa91e9745b340 (mode 644) --- /dev/null +++ src/libmemdraw/mkfile @@ -0,0 +1,32 @@ +<$PLAN9/src/mkhdr + +LIB=libmemdraw.a + +OFILES=\ + alloc.$O\ + arc.$O\ + cload.$O\ + cmap.$O\ + cread.$O\ + defont.$O\ + draw.$O\ + ellipse.$O\ + fillpoly.$O\ + hwdraw.$O\ + iprint.$O\ + line.$O\ + load.$O\ + openmemsubfont.$O\ + poly.$O\ + read.$O\ + string.$O\ + subfont.$O\ + unload.$O\ + write.$O\ + +HFILES=\ + $PLAN9/include/draw.h\ + $PLAN9/include/memdraw.h\ + +<$PLAN9/src/mksyslib + blob - /dev/null blob + c8d926e4a69378605dcfb59f83ebbc8e8c24f700 (mode 644) --- /dev/null +++ src/libmemdraw/openmemsubfont.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +Memsubfont* +openmemsubfont(char *name) +{ + Memsubfont *sf; + Memimage *i; + Fontchar *fc; + int fd, n; + char hdr[3*12+4+1]; + uchar *p; + + fd = open(name, OREAD); + if(fd < 0) + return nil; + p = nil; + i = readmemimage(fd); + if(i == nil) + goto Err; + if(read(fd, hdr, 3*12) != 3*12){ + werrstr("openmemsubfont: header read error: %r"); + goto Err; + } + n = atoi(hdr); + p = malloc(6*(n+1)); + if(p == nil) + goto Err; + if(read(fd, p, 6*(n+1)) != 6*(n+1)){ + werrstr("openmemsubfont: fontchar read error: %r"); + goto Err; + } + fc = malloc(sizeof(Fontchar)*(n+1)); + if(fc == nil) + goto Err; + _unpackinfo(fc, p, n); + sf = allocmemsubfont(name, n, atoi(hdr+12), atoi(hdr+24), fc, i); + if(sf == nil){ + free(fc); + goto Err; + } + free(p); + return sf; +Err: + close(fd); + if (i != nil) + freememimage(i); + if (p != nil) + free(p); + return nil; +} blob - /dev/null blob + d16c0a92a7bb0407aa5be4a7553b84a8a82ae966 (mode 644) --- /dev/null +++ src/libmemdraw/poly.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +void +mempoly(Memimage *dst, Point *vert, int nvert, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + int i, e0, e1; + Point d; + + if(nvert < 2) + return; + d = subpt(sp, vert[0]); + for(i=1; i +#include +#include +#include + +Memimage* +readmemimage(int fd) +{ + char hdr[5*12+1]; + int dy; + u32int chan; + uint l, n; + int m, j; + int new, miny, maxy; + Rectangle r; + uchar *tmp; + int ldepth, chunk; + Memimage *i; + + if(readn(fd, hdr, 11) != 11){ + werrstr("readimage: short header"); + return nil; + } + if(memcmp(hdr, "compressed\n", 11) == 0) + return creadmemimage(fd); + if(readn(fd, hdr+11, 5*12-11) != 5*12-11){ + werrstr("readimage: short header (2)"); + return nil; + } + + /* + * distinguish new channel descriptor from old ldepth. + * channel descriptors have letters as well as numbers, + * while ldepths are a single digit formatted as %-11d. + */ + new = 0; + for(m=0; m<10; m++){ + if(hdr[m] != ' '){ + new = 1; + break; + } + } + if(hdr[11] != ' '){ + werrstr("readimage: bad format"); + return nil; + } + if(new){ + hdr[11] = '\0'; + if((chan = strtochan(hdr)) == 0){ + werrstr("readimage: bad channel string %s", hdr); + return nil; + } + }else{ + ldepth = ((int)hdr[10])-'0'; + if(ldepth<0 || ldepth>3){ + werrstr("readimage: bad ldepth %d", ldepth); + return nil; + } + chan = drawld2chan[ldepth]; + } + + r.min.x = atoi(hdr+1*12); + r.min.y = atoi(hdr+2*12); + r.max.x = atoi(hdr+3*12); + r.max.y = atoi(hdr+4*12); + if(r.min.x>r.max.x || r.min.y>r.max.y){ + werrstr("readimage: bad rectangle"); + return nil; + } + + miny = r.min.y; + maxy = r.max.y; + + l = bytesperline(r, chantodepth(chan)); + i = allocmemimage(r, chan); + if(i == nil) + return nil; + chunk = 32*1024; + if(chunk < l) + chunk = l; + tmp = malloc(chunk); + if(tmp == nil) + goto Err; + while(maxy > miny){ + dy = maxy - miny; + if(dy*l > chunk) + dy = chunk/l; + if(dy <= 0){ + werrstr("readmemimage: image too wide for buffer"); + goto Err; + } + n = dy*l; + m = readn(fd, tmp, n); + if(m != n){ + werrstr("readmemimage: read count %d not %d: %r", m, n); + Err: + freememimage(i); + free(tmp); + return nil; + } + if(!new) /* an old image: must flip all the bits */ + for(j=0; j +#include +#include +#include + +Point +memimagestring(Memimage *b, Point p, Memimage *color, Point cp, Memsubfont *f, char *cs) +{ + int w, width; + uchar *s; + Rune c; + Fontchar *i; + + s = (uchar*)cs; + for(; c=*s; p.x+=width, cp.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; + i = f->info+c; + width = i->width; + memdraw(b, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i[0].x), p.y+i->bottom), + color, cp, f->bits, Pt(i->x, i->top), SoverD); + } + return p; +} + +Point +memsubfontwidth(Memsubfont *f, char *cs) +{ + Rune c; + Point p; + uchar *s; + Fontchar *i; + int w, width; + + p = Pt(0, f->height); + s = (uchar*)cs; + for(; c=*s; p.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; + i = f->info+c; + width = i->width; + } + return p; +} blob - /dev/null blob + e2bdee5c02d1af5f0551f015329c5ff6278ee58c (mode 644) --- /dev/null +++ src/libmemdraw/subfont.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +Memsubfont* +allocmemsubfont(char *name, int n, int height, int ascent, Fontchar *info, Memimage *i) +{ + Memsubfont *f; + + f = malloc(sizeof(Memsubfont)); + if(f == 0) + return 0; + f->n = n; + f->height = height; + f->ascent = ascent; + f->info = info; + f->bits = i; + if(name) + f->name = strdup(name); + else + f->name = 0; + return f; +} + +void +freememsubfont(Memsubfont *f) +{ + if(f == 0) + return; + free(f->info); /* note: f->info must have been malloc'ed! */ + freememimage(f->bits); + free(f); +} blob - /dev/null blob + 86835757b3dcf34a83621d1c04b04594c8e397e5 (mode 644) --- /dev/null +++ src/libmemdraw/unload.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int +_unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + for(y=r.min.y; ywidth*sizeof(u32int); + data += l; + } + return ndata; +} blob - /dev/null blob + c03c58e8728711e66d2a458c8341e737df9f3a71 (mode 644) --- /dev/null +++ src/libmemdraw/write.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#define CHUNK 8000 + +#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ +#define NHASH (1<<(HSHIFT*NMATCH)) +#define HMASK (NHASH-1) +#define hupdate(h, c) ((((h)<r; + bpl = bytesperline(r, i->depth); + n = Dy(r)*bpl; + data = malloc(n); + ncblock = _compblocksize(r, i->depth); + outbuf = malloc(ncblock); + hash = malloc(NHASH*sizeof(Hlist)); + chain = malloc(NMEM*sizeof(Hlist)); + if(data == 0 || outbuf == 0 || hash == 0 || chain == 0){ + ErrOut: + free(data); + free(outbuf); + free(hash); + free(chain); + return -1; + } + for(miny = r.min.y; miny != r.max.y; miny += dy){ + dy = r.max.y-miny; + if(dy*bpl > CHUNK) + dy = CHUNK/bpl; + nb = unloadmemimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), + data+(miny-r.min.y)*bpl, dy*bpl); + if(nb != dy*bpl) + goto ErrOut; + } + sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ", + chantostr(cbuf, i->chan), r.min.x, r.min.y, r.max.x, r.max.y); + if(write(fd, hdr, 11+5*12) != 11+5*12) + goto ErrOut; + edata = data+n; + eout = outbuf+ncblock; + line = data; + r.max.y = r.min.y; + while(line != edata){ + memset(hash, 0, NHASH*sizeof(Hlist)); + memset(chain, 0, NMEM*sizeof(Hlist)); + cp = chain; + h = 0; + outp = outbuf; + for(n = 0; n != NMATCH; n++) + h = hupdate(h, line[n]); + loutp = outbuf; + while(line != edata){ + ndump = 0; + eline = line+bpl; + for(p = line; p != eline; ){ + if(eline-p < NRUN) + es = eline; + else + es = p+NRUN; + q = 0; + runlen = 0; + for(hp = hash[h].next; hp; hp = hp->next){ + s = p + runlen; + if(s >= es) + continue; + t = hp->s + runlen; + for(; s >= p; s--) + if(*s != *t--) + goto matchloop; + t += runlen+2; + s += runlen+2; + for(; s < es; s++) + if(*s != *t++) + break; + n = s-p; + if(n > runlen){ + runlen = n; + q = hp->s; + if(n == NRUN) + break; + } + matchloop: ; + } + if(runlen < NMATCH){ + if(ndump == NDUMP){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + dumpbuf[ndump++] = *p; + runlen = 1; + } + else{ + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + offs = p-q-1; + if(eout-outp < 2) + goto Bfull; + *outp++ = ((runlen-NMATCH)<<2) + (offs>>8); + *outp++ = offs&255; + } + for(q = p+runlen; p != q; p++){ + if(cp->prev) + cp->prev->next = 0; + cp->next = hash[h].next; + cp->prev = &hash[h]; + if(cp->next) + cp->next->prev = cp; + cp->prev->next = cp; + cp->s = p; + if(++cp == &chain[NMEM]) + cp = chain; + if(edata-p > NMATCH) + h = hupdate(h, p[NMATCH]); + } + } + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + } + line = eline; + loutp = outp; + r.max.y++; + } + Bfull: + if(loutp == outbuf) + goto ErrOut; + n = loutp-outbuf; + sprint(hdr, "%11d %11ld ", r.max.y, n); + write(fd, hdr, 2*12); + write(fd, outbuf, n); + r.min.y = r.max.y; + } + free(data); + free(outbuf); + free(hash); + free(chain); + return 0; +} blob - /dev/null blob + c352a0b2c3fbceaab100c4d5af5f7ad829dd0921 (mode 644) --- /dev/null +++ src/libmemlayer/draw.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +struct Draw +{ + Point deltas; + Point deltam; + Memlayer *dstlayer; + Memimage *src; + Memimage *mask; + int op; +}; + +static +void +ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Draw *d; + Point p0, p1; + Rectangle oclipr, srcr, r, mr; + int ok; + + d = etc; + if(insave && d->dstlayer->save==nil) + return; + + p0 = addpt(screenr.min, d->deltas); + p1 = addpt(screenr.min, d->deltam); + + if(insave){ + r = rectsubpt(screenr, d->dstlayer->delta); + clipr = rectsubpt(clipr, d->dstlayer->delta); + }else + r = screenr; + + /* now in logical coordinates */ + + /* clipr may have narrowed what we should draw on, so clip if necessary */ + if(!rectinrect(r, clipr)){ + oclipr = dst->clipr; + dst->clipr = clipr; + ok = drawclip(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr); + dst->clipr = oclipr; + if(!ok) + return; + } + memdraw(dst, r, d->src, p0, d->mask, p1, d->op); +} + +void +memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op) +{ + struct Draw d; + Rectangle srcr, tr, mr; + Memlayer *dl, *sl; + + if(drawdebug) + iprint("memdraw %p %R %p %P %p %P\n", dst, r, src, p0, mask, p1); + + if(mask == nil) + mask = memopaque; + + if(mask->layer){ +if(drawdebug) iprint("mask->layer != nil\n"); + return; /* too hard, at least for now */ + } + + Top: + if(dst->layer==nil && src->layer==nil){ + memimagedraw(dst, r, src, p0, mask, p1, op); + return; + } + + if(drawclip(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0){ +if(drawdebug) iprint("drawclip dstcr %R srccr %R maskcr %R\n", dst->clipr, src->clipr, mask->clipr); + return; + } + + /* + * Convert to screen coordinates. + */ + dl = dst->layer; + if(dl != nil){ + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + } + Clearlayer: + if(dl!=nil && dl->clear){ + if(src == dst){ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + src = dl->screen->image; + } + dst = dl->screen->image; + goto Top; + } + + sl = src->layer; + if(sl != nil){ + p0.x += sl->delta.x; + p0.y += sl->delta.y; + srcr.min.x += sl->delta.x; + srcr.min.y += sl->delta.y; + srcr.max.x += sl->delta.x; + srcr.max.y += sl->delta.y; + } + + /* + * Now everything is in screen coordinates. + * mask is an image. dst and src are images or obscured layers. + */ + + /* + * if dst and src are the same layer, just draw in save area and expose. + */ + if(dl!=nil && dst==src){ + if(dl->save == nil) + return; /* refresh function makes this case unworkable */ + if(rectXrect(r, srcr)){ + tr = r; + if(srcr.min.x < tr.min.x){ + p1.x += tr.min.x - srcr.min.x; + tr.min.x = srcr.min.x; + } + if(srcr.min.y < tr.min.y){ + p1.y += tr.min.x - srcr.min.x; + tr.min.y = srcr.min.y; + } + if(srcr.max.x > tr.max.x) + tr.max.x = srcr.max.x; + if(srcr.max.y > tr.max.y) + tr.max.y = srcr.max.y; + memlhide(dst, tr); + }else{ + memlhide(dst, r); + memlhide(dst, srcr); + } + memdraw(dl->save, rectsubpt(r, dl->delta), dl->save, + subpt(srcr.min, src->layer->delta), mask, p1, op); + memlexpose(dst, r); + return; + } + + if(sl){ + if(sl->clear){ + src = sl->screen->image; + if(dl != nil){ + r.min.x -= dl->delta.x; + r.min.y -= dl->delta.y; + r.max.x -= dl->delta.x; + r.max.y -= dl->delta.y; + } + goto Top; + } + /* relatively rare case; use save area */ + if(sl->save == nil) + return; /* refresh function makes this case unworkable */ + memlhide(src, srcr); + /* convert back to logical coordinates */ + p0.x -= sl->delta.x; + p0.y -= sl->delta.y; + srcr.min.x -= sl->delta.x; + srcr.min.y -= sl->delta.y; + srcr.max.x -= sl->delta.x; + srcr.max.y -= sl->delta.y; + src = src->layer->save; + } + + /* + * src is now an image. dst may be an image or a clear layer + */ + if(dst->layer==nil) + goto Top; + if(dst->layer->clear) + goto Clearlayer; + + /* + * dst is an obscured layer + */ + d.deltas = subpt(p0, r.min); + d.deltam = subpt(p1, r.min); + d.dstlayer = dl; + d.src = src; + d.op = op; + d.mask = mask; + _memlayerop(ldrawop, dst, r, r, &d); +} blob - /dev/null blob + 17d934d319271a9fc2e8d1c44faebad413d7339b (mode 644) --- /dev/null +++ src/libmemlayer/lalloc.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +Memimage* +memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshptr, u32int val) +{ + Memlayer *l; + Memimage *n; + static Memimage *paint; + + if(paint == nil){ + paint = allocmemimage(Rect(0,0,1,1), RGBA32); + if(paint == nil) + return nil; + paint->flags |= Frepl; + paint->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + } + + n = allocmemimaged(screenr, s->image->chan, s->image->data, nil); + if(n == nil) + return nil; + l = malloc(sizeof(Memlayer)); + if(l == nil){ + free(n); + return nil; + } + + l->screen = s; + if(refreshfn) + l->save = nil; + else{ + l->save = allocmemimage(screenr, s->image->chan); + if(l->save == nil){ + free(l); + free(n); + return nil; + } + /* allocmemimage doesn't initialize memory; this paints save area */ + if(val != DNofill) + memfillcolor(l->save, val); + } + l->refreshfn = refreshfn; + l->refreshptr = nil; /* don't set it until we're done */ + l->screenr = screenr; + l->delta = Pt(0,0); + + n->data->ref++; + n->zero = s->image->zero; + n->width = s->image->width; + n->layer = l; + + /* start with new window behind all existing ones */ + l->front = s->rearmost; + l->rear = nil; + if(s->rearmost) + s->rearmost->layer->rear = n; + s->rearmost = n; + if(s->frontmost == nil) + s->frontmost = n; + l->clear = 0; + + /* now pull new window to front */ + _memltofrontfill(n, val != DNofill); + l->refreshptr = refreshptr; + + /* + * paint with requested color; previously exposed areas are already right + * if this window has backing store, but just painting the whole thing is simplest. + */ + if(val != DNofill){ + memsetchan(paint, n->chan); + memfillcolor(paint, val); + memdraw(n, n->r, paint, n->r.min, nil, n->r.min, S); + } + return n; +} blob - /dev/null blob + 800ffc859e7ce4be661c1f52bbda287f82cb63fb (mode 644) --- /dev/null +++ src/libmemlayer/layerop.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y), clipr, etc, front->layer->rear); + +static void +_layerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle r, + Rectangle clipr, + void *etc, + Memimage *front) +{ + Rectangle fr; + + Top: + if(front == i){ + /* no one is in front of this part of window; use the screen */ + fn(i->layer->screen->image, r, clipr, etc, 0); + return; + } + fr = front->layer->screenr; + if(rectXrect(r, fr) == 0){ + /* r doesn't touch this window; continue on next rearmost */ + /* assert(front && front->layer && front->layer->screen && front->layer->rear); */ + front = front->layer->rear; + goto Top; + } + if(fr.max.y < r.max.y){ + RECUR(r.min, fr.max, r.max, r.max); + r.max.y = fr.max.y; + } + if(r.min.y < fr.min.y){ + RECUR(r.min, r.min, r.max, fr.min); + r.min.y = fr.min.y; + } + if(fr.max.x < r.max.x){ + RECUR(fr.max, r.min, r.max, r.max); + r.max.x = fr.max.x; + } + if(r.min.x < fr.min.x){ + RECUR(r.min, r.min, fr.min, r.max); + r.min.x = fr.min.x; + } + /* r is covered by front, so put in save area */ + (*fn)(i->layer->save, r, clipr, etc, 1); +} + +/* + * Assumes incoming rectangle has already been clipped to i's logical r and clipr + */ +void +_memlayerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle screenr, /* clipped to window boundaries */ + Rectangle clipr, /* clipped also to clipping rectangles of hierarchy */ + void *etc) +{ + Memlayer *l; + Rectangle r, scr; + + l = i->layer; + if(!rectclip(&screenr, l->screenr)) + return; + if(l->clear){ + fn(l->screen->image, screenr, clipr, etc, 0); + return; + } + r = screenr; + scr = l->screen->image->clipr; + + /* + * Do the piece on the screen + */ + if(rectclip(&screenr, scr)) + _layerop(fn, i, screenr, clipr, etc, l->screen->frontmost); + if(rectinrect(r, scr)) + return; + + /* + * Do the piece off the screen + */ + if(!rectXrect(r, scr)){ + /* completely offscreen; easy */ + fn(l->save, r, clipr, etc, 1); + return; + } + if(r.min.y < scr.min.y){ + /* above screen */ + fn(l->save, Rect(r.min.x, r.min.y, r.max.x, scr.min.y), clipr, etc, 1); + r.min.y = scr.min.y; + } + if(r.max.y > scr.max.y){ + /* below screen */ + fn(l->save, Rect(r.min.x, scr.max.y, r.max.x, r.max.y), clipr, etc, 1); + r.max.y = scr.max.y; + } + if(r.min.x < scr.min.x){ + /* left of screen */ + fn(l->save, Rect(r.min.x, r.min.y, scr.min.x, r.max.y), clipr, etc, 1); + r.min.x = scr.min.x; + } + if(r.max.x > scr.max.x){ + /* right of screen */ + fn(l->save, Rect(scr.max.x, r.min.y, r.max.x, r.max.y), clipr, etc, 1); + } +} blob - /dev/null blob + 34cd6eadbc2a9d1c8ab46f5dd8caa286945d88be (mode 644) --- /dev/null +++ src/libmemlayer/ldelete.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +void +memldelete(Memimage *i) +{ + Memscreen *s; + Memlayer *l; + + l = i->layer; + /* free backing store and disconnect refresh, to make pushback fast */ + freememimage(l->save); + l->save = nil; + l->refreshptr = nil; + memltorear(i); + + /* window is now the rearmost; clean up screen structures and deallocate */ + s = i->layer->screen; + if(s->fill){ + i->clipr = i->r; + memdraw(i, i->r, s->fill, i->r.min, nil, i->r.min, S); + } + if(l->front){ + l->front->layer->rear = nil; + s->rearmost = l->front; + }else{ + s->frontmost = nil; + s->rearmost = nil; + } + free(l); + freememimage(i); +} + +/* + * Just free the data structures, don't do graphics + */ +void +memlfree(Memimage *i) +{ + Memlayer *l; + + l = i->layer; + freememimage(l->save); + free(l); + freememimage(i); +} + +void +_memlsetclear(Memscreen *s) +{ + Memimage *i, *j; + Memlayer *l; + + for(i=s->rearmost; i; i=i->layer->front){ + l = i->layer; + l->clear = rectinrect(l->screenr, l->screen->image->clipr); + if(l->clear) + for(j=l->front; j; j=j->layer->front) + if(rectXrect(l->screenr, j->layer->screenr)){ + l->clear = 0; + break; + } + } +} blob - /dev/null blob + d6aaa55fe2ce93dc01b060d7b567135f73b43f4e (mode 644) --- /dev/null +++ src/libmemlayer/lhide.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +/* + * Hide puts that portion of screenr now on the screen into the window's save area. + * Expose puts that portion of screenr now in the save area onto the screen. + * + * Hide and Expose both require that the layer structures in the screen + * match the geometry they are being asked to update, that is, they update the + * save area (hide) or screen (expose) based on what those structures tell them. + * This means they must be called at the correct time during window shuffles. + */ + +static +void +lhideop(Memimage *src, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Rectangle r; + Memlayer *l; + + USED(clipr.min.x); + USED(insave); + l = etc; + if(src != l->save){ /* do nothing if src is already in save area */ + r = rectsubpt(screenr, l->delta); + memdraw(l->save, r, src, screenr.min, nil, screenr.min, S); + } +} + +void +memlhide(Memimage *i, Rectangle screenr) +{ + if(i->layer->save == nil) + return; + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lhideop, i, screenr, screenr, i->layer); +} + +static +void +lexposeop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Memlayer *l; + Rectangle r; + + USED(clipr.min.x); + if(insave) /* if dst is save area, don't bother */ + return; + l = etc; + r = rectsubpt(screenr, l->delta); + if(l->save) + memdraw(dst, screenr, l->save, r.min, nil, r.min, S); + else + l->refreshfn(dst, r, l->refreshptr); +} + +void +memlexpose(Memimage *i, Rectangle screenr) +{ + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lexposeop, i, screenr, screenr, i->layer); +} blob - /dev/null blob + f74930ef0440253c6e3a26f22dde7a1b46594a38 (mode 644) --- /dev/null +++ src/libmemlayer/line.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +struct Lline +{ + Point p0; + Point p1; + Point delta; + int end0; + int end1; + int radius; + Point sp; + Memlayer *dstlayer; + Memimage *src; + int op; +}; + +static void llineop(Memimage*, Rectangle, Rectangle, void*, int); + +static +void +_memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + Rectangle r; + struct Lline ll; + Point d; + int srcclipped; + Memlayer *dl; + + if(radius < 0) + return; + if(src->layer) /* can't draw line with layered source */ + return; + srcclipped = 0; + + Top: + dl = dst->layer; + if(dl == nil){ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op); + return; + } + if(!srcclipped){ + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + srcclipped = 1; + } + + /* dst is known to be a layer */ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + p1.x += dl->delta.x; + p1.y += dl->delta.y; + clipr.min.x += dl->delta.x; + clipr.min.y += dl->delta.y; + clipr.max.x += dl->delta.x; + clipr.max.y += dl->delta.y; + if(dl->clear){ + dst = dst->layer->screen->image; + goto Top; + } + + /* XXX */ + /* this is not the correct set of tests */ +/* if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3) */ +/* return; */ + + /* can't use sutherland-cohen clipping because lines are wide */ + r = memlinebbox(p0, p1, end0, end1, radius); + /* + * r is now a bounding box for the line; + * use it as a clipping rectangle for subdivision + */ + if(rectclip(&r, clipr) == 0) + return; + ll.p0 = p0; + ll.p1 = p1; + ll.end0 = end0; + ll.end1 = end1; + ll.sp = sp; + ll.dstlayer = dst->layer; + ll.src = src; + ll.radius = radius; + ll.delta = dl->delta; + ll.op = op; + _memlayerop(llineop, dst, r, r, &ll); +} + +static +void +llineop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Lline *ll; + Point p0, p1; + + USED(screenr.min.x); + ll = etc; + if(insave && ll->dstlayer->save==nil) + return; + if(!rectclip(&clipr, screenr)) + return; + if(insave){ + p0 = subpt(ll->p0, ll->delta); + p1 = subpt(ll->p1, ll->delta); + clipr = rectsubpt(clipr, ll->delta); + }else{ + p0 = ll->p0; + p1 = ll->p1; + } + _memline(dst, p0, p1, ll->end0, ll->end1, ll->radius, ll->src, ll->sp, clipr, ll->op); +} + +void +memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} blob - /dev/null blob + d211564be0c81883cfa425d4e8a6f1c14edf8e95 (mode 644) --- /dev/null +++ src/libmemlayer/load.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +int +memload(Memimage *dst, Rectangle r, uchar *data, int n, int iscompressed) +{ + int (*loadfn)(Memimage*, Rectangle, uchar*, int); + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + loadfn = loadmemimage; + if(iscompressed) + loadfn = cloadmemimage; + + Top: + dl = dst->layer; + if(dl == nil) + return loadfn(dst, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/dst->depth); + if(dl->clear && dx==0){ + dst = dl->screen->image; + goto Top; + } + + /* + * dst is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + n = loadfn(dl->save, lr, data, n); + if(n > 0) + memlexpose(dst, r); + return n; + } + tmp = allocmemimage(lr, dst->chan); + if(tmp == nil) + return -1; + n = loadfn(tmp, lr, data, n); + memdraw(dst, lr, tmp, lr.min, nil, lr.min, S); + freememimage(tmp); + return n; +} blob - /dev/null blob + 0926ee8d216bd66119a4dc3c1a2932e2bb587287 (mode 644) --- /dev/null +++ src/libmemlayer/lorigin.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +/* + * Place i so i->r.min = log, i->layer->screenr.min == scr. +*/ +int +memlorigin(Memimage *i, Point log, Point scr) +{ + Memlayer *l; + Memscreen *s; + Memimage *t, *shad, *nsave; + Rectangle x, newr, oldr; + Point delta; + int overlap, eqlog, eqscr, wasclear; + + l = i->layer; + s = l->screen; + oldr = l->screenr; + newr = Rect(scr.x, scr.y, scr.x+Dx(oldr), scr.y+Dy(oldr)); + eqscr = eqpt(scr, oldr.min); + eqlog = eqpt(log, i->r.min); + if(eqscr && eqlog) + return 0; + nsave = nil; + if(eqlog==0 && l->save!=nil){ + nsave = allocmemimage(Rect(log.x, log.y, log.x+Dx(oldr), log.y+Dy(oldr)), i->chan); + if(nsave == nil) + return -1; + } + + /* + * Bring it to front and move logical coordinate system. + */ + memltofront(i); + wasclear = l->clear; + if(nsave){ + if(!wasclear) + memimagedraw(nsave, nsave->r, l->save, l->save->r.min, nil, Pt(0,0), S); + freememimage(l->save); + l->save = nsave; + } + delta = subpt(log, i->r.min); + i->r = rectaddpt(i->r, delta); + i->clipr = rectaddpt(i->clipr, delta); + l->delta = subpt(l->screenr.min, i->r.min); + if(eqscr) + return 0; + + /* + * To clean up old position, make a shadow window there, don't paint it, + * push it behind this one, and (later) delete it. Because the refresh function + * for this fake window is a no-op, this will cause no graphics action except + * to restore the background and expose the windows previously hidden. + */ + shad = memlalloc(s, oldr, memlnorefresh, nil, DNofill); + if(shad == nil) + return -1; + s->frontmost = i; + if(s->rearmost == i) + s->rearmost = shad; + else + l->rear->layer->front = shad; + shad->layer->front = i; + shad->layer->rear = l->rear; + l->rear = shad; + l->front = nil; + shad->layer->clear = 0; + + /* + * Shadow is now holding down the fort at the old position. + * Move the window and hide things obscured by new position. + */ + for(t=l->rear->layer->rear; t!=nil; t=t->layer->rear){ + x = newr; + overlap = rectclip(&x, t->layer->screenr); + if(overlap){ + memlhide(t, x); + t->layer->clear = 0; + } + } + l->screenr = newr; + l->delta = subpt(scr, i->r.min); + l->clear = rectinrect(newr, l->screen->image->clipr); + + /* + * Everything's covered. Copy to new position and delete shadow window. + */ + if(wasclear) + memdraw(s->image, newr, s->image, oldr.min, nil, Pt(0,0), S); + else + memlexpose(i, newr); + memldelete(shad); + + return 1; +} + +void +memlnorefresh(Memimage *l, Rectangle r, void *v) +{ + USED(l); + USED(r.min.x); + USED(v); +} blob - /dev/null blob + 526bd668a533cedbf0970ef1153c08e4795bc1f1 (mode 644) --- /dev/null +++ src/libmemlayer/lsetrefresh.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +int +memlsetrefresh(Memimage *i, Refreshfn fn, void *ptr) +{ + Memlayer *l; + + l = i->layer; + if(l->refreshfn!=0 && fn!=0){ /* just change functions */ + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + if(l->refreshfn == 0){ /* is using backup image; just free it */ + freememimage(l->save); + l->save = nil; + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + l->save = allocmemimage(i->r, i->chan); + if(l->save == nil) + return 0; + /* easiest way is just to update the entire save area */ + l->refreshfn(i, i->r, l->refreshptr); + l->refreshfn = 0; + l->refreshptr = nil; + return 1; +} blob - /dev/null blob + 447b40bdfdf51ccccab7932bdb72c2df30b26c00 (mode 644) --- /dev/null +++ src/libmemlayer/ltofront.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +/* + * Pull i towards top of screen, just behind front +*/ +static +void +_memltofront(Memimage *i, Memimage *front, int fill) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *ff, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->front != front){ + f = l->front; + x = l->screenr; + overlap = rectclip(&x, f->layer->screenr); + if(overlap){ + memlhide(f, x); + f->layer->clear = 0; + } + /* swap l and f in screen's list */ + ff = f->layer->front; + rr = l->rear; + if(ff == nil) + s->frontmost = i; + else + ff->layer->rear = i; + if(rr == nil) + s->rearmost = f; + else + rr->layer->front = f; + l->front = ff; + l->rear = f; + f->layer->front = i; + f->layer->rear = rr; + if(overlap && fill) + memlexpose(i, x); + } +} + +void +_memltofrontfill(Memimage *i, int fill) +{ + _memltofront(i, nil, fill); + _memlsetclear(i->layer->screen); +} + +void +memltofront(Memimage *i) +{ + _memltofront(i, nil, 1); + _memlsetclear(i->layer->screen); +} + +void +memltofrontn(Memimage **ip, int n) +{ + Memimage *i, *front; + Memscreen *s; + + if(n == 0) + return; + front = nil; + while(--n >= 0){ + i = *ip++; + _memltofront(i, front, 1); + front = i; + } + s = front->layer->screen; + _memlsetclear(s); +} blob - /dev/null blob + d53e8cc95e3a3c757c886690a9f47b2c007990d7 (mode 644) --- /dev/null +++ src/libmemlayer/ltorear.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +void +_memltorear(Memimage *i, Memimage *rear) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *r, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->rear != rear){ + r = l->rear; + x = l->screenr; + overlap = rectclip(&x, r->layer->screenr); + if(overlap){ + memlhide(i, x); + l->clear = 0; + } + /* swap l and r in screen's list */ + rr = r->layer->rear; + f = l->front; + if(rr == nil) + s->rearmost = i; + else + rr->layer->front = i; + if(f == nil) + s->frontmost = r; + else + f->layer->rear = r; + l->rear = rr; + l->front = r; + r->layer->rear = i; + r->layer->front = f; + if(overlap) + memlexpose(r, x); + } +} + +void +memltorear(Memimage *i) +{ + _memltorear(i, nil); + _memlsetclear(i->layer->screen); +} + +void +memltorearn(Memimage **ip, int n) +{ + Memimage *i, *rear; + Memscreen *s; + + if(n == 0) + return; + rear = nil; + while(--n >= 0){ + i = *ip++; + _memltorear(i, rear); + rear = i; + } + s = rear->layer->screen; + _memlsetclear(s); +} blob - /dev/null blob + c1d054190bb2ec47e3eaba67a37fb882d6949351 (mode 644) --- /dev/null +++ src/libmemlayer/mkfile @@ -0,0 +1,25 @@ +<$PLAN9/src/mkhdr + +LIB=libmemlayer.a + +OFILES=\ + draw.$O\ + lalloc.$O\ + layerop.$O\ + ldelete.$O\ + lhide.$O\ + line.$O\ + load.$O\ + lorigin.$O\ + lsetrefresh.$O\ + ltofront.$O\ + ltorear.$O\ + unload.$O\ + +HFILES=\ + $PLAN9/include/draw.h\ + $PLAN9/include/memdraw.h\ + $PLAN9/include/memlayer.h\ + +<$PLAN9/src/mksyslib + blob - /dev/null blob + b953411771b7a2800631983b5fefa3528a1a865f (mode 644) --- /dev/null +++ src/libmemlayer/unload.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +int +memunload(Memimage *src, Rectangle r, uchar *data, int n) +{ + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + Top: + dl = src->layer; + if(dl == nil) + return unloadmemimage(src, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/src->depth); + if(dl->clear && dx==0){ + src = dl->screen->image; + goto Top; + } + + /* + * src is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + if(dl->refreshfn != 0) + return -1; /* can't unload window if it's not Refbackup */ + if(n > 0) + memlhide(src, r); + n = unloadmemimage(dl->save, lr, data, n); + return n; + } + tmp = allocmemimage(lr, src->chan); + if(tmp == nil) + return -1; + memdraw(tmp, lr, src, lr.min, nil, lr.min, S); + n = unloadmemimage(tmp, lr, data, n); + freememimage(tmp); + return n; +}