Commit Diff


commit - 9361131304f39db29b1bec59d881e585035ec93c
commit + 324891a5579d6f504201a6107369c64dab245a98
blob - /dev/null
blob + b6ad61a73511c09d9c969629c292374b752e28bf (mode 644)
--- /dev/null
+++ src/libmemdraw/alloc.c
@@ -0,0 +1,204 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+#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(cc))-1;
+		i->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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+/*
+ * 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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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<n; i++)
+		memarc(x, c, a, b, thick, memblack, sp, alpha, phi, SoverD);
+	t1 = nsec();
+	print("%lld %lld\n", t1-t0-del, del);
+}
+
+int drawdebug = 0;
+
+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;
+}
+
blob - /dev/null
blob + 472caa6bac52ab44beaba8f009c5b85ed10363d4 (mode 644)
--- /dev/null
+++ src/libmemdraw/cload.c
@@ -0,0 +1,68 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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<maxy){
+			werrstr("readimage: bad maxy %d", maxy);
+			goto Errout;
+		}
+		if(nb<=0 || ncblock<nb){
+			werrstr("readimage: bad count %d", nb);
+			goto Errout;
+		}
+		if(readn(fd, buf, nb)!=nb)
+			goto Shortread;
+		if(!new)	/* old image: flip the data bits */
+			_twiddlecompressed(buf, nb);
+		cloadmemimage(i, Rect(r.min.x, miny, r.max.x, maxy), buf, nb);
+		miny = maxy;
+	}
+	free(buf);
+	return i;
+}
blob - /dev/null
blob + 21ea6cc013dfe8520b2230075ad4549a13640f02 (mode 644)
--- /dev/null
+++ src/libmemdraw/defont.c
@@ -0,0 +1,68 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 &par;
+}
+
+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<<j)-1);
+			replbit[j][i] = (small*replmul[j])>>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; i<n; i++){
+		print(" ");
+		if(p=b.grey){
+			print(" k%.2uX", *p);
+			b.grey += b.delta;
+		}else{	
+			if(p=b.red){
+				print(" r%.2uX", *p);
+				b.red += b.delta;
+			}
+			if(p=b.grn){
+				print(" g%.2uX", *p);
+				b.grn += b.delta;
+			}
+			if(p=b.blu){
+				print(" b%.2uX", *p);
+				b.blu += b.delta;
+			}
+		}
+		if((p=b.alpha) != &ones){
+			print(" α%.2uX", *p);
+			b.alpha += b.delta;
+		}
+	}
+	print("\n");
+}
+
+/*
+ * For each scan line, we expand the pixels from source, mask, and destination
+ * into byte-aligned red, green, blue, alpha, and grey channels.  If buffering is not
+ * needed and the channels were already byte-aligned (grey8, rgb24, rgba32, rgb32),
+ * the readers need not copy the data: they can simply return pointers to the data.
+ * If the destination image is grey and the source is not, it is converted using the NTSC
+ * formula.
+ *
+ * Once we have all the channels, we call either rgbcalc or greycalc, depending on 
+ * whether the destination image is color.  This is allowed to overwrite the dst buffer (perhaps
+ * the actual data, perhaps a copy) with its result.  It should only overwrite the dst buffer
+ * with the same format (i.e. red bytes with red bytes, etc.)  A new buffer is returned from
+ * the calculator, and that buffer is passed to a function to write it to the destination.
+ * If the buffer is already pointing at the destination, the writing function is a no-op.
+ */
+#define DBG if(drawdebug)
+static int
+alphadraw(Memdrawparam *par)
+{
+	int isgrey, starty, endy, op;
+	int needbuf, dsty, srcy, masky;
+	int y, dir, dx, dy;
+	Buffer bsrc, bdst, bmask;
+	Readfn *rdsrc, *rdmask, *rddst;
+	Calcfn *calc;
+	Writefn *wrdst;
+	Memimage *src, *mask, *dst;
+	Rectangle r, sr, mr;
+
+	if(drawdebug)
+		print("alphadraw %R\n", par->r);
+	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; i<dx; i++){
+		sa = *bsrc.alpha;
+		ma = *bmask.alpha;
+		fd = MUL(sa, ma, t);
+		if(op == DoutS)
+			fd = 255-fd;
+
+		if(grey){
+			*bdst.grey = MUL(fd, *bdst.grey, t);
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(q){
+				*bdst.rgba = MUL0123(fd, *bdst.rgba, s, t);
+				bsrc.rgba++;
+				bdst.rgba++;
+				bsrc.alpha += sadelta;
+				bmask.alpha += bmask.delta;
+				continue;
+			}
+			*bdst.red = MUL(fd, *bdst.red, t);
+			*bdst.grn = MUL(fd, *bdst.grn, t);
+			*bdst.blu = MUL(fd, *bdst.blu, t);
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = MUL(fd, *bdst.alpha, t);
+			bdst.alpha += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		bsrc.alpha += sadelta;
+	}
+	return obdst;
+}
+
+static Buffer
+alphacalc2810(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int fs, sadelta;
+	int i, ma, da, q;
+	u32int s, t;
+
+	obdst = bdst;
+	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
+	q = bsrc.delta == 4 && bdst.delta == 4;
+
+	for(i=0; i<dx; i++){
+		ma = *bmask.alpha;
+		da = *bdst.alpha;
+		if(op == SoutD)
+			da = 255-da;
+		fs = ma;
+		if(op != S)
+			fs = MUL(fs, da, t);
+
+		if(grey){
+			*bdst.grey = MUL(fs, *bsrc.grey, t);
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(q){
+				*bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t);
+				bsrc.rgba++;
+				bdst.rgba++;
+				bmask.alpha += bmask.delta;
+				bdst.alpha += bdst.delta;
+				continue;
+			}
+			*bdst.red = MUL(fs, *bsrc.red, t);
+			*bdst.grn = MUL(fs, *bsrc.grn, t);
+			*bdst.blu = MUL(fs, *bsrc.blu, t);
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = MUL(fs, *bsrc.alpha, t);
+			bdst.alpha += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		bsrc.alpha += sadelta;
+	}
+	return obdst;
+}
+
+static Buffer
+alphacalc3679(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int fs, fd, sadelta;
+	int i, sa, ma, da, q;
+	u32int s, t, u, v;
+
+	obdst = bdst;
+	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
+	q = bsrc.delta == 4 && bdst.delta == 4;
+
+	for(i=0; i<dx; i++){
+		sa = *bsrc.alpha;
+		ma = *bmask.alpha;
+		da = *bdst.alpha;
+		if(op == SatopD)
+			fs = MUL(ma, da, t);
+		else
+			fs = MUL(ma, 255-da, t);
+		if(op == DoverS)
+			fd = 255;
+		else{
+			fd = MUL(sa, ma, t);
+			if(op != DatopS)
+				fd = 255-fd;
+		}
+
+		if(grey){
+			*bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(q){
+				*bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t)+MUL0123(fd, *bdst.rgba, u, v);
+				bsrc.rgba++;
+				bdst.rgba++;
+				bsrc.alpha += sadelta;
+				bmask.alpha += bmask.delta;
+				bdst.alpha += bdst.delta;
+				continue;
+			}
+			*bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bdst.red, t);
+			*bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
+			*bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = MUL(fs, sa, s)+MUL(fd, da, t);
+			bdst.alpha += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		bsrc.alpha += sadelta;
+	}
+	return obdst;
+}
+
+static Buffer
+alphacalc5(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op)
+{
+	USED(dx);
+	USED(grey);
+	USED(op);
+	return bdst;
+}
+
+static Buffer
+alphacalc11(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, u, v;
+
+	USED(op);
+	obdst = bdst;
+	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
+	q = bsrc.delta == 4 && bdst.delta == 4;
+
+	for(i=0; i<dx; i++){
+		sa = *bsrc.alpha;
+		ma = *bmask.alpha;
+		fd = 255-MUL(sa, ma, t);
+
+		if(grey){
+			*bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(q){
+				*bdst.rgba = MUL0123(ma, *bsrc.rgba, s, t)+MUL0123(fd, *bdst.rgba, u, v);
+				bsrc.rgba++;
+				bdst.rgba++;
+				bsrc.alpha += sadelta;
+				bmask.alpha += bmask.delta;
+				continue;
+			}
+			*bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t);
+			*bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
+			*bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = MUL(ma, sa, s)+MUL(fd, *bdst.alpha, t);
+			bdst.alpha += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		bsrc.alpha += sadelta;
+	}
+	return obdst;
+}
+
+/*
+not used yet
+source and mask alpha 1
+static Buffer
+alphacalcS0(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int i;
+
+	USED(op);
+	obdst = bdst;
+	if(bsrc.delta == bdst.delta){
+		memmove(bdst.rgba, bsrc.rgba, dx*bdst.delta);
+		return obdst;
+	}
+	for(i=0; i<dx; i++){
+		if(grey){
+			*bdst.grey = *bsrc.grey;
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			*bdst.red = *bsrc.red;
+			*bdst.grn = *bsrc.grn;
+			*bdst.blu = *bsrc.blu;
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = 255;
+			bdst.alpha += bdst.delta;
+		}
+	}
+	return obdst;
+}
+*/
+
+/* source alpha 1 */
+static Buffer
+alphacalcS(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int fd;
+	int i, ma;
+	u32int s, t;
+
+	USED(op);
+	obdst = bdst;
+
+	for(i=0; i<dx; i++){
+		ma = *bmask.alpha;
+		fd = 255-ma;
+
+		if(grey){
+			*bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			*bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t);
+			*bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
+			*bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		if(bdst.alpha != &ones){
+			*bdst.alpha = ma+MUL(fd, *bdst.alpha, t);
+			bdst.alpha += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+	}
+	return obdst;
+}
+
+static Buffer
+boolcalc14(Buffer bdst, Buffer b1, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int i, ma, zero;
+
+	obdst = bdst;
+
+	for(i=0; i<dx; i++){
+		ma = *bmask.alpha;
+		zero = ma ? op == DoutS : op == DinS;
+
+		if(grey){
+			if(zero)
+				*bdst.grey = 0;
+			bdst.grey += bdst.delta;
+		}else{
+			if(zero)
+				*bdst.red = *bdst.grn = *bdst.blu = 0;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		if(bdst.alpha != &ones){
+			if(zero)
+				*bdst.alpha = 0;
+			bdst.alpha += bdst.delta;
+		}
+	}
+	return obdst;
+}
+
+static Buffer
+boolcalc236789(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int fs, fd;
+	int i, ma, da, zero;
+	u32int s, t;
+
+	obdst = bdst;
+	zero = !(op&1);
+
+	for(i=0; i<dx; i++){
+		ma = *bmask.alpha;
+		da = *bdst.alpha;
+		fs = da;
+		if(op&2)
+			fs = 255-da;
+		fd = 0;
+		if(op&4)
+			fd = 255;
+
+		if(grey){
+			if(ma)
+				*bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			else if(zero)
+				*bdst.grey = 0;
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(ma){
+				*bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bdst.red, t);
+				*bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
+				*bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			}
+			else if(zero)
+				*bdst.red = *bdst.grn = *bdst.blu = 0;
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		if(bdst.alpha != &ones){
+			if(ma)
+				*bdst.alpha = fs+MUL(fd, da, t);
+			else if(zero)
+				*bdst.alpha = 0;
+			bdst.alpha += bdst.delta;
+		}
+	}
+	return obdst;
+}
+
+static Buffer
+boolcalc1011(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
+{
+	Buffer obdst;
+	int i, ma, zero;
+
+	obdst = bdst;
+	zero = !(op&1);
+
+	for(i=0; i<dx; i++){
+		ma = *bmask.alpha;
+
+		if(grey){
+			if(ma)
+				*bdst.grey = *bsrc.grey;
+			else if(zero)
+				*bdst.grey = 0;
+			bsrc.grey += bsrc.delta;
+			bdst.grey += bdst.delta;
+		}else{
+			if(ma){
+				*bdst.red = *bsrc.red;
+				*bdst.grn = *bsrc.grn;
+				*bdst.blu = *bsrc.blu;
+			}
+			else if(zero)
+				*bdst.red = *bdst.grn = *bdst.blu = 0;
+			bsrc.red += bsrc.delta;
+			bsrc.blu += bsrc.delta;
+			bsrc.grn += bsrc.delta;
+			bdst.red += bdst.delta;
+			bdst.blu += bdst.delta;
+			bdst.grn += bdst.delta;
+		}
+		bmask.alpha += bmask.delta;
+		if(bdst.alpha != &ones){
+			if(ma)
+				*bdst.alpha = 255;
+			else if(zero)
+				*bdst.alpha = 0;
+			bdst.alpha += bdst.delta;
+		}
+	}
+	return obdst;
+}
+/*
+ * Replicated cached scan line read.  Call the function listed in the Param,
+ * but cache the result so that for replicated images we only do the work once.
+ */
+static Buffer
+replread(Param *p, uchar *s, int y)
+{
+	Buffer *b;
+
+	USED(s);
+	b = &p->bcache[y];
+	if((p->bfilled & (1<<y)) == 0){
+		p->bfilled |= 1<<y;
+		*b = p->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<n; i++){
+		if(nbits == 0){
+DBG print("(%.2ux)...", *r);
+			bits = *r++;
+			nbits = 8;
+		}
+		*w++ = repl[bits>>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<n; i++){
+		if(nbits == 0){
+			bits = *r++;
+			nbits = 8;
+		}
+		*w++ = repl[bits>>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<ex; x++){
+		bits <<= depth;
+DBG print(" %x", *r);
+		bits |= (*r++ >> sh);
+		nbits += depth;
+		if(nbits == 8){
+			*w++ = bits;
+			nbits = 0;
+		}
+	}
+
+	if(nbits){
+		sh = 8-nbits;
+		bits <<= sh;
+		bits |= *w & ((1<<sh)-1);
+		*w = bits;
+	}
+DBG print("\n");
+	return;
+}
+#undef DBG
+
+static Buffer
+readcmap(Param *p, uchar *buf, int y)
+{
+	Buffer b;
+	int a, convgrey, copyalpha, dx, i, m;
+	uchar *q, *cmap, *begin, *end, *r, *w;
+
+	memset(&b, 0, sizeof b);
+	begin = p->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; i<dx; i++){
+			*w++ = r[a];
+			q = cmap+r[m]*3;
+			r += 2;
+			if(r == end)
+				r = begin;
+			if(convgrey){
+				*w++ = RGB2K(q[0], q[1], q[2]);
+			}else{
+				*w++ = q[2];	/* blue */
+				*w++ = q[1];	/* green */
+				*w++ = q[0];	/* red */
+			}
+		}
+	}else{
+		b.alpha = &ones;
+		for(i=0; i<dx; i++){
+			q = cmap+*r++*3;
+			if(r == end)
+				r = begin;
+			if(convgrey){
+				*w++ = RGB2K(q[0], q[1], q[2]);
+			}else{
+				*w++ = q[2];	/* blue */
+				*w++ = q[1];	/* green */
+				*w++ = q[0];	/* red */
+			}
+		}
+	}
+
+	b.rgba = (u32int*)(buf-copyalpha);
+
+	if(convgrey){
+		b.grey = buf;
+		b.red = b.blu = b.grn = buf;
+		b.delta = 1+copyalpha;
+	}else{
+		b.blu = buf;
+		b.grn = buf+1;
+		b.red = buf+2;
+		b.grey = nil;
+		b.delta = 3+copyalpha;
+	}
+	return b;
+}
+
+static void
+writecmap(Param *p, uchar *w, Buffer src)
+{
+	uchar *cmap, *red, *grn, *blu;
+	int i, dx, delta;
+
+	cmap = p->img->cmap->rgb2cmap;
+	
+	delta = src.delta;
+	red= src.red;
+	grn = src.grn;
+	blu = src.blu;
+
+	dx = p->dx;
+	for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta)
+		*w++ = cmap[(*red>>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<dx; i++){
+		u = r[0] | (r[1]<<8) | (r[2]<<16) | (r[3]<<24);
+		if(copyalpha)
+			*w++ = arepl[(u>>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<<img->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<dx; i++){
+		u = w[0] | (w[1]<<8) | (w[2]<<16) | (w[3]<<24);
+DBG print("u %.8lux...", u);
+		u &= mask;
+DBG print("&mask %.8lux...", u);
+		if(isgrey){
+			u |= ((*grey >> (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(w<ew)
+			*w++ = *r++;
+	}
+
+	b.red = buf;
+	b.blu = b.grn = b.grey = b.alpha = nil;
+	b.rgba = (u32int*)buf;
+	b.delta = 0;
+	
+	return b;
+}
+
+static Readfn*
+convfn(Memimage *dst, Param *dpar, Memimage *src, Param *spar)
+{
+	if(dst->chan == 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<<bpp)-1;
+		break;
+	case 8:
+		val = p[0];
+		break;
+	case 16:
+		val = p[0]|(p[1]<<8);
+		break;
+	case 24:
+		val = p[0]|(p[1]<<8)|(p[2]<<16);
+		break;
+	case 32:
+		val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
+		break;
+	}
+	while(bpp<32){
+		val |= val<<bpp;
+		bpp *= 2;
+	}
+	return val;
+}
+
+static Calcfn*
+boolcopyfn(Memimage *img, Memimage *mask)
+{
+	if(mask->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<ep)
+		*p++ = val;
+}
+
+static void
+memsetl(void *vp, u32int val, int n)
+{
+	u32int *p, *ep;
+
+	p = vp;
+	ep = p+n;
+	while(p<ep)
+		*p++ = val;
+}
+
+static void
+memset24(void *vp, u32int val, int n)
+{
+	uchar *p, *ep;
+	uchar a,b,c;
+
+	p = vp;
+	ep = p+3*n;
+	a = val;
+	b = val>>8;
+	c = val>>16;
+	while(p<ep){
+		*p++ = a;
+		*p++ = b;
+		*p++ = c;
+	}
+}
+
+u32int
+_imgtorgba(Memimage *img, u32int val)
+{
+	uchar r, g, b, a;
+	int nb, ov, v;
+	u32int chan;
+	uchar *p;
+
+	a = 0xFF;
+	r = g = b = 0xAA;	/* garbage */
+	for(chan=img->chan; chan; chan>>=8){
+		nb = NBITS(chan);
+		ov = v = val&((1<<nb)-1);
+		val >>= nb;
+
+		while(nb < 8){
+			v |= v<<nb;
+			nb *= 2;
+		}
+		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))<<d;
+			break;
+		case CGreen:
+			v |= (g>>(8-nb))<<d;
+			break;
+		case CBlue:
+			v |= (b>>(8-nb))<<d;
+			break;
+		case CAlpha:
+			v |= (a>>(8-nb))<<d;
+			break;
+		case CMap:
+			p = img->cmap->rgb2cmap;
+			m = p[(r>>4)*256+(g>>4)*16+(b>>4)];
+			v |= (m>>(8-nb))<<d;
+			break;
+		case CGrey:
+			m = RGB2K(r,g,b);
+			v |= (m>>(8-nb))<<d;
+			break;
+		}
+		d += nb;
+	}
+/*	print("rgba2img %.8lux = %.*lux\n", rgba, 2*d/8, v); */
+	return v;
+}
+
+#define DBG if(0)
+static int
+memoptdraw(Memdrawparam *par)
+{
+	int m, y, dy, dx, op;
+	u32int v;
+	Memimage *src;
+	Memimage *dst;
+
+	dx = Dx(par->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<<d);
+			ppb = 8/dst->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<<nb)-1;
+DBG print("np %d x %d nb %d lm %ux ppb %d m %ux\n", np, par->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<<nb)-1);
+DBG print("np %d x %d nb %d rm %ux ppb %d m %ux\n", np, par->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<dy; y++, dp+=dwid)
+					*dp ^= (v ^ *dp) & lm;
+			}else if(dx == 0){	/* no full bytes */
+				if(lm)
+					dwid--;
+
+				for(y=0; y<dy; y++, dp+=dwid){
+					if(lm){
+DBG print("dp %p v %lux lm %ux (v ^ *dp) & lm %lux\n", dp, v, lm, (v^*dp)&lm);
+						*dp ^= (v ^ *dp) & lm;
+						dp++;
+					}
+					*dp ^= (v ^ *dp) & rm;
+				}
+			}else{		/* full bytes in middle */
+				dx /= ppb;
+				if(lm)
+					dwid--;
+				dwid -= dx;
+
+				for(y=0; y<dy; y++, dp+=dwid){
+					if(lm){
+						*dp ^= (v ^ *dp) & lm;
+						dp++;
+					}
+					memset(dp, v, dx);
+					dp += dx;
+					*dp ^= (v ^ *dp) & rm;
+				}
+			}
+			return 1;
+		case 8:
+			for(y=0; y<dy; y++, dp+=dwid)
+				memset(dp, v, dx);
+			return 1;
+		case 16:
+			p[0] = v;		/* make little endian */
+			p[1] = v>>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<dy; y++, dp+=dwid)
+				memsets(dp, v, dx);
+			return 1;
+		case 24:
+			for(y=0; y<dy; y++, dp+=dwid)
+				memset24(dp, v, dx);
+			return 1;
+		case 32:
+			p[0] = v;		/* make little endian */
+			p[1] = v>>8;
+			p[2] = v>>16;
+			p[3] = v>>24;
+			v = *(u32int*)p;
+			for(y=0; y<dy; y++, dp+=dwid)
+				memsetl(dp, v, dx);
+			return 1;
+		default:
+			assert(0 /* bad dest depth in memoptdraw */);
+		}
+	}
+
+	/*
+	 * If no source alpha, an opaque mask, we can just copy the
+	 * source onto the destination.  If the channels are the same and
+	 * the source is not replicated, memmove suffices.
+	 */
+	m = Simplemask|Fullmask;
+	if((par->state&(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; y<dy; y++, sp+=swid, dp+=dwid)
+			memmove(dp, sp, nb);
+		return 1;
+	}
+
+	/*
+	 * If we have a 1-bit mask, 1-bit source, and 1-bit destination, and
+	 * they're all bit aligned, we can just use bit operators.  This happens
+	 * when we're manipulating boolean masks, e.g. in the arc code.
+	 */
+	if((par->state&(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; y<dy; y++){
+				*dp ^= (*dp ^ *sp) & *mp & lm;
+				dp += dwid;
+				sp += swid;
+				mp += mwid;
+			}
+			return 1;
+		}
+
+		dx /= 8;
+		if(dir == 1){
+			i = (lm!=0)+dx+(rm!=0);
+			mwid -= i;
+			swid -= i;
+			dwid -= i;
+			for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
+				if(lm){
+					*dp ^= (*dp ^ *sp++) & *mp++ & lm;
+					dp++;
+				}
+				for(x=0; x<dx; x++){
+					*dp ^= (*dp ^ *sp++) & *mp++;
+					dp++;
+				}
+				if(rm){
+					*dp ^= (*dp ^ *sp++) & *mp++ & rm;
+					dp++;
+				}
+			}
+			return 1;
+		}else{
+		/* dir == -1 */
+			i = (lm!=0)+dx+(rm!=0);
+			dp += dwid*(dy-1)+i-1;
+			sp += swid*(dy-1)+i-1;
+			mp += mwid*(dy-1)+i-1;
+			dwid = -dwid+i;
+			swid = -swid+i;
+			mwid = -mwid+i;
+			for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
+				if(rm){
+					*dp ^= (*dp ^ *sp--) & *mp-- & rm;
+					dp--;
+				}
+				for(x=0; x<dx; x++){
+					*dp ^= (*dp ^ *sp--) & *mp--;
+					dp--;
+				}
+				if(lm){
+					*dp ^= (*dp ^ *sp--) & *mp-- & lm;
+					dp--;
+				}
+			}
+		}
+		return 1;
+	}
+	return 0;	
+}
+#undef DBG
+
+/*
+ * Boolean character drawing.
+ * Solid opaque color through a 1-bit greyscale mask.
+ */
+#define DBG if(0)
+static int
+chardraw(Memdrawparam *par)
+{
+	u32int bits;
+	int i, ddepth, dy, dx, x, bx, ex, y, npack, bsh, depth, op;
+	u32int v, maskwid, dstwid;
+	uchar *wp, *rp, *q, *wc;
+	ushort *ws;
+	u32int *wl;
+	uchar sp[4];
+	Rectangle r, mr;
+	Memimage *mask, *src, *dst;
+
+if(0) if(drawdebug) iprint("chardraw? mf %lux md %d sf %lux dxs %d dys %d dd %d ddat %p sdat %p\n",
+		par->mask->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; y<dy; y++, rp+=maskwid, wp+=dstwid){
+		q = rp;
+		if(bsh)
+			bits = *q++;
+		switch(ddepth){
+		case 8:
+/*if(drawdebug) iprint("8loop..."); */
+			wc = wp;
+			for(x=bx; x>ex; 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; y<i->r.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 <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <memdraw.h>
+
+#define DBG if(0)
+#define RGB2K(r,g,b)	((299*((u32int)(r))+587*((u32int)(g))+114*((u32int)(b)))/1000)
+
+/*
+ * This program tests the 'memimagedraw' primitive stochastically.
+ * It tests the combination aspects of it thoroughly, but since the
+ * three images it uses are disjoint, it makes no check of the
+ * correct behavior when images overlap.  That is, however, much
+ * easier to get right and to test.
+ */
+
+void	drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point);
+void	verifyone(void);
+void	verifyline(void);
+void	verifyrect(void);
+void	verifyrectrepl(int, int);
+void putpixel(Memimage *img, Point pt, u32int nv);
+u32int rgbatopix(uchar, uchar, uchar, uchar);
+
+char *dchan, *schan, *mchan;
+int dbpp, sbpp, mbpp;
+
+int drawdebug=0;
+int	seed;
+int	niters = 100;
+int	dbpp;	/* bits per pixel in destination */
+int	sbpp;	/* bits per pixel in src */
+int	mbpp;	/* bits per pixel in mask */
+int	dpm;	/* pixel mask at high part of byte, in destination */
+int	nbytes;	/* in destination */
+
+int	Xrange	= 64;
+int	Yrange	= 8;
+
+Memimage	*dst;
+Memimage	*src;
+Memimage	*mask;
+Memimage	*stmp;
+Memimage	*mtmp;
+Memimage	*ones;
+uchar	*dstbits;
+uchar	*srcbits;
+uchar	*maskbits;
+u32int	*savedstbits;
+
+void
+rdb(void)
+{
+}
+
+int
+iprint(char *fmt, ...)
+{
+	int n;	
+	va_list va;
+	char buf[1024];
+
+	va_start(va, fmt);
+	n = 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<<bpp)-1;
+/*	for(y=r.min.y; y<r.max.y; y++){ */
+	for(y=0; y<Yrange; y++){
+		nb = 0;
+		v = 0;
+		p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata);
+		Bprint(&b, "%-4d\t", y);
+/*		for(x=r.min.x; x<r.max.x; x++){ */
+		for(x=0; x<Xrange; x++){
+			if(x==0)
+				Bprint(&b, "\t");
+
+			if(x != 0 && (x%8)==0)
+				Bprint(&b, " ");
+
+			npr = 0;
+			if(x==labelpt.x && y==labelpt.y){
+				Bprint(&b, "*");
+				npr++;
+			}
+			if(npr == 0)
+				Bprint(&b, " ");
+
+			while(nb < bpp){
+				v &= (1<<nb)-1;
+				v |= (u32int)(*p++) << nb;
+				nb += 8;
+			}
+			nb -= bpp;
+/*			print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v, mask, nb); */
+			fmt(&b, arg, (v>>nb)&mask);
+		}
+		Bprint(&b, "\n");
+	}
+	Bterm(&b);
+}
+
+/*
+ * Verify that the destination pixel has the specified value.
+ * The value is in the high bits of v, suitably masked, but must
+ * be extracted from the destination Memimage.
+ */
+void
+checkone(Point p, Point sp, Point mp)
+{
+	int delta;
+	uchar *dp, *sdp;
+
+	delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata;
+	dp = (uchar*)dst->data->bdata+delta;
+	sdp = (uchar*)savedstbits+delta;
+
+	if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) {
+		fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp);
+		fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n",
+			dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]);
+		fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp));
+		dumpimage("src", src, src->data->bdata, sp);
+		dumpimage("mask", mask, mask->data->bdata, mp);
+		dumpimage("origdst", dst, dstbits, p);
+		dumpimage("dst", dst, dst->data->bdata, p);
+		dumpimage("gooddst", dst, savedstbits, p);
+		abort();
+	}
+}
+
+/*
+ * Verify that the destination line has the same value as the saved line.
+ */
+#define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
+void
+checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp)
+{
+	u32int *dp;
+	int nb;
+	u32int *saved;
+
+	dp = wordaddr(dst, Pt(0, y));
+	saved = savedstbits + y*dst->width;
+	if(dst->depth < 8)
+		nb = Xrange/(8/dst->depth);
+	else
+		nb = Xrange*(dst->depth/8);
+	if(memcmp(dp, saved, nb) != 0){
+		fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp);
+		fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp);
+		dumpimage("src", src, src->data->bdata, sp);
+		if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp);
+		dumpimage("mask", mask, mask->data->bdata, mp);
+		if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp);
+		dumpimage("origdst", dst, dstbits, r.min);
+		dumpimage("dst", dst, dst->data->bdata, r.min);
+		dumpimage("gooddst", dst, savedstbits, r.min);
+		abort();
+	}
+}
+
+/*
+ * Fill the bits of an image with random data.
+ * The Memimage parameter is used only to make sure
+ * the data is well formatted: only ucbits is written.
+ */
+void
+fill(Memimage *img, uchar *ucbits)
+{
+	int i, x, y;
+	ushort *up;
+	uchar alpha, r, g, b;
+	void *data;
+
+	if((img->flags&Falpha) == 0){
+		up = (ushort*)ucbits;
+		for(i=0; i<nbytes/2; i++)
+			*up++ = lrand() >> 7;
+		if(i+i != nbytes)
+			*(uchar*)up = lrand() >> 7;
+	}else{
+		data = img->data->bdata;
+		img->data->bdata = ucbits;
+
+		for(x=img->r.min.x; x<img->r.max.x; x++)
+		for(y=img->r.min.y; y<img->r.max.y; y++){
+			alpha = rand() >> 4;
+			r = rand()%(alpha+1);
+			g = rand()%(alpha+1);
+			b = rand()%(alpha+1);
+			putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha));
+		}
+		img->data->bdata = data;
+	}
+		
+}
+
+/*
+ * Mask is preset; do the rest
+ */
+void
+verifyonemask(void)
+{
+	Point dp, sp, mp;
+
+	fill(dst, dstbits);
+	fill(src, srcbits);
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+	memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+
+	dp.x = nrand(Xrange);
+	dp.y = nrand(Yrange);
+
+	sp.x = nrand(Xrange);
+	sp.y = nrand(Yrange);
+
+	mp.x = nrand(Xrange);
+	mp.y = nrand(Yrange);
+
+	drawonepixel(dst, dp, src, sp, mask, mp);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+	memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
+	
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+	memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+
+	checkone(dp, sp, mp);
+}
+
+void
+verifyone(void)
+{
+	int i;
+
+	/* mask all zeros */
+	memset(maskbits, 0, nbytes);
+	for(i=0; i<niters; i++)
+		verifyonemask();
+
+	/* mask all ones */
+	memset(maskbits, 0xFF, nbytes);
+	for(i=0; i<niters; i++)
+		verifyonemask();
+
+	/* random mask */
+	for(i=0; i<niters; i++){
+		fill(mask, maskbits);
+		verifyonemask();
+	}
+}
+
+/*
+ * Mask is preset; do the rest
+ */
+void
+verifylinemask(void)
+{
+	Point sp, mp, tp, up;
+	Rectangle dr;
+	int x;
+
+	fill(dst, dstbits);
+	fill(src, srcbits);
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+	memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+
+	dr.min.x = nrand(Xrange-1);
+	dr.min.y = nrand(Yrange-1);
+	dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
+	dr.max.y = dr.min.y + 1;
+
+	sp.x = nrand(Xrange);
+	sp.y = nrand(Yrange);
+
+	mp.x = nrand(Xrange);
+	mp.y = nrand(Yrange);
+
+	tp = sp;
+	up = mp;
+	for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
+		memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp, mask, up, SoverD);
+	memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
+
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+
+	memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
+	checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil);
+}
+
+void
+verifyline(void)
+{
+	int i;
+
+	/* mask all ones */
+	memset(maskbits, 0xFF, nbytes);
+	for(i=0; i<niters; i++)
+		verifylinemask();
+
+	/* mask all zeros */
+	memset(maskbits, 0, nbytes);
+	for(i=0; i<niters; i++)
+		verifylinemask();
+
+	/* random mask */
+	for(i=0; i<niters; i++){
+		fill(mask, maskbits);
+		verifylinemask();
+	}
+}
+
+/*
+ * Mask is preset; do the rest
+ */
+void
+verifyrectmask(void)
+{
+	Point sp, mp, tp, up;
+	Rectangle dr;
+	int x, y;
+
+	fill(dst, dstbits);
+	fill(src, srcbits);
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+	memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+
+	dr.min.x = nrand(Xrange-1);
+	dr.min.y = nrand(Yrange-1);
+	dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
+	dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y);
+
+	sp.x = nrand(Xrange);
+	sp.y = nrand(Yrange);
+
+	mp.x = nrand(Xrange);
+	mp.y = nrand(Yrange);
+
+	tp = sp;
+	up = mp;
+	for(y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++){
+		for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
+			memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask, up, SoverD);
+		tp.x = sp.x;
+		up.x = mp.x;
+	}
+	memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
+
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+
+	memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
+	for(y=0; y<Yrange; y++)
+		checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, nil, nil);
+}
+
+void
+verifyrect(void)
+{
+	int i;
+
+	/* mask all zeros */
+	memset(maskbits, 0, nbytes);
+	for(i=0; i<niters; i++)
+		verifyrectmask();
+
+	/* mask all ones */
+	memset(maskbits, 0xFF, nbytes);
+	for(i=0; i<niters; i++)
+		verifyrectmask();
+
+	/* random mask */
+	for(i=0; i<niters; i++){
+		fill(mask, maskbits);
+		verifyrectmask();
+	}
+}
+
+Rectangle
+randrect(void)
+{
+	Rectangle r;
+
+	r.min.x = nrand(Xrange-1);
+	r.min.y = nrand(Yrange-1);
+	r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x);
+	r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y);
+	return r;
+}
+
+/*
+ * Return coordinate corresponding to x withing range [minx, maxx)
+ */
+int
+tilexy(int minx, int maxx, int x)
+{
+	int sx;
+
+	sx = (x-minx) % (maxx-minx);
+	if(sx < 0)
+		sx += maxx-minx;
+	return sx+minx;
+}
+
+void
+replicate(Memimage *i, Memimage *tmp)
+{
+	Rectangle r, r1;
+	int x, y, nb;
+
+	/* choose the replication window (i->r) */
+	r.min.x = nrand(Xrange-1);
+	r.min.y = nrand(Yrange-1);
+	/* make it trivial more often than pure chance allows */
+	switch(lrand()&0){
+	case 1:
+		r.max.x = r.min.x + 2;
+		r.max.y = r.min.y + 2;
+		if(r.max.x < Xrange && r.max.y < Yrange)
+			break;
+		/* fall through */
+	case 0:
+		r.max.x = r.min.x + 1;
+		r.max.y = r.min.y + 1;
+		break;
+	default:
+		if(r.min.x+3 >= Xrange)
+			r.max.x = Xrange;
+		else
+			r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3));
+
+		if(r.min.y+3 >= Yrange)
+			r.max.y = Yrange;
+		else
+			r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3));
+	}
+	assert(r.min.x >= 0);	
+	assert(r.max.x <= Xrange);
+	assert(r.min.y >= 0);
+	assert(r.max.y <= Yrange);
+	/* copy from i to tmp so we have just the replicated bits */
+	nb = tmp->width*sizeof(u32int)*Yrange;
+	memset(tmp->data->bdata, 0, nb);
+	memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD);
+	memmove(i->data->bdata, tmp->data->bdata, nb);
+	/* i is now a non-replicated instance of the replication */
+	/* replicate it by hand through tmp */
+	memset(tmp->data->bdata, 0, nb);
+	x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x);
+	for(; x<Xrange; x+=Dx(r)){
+		y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y);
+		for(; y<Yrange; y+=Dy(r)){
+			/* set r1 to instance of tile by translation */
+			r1.min.x = x;
+			r1.min.y = y;
+			r1.max.x = r1.min.x+Dx(r);
+			r1.max.y = r1.min.y+Dy(r);
+			memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD);
+		}
+	}
+	i->flags |= Frepl;
+	i->r = r;
+	i->clipr = randrect();
+/*	fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y, */
+/*		i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); */
+	tmp->clipr = i->clipr;
+}
+
+/*
+ * Mask is preset; do the rest
+ */
+void
+verifyrectmaskrepl(int srcrepl, int maskrepl)
+{
+	Point sp, mp, tp, up;
+	Rectangle dr;
+	int x, y;
+	Memimage *s, *m;
+
+/*	print("verfrect %d %d\n", srcrepl, maskrepl); */
+	src->flags &= ~Frepl;
+	src->r = Rect(0, 0, Xrange, Yrange);
+	src->clipr = src->r;
+	stmp->flags &= ~Frepl;
+	stmp->r = Rect(0, 0, Xrange, Yrange);
+	stmp->clipr = src->r;
+	mask->flags &= ~Frepl;
+	mask->r = Rect(0, 0, Xrange, Yrange);
+	mask->clipr = mask->r;
+	mtmp->flags &= ~Frepl;
+	mtmp->r = Rect(0, 0, Xrange, Yrange);
+	mtmp->clipr = mask->r;
+
+	fill(dst, dstbits);
+	fill(src, srcbits);
+
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+	memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange);
+	memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange);
+
+	if(srcrepl){
+		replicate(src, stmp);
+		s = stmp;
+	}else
+		s = src;
+	if(maskrepl){
+		replicate(mask, mtmp);
+		m = mtmp;
+	}else
+		m = mask;
+
+	dr = randrect();
+
+	sp.x = nrand(Xrange);
+	sp.y = nrand(Yrange);
+
+	mp.x = nrand(Xrange);
+	mp.y = nrand(Yrange);
+
+DBG	print("smalldraws\n");
+	for(tp.y=sp.y,up.y=mp.y,y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++)
+		for(tp.x=sp.x,up.x=mp.x,x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
+			memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, SoverD);
+	memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrange);
+
+	memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange);
+
+DBG	print("bigdraw\n");
+	memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
+	for(y=0; y<Yrange; y++)
+		checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil);
+}
+
+void
+verifyrectrepl(int srcrepl, int maskrepl)
+{
+	int i;
+
+	/* mask all ones */
+	memset(maskbits, 0xFF, nbytes);
+	for(i=0; i<niters; i++)
+		verifyrectmaskrepl(srcrepl, maskrepl);
+
+	/* mask all zeros */
+	memset(maskbits, 0, nbytes);
+	for(i=0; i<niters; i++)
+		verifyrectmaskrepl(srcrepl, maskrepl);
+
+	/* random mask */
+	for(i=0; i<niters; i++){
+		fill(mask, maskbits);
+		verifyrectmaskrepl(srcrepl, maskrepl);
+	}
+}
+
+/*
+ * Trivial draw implementation.
+ * Color values are passed around as u32ints containing ααRRGGBB
+ */
+
+/*
+ * Convert v, which is nhave bits wide, into its nwant bits wide equivalent.
+ * Replicates to widen the value, truncates to narrow it.
+ */
+u32int
+replbits(u32int v, int nhave, int nwant)
+{
+	v &= (1<<nhave)-1;
+	for(; nhave<nwant; nhave*=2)
+		v |= v<<nhave;
+	v >>= (nhave-nwant);
+	return v & ((1<<nwant)-1);
+}
+
+/*
+ * Decode a pixel into the uchar* values.
+ */
+void
+pixtorgba(u32int v, uchar *r, uchar *g, uchar *b, uchar *a)
+{
+	*a = v>>24;
+	*r = v>>16;
+	*g = v>>8;
+	*b = v;
+}
+
+/*
+ * Convert uchar channels into u32int pixel.
+ */
+u32int
+rgbatopix(uchar r, uchar g, uchar b, uchar a)
+{
+	return (a<<24)|(r<<16)|(g<<8)|b;
+}
+
+/*
+ * Retrieve the pixel value at pt in the image.
+ */
+u32int
+getpixel(Memimage *img, Point pt)
+{
+	uchar r, g, b, a, *p;
+	int nbits, npack, bpp;
+	u32int v, c, rbits, bits;
+
+	r = g = b = 0;
+	a = ~0;	/* default alpha is full */
+
+	p = byteaddr(img, pt);
+	v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
+	bpp = img->depth;
+	if(bpp<8){
+		/*
+		 * Sub-byte greyscale pixels.
+		 *
+		 * We want to throw away the top pt.x%npack pixels and then use the next bpp bits
+		 * in the bottom byte of v.  This madness is due to having big endian bits
+		 * but little endian bytes.
+		 */
+		npack = 8/bpp;
+		v >>= 8 - bpp*(pt.x%npack+1);
+		v &= (1<<bpp)-1;
+		r = g = b = replbits(v, bpp, 8);
+	}else{
+		/*
+		 * General case.  We need to parse the channel descriptor and do what it says.
+		 * In all channels but the color map, we replicate to 8 bits because that's the
+		 * precision that all calculations are done at.
+		 *
+		 * In the case of the color map, we leave the bits alone, in case a color map
+		 * with less than 8 bits of index is used.  This is currently disallowed, so it's
+		 * sort of silly.
+		 */
+
+		for(c=img->chan; c; c>>=8){
+			nbits = NBITS(c);
+			bits = v & ((1<<nbits)-1);
+			rbits = replbits(bits, nbits, 8);
+			v >>= nbits;
+			switch(TYPE(c)){
+			case CRed:
+				r = rbits;
+				break;
+			case CGreen:
+				g = rbits;
+				break;
+			case CBlue:
+				b = rbits;
+				break;
+			case CGrey:
+				r = g = b = rbits;
+				break;
+			case CAlpha:
+				a = rbits;
+				break;
+			case CMap:
+				p = img->cmap->cmap2rgb + 3*bits;
+				r = p[0];
+				g = p[1];
+				b = p[2];
+				break;
+			case CIgnore:
+				break;
+			default:
+				fprint(2, "unknown channel type %lud\n", TYPE(c));
+				abort();
+			}
+		}
+	}
+	return rgbatopix(r, g, b, a);
+}
+
+/*
+ * Return the greyscale equivalent of a pixel.
+ */
+uchar
+getgrey(Memimage *img, Point pt)
+{
+	uchar r, g, b, a;
+	pixtorgba(getpixel(img, pt), &r, &g, &b, &a);
+	return RGB2K(r, g, b);
+}
+
+/*
+ * Return the value at pt in image, if image is interpreted
+ * as a mask.  This means the alpha channel if present, else
+ * the greyscale or its computed equivalent.
+ */
+uchar
+getmask(Memimage *img, Point pt)
+{
+	if(img->flags&Falpha)
+		return getpixel(img, pt)>>24;
+	else
+		return getgrey(img, pt);
+}
+#undef DBG
+
+#define DBG if(0)
+/*
+ * Write a pixel to img at point pt.
+ * 
+ * We do this by reading a 32-bit little endian
+ * value from p and then writing it back
+ * after tweaking the appropriate bits.  Because
+ * the data is little endian, we don't have to worry
+ * about what the actual depth is, as long as it is
+ * less than 32 bits.
+ */
+void
+putpixel(Memimage *img, Point pt, u32int nv)
+{
+	uchar r, g, b, a, *p, *q;
+	u32int c, mask, bits, v;
+	int bpp, sh, npack, nbits;
+
+	pixtorgba(nv, &r, &g, &b, &a);
+
+	p = byteaddr(img, pt);
+	v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
+	bpp = img->depth;
+DBG print("v %.8lux...", v);
+	if(bpp < 8){
+		/*
+		 * Sub-byte greyscale pixels.  We need to skip the leftmost pt.x%npack pixels,
+		 * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels.
+		 */	
+		npack = 8/bpp;
+		sh = bpp*(npack - pt.x%npack - 1);
+		bits = RGB2K(r,g,b);
+DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp));
+		bits = replbits(bits, 8, bpp);
+		mask = (1<<bpp)-1;
+DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
+		mask <<= sh;
+		bits <<= sh;
+DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask);
+		v = (v & ~mask) | (bits & mask);
+	} else {
+		/*
+		 * General case.  We need to parse the channel descriptor again.
+		 */
+		sh = 0;
+		for(c=img->chan; c; c>>=8){
+			nbits = NBITS(c);
+			switch(TYPE(c)){
+			case CRed:
+				bits = r;
+				break;
+			case CGreen:
+				bits = g;
+				break;
+			case CBlue:
+				bits = b;
+				break;
+			case CGrey:
+				bits = RGB2K(r, g, b);
+				break;
+			case CAlpha:
+				bits = a;
+				break;
+			case CIgnore:
+				bits = 0;
+				break;
+			case CMap:
+				q = img->cmap->rgb2cmap;
+				bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)];
+				break;
+			default:
+				SET(bits);
+				fprint(2, "unknown channel type %lud\n", TYPE(c));
+				abort();
+			}
+
+DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits));
+			if(TYPE(c) != CMap)
+				bits = replbits(bits, 8, nbits);
+			mask = (1<<nbits)-1;
+DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
+			bits <<= sh;
+			mask <<= sh;
+			v = (v & ~mask) | (bits & mask);
+			sh += nbits;
+		}
+	}
+DBG print("v %.8lux\n", v);
+	p[0] = v;
+	p[1] = v>>8;
+	p[2] = v>>16;
+	p[3] = v>>24;	
+}
+#undef DBG
+
+#define DBG if(0)
+void
+drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp)
+{
+	uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk;
+
+	pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da);
+	pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa);
+	m = getmask(mask, mp);
+	M = 255-(sa*m)/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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+/*
+ * 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<a: t/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(b<a && u>b*b || a<b && -u>a*a) {
+/*	if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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; i<nseg; i++, s++) {
+		*p = s;
+		if(s->p0.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(y<maxy) {
+		for(q = p = seg; p < ep; p++) {
+			s = *p;
+			if(s->p1.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<<fixshift);
+		iy++;
+	}
+}
+
+static void
+yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int fixshift, int op)
+{
+	long x, maxx, y, y2, yerr, yden, onehalf;
+	Seg **ep, **next, **p, **q, *s;
+	int n, i, ix, cnt, iy, iy2, miny, maxy;
+	Point pt;
+
+	for(i=0, s=segtab, p=seg; i<nseg; i++, s++) {
+		*p = s;
+		if(s->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(x<maxx) {
+		for(q = p = seg; p < ep; p++) {
+			s = *p;
+			if(s->p1.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<<fixshift);
+		ix++;
+	}
+}
+
+static void
+zsort(Seg **seg, Seg **ep)
+{
+	int done;
+	Seg **q, **p, *s;
+
+	if(ep-seg < 20) {
+		/* bubble sort by z - they should be almost sorted already */
+		q = ep;
+		do {
+			done = 1;
+			q--;
+			for(p = seg; p < q; p++) {
+				if(p[0]->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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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<clipr.max.y))
+			*d ^= (*d^srcval) & m;
+		if(e > 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<clipr.max.x))
+			*d ^= (*d^srcval) & m;
+		if(e > 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 && y<clipr.max.y){
+			d = byteaddr(dst, Pt(x, y));
+			sy = drawreplxy(src->r.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<clipr.max.x){
+			m = m0 >> (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<clipr.max.y){
+			d = byteaddr(dst, Pt(x, y));
+			s = byteaddr(src, addpt(dsrc, Pt(x, y)));
+			*d ^= (*d^*s) & m;
+		}
+		m >>= 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<clipr.max.x){
+			m = m0 >> (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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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; y<r.max.y; y++){
+			*q ^= (*data^*q) & m;
+			q += i->width*sizeof(u32int);
+			data++;
+		}
+		return ndata;
+	}
+	if(lpart==0 && rpart==0){	/* easy case */
+		for(y=r.min.y; y<r.max.y; y++){
+			memmove(q, data, l);
+			q += i->width*sizeof(u32int);
+			data += l;
+		}
+		return ndata;
+	}
+	mr = 0xFF ^ (0xFF >> rpart);
+	if(lpart!=0 && rpart==0){
+		for(y=r.min.y; y<r.max.y; y++){
+			*q ^= (*data^*q) & m;
+			if(l > 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<r.max.y; y++){
+			if(l > 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<r.max.y; y++){
+		*q ^= (*data^*q) & m;
+		if(l > 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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+/*
+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 <u.h>\n#include <libc.h>\n");
+	else
+		print("#include \"lib9.h\"\n");
+	print("#include <draw.h>\n");
+	print("#include <memdraw.h>\n\n");
+	print("static Memcmap def = {\n");
+	print("/* cmap2rgb */ {\n");
+	for(i=0; i<sizeof(c->cmap2rgb); ){
+		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; i<sizeof(c->rgb2cmap);){
+		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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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<nvert; i++){
+		e0 = e1 = Enddisc;
+		if(i == 1)
+			e0 = end0;
+		if(i == nvert-1)
+			e1 = end1;
+		memline(dst, vert[i-1], vert[i], e0, e1, radius, src, addpt(d, vert[i-1]), op);
+	}
+}
blob - /dev/null
blob + d7a535d7f2692e7cc411d51356f635841949dfb2 (mode 644)
--- /dev/null
+++ src/libmemdraw/read.c
@@ -0,0 +1,111 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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<chunk; j++)
+				tmp[j] ^= 0xFF;
+
+		if(loadmemimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), tmp, chunk) <= 0)
+			goto Err;
+		miny += dy;
+	}
+	free(tmp);
+	return i;
+}
blob - /dev/null
blob + 6ae19c012513ebfe5d368232c19228cca995049c (mode 644)
--- /dev/null
+++ src/libmemdraw/string.c
@@ -0,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+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; y<r.max.y; y++){
+		memmove(data, q, l);
+		q += i->width*sizeof(u32int);
+		data += l;
+	}
+	return ndata;
+}
blob - /dev/null
blob + c03c58e8728711e66d2a458c8341e737df9f3a71 (mode 644)
--- /dev/null
+++ src/libmemdraw/write.c
@@ -0,0 +1,183 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+#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)<<HSHIFT)^(c))&HMASK)
+typedef struct Hlist Hlist;
+struct Hlist{
+	uchar *s;
+	Hlist *next, *prev;
+};
+
+int
+writememimage(int fd, Memimage *i)
+{
+	uchar *outbuf, *outp, *eout;		/* encoded data, pointer, end */
+	uchar *loutp;				/* start of encoded line */
+	Hlist *hash;				/* heads of hash chains of past strings */
+	Hlist *chain, *hp;			/* hash chain members, pointer */
+	Hlist *cp;				/* next Hlist to fall out of window */
+	int h;					/* hash value */
+	uchar *line, *eline;			/* input line, end pointer */
+	uchar *data, *edata;			/* input buffer, end pointer */
+	u32int n;				/* length of input buffer */
+	u32int nb;				/* # of bytes returned by unloadimage */
+	int bpl;				/* input line length */
+	int offs, runlen;			/* offset, length of consumed data */
+	uchar dumpbuf[NDUMP];			/* dump accumulator */
+	int ndump;				/* length of dump accumulator */
+	int miny, dy;				/* y values while unloading input */
+	int ncblock;				/* size of compressed blocks */
+	Rectangle r;
+	uchar *p, *q, *s, *es, *t;
+	char hdr[11+5*12+1];
+	char cbuf[20];
+
+	r = i->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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+#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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * 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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * 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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * 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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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 <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+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;
+}