diff options
Diffstat (limited to 'src/libdraw')
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 ∥ +} + +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); +} + |