aboutsummaryrefslogtreecommitdiff
path: root/src/libdraw
diff options
context:
space:
mode:
Diffstat (limited to 'src/libdraw')
-rw-r--r--src/libdraw/BOT0
-rw-r--r--src/libdraw/Make.Darwin-PowerMacintosh6
-rw-r--r--src/libdraw/Make.FreeBSD-3867
-rw-r--r--src/libdraw/Make.HP-UX-90006
-rw-r--r--src/libdraw/Make.Linux-3867
-rw-r--r--src/libdraw/Make.NetBSD-3867
-rw-r--r--src/libdraw/Make.OSF1-alpha6
-rw-r--r--src/libdraw/Make.SunOS-sun4u2
-rw-r--r--src/libdraw/Make.SunOS-sun4u-cc6
-rw-r--r--src/libdraw/Make.SunOS-sun4u-gcc6
-rw-r--r--src/libdraw/Makefile194
-rw-r--r--src/libdraw/Makefile.MID123
-rw-r--r--src/libdraw/alloc.c237
-rw-r--r--src/libdraw/arith.c206
-rw-r--r--src/libdraw/buildfont.c141
-rw-r--r--src/libdraw/bytesperline.c34
-rw-r--r--src/libdraw/chan.c77
-rw-r--r--src/libdraw/creadimage.c113
-rw-r--r--src/libdraw/cursor.h7
-rw-r--r--src/libdraw/devdraw.c1587
-rw-r--r--src/libdraw/draw.h520
-rw-r--r--src/libdraw/ellipse.c82
-rw-r--r--src/libdraw/emenuhit.c271
-rw-r--r--src/libdraw/event.c486
-rw-r--r--src/libdraw/event.h63
-rw-r--r--src/libdraw/font.c401
-rw-r--r--src/libdraw/getsubfont.c36
-rw-r--r--src/libdraw/init.c203
-rw-r--r--src/libdraw/keyboard.c102
-rw-r--r--src/libdraw/keyboard.h36
-rw-r--r--src/libdraw/libdraw.x1
-rw-r--r--src/libdraw/md-alloc.c200
-rw-r--r--src/libdraw/md-arc.c116
-rw-r--r--src/libdraw/md-arctest.c61
-rw-r--r--src/libdraw/md-cload.c68
-rw-r--r--src/libdraw/md-cmap.c320
-rw-r--r--src/libdraw/md-cread.c96
-rw-r--r--src/libdraw/md-defont.c68
-rw-r--r--src/libdraw/md-draw.c2487
-rw-r--r--src/libdraw/md-drawtest.c1004
-rw-r--r--src/libdraw/md-ellipse.c247
-rw-r--r--src/libdraw/md-fillpoly.c524
-rw-r--r--src/libdraw/md-hwdraw.c12
-rw-r--r--src/libdraw/md-iprint.c12
-rw-r--r--src/libdraw/md-line.c484
-rw-r--r--src/libdraw/md-load.c72
-rw-r--r--src/libdraw/md-mkcmap.c79
-rw-r--r--src/libdraw/md-openmemsubfont.c53
-rw-r--r--src/libdraw/md-poly.c23
-rw-r--r--src/libdraw/md-read.c111
-rw-r--r--src/libdraw/md-string.c66
-rw-r--r--src/libdraw/md-subfont.c34
-rw-r--r--src/libdraw/md-unload.c25
-rw-r--r--src/libdraw/md-write.c183
-rw-r--r--src/libdraw/memdraw.h209
-rw-r--r--src/libdraw/memlayer.h48
-rw-r--r--src/libdraw/menuhit.c277
-rw-r--r--src/libdraw/mkfile1
-rw-r--r--src/libdraw/ml-draw.c192
-rw-r--r--src/libdraw/ml-lalloc.c79
-rw-r--r--src/libdraw/ml-layerop.c112
-rw-r--r--src/libdraw/ml-ldelete.c67
-rw-r--r--src/libdraw/ml-lhide.c67
-rw-r--r--src/libdraw/ml-line.c122
-rw-r--r--src/libdraw/ml-load.c55
-rw-r--r--src/libdraw/ml-lorigin.c107
-rw-r--r--src/libdraw/ml-lsetrefresh.c35
-rw-r--r--src/libdraw/ml-ltofront.c80
-rw-r--r--src/libdraw/ml-ltorear.c69
-rw-r--r--src/libdraw/ml-unload.c52
-rw-r--r--src/libdraw/mouse.c139
-rw-r--r--src/libdraw/mouse.h44
-rw-r--r--src/libdraw/openfont.c32
-rw-r--r--src/libdraw/readcolmap.c49
-rw-r--r--src/libdraw/readimage.c118
-rw-r--r--src/libdraw/readsubfont.c58
-rw-r--r--src/libdraw/string.c137
-rw-r--r--src/libdraw/stringwidth.c97
-rw-r--r--src/libdraw/subfont.c28
-rw-r--r--src/libdraw/subfontcache.c39
-rw-r--r--src/libdraw/subfontname.c44
-rw-r--r--src/libdraw/unix.c16
-rw-r--r--src/libdraw/unloadimage.c53
-rw-r--r--src/libdraw/writecolmap.c35
-rw-r--r--src/libdraw/x11-alloc.c120
-rw-r--r--src/libdraw/x11-cload.c19
-rw-r--r--src/libdraw/x11-draw.c143
-rw-r--r--src/libdraw/x11-event.c136
-rw-r--r--src/libdraw/x11-fill.c57
-rw-r--r--src/libdraw/x11-get.c110
-rw-r--r--src/libdraw/x11-inc.h31
-rw-r--r--src/libdraw/x11-init.c584
-rw-r--r--src/libdraw/x11-itrans.c258
-rw-r--r--src/libdraw/x11-keyboard.c71
-rw-r--r--src/libdraw/x11-load.c19
-rw-r--r--src/libdraw/x11-memdraw.h93
-rw-r--r--src/libdraw/x11-mouse.c107
-rw-r--r--src/libdraw/x11-pixelbits.c17
-rw-r--r--src/libdraw/x11-unload.c16
99 files changed, 15560 insertions, 0 deletions
diff --git a/src/libdraw/BOT b/src/libdraw/BOT
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/libdraw/BOT
diff --git a/src/libdraw/Make.Darwin-PowerMacintosh b/src/libdraw/Make.Darwin-PowerMacintosh
new file mode 100644
index 00000000..14b8d4e7
--- /dev/null
+++ b/src/libdraw/Make.Darwin-PowerMacintosh
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libdraw/Make.FreeBSD-386 b/src/libdraw/Make.FreeBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/libdraw/Make.FreeBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libdraw/Make.HP-UX-9000 b/src/libdraw/Make.HP-UX-9000
new file mode 100644
index 00000000..edbdc111
--- /dev/null
+++ b/src/libdraw/Make.HP-UX-9000
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS=-O -c -Ae -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libdraw/Make.Linux-386 b/src/libdraw/Make.Linux-386
new file mode 100644
index 00000000..74b0252c
--- /dev/null
+++ b/src/libdraw/Make.Linux-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libdraw/Make.NetBSD-386 b/src/libdraw/Make.NetBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/libdraw/Make.NetBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libdraw/Make.OSF1-alpha b/src/libdraw/Make.OSF1-alpha
new file mode 100644
index 00000000..3d45279b
--- /dev/null
+++ b/src/libdraw/Make.OSF1-alpha
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libdraw/Make.SunOS-sun4u b/src/libdraw/Make.SunOS-sun4u
new file mode 100644
index 00000000..c5fe67b8
--- /dev/null
+++ b/src/libdraw/Make.SunOS-sun4u
@@ -0,0 +1,2 @@
+include Make.SunOS-sun4u-$(CC)
+NAN=nan64.$O
diff --git a/src/libdraw/Make.SunOS-sun4u-cc b/src/libdraw/Make.SunOS-sun4u-cc
new file mode 100644
index 00000000..829301de
--- /dev/null
+++ b/src/libdraw/Make.SunOS-sun4u-cc
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I. -O
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libdraw/Make.SunOS-sun4u-gcc b/src/libdraw/Make.SunOS-sun4u-gcc
new file mode 100644
index 00000000..5c415948
--- /dev/null
+++ b/src/libdraw/Make.SunOS-sun4u-gcc
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libdraw/Makefile b/src/libdraw/Makefile
new file mode 100644
index 00000000..0aa2cd2d
--- /dev/null
+++ b/src/libdraw/Makefile
@@ -0,0 +1,194 @@
+
+# this works in gnu make
+SYSNAME:=${shell uname}
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'}
+
+# this works in bsd make
+SYSNAME!=uname
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'
+
+# the gnu rules will mess up bsd but not vice versa,
+# hence the gnu rules come first.
+
+include Make.$(SYSNAME)-$(OBJTYPE)
+
+PREFIX=/usr/local
+
+NUKEFILES=
+
+TGZFILES=
+
+LIB=libdraw.a
+VERSION=2.0
+PORTPLACE=devel/libdraw
+NAME=libdraw
+
+# keyboard.$O\
+# newwindow.$O\
+OFILES=\
+ alloc.$O\
+ allocimagemix.$O\
+ arith.$O\
+ bezier.$O\
+ border.$O\
+ buildfont.$O\
+ bytesperline.$O\
+ chan.$O\
+ cloadimage.$O\
+ computil.$O\
+ creadimage.$O\
+ debug.$O\
+ defont.$O\
+ draw.$O\
+ drawrepl.$O\
+ egetrect.$O\
+ ellipse.$O\
+ emenuhit.$O\
+ font.$O\
+ freesubfont.$O\
+ getdefont.$O\
+ getrect.$O\
+ getsubfont.$O\
+ icossin.$O\
+ icossin2.$O\
+ init.$O\
+ line.$O\
+ loadimage.$O\
+ menuhit.$O\
+ mkfont.$O\
+ openfont.$O\
+ poly.$O\
+ readcolmap.$O\
+ readimage.$O\
+ readsubfont.$O\
+ rectclip.$O\
+ replclipr.$O\
+ rgb.$O\
+ string.$O\
+ stringbg.$O\
+ stringsubfont.$O\
+ stringwidth.$O\
+ subfont.$O\
+ subfontcache.$O\
+ subfontname.$O\
+ unloadimage.$O\
+ window.$O\
+ writecolmap.$O\
+ writeimage.$O\
+ writesubfont.$O\
+ md-alloc.$O\
+ md-arc.$O\
+ md-cload.$O\
+ md-cmap.$O\
+ md-cread.$O\
+ md-defont.$O\
+ md-draw.$O\
+ md-ellipse.$O\
+ md-fillpoly.$O\
+ md-hwdraw.$O\
+ md-iprint.$O\
+ md-line.$O\
+ md-load.$O\
+ md-openmemsubfont.$O\
+ md-poly.$O\
+ md-read.$O\
+ md-string.$O\
+ md-subfont.$O\
+ md-unload.$O\
+ md-write.$O\
+ ml-draw.$O\
+ ml-lalloc.$O\
+ ml-layerop.$O\
+ ml-ldelete.$O\
+ ml-lhide.$O\
+ ml-line.$O\
+ ml-load.$O\
+ ml-lorigin.$O\
+ ml-lsetrefresh.$O\
+ ml-ltofront.$O\
+ ml-ltorear.$O\
+ ml-unload.$O\
+ x11-alloc.$O\
+ x11-cload.$O\
+ x11-draw.$O\
+ x11-event.$O\
+ x11-fill.$O\
+ x11-get.$O\
+ x11-init.$O\
+ x11-itrans.$O\
+ x11-keyboard.$O\
+ x11-load.$O\
+ x11-mouse.$O\
+ x11-pixelbits.$O\
+ x11-unload.$O\
+ devdraw.$O\
+ unix.$O\
+
+HFILES=\
+ draw.h\
+ memdraw.h
+
+all: $(LIB)
+
+install: $(LIB)
+ install -c -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+ install -c -m 0644 draw.h $(PREFIX)/include/draw.h
+ install -c -m 0644 event.h $(PREFIX)/include/event.h
+ install -c -m 0644 cursor.h $(PREFIX)/include/cursor.h
+ install -c -m 0644 mouse.h $(PREFIX)/include/mouse.h
+ install -c -m 0644 keyboard.h $(PREFIX)/include/keyboard.h
+
+test: test.o $(LIB)
+ gcc -o test test.o $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf -L/usr/X11R6/lib -lX11 -lm
+
+$(LIB): $(OFILES)
+ $(AR) $(ARFLAGS) $(LIB) $(OFILES)
+
+NUKEFILES+=$(LIB)
+.c.$O:
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
+
+%.$O: %.c
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
+
+
+$(OFILES): $(HFILES)
+
+tgz:
+ rm -rf $(NAME)-$(VERSION)
+ mkdir $(NAME)-$(VERSION)
+ cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.ports $(TGZFILES) $(NAME)-$(VERSION)
+ tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz
+ rm -rf $(NAME)-$(VERSION)
+
+clean:
+ rm -f $(OFILES) $(LIB)
+
+nuke:
+ rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES)
+
+rpm:
+ make tgz
+ cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES
+ rpm -ba rpm.spec
+ cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm .
+ cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm .
+ scp *.rpm rsc@amsterdam.lcs.mit.edu:public_html/software
+
+PORTDIR=/usr/ports/$(PORTPLACE)
+
+ports:
+ make tgz
+ rm -rf $(PORTDIR)
+ mkdir $(PORTDIR)
+ cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles
+ cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { ofile=$$2; next} {if(ofile) print >ofile}')
+ (cd $(PORTDIR); make makesum)
+ (cd $(PORTDIR); make)
+ (cd $(PORTDIR); /usr/local/bin/portlint)
+ rm -rf $(PORTDIR)/work
+ shar `find $(PORTDIR)` > ports.shar
+ (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz
+ scp *.tgz rsc@amsterdam.lcs.mit.edu:public_html/software
+
+.phony: all clean nuke install tgz rpm ports
diff --git a/src/libdraw/Makefile.MID b/src/libdraw/Makefile.MID
new file mode 100644
index 00000000..55cf7d4a
--- /dev/null
+++ b/src/libdraw/Makefile.MID
@@ -0,0 +1,123 @@
+LIB=libdraw.a
+VERSION=2.0
+PORTPLACE=devel/libdraw
+NAME=libdraw
+
+# keyboard.$O\
+# newwindow.$O\
+OFILES=\
+ alloc.$O\
+ allocimagemix.$O\
+ arith.$O\
+ bezier.$O\
+ border.$O\
+ buildfont.$O\
+ bytesperline.$O\
+ chan.$O\
+ cloadimage.$O\
+ computil.$O\
+ creadimage.$O\
+ debug.$O\
+ defont.$O\
+ draw.$O\
+ drawrepl.$O\
+ egetrect.$O\
+ ellipse.$O\
+ emenuhit.$O\
+ font.$O\
+ freesubfont.$O\
+ getdefont.$O\
+ getrect.$O\
+ getsubfont.$O\
+ icossin.$O\
+ icossin2.$O\
+ init.$O\
+ line.$O\
+ loadimage.$O\
+ menuhit.$O\
+ mkfont.$O\
+ openfont.$O\
+ poly.$O\
+ readcolmap.$O\
+ readimage.$O\
+ readsubfont.$O\
+ rectclip.$O\
+ replclipr.$O\
+ rgb.$O\
+ string.$O\
+ stringbg.$O\
+ stringsubfont.$O\
+ stringwidth.$O\
+ subfont.$O\
+ subfontcache.$O\
+ subfontname.$O\
+ unloadimage.$O\
+ window.$O\
+ writecolmap.$O\
+ writeimage.$O\
+ writesubfont.$O\
+ md-alloc.$O\
+ md-arc.$O\
+ md-cload.$O\
+ md-cmap.$O\
+ md-cread.$O\
+ md-defont.$O\
+ md-draw.$O\
+ md-ellipse.$O\
+ md-fillpoly.$O\
+ md-hwdraw.$O\
+ md-iprint.$O\
+ md-line.$O\
+ md-load.$O\
+ md-openmemsubfont.$O\
+ md-poly.$O\
+ md-read.$O\
+ md-string.$O\
+ md-subfont.$O\
+ md-unload.$O\
+ md-write.$O\
+ ml-draw.$O\
+ ml-lalloc.$O\
+ ml-layerop.$O\
+ ml-ldelete.$O\
+ ml-lhide.$O\
+ ml-line.$O\
+ ml-load.$O\
+ ml-lorigin.$O\
+ ml-lsetrefresh.$O\
+ ml-ltofront.$O\
+ ml-ltorear.$O\
+ ml-unload.$O\
+ x11-alloc.$O\
+ x11-cload.$O\
+ x11-draw.$O\
+ x11-event.$O\
+ x11-fill.$O\
+ x11-get.$O\
+ x11-init.$O\
+ x11-itrans.$O\
+ x11-keyboard.$O\
+ x11-load.$O\
+ x11-mouse.$O\
+ x11-pixelbits.$O\
+ x11-unload.$O\
+ devdraw.$O\
+ unix.$O\
+
+HFILES=\
+ draw.h\
+ memdraw.h
+
+all: $(LIB)
+
+install: $(LIB)
+ install -c -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+ install -c -m 0644 draw.h $(PREFIX)/include/draw.h
+ install -c -m 0644 event.h $(PREFIX)/include/event.h
+ install -c -m 0644 cursor.h $(PREFIX)/include/cursor.h
+ install -c -m 0644 mouse.h $(PREFIX)/include/mouse.h
+ install -c -m 0644 keyboard.h $(PREFIX)/include/keyboard.h
+
+test: test.o $(LIB)
+ gcc -o test test.o $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf -L/usr/X11R6/lib -lX11 -lm
+
diff --git a/src/libdraw/alloc.c b/src/libdraw/alloc.c
new file mode 100644
index 00000000..50b96fd8
--- /dev/null
+++ b/src/libdraw/alloc.c
@@ -0,0 +1,237 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Image*
+allocimage(Display *d, Rectangle r, u32int chan, int repl, u32int val)
+{
+ return _allocimage(nil, d, r, chan, repl, val, 0, 0);
+}
+
+Image*
+_allocimage(Image *ai, Display *d, Rectangle r, u32int chan, int repl, u32int val, int screenid, int refresh)
+{
+ uchar *a;
+ char *err;
+ Image *i;
+ Rectangle clipr;
+ int id;
+ int depth;
+
+ err = 0;
+ i = 0;
+
+ if(chan == 0){
+ werrstr("bad channel descriptor");
+ return nil;
+ }
+
+ depth = chantodepth(chan);
+ if(depth == 0){
+ err = "bad channel descriptor";
+ Error:
+ if(err)
+ werrstr("allocimage: %s", err);
+ else
+ werrstr("allocimage: %r");
+ free(i);
+ return 0;
+ }
+
+ /* flush pending data so we don't get error allocating the image */
+ flushimage(d, 0);
+ a = bufimage(d, 1+4+4+1+4+1+4*4+4*4+4);
+ if(a == 0)
+ goto Error;
+ d->imageid++;
+ id = d->imageid;
+ a[0] = 'b';
+ BPLONG(a+1, id);
+ BPLONG(a+5, screenid);
+ a[9] = refresh;
+ BPLONG(a+10, chan);
+ a[14] = repl;
+ BPLONG(a+15, r.min.x);
+ BPLONG(a+19, r.min.y);
+ BPLONG(a+23, r.max.x);
+ BPLONG(a+27, r.max.y);
+ if(repl)
+ /* huge but not infinite, so various offsets will leave it huge, not overflow */
+ clipr = Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF);
+ else
+ clipr = r;
+ BPLONG(a+31, clipr.min.x);
+ BPLONG(a+35, clipr.min.y);
+ BPLONG(a+39, clipr.max.x);
+ BPLONG(a+43, clipr.max.y);
+ BPLONG(a+47, val);
+ if(flushimage(d, 0) < 0)
+ goto Error;
+
+ if(ai)
+ i = ai;
+ else{
+ i = malloc(sizeof(Image));
+ if(i == nil){
+ a = bufimage(d, 1+4);
+ if(a){
+ a[0] = 'f';
+ BPLONG(a+1, id);
+ flushimage(d, 0);
+ }
+ goto Error;
+ }
+ }
+ i->display = d;
+ i->id = id;
+ i->depth = depth;
+ i->chan = chan;
+ i->r = r;
+ i->clipr = clipr;
+ i->repl = repl;
+ i->screen = 0;
+ i->next = 0;
+ return i;
+}
+
+Image*
+namedimage(Display *d, char *name)
+{
+ uchar *a;
+ char *err, buf[12*12+1];
+ Image *i;
+ int id, n;
+ u32int chan;
+
+ err = 0;
+ i = 0;
+
+ n = strlen(name);
+ if(n >= 256){
+ err = "name too long";
+ Error:
+ if(err)
+ werrstr("namedimage: %s", err);
+ else
+ werrstr("namedimage: %r");
+ if(i)
+ free(i);
+ return 0;
+ }
+ /* flush pending data so we don't get error allocating the image */
+ flushimage(d, 0);
+ a = bufimage(d, 1+4+1+n+1);
+ if(a == 0)
+ goto Error;
+ d->imageid++;
+ id = d->imageid;
+ a[0] = 'n';
+ BPLONG(a+1, id);
+ a[5] = n;
+ memmove(a+6, name, n);
+ a[6+n] = 'I';
+ if(flushimage(d, 0) < 0)
+ goto Error;
+ if(_drawmsgread(d, buf, sizeof buf) < 12*12)
+ goto Error;
+ buf[12*12] = '\0';
+
+ i = malloc(sizeof(Image));
+ if(i == nil){
+ Error1:
+ a = bufimage(d, 1+4);
+ if(a){
+ a[0] = 'f';
+ BPLONG(a+1, id);
+ flushimage(d, 0);
+ }
+ goto Error;
+ }
+ i->display = d;
+ i->id = id;
+ if((chan=strtochan(buf+2*12))==0){
+ werrstr("bad channel '%.12s' from devdraw", buf+2*12);
+ goto Error1;
+ }
+ i->chan = chan;
+ i->depth = chantodepth(chan);
+ i->repl = atoi(buf+3*12);
+ i->r.min.x = atoi(buf+4*12);
+ i->r.min.y = atoi(buf+5*12);
+ i->r.max.x = atoi(buf+6*12);
+ i->r.max.y = atoi(buf+7*12);
+ i->clipr.min.x = atoi(buf+8*12);
+ i->clipr.min.y = atoi(buf+9*12);
+ i->clipr.max.x = atoi(buf+10*12);
+ i->clipr.max.y = atoi(buf+11*12);
+ i->screen = 0;
+ i->next = 0;
+ return i;
+}
+
+int
+nameimage(Image *i, char *name, int in)
+{
+ uchar *a;
+ int n;
+
+ n = strlen(name);
+ a = bufimage(i->display, 1+4+1+1+n);
+ if(a == 0)
+ return 0;
+ a[0] = 'N';
+ BPLONG(a+1, i->id);
+ a[5] = in;
+ a[6] = n;
+ memmove(a+7, name, n);
+ if(flushimage(i->display, 0) < 0)
+ return 0;
+ return 1;
+}
+
+int
+_freeimage1(Image *i)
+{
+ uchar *a;
+ Display *d;
+ Image *w;
+
+ if(i == 0)
+ return 0;
+ /* make sure no refresh events occur on this if we block in the write */
+ d = i->display;
+ /* flush pending data so we don't get error deleting the image */
+ flushimage(d, 0);
+ a = bufimage(d, 1+4);
+ if(a == 0)
+ return -1;
+ a[0] = 'f';
+ BPLONG(a+1, i->id);
+ if(i->screen){
+ w = d->windows;
+ if(w == i)
+ d->windows = i->next;
+ else
+ while(w){
+ if(w->next == i){
+ w->next = i->next;
+ break;
+ }
+ w = w->next;
+ }
+ }
+ if(flushimage(d, i->screen!=0) < 0)
+ return -1;
+
+ return 0;
+}
+
+int
+freeimage(Image *i)
+{
+ int ret;
+
+ ret = _freeimage1(i);
+ free(i);
+ return ret;
+}
diff --git a/src/libdraw/arith.c b/src/libdraw/arith.c
new file mode 100644
index 00000000..41b30620
--- /dev/null
+++ b/src/libdraw/arith.c
@@ -0,0 +1,206 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Point
+Pt(int x, int y)
+{
+ Point p;
+
+ p.x = x;
+ p.y = y;
+ return p;
+}
+
+Rectangle
+Rect(int x, int y, int bx, int by)
+{
+ Rectangle r;
+
+ r.min.x = x;
+ r.min.y = y;
+ r.max.x = bx;
+ r.max.y = by;
+ return r;
+}
+
+Rectangle
+Rpt(Point min, Point max)
+{
+ Rectangle r;
+
+ r.min = min;
+ r.max = max;
+ return r;
+}
+
+Point
+addpt(Point a, Point b)
+{
+ a.x += b.x;
+ a.y += b.y;
+ return a;
+}
+
+Point
+subpt(Point a, Point b)
+{
+ a.x -= b.x;
+ a.y -= b.y;
+ return a;
+}
+
+Rectangle
+insetrect(Rectangle r, int n)
+{
+ r.min.x += n;
+ r.min.y += n;
+ r.max.x -= n;
+ r.max.y -= n;
+ return r;
+}
+
+Point
+divpt(Point a, int b)
+{
+ a.x /= b;
+ a.y /= b;
+ return a;
+}
+
+Point
+mulpt(Point a, int b)
+{
+ a.x *= b;
+ a.y *= b;
+ return a;
+}
+
+Rectangle
+rectsubpt(Rectangle r, Point p)
+{
+ r.min.x -= p.x;
+ r.min.y -= p.y;
+ r.max.x -= p.x;
+ r.max.y -= p.y;
+ return r;
+}
+
+Rectangle
+rectaddpt(Rectangle r, Point p)
+{
+ r.min.x += p.x;
+ r.min.y += p.y;
+ r.max.x += p.x;
+ r.max.y += p.y;
+ return r;
+}
+
+int
+eqpt(Point p, Point q)
+{
+ return p.x==q.x && p.y==q.y;
+}
+
+int
+eqrect(Rectangle r, Rectangle s)
+{
+ return r.min.x==s.min.x && r.max.x==s.max.x &&
+ r.min.y==s.min.y && r.max.y==s.max.y;
+}
+
+int
+rectXrect(Rectangle r, Rectangle s)
+{
+ return r.min.x<s.max.x && s.min.x<r.max.x &&
+ r.min.y<s.max.y && s.min.y<r.max.y;
+}
+
+int
+rectinrect(Rectangle r, Rectangle s)
+{
+ return s.min.x<=r.min.x && r.max.x<=s.max.x && s.min.y<=r.min.y && r.max.y<=s.max.y;
+}
+
+int
+ptinrect(Point p, Rectangle r)
+{
+ return p.x>=r.min.x && p.x<r.max.x &&
+ p.y>=r.min.y && p.y<r.max.y;
+}
+
+Rectangle
+canonrect(Rectangle r)
+{
+ int t;
+ if (r.max.x < r.min.x) {
+ t = r.min.x;
+ r.min.x = r.max.x;
+ r.max.x = t;
+ }
+ if (r.max.y < r.min.y) {
+ t = r.min.y;
+ r.min.y = r.max.y;
+ r.max.y = t;
+ }
+ return r;
+}
+
+void
+combinerect(Rectangle *r1, Rectangle r2)
+{
+ if(r1->min.x > r2.min.x)
+ r1->min.x = r2.min.x;
+ if(r1->min.y > r2.min.y)
+ r1->min.y = r2.min.y;
+ if(r1->max.x < r2.max.x)
+ r1->max.x = r2.max.x;
+ if(r1->max.y < r2.max.y)
+ r1->max.y = r2.max.y;
+}
+
+u32int
+drawld2chan[] = {
+ GREY1,
+ GREY2,
+ GREY4,
+ CMAP8,
+};
+
+int log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 };
+
+u32int
+setalpha(u32int color, uchar alpha)
+{
+ int red, green, blue;
+
+ red = (color >> 3*8) & 0xFF;
+ green = (color >> 2*8) & 0xFF;
+ blue = (color >> 1*8) & 0xFF;
+ /* ignore incoming alpha */
+ red = (red * alpha)/255;
+ green = (green * alpha)/255;
+ blue = (blue * alpha)/255;
+ return (red<<3*8) | (green<<2*8) | (blue<<1*8) | (alpha<<0*8);
+}
+
+Point ZP;
+Rectangle ZR;
+int
+Rfmt(Fmt *f)
+{
+ Rectangle r;
+
+ r = va_arg(f->args, Rectangle);
+ return fmtprint(f, "%P %P", r.min, r.max);
+}
+
+int
+Pfmt(Fmt *f)
+{
+ Point p;
+
+ p = va_arg(f->args, Point);
+ return fmtprint(f, "[%d %d]", p.x, p.y);
+}
+
diff --git a/src/libdraw/buildfont.c b/src/libdraw/buildfont.c
new file mode 100644
index 00000000..ba32e775
--- /dev/null
+++ b/src/libdraw/buildfont.c
@@ -0,0 +1,141 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static char*
+skip(char *s)
+{
+ while(*s==' ' || *s=='\n' || *s=='\t')
+ s++;
+ return s;
+}
+
+Font*
+buildfont(Display *d, char *buf, char *name)
+{
+ Font *fnt;
+ Cachefont *c;
+ char *s, *t;
+ ulong min, max;
+ int offset;
+ char badform[] = "bad font format: number expected (char position %d)";
+
+ s = buf;
+ fnt = malloc(sizeof(Font));
+ if(fnt == 0)
+ return 0;
+ memset(fnt, 0, sizeof(Font));
+ fnt->display = d;
+ fnt->name = strdup(name);
+ fnt->ncache = NFCACHE+NFLOOK;
+ fnt->nsubf = NFSUBF;
+ fnt->cache = malloc(fnt->ncache * sizeof(fnt->cache[0]));
+ fnt->subf = malloc(fnt->nsubf * sizeof(fnt->subf[0]));
+ if(fnt->name==0 || fnt->cache==0 || fnt->subf==0){
+ Err2:
+ free(fnt->name);
+ free(fnt->cache);
+ free(fnt->subf);
+ free(fnt->sub);
+ free(fnt);
+ return 0;
+ }
+ fnt->height = strtol(s, &s, 0);
+ s = skip(s);
+ fnt->ascent = strtol(s, &s, 0);
+ s = skip(s);
+ if(fnt->height<=0 || fnt->ascent<=0){
+ werrstr("bad height or ascent in font file");
+ goto Err2;
+ }
+ fnt->width = 0;
+ fnt->nsub = 0;
+ fnt->sub = 0;
+
+ memset(fnt->subf, 0, fnt->nsubf * sizeof(fnt->subf[0]));
+ memset(fnt->cache, 0, fnt->ncache*sizeof(fnt->cache[0]));
+ fnt->age = 1;
+ do{
+ /* must be looking at a number now */
+ if(*s<'0' || '9'<*s){
+ werrstr(badform, s-buf);
+ goto Err3;
+ }
+ min = strtol(s, &s, 0);
+ s = skip(s);
+ /* must be looking at a number now */
+ if(*s<'0' || '9'<*s){
+ werrstr(badform, s-buf);
+ goto Err3;
+ }
+ max = strtol(s, &s, 0);
+ s = skip(s);
+ if(*s==0 || min>=65536 || max>=65536 || min>max){
+ werrstr("illegal subfont range");
+ Err3:
+ freefont(fnt);
+ return 0;
+ }
+ t = s;
+ offset = strtol(s, &t, 0);
+ if(t>s && (*t==' ' || *t=='\t' || *t=='\n'))
+ s = skip(t);
+ else
+ offset = 0;
+ fnt->sub = realloc(fnt->sub, (fnt->nsub+1)*sizeof(Cachefont*));
+ if(fnt->sub == 0){
+ /* realloc manual says fnt->sub may have been destroyed */
+ fnt->nsub = 0;
+ goto Err3;
+ }
+ c = malloc(sizeof(Cachefont));
+ if(c == 0)
+ goto Err3;
+ fnt->sub[fnt->nsub] = c;
+ c->min = min;
+ c->max = max;
+ c->offset = offset;
+ t = s;
+ while(*s && *s!=' ' && *s!='\n' && *s!='\t')
+ s++;
+ *s++ = 0;
+ c->subfontname = 0;
+ c->name = strdup(t);
+ if(c->name == 0){
+ free(c);
+ goto Err3;
+ }
+ s = skip(s);
+ fnt->nsub++;
+ }while(*s);
+ return fnt;
+}
+
+void
+freefont(Font *f)
+{
+ int i;
+ Cachefont *c;
+ Subfont *s;
+
+ if(f == 0)
+ return;
+
+ for(i=0; i<f->nsub; i++){
+ c = f->sub[i];
+ free(c->subfontname);
+ free(c->name);
+ free(c);
+ }
+ for(i=0; i<f->nsubf; i++){
+ s = f->subf[i].f;
+ if(s && s!=display->defaultsubfont)
+ freesubfont(s);
+ }
+ freeimage(f->cacheimage);
+ free(f->name);
+ free(f->cache);
+ free(f->subf);
+ free(f->sub);
+ free(f);
+}
diff --git a/src/libdraw/bytesperline.c b/src/libdraw/bytesperline.c
new file mode 100644
index 00000000..08ff7d7f
--- /dev/null
+++ b/src/libdraw/bytesperline.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static
+int
+unitsperline(Rectangle r, int d, int bitsperunit)
+{
+ ulong l, t;
+
+ if(d <= 0 || d > 32) /* being called wrong. d is image depth. */
+ abort();
+
+ if(r.min.x >= 0){
+ l = (r.max.x*d+bitsperunit-1)/bitsperunit;
+ l -= (r.min.x*d)/bitsperunit;
+ }else{ /* make positive before divide */
+ t = (-r.min.x*d+bitsperunit-1)/bitsperunit;
+ l = t+(r.max.x*d+bitsperunit-1)/bitsperunit;
+ }
+ return l;
+}
+
+int
+wordsperline(Rectangle r, int d)
+{
+ return unitsperline(r, d, 8*sizeof(ulong));
+}
+
+int
+bytesperline(Rectangle r, int d)
+{
+ return unitsperline(r, d, 8);
+}
diff --git a/src/libdraw/chan.c b/src/libdraw/chan.c
new file mode 100644
index 00000000..3b76a32a
--- /dev/null
+++ b/src/libdraw/chan.c
@@ -0,0 +1,77 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static char channames[] = "rgbkamx";
+char*
+chantostr(char *buf, u32int cc)
+{
+ u32int c, rc;
+ char *p;
+
+ if(chantodepth(cc) == 0)
+ return nil;
+
+ /* reverse the channel descriptor so we can easily generate the string in the right order */
+ rc = 0;
+ for(c=cc; c; c>>=8){
+ rc <<= 8;
+ rc |= c&0xFF;
+ }
+
+ p = buf;
+ for(c=rc; c; c>>=8) {
+ *p++ = channames[TYPE(c)];
+ *p++ = '0'+NBITS(c);
+ }
+ *p = 0;
+
+ return buf;
+}
+
+/* avoid pulling in ctype when using with drawterm etc. */
+static int
+isspace(char c)
+{
+ return c==' ' || c== '\t' || c=='\r' || c=='\n';
+}
+
+u32int
+strtochan(char *s)
+{
+ char *p, *q;
+ u32int c;
+ int t, n;
+
+ c = 0;
+ p=s;
+ while(*p && isspace(*p))
+ p++;
+
+ while(*p && !isspace(*p)){
+ if((q = strchr(channames, p[0])) == nil)
+ return 0;
+ t = q-channames;
+ if(p[1] < '0' || p[1] > '9')
+ return 0;
+ n = p[1]-'0';
+ c = (c<<8) | __DC(t, n);
+ p += 2;
+ }
+ return c;
+}
+
+int
+chantodepth(u32int c)
+{
+ int n;
+
+ for(n=0; c; c>>=8){
+ if(TYPE(c) >= NChan || NBITS(c) > 8 || NBITS(c) <= 0)
+ return 0;
+ n += NBITS(c);
+ }
+ if(n==0 || (n>8 && n%8) || (n<8 && 8%n))
+ return 0;
+ return n;
+}
diff --git a/src/libdraw/creadimage.c b/src/libdraw/creadimage.c
new file mode 100644
index 00000000..99c42752
--- /dev/null
+++ b/src/libdraw/creadimage.c
@@ -0,0 +1,113 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Image *
+creadimage(Display *d, int fd, int dolock)
+{
+ char hdr[5*12+1];
+ Rectangle r;
+ int m, nb, miny, maxy, new, ldepth, ncblock;
+ uchar *buf, *a;
+ Image *i;
+ u32int chan;
+
+ if(readn(fd, hdr, 5*12) != 5*12)
+ 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;
+ }
+
+ if(dolock)
+ lockdisplay(d);
+ i = allocimage(d, r, chan, 0, 0);
+ if(dolock)
+ unlockdisplay(d);
+ 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){
+ Errout:
+ if(dolock)
+ lockdisplay(d);
+ Erroutlock:
+ freeimage(i);
+ if(dolock)
+ unlockdisplay(d);
+ free(buf);
+ return nil;
+ }
+ maxy = atoi(hdr+0*12);
+ nb = atoi(hdr+1*12);
+ if(maxy<=miny || r.max.y<maxy){
+ werrstr("creadimage: bad maxy %d", maxy);
+ goto Errout;
+ }
+ if(nb<=0 || ncblock<nb){
+ werrstr("creadimage: bad count %d", nb);
+ goto Errout;
+ }
+ if(readn(fd, buf, nb)!=nb)
+ goto Errout;
+ if(dolock)
+ lockdisplay(d);
+ a = bufimage(i->display, 21+nb);
+ if(a == nil)
+ goto Erroutlock;
+ a[0] = 'Y';
+ BPLONG(a+1, i->id);
+ BPLONG(a+5, r.min.x);
+ BPLONG(a+9, miny);
+ BPLONG(a+13, r.max.x);
+ BPLONG(a+17, maxy);
+ if(!new) /* old image: flip the data bits */
+ _twiddlecompressed(buf, nb);
+ memmove(a+21, buf, nb);
+ if(dolock)
+ unlockdisplay(d);
+ miny = maxy;
+ }
+ free(buf);
+ return i;
+}
diff --git a/src/libdraw/cursor.h b/src/libdraw/cursor.h
new file mode 100644
index 00000000..105cd0ef
--- /dev/null
+++ b/src/libdraw/cursor.h
@@ -0,0 +1,7 @@
+typedef struct Cursor Cursor;
+struct Cursor
+{
+ Point offset;
+ uchar clr[2*16];
+ uchar set[2*16];
+};
diff --git a/src/libdraw/devdraw.c b/src/libdraw/devdraw.c
new file mode 100644
index 00000000..b0036385
--- /dev/null
+++ b/src/libdraw/devdraw.c
@@ -0,0 +1,1587 @@
+/*
+ * /dev/draw simulator -- handles the messages prepared by the draw library.
+ * Includes all the memlayer code even though most programs don't use it.
+ * This whole approach is overkill, but cpu is cheap and it keeps things simple.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+#define NHASH (1<<5)
+#define HASHMASK (NHASH-1)
+
+extern void flushmemscreen(Rectangle);
+
+typedef struct Client Client;
+typedef struct Draw Draw;
+typedef struct DImage DImage;
+typedef struct DScreen DScreen;
+typedef struct CScreen CScreen;
+typedef struct FChar FChar;
+typedef struct Refresh Refresh;
+typedef struct Refx Refx;
+typedef struct DName DName;
+
+struct Draw
+{
+ QLock lk;
+ int clientid;
+ int nclient;
+ Client* client[1];
+ int nname;
+ DName* name;
+ int vers;
+ int softscreen;
+};
+
+struct Client
+{
+ /*Ref r;*/
+ DImage* dimage[NHASH];
+ CScreen* cscreen;
+ Refresh* refresh;
+ Rendez refrend;
+ uchar* readdata;
+ int nreaddata;
+ int busy;
+ int clientid;
+ int slot;
+ int refreshme;
+ int infoid;
+ int op;
+};
+
+struct Refresh
+{
+ DImage* dimage;
+ Rectangle r;
+ Refresh* next;
+};
+
+struct Refx
+{
+ Client* client;
+ DImage* dimage;
+};
+
+struct DName
+{
+ char *name;
+ Client *client;
+ DImage* dimage;
+ int vers;
+};
+
+struct FChar
+{
+ int minx; /* left edge of bits */
+ int maxx; /* right edge of bits */
+ uchar miny; /* first non-zero scan-line */
+ uchar maxy; /* last non-zero scan-line + 1 */
+ schar left; /* offset of baseline */
+ uchar width; /* width of baseline */
+};
+
+/*
+ * Reference counts in DImages:
+ * one per open by original client
+ * one per screen image or fill
+ * one per image derived from this one by name
+ */
+struct DImage
+{
+ int id;
+ int ref;
+ char *name;
+ int vers;
+ Memimage* image;
+ int ascent;
+ int nfchar;
+ FChar* fchar;
+ DScreen* dscreen; /* 0 if not a window */
+ DImage* fromname; /* image this one is derived from, by name */
+ DImage* next;
+};
+
+struct CScreen
+{
+ DScreen* dscreen;
+ CScreen* next;
+};
+
+struct DScreen
+{
+ int id;
+ int public;
+ int ref;
+ DImage *dimage;
+ DImage *dfill;
+ Memscreen* screen;
+ Client* owner;
+ DScreen* next;
+};
+
+static Draw sdraw;
+static Client *client0;
+static Memimage *screenimage;
+static Rectangle flushrect;
+static int waste;
+static DScreen* dscreen;
+static int drawuninstall(Client*, int);
+static Memimage* drawinstall(Client*, int, Memimage*, DScreen*);
+static void drawfreedimage(DImage*);
+
+void
+_initdisplaymemimage(Display *d, Memimage *m)
+{
+ screenimage = m;
+ client0 = mallocz(sizeof(Client), 1);
+ if(client0 == nil){
+ fprint(2, "initdraw: allocating client0: out of memory");
+ abort();
+ }
+ client0->slot = 0;
+ client0->clientid = ++sdraw.clientid;
+ client0->op = SoverD;
+ sdraw.client[0] = client0;
+ sdraw.nclient = 1;
+}
+
+void
+_drawreplacescreenimage(Memimage *m)
+{
+ /*
+ * Replace the screen image because the screen
+ * was resized.
+ *
+ * In theory there should only be one reference
+ * to the current screen image, and that's through
+ * client0's image 0, installed a few lines above.
+ * Once the client drops the image, the underlying backing
+ * store freed properly. The client is being notified
+ * about the resize through external means, so all we
+ * need to do is this assignment.
+ */
+ screenimage = m;
+}
+
+static
+void
+drawrefreshscreen(DImage *l, Client *client)
+{
+ while(l != nil && l->dscreen == nil)
+ l = l->fromname;
+ if(l != nil && l->dscreen->owner != client)
+ l->dscreen->owner->refreshme = 1;
+}
+
+static
+void
+drawrefresh(Memimage *m, Rectangle r, void *v)
+{
+ Refx *x;
+ DImage *d;
+ Client *c;
+ Refresh *ref;
+
+ USED(m);
+
+ if(v == 0)
+ return;
+ x = v;
+ c = x->client;
+ d = x->dimage;
+ for(ref=c->refresh; ref; ref=ref->next)
+ if(ref->dimage == d){
+ combinerect(&ref->r, r);
+ return;
+ }
+ ref = mallocz(sizeof(Refresh), 1);
+ if(ref){
+ ref->dimage = d;
+ ref->r = r;
+ ref->next = c->refresh;
+ c->refresh = ref;
+ }
+}
+
+static void
+addflush(Rectangle r)
+{
+ int abb, ar, anbb;
+ Rectangle nbb;
+
+ if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+ return;
+
+ if(flushrect.min.x >= flushrect.max.x){
+ flushrect = r;
+ waste = 0;
+ return;
+ }
+ nbb = flushrect;
+ combinerect(&nbb, r);
+ ar = Dx(r)*Dy(r);
+ abb = Dx(flushrect)*Dy(flushrect);
+ anbb = Dx(nbb)*Dy(nbb);
+ /*
+ * Area of new waste is area of new bb minus area of old bb,
+ * less the area of the new segment, which we assume is not waste.
+ * This could be negative, but that's OK.
+ */
+ waste += anbb-abb - ar;
+ if(waste < 0)
+ waste = 0;
+ /*
+ * absorb if:
+ * total area is small
+ * waste is less than half total area
+ * rectangles touch
+ */
+ if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
+ flushrect = nbb;
+ return;
+ }
+ /* emit current state */
+ if(flushrect.min.x < flushrect.max.x)
+ flushmemscreen(flushrect);
+ flushrect = r;
+ waste = 0;
+}
+
+static
+void
+dstflush(int dstid, Memimage *dst, Rectangle r)
+{
+ Memlayer *l;
+
+ if(dstid == 0){
+ combinerect(&flushrect, r);
+ return;
+ }
+ /* how can this happen? -rsc, dec 12 2002 */
+ if(dst == 0){
+ print("nil dstflush\n");
+ return;
+ }
+ l = dst->layer;
+ if(l == nil)
+ return;
+ do{
+ if(l->screen->image->data != screenimage->data)
+ return;
+ r = rectaddpt(r, l->delta);
+ l = l->screen->image->layer;
+ }while(l);
+ addflush(r);
+}
+
+static
+void
+drawflush(void)
+{
+ if(flushrect.min.x < flushrect.max.x)
+ flushmemscreen(flushrect);
+ flushrect = Rect(10000, 10000, -10000, -10000);
+}
+
+static
+int
+drawcmp(char *a, char *b, int n)
+{
+ if(strlen(a) != n)
+ return 1;
+ return memcmp(a, b, n);
+}
+
+static
+DName*
+drawlookupname(int n, char *str)
+{
+ DName *name, *ename;
+
+ name = sdraw.name;
+ ename = &name[sdraw.nname];
+ for(; name<ename; name++)
+ if(drawcmp(name->name, str, n) == 0)
+ return name;
+ return 0;
+}
+
+static
+int
+drawgoodname(DImage *d)
+{
+ DName *n;
+
+ /* if window, validate the screen's own images */
+ if(d->dscreen)
+ if(drawgoodname(d->dscreen->dimage) == 0
+ || drawgoodname(d->dscreen->dfill) == 0)
+ return 0;
+ if(d->name == nil)
+ return 1;
+ n = drawlookupname(strlen(d->name), d->name);
+ if(n==nil || n->vers!=d->vers)
+ return 0;
+ return 1;
+}
+
+static
+DImage*
+drawlookup(Client *client, int id, int checkname)
+{
+ DImage *d;
+
+ d = client->dimage[id&HASHMASK];
+ while(d){
+ if(d->id == id){
+ /*
+ * BUG: should error out but too hard.
+ * Return 0 instead.
+ */
+ if(checkname && !drawgoodname(d))
+ return 0;
+ return d;
+ }
+ d = d->next;
+ }
+ return 0;
+}
+
+static
+DScreen*
+drawlookupdscreen(int id)
+{
+ DScreen *s;
+
+ s = dscreen;
+ while(s){
+ if(s->id == id)
+ return s;
+ s = s->next;
+ }
+ return 0;
+}
+
+static
+DScreen*
+drawlookupscreen(Client *client, int id, CScreen **cs)
+{
+ CScreen *s;
+
+ s = client->cscreen;
+ while(s){
+ if(s->dscreen->id == id){
+ *cs = s;
+ return s->dscreen;
+ }
+ s = s->next;
+ }
+ /* caller must check! */
+ return 0;
+}
+
+static
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+ DImage *d;
+
+ d = mallocz(sizeof(DImage), 1);
+ if(d == 0)
+ return 0;
+ d->id = id;
+ d->ref = 1;
+ d->name = 0;
+ d->vers = 0;
+ d->image = i;
+ d->nfchar = 0;
+ d->fchar = 0;
+ d->fromname = 0;
+ d->dscreen = dscreen;
+ d->next = client->dimage[id&HASHMASK];
+ client->dimage[id&HASHMASK] = d;
+ return i;
+}
+
+static
+Memscreen*
+drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
+{
+ Memscreen *s;
+ CScreen *c;
+
+ c = mallocz(sizeof(CScreen), 1);
+ if(dimage && dimage->image && dimage->image->chan == 0){
+ print("bad image %p in drawinstallscreen", dimage->image);
+ abort();
+ }
+
+ if(c == 0)
+ return 0;
+ if(d == 0){
+ d = mallocz(sizeof(DScreen), 1);
+ if(d == 0){
+ free(c);
+ return 0;
+ }
+ s = mallocz(sizeof(Memscreen), 1);
+ if(s == 0){
+ free(c);
+ free(d);
+ return 0;
+ }
+ s->frontmost = 0;
+ s->rearmost = 0;
+ d->dimage = dimage;
+ if(dimage){
+ s->image = dimage->image;
+ dimage->ref++;
+ }
+ d->dfill = dfill;
+ if(dfill){
+ s->fill = dfill->image;
+ dfill->ref++;
+ }
+ d->ref = 0;
+ d->id = id;
+ d->screen = s;
+ d->public = public;
+ d->next = dscreen;
+ d->owner = client;
+ dscreen = d;
+ }
+ c->dscreen = d;
+ d->ref++;
+ c->next = client->cscreen;
+ client->cscreen = c;
+ return d->screen;
+}
+
+static
+void
+drawdelname(DName *name)
+{
+ int i;
+
+ i = name-sdraw.name;
+ memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
+ sdraw.nname--;
+}
+
+static
+void
+drawfreedscreen(DScreen *this)
+{
+ DScreen *ds, *next;
+
+ this->ref--;
+ if(this->ref < 0)
+ print("negative ref in drawfreedscreen\n");
+ if(this->ref > 0)
+ return;
+ ds = dscreen;
+ if(ds == this){
+ dscreen = this->next;
+ goto Found;
+ }
+ while(next = ds->next){ /* assign = */
+ if(next == this){
+ ds->next = this->next;
+ goto Found;
+ }
+ ds = next;
+ }
+ /*
+ * Should signal Enodrawimage, but too hard.
+ */
+ return;
+
+ Found:
+ if(this->dimage)
+ drawfreedimage(this->dimage);
+ if(this->dfill)
+ drawfreedimage(this->dfill);
+ free(this->screen);
+ free(this);
+}
+
+static
+void
+drawfreedimage(DImage *dimage)
+{
+ int i;
+ Memimage *l;
+ DScreen *ds;
+
+ dimage->ref--;
+ if(dimage->ref < 0)
+ print("negative ref in drawfreedimage\n");
+ if(dimage->ref > 0)
+ return;
+
+ /* any names? */
+ for(i=0; i<sdraw.nname; )
+ if(sdraw.name[i].dimage == dimage)
+ drawdelname(sdraw.name+i);
+ else
+ i++;
+ if(dimage->fromname){ /* acquired by name; owned by someone else*/
+ drawfreedimage(dimage->fromname);
+ goto Return;
+ }
+ //if(dimage->image == screenimage) /* don't free the display */
+ // goto Return;
+ ds = dimage->dscreen;
+ if(ds){
+ l = dimage->image;
+ if(l->data == screenimage->data)
+ addflush(l->layer->screenr);
+ if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */
+ free(l->layer->refreshptr);
+ l->layer->refreshptr = nil;
+ if(drawgoodname(dimage))
+ memldelete(l);
+ else
+ memlfree(l);
+ drawfreedscreen(ds);
+ }else
+ freememimage(dimage->image);
+ Return:
+ free(dimage->fchar);
+ free(dimage);
+}
+
+static
+void
+drawuninstallscreen(Client *client, CScreen *this)
+{
+ CScreen *cs, *next;
+
+ cs = client->cscreen;
+ if(cs == this){
+ client->cscreen = this->next;
+ drawfreedscreen(this->dscreen);
+ free(this);
+ return;
+ }
+ while(next = cs->next){ /* assign = */
+ if(next == this){
+ cs->next = this->next;
+ drawfreedscreen(this->dscreen);
+ free(this);
+ return;
+ }
+ cs = next;
+ }
+}
+
+static
+int
+drawuninstall(Client *client, int id)
+{
+ DImage *d, *next;
+
+ d = client->dimage[id&HASHMASK];
+ if(d == 0)
+ return -1;
+ if(d->id == id){
+ client->dimage[id&HASHMASK] = d->next;
+ drawfreedimage(d);
+ return 0;
+ }
+ while(next = d->next){ /* assign = */
+ if(next->id == id){
+ d->next = next->next;
+ drawfreedimage(next);
+ return 0;
+ }
+ d = next;
+ }
+ return -1;
+}
+
+static
+int
+drawaddname(Client *client, DImage *di, int n, char *str, char **err)
+{
+ DName *name, *ename, *new, *t;
+ char *ns;
+
+ name = sdraw.name;
+ ename = &name[sdraw.nname];
+ for(; name<ename; name++)
+ if(drawcmp(name->name, str, n) == 0){
+ *err = "image name in use";
+ return -1;
+ }
+ t = mallocz((sdraw.nname+1)*sizeof(DName), 1);
+ ns = malloc(n+1);
+ if(t == nil || ns == nil){
+ free(t);
+ free(ns);
+ *err = "out of memory";
+ return -1;
+ }
+ memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
+ free(sdraw.name);
+ sdraw.name = t;
+ new = &sdraw.name[sdraw.nname++];
+ new->name = ns;
+ memmove(new->name, str, n);
+ new->name[n] = 0;
+ new->dimage = di;
+ new->client = client;
+ new->vers = ++sdraw.vers;
+ return 0;
+}
+
+static int
+drawclientop(Client *cl)
+{
+ int op;
+
+ op = cl->op;
+ cl->op = SoverD;
+ return op;
+}
+
+static
+Memimage*
+drawimage(Client *client, uchar *a)
+{
+ DImage *d;
+
+ d = drawlookup(client, BGLONG(a), 1);
+ if(d == nil)
+ return nil; /* caller must check! */
+ return d->image;
+}
+
+static
+void
+drawrectangle(Rectangle *r, uchar *a)
+{
+ r->min.x = BGLONG(a+0*4);
+ r->min.y = BGLONG(a+1*4);
+ r->max.x = BGLONG(a+2*4);
+ r->max.y = BGLONG(a+3*4);
+}
+
+static
+void
+drawpoint(Point *p, uchar *a)
+{
+ p->x = BGLONG(a+0*4);
+ p->y = BGLONG(a+1*4);
+}
+
+static
+Point
+drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
+{
+ FChar *fc;
+ Rectangle r;
+ Point sp1;
+
+ fc = &font->fchar[index];
+ r.min.x = p.x+fc->left;
+ r.min.y = p.y-(font->ascent-fc->miny);
+ r.max.x = r.min.x+(fc->maxx-fc->minx);
+ r.max.y = r.min.y+(fc->maxy-fc->miny);
+ sp1.x = sp->x+fc->left;
+ sp1.y = sp->y+fc->miny;
+ memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+ p.x += fc->width;
+ sp->x += fc->width;
+ return p;
+}
+
+static
+uchar*
+drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
+{
+ int b, x;
+
+ if(p >= maxp)
+ return nil;
+ b = *p++;
+ x = b & 0x7F;
+ if(b & 0x80){
+ if(p+1 >= maxp)
+ return nil;
+ x |= *p++ << 7;
+ x |= *p++ << 15;
+ if(x & (1<<22))
+ x |= ~0<<23;
+ }else{
+ if(b & 0x40)
+ x |= ~0<<7;
+ x += oldx;
+ }
+ *newx = x;
+ return p;
+}
+
+int
+_drawmsgread(Display *d, void *a, int n)
+{
+ int inbuf;
+
+ inbuf = d->obufp - d->obuf;
+ if(n > inbuf)
+ n = inbuf;
+ memmove(a, d->obuf, n);
+ inbuf -= n;
+ if(inbuf)
+ memmove(d->obuf, d->obufp-inbuf, inbuf);
+ d->obufp = d->obuf+inbuf;
+ return n;
+}
+
+static void
+drawmsgsquirrel(Display *d, void *a, int n)
+{
+ uchar *ep;
+
+ ep = d->obuf + d->obufsize;
+ if(d->obufp + n > ep)
+ abort();
+ memmove(d->obufp, a, n);
+ d->obufp += n;
+}
+
+int
+_drawmsgwrite(Display *d, void *v, int n)
+{
+ char cbuf[40], *err, ibuf[12*12+1], *s;
+ int c, ci, doflush, dstid, e0, e1, esize, j, m;
+ int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y;
+ uchar *a, refresh, *u;
+ u32int chan, value;
+ Client *client;
+ CScreen *cs;
+ DImage *di, *ddst, *dsrc, *font, *ll;
+ DName *dn;
+ DScreen *dscrn;
+ FChar *fc;
+ Memimage *dst, *i, *l, **lp, *mask, *src;
+ Memscreen *scrn;
+ Point p, *pp, q, sp;
+ Rectangle clipr, r;
+ Refreshfn reffn;
+ Refx *refx;
+
+ d->obufp = d->obuf;
+ a = v;
+ m = 0;
+ oldn = n;
+ client = client0;
+
+ while((n-=m) > 0){
+ a += m;
+//fprint(2, "msgwrite %d(%d)...", n, *a);
+ switch(*a){
+ default:
+//fprint(2, "bad command %d\n", *a);
+ err = "bad draw command";
+ goto error;
+
+ /* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1]
+ R[4*4] clipR[4*4] rrggbbaa[4]
+ */
+ case 'b':
+ m = 1+4+4+1+4+1+4*4+4*4+4;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ scrnid = BGSHORT(a+5);
+ refresh = a[9];
+ chan = BGLONG(a+10);
+ repl = a[14];
+ drawrectangle(&r, a+15);
+ drawrectangle(&clipr, a+31);
+ value = BGLONG(a+47);
+ if(drawlookup(client, dstid, 0))
+ goto Eimageexists;
+ if(scrnid){
+ dscrn = drawlookupscreen(client, scrnid, &cs);
+ if(!dscrn)
+ goto Enodrawscreen;
+ scrn = dscrn->screen;
+ if(repl || chan!=scrn->image->chan){
+ err = "image parameters incompatibile with screen";
+ goto error;
+ }
+ reffn = nil;
+ switch(refresh){
+ case Refbackup:
+ break;
+ case Refnone:
+ reffn = memlnorefresh;
+ break;
+ case Refmesg:
+ reffn = drawrefresh;
+ break;
+ default:
+ err = "unknown refresh method";
+ goto error;
+ }
+ l = memlalloc(scrn, r, reffn, 0, value);
+ if(l == 0)
+ goto Edrawmem;
+ addflush(l->layer->screenr);
+ l->clipr = clipr;
+ rectclip(&l->clipr, r);
+ if(drawinstall(client, dstid, l, dscrn) == 0){
+ memldelete(l);
+ goto Edrawmem;
+ }
+ dscrn->ref++;
+ if(reffn){
+ refx = nil;
+ if(reffn == drawrefresh){
+ refx = mallocz(sizeof(Refx), 1);
+ if(refx == 0){
+ if(drawuninstall(client, dstid) < 0)
+ goto Enodrawimage;
+ goto Edrawmem;
+ }
+ refx->client = client;
+ refx->dimage = drawlookup(client, dstid, 1);
+ }
+ memlsetrefresh(l, reffn, refx);
+ }
+ continue;
+ }
+ i = allocmemimage(r, chan);
+ if(i == 0)
+ goto Edrawmem;
+ if(repl)
+ i->flags |= Frepl;
+ i->clipr = clipr;
+ if(!repl)
+ rectclip(&i->clipr, r);
+ if(drawinstall(client, dstid, i, 0) == 0){
+ freememimage(i);
+ goto Edrawmem;
+ }
+ memfillcolor(i, value);
+ continue;
+
+ /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
+ case 'A':
+ m = 1+4+4+4+1;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ if(dstid == 0)
+ goto Ebadarg;
+ if(drawlookupdscreen(dstid))
+ goto Escreenexists;
+ ddst = drawlookup(client, BGLONG(a+5), 1);
+ dsrc = drawlookup(client, BGLONG(a+9), 1);
+ if(ddst==0 || dsrc==0)
+ goto Enodrawimage;
+ if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
+ goto Edrawmem;
+ continue;
+
+ /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
+ case 'c':
+ m = 1+4+1+4*4;
+ if(n < m)
+ goto Eshortdraw;
+ ddst = drawlookup(client, BGLONG(a+1), 1);
+ if(ddst == nil)
+ goto Enodrawimage;
+ if(ddst->name){
+ err = "can't change repl/clipr of shared image";
+ goto error;
+ }
+ dst = ddst->image;
+ if(a[5])
+ dst->flags |= Frepl;
+ drawrectangle(&dst->clipr, a+6);
+ continue;
+
+ /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
+ case 'd':
+ m = 1+4+4+4+4*4+2*4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+ dst = drawimage(client, a+1);
+ dstid = BGLONG(a+1);
+ src = drawimage(client, a+5);
+ mask = drawimage(client, a+9);
+ if(!dst || !src || !mask)
+ goto Enodrawimage;
+ drawrectangle(&r, a+13);
+ drawpoint(&p, a+29);
+ drawpoint(&q, a+37);
+ op = drawclientop(client);
+ memdraw(dst, r, src, p, mask, q, op);
+ dstflush(dstid, dst, r);
+ continue;
+
+ /* toggle debugging: 'D' val[1] */
+ case 'D':
+ m = 1+1;
+ if(n < m)
+ goto Eshortdraw;
+ drawdebug = a[1];
+ continue;
+
+ /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
+ case 'e':
+ case 'E':
+ m = 1+4+4+2*4+4+4+4+2*4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+ dst = drawimage(client, a+1);
+ dstid = BGLONG(a+1);
+ src = drawimage(client, a+5);
+ if(!dst || !src)
+ goto Enodrawimage;
+ drawpoint(&p, a+9);
+ e0 = BGLONG(a+17);
+ e1 = BGLONG(a+21);
+ if(e0<0 || e1<0){
+ err = "invalid ellipse semidiameter";
+ goto error;
+ }
+ j = BGLONG(a+25);
+ if(j < 0){
+ err = "negative ellipse thickness";
+ goto error;
+ }
+
+ drawpoint(&sp, a+29);
+ c = j;
+ if(*a == 'E')
+ c = -1;
+ ox = BGLONG(a+37);
+ oy = BGLONG(a+41);
+ op = drawclientop(client);
+ /* high bit indicates arc angles are present */
+ if(ox & (1<<31)){
+ if((ox & (1<<30)) == 0)
+ ox &= ~(1<<31);
+ memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
+ }else
+ memellipse(dst, p, e0, e1, c, src, sp, op);
+ dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
+ continue;
+
+ /* free: 'f' id[4] */
+ case 'f':
+ m = 1+4;
+ if(n < m)
+ goto Eshortdraw;
+ ll = drawlookup(client, BGLONG(a+1), 0);
+ if(ll && ll->dscreen && ll->dscreen->owner != client)
+ ll->dscreen->owner->refreshme = 1;
+ if(drawuninstall(client, BGLONG(a+1)) < 0)
+ goto Enodrawimage;
+ continue;
+
+ /* free screen: 'F' id[4] */
+ case 'F':
+ m = 1+4;
+ if(n < m)
+ goto Eshortdraw;
+ if(!drawlookupscreen(client, BGLONG(a+1), &cs))
+ goto Enodrawscreen;
+ drawuninstallscreen(client, cs);
+ continue;
+
+ /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
+ case 'i':
+ m = 1+4+4+1;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ if(dstid == 0){
+ err = "can't use display as font";
+ goto error;
+ }
+ font = drawlookup(client, dstid, 1);
+ if(font == 0)
+ goto Enodrawimage;
+ if(font->image->layer){
+ err = "can't use window as font";
+ goto error;
+ }
+ ni = BGLONG(a+5);
+ if(ni<=0 || ni>4096){
+ err = "bad font size (4096 chars max)";
+ goto error;
+ }
+ free(font->fchar); /* should we complain if non-zero? */
+ font->fchar = mallocz(ni*sizeof(FChar), 1);
+ if(font->fchar == 0){
+ err = "no memory for font";
+ goto error;
+ }
+ memset(font->fchar, 0, ni*sizeof(FChar));
+ font->nfchar = ni;
+ font->ascent = a[9];
+ continue;
+
+ /* set image 0 to screen image */
+ case 'J':
+ m = 1;
+ if(n < m)
+ goto Eshortdraw;
+ drawinstall(client, 0, screenimage, 0);
+ client->infoid = 0;
+ continue;
+
+ /* get image info: 'I' */
+ case 'I':
+ m = 1;
+ if(n < m)
+ goto Eshortdraw;
+ if(client->infoid < 0)
+ goto Enodrawimage;
+ if(client->infoid == 0){
+ i = screenimage;
+ if(i == nil)
+ goto Enodrawimage;
+ }else{
+ di = drawlookup(client, client->infoid, 1);
+ if(di == nil)
+ goto Enodrawimage;
+ i = di->image;
+ }
+ ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d"
+ " %11d %11d %11d %11d ",
+ client->clientid,
+ client->infoid,
+ chantostr(cbuf, i->chan),
+ (i->flags&Frepl)==Frepl,
+ i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
+ i->clipr.min.x, i->clipr.min.y,
+ i->clipr.max.x, i->clipr.max.y);
+ drawmsgsquirrel(d, ibuf, ni);
+ client->infoid = -1;
+ continue;
+
+ /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
+ case 'l':
+ m = 1+4+4+2+4*4+2*4+1+1;
+ if(n < m)
+ goto Eshortdraw;
+ font = drawlookup(client, BGLONG(a+1), 1);
+ if(font == 0)
+ goto Enodrawimage;
+ if(font->nfchar == 0)
+ goto Enotfont;
+ src = drawimage(client, a+5);
+ if(!src)
+ goto Enodrawimage;
+ ci = BGSHORT(a+9);
+ if(ci >= font->nfchar)
+ goto Eindex;
+ drawrectangle(&r, a+11);
+ drawpoint(&p, a+27);
+ memdraw(font->image, r, src, p, memopaque, p, S);
+ fc = &font->fchar[ci];
+ fc->minx = r.min.x;
+ fc->maxx = r.max.x;
+ fc->miny = r.min.y;
+ fc->maxy = r.max.y;
+ fc->left = a[35];
+ fc->width = a[36];
+ continue;
+
+ /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
+ case 'L':
+ m = 1+4+2*4+2*4+4+4+4+4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+ dst = drawimage(client, a+1);
+ dstid = BGLONG(a+1);
+ drawpoint(&p, a+5);
+ drawpoint(&q, a+13);
+ e0 = BGLONG(a+21);
+ e1 = BGLONG(a+25);
+ j = BGLONG(a+29);
+ if(j < 0){
+ err = "negative line width";
+ goto error;
+ }
+ src = drawimage(client, a+33);
+ if(!dst || !src)
+ goto Enodrawimage;
+ drawpoint(&sp, a+37);
+ op = drawclientop(client);
+ memline(dst, p, q, e0, e1, j, src, sp, op);
+ /* avoid memlinebbox if possible */
+ if(dstid==0 || dst->layer!=nil){
+ /* BUG: this is terribly inefficient: update maximal containing rect*/
+ r = memlinebbox(p, q, e0, e1, j);
+ dstflush(dstid, dst, insetrect(r, -(1+1+j)));
+ }
+ continue;
+
+ /* create image mask: 'm' newid[4] id[4] */
+/*
+ *
+ case 'm':
+ m = 4+4;
+ if(n < m)
+ goto Eshortdraw;
+ break;
+ *
+ */
+
+ /* attach to a named image: 'n' dstid[4] j[1] name[j] */
+ case 'n':
+ m = 1+4+1;
+ if(n < m)
+ goto Eshortdraw;
+ j = a[5];
+ if(j == 0) /* give me a non-empty name please */
+ goto Eshortdraw;
+ m += j;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ if(drawlookup(client, dstid, 0))
+ goto Eimageexists;
+ dn = drawlookupname(j, (char*)a+6);
+ if(dn == nil)
+ goto Enoname;
+ s = malloc(j+1);
+ if(s == nil)
+ goto Enomem;
+ if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
+ goto Edrawmem;
+ di = drawlookup(client, dstid, 0);
+ if(di == 0)
+ goto Eoldname;
+ di->vers = dn->vers;
+ di->name = s;
+ di->fromname = dn->dimage;
+ di->fromname->ref++;
+ memmove(di->name, a+6, j);
+ di->name[j] = 0;
+ client->infoid = dstid;
+ continue;
+
+ /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
+ case 'N':
+ m = 1+4+1+1;
+ if(n < m)
+ goto Eshortdraw;
+ c = a[5];
+ j = a[6];
+ if(j == 0) /* give me a non-empty name please */
+ goto Eshortdraw;
+ m += j;
+ if(n < m)
+ goto Eshortdraw;
+ di = drawlookup(client, BGLONG(a+1), 0);
+ if(di == 0)
+ goto Enodrawimage;
+ if(di->name)
+ goto Enamed;
+ if(c)
+ if(drawaddname(client, di, j, (char*)a+7, &err) < 0)
+ goto error;
+ else{
+ dn = drawlookupname(j, (char*)a+7);
+ if(dn == nil)
+ goto Enoname;
+ if(dn->dimage != di)
+ goto Ewrongname;
+ drawdelname(dn);
+ }
+ continue;
+
+ /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
+ case 'o':
+ m = 1+4+2*4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+ dst = drawimage(client, a+1);
+ if(!dst)
+ goto Enodrawimage;
+ if(dst->layer){
+ drawpoint(&p, a+5);
+ drawpoint(&q, a+13);
+ r = dst->layer->screenr;
+ ni = memlorigin(dst, p, q);
+ if(ni < 0){
+ err = "image origin failed";
+ goto error;
+ }
+ if(ni > 0){
+ addflush(r);
+ addflush(dst->layer->screenr);
+ ll = drawlookup(client, BGLONG(a+1), 1);
+ drawrefreshscreen(ll, client);
+ }
+ }
+ continue;
+
+ /* set compositing operator for next draw operation: 'O' op */
+ case 'O':
+ m = 1+1;
+ if(n < m)
+ goto Eshortdraw;
+ client->op = a[1];
+ continue;
+
+ /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+ /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
+ case 'p':
+ case 'P':
+ m = 1+4+2+4+4+4+4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ dst = drawimage(client, a+1);
+ ni = BGSHORT(a+5);
+ if(ni < 0){
+ err = "negative cout in polygon";
+ goto error;
+ }
+ e0 = BGLONG(a+7);
+ e1 = BGLONG(a+11);
+ j = 0;
+ if(*a == 'p'){
+ j = BGLONG(a+15);
+ if(j < 0){
+ err = "negative polygon line width";
+ goto error;
+ }
+ }
+ src = drawimage(client, a+19);
+ if(!dst || !src)
+ goto Enodrawimage;
+ drawpoint(&sp, a+23);
+ drawpoint(&p, a+31);
+ ni++;
+ pp = mallocz(ni*sizeof(Point), 1);
+ if(pp == nil)
+ goto Enomem;
+ doflush = 0;
+ if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+ doflush = 1; /* simplify test in loop */
+ ox = oy = 0;
+ esize = 0;
+ u = a+m;
+ for(y=0; y<ni; y++){
+ q = p;
+ oesize = esize;
+ u = drawcoord(u, a+n, ox, &p.x);
+ if(!u)
+ goto Eshortdraw;
+ u = drawcoord(u, a+n, oy, &p.y);
+ if(!u)
+ goto Eshortdraw;
+ ox = p.x;
+ oy = p.y;
+ if(doflush){
+ esize = j;
+ if(*a == 'p'){
+ if(y == 0){
+ c = memlineendsize(e0);
+ if(c > esize)
+ esize = c;
+ }
+ if(y == ni-1){
+ c = memlineendsize(e1);
+ if(c > esize)
+ esize = c;
+ }
+ }
+ if(*a=='P' && e0!=1 && e0 !=~0)
+ r = dst->clipr;
+ else if(y > 0){
+ r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
+ combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+ }
+ if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */
+ dstflush(dstid, dst, r);
+ }
+ pp[y] = p;
+ }
+ if(y == 1)
+ dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
+ op = drawclientop(client);
+ if(*a == 'p')
+ mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
+ else
+ memfillpoly(dst, pp, ni, e0, src, sp, op);
+ free(pp);
+ m = u-a;
+ continue;
+
+ /* read: 'r' id[4] R[4*4] */
+ case 'r':
+ m = 1+4+4*4;
+ if(n < m)
+ goto Eshortdraw;
+ i = drawimage(client, a+1);
+ if(!i)
+ goto Enodrawimage;
+ drawrectangle(&r, a+5);
+ if(!rectinrect(r, i->r))
+ goto Ereadoutside;
+ c = bytesperline(r, i->depth);
+ c *= Dy(r);
+ free(client->readdata);
+ client->readdata = mallocz(c, 0);
+ if(client->readdata == nil){
+ err = "readimage malloc failed";
+ goto error;
+ }
+ client->nreaddata = memunload(i, r, client->readdata, c);
+ if(client->nreaddata < 0){
+ free(client->readdata);
+ client->readdata = nil;
+ err = "bad readimage call";
+ goto error;
+ }
+ continue;
+
+ /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
+ /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
+ case 's':
+ case 'x':
+ m = 1+4+4+4+2*4+4*4+2*4+2;
+ if(*a == 'x')
+ m += 4+2*4;
+ if(n < m)
+ goto Eshortdraw;
+
+ dst = drawimage(client, a+1);
+ dstid = BGLONG(a+1);
+ src = drawimage(client, a+5);
+ if(!dst || !src)
+ goto Enodrawimage;
+ font = drawlookup(client, BGLONG(a+9), 1);
+ if(font == 0)
+ goto Enodrawimage;
+ if(font->nfchar == 0)
+ goto Enotfont;
+ drawpoint(&p, a+13);
+ drawrectangle(&r, a+21);
+ drawpoint(&sp, a+37);
+ ni = BGSHORT(a+45);
+ u = a+m;
+ m += ni*2;
+ if(n < m)
+ goto Eshortdraw;
+ clipr = dst->clipr;
+ dst->clipr = r;
+ op = drawclientop(client);
+ if(*a == 'x'){
+ /* paint background */
+ l = drawimage(client, a+47);
+ if(!l)
+ goto Enodrawimage;
+ drawpoint(&q, a+51);
+ r.min.x = p.x;
+ r.min.y = p.y-font->ascent;
+ r.max.x = p.x;
+ r.max.y = r.min.y+Dy(font->image->r);
+ j = ni;
+ while(--j >= 0){
+ ci = BGSHORT(u);
+ if(ci<0 || ci>=font->nfchar){
+ dst->clipr = clipr;
+ goto Eindex;
+ }
+ r.max.x += font->fchar[ci].width;
+ u += 2;
+ }
+ memdraw(dst, r, l, q, memopaque, ZP, op);
+ u -= 2*ni;
+ }
+ q = p;
+ while(--ni >= 0){
+ ci = BGSHORT(u);
+ if(ci<0 || ci>=font->nfchar){
+ dst->clipr = clipr;
+ goto Eindex;
+ }
+ q = drawchar(dst, q, src, &sp, font, ci, op);
+ u += 2;
+ }
+ dst->clipr = clipr;
+ p.y -= font->ascent;
+ dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
+ continue;
+
+ /* use public screen: 'S' id[4] chan[4] */
+ case 'S':
+ m = 1+4+4;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ if(dstid == 0)
+ goto Ebadarg;
+ dscrn = drawlookupdscreen(dstid);
+ if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
+ goto Enodrawscreen;
+ if(dscrn->screen->image->chan != BGLONG(a+5)){
+ err = "inconsistent chan";
+ goto error;
+ }
+ if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
+ goto Edrawmem;
+ continue;
+
+ /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
+ case 't':
+ m = 1+1+2;
+ if(n < m)
+ goto Eshortdraw;
+ nw = BGSHORT(a+2);
+ if(nw < 0)
+ goto Ebadarg;
+ if(nw == 0)
+ continue;
+ m += nw*4;
+ if(n < m)
+ goto Eshortdraw;
+ lp = mallocz(nw*sizeof(Memimage*), 1);
+ if(lp == 0)
+ goto Enomem;
+ for(j=0; j<nw; j++){
+ lp[j] = drawimage(client, a+1+1+2+j*4);
+ if(lp[j] == nil){
+ free(lp);
+ goto Enodrawimage;
+ }
+ }
+ if(lp[0]->layer == 0){
+ err = "images are not windows";
+ free(lp);
+ goto error;
+ }
+ for(j=1; j<nw; j++)
+ if(lp[j]->layer->screen != lp[0]->layer->screen){
+ err = "images not on same screen";
+ free(lp);
+ goto error;
+ }
+ if(a[1])
+ memltofrontn(lp, nw);
+ else
+ memltorearn(lp, nw);
+ if(lp[0]->layer->screen->image->data == screenimage->data)
+ for(j=0; j<nw; j++)
+ addflush(lp[j]->layer->screenr);
+ free(lp);
+ ll = drawlookup(client, BGLONG(a+1+1+2), 1);
+ drawrefreshscreen(ll, client);
+ continue;
+
+ /* visible: 'v' */
+ case 'v':
+ m = 1;
+ drawflush();
+ continue;
+
+ /* write: 'y' id[4] R[4*4] data[x*1] */
+ /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
+ case 'y':
+ case 'Y':
+ m = 1+4+4*4;
+ if(n < m)
+ goto Eshortdraw;
+ dstid = BGLONG(a+1);
+ dst = drawimage(client, a+1);
+ if(!dst)
+ goto Enodrawimage;
+ drawrectangle(&r, a+5);
+ if(!rectinrect(r, dst->r))
+ goto Ewriteoutside;
+ y = memload(dst, r, a+m, n-m, *a=='Y');
+ if(y < 0){
+ err = "bad writeimage call";
+ goto error;
+ }
+ dstflush(dstid, dst, r);
+ m += y;
+ continue;
+ }
+ }
+ return oldn - n;
+
+Enodrawimage:
+ err = "unknown id for draw image";
+ goto error;
+Enodrawscreen:
+ err = "unknown id for draw screen";
+ goto error;
+Eshortdraw:
+ err = "short draw message";
+ goto error;
+Eshortread:
+ err = "draw read too short";
+ goto error;
+Eimageexists:
+ err = "image id in use";
+ goto error;
+Escreenexists:
+ err = "screen id in use";
+ goto error;
+Edrawmem:
+ err = "image memory allocation failed";
+ goto error;
+Ereadoutside:
+ err = "readimage outside image";
+ goto error;
+Ewriteoutside:
+ err = "writeimage outside image";
+ goto error;
+Enotfont:
+ err = "image not a font";
+ goto error;
+Eindex:
+ err = "character index out of range";
+ goto error;
+Enoclient:
+ err = "no such draw client";
+ goto error;
+Edepth:
+ err = "image has bad depth";
+ goto error;
+Enameused:
+ err = "image name in use";
+ goto error;
+Enoname:
+ err = "no image with that name";
+ goto error;
+Eoldname:
+ err = "named image no longer valid";
+ goto error;
+Enamed:
+ err = "image already has name";
+ goto error;
+Ewrongname:
+ err = "wrong name for image";
+ goto error;
+Enomem:
+ err = "out of memory";
+ goto error;
+Ebadarg:
+ err = "bad argument in draw message";
+ goto error;
+
+error:
+ drawerror(display, err);
+ return -1;
+}
+
+
diff --git a/src/libdraw/draw.h b/src/libdraw/draw.h
new file mode 100644
index 00000000..0f9ba63a
--- /dev/null
+++ b/src/libdraw/draw.h
@@ -0,0 +1,520 @@
+typedef struct Cachefont Cachefont;
+typedef struct Cacheinfo Cacheinfo;
+typedef struct Cachesubf Cachesubf;
+typedef struct Display Display;
+typedef struct Font Font;
+typedef struct Fontchar Fontchar;
+typedef struct Image Image;
+typedef struct Mouse Mouse;
+typedef struct Point Point;
+typedef struct Rectangle Rectangle;
+typedef struct RGB RGB;
+typedef struct Screen Screen;
+typedef struct Subfont Subfont;
+
+extern int Rfmt(Fmt*);
+extern int Pfmt(Fmt*);
+
+enum
+{
+ DOpaque = 0xFFFFFFFF,
+ DTransparent = 0x00000000, /* only useful for allocimage, memfillcolor */
+ DBlack = 0x000000FF,
+ DWhite = 0xFFFFFFFF,
+ DRed = 0xFF0000FF,
+ DGreen = 0x00FF00FF,
+ DBlue = 0x0000FFFF,
+ DCyan = 0x00FFFFFF,
+ DMagenta = 0xFF00FFFF,
+ DYellow = 0xFFFF00FF,
+ DPaleyellow = 0xFFFFAAFF,
+ DDarkyellow = 0xEEEE9EFF,
+ DDarkgreen = 0x448844FF,
+ DPalegreen = 0xAAFFAAFF,
+ DMedgreen = 0x88CC88FF,
+ DDarkblue = 0x000055FF,
+ DPalebluegreen= 0xAAFFFFFF,
+ DPaleblue = 0x0000BBFF,
+ DBluegreen = 0x008888FF,
+ DGreygreen = 0x55AAAAFF,
+ DPalegreygreen = 0x9EEEEEFF,
+ DYellowgreen = 0x99994CFF,
+ DMedblue = 0x000099FF,
+ DGreyblue = 0x005DBBFF,
+ DPalegreyblue = 0x4993DDFF,
+ DPurpleblue = 0x8888CCFF,
+
+ DNotacolor = 0xFFFFFF00,
+ DNofill = DNotacolor,
+
+};
+
+enum
+{
+ Displaybufsize = 8000,
+ ICOSSCALE = 1024,
+ Borderwidth = 4,
+};
+
+enum
+{
+ /* refresh methods */
+ Refbackup = 0,
+ Refnone = 1,
+ Refmesg = 2
+};
+#define NOREFRESH ((void*)-1)
+
+enum
+{
+ /* line ends */
+ Endsquare = 0,
+ Enddisc = 1,
+ Endarrow = 2,
+ Endmask = 0x1F
+};
+
+#define ARROW(a, b, c) (Endarrow|((a)<<5)|((b)<<14)|((c)<<23))
+
+typedef enum
+{
+ /* Porter-Duff compositing operators */
+ Clear = 0,
+
+ SinD = 8,
+ DinS = 4,
+ SoutD = 2,
+ DoutS = 1,
+
+ S = SinD|SoutD,
+ SoverD = SinD|SoutD|DoutS,
+ SatopD = SinD|DoutS,
+ SxorD = SoutD|DoutS,
+
+ D = DinS|DoutS,
+ DoverS = DinS|DoutS|SoutD,
+ DatopS = DinS|SoutD,
+ DxorS = DoutS|SoutD, /* == SxorD */
+
+ Ncomp = 12,
+} Drawop;
+
+/*
+ * image channel descriptors
+ */
+enum {
+ CRed = 0,
+ CGreen,
+ CBlue,
+ CGrey,
+ CAlpha,
+ CMap,
+ CIgnore,
+ NChan,
+};
+
+#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15))
+#define CHAN1(a,b) __DC(a,b)
+#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d)))
+#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f)))
+#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h)))
+
+#define NBITS(c) ((c)&15)
+#define TYPE(c) (((c)>>4)&15)
+
+enum {
+ GREY1 = CHAN1(CGrey, 1),
+ GREY2 = CHAN1(CGrey, 2),
+ GREY4 = CHAN1(CGrey, 4),
+ GREY8 = CHAN1(CGrey, 8),
+ CMAP8 = CHAN1(CMap, 8),
+ RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5),
+ RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5),
+ RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8),
+ BGR24 = CHAN3(CBlue, 8, CGreen, 8, CRed, 8),
+ RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8),
+ ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */
+ XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8),
+ XBGR32 = CHAN4(CIgnore, 8, CBlue, 8, CGreen, 8, CRed, 8),
+};
+
+extern char* chantostr(char*, u32int);
+extern u32int strtochan(char*);
+extern int chantodepth(u32int);
+
+struct Point
+{
+ int x;
+ int y;
+};
+
+struct Rectangle
+{
+ Point min;
+ Point max;
+};
+
+typedef void (*Reffn)(Image*, Rectangle, void*);
+
+struct Screen
+{
+ Display *display; /* display holding data */
+ int id; /* id of system-held Screen */
+ Image *image; /* unused; for reference only */
+ Image *fill; /* color to paint behind windows */
+};
+
+struct Display
+{
+ QLock qlock;
+ int locking; /*program is using lockdisplay */
+ int dirno;
+ int imageid;
+ int local;
+ void (*error)(Display*, char*);
+ char *devdir;
+ char *windir;
+ char oldlabel[64];
+ u32int dataqid;
+ Image *image;
+ Image *white;
+ Image *black;
+ Image *opaque;
+ Image *transparent;
+ uchar *buf;
+ int bufsize;
+ uchar *bufp;
+ uchar *obuf;
+ int obufsize;
+ uchar *obufp;
+ Font *defaultfont;
+ Subfont *defaultsubfont;
+ Image *windows;
+ Image *screenimage;
+ int _isnewdisplay;
+};
+
+struct Image
+{
+ Display *display; /* display holding data */
+ int id; /* id of system-held Image */
+ Rectangle r; /* rectangle in data area, local coords */
+ Rectangle clipr; /* clipping region */
+ int depth; /* number of bits per pixel */
+ u32int chan;
+ int repl; /* flag: data replicates to tile clipr */
+ Screen *screen; /* 0 if not a window */
+ Image *next; /* next in list of windows */
+};
+
+struct RGB
+{
+ u32int red;
+ u32int green;
+ u32int blue;
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ * i = f->info+c;
+ * draw(b, Rect(p.x+i->left, p.y+i->top,
+ * p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
+ * color, f->bits, Pt(i->x, i->top));
+ * p.x += i->width;
+ * to draw characters in the specified color (itself an Image) in Image b.
+ */
+
+struct Fontchar
+{
+ int x; /* left edge of bits */
+ uchar top; /* first non-zero scan-line */
+ uchar bottom; /* last non-zero scan-line + 1 */
+ char left; /* offset of baseline */
+ uchar width; /* width of baseline */
+};
+
+struct Subfont
+{
+ char *name;
+ short n; /* number of chars in font */
+ uchar height; /* height of image */
+ char ascent; /* top of image to baseline */
+ Fontchar *info; /* n+1 character descriptors */
+ Image *bits; /* of font */
+ int ref;
+};
+
+enum
+{
+ /* starting values */
+ LOG2NFCACHE = 6,
+ NFCACHE = (1<<LOG2NFCACHE), /* #chars cached */
+ NFLOOK = 5, /* #chars to scan in cache */
+ NFSUBF = 2, /* #subfonts to cache */
+ /* max value */
+ MAXFCACHE = 1024+NFLOOK, /* upper limit */
+ MAXSUBF = 50, /* generous upper limit */
+ /* deltas */
+ DSUBF = 4,
+ /* expiry ages */
+ SUBFAGE = 10000,
+ CACHEAGE = 10000
+};
+
+struct Cachefont
+{
+ Rune min; /* lowest rune value to be taken from subfont */
+ Rune max; /* highest rune value+1 to be taken from subfont */
+ int offset; /* position in subfont of character at min */
+ char *name; /* stored in font */
+ char *subfontname; /* to access subfont */
+};
+
+struct Cacheinfo
+{
+ ushort x; /* left edge of bits */
+ uchar width; /* width of baseline */
+ schar left; /* offset of baseline */
+ Rune value; /* value of character at this slot in cache */
+ ushort age;
+};
+
+struct Cachesubf
+{
+ u32int age; /* for replacement */
+ Cachefont *cf; /* font info that owns us */
+ Subfont *f; /* attached subfont */
+};
+
+struct Font
+{
+ char *name;
+ Display *display;
+ short height; /* max height of image, interline spacing */
+ short ascent; /* top of image to baseline */
+ short width; /* widest so far; used in caching only */
+ short nsub; /* number of subfonts */
+ u32int age; /* increasing counter; used for LRU */
+ int maxdepth; /* maximum depth of all loaded subfonts */
+ int ncache; /* size of cache */
+ int nsubf; /* size of subfont list */
+ Cacheinfo *cache;
+ Cachesubf *subf;
+ Cachefont **sub; /* as read from file */
+ Image *cacheimage;
+};
+
+#define Dx(r) ((r).max.x-(r).min.x)
+#define Dy(r) ((r).max.y-(r).min.y)
+
+/*
+ * Image management
+ */
+extern Image* _allocimage(Image*, Display*, Rectangle, u32int, int, u32int, int, int);
+extern Image* allocimage(Display*, Rectangle, u32int, int, u32int);
+extern uchar* bufimage(Display*, int);
+extern int bytesperline(Rectangle, int);
+extern void closedisplay(Display*);
+extern void drawerror(Display*, char*);
+extern int flushimage(Display*, int);
+extern int freeimage(Image*);
+extern int _freeimage1(Image*);
+extern int geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
+extern int initdraw(void(*)(Display*, char*), char*, char*);
+extern int newwindow(char*);
+extern int loadimage(Image*, Rectangle, uchar*, int);
+extern int cloadimage(Image*, Rectangle, uchar*, int);
+extern int getwindow(Display*, int);
+extern int gengetwindow(Display*, char*, Image**, Screen**, int);
+extern Image* readimage(Display*, int, int);
+extern Image* creadimage(Display*, int, int);
+extern int unloadimage(Image*, Rectangle, uchar*, int);
+extern int wordsperline(Rectangle, int);
+extern int writeimage(int, Image*, int);
+extern Image* namedimage(Display*, char*);
+extern int nameimage(Image*, char*, int);
+extern Image* allocimagemix(Display*, u32int, u32int);
+
+/*
+ * Colors
+ */
+extern void readcolmap(Display*, RGB*);
+extern void writecolmap(Display*, RGB*);
+extern u32int setalpha(u32int, uchar);
+
+/*
+ * Windows
+ */
+extern Screen* allocscreen(Image*, Image*, int);
+extern Image* _allocwindow(Image*, Screen*, Rectangle, int, u32int);
+extern Image* allocwindow(Screen*, Rectangle, int, u32int);
+extern void bottomnwindows(Image**, int);
+extern void bottomwindow(Image*);
+extern int freescreen(Screen*);
+extern Screen* publicscreen(Display*, int, u32int);
+extern void topnwindows(Image**, int);
+extern void topwindow(Image*);
+extern int originwindow(Image*, Point, Point);
+
+/*
+ * Geometry
+ */
+extern Point Pt(int, int);
+extern Rectangle Rect(int, int, int, int);
+extern Rectangle Rpt(Point, Point);
+extern Point addpt(Point, Point);
+extern Point subpt(Point, Point);
+extern Point divpt(Point, int);
+extern Point mulpt(Point, int);
+extern int eqpt(Point, Point);
+extern int eqrect(Rectangle, Rectangle);
+extern Rectangle insetrect(Rectangle, int);
+extern Rectangle rectaddpt(Rectangle, Point);
+extern Rectangle rectsubpt(Rectangle, Point);
+extern Rectangle canonrect(Rectangle);
+extern int rectXrect(Rectangle, Rectangle);
+extern int rectinrect(Rectangle, Rectangle);
+extern void combinerect(Rectangle*, Rectangle);
+extern int rectclip(Rectangle*, Rectangle);
+extern int ptinrect(Point, Rectangle);
+extern void replclipr(Image*, int, Rectangle);
+extern int drawreplxy(int, int, int); /* used to be drawsetxy */
+extern Point drawrepl(Rectangle, Point);
+extern int rgb2cmap(int, int, int);
+extern int cmap2rgb(int);
+extern int cmap2rgba(int);
+extern void icossin(int, int*, int*);
+extern void icossin2(int, int, int*, int*);
+
+/*
+ * Graphics
+ */
+extern void draw(Image*, Rectangle, Image*, Image*, Point);
+extern void drawop(Image*, Rectangle, Image*, Image*, Point, Drawop);
+extern void gendraw(Image*, Rectangle, Image*, Point, Image*, Point);
+extern void gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, Drawop);
+extern void line(Image*, Point, Point, int, int, int, Image*, Point);
+extern void lineop(Image*, Point, Point, int, int, int, Image*, Point, Drawop);
+extern void poly(Image*, Point*, int, int, int, int, Image*, Point);
+extern void polyop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern void fillpoly(Image*, Point*, int, int, Image*, Point);
+extern void fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern Point string(Image*, Point, Image*, Point, Font*, char*);
+extern Point stringop(Image*, Point, Image*, Point, Font*, char*, Drawop);
+extern Point stringn(Image*, Point, Image*, Point, Font*, char*, int);
+extern Point stringnop(Image*, Point, Image*, Point, Font*, char*, int, Drawop);
+extern Point runestring(Image*, Point, Image*, Point, Font*, Rune*);
+extern Point runestringop(Image*, Point, Image*, Point, Font*, Rune*, Drawop);
+extern Point runestringn(Image*, Point, Image*, Point, Font*, Rune*, int);
+extern Point runestringnop(Image*, Point, Image*, Point, Font*, Rune*, int, Drawop);
+extern Point stringbg(Image*, Point, Image*, Point, Font*, char*, Image*, Point);
+extern Point stringbgop(Image*, Point, Image*, Point, Font*, char*, Image*, Point, Drawop);
+extern Point stringnbg(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point);
+extern Point stringnbgop(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point, Drawop);
+extern Point runestringbg(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point);
+extern Point runestringbgop(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point, Drawop);
+extern Point runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point);
+extern Point runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point, Drawop);
+extern Point _string(Image*, Point, Image*, Point, Font*, char*, Rune*, int, Rectangle, Image*, Point, Drawop);
+extern Point stringsubfont(Image*, Point, Image*, Subfont*, char*);
+extern int bezier(Image*, Point, Point, Point, Point, int, int, int, Image*, Point);
+extern int bezierop(Image*, Point, Point, Point, Point, int, int, int, Image*, Point, Drawop);
+extern int bezspline(Image*, Point*, int, int, int, int, Image*, Point);
+extern int bezsplineop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern int bezsplinepts(Point*, int, Point**);
+extern int fillbezier(Image*, Point, Point, Point, Point, int, Image*, Point);
+extern int fillbezierop(Image*, Point, Point, Point, Point, int, Image*, Point, Drawop);
+extern int fillbezspline(Image*, Point*, int, int, Image*, Point);
+extern int fillbezsplineop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern void ellipse(Image*, Point, int, int, int, Image*, Point);
+extern void ellipseop(Image*, Point, int, int, int, Image*, Point, Drawop);
+extern void fillellipse(Image*, Point, int, int, Image*, Point);
+extern void fillellipseop(Image*, Point, int, int, Image*, Point, Drawop);
+extern void arc(Image*, Point, int, int, int, Image*, Point, int, int);
+extern void arcop(Image*, Point, int, int, int, Image*, Point, int, int, Drawop);
+extern void fillarc(Image*, Point, int, int, Image*, Point, int, int);
+extern void fillarcop(Image*, Point, int, int, Image*, Point, int, int, Drawop);
+extern void border(Image*, Rectangle, int, Image*, Point);
+extern void borderop(Image*, Rectangle, int, Image*, Point, Drawop);
+
+/*
+ * Font management
+ */
+extern Font* openfont(Display*, char*);
+extern Font* buildfont(Display*, char*, char*);
+extern void freefont(Font*);
+extern Font* mkfont(Subfont*, Rune);
+extern int cachechars(Font*, char**, Rune**, ushort*, int, int*, char**);
+extern void agefont(Font*);
+extern Subfont* allocsubfont(char*, int, int, int, Fontchar*, Image*);
+extern Subfont* lookupsubfont(Display*, char*);
+extern void installsubfont(char*, Subfont*);
+extern void uninstallsubfont(Subfont*);
+extern void freesubfont(Subfont*);
+extern Subfont* readsubfont(Display*, char*, int, int);
+extern Subfont* readsubfonti(Display*, char*, int, Image*, int);
+extern int writesubfont(int, Subfont*);
+extern void _unpackinfo(Fontchar*, uchar*, int);
+extern Point stringsize(Font*, char*);
+extern int stringwidth(Font*, char*);
+extern int stringnwidth(Font*, char*, int);
+extern Point runestringsize(Font*, Rune*);
+extern int runestringwidth(Font*, Rune*);
+extern int runestringnwidth(Font*, Rune*, int);
+extern Point strsubfontwidth(Subfont*, char*);
+extern int loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
+extern char* subfontname(char*, char*, int);
+extern Subfont* _getsubfont(Display*, char*);
+extern Subfont* getdefont(Display*);
+extern void lockdisplay(Display*);
+extern void unlockdisplay(Display*);
+extern int drawlsetrefresh(u32int, int, void*, void*);
+
+/*
+ * Predefined
+ */
+extern uchar defontdata[];
+extern int sizeofdefont;
+extern Point ZP;
+extern Rectangle ZR;
+
+/*
+ * Set up by initdraw()
+ */
+extern Display *display;
+extern Font *font;
+extern Image *screen;
+extern Screen *_screen;
+extern int _cursorfd;
+extern int _drawdebug; /* set to 1 to see errors from flushimage */
+extern void _setdrawop(Display*, Drawop);
+extern Display *_initdisplay(void(*)(Display*,char*), char*);
+
+#define BGSHORT(p) (((p)[0]<<0) | ((p)[1]<<8))
+#define BGLONG(p) ((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
+#define BPSHORT(p, v) ((p)[0]=(v), (p)[1]=((v)>>8))
+#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16))
+
+/*
+ * Compressed image file parameters and helper routines
+ */
+#define NMATCH 3 /* shortest match possible */
+#define NRUN (NMATCH+31) /* longest match possible */
+#define NMEM 1024 /* window size */
+#define NDUMP 128 /* maximum length of dump */
+#define NCBLOCK 6000 /* size of compressed blocks */
+extern void _twiddlecompressed(uchar*, int);
+extern int _compblocksize(Rectangle, int);
+
+/* XXX backwards helps; should go */
+extern int log2[];
+extern u32int drawld2chan[];
+extern void drawsetdebug(int);
+
+/*
+ * Port magic.
+ */
+int _drawmsgread(Display*, void*, int);
+int _drawmsgwrite(Display*, void*, int);
diff --git a/src/libdraw/ellipse.c b/src/libdraw/ellipse.c
new file mode 100644
index 00000000..7a063f11
--- /dev/null
+++ b/src/libdraw/ellipse.c
@@ -0,0 +1,82 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static
+void
+doellipse(int cmd, Image *dst, Point *c, int xr, int yr, int thick, Image *src, Point *sp, int alpha, int phi, Drawop op)
+{
+ uchar *a;
+
+ _setdrawop(dst->display, op);
+
+ a = bufimage(dst->display, 1+4+4+2*4+4+4+4+2*4+2*4);
+ if(a == 0){
+ fprint(2, "image ellipse: %r\n");
+ return;
+ }
+ a[0] = cmd;
+ BPLONG(a+1, dst->id);
+ BPLONG(a+5, src->id);
+ BPLONG(a+9, c->x);
+ BPLONG(a+13, c->y);
+ BPLONG(a+17, xr);
+ BPLONG(a+21, yr);
+ BPLONG(a+25, thick);
+ BPLONG(a+29, sp->x);
+ BPLONG(a+33, sp->y);
+ BPLONG(a+37, alpha);
+ BPLONG(a+41, phi);
+}
+
+void
+ellipse(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp)
+{
+ doellipse('e', dst, &c, a, b, thick, src, &sp, 0, 0, SoverD);
+}
+
+void
+ellipseop(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, Drawop op)
+{
+ doellipse('e', dst, &c, a, b, thick, src, &sp, 0, 0, op);
+}
+
+void
+fillellipse(Image *dst, Point c, int a, int b, Image *src, Point sp)
+{
+ doellipse('E', dst, &c, a, b, 0, src, &sp, 0, 0, SoverD);
+}
+
+void
+fillellipseop(Image *dst, Point c, int a, int b, Image *src, Point sp, Drawop op)
+{
+ doellipse('E', dst, &c, a, b, 0, src, &sp, 0, 0, op);
+}
+
+void
+arc(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, int alpha, int phi)
+{
+ alpha |= 1<<31;
+ doellipse('e', dst, &c, a, b, thick, src, &sp, alpha, phi, SoverD);
+}
+
+void
+arcop(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, int alpha, int phi, Drawop op)
+{
+ alpha |= 1<<31;
+ doellipse('e', dst, &c, a, b, thick, src, &sp, alpha, phi, op);
+}
+
+void
+fillarc(Image *dst, Point c, int a, int b, Image *src, Point sp, int alpha, int phi)
+{
+ alpha |= 1<<31;
+ doellipse('E', dst, &c, a, b, 0, src, &sp, alpha, phi, SoverD);
+}
+
+void
+fillarcop(Image *dst, Point c, int a, int b, Image *src, Point sp, int alpha, int phi, Drawop op)
+{
+ alpha |= 1<<31;
+ doellipse('E', dst, &c, a, b, 0, src, &sp, alpha, phi, op);
+}
diff --git a/src/libdraw/emenuhit.c b/src/libdraw/emenuhit.c
new file mode 100644
index 00000000..f596d7a2
--- /dev/null
+++ b/src/libdraw/emenuhit.c
@@ -0,0 +1,271 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+enum
+{
+ Margin = 4, /* outside to text */
+ Border = 2, /* outside to selection boxes */
+ Blackborder = 2, /* width of outlining border */
+ Vspacing = 2, /* extra spacing between lines of text */
+ Maxunscroll = 25, /* maximum #entries before scrolling turns on */
+ Nscroll = 20, /* number entries in scrolling part */
+ Scrollwid = 14, /* width of scroll bar */
+ Gap = 4, /* between text and scroll bar */
+};
+
+static Image *menutxt;
+static Image *back;
+static Image *high;
+static Image *bord;
+static Image *text;
+static Image *htext;
+
+static
+void
+menucolors(void)
+{
+ /* Main tone is greenish, with negative selection */
+ back = allocimagemix(display, DPalegreen, DWhite);
+ high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen); /* dark green */
+ bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen); /* not as dark green */
+ if(back==nil || high==nil || bord==nil)
+ goto Error;
+ text = display->black;
+ htext = back;
+ return;
+
+ Error:
+ freeimage(back);
+ freeimage(high);
+ freeimage(bord);
+ back = display->white;
+ high = display->black;
+ bord = display->black;
+ text = display->black;
+ htext = display->white;
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the rectangle, including its black edge, holding element i.
+ */
+static Rectangle
+menurect(Rectangle r, int i)
+{
+ if(i < 0)
+ return Rect(0, 0, 0, 0);
+ r.min.y += (font->height+Vspacing)*i;
+ r.max.y = r.min.y+font->height+Vspacing;
+ return insetrect(r, Border-Margin);
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the element number containing p.
+ */
+static int
+menusel(Rectangle r, Point p)
+{
+ r = insetrect(r, Margin);
+ if(!ptinrect(p, r))
+ return -1;
+ return (p.y-r.min.y)/(font->height+Vspacing);
+}
+
+static
+void
+paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
+{
+ char *item;
+ Rectangle r;
+ Point pt;
+
+ if(i < 0)
+ return;
+ r = menurect(textr, i);
+ if(restore){
+ draw(screen, r, restore, nil, restore->r.min);
+ return;
+ }
+ if(save)
+ draw(save, save->r, screen, nil, r.min);
+ item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
+ pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
+ pt.y = textr.min.y+i*(font->height+Vspacing);
+ draw(screen, r, highlight? high : back, nil, pt);
+ string(screen, pt, highlight? htext : text, pt, font, item);
+}
+
+/*
+ * menur is a rectangle holding all the highlightable text elements.
+ * track mouse while inside the box, return what's selected when button
+ * is raised, -1 as soon as it leaves box.
+ * invariant: nothing is highlighted on entry or exit.
+ */
+static int
+menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, Image *save)
+{
+ int i;
+
+ paintitem(menu, textr, off, lasti, 1, save, nil);
+ flushimage(display, 1); /* in case display->locking is set */
+ *m = emouse();
+ while(m->buttons & (1<<(but-1))){
+ flushimage(display, 1); /* in case display->locking is set */
+ *m = emouse();
+ i = menusel(textr, m->xy);
+ if(i != -1 && i == lasti)
+ continue;
+ paintitem(menu, textr, off, lasti, 0, nil, save);
+ if(i == -1)
+ return i;
+ lasti = i;
+ paintitem(menu, textr, off, lasti, 1, save, nil);
+ }
+ return lasti;
+}
+
+static void
+menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
+{
+ int i;
+
+ draw(screen, insetrect(textr, Border-Margin), back, nil, ZP);
+ for(i = 0; i<nitemdrawn; i++)
+ paintitem(menu, textr, off, i, 0, nil, nil);
+}
+
+static void
+menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
+{
+ Rectangle r;
+
+ draw(screen, scrollr, back, nil, ZP);
+ r.min.x = scrollr.min.x;
+ r.max.x = scrollr.max.x;
+ r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
+ r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
+ if(r.max.y < r.min.y+2)
+ r.max.y = r.min.y+2;
+ border(screen, r, 1, bord, ZP);
+ if(menutxt == 0)
+ menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen);
+ if(menutxt)
+ draw(screen, insetrect(r, 1), menutxt, nil, ZP);
+}
+
+int
+emenuhit(int but, Mouse *m, Menu *menu)
+{
+ int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
+ int scrolling;
+ Rectangle r, menur, sc, textr, scrollr;
+ Image *b, *save;
+ Point pt;
+ char *item;
+
+ if(back == nil)
+ menucolors();
+ sc = screen->clipr;
+ replclipr(screen, 0, screen->r);
+ maxwid = 0;
+ for(nitem = 0;
+ item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
+ nitem++){
+ i = stringwidth(font, item);
+ if(i > maxwid)
+ maxwid = i;
+ }
+ if(menu->lasthit<0 || menu->lasthit>=nitem)
+ menu->lasthit = 0;
+ screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
+ if(nitem>Maxunscroll || nitem>screenitem){
+ scrolling = 1;
+ nitemdrawn = Nscroll;
+ if(nitemdrawn > screenitem)
+ nitemdrawn = screenitem;
+ wid = maxwid + Gap + Scrollwid;
+ off = menu->lasthit - nitemdrawn/2;
+ if(off < 0)
+ off = 0;
+ if(off > nitem-nitemdrawn)
+ off = nitem-nitemdrawn;
+ lasti = menu->lasthit-off;
+ }else{
+ scrolling = 0;
+ nitemdrawn = nitem;
+ wid = maxwid;
+ off = 0;
+ lasti = menu->lasthit;
+ }
+ r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
+ r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
+ r = rectaddpt(r, m->xy);
+ pt = ZP;
+ if(r.max.x>screen->r.max.x)
+ pt.x = screen->r.max.x-r.max.x;
+ if(r.max.y>screen->r.max.y)
+ pt.y = screen->r.max.y-r.max.y;
+ if(r.min.x<screen->r.min.x)
+ pt.x = screen->r.min.x-r.min.x;
+ if(r.min.y<screen->r.min.y)
+ pt.y = screen->r.min.y-r.min.y;
+ menur = rectaddpt(r, pt);
+ textr.max.x = menur.max.x-Margin;
+ textr.min.x = textr.max.x-maxwid;
+ textr.min.y = menur.min.y+Margin;
+ textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
+ if(scrolling){
+ scrollr = insetrect(menur, Border);
+ scrollr.max.x = scrollr.min.x+Scrollwid;
+ }else
+ scrollr = Rect(0, 0, 0, 0);
+
+ b = allocimage(display, menur, screen->chan, 0, 0);
+ if(b == 0)
+ b = screen;
+ draw(b, menur, screen, nil, menur.min);
+ draw(screen, menur, back, nil, ZP);
+ border(screen, menur, Blackborder, bord, ZP);
+ save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
+ r = menurect(textr, lasti);
+ emoveto(divpt(addpt(r.min, r.max), 2));
+ menupaint(menu, textr, off, nitemdrawn);
+ if(scrolling)
+ menuscrollpaint(scrollr, off, nitem, nitemdrawn);
+ while(m->buttons & (1<<(but-1))){
+ lasti = menuscan(menu, but, m, textr, off, lasti, save);
+ if(lasti >= 0)
+ break;
+ while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
+ if(scrolling && ptinrect(m->xy, scrollr)){
+ noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
+ noff -= nitemdrawn/2;
+ if(noff < 0)
+ noff = 0;
+ if(noff > nitem-nitemdrawn)
+ noff = nitem-nitemdrawn;
+ if(noff != off){
+ off = noff;
+ menupaint(menu, textr, off, nitemdrawn);
+ menuscrollpaint(scrollr, off, nitem, nitemdrawn);
+ }
+ }
+ flushimage(display, 1); /* in case display->locking is set */
+ *m = emouse();
+ }
+ }
+ draw(screen, menur, b, nil, menur.min);
+ if(b != screen)
+ freeimage(b);
+ freeimage(save);
+ replclipr(screen, 0, sc);
+ flushimage(display, 1);
+ if(lasti >= 0){
+ menu->lasthit = lasti+off;
+ return menu->lasthit;
+ }
+ return -1;
+}
diff --git a/src/libdraw/event.c b/src/libdraw/event.c
new file mode 100644
index 00000000..2f67cde8
--- /dev/null
+++ b/src/libdraw/event.c
@@ -0,0 +1,486 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <cursor.h>
+#include <event.h>
+
+typedef struct Slave Slave;
+typedef struct Ebuf Ebuf;
+
+struct Slave
+{
+ int pid;
+ Ebuf *head; /* ueue of messages for this descriptor */
+ Ebuf *tail;
+ int (*fn)(int, Event*, uchar*, int);
+};
+
+struct Ebuf
+{
+ Ebuf *next;
+ int n; /* number of bytes in buf */
+ uchar buf[EMAXMSG];
+};
+
+static Slave eslave[MAXSLAVE];
+static int Skeyboard = -1;
+static int Smouse = -1;
+static int Stimer = -1;
+static int logfid;
+
+static int nslave;
+static int parentpid;
+static int epipe[2];
+static int eforkslave(ulong);
+static void extract(void);
+static void ekill(void);
+static int enote(void *, char *);
+static int mousefd;
+static int cursorfd;
+
+static
+Ebuf*
+ebread(Slave *s)
+{
+ Ebuf *eb;
+ Dir *d;
+ ulong l;
+
+ for(;;){
+ d = dirfstat(epipe[0]);
+ if(d == nil)
+ drawerror(display, "events: eread stat error");
+ l = d->length;
+ free(d);
+ if(s->head && l==0)
+ break;
+ extract();
+ }
+ eb = s->head;
+ s->head = s->head->next;
+ if(s->head == 0)
+ s->tail = 0;
+ return eb;
+}
+
+ulong
+event(Event *e)
+{
+ return eread(~0UL, e);
+}
+
+ulong
+eread(ulong keys, Event *e)
+{
+ Ebuf *eb;
+ int i, id;
+
+ if(keys == 0)
+ return 0;
+ for(;;){
+ for(i=0; i<nslave; i++)
+ if((keys & (1<<i)) && eslave[i].head){
+ id = 1<<i;
+ if(i == Smouse)
+ e->mouse = emouse();
+ else if(i == Skeyboard)
+ e->kbdc = ekbd();
+ else if(i == Stimer)
+ eslave[i].head = 0;
+ else{
+ eb = ebread(&eslave[i]);
+ e->n = eb->n;
+ if(eslave[i].fn)
+ id = (*eslave[i].fn)(id, e, eb->buf, eb->n);
+ else
+ memmove(e->data, eb->buf, eb->n);
+ free(eb);
+ }
+ return id;
+ }
+ extract();
+ }
+ return 0;
+}
+
+int
+ecanmouse(void)
+{
+ if(Smouse < 0)
+ drawerror(display, "events: mouse not initialized");
+ return ecanread(Emouse);
+}
+
+int
+ecankbd(void)
+{
+ if(Skeyboard < 0)
+ drawerror(display, "events: keyboard not initialzed");
+ return ecanread(Ekeyboard);
+}
+
+int
+ecanread(ulong keys)
+{
+ Dir *d;
+ int i;
+ ulong l;
+
+ for(;;){
+ for(i=0; i<nslave; i++)
+ if((keys & (1<<i)) && eslave[i].head)
+ return 1;
+ d = dirfstat(epipe[0]);
+ if(d == nil)
+ drawerror(display, "events: ecanread stat error");
+ l = d->length;
+ free(d);
+ if(l == 0)
+ return 0;
+ extract();
+ }
+ return -1;
+}
+
+ulong
+estartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int))
+{
+ char buf[EMAXMSG+1];
+ int i, r;
+
+ if(fd < 0)
+ drawerror(display, "events: bad file descriptor");
+ if(n <= 0 || n > EMAXMSG)
+ n = EMAXMSG;
+ i = eforkslave(key);
+ if(i < MAXSLAVE){
+ eslave[i].fn = fn;
+ return 1<<i;
+ }
+ buf[0] = i - MAXSLAVE;
+ while((r = read(fd, buf+1, n))>0)
+ if(write(epipe[1], buf, r+1)!=r+1)
+ break;
+ buf[0] = MAXSLAVE;
+ write(epipe[1], buf, 1);
+ _exits(0);
+ return 0;
+}
+
+ulong
+estart(ulong key, int fd, int n)
+{
+ return estartfn(key, fd, n, nil);
+}
+
+ulong
+etimer(ulong key, int n)
+{
+ char t[2];
+
+ if(Stimer != -1)
+ drawerror(display, "events: timer started twice");
+ Stimer = eforkslave(key);
+ if(Stimer < MAXSLAVE)
+ return 1<<Stimer;
+ if(n <= 0)
+ n = 1000;
+ t[0] = t[1] = Stimer - MAXSLAVE;
+ do
+ sleep(n);
+ while(write(epipe[1], t, 2) == 2);
+ t[0] = MAXSLAVE;
+ write(epipe[1], t, 1);
+ _exits(0);
+ return 0;
+}
+
+static void
+ekeyslave(int fd)
+{
+ Rune r;
+ char t[3], k[10];
+ int kr, kn, w;
+
+ if(eforkslave(Ekeyboard) < MAXSLAVE)
+ return;
+ kn = 0;
+ t[0] = Skeyboard;
+ for(;;){
+ while(!fullrune(k, kn)){
+ kr = read(fd, k+kn, sizeof k - kn);
+ if(kr <= 0)
+ goto breakout;
+ kn += kr;
+ }
+ w = chartorune(&r, k);
+ kn -= w;
+ memmove(k, &k[w], kn);
+ t[1] = r;
+ t[2] = r>>8;
+ if(write(epipe[1], t, 3) != 3)
+ break;
+ }
+breakout:;
+ t[0] = MAXSLAVE;
+ write(epipe[1], t, 1);
+ _exits(0);
+}
+
+void
+einit(ulong keys)
+{
+ int ctl, fd;
+ char buf[256];
+
+ parentpid = getpid();
+ if(pipe(epipe) < 0)
+ drawerror(display, "events: einit pipe");
+ atexit(ekill);
+ atnotify(enote, 1);
+ snprint(buf, sizeof buf, "%s/mouse", display->devdir);
+ mousefd = open(buf, ORDWR|OCEXEC);
+ if(mousefd < 0)
+ drawerror(display, "einit: can't open mouse\n");
+ snprint(buf, sizeof buf, "%s/cursor", display->devdir);
+ cursorfd = open(buf, ORDWR|OCEXEC);
+ if(cursorfd < 0)
+ drawerror(display, "einit: can't open cursor\n");
+ if(keys&Ekeyboard){
+ snprint(buf, sizeof buf, "%s/cons", display->devdir);
+ fd = open(buf, OREAD);
+ if(fd < 0)
+ drawerror(display, "events: can't open console");
+ snprint(buf, sizeof buf, "%s/consctl", display->devdir);
+ ctl = open("/dev/consctl", OWRITE|OCEXEC);
+ if(ctl < 0)
+ drawerror(display, "events: can't open consctl");
+ write(ctl, "rawon", 5);
+ for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++)
+ ;
+ ekeyslave(fd);
+ }
+ if(keys&Emouse){
+ estart(Emouse, mousefd, 1+4*12);
+ for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++)
+ ;
+ }
+}
+
+static void
+extract(void)
+{
+ Slave *s;
+ Ebuf *eb;
+ int i, n;
+ uchar ebuf[EMAXMSG+1];
+
+ /* avoid generating a message if there's nothing to show. */
+ /* this test isn't perfect, though; could do flushimage(display, 0) then call extract */
+ /* also: make sure we don't interfere if we're multiprocessing the display */
+ if(display->locking){
+ /* if locking is being done by program, this means it can't depend on automatic flush in emouse() etc. */
+ if(canqlock(&display->qlock)){
+ if(display->bufp > display->buf)
+ flushimage(display, 1);
+ unlockdisplay(display);
+ }
+ }else
+ if(display->bufp > display->buf)
+ flushimage(display, 1);
+loop:
+ if((n=read(epipe[0], ebuf, EMAXMSG+1)) < 0
+ || ebuf[0] >= MAXSLAVE)
+ drawerror(display, "eof on event pipe");
+ if(n == 0)
+ goto loop;
+ i = ebuf[0];
+ if(i >= nslave || n <= 1)
+ drawerror(display, "events: protocol error: short read");
+ s = &eslave[i];
+ if(i == Stimer){
+ s->head = (Ebuf *)1;
+ return;
+ }
+ if(i == Skeyboard && n != 3)
+ drawerror(display, "events: protocol error: keyboard");
+ if(i == Smouse){
+ if(n < 1+1+2*12)
+ drawerror(display, "events: protocol error: mouse");
+ if(ebuf[1] == 'r')
+ eresized(1);
+ /* squash extraneous mouse events */
+ if((eb=s->tail) && memcmp(eb->buf+1+2*12, ebuf+1+1+2*12, 12)==0){
+ memmove(eb->buf, &ebuf[1], n - 1);
+ return;
+ }
+ }
+ /* try to save space by only allocating as much buffer as we need */
+ eb = malloc(sizeof(*eb) - sizeof(eb->buf) + n - 1);
+ if(eb == 0)
+ drawerror(display, "events: protocol error 4");
+ eb->n = n - 1;
+ memmove(eb->buf, &ebuf[1], n - 1);
+ eb->next = 0;
+ if(s->head)
+ s->tail = s->tail->next = eb;
+ else
+ s->head = s->tail = eb;
+}
+
+static int
+eforkslave(ulong key)
+{
+ int i, pid;
+
+ for(i=0; i<MAXSLAVE; i++)
+ if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){
+ if(nslave <= i)
+ nslave = i + 1;
+ /*
+ * share the file descriptors so the last child
+ * out closes all connections to the window server.
+ */
+ switch(pid = rfork(RFPROC)){
+ case 0:
+ return MAXSLAVE+i;
+ case -1:
+ fprint(2, "events: fork error\n");
+ exits("fork");
+ }
+ eslave[i].pid = pid;
+ eslave[i].head = eslave[i].tail = 0;
+ return i;
+ }
+ drawerror(display, "events: bad slave assignment");
+ return 0;
+}
+
+static int
+enote(void *v, char *s)
+{
+ char t[1];
+ int i, pid;
+
+ USED(v, s);
+ pid = getpid();
+ if(pid != parentpid){
+ for(i=0; i<nslave; i++){
+ if(pid == eslave[i].pid){
+ t[0] = MAXSLAVE;
+ write(epipe[1], t, 1);
+ break;
+ }
+ }
+ return 0;
+ }
+ close(epipe[0]);
+ epipe[0] = -1;
+ close(epipe[1]);
+ epipe[1] = -1;
+ for(i=0; i<nslave; i++){
+ if(pid == eslave[i].pid)
+ continue; /* don't kill myself */
+ postnote(PNPROC, eslave[i].pid, "die");
+ }
+ return 0;
+}
+
+static void
+ekill(void)
+{
+ enote(0, 0);
+}
+
+Mouse
+emouse(void)
+{
+ Mouse m;
+ Ebuf *eb;
+ static but[2];
+ int b;
+
+ if(Smouse < 0)
+ drawerror(display, "events: mouse not initialized");
+ eb = ebread(&eslave[Smouse]);
+ m.xy.x = atoi((char*)eb->buf+1+0*12);
+ m.xy.y = atoi((char*)eb->buf+1+1*12);
+ b = atoi((char*)eb->buf+1+2*12);
+ m.buttons = b&7;
+ m.msec = atoi((char*)eb->buf+1+3*12);
+ if (logfid)
+ fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy);
+ free(eb);
+ return m;
+}
+
+int
+ekbd(void)
+{
+ Ebuf *eb;
+ int c;
+
+ if(Skeyboard < 0)
+ drawerror(display, "events: keyboard not initialzed");
+ eb = ebread(&eslave[Skeyboard]);
+ c = eb->buf[0] + (eb->buf[1]<<8);
+ free(eb);
+ return c;
+}
+
+void
+emoveto(Point pt)
+{
+ char buf[2*12+2];
+ int n;
+
+ n = sprint(buf, "m%d %d", pt.x, pt.y);
+ write(mousefd, buf, n);
+}
+
+void
+esetcursor(Cursor *c)
+{
+ uchar curs[2*4+2*2*16];
+
+ if(c == 0)
+ write(cursorfd, curs, 0);
+ else{
+ BPLONG(curs+0*4, c->offset.x);
+ BPLONG(curs+1*4, c->offset.y);
+ memmove(curs+2*4, c->clr, 2*2*16);
+ write(cursorfd, curs, sizeof curs);
+ }
+}
+
+int
+ereadmouse(Mouse *m)
+{
+ int n;
+ char buf[128];
+
+ do{
+ n = read(mousefd, buf, sizeof(buf));
+ if(n < 0) /* probably interrupted */
+ return -1;
+ n = eatomouse(m, buf, n);
+ }while(n == 0);
+ return n;
+}
+
+int
+eatomouse(Mouse *m, char *buf, int n)
+{
+ if(n != 1+4*12){
+ werrstr("atomouse: bad count");
+ return -1;
+ }
+
+ if(buf[0] == 'r')
+ eresized(1);
+ m->xy.x = atoi(buf+1+0*12);
+ m->xy.y = atoi(buf+1+1*12);
+ m->buttons = atoi(buf+1+2*12);
+ m->msec = atoi(buf+1+3*12);
+ return n;
+}
diff --git a/src/libdraw/event.h b/src/libdraw/event.h
new file mode 100644
index 00000000..e74183d4
--- /dev/null
+++ b/src/libdraw/event.h
@@ -0,0 +1,63 @@
+typedef struct Event Event;
+typedef struct Menu Menu;
+
+enum
+{
+ Emouse = 1,
+ Ekeyboard = 2,
+};
+
+enum
+{
+ MAXSLAVE = 32,
+ EMAXMSG = 128+8192, /* size of 9p header+data */
+};
+
+struct Mouse
+{
+ int buttons; /* bit array: LMR=124 */
+ Point xy;
+ ulong msec;
+};
+
+struct Event
+{
+ int kbdc;
+ Mouse mouse;
+ int n; /* number of characters in message */
+ void *v; /* data unpacked by general event-handling function */
+ uchar data[EMAXMSG]; /* message from an arbitrary file descriptor */
+};
+
+struct Menu
+{
+ char **item;
+ char *(*gen)(int);
+ int lasthit;
+};
+
+/*
+ * Events
+ */
+extern void einit(ulong);
+extern ulong estart(ulong, int, int);
+extern ulong estartfn(ulong, int, int, int (*fn)(int, Event*, uchar*, int));
+extern ulong etimer(ulong, int);
+extern ulong event(Event*);
+extern ulong eread(ulong, Event*);
+extern Mouse emouse(void);
+extern int ekbd(void);
+extern int ecanread(ulong);
+extern int ecanmouse(void);
+extern int ecankbd(void);
+extern void eresized(int); /* supplied by user */
+extern int emenuhit(int, Mouse*, Menu*);
+extern int eatomouse(Mouse*, char*, int);
+extern Rectangle getrect(int, Mouse*);
+struct Cursor;
+extern void esetcursor(struct Cursor*);
+extern void emoveto(Point);
+extern Rectangle egetrect(int, Mouse*);
+extern void edrawgetrect(Rectangle, int);
+extern int ereadmouse(Mouse*);
+extern int eatomouse(Mouse*, char*, int);
diff --git a/src/libdraw/font.c b/src/libdraw/font.c
new file mode 100644
index 00000000..6b14d3fc
--- /dev/null
+++ b/src/libdraw/font.c
@@ -0,0 +1,401 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static int fontresize(Font*, int, int, int);
+#if 0
+static int freeup(Font*);
+#endif
+
+#define PJW 0 /* use NUL==pjw for invisible characters */
+
+static Rune empty[] = { 0 };
+int
+cachechars(Font *f, char **ss, Rune **rr, ushort *cp, int max, int *wp, char **subfontname)
+{
+ int i, th, sh, h, ld, w, rw, wid, nc;
+ char *sp;
+ Rune r, *rp, vr;
+ ulong a;
+ Cacheinfo *c, *tc, *ec;
+
+ if(ss){
+ sp = *ss;
+ rp = empty;
+ }else{
+ sp = "";
+ rp = *rr;
+ }
+ wid = 0;
+ *subfontname = 0;
+ for(i=0; (*sp || *rp) && i<max; sp+=w, rp+=rw){
+ if(ss){
+ r = *(uchar*)sp;
+ if(r < Runeself)
+ w = 1;
+ else{
+ w = chartorune(&vr, sp);
+ r = vr;
+ }
+ rw = 0;
+ }else{
+ r = *rp;
+ w = 0;
+ rw = 1;
+ }
+
+ sh = (17 * (uint)r) & (f->ncache-NFLOOK-1);
+ c = &f->cache[sh];
+ ec = c+NFLOOK;
+ h = sh;
+ while(c < ec){
+ if(c->value==r && c->age)
+ goto Found;
+ c++;
+ h++;
+ }
+
+ /*
+ * Not found; toss out oldest entry
+ */
+ a = ~0;
+ th = sh;
+ tc = &f->cache[th];
+ while(tc < ec){
+ if(tc->age < a){
+ a = tc->age;
+ h = th;
+ c = tc;
+ }
+ tc++;
+ th++;
+ }
+
+ if(a && (f->age-a)<500){ /* kicking out too recent; resize */
+ nc = 2*(f->ncache-NFLOOK) + NFLOOK;
+ if(nc <= MAXFCACHE){
+ if(i == 0)
+ fontresize(f, f->width, nc, f->maxdepth);
+ /* else flush first; retry will resize */
+ break;
+ }
+ }
+
+ if(c->age == f->age) /* flush pending string output */
+ break;
+
+ ld = loadchar(f, r, c, h, i, subfontname);
+ if(ld <= 0){
+ if(ld == 0)
+ continue;
+ break;
+ }
+ c = &f->cache[h]; /* may have reallocated f->cache */
+
+ Found:
+ wid += c->width;
+ c->age = f->age;
+ cp[i] = h;
+ i++;
+ }
+ if(ss)
+ *ss = sp;
+ else
+ *rr = rp;
+ *wp = wid;
+ return i;
+}
+
+void
+agefont(Font *f)
+{
+ Cacheinfo *c, *ec;
+ Cachesubf *s, *es;
+
+ f->age++;
+ if(f->age == 65536){
+ /*
+ * Renormalize ages
+ */
+ c = f->cache;
+ ec = c+f->ncache;
+ while(c < ec){
+ if(c->age){
+ c->age >>= 2;
+ c->age++;
+ }
+ c++;
+ }
+ s = f->subf;
+ es = s+f->nsubf;
+ while(s < es){
+ if(s->age){
+ if(s->age<SUBFAGE && s->cf->name != nil){
+ /* clean up */
+ if(s->f != display->defaultsubfont)
+ freesubfont(s->f);
+ s->cf = nil;
+ s->f = nil;
+ s->age = 0;
+ }else{
+ s->age >>= 2;
+ s->age++;
+ }
+ }
+ s++;
+ }
+ f->age = (65536>>2) + 1;
+ }
+}
+
+static Subfont*
+cf2subfont(Cachefont *cf, Font *f)
+{
+ int depth;
+ char *name;
+ Subfont *sf;
+
+ name = cf->subfontname;
+ if(name == nil){
+ depth = 0;
+ if(f->display){
+ if(f->display->screenimage)
+ depth = f->display->screenimage->depth;
+ }
+ name = subfontname(cf->name, f->name, depth);
+ if(name == nil)
+ return nil;
+ cf->subfontname = name;
+ }
+ sf = lookupsubfont(f->display, name);
+ return sf;
+}
+
+/* return 1 if load succeeded, 0 if failed, -1 if must retry */
+int
+loadchar(Font *f, Rune r, Cacheinfo *c, int h, int noflush, char **subfontname)
+{
+ int i, oi, wid, top, bottom;
+ Rune pic;
+ Fontchar *fi;
+ Cachefont *cf;
+ Cachesubf *subf, *of;
+ uchar *b;
+
+ pic = r;
+ Again:
+ for(i=0; i<f->nsub; i++){
+ cf = f->sub[i];
+ if(cf->min<=pic && pic<=cf->max)
+ goto Found;
+ }
+ TryPJW:
+ if(pic != PJW){
+ pic = PJW;
+ goto Again;
+ }
+ return 0;
+
+ Found:
+ /*
+ * Choose exact or oldest
+ */
+ oi = 0;
+ subf = &f->subf[0];
+ for(i=0; i<f->nsubf; i++){
+ if(cf == subf->cf)
+ goto Found2;
+ if(subf->age < f->subf[oi].age)
+ oi = i;
+ subf++;
+ }
+ subf = &f->subf[oi];
+
+ if(subf->f){
+ if(f->age-subf->age>SUBFAGE || f->nsubf>MAXSUBF){
+ Toss:
+ /* ancient data; toss */
+ freesubfont(subf->f);
+ subf->cf = nil;
+ subf->f = nil;
+ subf->age = 0;
+ }else{ /* too recent; grow instead */
+ of = f->subf;
+ f->subf = malloc((f->nsubf+DSUBF)*sizeof *subf);
+ if(f->subf == nil){
+ f->subf = of;
+ goto Toss;
+ }
+ memmove(f->subf, of, (f->nsubf+DSUBF)*sizeof *subf);
+ memset(f->subf+f->nsubf, 0, DSUBF*sizeof *subf);
+ subf = &f->subf[f->nsubf];
+ f->nsubf += DSUBF;
+ free(of);
+ }
+ }
+ subf->age = 0;
+ subf->cf = nil;
+ subf->f = cf2subfont(cf, f);
+ if(subf->f == nil){
+ if(cf->subfontname == nil)
+ goto TryPJW;
+ *subfontname = cf->subfontname;
+ return -1;
+ }
+
+ subf->cf = cf;
+ if(subf->f->ascent > f->ascent){
+ /* should print something? this is a mistake in the font file */
+ /* must prevent c->top from going negative when loading cache */
+ Image *b;
+ int d, t;
+ d = subf->f->ascent - f->ascent;
+ b = subf->f->bits;
+ draw(b, b->r, b, nil, addpt(b->r.min, Pt(0, d)));
+ draw(b, Rect(b->r.min.x, b->r.max.y-d, b->r.max.x, b->r.max.y), f->display->black, nil, b->r.min);
+ for(i=0; i<subf->f->n; i++){
+ t = subf->f->info[i].top-d;
+ if(t < 0)
+ t = 0;
+ subf->f->info[i].top = t;
+ t = subf->f->info[i].bottom-d;
+ if(t < 0)
+ t = 0;
+ subf->f->info[i].bottom = t;
+ }
+ subf->f->ascent = f->ascent;
+ }
+
+ Found2:
+ subf->age = f->age;
+
+ pic += cf->offset;
+ if(pic-cf->min >= subf->f->n)
+ goto TryPJW;
+ fi = &subf->f->info[pic - cf->min];
+ if(fi->width == 0)
+ goto TryPJW;
+ wid = (fi+1)->x - fi->x;
+ if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->depth){
+ /*
+ * Flush, free, reload (easier than reformatting f->b)
+ */
+ if(noflush)
+ return -1;
+ if(f->width < wid)
+ f->width = wid;
+ if(f->maxdepth < subf->f->bits->depth)
+ f->maxdepth = subf->f->bits->depth;
+ i = fontresize(f, f->width, f->ncache, f->maxdepth);
+ if(i <= 0)
+ return i;
+ /* c is still valid as didn't reallocate f->cache */
+ }
+ c->value = r;
+ top = fi->top + (f->ascent-subf->f->ascent);
+ bottom = fi->bottom + (f->ascent-subf->f->ascent);
+ c->width = fi->width;
+ c->x = h*f->width;
+ c->left = fi->left;
+ flushimage(f->display, 0); /* flush any pending errors */
+ b = bufimage(f->display, 37);
+ if(b == 0)
+ return 0;
+ b[0] = 'l';
+ BPLONG(b+1, f->cacheimage->id);
+ BPLONG(b+5, subf->f->bits->id);
+ BPSHORT(b+9, c-f->cache);
+ BPLONG(b+11, c->x);
+ BPLONG(b+15, top);
+ BPLONG(b+19, c->x+((fi+1)->x-fi->x));
+ BPLONG(b+23, bottom);
+ BPLONG(b+27, fi->x);
+ BPLONG(b+31, fi->top);
+ b[35] = fi->left;
+ b[36] = fi->width;
+ return 1;
+}
+
+/* release all subfonts, return number freed */
+#if 0
+static
+int
+freeup(Font *f)
+{
+ Cachesubf *s, *es;
+ int nf;
+
+ if(f->sub[0]->name == nil) /* font from mkfont; don't free */
+ return 0;
+ s = f->subf;
+ es = s+f->nsubf;
+ nf = 0;
+ while(s < es){
+ if(s->age){
+ freesubfont(s->f);
+ s->cf = nil;
+ s->f = nil;
+ s->age = 0;
+ nf++;
+ }
+ s++;
+ }
+ return nf;
+}
+#endif
+
+/* return whether resize succeeded && f->cache is unchanged */
+static int
+fontresize(Font *f, int wid, int ncache, int depth)
+{
+ Cacheinfo *i;
+ int ret;
+ Image *new;
+ uchar *b;
+ Display *d;
+
+ ret = 0;
+ d = f->display;
+ if(depth <= 0)
+ depth = 1;
+
+ new = allocimage(d, Rect(0, 0, ncache*wid, f->height), CHAN1(CGrey, depth), 0, 0);
+ if(new == nil){
+ fprint(2, "font cache resize failed: %r\n");
+ abort();
+ goto Return;
+ }
+ flushimage(d, 0); /* flush any pending errors */
+ b = bufimage(d, 1+4+4+1);
+ if(b == 0){
+ freeimage(new);
+ goto Return;
+ }
+ b[0] = 'i';
+ BPLONG(b+1, new->id);
+ BPLONG(b+5, ncache);
+ b[9] = f->ascent;
+ if(flushimage(d, 0) < 0){
+ fprint(2, "resize: init failed: %r\n");
+ freeimage(new);
+ goto Return;
+ }
+ freeimage(f->cacheimage);
+ f->cacheimage = new;
+ f->width = wid;
+ f->maxdepth = depth;
+ ret = 1;
+ if(f->ncache != ncache){
+ i = malloc(ncache*sizeof f->cache[0]);
+ if(i != nil){
+ ret = 0;
+ free(f->cache);
+ f->ncache = ncache;
+ f->cache = i;
+ }
+ /* else just wipe the cache clean and things will be ok */
+ }
+ Return:
+ memset(f->cache, 0, f->ncache*sizeof f->cache[0]);
+ return ret;
+}
diff --git a/src/libdraw/getsubfont.c b/src/libdraw/getsubfont.c
new file mode 100644
index 00000000..b7a8e44a
--- /dev/null
+++ b/src/libdraw/getsubfont.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * Default version: treat as file name
+ */
+
+Subfont*
+_getsubfont(Display *d, char *name)
+{
+ int fd;
+ Subfont *f;
+
+ fd = open(name, OREAD);
+
+ if(fd < 0){
+ fprint(2, "getsubfont: can't open %s: %r\n", name);
+ return 0;
+ }
+ /*
+ * unlock display so i/o happens with display released, unless
+ * user is doing his own locking, in which case this could break things.
+ * _getsubfont is called only from string.c and stringwidth.c,
+ * which are known to be safe to have this done.
+ */
+ if(d->locking == 0)
+ unlockdisplay(d);
+ f = readsubfont(d, name, fd, d->locking==0);
+ if(d->locking == 0)
+ lockdisplay(d);
+ if(f == 0)
+ fprint(2, "getsubfont: can't read %s: %r\n", name);
+ close(fd);
+ return f;
+}
diff --git a/src/libdraw/init.c b/src/libdraw/init.c
new file mode 100644
index 00000000..0cf590d8
--- /dev/null
+++ b/src/libdraw/init.c
@@ -0,0 +1,203 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Display *display;
+Font *font;
+Image *screen;
+int _drawdebug;
+
+static char deffontname[] = "*default*";
+Screen *_screen;
+
+int debuglockdisplay = 0;
+
+static void
+drawshutdown(void)
+{
+ Display *d;
+
+ d = display;
+ if(d){
+ display = nil;
+ closedisplay(d);
+ }
+}
+
+int
+initdraw(void (*error)(Display*, char*), char *fontname, char *label)
+{
+ Subfont *df;
+ char buf[128];
+
+ display = _initdisplay(error, label); /* sets screen too */
+ if(display == nil)
+ return -1;
+
+ display->image = display->screenimage;
+ screen = display->screenimage;
+
+ /*
+ * Set up default font
+ */
+ df = getdefont(display);
+ display->defaultsubfont = df;
+ if(df == nil){
+ fprint(2, "imageinit: can't open default subfont: %r\n");
+ Error:
+ closedisplay(display);
+ display = nil;
+ return -1;
+ }
+ if(fontname == nil)
+ fontname = getenv("font"); /* leak */
+
+ /*
+ * Build fonts with caches==depth of screen, for speed.
+ * If conversion were faster, we'd use 0 and save memory.
+ */
+ if(fontname == nil){
+ snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent,
+ df->n-1, deffontname);
+//BUG: Need something better for this installsubfont("*default*", df);
+ font = buildfont(display, buf, deffontname);
+ if(font == nil){
+ fprint(2, "initdraw: can't open default font: %r\n");
+ goto Error;
+ }
+ }else{
+ font = openfont(display, fontname); /* BUG: grey fonts */
+ if(font == nil){
+ fprint(2, "initdraw: can't open font %s: %r\n", fontname);
+ goto Error;
+ }
+ }
+ display->defaultfont = font;
+
+ display->white = allocimage(display, Rect(0,0,1,1), GREY1, 1, DWhite);
+ display->black = allocimage(display, Rect(0,0,1,1), GREY1, 1, DBlack);
+ if(display->white == nil || display->black == nil){
+ fprint(2, "initdraw: can't allocate white and black");
+ goto Error;
+ }
+ display->opaque = display->white;
+ display->transparent = display->black;
+ atexit(drawshutdown);
+ return 1;
+}
+
+/*
+ * Call with d unlocked.
+ * Note that disp->defaultfont and defaultsubfont are not freed here.
+ */
+void
+closedisplay(Display *disp)
+{
+ int fd;
+ char buf[128];
+
+ if(disp == nil)
+ return;
+ if(disp == display)
+ display = nil;
+ if(disp->oldlabel[0]){
+ snprint(buf, sizeof buf, "%s/label", disp->windir);
+ fd = open(buf, OWRITE);
+ if(fd >= 0){
+ write(fd, disp->oldlabel, strlen(disp->oldlabel));
+ close(fd);
+ }
+ }
+
+ free(disp->devdir);
+ free(disp->windir);
+ freeimage(disp->white);
+ freeimage(disp->black);
+ qunlock(&disp->qlock);
+ free(disp);
+}
+
+void
+lockdisplay(Display *disp)
+{
+ if(debuglockdisplay){
+ /* avoid busy looping; it's rare we collide anyway */
+ while(!canqlock(&disp->qlock)){
+ fprint(1, "proc %d waiting for display lock...\n", getpid());
+ sleep(1000);
+ }
+ }else
+ qlock(&disp->qlock);
+}
+
+void
+unlockdisplay(Display *disp)
+{
+ qunlock(&disp->qlock);
+}
+
+void
+drawerror(Display *d, char *s)
+{
+ char err[ERRMAX];
+
+ if(d->error)
+ d->error(d, s);
+ else{
+ errstr(err, sizeof err);
+ fprint(2, "draw: %s: %s\n", s, err);
+ exits(s);
+ }
+}
+
+static
+int
+doflush(Display *d)
+{
+ int n;
+
+ n = d->bufp-d->buf;
+ if(n <= 0)
+ return 1;
+
+ if(_drawmsgwrite(d, d->buf, n) != n){
+ if(_drawdebug)
+ fprint(2, "flushimage fail: d=%p: %r\n", d); /**/
+ d->bufp = d->buf; /* might as well; chance of continuing */
+ return -1;
+ }
+ d->bufp = d->buf;
+ return 1;
+}
+
+int
+flushimage(Display *d, int visible)
+{
+ if(visible){
+ *d->bufp++ = 'v'; /* five bytes always reserved for this */
+ if(d->_isnewdisplay){
+ BPLONG(d->bufp, d->screenimage->id);
+ d->bufp += 4;
+ }
+ }
+ return doflush(d);
+}
+
+uchar*
+bufimage(Display *d, int n)
+{
+ uchar *p;
+
+ if(n<0 || n>d->bufsize){
+abort();
+ werrstr("bad count in bufimage");
+ return 0;
+ }
+ if(d->bufp+n > d->buf+d->bufsize)
+ if(doflush(d) < 0)
+ return 0;
+ p = d->bufp;
+ d->bufp += n;
+ return p;
+}
+
diff --git a/src/libdraw/keyboard.c b/src/libdraw/keyboard.c
new file mode 100644
index 00000000..5ab911ab
--- /dev/null
+++ b/src/libdraw/keyboard.c
@@ -0,0 +1,102 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+
+
+void
+closekeyboard(Keyboardctl *kc)
+{
+ if(kc == nil)
+ return;
+
+ postnote(PNPROC, kc->pid, "kill");
+
+#ifdef BUG
+ /* Drain the channel */
+ while(?kc->c)
+ <-kc->c;
+#endif
+
+ close(kc->ctlfd);
+ close(kc->consfd);
+ free(kc->file);
+ free(kc->c);
+ free(kc);
+}
+
+static
+void
+_ioproc(void *arg)
+{
+ int m, n;
+ char buf[20];
+ Rune r;
+ Keyboardctl *kc;
+
+ kc = arg;
+ threadsetname("kbdproc");
+ kc->pid = getpid();
+ n = 0;
+ for(;;){
+ while(n>0 && fullrune(buf, n)){
+ m = chartorune(&r, buf);
+ n -= m;
+ memmove(buf, buf+m, n);
+ send(kc->c, &r);
+ }
+ m = read(kc->consfd, buf+n, sizeof buf-n);
+ if(m <= 0){
+ yield(); /* if error is due to exiting, we'll exit here */
+ fprint(2, "keyboard read error: %r\n");
+ threadexits("error");
+ }
+ n += m;
+ }
+}
+
+Keyboardctl*
+initkeyboard(char *file)
+{
+ Keyboardctl *kc;
+ char *t;
+
+ kc = mallocz(sizeof(Keyboardctl), 1);
+ if(kc == nil)
+ return nil;
+ if(file == nil)
+ file = "/dev/cons";
+ kc->file = strdup(file);
+ kc->consfd = open(file, ORDWR|OCEXEC);
+ t = malloc(strlen(file)+16);
+ if(kc->consfd<0 || t==nil){
+Error1:
+ free(kc);
+ return nil;
+ }
+ sprint(t, "%sctl", file);
+ kc->ctlfd = open(t, OWRITE|OCEXEC);
+ if(kc->ctlfd < 0){
+ fprint(2, "initkeyboard: can't open %s: %r\n", t);
+Error2:
+ close(kc->consfd);
+ free(t);
+ goto Error1;
+ }
+ if(ctlkeyboard(kc, "rawon") < 0){
+ fprint(2, "initkeyboard: can't turn on raw mode on %s: %r\n", t);
+ close(kc->ctlfd);
+ goto Error2;
+ }
+ free(t);
+ kc->c = chancreate(sizeof(Rune), 20);
+ proccreate(_ioproc, kc, 4096);
+ return kc;
+}
+
+int
+ctlkeyboard(Keyboardctl *kc, char *m)
+{
+ return write(kc->ctlfd, m, strlen(m));
+}
diff --git a/src/libdraw/keyboard.h b/src/libdraw/keyboard.h
new file mode 100644
index 00000000..a6d99bf6
--- /dev/null
+++ b/src/libdraw/keyboard.h
@@ -0,0 +1,36 @@
+typedef struct Keyboardctl Keyboardctl;
+
+struct Keyboardctl
+{
+ struct Channel *c; /* chan(Rune)[20] */
+
+ char *file;
+ int consfd; /* to cons file */
+ int ctlfd; /* to ctl file */
+ int pid; /* of slave proc */
+};
+
+
+extern Keyboardctl* initkeyboard(char*);
+extern int ctlkeyboard(Keyboardctl*, char*);
+extern void closekeyboard(Keyboardctl*);
+
+enum {
+ KF= 0xF000, /* Rune: beginning of private Unicode space */
+ /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */
+ Khome= KF|0x0D,
+ Kup= KF|0x0E,
+ Kpgup= KF|0x0F,
+ Kprint= KF|0x10,
+ Kleft= KF|0x11,
+ Kright= KF|0x12,
+ Kdown= 0x80,
+ Kview= 0x80,
+ Kpgdown= KF|0x13,
+ Kins= KF|0x14,
+ Kend= '\r', /* [sic] */
+
+ Kalt= KF|0x15,
+ Kshift= KF|0x16,
+ Kctl= KF|0x17,
+};
diff --git a/src/libdraw/libdraw.x b/src/libdraw/libdraw.x
new file mode 100644
index 00000000..8b277f0d
--- /dev/null
+++ b/src/libdraw/libdraw.x
@@ -0,0 +1 @@
+!<arch>
diff --git a/src/libdraw/md-alloc.c b/src/libdraw/md-alloc.c
new file mode 100644
index 00000000..801c3930
--- /dev/null
+++ b/src/libdraw/md-alloc.c
@@ -0,0 +1,200 @@
+#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[2];
+}
+
+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;
+ 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, (2+nw)*sizeof(u32int));
+ if(md->base == nil){
+ free(md);
+ return nil;
+ }
+
+ md->base[0] = (u32int)md;
+ md->base[1] = getcallerpc(&r);
+
+ /* if this changes, memimagemove must change too */
+ md->bdata = (uchar*)&md->base[2];
+
+ 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*) ((u32int)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/libdraw/md-arc.c b/src/libdraw/md-arc.c
new file mode 100644
index 00000000..df836a00
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-arctest.c b/src/libdraw/md-arctest.c
new file mode 100644
index 00000000..b3a8c211
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-cload.c b/src/libdraw/md-cload.c
new file mode 100644
index 00000000..472caa6b
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-cmap.c b/src/libdraw/md-cmap.c
new file mode 100644
index 00000000..2561c3f1
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-cread.c b/src/libdraw/md-cread.c
new file mode 100644
index 00000000..4fa5942a
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-defont.c b/src/libdraw/md-defont.c
new file mode 100644
index 00000000..446c2e93
--- /dev/null
+++ b/src/libdraw/md-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 = (u32int)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/libdraw/md-draw.c b/src/libdraw/md-draw.c
new file mode 100644
index 00000000..d0f2d4fa
--- /dev/null
+++ b/src/libdraw/md-draw.c
@@ -0,0 +1,2487 @@
+#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(0)
+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(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 Param
+getparam(Memimage *img, Rectangle r, int convgrey, int needbuf)
+{
+ Param p;
+ 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;
+
+ return p;
+}
+
+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(0)
+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;
+
+ 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);
+
+ spar = getparam(src, sr, isgrey, needbuf);
+ dpar = getparam(dst, r, isgrey, needbuf);
+ mpar = getparam(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));
+
+ 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);
+ 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;
+
+ 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;
+
+ 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(0)
+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;
+
+DBG print("copyalpha %d alphaonly %d convgrey %d isgrey %d\n", copyalpha, alphaonly, convgrey, isgrey);
+ /* 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;
+ }
+
+DBG print("2\n");
+ 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]];
+DBG print("a %x\n", w[-1]);
+ }
+
+ 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){
+DBG print("g %x %x %x\n", ured, ugrn, ublu);
+ *w++ = RGB2K(ured, ugrn, ublu);
+DBG print("%x\n", w[-1]);
+ }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;
+DBG print("alpha %x grey %x\n", b.alpha ? *b.alpha : 0xFF, *b.grey);
+ }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(0)
+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;
+ 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);
+ 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 nil;
+}
+
+/*
+ * 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|Replmask)) != (Replsrc|Simplesrc)
+ || mask->depth != 1 || src->flags&Falpha || 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/libdraw/md-drawtest.c b/src/libdraw/md-drawtest.c
new file mode 100644
index 00000000..26eb54de
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-ellipse.c b/src/libdraw/md-ellipse.c
new file mode 100644
index 00000000..7dc45cd6
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-fillpoly.c b/src/libdraw/md-fillpoly.c
new file mode 100644
index 00000000..928ae1e1
--- /dev/null
+++ b/src/libdraw/md-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)(((u32int)z)^((u32int)y)) > 0 || z == 0)
+ return z;
+ return z + y;
+}
+
+static long
+sdiv(long x, long y)
+{
+ if((long)(((u32int)x)^((u32int)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/libdraw/md-hwdraw.c b/src/libdraw/md-hwdraw.c
new file mode 100644
index 00000000..3f36250f
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-iprint.c b/src/libdraw/md-iprint.c
new file mode 100644
index 00000000..923b6b44
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-line.c b/src/libdraw/md-line.c
new file mode 100644
index 00000000..632e8238
--- /dev/null
+++ b/src/libdraw/md-line.c
@@ -0,0 +1,484 @@
+#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/libdraw/md-load.c b/src/libdraw/md-load.c
new file mode 100644
index 00000000..6788fa90
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-mkcmap.c b/src/libdraw/md-mkcmap.c
new file mode 100644
index 00000000..e8d5efc3
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-openmemsubfont.c b/src/libdraw/md-openmemsubfont.c
new file mode 100644
index 00000000..c8d926e4
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-poly.c b/src/libdraw/md-poly.c
new file mode 100644
index 00000000..d16c0a92
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-read.c b/src/libdraw/md-read.c
new file mode 100644
index 00000000..d7a535d7
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-string.c b/src/libdraw/md-string.c
new file mode 100644
index 00000000..6ae19c01
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-subfont.c b/src/libdraw/md-subfont.c
new file mode 100644
index 00000000..e2bdee5c
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-unload.c b/src/libdraw/md-unload.c
new file mode 100644
index 00000000..86835757
--- /dev/null
+++ b/src/libdraw/md-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/libdraw/md-write.c b/src/libdraw/md-write.c
new file mode 100644
index 00000000..c03c58e8
--- /dev/null
+++ b/src/libdraw/md-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;
+}
diff --git a/src/libdraw/memdraw.h b/src/libdraw/memdraw.h
new file mode 100644
index 00000000..08784ce9
--- /dev/null
+++ b/src/libdraw/memdraw.h
@@ -0,0 +1,209 @@
+typedef struct Memimage Memimage;
+typedef struct Memdata Memdata;
+typedef struct Memsubfont Memsubfont;
+typedef struct Memlayer Memlayer;
+typedef struct Memcmap Memcmap;
+typedef struct Memdrawparam Memdrawparam;
+
+/*
+ * Memdata is allocated from main pool, but .data from the image pool.
+ * Memdata is allocated separately to permit patching its pointer after
+ * compaction when windows share the image data.
+ * The first word of data is a back pointer to the Memdata, to find
+ * The word to patch.
+ */
+
+struct Memdata
+{
+ u32int *base; /* allocated data pointer */
+ uchar *bdata; /* pointer to first byte of actual data; word-aligned */
+ int ref; /* number of Memimages using this data */
+ void* imref;
+ int allocd; /* is this malloc'd? */
+};
+
+enum {
+ Frepl = 1<<0, /* is replicated */
+ Fsimple = 1<<1, /* is 1x1 */
+ Fgrey = 1<<2, /* is grey */
+ Falpha = 1<<3, /* has explicit alpha */
+ Fcmap = 1<<4, /* has cmap channel */
+ Fbytes = 1<<5, /* has only 8-bit channels */
+};
+
+struct Memimage
+{
+ Rectangle r; /* rectangle in data area, local coords */
+ Rectangle clipr; /* clipping region */
+ int depth; /* number of bits of storage per pixel */
+ int nchan; /* number of channels */
+ u32int chan; /* channel descriptions */
+ Memcmap *cmap;
+
+ Memdata *data; /* pointer to data; shared by windows in this image */
+ int zero; /* data->bdata+zero==&byte containing (0,0) */
+ u32int width; /* width in words of a single scan line */
+ Memlayer *layer; /* nil if not a layer*/
+ u32int flags;
+ void *X;
+
+ int shift[NChan];
+ int mask[NChan];
+ int nbits[NChan];
+};
+
+struct Memcmap
+{
+ uchar cmap2rgb[3*256];
+ uchar rgb2cmap[16*16*16];
+};
+
+/*
+ * Subfonts
+ *
+ * given char c, Subfont *f, Fontchar *i, and Point p, one says
+ * i = f->info+c;
+ * draw(b, Rect(p.x+i->left, p.y+i->top,
+ * p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
+ * color, f->bits, Pt(i->x, i->top));
+ * p.x += i->width;
+ * to draw characters in the specified color (itself a Memimage) in Memimage b.
+ */
+
+struct Memsubfont
+{
+ char *name;
+ short n; /* number of chars in font */
+ uchar height; /* height of bitmap */
+ char ascent; /* top of bitmap to baseline */
+ Fontchar *info; /* n+1 character descriptors */
+ Memimage *bits; /* of font */
+};
+
+/*
+ * Encapsulated parameters and information for sub-draw routines.
+ */
+enum {
+ Simplesrc=1<<0,
+ Simplemask=1<<1,
+ Replsrc=1<<2,
+ Replmask=1<<3,
+ Fullmask=1<<4,
+};
+struct Memdrawparam
+{
+ Memimage *dst;
+ Rectangle r;
+ Memimage *src;
+ Rectangle sr;
+ Memimage *mask;
+ Rectangle mr;
+ int op;
+
+ u32int state;
+ u32int mval; /* if Simplemask, the mask pixel in mask format */
+ u32int mrgba; /* mval in rgba */
+ u32int sval; /* if Simplesrc, the source pixel in src format */
+ u32int srgba; /* sval in rgba */
+ u32int sdval; /* sval in dst format */
+};
+
+/*
+ * Memimage management
+ */
+
+extern Memimage* allocmemimage(Rectangle, u32int);
+extern Memimage* allocmemimaged(Rectangle, u32int, Memdata*, void*);
+extern Memimage* readmemimage(int);
+extern Memimage* creadmemimage(int);
+extern int writememimage(int, Memimage*);
+extern void freememimage(Memimage*);
+extern int loadmemimage(Memimage*, Rectangle, uchar*, int);
+extern int cloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern int unloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern u32int* wordaddr(Memimage*, Point);
+extern uchar* byteaddr(Memimage*, Point);
+extern int drawclip(Memimage*, Rectangle*, Memimage*, Point*,
+ Memimage*, Point*, Rectangle*, Rectangle*);
+extern void memfillcolor(Memimage*, u32int);
+extern int memsetchan(Memimage*, u32int);
+extern u32int pixelbits(Memimage*, Point);
+
+/*
+ * Graphics
+ */
+extern void memdraw(Memimage*, Rectangle, Memimage*, Point,
+ Memimage*, Point, int);
+extern void memline(Memimage*, Point, Point, int, int, int,
+ Memimage*, Point, int);
+extern void mempoly(Memimage*, Point*, int, int, int, int,
+ Memimage*, Point, int);
+extern void memfillpoly(Memimage*, Point*, int, int,
+ Memimage*, Point, int);
+extern void _memfillpolysc(Memimage*, Point*, int, int,
+ Memimage*, Point, int, int, int, int);
+extern void memimagedraw(Memimage*, Rectangle, Memimage*, Point,
+ Memimage*, Point, int);
+extern int hwdraw(Memdrawparam*);
+extern void memimageline(Memimage*, Point, Point, int, int, int,
+ Memimage*, Point, int);
+extern void _memimageline(Memimage*, Point, Point, int, int, int,
+ Memimage*, Point, Rectangle, int);
+extern Point memimagestring(Memimage*, Point, Memimage*, Point,
+ Memsubfont*, char*);
+extern void memellipse(Memimage*, Point, int, int, int,
+ Memimage*, Point, int);
+extern void memarc(Memimage*, Point, int, int, int, Memimage*,
+ Point, int, int, int);
+extern Rectangle memlinebbox(Point, Point, int, int, int);
+extern int memlineendsize(int);
+extern void _memmkcmap(void);
+extern void memimageinit(void);
+
+/*
+ * Subfont management
+ */
+extern Memsubfont* allocmemsubfont(char*, int, int, int, Fontchar*, Memimage*);
+extern Memsubfont* openmemsubfont(char*);
+extern void freememsubfont(Memsubfont*);
+extern Point memsubfontwidth(Memsubfont*, char*);
+extern Memsubfont* getmemdefont(void);
+
+/*
+ * Predefined
+ */
+extern Memimage* memwhite;
+extern Memimage* memblack;
+extern Memimage* memopaque;
+extern Memimage* memtransparent;
+extern Memcmap* memdefcmap;
+
+/*
+ * Kernel interface
+ */
+void memimagemove(void*, void*);
+
+/*
+ * Kernel cruft
+ */
+extern void rdb(void);
+extern int iprint(char*, ...);
+extern int drawdebug;
+
+/*
+ * For other implementations, like x11.
+ */
+extern void _memfillcolor(Memimage*, u32int);
+extern Memimage* _allocmemimage(Rectangle, u32int);
+extern int _cloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern int _loadmemimage(Memimage*, Rectangle, uchar*, int);
+extern void _freememimage(Memimage*);
+extern u32int _rgbatoimg(Memimage*, u32int);
+extern u32int _imgtorgba(Memimage*, u32int);
+extern u32int _pixelbits(Memimage*, Point);
+extern int _unloadmemimage(Memimage*, Rectangle, uchar*, int);
+extern Memdrawparam* _memimagedrawsetup(Memimage*,
+ Rectangle, Memimage*, Point, Memimage*,
+ Point, int);
+extern void _memimagedraw(Memdrawparam*);
+extern void _drawreplacescreenimage(Memimage*);
diff --git a/src/libdraw/memlayer.h b/src/libdraw/memlayer.h
new file mode 100644
index 00000000..36d87767
--- /dev/null
+++ b/src/libdraw/memlayer.h
@@ -0,0 +1,48 @@
+typedef struct Memscreen Memscreen;
+typedef void (*Refreshfn)(Memimage*, Rectangle, void*);
+
+struct Memscreen
+{
+ Memimage *frontmost; /* frontmost layer on screen */
+ Memimage *rearmost; /* rearmost layer on screen */
+ Memimage *image; /* upon which all layers are drawn */
+ Memimage *fill; /* if non-zero, picture to use when repainting */
+};
+
+struct Memlayer
+{
+ Rectangle screenr; /* true position of layer on screen */
+ Point delta; /* add delta to go from image coords to screen */
+ Memscreen *screen; /* screen this layer belongs to */
+ Memimage *front; /* window in front of this one */
+ Memimage *rear; /* window behind this one*/
+ int clear; /* layer is fully visible */
+ Memimage *save; /* save area for obscured parts */
+ Refreshfn refreshfn; /* function to call to refresh obscured parts if save==nil */
+ void *refreshptr; /* argument to refreshfn */
+};
+
+/*
+ * These functions accept local coordinates
+ */
+int memload(Memimage*, Rectangle, uchar*, int, int);
+int memunload(Memimage*, Rectangle, uchar*, int);
+
+/*
+ * All these functions accept screen coordinates, not local ones.
+ */
+void _memlayerop(void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), Memimage*, Rectangle, Rectangle, void*);
+Memimage* memlalloc(Memscreen*, Rectangle, Refreshfn, void*, u32int);
+void memldelete(Memimage*);
+void memlfree(Memimage*);
+void memltofront(Memimage*);
+void memltofrontn(Memimage**, int);
+void _memltofrontfill(Memimage*, int);
+void memltorear(Memimage*);
+void memltorearn(Memimage**, int);
+int memlsetrefresh(Memimage*, Refreshfn, void*);
+void memlhide(Memimage*, Rectangle);
+void memlexpose(Memimage*, Rectangle);
+void _memlsetclear(Memscreen*);
+int memlorigin(Memimage*, Point, Point);
+void memlnorefresh(Memimage*, Rectangle, void*);
diff --git a/src/libdraw/menuhit.c b/src/libdraw/menuhit.c
new file mode 100644
index 00000000..88304338
--- /dev/null
+++ b/src/libdraw/menuhit.c
@@ -0,0 +1,277 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+
+enum
+{
+ Margin = 4, /* outside to text */
+ Border = 2, /* outside to selection boxes */
+ Blackborder = 2, /* width of outlining border */
+ Vspacing = 2, /* extra spacing between lines of text */
+ Maxunscroll = 25, /* maximum #entries before scrolling turns on */
+ Nscroll = 20, /* number entries in scrolling part */
+ Scrollwid = 14, /* width of scroll bar */
+ Gap = 4, /* between text and scroll bar */
+};
+
+static Image *menutxt;
+static Image *back;
+static Image *high;
+static Image *bord;
+static Image *text;
+static Image *htext;
+
+static
+void
+menucolors(void)
+{
+ /* Main tone is greenish, with negative selection */
+ back = allocimagemix(display, DPalegreen, DWhite);
+ high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen); /* dark green */
+ bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen); /* not as dark green */
+ if(back==nil || high==nil || bord==nil)
+ goto Error;
+ text = display->black;
+ htext = back;
+ return;
+
+ Error:
+ freeimage(back);
+ freeimage(high);
+ freeimage(bord);
+ back = display->white;
+ high = display->black;
+ bord = display->black;
+ text = display->black;
+ htext = display->white;
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the rectangle, including its black edge, holding element i.
+ */
+static Rectangle
+menurect(Rectangle r, int i)
+{
+ if(i < 0)
+ return Rect(0, 0, 0, 0);
+ r.min.y += (font->height+Vspacing)*i;
+ r.max.y = r.min.y+font->height+Vspacing;
+ return insetrect(r, Border-Margin);
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the element number containing p.
+ */
+static int
+menusel(Rectangle r, Point p)
+{
+ r = insetrect(r, Margin);
+ if(!ptinrect(p, r))
+ return -1;
+ return (p.y-r.min.y)/(font->height+Vspacing);
+}
+
+static
+void
+paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
+{
+ char *item;
+ Rectangle r;
+ Point pt;
+
+ if(i < 0)
+ return;
+ r = menurect(textr, i);
+ if(restore){
+ draw(m, r, restore, nil, restore->r.min);
+ return;
+ }
+ if(save)
+ draw(save, save->r, m, nil, r.min);
+ item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
+ pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
+ pt.y = textr.min.y+i*(font->height+Vspacing);
+ draw(m, r, highlight? high : back, nil, pt);
+ string(m, pt, highlight? htext : text, pt, font, item);
+}
+
+/*
+ * menur is a rectangle holding all the highlightable text elements.
+ * track mouse while inside the box, return what's selected when button
+ * is raised, -1 as soon as it leaves box.
+ * invariant: nothing is highlighted on entry or exit.
+ */
+static int
+menuscan(Image *m, Menu *menu, int but, Mousectl *mc, Rectangle textr, int off, int lasti, Image *save)
+{
+ int i;
+
+ paintitem(m, menu, textr, off, lasti, 1, save, nil);
+ for(readmouse(mc); mc->m.buttons & (1<<(but-1)); readmouse(mc)){
+ i = menusel(textr, mc->m.xy);
+ if(i != -1 && i == lasti)
+ continue;
+ paintitem(m, menu, textr, off, lasti, 0, nil, save);
+ if(i == -1)
+ return i;
+ lasti = i;
+ paintitem(m, menu, textr, off, lasti, 1, save, nil);
+ }
+ return lasti;
+}
+
+static void
+menupaint(Image *m, Menu *menu, Rectangle textr, int off, int nitemdrawn)
+{
+ int i;
+
+ draw(m, insetrect(textr, Border-Margin), back, nil, ZP);
+ for(i = 0; i<nitemdrawn; i++)
+ paintitem(m, menu, textr, off, i, 0, nil, nil);
+}
+
+static void
+menuscrollpaint(Image *m, Rectangle scrollr, int off, int nitem, int nitemdrawn)
+{
+ Rectangle r;
+
+ draw(m, scrollr, back, nil, ZP);
+ r.min.x = scrollr.min.x;
+ r.max.x = scrollr.max.x;
+ r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
+ r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
+ if(r.max.y < r.min.y+2)
+ r.max.y = r.min.y+2;
+ border(m, r, 1, bord, ZP);
+ if(menutxt == 0)
+ menutxt = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkgreen); /* border color; BUG? */
+ if(menutxt)
+ draw(m, insetrect(r, 1), menutxt, nil, ZP);
+}
+
+int
+menuhit(int but, Mousectl *mc, Menu *menu, Screen *scr)
+{
+ int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
+ int scrolling;
+ Rectangle r, menur, sc, textr, scrollr;
+ Image *b, *save, *backup;
+ Point pt;
+ char *item;
+
+ if(back == nil)
+ menucolors();
+ sc = screen->clipr;
+ replclipr(screen, 0, screen->r);
+ maxwid = 0;
+ for(nitem = 0;
+ item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
+ nitem++){
+ i = stringwidth(font, item);
+ if(i > maxwid)
+ maxwid = i;
+ }
+ if(menu->lasthit<0 || menu->lasthit>=nitem)
+ menu->lasthit = 0;
+ screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
+ if(nitem>Maxunscroll || nitem>screenitem){
+ scrolling = 1;
+ nitemdrawn = Nscroll;
+ if(nitemdrawn > screenitem)
+ nitemdrawn = screenitem;
+ wid = maxwid + Gap + Scrollwid;
+ off = menu->lasthit - nitemdrawn/2;
+ if(off < 0)
+ off = 0;
+ if(off > nitem-nitemdrawn)
+ off = nitem-nitemdrawn;
+ lasti = menu->lasthit-off;
+ }else{
+ scrolling = 0;
+ nitemdrawn = nitem;
+ wid = maxwid;
+ off = 0;
+ lasti = menu->lasthit;
+ }
+ r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
+ r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
+ r = rectaddpt(r, mc->m.xy);
+ pt = ZP;
+ if(r.max.x>screen->r.max.x)
+ pt.x = screen->r.max.x-r.max.x;
+ if(r.max.y>screen->r.max.y)
+ pt.y = screen->r.max.y-r.max.y;
+ if(r.min.x<screen->r.min.x)
+ pt.x = screen->r.min.x-r.min.x;
+ if(r.min.y<screen->r.min.y)
+ pt.y = screen->r.min.y-r.min.y;
+ menur = rectaddpt(r, pt);
+ textr.max.x = menur.max.x-Margin;
+ textr.min.x = textr.max.x-maxwid;
+ textr.min.y = menur.min.y+Margin;
+ textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
+ if(scrolling){
+ scrollr = insetrect(menur, Border);
+ scrollr.max.x = scrollr.min.x+Scrollwid;
+ }else
+ scrollr = Rect(0, 0, 0, 0);
+
+ if(scr){
+ b = allocwindow(scr, menur, Refbackup, DWhite);
+ if(b == nil)
+ b = screen;
+ backup = nil;
+ }else{
+ b = screen;
+ backup = allocimage(display, menur, screen->chan, 0, -1);
+ if(backup)
+ draw(backup, menur, screen, nil, menur.min);
+ }
+ draw(b, menur, back, nil, ZP);
+ border(b, menur, Blackborder, bord, ZP);
+ save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
+ r = menurect(textr, lasti);
+ moveto(mc, divpt(addpt(r.min, r.max), 2));
+ menupaint(b, menu, textr, off, nitemdrawn);
+ if(scrolling)
+ menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+ while(mc->m.buttons & (1<<(but-1))){
+ lasti = menuscan(b, menu, but, mc, textr, off, lasti, save);
+ if(lasti >= 0)
+ break;
+ while(!ptinrect(mc->m.xy, textr) && (mc->m.buttons & (1<<(but-1)))){
+ if(scrolling && ptinrect(mc->m.xy, scrollr)){
+ noff = ((mc->m.xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
+ noff -= nitemdrawn/2;
+ if(noff < 0)
+ noff = 0;
+ if(noff > nitem-nitemdrawn)
+ noff = nitem-nitemdrawn;
+ if(noff != off){
+ off = noff;
+ menupaint(b, menu, textr, off, nitemdrawn);
+ menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+ }
+ }
+ readmouse(mc);
+ }
+ }
+ if(b != screen)
+ freeimage(b);
+ if(backup){
+ draw(screen, menur, backup, nil, menur.min);
+ freeimage(backup);
+ }
+ freeimage(save);
+ replclipr(screen, 0, sc);
+ flushimage(display, 1);
+ if(lasti >= 0){
+ menu->lasthit = lasti+off;
+ return menu->lasthit;
+ }
+ return -1;
+}
diff --git a/src/libdraw/mkfile b/src/libdraw/mkfile
new file mode 100644
index 00000000..bb99a25a
--- /dev/null
+++ b/src/libdraw/mkfile
@@ -0,0 +1 @@
+<../libutf/mkfile
diff --git a/src/libdraw/ml-draw.c b/src/libdraw/ml-draw.c
new file mode 100644
index 00000000..c352a0b2
--- /dev/null
+++ b/src/libdraw/ml-draw.c
@@ -0,0 +1,192 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+struct Draw
+{
+ Point deltas;
+ Point deltam;
+ Memlayer *dstlayer;
+ Memimage *src;
+ Memimage *mask;
+ int op;
+};
+
+static
+void
+ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ struct Draw *d;
+ Point p0, p1;
+ Rectangle oclipr, srcr, r, mr;
+ int ok;
+
+ d = etc;
+ if(insave && d->dstlayer->save==nil)
+ return;
+
+ p0 = addpt(screenr.min, d->deltas);
+ p1 = addpt(screenr.min, d->deltam);
+
+ if(insave){
+ r = rectsubpt(screenr, d->dstlayer->delta);
+ clipr = rectsubpt(clipr, d->dstlayer->delta);
+ }else
+ r = screenr;
+
+ /* now in logical coordinates */
+
+ /* clipr may have narrowed what we should draw on, so clip if necessary */
+ if(!rectinrect(r, clipr)){
+ oclipr = dst->clipr;
+ dst->clipr = clipr;
+ ok = drawclip(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr);
+ dst->clipr = oclipr;
+ if(!ok)
+ return;
+ }
+ memdraw(dst, r, d->src, p0, d->mask, p1, d->op);
+}
+
+void
+memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op)
+{
+ struct Draw d;
+ Rectangle srcr, tr, mr;
+ Memlayer *dl, *sl;
+
+ if(drawdebug)
+ iprint("memdraw %p %R %p %P %p %P\n", dst, r, src, p0, mask, p1);
+
+ if(mask == nil)
+ mask = memopaque;
+
+ if(mask->layer){
+if(drawdebug) iprint("mask->layer != nil\n");
+ return; /* too hard, at least for now */
+ }
+
+ Top:
+ if(dst->layer==nil && src->layer==nil){
+ memimagedraw(dst, r, src, p0, mask, p1, op);
+ return;
+ }
+
+ if(drawclip(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0){
+if(drawdebug) iprint("drawclip dstcr %R srccr %R maskcr %R\n", dst->clipr, src->clipr, mask->clipr);
+ return;
+ }
+
+ /*
+ * Convert to screen coordinates.
+ */
+ dl = dst->layer;
+ if(dl != nil){
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ }
+ Clearlayer:
+ if(dl!=nil && dl->clear){
+ if(src == dst){
+ p0.x += dl->delta.x;
+ p0.y += dl->delta.y;
+ src = dl->screen->image;
+ }
+ dst = dl->screen->image;
+ goto Top;
+ }
+
+ sl = src->layer;
+ if(sl != nil){
+ p0.x += sl->delta.x;
+ p0.y += sl->delta.y;
+ srcr.min.x += sl->delta.x;
+ srcr.min.y += sl->delta.y;
+ srcr.max.x += sl->delta.x;
+ srcr.max.y += sl->delta.y;
+ }
+
+ /*
+ * Now everything is in screen coordinates.
+ * mask is an image. dst and src are images or obscured layers.
+ */
+
+ /*
+ * if dst and src are the same layer, just draw in save area and expose.
+ */
+ if(dl!=nil && dst==src){
+ if(dl->save == nil)
+ return; /* refresh function makes this case unworkable */
+ if(rectXrect(r, srcr)){
+ tr = r;
+ if(srcr.min.x < tr.min.x){
+ p1.x += tr.min.x - srcr.min.x;
+ tr.min.x = srcr.min.x;
+ }
+ if(srcr.min.y < tr.min.y){
+ p1.y += tr.min.x - srcr.min.x;
+ tr.min.y = srcr.min.y;
+ }
+ if(srcr.max.x > tr.max.x)
+ tr.max.x = srcr.max.x;
+ if(srcr.max.y > tr.max.y)
+ tr.max.y = srcr.max.y;
+ memlhide(dst, tr);
+ }else{
+ memlhide(dst, r);
+ memlhide(dst, srcr);
+ }
+ memdraw(dl->save, rectsubpt(r, dl->delta), dl->save,
+ subpt(srcr.min, src->layer->delta), mask, p1, op);
+ memlexpose(dst, r);
+ return;
+ }
+
+ if(sl){
+ if(sl->clear){
+ src = sl->screen->image;
+ if(dl != nil){
+ r.min.x -= dl->delta.x;
+ r.min.y -= dl->delta.y;
+ r.max.x -= dl->delta.x;
+ r.max.y -= dl->delta.y;
+ }
+ goto Top;
+ }
+ /* relatively rare case; use save area */
+ if(sl->save == nil)
+ return; /* refresh function makes this case unworkable */
+ memlhide(src, srcr);
+ /* convert back to logical coordinates */
+ p0.x -= sl->delta.x;
+ p0.y -= sl->delta.y;
+ srcr.min.x -= sl->delta.x;
+ srcr.min.y -= sl->delta.y;
+ srcr.max.x -= sl->delta.x;
+ srcr.max.y -= sl->delta.y;
+ src = src->layer->save;
+ }
+
+ /*
+ * src is now an image. dst may be an image or a clear layer
+ */
+ if(dst->layer==nil)
+ goto Top;
+ if(dst->layer->clear)
+ goto Clearlayer;
+
+ /*
+ * dst is an obscured layer
+ */
+ d.deltas = subpt(p0, r.min);
+ d.deltam = subpt(p1, r.min);
+ d.dstlayer = dl;
+ d.src = src;
+ d.op = op;
+ d.mask = mask;
+ _memlayerop(ldrawop, dst, r, r, &d);
+}
diff --git a/src/libdraw/ml-lalloc.c b/src/libdraw/ml-lalloc.c
new file mode 100644
index 00000000..17d934d3
--- /dev/null
+++ b/src/libdraw/ml-lalloc.c
@@ -0,0 +1,79 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+Memimage*
+memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshptr, u32int val)
+{
+ Memlayer *l;
+ Memimage *n;
+ static Memimage *paint;
+
+ if(paint == nil){
+ paint = allocmemimage(Rect(0,0,1,1), RGBA32);
+ if(paint == nil)
+ return nil;
+ paint->flags |= Frepl;
+ paint->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
+ }
+
+ n = allocmemimaged(screenr, s->image->chan, s->image->data, nil);
+ if(n == nil)
+ return nil;
+ l = malloc(sizeof(Memlayer));
+ if(l == nil){
+ free(n);
+ return nil;
+ }
+
+ l->screen = s;
+ if(refreshfn)
+ l->save = nil;
+ else{
+ l->save = allocmemimage(screenr, s->image->chan);
+ if(l->save == nil){
+ free(l);
+ free(n);
+ return nil;
+ }
+ /* allocmemimage doesn't initialize memory; this paints save area */
+ if(val != DNofill)
+ memfillcolor(l->save, val);
+ }
+ l->refreshfn = refreshfn;
+ l->refreshptr = nil; /* don't set it until we're done */
+ l->screenr = screenr;
+ l->delta = Pt(0,0);
+
+ n->data->ref++;
+ n->zero = s->image->zero;
+ n->width = s->image->width;
+ n->layer = l;
+
+ /* start with new window behind all existing ones */
+ l->front = s->rearmost;
+ l->rear = nil;
+ if(s->rearmost)
+ s->rearmost->layer->rear = n;
+ s->rearmost = n;
+ if(s->frontmost == nil)
+ s->frontmost = n;
+ l->clear = 0;
+
+ /* now pull new window to front */
+ _memltofrontfill(n, val != DNofill);
+ l->refreshptr = refreshptr;
+
+ /*
+ * paint with requested color; previously exposed areas are already right
+ * if this window has backing store, but just painting the whole thing is simplest.
+ */
+ if(val != DNofill){
+ memsetchan(paint, n->chan);
+ memfillcolor(paint, val);
+ memdraw(n, n->r, paint, n->r.min, nil, n->r.min, S);
+ }
+ return n;
+}
diff --git a/src/libdraw/ml-layerop.c b/src/libdraw/ml-layerop.c
new file mode 100644
index 00000000..27fdcfc5
--- /dev/null
+++ b/src/libdraw/ml-layerop.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y), clipr, etc, front->layer->rear);
+
+static void
+_layerop(
+ void (*fn)(Memimage*, Rectangle, Rectangle, void*, int),
+ Memimage *i,
+ Rectangle r,
+ Rectangle clipr,
+ void *etc,
+ Memimage *front)
+{
+ Rectangle fr;
+
+ Top:
+ if(front == i){
+ /* no one is in front of this part of window; use the screen */
+ fn(i->layer->screen->image, r, clipr, etc, 0);
+ return;
+ }
+ fr = front->layer->screenr;
+ if(rectXrect(r, fr) == 0){
+ /* r doesn't touch this window; continue on next rearmost */
+ // assert(front && front->layer && front->layer->screen && front->layer->rear);
+ front = front->layer->rear;
+ goto Top;
+ }
+ if(fr.max.y < r.max.y){
+ RECUR(r.min, fr.max, r.max, r.max);
+ r.max.y = fr.max.y;
+ }
+ if(r.min.y < fr.min.y){
+ RECUR(r.min, r.min, r.max, fr.min);
+ r.min.y = fr.min.y;
+ }
+ if(fr.max.x < r.max.x){
+ RECUR(fr.max, r.min, r.max, r.max);
+ r.max.x = fr.max.x;
+ }
+ if(r.min.x < fr.min.x){
+ RECUR(r.min, r.min, fr.min, r.max);
+ r.min.x = fr.min.x;
+ }
+ /* r is covered by front, so put in save area */
+ (*fn)(i->layer->save, r, clipr, etc, 1);
+}
+
+/*
+ * Assumes incoming rectangle has already been clipped to i's logical r and clipr
+ */
+void
+_memlayerop(
+ void (*fn)(Memimage*, Rectangle, Rectangle, void*, int),
+ Memimage *i,
+ Rectangle screenr, /* clipped to window boundaries */
+ Rectangle clipr, /* clipped also to clipping rectangles of hierarchy */
+ void *etc)
+{
+ Memlayer *l;
+ Rectangle r, scr;
+
+ l = i->layer;
+ if(!rectclip(&screenr, l->screenr))
+ return;
+ if(l->clear){
+ fn(l->screen->image, screenr, clipr, etc, 0);
+ return;
+ }
+ r = screenr;
+ scr = l->screen->image->clipr;
+
+ /*
+ * Do the piece on the screen
+ */
+ if(rectclip(&screenr, scr))
+ _layerop(fn, i, screenr, clipr, etc, l->screen->frontmost);
+ if(rectinrect(r, scr))
+ return;
+
+ /*
+ * Do the piece off the screen
+ */
+ if(!rectXrect(r, scr)){
+ /* completely offscreen; easy */
+ fn(l->save, r, clipr, etc, 1);
+ return;
+ }
+ if(r.min.y < scr.min.y){
+ /* above screen */
+ fn(l->save, Rect(r.min.x, r.min.y, r.max.x, scr.min.y), clipr, etc, 1);
+ r.min.y = scr.min.y;
+ }
+ if(r.max.y > scr.max.y){
+ /* below screen */
+ fn(l->save, Rect(r.min.x, scr.max.y, r.max.x, r.max.y), clipr, etc, 1);
+ r.max.y = scr.max.y;
+ }
+ if(r.min.x < scr.min.x){
+ /* left of screen */
+ fn(l->save, Rect(r.min.x, r.min.y, scr.min.x, r.max.y), clipr, etc, 1);
+ r.min.x = scr.min.x;
+ }
+ if(r.max.x > scr.max.x){
+ /* right of screen */
+ fn(l->save, Rect(scr.max.x, r.min.y, r.max.x, r.max.y), clipr, etc, 1);
+ }
+}
diff --git a/src/libdraw/ml-ldelete.c b/src/libdraw/ml-ldelete.c
new file mode 100644
index 00000000..34cd6ead
--- /dev/null
+++ b/src/libdraw/ml-ldelete.c
@@ -0,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+void
+memldelete(Memimage *i)
+{
+ Memscreen *s;
+ Memlayer *l;
+
+ l = i->layer;
+ /* free backing store and disconnect refresh, to make pushback fast */
+ freememimage(l->save);
+ l->save = nil;
+ l->refreshptr = nil;
+ memltorear(i);
+
+ /* window is now the rearmost; clean up screen structures and deallocate */
+ s = i->layer->screen;
+ if(s->fill){
+ i->clipr = i->r;
+ memdraw(i, i->r, s->fill, i->r.min, nil, i->r.min, S);
+ }
+ if(l->front){
+ l->front->layer->rear = nil;
+ s->rearmost = l->front;
+ }else{
+ s->frontmost = nil;
+ s->rearmost = nil;
+ }
+ free(l);
+ freememimage(i);
+}
+
+/*
+ * Just free the data structures, don't do graphics
+ */
+void
+memlfree(Memimage *i)
+{
+ Memlayer *l;
+
+ l = i->layer;
+ freememimage(l->save);
+ free(l);
+ freememimage(i);
+}
+
+void
+_memlsetclear(Memscreen *s)
+{
+ Memimage *i, *j;
+ Memlayer *l;
+
+ for(i=s->rearmost; i; i=i->layer->front){
+ l = i->layer;
+ l->clear = rectinrect(l->screenr, l->screen->image->clipr);
+ if(l->clear)
+ for(j=l->front; j; j=j->layer->front)
+ if(rectXrect(l->screenr, j->layer->screenr)){
+ l->clear = 0;
+ break;
+ }
+ }
+}
diff --git a/src/libdraw/ml-lhide.c b/src/libdraw/ml-lhide.c
new file mode 100644
index 00000000..d6aaa55f
--- /dev/null
+++ b/src/libdraw/ml-lhide.c
@@ -0,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Hide puts that portion of screenr now on the screen into the window's save area.
+ * Expose puts that portion of screenr now in the save area onto the screen.
+ *
+ * Hide and Expose both require that the layer structures in the screen
+ * match the geometry they are being asked to update, that is, they update the
+ * save area (hide) or screen (expose) based on what those structures tell them.
+ * This means they must be called at the correct time during window shuffles.
+ */
+
+static
+void
+lhideop(Memimage *src, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ Rectangle r;
+ Memlayer *l;
+
+ USED(clipr.min.x);
+ USED(insave);
+ l = etc;
+ if(src != l->save){ /* do nothing if src is already in save area */
+ r = rectsubpt(screenr, l->delta);
+ memdraw(l->save, r, src, screenr.min, nil, screenr.min, S);
+ }
+}
+
+void
+memlhide(Memimage *i, Rectangle screenr)
+{
+ if(i->layer->save == nil)
+ return;
+ if(rectclip(&screenr, i->layer->screen->image->r) == 0)
+ return;
+ _memlayerop(lhideop, i, screenr, screenr, i->layer);
+}
+
+static
+void
+lexposeop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ Memlayer *l;
+ Rectangle r;
+
+ USED(clipr.min.x);
+ if(insave) /* if dst is save area, don't bother */
+ return;
+ l = etc;
+ r = rectsubpt(screenr, l->delta);
+ if(l->save)
+ memdraw(dst, screenr, l->save, r.min, nil, r.min, S);
+ else
+ l->refreshfn(dst, r, l->refreshptr);
+}
+
+void
+memlexpose(Memimage *i, Rectangle screenr)
+{
+ if(rectclip(&screenr, i->layer->screen->image->r) == 0)
+ return;
+ _memlayerop(lexposeop, i, screenr, screenr, i->layer);
+}
diff --git a/src/libdraw/ml-line.c b/src/libdraw/ml-line.c
new file mode 100644
index 00000000..8c09a535
--- /dev/null
+++ b/src/libdraw/ml-line.c
@@ -0,0 +1,122 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+struct Lline
+{
+ Point p0;
+ Point p1;
+ Point delta;
+ int end0;
+ int end1;
+ int radius;
+ Point sp;
+ Memlayer *dstlayer;
+ Memimage *src;
+ int op;
+};
+
+static void llineop(Memimage*, Rectangle, Rectangle, void*, int);
+
+static
+void
+_memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op)
+{
+ Rectangle r;
+ struct Lline ll;
+ Point d;
+ int srcclipped;
+ Memlayer *dl;
+
+ if(radius < 0)
+ return;
+ if(src->layer) /* can't draw line with layered source */
+ return;
+ srcclipped = 0;
+
+ Top:
+ dl = dst->layer;
+ if(dl == nil){
+ _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op);
+ return;
+ }
+ if(!srcclipped){
+ d = subpt(sp, p0);
+ if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0)
+ return;
+ if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0)
+ return;
+ srcclipped = 1;
+ }
+
+ /* dst is known to be a layer */
+ p0.x += dl->delta.x;
+ p0.y += dl->delta.y;
+ p1.x += dl->delta.x;
+ p1.y += dl->delta.y;
+ clipr.min.x += dl->delta.x;
+ clipr.min.y += dl->delta.y;
+ clipr.max.x += dl->delta.x;
+ clipr.max.y += dl->delta.y;
+ if(dl->clear){
+ dst = dst->layer->screen->image;
+ goto Top;
+ }
+
+ /* XXX */
+ /* this is not the correct set of tests */
+// if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3)
+// return;
+
+ /* can't use sutherland-cohen clipping because lines are wide */
+ r = memlinebbox(p0, p1, end0, end1, radius);
+ /*
+ * r is now a bounding box for the line;
+ * use it as a clipping rectangle for subdivision
+ */
+ if(rectclip(&r, clipr) == 0)
+ return;
+ ll.p0 = p0;
+ ll.p1 = p1;
+ ll.end0 = end0;
+ ll.end1 = end1;
+ ll.sp = sp;
+ ll.dstlayer = dst->layer;
+ ll.src = src;
+ ll.radius = radius;
+ ll.delta = dl->delta;
+ ll.op = op;
+ _memlayerop(llineop, dst, r, r, &ll);
+}
+
+static
+void
+llineop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave)
+{
+ struct Lline *ll;
+ Point p0, p1;
+
+ USED(screenr.min.x);
+ ll = etc;
+ if(insave && ll->dstlayer->save==nil)
+ return;
+ if(!rectclip(&clipr, screenr))
+ return;
+ if(insave){
+ p0 = subpt(ll->p0, ll->delta);
+ p1 = subpt(ll->p1, ll->delta);
+ clipr = rectsubpt(clipr, ll->delta);
+ }else{
+ p0 = ll->p0;
+ p1 = ll->p1;
+ }
+ _memline(dst, p0, p1, ll->end0, ll->end1, ll->radius, ll->src, ll->sp, clipr, ll->op);
+}
+
+void
+memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op)
+{
+ _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op);
+}
diff --git a/src/libdraw/ml-load.c b/src/libdraw/ml-load.c
new file mode 100644
index 00000000..d211564b
--- /dev/null
+++ b/src/libdraw/ml-load.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memload(Memimage *dst, Rectangle r, uchar *data, int n, int iscompressed)
+{
+ int (*loadfn)(Memimage*, Rectangle, uchar*, int);
+ Memimage *tmp;
+ Memlayer *dl;
+ Rectangle lr;
+ int dx;
+
+ loadfn = loadmemimage;
+ if(iscompressed)
+ loadfn = cloadmemimage;
+
+ Top:
+ dl = dst->layer;
+ if(dl == nil)
+ return loadfn(dst, r, data, n);
+
+ /*
+ * Convert to screen coordinates.
+ */
+ lr = r;
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ dx = dl->delta.x&(7/dst->depth);
+ if(dl->clear && dx==0){
+ dst = dl->screen->image;
+ goto Top;
+ }
+
+ /*
+ * dst is an obscured layer or data is unaligned
+ */
+ if(dl->save && dx==0){
+ n = loadfn(dl->save, lr, data, n);
+ if(n > 0)
+ memlexpose(dst, r);
+ return n;
+ }
+ tmp = allocmemimage(lr, dst->chan);
+ if(tmp == nil)
+ return -1;
+ n = loadfn(tmp, lr, data, n);
+ memdraw(dst, lr, tmp, lr.min, nil, lr.min, S);
+ freememimage(tmp);
+ return n;
+}
diff --git a/src/libdraw/ml-lorigin.c b/src/libdraw/ml-lorigin.c
new file mode 100644
index 00000000..0926ee8d
--- /dev/null
+++ b/src/libdraw/ml-lorigin.c
@@ -0,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Place i so i->r.min = log, i->layer->screenr.min == scr.
+*/
+int
+memlorigin(Memimage *i, Point log, Point scr)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *t, *shad, *nsave;
+ Rectangle x, newr, oldr;
+ Point delta;
+ int overlap, eqlog, eqscr, wasclear;
+
+ l = i->layer;
+ s = l->screen;
+ oldr = l->screenr;
+ newr = Rect(scr.x, scr.y, scr.x+Dx(oldr), scr.y+Dy(oldr));
+ eqscr = eqpt(scr, oldr.min);
+ eqlog = eqpt(log, i->r.min);
+ if(eqscr && eqlog)
+ return 0;
+ nsave = nil;
+ if(eqlog==0 && l->save!=nil){
+ nsave = allocmemimage(Rect(log.x, log.y, log.x+Dx(oldr), log.y+Dy(oldr)), i->chan);
+ if(nsave == nil)
+ return -1;
+ }
+
+ /*
+ * Bring it to front and move logical coordinate system.
+ */
+ memltofront(i);
+ wasclear = l->clear;
+ if(nsave){
+ if(!wasclear)
+ memimagedraw(nsave, nsave->r, l->save, l->save->r.min, nil, Pt(0,0), S);
+ freememimage(l->save);
+ l->save = nsave;
+ }
+ delta = subpt(log, i->r.min);
+ i->r = rectaddpt(i->r, delta);
+ i->clipr = rectaddpt(i->clipr, delta);
+ l->delta = subpt(l->screenr.min, i->r.min);
+ if(eqscr)
+ return 0;
+
+ /*
+ * To clean up old position, make a shadow window there, don't paint it,
+ * push it behind this one, and (later) delete it. Because the refresh function
+ * for this fake window is a no-op, this will cause no graphics action except
+ * to restore the background and expose the windows previously hidden.
+ */
+ shad = memlalloc(s, oldr, memlnorefresh, nil, DNofill);
+ if(shad == nil)
+ return -1;
+ s->frontmost = i;
+ if(s->rearmost == i)
+ s->rearmost = shad;
+ else
+ l->rear->layer->front = shad;
+ shad->layer->front = i;
+ shad->layer->rear = l->rear;
+ l->rear = shad;
+ l->front = nil;
+ shad->layer->clear = 0;
+
+ /*
+ * Shadow is now holding down the fort at the old position.
+ * Move the window and hide things obscured by new position.
+ */
+ for(t=l->rear->layer->rear; t!=nil; t=t->layer->rear){
+ x = newr;
+ overlap = rectclip(&x, t->layer->screenr);
+ if(overlap){
+ memlhide(t, x);
+ t->layer->clear = 0;
+ }
+ }
+ l->screenr = newr;
+ l->delta = subpt(scr, i->r.min);
+ l->clear = rectinrect(newr, l->screen->image->clipr);
+
+ /*
+ * Everything's covered. Copy to new position and delete shadow window.
+ */
+ if(wasclear)
+ memdraw(s->image, newr, s->image, oldr.min, nil, Pt(0,0), S);
+ else
+ memlexpose(i, newr);
+ memldelete(shad);
+
+ return 1;
+}
+
+void
+memlnorefresh(Memimage *l, Rectangle r, void *v)
+{
+ USED(l);
+ USED(r.min.x);
+ USED(v);
+}
diff --git a/src/libdraw/ml-lsetrefresh.c b/src/libdraw/ml-lsetrefresh.c
new file mode 100644
index 00000000..44079be2
--- /dev/null
+++ b/src/libdraw/ml-lsetrefresh.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memlsetrefresh(Memimage *i, Refreshfn fn, void *ptr)
+{
+ Memlayer *l;
+
+ l = i->layer;
+ if(l->refreshfn!=nil && fn!=nil){ /* just change functions */
+ l->refreshfn = fn;
+ l->refreshptr = ptr;
+ return 1;
+ }
+
+ if(l->refreshfn == nil){ /* is using backup image; just free it */
+ freememimage(l->save);
+ l->save = nil;
+ l->refreshfn = fn;
+ l->refreshptr = ptr;
+ return 1;
+ }
+
+ l->save = allocmemimage(i->r, i->chan);
+ if(l->save == nil)
+ return 0;
+ /* easiest way is just to update the entire save area */
+ l->refreshfn(i, i->r, l->refreshptr);
+ l->refreshfn = nil;
+ l->refreshptr = nil;
+ return 1;
+}
diff --git a/src/libdraw/ml-ltofront.c b/src/libdraw/ml-ltofront.c
new file mode 100644
index 00000000..447b40bd
--- /dev/null
+++ b/src/libdraw/ml-ltofront.c
@@ -0,0 +1,80 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+/*
+ * Pull i towards top of screen, just behind front
+*/
+static
+void
+_memltofront(Memimage *i, Memimage *front, int fill)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *f, *ff, *rr;
+ Rectangle x;
+ int overlap;
+
+ l = i->layer;
+ s = l->screen;
+ while(l->front != front){
+ f = l->front;
+ x = l->screenr;
+ overlap = rectclip(&x, f->layer->screenr);
+ if(overlap){
+ memlhide(f, x);
+ f->layer->clear = 0;
+ }
+ /* swap l and f in screen's list */
+ ff = f->layer->front;
+ rr = l->rear;
+ if(ff == nil)
+ s->frontmost = i;
+ else
+ ff->layer->rear = i;
+ if(rr == nil)
+ s->rearmost = f;
+ else
+ rr->layer->front = f;
+ l->front = ff;
+ l->rear = f;
+ f->layer->front = i;
+ f->layer->rear = rr;
+ if(overlap && fill)
+ memlexpose(i, x);
+ }
+}
+
+void
+_memltofrontfill(Memimage *i, int fill)
+{
+ _memltofront(i, nil, fill);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltofront(Memimage *i)
+{
+ _memltofront(i, nil, 1);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltofrontn(Memimage **ip, int n)
+{
+ Memimage *i, *front;
+ Memscreen *s;
+
+ if(n == 0)
+ return;
+ front = nil;
+ while(--n >= 0){
+ i = *ip++;
+ _memltofront(i, front, 1);
+ front = i;
+ }
+ s = front->layer->screen;
+ _memlsetclear(s);
+}
diff --git a/src/libdraw/ml-ltorear.c b/src/libdraw/ml-ltorear.c
new file mode 100644
index 00000000..d53e8cc9
--- /dev/null
+++ b/src/libdraw/ml-ltorear.c
@@ -0,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+void
+_memltorear(Memimage *i, Memimage *rear)
+{
+ Memlayer *l;
+ Memscreen *s;
+ Memimage *f, *r, *rr;
+ Rectangle x;
+ int overlap;
+
+ l = i->layer;
+ s = l->screen;
+ while(l->rear != rear){
+ r = l->rear;
+ x = l->screenr;
+ overlap = rectclip(&x, r->layer->screenr);
+ if(overlap){
+ memlhide(i, x);
+ l->clear = 0;
+ }
+ /* swap l and r in screen's list */
+ rr = r->layer->rear;
+ f = l->front;
+ if(rr == nil)
+ s->rearmost = i;
+ else
+ rr->layer->front = i;
+ if(f == nil)
+ s->frontmost = r;
+ else
+ f->layer->rear = r;
+ l->rear = rr;
+ l->front = r;
+ r->layer->rear = i;
+ r->layer->front = f;
+ if(overlap)
+ memlexpose(r, x);
+ }
+}
+
+void
+memltorear(Memimage *i)
+{
+ _memltorear(i, nil);
+ _memlsetclear(i->layer->screen);
+}
+
+void
+memltorearn(Memimage **ip, int n)
+{
+ Memimage *i, *rear;
+ Memscreen *s;
+
+ if(n == 0)
+ return;
+ rear = nil;
+ while(--n >= 0){
+ i = *ip++;
+ _memltorear(i, rear);
+ rear = i;
+ }
+ s = rear->layer->screen;
+ _memlsetclear(s);
+}
diff --git a/src/libdraw/ml-unload.c b/src/libdraw/ml-unload.c
new file mode 100644
index 00000000..23a8b290
--- /dev/null
+++ b/src/libdraw/ml-unload.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <memlayer.h>
+
+int
+memunload(Memimage *src, Rectangle r, uchar *data, int n)
+{
+ Memimage *tmp;
+ Memlayer *dl;
+ Rectangle lr;
+ int dx;
+
+ Top:
+ dl = src->layer;
+ if(dl == nil)
+ return unloadmemimage(src, r, data, n);
+
+ /*
+ * Convert to screen coordinates.
+ */
+ lr = r;
+ r.min.x += dl->delta.x;
+ r.min.y += dl->delta.y;
+ r.max.x += dl->delta.x;
+ r.max.y += dl->delta.y;
+ dx = dl->delta.x&(7/src->depth);
+ if(dl->clear && dx==0){
+ src = dl->screen->image;
+ goto Top;
+ }
+
+ /*
+ * src is an obscured layer or data is unaligned
+ */
+ if(dl->save && dx==0){
+ if(dl->refreshfn != nil)
+ return -1; /* can't unload window if it's not Refbackup */
+ if(n > 0)
+ memlhide(src, r);
+ n = unloadmemimage(dl->save, lr, data, n);
+ return n;
+ }
+ tmp = allocmemimage(lr, src->chan);
+ if(tmp == nil)
+ return -1;
+ memdraw(tmp, lr, src, lr.min, nil, lr.min, S);
+ n = unloadmemimage(tmp, lr, data, n);
+ freememimage(tmp);
+ return n;
+}
diff --git a/src/libdraw/mouse.c b/src/libdraw/mouse.c
new file mode 100644
index 00000000..e7b8f890
--- /dev/null
+++ b/src/libdraw/mouse.c
@@ -0,0 +1,139 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+
+void
+moveto(Mousectl *m, Point pt)
+{
+ fprint(m->mfd, "m%d %d", pt.x, pt.y);
+ m->xy = pt;
+}
+
+void
+closemouse(Mousectl *mc)
+{
+ if(mc == nil)
+ return;
+
+ postnote(PNPROC, mc->pid, "kill");
+
+ do; while(nbrecv(mc->c, &mc->Mouse) > 0);
+
+ close(mc->mfd);
+ close(mc->cfd);
+ free(mc->file);
+ free(mc->c);
+ free(mc->resizec);
+ free(mc);
+}
+
+int
+readmouse(Mousectl *mc)
+{
+ if(mc->image)
+ flushimage(mc->image->display, 1);
+ if(recv(mc->c, &mc->Mouse) < 0){
+ fprint(2, "readmouse: %r\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+void
+_ioproc(void *arg)
+{
+ int n, nerr, one;
+ char buf[1+5*12];
+ Mouse m;
+ Mousectl *mc;
+
+ mc = arg;
+ threadsetname("mouseproc");
+ one = 1;
+ memset(&m, 0, sizeof m);
+ mc->pid = getpid();
+ nerr = 0;
+ for(;;){
+ n = read(mc->mfd, buf, sizeof buf);
+ if(n != 1+4*12){
+ yield(); /* if error is due to exiting, we'll exit here */
+ fprint(2, "mouse: bad count %d not 49: %r\n", n);
+ if(n<0 || ++nerr>10)
+ threadexits("read error");
+ continue;
+ }
+ nerr = 0;
+ switch(buf[0]){
+ case 'r':
+ send(mc->resizec, &one);
+ /* fall through */
+ case 'm':
+ m.xy.x = atoi(buf+1+0*12);
+ m.xy.y = atoi(buf+1+1*12);
+ m.buttons = atoi(buf+1+2*12);
+ m.msec = atoi(buf+1+3*12);
+ send(mc->c, &m);
+ /*
+ * mc->Mouse is updated after send so it doesn't have wrong value if we block during send.
+ * This means that programs should receive into mc->Mouse (see readmouse() above) if
+ * they want full synchrony.
+ */
+ mc->Mouse = m;
+ break;
+ }
+ }
+}
+
+Mousectl*
+initmouse(char *file, Image *i)
+{
+ Mousectl *mc;
+ char *t, *sl;
+
+ mc = mallocz(sizeof(Mousectl), 1);
+ if(file == nil)
+ file = "/dev/mouse";
+ mc->file = strdup(file);
+ mc->mfd = open(file, ORDWR|OCEXEC);
+ if(mc->mfd<0 && strcmp(file, "/dev/mouse")==0){
+ bind("#m", "/dev", MAFTER);
+ mc->mfd = open(file, ORDWR|OCEXEC);
+ }
+ if(mc->mfd < 0){
+ free(mc);
+ return nil;
+ }
+ t = malloc(strlen(file)+16);
+ strcpy(t, file);
+ sl = utfrrune(t, '/');
+ if(sl)
+ strcpy(sl, "/cursor");
+ else
+ strcpy(t, "/dev/cursor");
+ mc->cfd = open(t, ORDWR|OCEXEC);
+ free(t);
+ mc->image = i;
+ mc->c = chancreate(sizeof(Mouse), 0);
+ mc->resizec = chancreate(sizeof(int), 2);
+ proccreate(_ioproc, mc, 4096);
+ return mc;
+}
+
+void
+setcursor(Mousectl *mc, Cursor *c)
+{
+ char curs[2*4+2*2*16];
+
+ if(c == nil)
+ write(mc->cfd, curs, 0);
+ else{
+ BPLONG(curs+0*4, c->offset.x);
+ BPLONG(curs+1*4, c->offset.y);
+ memmove(curs+2*4, c->clr, 2*2*16);
+ write(mc->cfd, curs, sizeof curs);
+ }
+}
diff --git a/src/libdraw/mouse.h b/src/libdraw/mouse.h
new file mode 100644
index 00000000..f0a0f697
--- /dev/null
+++ b/src/libdraw/mouse.h
@@ -0,0 +1,44 @@
+typedef struct Menu Menu;
+typedef struct Mousectl Mousectl;
+
+struct Mouse
+{
+ int buttons; /* bit array: LMR=124 */
+ Point xy;
+ ulong msec;
+};
+
+struct Mousectl
+{
+ Mouse m;
+ struct Channel *c; /* chan(Mouse) */
+ struct Channel *resizec; /* chan(int)[2] */
+ /* buffered in case client is waiting for a mouse action before handling resize */
+
+ char *file;
+ int mfd; /* to mouse file */
+ int cfd; /* to cursor file */
+ int pid; /* of slave proc */
+ Display *display;
+ /*Image* image; / * of associated window/display */
+};
+
+struct Menu
+{
+ char **item;
+ char *(*gen)(int);
+ int lasthit;
+};
+
+/*
+ * Mouse
+ */
+extern Mousectl* initmouse(char*, Image*);
+extern void moveto(Mousectl*, Point);
+extern int readmouse(Mousectl*);
+extern void closemouse(Mousectl*);
+struct Cursor;
+extern void setcursor(Mousectl*, struct Cursor*);
+extern void drawgetrect(Rectangle, int);
+extern Rectangle getrect(int, Mousectl*);
+extern int menuhit(int, Mousectl*, Menu*, Screen*);
diff --git a/src/libdraw/openfont.c b/src/libdraw/openfont.c
new file mode 100644
index 00000000..dc16e379
--- /dev/null
+++ b/src/libdraw/openfont.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Font*
+openfont(Display *d, char *name)
+{
+ Font *fnt;
+ int fd, i, n;
+ char *buf;
+
+ fd = open(name, OREAD);
+ if(fd < 0)
+ return 0;
+
+ n = flength(fd);
+ buf = malloc(n+1);
+ if(buf == 0){
+ close(fd);
+ return 0;
+ }
+ buf[n] = 0;
+ i = read(fd, buf, n);
+ close(fd);
+ if(i != n){
+ free(buf);
+ return 0;
+ }
+ fnt = buildfont(d, buf, name);
+ free(buf);
+ return fnt;
+}
diff --git a/src/libdraw/readcolmap.c b/src/libdraw/readcolmap.c
new file mode 100644
index 00000000..6eb8ee26
--- /dev/null
+++ b/src/libdraw/readcolmap.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <bio.h>
+
+static ulong
+getval(char **p)
+{
+ ulong v;
+ char *q;
+
+ v = strtoul(*p, &q, 0);
+ v |= v<<8;
+ v |= v<<16;
+ *p = q;
+ return v;
+}
+
+void
+readcolmap(Display *d, RGB *colmap)
+{
+ int i;
+ char *p, *q;
+ Biobuf *b;
+ char buf[128];
+
+ USED(screen);
+
+ sprint(buf, "/dev/draw/%d/colormap", d->dirno);
+ b = Bopen(buf, OREAD);
+ if(b == 0)
+ drawerror(d, "rdcolmap: can't open colormap device");
+
+ for(;;) {
+ p = Brdline(b, '\n');
+ if(p == 0)
+ break;
+ i = strtoul(p, &q, 0);
+ if(i < 0 || i > 255) {
+ fprint(2, "rdcolmap: bad index\n");
+ exits("bad");
+ }
+ p = q;
+ colmap[255-i].red = getval(&p);
+ colmap[255-i].green = getval(&p);
+ colmap[255-i].blue = getval(&p);
+ }
+ Bterm(b);
+}
diff --git a/src/libdraw/readimage.c b/src/libdraw/readimage.c
new file mode 100644
index 00000000..1d2717b0
--- /dev/null
+++ b/src/libdraw/readimage.c
@@ -0,0 +1,118 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Image*
+readimage(Display *d, int fd, int dolock)
+{
+ char hdr[5*12+1];
+ int dy;
+ int new;
+ uint l, n;
+ int m, j, chunk;
+ int miny, maxy;
+ Rectangle r;
+ int ldepth;
+ u32int chan;
+ uchar *tmp;
+ Image *i;
+
+ if(readn(fd, hdr, 11) != 11)
+ return nil;
+ if(memcmp(hdr, "compressed\n", 11) == 0)
+ return creadimage(d, fd, dolock);
+ if(readn(fd, hdr+11, 5*12-11) != 5*12-11)
+ return nil;
+ chunk = d->bufsize - 32; /* a little room for header */
+
+ /*
+ * 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));
+ if(dolock)
+ lockdisplay(d);
+ i = allocimage(d, r, chan, 0, -1);
+ if(dolock)
+ unlockdisplay(d);
+ if(i == nil)
+ return nil;
+ 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("readimage: image too wide for buffer");
+ goto Err;
+ }
+ n = dy*l;
+ m = readn(fd, tmp, n);
+ if(m != n){
+ werrstr("readimage: read count %d not %d: %r", m, n);
+ Err:
+ if(dolock)
+ lockdisplay(d);
+ Err1:
+ freeimage(i);
+ if(dolock)
+ unlockdisplay(d);
+ free(tmp);
+ return nil;
+ }
+ if(!new) /* an old image: must flip all the bits */
+ for(j=0; j<chunk; j++)
+ tmp[j] ^= 0xFF;
+
+ if(dolock)
+ lockdisplay(d);
+ if(loadimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), tmp, chunk) <= 0)
+ goto Err1;
+ if(dolock)
+ unlockdisplay(d);
+ miny += dy;
+ }
+ free(tmp);
+ return i;
+}
diff --git a/src/libdraw/readsubfont.c b/src/libdraw/readsubfont.c
new file mode 100644
index 00000000..0e587b48
--- /dev/null
+++ b/src/libdraw/readsubfont.c
@@ -0,0 +1,58 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Subfont*
+readsubfonti(Display*d, char *name, int fd, Image *ai, int dolock)
+{
+ char hdr[3*12+4+1];
+ int n;
+ uchar *p;
+ Fontchar *fc;
+ Subfont *f;
+ Image *i;
+
+ i = ai;
+ if(i == nil){
+ i = readimage(d, fd, dolock);
+ if(i == nil)
+ return nil;
+ }
+ if(read(fd, hdr, 3*12) != 3*12){
+ if(ai == nil)
+ freeimage(i);
+ werrstr("rdsubfonfile: header read error: %r");
+ return nil;
+ }
+ n = atoi(hdr);
+ p = malloc(6*(n+1));
+ if(p == nil)
+ return nil;
+ if(read(fd, p, 6*(n+1)) != 6*(n+1)){
+ werrstr("rdsubfonfile: fontchar read error: %r");
+ Err:
+ free(p);
+ return nil;
+ }
+ fc = malloc(sizeof(Fontchar)*(n+1));
+ if(fc == nil)
+ goto Err;
+ _unpackinfo(fc, p, n);
+ if(dolock)
+ lockdisplay(d);
+ f = allocsubfont(name, n, atoi(hdr+12), atoi(hdr+24), fc, i);
+ if(dolock)
+ unlockdisplay(d);
+ if(f == nil){
+ free(fc);
+ goto Err;
+ }
+ free(p);
+ return f;
+}
+
+Subfont*
+readsubfont(Display*d, char *name, int fd, int dolock)
+{
+ return readsubfonti(d, name, fd, nil, dolock);
+}
diff --git a/src/libdraw/string.c b/src/libdraw/string.c
new file mode 100644
index 00000000..4dfb27fa
--- /dev/null
+++ b/src/libdraw/string.c
@@ -0,0 +1,137 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+enum
+{
+ Max = 100
+};
+
+Point
+string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s)
+{
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, nil, ZP, SoverD);
+}
+
+Point
+stringop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, nil, ZP, op);
+}
+
+Point
+stringn(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len)
+{
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, nil, ZP, SoverD);
+}
+
+Point
+stringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, nil, ZP, op);
+}
+
+Point
+runestring(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r)
+{
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, nil, ZP, SoverD);
+}
+
+Point
+runestringop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, nil, ZP, op);
+}
+
+Point
+runestringn(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len)
+{
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, SoverD);
+}
+
+Point
+runestringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, op);
+}
+
+Point
+_string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Rune *r, int len, Rectangle clipr, Image *bg, Point bgp, Drawop op)
+{
+ int m, n, wid, max;
+ ushort cbuf[Max], *c, *ec;
+ uchar *b;
+ char *subfontname;
+ char **sptr;
+ Rune **rptr;
+ Font *def;
+
+ if(s == nil){
+ s = "";
+ sptr = nil;
+ }else
+ sptr = &s;
+ if(r == nil){
+ r = (Rune*) L"";
+ rptr = nil;
+ }else
+ rptr = &r;
+ while((*s || *r) && len){
+ max = Max;
+ if(len < max)
+ max = len;
+ n = cachechars(f, sptr, rptr, cbuf, max, &wid, &subfontname);
+ if(n > 0){
+ _setdrawop(dst->display, op);
+
+ m = 47+2*n;
+ if(bg)
+ m += 4+2*4;
+ b = bufimage(dst->display, m);
+ if(b == 0){
+ fprint(2, "string: %r\n");
+ break;
+ }
+ if(bg)
+ b[0] = 'x';
+ else
+ b[0] = 's';
+ BPLONG(b+1, dst->id);
+ BPLONG(b+5, src->id);
+ BPLONG(b+9, f->cacheimage->id);
+ BPLONG(b+13, pt.x);
+ BPLONG(b+17, pt.y+f->ascent);
+ BPLONG(b+21, clipr.min.x);
+ BPLONG(b+25, clipr.min.y);
+ BPLONG(b+29, clipr.max.x);
+ BPLONG(b+33, clipr.max.y);
+ BPLONG(b+37, sp.x);
+ BPLONG(b+41, sp.y);
+ BPSHORT(b+45, n);
+ b += 47;
+ if(bg){
+ BPLONG(b, bg->id);
+ BPLONG(b+4, bgp.x);
+ BPLONG(b+8, bgp.y);
+ b += 12;
+ }
+ ec = &cbuf[n];
+ for(c=cbuf; c<ec; c++, b+=2)
+ BPSHORT(b, *c);
+ pt.x += wid;
+ bgp.x += wid;
+ agefont(f);
+ len -= n;
+ }
+ if(subfontname){
+ if(_getsubfont(f->display, subfontname) == 0){
+ def = f->display->defaultfont;
+ if(def && f!=def)
+ f = def;
+ else
+ break;
+ }
+ }
+ }
+ return pt;
+}
diff --git a/src/libdraw/stringwidth.c b/src/libdraw/stringwidth.c
new file mode 100644
index 00000000..c4877912
--- /dev/null
+++ b/src/libdraw/stringwidth.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static Rune empty[] = { 0 };
+int
+_stringnwidth(Font *f, char *s, Rune *r, int len)
+{
+ int wid, twid, n, max, l;
+ char *name;
+ enum { Max = 64 };
+ ushort cbuf[Max];
+ Rune rune, **rptr;
+ char *subfontname, **sptr;
+ Font *def;
+
+ if(s == nil){
+ s = "";
+ sptr = nil;
+ }else
+ sptr = &s;
+ if(r == nil){
+ r = empty;
+ rptr = nil;
+ }else
+ rptr = &r;
+ twid = 0;
+ while((*s || *r) && len){
+ max = Max;
+ if(len < max)
+ max = len;
+ n = 0;
+ while((l = cachechars(f, sptr, rptr, cbuf, max, &wid, &subfontname)) <= 0){
+ if(++n > 10){
+ if(*r)
+ rune = *r;
+ else
+ chartorune(&rune, s);
+ if(f->name != nil)
+ name = f->name;
+ else
+ name = "unnamed font";
+ fprint(2, "stringwidth: bad character set for rune 0x%.4ux in %s\n", rune, name);
+ return twid;
+ }
+ if(subfontname){
+ if(_getsubfont(f->display, subfontname) == 0){
+ def = f->display->defaultfont;
+ if(def && f!=def)
+ f = def;
+ else
+ break;
+ }
+ }
+ }
+ agefont(f);
+ twid += wid;
+ len -= l;
+ }
+ return twid;
+}
+
+int
+stringnwidth(Font *f, char *s, int len)
+{
+ return _stringnwidth(f, s, nil, len);
+}
+
+int
+stringwidth(Font *f, char *s)
+{
+ return _stringnwidth(f, s, nil, 1<<24);
+}
+
+Point
+stringsize(Font *f, char *s)
+{
+ return Pt(_stringnwidth(f, s, nil, 1<<24), f->height);
+}
+
+int
+runestringnwidth(Font *f, Rune *r, int len)
+{
+ return _stringnwidth(f, nil, r, len);
+}
+
+int
+runestringwidth(Font *f, Rune *r)
+{
+ return _stringnwidth(f, nil, r, 1<<24);
+}
+
+Point
+runestringsize(Font *f, Rune *r)
+{
+ return Pt(_stringnwidth(f, nil, r, 1<<24), f->height);
+}
diff --git a/src/libdraw/subfont.c b/src/libdraw/subfont.c
new file mode 100644
index 00000000..61838b05
--- /dev/null
+++ b/src/libdraw/subfont.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+Subfont*
+allocsubfont(char *name, int n, int height, int ascent, Fontchar *info, Image *i)
+{
+ Subfont *f;
+
+ assert(height != 0 /* allocsubfont */);
+
+ f = malloc(sizeof(Subfont));
+ if(f == 0)
+ return 0;
+ f->n = n;
+ f->height = height;
+ f->ascent = ascent;
+ f->info = info;
+ f->bits = i;
+ f->ref = 1;
+ if(name){
+ f->name = strdup(name);
+ if(lookupsubfont(i->display, name) == 0)
+ installsubfont(name, f);
+ }else
+ f->name = 0;
+ return f;
+}
diff --git a/src/libdraw/subfontcache.c b/src/libdraw/subfontcache.c
new file mode 100644
index 00000000..eb0bdba8
--- /dev/null
+++ b/src/libdraw/subfontcache.c
@@ -0,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * Easy versions of the cache routines; may be substituted by fancier ones for other purposes
+ */
+
+static char *lastname;
+Subfont *lastsubfont;
+
+Subfont*
+lookupsubfont(Display *d, char *name)
+{
+ if(strcmp(name, "*default*") == 0)
+ return d->defaultsubfont;
+ if(lastname && strcmp(name, lastname)==0 && d==lastsubfont->bits->display){
+ lastsubfont->ref++;
+ return lastsubfont;
+ }
+ return 0;
+}
+
+void
+installsubfont(char *name, Subfont *subfont)
+{
+ free(lastname);
+ lastname = strdup(name);
+ lastsubfont = subfont; /* notice we don't free the old one; that's your business */
+}
+
+void
+uninstallsubfont(Subfont *subfont)
+{
+ if(subfont == lastsubfont){
+ lastname = 0;
+ lastsubfont = 0;
+ }
+}
diff --git a/src/libdraw/subfontname.c b/src/libdraw/subfontname.c
new file mode 100644
index 00000000..bcb4e3a1
--- /dev/null
+++ b/src/libdraw/subfontname.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * Default version: convert to file name
+ */
+
+char*
+subfontname(char *cfname, char *fname, int maxdepth)
+{
+ char *t, *u, tmp1[64], tmp2[64];
+ int i;
+
+ if(strcmp(cfname, "*default*") == 0)
+ return strdup(cfname);
+ t = cfname;
+ if(t[0] != '/'){
+ snprint(tmp2, sizeof tmp2, "%s", fname);
+ u = utfrrune(tmp2, '/');
+ if(u)
+ u[0] = 0;
+ else
+ strcpy(tmp2, ".");
+ snprint(tmp1, sizeof tmp1, "%s/%s", tmp2, t);
+ t = tmp1;
+ }
+
+ if(maxdepth > 8)
+ maxdepth = 8;
+
+ for(i=log2[maxdepth]; i>=0; i--){
+ /* try i-bit grey */
+ snprint(tmp2, sizeof tmp2, "%s.%d", t, i);
+ if(access(tmp2, AREAD) == 0)
+ return strdup(tmp2);
+ }
+
+ /* try default */
+ if(access(t, AREAD) == 0)
+ return strdup(t);
+
+ return nil;
+}
diff --git a/src/libdraw/unix.c b/src/libdraw/unix.c
new file mode 100644
index 00000000..2203b087
--- /dev/null
+++ b/src/libdraw/unix.c
@@ -0,0 +1,16 @@
+#include <sys/stat.h>
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+vlong
+flength(int fd)
+{
+ struct stat s;
+
+ if(fstat(fd, &s) < 0)
+ return -1;
+ return s.st_size;
+}
+
diff --git a/src/libdraw/unloadimage.c b/src/libdraw/unloadimage.c
new file mode 100644
index 00000000..c203b9c2
--- /dev/null
+++ b/src/libdraw/unloadimage.c
@@ -0,0 +1,53 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+int
+unloadimage(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ int bpl, n, ntot, dy;
+ uchar *a;
+ Display *d;
+
+ if(!rectinrect(r, i->r)){
+ werrstr("unloadimage: bad rectangle");
+ return -1;
+ }
+ bpl = bytesperline(r, i->depth);
+ if(ndata < bpl*Dy(r)){
+ werrstr("unloadimage: buffer too small");
+ return -1;
+ }
+
+ d = i->display;
+ flushimage(d, 0); /* make sure subsequent flush is for us only */
+ ntot = 0;
+ while(r.min.y < r.max.y){
+ a = bufimage(d, 1+4+4*4);
+ if(a == 0){
+ werrstr("unloadimage: %r");
+ return -1;
+ }
+ dy = 8000/bpl;
+ if(dy <= 0){
+ werrstr("unloadimage: image too wide");
+ return -1;
+ }
+ if(dy > Dy(r))
+ dy = Dy(r);
+ a[0] = 'r';
+ BPLONG(a+1, i->id);
+ BPLONG(a+5, r.min.x);
+ BPLONG(a+9, r.min.y);
+ BPLONG(a+13, r.max.x);
+ BPLONG(a+17, r.min.y+dy);
+ if(flushimage(d, 0) < 0)
+ return -1;
+ n = _drawmsgread(d, data+ntot, ndata-ntot);
+ if(n < 0)
+ return n;
+ ntot += n;
+ r.min.y += dy;
+ }
+ return ntot;
+}
diff --git a/src/libdraw/writecolmap.c b/src/libdraw/writecolmap.c
new file mode 100644
index 00000000..30b026f9
--- /dev/null
+++ b/src/libdraw/writecolmap.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * This code (and the devdraw interface) will have to change
+ * if we ever get bitmaps with ldepth > 3, because the
+ * colormap will have to be written in chunks
+ */
+
+void
+writecolmap(Display *d, RGB *m)
+{
+ int i, n, fd;
+ char buf[64], *t;
+ ulong r, g, b;
+
+ sprint(buf, "/dev/draw/%d/colormap", d->dirno);
+ fd = open(buf, OWRITE);
+ if(fd < 0)
+ drawerror(d, "wrcolmap: open colormap failed");
+ t = malloc(8192);
+ n = 0;
+ for(i = 0; i < 256; i++) {
+ r = m[i].red>>24;
+ g = m[i].green>>24;
+ b = m[i].blue>>24;
+ n += sprint(t+n, "%d %lud %lud %lud\n", 255-i, r, g, b);
+ }
+ i = write(fd, t, n);
+ free(t);
+ close(fd);
+ if(i != n)
+ drawerror(d, "wrcolmap: bad write");
+}
diff --git a/src/libdraw/x11-alloc.c b/src/libdraw/x11-alloc.c
new file mode 100644
index 00000000..19475c74
--- /dev/null
+++ b/src/libdraw/x11-alloc.c
@@ -0,0 +1,120 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+/*
+ * Allocate a Memimage with an optional pixmap backing on the X server.
+ */
+Memimage*
+xallocmemimage(Rectangle r, u32int chan, int pixmap)
+{
+ int d, offset;
+ Memimage *m;
+ Xmem *xm;
+ XImage *xi;
+
+ m = _allocmemimage(r, chan);
+ if(chan != GREY1 && chan != _x.chan)
+ return m;
+
+ /*
+ * For bootstrapping, don't bother storing 1x1 images
+ * on the X server. Memimageinit needs to allocate these
+ * and we memimageinit before we do the rest of the X stuff.
+ * Of course, 1x1 images on the server are useless anyway.
+ */
+ if(Dx(r)==1 && Dy(r)==1)
+ return m;
+
+ xm = mallocz(sizeof(Xmem), 1);
+ if(xm == nil){
+ freememimage(m);
+ return nil;
+ }
+
+ /*
+ * Allocate backing store. What we call a 32-bit image
+ * the X server calls a 24-bit image.
+ */
+ d = m->depth;
+ if(pixmap != PMundef)
+ xm->pixmap = pixmap;
+ else
+ xm->pixmap = XCreatePixmap(_x.display, _x.drawable,
+ Dx(r), Dy(r), d==32 ? 24 : d);
+
+ /*
+ * We want to align pixels on word boundaries.
+ */
+ if(d == 24)
+ offset = r.min.x&3;
+ else
+ offset = r.min.x&(31/m->depth);
+ r.min.x -= offset;
+ assert(wordsperline(r, m->depth) <= m->width);
+
+ /*
+ * Wrap our data in an XImage structure.
+ */
+ xi = XCreateImage(_x.display, _x.vis, d==32 ? 24 : d,
+ ZPixmap, 0, (char*)m->data->bdata, Dx(r), Dy(r),
+ 32, m->width*sizeof(u32int));
+ if(xi == nil){
+ freememimage(m);
+ if(xm->pixmap != pixmap)
+ XFreePixmap(_x.display, xm->pixmap);
+ return nil;
+ }
+
+ xm->xi = xi;
+ xm->r = r;
+
+ /*
+ * Set the XImage parameters so that it looks exactly like
+ * a Memimage -- we're using the same data.
+ */
+ if(m->depth < 8 || m->depth == 24)
+ xi->bitmap_unit = 8;
+ else
+ xi->bitmap_unit = m->depth;
+ xi->byte_order = LSBFirst;
+ xi->bitmap_bit_order = MSBFirst;
+ xi->bitmap_pad = 32;
+ XInitImage(xi);
+ XFlush(_x.display);
+
+ m->X = xm;
+ return m;
+}
+
+Memimage*
+allocmemimage(Rectangle r, u32int chan)
+{
+ return xallocmemimage(r, chan, PMundef);
+}
+
+void
+freememimage(Memimage *m)
+{
+ Xmem *xm;
+
+ if(m == nil)
+ return;
+
+ xm = m->X;
+ if(xm && m->data->ref == 1){
+ if(xm->xi){
+ xm->xi->data = nil;
+ XFree(xm->xi);
+ }
+ XFreePixmap(_x.display, xm->pixmap);
+ free(xm);
+ m->X = nil;
+ }
+ _freememimage(m);
+}
+
diff --git a/src/libdraw/x11-cload.c b/src/libdraw/x11-cload.c
new file mode 100644
index 00000000..25a325c6
--- /dev/null
+++ b/src/libdraw/x11-cload.c
@@ -0,0 +1,19 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+int
+cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
+{
+ int n;
+
+ n = _cloadmemimage(i, r, data, ndata);
+ if(n > 0 && i->X)
+ xputxdata(i, r);
+ return n;
+}
+
diff --git a/src/libdraw/x11-draw.c b/src/libdraw/x11-draw.c
new file mode 100644
index 00000000..33b92c87
--- /dev/null
+++ b/src/libdraw/x11-draw.c
@@ -0,0 +1,143 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+static int xdraw(Memdrawparam*);
+
+/*
+ * The X acceleration doesn't fit into the standard hwaccel
+ * model because we have the extra steps of pulling the image
+ * data off the server and putting it back when we're done.
+ */
+void
+memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp,
+ Memimage *mask, Point mp, int op)
+{
+ Memdrawparam *par;
+
+ if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op)) == nil)
+ return;
+
+ if(xdraw(par))
+ return;
+
+ /* only fetch dst data if we need it */
+ if((par->state&(Simplemask|Fullmask)) != (Simplemask|Fullmask))
+ xgetxdata(dst, par->r);
+
+ /* always fetch source and mask */
+ xgetxdata(src, par->sr);
+ xgetxdata(mask, par->mr);
+
+ /* now can run memimagedraw on the in-memory bits */
+ _memimagedraw(par);
+
+ /* put bits back on x server */
+ xputxdata(dst, par->r);
+}
+
+static int
+xdraw(Memdrawparam *par)
+{
+ u32int sdval;
+ uint m, state;
+ Memimage *src, *dst, *mask;
+ Point dp, mp, sp;
+ Rectangle r;
+ Xmem *xdst, *xmask, *xsrc;
+ XGC gc;
+
+ if(par->dst->X == nil)
+ return 0;
+
+ dst = par->dst;
+ mask = par->mask;
+ r = par->r;
+ src = par->src;
+ state = par->state;
+
+ /*
+ * If we have an opaque mask and source is one opaque pixel,
+ * we can convert to the destination format and just XFillRectangle.
+ */
+ m = Simplesrc|Simplemask|Fullmask;
+ if((state&m) == m){
+ xfillcolor(dst, r, par->sdval);
+ xdirtyxdata(dst, r);
+ return 1;
+ }
+
+ /*
+ * If no source alpha and an opaque mask, we can just copy
+ * the source onto the destination. If the channels are the
+ * same and the source is not replicated, XCopyArea works.
+ */
+ m = Simplemask|Fullmask;
+ if((state&(m|Replsrc))==m && src->chan==dst->chan && src->X){
+ xdst = dst->X;
+ xsrc = src->X;
+ dp = subpt(r.min, dst->r.min);
+ sp = subpt(par->sr.min, src->r.min);
+ gc = dst->chan==GREY1 ? _x.gccopy0 : _x.gccopy;
+
+ XCopyArea(_x.display, xsrc->pixmap, xdst->pixmap, gc,
+ sp.x, sp.y, Dx(r), Dy(r), dp.x, dp.y);
+ xdirtyxdata(dst, r);
+ return 1;
+ }
+
+ /*
+ * If no source alpha, a 1-bit mask, and a simple source,
+ * we can copy through the mask onto the destination.
+ */
+ if(dst->X && mask->X && !(mask->flags&Frepl)
+ && mask->chan==GREY1 && (state&Simplesrc)){
+ xdst = dst->X;
+ xmask = mask->X;
+ sdval = par->sdval;
+
+ dp = subpt(r.min, dst->r.min);
+ mp = subpt(r.min, subpt(par->mr.min, mask->r.min));
+
+ if(dst->chan == GREY1){
+ gc = _x.gcsimplesrc0;
+ if(_x.gcsimplesrc0color != sdval){
+ XSetForeground(_x.display, gc, sdval);
+ _x.gcsimplesrc0color = sdval;
+ }
+ if(_x.gcsimplesrc0pixmap != xmask->pixmap){
+ XSetStipple(_x.display, gc, xmask->pixmap);
+ _x.gcsimplesrc0pixmap = xmask->pixmap;
+ }
+ }else{
+ /* this doesn't work on rob's mac? */
+ gc = _x.gcsimplesrc;
+ if(dst->chan == CMAP8 && _x.usetable)
+ sdval = _x.tox11[sdval];
+
+ if(_x.gcsimplesrccolor != sdval){
+ XSetForeground(_x.display, gc, sdval);
+ _x.gcsimplesrccolor = sdval;
+ }
+ if(_x.gcsimplesrcpixmap != xmask->pixmap){
+ XSetStipple(_x.display, gc, xmask->pixmap);
+ _x.gcsimplesrcpixmap = xmask->pixmap;
+ }
+ }
+ XSetTSOrigin(_x.display, gc, mp.x, mp.y);
+ XFillRectangle(_x.display, xdst->pixmap, gc, dp.x, dp.y,
+ Dx(r), Dy(r));
+ xdirtyxdata(dst, r);
+ return 1;
+ }
+
+ /*
+ * Can't accelerate.
+ */
+ return 0;
+}
+
diff --git a/src/libdraw/x11-event.c b/src/libdraw/x11-event.c
new file mode 100644
index 00000000..9a23547a
--- /dev/null
+++ b/src/libdraw/x11-event.c
@@ -0,0 +1,136 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <cursor.h>
+#include <event.h>
+
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+ulong
+event(Event *e)
+{
+ return eread(~0UL, e);
+}
+
+ulong
+eread(ulong keys, Event *e)
+{
+ ulong xmask;
+ XEvent xevent;
+
+ xmask = ExposureMask;
+
+ if(keys&Emouse)
+ xmask |= MouseMask|StructureNotifyMask;
+ if(keys&Ekeyboard)
+ xmask |= KeyPressMask;
+
+ XSelectInput(_x.display, _x.drawable, xmask);
+again:
+ XWindowEvent(_x.display, _x.drawable, xmask, &xevent);
+
+ switch(xevent.type){
+ case Expose:
+ xexpose(&xevent, _x.display);
+ goto again;
+ case ConfigureNotify:
+ if(xconfigure(&xevent, _x.display))
+ eresized(1);
+ goto again;
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ if(xtoplan9mouse(&xevent, &e->mouse) < 0)
+ goto again;
+ return Emouse;
+ case KeyPress:
+ e->kbdc = xtoplan9kbd(&xevent);
+ if(e->kbdc == -1)
+ goto again;
+ return Ekeyboard;
+ default:
+ return 0;
+ }
+}
+
+void
+einit(ulong keys)
+{
+ keys &= ~(Emouse|Ekeyboard);
+ if(keys){
+ fprint(2, "unknown keys in einit\n");
+ abort();
+ }
+}
+
+int
+ekbd(void)
+{
+ Event e;
+
+ eread(Ekeyboard, &e);
+ return e.kbdc;
+}
+
+Mouse
+emouse(void)
+{
+ Event e;
+
+ eread(Emouse, &e);
+ return e.mouse;
+}
+
+int
+ecanread(ulong keys)
+{
+ int can;
+
+ can = 0;
+ if(keys&Emouse)
+ can |= ecanmouse();
+ if(keys&Ekeyboard)
+ can |= ecankbd();
+ return can;
+}
+
+int
+ecanmouse(void)
+{
+ XEvent xe;
+ Mouse m;
+
+again:
+ if(XCheckWindowEvent(_x.display, _x.drawable, MouseMask, &xe)){
+ if(xtoplan9mouse(&xe, &m) < 0)
+ goto again;
+ XPutBackEvent(_x.display, &xe);
+ return 1;
+ }
+ return 0;
+}
+
+int
+ecankbd(void)
+{
+ XEvent xe;
+
+again:
+ if(XCheckWindowEvent(_x.display, _x.drawable, KeyPressMask, &xe)){
+ if(xtoplan9kbd(&xe) == -1)
+ goto again;
+ XPutBackEvent(_x.display, &xe);
+ return 1;
+ }
+ return 0;
+}
+
+void
+emoveto(Point p)
+{
+ xmoveto(p);
+}
+
diff --git a/src/libdraw/x11-fill.c b/src/libdraw/x11-fill.c
new file mode 100644
index 00000000..ff0b2e86
--- /dev/null
+++ b/src/libdraw/x11-fill.c
@@ -0,0 +1,57 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+void
+memfillcolor(Memimage *m, u32int val)
+{
+ _memfillcolor(m, val);
+ if(m->X == nil)
+ return;
+ if((val & 0xFF) == 0xFF) /* full alpha */
+ xfillcolor(m, m->r, _rgbatoimg(m, val));
+ else
+ xputxdata(m, m->r);
+}
+
+void
+xfillcolor(Memimage *m, Rectangle r, u32int v)
+{
+ Point p;
+ Xmem *xm;
+ XGC gc;
+
+ xm = m->X;
+ assert(xm != nil);
+
+ /*
+ * Set up fill context appropriately.
+ */
+ if(m->chan == GREY1){
+ gc = _x.gcfill0;
+ if(_x.gcfill0color != v){
+ XSetForeground(_x.display, gc, v);
+ _x.gcfill0color = v;
+ }
+ }else{
+ if(m->chan == CMAP8 && _x.usetable)
+ v = _x.tox11[v];
+ gc = _x.gcfill;
+ if(_x.gcfillcolor != v){
+ XSetForeground(_x.display, gc, v);
+ _x.gcfillcolor = v;
+ }
+ }
+
+ /*
+ * XFillRectangle takes coordinates relative to image rectangle.
+ */
+ p = subpt(r.min, m->r.min);
+ XFillRectangle(_x.display, xm->pixmap, gc, p.x, p.y, Dx(r), Dy(r));
+}
+
+
diff --git a/src/libdraw/x11-get.c b/src/libdraw/x11-get.c
new file mode 100644
index 00000000..feed46c6
--- /dev/null
+++ b/src/libdraw/x11-get.c
@@ -0,0 +1,110 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+static void
+addrect(Rectangle *rp, Rectangle r)
+{
+ if(rp->min.x >= rp->max.x)
+ *rp = r;
+ else
+ combinerect(rp, r);
+}
+
+XImage*
+xgetxdata(Memimage *m, Rectangle r)
+{
+ int x, y;
+ uchar *p;
+ Point tp, xdelta, delta;
+ Xmem *xm;
+
+ xm = m->X;
+ if(xm == nil)
+ return nil;
+
+ if(xm->dirty == 0)
+ return xm->xi;
+
+ r = xm->dirtyr;
+ if(Dx(r)==0 || Dy(r)==0)
+ return xm->xi;
+
+ delta = subpt(r.min, m->r.min);
+
+ tp = xm->r.min; /* need temp for Digital UNIX */
+ xdelta = subpt(r.min, tp);
+
+ XGetSubImage(_x.display, xm->pixmap, delta.x, delta.y, Dx(r), Dy(r),
+ AllPlanes, ZPixmap, xm->xi, xdelta.x, delta.y);
+
+ if(_x.usetable && m->chan==CMAP8){
+ for(y=r.min.y; y<r.max.y; y++)
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
+ *p = _x.toplan9[*p];
+ }
+ xm->dirty = 0;
+ xm->dirtyr = Rect(0,0,0,0);
+ return xm->xi;
+}
+
+void
+xputxdata(Memimage *m, Rectangle r)
+{
+ int offset, x, y;
+ uchar *p;
+ Point tp, xdelta, delta;
+ Xmem *xm;
+ XGC gc;
+ XImage *xi;
+
+ xm = m->X;
+ if(xm == nil)
+ return;
+
+ xi = xm->xi;
+ gc = m->chan==GREY1 ? _x.gccopy0 : _x.gccopy;
+ if(m->depth == 24)
+ offset = r.min.x & 3;
+ else
+ offset = r.min.x & (31/m->depth);
+
+ delta = subpt(r.min, m->r.min);
+
+ tp = xm->r.min; /* need temporary on Digital UNIX */
+ xdelta = subpt(r.min, tp);
+
+ if(_x.usetable && m->chan==CMAP8){
+ for(y=r.min.y; y<r.max.y; y++)
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
+ *p = _x.tox11[*p];
+ }
+
+ XPutImage(_x.display, xm->pixmap, gc, xi, xdelta.x, xdelta.y, delta.x, delta.y,
+ Dx(r), Dy(r));
+
+ if(_x.usetable && m->chan==CMAP8){
+ for(y=r.min.y; y<r.max.y; y++)
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
+ *p = _x.toplan9[*p];
+ }
+}
+
+void
+xdirtyxdata(Memimage *m, Rectangle r)
+{
+ Xmem *xm;
+
+ xm = m->X;
+ if(xm == nil)
+ return;
+ xm->dirty = 1;
+ addrect(&xm->dirtyr, r);
+}
+
+
+
diff --git a/src/libdraw/x11-inc.h b/src/libdraw/x11-inc.h
new file mode 100644
index 00000000..4baf4b1a
--- /dev/null
+++ b/src/libdraw/x11-inc.h
@@ -0,0 +1,31 @@
+#define Colormap XColormap
+#define Cursor XCursor
+#define Display XDisplay
+#define Drawable XDrawable
+#define Font XFont
+#define GC XGC
+#define Point XPoint
+#define Rectangle XRectangle
+#define Screen XScreen
+#define Visual XVisual
+#define Window XWindow
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+
+#undef Colormap
+#undef Cursor
+#undef Display
+#undef Drawable
+#undef Font
+#undef GC
+#undef Point
+#undef Rectangle
+#undef Screen
+#undef Visual
+#undef Window
+
diff --git a/src/libdraw/x11-init.c b/src/libdraw/x11-init.c
new file mode 100644
index 00000000..31001240
--- /dev/null
+++ b/src/libdraw/x11-init.c
@@ -0,0 +1,584 @@
+/*
+ * Some of the stuff in this file is not X-dependent and should be elsewhere.
+ */
+#include "x11-inc.h"
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+#include "x11-memdraw.h"
+
+static Memimage *xattach(char*);
+static void plan9cmap(void);
+static int setupcmap(XWindow);
+static XGC xgc(XDrawable, int, int);
+static Image *getimage0(Display*);
+
+Xprivate _x;
+
+Display*
+_initdisplay(void (*error)(Display*, char*), char *label)
+{
+ Display *d;
+ Memimage *m;
+
+ memimageinit();
+
+ d = mallocz(sizeof(Display), 1);
+ if(d == nil)
+ return nil;
+
+ d->buf = malloc(16000+5);
+ d->obuf = malloc(16000);
+ if(d->buf == nil || d->obuf == nil){
+ free(d->buf);
+ free(d->obuf);
+ free(d);
+ return nil;
+ }
+ d->bufsize = 16000;
+ d->obufsize = 16000;
+ d->bufp = d->buf;
+ d->obufp = d->obuf;
+
+ m = xattach(label);
+ if(m == nil){
+ free(d);
+ return nil;
+ }
+
+ d->error = error;
+ _initdisplaymemimage(d, m);
+ d->screenimage = getimage0(d);
+ return d;
+}
+
+static Image*
+getimage0(Display *d)
+{
+ char *a, info[12*12+1];
+ int n;
+ Image *image;
+
+ a = bufimage(d, 2);
+ a[0] = 'J';
+ a[1] = 'I';
+ if(flushimage(d, 0) < 0){
+ fprint(2, "cannot read screen info: %r\n");
+ abort();
+ }
+
+ n = _drawmsgread(d, info, sizeof info);
+ if(n != 12*12){
+ fprint(2, "short screen info\n");
+ abort();
+ }
+
+ image = mallocz(sizeof(Image), 1);
+ image->display = d;
+ image->id = 0;
+ image->chan = strtochan(info+2*12);
+ image->depth = chantodepth(image->chan);
+ image->repl = atoi(info+3*12);
+ image->r.min.x = atoi(info+4*12);
+ image->r.min.y = atoi(info+5*12);
+ image->r.max.x = atoi(info+6*12);
+ image->r.max.y = atoi(info+7*12);
+ image->clipr.min.x = atoi(info+8*12);
+ image->clipr.min.y = atoi(info+9*12);
+ image->clipr.max.x = atoi(info+10*12);
+ image->clipr.max.y = atoi(info+11*12);
+ return image;
+}
+
+int
+getwindow(Display *d, int ref)
+{
+ Image *i;
+
+ freeimage(d->screenimage);
+ i = getimage0(d);
+ screen = d->screenimage = d->image = i;
+ return 0;
+}
+
+static int
+xerror(XDisplay *d, XErrorEvent *e)
+{
+ char buf[200];
+
+ print("X error: error_code=%d, request_code=%d, minor=%d\n",
+ e->error_code, e->request_code, e->minor_code);
+ XGetErrorText(d, e->error_code, buf, sizeof buf);
+ print("%s\n", buf);
+ return 0;
+}
+
+static int
+xioerror(XDisplay *d)
+{
+ print("X I/O error\n");
+ exit(1);
+ return -1;
+}
+
+
+static Memimage*
+xattach(char *label)
+{
+ char *argv[2], *disp;
+ int i, n, xrootid;
+ Rectangle r;
+ XClassHint classhint;
+ XDrawable pmid;
+ XPixmapFormatValues *pfmt;
+ XScreen *xscreen;
+ XSetWindowAttributes attr;
+ XSizeHints normalhint;
+ XTextProperty name;
+ XVisualInfo xvi;
+ XWindow xrootwin;
+ XWMHints hint;
+
+ /*
+ * Connect to X server.
+ */
+ _x.display = XOpenDisplay(NULL);
+ if(_x.display == nil){
+ disp = getenv("DISPLAY");
+ werrstr("XOpenDisplay %s: %r", disp ? disp : ":0");
+ free(disp);
+ return nil;
+ }
+ XSetErrorHandler(xerror);
+ XSetIOErrorHandler(xioerror);
+ xrootid = DefaultScreen(_x.display);
+ xrootwin = DefaultRootWindow(_x.display);
+
+ /*
+ * Figure out underlying screen format.
+ */
+ _x.depth = DefaultDepth(_x.display, xrootid);
+ if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi)
+ || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){
+ _x.vis = xvi.visual;
+ _x.depth = 16;
+ _x.usetable = 1;
+ }
+ else
+ if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi)
+ || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){
+ _x.vis = xvi.visual;
+ _x.depth = 24;
+ _x.usetable = 1;
+ }
+ else
+ if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi)
+ || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){
+ if(_x.depth > 8){
+ werrstr("can't deal with colormapped depth %d screens",
+ _x.depth);
+ goto err0;
+ }
+ _x.vis = xvi.visual;
+ _x.depth = 8;
+ }
+ else{
+ if(_x.depth != 8){
+ werrstr("can't understand depth %d screen", _x.depth);
+ goto err0;
+ }
+ _x.vis = DefaultVisual(_x.display, xrootid);
+ }
+
+ /*
+ * _x.depth is only the number of significant pixel bits,
+ * not the total number of pixel bits. We need to walk the
+ * display list to find how many actual bits are used
+ * per pixel.
+ */
+ _x.chan = 0;
+ pfmt = XListPixmapFormats(_x.display, &n);
+ for(i=0; i<n; i++){
+ if(pfmt[i].depth == _x.depth){
+ switch(pfmt[i].bits_per_pixel){
+ case 1: /* untested */
+ _x.chan = GREY1;
+ break;
+ case 2: /* untested */
+ _x.chan = GREY2;
+ break;
+ case 4: /* untested */
+ _x.chan = GREY4;
+ break;
+ case 8:
+ _x.chan = CMAP8;
+ break;
+ case 16: /* how to tell RGB15? */
+ _x.chan = RGB16;
+ break;
+ case 24: /* untested (impossible?) */
+ _x.chan = RGB24;
+ break;
+ case 32:
+ _x.chan = XRGB32;
+ break;
+ }
+ }
+ }
+ if(_x.chan == 0){
+ werrstr("could not determine screen pixel format");
+ goto err0;
+ }
+
+ /*
+ * Set up color map if necessary.
+ */
+ xscreen = DefaultScreenOfDisplay(_x.display);
+ _x.cmap = DefaultColormapOfScreen(xscreen);
+ if(_x.vis->class != StaticColor){
+ plan9cmap();
+ setupcmap(xrootwin);
+ }
+
+ /*
+ * We get to choose the initial rectangle size.
+ * This is arbitrary. In theory we should read the
+ * command line and allow the traditional X options.
+ */
+ r = Rect(0, 0, WidthOfScreen(xscreen)*3/4,
+ HeightOfScreen(xscreen)*3/4);
+
+ memset(&attr, 0, sizeof attr);
+ attr.colormap = _x.cmap;
+ attr.background_pixel = 0;
+ attr.border_pixel = 0;
+ _x.drawable = XCreateWindow(
+ _x.display, /* display */
+ xrootwin, /* parent */
+ 0, /* x */
+ 0, /* y */
+ Dx(r), /* width */
+ Dy(r), /* height */
+ 0, /* border width */
+ _x.depth, /* depth */
+ InputOutput, /* class */
+ _x.vis, /* visual */
+ /* valuemask */
+ CWBackPixel|CWBorderPixel|CWColormap,
+ &attr /* attributes (the above aren't?!) */
+ );
+
+ /*
+ * Label and other properties required by ICCCCM.
+ */
+ memset(&name, 0, sizeof name);
+ if(label == nil)
+ label = "pjw-face-here";
+ name.value = (uchar*)label;
+ name.encoding = XA_STRING;
+ name.format = 8;
+ name.nitems = strlen(name.value);
+
+ memset(&normalhint, 0, sizeof normalhint);
+ normalhint.flags = USSize|PMaxSize;
+ normalhint.max_width = WidthOfScreen(xscreen);
+ normalhint.max_height = HeightOfScreen(xscreen);
+
+ memset(&hint, 0, sizeof hint);
+ hint.flags = InputHint|StateHint;
+ hint.input = 1;
+ hint.initial_state = NormalState;
+
+ memset(&classhint, 0, sizeof classhint);
+ classhint.res_name = label;
+ classhint.res_class = label;
+
+ argv[0] = label;
+ argv[1] = nil;
+
+ XSetWMProperties(
+ _x.display, /* display */
+ _x.drawable, /* window */
+ &name, /* XA_WM_NAME property */
+ &name, /* XA_WM_ICON_NAME property */
+ argv, /* XA_WM_COMMAND */
+ 1, /* argc */
+ &normalhint, /* XA_WM_NORMAL_HINTS */
+ &hint, /* XA_WM_HINTS */
+ &classhint /* XA_WM_CLASSHINTS */
+ );
+ XFlush(_x.display);
+
+ /*
+ * Allocate our local backing store.
+ */
+ _x.screenr = r;
+ _x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth);
+ _x.screenimage = xallocmemimage(r, _x.chan, _x.screenpm);
+
+ /*
+ * Allocate some useful graphics contexts for the future.
+ */
+ _x.gcfill = xgc(_x.screenpm, FillSolid, -1);
+ _x.gccopy = xgc(_x.screenpm, -1, -1);
+ _x.gcsimplesrc = xgc(_x.screenpm, FillStippled, -1);
+ _x.gczero = xgc(_x.screenpm, -1, -1);
+ _x.gcreplsrc = xgc(_x.screenpm, FillTiled, -1);
+
+ pmid = XCreatePixmap(_x.display, _x.drawable, 1, 1, 1);
+ _x.gcfill0 = xgc(pmid, FillSolid, 0);
+ _x.gccopy0 = xgc(pmid, -1, -1);
+ _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1);
+ _x.gczero0 = xgc(pmid, -1, -1);
+ _x.gcreplsrc0 = xgc(pmid, FillTiled, -1);
+ XFreePixmap(_x.display, pmid);
+
+ /*
+ * Put the window on the screen.
+ */
+ XMapWindow(_x.display, _x.drawable);
+ XFlush(_x.display);
+
+ /*
+ * Lots of display connections for various threads.
+ */
+ _x.kbdcon = XOpenDisplay(NULL);
+ _x.mousecon = XOpenDisplay(NULL);
+ _x.snarfcon = XOpenDisplay(NULL);
+
+ _x.black = xscreen->black_pixel;
+ _x.white = xscreen->white_pixel;
+
+ return _x.screenimage;
+
+err0:
+ /*
+ * Should do a better job of cleaning up here.
+ */
+ XCloseDisplay(_x.display);
+ return nil;
+}
+
+/*
+ * Create a GC with a particular fill style and XXX.
+ * Disable generation of GraphicsExpose/NoExpose events in the GC.
+ */
+static XGC
+xgc(XDrawable d, int fillstyle, int foreground)
+{
+ XGC gc;
+ XGCValues v;
+
+ memset(&v, 0, sizeof v);
+ v.function = GXcopy;
+ v.graphics_exposures = False;
+ gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v);
+ if(fillstyle != -1)
+ XSetFillStyle(_x.display, gc, fillstyle);
+ if(foreground != -1)
+ XSetForeground(_x.display, gc, 0);
+ return gc;
+}
+
+
+/*
+ * Initialize map with the Plan 9 rgbv color map.
+ */
+static void
+plan9cmap(void)
+{
+ int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
+ static int once;
+
+ if(once)
+ return;
+ once = 1;
+
+ for(r=0; r!=4; r++)
+ for(g = 0; g != 4; g++)
+ for(b = 0; b!=4; b++)
+ for(v = 0; v!=4; v++){
+ den=r;
+ if(g > den)
+ den=g;
+ if(b > den)
+ den=b;
+ /* divide check -- pick grey shades */
+ if(den==0)
+ cr=cg=cb=v*17;
+ else {
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
+ _x.map[idx].red = cr*0x0101;
+ _x.map[idx].green = cg*0x0101;
+ _x.map[idx].blue = cb*0x0101;
+ _x.map[idx].pixel = idx;
+ _x.map[idx].flags = DoRed|DoGreen|DoBlue;
+
+ v7 = v >> 1;
+ idx7 = r*32 + v7*16 + g*4 + b;
+ if((v & 1) == v7){
+ _x.map7to8[idx7][0] = idx;
+ if(den == 0) { /* divide check -- pick grey shades */
+ cr = ((255.0/7.0)*v7)+0.5;
+ cg = cr;
+ cb = cr;
+ }
+ else {
+ num=17*15*(4*den+v7*2)/14;
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ _x.map7[idx7].red = cr*0x0101;
+ _x.map7[idx7].green = cg*0x0101;
+ _x.map7[idx7].blue = cb*0x0101;
+ _x.map7[idx7].pixel = idx7;
+ _x.map7[idx7].flags = DoRed|DoGreen|DoBlue;
+ }
+ else
+ _x.map7to8[idx7][1] = idx;
+ }
+}
+
+/*
+ * Initialize and install the rgbv color map as a private color map
+ * for this application. It gets the best colors when it has the
+ * cursor focus.
+ */
+static int
+setupcmap(XWindow w)
+{
+ char buf[30];
+ int i;
+ u32int p, pp;
+ XColor c;
+
+ if(_x.depth <= 1)
+ return 0;
+
+ if(_x.depth >= 24) {
+ /*
+ * The pixel value returned from XGetPixel needs to
+ * be converted to RGB so we can call rgb2cmap()
+ * to translate between 24 bit X and our color. Unfortunately,
+ * the return value appears to be display server endian
+ * dependant. Therefore, we run some heuristics to later
+ * determine how to mask the int value correctly.
+ * Yeah, I know we can look at _x.vis->byte_order but
+ * some displays say MSB even though they run on LSB.
+ * Besides, this is more anal.
+ */
+
+ c = _x.map[19]; /* known to have different R, G, B values */
+ if(!XAllocColor(_x.display, _x.cmap, &c)){
+ werrstr("XAllocColor: %r");
+ return -1;
+ }
+ p = c.pixel;
+ pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
+ if(pp != _x.map[19].pixel) {
+ /* check if endian is other way */
+ pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
+ if(pp != _x.map[19].pixel){
+ werrstr("cannot detect X server byte order");
+ return -1;
+ }
+
+ switch(_x.chan){
+ case RGB24:
+ _x.chan = BGR24;
+ break;
+ case XRGB32:
+ _x.chan = XBGR32;
+ break;
+ default:
+ werrstr("cannot byteswap channel %s",
+ chantostr(buf, _x.chan));
+ break;
+ }
+ }
+ }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){
+ /*
+ * Do nothing. We have no way to express a
+ * mixed-endian 16-bit screen, so pretend they don't exist.
+ */
+ }else if(_x.vis->class == PseudoColor){
+ if(_x.usetable == 0){
+ _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll);
+ XStoreColors(_x.display, _x.cmap, _x.map, 256);
+ for(i = 0; i < 256; i++){
+ _x.tox11[i] = i;
+ _x.toplan9[i] = i;
+ }
+ }else{
+ for(i = 0; i < 128; i++){
+ c = _x.map7[i];
+ if(!XAllocColor(_x.display, _x.cmap, &c)){
+ werrstr("can't allocate colors in 7-bit map");
+ return -1;
+ }
+ _x.tox11[_x.map7to8[i][0]] = c.pixel;
+ _x.tox11[_x.map7to8[i][1]] = c.pixel;
+ _x.toplan9[c.pixel] = _x.map7to8[i][0];
+ }
+ }
+ }else{
+ werrstr("unsupported visual class %d", _x.vis->class);
+ return -1;
+ }
+ return 0;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+ return;
+ XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y,
+ Dx(r), Dy(r), r.min.x, r.min.y);
+ XFlush(_x.display);
+}
+
+void
+xexpose(XEvent *e, XDisplay *xd)
+{
+ XExposeEvent *xe;
+ Rectangle r;
+
+ xe = (XExposeEvent*)e;
+ r.min.x = xe->x;
+ r.min.y = xe->y;
+ r.max.x = xe->x+xe->width;
+ r.max.y = xe->y+xe->height;
+ XCopyArea(xd, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y,
+ Dx(r), Dy(r), r.min.x, r.min.y);
+ XFlush(xd);
+}
+
+int
+xconfigure(XEvent *e, XDisplay *xd)
+{
+ Memimage *m;
+ XConfigureEvent *xe = (XConfigureEvent*)e;
+ XDrawable pixmap;
+
+ if(xe->width == Dx(_x.screenr) && xe->height == Dy(_x.screenr))
+ return 0;
+
+ pixmap = XCreatePixmap(xd, _x.drawable, xe->width, xe->height, _x.depth);
+ m = xallocmemimage(Rect(0, 0, xe->width, xe->height), _x.chan, pixmap);
+ _x.screenpm = pixmap;
+ _x.screenr = Rect(0, 0, xe->width, xe->height);
+ _drawreplacescreenimage(m);
+ return 1;
+}
+
diff --git a/src/libdraw/x11-itrans.c b/src/libdraw/x11-itrans.c
new file mode 100644
index 00000000..49ee3fbb
--- /dev/null
+++ b/src/libdraw/x11-itrans.c
@@ -0,0 +1,258 @@
+/* input event and data structure translation */
+
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <keyboard.h>
+#include "x11-memdraw.h"
+
+int
+xtoplan9kbd(XEvent *e)
+{
+ int ind, k, md;
+
+ md = e->xkey.state;
+ ind = 0;
+ if(md & ShiftMask)
+ ind = 1;
+
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+ if(k == XK_Multi_key || k == NoSymbol)
+ return -1;
+
+ if(k&0xFF00){
+ switch(k){
+ case XK_BackSpace:
+ case XK_Tab:
+ case XK_Escape:
+ case XK_Delete:
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ case XK_KP_Divide:
+ case XK_KP_Multiply:
+ case XK_KP_Subtract:
+ case XK_KP_Add:
+ case XK_KP_Decimal:
+ k &= 0x7F;
+ break;
+ case XK_Linefeed:
+ k = '\r';
+ break;
+ case XK_KP_Space:
+ k = ' ';
+ break;
+ case XK_Home:
+ case XK_KP_Home:
+ k = Khome;
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ k = Kleft;
+ break;
+ case XK_Up:
+ case XK_KP_Up:
+ k = Kup;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ k = Kdown;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ k = Kright;
+ break;
+ case XK_Page_Down:
+ case XK_KP_Page_Down:
+ k = Kpgdown;
+ break;
+ case XK_End:
+ case XK_KP_End:
+ k = Kend;
+ break;
+ case XK_Page_Up:
+ case XK_KP_Page_Up:
+ k = Kpgup;
+ break;
+ case XK_Insert:
+ case XK_KP_Insert:
+ k = Kins;
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ k = '\n';
+ break;
+ case XK_Alt_L:
+ case XK_Alt_R:
+ k = Kalt;
+ break;
+ default: /* not ISO-1 or tty control */
+ return -1;
+ }
+ }
+
+ /* Compensate for servers that call a minus a hyphen */
+ if(k == XK_hyphen)
+ k = XK_minus;
+ /* Do control mapping ourselves if translator doesn't */
+ if(e->xkey.state&ControlMask)
+ k &= 0x9f;
+ if(k == NoSymbol) {
+ return -1;
+ }
+
+ /* BUG: could/should do Alt translation here! */
+ return k;
+}
+
+int
+xtoplan9mouse(XEvent *e, Mouse *m)
+{
+ int s;
+ XButtonEvent *be;
+ XMotionEvent *me;
+
+ switch(e->type){
+ case ButtonPress:
+ be = (XButtonEvent*)e;
+ /* BUG? on mac need to inherit these from elsewhere? */
+ m->xy.x = be->x;
+ m->xy.y = be->y;
+ s = be->state;
+ m->msec = be->time;
+ switch(be->button){
+ case 1:
+ s |= Button1Mask;
+ break;
+ case 2:
+ s |= Button2Mask;
+ break;
+ case 3:
+ s |= Button3Mask;
+ break;
+ }
+ break;
+ case ButtonRelease:
+ be = (XButtonEvent*)e;
+ m->xy.x = be->x;
+ m->xy.y = be->y;
+ s = be->state;
+ m->msec = be->time;
+ switch(be->button){
+ case 1:
+ s &= ~Button1Mask;
+ break;
+ case 2:
+ s &= ~Button2Mask;
+ break;
+ case 3:
+ s &= ~Button3Mask;
+ break;
+ }
+ break;
+
+ case MotionNotify:
+ me = (XMotionEvent*)e;
+ s = me->state;
+ m->xy.x = me->x;
+ m->xy.y = me->y;
+ m->msec = me->time;
+ break;
+
+ default:
+ return -1;
+ }
+
+ m->buttons = 0;
+ if(s & Button1Mask)
+ m->buttons |= 1;
+ if(s & Button2Mask)
+ m->buttons |= 2;
+ if(s & Button3Mask)
+ m->buttons |= 4;
+
+ return 0;
+}
+
+void
+xmoveto(Point p)
+{
+ XWarpPointer(_x.display, None, _x.drawable, 0, 0, 0, 0, p.x, p.y);
+ XFlush(_x.display);
+}
+
+static int
+revbyte(int b)
+{
+ int r;
+
+ r = 0;
+ r |= (b&0x01) << 7;
+ r |= (b&0x02) << 5;
+ r |= (b&0x04) << 3;
+ r |= (b&0x08) << 1;
+ r |= (b&0x10) >> 1;
+ r |= (b&0x20) >> 3;
+ r |= (b&0x40) >> 5;
+ r |= (b&0x80) >> 7;
+ return r;
+}
+
+static void
+xcursorarrow(void)
+{
+ if(_x.cursor != 0){
+ XFreeCursor(_x.display, _x.cursor);
+ _x.cursor = 0;
+ }
+ XUndefineCursor(_x.display, _x.drawable);
+ XFlush(_x.display);
+}
+
+
+void
+xsetcursor(Cursor *c)
+{
+ XColor fg, bg;
+ XCursor xc;
+ Pixmap xsrc, xmask;
+ int i;
+ uchar src[2*16], mask[2*16];
+
+ if(c == nil){
+ xcursorarrow();
+ return;
+ }
+ for(i=0; i<2*16; i++){
+ src[i] = revbyte(c->set[i]);
+ mask[i] = revbyte(c->set[i] | c->clr[i]);
+ }
+
+ fg = _x.map[0];
+ bg = _x.map[255];
+ xsrc = XCreateBitmapFromData(_x.display, _x.drawable, src, 16, 16);
+ xmask = XCreateBitmapFromData(_x.display, _x.drawable, mask, 16, 16);
+ xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y);
+ if(xc != 0) {
+ XDefineCursor(_x.display, _x.drawable, xc);
+ if(_x.cursor != 0)
+ XFreeCursor(_x.display, _x.cursor);
+ _x.cursor = xc;
+ }
+ XFreePixmap(_x.display, xsrc);
+ XFreePixmap(_x.display, xmask);
+ XFlush(_x.display);
+}
+
diff --git a/src/libdraw/x11-keyboard.c b/src/libdraw/x11-keyboard.c
new file mode 100644
index 00000000..188e52bf
--- /dev/null
+++ b/src/libdraw/x11-keyboard.c
@@ -0,0 +1,71 @@
+#include "x11-inc.h"
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include "x11-memdraw.h"
+
+void
+closekeyboard(Keyboardctl *kc)
+{
+ if(kc == nil)
+ return;
+
+/* postnote(PNPROC, kc->pid, "kill");
+*/
+
+#ifdef BUG
+ /* Drain the channel */
+ while(?kc->c)
+ <-kc->c;
+#endif
+
+ close(kc->ctlfd);
+ close(kc->consfd);
+ free(kc->file);
+ free(kc->c);
+ free(kc);
+}
+
+static
+void
+_ioproc(void *arg)
+{
+ int i;
+ Keyboardctl *kc;
+ Rune r;
+ XEvent xevent;
+
+ kc = arg;
+ threadsetname("kbdproc");
+ kc->pid = getpid();
+ for(;;){
+ XSelectInput(_x.kbdcon, _x.drawable, KeyPressMask);
+ XWindowEvent(_x.kbdcon, _x.drawable, KeyPressMask, &xevent);
+ switch(xevent.type){
+ case KeyPress:
+ i = xtoplan9kbd(&xevent);
+ if(i == -1)
+ continue;
+ r = i;
+ send(kc->c, &r);
+ break;
+ }
+ }
+}
+
+Keyboardctl*
+initkeyboard(char *file)
+{
+ Keyboardctl *kc;
+
+ kc = mallocz(sizeof(Keyboardctl), 1);
+ if(kc == nil)
+ return nil;
+ kc->c = chancreate(sizeof(Rune), 20);
+ proccreate(_ioproc, kc, 4096);
+ return kc;
+}
+
diff --git a/src/libdraw/x11-load.c b/src/libdraw/x11-load.c
new file mode 100644
index 00000000..e4e32bf2
--- /dev/null
+++ b/src/libdraw/x11-load.c
@@ -0,0 +1,19 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+int
+loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
+{
+ int n;
+
+ n = _loadmemimage(i, r, data, ndata);
+ if(n > 0 && i->X)
+ xputxdata(i, r);
+ return n;
+}
+
diff --git a/src/libdraw/x11-memdraw.h b/src/libdraw/x11-memdraw.h
new file mode 100644
index 00000000..c8234b20
--- /dev/null
+++ b/src/libdraw/x11-memdraw.h
@@ -0,0 +1,93 @@
+/*
+ * Structure pointed to by X field of Memimage
+ */
+
+typedef struct Xmem Xmem;
+typedef struct Xprivate Xprivate;
+
+enum
+{
+ PMundef = ~0
+};
+
+struct Xmem
+{
+ int pixmap; /* pixmap id */
+ XImage *xi; /* local image */
+ int dirty; /* is the X server ahead of us? */
+ Rectangle dirtyr; /* which pixels? */
+ Rectangle r; /* size of image */
+};
+
+struct Xprivate {
+ u32int black;
+ u32int chan;
+ XColormap cmap;
+ XCursor cursor;
+ XDisplay *display;
+ int depth; /* of screen */
+ XDrawable drawable;
+ XColor map[256];
+ XColor map7[128];
+ uchar map7to8[128][2];
+ XGC gccopy;
+ XGC gccopy0;
+ XGC gcfill;
+ u32int gcfillcolor;
+ XGC gcfill0;
+ u32int gcfill0color;
+ XGC gcreplsrc;
+ u32int gcreplsrctile;
+ XGC gcreplsrc0;
+ u32int gcreplsrc0tile;
+ XGC gcsimplesrc;
+ u32int gcsimplesrccolor;
+ u32int gcsimplesrcpixmap;
+ XGC gcsimplesrc0;
+ u32int gcsimplesrc0color;
+ u32int gcsimplesrc0pixmap;
+ XGC gczero;
+ u32int gczeropixmap;
+ XGC gczero0;
+ u32int gczero0pixmap;
+ XDisplay *kbdcon;
+ XDisplay *mousecon;
+ Memimage* screenimage;
+ XDrawable screenpm;
+ Rectangle screenr;
+ XDisplay *snarfcon;
+ int toplan9[256];
+ int tox11[256];
+ int usetable;
+ XVisual *vis;
+ u32int white;
+};
+
+extern Xprivate _x;
+
+extern Memimage *xallocmemimage(Rectangle, u32int, int);
+extern XImage *xallocxdata(Memimage*, Rectangle);
+extern void xdirtyxdata(Memimage*, Rectangle);
+extern void xfillcolor(Memimage*, Rectangle, u32int);
+extern void xfreexdata(Memimage*);
+extern XImage *xgetxdata(Memimage*, Rectangle);
+extern void xputxdata(Memimage*, Rectangle);
+
+struct Mouse;
+extern int xtoplan9mouse(XEvent*, struct Mouse*);
+extern int xtoplan9kbd(XEvent*);
+extern void xexpose(XEvent*, XDisplay*);
+extern int xconfigure(XEvent*, XDisplay*);
+extern void flushmemscreen(Rectangle);
+extern void xmoveto(Point);
+struct Cursor;
+extern void xsetcursor(struct Cursor*);
+
+#define MouseMask (\
+ ButtonPressMask|\
+ ButtonReleaseMask|\
+ PointerMotionMask|\
+ Button1MotionMask|\
+ Button2MotionMask|\
+ Button3MotionMask)
+
diff --git a/src/libdraw/x11-mouse.c b/src/libdraw/x11-mouse.c
new file mode 100644
index 00000000..c6a26851
--- /dev/null
+++ b/src/libdraw/x11-mouse.c
@@ -0,0 +1,107 @@
+#include "x11-inc.h"
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+void
+moveto(Mousectl *m, Point pt)
+{
+ xmoveto(pt);
+}
+
+void
+closemouse(Mousectl *mc)
+{
+ if(mc == nil)
+ return;
+
+/* postnote(PNPROC, mc->pid, "kill");
+*/
+ do; while(nbrecv(mc->c, &mc->m) > 0);
+ close(mc->mfd);
+ close(mc->cfd);
+ free(mc->file);
+ chanfree(mc->c);
+ chanfree(mc->resizec);
+ free(mc);
+}
+
+int
+readmouse(Mousectl *mc)
+{
+ if(mc->display)
+ flushimage(mc->display, 1);
+ if(recv(mc->c, &mc->m) < 0){
+ fprint(2, "readmouse: %r\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+void
+_ioproc(void *arg)
+{
+ int one;
+ Mouse m;
+ Mousectl *mc;
+ XEvent xevent;
+
+ one = 1;
+ mc = arg;
+ threadsetname("mouseproc");
+ memset(&m, 0, sizeof m);
+ mc->pid = getpid();
+ for(;;){
+ XSelectInput(_x.mousecon, _x.drawable, MouseMask|ExposureMask|StructureNotifyMask);
+ XWindowEvent(_x.mousecon, _x.drawable, MouseMask|ExposureMask|StructureNotifyMask, &xevent);
+ switch(xevent.type){
+ case Expose:
+ xexpose(&xevent, _x.mousecon);
+ continue;
+ case ConfigureNotify:
+ if(xconfigure(&xevent, _x.mousecon))
+ nbsend(mc->resizec, &one);
+ continue;
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ if(xtoplan9mouse(&xevent, &m) < 0)
+ continue;
+ send(mc->c, &m);
+ /*
+ * mc->Mouse is updated after send so it doesn't have wrong value if we block during send.
+ * This means that programs should receive into mc->Mouse (see readmouse() above) if
+ * they want full synchrony.
+ */
+ mc->m = m;
+ break;
+ }
+ }
+}
+
+Mousectl*
+initmouse(char *file, Image *i)
+{
+ Mousectl *mc;
+
+ mc = mallocz(sizeof(Mousectl), 1);
+ if(i)
+ mc->display = i->display;
+ mc->c = chancreate(sizeof(Mouse), 0);
+ mc->resizec = chancreate(sizeof(int), 2);
+ proccreate(_ioproc, mc, 16384);
+ return mc;
+}
+
+void
+setcursor(Mousectl *mc, Cursor *c)
+{
+ xsetcursor(c);
+}
+
diff --git a/src/libdraw/x11-pixelbits.c b/src/libdraw/x11-pixelbits.c
new file mode 100644
index 00000000..8635b0ba
--- /dev/null
+++ b/src/libdraw/x11-pixelbits.c
@@ -0,0 +1,17 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+u32int
+pixelbits(Memimage *m, Point p)
+{
+ if(m->X)
+ xgetxdata(m, Rect(p.x, p.y, p.x+1, p.y+1));
+ return _pixelbits(m, p);
+}
+
+
diff --git a/src/libdraw/x11-unload.c b/src/libdraw/x11-unload.c
new file mode 100644
index 00000000..3e8a635c
--- /dev/null
+++ b/src/libdraw/x11-unload.c
@@ -0,0 +1,16 @@
+#include "x11-inc.h"
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "x11-memdraw.h"
+
+int
+unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
+{
+ if(i->X)
+ xgetxdata(i, r);
+ return _unloadmemimage(i, r, data, ndata);
+}
+