aboutsummaryrefslogtreecommitdiff
path: root/src/libmemdraw
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2006-06-25 18:58:06 +0000
committerrsc <devnull@localhost>2006-06-25 18:58:06 +0000
commit324891a5579d6f504201a6107369c64dab245a98 (patch)
tree3069429c14484b100d86dd5a3b1013565bf5db6c /src/libmemdraw
parent9361131304f39db29b1bec59d881e585035ec93c (diff)
downloadplan9port-324891a5579d6f504201a6107369c64dab245a98.tar.gz
plan9port-324891a5579d6f504201a6107369c64dab245a98.tar.bz2
plan9port-324891a5579d6f504201a6107369c64dab245a98.zip
separate out
Diffstat (limited to 'src/libmemdraw')
-rw-r--r--src/libmemdraw/alloc.c204
-rw-r--r--src/libmemdraw/arc.c116
-rw-r--r--src/libmemdraw/arctest.c61
-rw-r--r--src/libmemdraw/cload.c68
-rw-r--r--src/libmemdraw/cmap.c320
-rw-r--r--src/libmemdraw/cread.c96
-rw-r--r--src/libmemdraw/defont.c68
-rw-r--r--src/libmemdraw/draw.c2489
-rw-r--r--src/libmemdraw/drawtest.c1004
-rw-r--r--src/libmemdraw/ellipse.c247
-rw-r--r--src/libmemdraw/fillpoly.c524
-rw-r--r--src/libmemdraw/hwdraw.c12
-rw-r--r--src/libmemdraw/iprint.c12
-rw-r--r--src/libmemdraw/line.c486
-rw-r--r--src/libmemdraw/load.c72
-rw-r--r--src/libmemdraw/mkcmap.c79
-rw-r--r--src/libmemdraw/mkfile32
-rw-r--r--src/libmemdraw/openmemsubfont.c53
-rw-r--r--src/libmemdraw/poly.c23
-rw-r--r--src/libmemdraw/read.c111
-rw-r--r--src/libmemdraw/string.c66
-rw-r--r--src/libmemdraw/subfont.c34
-rw-r--r--src/libmemdraw/unload.c25
-rw-r--r--src/libmemdraw/write.c183
24 files changed, 6385 insertions, 0 deletions
diff --git a/src/libmemdraw/alloc.c b/src/libmemdraw/alloc.c
new file mode 100644
index 00000000..b6ad61a7
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/arc.c b/src/libmemdraw/arc.c
new file mode 100644
index 00000000..df836a00
--- /dev/null
+++ b/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);
+}
diff --git a/src/libmemdraw/arctest.c b/src/libmemdraw/arctest.c
new file mode 100644
index 00000000..b3a8c211
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/libmemdraw/cload.c b/src/libmemdraw/cload.c
new file mode 100644
index 00000000..472caa6b
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/cmap.c b/src/libmemdraw/cmap.c
new file mode 100644
index 00000000..2561c3f1
--- /dev/null
+++ b/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){}
diff --git a/src/libmemdraw/cread.c b/src/libmemdraw/cread.c
new file mode 100644
index 00000000..4fa5942a
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/defont.c b/src/libmemdraw/defont.c
new file mode 100644
index 00000000..21ea6cc0
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/draw.c b/src/libmemdraw/draw.c
new file mode 100644
index 00000000..2d8681e5
--- /dev/null
+++ b/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;
+ }
+}
+
diff --git a/src/libmemdraw/drawtest.c b/src/libmemdraw/drawtest.c
new file mode 100644
index 00000000..9d991782
--- /dev/null
+++ b/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));
+}
diff --git a/src/libmemdraw/ellipse.c b/src/libmemdraw/ellipse.c
new file mode 100644
index 00000000..7dc45cd6
--- /dev/null
+++ b/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);
+}
diff --git a/src/libmemdraw/fillpoly.c b/src/libmemdraw/fillpoly.c
new file mode 100644
index 00000000..fcacae51
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/hwdraw.c b/src/libmemdraw/hwdraw.c
new file mode 100644
index 00000000..3f36250f
--- /dev/null
+++ b/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 */
+}
+
diff --git a/src/libmemdraw/iprint.c b/src/libmemdraw/iprint.c
new file mode 100644
index 00000000..923b6b44
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/libmemdraw/line.c b/src/libmemdraw/line.c
new file mode 100644
index 00000000..c7042b9f
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/load.c b/src/libmemdraw/load.c
new file mode 100644
index 00000000..6788fa90
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/mkcmap.c b/src/libmemdraw/mkcmap.c
new file mode 100644
index 00000000..e8d5efc3
--- /dev/null
+++ b/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);
+}
diff --git a/src/libmemdraw/mkfile b/src/libmemdraw/mkfile
new file mode 100644
index 00000000..4c99bd60
--- /dev/null
+++ b/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
+
diff --git a/src/libmemdraw/openmemsubfont.c b/src/libmemdraw/openmemsubfont.c
new file mode 100644
index 00000000..c8d926e4
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/poly.c b/src/libmemdraw/poly.c
new file mode 100644
index 00000000..d16c0a92
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/src/libmemdraw/read.c b/src/libmemdraw/read.c
new file mode 100644
index 00000000..d7a535d7
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/string.c b/src/libmemdraw/string.c
new file mode 100644
index 00000000..6ae19c01
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/subfont.c b/src/libmemdraw/subfont.c
new file mode 100644
index 00000000..e2bdee5c
--- /dev/null
+++ b/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);
+}
diff --git a/src/libmemdraw/unload.c b/src/libmemdraw/unload.c
new file mode 100644
index 00000000..86835757
--- /dev/null
+++ b/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;
+}
diff --git a/src/libmemdraw/write.c b/src/libmemdraw/write.c
new file mode 100644
index 00000000..c03c58e8
--- /dev/null
+++ b/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;
+}