summaryrefslogtreecommitdiff
path: root/libdraw
diff options
context:
space:
mode:
Diffstat (limited to 'libdraw')
-rw-r--r--libdraw/NOTICE31
-rw-r--r--libdraw/alloc.c237
-rw-r--r--libdraw/allocimagemix.c42
-rw-r--r--libdraw/arith.c203
-rw-r--r--libdraw/bezier.c243
-rw-r--r--libdraw/border.c26
-rw-r--r--libdraw/buildfont.c142
-rw-r--r--libdraw/bytesperline.c30
-rw-r--r--libdraw/chan.c76
-rw-r--r--libdraw/cloadimage.c49
-rw-r--r--libdraw/computil.c37
-rw-r--r--libdraw/creadimage.c116
-rw-r--r--libdraw/defont.c401
-rw-r--r--libdraw/draw.c68
-rw-r--r--libdraw/drawrepl.c22
-rw-r--r--libdraw/ellipse.c81
-rw-r--r--libdraw/font.c398
-rw-r--r--libdraw/freesubfont.c16
-rw-r--r--libdraw/getdefont.c59
-rw-r--r--libdraw/getsubfont.c36
-rw-r--r--libdraw/init.c341
-rw-r--r--libdraw/line.c34
-rw-r--r--libdraw/loadimage.c70
-rw-r--r--libdraw/mkfile54
-rw-r--r--libdraw/mkfont.c54
-rw-r--r--libdraw/openfont.c37
-rw-r--r--libdraw/poly.c86
-rw-r--r--libdraw/readcolmap.c46
-rw-r--r--libdraw/readimage.c123
-rw-r--r--libdraw/readsubfont.c58
-rw-r--r--libdraw/rectclip.c24
-rw-r--r--libdraw/replclipr.c24
-rw-r--r--libdraw/rgb.c98
-rw-r--r--libdraw/string.c136
-rw-r--r--libdraw/stringbg.c50
-rw-r--r--libdraw/stringsubfont.c64
-rw-r--r--libdraw/stringwidth.c96
-rw-r--r--libdraw/subfont.c27
-rw-r--r--libdraw/subfontcache.c38
-rw-r--r--libdraw/subfontname.c45
-rw-r--r--libdraw/test.c9
-rw-r--r--libdraw/unloadimage.c58
-rw-r--r--libdraw/window.c203
-rw-r--r--libdraw/writecolmap.c37
-rw-r--r--libdraw/writeimage.c185
-rw-r--r--libdraw/writesubfont.c45
46 files changed, 4355 insertions, 0 deletions
diff --git a/libdraw/NOTICE b/libdraw/NOTICE
new file mode 100644
index 00000000..39682bb2
--- /dev/null
+++ b/libdraw/NOTICE
@@ -0,0 +1,31 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Portions Copyright © 1997-1999 Vita Nuova Limited
+ Portions Copyright © 2000-2006 Vita Nuova Holdings Limited (www.vitanuova.com)
+ Revisions Copyright © 2000-2006 Lucent Technologies Inc. and others
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/libdraw/alloc.c b/libdraw/alloc.c
new file mode 100644
index 00000000..f2da35da
--- /dev/null
+++ b/libdraw/alloc.c
@@ -0,0 +1,237 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+Image*
+allocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
+{
+ return _allocimage(nil, d, r, chan, repl, val, 0, 0);
+}
+
+Image*
+_allocimage(Image *ai, Display *d, Rectangle r, ulong chan, int repl, ulong val, int screenid, int refresh)
+{
+ uchar *a;
+ char *err;
+ Image *i;
+ Rectangle clipr;
+ int id;
+ int depth;
+
+ err = 0;
+ i = 0;
+
+ if(chan == 0){
+ kwerrstr("bad channel descriptor");
+ return nil;
+ }
+
+ depth = chantodepth(chan);
+ if(depth == 0){
+ err = "bad channel descriptor";
+ Error:
+ if(err)
+ kwerrstr("allocimage: %s", err);
+ else
+ kwerrstr("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;
+ ulong chan;
+
+ err = 0;
+ i = 0;
+
+ n = strlen(name);
+ if(n >= 256){
+ err = "name too long";
+ Error:
+ if(err)
+ kwerrstr("namedimage: %s", err);
+ else
+ kwerrstr("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);
+ 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);
+ if(flushimage(d, 0) < 0)
+ goto Error;
+
+ if(kchanio(d->ctlchan, buf, sizeof buf, OREAD) < 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){
+ kwerrstr("bad channel from devdraw");
+ 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/libdraw/allocimagemix.c b/libdraw/allocimagemix.c
new file mode 100644
index 00000000..73501fea
--- /dev/null
+++ b/libdraw/allocimagemix.c
@@ -0,0 +1,42 @@
+#include "lib9.h"
+#include "draw.h"
+
+Image*
+allocimagemix(Display *d, ulong color1, ulong color3)
+{
+ Image *t, *b;
+ static Image *qmask;
+
+ if(qmask == nil)
+ qmask = allocimage(d, Rect(0,0,1,1), GREY8, 1, 0x3F3F3FFF);
+
+ if(d->depth <= 8){ /* create a 2×2 texture */
+ t = allocimage(d, Rect(0,0,1,1), d->chan, 0, color1);
+ if(t == nil)
+ return nil;
+
+ b = allocimage(d, Rect(0,0,2,2), d->chan, 1, color3);
+ if(b == nil){
+ freeimage(t);
+ return nil;
+ }
+
+ draw(b, Rect(0,0,1,1), t, nil, ZP);
+ freeimage(t);
+ return b;
+ }else{ /* use a solid color, blended using alpha */
+ t = allocimage(d, Rect(0,0,1,1), d->chan, 1, color1);
+ if(t == nil)
+ return nil;
+
+ b = allocimage(d, Rect(0,0,1,1), d->chan, 1, color3);
+ if(b == nil){
+ freeimage(t);
+ return nil;
+ }
+
+ draw(b, b->r, t, qmask, ZP);
+ freeimage(t);
+ return b;
+ }
+}
diff --git a/libdraw/arith.c b/libdraw/arith.c
new file mode 100644
index 00000000..178b1e82
--- /dev/null
+++ b/libdraw/arith.c
@@ -0,0 +1,203 @@
+#include "lib9.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;
+}
+
+ulong
+drawld2chan[] = {
+ GREY1,
+ GREY2,
+ GREY4,
+ CMAP8,
+};
+
+ulong
+setalpha(ulong 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/libdraw/bezier.c b/libdraw/bezier.c
new file mode 100644
index 00000000..d26fc444
--- /dev/null
+++ b/libdraw/bezier.c
@@ -0,0 +1,243 @@
+#include "lib9.h"
+#include "draw.h"
+
+#define PINC 32 /* realloc granularity */
+
+typedef struct Plist Plist;
+struct Plist
+{
+ Point *p;
+ int np; /* -1 if malloc/realloc failed */
+};
+
+static void
+appendpt(Plist *l, Point p)
+{
+ if(l->np == -1)
+ return;
+ if(l->np == 0)
+ l->p = malloc(PINC*sizeof(Point));
+ else if(l->np%PINC == 0)
+ l->p = realloc(l->p, (l->np+PINC)*sizeof(Point));
+ if(l->p == 0){
+ l->np = -1;
+ return;
+ }
+ l->p[l->np++] = p;
+}
+
+static int
+normsq(Point p)
+{
+ return p.x*p.x+p.y*p.y;
+}
+
+static int
+psdist(Point p, Point a, Point b)
+{
+ int num, den;
+
+ p = subpt(p, a);
+ b = subpt(b, a);
+ num = p.x*b.x + p.y*b.y;
+ if(num <= 0)
+ return normsq(p);
+ den = normsq(b);
+ if(num >= den)
+ return normsq(subpt(b, p));
+ return normsq(subpt(divpt(mulpt(b, num), den), p));
+}
+
+/*
+ * Convert cubic Bezier curve control points to polyline
+ * vertices. Leaves the last vertex off, so you can continue
+ * with another curve.
+ */
+static void
+bpts1(Plist *l, Point p0, Point p1, Point p2, Point p3, int scale)
+{
+ Point p01, p12, p23, p012, p123, p0123;
+ Point tp0, tp1, tp2, tp3;
+ tp0=divpt(p0, scale);
+ tp1=divpt(p1, scale);
+ tp2=divpt(p2, scale);
+ tp3=divpt(p3, scale);
+ if(psdist(tp1, tp0, tp3)<=1 && psdist(tp2, tp0, tp3)<=1){
+ appendpt(l, tp0);
+ appendpt(l, tp1);
+ appendpt(l, tp2);
+ }
+ else{
+ /*
+ * if scale factor is getting too big for comfort,
+ * rescale now & concede the rounding error
+ */
+ if(scale>(1<<12)){
+ p0=tp0;
+ p1=tp1;
+ p2=tp2;
+ p3=tp3;
+ scale=1;
+ }
+ p01=addpt(p0, p1);
+ p12=addpt(p1, p2);
+ p23=addpt(p2, p3);
+ p012=addpt(p01, p12);
+ p123=addpt(p12, p23);
+ p0123=addpt(p012, p123);
+ bpts1(l, mulpt(p0, 8), mulpt(p01, 4), mulpt(p012, 2), p0123, scale*8);
+ bpts1(l, p0123, mulpt(p123, 2), mulpt(p23, 4), mulpt(p3, 8), scale*8);
+ }
+}
+
+static void
+bpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
+{
+ bpts1(l, p0, p1, p2, p3, 1);
+}
+
+static void
+bezierpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
+{
+ bpts(l, p0, p1, p2, p3);
+ appendpt(l, p3);
+}
+
+static void
+bezsplinepts(Plist *l, Point *pt, int npt)
+{
+ Point *p, *ep;
+ Point a, b, c, d;
+ int periodic;
+
+ if(npt<3)
+ return;
+ ep = &pt[npt-3];
+ periodic = eqpt(pt[0], ep[2]);
+ if(periodic){
+ a = divpt(addpt(ep[1], pt[0]), 2);
+ b = divpt(addpt(ep[1], mulpt(pt[0], 5)), 6);
+ c = divpt(addpt(mulpt(pt[0], 5), pt[1]), 6);
+ d = divpt(addpt(pt[0], pt[1]), 2);
+ bpts(l, a, b, c, d);
+ }
+ for(p=pt; p<=ep; p++){
+ if(p==pt && !periodic){
+ a = p[0];
+ b = divpt(addpt(p[0], mulpt(p[1], 2)), 3);
+ }
+ else{
+ a = divpt(addpt(p[0], p[1]), 2);
+ b = divpt(addpt(p[0], mulpt(p[1], 5)), 6);
+ }
+ if(p==ep && !periodic){
+ c = divpt(addpt(mulpt(p[1], 2), p[2]), 3);
+ d = p[2];
+ }
+ else{
+ c = divpt(addpt(mulpt(p[1], 5), p[2]), 6);
+ d = divpt(addpt(p[1], p[2]), 2);
+ }
+ bpts(l, a, b, c, d);
+ }
+ appendpt(l, d);
+}
+
+int
+getbezsplinepts(Point *pt, int npt, Point **pp)
+{
+ Plist l;
+ l.np = 0;
+ l.p = nil;
+ bezsplinepts(&l, pt, npt);
+ *pp = l.p;
+ return l.np;
+}
+
+int
+bezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp)
+{
+ return bezierop(dst, p0, p1, p2, p3, end0, end1, radius, src, sp, SoverD);
+}
+
+int
+bezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezierpts(&l, p0, p1, p2, p3);
+ if(l.np == -1)
+ return 0;
+ if(l.np != 0){
+ polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, p0), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+bezspline(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp)
+{
+ return bezsplineop(dst, pt, npt, end0, end1, radius, src, sp, SoverD);
+}
+
+int
+bezsplineop(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezsplinepts(&l, pt, npt);
+ if(l.np==-1)
+ return 0;
+ if(l.np != 0){
+ polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+fillbezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp)
+{
+ return fillbezierop(dst, p0, p1, p2, p3, w, src, sp, SoverD);
+}
+
+int
+fillbezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezierpts(&l, p0, p1, p2, p3);
+ if(l.np == -1)
+ return 0;
+ if(l.np != 0){
+ fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, p0), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+fillbezspline(Image *dst, Point *pt, int npt, int w, Image *src, Point sp)
+{
+ return fillbezsplineop(dst, pt, npt, w, src, sp, SoverD);
+}
+
+int
+fillbezsplineop(Image *dst, Point *pt, int npt, int w, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezsplinepts(&l, pt, npt);
+ if(l.np == -1)
+ return 0;
+ if(l.np > 0){
+ fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
diff --git a/libdraw/border.c b/libdraw/border.c
new file mode 100644
index 00000000..a75be159
--- /dev/null
+++ b/libdraw/border.c
@@ -0,0 +1,26 @@
+#include "lib9.h"
+#include "draw.h"
+
+void
+borderop(Image *im, Rectangle r, int i, Image *color, Point sp, Drawop op)
+{
+ if(i < 0){
+ r = insetrect(r, i);
+ sp = addpt(sp, Pt(i,i));
+ i = -i;
+ }
+ drawop(im, Rect(r.min.x, r.min.y, r.max.x, r.min.y+i),
+ color, nil, sp, op);
+ drawop(im, Rect(r.min.x, r.max.y-i, r.max.x, r.max.y),
+ color, nil, Pt(sp.x, sp.y+Dy(r)-i), op);
+ drawop(im, Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i),
+ color, nil, Pt(sp.x, sp.y+i), op);
+ drawop(im, Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i),
+ color, nil, Pt(sp.x+Dx(r)-i, sp.y+i), op);
+}
+
+void
+border(Image *im, Rectangle r, int i, Image *color, Point sp)
+{
+ borderop(im, r, i, color, sp, SoverD);
+}
diff --git a/libdraw/buildfont.c b/libdraw/buildfont.c
new file mode 100644
index 00000000..f86fdc81
--- /dev/null
+++ b/libdraw/buildfont.c
@@ -0,0 +1,142 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.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){
+ kwerrstr("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){
+ kwerrstr(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){
+ kwerrstr(badform, s-buf);
+ goto Err3;
+ }
+ max = strtol(s, &s, 0);
+ s = skip(s);
+ if(*s==0 || min>=65536 || max>=65536 || min>max){
+ kwerrstr("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)*/ /* Plan 9 uses this */
+ if(s)
+ freesubfont(s);
+ }
+ freeimage(f->cacheimage);
+ free(f->name);
+ free(f->cache);
+ free(f->subf);
+ free(f->sub);
+ free(f);
+}
diff --git a/libdraw/bytesperline.c b/libdraw/bytesperline.c
new file mode 100644
index 00000000..4fbf7f5a
--- /dev/null
+++ b/libdraw/bytesperline.c
@@ -0,0 +1,30 @@
+#include "lib9.h"
+#include "draw.h"
+
+static
+int
+unitsperline(Rectangle r, int d, int bitsperunit)
+{
+ ulong l, t;
+
+ 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/libdraw/chan.c b/libdraw/chan.c
new file mode 100644
index 00000000..96ec3268
--- /dev/null
+++ b/libdraw/chan.c
@@ -0,0 +1,76 @@
+#include "lib9.h"
+#include "draw.h"
+
+static char channames[] = "rgbkamx";
+char*
+chantostr(char *buf, ulong cc)
+{
+ ulong 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';
+}
+
+ulong
+strtochan(char *s)
+{
+ char *p, *q;
+ ulong 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(ulong 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/libdraw/cloadimage.c b/libdraw/cloadimage.c
new file mode 100644
index 00000000..5c8c792f
--- /dev/null
+++ b/libdraw/cloadimage.c
@@ -0,0 +1,49 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+int
+cloadimage(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ int m, nb, miny, maxy, ncblock;
+ uchar *a;
+
+ if(!rectinrect(r, i->r)){
+ werrstr("cloadimage: bad rectangle");
+ return -1;
+ }
+
+ miny = r.min.y;
+ m = 0;
+ ncblock = _compblocksize(r, i->depth);
+ while(miny != r.max.y){
+ maxy = atoi((char*)data+0*12);
+ nb = atoi((char*)data+1*12);
+ if(maxy<=miny || r.max.y<maxy){
+ werrstr("creadimage: bad maxy %d", maxy);
+ return -1;
+ }
+ data += 2*12;
+ ndata -= 2*12;
+ m += 2*12;
+ if(nb<=0 || ncblock<nb || nb>ndata){
+ werrstr("creadimage: bad count %d", nb);
+ return -1;
+ }
+ a = bufimage(i->display, 21+nb);
+ if(a == nil)
+ return -1;
+ 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);
+ memmove(a+21, data, nb);
+ miny = maxy;
+ data += nb;
+ ndata += nb;
+ m += nb;
+ }
+ return m;
+}
diff --git a/libdraw/computil.c b/libdraw/computil.c
new file mode 100644
index 00000000..3ec103b8
--- /dev/null
+++ b/libdraw/computil.c
@@ -0,0 +1,37 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * compressed data are seuences of byte codes.
+ * if the first byte b has the 0x80 bit set, the next (b^0x80)+1 bytes
+ * are data. otherwise, it's two bytes specifying a previous string to repeat.
+ */
+void
+_twiddlecompressed(uchar *buf, int n)
+{
+ uchar *ebuf;
+ int j, k, c;
+
+ ebuf = buf+n;
+ while(buf < ebuf){
+ c = *buf++;
+ if(c >= 128){
+ k = c-128+1;
+ for(j=0; j<k; j++, buf++)
+ *buf ^= 0xFF;
+ }else
+ buf++;
+ }
+}
+
+int
+_compblocksize(Rectangle r, int depth)
+{
+ int bpl;
+
+ bpl = bytesperline(r, depth);
+ bpl = 2*bpl; /* add plenty extra for blocking, etc. */
+ if(bpl < NCBLOCK)
+ return NCBLOCK;
+ return bpl;
+}
diff --git a/libdraw/creadimage.c b/libdraw/creadimage.c
new file mode 100644
index 00000000..b00726fd
--- /dev/null
+++ b/libdraw/creadimage.c
@@ -0,0 +1,116 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.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;
+ ulong chan;
+ int font;
+
+ font = dolock&2;
+ dolock &= 1;
+ if(libreadn(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] != ' '){
+ kwerrstr("creadimage: bad format");
+ return nil;
+ }
+ if(new){
+ hdr[11] = '\0';
+ if((chan = strtochan(hdr)) == 0){
+ kwerrstr("creadimage: bad channel string %s", hdr);
+ return nil;
+ }
+ }else{
+ ldepth = ((int)hdr[10])-'0';
+ if(ldepth<0 || ldepth>3){
+ kwerrstr("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){
+ kwerrstr("creadimage: bad rectangle");
+ return nil;
+ }
+
+ if(dolock)
+ 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(libreadn(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){
+ kwerrstr("creadimage: bad maxy %d", maxy);
+ goto Errout;
+ }
+ if(nb<=0 || ncblock<nb){
+ kwerrstr("creadimage: bad count %d", nb);
+ goto Errout;
+ }
+ if(libreadn(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(!font&&!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/libdraw/defont.c b/libdraw/defont.c
new file mode 100644
index 00000000..759cd7a5
--- /dev/null
+++ b/libdraw/defont.c
@@ -0,0 +1,401 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * lucm/latin1.9, in uncompressed form
+ */
+uchar
+defontdata[] =
+{
+0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,
+0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x32,0x33,0x30,0x34,0x20,
+0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x35,0x20,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x30,0x06,0x06,0x03,0x42,0x40,0x00,0x00,0x00,0x18,0x03,0x03,
+0x02,0x43,0x00,0x60,0x60,0x48,0x00,0x0d,0x0c,0x01,0x81,0x80,0xd0,0x90,0x00,0x00,
+0x18,0x01,0x81,0x81,0x40,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x7f,0x9c,0x1c,
+0x0e,0x07,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x70,
+0x38,0x1c,0x0e,0x04,0x81,0xc1,0xc0,0x70,0x00,0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xaa,0x80,0xc0,0x63,0xe3,
+0xf1,0xf8,0xfe,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x7f,0xff,0xff,0x1f,0x8f,
+0xc7,0xe3,0xf1,0xfb,0x7e,0x3e,0x3f,0x8f,0xff,0xe3,0xe3,0xff,0xff,0xff,0xff,0xff,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0c,0x18,0x09,0x05,0x82,0x40,0xc0,0x00,0x00,0x06,0x0c,0x04,
+0x82,0x40,0xc1,0x80,0x90,0x48,0x00,0x16,0x03,0x06,0x02,0x41,0x60,0x90,0x00,0x00,
+0x06,0x06,0x02,0x41,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x7f,0xa0,0x10,
+0x08,0x04,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x48,
+0x24,0x12,0x09,0x06,0x82,0x01,0x00,0x90,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x04,0x80,0x00,0x40,0x00,0x00,0x38,0x06,0x18,0x00,0x00,0x00,0x00,0x00,
+0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x07,0xc6,0x01,0xf0,0x00,0x00,0x0c,0x00,0x18,0x00,0x00,0x30,0x00,0x3c,
+0x00,0x60,0x06,0x01,0x8c,0x07,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xe0,0xc3,0xc0,0x01,0x54,0x9c,0xc0,0x5f,0xef,
+0xf7,0xfb,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0x7f,0xff,0xff,0x6f,0xb7,
+0xdb,0xed,0xf6,0xf9,0x7d,0xfe,0xff,0x6f,0xff,0xdf,0xef,0xff,0xff,0xff,0xff,0xff,
+0xff,0x00,0x01,0x00,0x00,0x00,0x00,0x30,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x30,0x06,0x06,0x06,0x82,0x80,0xc0,0x00,
+0x00,0x18,0x03,0x03,0x02,0x41,0x80,0x30,0x30,0x24,0x76,0x0d,0x0c,0x00,0xc0,0xc0,
+0xd0,0x50,0x00,0x00,0x18,0x01,0x81,0x81,0x40,0x30,0x00,0x28,0x0f,0x7f,0xbc,0x1c,
+0x0e,0x07,0x03,0xc0,0x10,0x70,0x24,0x10,0x09,0x07,0x03,0x80,0xe0,0x70,0x90,0x48,
+0x24,0x12,0x09,0x05,0x81,0x81,0xc0,0x80,0x70,0x18,0x1c,0x07,0x01,0xc1,0xc0,0x90,
+0x00,0x0c,0x04,0x84,0x83,0xe1,0xc0,0xe0,0x38,0x0c,0x0c,0x02,0x00,0x00,0x00,0x00,
+0x00,0x06,0x1c,0x06,0x0f,0x87,0xc0,0x63,0xf8,0x78,0xfe,0x3e,0x0e,0x00,0x00,0x00,
+0x00,0x00,0x00,0x7c,0x1c,0x0c,0x1f,0x03,0xc7,0xc3,0xf1,0xf8,0x3c,0x63,0x3f,0x0f,
+0x8c,0x66,0x06,0x19,0x84,0x78,0x7e,0x1e,0x1f,0x07,0xcf,0xf3,0x1b,0x0d,0x86,0x63,
+0x61,0x9f,0xc6,0x06,0x00,0x30,0x00,0x00,0x10,0x00,0x18,0x00,0x00,0x30,0x00,0x60,
+0x00,0x60,0x06,0x01,0x8c,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xc0,0x60,0x00,0xaa,0xb6,0xc0,0x43,0xe3,
+0xf1,0xf8,0xfc,0x3f,0xef,0x8f,0xdb,0xef,0xf6,0xf8,0xfb,0xff,0x1f,0x8f,0x6f,0xb7,
+0xdb,0xed,0xf6,0xfa,0x7e,0x7e,0x3f,0x7f,0x8f,0xe7,0xe3,0xf8,0xfe,0x3e,0x3f,0x6f,
+0x00,0x00,0x01,0x01,0xc8,0x0b,0x0c,0x30,0x7c,0x14,0x0f,0x0f,0x00,0x00,0x00,0x00,
+0x78,0x00,0x1c,0x00,0x0f,0x07,0x81,0x80,0x00,0x7c,0x00,0x00,0x1c,0x0f,0x80,0x04,
+0x42,0x23,0x90,0x00,0x18,0x0c,0x06,0x03,0x01,0x80,0xc0,0x3c,0x3c,0x3f,0x1f,0x8f,
+0xc7,0xe7,0xe3,0xf1,0xf8,0xfc,0x7c,0x30,0x8f,0x07,0x83,0xc1,0xe0,0xf0,0x00,0x3d,
+0x31,0x98,0xcc,0x66,0x36,0x19,0x80,0xcc,0x0c,0x18,0x09,0x0b,0x02,0x81,0x20,0x00,
+0x00,0x06,0x0c,0x04,0x82,0x40,0x60,0xc0,0x48,0x24,0x18,0x16,0x03,0x03,0x01,0x21,
+0x60,0x50,0x00,0x00,0x06,0x06,0x02,0x41,0x40,0xc1,0x80,0x28,0x87,0x7f,0x84,0x10,
+0x08,0x04,0x02,0x40,0x38,0x48,0x24,0x10,0x09,0x04,0x04,0x81,0x00,0x80,0x90,0x48,
+0x24,0x12,0x09,0x04,0x80,0x41,0x00,0x80,0x40,0x04,0x10,0x04,0x02,0x01,0x20,0x90,
+0x00,0x0c,0x04,0x84,0x86,0x53,0x65,0xb0,0x08,0x18,0x06,0x0a,0x80,0x00,0x00,0x00,
+0x00,0x0c,0x36,0x0e,0x19,0xcc,0xe0,0xe3,0xf8,0xcc,0xfe,0x63,0x1b,0x00,0x00,0x00,
+0x00,0x00,0x00,0xc6,0x62,0x0c,0x19,0x86,0x66,0x63,0x01,0x80,0x66,0x63,0x0c,0x01,
+0x8c,0xc6,0x06,0x19,0xc4,0xcc,0x63,0x33,0x19,0x8c,0x61,0x83,0x1b,0x0d,0x86,0x63,
+0x61,0x80,0xc6,0x03,0x00,0x30,0x30,0x00,0x1c,0x00,0x18,0x00,0x00,0x30,0x00,0x60,
+0x00,0x60,0x00,0x00,0x0c,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,
+0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xc0,0x60,0x01,0x54,0x86,0xc0,0x7b,0xef,
+0xf7,0xfb,0xfd,0xbf,0xc7,0xb7,0xdb,0xef,0xf6,0xfb,0xfb,0x7e,0xff,0x7f,0x6f,0xb7,
+0xdb,0xed,0xf6,0xfb,0x7f,0xbe,0xff,0x7f,0xbf,0xfb,0xef,0xfb,0xfd,0xfe,0xdf,0x6f,
+0xff,0x00,0x07,0x83,0x24,0x13,0x0c,0x30,0xc6,0x00,0x10,0x81,0x80,0x00,0x00,0x00,
+0x84,0x00,0x22,0x00,0x01,0x80,0xc0,0x00,0x00,0xf4,0x00,0x00,0x2c,0x18,0xc0,0x0c,
+0x46,0x20,0x90,0x00,0x18,0x0c,0x06,0x03,0x01,0x80,0xc0,0x70,0x66,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x66,0x38,0x99,0x8c,0xc6,0x63,0x31,0x98,0x00,0x66,
+0x31,0x98,0xcc,0x66,0x36,0x19,0x80,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0xff,0x7f,0xb8,0x1c,
+0x0e,0x07,0x02,0x40,0x7c,0x70,0x3c,0x10,0x09,0x07,0x04,0x00,0xc0,0x60,0xe0,0x70,
+0x38,0x1c,0x0e,0x04,0x83,0x81,0xc0,0x70,0x70,0x38,0x1c,0x07,0x02,0xc1,0xc0,0x90,
+0x00,0x0c,0x00,0x04,0x86,0x43,0x69,0xb0,0x30,0x18,0x06,0x07,0x01,0x00,0x00,0x00,
+0x00,0x0c,0x63,0x16,0x00,0xc0,0x61,0x62,0x01,0x80,0x06,0x63,0x31,0x80,0x00,0x00,
+0x60,0x00,0xc0,0x06,0x43,0x16,0x19,0x8c,0x06,0x33,0x01,0x80,0xc0,0x63,0x0c,0x01,
+0x8c,0x86,0x07,0x39,0xc5,0x86,0x63,0x61,0x99,0x8c,0x01,0x83,0x1b,0x0d,0xb6,0x63,
+0x31,0x01,0x86,0x03,0x00,0x30,0x30,0x00,0x1c,0x3e,0x1b,0x03,0xc1,0xf0,0xf0,0x60,
+0x3e,0x6e,0x3e,0x0f,0x8c,0x60,0xc5,0xb1,0xb8,0x38,0x6c,0x0f,0x8c,0xc7,0xc1,0x83,
+0x19,0x8d,0x82,0x63,0x31,0x9f,0xc1,0x80,0xc0,0xc0,0x00,0xaa,0x86,0xc0,0x47,0xe3,
+0xf1,0xf8,0xfd,0xbf,0x83,0x8f,0xc3,0xef,0xf6,0xf8,0xfc,0xff,0x3f,0x9f,0x1f,0x8f,
+0xc7,0xe3,0xf1,0xfb,0x7c,0x7e,0x3f,0x8f,0x8f,0xc7,0xe3,0xf8,0xfd,0x3e,0x3f,0x6f,
+0x00,0x0c,0x0d,0x43,0x03,0xe1,0x88,0x30,0xc0,0x00,0x27,0x41,0x80,0x00,0x00,0x01,
+0x72,0x00,0x22,0x04,0x01,0x80,0xc0,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xc0,0x04,
+0x82,0x43,0x20,0x18,0x2c,0x16,0x0b,0x05,0x82,0xc1,0x60,0xb0,0xc0,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x38,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x00,0xc7,
+0x31,0x98,0xcc,0x66,0x33,0x11,0xf8,0xc8,0x7c,0x3e,0x1f,0x0f,0x87,0xc3,0xe1,0xd8,
+0x3c,0x1e,0x0f,0x07,0x83,0xc7,0xc3,0xe1,0xf0,0xf8,0x06,0x37,0x07,0x03,0x81,0xc0,
+0xe0,0x70,0x10,0x1d,0x31,0x98,0xcc,0x66,0x33,0x19,0xb0,0xc6,0x8f,0x7f,0x87,0x03,
+0x81,0x80,0x90,0x30,0x6c,0x48,0x24,0x10,0x06,0x04,0x04,0x80,0x20,0x10,0x10,0x0e,
+0x07,0x03,0x81,0xc0,0x60,0x88,0x38,0x0c,0x40,0x09,0x03,0x84,0x02,0x41,0x40,0x90,
+0x00,0x0c,0x00,0x1f,0xe7,0x41,0xd1,0xa0,0x00,0x30,0x03,0x0a,0x81,0x00,0x00,0x00,
+0x00,0x18,0x63,0x06,0x00,0xc0,0xc2,0x62,0x01,0xb0,0x0c,0x72,0x31,0x86,0x03,0x00,
+0xc0,0x00,0x60,0x06,0x8f,0x16,0x19,0x0c,0x06,0x33,0x01,0x80,0xc0,0x63,0x0c,0x01,
+0x8d,0x06,0x07,0x39,0x65,0x86,0x63,0x61,0x99,0x0e,0x01,0x83,0x19,0x89,0xb6,0x32,
+0x33,0x03,0x06,0x01,0x80,0x30,0x78,0x00,0x00,0x03,0x1d,0x86,0x23,0x31,0x99,0xfc,
+0x66,0x77,0x06,0x01,0x8c,0x40,0xc6,0xd9,0xdc,0x6c,0x76,0x19,0x8d,0xcc,0x27,0xf3,
+0x19,0x8d,0x82,0x63,0x31,0x80,0xc0,0x80,0xc0,0x80,0x01,0x54,0x8c,0xc0,0x78,0xfc,
+0x7e,0x7f,0x6f,0xcf,0x93,0xb7,0xdb,0xef,0xf9,0xfb,0xff,0xff,0xdf,0xef,0xef,0xf1,
+0xf8,0xfc,0x7e,0x3f,0x9f,0x77,0xc7,0xf3,0xbf,0xf6,0xfc,0x7b,0xfd,0xbe,0xbf,0x6f,
+0xff,0x0c,0x19,0x03,0x03,0x61,0x98,0x30,0x78,0x00,0x28,0x4f,0x83,0x30,0x00,0x01,
+0x4a,0x00,0x1c,0x04,0x03,0x03,0x80,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xd9,0x84,
+0x82,0x40,0xa0,0x18,0x2c,0x16,0x0b,0x05,0x82,0xc1,0x60,0xb0,0xc0,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x2c,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x64,0xcb,
+0x31,0x98,0xcc,0x66,0x33,0x31,0x8c,0xd8,0x06,0x03,0x01,0x80,0xc0,0x60,0x30,0x6c,
+0x62,0x33,0x19,0x8c,0xc6,0x60,0xc0,0x60,0x30,0x18,0x1e,0x3b,0x8d,0x86,0xc3,0x61,
+0xb0,0xd8,0x10,0x36,0x31,0x98,0xcc,0x66,0x33,0x19,0xd8,0xc6,0x0f,0x7f,0x82,0x01,
+0x02,0x40,0xd0,0x40,0x6c,0x70,0x24,0x1c,0x06,0x04,0x03,0x01,0xc0,0xe0,0x10,0x12,
+0x09,0x04,0x82,0x40,0x90,0x50,0x10,0x12,0x70,0x09,0x04,0x04,0x01,0xc1,0x20,0x60,
+0x00,0x0c,0x00,0x04,0x83,0xc0,0x20,0xcc,0x00,0x30,0x03,0x02,0x01,0x00,0x00,0x00,
+0x00,0x18,0x63,0x06,0x01,0x83,0x84,0x63,0xf1,0xd8,0x18,0x3c,0x31,0x86,0x03,0x01,
+0x83,0xf8,0x30,0x1c,0x9b,0x33,0x1e,0x0c,0x06,0x33,0xe1,0x80,0xc0,0x7f,0x0c,0x01,
+0x8f,0x06,0x07,0x79,0x65,0x86,0x66,0x61,0x9e,0x07,0x81,0x83,0x19,0x89,0xb6,0x1c,
+0x1a,0x03,0x06,0x01,0x80,0x30,0x48,0x00,0x00,0x03,0x18,0xcc,0x06,0x33,0x18,0x60,
+0xc6,0x63,0x06,0x01,0x8c,0x80,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8e,0x4c,0x01,0x83,
+0x19,0x8d,0x92,0x32,0x31,0x81,0x87,0x00,0xc0,0x70,0xe4,0xaa,0x98,0xc0,0x7d,0xfe,
+0xfd,0xbf,0x2f,0xbf,0x93,0x8f,0xdb,0xe3,0xf9,0xfb,0xff,0x1e,0x3f,0x1f,0xef,0xed,
+0xf6,0xfb,0x7d,0xbf,0x6f,0xaf,0xef,0xed,0x8f,0xf6,0xfb,0xfb,0xfe,0x3e,0xdf,0x9f,
+0x00,0x00,0x19,0x0f,0xc6,0x30,0xd0,0x00,0xcc,0x00,0x28,0x59,0x86,0x67,0xf0,0x01,
+0x72,0x00,0x00,0x3f,0x86,0x00,0xc0,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xcc,0xc5,
+0x32,0x83,0x4c,0x00,0x66,0x33,0x19,0x8c,0xc6,0x63,0x31,0xbc,0xc0,0x3e,0x1f,0x0f,
+0x87,0xc1,0x80,0xc0,0x60,0x30,0xfb,0x2c,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x38,0xcb,
+0x31,0x98,0xcc,0x66,0x31,0xa1,0x8c,0xcc,0x06,0x03,0x01,0x80,0xc0,0x60,0x30,0x6c,
+0xc0,0x63,0x31,0x98,0xcc,0x60,0xc0,0x60,0x30,0x18,0x37,0x31,0x98,0xcc,0x66,0x33,
+0x19,0x8c,0x00,0x67,0x31,0x98,0xcc,0x66,0x33,0x19,0x8c,0xc6,0x1f,0x7f,0x82,0x01,
+0x02,0x40,0xb0,0x40,0x6c,0x07,0x03,0x83,0x80,0xe0,0xe0,0x00,0x18,0x0e,0x10,0x10,
+0x08,0x04,0x02,0x00,0xf0,0x20,0x10,0x1e,0x08,0x89,0x03,0x00,0xe0,0x38,0x1c,0x0e,
+0x00,0x0c,0x00,0x04,0x81,0xe0,0x41,0x6c,0x00,0x30,0x03,0x00,0x0f,0xe0,0x03,0xf8,
+0x00,0x30,0x63,0x06,0x03,0x00,0xc7,0xf0,0x39,0x8c,0x30,0x3e,0x1b,0x80,0x00,0x03,
+0x00,0x00,0x18,0x30,0x9b,0x23,0x19,0x0c,0x06,0x33,0x01,0xf8,0xc6,0x63,0x0c,0x01,
+0x8d,0x86,0x05,0xd9,0x35,0x86,0x7c,0x61,0x9b,0x01,0xc1,0x83,0x19,0x99,0xb4,0x1c,
+0x0c,0x06,0x06,0x00,0xc0,0x30,0xcc,0x00,0x00,0x3f,0x18,0xcc,0x06,0x33,0xf8,0x60,
+0xc6,0x63,0x06,0x01,0x8f,0x00,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x0f,0x81,0x83,
+0x18,0xd9,0xba,0x1c,0x1b,0x03,0x00,0x80,0xc0,0x81,0x75,0x54,0x98,0xc0,0x7d,0xfe,
+0xfd,0xbf,0x4f,0xbf,0x93,0xf8,0xfc,0x7c,0x7f,0x1f,0x1f,0x6f,0xe7,0xf1,0xef,0xef,
+0xf7,0xfb,0xfd,0xff,0x0f,0xdf,0xef,0xe1,0xf7,0x76,0xfc,0xff,0x1f,0xc7,0xe3,0xf1,
+0xff,0x08,0x19,0x03,0x06,0x31,0xf8,0x00,0xc6,0x00,0x28,0x5b,0x8c,0xc0,0x11,0xf1,
+0x4a,0x00,0x00,0x04,0x0c,0x00,0xc0,0x03,0x18,0x74,0x38,0x00,0x0c,0x18,0xc6,0x65,
+0x52,0xb8,0x54,0x18,0x46,0x23,0x11,0x88,0xc4,0x62,0x31,0x30,0xc0,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x26,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x10,0xd3,
+0x31,0x98,0xcc,0x66,0x30,0xc1,0x8c,0xc6,0x7e,0x3f,0x1f,0x8f,0xc7,0xe3,0xf1,0xfc,
+0xc0,0x7f,0x3f,0x9f,0xcf,0xe0,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33,
+0x19,0x8c,0xfe,0x6b,0x31,0x98,0xcc,0x66,0x31,0xb1,0x8c,0x6c,0x0e,0x7f,0x82,0x01,
+0x01,0x80,0x90,0x30,0xc6,0x08,0x01,0x02,0x00,0x40,0x80,0xe0,0x24,0x04,0x1c,0x10,
+0x08,0x04,0x02,0x00,0x90,0x20,0x10,0x12,0x0d,0x86,0x00,0x81,0x00,0x40,0x20,0x10,
+0x00,0x04,0x00,0x1f,0xe1,0x70,0xbb,0x28,0x00,0x30,0x03,0x00,0x01,0x00,0x00,0x00,
+0x00,0x30,0x63,0x06,0x06,0x00,0x67,0xf0,0x19,0x8c,0x30,0x67,0x0d,0x80,0x00,0x01,
+0x83,0xf8,0x30,0x30,0x9b,0x7f,0x19,0x8c,0x06,0x33,0x01,0x80,0xc6,0x63,0x0c,0x01,
+0x8c,0xc6,0x05,0xd9,0x35,0x86,0x60,0x61,0x99,0x80,0xe1,0x83,0x18,0xd0,0xdc,0x26,
+0x0c,0x0c,0x06,0x00,0xc0,0x30,0x84,0x00,0x00,0x63,0x18,0xcc,0x06,0x33,0x00,0x60,
+0xc6,0x63,0x06,0x01,0x8d,0x80,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x03,0xe1,0x83,
+0x18,0xd9,0xba,0x1c,0x1b,0x06,0x01,0x80,0xc0,0xc1,0x38,0xaa,0x80,0xc0,0x7d,0xfe,
+0xfe,0x7f,0x6f,0xcf,0x39,0xf7,0xfe,0xfd,0xff,0xbf,0x7f,0x0f,0xdb,0xfb,0xe3,0xef,
+0xf7,0xfb,0xfd,0xff,0x6f,0xdf,0xef,0xed,0xf2,0x79,0xff,0x7e,0xff,0xbf,0xdf,0xef,
+0x00,0x0c,0x19,0x03,0x03,0x60,0x60,0x30,0x66,0x00,0x28,0x4d,0xc6,0x60,0x10,0x00,
+0x84,0x00,0x00,0x04,0x0f,0x87,0x80,0x03,0x18,0x14,0x38,0x00,0x3f,0x0f,0x8c,0xc2,
+0x90,0x84,0xa4,0x18,0xfe,0x7f,0x3f,0x9f,0xcf,0xe7,0xf1,0xf0,0xc0,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x26,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x38,0xd3,
+0x31,0x98,0xcc,0x66,0x30,0xc1,0x98,0xc6,0xc6,0x63,0x31,0x98,0xcc,0x66,0x33,0x60,
+0xc0,0x60,0x30,0x18,0x0c,0x00,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33,
+0x19,0x8c,0x00,0x6b,0x31,0x98,0xcc,0x66,0x31,0xb1,0x8c,0x6c,0x1c,0x7f,0x81,0x20,
+0x90,0x38,0x18,0x0b,0x83,0x06,0x01,0x03,0x80,0x40,0xe0,0x90,0x24,0x04,0x03,0x8e,
+0x86,0xc3,0x61,0x90,0x24,0x12,0x0e,0x04,0x8a,0x81,0xc7,0x70,0xc0,0x30,0x18,0x0c,
+0x00,0x00,0x00,0x04,0x81,0x31,0x6f,0x30,0x00,0x18,0x06,0x00,0x01,0x00,0x00,0x00,
+0x00,0x60,0x63,0x06,0x0c,0x00,0x60,0x60,0x19,0x8c,0x60,0x63,0x01,0x80,0x00,0x00,
+0xc0,0x00,0x60,0x00,0x4d,0xe1,0x99,0x8c,0x06,0x33,0x01,0x80,0xc6,0x63,0x0c,0x01,
+0x8c,0xc6,0x04,0x99,0x1d,0x86,0x60,0x61,0x99,0x80,0x61,0x83,0x18,0xd0,0xdc,0x63,
+0x0c,0x0c,0x06,0x00,0x60,0x30,0x84,0x00,0x00,0x63,0x18,0xcc,0x06,0x33,0x00,0x60,
+0x6e,0x63,0x06,0x01,0x8c,0xc0,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x00,0x61,0x83,
+0x18,0xd0,0xcc,0x26,0x0e,0x0c,0x03,0x00,0xc0,0x60,0x01,0x54,0x98,0xc0,0x7e,0xdf,
+0x6f,0xc7,0xe7,0xf4,0x7c,0xf9,0xfe,0xfc,0x7f,0xbf,0x1f,0x5f,0xdb,0xfb,0xfc,0x71,
+0x79,0x3c,0x9e,0x6f,0xdb,0xed,0xf1,0xfb,0x75,0x7e,0x38,0x8f,0x3f,0xcf,0xe7,0xf3,
+0xff,0x0c,0x0d,0x03,0x03,0xe1,0xf8,0x30,0x3c,0x00,0x27,0x40,0x03,0x30,0x00,0x00,
+0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x18,0x14,0x00,0x00,0x00,0x00,0x19,0x82,
+0xf8,0x98,0xbe,0x70,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x30,0xc0,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x23,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x4c,0xe3,
+0x31,0x98,0xcc,0x66,0x30,0xc1,0xf0,0xc6,0xc6,0x63,0x31,0x98,0xcc,0x66,0x33,0x60,
+0xc0,0x60,0x30,0x18,0x0c,0x00,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33,
+0x19,0x8c,0x10,0x73,0x31,0x98,0xcc,0x66,0x30,0xe1,0x8c,0x38,0x1c,0x7f,0x80,0xa0,
+0x50,0x10,0x24,0x0d,0xff,0x01,0x01,0x02,0x00,0x40,0x80,0xf0,0x24,0x04,0x02,0x01,
+0x81,0x20,0x10,0x30,0x28,0x1a,0x09,0x06,0x8a,0x81,0x20,0x90,0x20,0x08,0x04,0x02,
+0x00,0x0c,0x00,0x04,0x85,0x32,0x6f,0xb8,0x00,0x18,0x06,0x00,0x01,0x01,0xc0,0x00,
+0x70,0x60,0x36,0x06,0x1f,0xcc,0xe0,0x63,0x30,0xd8,0x60,0x63,0x33,0x06,0x03,0x00,
+0x60,0x00,0xc0,0x30,0x60,0x61,0x99,0x86,0x66,0x63,0x01,0x80,0x66,0x63,0x0c,0x03,
+0x0c,0x66,0x04,0x19,0x1c,0xcc,0x60,0x33,0x18,0xcc,0x61,0x81,0xb0,0x60,0xcc,0x63,
+0x0c,0x18,0x06,0x00,0x60,0x30,0x00,0x00,0x00,0x67,0x19,0x86,0x23,0x71,0x88,0x60,
+0x36,0x63,0x06,0x01,0x8c,0x60,0xc6,0xd9,0x8c,0x6c,0x66,0x1b,0x8c,0x08,0x61,0x83,
+0xb8,0x70,0xcc,0x63,0x0c,0x18,0x03,0x00,0xc0,0x60,0x00,0xaa,0x98,0xc0,0x7f,0x5f,
+0xaf,0xef,0xdb,0xf2,0x00,0xfe,0xfe,0xfd,0xff,0xbf,0x7f,0x6f,0xdb,0xfb,0xfd,0xfe,
+0x7e,0xdf,0xef,0xcf,0xd7,0xe5,0xf6,0xf9,0x75,0x7e,0xdf,0x6f,0xdf,0xf7,0xfb,0xfd,
+0x00,0x0c,0x07,0xc6,0x04,0x10,0x60,0x30,0x06,0x00,0x10,0x80,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x3f,0x80,0x00,0x00,0x03,0xb8,0x14,0x00,0x00,0x00,0x00,0x00,0x04,
+0x11,0x21,0x04,0xc0,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x30,0x66,0x30,0x18,0x0c,
+0x06,0x01,0x80,0xc0,0x60,0x30,0x66,0x23,0x99,0x8c,0xc6,0x63,0x31,0x98,0x00,0x66,
+0x1b,0x0d,0x86,0xc3,0x60,0xc1,0x80,0xc6,0xce,0x67,0x33,0x99,0xcc,0xe6,0x73,0x74,
+0x62,0x31,0x18,0x8c,0x46,0x20,0xc0,0x60,0x30,0x18,0x36,0x31,0x8d,0x86,0xc3,0x61,
+0xb0,0xd8,0x10,0x36,0x3b,0x9d,0xce,0xe7,0x70,0xc1,0x98,0x30,0x00,0x7f,0x80,0xc0,
+0x60,0x10,0x24,0x0c,0x38,0x0e,0x01,0x02,0x00,0x40,0x80,0xa0,0x18,0x0e,0x03,0x00,
+0x80,0x40,0x60,0x50,0x30,0x16,0x0e,0x05,0x88,0x81,0xc0,0x81,0xc0,0x70,0x38,0x1c,
+0x00,0x0c,0x00,0x04,0x83,0xe0,0x39,0xcc,0x00,0x0c,0x0c,0x00,0x00,0x01,0xc0,0x00,
+0x70,0xc0,0x1c,0x06,0x1f,0xc7,0xc0,0x61,0xe0,0x70,0x60,0x3e,0x1e,0x06,0x03,0x00,
+0x00,0x00,0x00,0x30,0x1e,0x61,0x9f,0x03,0xc7,0xc3,0xf1,0x80,0x3e,0x63,0x3f,0x1e,
+0x0c,0x67,0xe4,0x19,0x0c,0x78,0x60,0x1e,0x18,0xc7,0xc1,0x80,0xe0,0x60,0xcc,0x63,
+0x0c,0x1f,0xc6,0x00,0x30,0x30,0x00,0x00,0x00,0x3b,0x9f,0x03,0xc1,0xb0,0xf0,0x60,
+0x06,0x63,0x06,0x01,0x8c,0x70,0xc6,0xd9,0x8c,0x38,0x7c,0x0d,0x8c,0x07,0xc0,0xf1,
+0xd8,0x60,0xcc,0x63,0x0c,0x1f,0xc3,0x00,0xc0,0x60,0x01,0x54,0x80,0xc0,0x7f,0x3f,
+0x9f,0xef,0xdb,0xf3,0xc7,0xf1,0xfe,0xfd,0xff,0xbf,0x7f,0xff,0xe7,0xf1,0xfc,0xff,
+0x7f,0xbf,0x9f,0xaf,0xcf,0xe9,0xf1,0xfa,0x77,0x7e,0x3f,0x7e,0x3f,0x8f,0xc7,0xe3,
+0xff,0x0c,0x01,0x0f,0xe8,0x08,0x60,0x30,0xc6,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xd8,0x14,0x00,0x00,0x00,0x00,0x00,0x04,
+0x11,0x3d,0x04,0xc0,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x3c,0x3c,0x3f,0x1f,0x8f,
+0xc7,0xe7,0xe3,0xf1,0xf8,0xfc,0x7c,0x21,0x8f,0x07,0x83,0xc1,0xe0,0xf0,0x00,0xbc,
+0x0e,0x07,0x03,0x81,0xc0,0xc1,0x80,0xcc,0x77,0x3b,0x9d,0xce,0xe7,0x73,0xb9,0x98,
+0x3c,0x1e,0x0f,0x07,0x83,0xc0,0xc0,0x60,0x30,0x18,0x1c,0x31,0x87,0x03,0x81,0xc0,
+0xe0,0x70,0x00,0x5c,0x1d,0x8e,0xc7,0x63,0xb0,0xc1,0xf0,0x30,0x00,0x7f,0x81,0x40,
+0xa0,0x10,0x28,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x02,0x00,
+0x80,0x80,0x10,0xf8,0x28,0x12,0x09,0x04,0x80,0x01,0x20,0x80,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x06,0x18,0x00,0x00,0x00,0x40,0x00,
+0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x07,0xc0,0x31,0xf0,0x01,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0xcc,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x60,0x01,0x80,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x00,0x01,0xe0,0xc3,0xc0,0x00,0x00,0xff,0xc0,0x7e,0xbf,
+0x5f,0xef,0xd7,0xf5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,
+0x7f,0x7f,0xef,0x07,0xd7,0xed,0xf6,0xfb,0x7f,0xfe,0xdf,0x7f,0xff,0xff,0xff,0xff,
+0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x30,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x14,0x00,0x08,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x81,0x80,0x60,0x00,0x7f,0x81,0x20,
+0x90,0x10,0x1c,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,
+0x81,0xe0,0x60,0x10,0x24,0x12,0x0e,0x04,0x80,0x01,0xc0,0x70,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x78,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x01,0x80,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xfe,0xdf,
+0x6f,0xef,0xe3,0xf5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x7f,
+0x7e,0x1f,0x9f,0xef,0xdb,0xed,0xf1,0xfb,0x7f,0xfe,0x3f,0x8f,0xff,0xff,0xff,0xff,
+0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x81,0x80,0x60,0x20,0x20,0x20,0x20,
+0x20,0x20,0x20,0x20,0x32,0x35,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+0x20,0x31,0x35,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x33,0x20,
+0x00,0x00,0x01,0x0c,0x00,0x09,0x09,0x00,0x01,0x0f,0x00,0x09,0x12,0x00,0x01,0x0f,
+0x00,0x09,0x1b,0x00,0x01,0x0f,0x00,0x09,0x24,0x00,0x01,0x0f,0x00,0x09,0x2d,0x00,
+0x01,0x0f,0x00,0x09,0x36,0x00,0x01,0x0f,0x00,0x09,0x3f,0x00,0x03,0x0d,0x00,0x09,
+0x48,0x00,0x03,0x0d,0x00,0x09,0x51,0x00,0x03,0x0d,0x00,0x09,0x5a,0x00,0x03,0x0d,
+0x00,0x09,0x63,0x00,0x03,0x0d,0x00,0x09,0x6c,0x00,0x03,0x0d,0x00,0x09,0x75,0x00,
+0x03,0x0e,0x00,0x09,0x7e,0x00,0x03,0x0d,0x00,0x09,0x87,0x00,0x03,0x0d,0x00,0x09,
+0x90,0x00,0x01,0x0f,0x00,0x09,0x99,0x00,0x01,0x0f,0x00,0x09,0xa2,0x00,0x01,0x0f,
+0x00,0x09,0xab,0x00,0x01,0x0f,0x00,0x09,0xb4,0x00,0x01,0x0f,0x00,0x09,0xbd,0x00,
+0x01,0x0f,0x00,0x09,0xc6,0x00,0x01,0x0f,0x00,0x09,0xcf,0x00,0x01,0x0f,0x00,0x09,
+0xd8,0x00,0x01,0x0f,0x00,0x09,0xe1,0x00,0x03,0x0d,0x00,0x09,0xea,0x00,0x01,0x0f,
+0x00,0x09,0xf3,0x00,0x01,0x0f,0x00,0x09,0xfc,0x00,0x03,0x0d,0x00,0x09,0x05,0x01,
+0x03,0x0d,0x00,0x09,0x0e,0x01,0x03,0x0d,0x00,0x09,0x17,0x01,0x03,0x0d,0x00,0x09,
+0x20,0x01,0x00,0x00,0x00,0x09,0x29,0x01,0x03,0x0d,0x00,0x09,0x32,0x01,0x02,0x05,
+0x00,0x09,0x3b,0x01,0x03,0x0d,0x00,0x09,0x44,0x01,0x02,0x0e,0x00,0x09,0x4d,0x01,
+0x03,0x0d,0x00,0x09,0x56,0x01,0x03,0x0d,0x00,0x09,0x5f,0x01,0x02,0x06,0x00,0x09,
+0x68,0x01,0x02,0x0e,0x00,0x09,0x71,0x01,0x02,0x0e,0x00,0x09,0x7a,0x01,0x03,0x08,
+0x00,0x09,0x83,0x01,0x05,0x0c,0x00,0x09,0x8c,0x01,0x0b,0x0f,0x00,0x09,0x95,0x01,
+0x08,0x09,0x00,0x09,0x9e,0x01,0x0b,0x0d,0x00,0x09,0xa7,0x01,0x02,0x0e,0x00,0x09,
+0xb0,0x01,0x03,0x0d,0x00,0x09,0xb9,0x01,0x03,0x0d,0x00,0x09,0xc2,0x01,0x03,0x0d,
+0x00,0x09,0xcb,0x01,0x03,0x0d,0x00,0x09,0xd4,0x01,0x03,0x0d,0x00,0x09,0xdd,0x01,
+0x03,0x0d,0x00,0x09,0xe6,0x01,0x03,0x0d,0x00,0x09,0xef,0x01,0x03,0x0d,0x00,0x09,
+0xf8,0x01,0x03,0x0d,0x00,0x09,0x01,0x02,0x03,0x0d,0x00,0x09,0x0a,0x02,0x06,0x0d,
+0x00,0x09,0x13,0x02,0x06,0x0f,0x00,0x09,0x1c,0x02,0x05,0x0c,0x00,0x09,0x25,0x02,
+0x07,0x0a,0x00,0x09,0x2e,0x02,0x05,0x0c,0x00,0x09,0x37,0x02,0x03,0x0d,0x00,0x09,
+0x40,0x02,0x03,0x0d,0x00,0x09,0x49,0x02,0x03,0x0d,0x00,0x09,0x52,0x02,0x03,0x0d,
+0x00,0x09,0x5b,0x02,0x03,0x0d,0x00,0x09,0x64,0x02,0x03,0x0d,0x00,0x09,0x6d,0x02,
+0x03,0x0d,0x00,0x09,0x76,0x02,0x03,0x0d,0x00,0x09,0x7f,0x02,0x03,0x0d,0x00,0x09,
+0x88,0x02,0x03,0x0d,0x00,0x09,0x91,0x02,0x03,0x0d,0x00,0x09,0x9a,0x02,0x03,0x0d,
+0x00,0x09,0xa3,0x02,0x03,0x0d,0x00,0x09,0xac,0x02,0x03,0x0d,0x00,0x09,0xb5,0x02,
+0x03,0x0d,0x00,0x09,0xbe,0x02,0x03,0x0d,0x00,0x09,0xc7,0x02,0x03,0x0d,0x00,0x09,
+0xd0,0x02,0x03,0x0d,0x00,0x09,0xd9,0x02,0x03,0x0f,0x00,0x09,0xe2,0x02,0x03,0x0d,
+0x00,0x09,0xeb,0x02,0x03,0x0d,0x00,0x09,0xf4,0x02,0x03,0x0d,0x00,0x09,0xfd,0x02,
+0x03,0x0d,0x00,0x09,0x06,0x03,0x03,0x0d,0x00,0x09,0x0f,0x03,0x03,0x0d,0x00,0x09,
+0x18,0x03,0x03,0x0d,0x00,0x09,0x21,0x03,0x03,0x0d,0x00,0x09,0x2a,0x03,0x03,0x0d,
+0x00,0x09,0x33,0x03,0x02,0x0e,0x00,0x09,0x3c,0x03,0x02,0x0e,0x00,0x09,0x45,0x03,
+0x02,0x0e,0x00,0x09,0x4e,0x03,0x04,0x0b,0x00,0x09,0x57,0x03,0x0d,0x0e,0x00,0x09,
+0x60,0x03,0x02,0x06,0x00,0x09,0x69,0x03,0x05,0x0d,0x00,0x09,0x72,0x03,0x02,0x0d,
+0x00,0x09,0x7b,0x03,0x05,0x0d,0x00,0x09,0x84,0x03,0x02,0x0d,0x00,0x09,0x8d,0x03,
+0x05,0x0d,0x00,0x09,0x96,0x03,0x02,0x0d,0x00,0x09,0x9f,0x03,0x05,0x0f,0x00,0x09,
+0xa8,0x03,0x02,0x0d,0x00,0x09,0xb1,0x03,0x02,0x0d,0x00,0x09,0xba,0x03,0x02,0x0f,
+0x00,0x09,0xc3,0x03,0x02,0x0d,0x00,0x09,0xcc,0x03,0x02,0x0d,0x00,0x09,0xd5,0x03,
+0x05,0x0d,0x00,0x09,0xde,0x03,0x05,0x0d,0x00,0x09,0xe7,0x03,0x05,0x0d,0x00,0x09,
+0xf0,0x03,0x05,0x0f,0x00,0x09,0xf9,0x03,0x05,0x0f,0x00,0x09,0x02,0x04,0x05,0x0d,
+0x00,0x09,0x0b,0x04,0x05,0x0d,0x00,0x09,0x14,0x04,0x03,0x0d,0x00,0x09,0x1d,0x04,
+0x05,0x0d,0x00,0x09,0x26,0x04,0x05,0x0d,0x00,0x09,0x2f,0x04,0x05,0x0d,0x00,0x09,
+0x38,0x04,0x05,0x0d,0x00,0x09,0x41,0x04,0x05,0x0f,0x00,0x09,0x4a,0x04,0x05,0x0d,
+0x00,0x09,0x53,0x04,0x02,0x0e,0x00,0x09,0x5c,0x04,0x02,0x0e,0x00,0x09,0x65,0x04,
+0x02,0x0e,0x00,0x09,0x6e,0x04,0x07,0x0a,0x00,0x09,0x77,0x04,0x01,0x0d,0x00,0x09,
+0x80,0x04,0x00,0x0e,0x00,0x09,0x89,0x04,0x00,0x0f,0x00,0x09,0x92,0x04,0x00,0x0f,
+0x00,0x09,0x9b,0x04,0x00,0x0f,0x00,0x09,0xa4,0x04,0x00,0x0f,0x00,0x09,0xad,0x04,
+0x00,0x0f,0x00,0x09,0xb6,0x04,0x00,0x0f,0x00,0x09,0xbf,0x04,0x00,0x0f,0x00,0x09,
+0xc8,0x04,0x00,0x0f,0x00,0x09,0xd1,0x04,0x00,0x0f,0x00,0x09,0xda,0x04,0x00,0x0f,
+0x00,0x09,0xe3,0x04,0x00,0x0f,0x00,0x09,0xec,0x04,0x00,0x0f,0x00,0x09,0xf5,0x04,
+0x00,0x0f,0x00,0x09,0xfe,0x04,0x00,0x0f,0x00,0x09,0x07,0x05,0x00,0x0f,0x00,0x09,
+0x10,0x05,0x00,0x0f,0x00,0x09,0x19,0x05,0x00,0x0f,0x00,0x09,0x22,0x05,0x00,0x0f,
+0x00,0x09,0x2b,0x05,0x00,0x0f,0x00,0x09,0x34,0x05,0x00,0x0f,0x00,0x09,0x3d,0x05,
+0x00,0x0f,0x00,0x09,0x46,0x05,0x00,0x0f,0x00,0x09,0x4f,0x05,0x00,0x0f,0x00,0x09,
+0x58,0x05,0x00,0x0f,0x00,0x09,0x61,0x05,0x00,0x0f,0x00,0x09,0x6a,0x05,0x00,0x0f,
+0x00,0x09,0x73,0x05,0x00,0x0f,0x00,0x09,0x7c,0x05,0x00,0x0f,0x00,0x09,0x85,0x05,
+0x00,0x0f,0x00,0x09,0x8e,0x05,0x00,0x0f,0x00,0x09,0x97,0x05,0x00,0x0f,0x00,0x09,
+0xa0,0x05,0x00,0x0d,0x00,0x09,0xa9,0x05,0x05,0x0f,0x00,0x09,0xb2,0x05,0x02,0x0e,
+0x00,0x09,0xbb,0x05,0x03,0x0d,0x00,0x09,0xc4,0x05,0x03,0x0d,0x00,0x09,0xcd,0x05,
+0x03,0x0d,0x00,0x09,0xd6,0x05,0x02,0x0e,0x00,0x09,0xdf,0x05,0x03,0x0e,0x00,0x09,
+0xe8,0x05,0x02,0x04,0x00,0x09,0xf1,0x05,0x03,0x0d,0x00,0x09,0xfa,0x05,0x03,0x0a,
+0x00,0x09,0x03,0x06,0x06,0x0b,0x00,0x09,0x0c,0x06,0x07,0x0a,0x00,0x09,0x15,0x06,
+0x08,0x09,0x00,0x09,0x1e,0x06,0x03,0x0b,0x00,0x09,0x27,0x06,0x02,0x03,0x00,0x09,
+0x30,0x06,0x03,0x07,0x00,0x09,0x39,0x06,0x05,0x0c,0x00,0x09,0x42,0x06,0x03,0x0a,
+0x00,0x09,0x4b,0x06,0x03,0x0a,0x00,0x09,0x54,0x06,0x02,0x04,0x00,0x09,0x5d,0x06,
+0x05,0x0f,0x00,0x09,0x66,0x06,0x03,0x0e,0x00,0x09,0x6f,0x06,0x08,0x0a,0x00,0x09,
+0x78,0x06,0x0d,0x0f,0x00,0x09,0x81,0x06,0x03,0x0a,0x00,0x09,0x8a,0x06,0x03,0x0a,
+0x00,0x09,0x93,0x06,0x06,0x0b,0x00,0x09,0x9c,0x06,0x03,0x0d,0x00,0x09,0xa5,0x06,
+0x03,0x0d,0x00,0x09,0xae,0x06,0x03,0x0d,0x00,0x09,0xb7,0x06,0x05,0x0f,0x00,0x09,
+0xc0,0x06,0x00,0x0d,0x00,0x09,0xc9,0x06,0x00,0x0d,0x00,0x09,0xd2,0x06,0x00,0x0d,
+0x00,0x09,0xdb,0x06,0x00,0x0d,0x00,0x09,0xe4,0x06,0x00,0x0d,0x00,0x09,0xed,0x06,
+0x01,0x0d,0x00,0x09,0xf6,0x06,0x03,0x0d,0x00,0x09,0xff,0x06,0x03,0x0f,0x00,0x09,
+0x08,0x07,0x00,0x0d,0x00,0x09,0x11,0x07,0x00,0x0d,0x00,0x09,0x1a,0x07,0x00,0x0d,
+0x00,0x09,0x23,0x07,0x00,0x0d,0x00,0x09,0x2c,0x07,0x00,0x0d,0x00,0x09,0x35,0x07,
+0x00,0x0d,0x00,0x09,0x3e,0x07,0x00,0x0d,0x00,0x09,0x47,0x07,0x00,0x0d,0x00,0x09,
+0x50,0x07,0x03,0x0d,0x00,0x09,0x59,0x07,0x00,0x0d,0x00,0x09,0x62,0x07,0x00,0x0d,
+0x00,0x09,0x6b,0x07,0x00,0x0d,0x00,0x09,0x74,0x07,0x00,0x0d,0x00,0x09,0x7d,0x07,
+0x00,0x0d,0x00,0x09,0x86,0x07,0x00,0x0d,0x00,0x09,0x8f,0x07,0x06,0x0b,0x00,0x09,
+0x98,0x07,0x03,0x0d,0x00,0x09,0xa1,0x07,0x00,0x0d,0x00,0x09,0xaa,0x07,0x00,0x0d,
+0x00,0x09,0xb3,0x07,0x00,0x0d,0x00,0x09,0xbc,0x07,0x00,0x0d,0x00,0x09,0xc5,0x07,
+0x00,0x0d,0x00,0x09,0xce,0x07,0x03,0x0d,0x00,0x09,0xd7,0x07,0x02,0x0d,0x00,0x09,
+0xe0,0x07,0x02,0x0d,0x00,0x09,0xe9,0x07,0x02,0x0d,0x00,0x09,0xf2,0x07,0x02,0x0d,
+0x00,0x09,0xfb,0x07,0x02,0x0d,0x00,0x09,0x04,0x08,0x02,0x0d,0x00,0x09,0x0d,0x08,
+0x02,0x0d,0x00,0x09,0x16,0x08,0x05,0x0d,0x00,0x09,0x1f,0x08,0x05,0x0f,0x00,0x09,
+0x28,0x08,0x02,0x0d,0x00,0x09,0x31,0x08,0x02,0x0d,0x00,0x09,0x3a,0x08,0x02,0x0d,
+0x00,0x09,0x43,0x08,0x02,0x0d,0x00,0x09,0x4c,0x08,0x02,0x0d,0x00,0x09,0x55,0x08,
+0x02,0x0d,0x00,0x09,0x5e,0x08,0x02,0x0d,0x00,0x09,0x67,0x08,0x02,0x0d,0x00,0x09,
+0x70,0x08,0x02,0x0d,0x00,0x09,0x79,0x08,0x02,0x0d,0x00,0x09,0x82,0x08,0x02,0x0d,
+0x00,0x09,0x8b,0x08,0x02,0x0d,0x00,0x09,0x94,0x08,0x02,0x0d,0x00,0x09,0x9d,0x08,
+0x02,0x0d,0x00,0x09,0xa6,0x08,0x02,0x0d,0x00,0x09,0xaf,0x08,0x05,0x0c,0x00,0x09,
+0xb8,0x08,0x05,0x0d,0x00,0x09,0xc1,0x08,0x02,0x0d,0x00,0x09,0xca,0x08,0x02,0x0d,
+0x00,0x09,0xd3,0x08,0x02,0x0d,0x00,0x09,0xdc,0x08,0x02,0x0d,0x00,0x09,0xe5,0x08,
+0x02,0x0f,0x00,0x09,0xee,0x08,0x03,0x0f,0x00,0x09,0xf7,0x08,0x02,0x0f,0x00,0x09,
+0x00,0x09,0x00,0x00,0x00,0x00,
+};
+
+int sizeofdefont = sizeof defontdata;
+
+void
+_unpackinfo(Fontchar *fc, uchar *p, int n)
+{
+ int j;
+
+ for(j=0; j<=n; j++){
+ fc->x = p[0]|(p[1]<<8);
+ fc->top = p[2];
+ fc->bottom = p[3];
+ fc->left = p[4];
+ fc->width = p[5];
+ fc++;
+ p += 6;
+ }
+}
diff --git a/libdraw/draw.c b/libdraw/draw.c
new file mode 100644
index 00000000..8fdc487f
--- /dev/null
+++ b/libdraw/draw.c
@@ -0,0 +1,68 @@
+#include "lib9.h"
+#include "draw.h"
+
+void
+_setdrawop(Display *d, Drawop op)
+{
+ uchar *a;
+
+ if(op != SoverD){
+ a = bufimage(d, 1+1);
+ if(a == 0)
+ return;
+ a[0] = 'O';
+ a[1] = op;
+ }
+}
+
+static void
+draw1(Image *dst, Rectangle *r, Image *src, Point *p0, Image *mask, Point *p1, Drawop op)
+{
+ uchar *a;
+
+ _setdrawop(dst->display, op);
+
+ a = bufimage(dst->display, 1+4+4+4+4*4+2*4+2*4);
+ if(a == 0)
+ return;
+ if(src == nil)
+ src = dst->display->black;
+ if(mask == nil)
+ mask = dst->display->opaque;
+ a[0] = 'd';
+ BPLONG(a+1, dst->id);
+ BPLONG(a+5, src->id);
+ BPLONG(a+9, mask->id);
+ BPLONG(a+13, r->min.x);
+ BPLONG(a+17, r->min.y);
+ BPLONG(a+21, r->max.x);
+ BPLONG(a+25, r->max.y);
+ BPLONG(a+29, p0->x);
+ BPLONG(a+33, p0->y);
+ BPLONG(a+37, p1->x);
+ BPLONG(a+41, p1->y);
+}
+
+void
+draw(Image *dst, Rectangle r, Image *src, Image *mask, Point p1)
+{
+ draw1(dst, &r, src, &p1, mask, &p1, SoverD);
+}
+
+void
+drawop(Image *dst, Rectangle r, Image *src, Image *mask, Point p1, Drawop op)
+{
+ draw1(dst, &r, src, &p1, mask, &p1, op);
+}
+
+void
+gendraw(Image *dst, Rectangle r, Image *src, Point p0, Image *mask, Point p1)
+{
+ draw1(dst, &r, src, &p0, mask, &p1, SoverD);
+}
+
+void
+gendrawop(Image *dst, Rectangle r, Image *src, Point p0, Image *mask, Point p1, Drawop op)
+{
+ draw1(dst, &r, src, &p0, mask, &p1, op);
+}
diff --git a/libdraw/drawrepl.c b/libdraw/drawrepl.c
new file mode 100644
index 00000000..e1488e46
--- /dev/null
+++ b/libdraw/drawrepl.c
@@ -0,0 +1,22 @@
+#include "lib9.h"
+#include "draw.h"
+
+int
+drawreplxy(int min, int max, int x)
+{
+ int sx;
+
+ sx = (x-min)%(max-min);
+ if(sx < 0)
+ sx += max-min;
+ return sx+min;
+}
+
+Point
+drawrepl(Rectangle r, Point p)
+{
+ p.x = drawreplxy(r.min.x, r.max.x, p.x);
+ p.y = drawreplxy(r.min.y, r.max.y, p.y);
+ return p;
+}
+
diff --git a/libdraw/ellipse.c b/libdraw/ellipse.c
new file mode 100644
index 00000000..51a993ab
--- /dev/null
+++ b/libdraw/ellipse.c
@@ -0,0 +1,81 @@
+#include "lib9.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){
+ _drawprint(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/libdraw/font.c b/libdraw/font.c
new file mode 100644
index 00000000..f51804d2
--- /dev/null
+++ b/libdraw/font.c
@@ -0,0 +1,398 @@
+#include "lib9.h"
+#include "draw.h"
+
+static int fontresize(Font*, int, int, int);
+static int freeup(Font*);
+
+#define PJW 0 /* use NUL==pjw for invisible characters */
+
+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 = (Rune*) L"";
+ }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) */ /* plan 9 uses this */
+ if(s->f)
+ 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)
+{
+ char *name;
+ Subfont *sf;
+ int depth;
+
+ name = cf->subfontname;
+ if(name == nil){
+ depth = 0;
+ if(f->display){
+ if(f->display->image)
+ depth = f->display->image->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 */
+ if (f->cacheimage == 0)
+ return 0;
+ 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 */
+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;
+}
+
+/* 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){
+ _drawprint(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){
+ _drawprint(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/libdraw/freesubfont.c b/libdraw/freesubfont.c
new file mode 100644
index 00000000..1063bb00
--- /dev/null
+++ b/libdraw/freesubfont.c
@@ -0,0 +1,16 @@
+#include "lib9.h"
+#include "draw.h"
+
+void
+freesubfont(Subfont *f)
+{
+ if(f == 0)
+ return;
+ f->ref--;
+ if(f->ref > 0)
+ return;
+ uninstallsubfont(f);
+ free(f->info); /* note: f->info must have been malloc'ed! */
+ freeimage(f->bits);
+ free(f);
+}
diff --git a/libdraw/getdefont.c b/libdraw/getdefont.c
new file mode 100644
index 00000000..8a9e6355
--- /dev/null
+++ b/libdraw/getdefont.c
@@ -0,0 +1,59 @@
+#include "lib9.h"
+#include "draw.h"
+
+Subfont*
+getdefont(Display *d)
+{
+ char *hdr, *p;
+ int n;
+ Fontchar *fc;
+ Subfont *f;
+ int ld;
+ Rectangle r;
+ Image *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 ulong*.
+ */
+ p = (char*)defontdata;
+ n = (ulong)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);
+
+ i = allocimage(d, r, drawld2chan[ld], 0, 0);
+ if(i == 0)
+ return 0;
+
+ p += 5*12;
+ n = loadimage(i, r, (uchar*)p, (defontdata+sizeofdefont)-(uchar*)p);
+ if(n < 0){
+ freeimage(i);
+ return 0;
+ }
+
+ hdr = p+n;
+ n = atoi(hdr);
+ p = hdr+3*12;
+ fc = malloc(sizeof(Fontchar)*(n+1));
+ if(fc == 0){
+ freeimage(i);
+ return 0;
+ }
+ _unpackinfo(fc, (uchar*)p, n);
+ f = allocsubfont("*default*", n, atoi(hdr+12), atoi(hdr+24), fc, i);
+ if(f == 0){
+ freeimage(i);
+ free(fc);
+ return 0;
+ }
+ return f;
+}
diff --git a/libdraw/getsubfont.c b/libdraw/getsubfont.c
new file mode 100644
index 00000000..0208d634
--- /dev/null
+++ b/libdraw/getsubfont.c
@@ -0,0 +1,36 @@
+#include "lib9.h"
+#include "kernel.h"
+#include "draw.h"
+
+/*
+ * Default version: treat as file name
+ */
+
+Subfont*
+_getsubfont(Display *d, char *name)
+{
+ int fd;
+ Subfont *f;
+
+ fd = libopen(name, OREAD);
+
+ if(fd < 0){
+ _drawprint(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->local == 0)
+ unlockdisplay(d);
+ f = readsubfont(d, name, fd, d->local == 0);
+ if(d->local == 0)
+ lockdisplay(d);
+ if(f == 0)
+ _drawprint(2, "getsubfont: can't read %s: %r\n", name);
+ libclose(fd);
+ return f;
+}
diff --git a/libdraw/init.c b/libdraw/init.c
new file mode 100644
index 00000000..f7a8676c
--- /dev/null
+++ b/libdraw/init.c
@@ -0,0 +1,341 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+#include "interp.h"
+
+int _drawdebug;
+
+enum {
+ CHECKLOCKING = 0
+};
+
+/*
+ * Attach, or possibly reattach, to window.
+ * If reattaching, maintain value of screen pointer.
+ */
+int
+gengetwindow(Display *d, char *winname, Image **winp, Screen **scrp, int ref)
+{
+ int n, fd;
+ char buf[64+1];
+ Image *image;
+
+ fd = libopen(winname, OREAD);
+ if(fd<0 || (n=libread(fd, buf, sizeof buf-1))<=0){
+ *winp = d->image;
+ assert(*winp && (*winp)->chan != 0);
+ return 1;
+ }
+ libclose(fd);
+ buf[n] = '\0';
+ if(*winp != nil){
+ _freeimage1(*winp);
+ freeimage((*scrp)->image);
+ freescreen(*scrp);
+ *scrp = nil;
+ }
+ image = namedimage(d, buf);
+ if(image == 0){
+ *winp = nil;
+ return -1;
+ }
+ assert(image->chan != 0);
+
+ *scrp = allocscreen(image, d->white, 0);
+ if(*scrp == nil){
+ *winp = nil;
+ return -1;
+ }
+
+ *winp = _allocwindow(*winp, *scrp, insetrect(image->r, Borderwidth), ref, DWhite);
+ if(*winp == nil)
+ return -1;
+ assert((*winp)->chan != 0);
+ return 1;
+}
+
+#define NINFO 12*12
+
+Display*
+initdisplay(char *dev, char *win, void(*error)(Display*, char*))
+{
+ char buf[128], info[NINFO+1], *t;
+ int datafd, ctlfd, reffd;
+ Display *disp;
+ Image *image;
+ Dir *dir;
+ void *q;
+ ulong chan;
+
+ fmtinstall('P', Pfmt);
+ fmtinstall('R', Rfmt);
+ if(dev == 0)
+ dev = "/dev";
+ if(win == 0)
+ win = "/dev";
+ if(strlen(dev)>sizeof buf-25 || strlen(win)>sizeof buf-25){
+ kwerrstr("initdisplay: directory name too long");
+ return nil;
+ }
+ t = strdup(win);
+ if(t == nil)
+ return nil;
+
+ q = libqlalloc();
+ if(q == nil)
+ return nil;
+
+ sprint(buf, "%s/draw/new", dev);
+ ctlfd = libopen(buf, ORDWR);
+ if(ctlfd < 0){
+ if(libbind("#i", dev, MBEFORE) < 0){
+ Error1:
+ libqlfree(q);
+ free(t);
+ kwerrstr("initdisplay: %s: %r", buf);
+ return 0;
+ }
+ ctlfd = libopen(buf, ORDWR);
+ }
+ if(ctlfd < 0)
+ goto Error1;
+ if(libread(ctlfd, info, sizeof info) < NINFO){
+ Error2:
+ libclose(ctlfd);
+ goto Error1;
+ }
+
+ if((chan=strtochan(info+2*12)) == 0){
+ kwerrstr("bad channel in %s", buf);
+ goto Error2;
+ }
+
+ sprint(buf, "%s/draw/%d/data", dev, atoi(info+0*12));
+ datafd = libopen(buf, ORDWR);
+ if(datafd < 0)
+ goto Error2;
+ sprint(buf, "%s/draw/%d/refresh", dev, atoi(info+0*12));
+ reffd = libopen(buf, OREAD);
+ if(reffd < 0){
+ Error3:
+ libclose(datafd);
+ goto Error2;
+ }
+ strcpy(buf, "allocation failed");
+ disp = malloc(sizeof(Display));
+ if(disp == 0){
+ Error4:
+ libclose(reffd);
+ goto Error3;
+ }
+ image = malloc(sizeof(Image));
+ if(image == 0){
+ Error5:
+ free(disp);
+ goto Error4;
+ }
+ memset(image, 0, sizeof(Image));
+ memset(disp, 0, sizeof(Display));
+ image->display = disp;
+ image->id = 0;
+ image->chan = chan;
+ image->depth = chantodepth(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);
+ disp->dirno = atoi(info+0*12);
+ disp->datachan = libfdtochan(datafd, ORDWR);
+ disp->refchan = libfdtochan(reffd, OREAD);
+ disp->ctlchan = libfdtochan(ctlfd, ORDWR);
+ if(disp->datachan == nil || disp->refchan == nil || disp->ctlchan == nil)
+ goto Error4;
+ disp->bufsize = Displaybufsize; /* TO DO: iounit(datafd) */
+ if(disp->bufsize <= 0)
+ disp->bufsize = Displaybufsize;
+ if(disp->bufsize < 512){
+ kwerrstr("iounit %d too small", disp->bufsize);
+ goto Error5;
+ }
+ /* TO DO: allocate buffer */
+
+ libclose(datafd);
+ libclose(reffd);
+ disp->image = image;
+ disp->bufp = disp->buf;
+ disp->error = error;
+ disp->chan = image->chan;
+ disp->depth = image->depth;
+ disp->windir = t;
+ disp->devdir = strdup(dev);
+ disp->qlock = q;
+ libqlock(q);
+ disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite);
+ disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack);
+ disp->opaque = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite);
+ disp->transparent = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack);
+ if(disp->white == nil || disp->black == nil || disp->opaque == nil || disp->transparent == nil){
+ free(image);
+ free(disp->devdir);
+ free(disp->white);
+ free(disp->black);
+ libclose(ctlfd);
+ goto Error5;
+ }
+ if((dir = libdirfstat(ctlfd))!=nil && dir->type=='i'){
+ disp->local = 1;
+ disp->dataqid = dir->qid.path;
+ }
+ free(dir);
+ libclose(ctlfd);
+
+ if(CHECKLOCKING)
+ disp->local = 0; /* force display locking even for local access */
+
+ assert(disp->chan != 0 && image->chan != 0);
+ return disp;
+}
+
+/*
+ * 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;
+ libqlock(disp->qlock);
+ if(disp->oldlabel[0]){
+ snprint(buf, sizeof buf, "%s/label", disp->windir);
+ fd = libopen(buf, OWRITE);
+ if(fd >= 0){
+ libwrite(fd, disp->oldlabel, strlen(disp->oldlabel));
+ libclose(fd);
+ }
+ }
+
+ free(disp->devdir);
+ free(disp->windir);
+ freeimage(disp->white);
+ freeimage(disp->black);
+ freeimage(disp->opaque);
+ freeimage(disp->transparent);
+ free(disp->image);
+ libchanclose(disp->datachan);
+ libchanclose(disp->refchan);
+ libchanclose(disp->ctlchan);
+ /* should cause refresh slave to shut down */
+ libqunlock(disp->qlock);
+ libqlfree(disp->qlock);
+ free(disp);
+}
+
+int
+lockdisplay(Display *disp)
+{
+ if(disp->local)
+ return 0;
+ if(libqlowner(disp->qlock) != currun()){
+ libqlock(disp->qlock);
+ return 1;
+ }
+ return 0;
+}
+
+void
+unlockdisplay(Display *disp)
+{
+ if(disp->local)
+ return;
+ libqunlock(disp->qlock);
+}
+
+/* use static buffer to avoid stack bloat */
+int
+_drawprint(int fd, char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[128];
+// static QLock l;
+
+// qlock(&l);
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+ n = libwrite(fd, buf, strlen(buf));
+// qunlock(&l);
+ return n;
+}
+
+#ifdef YYY
+void
+drawerror(Display *d, char *s)
+{
+ char err[ERRMAX];
+
+ if(d->error)
+ d->error(d, s);
+ else{
+ err[0] = 0;
+ errstr(err, sizeof err);
+ _drawprint(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(kchanio(d->datachan, d->buf, n, OWRITE) != n){
+ if(_drawdebug)
+ _drawprint(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'; /* one byte always reserved for this */
+ return doflush(d);
+}
+
+uchar*
+bufimage(Display *d, int n)
+{
+ uchar *p;
+
+ if(n<0 || n>Displaybufsize){
+ kwerrstr("bad count in bufimage");
+ return 0;
+ }
+ if(d->bufp+n > d->buf+Displaybufsize)
+ if(doflush(d) < 0)
+ return 0;
+ p = d->bufp;
+ d->bufp += n;
+ return p;
+}
+
+#endif
diff --git a/libdraw/line.c b/libdraw/line.c
new file mode 100644
index 00000000..32a1213d
--- /dev/null
+++ b/libdraw/line.c
@@ -0,0 +1,34 @@
+#include "lib9.h"
+#include "draw.h"
+
+void
+line(Image *dst, Point p0, Point p1, int end0, int end1, int radius, Image *src, Point sp)
+{
+ lineop(dst, p0, p1, end0, end1, radius, src, sp, SoverD);
+}
+
+void
+lineop(Image *dst, Point p0, Point p1, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ uchar *a;
+
+ _setdrawop(dst->display, op);
+
+ a = bufimage(dst->display, 1+4+2*4+2*4+4+4+4+4+2*4);
+ if(a == 0){
+ _drawprint(2, "image line: %r\n");
+ return;
+ }
+ a[0] = 'L';
+ BPLONG(a+1, dst->id);
+ BPLONG(a+5, p0.x);
+ BPLONG(a+9, p0.y);
+ BPLONG(a+13, p1.x);
+ BPLONG(a+17, p1.y);
+ BPLONG(a+21, end0);
+ BPLONG(a+25, end1);
+ BPLONG(a+29, radius);
+ BPLONG(a+33, src->id);
+ BPLONG(a+37, sp.x);
+ BPLONG(a+41, sp.y);
+}
diff --git a/libdraw/loadimage.c b/libdraw/loadimage.c
new file mode 100644
index 00000000..6c00a250
--- /dev/null
+++ b/libdraw/loadimage.c
@@ -0,0 +1,70 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+int
+loadimage(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ long dy;
+ int n, bpl, roff, dstroff, lskip, llen, y;
+ uchar *a;
+ int chunk;
+ Rectangle dstr;
+
+ chunk = i->display->bufsize - 64;
+
+ bpl = bytesperline(r, i->depth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ kwerrstr("loadimage: insufficient data");
+ return -1;
+ }
+
+ dstr = r;
+ rectclip(&dstr, i->r);
+ rectclip(&dstr, i->clipr);
+
+ if (!rectinrect(dstr, i->r))
+ return 0;
+
+ roff = (r.min.x*i->depth)>>3;
+ dstroff = dstr.min.x * i->depth >> 3;
+ lskip = dstroff - roff;
+ llen = (dstr.max.x*i->depth + 7 >> 3) - dstroff;
+ data += (dstr.min.y - r.min.y) * bpl + lskip;
+
+ ndata = 0;
+ while(dstr.max.y > dstr.min.y){
+ dy = dstr.max.y - dstr.min.y;
+ if(dy*llen > chunk)
+ dy = chunk/llen;
+ if(dy <= 0){
+ kwerrstr("loadimage: image too wide for buffer");
+ return -1;
+ }
+ n = dy*llen;
+ a = bufimage(i->display, 21+n);
+ if(a == nil){
+ kwerrstr("bufimage failed");
+ return -1;
+ }
+ a[0] = 'y';
+ BPLONG(a+1, i->id);
+ BPLONG(a+5, dstr.min.x);
+ BPLONG(a+9, dstr.min.y);
+ BPLONG(a+13, dstr.max.x);
+ BPLONG(a+17, dstr.min.y+dy);
+ a += 21;
+ for (y = 0; y < dy; y++) {
+ memmove(a, data, llen);
+ a += llen;
+ ndata += llen;
+ data += bpl;
+ }
+ dstr.min.y += dy;
+ }
+ if(flushimage(i->display, 0) < 0)
+ return -1;
+ return ndata;
+}
+
diff --git a/libdraw/mkfile b/libdraw/mkfile
new file mode 100644
index 00000000..27cba37e
--- /dev/null
+++ b/libdraw/mkfile
@@ -0,0 +1,54 @@
+<../mkconfig
+
+LIB=libdraw.a
+
+OFILES= alloc.$O\
+ allocimagemix.$O\
+ arith.$O\
+ bezier.$O\
+ border.$O\
+ buildfont.$O\
+ bytesperline.$O\
+ chan.$O\
+ cloadimage.$O\
+ computil.$O\
+ creadimage.$O\
+ defont.$O\
+ draw.$O\
+ drawrepl.$O\
+ ellipse.$O\
+ font.$O\
+ freesubfont.$O\
+ getdefont.$O\
+ getsubfont.$O\
+ init.$O\
+ line.$O\
+ mkfont.$O\
+ openfont.$O\
+ poly.$O\
+ loadimage.$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\
+
+HFILES=\
+ $ROOT/include/draw.h \
+ $ROOT/include/interp.h\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+
diff --git a/libdraw/mkfont.c b/libdraw/mkfont.c
new file mode 100644
index 00000000..cb982f46
--- /dev/null
+++ b/libdraw/mkfont.c
@@ -0,0 +1,54 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * Cobble fake font using existing subfont
+ */
+Font*
+mkfont(Subfont *subfont, Rune min)
+{
+ Font *font;
+ Cachefont *c;
+
+ font = malloc(sizeof(Font));
+ if(font == 0)
+ return 0;
+ memset(font, 0, sizeof(Font));
+ font->display = subfont->bits->display;
+ font->name = strdup("<synthetic>");
+ font->ncache = NFCACHE+NFLOOK;
+ font->nsubf = NFSUBF;
+ font->cache = malloc(font->ncache * sizeof(font->cache[0]));
+ font->subf = malloc(font->nsubf * sizeof(font->subf[0]));
+ if(font->name==0 || font->cache==0 || font->subf==0){
+ Err:
+ free(font->name);
+ free(font->cache);
+ free(font->subf);
+ free(font->sub);
+ free(font);
+ return 0;
+ }
+ memset(font->cache, 0, font->ncache*sizeof(font->cache[0]));
+ memset(font->subf, 0, font->nsubf*sizeof(font->subf[0]));
+ font->height = subfont->height;
+ font->ascent = subfont->ascent;
+ font->age = 1;
+ font->sub = malloc(sizeof(Cachefont*));
+ if(font->sub == 0)
+ goto Err;
+ c = malloc(sizeof(Cachefont));
+ if(c == 0)
+ goto Err;
+ font->nsub = 1;
+ font->sub[0] = c;
+ c->min = min;
+ c->max = min+subfont->n-1;
+ c->offset = 0;
+ c->name = 0; /* noticed by freeup() and agefont() */
+ c->subfontname = 0;
+ font->subf[0].age = 0;
+ font->subf[0].cf = c;
+ font->subf[0].f = subfont;
+ return font;
+}
diff --git a/libdraw/openfont.c b/libdraw/openfont.c
new file mode 100644
index 00000000..d2829d36
--- /dev/null
+++ b/libdraw/openfont.c
@@ -0,0 +1,37 @@
+#include "lib9.h"
+#include "kernel.h"
+#include "draw.h"
+
+Font*
+openfont(Display *d, char *name)
+{
+ Font *fnt;
+ int fd, i, n;
+ char *buf;
+ Dir *dir;
+
+ fd = libopen(name, OREAD);
+ if(fd < 0)
+ return 0;
+
+ if((dir = libdirfstat(fd)) == nil){
+ Err0:
+ libclose(fd);
+ return 0;
+ }
+ n = dir->length;
+ free(dir);
+ buf = malloc(n+1);
+ if(buf == 0)
+ goto Err0;
+ buf[n] = 0;
+ i = libreadn(fd, buf, n);
+ libclose(fd);
+ if(i != n){
+ free(buf);
+ return 0;
+ }
+ fnt = buildfont(d, buf, name);
+ free(buf);
+ return fnt;
+}
diff --git a/libdraw/poly.c b/libdraw/poly.c
new file mode 100644
index 00000000..ec643690
--- /dev/null
+++ b/libdraw/poly.c
@@ -0,0 +1,86 @@
+#include "lib9.h"
+#include "draw.h"
+
+static
+uchar*
+addcoord(uchar *p, int oldx, int newx)
+{
+ int dx;
+
+ dx = newx-oldx;
+ /* does dx fit in 7 signed bits? */
+ if((unsigned)(dx - -0x40) <= 0x7F)
+ *p++ = dx&0x7F;
+ else{
+ *p++ = 0x80 | (newx&0x7F);
+ *p++ = newx>>7;
+ *p++ = newx>>15;
+ }
+ return p;
+}
+
+static
+void
+dopoly(int cmd, Image *dst, Point *pp, int np, int end0, int end1, int radius, Image *src, Point *sp, Drawop op)
+{
+ uchar *a, *t, *u;
+ int i, ox, oy;
+
+ if(np == 0)
+ return;
+ t = malloc(np*2*3);
+ if(t == nil)
+ return;
+ u = t;
+ ox = oy = 0;
+ for(i=0; i<np; i++){
+ u = addcoord(u, ox, pp[i].x);
+ ox = pp[i].x;
+ u = addcoord(u, oy, pp[i].y);
+ oy = pp[i].y;
+ }
+
+ _setdrawop(dst->display, op);
+
+ a = bufimage(dst->display, 1+4+2+4+4+4+4+2*4+(u-t));
+ if(a == 0){
+ free(t);
+ _drawprint(2, "image poly: %r\n");
+ return;
+ }
+ a[0] = cmd;
+ BPLONG(a+1, dst->id);
+ BPSHORT(a+5, np-1);
+ BPLONG(a+7, end0);
+ BPLONG(a+11, end1);
+ BPLONG(a+15, radius);
+ BPLONG(a+19, src->id);
+ BPLONG(a+23, sp->x);
+ BPLONG(a+27, sp->y);
+ memmove(a+31, t, u-t);
+ free(t);
+}
+
+void
+poly(Image *dst, Point *p, int np, int end0, int end1, int radius, Image *src, Point sp)
+{
+ dopoly('p', dst, p, np, end0, end1, radius, src, &sp, SoverD);
+}
+
+void
+polyop(Image *dst, Point *p, int np, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ dopoly('p', dst, p, np, end0, end1, radius, src, &sp, op);
+}
+
+void
+fillpoly(Image *dst, Point *p, int np, int wind, Image *src, Point sp)
+{
+ dopoly('P', dst, p, np, wind, 0, 0, src, &sp, SoverD);
+}
+
+void
+fillpolyop(Image *dst, Point *p, int np, int wind, Image *src, Point sp, Drawop op)
+{
+ dopoly('P', dst, p, np, wind, 0, 0, src, &sp, op);
+}
diff --git a/libdraw/readcolmap.c b/libdraw/readcolmap.c
new file mode 100644
index 00000000..6bae5a24
--- /dev/null
+++ b/libdraw/readcolmap.c
@@ -0,0 +1,46 @@
+#include "lib9.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];
+
+ 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) {
+ _drawprint(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/libdraw/readimage.c b/libdraw/readimage.c
new file mode 100644
index 00000000..3d74b15e
--- /dev/null
+++ b/libdraw/readimage.c
@@ -0,0 +1,123 @@
+#include "lib9.h"
+#include "kernel.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;
+ ulong chan;
+ uchar *tmp;
+ Image *i;
+
+ if(libreadn(fd, hdr, 11) != 11) {
+ kwerrstr("readimage: short header");
+ return nil;
+ }
+ if(memcmp(hdr, "compressed\n", 11) == 0)
+ return creadimage(d, fd, dolock);
+ dolock &= 1;
+ if(libreadn(fd, hdr+11, 5*12-11) != 5*12-11) {
+ kwerrstr("readimage: short header");
+ 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] != ' '){
+ kwerrstr("readimage: bad format");
+ return nil;
+ }
+ if(new){
+ hdr[11] = '\0';
+ if((chan = strtochan(hdr)) == 0){
+ kwerrstr("readimage: bad channel string %s", hdr);
+ return nil;
+ }
+ }else{
+ ldepth = ((int)hdr[10])-'0';
+ if(ldepth<0 || ldepth>3){
+ kwerrstr("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){
+ kwerrstr("readimage: bad rectangle");
+ return nil;
+ }
+
+ miny = r.min.y;
+ maxy = r.max.y;
+
+ l = bytesperline(r, chantodepth(chan));
+ if(dolock)
+ 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){
+ kwerrstr("readimage: image too wide for buffer");
+ goto Err;
+ }
+ n = dy*l;
+ m = libreadn(fd, tmp, n);
+ if(m != n){
+ kwerrstr("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/libdraw/readsubfont.c b/libdraw/readsubfont.c
new file mode 100644
index 00000000..50933b7d
--- /dev/null
+++ b/libdraw/readsubfont.c
@@ -0,0 +1,58 @@
+#include "lib9.h"
+#include "kernel.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|2);
+ if(i == nil)
+ return nil;
+ }
+ if(libreadn(fd, hdr, 3*12) != 3*12){
+ if(ai == nil)
+ freeimage(i);
+ kwerrstr("rdsubfonfile: header read error: %r");
+ return nil;
+ }
+ n = atoi(hdr);
+ p = malloc(6*(n+1));
+ if(p == nil)
+ return nil;
+ if(libreadn(fd, p, 6*(n+1)) != 6*(n+1)){
+ kwerrstr("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/libdraw/rectclip.c b/libdraw/rectclip.c
new file mode 100644
index 00000000..740c7c4b
--- /dev/null
+++ b/libdraw/rectclip.c
@@ -0,0 +1,24 @@
+#include "lib9.h"
+#include "draw.h"
+
+int
+rectclip(Rectangle *rp, Rectangle b) /* first by reference, second by value */
+{
+ Rectangle *bp = &b;
+ /*
+ * Expand rectXrect() in line for speed
+ */
+ if((rp->min.x<bp->max.x && bp->min.x<rp->max.x &&
+ rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0)
+ return 0;
+ /* They must overlap */
+ if(rp->min.x < bp->min.x)
+ rp->min.x = bp->min.x;
+ if(rp->min.y < bp->min.y)
+ rp->min.y = bp->min.y;
+ if(rp->max.x > bp->max.x)
+ rp->max.x = bp->max.x;
+ if(rp->max.y > bp->max.y)
+ rp->max.y = bp->max.y;
+ return 1;
+}
diff --git a/libdraw/replclipr.c b/libdraw/replclipr.c
new file mode 100644
index 00000000..b3f0085f
--- /dev/null
+++ b/libdraw/replclipr.c
@@ -0,0 +1,24 @@
+#include "lib9.h"
+#include "draw.h"
+
+void
+replclipr(Image *i, int repl, Rectangle clipr)
+{
+ uchar *b;
+
+ b = bufimage(i->display, 22);
+ if (b == 0) {
+ _drawprint(2, "replclipr: no bufimage\n");
+ return;
+ }
+ b[0] = 'c';
+ BPLONG(b+1, i->id);
+ repl = repl!=0;
+ b[5] = repl;
+ BPLONG(b+6, clipr.min.x);
+ BPLONG(b+10, clipr.min.y);
+ BPLONG(b+14, clipr.max.x);
+ BPLONG(b+18, clipr.max.y);
+ i->repl = repl;
+ i->clipr = clipr;
+}
diff --git a/libdraw/rgb.c b/libdraw/rgb.c
new file mode 100644
index 00000000..7ae0df77
--- /dev/null
+++ b/libdraw/rgb.c
@@ -0,0 +1,98 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * This original version, although fast and a true inverse of
+ * cmap2rgb, in the sense that rgb2cmap(cmap2rgb(c))
+ * returned the original color, does a terrible job for RGB
+ * triples that do not appear in the color map, so it has been
+ * replaced by the much slower version below, that loops
+ * over the color map looking for the nearest point in RGB
+ * space. There is no visual psychology reason for that
+ * criterion, but it's easy to implement and the results are
+ * far more pleasing.
+ *
+int
+rgb2cmap(int cr, int cg, int cb)
+{
+ int r, g, b, v, cv;
+
+ if(cr < 0)
+ cr = 0;
+ else if(cr > 255)
+ cr = 255;
+ if(cg < 0)
+ cg = 0;
+ else if(cg > 255)
+ cg = 255;
+ if(cb < 0)
+ cb = 0;
+ else if(cb > 255)
+ cb = 255;
+ r = cr>>6;
+ g = cg>>6;
+ b = cb>>6;
+ cv = cr;
+ if(cg > cv)
+ cv = cg;
+ if(cb > cv)
+ cv = cb;
+ v = (cv>>4)&3;
+ return ((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15));
+}
+*/
+
+int
+rgb2cmap(int cr, int cg, int cb)
+{
+ int i, r, g, b, sq;
+ ulong rgb;
+ int best, bestsq;
+
+ best = 0;
+ bestsq = 0x7FFFFFFF;
+ for(i=0; i<256; i++){
+ rgb = cmap2rgb(i);
+ r = (rgb>>16) & 0xFF;
+ g = (rgb>>8) & 0xFF;
+ b = (rgb>>0) & 0xFF;
+ sq = (r-cr)*(r-cr)+(g-cg)*(g-cg)+(b-cb)*(b-cb);
+ if(sq < bestsq){
+ bestsq = sq;
+ best = i;
+ }
+ }
+ return best;
+}
+
+int
+cmap2rgb(int c)
+{
+ int j, num, den, r, g, b, v, rgb;
+
+ r = c>>6;
+ v = (c>>4)&3;
+ j = (c-v+r)&15;
+ g = j>>2;
+ b = j&3;
+ den=r;
+ if(g>den)
+ den=g;
+ if(b>den)
+ den=b;
+ if(den==0) {
+ v *= 17;
+ rgb = (v<<16)|(v<<8)|v;
+ }
+ else{
+ num=17*(4*den+v);
+ rgb = ((r*num/den)<<16)|((g*num/den)<<8)|(b*num/den);
+ }
+ return rgb;
+}
+
+int
+cmap2rgba(int c)
+{
+ return (cmap2rgb(c)<<8)|0xFF;
+}
diff --git a/libdraw/string.c b/libdraw/string.c
new file mode 100644
index 00000000..7ef21f1a
--- /dev/null
+++ b/libdraw/string.c
@@ -0,0 +1,136 @@
+#include "lib9.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){
+ _drawprint(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/libdraw/stringbg.c b/libdraw/stringbg.c
new file mode 100644
index 00000000..1ec4b093
--- /dev/null
+++ b/libdraw/stringbg.c
@@ -0,0 +1,50 @@
+#include "lib9.h"
+#include "draw.h"
+
+Point
+stringbg(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Image *bg, Point bgp)
+{
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, bg, bgp, SoverD);
+}
+
+Point
+stringbgop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Image *bg, Point bgp, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, bg, bgp, op);
+}
+
+Point
+stringnbg(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len, Image *bg, Point bgp)
+{
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, bg, bgp, SoverD);
+}
+
+Point
+stringnbgop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len, Image *bg, Point bgp, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, bg, bgp, op);
+}
+
+Point
+runestringbg(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, Image *bg, Point bgp)
+{
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, bg, bgp, SoverD);
+}
+
+Point
+runestringbgop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, Image *bg, Point bgp, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, bg, bgp, op);
+}
+
+Point
+runestringnbg(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len, Image *bg, Point bgp)
+{
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, bg, bgp, SoverD);
+}
+
+Point
+runestringnbgop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len, Image *bg, Point bgp, Drawop op)
+{
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, bg, bgp, op);
+}
diff --git a/libdraw/stringsubfont.c b/libdraw/stringsubfont.c
new file mode 100644
index 00000000..a96ab5d1
--- /dev/null
+++ b/libdraw/stringsubfont.c
@@ -0,0 +1,64 @@
+#include "lib9.h"
+#include "draw.h"
+
+Point
+stringsubfont(Image *b, Point p, Image *color, Subfont *f, char *cs)
+{
+ int w, width;
+ uchar *s;
+ Rune c;
+ Fontchar *i;
+
+ 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;
+ draw(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, f->bits, Pt(i->x, i->top));
+ }
+ return p;
+}
+
+Point
+strsubfontwidth(Subfont *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/libdraw/stringwidth.c b/libdraw/stringwidth.c
new file mode 100644
index 00000000..5dc2167f
--- /dev/null
+++ b/libdraw/stringwidth.c
@@ -0,0 +1,96 @@
+#include "lib9.h"
+#include "draw.h"
+
+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;
+ static Rune rnull;
+
+ if(s == nil){
+ s = "";
+ sptr = nil;
+ }else
+ sptr = &s;
+ if(r == nil){
+ r = &rnull;
+ rptr = nil;
+ }else
+ rptr = &r;
+ twid = 0;
+ while(len>0 && (*s || *r)){
+ 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";
+ _drawprint(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/libdraw/subfont.c b/libdraw/subfont.c
new file mode 100644
index 00000000..7f70a4f2
--- /dev/null
+++ b/libdraw/subfont.c
@@ -0,0 +1,27 @@
+#include "lib9.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/libdraw/subfontcache.c b/libdraw/subfontcache.c
new file mode 100644
index 00000000..fb5c11f4
--- /dev/null
+++ b/libdraw/subfontcache.c
@@ -0,0 +1,38 @@
+#include "lib9.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/libdraw/subfontname.c b/libdraw/subfontname.c
new file mode 100644
index 00000000..ceaaa4a0
--- /dev/null
+++ b/libdraw/subfontname.c
@@ -0,0 +1,45 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * Default version: convert to file name
+ */
+
+char*
+subfontname(char *cfname, char *fname, int maxdepth)
+{
+ char *t, *u, tmp1[128], tmp2[128];
+ 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=3; i>=0; i--){
+ if((1<<i) > maxdepth)
+ continue;
+ /* 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/libdraw/test.c b/libdraw/test.c
new file mode 100644
index 00000000..937d6911
--- /dev/null
+++ b/libdraw/test.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+#include "draw.h"
+
+
+void
+main(int argc, char **argv)
+{
+ print("%dn", wordsperline(Rect(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4])), atoi(argv[5])));
+}
diff --git a/libdraw/unloadimage.c b/libdraw/unloadimage.c
new file mode 100644
index 00000000..9b1be8a7
--- /dev/null
+++ b/libdraw/unloadimage.c
@@ -0,0 +1,58 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+#include "interp.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)){
+ kwerrstr("unloadimage: bad rectangle");
+ return -1;
+ }
+ bpl = bytesperline(r, i->depth);
+ if(ndata < bpl*Dy(r)){
+ kwerrstr("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){
+ kwerrstr("unloadimage: %r");
+ return -1;
+ }
+ dy = 8000/bpl;
+ if(dy <= 0){
+ kwerrstr("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;
+ if(d->local == 0)
+ release();
+ n = kchanio(d->datachan, data+ntot, ndata-ntot, OREAD);
+ if(d->local == 0)
+ acquire();
+ if(n < 0)
+ return n;
+ ntot += n;
+ r.min.y += dy;
+ }
+ return ntot;
+}
diff --git a/libdraw/window.c b/libdraw/window.c
new file mode 100644
index 00000000..31b9912d
--- /dev/null
+++ b/libdraw/window.c
@@ -0,0 +1,203 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+typedef struct Memimage Memimage;
+
+static int screenid;
+
+Screen*
+allocscreen(Image *image, Image *fill, int public)
+{
+ uchar *a;
+ Screen *s;
+ int id, try;
+ Display *d;
+
+ d = image->display;
+ if(d != fill->display){
+ kwerrstr("allocscreen: image and fill on different displays");
+ return 0;
+ }
+ s = malloc(sizeof(Screen));
+ if(s == 0)
+ return 0;
+ SET(id);
+ for(try=0; try<25; try++){
+ /* loop until find a free id */
+ a = bufimage(d, 1+4+4+4+1);
+ if(a == 0){
+ free(s);
+ return 0;
+ }
+ id = ++screenid;
+ a[0] = 'A';
+ BPLONG(a+1, id);
+ BPLONG(a+5, image->id);
+ BPLONG(a+9, fill->id);
+ a[13] = public;
+ if(flushimage(d, 0) != -1)
+ break;
+ }
+ s->display = d;
+ s->id = id;
+ s->image = image;
+// assert(s->image && s->image->chan != 0);
+
+ s->fill = fill;
+ return s;
+}
+
+Screen*
+publicscreen(Display *d, int id, ulong chan)
+{
+ uchar *a;
+ Screen *s;
+
+ s = malloc(sizeof(Screen));
+ if(s == 0)
+ return 0;
+ a = bufimage(d, 1+4+4);
+ if(a == 0){
+ Error:
+ free(s);
+ return 0;
+ }
+ a[0] = 'S';
+ BPLONG(a+1, id);
+ BPLONG(a+5, chan);
+ if(flushimage(d, 0) < 0)
+ goto Error;
+
+ s->display = d;
+ s->id = id;
+ s->image = 0;
+ s->fill = 0;
+ return s;
+}
+
+int
+freescreen(Screen *s)
+{
+ uchar *a;
+ Display *d;
+
+ if(s == 0)
+ return 0;
+ d = s->display;
+ a = bufimage(d, 1+4);
+ if(a == 0)
+ return -1;
+ a[0] = 'F';
+ BPLONG(a+1, s->id);
+ /*
+ * flush(1) because screen is likely holding last reference to
+ * window, and want it to disappear visually.
+ */
+ if(flushimage(d, 1) < 0)
+ return -1;
+ free(s);
+ return 1;
+}
+
+Image*
+allocwindow(Screen *s, Rectangle r, int ref, ulong val)
+{
+ return _allocwindow(nil, s, r, ref, val);
+}
+
+Image*
+_allocwindow(Image *i, Screen *s, Rectangle r, int ref, ulong val)
+{
+ Display *d;
+
+ d = s->display;
+ i = _allocimage(i, d, r, s->image->chan, 0, val, s->id, ref);
+ if(i == 0)
+ return 0;
+ i->screen = s;
+ i->next = s->display->windows;
+ s->display->windows = i;
+ return i;
+}
+
+static
+void
+topbottom(Image **w, int n, int top)
+{
+ int i;
+ uchar *b;
+ Display *d;
+
+ if(n<0 || n>(Displaybufsize-100)/4){
+ _drawprint(2, "top/bottom: ridiculous number of windows\n");
+ return;
+ }
+ if(n==0)
+ return;
+ /* check that all images are on the same display; only it can check the screens */
+ d = w[0]->display;
+ for(i=1; i<n; i++)
+ if(w[i]->display != d){
+ _drawprint(2, "top/bottom: windows not on same display\n");
+ return;
+ }
+ b = bufimage(d, 1+1+2+4*n);
+ if (b == 0) {
+ _drawprint(2, "top/bottom: no bufimage\n");
+ return;
+ }
+ b[0] = 't';
+ b[1] = top;
+ BPSHORT(b+2, n);
+ for(i=0; i<n; i++)
+ BPLONG(b+4+4*i, w[i]->id);
+}
+
+void
+bottomwindow(Image *w)
+{
+ topbottom(&w, 1, 0);
+}
+
+void
+topwindow(Image *w)
+{
+ topbottom(&w, 1, 1);
+}
+
+void
+bottomnwindows(Image **w, int n)
+{
+ topbottom(w, n, 0);
+}
+
+void
+topnwindows(Image **w, int n)
+{
+ topbottom(w, n, 1);
+}
+
+int
+originwindow(Image *w, Point log, Point scr)
+{
+ uchar *b;
+ Point delta;
+
+ flushimage(w->display, 0);
+ b = bufimage(w->display, 1+4+2*4+2*4);
+ if(b == nil)
+ return 0;
+ b[0] = 'o';
+ BPLONG(b+1, w->id);
+ BPLONG(b+5, log.x);
+ BPLONG(b+9, log.y);
+ BPLONG(b+13, scr.x);
+ BPLONG(b+17, scr.y);
+ if(flushimage(w->display, 1) < 0)
+ return -1;
+ delta = subpt(log, w->r.min);
+ w->r = rectaddpt(w->r, delta);
+ w->clipr = rectaddpt(w->clipr, delta);
+ return 1;
+}
diff --git a/libdraw/writecolmap.c b/libdraw/writecolmap.c
new file mode 100644
index 00000000..6bbc7feb
--- /dev/null
+++ b/libdraw/writecolmap.c
@@ -0,0 +1,37 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.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);
+ if(t == nil)
+ return;
+ 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 = libwrite(fd, t, n);
+ free(t);
+ close(fd);
+ if(i != n)
+ drawerror(d, "wrcolmap: bad write");
+}
diff --git a/libdraw/writeimage.c b/libdraw/writeimage.c
new file mode 100644
index 00000000..cd5515d5
--- /dev/null
+++ b/libdraw/writeimage.c
@@ -0,0 +1,185 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+#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
+writeimage(int fd, Image *i, int dolock)
+{
+ 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 */
+ ulong n; /* length of input buffer */
+ ulong 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 chunk, ncblock;
+ Rectangle r;
+ uchar *p, *q, *s, *es, *t;
+ char hdr[11+5*12+1];
+ char cbuf[20];
+
+ chunk = i->display->bufsize - 32; /* a little room for header */
+ 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;
+ if(dolock)
+ lockdisplay(i->display);
+ nb = unloadimage(i, Rect(r.min.x, miny, r.max.x, miny+dy),
+ data+(miny-r.min.y)*bpl, dy*bpl);
+ if(dolock)
+ unlockdisplay(i->display);
+ 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(libwrite(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);
+ libwrite(fd, hdr, 2*12);
+ libwrite(fd, outbuf, n);
+ r.min.y = r.max.y;
+ }
+ free(data);
+ free(outbuf);
+ free(hash);
+ free(chain);
+ return 0;
+}
diff --git a/libdraw/writesubfont.c b/libdraw/writesubfont.c
new file mode 100644
index 00000000..af7f0dc5
--- /dev/null
+++ b/libdraw/writesubfont.c
@@ -0,0 +1,45 @@
+#include "lib9.h"
+#include "draw.h"
+#include "kernel.h"
+
+static
+void
+packinfo(Fontchar *fc, uchar *p, int n)
+{
+ int j;
+
+ for(j=0; j<=n; j++){
+ p[0] = fc->x;
+ p[1] = fc->x>>8;
+ p[2] = fc->top;
+ p[3] = fc->bottom;
+ p[4] = fc->left;
+ p[5] = fc->width;
+ fc++;
+ p += 6;
+ }
+}
+
+int
+writesubfont(int fd, Subfont *f)
+{
+ char hdr[3*12+1];
+ uchar *data;
+ int nb;
+
+ sprint(hdr, "%11d %11d %11d ", f->n, f->height, f->ascent);
+ if(libwrite(fd, hdr, 3*12) != 3*12){
+ Err:
+ kwerrstr("writesubfont: bad write: %r");
+ return -1;
+ }
+ nb = 6*(f->n+1);
+ data = malloc(nb);
+ if(data == nil)
+ return -1;
+ packinfo(f->info, data, f->n);
+ if(libwrite(fd, data, nb) != nb)
+ goto Err;
+ free(data);
+ return 0;
+}