aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-21 22:19:33 +0000
committerrsc <devnull@localhost>2004-04-21 22:19:33 +0000
commit28994509cc11ac6a5443054dfae1fedfb69039bc (patch)
tree9d5adcd11af2708db0ecc246e008c308ca0f97d4 /src
parenta01e58366c54804f15f84d6e21d13f2e4080977a (diff)
downloadplan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.gz
plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.bz2
plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.zip
Why not?
Diffstat (limited to 'src')
-rw-r--r--src/cmd/jpg/bmp.c210
-rw-r--r--src/cmd/jpg/bmp.h37
-rw-r--r--src/cmd/jpg/close.c121
-rw-r--r--src/cmd/jpg/gif.c421
-rw-r--r--src/cmd/jpg/ico.c506
-rw-r--r--src/cmd/jpg/imagefile.h82
-rw-r--r--src/cmd/jpg/jpegdump.c346
-rw-r--r--src/cmd/jpg/jpg.c342
-rw-r--r--src/cmd/jpg/mkfile52
-rw-r--r--src/cmd/jpg/multichan.c60
-rw-r--r--src/cmd/jpg/onechan.c229
-rw-r--r--src/cmd/jpg/png.c224
-rw-r--r--src/cmd/jpg/ppm.c209
-rw-r--r--src/cmd/jpg/readbmp.c626
-rw-r--r--src/cmd/jpg/readgif.c535
-rw-r--r--src/cmd/jpg/readjpg.c1661
-rw-r--r--src/cmd/jpg/readpng.c334
-rw-r--r--src/cmd/jpg/readppm.c238
-rw-r--r--src/cmd/jpg/readyuv.c190
-rw-r--r--src/cmd/jpg/rgbrgbv.c69
-rw-r--r--src/cmd/jpg/rgbycc.c120
-rw-r--r--src/cmd/jpg/togif.c147
-rw-r--r--src/cmd/jpg/toico.c322
-rw-r--r--src/cmd/jpg/topng.c70
-rw-r--r--src/cmd/jpg/toppm.c90
-rw-r--r--src/cmd/jpg/torgbv.c299
-rw-r--r--src/cmd/jpg/totruecolor.c163
-rw-r--r--src/cmd/jpg/writegif.c568
-rw-r--r--src/cmd/jpg/writepng.c220
-rw-r--r--src/cmd/jpg/writeppm.c164
-rw-r--r--src/cmd/jpg/writerawimage.c206
-rw-r--r--src/cmd/jpg/yuv.c211
-rw-r--r--src/cmd/map/index.c88
-rw-r--r--src/cmd/map/iplot.h51
-rw-r--r--src/cmd/map/libmap/aitoff.c26
-rw-r--r--src/cmd/map/libmap/albers.c117
-rw-r--r--src/cmd/map/libmap/azequalarea.c19
-rw-r--r--src/cmd/map/libmap/azequidist.c19
-rw-r--r--src/cmd/map/libmap/bicentric.c25
-rw-r--r--src/cmd/map/libmap/bonne.c36
-rw-r--r--src/cmd/map/libmap/ccubrt.c13
-rw-r--r--src/cmd/map/libmap/complex.c85
-rw-r--r--src/cmd/map/libmap/conic.c27
-rw-r--r--src/cmd/map/libmap/cubrt.c30
-rw-r--r--src/cmd/map/libmap/cuts.c39
-rw-r--r--src/cmd/map/libmap/cylequalarea.c24
-rw-r--r--src/cmd/map/libmap/cylindrical.c19
-rw-r--r--src/cmd/map/libmap/elco2.c132
-rw-r--r--src/cmd/map/libmap/elliptic.c35
-rw-r--r--src/cmd/map/libmap/fisheye.c26
-rw-r--r--src/cmd/map/libmap/gall.c29
-rw-r--r--src/cmd/map/libmap/gilbert.c51
-rw-r--r--src/cmd/map/libmap/guyou.c101
-rw-r--r--src/cmd/map/libmap/harrison.c40
-rw-r--r--src/cmd/map/libmap/hex.c122
-rw-r--r--src/cmd/map/libmap/homing.c121
-rw-r--r--src/cmd/map/libmap/lagrange.c30
-rw-r--r--src/cmd/map/libmap/lambert.c46
-rw-r--r--src/cmd/map/libmap/laue.c24
-rw-r--r--src/cmd/map/libmap/lune.c62
-rw-r--r--src/cmd/map/libmap/mercator.c36
-rw-r--r--src/cmd/map/libmap/mkfile50
-rw-r--r--src/cmd/map/libmap/mollweide.c25
-rw-r--r--src/cmd/map/libmap/newyorker.c28
-rw-r--r--src/cmd/map/libmap/orthographic.c35
-rw-r--r--src/cmd/map/libmap/perspective.c84
-rw-r--r--src/cmd/map/libmap/polyconic.c28
-rw-r--r--src/cmd/map/libmap/rectangular.c22
-rw-r--r--src/cmd/map/libmap/simpleconic.c34
-rw-r--r--src/cmd/map/libmap/sinusoidal.c17
-rw-r--r--src/cmd/map/libmap/tetra.c206
-rw-r--r--src/cmd/map/libmap/trapezoidal.c30
-rw-r--r--src/cmd/map/libmap/twocirc.c80
-rw-r--r--src/cmd/map/libmap/zcoord.c143
-rw-r--r--src/cmd/map/map.c1226
-rw-r--r--src/cmd/map/map.h147
-rwxr-xr-xsrc/cmd/map/map.rc103
-rwxr-xr-xsrc/cmd/map/mapdemo.rc83
-rw-r--r--src/cmd/map/mkfile32
-rw-r--r--src/cmd/map/route.c131
-rw-r--r--src/cmd/map/sqrt.c52
-rw-r--r--src/cmd/map/symbol.c192
82 files changed, 13293 insertions, 0 deletions
diff --git a/src/cmd/jpg/bmp.c b/src/cmd/jpg/bmp.c
new file mode 100644
index 00000000..f7e07a01
--- /dev/null
+++ b/src/cmd/jpg/bmp.c
@@ -0,0 +1,210 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int output = 0;
+ulong outchan = CMAP8;
+int defaultcolor = 1;
+Image *image;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*);
+
+Rawimage** readbmp(int fd, int colorspace);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "bmp: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i;
+ char *err;
+
+ ARGBEGIN{
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: bmp -39cdektv [file.bmp ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>");
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "bmp: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i]);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "bmp: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+int
+init(void)
+{
+ static int inited;
+
+ if(inited == 0){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "bmp: initdraw failed: %r");
+ return -1;
+ }
+ einit(Ekeyboard|Emouse);
+ inited++;
+ }
+ return 1;
+}
+
+char*
+show(int fd, char *name)
+{
+ Rawimage **array, *r, *c;
+ Image *i;
+ int j, ch;
+ char buf[32];
+
+ array = readbmp(fd, CRGB);
+ if(array == nil || array[0]==nil){
+ fprint(2, "bmp: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ if(!dflag){
+ if(init() < 0)
+ return "initdraw";
+ if(defaultcolor && screen->depth>8)
+ outchan = RGB24;
+ }
+ r = array[0];
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
+ c = totruecolor(r, CY);
+ else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "bmp: converting %s to local format failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(r->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "bmp: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "bmp: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "bmp: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "bmp: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ return nil;
+}
diff --git a/src/cmd/jpg/bmp.h b/src/cmd/jpg/bmp.h
new file mode 100644
index 00000000..ca003684
--- /dev/null
+++ b/src/cmd/jpg/bmp.h
@@ -0,0 +1,37 @@
+
+#define BMP_RGB 0
+#define BMP_RLE8 1
+#define BMP_RLE4 2
+#define BMP_BITFIELDS 3
+
+typedef struct {
+ uchar red;
+ uchar green;
+ uchar blue;
+ uchar alpha;
+} Rgb;
+
+typedef struct {
+ short type;
+ long size;
+ short reserved1;
+ short reserved2;
+ long offbits;
+} Filehdr;
+
+typedef struct {
+ long size; /* Size of the Bitmap-file */
+ long lReserved; /* Reserved */
+ long dataoff; /* Picture data location */
+ long hsize; /* Header-Size */
+ long width; /* Picture width (pixels) */
+ long height; /* Picture height (pixels) */
+ short planes; /* Planes (must be 1) */
+ short bpp; /* Bits per pixel (1, 4, 8 or 24) */
+ long compression; /* Compression mode */
+ long imagesize; /* Image size (bytes) */
+ long hres; /* Horizontal Resolution (pels/meter) */
+ long vres; /* Vertical Resolution (pels/meter) */
+ long colours; /* Used Colours (Col-Table index) */
+ long impcolours; /* Important colours (Col-Table index) */
+} Infohdr;
diff --git a/src/cmd/jpg/close.c b/src/cmd/jpg/close.c
new file mode 100644
index 00000000..7a260c10
--- /dev/null
+++ b/src/cmd/jpg/close.c
@@ -0,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+float c1 = 1.402;
+float c2 = 0.34414;
+float c3 = 0.71414;
+float c4 = 1.772;
+
+
+int
+closest(int Y, int Cb, int Cr)
+{
+ double r, g, b;
+ double diff, min;
+ int rgb, R, G, B, v, i;
+ int y1, cb1, cr1;
+
+ Cb -= 128;
+ Cr -= 128;
+ r = Y+c1*Cr;
+ g = Y-c2*Cb-c3*Cr;
+ b = Y+c4*Cb;
+
+//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b);
+
+ min = 1000000.;
+ v = 1000;
+ for(i=0; i<256; i++){
+ rgb = cmap2rgb(i);
+ R = (rgb >> 16) & 0xFF;
+ G = (rgb >> 8) & 0xFF;
+ B = (rgb >> 0) & 0xFF;
+ diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b);
+// y1 = 0.5870*G + 0.114*B + 0.299*R;
+// cb1 = (B-y1)/1.772;
+// cr1 = (R-y1)/1.402;
+ if(diff < min){
+// if(Y==0 && y1!=0)
+// continue;
+// if(Y==256-16 && y1<256-16)
+// continue;
+// if(Cb==0 && cb1!=0)
+// continue;
+// if(Cb==256-16 && cb1<256-16)
+// continue;
+// if(Cr==0 && cr1!=0)
+// continue;
+// if(Cr==256-16 && cr1<256-16)
+// continue;
+//print("%d %d %d\n", R, G, B);
+ min = diff;
+ v = i;
+ }
+ }
+ if(v > 255)
+ abort();
+ return v;
+}
+
+#define SHIFT 5
+#define INC (1<<SHIFT)
+
+typedef struct Color Color;
+
+struct Color
+{
+ int col;
+ Color *next;
+};
+
+Color *col[INC*INC*INC];
+
+void
+add(int c, int y, int cb, int cr)
+{
+ Color *cp;
+
+ y >>= 8-SHIFT;
+ cb >>= 8-SHIFT;
+ cr >>= 8-SHIFT;
+ cp = col[cr+INC*(cb+INC*y)];
+ while(cp != nil){
+ if(cp->col == c)
+ return;
+ cp = cp->next;
+ }
+ cp = malloc(sizeof(Color));
+ cp->col = c;
+ cp->next = col[cr+INC*(cb+INC*y)];
+ col[cr+INC*(cb+INC*y)] = cp;
+}
+
+void
+main(void)
+{
+ int y, cb, cr, n;
+ Color *cp;
+
+ for(y=0; y<256; y++){
+ for(cb=0; cb<256; cb++)
+ for(cr=0;cr<256;cr++)
+ add(closest(y, cb, cr), y, cb, cr);
+ fprint(2, "%d done\n", y);
+ }
+ for(y=0; y<INC*INC*INC; y++){
+ n = 0;
+ cp = col[y];
+ while(cp != nil){
+ n++;
+ cp = cp->next;
+ }
+ cp = col[y];
+ while(cp != nil){
+ n++;
+ print("%d ", cp->col);
+ cp = cp->next;
+ }
+ print("\n");
+ }
+}
diff --git a/src/cmd/jpg/gif.c b/src/cmd/jpg/gif.c
new file mode 100644
index 00000000..f9927017
--- /dev/null
+++ b/src/cmd/jpg/gif.c
@@ -0,0 +1,421 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int output = 0;
+ulong outchan = CMAP8;
+Image **allims;
+Image **allmasks;
+int which;
+int defaultcolor = 1;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*);
+
+Rectangle
+imager(void)
+{
+ Rectangle r;
+
+ if(allims==nil || allims[0]==nil)
+ return screen->r;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(allims[0]->r);
+ r.max.y = r.min.y+Dy(allims[0]->r);
+ return r;
+}
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "gif: can't reattach to window\n");
+ exits("resize");
+ }
+ if(allims==nil || allims[which]==nil)
+ return;
+ r = imager();
+ border(screen, r, -Border, nil, ZP);
+ r.min.x += allims[which]->r.min.x - allims[0]->r.min.x;
+ r.min.y += allims[which]->r.min.y - allims[0]->r.min.y;
+ drawop(screen, r, allims[which], allmasks[which], allims[which]->r.min, S);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i;
+ char *err;
+
+ ARGBEGIN{
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: gif -39cdektv [file.gif ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>");
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "gif: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i]);
+ close(fd);
+ }
+ if(output && argc>1 && err==nil){
+ fprint(2, "gif: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+Image*
+transparency(Rawimage *r, char *name)
+{
+ Image *i;
+ int j, index;
+ uchar *pic, *mpic, *mask;
+
+ if((r->gifflags&TRANSP) == 0)
+ return nil;
+ i = allocimage(display, r->r, GREY8, 0, 0);
+ if(i == nil){
+ fprint(2, "gif: allocimage for mask of %s failed: %r\n", name);
+ return nil;
+ }
+ pic = r->chans[0];
+ mask = malloc(r->chanlen);
+ if(mask == nil){
+ fprint(2, "gif: malloc for mask of %s failed: %r\n", name);
+ freeimage(i);
+ return nil;
+ }
+ index = r->giftrindex;
+ mpic = mask;
+ for(j=0; j<r->chanlen; j++)
+ if(*pic++ == index)
+ *mpic++ = 0;
+ else
+ *mpic++ = 0xFF;
+ if(loadimage(i, i->r, mask, r->chanlen) < 0){
+ fprint(2, "gif: loadimage for mask of %s failed: %r\n", name);
+ free(mask);
+ freeimage(i);
+ return nil;
+ }
+ free(mask);
+ return i;
+}
+
+/* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */
+uchar*
+expand(uchar *u, int chanlen, int nchan)
+{
+ int j, k;
+ uchar *v, *up, *vp;
+
+ v = malloc(chanlen*(nchan+1));
+ if(v == nil){
+ fprint(2, "gif: malloc fails: %r\n");
+ exits("malloc");
+ }
+ up = u;
+ vp = v;
+ for(j=0; j<chanlen; j++){
+ *vp++ = 0xFF;
+ for(k=0; k<nchan; k++)
+ *vp++ = *up++;
+ }
+ return v;
+}
+
+void
+addalpha(Rawimage *i)
+{
+ char buf[32];
+
+ switch(outchan){
+ case CMAP8:
+ i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
+ i->chanlen = 2*(i->chanlen/1);
+ i->chandesc = CRGBVA16;
+ outchan = CHAN2(CMap, 8, CAlpha, 8);
+ break;
+
+ case GREY8:
+ i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
+ i->chanlen = 2*(i->chanlen/1);
+ i->chandesc = CYA16;
+ outchan = CHAN2(CGrey, 8, CAlpha, 8);
+ break;
+
+ case RGB24:
+ i->chans[0] = expand(i->chans[0], i->chanlen/3, 3);
+ i->chanlen = 4*(i->chanlen/3);
+ i->chandesc = CRGBA32;
+ outchan = RGBA32;
+ break;
+
+ default:
+ chantostr(buf, outchan);
+ fprint(2, "gif: can't add alpha to type %s\n", buf);
+ exits("err");
+ }
+}
+
+/*
+ * Called only when writing output. If the output is RGBA32,
+ * we must write four bytes per pixel instead of two.
+ * There's always at least two: data plus alpha.
+ * r is used only for reference; the image is already in c.
+ */
+void
+whiteout(Rawimage *r, Rawimage *c)
+{
+ int i, trindex;
+ uchar *rp, *cp;
+
+ rp = r->chans[0];
+ cp = c->chans[0];
+ trindex = r->giftrindex;
+ if(outchan == RGBA32)
+ for(i=0; i<r->chanlen; i++){
+ if(*rp == trindex){
+ *cp++ = 0x00;
+ *cp++ = 0xFF;
+ *cp++ = 0xFF;
+ *cp++ = 0xFF;
+ }else{
+ *cp++ = 0xFF;
+ cp += 3;
+ }
+ rp++;
+ }
+ else
+ for(i=0; i<r->chanlen; i++){
+ if(*rp == trindex){
+ *cp++ = 0x00;
+ *cp++ = 0xFF;
+ }else{
+ *cp++ = 0xFF;
+ cp++;
+ }
+ rp++;
+ }
+}
+
+int
+init(void)
+{
+ static int inited;
+
+ if(inited == 0){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "gif: initdraw failed: %r\n");
+ return -1;
+ }
+ einit(Ekeyboard|Emouse);
+ inited++;
+ }
+ return 1;
+}
+
+char*
+show(int fd, char *name)
+{
+ Rawimage **images, **rgbv;
+ Image **ims, **masks;
+ int j, k, n, ch, nloop, loopcount, dt;
+ char *err;
+ char buf[32];
+
+ err = nil;
+ images = readgif(fd, CRGB);
+ if(images == nil){
+ fprint(2, "gif: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ for(n=0; images[n]; n++)
+ ;
+ ims = malloc((n+1)*sizeof(Image*));
+ masks = malloc((n+1)*sizeof(Image*));
+ rgbv = malloc((n+1)*sizeof(Rawimage*));
+ if(masks==nil || rgbv==nil || ims==nil){
+ fprint(2, "gif: malloc of masks for %s failed: %r\n", name);
+ err = "malloc";
+ goto Return;
+ }
+ memset(masks, 0, (n+1)*sizeof(Image*));
+ memset(ims, 0, (n+1)*sizeof(Image*));
+ memset(rgbv, 0, (n+1)*sizeof(Rawimage*));
+ if(!dflag){
+ if(init() < 0){
+ err = "initdraw";
+ goto Return;
+ }
+ if(defaultcolor && screen->depth>8)
+ outchan = RGB24;
+ }
+
+ for(k=0; k<n; k++){
+ if(outchan == CMAP8)
+ rgbv[k] = torgbv(images[k], !eflag);
+ else{
+ if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0))
+ rgbv[k] = totruecolor(images[k], CY);
+ else
+ rgbv[k] = totruecolor(images[k], CRGB24);
+ }
+ if(rgbv[k] == nil){
+ fprint(2, "gif: converting %s to local format failed: %r\n", name);
+ err = "torgbv";
+ goto Return;
+ }
+ if(!dflag){
+ masks[k] = transparency(images[k], name);
+ if(rgbv[k]->chandesc == CY)
+ ims[k] = allocimage(display, rgbv[k]->r, GREY8, 0, 0);
+ else
+ ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0);
+ if(ims[k] == nil){
+ fprint(2, "gif: allocimage %s failed: %r\n", name);
+ err = "allocimage";
+ goto Return;
+ }
+ if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){
+ fprint(2, "gif: loadimage %s failed: %r\n", name);
+ err = "loadimage";
+ goto Return;
+ }
+ }
+ }
+
+ allims = ims;
+ allmasks = masks;
+ loopcount = images[0]->gifloopcount;
+ if(!dflag){
+ nloop = 0;
+ do{
+ for(k=0; k<n; k++){
+ which = k;
+ eresized(0);
+ dt = images[k]->gifdelay*10;
+ if(dt < 50)
+ dt = 50;
+ while(n==1 || ecankbd()){
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) /* an odd, democratic list */
+ exits(nil);
+ if(ch == '\n')
+ goto Out;
+ }sleep(dt);
+ }
+ /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/
+ }while(loopcount==0 || ++nloop<loopcount);
+ /* loop count has run out */
+ ekbd();
+ Out:
+ drawop(screen, screen->clipr, display->white, nil, ZP, S);
+ }
+ if(n>1 && output)
+ fprint(2, "gif: warning: only writing first image in %d-image GIF %s\n", n, name);
+ if(nineflag){
+ if(images[0]->gifflags&TRANSP){
+ addalpha(rgbv[0]);
+ whiteout(images[0], rgbv[0]);
+ }
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y);
+ if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){
+ fprint(2, "gif: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(images[0]->gifflags&TRANSP){
+ addalpha(rgbv[0]);
+ whiteout(images[0], rgbv[0]);
+ }
+ if(writerawimage(1, rgbv[0]) < 0){
+ fprint(2, "gif: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+
+ Return:
+ allims = nil;
+ allmasks = nil;
+ for(k=0; images[k]; k++){
+ for(j=0; j<images[k]->nchans; j++)
+ free(images[k]->chans[j]);
+ free(images[k]->cmap);
+ if(rgbv[k])
+ free(rgbv[k]->chans[0]);
+ freeimage(ims[k]);
+ freeimage(masks[k]);
+ free(images[k]);
+ free(rgbv[k]);
+ }
+ free(images);
+ free(masks);
+ free(ims);
+ return err;
+}
diff --git a/src/cmd/jpg/ico.c b/src/cmd/jpg/ico.c
new file mode 100644
index 00000000..31806afb
--- /dev/null
+++ b/src/cmd/jpg/ico.c
@@ -0,0 +1,506 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include <cursor.h>
+
+typedef struct Icon Icon;
+struct Icon
+{
+ Icon *next;
+
+ uchar w; /* icon width */
+ uchar h; /* icon height */
+ ushort ncolor; /* number of colors */
+ ushort nplane; /* number of bit planes */
+ ushort bits; /* bits per pixel */
+ ulong len; /* length of data */
+ ulong offset; /* file offset to data */
+
+ Image *img;
+ Image *mask;
+
+ Rectangle r; /* relative */
+ Rectangle sr; /* abs */
+};
+
+typedef struct Header Header;
+struct Header
+{
+ uint n;
+ Icon *first;
+ Icon *last;
+};
+
+int debug;
+Mouse mouse;
+Header h;
+Image *background;
+
+ushort
+gets(uchar *p)
+{
+ return p[0] | (p[1]<<8);
+}
+
+ulong
+getl(uchar *p)
+{
+ return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+}
+
+int
+Bgetheader(Biobuf *b, Header *h)
+{
+ Icon *icon;
+ int i;
+ uchar buf[40];
+
+ memset(h, 0, sizeof(*h));
+ if(Bread(b, buf, 6) != 6)
+ goto eof;
+ if(gets(&buf[0]) != 0)
+ goto header;
+ if(gets(&buf[2]) != 1)
+ goto header;
+ h->n = gets(&buf[4]);
+
+ for(i = 0; i < h->n; i++){
+ icon = mallocz(sizeof(*icon), 1);
+ if(icon == nil)
+ sysfatal("malloc: %r");
+ if(Bread(b, buf, 16) != 16)
+ goto eof;
+ icon->w = buf[0];
+ icon->h = buf[1];
+ icon->ncolor = buf[2] == 0 ? 256 : buf[2];
+ if(buf[3] != 0)
+ goto header;
+ icon->nplane = gets(&buf[4]);
+ icon->bits = gets(&buf[6]);
+ icon->len = getl(&buf[8]);
+ icon->offset = getl(&buf[12]);
+
+ if(i == 0)
+ h->first = icon;
+ else
+ h->last->next = icon;
+ h->last = icon;
+ }
+ return 0;
+
+eof:
+ werrstr("unexpected EOF");
+ return -1;
+header:
+ werrstr("unknown header format");
+ return -1;
+}
+
+uchar*
+transcmap(Icon *icon, uchar *map)
+{
+ uchar *m, *p;
+ int i;
+
+ p = m = malloc(sizeof(int)*(1<<icon->bits));
+ for(i = 0; i < icon->ncolor; i++){
+ *p++ = rgb2cmap(map[2], map[1], map[0]);
+ map += 4;
+ }
+ return m;
+}
+
+Image*
+xor2img(Icon *icon, uchar *xor, uchar *map)
+{
+ uchar *data;
+ Image *img;
+ int inxlen;
+ uchar *from, *to;
+ int s, byte, mask;
+ int x, y;
+
+ inxlen = 4*((icon->bits*icon->w+31)/32);
+ to = data = malloc(icon->w*icon->h);
+
+ /* rotate around the y axis, go to 8 bits, and convert color */
+ mask = (1<<icon->bits)-1;
+ for(y = 0; y < icon->h; y++){
+ s = -1;
+ byte = 0;
+ from = xor + (icon->h - 1 - y)*inxlen;
+ for(x = 0; x < icon->w; x++){
+ if(s < 0){
+ byte = *from++;
+ s = 8-icon->bits;
+ }
+ *to++ = map[(byte>>s) & mask];
+ s -= icon->bits;
+ }
+ }
+
+ /* stick in an image */
+ img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill);
+ loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
+
+ free(data);
+ return img;
+}
+
+Image*
+and2img(Icon *icon, uchar *and)
+{
+ uchar *data;
+ Image *img;
+ int inxlen;
+ int outxlen;
+ uchar *from, *to;
+ int x, y;
+
+ inxlen = 4*((icon->w+31)/32);
+ to = data = malloc(inxlen*icon->h);
+
+ /* rotate around the y axis and invert bits */
+ outxlen = (icon->w+7)/8;
+ for(y = 0; y < icon->h; y++){
+ from = and + (icon->h - 1 - y)*inxlen;
+ for(x = 0; x < outxlen; x++){
+ *to++ = ~(*from++);
+ }
+ }
+
+ /* stick in an image */
+ img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill);
+ loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
+
+ free(data);
+ return img;
+}
+
+int
+Bgeticon(Biobuf *b, Icon *icon)
+{
+ ulong l;
+ ushort s;
+ uchar *xor;
+ uchar *and;
+ uchar *cm;
+ uchar *buf;
+ uchar *map2map;
+ Image *img;
+
+ Bseek(b, icon->offset, 0);
+ buf = malloc(icon->len);
+ if(buf == nil)
+ return -1;
+ if(Bread(b, buf, icon->len) != icon->len){
+ werrstr("unexpected EOF");
+ return -1;
+ }
+
+ /* this header's info takes precedence over previous one */
+ if(getl(buf) != 40){
+ werrstr("bad icon header");
+ return -1;
+ }
+ l = getl(buf+4);
+ if(l != icon->w)
+ icon->w = l;
+ l = getl(buf+8);
+ if(l>>1 != icon->h)
+ icon->h = l>>1;
+ s = gets(buf+12);
+ if(s != icon->nplane)
+ icon->nplane = s;
+ s = gets(buf+14);
+ if(s != icon->bits)
+ icon->bits = s;
+
+ /* limit what we handle */
+ switch(icon->bits){
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ werrstr("don't support %d bit pixels", icon->bits);
+ return -1;
+ }
+ if(icon->nplane != 1){
+ werrstr("don't support %d planes", icon->nplane);
+ return -1;
+ }
+
+ cm = buf + 40;
+ xor = cm + 4*icon->ncolor;
+ and = xor + icon->h*4*((icon->bits*icon->w+31)/32);
+
+ /* translate the color map to a plan 9 one */
+ map2map = transcmap(icon, cm);
+
+ /* convert the images */
+ icon->img = xor2img(icon, xor, map2map);
+ icon->mask = and2img(icon, and);
+
+ /* so that we save an image with a white background */
+ img = allocimage(display, icon->img->r, CMAP8, 0, DWhite);
+ draw(img, icon->img->r, icon->img, icon->mask, ZP);
+ icon->img = img;
+
+ free(buf);
+ free(map2map);
+ return 0;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [file]\n", argv0);
+ exits("usage");
+}
+
+enum
+{
+ Mimage,
+ Mmask,
+ Mexit,
+
+ Up= 1,
+ Down= 0,
+};
+
+char *menu3str[] = {
+ [Mimage] "write image",
+ [Mmask] "write mask",
+ [Mexit] "exit",
+ 0,
+};
+
+Menu menu3 = {
+ menu3str
+};
+
+Cursor sight = {
+ {-7, -7},
+ {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+ 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+ 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
+ {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+ 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+ 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
+};
+
+void
+buttons(int ud)
+{
+ while((mouse.buttons==0) != ud)
+ mouse = emouse();
+}
+
+void
+mesg(char *fmt, ...)
+{
+ va_list arg;
+ char buf[1024];
+ static char obuf[1024];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ string(screen, screen->r.min, background, ZP, font, obuf);
+ string(screen, screen->r.min, display->white, ZP, font, buf);
+ strcpy(obuf, buf);
+}
+
+void
+doimage(Icon *icon)
+{
+ int rv;
+ char file[256];
+ int fd;
+
+ rv = -1;
+ snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
+ fd = create(file, OWRITE, 0664);
+ if(fd >= 0){
+ rv = writeimage(fd, icon->img, 0);
+ close(fd);
+ }
+ if(rv < 0)
+ mesg("error writing %s: %r", file);
+ else
+ mesg("created %s", file);
+}
+
+void
+domask(Icon *icon)
+{
+ int rv;
+ char file[64];
+ int fd;
+
+ rv = -1;
+ snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
+ fd = create(file, OWRITE, 0664);
+ if(fd >= 0){
+ rv = writeimage(fd, icon->mask, 0);
+ close(fd);
+ }
+ if(rv < 0)
+ mesg("error writing %s: %r", file);
+ else
+ mesg("created %s", file);
+}
+
+void
+apply(void (*f)(Icon*))
+{
+ Icon *icon;
+
+ esetcursor(&sight);
+ buttons(Down);
+ if(mouse.buttons == 4)
+ for(icon = h.first; icon; icon = icon->next)
+ if(ptinrect(mouse.xy, icon->sr)){
+ buttons(Up);
+ f(icon);
+ break;
+ }
+ buttons(Up);
+ esetcursor(0);
+}
+
+void
+menu(void)
+{
+ int sel;
+
+ sel = emenuhit(3, &mouse, &menu3);
+ switch(sel){
+ case Mimage:
+ apply(doimage);
+ break;
+ case Mmask:
+ apply(domask);
+ break;
+ case Mexit:
+ exits(0);
+ break;
+ }
+}
+
+void
+mousemoved(void)
+{
+ Icon *icon;
+
+ for(icon = h.first; icon; icon = icon->next)
+ if(ptinrect(mouse.xy, icon->sr)){
+ mesg("%dx%d", icon->w, icon->h);
+ return;
+ }
+ mesg("");
+}
+
+enum
+{
+ BORDER= 1,
+};
+
+void
+eresized(int new)
+{
+ Icon *icon;
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0)
+ sysfatal("can't reattach to window");
+ draw(screen, screen->clipr, background, nil, ZP);
+ r.max.x = screen->r.min.x;
+ r.min.y = screen->r.min.y + font->height + 2*BORDER;
+ for(icon = h.first; icon != nil; icon = icon->next){
+ r.min.x = r.max.x + BORDER;
+ r.max.x = r.min.x + Dx(icon->img->r);
+ r.max.y = r.min.y + Dy(icon->img->r);
+ draw(screen, r, icon->img, nil, ZP);
+ border(screen, r, -BORDER, display->black, ZP);
+ icon->sr = r;
+ }
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char **argv)
+{
+ Biobuf in;
+ Icon *icon;
+ int fd;
+ Rectangle r;
+ Event e;
+
+ ARGBEGIN{
+ case 'd':
+ debug = 1;
+ break;
+ }ARGEND;
+
+ fd = -1;
+ switch(argc){
+ case 0:
+ fd = 0;
+ break;
+ case 1:
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("opening: %r");
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ Binit(&in, fd, OREAD);
+
+ if(Bgetheader(&in, &h) < 0)
+ sysfatal("reading header: %r");
+
+ initdraw(nil, nil, "ico");
+ background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF);
+
+ einit(Emouse|Ekeyboard);
+
+ r.min = Pt(4, 4);
+ for(icon = h.first; icon != nil; icon = icon->next){
+ if(Bgeticon(&in, icon) < 0){
+ fprint(2, "bad rectangle: %r\n");
+ continue;
+ }
+ if(debug)
+ fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
+ icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
+ r.max = addpt(r.min, Pt(icon->w, icon->h));
+ icon->r = r;
+ r.min.x += r.max.x;
+ }
+ eresized(0);
+
+ for(;;)
+ switch(event(&e)){
+ case Ekeyboard:
+ break;
+ case Emouse:
+ mouse = e.mouse;
+ if(mouse.buttons & 4)
+ menu();
+ else
+ mousemoved();
+ break;
+ }
+
+ exits(0);
+}
diff --git a/src/cmd/jpg/imagefile.h b/src/cmd/jpg/imagefile.h
new file mode 100644
index 00000000..592c5a2e
--- /dev/null
+++ b/src/cmd/jpg/imagefile.h
@@ -0,0 +1,82 @@
+typedef struct Rawimage Rawimage;
+
+struct Rawimage
+{
+ Rectangle r;
+ uchar *cmap;
+ int cmaplen;
+ int nchans;
+ uchar *chans[4];
+ int chandesc;
+ int chanlen;
+
+ int fields; /* defined by format */
+ int gifflags; /* gif only; graphics control extension flag word */
+ int gifdelay; /* gif only; graphics control extension delay in cs */
+ int giftrindex; /* gif only; graphics control extension transparency index */
+ int gifloopcount; /* number of times to loop in animation; 0 means forever */
+};
+
+enum
+{
+ /* Channel descriptors */
+ CRGB = 0, /* three channels, no map */
+ CYCbCr = 1, /* three channels, no map, level-shifted 601 color space */
+ CY = 2, /* one channel, luminance */
+ CRGB1 = 3, /* one channel, map present */
+ CRGBV = 4, /* one channel, map is RGBV, understood */
+ CRGB24 = 5, /* one channel in correct data order for loadimage(RGB24) */
+ CRGBA32 = 6, /* one channel in correct data order for loadimage(RGBA32) */
+ CYA16 = 7, /* one channel in correct data order for loadimage(Grey8+Alpha8) */
+ CRGBVA16= 8, /* one channel in correct data order for loadimage(CMAP8+Alpha8) */
+
+ /* GIF flags */
+ TRANSP = 1,
+ INPUT = 2,
+ DISPMASK = 7<<2
+};
+
+
+enum{ /* PNG flags */
+ II_GAMMA = 1 << 0,
+ II_COMMENT = 1 << 1,
+};
+
+typedef struct ImageInfo {
+ ulong fields_set;
+ double gamma;
+ char *comment;
+} ImageInfo;
+
+
+Rawimage** readjpg(int, int);
+Rawimage** Breadjpg(Biobuf *b, int);
+Rawimage** readpng(int, int);
+Rawimage** Breadpng(Biobuf *b, int);
+Rawimage** readgif(int, int);
+Rawimage** readpixmap(int, int);
+Rawimage* torgbv(Rawimage*, int);
+Rawimage* totruecolor(Rawimage*, int);
+int writerawimage(int, Rawimage*);
+void* _remaperror(char*, ...);
+
+#ifndef _MEMDRAW_H_
+typedef struct Memimage Memimage; /* avoid necessity to include memdraw.h */
+#endif
+
+char* startgif(Biobuf*, Image*, int);
+char* writegif(Biobuf*, Image*, char*, int, int);
+void endgif(Biobuf*);
+char* memstartgif(Biobuf*, Memimage*, int);
+char* memwritegif(Biobuf*, Memimage*, char*, int, int);
+void memendgif(Biobuf*);
+Image* onechan(Image*);
+Memimage* memonechan(Memimage*);
+
+char* writeppm(Biobuf*, Image*, char*);
+char* memwriteppm(Biobuf*, Memimage*, char*);
+Image* multichan(Image*);
+Memimage* memmultichan(Memimage*);
+
+char* memwritepng(Biobuf*, Memimage*, ImageInfo*);
+extern int drawlog2[];
diff --git a/src/cmd/jpg/jpegdump.c b/src/cmd/jpg/jpegdump.c
new file mode 100644
index 00000000..0b132cb7
--- /dev/null
+++ b/src/cmd/jpg/jpegdump.c
@@ -0,0 +1,346 @@
+/* jpeg parser by tom szymanski */
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+/* subroutines done by macros */
+#define min(A,B) ((A)<(B) ? (A) : (B))
+#define max(A,B) ((A)>(B) ? (A) : (B))
+#define maxeql(A,B) if (A < (B)) A = (B);
+#define mineql(A,B) if (A > (B)) A = (B);
+#define eatarg0 (argc--, argv++)
+#define arrayLength(A) ((sizeof A)/ (sizeof A[0]))
+
+FILE *infile;
+char *fname;
+
+/* Routines to print error messages of varying severity */
+
+/* externally visible variables */
+int warncnt;
+char *myname;
+
+void getname (char *arg) {
+ /* Save name of invoking program for use by error routines */
+ register char *p;
+ p = strrchr (arg, '/');
+ if (p == NULL)
+ myname = arg;
+ else
+ myname = ++p;
+}
+
+static void introduction (void) {
+ warncnt++;
+ fflush (stdout);
+ if (myname != NULL)
+ fprintf (stderr, "%s: ", myname);
+}
+
+void warn (char *fmt, ...) {
+ va_list args;
+ introduction ();
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end (args);
+ fputc ('\n', stderr);
+ fflush (stderr);
+}
+
+void quit (char *fmt, ...) {
+ va_list args;
+ introduction ();
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end (args);
+ fputc ('\n', stderr);
+ fflush (stderr);
+ exit (1);
+}
+
+void fatal (char *fmt, ...) {
+ va_list args;
+ introduction ();
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end (args);
+ fprintf (stderr, "\nbetter get help!\n");
+ fflush (stderr);
+ abort ();
+}
+
+int toption = 0;
+int dqt[16][64];
+
+int get1 (void) {
+ unsigned char x;
+ if (fread(&x, 1, 1, infile) == 0)
+ quit ("unexpected EOF");
+ return x;
+}
+
+int get2 (void) {
+ int x;
+
+ x = get1() << 8;
+ return x | get1();
+}
+
+void eatmarker (int kind) {
+ int l, c;
+ l = get2();
+ printf ("%02x len=%d\n", kind, l);
+ for (l -= 2; l > 0; l--)
+ get1();
+}
+
+char *sofName[16] = {
+ "Baseline sequential DCT - Huffman coding",
+ "Extended sequential DCT - Huffman coding",
+ "Progressive DCT - Huffman coding",
+ "Lossless - Huffman coding",
+ "4 is otherwise used",
+ "Sequential DCT - differential Huffman coding",
+ "Progressive DCT - differential Huffman coding",
+ "Lossless - differential Huffman coding",
+ "8 is reserved",
+ "Extended Sequential DCT - arithmetic coding",
+ "Progressive DCT - arithmetic coding",
+ "Lossless - arithmetic coding",
+ "c is otherwise used",
+ "Sequential DCT - differential arithmetic coding",
+ "Progressive DCT - differential arithmetic coding",
+ "Lossless - differential arithmetic coding",
+};
+
+void get_sof (int kind) {
+ int i, length, height, width, precision, ncomponents;
+ int id, sf, tab;
+ length = get2();
+ precision = get1();
+ height = get2();
+ width = get2();
+ ncomponents = get1();
+ printf ("SOF%d:\t%s\n", kind - 0xc0, sofName[kind - 0xc0]);
+ printf ("\t%d wide, %d high, %d deep, %d components\n",
+ width, height, precision, ncomponents);
+ for (i = 0; i < ncomponents; i++) {
+ id = get1();
+ sf = get1();
+ tab = get1();
+ printf ("\tcomponent %d: %d hsample, %d vsample, quantization table %d\n",
+ id, sf >> 4, sf & 0xf, tab);
+ }
+}
+
+void get_com (int kind) {
+ int l, c;
+ l = get2();
+ printf ("COM len=%d '", l);
+ for (l -= 2; l > 0; l--)
+ putchar (c = get1());
+ printf ("'\n");
+}
+
+void get_app (int kind) {
+ int l, c, first;
+ char buf[6];
+ int nbuf, nok;
+ l = get2();
+ printf ("APP%d len=%d\n", kind - 0xe0, l);
+ nbuf = 0;
+ nok = 0;
+ first = 1;
+ /* dump printable strings in comment */
+ for (l -= 2; l > 0; l--){
+ c = get1();
+ if(isprint(c)){
+ if(nbuf >= sizeof buf){
+ if(!first && nbuf == nok)
+ printf(" ");
+ printf("%.*s", nbuf, buf);
+ nbuf = 0;
+ first = 0;
+ }
+ buf[nbuf++] = c;
+ nok++;
+ }else{
+ if(nok >= sizeof buf)
+ if(nbuf > 0)
+ printf("%.*s", nbuf, buf);
+ nbuf = 0;
+ nok = 0;
+ }
+ }
+ if(nok >= sizeof buf)
+ if(nbuf > 0){
+ if(!first && nbuf == nok)
+ printf(" ");
+ printf("%.*s", nbuf, buf);
+ }
+}
+
+void get_dac (int kind) {
+ eatmarker (kind);
+}
+
+int get1dqt (void) {
+ int t, p, i, *tab;
+ t = get1();
+ p = t >> 4;
+ t = t & 0xf;
+ printf ("DQT:\tp = %d, table = %d\n", p, t);
+ tab = &dqt[t][0];
+ for (i = 0; i < 64; i++)
+ tab[i] = p ? get2() : get1();
+ if (toption) {
+ for (i = 0; i < 64; i++)
+ printf ("\t%q[%02d] = %d\n", i, tab[i]);
+ }
+ return p ? 65 : 129;
+}
+
+void get_dqt (int kind) {
+ int length;
+ length = get2() - 2;
+ while (length > 0)
+ length -= get1dqt();
+}
+
+int get1dht (void) {
+ int l, tcth, p, i, j, v[16], vv[16][256];
+ tcth = get1();
+ printf ("DHT:\tclass = %d, table = %d\n", tcth >> 4, tcth & 0xf);
+ for (i = 0; i < 16; i++)
+ v[i] = get1();
+ l = 17;
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < v[i]; j++) {
+ vv[i][j] = get1();
+ l += 1;
+ }
+ if (toption) {
+ for (i = 0; i < 16; i++)
+ printf ("\t%l[%02d] = %d\n", i+1, v[i]);
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < v[i]; j++)
+ printf ("\t%v[%02d,%02d] = %d\n", i+1, j+1, vv[i][j]);
+ }
+ return l;
+}
+
+void get_dht (int kind) {
+ int length;
+ length = get2() - 2;
+ while (length > 0)
+ length -= get1dht();
+}
+
+void get_sos (int kind) {
+ int i, length, ncomponents, id, dcac, ahal;
+ length = get2();
+ ncomponents = get1();
+ printf ("SOS:\t%d components\n", ncomponents);
+ for (i = 0; i < ncomponents; i++) {
+ id = get1();
+ dcac = get1();
+ printf ("\tcomponent %d: %d DC, %d AC\n", id, dcac >> 4, dcac & 0xf);
+ }
+ printf ("\tstart spectral %d\n", get1());
+ printf ("\tend spectral %d\n", get1());
+ ahal = get1();
+ printf ("\tah = %d, al = %d\n", ahal >> 4, ahal &0xf);
+}
+
+main (int argc, char *argv[]) {
+ int l, stuff, i, j, c;
+ while (argc > 1 && argv[1][0] == '-') {
+ switch (argv[1][1]) {
+ case 't':
+ toption = 1;
+ break;
+ default:
+ warn ("bad option '%c'", argv[1][1]);
+ }
+ eatarg0;
+ }
+ fname = argv[1];
+ infile = fopen (fname, "r");
+ if (infile == NULL)
+ quit ("can't open %s\n", fname);
+ Start:
+// if (get1() != 0xff || get1() != 0xd8)
+// quit ("not JFIF");
+// printf ("SOI\n");
+// get_app (0xe0);
+ for (;;) {
+ c = get1();
+ if (c != 0xff)
+ quit ("expected marker, got %2x", c);
+ do {
+ c = get1();
+ } while (c == 0xff);
+marker:
+ switch (c) {
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3:
+ case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb:
+ case 0xcd: case 0xce: case 0xcf:
+ get_sof (c);
+ break;
+ case 0xc4:
+ get_dht (c);
+ break;
+ case 0xcc:
+ get_dac (c);
+ break;
+ case 0xd8:
+ printf ("SOI\n");
+ break;
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
+ get_app(c);
+ break;
+ case 0xda:
+ get_sos (c);
+ goto newentropy;
+ case 0xdb:
+ get_dqt (c);
+ break;
+ case 0xfe:
+ get_com (c);
+ break;
+ case 0xd9:
+ printf ("EOI\n");
+ if((c=getc(infile)) == EOF)
+ exit(0);
+ ungetc(c, infile);
+ goto Start;
+ default:
+ eatmarker (c);
+ }
+ continue;
+newentropy:
+ l = stuff = 0;
+entropy:
+ while ((c = get1()) != 0xff)
+ l += 1;
+ while (c == 0xff)
+ c = get1();
+ if (c == 0) {
+ stuff += 1;
+ goto entropy;
+ }
+ printf ("sequence length %d with %d stuffs\n", l, stuff);
+ if (0xd0 <= c && c <= 0xd7) {
+ printf ("restart %d\n", c - 0xd0);
+ goto newentropy;
+ }
+ goto marker;
+ }
+}
diff --git a/src/cmd/jpg/jpg.c b/src/cmd/jpg/jpg.c
new file mode 100644
index 00000000..cf438a93
--- /dev/null
+++ b/src/cmd/jpg/jpg.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int jflag = 0;
+int fflag = 0;
+int Fflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */
+int output = 0;
+ulong outchan = CMAP8;
+Image *image;
+int defaultcolor = 1;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*, int);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "jpg: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i, yflag;
+ char *err;
+ char buf[12+1];
+
+ yflag = 0;
+ ARGBEGIN{
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'F':
+ Fflag++; /* make a movie */
+ fflag++; /* merge two fields per image */
+ break;
+ case 'f':
+ fflag++; /* merge two fields per image */
+ break;
+ case 'J': /* decode jpeg only; no display or remap (for debugging, etc.) */
+ jflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'r':
+ colorspace = CRGB;
+ break;
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case 'y': /* leave it in CYCbCr; for debugging only */
+ yflag = 1;
+ colorspace = CYCbCr;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: jpg -39cdefFkJrtv [file.jpg ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */
+ fd = open("/dev/screen", OREAD);
+ if(fd > 0){
+ buf[12] = '\0';
+ if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
+ colorspace = CRGB;
+ close(fd);
+ }
+ }
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>", outchan);
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "jpg: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i], outchan);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "jpg: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+Rawimage**
+vidmerge(Rawimage **aa1, Rawimage **aa2)
+{
+ Rawimage **aao, *ao, *a1, *a2;
+ int i, c, row, col;
+
+ aao = nil;
+ for (i = 0; aa1[i]; i++) {
+
+ a1 = aa1[i];
+ a2 = aa2[i];
+ if (a2 == nil){
+ fprint(2, "jpg: vidmerge: unequal lengths\n");
+ return nil;
+ }
+ aao = realloc(aao, (i+2)*sizeof(Rawimage *));
+ if (aao == nil){
+ fprint(2, "jpg: vidmerge: realloc\n");
+ return nil;
+ }
+ aao[i+1] = nil;
+ ao = aao[i] = malloc(sizeof(Rawimage));
+ if (ao == nil){
+ fprint(2, "jpg: vidmerge: realloc\n");
+ return nil;
+ }
+ memcpy(ao, a1, sizeof(Rawimage));
+ if (!eqrect(a1->r , a2->r)){
+ fprint(2, "jpg: vidmerge: rects different in img %d\n", i);
+ return nil;
+ }
+ if (a1->cmaplen != a2->cmaplen){
+ fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i);
+ return nil;
+ }
+ if (a1->nchans != a2->nchans){
+ fprint(2, "jpg: vidmerge: nchans different in img %d\n", i);
+ return nil;
+ }
+ if (a1->fields != a2->fields){
+ fprint(2, "jpg: vidmerge: fields different in img %d\n", i);
+ return nil;
+ }
+ ao->r.max.y += Dy(ao->r);
+ ao->chanlen += ao->chanlen;
+ if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){
+ fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n",
+ ao->chanlen, Dx(ao->r), Dy(ao->r));
+ return nil;
+ }
+ row = Dx(a1->r);
+ for (c = 0; c < ao->nchans; c++) {
+ uchar *po, *p1, *p2;
+
+ ao->chans[c] = malloc(ao->chanlen);
+ po = ao->chans[c];
+ p1 = a1->chans[c];
+ p2 = a2->chans[c];
+ for (col = 0; col < Dy(a1->r); col++) {
+ memcpy(po, p1, row);
+ po += row, p1 += row;
+ memcpy(po, p2, row);
+ po += row, p2 += row;
+ }
+ free(a1->chans[c]);
+ free(a2->chans[c]);
+ }
+ if(a2->cmap != nil)
+ free(a2->cmap);
+ free(a1);
+ free(a2);
+ }
+ if (aa2[i] != nil)
+ fprint(2, "jpg: vidmerge: unequal lengths\n");
+ free(aa1);
+ free(aa2);
+ return aao;
+}
+
+char*
+show(int fd, char *name, int outc)
+{
+ Rawimage **array, *r, *c;
+ static int inited;
+ Image *i;
+ int j, ch, outchan;
+ Biobuf b;
+ char buf[32];
+
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ outchan = outc;
+rpt: array = Breadjpg(&b, colorspace);
+ if(array == nil || array[0]==nil){
+ fprint(2, "jpg: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ if (fflag) {
+ Rawimage **a;
+
+ a = Breadjpg(&b, colorspace);
+ if(a == nil || a[0]==nil){
+ fprint(2, "jpg: decode %s-2 failed: %r\n", name);
+ return "decode";
+ }
+ array = vidmerge(a, array);
+ } else
+ Bterm(&b);
+
+ r = array[0];
+ c = nil;
+ if(jflag)
+ goto Return;
+ if(!dflag && !inited){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "jpg: initdraw failed: %r\n");
+ return "initdraw";
+ }
+ if(Fflag == 0)
+ einit(Ekeyboard|Emouse);
+ if(defaultcolor && screen->depth>8 && outchan==CMAP8)
+ outchan = RGB24;
+ inited++;
+ }
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
+ c = totruecolor(r, CY);
+ outchan = GREY8;
+ }else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "jpg: conversion of %s failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(c->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "jpg: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "jpg: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if (Fflag) {
+ freeimage(i);
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ goto rpt;
+ }
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "jpg: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "jpg: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ Return:
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ if (Fflag) goto rpt;
+ return nil;
+}
diff --git a/src/cmd/jpg/mkfile b/src/cmd/jpg/mkfile
new file mode 100644
index 00000000..198f8e2f
--- /dev/null
+++ b/src/cmd/jpg/mkfile
@@ -0,0 +1,52 @@
+<$PLAN9/src/mkhdr
+
+TARG=jpg\
+ gif\
+ togif\
+ ppm\
+ toppm\
+ png\
+ topng\
+ yuv\
+ ico\
+ toico\
+ bmp\
+
+IMFILES=\
+ torgbv.$O\
+ totruecolor.$O\
+ writerawimage.$O\
+
+HFILES=imagefile.h\
+
+SHORTLIB=draw flate bio 9
+LDFLAGS=$LDFLAGS -L$X11/lib -lX11
+
+<$PLAN9/src/mkmany
+
+$O.jpg: $IMFILES readjpg.$O jpg.$O
+$O.gif: $IMFILES readgif.$O gif.$O
+$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O multichan.$O
+$O.ppm: $IMFILES readppm.$O ppm.$O
+$O.toppm: writeppm.$O multichan.$O toppm.$O
+$O.png: $IMFILES readpng.$O png.$O multichan.$O
+$O.topng: writepng.$O topng.$O
+$O.yuv: $IMFILES readyuv.$O yuv.$O
+$O.bmp: $IMFILES readbmp.$O bmp.$O
+
+torgbv.$O: ycbcr.h rgbv.h
+
+ycbcr.h: rgbycc.c
+ 9c rgbycc.c
+ 9l -o o.rgbycc rgbycc.c
+ ./o.rgbycc >ycbcr.h
+
+rgbv.h: rgbrgbv.c
+ 9c rgbrgbv.c
+ 9l -o o.rgbrgbv rgbrgbv.c
+ ./o.rgbrgbv >rgbv.h
+
+nuke:V: nuke-headers
+
+nuke-headers:V:
+ rm -f rgbv.h ycbcr.h
diff --git a/src/cmd/jpg/multichan.c b/src/cmd/jpg/multichan.c
new file mode 100644
index 00000000..03971215
--- /dev/null
+++ b/src/cmd/jpg/multichan.c
@@ -0,0 +1,60 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+#include "imagefile.h"
+
+/* Separate colors, if not a grey scale or bitmap, into one byte per color per pixel, no alpha or X */
+/* Result is GREY[1248] or RGB24 */
+
+int drawlog2[] = {
+ 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 5
+};
+
+static
+int
+notrans(ulong chan)
+{
+ switch(chan){
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ case GREY8:
+ case RGB24:
+ return 1;
+ }
+ return 0;
+}
+
+Image*
+multichan(Image *i)
+{
+ Image *ni;
+
+ if(notrans(i->chan))
+ return i;
+
+ ni = allocimage(display, i->r, RGB24, 0, DNofill);
+ if(ni == nil)
+ return ni;
+ draw(ni, ni->r, i, nil, i->r.min);
+ return ni;
+}
+
+Memimage*
+memmultichan(Memimage *i)
+{
+ Memimage *ni;
+
+ if(notrans(i->chan))
+ return i;
+
+ ni = allocmemimage(i->r, RGB24);
+ if(ni == nil)
+ return ni;
+ memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
+ return ni;
+}
diff --git a/src/cmd/jpg/onechan.c b/src/cmd/jpg/onechan.c
new file mode 100644
index 00000000..ea1c489b
--- /dev/null
+++ b/src/cmd/jpg/onechan.c
@@ -0,0 +1,229 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+#include "imagefile.h"
+
+/* Convert image to a single channel, one byte per pixel */
+
+static
+int
+notrans(ulong chan)
+{
+ switch(chan){
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ case CMAP8:
+ case GREY8:
+ return 1;
+ }
+ return 0;
+}
+
+static
+int
+easycase(ulong chan)
+{
+ switch(chan){
+ case RGB16:
+ case RGB24:
+ case RGBA32:
+ case ARGB32:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Convert to one byte per pixel, RGBV or grey, depending
+ */
+
+static
+uchar*
+load(Image *image, Memimage *memimage)
+{
+ uchar *data, *p, *q0, *q1, *q2;
+ uchar *rgbv;
+ int depth, ndata, dx, dy, i, v;
+ ulong chan, pixel;
+ Rectangle r;
+ Rawimage ri, *nri;
+
+ if(memimage == nil){
+ r = image->r;
+ depth = image->depth;
+ chan = image->chan;
+ }else{
+ r = memimage->r;
+ depth = memimage->depth;
+ chan = memimage->chan;
+ }
+ dx = Dx(r);
+ dy = Dy(r);
+
+ /*
+ * Read image data into memory
+ * potentially one extra byte on each end of each scan line.
+ */
+ ndata = dy*(2+bytesperline(r, depth));
+ data = malloc(ndata);
+ if(data == nil)
+ return nil;
+ if(memimage != nil)
+ ndata = unloadmemimage(memimage, r, data, ndata);
+ else
+ ndata = unloadimage(image, r, data, ndata);
+ if(ndata < 0){
+ werrstr("onechan: %r");
+ free(data);
+ return nil;
+ }
+
+ /*
+ * Repack
+ */
+ memset(&ri, 0, sizeof(ri));
+ ri.r = r;
+ ri.cmap = nil;
+ ri.cmaplen = 0;
+ ri.nchans = 3;
+ ri.chanlen = dx*dy;
+ ri.chans[0] = malloc(ri.chanlen);
+ ri.chans[1] = malloc(ri.chanlen);
+ ri.chans[2] = malloc(ri.chanlen);
+ if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){
+ Err:
+ free(ri.chans[0]);
+ free(ri.chans[1]);
+ free(ri.chans[2]);
+ free(data);
+ return nil;
+ }
+ ri.chandesc = CRGB;
+
+ p = data;
+ q0 = ri.chans[0];
+ q1 = ri.chans[1];
+ q2 = ri.chans[2];
+
+ switch(chan){
+ default:
+ werrstr("can't handle image type 0x%lux", chan);
+ goto Err;
+ case RGB16:
+ for(i=0; i<ri.chanlen; i++, p+=2){
+ pixel = (p[1]<<8)|p[0]; /* rrrrrggg gggbbbbb */
+ v = (pixel & 0xF800) >> 8;
+ *q0++ = v | (v>>5);
+ v = (pixel & 0x07E0) >> 3;
+ *q1++ = v | (v>>6);
+ v = (pixel & 0x001F) << 3;
+ *q2++ = v | (v>>5);
+ }
+ break;
+ case RGB24:
+ for(i=0; i<ri.chanlen; i++){
+ *q2++ = *p++;
+ *q1++ = *p++;
+ *q0++ = *p++;
+ }
+ break;
+ case RGBA32:
+ for(i=0; i<ri.chanlen; i++){
+ *q2++ = *p++;
+ *q1++ = *p++;
+ *q0++ = *p++;
+ p++;
+ }
+ break;
+ case ARGB32:
+ for(i=0; i<ri.chanlen; i++){
+ p++;
+ *q2++ = *p++;
+ *q1++ = *p++;
+ *q0++ = *p++;
+ }
+ break;
+ }
+
+ rgbv = nil;
+ nri = torgbv(&ri, 1);
+ if(nri != nil){
+ rgbv = nri->chans[0];
+ free(nri);
+ }
+
+ free(ri.chans[0]);
+ free(ri.chans[1]);
+ free(ri.chans[2]);
+ free(data);
+ return rgbv;
+}
+
+Image*
+onechan(Image *i)
+{
+ uchar *data;
+ Image *ni;
+
+ if(notrans(i->chan))
+ return i;
+
+ if(easycase(i->chan))
+ data = load(i, nil);
+ else{
+ ni = allocimage(display, i->r, RGB24, 0, DNofill);
+ if(ni == nil)
+ return ni;
+ draw(ni, ni->r, i, nil, i->r.min);
+ data = load(ni, nil);
+ freeimage(ni);
+ }
+
+ if(data == nil)
+ return nil;
+
+ ni = allocimage(display, i->r, CMAP8, 0, DNofill);
+ if(ni != nil)
+ if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
+ freeimage(ni);
+ ni = nil;
+ }
+ free(data);
+ return ni;
+}
+
+Memimage*
+memonechan(Memimage *i)
+{
+ uchar *data;
+ Memimage *ni;
+
+ if(notrans(i->chan))
+ return i;
+
+ if(easycase(i->chan))
+ data = load(nil, i);
+ else{
+ ni = allocmemimage(i->r, RGB24);
+ if(ni == nil)
+ return ni;
+ memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S);
+ data = load(nil, ni);
+ freememimage(ni);
+ }
+
+ if(data == nil)
+ return nil;
+
+ ni = allocmemimage(i->r, CMAP8);
+ if(ni != nil)
+ if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
+ freememimage(ni);
+ ni = nil;
+ }
+ free(data);
+ return ni;
+}
diff --git a/src/cmd/jpg/png.c b/src/cmd/jpg/png.c
new file mode 100644
index 00000000..d653fe6a
--- /dev/null
+++ b/src/cmd/jpg/png.c
@@ -0,0 +1,224 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+extern int debug;
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int colorspace = CRGB;
+int output = 0;
+ulong outchan = CMAP8;
+Image *image;
+int defaultcolor = 1;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*, int);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "png: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i;
+ char *err;
+ char buf[12+1];
+
+ ARGBEGIN{
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'D':
+ debug++;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'r':
+ colorspace = CRGB;
+ break;
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: png -39cdekrtv [file.png ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ if(dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */
+ fd = open("/dev/screen", OREAD);
+ if(fd > 0){
+ buf[12] = '\0';
+ if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
+ colorspace = CRGB;
+ close(fd);
+ }
+ }
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>", outchan);
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "png: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i], outchan);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "png: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+char*
+show(int fd, char *name, int outc)
+{
+ Rawimage **array, *r, *c;
+ static int inited;
+ Image *i;
+ int j, ch, outchan;
+ Biobuf b;
+ char buf[32];
+
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ outchan = outc;
+ array = Breadpng(&b, colorspace);
+ if(array == nil || array[0]==nil){
+ fprint(2, "png: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ Bterm(&b);
+
+ r = array[0];
+ if(!dflag && !inited){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "png: initdraw failed: %r\n");
+ return "initdraw";
+ }
+ einit(Ekeyboard|Emouse);
+ if(defaultcolor && screen->depth>8 && outchan==CMAP8)
+ outchan = RGB24;
+ inited++;
+ }
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
+ c = totruecolor(r, CY);
+ outchan = GREY8;
+ }else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "png: conversion of %s failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(c->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "png: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "png: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "png: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "png: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ return nil;
+}
diff --git a/src/cmd/jpg/ppm.c b/src/cmd/jpg/ppm.c
new file mode 100644
index 00000000..24019dfa
--- /dev/null
+++ b/src/cmd/jpg/ppm.c
@@ -0,0 +1,209 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int output = 0;
+ulong outchan = CMAP8;
+int defaultcolor = 1;
+Image *image;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "ppm: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i;
+ char *err;
+
+ ARGBEGIN{
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: ppm -39cdektv [file.ppm ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>");
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "ppm: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i]);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "ppm: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+int
+init(void)
+{
+ static int inited;
+
+ if(inited == 0){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "ppm: initdraw failed: %r");
+ return -1;
+ }
+ einit(Ekeyboard|Emouse);
+ inited++;
+ }
+ return 1;
+}
+
+char*
+show(int fd, char *name)
+{
+ Rawimage **array, *r, *c;
+ Image *i;
+ int j, ch;
+ char buf[32];
+
+ array = readpixmap(fd, CRGB);
+ if(array == nil || array[0]==nil){
+ fprint(2, "ppm: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ if(!dflag){
+ if(init() < 0)
+ return "initdraw";
+ if(defaultcolor && screen->depth>8)
+ outchan = RGB24;
+ }
+ r = array[0];
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
+ c = totruecolor(r, CY);
+ else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "ppm: converting %s to local format failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(r->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "ppm: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "ppm: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "ppm: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "ppm: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ return nil;
+}
diff --git a/src/cmd/jpg/readbmp.c b/src/cmd/jpg/readbmp.c
new file mode 100644
index 00000000..154cb48d
--- /dev/null
+++ b/src/cmd/jpg/readbmp.c
@@ -0,0 +1,626 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+#include "bmp.h"
+
+/*
+ MS-BMP file reader
+ (c) 2003, I.P.Keller
+
+ aims to decode *all* valid bitmap formats, although some of the
+ flavours couldn't be verified due to lack of suitable test-files.
+ the following flavours are supported:
+
+ Bit/Pix Orientation Compression Tested?
+ 1 top->bottom n/a yes
+ 1 bottom->top n/a yes
+ 4 top->bottom no yes
+ 4 bottom->top no yes
+ 4 top->bottom RLE4 yes, but not with displacement
+ 8 top->bottom no yes
+ 8 bottom->top no yes
+ 8 top->bottom RLE8 yes, but not with displacement
+ 16 top->bottom no no
+ 16 bottom->top no no
+ 16 top->bottom BITMASK no
+ 16 bottom->top BITMASK no
+ 24 top->bottom n/a yes
+ 24 bottom->top n/a yes
+ 32 top->bottom no no
+ 32 bottom->top no no
+ 32 top->bottom BITMASK no
+ 32 bottom->top BITMASK no
+
+ OS/2 1.x bmp files are recognised as well, but testing was very limited.
+
+ verifying was done with a number of test files, generated by
+ different tools. nevertheless, the tests were in no way exhaustive
+ enough to guarantee bug-free decoding. caveat emptor!
+*/
+
+static short
+r16(Biobuf*b)
+{
+ short s;
+
+ s = Bgetc(b);
+ s |= ((short)Bgetc(b)) << 8;
+ return s;
+}
+
+
+static long
+r32(Biobuf*b)
+{
+ long l;
+
+ l = Bgetc(b);
+ l |= ((long)Bgetc(b)) << 8;
+ l |= ((long)Bgetc(b)) << 16;
+ l |= ((long)Bgetc(b)) << 24;
+ return l;
+}
+
+
+/* get highest bit set */
+static int
+msb(ulong x)
+{
+ int i;
+ for(i = 32; i; i--, x <<= 1)
+ if(x & 0x80000000L)
+ return i;
+ return 0;
+}
+
+/* Load a 1-Bit encoded BMP file (uncompressed) */
+static int
+load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32;
+ int val = 0, n;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ for(iy = height; iy; iy--, i += step_up)
+ for(ix = 0, n = 0; ix < padded_width; ix++, n--) {
+ if(!n) {
+ val = Bgetc(b);
+ n = 8;
+ }
+ if(ix < width) {
+ buf[i] = clut[val & 0x80 ? 1 : 0];
+ i++;
+ }
+ val <<= 1;
+ }
+ return 0;
+}
+
+/* Load a 4-Bit encoded BMP file (uncompressed) */
+static int
+load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3;
+ uint valH, valL;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ for(iy = height; iy; iy--, i += step_up) {
+ for(ix = 0; ix < width; ) {
+ valH = valL = Bgetc(b) & 0xff;
+ valH >>= 4;
+
+ buf[i] = clut[valH];
+ i++; ix++;
+
+ if(ix < width) {
+ valL &= 0xf;
+ buf[i] = clut[valL];
+ i++; ix++;
+ }
+ }
+ Bseek(b, skip, 1);
+ }
+ return 0;
+}
+
+/* Load a 4-Bit encoded BMP file (RLE4-compressed) */
+static int
+load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ long ix, iy = height -1;
+ uint val, valS, skip;
+ Rgb* p;
+
+ while(iy >= 0) {
+ ix = 0;
+ while(ix < width) {
+ val = Bgetc(b);
+
+ if(0 != val) {
+ valS = (uint)Bgetc(b);
+ p = &buf[ix + iy * width];
+ while(val--) {
+ *p = clut[0xf & (valS >> 4)];
+ p++;
+ ix++;
+ if(0 < val) {
+ *p = clut[0xf & valS];
+ p++;
+ ix++;
+ val--;
+ }
+ }
+ } else {
+ /* Special modes... */
+ val = Bgetc(b);
+ switch(val) {
+ case 0: /* End-Of-Line detected */
+ ix = width;
+ iy--;
+ break;
+ case 1: /* End-Of-Picture detected -->> abort */
+ ix = width;
+ iy = -1;
+ break;
+ case 2: /* Position change detected */
+ val = Bgetc(b);
+ ix += val;
+ val = Bgetc(b);
+ iy -= val;
+ break;
+
+ default:/* Transparent data sequence detected */
+ p = &buf[ix + iy * width];
+ if((1 == (val & 3)) || (2 == (val & 3)))
+ skip = 1;
+ else
+ skip = 0;
+
+ while(val--) {
+ valS = (uint)Bgetc(b);
+ *p = clut[0xf & (valS >> 4)];
+ p++;
+ ix++;
+ if(0 < val) {
+ *p = clut[0xf & valS];
+ p++;
+ ix++;
+ val--;
+ }
+ }
+ if(skip)
+ Bgetc(b);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Load a 8-Bit encoded BMP file (uncompressed) */
+static int
+load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ for(iy = height; iy; iy--, i += step_up) {
+ for(ix = 0; ix < width; ix++, i++)
+ buf[i] = clut[Bgetc(b) & 0xff];
+ Bseek(b, skip, 1);
+ }
+ return 0;
+}
+
+/* Load a 8-Bit encoded BMP file (RLE8-compressed) */
+static int
+load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ long ix, iy = height -1;
+ int val, valS, skip;
+ Rgb* p;
+
+ while(iy >= 0) {
+ ix = 0;
+ while(ix < width) {
+ val = Bgetc(b);
+
+ if(0 != val) {
+ valS = Bgetc(b);
+ p = &buf[ix + iy * width];
+ while(val--) {
+ *p = clut[valS];
+ p++;
+ ix++;
+ }
+ } else {
+ /* Special modes... */
+ val = Bgetc(b);
+ switch(val) {
+ case 0: /* End-Of-Line detected */
+ ix = width;
+ iy--;
+ break;
+ case 1: /* End-Of-Picture detected */
+ ix = width;
+ iy = -1;
+ break;
+ case 2: /* Position change detected */
+ val = Bgetc(b);
+ ix += val;
+ val = Bgetc(b);
+ iy -= val;
+ break;
+ default: /* Transparent (not compressed) sequence detected */
+ p = &buf[ix + iy * width];
+ if(val & 1)
+ skip = 1;
+ else
+ skip = 0;
+
+ while(val--) {
+ valS = Bgetc(b);
+ *p = clut[valS];
+ p++;
+ ix++;
+ }
+ if(skip)
+ /* Align data stream */
+ Bgetc(b);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Load a 16-Bit encoded BMP file (uncompressed) */
+static int
+load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ uchar c[2];
+ long ix, iy, i = 0, step_up = 0;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ if(clut) {
+ unsigned mask_blue = (unsigned)clut[0].blue +
+ ((unsigned)clut[0].green << 8);
+ unsigned mask_green = (unsigned)clut[1].blue +
+ ((unsigned)clut[1].green << 8);
+ unsigned mask_red = (unsigned)clut[2].blue +
+ ((unsigned)clut[2].green << 8);
+ int shft_blue = msb((ulong)mask_blue) - 8;
+ int shft_green = msb((ulong)mask_green) - 8;
+ int shft_red = msb((ulong)mask_red) - 8;
+
+ for(iy = height; iy; iy--, i += step_up)
+ for(ix = 0; ix < width; ix++, i++) {
+ unsigned val;
+ Bread(b, c, sizeof(c));
+ val = (unsigned)c[0] + ((unsigned)c[1] << 8);
+
+ buf[i].alpha = 0;
+ if(shft_blue >= 0)
+ buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
+ else
+ buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
+ if(shft_green >= 0)
+ buf[i].green = (uchar)((val & mask_green) >> shft_green);
+ else
+ buf[i].green = (uchar)((val & mask_green) << -shft_green);
+ if(shft_red >= 0)
+ buf[i].red = (uchar)((val & mask_red) >> shft_red);
+ else
+ buf[i].red = (uchar)((val & mask_red) << -shft_red);
+ }
+ } else
+ for(iy = height; iy; iy--, i += step_up)
+ for(ix = 0; ix < width; ix++, i++) {
+ Bread(b, c, sizeof(c));
+ buf[i].blue = (uchar)((c[0] << 3) & 0xf8);
+ buf[i].green = (uchar)(((((unsigned)c[1] << 6) +
+ (((unsigned)c[0]) >> 2))) & 0xf8);
+ buf[i].red = (uchar)((c[1] << 1) & 0xf8);
+ }
+ return 0;
+}
+
+/* Load a 24-Bit encoded BMP file (uncompressed) */
+static int
+load_24T(Biobuf* b, long width, long height, Rgb* buf)
+{
+ long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ for(iy = height; iy; iy--, i += step_up) {
+ for(ix = 0; ix < width; ix++, i++) {
+ buf[i].alpha = 0;
+ buf[i].blue = Bgetc(b);
+ buf[i].green = Bgetc(b);
+ buf[i].red = Bgetc(b);
+ }
+ Bseek(b, skip, 1);
+ }
+ return 0;
+}
+
+/* Load a 32-Bit encoded BMP file (uncompressed) */
+static int
+load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
+{
+ uchar c[4];
+ long ix, iy, i = 0, step_up = 0;
+
+ if(height > 0) { /* bottom-up */
+ i = (height - 1) * width;
+ step_up = -2 * width;
+ } else
+ height = -height;
+
+ if(clut) {
+ ulong mask_blue = (ulong)clut[0].blue +
+ ((ulong)clut[0].green << 8) +
+ ((ulong)clut[0].red << 16) +
+ ((ulong)clut[0].alpha << 24);
+ ulong mask_green = (ulong)clut[1].blue +
+ ((ulong)clut[1].green << 8) +
+ ((ulong)clut[1].red << 16) +
+ ((ulong)clut[1].alpha << 24);
+ ulong mask_red = (ulong)clut[2].blue +
+ ((ulong)clut[2].green << 8) +
+ ((ulong)clut[2].red << 16) +
+ ((ulong)clut[2].alpha << 24);
+ int shft_blue = msb(mask_blue) - 8;
+ int shft_green = msb(mask_green) - 8;
+ int shft_red = msb(mask_red) - 8;
+
+ for(iy = height; iy; iy--, i += step_up)
+ for(ix = 0; ix < width; ix++, i++) {
+ ulong val;
+ Bread(b, c, sizeof(c));
+ val = (ulong)c[0] + ((ulong)c[1] << 8) +
+ ((ulong)c[2] << 16) + ((ulong)c[1] << 24);
+
+ buf[i].alpha = 0;
+ if(shft_blue >= 0)
+ buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
+ else
+ buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
+ if(shft_green >= 0)
+ buf[i].green = (uchar)((val & mask_green) >> shft_green);
+ else
+ buf[i].green = (uchar)((val & mask_green) << -shft_green);
+ if(shft_red >= 0)
+ buf[i].red = (uchar)((val & mask_red) >> shft_red);
+ else
+ buf[i].red = (uchar)((val & mask_red) << -shft_red);
+ }
+ } else
+ for(iy = height; iy; iy--, i += step_up)
+ for(ix = 0; ix < width; ix++, i++) {
+ Bread(b, c, nelem(c));
+ buf[i].blue = c[0];
+ buf[i].green = c[1];
+ buf[i].red = c[2];
+ }
+ return 0;
+}
+
+
+static Rgb*
+ReadBMP(Biobuf *b, int *width, int *height)
+{
+ int colours, num_coltab = 0;
+ Filehdr bmfh;
+ Infohdr bmih;
+ Rgb clut[256];
+ Rgb* buf;
+
+ bmfh.type = r16(b);
+ if(bmfh.type != 0x4d42) /* signature must be 'BM' */
+ sysfatal("bad magic number, not a BMP file");
+
+ bmfh.size = r32(b);
+ bmfh.reserved1 = r16(b);
+ bmfh.reserved2 = r16(b);
+ bmfh.offbits = r32(b);
+
+ memset(&bmih, 0, sizeof(bmih));
+ bmih.size = r32(b);
+
+ if(bmih.size == 0x0c) { /* OS/2 1.x version */
+ bmih.width = r16(b);
+ bmih.height = r16(b);
+ bmih.planes = r16(b);
+ bmih.bpp = r16(b);
+ bmih.compression = BMP_RGB;
+ } else { /* Windows */
+ bmih.width = r32(b);
+ bmih.height = r32(b);
+ bmih.planes = r16(b);
+ bmih.bpp = r16(b);
+ bmih.compression = r32(b);
+ bmih.imagesize = r32(b);
+ bmih.hres = r32(b);
+ bmih.vres = r32(b);
+ bmih.colours = r32(b);
+ bmih.impcolours = r32(b);
+ }
+
+ if(bmih.bpp < 16) {
+ /* load colour table */
+ if(bmih.impcolours)
+ num_coltab = (int)bmih.impcolours;
+ else
+ num_coltab = 1 << bmih.bpp;
+ } else if(bmih.compression == BMP_BITFIELDS &&
+ (bmih.bpp == 16 || bmih.bpp == 32))
+ /* load bitmasks */
+ num_coltab = 3;
+
+ if(num_coltab) {
+ int i;
+ Bseek(b, bmih.size + sizeof(Infohdr), 0);
+
+ for(i = 0; i < num_coltab; i++) {
+ clut[i].blue = (uchar)Bgetc(b);
+ clut[i].green = (uchar)Bgetc(b);
+ clut[i].red = (uchar)Bgetc(b);
+ clut[i].alpha = (uchar)Bgetc(b);
+ }
+ }
+
+ *width = bmih.width;
+ *height = bmih.height;
+ colours = bmih.bpp;
+
+ Bseek(b, bmfh.offbits, 0);
+
+ if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil)
+ sysfatal("no memory");
+
+ switch(colours) {
+ case 1:
+ load_1T(b, *width, *height, buf, clut);
+ break;
+ case 4:
+ if(bmih.compression == BMP_RLE4)
+ load_4C(b, *width, *height, buf, clut);
+ else
+ load_4T(b, *width, *height, buf, clut);
+ break;
+ case 8:
+ if(bmih.compression == BMP_RLE8)
+ load_8C(b, *width, *height, buf, clut);
+ else
+ load_8T(b, *width, *height, buf, clut);
+ break;
+ case 16:
+ load_16(b, *width, *height, buf,
+ bmih.compression == BMP_BITFIELDS ? clut : nil);
+ break;
+ case 24:
+ load_24T(b, *width, *height, buf);
+ break;
+ case 32:
+ load_32(b, *width, *height, buf,
+ bmih.compression == BMP_BITFIELDS ? clut : nil);
+ break;
+ }
+ return buf;
+}
+
+Rawimage**
+Breadbmp(Biobuf *bp, int colourspace)
+{
+ Rawimage *a, **array;
+ int c, width, height;
+ uchar *r, *g, *b;
+ Rgb *s, *e;
+ Rgb *bmp;
+ char ebuf[128];
+
+ a = nil;
+ bmp = nil;
+ array = nil;
+ USED(a);
+ USED(bmp);
+ if (colourspace != CRGB) {
+ errstr(ebuf, sizeof ebuf); /* throw it away */
+ werrstr("ReadRGB: unknown colour space %d", colourspace);
+ return nil;
+ }
+
+ if ((bmp = ReadBMP(bp, &width, &height)) == nil)
+ return nil;
+
+ if ((a = calloc(sizeof(Rawimage), 1)) == nil)
+ goto Error;
+
+ for (c = 0; c < 3; c++)
+ if ((a->chans[c] = calloc(width, height)) == nil)
+ goto Error;
+
+ if ((array = calloc(sizeof(Rawimage *), 2)) == nil)
+ goto Error;
+ array[0] = a;
+ array[1] = nil;
+
+ a->nchans = 3;
+ a->chandesc = CRGB;
+ a->chanlen = width * height;
+ a->r = Rect(0, 0, width, height);
+
+ s = bmp;
+ e = s + width * height;
+ r = a->chans[0];
+ g = a->chans[1];
+ b = a->chans[2];
+
+ do {
+ *r++ = s->red;
+ *g++ = s->green;
+ *b++ = s->blue;
+ }while(++s < e);
+
+ free(bmp);
+ return array;
+
+Error:
+ if (a)
+ for (c = 0; c < 3; c++)
+ if (a->chans[c])
+ free(a->chans[c]);
+ if (a)
+ free(a);
+ if (array)
+ free(array);
+ if (bmp)
+ free(bmp);
+ return nil;
+
+}
+
+Rawimage**
+readbmp(int fd, int colorspace)
+{
+ Rawimage * *a;
+ Biobuf b;
+
+ if (Binit(&b, fd, OREAD) < 0)
+ return nil;
+ a = Breadbmp(&b, colorspace);
+ Bterm(&b);
+ return a;
+}
+
+
diff --git a/src/cmd/jpg/readgif.c b/src/cmd/jpg/readgif.c
new file mode 100644
index 00000000..22a0b68c
--- /dev/null
+++ b/src/cmd/jpg/readgif.c
@@ -0,0 +1,535 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+typedef struct Entry Entry;
+typedef struct Header Header;
+
+struct Entry{
+ int prefix;
+ int exten;
+};
+
+
+struct Header{
+ Biobuf *fd;
+ char err[256];
+ jmp_buf errlab;
+ uchar buf[3*256];
+ char vers[8];
+ uchar *globalcmap;
+ int screenw;
+ int screenh;
+ int fields;
+ int bgrnd;
+ int aspect;
+ int flags;
+ int delay;
+ int trindex;
+ int loopcount;
+ Entry tbl[4096];
+ Rawimage **array;
+ Rawimage *new;
+
+ uchar *pic;
+};
+
+static char readerr[] = "ReadGIF: read error: %r";
+static char extreaderr[] = "ReadGIF: can't read extension: %r";
+static char memerr[] = "ReadGIF: malloc failed: %r";
+
+static Rawimage** readarray(Header*);
+static Rawimage* readone(Header*);
+static void readheader(Header*);
+static void skipextension(Header*);
+static uchar* readcmap(Header*, int);
+static uchar* decode(Header*, Rawimage*, Entry*);
+static void interlace(Header*, Rawimage*);
+
+static
+void
+clear(void *pp)
+{
+ void **p = (void**)pp;
+
+ if(*p){
+ free(*p);
+ *p = nil;
+ }
+}
+
+static
+void
+giffreeall(Header *h, int freeimage)
+{
+ int i;
+
+ if(h->fd){
+ Bterm(h->fd);
+ h->fd = nil;
+ }
+ clear(&h->pic);
+ if(h->new){
+ clear(&h->new->cmap);
+ clear(&h->new->chans[0]);
+ clear(&h->new);
+ }
+ clear(&h->globalcmap);
+ if(freeimage && h->array!=nil){
+ for(i=0; h->array[i]; i++){
+ clear(&h->array[i]->cmap);
+ clear(&h->array[i]->chans[0]);
+ }
+ clear(&h->array);
+ }
+}
+
+static
+void
+giferror(Header *h, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(h->err, h->err+sizeof h->err, fmt, arg);
+ va_end(arg);
+
+ werrstr(h->err);
+ giffreeall(h, 1);
+ longjmp(h->errlab, 1);
+}
+
+
+Rawimage**
+readgif(int fd, int colorspace)
+{
+ Rawimage **a;
+ Biobuf b;
+ Header *h;
+ char buf[ERRMAX];
+
+ buf[0] = '\0';
+ USED(colorspace);
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ h = malloc(sizeof(Header));
+ if(h == nil){
+ Bterm(&b);
+ return nil;
+ }
+ memset(h, 0, sizeof(Header));
+ h->fd = &b;
+ errstr(buf, sizeof buf); /* throw it away */
+ if(setjmp(h->errlab))
+ a = nil;
+ else
+ a = readarray(h);
+ giffreeall(h, 0);
+ free(h);
+ return a;
+}
+
+static
+void
+inittbl(Header *h)
+{
+ int i;
+ Entry *tbl;
+
+ tbl = h->tbl;
+ for(i=0; i<258; i++) {
+ tbl[i].prefix = -1;
+ tbl[i].exten = i;
+ }
+}
+
+static
+Rawimage**
+readarray(Header *h)
+{
+ Entry *tbl;
+ Rawimage *new, **array;
+ int c, nimages;
+
+ tbl = h->tbl;
+
+ readheader(h);
+
+ if(h->fields & 0x80)
+ h->globalcmap = readcmap(h, (h->fields&7)+1);
+
+ array = malloc(sizeof(Rawimage**));
+ if(array == nil)
+ giferror(h, memerr);
+ nimages = 0;
+ array[0] = nil;
+ h->array = array;
+
+ for(;;){
+ switch(c = Bgetc(h->fd)){
+ case Beof:
+ goto Return;
+
+ case 0x21: /* Extension (ignored) */
+ skipextension(h);
+ break;
+
+ case 0x2C: /* Image Descriptor */
+ inittbl(h);
+ new = readone(h);
+ if(new->fields & 0x80){
+ new->cmaplen = 3*(1<<((new->fields&7)+1));
+ new->cmap = readcmap(h, (new->fields&7)+1);
+ }else{
+ new->cmaplen = 3*(1<<((h->fields&7)+1));
+ new->cmap = malloc(new->cmaplen);
+ memmove(new->cmap, h->globalcmap, new->cmaplen);
+ }
+ h->new = new;
+ new->chans[0] = decode(h, new, tbl);
+ if(new->fields & 0x40)
+ interlace(h, new);
+ new->gifflags = h->flags;
+ new->gifdelay = h->delay;
+ new->giftrindex = h->trindex;
+ new->gifloopcount = h->loopcount;
+ array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
+ if(array == nil)
+ giferror(h, memerr);
+ array[nimages++] = new;
+ array[nimages] = nil;
+ h->array = array;
+ h->new = nil;
+ break;
+
+ case 0x3B: /* Trailer */
+ goto Return;
+
+ default:
+ fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
+ goto Return;
+ }
+ }
+
+ Return:
+ if(array[0]==nil || array[0]->chans[0] == nil)
+ giferror(h, "ReadGIF: no picture in file");
+
+ return array;
+}
+
+static
+void
+readheader(Header *h)
+{
+ if(Bread(h->fd, h->buf, 13) != 13)
+ giferror(h, "ReadGIF: can't read header: %r");
+ memmove(h->vers, h->buf, 6);
+ if(strcmp(h->vers, "GIF87a")!=0 && strcmp(h->vers, "GIF89a")!=0)
+ giferror(h, "ReadGIF: can't recognize format %s", h->vers);
+ h->screenw = h->buf[6]+(h->buf[7]<<8);
+ h->screenh = h->buf[8]+(h->buf[9]<<8);
+ h->fields = h->buf[10];
+ h->bgrnd = h->buf[11];
+ h->aspect = h->buf[12];
+ h->flags = 0;
+ h->delay = 0;
+ h->trindex = 0;
+ h->loopcount = -1;
+}
+
+static
+uchar*
+readcmap(Header *h, int size)
+{
+ uchar *map;
+
+ if(size > 8)
+ giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
+ size = 3*(1<<size);
+ if(Bread(h->fd, h->buf, size) != size)
+ giferror(h, "ReadGIF: short read on color map");
+ map = malloc(size);
+ if(map == nil)
+ giferror(h, memerr);
+ memmove(map, h->buf, size);
+ return map;
+}
+
+static
+Rawimage*
+readone(Header *h)
+{
+ Rawimage *i;
+ int left, top, width, height;
+
+ if(Bread(h->fd, h->buf, 9) != 9)
+ giferror(h, "ReadGIF: can't read image descriptor: %r");
+ i = malloc(sizeof(Rawimage));
+ if(i == nil)
+ giferror(h, memerr);
+ left = h->buf[0]+(h->buf[1]<<8);
+ top = h->buf[2]+(h->buf[3]<<8);
+ width = h->buf[4]+(h->buf[5]<<8);
+ height = h->buf[6]+(h->buf[7]<<8);
+ i->fields = h->buf[8];
+ i->r.min.x = left;
+ i->r.min.y = top;
+ i->r.max.x = left+width;
+ i->r.max.y = top+height;
+ i->nchans = 1;
+ i->chandesc = CRGB1;
+ return i;
+}
+
+
+static
+int
+readdata(Header *h, uchar *data)
+{
+ int nbytes, n;
+
+ nbytes = Bgetc(h->fd);
+ if(nbytes < 0)
+ giferror(h, "ReadGIF: can't read data: %r");
+ if(nbytes == 0)
+ return 0;
+ n = Bread(h->fd, data, nbytes);
+ if(n < 0)
+ giferror(h, "ReadGIF: can't read data: %r");
+ if(n != nbytes)
+ fprint(2, "ReadGIF: short data subblock\n");
+ return n;
+}
+
+static
+void
+graphiccontrol(Header *h)
+{
+ if(Bread(h->fd, h->buf, 5+1) != 5+1)
+ giferror(h, readerr);
+ h->flags = h->buf[1];
+ h->delay = h->buf[2]+(h->buf[3]<<8);
+ h->trindex = h->buf[4];
+}
+
+static
+void
+skipextension(Header *h)
+{
+ int type, hsize, hasdata, n;
+ uchar data[256];
+
+ hsize = 0;
+ hasdata = 0;
+
+ type = Bgetc(h->fd);
+ switch(type){
+ case Beof:
+ giferror(h, extreaderr);
+ break;
+ case 0x01: /* Plain Text Extension */
+ hsize = 13;
+ hasdata = 1;
+ break;
+ case 0xF9: /* Graphic Control Extension */
+ graphiccontrol(h);
+ return;
+ case 0xFE: /* Comment Extension */
+ hasdata = 1;
+ break;
+ case 0xFF: /* Application Extension */
+ hsize = Bgetc(h->fd);
+ /* standard says this must be 11, but Adobe likes to put out 10-byte ones,
+ * so we pay attention to the field. */
+ hasdata = 1;
+ break;
+ default:
+ giferror(h, "ReadGIF: unknown extension");
+ }
+ if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
+ giferror(h, extreaderr);
+ if(!hasdata){
+ if(h->buf[hsize-1] != 0)
+ giferror(h, "ReadGIF: bad extension format");
+ return;
+ }
+
+ /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
+ if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
+ n = readdata(h, data);
+ if(n == 0)
+ return;
+ if(n==3 && data[0]==1)
+ h->loopcount = data[1] | (data[2]<<8);
+ }
+ while(readdata(h, data) != 0)
+ ;
+}
+
+static
+uchar*
+decode(Header *h, Rawimage *i, Entry *tbl)
+{
+ int c, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
+ int csize, nentry, maxentry, first, ocode, ndata, nb;
+ uchar *pic;
+ uchar stack[4096], data[256];
+
+ if(Bread(h->fd, h->buf, 1) != 1)
+ giferror(h, "ReadGIF: can't read data: %r");
+ codesize = h->buf[0];
+ if(codesize>8 || 0>codesize)
+ giferror(h, "ReadGIF: can't handle codesize %d", codesize);
+ if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
+ && (codesize!=2 || i->cmaplen!=3*2)) /* peculiar GIF bitmap files... */
+ giferror(h, "ReadGIF: codesize %d doesn't match color map 3*%d", codesize, i->cmaplen/3);
+
+ CTM =1<<codesize;
+ EOD = CTM+1;
+
+ piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
+ i->chanlen = piclen;
+ pic = malloc(piclen);
+ if(pic == nil)
+ giferror(h, memerr);
+ h->pic = pic;
+ pici = 0;
+ ndata = 0;
+ datai = 0;
+ nbits = 0;
+ sreg = 0;
+ fc = 0;
+
+ Loop:
+ for(;;){
+ csize = codesize+1;
+ nentry = EOD+1;
+ maxentry = (1<<csize)-1;
+ first = 1;
+ ocode = -1;
+
+ for(;; ocode = incode) {
+ while(nbits < csize) {
+ if(datai == ndata){
+ ndata = readdata(h, data);
+ if(ndata == 0)
+ goto Return;
+ datai = 0;
+ }
+ c = data[datai++];
+ sreg |= c<<nbits;
+ nbits += 8;
+ }
+ code = sreg & ((1<<csize) - 1);
+ sreg >>= csize;
+ nbits -= csize;
+
+ if(code == EOD){
+ ndata = readdata(h, data);
+ if(ndata != 0)
+ fprint(2, "ReadGIF: unexpected data past EOD");
+ goto Return;
+ }
+
+ if(code == CTM)
+ goto Loop;
+
+ stacki = (sizeof stack)-1;
+
+ incode = code;
+
+ /* special case for KwKwK */
+ if(code == nentry) {
+ stack[stacki--] = fc;
+ code = ocode;
+ }
+
+ if(code > nentry)
+ giferror(h, "ReadGIF: bad code %x %x", code, nentry);
+
+ for(c=code; c>=0; c=tbl[c].prefix)
+ stack[stacki--] = tbl[c].exten;
+
+ nb = (sizeof stack)-(stacki+1);
+ if(pici+nb > piclen){
+ /* this common error is harmless
+ * we have to keep reading to keep the blocks in sync */
+ ;
+ }else{
+ memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
+ pici += nb;
+ }
+
+ fc = stack[stacki+1];
+
+ if(first){
+ first = 0;
+ continue;
+ }
+ #define early 0 /* peculiar tiff feature here for reference */
+ if(nentry == maxentry-early) {
+ if(csize >= 12)
+ continue;
+ csize++;
+ maxentry = (1<<csize);
+ if(csize < 12)
+ maxentry--;
+ }
+ tbl[nentry].prefix = ocode;
+ tbl[nentry].exten = fc;
+ nentry++;
+ }
+ }
+
+Return:
+ h->pic = nil;
+ return pic;
+}
+
+static
+void
+interlace(Header *h, Rawimage *image)
+{
+ uchar *pic;
+ Rectangle r;
+ int dx, yy, y;
+ uchar *ipic;
+
+ pic = image->chans[0];
+ r = image->r;
+ dx = r.max.x-r.min.x;
+ ipic = malloc(dx*(r.max.y-r.min.y));
+ if(ipic == nil)
+ giferror(h, nil);
+
+ /* Group 1: every 8th row, starting with row 0 */
+ yy = 0;
+ for(y=r.min.y; y<r.max.y; y+=8){
+ memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
+ yy++;
+ }
+
+ /* Group 2: every 8th row, starting with row 4 */
+ for(y=r.min.y+4; y<r.max.y; y+=8){
+ memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
+ yy++;
+ }
+
+ /* Group 3: every 4th row, starting with row 2 */
+ for(y=r.min.y+2; y<r.max.y; y+=4){
+ memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
+ yy++;
+ }
+
+ /* Group 4: every 2nd row, starting with row 1 */
+ for(y=r.min.y+1; y<r.max.y; y+=2){
+ memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
+ yy++;
+ }
+
+ free(image->chans[0]);
+ image->chans[0] = ipic;
+}
diff --git a/src/cmd/jpg/readjpg.c b/src/cmd/jpg/readjpg.c
new file mode 100644
index 00000000..b154e75b
--- /dev/null
+++ b/src/cmd/jpg/readjpg.c
@@ -0,0 +1,1661 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+enum {
+ /* Constants, all preceded by byte 0xFF */
+ SOF =0xC0, /* Start of Frame */
+ SOF2=0xC2, /* Start of Frame; progressive Huffman */
+ JPG =0xC8, /* Reserved for JPEG extensions */
+ DHT =0xC4, /* Define Huffman Tables */
+ DAC =0xCC, /* Arithmetic coding conditioning */
+ RST =0xD0, /* Restart interval termination */
+ RST7 =0xD7, /* Restart interval termination (highest value) */
+ SOI =0xD8, /* Start of Image */
+ EOI =0xD9, /* End of Image */
+ SOS =0xDA, /* Start of Scan */
+ DQT =0xDB, /* Define quantization tables */
+ DNL =0xDC, /* Define number of lines */
+ DRI =0xDD, /* Define restart interval */
+ DHP =0xDE, /* Define hierarchical progression */
+ EXP =0xDF, /* Expand reference components */
+ APPn =0xE0, /* Reserved for application segments */
+ JPGn =0xF0, /* Reserved for JPEG extensions */
+ COM =0xFE, /* Comment */
+
+ CLAMPOFF = 300,
+ NCLAMP = CLAMPOFF+700
+};
+
+typedef struct Framecomp Framecomp;
+typedef struct Header Header;
+typedef struct Huffman Huffman;
+
+struct Framecomp /* Frame component specifier from SOF marker */
+{
+ int C;
+ int H;
+ int V;
+ int Tq;
+};
+
+struct Huffman
+{
+ int *size; /* malloc'ed */
+ int *code; /* malloc'ed */
+ int *val; /* malloc'ed */
+ int mincode[17];
+ int maxcode[17];
+ int valptr[17];
+ /* fast lookup */
+ int value[256];
+ int shift[256];
+};
+
+
+struct Header
+{
+ Biobuf *fd;
+ char err[256];
+ jmp_buf errlab;
+ /* variables in i/o routines */
+ int sr; /* shift register, right aligned */
+ int cnt; /* # bits in right part of sr */
+ uchar *buf;
+ int nbuf;
+ int peek;
+
+ int Nf;
+
+ Framecomp comp[3];
+ uchar mode;
+ int X;
+ int Y;
+ int qt[4][64]; /* quantization tables */
+ Huffman dcht[4];
+ Huffman acht[4];
+ int **data[3];
+ int ndata[3];
+
+ uchar *sf; /* start of frame; do better later */
+ uchar *ss; /* start of scan; do better later */
+ int ri; /* restart interval */
+
+ /* progressive scan */
+ Rawimage *image;
+ Rawimage **array;
+ int *dccoeff[3];
+ int **accoeff[3]; /* only need 8 bits plus quantization */
+ int naccoeff[3];
+ int nblock[3];
+ int nacross;
+ int ndown;
+ int Hmax;
+ int Vmax;
+};
+
+static uchar clamp[NCLAMP];
+
+static Rawimage *readslave(Header*, int);
+static int readsegment(Header*, int*);
+static void quanttables(Header*, uchar*, int);
+static void huffmantables(Header*, uchar*, int);
+static void soiheader(Header*);
+static int nextbyte(Header*, int);
+static int int2(uchar*, int);
+static void nibbles(int, int*, int*);
+static int receive(Header*, int);
+static int receiveEOB(Header*, int);
+static int receivebit(Header*);
+static void restart(Header*, int);
+static int decode(Header*, Huffman*);
+static Rawimage* baselinescan(Header*, int);
+static void progressivescan(Header*, int);
+static Rawimage* progressiveIDCT(Header*, int);
+static void idct(int*);
+static void colormap1(Header*, int, Rawimage*, int*, int, int);
+static void colormapall1(Header*, int, Rawimage*, int*, int*, int*, int, int);
+static void colormap(Header*, int, Rawimage*, int**, int**, int**, int, int, int, int, int*, int*);
+static void jpgerror(Header*, char*, ...);
+
+static char readerr[] = "ReadJPG: read error: %r";
+static char memerr[] = "ReadJPG: malloc failed: %r";
+
+static int zig[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, /* 0-7 */
+ 24, 32, 25, 18, 11, 4, 5, /* 8-15 */
+ 12, 19, 26, 33, 40, 48, 41, 34, /* 16-23 */
+ 27, 20, 13, 6, 7, 14, 21, 28, /* 24-31 */
+ 35, 42, 49, 56, 57, 50, 43, 36, /* 32-39 */
+ 29, 22, 15, 23, 30, 37, 44, 51, /* 40-47 */
+ 58, 59, 52, 45, 38, 31, 39, 46, /* 48-55 */
+ 53, 60, 61, 54, 47, 55, 62, 63 /* 56-63 */
+};
+
+static
+void
+jpginit(void)
+{
+ int k;
+ static int inited;
+
+ if(inited)
+ return;
+ inited = 1;
+ for(k=0; k<CLAMPOFF; k++)
+ clamp[k] = 0;
+ for(; k<CLAMPOFF+256; k++)
+ clamp[k] = k-CLAMPOFF;
+ for(; k<NCLAMP; k++)
+ clamp[k] = 255;
+}
+
+static
+void*
+jpgmalloc(Header *h, int n, int clear)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ jpgerror(h, memerr);
+ if(clear)
+ memset(p, 0, n);
+ return p;
+}
+
+static
+void
+clear(void *pp)
+{
+ void **p = (void**)pp;
+
+ if(*p){
+ free(*p);
+ *p = nil;
+ }
+}
+
+static
+void
+jpgfreeall(Header *h, int freeimage)
+{
+ int i, j;
+
+ clear(&h->buf);
+ if(h->dccoeff[0])
+ for(i=0; i<3; i++)
+ clear(&h->dccoeff[i]);
+ if(h->accoeff[0])
+ for(i=0; i<3; i++){
+ if(h->accoeff[i])
+ for(j=0; j<h->naccoeff[i]; j++)
+ clear(&h->accoeff[i][j]);
+ clear(&h->accoeff[i]);
+ }
+ for(i=0; i<4; i++){
+ clear(&h->dcht[i].size);
+ clear(&h->acht[i].size);
+ clear(&h->dcht[i].code);
+ clear(&h->acht[i].code);
+ clear(&h->dcht[i].val);
+ clear(&h->acht[i].val);
+ }
+ if(h->data[0])
+ for(i=0; i<3; i++){
+ if(h->data[i])
+ for(j=0; j<h->ndata[i]; j++)
+ clear(&h->data[i][j]);
+ clear(&h->data[i]);
+ }
+ if(freeimage && h->image!=nil){
+ clear(&h->array);
+ clear(&h->image->cmap);
+ for(i=0; i<3; i++)
+ clear(&h->image->chans[i]);
+ clear(&h->image);
+ }
+}
+
+static
+void
+jpgerror(Header *h, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(h->err, h->err+sizeof h->err, fmt, arg);
+ va_end(arg);
+
+ werrstr(h->err);
+ jpgfreeall(h, 1);
+ longjmp(h->errlab, 1);
+}
+
+Rawimage**
+Breadjpg(Biobuf *b, int colorspace)
+{
+ Rawimage *r, **array;
+ Header *h;
+ char buf[ERRMAX];
+
+ buf[0] = '\0';
+ if(colorspace!=CYCbCr && colorspace!=CRGB){
+ errstr(buf, sizeof buf); /* throw it away */
+ werrstr("ReadJPG: unknown color space");
+ return nil;
+ }
+ jpginit();
+ h = malloc(sizeof(Header));
+ array = malloc(sizeof(Header));
+ if(h==nil || array==nil){
+ free(h);
+ free(array);
+ return nil;
+ }
+ h->array = array;
+ memset(h, 0, sizeof(Header));
+ h->fd = b;
+ errstr(buf, sizeof buf); /* throw it away */
+ if(setjmp(h->errlab))
+ r = nil;
+ else
+ r = readslave(h, colorspace);
+ jpgfreeall(h, 0);
+ free(h);
+ array[0] = r;
+ array[1] = nil;
+ return array;
+}
+
+Rawimage**
+readjpg(int fd, int colorspace)
+{
+ Rawimage** a;
+ Biobuf b;
+
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ a = Breadjpg(&b, colorspace);
+ Bterm(&b);
+ return a;
+}
+
+static
+Rawimage*
+readslave(Header *header, int colorspace)
+{
+ Rawimage *image;
+ int nseg, i, H, V, m, n;
+ uchar *b;
+
+ soiheader(header);
+ nseg = 0;
+ image = nil;
+
+ header->buf = jpgmalloc(header, 4096, 0);
+ header->nbuf = 4096;
+ while(header->err[0] == '\0'){
+ nseg++;
+ n = readsegment(header, &m);
+ b = header->buf;
+ switch(m){
+ case -1:
+ return image;
+
+ case APPn+0:
+ if(nseg==1 && strncmp((char*)b, "JFIF", 4)==0) /* JFIF header; check version */
+ if(b[5]>1 || b[6]>2)
+ sprint(header->err, "ReadJPG: can't handle JFIF version %d.%2d", b[5], b[6]);
+ break;
+
+ case APPn+1: case APPn+2: case APPn+3: case APPn+4: case APPn+5:
+ case APPn+6: case APPn+7: case APPn+8: case APPn+9: case APPn+10:
+ case APPn+11: case APPn+12: case APPn+13: case APPn+14: case APPn+15:
+ break;
+
+ case DQT:
+ quanttables(header, b, n);
+ break;
+
+ case SOF:
+ case SOF2:
+ header->Y = int2(b, 1);
+ header->X = int2(b, 3);
+ header->Nf =b[5];
+ for(i=0; i<header->Nf; i++){
+ header->comp[i].C = b[6+3*i+0];
+ nibbles(b[6+3*i+1], &H, &V);
+ if(H<=0 || V<=0)
+ jpgerror(header, "non-positive sampling factor (Hsamp or Vsamp)");
+ header->comp[i].H = H;
+ header->comp[i].V = V;
+ header->comp[i].Tq = b[6+3*i+2];
+ }
+ header->mode = m;
+ header->sf = b;
+ break;
+
+ case SOS:
+ header->ss = b;
+ switch(header->mode){
+ case SOF:
+ image = baselinescan(header, colorspace);
+ break;
+ case SOF2:
+ progressivescan(header, colorspace);
+ break;
+ default:
+ sprint(header->err, "unrecognized or unspecified encoding %d", header->mode);
+ break;
+ }
+ break;
+
+ case DHT:
+ huffmantables(header, b, n);
+ break;
+
+ case DRI:
+ header->ri = int2(b, 0);
+ break;
+
+ case COM:
+ break;
+
+ case EOI:
+ if(header->mode == SOF2)
+ image = progressiveIDCT(header, colorspace);
+ return image;
+
+ default:
+ sprint(header->err, "ReadJPG: unknown marker %.2x", m);
+ break;
+ }
+ }
+ return image;
+}
+
+/* readsegment is called after reading scan, which can have */
+/* read ahead a byte. so we must check peek here */
+static
+int
+readbyte(Header *h)
+{
+ uchar x;
+
+ if(h->peek >= 0){
+ x = h->peek;
+ h->peek = -1;
+ }else if(Bread(h->fd, &x, 1) != 1)
+ jpgerror(h, readerr);
+ return x;
+}
+
+static
+int
+marker(Header *h)
+{
+ int c;
+
+ while((c=readbyte(h)) == 0)
+ fprint(2, "ReadJPG: skipping zero byte at offset %lld\n", Boffset(h->fd));
+ if(c != 0xFF)
+ jpgerror(h, "ReadJPG: expecting marker; found 0x%x at offset %lld\n", c, Boffset(h->fd));
+ while(c == 0xFF)
+ c = readbyte(h);
+ return c;
+}
+
+static
+int
+int2(uchar *buf, int n)
+{
+ return (buf[n]<<8) + buf[n+1];
+}
+
+static
+void
+nibbles(int b, int *p0, int *p1)
+{
+ *p0 = (b>>4) & 0xF;
+ *p1 = b & 0xF;
+}
+
+static
+void
+soiheader(Header *h)
+{
+ h->peek = -1;
+ if(marker(h) != SOI)
+ jpgerror(h, "ReadJPG: unrecognized marker in header");
+ h->err[0] = '\0';
+ h->mode = 0;
+ h->ri = 0;
+}
+
+static
+int
+readsegment(Header *h, int *markerp)
+{
+ int m, n;
+ uchar tmp[2];
+
+ m = marker(h);
+ switch(m){
+ case EOI:
+ *markerp = m;
+ return 0;
+ case 0:
+ jpgerror(h, "ReadJPG: expecting marker; saw %.2x at offset %lld", m, Boffset(h->fd));
+ }
+ if(Bread(h->fd, tmp, 2) != 2)
+ Readerr:
+ jpgerror(h, readerr);
+ n = int2(tmp, 0);
+ if(n < 2)
+ goto Readerr;
+ n -= 2;
+ if(n > h->nbuf){
+ free(h->buf);
+ h->buf = jpgmalloc(h, n+1, 0); /* +1 for sentinel */
+ h->nbuf = n;
+ }
+ if(Bread(h->fd, h->buf, n) != n)
+ goto Readerr;
+ *markerp = m;
+ return n;
+}
+
+static
+int
+huffmantable(Header *h, uchar *b)
+{
+ Huffman *t;
+ int Tc, th, n, nsize, i, j, k, v, cnt, code, si, sr, m;
+ int *maxcode;
+
+ nibbles(b[0], &Tc, &th);
+ if(Tc > 1)
+ jpgerror(h, "ReadJPG: unknown Huffman table class %d", Tc);
+ if(th>3 || (h->mode==SOF && th>1))
+ jpgerror(h, "ReadJPG: unknown Huffman table index %d", th);
+ if(Tc == 0)
+ t = &h->dcht[th];
+ else
+ t = &h->acht[th];
+
+ /* flow chart C-2 */
+ nsize = 0;
+ for(i=0; i<16; i++)
+ nsize += b[1+i];
+ t->size = jpgmalloc(h, (nsize+1)*sizeof(int), 1);
+ k = 0;
+ for(i=1; i<=16; i++){
+ n = b[i];
+ for(j=0; j<n; j++)
+ t->size[k++] = i;
+ }
+ t->size[k] = 0;
+
+ /* initialize HUFFVAL */
+ t->val = jpgmalloc(h, nsize*sizeof(int), 1);
+ for(i=0; i<nsize; i++)
+ t->val[i] = b[17+i];
+
+ /* flow chart C-3 */
+ t->code = jpgmalloc(h, (nsize+1)*sizeof(int), 1);
+ k = 0;
+ code = 0;
+ si = t->size[0];
+ for(;;){
+ do
+ t->code[k++] = code++;
+ while(t->size[k] == si);
+ if(t->size[k] == 0)
+ break;
+ do{
+ code <<= 1;
+ si++;
+ }while(t->size[k] != si);
+ }
+
+ /* flow chart F-25 */
+ i = 0;
+ j = 0;
+ for(;;){
+ for(;;){
+ i++;
+ if(i > 16)
+ goto outF25;
+ if(b[i] != 0)
+ break;
+ t->maxcode[i] = -1;
+ }
+ t->valptr[i] = j;
+ t->mincode[i] = t->code[j];
+ j += b[i]-1;
+ t->maxcode[i] = t->code[j];
+ j++;
+ }
+outF25:
+
+ /* create byte-indexed fast path tables */
+ maxcode = t->maxcode;
+ /* stupid startup algorithm: just run machine for each byte value */
+ for(v=0; v<256; ){
+ cnt = 7;
+ m = 1<<7;
+ code = 0;
+ sr = v;
+ i = 1;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ t->shift[v] = 0;
+ t->value[v] = -1;
+ goto continueBytes;
+ }
+ cnt--;
+ }
+ t->shift[v] = 8-cnt;
+ t->value[v] = t->val[t->valptr[i]+(code-t->mincode[i])];
+
+ continueBytes:
+ v++;
+ }
+
+ return nsize;
+}
+
+static
+void
+huffmantables(Header *h, uchar *b, int n)
+{
+ int l, mt;
+
+ for(l=0; l<n; l+=17+mt)
+ mt = huffmantable(h, &b[l]);
+}
+
+static
+int
+quanttable(Header *h, uchar *b)
+{
+ int i, pq, tq, *q;
+
+ nibbles(b[0], &pq, &tq);
+ if(pq > 1)
+ jpgerror(h, "ReadJPG: unknown quantization table class %d", pq);
+ if(tq > 3)
+ jpgerror(h, "ReadJPG: unknown quantization table index %d", tq);
+ q = h->qt[tq];
+ for(i=0; i<64; i++){
+ if(pq == 0)
+ q[i] = b[1+i];
+ else
+ q[i] = int2(b, 1+2*i);
+ }
+ return 64*(1+pq);
+}
+
+static
+void
+quanttables(Header *h, uchar *b, int n)
+{
+ int l, m;
+
+ for(l=0; l<n; l+=1+m)
+ m = quanttable(h, &b[l]);
+}
+
+static
+Rawimage*
+baselinescan(Header *h, int colorspace)
+{
+ int Ns, z, k, m, Hmax, Vmax, comp;
+ int allHV1, nblock, ri, mcu, nacross, nmcu;
+ Huffman *dcht, *acht;
+ int block, t, diff, *qt;
+ uchar *ss;
+ Rawimage *image;
+ int Td[3], Ta[3], H[3], V[3], DC[3];
+ int ***data, *zz;
+
+ ss = h->ss;
+ Ns = ss[0];
+ if((Ns!=3 && Ns!=1) || Ns!=h->Nf)
+ jpgerror(h, "ReadJPG: can't handle scan not 3 components");
+
+ image = jpgmalloc(h, sizeof(Rawimage), 1);
+ h->image = image;
+ image->r = Rect(0, 0, h->X, h->Y);
+ image->cmap = nil;
+ image->cmaplen = 0;
+ image->chanlen = h->X*h->Y;
+ image->fields = 0;
+ image->gifflags = 0;
+ image->gifdelay = 0;
+ image->giftrindex = 0;
+ if(Ns == 3)
+ image->chandesc = colorspace;
+ else
+ image->chandesc = CY;
+ image->nchans = h->Nf;
+ for(k=0; k<h->Nf; k++)
+ image->chans[k] = jpgmalloc(h, h->X*h->Y, 0);
+
+ /* compute maximum H and V */
+ Hmax = 0;
+ Vmax = 0;
+ for(comp=0; comp<Ns; comp++){
+ if(h->comp[comp].H > Hmax)
+ Hmax = h->comp[comp].H;
+ if(h->comp[comp].V > Vmax)
+ Vmax = h->comp[comp].V;
+ }
+
+ /* initialize data structures */
+ allHV1 = 1;
+ data = h->data;
+ for(comp=0; comp<Ns; comp++){
+ /* JPEG requires scan components to be in same order as in frame, */
+ /* so if both have 3 we know scan is Y Cb Cr and there's no need to */
+ /* reorder */
+ nibbles(ss[2+2*comp], &Td[comp], &Ta[comp]);
+ H[comp] = h->comp[comp].H;
+ V[comp] = h->comp[comp].V;
+ nblock = H[comp]*V[comp];
+ if(nblock != 1)
+ allHV1 = 0;
+ data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0);
+ h->ndata[comp] = nblock;
+ DC[comp] = 0;
+ for(m=0; m<nblock; m++)
+ data[comp][m] = jpgmalloc(h, 8*8*sizeof(int), 0);
+ }
+
+ ri = h->ri;
+
+ h->cnt = 0;
+ h->sr = 0;
+ h->peek = -1;
+ nacross = ((h->X+(8*Hmax-1))/(8*Hmax));
+ nmcu = ((h->Y+(8*Vmax-1))/(8*Vmax))*nacross;
+ for(mcu=0; mcu<nmcu; ){
+ for(comp=0; comp<Ns; comp++){
+ dcht = &h->dcht[Td[comp]];
+ acht = &h->acht[Ta[comp]];
+ qt = h->qt[h->comp[comp].Tq];
+
+ for(block=0; block<H[comp]*V[comp]; block++){
+ /* F-22 */
+ t = decode(h, dcht);
+ diff = receive(h, t);
+ DC[comp] += diff;
+
+ /* F-23 */
+ zz = data[comp][block];
+ memset(zz, 0, 8*8*sizeof(int));
+ zz[0] = qt[0]*DC[comp];
+ k = 1;
+
+ for(;;){
+ t = decode(h, acht);
+ if((t&0x0F) == 0){
+ if((t&0xF0) != 0xF0)
+ break;
+ k += 16;
+ }else{
+ k += t>>4;
+ z = receive(h, t&0xF);
+ zz[zig[k]] = z*qt[k];
+ if(k == 63)
+ break;
+ k++;
+ }
+ }
+
+ idct(zz);
+ }
+ }
+
+ /* rotate colors to RGB and assign to bytes */
+ if(Ns == 1) /* very easy */
+ colormap1(h, colorspace, image, data[0][0], mcu, nacross);
+ else if(allHV1) /* fairly easy */
+ colormapall1(h, colorspace, image, data[0][0], data[1][0], data[2][0], mcu, nacross);
+ else /* miserable general case */
+ colormap(h, colorspace, image, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V);
+ /* process restart marker, if present */
+ mcu++;
+ if(ri>0 && mcu<nmcu && mcu%ri==0){
+ restart(h, mcu);
+ for(comp=0; comp<Ns; comp++)
+ DC[comp] = 0;
+ }
+ }
+ return image;
+}
+
+static
+void
+restart(Header *h, int mcu)
+{
+ int rest, rst, nskip;
+
+ rest = mcu/h->ri-1;
+ nskip = 0;
+ do{
+ do{
+ rst = nextbyte(h, 1);
+ nskip++;
+ }while(rst>=0 && rst!=0xFF);
+ if(rst == 0xFF){
+ rst = nextbyte(h, 1);
+ nskip++;
+ }
+ }while(rst>=0 && (rst&~7)!=RST);
+ if(nskip != 2)
+ sprint(h->err, "ReadJPG: skipped %d bytes at restart %d\n", nskip-2, rest);
+ if(rst < 0)
+ jpgerror(h, readerr);
+ if((rst&7) != (rest&7))
+ jpgerror(h, "ReadJPG: expected RST%d got %d", rest&7, rst&7);
+ h->cnt = 0;
+ h->sr = 0;
+}
+
+static
+Rawimage*
+progressiveIDCT(Header *h, int colorspace)
+{
+ int k, m, comp, block, Nf, bn;
+ int allHV1, nblock, mcu, nmcu;
+ int H[3], V[3], blockno[3];
+ int *dccoeff, **accoeff;
+ int ***data, *zz;
+
+ Nf = h->Nf;
+ allHV1 = 1;
+ data = h->data;
+
+ for(comp=0; comp<Nf; comp++){
+ H[comp] = h->comp[comp].H;
+ V[comp] = h->comp[comp].V;
+ nblock = h->nblock[comp];
+ if(nblock != 1)
+ allHV1 = 0;
+ h->ndata[comp] = nblock;
+ data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0);
+ for(m=0; m<nblock; m++)
+ data[comp][m] = jpgmalloc(h, 8*8*sizeof(int), 0);
+ }
+
+ memset(blockno, 0, sizeof blockno);
+ nmcu = h->nacross*h->ndown;
+ for(mcu=0; mcu<nmcu; mcu++){
+ for(comp=0; comp<Nf; comp++){
+ dccoeff = h->dccoeff[comp];
+ accoeff = h->accoeff[comp];
+ bn = blockno[comp];
+ for(block=0; block<h->nblock[comp]; block++){
+ zz = data[comp][block];
+ memset(zz, 0, 8*8*sizeof(int));
+ zz[0] = dccoeff[bn];
+
+ for(k=1; k<64; k++)
+ zz[zig[k]] = accoeff[bn][k];
+
+ idct(zz);
+ bn++;
+ }
+ blockno[comp] = bn;
+ }
+
+ /* rotate colors to RGB and assign to bytes */
+ if(Nf == 1) /* very easy */
+ colormap1(h, colorspace, h->image, data[0][0], mcu, h->nacross);
+ else if(allHV1) /* fairly easy */
+ colormapall1(h, colorspace, h->image, data[0][0], data[1][0], data[2][0], mcu, h->nacross);
+ else /* miserable general case */
+ colormap(h, colorspace, h->image, data[0], data[1], data[2], mcu, h->nacross, h->Hmax, h->Vmax, H, V);
+ }
+
+ return h->image;
+}
+
+static
+void
+progressiveinit(Header *h, int colorspace)
+{
+ int Nf, Ns, j, k, nmcu, comp;
+ uchar *ss;
+ Rawimage *image;
+
+ ss = h->ss;
+ Ns = ss[0];
+ Nf = h->Nf;
+ if((Ns!=3 && Ns!=1) || Ns!=Nf)
+ jpgerror(h, "ReadJPG: image must have 1 or 3 components");
+
+ image = jpgmalloc(h, sizeof(Rawimage), 1);
+ h->image = image;
+ image->r = Rect(0, 0, h->X, h->Y);
+ image->cmap = nil;
+ image->cmaplen = 0;
+ image->chanlen = h->X*h->Y;
+ image->fields = 0;
+ image->gifflags = 0;
+ image->gifdelay = 0;
+ image->giftrindex = 0;
+ if(Nf == 3)
+ image->chandesc = colorspace;
+ else
+ image->chandesc = CY;
+ image->nchans = h->Nf;
+ for(k=0; k<Nf; k++){
+ image->chans[k] = jpgmalloc(h, h->X*h->Y, 0);
+ h->nblock[k] = h->comp[k].H*h->comp[k].V;
+ }
+
+ /* compute maximum H and V */
+ h->Hmax = 0;
+ h->Vmax = 0;
+ for(comp=0; comp<Nf; comp++){
+ if(h->comp[comp].H > h->Hmax)
+ h->Hmax = h->comp[comp].H;
+ if(h->comp[comp].V > h->Vmax)
+ h->Vmax = h->comp[comp].V;
+ }
+ h->nacross = ((h->X+(8*h->Hmax-1))/(8*h->Hmax));
+ h->ndown = ((h->Y+(8*h->Vmax-1))/(8*h->Vmax));
+ nmcu = h->nacross*h->ndown;
+
+ for(k=0; k<Nf; k++){
+ h->dccoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int), 1);
+ h->accoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int*), 1);
+ h->naccoeff[k] = h->nblock[k]*nmcu;
+ for(j=0; j<h->nblock[k]*nmcu; j++)
+ h->accoeff[k][j] = jpgmalloc(h, 64*sizeof(int), 1);
+ }
+
+}
+
+static
+void
+progressivedc(Header *h, int comp, int Ah, int Al)
+{
+ int Ns, z, ri, mcu, nmcu;
+ int block, t, diff, qt, *dc, bn;
+ Huffman *dcht;
+ uchar *ss;
+ int Td[3], DC[3], blockno[3];
+
+ ss= h->ss;
+ Ns = ss[0];
+ if(Ns!=h->Nf)
+ jpgerror(h, "ReadJPG: can't handle progressive with Nf!=Ns in DC scan");
+
+ /* initialize data structures */
+ h->cnt = 0;
+ h->sr = 0;
+ h->peek = -1;
+ for(comp=0; comp<Ns; comp++){
+ /*
+ * JPEG requires scan components to be in same order as in frame,
+ * so if both have 3 we know scan is Y Cb Cr and there's no need to
+ * reorder
+ */
+ nibbles(ss[2+2*comp], &Td[comp], &z); /* z is ignored */
+ DC[comp] = 0;
+ }
+
+ ri = h->ri;
+
+ nmcu = h->nacross*h->ndown;
+ memset(blockno, 0, sizeof blockno);
+ for(mcu=0; mcu<nmcu; ){
+ for(comp=0; comp<Ns; comp++){
+ dcht = &h->dcht[Td[comp]];
+ qt = h->qt[h->comp[comp].Tq][0];
+ dc = h->dccoeff[comp];
+ bn = blockno[comp];
+
+ for(block=0; block<h->nblock[comp]; block++){
+ if(Ah == 0){
+ t = decode(h, dcht);
+ diff = receive(h, t);
+ DC[comp] += diff;
+ dc[bn] = qt*DC[comp]<<Al;
+ }else
+ dc[bn] |= qt*receivebit(h)<<Al;
+ bn++;
+ }
+ blockno[comp] = bn;
+ }
+
+ /* process restart marker, if present */
+ mcu++;
+ if(ri>0 && mcu<nmcu && mcu%ri==0){
+ restart(h, mcu);
+ for(comp=0; comp<Ns; comp++)
+ DC[comp] = 0;
+ }
+ }
+}
+
+static
+void
+progressiveac(Header *h, int comp, int Al)
+{
+ int Ns, Ss, Se, z, k, eobrun, x, y, nver, tmcu, blockno, *acc, rs;
+ int ri, mcu, nacross, ndown, nmcu, nhor;
+ Huffman *acht;
+ int *qt, rrrr, ssss, q;
+ uchar *ss;
+ int Ta, H, V;
+
+ ss = h->ss;
+ Ns = ss[0];
+ if(Ns != 1)
+ jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan");
+ Ss = ss[1+2];
+ Se = ss[2+2];
+ H = h->comp[comp].H;
+ V = h->comp[comp].V;
+
+ nacross = h->nacross*H;
+ ndown = h->ndown*V;
+ q = 8*h->Hmax/H;
+ nhor = (h->X+q-1)/q;
+ q = 8*h->Vmax/V;
+ nver = (h->Y+q-1)/q;
+
+ /* initialize data structures */
+ h->cnt = 0;
+ h->sr = 0;
+ h->peek = -1;
+ nibbles(ss[1+1], &z, &Ta); /* z is thrown away */
+
+ ri = h->ri;
+
+ eobrun = 0;
+ acht = &h->acht[Ta];
+ qt = h->qt[h->comp[comp].Tq];
+ nmcu = nacross*ndown;
+ mcu = 0;
+ for(y=0; y<nver; y++){
+ for(x=0; x<nhor; x++){
+ /* Figure G-3 */
+ if(eobrun > 0){
+ --eobrun;
+ continue;
+ }
+
+ /* arrange blockno to be in same sequence as original scan calculation. */
+ tmcu = x/H + (nacross/H)*(y/V);
+ blockno = tmcu*H*V + H*(y%V) + x%H;
+ acc = h->accoeff[comp][blockno];
+ k = Ss;
+ for(;;){
+ rs = decode(h, acht);
+ /* XXX remove rrrr ssss as in baselinescan */
+ nibbles(rs, &rrrr, &ssss);
+ if(ssss == 0){
+ if(rrrr < 15){
+ eobrun = 0;
+ if(rrrr > 0)
+ eobrun = receiveEOB(h, rrrr)-1;
+ break;
+ }
+ k += 16;
+ }else{
+ k += rrrr;
+ z = receive(h, ssss);
+ acc[k] = z*qt[k]<<Al;
+ if(k == Se)
+ break;
+ k++;
+ }
+ }
+ }
+
+ /* process restart marker, if present */
+ mcu++;
+ if(ri>0 && mcu<nmcu && mcu%ri==0){
+ restart(h, mcu);
+ eobrun = 0;
+ }
+ }
+}
+
+static
+void
+increment(Header *h, int acc[], int k, int Pt)
+{
+ if(acc[k] == 0)
+ return;
+ if(receivebit(h) != 0)
+ if(acc[k] < 0)
+ acc[k] -= Pt;
+ else
+ acc[k] += Pt;
+}
+
+static
+void
+progressiveacinc(Header *h, int comp, int Al)
+{
+ int Ns, i, z, k, Ss, Se, Ta, **ac, H, V;
+ int ri, mcu, nacross, ndown, nhor, nver, eobrun, nzeros, pending, x, y, tmcu, blockno, q, nmcu;
+ Huffman *acht;
+ int *qt, rrrr, ssss, *acc, rs;
+ uchar *ss;
+
+ ss = h->ss;
+ Ns = ss[0];
+ if(Ns != 1)
+ jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan");
+ Ss = ss[1+2];
+ Se = ss[2+2];
+ H = h->comp[comp].H;
+ V = h->comp[comp].V;
+
+ nacross = h->nacross*H;
+ ndown = h->ndown*V;
+ q = 8*h->Hmax/H;
+ nhor = (h->X+q-1)/q;
+ q = 8*h->Vmax/V;
+ nver = (h->Y+q-1)/q;
+
+ /* initialize data structures */
+ h->cnt = 0;
+ h->sr = 0;
+ h->peek = -1;
+ nibbles(ss[1+1], &z, &Ta); /* z is thrown away */
+ ri = h->ri;
+
+ eobrun = 0;
+ ac = h->accoeff[comp];
+ acht = &h->acht[Ta];
+ qt = h->qt[h->comp[comp].Tq];
+ nmcu = nacross*ndown;
+ mcu = 0;
+ pending = 0;
+ nzeros = -1;
+ for(y=0; y<nver; y++){
+ for(x=0; x<nhor; x++){
+ /* Figure G-7 */
+
+ /* arrange blockno to be in same sequence as original scan calculation. */
+ tmcu = x/H + (nacross/H)*(y/V);
+ blockno = tmcu*H*V + H*(y%V) + x%H;
+ acc = ac[blockno];
+ if(eobrun > 0){
+ if(nzeros > 0)
+ jpgerror(h, "ReadJPG: zeros pending at block start");
+ for(k=Ss; k<=Se; k++)
+ increment(h, acc, k, qt[k]<<Al);
+ --eobrun;
+ continue;
+ }
+
+ for(k=Ss; k<=Se; ){
+ if(nzeros >= 0){
+ if(acc[k] != 0)
+ increment(h, acc, k, qt[k]<<Al);
+ else if(nzeros-- == 0)
+ acc[k] = pending;
+ k++;
+ continue;
+ }
+ rs = decode(h, acht);
+ nibbles(rs, &rrrr, &ssss);
+ if(ssss == 0){
+ if(rrrr < 15){
+ eobrun = 0;
+ if(rrrr > 0)
+ eobrun = receiveEOB(h, rrrr)-1;
+ while(k <= Se){
+ increment(h, acc, k, qt[k]<<Al);
+ k++;
+ }
+ break;
+ }
+ for(i=0; i<16; k++){
+ increment(h, acc, k, qt[k]<<Al);
+ if(acc[k] == 0)
+ i++;
+ }
+ continue;
+ }else if(ssss != 1)
+ jpgerror(h, "ReadJPG: ssss!=1 in progressive increment");
+ nzeros = rrrr;
+ pending = receivebit(h);
+ if(pending == 0)
+ pending = -1;
+ pending *= qt[k]<<Al;
+ }
+ }
+
+ /* process restart marker, if present */
+ mcu++;
+ if(ri>0 && mcu<nmcu && mcu%ri==0){
+ restart(h, mcu);
+ eobrun = 0;
+ nzeros = -1;
+ }
+ }
+}
+
+static
+void
+progressivescan(Header *h, int colorspace)
+{
+ uchar *ss;
+ int Ns, Ss, Ah, Al, c, comp, i;
+
+ if(h->dccoeff[0] == nil)
+ progressiveinit(h, colorspace);
+
+ ss = h->ss;
+ Ns = ss[0];
+ Ss = ss[1+2*Ns];
+ nibbles(ss[3+2*Ns], &Ah, &Al);
+ c = ss[1];
+ comp = -1;
+ for(i=0; i<h->Nf; i++)
+ if(h->comp[i].C == c)
+ comp = i;
+ if(comp == -1)
+ jpgerror(h, "ReadJPG: bad component index in scan header");
+
+ if(Ss == 0){
+ progressivedc(h, comp, Ah, Al);
+ return;
+ }
+ if(Ah == 0){
+ progressiveac(h, comp, Al);
+ return;
+ }
+ progressiveacinc(h, comp, Al);
+}
+
+enum {
+ c1 = 2871, /* 1.402 * 2048 */
+ c2 = 705, /* 0.34414 * 2048 */
+ c3 = 1463, /* 0.71414 * 2048 */
+ c4 = 3629, /* 1.772 * 2048 */
+};
+
+static
+void
+colormap1(Header *h, int colorspace, Rawimage *image, int data[8*8], int mcu, int nacross)
+{
+ uchar *pic;
+ int x, y, dx, dy, minx, miny;
+ int r, k, pici;
+
+ USED(colorspace);
+ pic = image->chans[0];
+ minx = 8*(mcu%nacross);
+ dx = 8;
+ if(minx+dx > h->X)
+ dx = h->X-minx;
+ miny = 8*(mcu/nacross);
+ dy = 8;
+ if(miny+dy > h->Y)
+ dy = h->Y-miny;
+ pici = miny*h->X+minx;
+ k = 0;
+ for(y=0; y<dy; y++){
+ for(x=0; x<dx; x++){
+ r = clamp[(data[k+x]+128)+CLAMPOFF];
+ pic[pici+x] = r;
+ }
+ pici += h->X;
+ k += 8;
+ }
+}
+
+static
+void
+colormapall1(Header *h, int colorspace, Rawimage *image, int data0[8*8], int data1[8*8], int data2[8*8], int mcu, int nacross)
+{
+ uchar *rpic, *gpic, *bpic, *rp, *gp, *bp;
+ int *p0, *p1, *p2;
+ int x, y, dx, dy, minx, miny;
+ int r, g, b, k, pici;
+ int Y, Cr, Cb;
+
+ rpic = image->chans[0];
+ gpic = image->chans[1];
+ bpic = image->chans[2];
+ minx = 8*(mcu%nacross);
+ dx = 8;
+ if(minx+dx > h->X)
+ dx = h->X-minx;
+ miny = 8*(mcu/nacross);
+ dy = 8;
+ if(miny+dy > h->Y)
+ dy = h->Y-miny;
+ pici = miny*h->X+minx;
+ k = 0;
+ for(y=0; y<dy; y++){
+ p0 = data0+k;
+ p1 = data1+k;
+ p2 = data2+k;
+ rp = rpic+pici;
+ gp = gpic+pici;
+ bp = bpic+pici;
+ if(colorspace == CYCbCr)
+ for(x=0; x<dx; x++){
+ *rp++ = clamp[*p0++ + 128 + CLAMPOFF];
+ *gp++ = clamp[*p1++ + 128 + CLAMPOFF];
+ *bp++ = clamp[*p2++ + 128 + CLAMPOFF];
+ }
+ else
+ for(x=0; x<dx; x++){
+ Y = (*p0++ + 128) << 11;
+ Cb = *p1++;
+ Cr = *p2++;
+ r = Y+c1*Cr;
+ g = Y-c2*Cb-c3*Cr;
+ b = Y+c4*Cb;
+ *rp++ = clamp[(r>>11)+CLAMPOFF];
+ *gp++ = clamp[(g>>11)+CLAMPOFF];
+ *bp++ = clamp[(b>>11)+CLAMPOFF];
+ }
+ pici += h->X;
+ k += 8;
+ }
+}
+
+static
+void
+colormap(Header *h, int colorspace, Rawimage *image, int *data0[8*8], int *data1[8*8], int *data2[8*8], int mcu, int nacross, int Hmax, int Vmax, int *H, int *V)
+{
+ uchar *rpic, *gpic, *bpic;
+ int x, y, dx, dy, minx, miny;
+ int r, g, b, pici, H0, H1, H2;
+ int t, b0, b1, b2, y0, y1, y2, x0, x1, x2;
+ int Y, Cr, Cb;
+
+ rpic = image->chans[0];
+ gpic = image->chans[1];
+ bpic = image->chans[2];
+ minx = 8*Hmax*(mcu%nacross);
+ dx = 8*Hmax;
+ if(minx+dx > h->X)
+ dx = h->X-minx;
+ miny = 8*Vmax*(mcu/nacross);
+ dy = 8*Vmax;
+ if(miny+dy > h->Y)
+ dy = h->Y-miny;
+ pici = miny*h->X+minx;
+ H0 = H[0];
+ H1 = H[1];
+ H2 = H[2];
+ for(y=0; y<dy; y++){
+ t = y*V[0];
+ b0 = H0*(t/(8*Vmax));
+ y0 = 8*((t/Vmax)&7);
+ t = y*V[1];
+ b1 = H1*(t/(8*Vmax));
+ y1 = 8*((t/Vmax)&7);
+ t = y*V[2];
+ b2 = H2*(t/(8*Vmax));
+ y2 = 8*((t/Vmax)&7);
+ x0 = 0;
+ x1 = 0;
+ x2 = 0;
+ for(x=0; x<dx; x++){
+ if(colorspace == CYCbCr){
+ rpic[pici+x] = clamp[data0[b0][y0+x0++*H0/Hmax] + 128 + CLAMPOFF];
+ gpic[pici+x] = clamp[data1[b1][y1+x1++*H1/Hmax] + 128 + CLAMPOFF];
+ bpic[pici+x] = clamp[data2[b2][y2+x2++*H2/Hmax] + 128 + CLAMPOFF];
+ }else{
+ Y = (data0[b0][y0+x0++*H0/Hmax]+128)<<11;
+ Cb = data1[b1][y1+x1++*H1/Hmax];
+ Cr = data2[b2][y2+x2++*H2/Hmax];
+ r = Y+c1*Cr;
+ g = Y-c2*Cb-c3*Cr;
+ b = Y+c4*Cb;
+ rpic[pici+x] = clamp[(r>>11)+CLAMPOFF];
+ gpic[pici+x] = clamp[(g>>11)+CLAMPOFF];
+ bpic[pici+x] = clamp[(b>>11)+CLAMPOFF];
+ }
+ if(x0*H0/Hmax >= 8){
+ x0 = 0;
+ b0++;
+ }
+ if(x1*H1/Hmax >= 8){
+ x1 = 0;
+ b1++;
+ }
+ if(x2*H2/Hmax >= 8){
+ x2 = 0;
+ b2++;
+ }
+ }
+ pici += h->X;
+ }
+}
+
+/*
+ * decode next 8-bit value from entropy-coded input. chart F-26
+ */
+static
+int
+decode(Header *h, Huffman *t)
+{
+ int code, v, cnt, m, sr, i;
+ int *maxcode;
+ static int badcode;
+
+ maxcode = t->maxcode;
+ if(h->cnt < 8)
+ nextbyte(h, 0);
+ /* fast lookup */
+ code = (h->sr>>(h->cnt-8))&0xFF;
+ v = t->value[code];
+ if(v >= 0){
+ h->cnt -= t->shift[code];
+ return v;
+ }
+
+ h->cnt -= 8;
+ if(h->cnt == 0)
+ nextbyte(h, 0);
+ h->cnt--;
+ cnt = h->cnt;
+ m = 1<<cnt;
+ sr = h->sr;
+ code <<= 1;
+ i = 9;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ sr = nextbyte(h, 0);
+ m = 0x80;
+ cnt = 8;
+ }
+ cnt--;
+ }
+ if(i >= 17){
+ if(badcode == 0)
+ fprint(2, "badly encoded %dx%d JPEG file; ignoring bad value\n", h->X, h->Y);
+ badcode = 1;
+ i = 0;
+ }
+ h->cnt = cnt;
+ return t->val[t->valptr[i]+(code-t->mincode[i])];
+}
+
+/*
+ * load next byte of input
+ */
+static
+int
+nextbyte(Header *h, int marker)
+{
+ int b, b2;
+
+ if(h->peek >= 0){
+ b = h->peek;
+ h->peek = -1;
+ }else{
+ b = Bgetc(h->fd);
+ if(b == Beof)
+ jpgerror(h, "truncated file");
+ b &= 0xFF;
+ }
+
+ if(b == 0xFF){
+ if(marker)
+ return b;
+ b2 = Bgetc(h->fd);
+ if(b2 != 0){
+ if(b2 == Beof)
+ jpgerror(h, "truncated file");
+ b2 &= 0xFF;
+ if(b2 == DNL)
+ jpgerror(h, "ReadJPG: DNL marker unimplemented");
+ /* decoder is reading into marker; satisfy it and restore state */
+ Bungetc(h->fd);
+ h->peek = b;
+ }
+ }
+ h->cnt += 8;
+ h->sr = (h->sr<<8) | b;
+ return b;
+}
+
+/*
+ * return next s bits of input, MSB first, and level shift it
+ */
+static
+int
+receive(Header *h, int s)
+{
+ int v, m;
+
+ while(h->cnt < s)
+ nextbyte(h, 0);
+ h->cnt -= s;
+ v = h->sr >> h->cnt;
+ m = (1<<s);
+ v &= m-1;
+ /* level shift */
+ if(v < (m>>1))
+ v += ~(m-1)+1;
+ return v;
+}
+
+/*
+ * return next s bits of input, decode as EOB
+ */
+static
+int
+receiveEOB(Header *h, int s)
+{
+ int v, m;
+
+ while(h->cnt < s)
+ nextbyte(h, 0);
+ h->cnt -= s;
+ v = h->sr >> h->cnt;
+ m = (1<<s);
+ v &= m-1;
+ /* level shift */
+ v += m;
+ return v;
+}
+
+/*
+ * return next bit of input
+ */
+static
+int
+receivebit(Header *h)
+{
+ if(h->cnt < 1)
+ nextbyte(h, 0);
+ h->cnt--;
+ return (h->sr >> h->cnt) & 1;
+}
+
+/*
+ * Scaled integer implementation.
+ * inverse two dimensional DCT, Chen-Wang algorithm
+ * (IEEE ASSP-32, pp. 803-816, Aug. 1984)
+ * 32-bit integer arithmetic (8 bit coefficients)
+ * 11 mults, 29 adds per DCT
+ *
+ * coefficients extended to 12 bit for IEEE1180-1990 compliance
+ */
+
+enum {
+ W1 = 2841, /* 2048*sqrt(2)*cos(1*pi/16)*/
+ W2 = 2676, /* 2048*sqrt(2)*cos(2*pi/16)*/
+ W3 = 2408, /* 2048*sqrt(2)*cos(3*pi/16)*/
+ W5 = 1609, /* 2048*sqrt(2)*cos(5*pi/16)*/
+ W6 = 1108, /* 2048*sqrt(2)*cos(6*pi/16)*/
+ W7 = 565, /* 2048*sqrt(2)*cos(7*pi/16)*/
+
+ W1pW7 = 3406, /* W1+W7*/
+ W1mW7 = 2276, /* W1-W7*/
+ W3pW5 = 4017, /* W3+W5*/
+ W3mW5 = 799, /* W3-W5*/
+ W2pW6 = 3784, /* W2+W6*/
+ W2mW6 = 1567, /* W2-W6*/
+
+ R2 = 181 /* 256/sqrt(2)*/
+};
+
+static
+void
+idct(int b[8*8])
+{
+ int x, y, eighty, v;
+ int x0, x1, x2, x3, x4, x5, x6, x7, x8;
+ int *p;
+
+ /* transform horizontally*/
+ for(y=0; y<8; y++){
+ eighty = y<<3;
+ /* if all non-DC components are zero, just propagate the DC term*/
+ p = b+eighty;
+ if(p[1]==0)
+ if(p[2]==0 && p[3]==0)
+ if(p[4]==0 && p[5]==0)
+ if(p[6]==0 && p[7]==0){
+ v = p[0]<<3;
+ p[0] = v;
+ p[1] = v;
+ p[2] = v;
+ p[3] = v;
+ p[4] = v;
+ p[5] = v;
+ p[6] = v;
+ p[7] = v;
+ continue;
+ }
+ /* prescale*/
+ x0 = (p[0]<<11)+128;
+ x1 = p[4]<<11;
+ x2 = p[6];
+ x3 = p[2];
+ x4 = p[1];
+ x5 = p[7];
+ x6 = p[5];
+ x7 = p[3];
+ /* first stage*/
+ x8 = W7*(x4+x5);
+ x4 = x8 + W1mW7*x4;
+ x5 = x8 - W1pW7*x5;
+ x8 = W3*(x6+x7);
+ x6 = x8 - W3mW5*x6;
+ x7 = x8 - W3pW5*x7;
+ /* second stage*/
+ x8 = x0 + x1;
+ x0 -= x1;
+ x1 = W6*(x3+x2);
+ x2 = x1 - W2pW6*x2;
+ x3 = x1 + W2mW6*x3;
+ x1 = x4 + x6;
+ x4 -= x6;
+ x6 = x5 + x7;
+ x5 -= x7;
+ /* third stage*/
+ x7 = x8 + x3;
+ x8 -= x3;
+ x3 = x0 + x2;
+ x0 -= x2;
+ x2 = (R2*(x4+x5)+128)>>8;
+ x4 = (R2*(x4-x5)+128)>>8;
+ /* fourth stage*/
+ p[0] = (x7+x1)>>8;
+ p[1] = (x3+x2)>>8;
+ p[2] = (x0+x4)>>8;
+ p[3] = (x8+x6)>>8;
+ p[4] = (x8-x6)>>8;
+ p[5] = (x0-x4)>>8;
+ p[6] = (x3-x2)>>8;
+ p[7] = (x7-x1)>>8;
+ }
+ /* transform vertically*/
+ for(x=0; x<8; x++){
+ /* if all non-DC components are zero, just propagate the DC term*/
+ p = b+x;
+ if(p[8*1]==0)
+ if(p[8*2]==0 && p[8*3]==0)
+ if(p[8*4]==0 && p[8*5]==0)
+ if(p[8*6]==0 && p[8*7]==0){
+ v = (p[8*0]+32)>>6;
+ p[8*0] = v;
+ p[8*1] = v;
+ p[8*2] = v;
+ p[8*3] = v;
+ p[8*4] = v;
+ p[8*5] = v;
+ p[8*6] = v;
+ p[8*7] = v;
+ continue;
+ }
+ /* prescale*/
+ x0 = (p[8*0]<<8)+8192;
+ x1 = p[8*4]<<8;
+ x2 = p[8*6];
+ x3 = p[8*2];
+ x4 = p[8*1];
+ x5 = p[8*7];
+ x6 = p[8*5];
+ x7 = p[8*3];
+ /* first stage*/
+ x8 = W7*(x4+x5) + 4;
+ x4 = (x8+W1mW7*x4)>>3;
+ x5 = (x8-W1pW7*x5)>>3;
+ x8 = W3*(x6+x7) + 4;
+ x6 = (x8-W3mW5*x6)>>3;
+ x7 = (x8-W3pW5*x7)>>3;
+ /* second stage*/
+ x8 = x0 + x1;
+ x0 -= x1;
+ x1 = W6*(x3+x2) + 4;
+ x2 = (x1-W2pW6*x2)>>3;
+ x3 = (x1+W2mW6*x3)>>3;
+ x1 = x4 + x6;
+ x4 -= x6;
+ x6 = x5 + x7;
+ x5 -= x7;
+ /* third stage*/
+ x7 = x8 + x3;
+ x8 -= x3;
+ x3 = x0 + x2;
+ x0 -= x2;
+ x2 = (R2*(x4+x5)+128)>>8;
+ x4 = (R2*(x4-x5)+128)>>8;
+ /* fourth stage*/
+ p[8*0] = (x7+x1)>>14;
+ p[8*1] = (x3+x2)>>14;
+ p[8*2] = (x0+x4)>>14;
+ p[8*3] = (x8+x6)>>14;
+ p[8*4] = (x8-x6)>>14;
+ p[8*5] = (x0-x4)>>14;
+ p[8*6] = (x3-x2)>>14;
+ p[8*7] = (x7-x1)>>14;
+ }
+}
diff --git a/src/cmd/jpg/readpng.c b/src/cmd/jpg/readpng.c
new file mode 100644
index 00000000..1cb85946
--- /dev/null
+++ b/src/cmd/jpg/readpng.c
@@ -0,0 +1,334 @@
+// work in progress... this version only good enough to read
+// non-interleaved, 24bit RGB images
+
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <flate.h>
+#include <draw.h>
+#include "imagefile.h"
+
+int debug;
+
+enum{ IDATSIZE=1000000,
+ /* filtering algorithms, supposedly increase compression */
+ FilterNone = 0, /* new[x][y] = buf[x][y] */
+ FilterSub = 1, /* new[x][y] = buf[x][y] + new[x-1][y] */
+ FilterUp = 2, /* new[x][y] = buf[x][y] + new[x][y-1] */
+ FilterAvg = 3, /* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */
+ FilterPaeth= 4, /* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
+ FilterLast = 5,
+ PropertyBit = 1<<5,
+};
+
+typedef struct ZlibR{
+ Biobuf *bi;
+ uchar *buf;
+ uchar *b; // next byte to decompress
+ uchar *e; // past end of buf
+} ZlibR;
+
+typedef struct ZlibW{
+ uchar *r, *g, *b; // Rawimage channels
+ int chan; // next channel to write
+ int col; // column index of current pixel
+ // -1 = one-byte pseudo-column for filter spec
+ int row; // row index of current pixel
+ int ncol, nrow; // image width, height
+ int filter; // algorithm for current scanline
+} ZlibW;
+
+static ulong *crctab;
+static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
+static char memerr[] = "ReadPNG: malloc failed: %r";
+
+static ulong
+get4(uchar *a)
+{
+ return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
+}
+
+static
+void
+pnginit(void)
+{
+ static int inited;
+
+ if(inited)
+ return;
+ inited = 1;
+ crctab = mkcrctab(0xedb88320);
+ if(crctab == nil)
+ sysfatal("mkcrctab error");
+ inflateinit();
+}
+
+static
+void*
+pngmalloc(ulong n, int clear)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ sysfatal(memerr);
+ if(clear)
+ memset(p, 0, n);
+ return p;
+}
+
+static int
+getchunk(Biobuf *b, char *type, uchar *d, int m)
+{
+ uchar buf[8];
+ ulong crc = 0, crc2;
+ int n, nr;
+
+ if(Bread(b, buf, 8) != 8)
+ return -1;
+ n = get4(buf);
+ memmove(type, buf+4, 4);
+ type[4] = 0;
+ if(n > m)
+ sysfatal("getchunk needed %d, had %d", n, m);
+ nr = Bread(b, d, n);
+ if(nr != n)
+ sysfatal("getchunk read %d, expected %d", nr, n);
+ crc = blockcrc(crctab, crc, type, 4);
+ crc = blockcrc(crctab, crc, d, n);
+ if(Bread(b, buf, 4) != 4)
+ sysfatal("getchunk tlr failed");
+ crc2 = get4(buf);
+ if(crc != crc2)
+ sysfatal("getchunk crc failed");
+ return n;
+}
+
+static int
+zread(void *va)
+{
+ ZlibR *z = va;
+ char type[5];
+ int n;
+
+ if(z->b >= z->e){
+refill_buffer:
+ z->b = z->buf;
+ n = getchunk(z->bi, type, z->b, IDATSIZE);
+ if(n < 0 || strcmp(type, "IEND") == 0)
+ return -1;
+ z->e = z->b + n;
+ if(type[0] & PropertyBit)
+ goto refill_buffer; /* skip auxiliary chunks for now */
+ if(strcmp(type,"IDAT") != 0)
+ sysfatal("unrecognized mandatory chunk %s", type);
+ }
+ return *z->b++;
+}
+
+static uchar
+paeth(uchar a, uchar b, uchar c)
+{
+ int p, pa, pb, pc;
+
+ p = (int)a + (int)b - (int)c;
+ pa = abs(p - (int)a);
+ pb = abs(p - (int)b);
+ pc = abs(p - (int)c);
+
+ if(pa <= pb && pa <= pc)
+ return a;
+ else if(pb <= pc)
+ return b;
+ return c;
+}
+
+static void
+unfilter(int alg, uchar *buf, uchar *ebuf, int up)
+{
+ switch(alg){
+ case FilterSub:
+ while (++buf < ebuf)
+ *buf += buf[-1];
+ break;
+ case FilterUp:
+ if (up != 0)
+ do
+ *buf += buf[up];
+ while (++buf < ebuf);
+ break;
+ case FilterAvg:
+ if (up == 0)
+ while (++buf < ebuf)
+ *buf += buf[-1]/2;
+ else{
+ *buf += buf[up]/2;
+ while (++buf < ebuf)
+ *buf += (buf[-1]+buf[up])/2;
+ }
+ break;
+ case FilterPaeth:
+ if (up == 0)
+ while (++buf < ebuf)
+ *buf += buf[-1];
+ else{
+ *buf += paeth(0, buf[up], 0);
+ while (++buf < ebuf)
+ *buf += paeth(buf[-1], buf[up], buf[up-1]);
+ }
+ break;
+ }
+}
+
+static int
+zwrite(void *va, void *vb, int n)
+{
+ ZlibW *z = va;
+ uchar *buf = vb;
+ int i, up;
+ for(i=0; i<n; i++){
+ if(z->col == -1){
+ // set filter byte
+ z->filter = *buf++;
+ if (z->filter >= FilterLast)
+ sysfatal("unknown filter algorithm %d for row %d", z->row, z->filter);
+ z->col++;
+ continue;
+ }
+ switch(z->chan){
+ case 0:
+ *z->r++ = *buf++;
+ z->chan = 1;
+ break;
+ case 1:
+ *z->g++ = *buf++;
+ z->chan = 2;
+ break;
+ case 2:
+ *z->b++ = *buf++;
+ z->chan = 0;
+ z->col++;
+ if(z->col == z->ncol){
+ if (z->filter){
+ if(z->row == 0)
+ up = 0;
+ else
+ up = -z->ncol;
+ unfilter(z->filter, z->r - z->col, z->r, up);
+ unfilter(z->filter, z->g - z->col, z->g, up);
+ unfilter(z->filter, z->b - z->col, z->b, up);
+ }
+ z->col = -1;
+ z->row++;
+ if((z->row >= z->nrow) && (i < n-1) )
+ sysfatal("header said %d rows; data goes further", z->nrow);
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+static Rawimage*
+readslave(Biobuf *b)
+{
+ ZlibR zr;
+ ZlibW zw;
+ Rawimage *image;
+ char type[5];
+ uchar *buf, *h;
+ int k, n, nrow, ncol, err;
+
+ buf = pngmalloc(IDATSIZE, 0);
+ Bread(b, buf, sizeof PNGmagic);
+ if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
+ sysfatal("bad PNGmagic");
+
+ n = getchunk(b, type, buf, IDATSIZE);
+ if(n < 13 || strcmp(type,"IHDR") != 0)
+ sysfatal("missing IHDR chunk");
+ h = buf;
+ ncol = get4(h); h += 4;
+ nrow = get4(h); h += 4;
+ if(ncol <= 0 || nrow <= 0)
+ sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol);
+ if(debug)
+ fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol);
+ if(*h++ != 8)
+ sysfatal("only 24 bit per pixel supported for now [%d]", h[-1]);
+ if(*h++ != 2)
+ sysfatal("only rgb supported for now [%d]", h[-1]);
+ if(*h++ != 0)
+ sysfatal("only deflate supported for now [%d]", h[-1]);
+ if(*h++ != FilterNone)
+ sysfatal("only FilterNone supported for now [%d]", h[-1]);
+ if(*h != 0)
+ sysfatal("only non-interlaced supported for now [%d]", h[-1]);
+
+ image = pngmalloc(sizeof(Rawimage), 1);
+ image->r = Rect(0, 0, ncol, nrow);
+ image->cmap = nil;
+ image->cmaplen = 0;
+ image->chanlen = ncol*nrow;
+ image->fields = 0;
+ image->gifflags = 0;
+ image->gifdelay = 0;
+ image->giftrindex = 0;
+ image->chandesc = CRGB;
+ image->nchans = 3;
+ for(k=0; k<3; k++)
+ image->chans[k] = pngmalloc(ncol*nrow, 0);
+ zr.bi = b;
+ zr.buf = buf;
+ zr.b = zr.e = buf + IDATSIZE;
+ zw.r = image->chans[0];
+ zw.g = image->chans[1];
+ zw.b = image->chans[2];
+ zw.chan = 0;
+ zw.col = -1;
+ zw.row = 0;
+ zw.ncol = ncol;
+ zw.nrow = nrow;
+ err = inflatezlib(&zw, zwrite, &zr, zread);
+ if(err)
+ sysfatal("inflatezlib %s\n", flateerr(err));
+ free(buf);
+ return image;
+}
+
+Rawimage**
+Breadpng(Biobuf *b, int colorspace)
+{
+ Rawimage *r, **array;
+ char buf[ERRMAX];
+
+ buf[0] = '\0';
+ if(colorspace != CRGB){
+ errstr(buf, sizeof buf); /* throw it away */
+ werrstr("ReadPNG: unknown color space %d", colorspace);
+ return nil;
+ }
+ pnginit();
+ array = malloc(2*sizeof(*array));
+ if(array==nil)
+ return nil;
+ errstr(buf, sizeof buf); /* throw it away */
+ r = readslave(b);
+ array[0] = r;
+ array[1] = nil;
+ return array;
+}
+
+Rawimage**
+readpng(int fd, int colorspace)
+{
+ Rawimage** a;
+ Biobuf b;
+
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+ a = Breadpng(&b, colorspace);
+ Bterm(&b);
+ return a;
+}
diff --git a/src/cmd/jpg/readppm.c b/src/cmd/jpg/readppm.c
new file mode 100644
index 00000000..28b7f4ea
--- /dev/null
+++ b/src/cmd/jpg/readppm.c
@@ -0,0 +1,238 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <ctype.h>
+#include "imagefile.h"
+
+Rawimage *readppm(Biobuf*, Rawimage*);
+
+/*
+ * fetch a non-comment character.
+ */
+static
+int
+Bgetch(Biobuf *b)
+{
+ int c;
+
+ for(;;) {
+ c = Bgetc(b);
+ if(c == '#') {
+ while((c = Bgetc(b)) != Beof && c != '\n')
+ ;
+ }
+ return c;
+ }
+}
+
+/*
+ * fetch a nonnegative decimal integer.
+ */
+static
+int
+Bgetint(Biobuf *b)
+{
+ int c;
+ int i;
+
+ while((c = Bgetch(b)) != Beof && !isdigit(c))
+ ;
+ if(c == Beof)
+ return -1;
+
+ i = 0;
+ do {
+ i = i*10 + (c-'0');
+ } while((c = Bgetch(b)) != Beof && isdigit(c));
+
+ return i;
+}
+
+static
+int
+Bgetdecimalbit(Biobuf *b)
+{
+ int c;
+ while((c = Bgetch(b)) != Beof && c != '0' && c != '1')
+ ;
+ if(c == Beof)
+ return -1;
+ return c == '1';
+}
+
+static int bitc, nbit;
+
+static
+int
+Bgetbit(Biobuf *b)
+{
+ if(nbit == 0) {
+ nbit = 8;
+ bitc = Bgetc(b);
+ if(bitc == -1)
+ return -1;
+ }
+ nbit--;
+ return (bitc >> (nbit-1)) & 0x1;
+}
+
+static
+void
+Bflushbit(Biobuf *b)
+{
+ USED(b);
+ nbit = 0;
+}
+
+
+Rawimage**
+readpixmap(int fd, int colorspace)
+{
+ Rawimage **array, *a;
+ Biobuf b;
+ char buf[ERRMAX];
+ int i;
+ char *e;
+
+ USED(colorspace);
+ if(Binit(&b, fd, OREAD) < 0)
+ return nil;
+
+ werrstr("");
+ e = "out of memory";
+ if((array = malloc(sizeof *array)) == nil)
+ goto Error;
+ if((array[0] = malloc(sizeof *array[0])) == nil)
+ goto Error;
+ memset(array[0], 0, sizeof *array[0]);
+
+ for(i=0; i<3; i++)
+ array[0]->chans[i] = nil;
+
+ e = "bad file format";
+ switch(Bgetc(&b)) {
+ case 'P':
+ Bungetc(&b);
+ a = readppm(&b, array[0]);
+ break;
+ default:
+ a = nil;
+ break;
+ }
+ if(a == nil)
+ goto Error;
+ array[0] = a;
+
+ return array;
+
+Error:
+ if(array)
+ free(array[0]);
+ free(array);
+
+ errstr(buf, sizeof buf);
+ if(buf[0] == 0)
+ strcpy(buf, e);
+ errstr(buf, sizeof buf);
+
+ return nil;
+}
+
+typedef struct Pix Pix;
+struct Pix {
+ char magic;
+ int maxcol;
+ int (*fetch)(Biobuf*);
+ int nchan;
+ int chandesc;
+ int invert;
+ void (*flush)(Biobuf*);
+};
+
+static Pix pix[] = {
+ { '1', 1, Bgetdecimalbit, 1, CY, 1, nil }, /* portable bitmap */
+ { '4', 1, Bgetbit, 1, CY, 1, Bflushbit }, /* raw portable bitmap */
+ { '2', 0, Bgetint, 1, CY, 0, nil }, /* portable greymap */
+ { '5', 0, Bgetc, 1, CY, 0, nil }, /* raw portable greymap */
+ { '3', 0, Bgetint, 3, CRGB, 0, nil }, /* portable pixmap */
+ { '6', 0, Bgetc, 3, CRGB, 0, nil }, /* raw portable pixmap */
+ { 0 },
+};
+
+Rawimage*
+readppm(Biobuf *b, Rawimage *a)
+{
+ int i, ch, wid, ht, r, c;
+ int maxcol, nchan, invert;
+ int (*fetch)(Biobuf*);
+ uchar *rgb[3];
+ char buf[ERRMAX];
+ char *e;
+ Pix *p;
+
+ e = "bad file format";
+ if(Bgetc(b) != 'P')
+ goto Error;
+
+ c = Bgetc(b);
+ for(p=pix; p->magic; p++)
+ if(p->magic == c)
+ break;
+ if(p->magic == 0)
+ goto Error;
+
+
+ wid = Bgetint(b);
+ ht = Bgetint(b);
+ if(wid <= 0 || ht <= 0)
+ goto Error;
+ a->r = Rect(0,0,wid,ht);
+
+ maxcol = p->maxcol;
+ if(maxcol == 0) {
+ maxcol = Bgetint(b);
+ if(maxcol <= 0)
+ goto Error;
+ }
+
+ e = "out of memory";
+ for(i=0; i<p->nchan; i++)
+ if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil)
+ goto Error;
+ a->nchans = p->nchan;
+ a->chanlen = wid*ht;
+ a->chandesc = p->chandesc;
+
+ e = "error reading file";
+
+ fetch = p->fetch;
+ nchan = p->nchan;
+ invert = p->invert;
+ for(r=0; r<ht; r++) {
+ for(c=0; c<wid; c++) {
+ for(i=0; i<nchan; i++) {
+ if((ch = (*fetch)(b)) < 0)
+ goto Error;
+ if(invert)
+ ch = maxcol - ch;
+ *rgb[i]++ = (ch * 255)/maxcol;
+ }
+ }
+ if(p->flush)
+ (*p->flush)(b);
+ }
+
+ return a;
+
+Error:
+ errstr(buf, sizeof buf);
+ if(buf[0] == 0)
+ strcpy(buf, e);
+ errstr(buf, sizeof buf);
+
+ for(i=0; i<3; i++)
+ free(a->chans[i]);
+ free(a->cmap);
+ return nil;
+}
diff --git a/src/cmd/jpg/readyuv.c b/src/cmd/jpg/readyuv.c
new file mode 100644
index 00000000..eae2764e
--- /dev/null
+++ b/src/cmd/jpg/readyuv.c
@@ -0,0 +1,190 @@
+/* readyuv.c - read an Abekas A66 style image file. Steve Simon, 2003 */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <ctype.h>
+#include "imagefile.h"
+
+/*
+ * ITU/CCIR Rec601 states:
+ *
+ * R = y + 1.402 * Cr
+ * B = Y + 1.77305 * Cb
+ * G = Y - 0.72414 * Cr - 0.34414 * Cb
+ *
+ * using 8 bit traffic
+ * Y = 16 + 219 * Y
+ * Cr = 128 + 224 * Cr
+ * Cb = 128 + 224 * Cb
+ * or, if 10bit is used
+ * Y = 64 + 876 * Y
+ * Cr = 512 + 896 * Cr
+ * Cb = 512 + 896 * Cb
+ */
+
+enum {
+ PAL = 576, NTSC = 486 };
+
+
+static int lsbtab[] = { 6, 4, 2, 0};
+
+static int
+clip(int x)
+{
+ x >>= 18;
+
+ if (x > 255)
+ return 0xff;
+ if (x <= 0)
+ return 0;
+ return x;
+}
+
+
+Rawimage**
+Breadyuv(Biobuf *bp, int colourspace)
+{
+ Dir * d;
+ Rawimage * a, **array;
+ char *e, ebuf[128];
+ ushort * mux, *end, *frm;
+ uchar buf[720 * 2], *r, *g, *b;
+ int y1, y2, cb, cr, sz, c, l, w, base, bits, lines;
+
+ frm = 0;
+ if (colourspace != CYCbCr) {
+ errstr(ebuf, sizeof ebuf); /* throw it away */
+ werrstr("ReadYUV: unknown colour space %d", colourspace);
+ return nil;
+ }
+
+ if ((a = calloc(sizeof(Rawimage), 1)) == nil)
+ sysfatal("no memory");
+
+ if ((array = calloc(sizeof(Rawimage * ), 2)) == nil)
+ sysfatal("no memory");
+ array[0] = a;
+ array[1] = nil;
+
+ if ((d = dirfstat(Bfildes(bp))) != nil) {
+ sz = d->length;
+ free(d);
+ } else {
+ fprint(2, "cannot stat input, assuming 720x576x10bit\n");
+ sz = 720 * PAL * 2L + (720 * PAL / 2L);
+ }
+
+ switch (sz) {
+ case 720 * PAL * 2: // 625 x 8bit
+ bits = 8;
+ lines = PAL;
+ break;
+ case 720 * NTSC * 2: // 525 x 8bit
+ bits = 8;
+ lines = NTSC;
+ break;
+ case 720 * PAL * 2 + (720 * PAL / 2) : // 625 x 10bit
+ bits = 10;
+ lines = PAL;
+ break;
+ case 720 * NTSC * 2 + (720 * NTSC / 2) : // 525 x 10bit
+ bits = 10;
+ lines = NTSC;
+ break;
+ default:
+ e = "unknown file size";
+ goto Error;
+ }
+
+ // print("bits=%d pixels=%d lines=%d\n", bits, 720, lines);
+ //
+ a->nchans = 3;
+ a->chandesc = CRGB;
+ a->chanlen = 720 * lines;
+ a->r = Rect(0, 0, 720, lines);
+
+ e = "no memory";
+ if ((frm = malloc(720 * 2 * lines * sizeof(ushort))) == nil)
+ goto Error;
+
+ for (c = 0; c < 3; c++)
+ if ((a->chans[c] = malloc(720 * lines)) == nil)
+ goto Error;
+
+ e = "read file";
+ for (l = 0; l < lines; l++) {
+ if (Bread(bp, buf, 720 * 2) == -1)
+ goto Error;
+
+ base = l * 720 * 2;
+ for (w = 0; w < 720 * 2; w++)
+ frm[base + w] = ((ushort)buf[w]) << 2;
+ }
+
+
+ if (bits == 10)
+ for (l = 0; l < lines; l++) {
+ if (Bread(bp, buf, 720 / 2) == -1)
+ goto Error;
+
+
+ base = l * 720 * 2;
+ for (w = 0; w < 720 * 2; w++)
+ frm[base + w] |= buf[w / 4] >> lsbtab[w % 4];
+ }
+
+ mux = frm;
+ end = frm + 720 * lines * 2;
+ r = a->chans[0];
+ g = a->chans[1];
+ b = a->chans[2];
+
+ while (mux < end) {
+ cb = *mux++ - 512;
+ y1 = (*mux++ - 64) * 76310;
+ cr = *mux++ - 512;
+ y2 = (*mux++ - 64) * 76310;
+
+ *r++ = clip((104635 * cr) + y1);
+ *g++ = clip((-25690 * cb + -53294 * cr) + y1);
+ *b++ = clip((132278 * cb) + y1);
+
+ *r++ = clip((104635 * cr) + y2);
+ *g++ = clip((-25690 * cb + -53294 * cr) + y2);
+ *b++ = clip((132278 * cb) + y2);
+ }
+ free(frm);
+ return array;
+
+Error:
+
+ errstr(ebuf, sizeof ebuf);
+ if (ebuf[0] == 0)
+ strcpy(ebuf, e);
+ errstr(ebuf, sizeof ebuf);
+
+ for (c = 0; c < 3; c++)
+ free(a->chans[c]);
+ free(a->cmap);
+ free(array[0]);
+ free(array);
+ free(frm);
+ return nil;
+}
+
+
+Rawimage**
+readyuv(int fd, int colorspace)
+{
+ Rawimage * *a;
+ Biobuf b;
+
+ if (Binit(&b, fd, OREAD) < 0)
+ return nil;
+ a = Breadyuv(&b, colorspace);
+ Bterm(&b);
+ return a;
+}
+
+
diff --git a/src/cmd/jpg/rgbrgbv.c b/src/cmd/jpg/rgbrgbv.c
new file mode 100644
index 00000000..bc55947a
--- /dev/null
+++ b/src/cmd/jpg/rgbrgbv.c
@@ -0,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+/*
+ * This version of closest() is now (feb 20, 2001) installed as rgb2cmap in libdraw
+ */
+
+int
+closest(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;
+}
+
+void
+main(int argc, char *argv[])
+{
+ int i, rgb;
+ int r, g, b;
+ uchar close[16*16*16];
+
+ /* rgbmap */
+ print("uint rgbmap[256] = {\n");
+ for(i=0; i<256; i++){
+ if(i%8 == 0)
+ print("\t");
+ rgb = cmap2rgb(i);
+ r = (rgb>>16) & 0xFF;
+ g = (rgb>>8) & 0xFF;
+ b = (rgb>>0) & 0xFF;
+ print("0x%.6ulX, ", (r<<16) | (g<<8) | b);
+ if(i%8 == 7)
+ print("\n");
+ }
+ print("};\n\n");
+
+ /* closestrgb */
+ print("uchar closestrgb[16*16*16] = {\n");
+ for(r=0; r<256; r+=16)
+ for(g=0; g<256; g+=16)
+ for(b=0; b<256; b+=16)
+ close[(b/16)+16*((g/16)+16*(r/16))] = closest(r+8, g+8, b+8);
+ for(i=0; i<16*16*16; i++){
+ if(i%16 == 0)
+ print("\t");
+ print("%d,", close[i]);
+ if(i%16 == 15)
+ print("\n");
+ }
+ print("};\n\n");
+ exits(nil);
+}
diff --git a/src/cmd/jpg/rgbycc.c b/src/cmd/jpg/rgbycc.c
new file mode 100644
index 00000000..3654a2d2
--- /dev/null
+++ b/src/cmd/jpg/rgbycc.c
@@ -0,0 +1,120 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+float c1 = 1.402;
+float c2 = 0.34414;
+float c3 = 0.71414;
+float c4 = 1.772;
+
+int
+closest(int Y, int Cb, int Cr)
+{
+ double r, g, b;
+ double diff, min;
+ int rgb, R, G, B, v, i;
+ int y1, cb1, cr1;
+
+ Cb -= 128;
+ Cr -= 128;
+ r = Y+c1*Cr;
+ g = Y-c2*Cb-c3*Cr;
+ b = Y+c4*Cb;
+
+//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b);
+
+ min = 1000000.;
+ v = 1000;
+ for(i=0; i<256; i++){
+ rgb = cmap2rgb(i);
+ R = (rgb >> 16) & 0xFF;
+ G = (rgb >> 8) & 0xFF;
+ B = (rgb >> 0) & 0xFF;
+ diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b);
+ y1 = 0.5870*G + 0.114*B + 0.299*R;
+ cb1 = (B-y1)/1.772;
+ cr1 = (R-y1)/1.402;
+ if(diff < min){
+// if(Y==0 && y1!=0)
+// continue;
+ if(Y==256-16 && y1<256-16)
+ continue;
+// if(Cb==0 && cb1!=0)
+// continue;
+ if(Cb==256-16 && cb1<256-16)
+ continue;
+// if(Cr==0 && cr1!=0)
+// continue;
+ if(Cr==256-16 && cr1<256-16)
+ continue;
+//print("%d %d %d\n", R, G, B);
+ min = diff;
+ v = i;
+ }
+ }
+ if(v > 255)
+ abort();
+ return v;
+}
+
+void
+main(int argc, char *argv[])
+{
+ int i, rgb;
+ int r, g, b;
+ double Y, Cr, Cb;
+ int y, cb, cr;
+ uchar close[16*16*16];
+
+//print("%d\n", closest(atoi(argv[1]), atoi(argv[2]), atoi(argv[3])));
+//exits("X");
+
+ /* ycbcrmap */
+ print("uint ycbcrmap[256] = {\n");
+ for(i=0; i<256; i++){
+ if(i%8 == 0)
+ print("\t");
+ rgb = cmap2rgb(i);
+ r = (rgb>>16) & 0xFF;
+ g = (rgb>>8) & 0xFF;
+ b = (rgb>>0) & 0xFF;
+ Y = 0.5870*g + 0.114*b + 0.299*r;
+ Cr = (r-Y)/1.402 + 128.;
+ Cb = (b-Y)/1.772 + 128.;
+ if(Y<0. || Y>=256. || Cr<0. || Cr>=256. || Cb<0. || Cb>=256.)
+ print("bad at %d: %d %d %d; %g %g %g\n", i, r, g, b, Y, Cb, Cr);
+ r = Y;
+ g = Cb;
+ b = Cr;
+ print("0x%.6ulX, ", (r<<16) | (g<<8) | b);
+ if(i%8 == 7)
+ print("\n");
+ }
+ print("};\n\n");
+
+ /* closestycbcr */
+ print("uchar closestycbcr[16*16*16] = {\n");
+ for(y=0; y<256; y+=16)
+ for(cb=0; cb<256; cb+=16)
+ for(cr=0; cr<256; cr+=16)
+ close[(cr/16)+16*((cb/16)+16*(y/16))] = closest(y, cb, cr);
+if(0){
+ /*weird: set white for nearly white */
+ for(cb=128-32; cb<=128+32; cb+=16)
+ for(cr=128-32; cr<=128+32; cr+=16)
+ close[(cr/16)+16*((cb/16)+16*(255/16))] = 0;
+ /*weird: set black for nearly black */
+ for(cb=128-32; cb<=128+32; cb+=16)
+ for(cr=128-32; cr<=128+32; cr+=16)
+ close[(cr/16)+16*((cb/16)+16*(0/16))] = 255;
+}
+ for(i=0; i<16*16*16; i++){
+ if(i%16 == 0)
+ print("\t");
+ print("%d,", close[i]);
+ if(i%16 == 15)
+ print("\n");
+ }
+ print("};\n\n");
+ exits(nil);
+}
diff --git a/src/cmd/jpg/togif.c b/src/cmd/jpg/togif.c
new file mode 100644
index 00000000..35336883
--- /dev/null
+++ b/src/cmd/jpg/togif.c
@@ -0,0 +1,147 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <ctype.h>
+#include <bio.h>
+#include "imagefile.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: togif [-l loopcount] [-c 'comment'] [-d Δt (ms)] [-t transparency-index] [file ... [-d Δt] file ...]\n");
+ exits("usage");
+}
+
+#define UNSET (-12345678)
+
+void
+main(int argc, char *argv[])
+{
+ Biobuf bout;
+ Memimage *i, *ni;
+ int fd, j, dt, trans, loop;
+ char buf[256];
+ char *err, *comment, *s;
+
+ comment = nil;
+ dt = -1;
+ trans = -1;
+ loop = UNSET;
+ ARGBEGIN{
+ case 'l':
+ s = ARGF();
+ if(s==nil || (!isdigit(s[0]) && s[0]!='-'))
+ usage();
+ loop = atoi(s);
+ break;
+ case 'c':
+ comment = ARGF();
+ if(comment == nil)
+ usage();
+ break;
+ case 'd':
+ s = ARGF();
+ if(s==nil || !isdigit(s[0]))
+ usage();
+ dt = atoi(s);
+ break;
+ case 't':
+ s = ARGF();
+ if(s==nil || !isdigit(s[0]))
+ usage();
+ trans = atoi(s);
+ if(trans > 255)
+ usage();
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(Binit(&bout, 1, OWRITE) < 0)
+ sysfatal("Binit failed: %r");
+
+ memimageinit();
+
+ err = nil;
+
+ if(argc == 0){
+ i = readmemimage(0);
+ if(i == nil)
+ sysfatal("reading input: %r");
+ ni = memonechan(i);
+ if(ni == nil)
+ sysfatal("converting image to RGBV: %r");
+ if(i != ni){
+ freememimage(i);
+ i = ni;
+ }
+ err = memstartgif(&bout, i, -1);
+ if(err == nil){
+ if(comment)
+ err = memwritegif(&bout, i, comment, dt, trans);
+ else{
+ snprint(buf, sizeof buf, "Converted by Plan 9 from <stdin>");
+ err = memwritegif(&bout, i, buf, dt, trans);
+ }
+ }
+ }else{
+ if(loop == UNSET){
+ if(argc == 1)
+ loop = -1; /* no loop for single image */
+ else
+ loop = 0; /* the default case: 0 means infinite loop */
+ }
+ for(j=0; j<argc; j++){
+ if(argv[j][0] == '-' && argv[j][1]=='d'){
+ /* time change */
+ if(argv[j][2] == '\0'){
+ s = argv[++j];
+ if(j == argc)
+ usage();
+ }else
+ s = &argv[j][2];
+ if(!isdigit(s[0]))
+ usage();
+ dt = atoi(s);
+ if(j == argc-1) /* last argument must be file */
+ usage();
+ continue;
+ }
+ fd = open(argv[j], OREAD);
+ if(fd < 0)
+ sysfatal("can't open %s: %r", argv[j]);
+ i = readmemimage(fd);
+ if(i == nil)
+ sysfatal("can't readimage %s: %r", argv[j]);
+ close(fd);
+ ni = memonechan(i);
+ if(ni == nil)
+ sysfatal("converting image to RGBV: %r");
+ if(i != ni){
+ freememimage(i);
+ i = ni;
+ }
+ if(j == 0){
+ err = memstartgif(&bout, i, loop);
+ if(err != nil)
+ break;
+ }
+ if(comment)
+ err = memwritegif(&bout, i, comment, dt, trans);
+ else{
+ snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[j]);
+ err = memwritegif(&bout, i, buf, dt, trans);
+ }
+ if(err != nil)
+ break;
+ freememimage(i);
+ comment = nil;
+ }
+ }
+ memendgif(&bout);
+
+ if(err != nil)
+ fprint(2, "togif: %s\n", err);
+ exits(err);
+}
diff --git a/src/cmd/jpg/toico.c b/src/cmd/jpg/toico.c
new file mode 100644
index 00000000..059e018b
--- /dev/null
+++ b/src/cmd/jpg/toico.c
@@ -0,0 +1,322 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+
+enum
+{
+ FileHdrLen= 6,
+ IconDescrLen= 16,
+ IconHdrLen= 40,
+};
+
+typedef struct Icon Icon;
+struct Icon
+{
+ Icon *next;
+ char *file;
+
+ uchar w; /* icon width */
+ uchar h; /* icon height */
+ ushort ncolor; /* number of colors */
+ ushort nplane; /* number of bit planes */
+ ushort bits; /* bits per pixel */
+ ulong len; /* length of data */
+ ulong offset; /* file offset to data */
+ uchar map[4*256]; /* color map */
+
+ Image *img;
+
+ uchar *xor;
+ int xorlen;
+ uchar *and;
+ int andlen;
+};
+
+typedef struct Header Header;
+struct Header
+{
+ uint n;
+ Icon *first;
+ Icon *last;
+};
+
+void
+Bputs(Biobuf *b, ushort x)
+{
+ Bputc(b, x&0xff);
+ Bputc(b, x>>8);
+}
+
+void
+Bputl(Biobuf *b, ulong x)
+{
+ Bputs(b, x&0xffff);
+ Bputs(b, x>>16);
+}
+
+Header h;
+
+void* emalloc(int);
+void mk8bit(Icon*, int);
+void mkxorand(Icon*, int);
+void readicon(char*);
+
+void
+main(int argc, char **argv)
+{
+ int i;
+ Biobuf *b, out;
+ Icon *icon;
+ ulong offset;
+ ulong len;
+
+ ARGBEGIN{
+ }ARGEND;
+
+ /* read in all the images */
+ initdraw(nil, nil, nil);
+ if(argc < 1){
+ readicon("/fd/0");
+ } else {
+ for(i = 0; i < argc; i++)
+ readicon(argv[i]);
+ }
+
+ /* create the .ico file */
+ b = &out;
+ Binit(b, 1, OWRITE);
+
+ /* offset to first icon */
+ offset = FileHdrLen + h.n*IconDescrLen;
+
+ /* file header is */
+ Bputs(b, 0);
+ Bputs(b, 1);
+ Bputs(b, h.n);
+
+ /* icon description */
+ for(icon = h.first; icon != nil; icon = icon->next){
+ Bputc(b, icon->w);
+ Bputc(b, icon->h);
+ Bputc(b, icon->ncolor);
+ Bputc(b, 0);
+ Bputs(b, icon->nplane);
+ Bputs(b, icon->bits);
+ len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen;
+ Bputl(b, len);
+ Bputl(b, offset);
+ offset += len;
+ }
+
+ /* icons */
+ for(icon = h.first; icon != nil; icon = icon->next){
+ /* icon header (BMP like) */
+ Bputl(b, IconHdrLen);
+ Bputl(b, icon->w);
+ Bputl(b, 2*icon->h);
+ Bputs(b, icon->nplane);
+ Bputs(b, icon->bits);
+ Bputl(b, 0); /* compression info */
+ Bputl(b, 0);
+ Bputl(b, 0);
+ Bputl(b, 0);
+ Bputl(b, 0);
+ Bputl(b, 0);
+
+ /* color map */
+ if(Bwrite(b, icon->map, 4*icon->ncolor) < 0)
+ sysfatal("writing color map: %r");
+
+ /* xor bits */
+ if(Bwrite(b, icon->xor, icon->xorlen) < 0)
+ sysfatal("writing xor bits: %r");
+
+ /* and bits */
+ if(Bwrite(b, icon->and, icon->andlen) < 0)
+ sysfatal("writing and bits: %r");
+ }
+
+ Bterm(b);
+ exits(0);
+}
+
+void
+readicon(char *file)
+{
+ int fd;
+ Icon *icon;
+
+ fd = open(file, OREAD);
+ if(fd < 0)
+ sysfatal("opening %s: %r", file);
+ icon = emalloc(sizeof(Icon));
+ icon->img = readimage(display, fd, 0);
+ if(icon->img == nil)
+ sysfatal("reading image %s: %r", file);
+ close(fd);
+
+ if(h.first)
+ h.last->next = icon;
+ else
+ h.first = icon;
+ h.last = icon;
+ h.n++;
+
+ icon->h = Dy(icon->img->r);
+ icon->w = Dx(icon->img->r);
+ icon->bits = 1<<icon->img->depth;
+ icon->nplane = 1;
+
+ /* convert to 8 bits per pixel */
+ switch(icon->img->chan){
+ case GREY8:
+ case CMAP8:
+ break;
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ mk8bit(icon, 1);
+ break;
+ default:
+ mk8bit(icon, 0);
+ break;
+ }
+ icon->bits = 8;
+ icon->file = file;
+
+ /* create xor/and masks, minimizing bits per pixel */
+ mkxorand(icon, icon->img->chan == GREY8);
+}
+
+void*
+emalloc(int len)
+{
+ void *x;
+
+ x = mallocz(len, 1);
+ if(x == nil)
+ sysfatal("memory: %r");
+ return x;
+}
+
+/* convert to 8 bit */
+void
+mk8bit(Icon *icon, int grey)
+{
+ Image *img;
+
+ img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill);
+ if(img == nil)
+ sysfatal("can't allocimage: %r");
+ draw(img, img->r, icon->img, nil, ZP);
+ freeimage(icon->img);
+ icon->img = img;
+}
+
+/* make xor and and mask */
+void
+mkxorand(Icon *icon, int grey)
+{
+ int i, x, y, s, sa;
+ uchar xx[256];
+ uchar *data, *p, *e;
+ int ndata;
+ uchar *mp;
+ int ncolor;
+ ulong color;
+ int bits;
+ uchar andbyte, xorbyte;
+ uchar *ato, *xto;
+ int xorrl, andrl;
+
+ ndata = icon->h * icon->w;
+ data = emalloc(ndata);
+ if(unloadimage(icon->img, icon->img->r, data, ndata) < 0)
+ sysfatal("can't unload %s: %r", icon->file);
+ e = data + ndata;
+
+ /* find colors used */
+ memset(xx, 0, sizeof xx);
+ for(p = data; p < e; p++)
+ xx[*p]++;
+
+ /* count the colors and create a mapping from plan 9 */
+ mp = icon->map;
+ ncolor = 0;
+ for(i = 0; i < 256; i++){
+ if(xx[i] == 0)
+ continue;
+ if(grey){
+ *mp++ = i;
+ *mp++ = i;
+ *mp++ = i;
+ *mp++ = 0;
+ } else {
+ color = cmap2rgb(i);
+ *mp++ = color;
+ *mp++ = color>>8;
+ *mp++ = color>>16;
+ *mp++ = 0;
+ }
+ xx[i] = ncolor;
+ ncolor++;
+ }
+
+ /* get minimum number of pixels per bit (with a color map) */
+ if(ncolor <= 2){
+ ncolor = 2;
+ bits = 1;
+ } else if(ncolor <= 4){
+ ncolor = 4;
+ bits = 2;
+ } else if(ncolor <= 16){
+ ncolor = 16;
+ bits = 4;
+ } else {
+ ncolor = 256;
+ bits = 8;
+ }
+ icon->bits = bits;
+ icon->ncolor = ncolor;
+
+ /* the xor mask rows are justified to a 32 bit boundary */
+ /* the and mask is 1 bit grey */
+ xorrl = 4*((bits*icon->w + 31)/32);
+ andrl = 4*((icon->w + 31)/32);
+ icon->xor = emalloc(xorrl * icon->h);
+ icon->and = emalloc(andrl * icon->h);
+ icon->xorlen = xorrl*icon->h;
+ icon->andlen = andrl*icon->h;
+
+ /* make both masks. they're upside down relative to plan9 ones */
+ p = data;
+ for(y = 0; y < icon->h; y++){
+ andbyte = 0;
+ xorbyte = 0;
+ sa = s = 0;
+ xto = icon->xor + (icon->h-1-y)*xorrl;
+ ato = icon->and + (icon->h-1-y)*andrl;
+ for(x = 0; x < icon->w; x++){
+ xorbyte <<= bits;
+ xorbyte |= xx[*p];
+ s += bits;
+ if(s == 8){
+ *xto++ = xorbyte;
+ xorbyte = 0;
+ s = 0;
+ }
+ andbyte <<= 1;
+ if(*p == 0xff)
+ andbyte |= 1;
+ sa++;
+ if(sa == 0){
+ *ato++ = andbyte;
+ sa = 0;
+ andbyte = 0;
+ }
+ p++;
+ }
+ }
+ free(data);
+}
diff --git a/src/cmd/jpg/topng.c b/src/cmd/jpg/topng.c
new file mode 100644
index 00000000..09c00613
--- /dev/null
+++ b/src/cmd/jpg/topng.c
@@ -0,0 +1,70 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <ctype.h>
+#include <bio.h>
+#include <flate.h>
+#include "imagefile.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: topng [-c 'comment'] [-g 'gamma'] [file]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ Biobuf bout;
+ Memimage *i;
+ int fd;
+ char *err, *filename;
+ ImageInfo II;
+
+ ARGBEGIN{
+ case 'c':
+ II.comment = ARGF();
+ if(II.comment == nil)
+ usage();
+ II.fields_set |= II_COMMENT;
+ break;
+ case 'g':
+ II.gamma = atof(ARGF());
+ if(II.gamma == 0.)
+ usage();
+ II.fields_set |= II_GAMMA;
+ break;
+ case 't':
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(Binit(&bout, 1, OWRITE) < 0)
+ sysfatal("Binit failed: %r");
+ memimageinit();
+
+ if(argc == 0){
+ fd = 0;
+ filename = "<stdin>";
+ }else{
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("can't open %s: %r", argv[0]);
+ filename = argv[0];
+ }
+
+ i = readmemimage(fd);
+ if(i == nil)
+ sysfatal("can't readimage %s: %r", filename);
+ close(fd);
+
+ err = memwritepng(&bout, i, &II);
+ freememimage(i);
+
+ if(err != nil)
+ fprint(2, "topng: %s\n", err);
+ exits(err);
+}
diff --git a/src/cmd/jpg/toppm.c b/src/cmd/jpg/toppm.c
new file mode 100644
index 00000000..e3094655
--- /dev/null
+++ b/src/cmd/jpg/toppm.c
@@ -0,0 +1,90 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <ctype.h>
+#include <bio.h>
+#include "imagefile.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: toppm [-c 'comment'] [file]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ Biobuf bout;
+ Memimage *i, *ni;
+ int fd;
+ char buf[256];
+ char *err, *comment;
+
+ comment = nil;
+ ARGBEGIN{
+ case 'c':
+ comment = ARGF();
+ if(comment == nil)
+ usage();
+ if(strchr(comment, '\n') != nil){
+ fprint(2, "ppm: comment cannot contain newlines\n");
+ usage();
+ }
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 1)
+ usage();
+
+ if(Binit(&bout, 1, OWRITE) < 0)
+ sysfatal("Binit failed: %r");
+
+ memimageinit();
+
+ err = nil;
+
+ if(argc == 0){
+ i = readmemimage(0);
+ if(i == nil)
+ sysfatal("reading input: %r");
+ ni = memmultichan(i);
+ if(ni == nil)
+ sysfatal("converting image to RGBV: %r");
+ if(i != ni){
+ freememimage(i);
+ i = ni;
+ }
+ if(err == nil)
+ err = memwriteppm(&bout, i, comment);
+ }else{
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("can't open %s: %r", argv[0]);
+ i = readmemimage(fd);
+ if(i == nil)
+ sysfatal("can't readimage %s: %r", argv[0]);
+ close(fd);
+ ni = memmultichan(i);
+ if(ni == nil)
+ sysfatal("converting image to RGBV: %r");
+ if(i != ni){
+ freememimage(i);
+ i = ni;
+ }
+ if(comment)
+ err = memwriteppm(&bout, i, comment);
+ else{
+ snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[0]);
+ err = memwriteppm(&bout, i, buf);
+ }
+ freememimage(i);
+ }
+
+ if(err != nil)
+ fprint(2, "toppm: %s\n", err);
+ exits(err);
+}
diff --git a/src/cmd/jpg/torgbv.c b/src/cmd/jpg/torgbv.c
new file mode 100644
index 00000000..e83f82cd
--- /dev/null
+++ b/src/cmd/jpg/torgbv.c
@@ -0,0 +1,299 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+#include "rgbv.h"
+#include "ycbcr.h"
+
+#define CLAMPOFF 128
+
+static int clamp[CLAMPOFF+256+CLAMPOFF];
+static int inited;
+
+void*
+_remaperror(char *fmt, ...)
+{
+ va_list arg;
+ char buf[256];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+
+ werrstr(buf);
+ return nil;
+}
+
+Rawimage*
+torgbv(Rawimage *i, int errdiff)
+{
+ int j, k, rgb, x, y, er, eg, eb, col, t;
+ int r, g, b, r1, g1, b1;
+ int *ered, *egrn, *eblu, *rp, *gp, *bp;
+ uint *map3;
+ uchar *closest;
+ Rawimage *im;
+ int dx, dy;
+ char err[ERRMAX];
+ uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic;
+
+ err[0] = '\0';
+ errstr(err, sizeof err); /* throw it away */
+ im = malloc(sizeof(Rawimage));
+ if(im == nil)
+ return nil;
+ memset(im, 0, sizeof(Rawimage));
+ im->chans[0] = malloc(i->chanlen);
+ if(im->chans[0] == nil){
+ free(im);
+ return nil;
+ }
+ im->r = i->r;
+ im->nchans = 1;
+ im->chandesc = CRGBV;
+ im->chanlen = i->chanlen;
+
+ dx = i->r.max.x-i->r.min.x;
+ dy = i->r.max.y-i->r.min.y;
+ cmap = i->cmap;
+
+ if(inited == 0){
+ inited = 1;
+ for(j=0; j<CLAMPOFF; j++)
+ clamp[j] = 0;
+ for(j=0; j<256; j++)
+ clamp[CLAMPOFF+j] = (j>>4);
+ for(j=0; j<CLAMPOFF; j++)
+ clamp[CLAMPOFF+256+j] = (255>>4);
+ }
+
+ in = i->chans[0];
+ inp = in;
+ out = im->chans[0];
+ outp = out;
+
+ ered = malloc((dx+1)*sizeof(int));
+ egrn = malloc((dx+1)*sizeof(int));
+ eblu = malloc((dx+1)*sizeof(int));
+ if(ered==nil || egrn==nil || eblu==nil){
+ free(im->chans[0]);
+ free(im);
+ free(ered);
+ free(egrn);
+ free(eblu);
+ return _remaperror("remap: malloc failed: %r");
+ }
+ memset(ered, 0, (dx+1)*sizeof(int));
+ memset(egrn, 0, (dx+1)*sizeof(int));
+ memset(eblu, 0, (dx+1)*sizeof(int));
+
+ switch(i->chandesc){
+ default:
+ return _remaperror("remap: can't recognize channel type %d", i->chandesc);
+ case CRGB1:
+ if(cmap == nil)
+ return _remaperror("remap: image has no color map");
+ if(i->nchans != 1)
+ return _remaperror("remap: can't handle nchans %d", i->nchans);
+ for(j=1; j<=8; j++)
+ if(i->cmaplen == 3*(1<<j))
+ break;
+ if(j > 8)
+ return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
+ if(i->cmaplen != 3*256){
+ /* to avoid a range check in inner loop below, make a full-size cmap */
+ memmove(cmap1, cmap, i->cmaplen);
+ cmap = cmap1;
+ }
+ if(errdiff == 0){
+ k = 0;
+ for(j=0; j<256; j++){
+ r = cmap[k]>>4;
+ g = cmap[k+1]>>4;
+ b = cmap[k+2]>>4;
+ k += 3;
+ map[j] = closestrgb[b+16*(g+16*r)];
+ }
+ for(j=0; j<i->chanlen; j++)
+ out[j] = map[in[j]];
+ }else{
+ /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
+ for(y=0; y<dy; y++){
+ er = 0;
+ eg = 0;
+ eb = 0;
+ rp = ered;
+ gp = egrn;
+ bp = eblu;
+ for(x=0; x<dx; x++){
+ cm = &cmap[3 * *inp++];
+ r = cm[0] +*rp;
+ g = cm[1] +*gp;
+ b = cm[2] +*bp;
+
+ /* sanity checks are new */
+ if(r >= 256+CLAMPOFF)
+ r = 0;
+ if(g >= 256+CLAMPOFF)
+ g = 0;
+ if(b >= 256+CLAMPOFF)
+ b = 0;
+ r1 = clamp[r+CLAMPOFF];
+ g1 = clamp[g+CLAMPOFF];
+ b1 = clamp[b+CLAMPOFF];
+ if(r1 >= 16 || g1 >= 16 || b1 >= 16)
+ col = 0;
+ else
+ col = closestrgb[b1+16*(g1+16*r1)];
+ *outp++ = col;
+
+ rgb = rgbmap[col];
+ r -= (rgb>>16) & 0xFF;
+ t = (3*r)>>4;
+ *rp++ = t+er;
+ *rp += t;
+ er = r-3*t;
+
+ g -= (rgb>>8) & 0xFF;
+ t = (3*g)>>4;
+ *gp++ = t+eg;
+ *gp += t;
+ eg = g-3*t;
+
+ b -= rgb & 0xFF;
+ t = (3*b)>>4;
+ *bp++ = t+eb;
+ *bp += t;
+ eb = b-3*t;
+ }
+ }
+ }
+ break;
+
+ case CYCbCr:
+ closest = closestycbcr;
+ map3 = ycbcrmap;
+ goto Threecolor;
+
+ case CRGB:
+ closest = closestrgb;
+ map3 = rgbmap;
+
+ Threecolor:
+ if(i->nchans != 3)
+ return _remaperror("remap: RGB image has %d channels", i->nchans);
+ rpic = i->chans[0];
+ gpic = i->chans[1];
+ bpic = i->chans[2];
+ if(errdiff == 0){
+ for(j=0; j<i->chanlen; j++){
+ r = rpic[j]>>4;
+ g = gpic[j]>>4;
+ b = bpic[j]>>4;
+ out[j] = closest[b+16*(g+16*r)];
+ }
+ }else{
+ /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
+ for(y=0; y<dy; y++){
+ er = 0;
+ eg = 0;
+ eb = 0;
+ rp = ered;
+ gp = egrn;
+ bp = eblu;
+ for(x=0; x<dx; x++){
+ r = *rpic++ + *rp;
+ g = *gpic++ + *gp;
+ b = *bpic++ + *bp;
+ /*
+ * Errors can be uncorrectable if converting from YCbCr,
+ * since we can't guarantee that an extremal value of one of
+ * the components selects a color with an extremal value.
+ * If we don't, the errors accumulate without bound. This
+ * doesn't happen in RGB because the closest table can guarantee
+ * a color on the edge of the gamut, producing a zero error in
+ * that component. For the rotation YCbCr space, there may be
+ * no color that can guarantee zero error at the edge.
+ * Therefore we must clamp explicitly rather than by assuming
+ * an upper error bound of CLAMPOFF. The performance difference
+ * is miniscule anyway.
+ */
+ if(r < 0)
+ r = 0;
+ else if(r > 255)
+ r = 255;
+ if(g < 0)
+ g = 0;
+ else if(g > 255)
+ g = 255;
+ if(b < 0)
+ b = 0;
+ else if(b > 255)
+ b = 255;
+ r1 = r>>4;
+ g1 = g>>4;
+ b1 = b>>4;
+ col = closest[b1+16*(g1+16*r1)];
+ *outp++ = col;
+
+ rgb = map3[col];
+ r -= (rgb>>16) & 0xFF;
+ t = (3*r)>>4;
+ *rp++ = t+er;
+ *rp += t;
+ er = r-3*t;
+
+ g -= (rgb>>8) & 0xFF;
+ t = (3*g)>>4;
+ *gp++ = t+eg;
+ *gp += t;
+ eg = g-3*t;
+
+ b -= rgb & 0xFF;
+ t = (3*b)>>4;
+ *bp++ = t+eb;
+ *bp += t;
+ eb = b-3*t;
+ }
+ }
+ }
+ break;
+
+ case CY:
+ if(i->nchans != 1)
+ return _remaperror("remap: Y image has %d chans", i->nchans);
+ rpic = i->chans[0];
+ if(errdiff == 0){
+ for(j=0; j<i->chanlen; j++){
+ r = rpic[j]>>4;
+ *outp++ = closestrgb[r+16*(r+16*r)];
+ }
+ }else{
+ /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
+ for(y=0; y<dy; y++){
+ er = 0;
+ rp = ered;
+ for(x=0; x<dx; x++){
+ r = *inp++ + *rp;
+ r1 = clamp[r+CLAMPOFF];
+ col = closestrgb[r1+16*(r1+16*r1)];
+ *outp++ = col;
+
+ rgb = rgbmap[col];
+ r -= (rgb>>16) & 0xFF;
+ t = (3*r)>>4;
+ *rp++ = t+er;
+ *rp += t;
+ er = r-3*t;
+ }
+ }
+ }
+ break;
+ }
+ free(ered);
+ free(egrn);
+ free(eblu);
+ return im;
+}
diff --git a/src/cmd/jpg/totruecolor.c b/src/cmd/jpg/totruecolor.c
new file mode 100644
index 00000000..0da5d9a3
--- /dev/null
+++ b/src/cmd/jpg/totruecolor.c
@@ -0,0 +1,163 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+enum {
+ c1 = 2871, /* 1.402 * 2048 */
+ c2 = 705, /* 0.34414 * 2048 */
+ c3 = 1463, /* 0.71414 * 2048 */
+ c4 = 3629, /* 1.772 * 2048 */
+};
+
+Rawimage*
+totruecolor(Rawimage *i, int chandesc)
+{
+ int j, k;
+ Rawimage *im;
+ char err[ERRMAX];
+ uchar *rp, *gp, *bp, *cmap, *inp, *outp, cmap1[3*256];
+ int r, g, b, Y, Cr, Cb;
+
+ if(chandesc!=CY && chandesc!=CRGB24)
+ return _remaperror("remap: can't convert to chandesc %d", chandesc);
+
+ err[0] = '\0';
+ errstr(err, sizeof err); /* throw it away */
+ im = malloc(sizeof(Rawimage));
+ if(im == nil)
+ return nil;
+ memset(im, 0, sizeof(Rawimage));
+ if(chandesc == CY)
+ im->chanlen = i->chanlen;
+ else
+ im->chanlen = 3*i->chanlen;
+ im->chandesc = chandesc;
+ im->chans[0] = malloc(im->chanlen);
+ if(im->chans[0] == nil){
+ free(im);
+ return nil;
+ }
+ im->r = i->r;
+ im->nchans = 1;
+
+ cmap = i->cmap;
+
+ outp = im->chans[0];
+
+ switch(i->chandesc){
+ default:
+ return _remaperror("remap: can't recognize channel type %d", i->chandesc);
+ case CY:
+ if(i->nchans != 1)
+ return _remaperror("remap: Y image has %d chans", i->nchans);
+ if(chandesc == CY){
+ memmove(im->chans[0], i->chans[0], i->chanlen);
+ break;
+ }
+ /* convert to three color */
+ inp = i->chans[0];
+ for(j=0; j<i->chanlen; j++){
+ k = *inp++;
+ *outp++ = k;
+ *outp++ = k;
+ *outp++ = k;
+ }
+ break;
+
+ case CRGB1:
+ if(cmap == nil)
+ return _remaperror("remap: image has no color map");
+ if(i->nchans != 1)
+ return _remaperror("remap: can't handle nchans %d", i->nchans);
+ for(j=1; j<=8; j++)
+ if(i->cmaplen == 3*(1<<j))
+ break;
+ if(j > 8)
+ return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
+ if(i->cmaplen != 3*256){
+ /* to avoid a range check in loop below, make a full-size cmap */
+ memmove(cmap1, cmap, i->cmaplen);
+ cmap = cmap1;
+ }
+ inp = i->chans[0];
+ if(chandesc == CY){
+ for(j=0; j<i->chanlen; j++){
+ k = *inp++;
+ r = cmap[3*k+2];
+ g = cmap[3*k+1];
+ b = cmap[3*k+0];
+ r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */
+ *outp++ = r;
+ }
+ }else{
+ for(j=0; j<i->chanlen; j++){
+ k = *inp++;
+ *outp++ = cmap[3*k+2];
+ *outp++ = cmap[3*k+1];
+ *outp++ = cmap[3*k+0];
+ }
+ }
+ break;
+
+ case CRGB:
+ if(i->nchans != 3)
+ return _remaperror("remap: can't handle nchans %d", i->nchans);
+ rp = i->chans[0];
+ gp = i->chans[1];
+ bp = i->chans[2];
+ if(chandesc == CY){
+ for(j=0; j<i->chanlen; j++){
+ r = *bp++;
+ g = *gp++;
+ b = *rp++;
+ r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */
+ *outp++ = r;
+ }
+ }else
+ for(j=0; j<i->chanlen; j++){
+ *outp++ = *bp++;
+ *outp++ = *gp++;
+ *outp++ = *rp++;
+ }
+ break;
+
+ case CYCbCr:
+ if(i->nchans != 3)
+ return _remaperror("remap: can't handle nchans %d", i->nchans);
+ rp = i->chans[0];
+ gp = i->chans[1];
+ bp = i->chans[2];
+ for(j=0; j<i->chanlen; j++){
+ Y = *rp++ << 11;
+ Cb = *gp++ - 128;
+ Cr = *bp++ - 128;
+ r = (Y+c1*Cr) >> 11;
+ g = (Y-c2*Cb-c3*Cr) >> 11;
+ b = (Y+c4*Cb) >> 11;
+ if(r < 0)
+ r = 0;
+ if(r > 255)
+ r = 255;
+ if(g < 0)
+ g = 0;
+ if(g > 255)
+ g = 255;
+ if(b < 0)
+ b = 0;
+ if(b > 255)
+ b = 255;
+ if(chandesc == CY){
+ r = (2125*r + 7154*g + 721*b)/10000;
+ *outp++ = r;
+ }else{
+ *outp++ = b;
+ *outp++ = g;
+ *outp++ = r;
+ }
+ }
+ break;
+ }
+ return im;
+}
diff --git a/src/cmd/jpg/writegif.c b/src/cmd/jpg/writegif.c
new file mode 100644
index 00000000..16402ef8
--- /dev/null
+++ b/src/cmd/jpg/writegif.c
@@ -0,0 +1,568 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+#include "imagefile.h"
+
+enum
+{
+ Nhash = 4001,
+ Nbuf = 300,
+};
+
+typedef struct Entry Entry;
+typedef struct IO IO;
+
+
+struct Entry
+{
+ int index;
+ int prefix;
+ int exten;
+ Entry *next;
+};
+
+struct IO
+{
+ Biobuf *fd;
+ uchar buf[Nbuf];
+ int i;
+ int nbits; /* bits in right side of shift register */
+ int sreg; /* shift register */
+};
+
+static Rectangle mainrect;
+static Entry tbl[4096];
+static uchar *colormap[5]; /* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */
+#define GREYMAP 4
+static int colormapsize[] = { 2, 4, 16, 256, 256 }; /* 2 for zero is an odd property of GIF */
+
+static void writeheader(Biobuf*, Rectangle, int, ulong, int);
+static void writedescriptor(Biobuf*, Rectangle);
+static char* writedata(Biobuf*, Image*, Memimage*);
+static void writecomment(Biobuf *fd, char*);
+static void writegraphiccontrol(Biobuf *fd, int, int);
+static void* gifmalloc(ulong);
+static void encode(Biobuf*, Rectangle, int, uchar*, uint);
+
+static
+char*
+startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount)
+{
+ int i;
+
+ for(i=0; i<nelem(tbl); i++)
+ tbl[i] = (Entry){i, -1, i, nil};
+
+ switch(chan){
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ case CMAP8:
+ case GREY8:
+ break;
+ default:
+ return "WriteGIF: can't handle channel type";
+ }
+
+ mainrect = r;
+ writeheader(fd, r, depth, chan, loopcount);
+ return nil;
+}
+
+char*
+startgif(Biobuf *fd, Image *image, int loopcount)
+{
+ return startgif0(fd, image->chan, image->r, image->depth, loopcount);
+}
+
+char*
+memstartgif(Biobuf *fd, Memimage *memimage, int loopcount)
+{
+ return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount);
+}
+
+static
+char*
+writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans)
+{
+ char *err;
+
+ switch(chan){
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ case CMAP8:
+ case GREY8:
+ break;
+ default:
+ return "WriteGIF: can't handle channel type";
+ }
+
+ writecomment(fd, comment);
+ writegraphiccontrol(fd, dt, trans);
+ writedescriptor(fd, r);
+
+ err = writedata(fd, image, memimage);
+ if(err != nil)
+ return err;
+
+ return nil;
+}
+
+char*
+writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans)
+{
+ return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans);
+}
+
+char*
+memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans)
+{
+ return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans);
+}
+
+/*
+ * Write little-endian 16-bit integer
+ */
+static
+void
+put2(Biobuf *fd, int i)
+{
+ Bputc(fd, i);
+ Bputc(fd, i>>8);
+}
+
+/*
+ * Get color map for all ldepths, in format suitable for writing out
+ */
+static
+void
+getcolormap(void)
+{
+ int i, col;
+ ulong rgb;
+ uchar *c;
+
+ if(colormap[0] != nil)
+ return;
+ for(i=0; i<nelem(colormap); i++)
+ colormap[i] = gifmalloc(3* colormapsize[i]);
+ c = colormap[GREYMAP]; /* GREY8 */
+ for(i=0; i<256; i++){
+ c[3*i+0] = i; /* red */
+ c[3*i+1] = i; /* green */
+ c[3*i+2] = i; /* blue */
+ }
+ c = colormap[3]; /* RGBV */
+ for(i=0; i<256; i++){
+ rgb = cmap2rgb(i);
+ c[3*i+0] = (rgb>>16) & 0xFF; /* red */
+ c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
+ c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
+ }
+ c = colormap[2]; /* GREY4 */
+ for(i=0; i<16; i++){
+ col = (i<<4)|i;
+ rgb = cmap2rgb(col);
+ c[3*i+0] = (rgb>>16) & 0xFF; /* red */
+ c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
+ c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
+ }
+ c = colormap[1]; /* GREY2 */
+ for(i=0; i<4; i++){
+ col = (i<<6)|(i<<4)|(i<<2)|i;
+ rgb = cmap2rgb(col);
+ c[3*i+0] = (rgb>>16) & 0xFF; /* red */
+ c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
+ c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
+ }
+ c = colormap[0]; /* GREY1 */
+ for(i=0; i<2; i++){
+ if(i == 0)
+ col = 0;
+ else
+ col = 0xFF;
+ rgb = cmap2rgb(col);
+ c[3*i+0] = (rgb>>16) & 0xFF; /* red */
+ c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
+ c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
+ }
+}
+
+/*
+ * Write header, logical screen descriptor, and color map
+ */
+static
+void
+writeheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount)
+{
+ /* Header */
+ Bprint(fd, "%s", "GIF89a");
+
+ /* Logical Screen Descriptor */
+ put2(fd, Dx(r));
+ put2(fd, Dy(r));
+
+ /* Color table present, 4 bits per color (for RGBV best case), size of color map */
+ Bputc(fd, (1<<7)|(3<<4)|(depth-1)); /* not right for GREY8, but GIF doesn't let us specify enough bits */
+ Bputc(fd, 0xFF); /* white background (doesn't matter anyway) */
+ Bputc(fd, 0); /* pixel aspect ratio - unused */
+
+ /* Global Color Table */
+ getcolormap();
+ if(chan == GREY8)
+ depth = GREYMAP;
+ else
+ depth = drawlog2[depth];
+ Bwrite(fd, colormap[depth], 3*colormapsize[depth]);
+
+ if(loopcount >= 0){ /* hard-to-discover way to force cycled animation */
+ /* Application Extension with (1 loopcountlo loopcounthi) as data */
+ Bputc(fd, 0x21);
+ Bputc(fd, 0xFF);
+ Bputc(fd, 11);
+ Bwrite(fd, "NETSCAPE2.0", 11);
+ Bputc(fd, 3);
+ Bputc(fd, 1);
+ put2(fd, loopcount);
+ Bputc(fd, 0);
+ }
+}
+
+/*
+ * Write optional comment block
+ */
+static
+void
+writecomment(Biobuf *fd, char *comment)
+{
+ int n;
+
+ if(comment==nil || comment[0]=='\0')
+ return;
+
+ /* Comment extension and label */
+ Bputc(fd, 0x21);
+ Bputc(fd, 0xFE);
+
+ /* Comment data */
+ n = strlen(comment);
+ if(n > 255)
+ n = 255;
+ Bputc(fd, n);
+ Bwrite(fd, comment, n);
+
+ /* Block terminator */
+ Bputc(fd, 0x00);
+}
+
+/*
+ * Write optional control block (sets Delay Time)
+ */
+static
+void
+writegraphiccontrol(Biobuf *fd, int dt, int trans)
+{
+ if(dt < 0 && trans < 0)
+ return;
+
+ /* Comment extension and label and block size*/
+ Bputc(fd, 0x21);
+ Bputc(fd, 0xF9);
+ Bputc(fd, 0x04);
+
+ /* Disposal method and other flags (none) */
+ if(trans >= 0)
+ Bputc(fd, 0x01);
+ else
+ Bputc(fd, 0x00);
+
+ /* Delay time, in centisec (argument is millisec for sanity) */
+ if(dt < 0)
+ dt = 0;
+ else if(dt < 10)
+ dt = 1;
+ else
+ dt = (dt+5)/10;
+ put2(fd, dt);
+
+ /* Transparency index */
+ if(trans < 0)
+ trans = 0;
+ Bputc(fd, trans);
+
+ /* Block terminator */
+ Bputc(fd, 0x00);
+}
+
+/*
+ * Write image descriptor
+ */
+static
+void
+writedescriptor(Biobuf *fd, Rectangle r)
+{
+ /* Image Separator */
+ Bputc(fd, 0x2C);
+
+ /* Left, top, width, height */
+ put2(fd, r.min.x-mainrect.min.x);
+ put2(fd, r.min.y-mainrect.min.y);
+ put2(fd, Dx(r));
+ put2(fd, Dy(r));
+ /* no special processing */
+ Bputc(fd, 0);
+}
+
+/*
+ * Write data
+ */
+static
+char*
+writedata(Biobuf *fd, Image *image, Memimage *memimage)
+{
+ char *err;
+ uchar *data;
+ int ndata, depth;
+ Rectangle r;
+
+ if(memimage != nil){
+ r = memimage->r;
+ depth = memimage->depth;
+ }else{
+ r = image->r;
+ depth = image->depth;
+ }
+
+ /* LZW Minimum code size */
+ if(depth == 1)
+ Bputc(fd, 2);
+ else
+ Bputc(fd, depth);
+
+ /*
+ * Read image data into memory
+ * potentially one extra byte on each end of each scan line
+ */
+ ndata = Dy(r)*(2+(Dx(r)>>(3-drawlog2[depth])));
+ data = gifmalloc(ndata);
+ if(memimage != nil)
+ ndata = unloadmemimage(memimage, r, data, ndata);
+ else
+ ndata = unloadimage(image, r, data, ndata);
+ if(ndata < 0){
+ err = gifmalloc(ERRMAX);
+ snprint(err, ERRMAX, "WriteGIF: %r");
+ free(data);
+ return err;
+ }
+
+ /* Encode and emit the data */
+ encode(fd, r, depth, data, ndata);
+ free(data);
+
+ /* Block Terminator */
+ Bputc(fd, 0);
+ return nil;
+}
+
+/*
+ * Write trailer
+ */
+void
+endgif(Biobuf *fd)
+{
+ Bputc(fd, 0x3B);
+ Bflush(fd);
+}
+
+void
+memendgif(Biobuf *fd)
+{
+ endgif(fd);
+}
+
+/*
+ * Put n bits of c into output at io.buf[i];
+ */
+static
+void
+output(IO *io, int c, int n)
+{
+ if(c < 0){
+ if(io->nbits != 0)
+ io->buf[io->i++] = io->sreg;
+ Bputc(io->fd, io->i);
+ Bwrite(io->fd, io->buf, io->i);
+ io->nbits = 0;
+ return;
+ }
+
+ if(io->nbits+n >= 31){
+ fprint(2, "panic: WriteGIF sr overflow\n");
+ exits("WriteGIF panic");
+ }
+ io->sreg |= c<<io->nbits;
+ io->nbits += n;
+
+ while(io->nbits >= 8){
+ io->buf[io->i++] = io->sreg;
+ io->sreg >>= 8;
+ io->nbits -= 8;
+ }
+
+ if(io->i >= 255){
+ Bputc(io->fd, 255);
+ Bwrite(io->fd, io->buf, 255);
+ memmove(io->buf, io->buf+255, io->i-255);
+ io->i -= 255;
+ }
+}
+
+/*
+ * LZW encoder
+ */
+static
+void
+encode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata)
+{
+ int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel;
+ int CTM, EOD, codesize, ld0, datai, x, ld, pm;
+ int nentry, maxentry, early;
+ Entry *e, *oe;
+ IO *io;
+ Entry **hash;
+
+ first = 1;
+ ld = drawlog2[depth];
+ /* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */
+ ld0 = ld;
+ if(ld0 == 0)
+ ld0 = 1;
+ codesize = (1<<ld0);
+ CTM = 1<<codesize;
+ EOD = CTM+1;
+
+ io = gifmalloc(sizeof(IO));
+ io->fd = fd;
+ sreg = 0;
+ nbits = 0;
+ bitsperpixel = 1<<ld;
+ pm = (1<<bitsperpixel)-1;
+
+ datai = 0;
+ x = r.min.x;
+ hash = gifmalloc(Nhash*sizeof(Entry*));
+
+Init:
+ memset(hash, 0, Nhash*sizeof(Entry*));
+ csize = codesize+1;
+ nentry = EOD+1;
+ maxentry = (1<<csize);
+ for(i = 0; i<nentry; i++){
+ e = &tbl[i];
+ h = (e->prefix<<24) | (e->exten<<8);
+ h %= Nhash;
+ if(h < 0)
+ h += Nhash;
+ e->next = hash[h];
+ hash[h] = e;
+ }
+ prefix = -1;
+ if(first)
+ output(io, CTM, csize);
+ first = 0;
+
+ /*
+ * Scan over pixels. Because of partially filled bytes on ends of scan lines,
+ * which must be ignored in the data stream passed to GIF, this is more
+ * complex than we'd like.
+ */
+Next:
+ for(;;){
+ if(ld != 3){
+ /* beginning of scan line is difficult; prime the shift register */
+ if(x == r.min.x){
+ if(datai == ndata)
+ break;
+ sreg = data[datai++];
+ nbits = 8-((x&(7>>ld))<<ld);
+ }
+ x++;
+ if(x == r.max.x)
+ x = r.min.x;
+ }
+ if(nbits == 0){
+ if(datai == ndata)
+ break;
+ sreg = data[datai++];
+ nbits = 8;
+ }
+ nbits -= bitsperpixel;
+ c = sreg>>nbits & pm;
+ h = prefix<<24 | c<<8;
+ h %= Nhash;
+ if(h < 0)
+ h += Nhash;
+ oe = nil;
+ for(e = hash[h]; e!=nil; e=e->next){
+ if(e->prefix == prefix && e->exten == c){
+ if(oe != nil){
+ oe->next = e->next;
+ e->next = hash[h];
+ hash[h] = e;
+ }
+ prefix = e->index;
+ goto Next;
+ }
+ oe = e;
+ }
+
+ output(io, prefix, csize);
+ early = 0; /* peculiar tiff feature here for reference */
+ if(nentry == maxentry-early){
+ if(csize == 12){
+ nbits += bitsperpixel; /* unget pixel */
+ x--;
+ if(ld != 3 && x == r.min.x)
+ datai--;
+ output(io, CTM, csize);
+ goto Init;
+ }
+ csize++;
+ maxentry = (1<<csize);
+ }
+
+ e = &tbl[nentry];
+ e->prefix = prefix;
+ e->exten = c;
+ e->next = hash[h];
+ hash[h] = e;
+
+ prefix = c;
+ nentry++;
+ }
+
+ output(io, prefix, csize);
+ output(io, EOD, csize);
+ output(io, -1, csize);
+ free(io);
+ free(hash);
+}
+
+static
+void*
+gifmalloc(ulong sz)
+{
+ void *v;
+ v = malloc(sz);
+ if(v == nil) {
+ fprint(2, "WriteGIF: out of memory allocating %ld\n", sz);
+abort();
+ exits("mem");
+ }
+ memset(v, 0, sz);
+ return v;
+}
diff --git a/src/cmd/jpg/writepng.c b/src/cmd/jpg/writepng.c
new file mode 100644
index 00000000..44bfe10c
--- /dev/null
+++ b/src/cmd/jpg/writepng.c
@@ -0,0 +1,220 @@
+// based on PNG 1.2 specification, July 1999 (see also rfc2083)
+// Alpha is not supported yet because of lack of industry acceptance and
+// because Plan9 Image uses premultiplied alpha, so png can't be lossless.
+// Only 24bit color supported, because 8bit may as well use GIF.
+
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <ctype.h>
+#include <bio.h>
+#include <flate.h>
+#include "imagefile.h"
+
+enum{ IDATSIZE = 20000,
+ FilterNone = 0,
+};
+
+typedef struct ZlibR{
+ uchar *data;
+ int width;
+ int nrow, ncol;
+ int row, col; // next pixel to send
+} ZlibR;
+
+typedef struct ZlibW{
+ Biobuf *bo;
+ uchar *buf;
+ uchar *b; // next place to write
+ uchar *e; // past end of buf
+} ZlibW;
+
+static ulong *crctab;
+static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
+
+static void
+put4(uchar *a, ulong v)
+{
+ a[0] = v>>24;
+ a[1] = v>>16;
+ a[2] = v>>8;
+ a[3] = v;
+}
+
+static void
+chunk(Biobuf *bo, char *type, uchar *d, int n)
+{
+ uchar buf[4];
+ ulong crc = 0;
+
+ if(strlen(type) != 4)
+ return;
+ put4(buf, n);
+ Bwrite(bo, buf, 4);
+ Bwrite(bo, type, 4);
+ Bwrite(bo, d, n);
+ crc = blockcrc(crctab, crc, type, 4);
+ crc = blockcrc(crctab, crc, d, n);
+ put4(buf, crc);
+ Bwrite(bo, buf, 4);
+}
+
+static int
+zread(void *va, void *buf, int n)
+{
+ ZlibR *z = va;
+ int nrow = z->nrow;
+ int ncol = z->ncol;
+ uchar *b = buf, *e = b+n, *img;
+ int i, pixels; // number of pixels in row that can be sent now
+
+ while(b+3 <= e){ // loop over image rows
+ if(z->row >= nrow)
+ break;
+ if(z->col==0)
+ *b++ = FilterNone;
+ pixels = (e-b)/3;
+ if(pixels > ncol - z->col)
+ pixels = ncol - z->col;
+ img = z->data + z->width * z->row + 3 * z->col;
+
+ // Plan 9 image format is BGR?!!!
+ // memmove(b, img, 3*pixels);
+ // b += 3*pixels;
+ for(i=0; i<pixels; i++, img += 3){
+ *b++ = img[2];
+ *b++ = img[1];
+ *b++ = img[0];
+ }
+
+ z->col += pixels;
+ if(z->col >= ncol){
+ z->col = 0;
+ z->row++;
+ }
+ }
+ return b - (uchar*)buf;
+}
+
+static void
+IDAT(ZlibW *z)
+{
+ chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
+ z->b = z->buf;
+}
+
+static int
+zwrite(void *va, void *buf, int n)
+{
+ ZlibW *z = va;
+ uchar *b = buf, *e = b+n;
+ int m;
+
+ while(b < e){ // loop over IDAT chunks
+ m = z->e - z->b;
+ if(m > e - b)
+ m = e - b;
+ memmove(z->b, b, m);
+ z->b += m;
+ b += m;
+ if(z->b >= z->e)
+ IDAT(z);
+ }
+ return n;
+}
+
+static Memimage*
+memRGB(Memimage *i)
+{
+ Memimage *ni;
+
+ if(i->chan == RGB24)
+ return i;
+
+ ni = allocmemimage(i->r, RGB24);
+ if(ni == nil)
+ return ni;
+ memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
+ return ni;
+}
+
+char*
+memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
+{
+ uchar buf[200], *h;
+ ulong vgamma;
+ int err, n;
+ ZlibR zr;
+ ZlibW zw;
+ int nrow = r->r.max.y - r->r.min.y;
+ int ncol = r->r.max.x - r->r.min.x;
+ Tm *tm;
+ Memimage *rgb;
+
+ rgb = memRGB(r);
+ if(rgb == nil)
+ return "allocmemimage nil";
+ crctab = mkcrctab(0xedb88320);
+ if(crctab == nil)
+ sysfatal("mkcrctab error");
+ deflateinit();
+
+ Bwrite(bo, PNGmagic, sizeof PNGmagic);
+ // IHDR chunk
+ h = buf;
+ put4(h, ncol); h += 4;
+ put4(h, nrow); h += 4;
+ *h++ = 8; // bit depth = 24 bit per pixel
+ *h++ = 2; // color type = rgb
+ *h++ = 0; // compression method = deflate
+ *h++ = 0; // filter method
+ *h++ = 0; // interlace method = no interlace
+ chunk(bo, "IHDR", buf, h-buf);
+
+ tm = gmtime(time(0));
+ h = buf;
+ *h++ = (tm->year + 1900)>>8;
+ *h++ = (tm->year + 1900)&0xff;
+ *h++ = tm->mon + 1;
+ *h++ = tm->mday;
+ *h++ = tm->hour;
+ *h++ = tm->min;
+ *h++ = tm->sec;
+ chunk(bo, "tIME", buf, h-buf);
+
+ if(II->fields_set & II_GAMMA){
+ vgamma = II->gamma*100000;
+ put4(buf, vgamma);
+ chunk(bo, "gAMA", buf, 4);
+ }
+
+ if(II->fields_set & II_COMMENT){
+ strncpy((char*)buf, "Comment", sizeof buf);
+ n = strlen((char*)buf)+1; // leave null between Comment and text
+ strncpy((char*)(buf+n), II->comment, sizeof buf - n);
+ chunk(bo, "tEXt", buf, n+strlen((char*)buf+n));
+ }
+
+ // image chunks
+ zr.nrow = nrow;
+ zr.ncol = ncol;
+ zr.width = rgb->width * sizeof(ulong);
+ zr.data = rgb->data->bdata;
+ zr.row = zr.col = 0;
+ zw.bo = bo;
+ zw.buf = malloc(IDATSIZE);
+ zw.b = zw.buf;
+ zw.e = zw.b + IDATSIZE;
+ err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0);
+ if(zw.b > zw.buf)
+ IDAT(&zw);
+ free(zw.buf);
+ if(err)
+ sysfatal("deflatezlib %s\n", flateerr(err));
+ chunk(bo, "IEND", nil, 0);
+
+ if(r != rgb)
+ freememimage(rgb);
+ return nil;
+}
diff --git a/src/cmd/jpg/writeppm.c b/src/cmd/jpg/writeppm.c
new file mode 100644
index 00000000..c8378652
--- /dev/null
+++ b/src/cmd/jpg/writeppm.c
@@ -0,0 +1,164 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+#include "imagefile.h"
+
+#define MAXLINE 70
+
+/*
+ * Write data
+ */
+static
+char*
+writedata(Biobuf *fd, Image *image, Memimage *memimage)
+{
+ char *err;
+ uchar *data;
+ int i, x, y, ndata, depth, col, pix, xmask, pmask;
+ ulong chan;
+ Rectangle r;
+
+ if(memimage != nil){
+ r = memimage->r;
+ depth = memimage->depth;
+ chan = memimage->chan;
+ }else{
+ r = image->r;
+ depth = image->depth;
+ chan = image->chan;
+ }
+
+ /*
+ * Read image data into memory
+ * potentially one extra byte on each end of each scan line
+ */
+ ndata = Dy(r)*(2+Dx(r)*depth/8);
+ data = malloc(ndata);
+ if(data == nil)
+ return "WritePPM: malloc failed";
+ if(memimage != nil)
+ ndata = unloadmemimage(memimage, r, data, ndata);
+ else
+ ndata = unloadimage(image, r, data, ndata);
+ if(ndata < 0){
+ err = malloc(ERRMAX);
+ if(err == nil)
+ return "WritePPM: malloc failed";
+ snprint(err, ERRMAX, "WriteGIF: %r");
+ free(data);
+ return err;
+ }
+
+ /* Encode and emit the data */
+ col = 0;
+ switch(chan){
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ pmask = (1<<depth)-1;
+ xmask = 7>>drawlog2[depth];
+ for(y=r.min.y; y<r.max.y; y++){
+ i = (y-r.min.y)*bytesperline(r, depth);
+ for(x=r.min.x; x<r.max.x; x++){
+ pix = (data[i]>>depth*((xmask-x)&xmask))&pmask;
+ if(((x+1)&xmask) == 0)
+ i++;
+ col += Bprint(fd, "%d ", pix);
+ if(col >= MAXLINE-(2+1)){
+ Bprint(fd, "\n");
+ col = 0;
+ }else
+ col += Bprint(fd, " ");
+ }
+ }
+ break;
+ case GREY8:
+ for(i=0; i<ndata; i++){
+ col += Bprint(fd, "%d ", data[i]);
+ if(col >= MAXLINE-(4+1)){
+ Bprint(fd, "\n");
+ col = 0;
+ }else
+ col += Bprint(fd, " ");
+ }
+ break;
+ case RGB24:
+ for(i=0; i<ndata; i+=3){
+ col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]);
+ if(col >= MAXLINE-(4+4+4+1)){
+ Bprint(fd, "\n");
+ col = 0;
+ }else
+ col += Bprint(fd, " ");
+ }
+ break;
+ default:
+ return "WritePPM: can't handle channel type";
+ }
+
+ return nil;
+}
+
+static
+char*
+writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment)
+{
+ char *err;
+
+ switch(chan){
+ case GREY1:
+ Bprint(fd, "P1\n");
+ break;
+ case GREY2:
+ case GREY4:
+ case GREY8:
+ Bprint(fd, "P2\n");
+ break;
+ case RGB24:
+ Bprint(fd, "P3\n");
+ break;
+ default:
+ return "WritePPM: can't handle channel type";
+ }
+
+ if(comment!=nil && comment[0]!='\0'){
+ Bprint(fd, "# %s", comment);
+ if(comment[strlen(comment)-1] != '\n')
+ Bprint(fd, "\n");
+ }
+ Bprint(fd, "%d %d\n", Dx(r), Dy(r));
+
+ /* maximum pixel value */
+ switch(chan){
+ case GREY2:
+ Bprint(fd, "%d\n", 3);
+ break;
+ case GREY4:
+ Bprint(fd, "%d\n", 15);
+ break;
+ case GREY8:
+ case RGB24:
+ Bprint(fd, "%d\n", 255);
+ break;
+ }
+
+ err = writedata(fd, image, memimage);
+
+ Bprint(fd, "\n");
+ Bflush(fd);
+ return err;
+}
+
+char*
+writeppm(Biobuf *fd, Image *image, char *comment)
+{
+ return writeppm0(fd, image, nil, image->r, image->chan, comment);
+}
+
+char*
+memwriteppm(Biobuf *fd, Memimage *memimage, char *comment)
+{
+ return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment);
+}
diff --git a/src/cmd/jpg/writerawimage.c b/src/cmd/jpg/writerawimage.c
new file mode 100644
index 00000000..26e0cd16
--- /dev/null
+++ b/src/cmd/jpg/writerawimage.c
@@ -0,0 +1,206 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include "imagefile.h"
+
+/*
+ * Hacked version for writing from Rawimage to file.
+ * Assumes 8 bits per component.
+ */
+
+#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
+writerawimage(int fd, Rawimage *i)
+{
+ uchar *outbuf, *outp, *eout; /* encoded data, pointer, end */
+ uchar *loutp; /* start of encoded line */
+ Hlist *hash; /* heads of hash chains of past strings */
+ Hlist *chain, *hp; /* hash chain members, pointer */
+ Hlist *cp; /* next Hlist to fall out of window */
+ int h; /* hash value */
+ uchar *line, *eline; /* input line, end pointer */
+ uchar *data, *edata; /* input buffer, end pointer */
+ ulong n; /* length of input buffer */
+ 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 ncblock; /* size of buffer */
+ Rectangle r;
+ uchar *p, *q, *s, *es, *t;
+ char hdr[11+5*12+1], buf[16];
+ ulong desc;
+
+ r = i->r;
+ switch(i->chandesc){
+ default:
+ werrstr("can't handle chandesc %d", i->chandesc);
+ return -1;
+ case CY:
+ bpl = Dx(r);
+ desc = GREY8;
+ break;
+ case CYA16:
+ bpl = 2*Dx(r);
+ desc = CHAN2(CGrey, 8, CAlpha, 8);
+ break;
+ case CRGBV:
+ bpl = Dx(r);
+ desc = CMAP8;
+ break;
+ case CRGBVA16:
+ bpl = 2*Dx(r);
+ desc = CHAN2(CMap, 8, CAlpha, 8);
+ break;
+ case CRGB24:
+ bpl = 3*Dx(r);
+ desc = RGB24;
+ break;
+ case CRGBA32:
+ bpl = 4*Dx(r);
+ desc = RGBA32;
+ break;
+ }
+ ncblock = _compblocksize(r, bpl/Dx(r));
+ outbuf = malloc(ncblock);
+ hash = malloc(NHASH*sizeof(Hlist));
+ chain = malloc(NMEM*sizeof(Hlist));
+ if(outbuf == 0 || hash == 0 || chain == 0){
+ ErrOut:
+ free(outbuf);
+ free(hash);
+ free(chain);
+ return -1;
+ }
+ n = Dy(r)*bpl;
+ data = i->chans[0];
+ sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ",
+ chantostr(buf, desc), r.min.x, r.min.y, r.max.x, r.max.y);
+ if(write(fd, hdr, 11+5*12) != 11+5*12){
+ werrstr("i/o error writing header");
+ 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){
+ werrstr("compressor out of sync");
+ goto ErrOut;
+ }
+ n = loutp-outbuf;
+ sprint(hdr, "%11d %11ld ", r.max.y, n);
+ write(fd, hdr, 2*12);
+ write(fd, outbuf, n);
+ r.min.y = r.max.y;
+ }
+ free(outbuf);
+ free(hash);
+ free(chain);
+ return 0;
+}
diff --git a/src/cmd/jpg/yuv.c b/src/cmd/jpg/yuv.c
new file mode 100644
index 00000000..929ccf39
--- /dev/null
+++ b/src/cmd/jpg/yuv.c
@@ -0,0 +1,211 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "imagefile.h"
+
+int cflag = 0;
+int dflag = 0;
+int eflag = 0;
+int nineflag = 0;
+int threeflag = 0;
+int output = 0;
+ulong outchan = CMAP8;
+int defaultcolor = 1;
+Image *image;
+
+enum{
+ Border = 2,
+ Edge = 5
+};
+
+char *show(int, char*);
+
+Rawimage** readyuv(int fd, int colorspace);
+
+void
+eresized(int new)
+{
+ Rectangle r;
+
+ if(new && getwindow(display, Refnone) < 0){
+ fprint(2, "yuv: can't reattach to window\n");
+ exits("resize");
+ }
+ if(image == nil)
+ return;
+ r = insetrect(screen->clipr, Edge+Border);
+ r.max.x = r.min.x+Dx(image->r);
+ r.max.y = r.min.y+Dy(image->r);
+ border(screen, r, -Border, nil, ZP);
+ draw(screen, r, image, nil, image->r.min);
+ flushimage(display, 1);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, i;
+ char *err;
+
+ ARGBEGIN{
+ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
+ threeflag++;
+ /* fall through */
+ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ defaultcolor = 0;
+ outchan = RGB24;
+ break;
+ case 'c': /* produce encoded, compressed, bitmap file; no display by default */
+ cflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ case 'd': /* suppress display of image */
+ dflag++;
+ break;
+ case 'e': /* disable floyd-steinberg error diffusion */
+ eflag++;
+ break;
+ case 'k': /* force black and white */
+ defaultcolor = 0;
+ outchan = GREY8;
+ break;
+ case 'v': /* force RGBV */
+ defaultcolor = 0;
+ outchan = CMAP8;
+ break;
+ case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
+ nineflag++;
+ dflag++;
+ output++;
+ if(defaultcolor)
+ outchan = CMAP8;
+ break;
+ default:
+ fprint(2, "usage: yuv -39cdektv [file.yuv ...]\n");
+ exits("usage");
+ }ARGEND;
+
+ err = nil;
+ if(argc == 0)
+ err = show(0, "<stdin>");
+ else{
+ for(i=0; i<argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd < 0){
+ fprint(2, "yuv: can't open %s: %r\n", argv[i]);
+ err = "open";
+ }else{
+ err = show(fd, argv[i]);
+ close(fd);
+ }
+ if((nineflag || cflag) && argc>1 && err==nil){
+ fprint(2, "yuv: exiting after one file\n");
+ break;
+ }
+ }
+ }
+ exits(err);
+}
+
+int
+init(void)
+{
+ static int inited;
+
+ if(inited == 0){
+ if(initdraw(0, 0, 0) < 0){
+ fprint(2, "yuv: initdraw failed: %r");
+ return -1;
+ }
+ einit(Ekeyboard|Emouse);
+ inited++;
+ }
+ return 1;
+}
+
+char*
+show(int fd, char *name)
+{
+ Rawimage **array, *r, *c;
+ Image *i;
+ int j, ch;
+ char buf[32];
+
+ array = readyuv(fd, CYCbCr);
+ if(array == nil || array[0]==nil){
+ fprint(2, "yuv: decode %s failed: %r\n", name);
+ return "decode";
+ }
+ if(!dflag){
+ if(init() < 0)
+ return "initdraw";
+ if(defaultcolor && screen->depth>8)
+ outchan = RGB24;
+ }
+ r = array[0];
+ if(outchan == CMAP8)
+ c = torgbv(r, !eflag);
+ else{
+ if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
+ c = totruecolor(r, CY);
+ else
+ c = totruecolor(r, CRGB24);
+ }
+ if(c == nil){
+ fprint(2, "yuv: converting %s to local format failed: %r\n", name);
+ return "torgbv";
+ }
+ if(!dflag){
+ if(r->chandesc == CY)
+ i = allocimage(display, c->r, GREY8, 0, 0);
+ else
+ i = allocimage(display, c->r, outchan, 0, 0);
+ if(i == nil){
+ fprint(2, "yuv: allocimage %s failed: %r\n", name);
+ return "allocimage";
+ }
+ if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
+ fprint(2, "yuv: loadimage %s failed: %r\n", name);
+ return "loadimage";
+ }
+ image = i;
+ eresized(0);
+ if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
+ exits(nil);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+ image = nil;
+ freeimage(i);
+ }
+ if(nineflag){
+ chantostr(buf, outchan);
+ print("%11s %11d %11d %11d %11d ", buf,
+ c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
+ if(write(1, c->chans[0], c->chanlen) != c->chanlen){
+ fprint(2, "yuv: %s: write error %r\n", name);
+ return "write";
+ }
+ }else if(cflag){
+ if(writerawimage(1, c) < 0){
+ fprint(2, "yuv: %s: write error: %r\n", name);
+ return "write";
+ }
+ }
+ for(j=0; j<r->nchans; j++)
+ free(r->chans[j]);
+ free(r->cmap);
+ free(r);
+ free(array);
+ if(c){
+ free(c->chans[0]);
+ free(c);
+ }
+ return nil;
+}
diff --git a/src/cmd/map/index.c b/src/cmd/map/index.c
new file mode 100644
index 00000000..0ff75f5d
--- /dev/null
+++ b/src/cmd/map/index.c
@@ -0,0 +1,88 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static proj Yaitoff(double p0, double p1){USED(p0); USED(p1); return aitoff();}
+static proj Yalbers(double p0,double p1){USED(p0); USED(p1); return albers(p0,p1);}
+static proj Yazequalarea(double p0, double p1){USED(p0); USED(p1); return azequalarea();}
+static proj Yazequidistant(double p0, double p1){USED(p0); USED(p1); return azequidistant();}
+static proj Ybicentric(double p0,double p1){USED(p0); USED(p1); return bicentric(p0);}
+static proj Ybonne(double p0,double p1){USED(p0); USED(p1); return bonne(p0);}
+static proj Yconic(double p0,double p1){USED(p0); USED(p1); return conic(p0);}
+static proj Ycylequalarea(double p0,double p1){USED(p0); USED(p1); return cylequalarea(p0);}
+static proj Ycylindrical(double p0, double p1){USED(p0); USED(p1); return cylindrical();}
+static proj Yelliptic(double p0,double p1){USED(p0); USED(p1); return elliptic(p0);}
+static proj Yfisheye(double p0,double p1){USED(p0); USED(p1); return fisheye(p0);}
+static proj Ygall(double p0,double p1){USED(p0); USED(p1); return gall(p0);}
+static proj Ygilbert(double p0, double p1){USED(p0); USED(p1); return gilbert();}
+static proj Yglobular(double p0, double p1){USED(p0); USED(p1); return globular();}
+static proj Ygnomonic(double p0, double p1){USED(p0); USED(p1); return gnomonic();}
+static proj Yguyou(double p0, double p1){USED(p0); USED(p1); return guyou();}
+static proj Yharrison(double p0,double p1){USED(p0); USED(p1); return harrison(p0,p1);}
+static proj Yhex(double p0, double p1){USED(p0); USED(p1); return hex();}
+static proj Yhoming(double p0,double p1){USED(p0); USED(p1); return homing(p0);}
+static proj Ylagrange(double p0, double p1){USED(p0); USED(p1); return lagrange();}
+static proj Ylambert(double p0,double p1){USED(p0); USED(p1); return lambert(p0,p1);}
+static proj Ylaue(double p0, double p1){USED(p0); USED(p1); return laue();}
+static proj Ylune(double p0,double p1){USED(p0); USED(p1); return lune(p0,p1);}
+static proj Ymecca(double p0, double p1){USED(p0); USED(p1); return mecca(p0);}
+static proj Ymercator(double p0, double p1){USED(p0); USED(p1); return mercator();}
+static proj Ymollweide(double p0, double p1){USED(p0); USED(p1); return mollweide();}
+static proj Ynewyorker(double p0,double p1){USED(p0); USED(p1); return newyorker(p0);}
+static proj Yorthographic(double p0, double p1){USED(p0); USED(p1); return orthographic();}
+static proj Yperspective(double p0,double p1){USED(p0); USED(p1); return perspective(p0);}
+static proj Ypolyconic(double p0, double p1){USED(p0); USED(p1); return polyconic();}
+static proj Yrectangular(double p0,double p1){USED(p0); USED(p1); return rectangular(p0);}
+static proj Ysimpleconic(double p0,double p1){USED(p0); USED(p1); return simpleconic(p0,p1);}
+static proj Ysinusoidal(double p0, double p1){USED(p0); USED(p1); return sinusoidal();}
+static proj Ysp_albers(double p0,double p1){USED(p0); USED(p1); return sp_albers(p0,p1);}
+static proj Ysp_mercator(double p0, double p1){USED(p0); USED(p1); return sp_mercator();}
+static proj Ysquare(double p0, double p1){USED(p0); USED(p1); return square();}
+static proj Ystereographic(double p0, double p1){USED(p0); USED(p1); return stereographic();}
+static proj Ytetra(double p0, double p1){USED(p0); USED(p1); return tetra();}
+static proj Ytrapezoidal(double p0,double p1){USED(p0); USED(p1); return trapezoidal(p0,p1);}
+static proj Yvandergrinten(double p0, double p1){USED(p0); USED(p1); return vandergrinten();}
+
+struct index index[] = {
+ {"aitoff", Yaitoff, 0, picut, 0, 0, 0},
+ {"albers", Yalbers, 2, picut, 3, 0, 0},
+ {"azequalarea", Yazequalarea, 0, nocut, 1, 0, 0},
+ {"azequidistant", Yazequidistant, 0, nocut, 1, 0, 0},
+ {"bicentric", Ybicentric, 1, nocut, 0, 0, 0},
+ {"bonne", Ybonne, 1, picut, 0, 0, 0},
+ {"conic", Yconic, 1, picut, 0, 0, 0},
+ {"cylequalarea", Ycylequalarea, 1, picut, 3, 0, 0},
+ {"cylindrical", Ycylindrical, 0, picut, 0, 0, 0},
+ {"elliptic", Yelliptic, 1, picut, 0, 0, 0},
+ {"fisheye", Yfisheye, 1, nocut, 0, 0, 0},
+ {"gall", Ygall, 1, picut, 3, 0, 0},
+ {"gilbert", Ygilbert, 0, picut, 0, 0, 0},
+ {"globular", Yglobular, 0, picut, 0, 0, 0},
+ {"gnomonic", Ygnomonic, 0, nocut, 0, 0, plimb},
+ {"guyou", Yguyou, 0, guycut, 0, 0, 0},
+ {"harrison", Yharrison, 2, nocut, 0, 0, plimb},
+ {"hex", Yhex, 0, hexcut, 0, 0, 0},
+ {"homing", Yhoming, 1, nocut, 3, 0, hlimb},
+ {"lagrange", Ylagrange,0,picut,0, 0, 0},
+ {"lambert", Ylambert, 2, picut, 0, 0, 0},
+ {"laue", Ylaue, 0, nocut, 0, 0, 0},
+ {"lune", Ylune, 2, nocut, 0, 0, 0},
+ {"mecca", Ymecca, 1, picut, 3, 0, mlimb},
+ {"mercator", Ymercator, 0, picut, 3, 0, 0},
+ {"mollweide", Ymollweide, 0, picut, 0, 0, 0},
+ {"newyorker", Ynewyorker, 1, nocut, 0, 0, 0},
+ {"orthographic", Yorthographic, 0, nocut, 0, 0, olimb},
+ {"perspective", Yperspective, 1, nocut, 0, 0, plimb},
+ {"polyconic", Ypolyconic, 0, picut, 0, 0, 0},
+ {"rectangular", Yrectangular, 1, picut, 3, 0, 0},
+ {"simpleconic", Ysimpleconic, 2, picut, 3, 0, 0},
+ {"sinusoidal", Ysinusoidal, 0, picut, 0, 0, 0},
+ {"sp_albers", Ysp_albers, 2, picut, 3, 1, 0},
+ {"sp_mercator", Ysp_mercator, 0, picut, 0, 1, 0},
+ {"square", Ysquare, 0, picut, 0, 0, 0},
+ {"stereographic", Ystereographic, 0, nocut, 0, 0, 0},
+ {"tetra", Ytetra, 0, tetracut, 0, 0, 0},
+ {"trapezoidal", Ytrapezoidal, 2, picut, 3, 0, 0},
+ {"vandergrinten", Yvandergrinten, 0, picut, 0, 0, 0},
+ 0
+};
diff --git a/src/cmd/map/iplot.h b/src/cmd/map/iplot.h
new file mode 100644
index 00000000..be472fff
--- /dev/null
+++ b/src/cmd/map/iplot.h
@@ -0,0 +1,51 @@
+/* Plotting functions for v8 and v9 systems */
+/* This file is an alternative to plot.h */
+
+/* open the plotting output */
+#define openpl() print("o\n")
+
+/* close the plotting output */
+#define closepl() print("cl\n")
+
+/* make sure the page or screen is clear */
+#define erase() print("e\n")
+
+/* plot a point at _x,_y, which becomes current */
+#define point(_x,_y) print("poi %d %d\n", _x,_y)
+
+/* coordinates to be assigned to lower left and upper right
+ corners of (square) plotting area */
+#define range(_x,_y,_X,_Y) print("ra %d %d %d %d\n", _x,_y,_X,_Y)
+
+/* place text, first letter at current point, which does not change */
+#define text(_s) {if(*(_s) == ' ')print("t \"%s\"\n",_s); else print("t %s\n", _s); }
+
+/* draw line from current point to _x,_y, which becomes current */
+#define vec(_x,_y) print("v %d %d\n", _x,_y)
+
+/* _x,_y becomes current point */
+#define move(_x, _y) print("m %d %d\n", _x, _y)
+
+/* specify style for drawing lines */
+
+#define SOLID "solid"
+#define DOTTED "dotted"
+#define DASHED "dashed"
+#define DOTDASH "dotdash"
+
+#define pen(_s) print("pe %s\n", _s)
+
+#define BLACK "z"
+#define RED "r"
+#define YELLOW "y"
+#define GREEN "g"
+#define BLUE "b"
+#define CYAN "c"
+#define MAGENTA "m"
+#define WHITE "w"
+
+#define colorcode(_s) ((strcmp(_s,"black")==0)?BLACK:_s)
+
+#define colorx(_s) print("co %s\n", _s); /* funny name is all ken's fault */
+
+#define comment(s,f)
diff --git a/src/cmd/map/libmap/aitoff.c b/src/cmd/map/libmap/aitoff.c
new file mode 100644
index 00000000..83b777fb
--- /dev/null
+++ b/src/cmd/map/libmap/aitoff.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+#define Xaitwist Xaitpole.nlat
+static struct place Xaitpole;
+
+static int
+Xaitoff(struct place *place, double *x, double *y)
+{
+ struct place p;
+ copyplace(place,&p);
+ p.wlon.l /= 2.;
+ sincos(&p.wlon);
+ norm(&p,&Xaitpole,&Xaitwist);
+ Xazequalarea(&p,x,y);
+ *x *= 2.;
+ return(1);
+}
+
+proj
+aitoff(void)
+{
+ latlon(0.,0.,&Xaitpole);
+ return(Xaitoff);
+}
diff --git a/src/cmd/map/libmap/albers.c b/src/cmd/map/libmap/albers.c
new file mode 100644
index 00000000..56e3e9f5
--- /dev/null
+++ b/src/cmd/map/libmap/albers.c
@@ -0,0 +1,117 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+/* For Albers formulas see Deetz and Adams "Elements of Map Projection", */
+/* USGS Special Publication No. 68, GPO 1921 */
+
+static double r0sq, r1sq, d2, n, den, sinb1, sinb2;
+static struct coord plat1, plat2;
+static int southpole;
+
+static double num(double s)
+{
+ if(d2==0)
+ return(1);
+ s = d2*s*s;
+ return(1+s*(2./3+s*(3./5+s*(4./7+s*5./9))));
+}
+
+/* Albers projection for a spheroid, good only when N pole is fixed */
+
+static int
+Xspalbers(struct place *place, double *x, double *y)
+{
+ double r = sqrt(r0sq-2*(1-d2)*place->nlat.s*num(place->nlat.s)/n);
+ double t = n*place->wlon.l;
+ *y = r*cos(t);
+ *x = -r*sin(t);
+ if(!southpole)
+ *y = -*y;
+ else
+ *x = -*x;
+ return(1);
+}
+
+/* lat1, lat2: std parallels; e2: squared eccentricity */
+
+static proj albinit(double lat1, double lat2, double e2)
+{
+ double r1;
+ double t;
+ for(;;) {
+ if(lat1 < -90)
+ lat1 = -180 - lat1;
+ if(lat2 > 90)
+ lat2 = 180 - lat2;
+ if(lat1 <= lat2)
+ break;
+ t = lat1; lat1 = lat2; lat2 = t;
+ }
+ if(lat2-lat1 < 1) {
+ if(lat1 > 89)
+ return(azequalarea());
+ return(0);
+ }
+ if(fabs(lat2+lat1) < 1)
+ return(cylequalarea(lat1));
+ d2 = e2;
+ den = num(1.);
+ deg2rad(lat1,&plat1);
+ deg2rad(lat2,&plat2);
+ sinb1 = plat1.s*num(plat1.s)/den;
+ sinb2 = plat2.s*num(plat2.s)/den;
+ n = (plat1.c*plat1.c/(1-e2*plat1.s*plat1.s) -
+ plat2.c*plat2.c/(1-e2*plat2.s*plat2.s)) /
+ (2*(1-e2)*den*(sinb2-sinb1));
+ r1 = plat1.c/(n*sqrt(1-e2*plat1.s*plat1.s));
+ r1sq = r1*r1;
+ r0sq = r1sq + 2*(1-e2)*den*sinb1/n;
+ southpole = lat1<0 && plat2.c>plat1.c;
+ return(Xspalbers);
+}
+
+proj
+sp_albers(double lat1, double lat2)
+{
+ return(albinit(lat1,lat2,EC2));
+}
+
+proj
+albers(double lat1, double lat2)
+{
+ return(albinit(lat1,lat2,0.));
+}
+
+static double scale = 1;
+static double twist = 0;
+
+void
+albscale(double x, double y, double lat, double lon)
+{
+ struct place place;
+ double alat, alon, x1,y1;
+ scale = 1;
+ twist = 0;
+ invalb(x,y,&alat,&alon);
+ twist = lon - alon;
+ deg2rad(lat,&place.nlat);
+ deg2rad(lon,&place.wlon);
+ Xspalbers(&place,&x1,&y1);
+ scale = sqrt((x1*x1+y1*y1)/(x*x+y*y));
+}
+
+void
+invalb(double x, double y, double *lat, double *lon)
+{
+ int i;
+ double sinb_den, sinp;
+ x *= scale;
+ y *= scale;
+ *lon = atan2(-x,fabs(y))/(RAD*n) + twist;
+ sinb_den = (r0sq - x*x - y*y)*n/(2*(1-d2));
+ sinp = sinb_den;
+ for(i=0; i<5; i++)
+ sinp = sinb_den/num(sinp);
+ *lat = asin(sinp)/RAD;
+}
diff --git a/src/cmd/map/libmap/azequalarea.c b/src/cmd/map/libmap/azequalarea.c
new file mode 100644
index 00000000..6bae893d
--- /dev/null
+++ b/src/cmd/map/libmap/azequalarea.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xazequalarea(struct place *place, double *x, double *y)
+{
+ double r;
+ r = sqrt(1. - place->nlat.s);
+ *x = - r * place->wlon.s;
+ *y = - r * place->wlon.c;
+ return(1);
+}
+
+proj
+azequalarea(void)
+{
+ return(Xazequalarea);
+}
diff --git a/src/cmd/map/libmap/azequidist.c b/src/cmd/map/libmap/azequidist.c
new file mode 100644
index 00000000..d26d33d4
--- /dev/null
+++ b/src/cmd/map/libmap/azequidist.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xazequidistant(struct place *place, double *x, double *y)
+{
+ double colat;
+ colat = PI/2 - place->nlat.l;
+ *x = -colat * place->wlon.s;
+ *y = -colat * place->wlon.c;
+ return(1);
+}
+
+proj
+azequidistant(void)
+{
+ return(Xazequidistant);
+}
diff --git a/src/cmd/map/libmap/bicentric.c b/src/cmd/map/libmap/bicentric.c
new file mode 100644
index 00000000..33fd8d19
--- /dev/null
+++ b/src/cmd/map/libmap/bicentric.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord center;
+
+static int
+Xbicentric(struct place *place, double *x, double *y)
+{
+ if(place->wlon.c<=.01||place->nlat.c<=.01)
+ return(-1);
+ *x = -center.c*place->wlon.s/place->wlon.c;
+ *y = place->nlat.s/(place->nlat.c*place->wlon.c);
+ return(*x**x+*y**y<=9);
+}
+
+proj
+bicentric(double l)
+{
+ l = fabs(l);
+ if(l>89)
+ return(0);
+ deg2rad(l,&center);
+ return(Xbicentric);
+}
diff --git a/src/cmd/map/libmap/bonne.c b/src/cmd/map/libmap/bonne.c
new file mode 100644
index 00000000..858f0d69
--- /dev/null
+++ b/src/cmd/map/libmap/bonne.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord stdpar;
+static double r0;
+
+static int
+Xbonne(struct place *place, double *x, double *y)
+{
+ double r, alpha;
+ r = r0 - place->nlat.l;
+ if(r<.001)
+ if(fabs(stdpar.c)<1e-10)
+ alpha = place->wlon.l;
+ else if(fabs(place->nlat.c)==0)
+ alpha = 0;
+ else
+ alpha = place->wlon.l/(1+
+ stdpar.c*stdpar.c*stdpar.c/place->nlat.c/3);
+ else
+ alpha = place->wlon.l * place->nlat.c / r;
+ *x = - r*sin(alpha);
+ *y = - r*cos(alpha);
+ return(1);
+}
+
+proj
+bonne(double par)
+{
+ if(fabs(par*RAD) < .01)
+ return(Xsinusoidal);
+ deg2rad(par, &stdpar);
+ r0 = stdpar.c/stdpar.s + stdpar.l;
+ return(Xbonne);
+}
diff --git a/src/cmd/map/libmap/ccubrt.c b/src/cmd/map/libmap/ccubrt.c
new file mode 100644
index 00000000..8a7af566
--- /dev/null
+++ b/src/cmd/map/libmap/ccubrt.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+void
+ccubrt(double zr, double zi, double *wr, double *wi)
+{
+ double r, theta;
+ theta = atan2(zi,zr);
+ r = cubrt(hypot(zr,zi));
+ *wr = r*cos(theta/3);
+ *wi = r*sin(theta/3);
+}
diff --git a/src/cmd/map/libmap/complex.c b/src/cmd/map/libmap/complex.c
new file mode 100644
index 00000000..b3099fd4
--- /dev/null
+++ b/src/cmd/map/libmap/complex.c
@@ -0,0 +1,85 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+/*complex divide, defensive against overflow from
+ * * and /, but not from + and -
+ * assumes underflow yields 0.0
+ * uses identities:
+ * (a + bi)/(c + di) = ((a + bd/c) + (b - ad/c)i)/(c + dd/c)
+ * (a + bi)/(c + di) = (b - ai)/(d - ci)
+*/
+void
+cdiv(double a, double b, double c, double d, double *u, double *v)
+{
+ double r,t;
+ if(fabs(c)<fabs(d)) {
+ t = -c; c = d; d = t;
+ t = -a; a = b; b = t;
+ }
+ r = d/c;
+ t = c + r*d;
+ *u = (a + r*b)/t;
+ *v = (b - r*a)/t;
+}
+
+void
+cmul(double c1, double c2, double d1, double d2, double *e1, double *e2)
+{
+ *e1 = c1*d1 - c2*d2;
+ *e2 = c1*d2 + c2*d1;
+}
+
+void
+csq(double c1, double c2, double *e1, double *e2)
+{
+ *e1 = c1*c1 - c2*c2;
+ *e2 = c1*c2*2;
+}
+
+/* complex square root
+ * assumes underflow yields 0.0
+ * uses these identities:
+ * sqrt(x+_iy) = sqrt(r(cos(t)+_isin(t))
+ * = sqrt(r)(cos(t/2)+_isin(t/2))
+ * cos(t/2) = sin(t)/2sin(t/2) = sqrt((1+cos(t)/2)
+ * sin(t/2) = sin(t)/2cos(t/2) = sqrt((1-cos(t)/2)
+*/
+void
+csqrt(double c1, double c2, double *e1, double *e2)
+{
+ double r,s;
+ double x,y;
+ x = fabs(c1);
+ y = fabs(c2);
+ if(x>=y) {
+ if(x==0) {
+ *e1 = *e2 = 0;
+ return;
+ }
+ r = x;
+ s = y/x;
+ } else {
+ r = y;
+ s = x/y;
+ }
+ r *= sqrt(1+ s*s);
+ if(c1>0) {
+ *e1 = sqrt((r+c1)/2);
+ *e2 = c2/(2* *e1);
+ } else {
+ *e2 = sqrt((r-c1)/2);
+ if(c2<0)
+ *e2 = -*e2;
+ *e1 = c2/(2* *e2);
+ }
+}
+
+
+void cpow(double c1, double c2, double *d1, double *d2, double pwr)
+{
+ double theta = pwr*atan2(c2,c1);
+ double r = pow(hypot(c1,c2), pwr);
+ *d1 = r*cos(theta);
+ *d2 = r*sin(theta);
+}
diff --git a/src/cmd/map/libmap/conic.c b/src/cmd/map/libmap/conic.c
new file mode 100644
index 00000000..ba4430c6
--- /dev/null
+++ b/src/cmd/map/libmap/conic.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord stdpar;
+
+static int
+Xconic(struct place *place, double *x, double *y)
+{
+ double r;
+ if(fabs(place->nlat.l-stdpar.l) > 80.*RAD)
+ return(-1);
+ r = stdpar.c/stdpar.s - tan(place->nlat.l - stdpar.l);
+ *x = - r*sin(place->wlon.l * stdpar.s);
+ *y = - r*cos(place->wlon.l * stdpar.s);
+ if(r>3) return(0);
+ return(1);
+}
+
+proj
+conic(double par)
+{
+ if(fabs(par) <.1)
+ return(Xcylindrical);
+ deg2rad(par, &stdpar);
+ return(Xconic);
+}
diff --git a/src/cmd/map/libmap/cubrt.c b/src/cmd/map/libmap/cubrt.c
new file mode 100644
index 00000000..fd508d2b
--- /dev/null
+++ b/src/cmd/map/libmap/cubrt.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+double
+cubrt(double a)
+{
+ double x,y,x1;
+ if(a==0)
+ return(0.);
+ y = 1;
+ if(a<0) {
+ y = -y;
+ a = -a;
+ }
+ while(a<1) {
+ a *= 8;
+ y /= 2;
+ }
+ while(a>1) {
+ a /= 8;
+ y *= 2;
+ }
+ x = 1;
+ do {
+ x1 = x;
+ x = (2*x1+a/(x1*x1))/3;
+ } while(fabs(x-x1)>10.e-15);
+ return(x*y);
+}
diff --git a/src/cmd/map/libmap/cuts.c b/src/cmd/map/libmap/cuts.c
new file mode 100644
index 00000000..ad9d6dc3
--- /dev/null
+++ b/src/cmd/map/libmap/cuts.c
@@ -0,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+extern void abort(void);
+
+/* these routines duplicate names found in map.c. they are
+called from routines in hex.c, guyou.c, and tetra.c, which
+are in turn invoked directly from map.c. this bad organization
+arises from data hiding; only these three files know stuff
+that's necessary for the proper handling of the unusual cuts
+involved in these projections.
+
+the calling routines are not advertised as part of the library,
+and the library duplicates should never get loaded, however they
+are included to make the libary self-standing.*/
+
+int
+picut(struct place *g, struct place *og, double *cutlon)
+{
+ g; og; cutlon;
+ abort();
+ return 0;
+}
+
+int
+ckcut(struct place *g1, struct place *g2, double lon)
+{
+ g1; g2; lon;
+ abort();
+ return 0;
+}
+
+double
+reduce(double x)
+{
+ x;
+ abort();
+ return 0;
+}
diff --git a/src/cmd/map/libmap/cylequalarea.c b/src/cmd/map/libmap/cylequalarea.c
new file mode 100644
index 00000000..3cf222ff
--- /dev/null
+++ b/src/cmd/map/libmap/cylequalarea.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double a;
+
+static int
+Xcylequalarea(struct place *place, double *x, double *y)
+{
+ *x = - place->wlon.l * a;
+ *y = place->nlat.s;
+ return(1);
+}
+
+proj
+cylequalarea(double par)
+{
+ struct coord stdp0;
+ if(par > 89.0)
+ return(0);
+ deg2rad(par, &stdp0);
+ a = stdp0.c*stdp0.c;
+ return(Xcylequalarea);
+}
diff --git a/src/cmd/map/libmap/cylindrical.c b/src/cmd/map/libmap/cylindrical.c
new file mode 100644
index 00000000..4d01bc23
--- /dev/null
+++ b/src/cmd/map/libmap/cylindrical.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xcylindrical(struct place *place, double *x, double *y)
+{
+ if(fabs(place->nlat.l) > 80.*RAD)
+ return(-1);
+ *x = - place->wlon.l;
+ *y = place->nlat.s / place->nlat.c;
+ return(1);
+}
+
+proj
+cylindrical(void)
+{
+ return(Xcylindrical);
+}
diff --git a/src/cmd/map/libmap/elco2.c b/src/cmd/map/libmap/elco2.c
new file mode 100644
index 00000000..b4c9bbf6
--- /dev/null
+++ b/src/cmd/map/libmap/elco2.c
@@ -0,0 +1,132 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+/* elliptic integral routine, R.Bulirsch,
+ * Numerische Mathematik 7(1965) 78-90
+ * calculate integral from 0 to x+iy of
+ * (a+b*t^2)/((1+t^2)*sqrt((1+t^2)*(1+kc^2*t^2)))
+ * yields about D valid figures, where CC=10e-D
+ * for a*b>=0, except at branchpoints x=0,y=+-i,+-i/kc;
+ * there the accuracy may be reduced.
+ * fails for kc=0 or x<0
+ * return(1) for success, return(0) for fail
+ *
+ * special case a=b=1 is equivalent to
+ * standard elliptic integral of first kind
+ * from 0 to atan(x+iy) of
+ * 1/sqrt(1-k^2*(sin(t))^2) where k^2=1-kc^2
+*/
+
+#define ROOTINF 10.e18
+#define CC 1.e-6
+
+int
+elco2(double x, double y, double kc, double a, double b, double *u, double *v)
+{
+ double c,d,dn1,dn2,e,e1,e2,f,f1,f2,h,k,m,m1,m2,sy;
+ double d1[13],d2[13];
+ int i,l;
+ if(kc==0||x<0)
+ return(0);
+ sy = y>0? 1: y==0? 0: -1;
+ y = fabs(y);
+ csq(x,y,&c,&e2);
+ d = kc*kc;
+ k = 1-d;
+ e1 = 1+c;
+ cdiv2(1+d*c,d*e2,e1,e2,&f1,&f2);
+ f2 = -k*x*y*2/f2;
+ csqr(f1,f2,&dn1,&dn2);
+ if(f1<0) {
+ f1 = dn1;
+ dn1 = -dn2;
+ dn2 = -f1;
+ }
+ if(k<0) {
+ dn1 = fabs(dn1);
+ dn2 = fabs(dn2);
+ }
+ c = 1+dn1;
+ cmul(e1,e2,c,dn2,&f1,&f2);
+ cdiv(x,y,f1,f2,&d1[0],&d2[0]);
+ h = a-b;
+ d = f = m = 1;
+ kc = fabs(kc);
+ e = a;
+ a += b;
+ l = 4;
+ for(i=1;;i++) {
+ m1 = (kc+m)/2;
+ m2 = m1*m1;
+ k *= f/(m2*4);
+ b += e*kc;
+ e = a;
+ cdiv2(kc+m*dn1,m*dn2,c,dn2,&f1,&f2);
+ csqr(f1/m1,k*dn2*2/f2,&dn1,&dn2);
+ cmul(dn1,dn2,x,y,&f1,&f2);
+ x = fabs(f1);
+ y = fabs(f2);
+ a += b/m1;
+ l *= 2;
+ c = 1 +dn1;
+ d *= k/2;
+ cmul(x,y,x,y,&e1,&e2);
+ k *= k;
+
+ cmul(c,dn2,1+e1*m2,e2*m2,&f1,&f2);
+ cdiv(d*x,d*y,f1,f2,&d1[i],&d2[i]);
+ if(k<=CC)
+ break;
+ kc = sqrt(m*kc);
+ f = m2;
+ m = m1;
+ }
+ f1 = f2 = 0;
+ for(;i>=0;i--) {
+ f1 += d1[i];
+ f2 += d2[i];
+ }
+ x *= m1;
+ y *= m1;
+ cdiv2(1-y,x,1+y,-x,&e1,&e2);
+ e2 = x*2/e2;
+ d = a/(m1*l);
+ *u = atan2(e2,e1);
+ if(*u<0)
+ *u += PI;
+ a = d*sy/2;
+ *u = d*(*u) + f1*h;
+ *v = (-1-log(e1*e1+e2*e2))*a + f2*h*sy + a;
+ return(1);
+}
+
+void
+cdiv2(double c1, double c2, double d1, double d2, double *e1, double *e2)
+{
+ double t;
+ if(fabs(d2)>fabs(d1)) {
+ t = d1, d1 = d2, d2 = t;
+ t = c1, c1 = c2, c2 = t;
+ }
+ if(fabs(d1)>ROOTINF)
+ *e2 = ROOTINF*ROOTINF;
+ else
+ *e2 = d1*d1 + d2*d2;
+ t = d2/d1;
+ *e1 = (c1+t*c2)/(d1+t*d2); /* (c1*d1+c2*d2)/(d1*d1+d2*d2) */
+}
+
+/* complex square root of |x|+iy */
+void
+csqr(double c1, double c2, double *e1, double *e2)
+{
+ double r2;
+ r2 = c1*c1 + c2*c2;
+ if(r2<=0) {
+ *e1 = *e2 = 0;
+ return;
+ }
+ *e1 = sqrt((sqrt(r2) + fabs(c1))/2);
+ *e2 = c2/(*e1*2);
+}
diff --git a/src/cmd/map/libmap/elliptic.c b/src/cmd/map/libmap/elliptic.c
new file mode 100644
index 00000000..3f3b1d57
--- /dev/null
+++ b/src/cmd/map/libmap/elliptic.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+struct coord center;
+
+static int
+Xelliptic(struct place *place, double *x, double *y)
+{
+ double r1,r2;
+ r1 = acos(place->nlat.c*(place->wlon.c*center.c
+ - place->wlon.s*center.s));
+ r2 = acos(place->nlat.c*(place->wlon.c*center.c
+ + place->wlon.s*center.s));
+ *x = -(r1*r1 - r2*r2)/(4*center.l);
+ *y = (r1*r1+r2*r2)/2 - (center.l*center.l+*x**x);
+ if(*y < 0)
+ *y = 0;
+ *y = sqrt(*y);
+ if(place->nlat.l<0)
+ *y = -*y;
+ return(1);
+}
+
+proj
+elliptic(double l)
+{
+ l = fabs(l);
+ if(l>89)
+ return(0);
+ if(l<1)
+ return(Xazequidistant);
+ deg2rad(l,&center);
+ return(Xelliptic);
+}
diff --git a/src/cmd/map/libmap/fisheye.c b/src/cmd/map/libmap/fisheye.c
new file mode 100644
index 00000000..412d65e7
--- /dev/null
+++ b/src/cmd/map/libmap/fisheye.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+/* refractive fisheye, not logarithmic */
+
+static double n;
+
+static int
+Xfisheye(struct place *place, double *x, double *y)
+{
+ double r;
+ double u = sin(PI/4-place->nlat.l/2)/n;
+ if(fabs(u) > .97)
+ return -1;
+ r = tan(asin(u));
+ *x = -r*place->wlon.s;
+ *y = -r*place->wlon.c;
+ return 1;
+}
+
+proj
+fisheye(double par)
+{
+ n = par;
+ return n<.1? 0: Xfisheye;
+}
diff --git a/src/cmd/map/libmap/gall.c b/src/cmd/map/libmap/gall.c
new file mode 100644
index 00000000..84da651b
--- /dev/null
+++ b/src/cmd/map/libmap/gall.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double scale;
+
+static int
+Xgall(struct place *place, double *x, double *y)
+{
+ /* two ways to compute tan(place->nlat.l/2) */
+ if(fabs(place->nlat.s)<.1)
+ *y = sin(place->nlat.l/2)/cos(place->nlat.l/2);
+ else
+ *y = (1-place->nlat.c)/place->nlat.s;
+ *x = -scale*place->wlon.l;
+ return 1;
+}
+
+proj
+gall(double par)
+{
+ double coshalf;
+ if(fabs(par)>80)
+ return 0;
+ par *= RAD;
+ coshalf = cos(par/2);
+ scale = cos(par)/(2*coshalf*coshalf);
+ return Xgall;
+}
diff --git a/src/cmd/map/libmap/gilbert.c b/src/cmd/map/libmap/gilbert.c
new file mode 100644
index 00000000..173ffcd3
--- /dev/null
+++ b/src/cmd/map/libmap/gilbert.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xgilbert(struct place *p, double *x, double *y)
+{
+/* the interesting part - map the sphere onto a hemisphere */
+ struct place q;
+ q.nlat.s = tan(0.5*(p->nlat.l));
+ if(q.nlat.s > 1) q.nlat.s = 1;
+ if(q.nlat.s < -1) q.nlat.s = -1;
+ q.nlat.c = sqrt(1 - q.nlat.s*q.nlat.s);
+ q.wlon.l = p->wlon.l/2;
+ sincos(&q.wlon);
+/* the dull part: present the hemisphere orthogrpahically */
+ *y = q.nlat.s;
+ *x = -q.wlon.s*q.nlat.c;
+ return(1);
+}
+
+proj
+gilbert(void)
+{
+ return(Xgilbert);
+}
+
+/* derivation of the interesting part:
+ map the sphere onto the plane by stereographic projection;
+ map the plane onto a half plane by sqrt;
+ map the half plane back to the sphere by stereographic
+ projection
+
+ n,w are original lat and lon
+ r is stereographic radius
+ primes are transformed versions
+
+ r = cos(n)/(1+sin(n))
+ r' = sqrt(r) = cos(n')/(1+sin(n'))
+
+ r'^2 = (1-sin(n')^2)/(1+sin(n')^2) = cos(n)/(1+sin(n))
+
+ this is a linear equation for sin n', with solution
+
+ sin n' = (1+sin(n)-cos(n))/(1+sin(n)+cos(n))
+
+ use standard formula: tan x/2 = (1-cos x)/sin x = sin x/(1+cos x)
+ to show that the right side of the last equation is tan(n/2)
+*/
+
+
diff --git a/src/cmd/map/libmap/guyou.c b/src/cmd/map/libmap/guyou.c
new file mode 100644
index 00000000..b37736f2
--- /dev/null
+++ b/src/cmd/map/libmap/guyou.c
@@ -0,0 +1,101 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct place gywhem, gyehem;
+static struct coord gytwist;
+static double gyconst, gykc, gyside;
+
+
+static void
+dosquare(double z1, double z2, double *x, double *y)
+{
+ double w1,w2;
+ w1 = z1 -1;
+ if(fabs(w1*w1+z2*z2)>.000001) {
+ cdiv(z1+1,z2,w1,z2,&w1,&w2);
+ w1 *= gyconst;
+ w2 *= gyconst;
+ if(w1<0)
+ w1 = 0;
+ elco2(w1,w2,gykc,1.,1.,x,y);
+ } else {
+ *x = gyside;
+ *y = 0;
+ }
+}
+
+int
+Xguyou(struct place *place, double *x, double *y)
+{
+ int ew; /*which hemisphere*/
+ double z1,z2;
+ struct place pl;
+ ew = place->wlon.l<0;
+ copyplace(place,&pl);
+ norm(&pl,ew?&gyehem:&gywhem,&gytwist);
+ Xstereographic(&pl,&z1,&z2);
+ dosquare(z1/2,z2/2,x,y);
+ if(!ew)
+ *x -= gyside;
+ return(1);
+}
+
+proj
+guyou(void)
+{
+ double junk;
+ gykc = 1/(3+2*sqrt(2.));
+ gyconst = -(1+sqrt(2.));
+ elco2(-gyconst,0.,gykc,1.,1.,&gyside,&junk);
+ gyside *= 2;
+ latlon(0.,90.,&gywhem);
+ latlon(0.,-90.,&gyehem);
+ deg2rad(0.,&gytwist);
+ return(Xguyou);
+}
+
+int
+guycut(struct place *g, struct place *og, double *cutlon)
+{
+ int c;
+ c = picut(g,og,cutlon);
+ if(c!=1)
+ return(c);
+ *cutlon = 0.;
+ if(g->nlat.c<.7071||og->nlat.c<.7071)
+ return(ckcut(g,og,0.));
+ return(1);
+}
+
+static int
+Xsquare(struct place *place, double *x, double *y)
+{
+ double z1,z2;
+ double r, theta;
+ struct place p;
+ copyplace(place,&p);
+ if(place->nlat.l<0) {
+ p.nlat.l = -p.nlat.l;
+ p.nlat.s = -p.nlat.s;
+ }
+ if(p.nlat.l<FUZZ && fabs(p.wlon.l)>PI-FUZZ){
+ *y = -gyside/2;
+ *x = p.wlon.l>0?0:gyside;
+ return(1);
+ }
+ Xstereographic(&p,&z1,&z2);
+ r = sqrt(sqrt(hypot(z1,z2)/2));
+ theta = atan2(z1,-z2)/4;
+ dosquare(r*sin(theta),-r*cos(theta),x,y);
+ if(place->nlat.l<0)
+ *y = -gyside - *y;
+ return(1);
+}
+
+proj
+square(void)
+{
+ guyou();
+ return(Xsquare);
+}
diff --git a/src/cmd/map/libmap/harrison.c b/src/cmd/map/libmap/harrison.c
new file mode 100644
index 00000000..6b6003c2
--- /dev/null
+++ b/src/cmd/map/libmap/harrison.c
@@ -0,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double v3,u2,u3,a,b; /*v=view,p=obj,u=unit.y*/
+
+static int
+Xharrison(struct place *place, double *x, double *y)
+{
+ double p1 = -place->nlat.c*place->wlon.s;
+ double p2 = -place->nlat.c*place->wlon.c;
+ double p3 = place->nlat.s;
+ double d = b + u3*p2 - u2*p3;
+ double t;
+ if(d < .01)
+ return -1;
+ t = a/d;
+ if(v3*place->nlat.s < 1.)
+ return -1;
+ *y = t*p2*u2 + (v3-t*(v3-p3))*u3;
+ *x = t*p1;
+ if(t < 0)
+ return 0;
+ if(*x * *x + *y * *y > 16)
+ return -1;
+ return 1;
+}
+
+proj
+harrison(double r, double alpha)
+{
+ u2 = cos(alpha*RAD);
+ u3 = sin(alpha*RAD);
+ v3 = r;
+ b = r*u2;
+ a = 1 + b;
+ if(r<1.001 || a<sqrt(r*r-1))
+ return 0;
+ return Xharrison;
+}
diff --git a/src/cmd/map/libmap/hex.c b/src/cmd/map/libmap/hex.c
new file mode 100644
index 00000000..851f138f
--- /dev/null
+++ b/src/cmd/map/libmap/hex.c
@@ -0,0 +1,122 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+#define BIG 1.e15
+#define HFUZZ .0001
+
+static double hcut[3] ;
+static double kr[3] = { .5, -1., .5 };
+static double ki[3] = { -1., 0., 1. }; /*to multiply by sqrt(3)/2*/
+static double cr[3];
+static double ci[3];
+static struct place hem;
+static struct coord twist;
+static double rootroot3, hkc;
+static double w2;
+static double rootk;
+
+static void
+reflect(int i, double wr, double wi, double *x, double *y)
+{
+ double pr,pi,l;
+ pr = cr[i]-wr;
+ pi = ci[i]-wi;
+ l = 2*(kr[i]*pr + ki[i]*pi);
+ *x = wr + l*kr[i];
+ *y = wi + l*ki[i];
+}
+
+static int
+Xhex(struct place *place, double *x, double *y)
+{
+ int ns;
+ int i;
+ double zr,zi;
+ double sr,si,tr,ti,ur,ui,vr,vi,yr,yi;
+ struct place p;
+ copyplace(place,&p);
+ ns = place->nlat.l >= 0;
+ if(!ns) {
+ p.nlat.l = -p.nlat.l;
+ p.nlat.s = -p.nlat.s;
+ }
+ if(p.nlat.l<HFUZZ) {
+ for(i=0;i<3;i++)
+ if(fabs(reduce(p.wlon.l-hcut[i]))<HFUZZ) {
+ if(i==2) {
+ *x = 2*cr[0] - cr[1];
+ *y = 0;
+ } else {
+ *x = cr[1];
+ *y = 2*ci[2*i];
+ }
+ return(1);
+ }
+ p.nlat.l = HFUZZ;
+ sincos(&p.nlat);
+ }
+ norm(&p,&hem,&twist);
+ Xstereographic(&p,&zr,&zi);
+ zr /= 2;
+ zi /= 2;
+ cdiv(1-zr,-zi,1+zr,zi,&sr,&si);
+ csq(sr,si,&tr,&ti);
+ ccubrt(1+3*tr,3*ti,&ur,&ui);
+ csqrt(ur-1,ui,&vr,&vi);
+ cdiv(rootroot3+vr,vi,rootroot3-vr,-vi,&yr,&yi);
+ yr /= rootk;
+ yi /= rootk;
+ elco2(fabs(yr),yi,hkc,1.,1.,x,y);
+ if(yr < 0)
+ *x = w2 - *x;
+ if(!ns) reflect(hcut[0]>place->wlon.l?0:
+ hcut[1]>=place->wlon.l?1:
+ 2,*x,*y,x,y);
+ return(1);
+}
+
+proj
+hex(void)
+{
+ int i;
+ double t;
+ double root3;
+ double c,d;
+ struct place p;
+ hcut[2] = PI;
+ hcut[1] = hcut[2]/3;
+ hcut[0] = -hcut[1];
+ root3 = sqrt(3.);
+ rootroot3 = sqrt(root3);
+ t = 15 -8*root3;
+ hkc = t*(1-sqrt(1-1/(t*t)));
+ elco2(BIG,0.,hkc,1.,1.,&w2,&t);
+ w2 *= 2;
+ rootk = sqrt(hkc);
+ latlon(90.,90.,&hem);
+ latlon(90.,0.,&p);
+ Xhex(&p,&c,&t);
+ latlon(0.,0.,&p);
+ Xhex(&p,&d,&t);
+ for(i=0;i<3;i++) {
+ ki[i] *= root3/2;
+ cr[i] = c + (c-d)*kr[i];
+ ci[i] = (c-d)*ki[i];
+ }
+ deg2rad(0.,&twist);
+ return(Xhex);
+}
+
+int
+hexcut(struct place *g, struct place *og, double *cutlon)
+{
+ int t,i;
+ if(g->nlat.l>=-HFUZZ&&og->nlat.l>=-HFUZZ)
+ return(1);
+ for(i=0;i<3;i++) {
+ t = ckcut(g,og,*cutlon=hcut[i]);
+ if(t!=1) return(t);
+ }
+ return(1);
+}
diff --git a/src/cmd/map/libmap/homing.c b/src/cmd/map/libmap/homing.c
new file mode 100644
index 00000000..366f69fe
--- /dev/null
+++ b/src/cmd/map/libmap/homing.c
@@ -0,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord p0; /* standard parallel */
+
+int first;
+
+static double
+trigclamp(double x)
+{
+ return x>1? 1: x<-1? -1: x;
+}
+
+static struct coord az; /* azimuth of p0 seen from place */
+static struct coord rad; /* angular dist from place to p0 */
+
+static int
+azimuth(struct place *place)
+{
+ if(place->nlat.c < FUZZ) {
+ az.l = PI/2 + place->nlat.l - place->wlon.l;
+ sincos(&az);
+ rad.l = fabs(place->nlat.l - p0.l);
+ if(rad.l > PI)
+ rad.l = 2*PI - rad.l;
+ sincos(&rad);
+ return 1;
+ }
+ rad.c = trigclamp(p0.s*place->nlat.s + /* law of cosines */
+ p0.c*place->nlat.c*place->wlon.c);
+ rad.s = sqrt(1 - rad.c*rad.c);
+ if(fabs(rad.s) < .001) {
+ az.s = 0;
+ az.c = 1;
+ } else {
+ az.s = trigclamp(p0.c*place->wlon.s/rad.s); /* sines */
+ az.c = trigclamp((p0.s - rad.c*place->nlat.s)
+ /(rad.s*place->nlat.c));
+ }
+ rad.l = atan2(rad.s, rad.c);
+ return 1;
+}
+
+static int
+Xmecca(struct place *place, double *x, double *y)
+{
+ if(!azimuth(place))
+ return 0;
+ *x = -place->wlon.l;
+ *y = fabs(az.s)<.02? -az.c*rad.s/p0.c: *x*az.c/az.s;
+ return fabs(*y)>2? -1:
+ rad.c<0? 0:
+ 1;
+}
+
+proj
+mecca(double par)
+{
+ first = 1;
+ if(fabs(par)>80.)
+ return(0);
+ deg2rad(par,&p0);
+ return(Xmecca);
+}
+
+static int
+Xhoming(struct place *place, double *x, double *y)
+{
+ if(!azimuth(place))
+ return 0;
+ *x = -rad.l*az.s;
+ *y = -rad.l*az.c;
+ return place->wlon.c<0? 0: 1;
+}
+
+proj
+homing(double par)
+{
+ first = 1;
+ if(fabs(par)>80.)
+ return(0);
+ deg2rad(par,&p0);
+ return(Xhoming);
+}
+
+int
+hlimb(double *lat, double *lon, double res)
+{
+ if(first) {
+ *lon = -90;
+ *lat = -90;
+ first = 0;
+ return 0;
+ }
+ *lat += res;
+ if(*lat <= 90)
+ return 1;
+ if(*lon == 90)
+ return -1;
+ *lon = 90;
+ *lat = -90;
+ return 0;
+}
+
+int
+mlimb(double *lat, double *lon, double res)
+{
+ int ret = !first;
+ if(fabs(p0.s) < .01)
+ return -1;
+ if(first) {
+ *lon = -180;
+ first = 0;
+ } else
+ *lon += res;
+ if(*lon > 180)
+ return -1;
+ *lat = atan(-cos(*lon*RAD)/p0.s*p0.c)/RAD;
+ return ret;
+}
diff --git a/src/cmd/map/libmap/lagrange.c b/src/cmd/map/libmap/lagrange.c
new file mode 100644
index 00000000..02dd29eb
--- /dev/null
+++ b/src/cmd/map/libmap/lagrange.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static int
+Xlagrange(struct place *place, double *x, double *y)
+{
+ double z1,z2;
+ double w1,w2,t1,t2;
+ struct place p;
+ copyplace(place,&p);
+ if(place->nlat.l<0) {
+ p.nlat.l = -p.nlat.l;
+ p.nlat.s = -p.nlat.s;
+ }
+ Xstereographic(&p,&z1,&z2);
+ csqrt(-z2/2,z1/2,&w1,&w2);
+ cdiv(w1-1,w2,w1+1,w2,&t1,&t2);
+ *y = -t1;
+ *x = t2;
+ if(place->nlat.l<0)
+ *y = -*y;
+ return(1);
+}
+
+proj
+lagrange(void)
+{
+ return(Xlagrange);
+}
diff --git a/src/cmd/map/libmap/lambert.c b/src/cmd/map/libmap/lambert.c
new file mode 100644
index 00000000..6b688aa3
--- /dev/null
+++ b/src/cmd/map/libmap/lambert.c
@@ -0,0 +1,46 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord stdp0, stdp1;
+static double k;
+
+static int
+Xlambert(struct place *place, double *x, double *y)
+{
+ double r;
+ if(place->nlat.l < -80.*RAD)
+ return(-1);
+ if(place->nlat.l > 89.*RAD)
+ r = 0; /* slovenly */
+ else
+ r = stdp0.c*exp(0.5*k*log(
+ (1+stdp0.s)*(1-place->nlat.s)/((1-stdp0.s)*(1+place->nlat.s))));
+ if(stdp1.l<0.)
+ r = -r;
+ *x = - r*sin(k * place->wlon.l);
+ *y = - r*cos(k * place->wlon.l);
+ return(1);
+}
+
+proj
+lambert(double par0, double par1)
+{
+ double temp;
+ if(fabs(par0)>fabs(par1)){
+ temp = par0;
+ par0 = par1;
+ par1 = temp;
+ }
+ deg2rad(par0, &stdp0);
+ deg2rad(par1, &stdp1);
+ if(fabs(par1+par0)<.1)
+ return(mercator());
+ if(fabs(par1-par0)<.1)
+ return(perspective(-1.));
+ if(fabs(par0)>89.5||fabs(par1)>89.5)
+ return(0);
+ k = 2*log(stdp1.c/stdp0.c)/log(
+ (1+stdp0.s)*(1-stdp1.s)/((1-stdp0.s)*(1+stdp1.s)));
+ return(Xlambert);
+}
diff --git a/src/cmd/map/libmap/laue.c b/src/cmd/map/libmap/laue.c
new file mode 100644
index 00000000..06c5f3a7
--- /dev/null
+++ b/src/cmd/map/libmap/laue.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+
+static int
+Xlaue(struct place *place, double *x, double *y)
+{
+ double r;
+ if(place->nlat.l<PI/4+FUZZ)
+ return(-1);
+ r = tan(PI-2*place->nlat.l);
+ if(r>3)
+ return(-1);
+ *x = - r * place->wlon.s;
+ *y = - r * place->wlon.c;
+ return(1);
+}
+
+proj
+laue(void)
+{
+ return(Xlaue);
+}
diff --git a/src/cmd/map/libmap/lune.c b/src/cmd/map/libmap/lune.c
new file mode 100644
index 00000000..dc58c6f0
--- /dev/null
+++ b/src/cmd/map/libmap/lune.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int Xstereographic(struct place *place, double *x, double *y);
+
+static struct place eastpole;
+static struct place westpole;
+static double eastx, easty;
+static double westx, westy;
+static double scale;
+static double pwr;
+
+/* conformal map w = ((1+z)^A - (1-z)^A)/((1+z)^A + (1-z)^A),
+ where A<1, maps unit circle onto a convex lune with x= +-1
+ mapping to vertices of angle A*PI at w = +-1 */
+
+/* there are cuts from E and W poles to S pole,
+ in absence of a cut routine, error is returned for
+ points outside a polar cap through E and W poles */
+
+static int Xlune(struct place *place, double *x, double *y)
+{
+ double stereox, stereoy;
+ double z1x, z1y, z2x, z2y;
+ double w1x, w1y, w2x, w2y;
+ double numx, numy, denx, deny;
+ if(place->nlat.l < eastpole.nlat.l-FUZZ)
+ return -1;
+ Xstereographic(place, &stereox, &stereoy);
+ stereox *= scale;
+ stereoy *= scale;
+ z1x = 1 + stereox;
+ z1y = stereoy;
+ z2x = 1 - stereox;
+ z2y = -stereoy;
+ cpow(z1x,z1y,&w1x,&w1y,pwr);
+ cpow(z2x,z2y,&w2x,&w2y,pwr);
+ numx = w1x - w2x;
+ numy = w1y - w2y;
+ denx = w1x + w2x;
+ deny = w1y + w2y;
+ cdiv(numx, numy, denx, deny, x, y);
+ return 1;
+}
+
+proj
+lune(double lat, double theta)
+{
+ deg2rad(lat, &eastpole.nlat);
+ deg2rad(-90.,&eastpole.wlon);
+ deg2rad(lat, &westpole.nlat);
+ deg2rad(90. ,&westpole.wlon);
+ Xstereographic(&eastpole, &eastx, &easty);
+ Xstereographic(&westpole, &westx, &westy);
+ if(fabs(easty)>FUZZ || fabs(westy)>FUZZ ||
+ fabs(eastx+westx)>FUZZ)
+ abort();
+ scale = 1/eastx;
+ pwr = theta/180;
+ return Xlune;
+}
diff --git a/src/cmd/map/libmap/mercator.c b/src/cmd/map/libmap/mercator.c
new file mode 100644
index 00000000..0ab63653
--- /dev/null
+++ b/src/cmd/map/libmap/mercator.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static int
+Xmercator(struct place *place, double *x, double *y)
+{
+ if(fabs(place->nlat.l) > 80.*RAD)
+ return(-1);
+ *x = -place->wlon.l;
+ *y = 0.5*log((1+place->nlat.s)/(1-place->nlat.s));
+ return(1);
+}
+
+proj
+mercator(void)
+{
+ return(Xmercator);
+}
+
+static double ecc = ECC;
+
+static int
+Xspmercator(struct place *place, double *x, double *y)
+{
+ if(Xmercator(place,x,y) < 0)
+ return(-1);
+ *y += 0.5*ecc*log((1-ecc*place->nlat.s)/(1+ecc*place->nlat.s));
+ return(1);
+}
+
+proj
+sp_mercator(void)
+{
+ return(Xspmercator);
+}
diff --git a/src/cmd/map/libmap/mkfile b/src/cmd/map/libmap/mkfile
new file mode 100644
index 00000000..e9031246
--- /dev/null
+++ b/src/cmd/map/libmap/mkfile
@@ -0,0 +1,50 @@
+<$PLAN9/src/mkhdr
+
+LIB=libmap.a
+OFILES=aitoff.$O\
+ albers.$O\
+ azequalarea.$O\
+ azequidist.$O\
+ bicentric.$O\
+ bonne.$O\
+ ccubrt.$O\
+ complex.$O\
+ conic.$O\
+ cubrt.$O\
+ cylequalarea.$O\
+ cylindrical.$O\
+ elco2.$O\
+ elliptic.$O\
+ fisheye.$O\
+ gall.$O\
+ gilbert.$O\
+ guyou.$O\
+ harrison.$O\
+ hex.$O\
+ homing.$O\
+ lagrange.$O\
+ lambert.$O\
+ laue.$O\
+ lune.$O\
+ mercator.$O\
+ mollweide.$O\
+ newyorker.$O\
+ orthographic.$O\
+ perspective.$O\
+ polyconic.$O\
+ rectangular.$O\
+ simpleconic.$O\
+ sinusoidal.$O\
+ tetra.$O\
+ trapezoidal.$O\
+ twocirc.$O\
+ zcoord.$O\
+
+HFILES=../map.h\
+
+<$PLAN9/src/mklib
+CFLAGS=$CFLAGS -I..
+
+nuke:V:
+ mk clean
+ rm -f libmap.a[$OS]
diff --git a/src/cmd/map/libmap/mollweide.c b/src/cmd/map/libmap/mollweide.c
new file mode 100644
index 00000000..3284c495
--- /dev/null
+++ b/src/cmd/map/libmap/mollweide.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static int
+Xmollweide(struct place *place, double *x, double *y)
+{
+ double z;
+ double w;
+ z = place->nlat.l;
+ if(fabs(z)<89.9*RAD)
+ do { /*newton for 2z+sin2z=pi*sin(lat)*/
+ w = (2*z+sin(2*z)-PI*place->nlat.s)/(2+2*cos(2*z));
+ z -= w;
+ } while(fabs(w)>=.00001);
+ *y = sin(z);
+ *x = - (2/PI)*cos(z)*place->wlon.l;
+ return(1);
+}
+
+proj
+mollweide(void)
+{
+ return(Xmollweide);
+}
diff --git a/src/cmd/map/libmap/newyorker.c b/src/cmd/map/libmap/newyorker.c
new file mode 100644
index 00000000..370e3b37
--- /dev/null
+++ b/src/cmd/map/libmap/newyorker.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double a;
+
+static int
+Xnewyorker(struct place *place, double *x, double *y)
+{
+ double r = PI/2 - place->nlat.l;
+ double s;
+ if(r<.001) /* cheat to plot center */
+ s = 0;
+ else if(r<a)
+ return -1;
+ else
+ s = log(r/a);
+ *x = -s * place->wlon.s;
+ *y = -s * place->wlon.c;
+ return(1);
+}
+
+proj
+newyorker(double a0)
+{
+ a = a0*RAD;
+ return(Xnewyorker);
+}
diff --git a/src/cmd/map/libmap/orthographic.c b/src/cmd/map/libmap/orthographic.c
new file mode 100644
index 00000000..7aac5b15
--- /dev/null
+++ b/src/cmd/map/libmap/orthographic.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+
+int
+Xorthographic(struct place *place, double *x, double *y)
+{
+ *x = - place->nlat.c * place->wlon.s;
+ *y = - place->nlat.c * place->wlon.c;
+ return(place->nlat.l<0.? 0 : 1);
+}
+
+proj
+orthographic(void)
+{
+ return(Xorthographic);
+}
+
+int
+olimb(double *lat, double *lon, double res)
+{
+ static int first = 1;
+ if(first) {
+ *lat = 0;
+ *lon = -180;
+ first = 0;
+ return 0;
+ }
+ *lon += res;
+ if(*lon <= 180)
+ return 1;
+ first = 1;
+ return -1;
+}
diff --git a/src/cmd/map/libmap/perspective.c b/src/cmd/map/libmap/perspective.c
new file mode 100644
index 00000000..7ce6b0d6
--- /dev/null
+++ b/src/cmd/map/libmap/perspective.c
@@ -0,0 +1,84 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+#define ORTHRAD 1000
+static double viewpt;
+
+static int
+Xperspective(struct place *place, double *x, double *y)
+{
+ double r;
+ if(viewpt<=1+FUZZ && fabs(place->nlat.s<=viewpt+.01))
+ return(-1);
+ r = place->nlat.c*(viewpt - 1.)/(viewpt - place->nlat.s);
+ *x = - r*place->wlon.s;
+ *y = - r*place->wlon.c;
+ if(r>4.)
+ return(-1);
+ if(fabs(viewpt)>1 && place->nlat.s<1/viewpt ||
+ fabs(viewpt)<=1 && place->nlat.s<viewpt)
+ return 0;
+ return(1);
+}
+
+proj
+perspective(double radius)
+{
+ viewpt = radius;
+ if(viewpt >= ORTHRAD)
+ return(Xorthographic);
+ if(fabs(viewpt-1.)<.0001)
+ return(0);
+ return(Xperspective);
+}
+
+ /* called from various conformal projections,
+ but not from stereographic itself */
+int
+Xstereographic(struct place *place, double *x, double *y)
+{
+ double v = viewpt;
+ int retval;
+ viewpt = -1;
+ retval = Xperspective(place, x, y);
+ viewpt = v;
+ return retval;
+}
+
+proj
+stereographic(void)
+{
+ viewpt = -1.;
+ return(Xperspective);
+}
+
+proj
+gnomonic(void)
+{
+ viewpt = 0.;
+ return(Xperspective);
+}
+
+int
+plimb(double *lat, double *lon, double res)
+{
+ static int first = 1;
+ if(viewpt >= ORTHRAD)
+ return olimb(lat, lon, res);
+ if(first) {
+ first = 0;
+ *lon = -180;
+ if(fabs(viewpt) < .01)
+ *lat = 0;
+ else if(fabs(viewpt)<=1)
+ *lat = asin(viewpt)/RAD;
+ else
+ *lat = asin(1/viewpt)/RAD;
+ } else
+ *lon += res;
+ if(*lon <= 180)
+ return 1;
+ first = 1;
+ return -1;
+}
diff --git a/src/cmd/map/libmap/polyconic.c b/src/cmd/map/libmap/polyconic.c
new file mode 100644
index 00000000..a07c97e3
--- /dev/null
+++ b/src/cmd/map/libmap/polyconic.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xpolyconic(struct place *place, double *x, double *y)
+{
+ double r, alpha;
+ double lat2, lon2;
+ if(fabs(place->nlat.l) > .01) {
+ r = place->nlat.c / place->nlat.s;
+ alpha = place->wlon.l * place->nlat.s;
+ *y = place->nlat.l + r*(1 - cos(alpha));
+ *x = - r*sin(alpha);
+ } else {
+ lon2 = place->wlon.l * place->wlon.l;
+ lat2 = place->nlat.l * place->nlat.l;
+ *y = place->nlat.l * (1+(lon2/2)*(1-(8+lon2)*lat2/12));
+ *x = - place->wlon.l * (1-lat2*(3+lon2)/6);
+ }
+ return(1);
+}
+
+proj
+polyconic(void)
+{
+ return(Xpolyconic);
+}
diff --git a/src/cmd/map/libmap/rectangular.c b/src/cmd/map/libmap/rectangular.c
new file mode 100644
index 00000000..d4a86c98
--- /dev/null
+++ b/src/cmd/map/libmap/rectangular.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double scale;
+
+static int
+Xrectangular(struct place *place, double *x, double *y)
+{
+ *x = -scale*place->wlon.l;
+ *y = place->nlat.l;
+ return(1);
+}
+
+proj
+rectangular(double par)
+{
+ scale = cos(par*RAD);
+ if(scale<.1)
+ return 0;
+ return(Xrectangular);
+}
diff --git a/src/cmd/map/libmap/simpleconic.c b/src/cmd/map/libmap/simpleconic.c
new file mode 100644
index 00000000..1ed1d1aa
--- /dev/null
+++ b/src/cmd/map/libmap/simpleconic.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double r0, a;
+
+static int
+Xsimpleconic(struct place *place, double *x, double *y)
+{
+ double r = r0 - place->nlat.l;
+ double t = a*place->wlon.l;
+ *x = -r*sin(t);
+ *y = -r*cos(t);
+ return 1;
+}
+
+proj
+simpleconic(double par0, double par1)
+{
+ struct coord lat0;
+ struct coord lat1;
+ deg2rad(par0,&lat0);
+ deg2rad(par1,&lat1);
+ if(fabs(lat0.l+lat1.l)<.01)
+ return rectangular(par0);
+ if(fabs(lat0.l-lat1.l)<.01) {
+ a = lat0.s/lat0.l;
+ r0 = lat0.c/lat0.s + lat0.l;
+ } else {
+ a = (lat1.c-lat0.c)/(lat0.l-lat1.l);
+ r0 = ((lat0.c+lat1.c)/a + lat1.l + lat0.l)/2;
+ }
+ return Xsimpleconic;
+}
diff --git a/src/cmd/map/libmap/sinusoidal.c b/src/cmd/map/libmap/sinusoidal.c
new file mode 100644
index 00000000..6a79706e
--- /dev/null
+++ b/src/cmd/map/libmap/sinusoidal.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+int
+Xsinusoidal(struct place *place, double *x, double *y)
+{
+ *x = - place->wlon.l * place->nlat.c;
+ *y = place->nlat.l;
+ return(1);
+}
+
+proj
+sinusoidal(void)
+{
+ return(Xsinusoidal);
+}
diff --git a/src/cmd/map/libmap/tetra.c b/src/cmd/map/libmap/tetra.c
new file mode 100644
index 00000000..6bdef49b
--- /dev/null
+++ b/src/cmd/map/libmap/tetra.c
@@ -0,0 +1,206 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+/*
+ * conformal map of earth onto tetrahedron
+ * the stages of mapping are
+ * (a) stereo projection of tetrahedral face onto
+ * isosceles curvilinear triangle with 3 120-degree
+ * angles and one straight side
+ * (b) map of this triangle onto half plane cut along
+ * 3 rays from the roots of unity to infinity
+ * formula (z^4+2*3^.5*z^2-1)/(z^4-2*3^.5*z^2-1)
+ * (c) do 3 times for each sector of plane:
+ * map of |arg z|<=pi/6, cut along z>1 into
+ * triangle |arg z|<=pi/6, Re z<=const,
+ * with upper side of cut going into upper half of
+ * of vertical side of triangle and lowere into lower
+ * formula int from 0 to z dz/sqrt(1-z^3)
+ *
+ * int from u to 1 3^.25*du/sqrt(1-u^3) =
+ F(acos((rt3-1+u)/(rt3+1-u)),sqrt(1/2+rt3/4))
+ * int from 1 to u 3^.25*du/sqrt(u^3-1) =
+ * F(acos((rt3+1-u)/(rt3-1+u)),sqrt(1/2-rt3/4))
+ * this latter formula extends analytically down to
+ * u=0 and is the basis of this routine, with the
+ * argument of complex elliptic integral elco2
+ * being tan(acos...)
+ * the formula F(pi-x,k) = 2*F(pi/2,k)-F(x,k) is
+ * used to cross over into the region where Re(acos...)>pi/2
+ * f0 and fpi are suitably scaled complete integrals
+*/
+
+#define TFUZZ 0.00001
+
+static struct place tpole[4]; /* point of tangency of tetrahedron face*/
+static double tpoleinit[4][2] = {
+ 1., 0.,
+ 1., 180.,
+ -1., 90.,
+ -1., -90.
+};
+static struct tproj {
+ double tlat,tlon; /* center of stereo projection*/
+ double ttwist; /* rotatn before stereo*/
+ double trot; /*rotate after projection*/
+ struct place projpl; /*same as tlat,tlon*/
+ struct coord projtw; /*same as ttwist*/
+ struct coord postrot; /*same as trot*/
+} tproj[4][4] = {
+{/*00*/ {0.},
+ /*01*/ {90., 0., 90., -90.},
+ /*02*/ {0., 45., -45., 150.},
+ /*03*/ {0., -45., -135., 30.}
+},
+{/*10*/ {90., 0., -90., 90.},
+ /*11*/ {0.},
+ /*12*/ {0., 135., -135., -150.},
+ /*13*/ {0., -135., -45., -30.}
+},
+{/*20*/ {0., 45., 135., -30.},
+ /*21*/ {0., 135., 45., -150.},
+ /*22*/ {0.},
+ /*23*/ {-90., 0., 180., 90.}
+},
+{/*30*/ {0., -45., 45., -150.},
+ /*31*/ {0., -135., 135., -30.},
+ /*32*/ {-90., 0., 0., 90.},
+ /*33*/ {0.}
+}};
+static double tx[4] = { /*where to move facet after final rotation*/
+ 0., 0., -1., 1. /*-1,1 to be sqrt(3)*/
+};
+static double ty[4] = {
+ 0., 2., -1., -1.
+};
+static double root3;
+static double rt3inv;
+static double two_rt3;
+static double tkc,tk,tcon;
+static double f0r,f0i,fpir,fpii;
+
+static void
+twhichp(struct place *g, int *p, int *q)
+{
+ int i,j,k;
+ double cosdist[4];
+ struct place *tp;
+ for(i=0;i<4;i++) {
+ tp = &tpole[i];
+ cosdist[i] = g->nlat.s*tp->nlat.s +
+ g->nlat.c*tp->nlat.c*(
+ g->wlon.s*tp->wlon.s +
+ g->wlon.c*tp->wlon.c);
+ }
+ j = 0;
+ for(i=1;i<4;i++)
+ if(cosdist[i] > cosdist[j])
+ j = i;
+ *p = j;
+ k = j==0?1:0;
+ for(i=0;i<4;i++)
+ if(i!=j&&cosdist[i]>cosdist[k])
+ k = i;
+ *q = k;
+}
+
+int
+Xtetra(struct place *place, double *x, double *y)
+{
+ int i,j;
+ struct place pl;
+ register struct tproj *tpp;
+ double vr, vi;
+ double br, bi;
+ double zr,zi,z2r,z2i,z4r,z4i,sr,si,tr,ti;
+ twhichp(place,&i,&j);
+ copyplace(place,&pl);
+ norm(&pl,&tproj[i][j].projpl,&tproj[i][j].projtw);
+ Xstereographic(&pl,&vr,&vi);
+ zr = vr/2;
+ zi = vi/2;
+ if(zr<=TFUZZ)
+ zr = TFUZZ;
+ csq(zr,zi,&z2r,&z2i);
+ csq(z2r,z2i,&z4r,&z4i);
+ z2r *= two_rt3;
+ z2i *= two_rt3;
+ cdiv(z4r+z2r-1,z4i+z2i,z4r-z2r-1,z4i-z2i,&sr,&si);
+ csqrt(sr-1,si,&tr,&ti);
+ cdiv(tcon*tr,tcon*ti,root3+1-sr,-si,&br,&bi);
+ if(br<0) {
+ br = -br;
+ bi = -bi;
+ if(!elco2(br,bi,tk,1.,1.,&vr,&vi))
+ return 0;
+ vr = fpir - vr;
+ vi = fpii - vi;
+ } else
+ if(!elco2(br,bi,tk,1.,1.,&vr,&vi))
+ return 0;
+ if(si>=0) {
+ tr = f0r - vi;
+ ti = f0i + vr;
+ } else {
+ tr = f0r + vi;
+ ti = f0i - vr;
+ }
+ tpp = &tproj[i][j];
+ *x = tr*tpp->postrot.c +
+ ti*tpp->postrot.s + tx[i];
+ *y = ti*tpp->postrot.c -
+ tr*tpp->postrot.s + ty[i];
+ return(1);
+}
+
+int
+tetracut(struct place *g, struct place *og, double *cutlon)
+{
+ int i,j,k;
+ if((g->nlat.s<=-rt3inv&&og->nlat.s<=-rt3inv) &&
+ (ckcut(g,og,*cutlon=0.)==2||ckcut(g,og,*cutlon=PI)==2))
+ return(2);
+ twhichp(g,&i,&k);
+ twhichp(og,&j,&k);
+ if(i==j||i==0||j==0)
+ return(1);
+ return(0);
+}
+
+proj
+tetra(void)
+{
+ int i;
+ int j;
+ register struct place *tp;
+ register struct tproj *tpp;
+ double t;
+ root3 = sqrt(3.);
+ rt3inv = 1/root3;
+ two_rt3 = 2*root3;
+ tkc = sqrt(.5-.25*root3);
+ tk = sqrt(.5+.25*root3);
+ tcon = 2*sqrt(root3);
+ elco2(tcon/(root3-1),0.,tkc,1.,1.,&f0r,&f0i);
+ elco2(1.e15,0.,tk,1.,1.,&fpir,&fpii);
+ fpir *= 2;
+ fpii *= 2;
+ for(i=0;i<4;i++) {
+ tx[i] *= f0r*root3;
+ ty[i] *= f0r;
+ tp = &tpole[i];
+ t = tp->nlat.s = tpoleinit[i][0]/root3;
+ tp->nlat.c = sqrt(1 - t*t);
+ tp->nlat.l = atan2(tp->nlat.s,tp->nlat.c);
+ deg2rad(tpoleinit[i][1],&tp->wlon);
+ for(j=0;j<4;j++) {
+ tpp = &tproj[i][j];
+ latlon(tpp->tlat,tpp->tlon,&tpp->projpl);
+ deg2rad(tpp->ttwist,&tpp->projtw);
+ deg2rad(tpp->trot,&tpp->postrot);
+ }
+ }
+ return(Xtetra);
+}
+
diff --git a/src/cmd/map/libmap/trapezoidal.c b/src/cmd/map/libmap/trapezoidal.c
new file mode 100644
index 00000000..977cf60c
--- /dev/null
+++ b/src/cmd/map/libmap/trapezoidal.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static struct coord stdpar0, stdpar1;
+static double k;
+static double yeq;
+
+static int
+Xtrapezoidal(struct place *place, double *x, double *y)
+{
+ *y = yeq + place->nlat.l;
+ *x = *y*k*place->wlon.l;
+ return 1;
+}
+
+proj
+trapezoidal(double par0, double par1)
+{
+ if(fabs(fabs(par0)-fabs(par1))<.1)
+ return rectangular(par0);
+ deg2rad(par0,&stdpar0);
+ deg2rad(par1,&stdpar1);
+ if(fabs(par1-par0) < .1)
+ k = stdpar1.s;
+ else
+ k = (stdpar1.c-stdpar0.c)/(stdpar0.l-stdpar1.l);
+ yeq = -stdpar1.l - stdpar1.c/k;
+ return Xtrapezoidal;
+}
diff --git a/src/cmd/map/libmap/twocirc.c b/src/cmd/map/libmap/twocirc.c
new file mode 100644
index 00000000..0d0e48a4
--- /dev/null
+++ b/src/cmd/map/libmap/twocirc.c
@@ -0,0 +1,80 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+static double
+quadratic(double a, double b, double c)
+{
+ double disc = b*b - 4*a*c;
+ return disc<0? 0: (-b-sqrt(disc))/(2*a);
+}
+
+/* for projections with meridians being circles centered
+on the x axis and parallels being circles centered on the y
+axis. Find the intersection of the meridian thru (m,0), (90,0),
+with the parallel thru (0,p), (p1,p2) */
+
+static int
+twocircles(double m, double p, double p1, double p2, double *x, double *y)
+{
+ double a; /* center of meridian circle, a>0 */
+ double b; /* center of parallel circle, b>0 */
+ double t,bb;
+ if(m > 0) {
+ twocircles(-m,p,p1,p2,x,y);
+ *x = -*x;
+ } else if(p < 0) {
+ twocircles(m,-p,p1,-p2,x,y);
+ *y = -*y;
+ } else if(p < .01) {
+ *x = m;
+ t = m/p1;
+ *y = p + (p2-p)*t*t;
+ } else if(m > -.01) {
+ *y = p;
+ *x = m - m*p*p;
+ } else {
+ b = p>=1? 1: p>.99? 0.5*(p+1 + p1*p1/(1-p)):
+ 0.5*(p*p-p1*p1-p2*p2)/(p-p2);
+ a = .5*(m - 1/m);
+ t = m*m-p*p+2*(b*p-a*m);
+ bb = b*b;
+ *x = quadratic(1+a*a/bb, -2*a + a*t/bb,
+ t*t/(4*bb) - m*m + 2*a*m);
+ *y = (*x*a+t/2)/b;
+ }
+ return 1;
+}
+
+static int
+Xglobular(struct place *place, double *x, double *y)
+{
+ twocircles(-2*place->wlon.l/PI,
+ 2*place->nlat.l/PI, place->nlat.c, place->nlat.s, x, y);
+ return 1;
+}
+
+proj
+globular(void)
+{
+ return Xglobular;
+}
+
+static int
+Xvandergrinten(struct place *place, double *x, double *y)
+{
+ double t = 2*place->nlat.l/PI;
+ double abst = fabs(t);
+ double pval = abst>=1? 1: abst/(1+sqrt(1-t*t));
+ double p2 = 2*pval/(1+pval);
+ twocircles(-place->wlon.l/PI, pval, sqrt(1-p2*p2), p2, x, y);
+ if(t < 0)
+ *y = -*y;
+ return 1;
+}
+
+proj
+vandergrinten(void)
+{
+ return Xvandergrinten;
+}
diff --git a/src/cmd/map/libmap/zcoord.c b/src/cmd/map/libmap/zcoord.c
new file mode 100644
index 00000000..7c3d3ad7
--- /dev/null
+++ b/src/cmd/map/libmap/zcoord.c
@@ -0,0 +1,143 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "map.h"
+
+static double cirmod(double);
+
+static struct place pole; /* map pole is tilted to here */
+static struct coord twist; /* then twisted this much */
+static struct place ipole; /* inverse transfrom */
+static struct coord itwist;
+
+void
+orient(double lat, double lon, double theta)
+{
+ lat = cirmod(lat);
+ if(lat>90.) {
+ lat = 180. - lat;
+ lon -= 180.;
+ theta -= 180.;
+ } else if(lat < -90.) {
+ lat = -180. - lat;
+ lon -= 180.;
+ theta -= 180;
+ }
+ latlon(lat,lon,&pole);
+ deg2rad(theta, &twist);
+ latlon(lat,180.-theta,&ipole);
+ deg2rad(180.-lon, &itwist);
+}
+
+void
+latlon(double lat, double lon, struct place *p)
+{
+ lat = cirmod(lat);
+ if(lat>90.) {
+ lat = 180. - lat;
+ lon -= 180.;
+ } else if(lat < -90.) {
+ lat = -180. - lat;
+ lon -= 180.;
+ }
+ deg2rad(lat,&p->nlat);
+ deg2rad(lon,&p->wlon);
+}
+
+void
+deg2rad(double theta, struct coord *coord)
+{
+ theta = cirmod(theta);
+ coord->l = theta*RAD;
+ if(theta==90) {
+ coord->s = 1;
+ coord->c = 0;
+ } else if(theta== -90) {
+ coord->s = -1;
+ coord->c = 0;
+ } else
+ sincos(coord);
+}
+
+static double
+cirmod(double theta)
+{
+ while(theta >= 180.)
+ theta -= 360;
+ while(theta<-180.)
+ theta += 360.;
+ return(theta);
+}
+
+void
+sincos(struct coord *coord)
+{
+ coord->s = sin(coord->l);
+ coord->c = cos(coord->l);
+}
+
+void
+normalize(struct place *gg)
+{
+ norm(gg,&pole,&twist);
+}
+
+void
+invert(struct place *g)
+{
+ norm(g,&ipole,&itwist);
+}
+
+void
+norm(struct place *gg, struct place *pp, struct coord *tw)
+{
+ register struct place *g; /*geographic coords */
+ register struct place *p; /* new pole in old coords*/
+ struct place m; /* standard map coords*/
+ g = gg;
+ p = pp;
+ if(p->nlat.s == 1.) {
+ if(p->wlon.l+tw->l == 0.)
+ return;
+ g->wlon.l -= p->wlon.l+tw->l;
+ } else {
+ if(p->wlon.l != 0) {
+ g->wlon.l -= p->wlon.l;
+ sincos(&g->wlon);
+ }
+ m.nlat.s = p->nlat.s * g->nlat.s
+ + p->nlat.c * g->nlat.c * g->wlon.c;
+ m.nlat.c = sqrt(1. - m.nlat.s * m.nlat.s);
+ m.nlat.l = atan2(m.nlat.s, m.nlat.c);
+ m.wlon.s = g->nlat.c * g->wlon.s;
+ m.wlon.c = p->nlat.c * g->nlat.s
+ - p->nlat.s * g->nlat.c * g->wlon.c;
+ m.wlon.l = atan2(m.wlon.s, - m.wlon.c)
+ - tw->l;
+ *g = m;
+ }
+ sincos(&g->wlon);
+ if(g->wlon.l>PI)
+ g->wlon.l -= 2*PI;
+ else if(g->wlon.l<-PI)
+ g->wlon.l += 2*PI;
+}
+
+double
+tan(double x)
+{
+ return(sin(x)/cos(x));
+}
+
+void
+printp(struct place *g)
+{
+printf("%.3f %.3f %.3f %.3f %.3f %.3f\n",
+g->nlat.l,g->nlat.s,g->nlat.c,g->wlon.l,g->wlon.s,g->wlon.c);
+}
+
+void
+copyplace(struct place *g1, struct place *g2)
+{
+ *g2 = *g1;
+}
diff --git a/src/cmd/map/map.c b/src/cmd/map/map.c
new file mode 100644
index 00000000..47109d43
--- /dev/null
+++ b/src/cmd/map/map.c
@@ -0,0 +1,1226 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "map.h"
+#include "iplot.h"
+
+#define NVERT 20 /* max number of vertices in a -v polygon */
+#define HALFWIDTH 8192 /* output scaled to fit in -HALFWIDTH,HALFWIDTH */
+#define LONGLINES (HALFWIDTH*4) /* permissible segment lengths */
+#define SHORTLINES (HALFWIDTH/8)
+#define SCALERATIO 10 /* of abs to rel data (see map(5)) */
+#define RESOL 2. /* coarsest resolution for tracing grid (degrees) */
+#define TWO_THRD 0.66666666666666667
+
+int normproj(double, double, double *, double *);
+int posproj(double, double, double *, double *);
+int picut(struct place *, struct place *, double *);
+double reduce(double);
+short getshort(FILE *);
+char *mapindex(char *);
+proj projection;
+
+
+static char *mapdir = "/lib/map"; /* default map directory */
+struct file {
+ char *name;
+ char *color;
+ char *style;
+};
+static struct file dfltfile = {
+ "world", BLACK, SOLID /* default map */
+};
+static struct file *file = &dfltfile; /* list of map files */
+static int nfile = 1; /* length of list */
+static char *currcolor = BLACK; /* current color */
+static char *gridcolor = BLACK;
+static char *bordcolor = BLACK;
+
+extern struct index index[];
+int halfwidth = HALFWIDTH;
+
+static int (*cut)(struct place *, struct place *, double *);
+static int (*limb)(double*, double*, double);
+static void dolimb(void);
+static int onlimb;
+static int poles;
+static double orientation[3] = { 90., 0., 0. }; /* -o option */
+static int oriented; /* nonzero if -o option occurred */
+static int upright; /* 1 if orientation[0]==90, -1 if -90, else 0*/
+static int delta = 1; /* -d setting */
+static double limits[4] = { /* -l parameters */
+ -90., 90., -180., 180.
+};
+static double klimits[4] = { /* -k parameters */
+ -90., 90., -180., 180.
+};
+static int limcase;
+static double rlimits[4]; /* limits expressed in radians */
+static double lolat, hilat, lolon, hilon;
+static double window[4] = { /* option -w */
+ -90., 90., -180., 180.
+};
+static int windowed; /* nozero if option -w */
+static struct vert { double x, y; } v[NVERT+2]; /*clipping polygon*/
+static struct edge { double a, b, c; } e[NVERT]; /* coeffs for linear inequality */
+static int nvert; /* number of vertices in clipping polygon */
+
+static double rwindow[4]; /* window, expressed in radians */
+static double params[2]; /* projection params */
+/* bounds on output values before scaling; found by coarse survey */
+static double xmin = 100.;
+static double xmax = -100.;
+static double ymin = 100.;
+static double ymax = -100.;
+static double xcent, ycent;
+static double xoff, yoff;
+double xrange, yrange;
+static int left = -HALFWIDTH;
+static int right = HALFWIDTH;
+static int bottom = -HALFWIDTH;
+static int top = HALFWIDTH;
+static int longlines = SHORTLINES; /* drop longer segments */
+static int shortlines = SHORTLINES;
+static int bflag = 1; /* 0 for option -b */
+static int s1flag = 0; /* 1 for option -s1 */
+static int s2flag = 0; /* 1 for option -s2 */
+static int rflag = 0; /* 1 for option -r */
+static int kflag = 0; /* 1 if option -k occurred */
+static int xflag = 0; /* 1 for option -x */
+ int vflag = 1; /* -1 if option -v occurred */
+static double position[3]; /* option -p */
+static double center[3] = {0., 0., 0.}; /* option -c */
+static struct coord crot; /* option -c */
+static double grid[3] = { 10., 10., RESOL }; /* option -g */
+static double dlat, dlon; /* resolution for tracing grid in lat and lon */
+static double scaling; /* to compute final integer output */
+static struct file *track; /* options -t and -u */
+static int ntrack; /* number of tracks present */
+static char *symbolfile; /* option -y */
+
+void clamp(double *px, double v);
+void clipinit(void);
+double diddle(struct place *, double, double);
+double diddle(struct place *, double, double);
+void dobounds(double, double, double, double, int);
+void dogrid(double, double, double, double);
+int duple(struct place *, double);
+double fmax(double, double);
+double fmin(double, double);
+void getdata(char *);
+int gridpt(double, double, int);
+int inpoly(double, double);
+int inwindow(struct place *);
+void pathnames(void);
+int pnorm(double);
+void radbds(double *w, double *rw);
+void revlon(struct place *, double);
+void satellite(struct file *);
+int seeable(double, double);
+void windlim(void);
+void realcut(void);
+
+int
+option(char *s)
+{
+
+ if(s[0]=='-' && (s[1]<'0'||s[1]>'9'))
+ return(s[1]!='.'&&s[1]!=0);
+ else
+ return(0);
+}
+
+void
+conv(int k, struct coord *g)
+{
+ g->l = (0.0001/SCALERATIO)*k;
+ sincos(g);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i,k;
+ char *s, *t, *style;
+ double x, y;
+ double lat, lon;
+ double *wlim;
+ double dd;
+ if(sizeof(short)!=2)
+ abort(); /* getshort() won't work */
+ s = getenv("MAP");
+ if(s)
+ file[0].name = s;
+ s = getenv("MAPDIR");
+ if(s)
+ mapdir = s;
+ if(argc<=1)
+ error("usage: map projection params options");
+ for(k=0;index[k].name;k++) {
+ s = index[k].name;
+ t = argv[1];
+ while(*s == *t){
+ if(*s==0) goto found;
+ s++;
+ t++;
+ }
+ }
+ fprintf(stderr,"projections:\n");
+ for(i=0;index[i].name;i++) {
+ fprintf(stderr,"%s",index[i].name);
+ for(k=0; k<index[i].npar; k++)
+ fprintf(stderr," p%d", k);
+ fprintf(stderr,"\n");
+ }
+ exits("error");
+found:
+ argv += 2;
+ argc -= 2;
+ cut = index[k].cut;
+ limb = index[k].limb;
+ poles = index[k].poles;
+ for(i=0;i<index[k].npar;i++) {
+ if(i>=argc||option(argv[i])) {
+ fprintf(stderr,"%s needs %d params\n",index[k].name,index[k].npar);
+ exits("error");
+ }
+ params[i] = atof(argv[i]);
+ }
+ argv += i;
+ argc -= i;
+ while(argc>0&&option(argv[0])) {
+ argc--;
+ argv++;
+ switch(argv[-1][1]) {
+ case 'm':
+ if(file == &dfltfile) {
+ file = 0;
+ nfile = 0;
+ }
+ while(argc && !option(*argv)) {
+ file = realloc(file,(nfile+1)*sizeof(*file));
+ file[nfile].name = *argv;
+ file[nfile].color = currcolor;
+ file[nfile].style = SOLID;
+ nfile++;
+ argv++;
+ argc--;
+ }
+ break;
+ case 'b':
+ bflag = 0;
+ for(nvert=0;nvert<NVERT&&argc>=2;nvert++) {
+ if(option(*argv))
+ break;
+ v[nvert].x = atof(*argv++);
+ argc--;
+ if(option(*argv))
+ break;
+ v[nvert].y = atof(*argv++);
+ argc--;
+ }
+ if(nvert>=NVERT)
+ error("too many clipping vertices");
+ break;
+ case 'g':
+ gridcolor = currcolor;
+ for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
+ grid[i] = atof(argv[i]);
+ switch(i) {
+ case 0:
+ grid[0] = grid[1] = 0.;
+ break;
+ case 1:
+ grid[1] = grid[0];
+ }
+ argc -= i;
+ argv += i;
+ break;
+ case 't':
+ style = SOLID;
+ goto casetu;
+ case 'u':
+ style = DOTDASH;
+ casetu:
+ while(argc && !option(*argv)) {
+ track = realloc(track,(ntrack+1)*sizeof(*track));
+ track[ntrack].name = *argv;
+ track[ntrack].color = currcolor;
+ track[ntrack].style = style;
+ ntrack++;
+ argv++;
+ argc--;
+ }
+ break;
+ case 'r':
+ rflag++;
+ break;
+ case 's':
+ switch(argv[-1][2]) {
+ case '1':
+ s1flag++;
+ break;
+ case 0: /* compatibility */
+ case '2':
+ s2flag++;
+ }
+ break;
+ case 'o':
+ for(i=0;i<3&&i<argc&&!option(argv[i]);i++)
+ orientation[i] = atof(argv[i]);
+ oriented++;
+ argv += i;
+ argc -= i;
+ break;
+ case 'l':
+ bordcolor = currcolor;
+ for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
+ limits[i] = atof(argv[i]);
+ argv += i;
+ argc -= i;
+ break;
+ case 'k':
+ kflag++;
+ for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
+ klimits[i] = atof(argv[i]);
+ argv += i;
+ argc -= i;
+ break;
+ case 'd':
+ if(argc>0&&!option(argv[0])) {
+ delta = atoi(argv[0]);
+ argv++;
+ argc--;
+ }
+ break;
+ case 'w':
+ bordcolor = currcolor;
+ windowed++;
+ for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
+ window[i] = atof(argv[i]);
+ argv += i;
+ argc -= i;
+ break;
+ case 'c':
+ for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
+ center[i] = atof(argv[i]);
+ argc -= i;
+ argv += i;
+ break;
+ case 'p':
+ for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
+ position[i] = atof(argv[i]);
+ argc -= i;
+ argv += i;
+ if(i!=3||position[2]<=0)
+ error("incomplete positioning");
+ break;
+ case 'y':
+ if(argc>0&&!option(argv[0])) {
+ symbolfile = argv[0];
+ argc--;
+ argv++;
+ }
+ break;
+ case 'v':
+ if(index[k].limb == 0)
+ error("-v does not apply here");
+ vflag = -1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case 'C':
+ if(argc && !option(*argv)) {
+ currcolor = colorcode(*argv);
+ argc--;
+ argv++;
+ }
+ break;
+ }
+ }
+ if(argc>0)
+ error("error in arguments");
+ pathnames();
+ clamp(&limits[0],-90.);
+ clamp(&limits[1],90.);
+ clamp(&klimits[0],-90.);
+ clamp(&klimits[1],90.);
+ clamp(&window[0],-90.);
+ clamp(&window[1],90.);
+ radbds(limits,rlimits);
+ limcase = limits[2]<-180.?0:
+ limits[3]>180.?2:
+ 1;
+ if(
+ window[0]>=window[1]||
+ window[2]>=window[3]||
+ window[0]>90.||
+ window[1]<-90.||
+ window[2]>180.||
+ window[3]<-180.)
+ error("unreasonable window");
+ windlim();
+ radbds(window,rwindow);
+ upright = orientation[0]==90? 1: orientation[0]==-90? -1: 0;
+ if(index[k].spheroid && !upright)
+ error("can't tilt the spheroid");
+ if(limits[2]>limits[3])
+ limits[3] += 360;
+ if(!oriented)
+ orientation[2] = (limits[2]+limits[3])/2;
+ orient(orientation[0],orientation[1],orientation[2]);
+ projection = (*index[k].prog)(params[0],params[1]);
+ if(projection == 0)
+ error("unreasonable projection parameters");
+ clipinit();
+ grid[0] = fabs(grid[0]);
+ grid[1] = fabs(grid[1]);
+ if(!kflag)
+ for(i=0;i<4;i++)
+ klimits[i] = limits[i];
+ if(klimits[2]>klimits[3])
+ klimits[3] += 360;
+ lolat = limits[0];
+ hilat = limits[1];
+ lolon = limits[2];
+ hilon = limits[3];
+ if(lolon>=hilon||lolat>=hilat||lolat<-90.||hilat>90.)
+ error("unreasonable limits");
+ wlim = kflag? klimits: window;
+ dlat = fmin(hilat-lolat,wlim[1]-wlim[0])/16;
+ dlon = fmin(hilon-lolon,wlim[3]-wlim[2])/32;
+ dd = fmax(dlat,dlon);
+ while(grid[2]>fmin(dlat,dlon)/2)
+ grid[2] /= 2;
+ realcut();
+ if(nvert<=0) {
+ for(lat=klimits[0];lat<klimits[1]+dd-FUZZ;lat+=dd) {
+ if(lat>klimits[1])
+ lat = klimits[1];
+ for(lon=klimits[2];lon<klimits[3]+dd-FUZZ;lon+=dd) {
+ i = (kflag?posproj:normproj)
+ (lat,lon+(lon<klimits[3]?FUZZ:-FUZZ),
+ &x,&y);
+ if(i*vflag <= 0)
+ continue;
+ if(x<xmin) xmin = x;
+ if(x>xmax) xmax = x;
+ if(y<ymin) ymin = y;
+ if(y>ymax) ymax = y;
+ }
+ }
+ } else {
+ for(i=0; i<nvert; i++) {
+ x = v[i].x;
+ y = v[i].y;
+ if(x<xmin) xmin = x;
+ if(x>xmax) xmax = x;
+ if(y<ymin) ymin = y;
+ if(y>ymax) ymax = y;
+ }
+ }
+ xrange = xmax - xmin;
+ yrange = ymax - ymin;
+ if(xrange<=0||yrange<=0)
+ error("map seems to be empty");
+ scaling = 2; /*plotting area from -1 to 1*/
+ if(position[2]!=0) {
+ if(posproj(position[0]-.5,position[1],&xcent,&ycent)==0||
+ posproj(position[0]+.5,position[1],&x,&y)==0)
+ error("unreasonable position");
+ scaling /= (position[2]*hypot(x-xcent,y-ycent));
+ if(posproj(position[0],position[1],&xcent,&ycent)==0)
+ error("unreasonable position");
+ } else {
+ scaling /= (xrange>yrange?xrange:yrange);
+ xcent = (xmin+xmax)/2;
+ ycent = (ymin+ymax)/2;
+ }
+ xoff = center[0]/scaling;
+ yoff = center[1]/scaling;
+ crot.l = center[2]*RAD;
+ sincos(&crot);
+ scaling *= HALFWIDTH*0.9;
+ if(symbolfile)
+ getsyms(symbolfile);
+ if(!s2flag) {
+ openpl();
+ erase();
+ }
+ range(left,bottom,right,top);
+ comment("grid","");
+ colorx(gridcolor);
+ pen(DOTTED);
+ if(grid[0]>0.)
+ for(lat=ceil(lolat/grid[0])*grid[0];
+ lat<=hilat;lat+=grid[0])
+ dogrid(lat,lat,lolon,hilon);
+ if(grid[1]>0.)
+ for(lon=ceil(lolon/grid[1])*grid[1];
+ lon<=hilon;lon+=grid[1])
+ dogrid(lolat,hilat,lon,lon);
+ comment("border","");
+ colorx(bordcolor);
+ pen(SOLID);
+ if(bflag) {
+ dolimb();
+ dobounds(lolat,hilat,lolon,hilon,0);
+ dobounds(window[0],window[1],window[2],window[3],1);
+ }
+ lolat = floor(limits[0]/10)*10;
+ hilat = ceil(limits[1]/10)*10;
+ lolon = floor(limits[2]/10)*10;
+ hilon = ceil(limits[3]/10)*10;
+ if(lolon>hilon)
+ hilon += 360.;
+ /*do tracks first so as not to lose the standard input*/
+ for(i=0;i<ntrack;i++) {
+ longlines = LONGLINES;
+ satellite(&track[i]);
+ longlines = shortlines;
+ }
+ for(i=0;i<nfile;i++) {
+ comment("mapfile",file[i].name);
+ colorx(file[i].color);
+ pen(file[i].style);
+ getdata(file[i].name);
+ }
+ move(right,bottom);
+ if(!s1flag)
+ closepl();
+ return 0;
+}
+
+/* Out of perverseness (really to recover from a dubious,
+ but documented, convention) the returns from projection
+ functions (-1 unplottable, 0 wrong sheet, 1 good) are
+ recoded into -1 wrong sheet, 0 unplottable, 1 good. */
+
+int
+fixproj(struct place *g, double *x, double *y)
+{
+ int i = (*projection)(g,x,y);
+ return i<0? 0: i==0? -1: 1;
+}
+
+int
+normproj(double lat, double lon, double *x, double *y)
+{
+ int i;
+ struct place geog;
+ latlon(lat,lon,&geog);
+/*
+ printp(&geog);
+*/
+ normalize(&geog);
+ if(!inwindow(&geog))
+ return(-1);
+ i = fixproj(&geog,x,y);
+ if(rflag)
+ *x = -*x;
+/*
+ printp(&geog);
+ fprintf(stderr,"%d %.3f %.3f\n",i,*x,*y);
+*/
+ return(i);
+}
+
+int
+posproj(double lat, double lon, double *x, double *y)
+{
+ int i;
+ struct place geog;
+ latlon(lat,lon,&geog);
+ normalize(&geog);
+ i = fixproj(&geog,x,y);
+ if(rflag)
+ *x = -*x;
+ return(i);
+}
+
+int
+inwindow(struct place *geog)
+{
+ if(geog->nlat.l<rwindow[0]-FUZZ||
+ geog->nlat.l>rwindow[1]+FUZZ||
+ geog->wlon.l<rwindow[2]-FUZZ||
+ geog->wlon.l>rwindow[3]+FUZZ)
+ return(0);
+ else return(1);
+}
+
+int
+inlimits(struct place *g)
+{
+ if(rlimits[0]-FUZZ>g->nlat.l||
+ rlimits[1]+FUZZ<g->nlat.l)
+ return(0);
+ switch(limcase) {
+ case 0:
+ if(rlimits[2]+TWOPI-FUZZ>g->wlon.l&&
+ rlimits[3]+FUZZ<g->wlon.l)
+ return(0);
+ break;
+ case 1:
+ if(rlimits[2]-FUZZ>g->wlon.l||
+ rlimits[3]+FUZZ<g->wlon.l)
+ return(0);
+ break;
+ case 2:
+ if(rlimits[2]>g->wlon.l&&
+ rlimits[3]-TWOPI+FUZZ<g->wlon.l)
+ return(0);
+ break;
+ }
+ return(1);
+}
+
+
+long patch[18][36];
+
+void
+getdata(char *mapfile)
+{
+ char *indexfile;
+ int kx,ky,c;
+ int k;
+ long b;
+ long *p;
+ int ip, jp;
+ int n;
+ struct place g;
+ int i, j;
+ double lat, lon;
+ int conn;
+ FILE *ifile, *xfile;
+
+ indexfile = mapindex(mapfile);
+ xfile = fopen(indexfile,"r");
+ if(xfile==NULL)
+ filerror("can't find map index", indexfile);
+ free(indexfile);
+ for(i=0,p=patch[0];i<18*36;i++,p++)
+ *p = 1;
+ while(!feof(xfile) && fscanf(xfile,"%d%d%ld",&i,&j,&b)==3)
+ patch[i+9][j+18] = b;
+ fclose(xfile);
+ ifile = fopen(mapfile,"r");
+ if(ifile==NULL)
+ filerror("can't find map data", mapfile);
+ for(lat=lolat;lat<hilat;lat+=10.)
+ for(lon=lolon;lon<hilon;lon+=10.) {
+ if(!seeable(lat,lon))
+ continue;
+ i = pnorm(lat);
+ j = pnorm(lon);
+ if((b=patch[i+9][j+18])&1)
+ continue;
+ fseek(ifile,b,0);
+ while((ip=getc(ifile))>=0&&(jp=getc(ifile))>=0){
+ if(ip!=(i&0377)||jp!=(j&0377))
+ break;
+ n = getshort(ifile);
+ conn = 0;
+ if(n > 0) { /* absolute coordinates */
+ kx = ky = 0; /* set */
+ for(k=0;k<n;k++){
+ kx = SCALERATIO*getshort(ifile);
+ ky = SCALERATIO*getshort(ifile);
+ if (((k%delta) != 0) && (k != (n-1)))
+ continue;
+ conv(kx,&g.nlat);
+ conv(ky,&g.wlon);
+ conn = plotpt(&g,conn);
+ }
+ } else { /* differential, scaled by SCALERATI0 */
+ n = -n;
+ kx = SCALERATIO*getshort(ifile);
+ ky = SCALERATIO*getshort(ifile);
+ for(k=0; k<n; k++) {
+ c = getc(ifile);
+ if(c&0200) c|= ~0177;
+ kx += c;
+ c = getc(ifile);
+ if(c&0200) c|= ~0177;
+ ky += c;
+ if(k%delta!=0&&k!=n-1)
+ continue;
+ conv(kx,&g.nlat);
+ conv(ky,&g.wlon);
+ conn = plotpt(&g,conn);
+ }
+ }
+ if(k==1) {
+ conv(kx,&g.nlat);
+ conv(ky,&g.wlon);
+ plotpt(&g,conn);
+ }
+ }
+ }
+ fclose(ifile);
+}
+
+int
+seeable(double lat0, double lon0)
+{
+ double x, y;
+ double lat, lon;
+ for(lat=lat0;lat<=lat0+10;lat+=2*grid[2])
+ for(lon=lon0;lon<=lon0+10;lon+=2*grid[2])
+ if(normproj(lat,lon,&x,&y)*vflag>0)
+ return(1);
+ return(0);
+}
+
+void
+satellite(struct file *t)
+{
+ char sym[50];
+ char lbl[50];
+ double scale;
+ int conn;
+ double lat,lon;
+ struct place place;
+ static FILE *ifile;
+
+ if(ifile == nil)
+ ifile = stdin;
+ if(t->name[0]!='-'||t->name[1]!=0) {
+ fclose(ifile);
+ if((ifile=fopen(t->name,"r"))==NULL)
+ filerror("can't find track", t->name);
+ }
+ comment("track",t->name);
+ colorx(t->color);
+ pen(t->style);
+ for(;;) {
+ conn = 0;
+ while(!feof(ifile) && fscanf(ifile,"%lf%lf",&lat,&lon)==2){
+ latlon(lat,lon,&place);
+ if(fscanf(ifile,"%1s",lbl) == 1) {
+ if(strchr("+-.0123456789",*lbl)==0)
+ break;
+ ungetc(*lbl,ifile);
+ }
+ conn = plotpt(&place,conn);
+ }
+ if(feof(ifile))
+ return;
+ fscanf(ifile,"%[^\n]",lbl+1);
+ switch(*lbl) {
+ case '"':
+ if(plotpt(&place,conn))
+ text(lbl+1);
+ break;
+ case ':':
+ case '!':
+ if(sscanf(lbl+1,"%s %lf",sym,&scale) <= 1)
+ scale = 1;
+ if(plotpt(&place,conn?conn:-1)) {
+ int r = *lbl=='!'?0:rflag?-1:1;
+ pen(SOLID);
+ if(putsym(&place,sym,scale,r) == 0)
+ text(lbl);
+ pen(t->style);
+ }
+ break;
+ default:
+ if(plotpt(&place,conn))
+ text(lbl);
+ break;
+ }
+ }
+}
+
+int
+pnorm(double x)
+{
+ int i;
+ i = x/10.;
+ i %= 36;
+ if(i>=18) return(i-36);
+ if(i<-18) return(i+36);
+ return(i);
+}
+
+void
+error(char *s)
+{
+ fprintf(stderr,"map: \r\n%s\n",s);
+ exits("error");
+}
+
+void
+filerror(char *s, char *f)
+{
+ fprintf(stderr,"\r\n%s %s\n",s,f);
+ exits("error");
+}
+
+char *
+mapindex(char *s)
+{
+ char *t = malloc(strlen(s)+3);
+ strcpy(t,s);
+ strcat(t,".x");
+ return t;
+}
+
+#define NOPT 32767
+static int ox = NOPT;
+static int oy = NOPT;
+
+int
+cpoint(int xi, int yi, int conn)
+{
+ int dx = abs(ox-xi);
+ int dy = abs(oy-yi);
+ if(!xflag && (xi<left||xi>=right || yi<bottom||yi>=top)) {
+ ox = oy = NOPT;
+ return 0;
+ }
+ if(conn == -1) /* isolated plotting symbol */
+ {}
+ else if(!conn)
+ point(xi,yi);
+ else {
+ if(dx+dy>longlines) {
+ ox = oy = NOPT; /* don't leap across cuts */
+ return 0;
+ }
+ if(dx || dy)
+ vec(xi,yi);
+ }
+ ox = xi, oy = yi;
+ return dx+dy<=2? 2: 1; /* 2=very near; see dogrid */
+}
+
+
+struct place oldg;
+
+int
+plotpt(struct place *g, int conn)
+{
+ int kx,ky;
+ int ret;
+ double cutlon;
+ if(!inlimits(g)) {
+ return(0);
+}
+ normalize(g);
+ if(!inwindow(g)) {
+ return(0);
+}
+ switch((*cut)(g,&oldg,&cutlon)) {
+ case 2:
+ if(conn) {
+ ret = duple(g,cutlon)|duple(g,cutlon);
+ oldg = *g;
+ return(ret);
+ }
+ case 0:
+ conn = 0;
+ default: /* prevent diags about bad return value */
+ case 1:
+ oldg = *g;
+ ret = doproj(g,&kx,&ky);
+ if(ret==0 || !onlimb && ret*vflag<=0)
+ return(0);
+ ret = cpoint(kx,ky,conn);
+ return ret;
+ }
+}
+
+int
+doproj(struct place *g, int *kx, int *ky)
+{
+ int i;
+ double x,y,x1,y1;
+/*fprintf(stderr,"dopr1 %f %f \n",g->nlat.l,g->wlon.l);*/
+ i = fixproj(g,&x,&y);
+ if(i == 0)
+ return(0);
+ if(rflag)
+ x = -x;
+/*fprintf(stderr,"dopr2 %f %f\n",x,y);*/
+ if(!inpoly(x,y)) {
+ return 0;
+}
+ x1 = x - xcent;
+ y1 = y - ycent;
+ x = (x1*crot.c - y1*crot.s + xoff)*scaling;
+ y = (x1*crot.s + y1*crot.c + yoff)*scaling;
+ *kx = x + (x>0?.5:-.5);
+ *ky = y + (y>0?.5:-.5);
+ return(i);
+}
+
+int
+duple(struct place *g, double cutlon)
+{
+ int kx,ky;
+ int okx,oky;
+ struct place ig;
+ revlon(g,cutlon);
+ revlon(&oldg,cutlon);
+ ig = *g;
+ invert(&ig);
+ if(!inlimits(&ig))
+ return(0);
+ if(doproj(g,&kx,&ky)*vflag<=0 ||
+ doproj(&oldg,&okx,&oky)*vflag<=0)
+ return(0);
+ cpoint(okx,oky,0);
+ cpoint(kx,ky,1);
+ return(1);
+}
+
+void
+revlon(struct place *g, double cutlon)
+{
+ g->wlon.l = reduce(cutlon-reduce(g->wlon.l-cutlon));
+ sincos(&g->wlon);
+}
+
+
+/* recognize problems of cuts
+ * move a point across cut to side of its predecessor
+ * if its very close to the cut
+ * return(0) if cut interrupts the line
+ * return(1) if line is to be drawn normally
+ * return(2) if line is so close to cut as to
+ * be properly drawn on both sheets
+*/
+
+int
+picut(struct place *g, struct place *og, double *cutlon)
+{
+ *cutlon = PI;
+ return(ckcut(g,og,PI));
+}
+
+int
+nocut(struct place *g, struct place *og, double *cutlon)
+{
+ USED(g);
+ USED(og);
+ USED(cutlon);
+/*
+#pragma ref g
+#pragma ref og
+#pragma ref cutlon
+*/
+ return(1);
+}
+
+int
+ckcut(struct place *g1, struct place *g2, double lon)
+{
+ double d1, d2;
+ double f1, f2;
+ int kx,ky;
+ d1 = reduce(g1->wlon.l -lon);
+ d2 = reduce(g2->wlon.l -lon);
+ if((f1=fabs(d1))<FUZZ)
+ d1 = diddle(g1,lon,d2);
+ if((f2=fabs(d2))<FUZZ) {
+ d2 = diddle(g2,lon,d1);
+ if(doproj(g2,&kx,&ky)*vflag>0)
+ cpoint(kx,ky,0);
+ }
+ if(f1<FUZZ&&f2<FUZZ)
+ return(2);
+ if(f1>PI*TWO_THRD||f2>PI*TWO_THRD)
+ return(1);
+ return(d1*d2>=0);
+}
+
+double
+diddle(struct place *g, double lon, double d)
+{
+ double d1;
+ d1 = FUZZ/2;
+ if(d<0)
+ d1 = -d1;
+ g->wlon.l = reduce(lon+d1);
+ sincos(&g->wlon);
+ return(d1);
+}
+
+double
+reduce(double lon)
+{
+ if(lon>PI)
+ lon -= 2*PI;
+ else if(lon<-PI)
+ lon += 2*PI;
+ return(lon);
+}
+
+
+double tetrapt = 35.26438968; /* atan(1/sqrt(2)) */
+
+void
+dogrid(double lat0, double lat1, double lon0, double lon1)
+{
+ double slat,slon,tlat,tlon;
+ register int conn, oconn;
+ slat = tlat = slon = tlon = 0;
+ if(lat1>lat0)
+ slat = tlat = fmin(grid[2],dlat);
+ else
+ slon = tlon = fmin(grid[2],dlon);;
+ conn = oconn = 0;
+ while(lat0<=lat1&&lon0<=lon1) {
+ conn = gridpt(lat0,lon0,conn);
+ if(projection==Xguyou&&slat>0) {
+ if(lat0<-45&&lat0+slat>-45)
+ conn = gridpt(-45.,lon0,conn);
+ else if(lat0<45&&lat0+slat>45)
+ conn = gridpt(45.,lon0,conn);
+ } else if(projection==Xtetra&&slat>0) {
+ if(lat0<-tetrapt&&lat0+slat>-tetrapt) {
+ gridpt(-tetrapt-.001,lon0,conn);
+ conn = gridpt(-tetrapt+.001,lon0,0);
+ }
+ else if(lat0<tetrapt&&lat0+slat>tetrapt) {
+ gridpt(tetrapt-.001,lon0,conn);
+ conn = gridpt(tetrapt+.001,lon0,0);
+ }
+ }
+ if(conn==0 && oconn!=0) {
+ if(slat+slon>.05) {
+ lat0 -= slat; /* steps too big */
+ lon0 -= slon; /* or near bdry */
+ slat /= 2;
+ slon /= 2;
+ conn = oconn = gridpt(lat0,lon0,conn);
+ } else
+ oconn = 0;
+ } else {
+ if(conn==2) {
+ slat = tlat;
+ slon = tlon;
+ conn = 1;
+ }
+ oconn = conn;
+ }
+ lat0 += slat;
+ lon0 += slon;
+ }
+ gridpt(lat1,lon1,conn);
+}
+
+static int gridinv; /* nonzero when doing window bounds */
+
+int
+gridpt(double lat, double lon, int conn)
+{
+ struct place g;
+/*fprintf(stderr,"%f %f\n",lat,lon);*/
+ latlon(lat,lon,&g);
+ if(gridinv)
+ invert(&g);
+ return(plotpt(&g,conn));
+}
+
+/* win=0 ordinary grid lines, win=1 window lines */
+
+void
+dobounds(double lolat, double hilat, double lolon, double hilon, int win)
+{
+ gridinv = win;
+ if(lolat>-90 || win && (poles&1)!=0)
+ dogrid(lolat+FUZZ,lolat+FUZZ,lolon,hilon);
+ if(hilat<90 || win && (poles&2)!=0)
+ dogrid(hilat-FUZZ,hilat-FUZZ,lolon,hilon);
+ if(hilon-lolon<360 || win && cut==picut) {
+ dogrid(lolat,hilat,lolon+FUZZ,lolon+FUZZ);
+ dogrid(lolat,hilat,hilon-FUZZ,hilon-FUZZ);
+ }
+ gridinv = 0;
+}
+
+static void
+dolimb(void)
+{
+ double lat, lon;
+ double res = fmin(dlat, dlon)/4;
+ int conn = 0;
+ int newconn;
+ if(limb == 0)
+ return;
+ onlimb = gridinv = 1;
+ for(;;) {
+ newconn = (*limb)(&lat, &lon, res);
+ if(newconn == -1)
+ break;
+ conn = gridpt(lat, lon, conn*newconn);
+ }
+ onlimb = gridinv = 0;
+}
+
+
+void
+radbds(double *w, double *rw)
+{
+ int i;
+ for(i=0;i<4;i++)
+ rw[i] = w[i]*RAD;
+ rw[0] -= FUZZ;
+ rw[1] += FUZZ;
+ rw[2] -= FUZZ;
+ rw[3] += FUZZ;
+}
+
+void
+windlim(void)
+{
+ double center = orientation[0];
+ double colat;
+ if(center>90)
+ center = 180 - center;
+ if(center<-90)
+ center = -180 - center;
+ if(fabs(center)>90)
+ error("unreasonable orientation");
+ colat = 90 - window[0];
+ if(center-colat>limits[0])
+ limits[0] = center - colat;
+ if(center+colat<limits[1])
+ limits[1] = center + colat;
+}
+
+
+short
+getshort(FILE *f)
+{
+ int c, r;
+ c = getc(f);
+ r = (c | getc(f)<<8);
+ if (r&0x8000)
+ r |= ~0xFFFF; /* in case short > 16 bits */
+ return r;
+}
+
+double
+fmin(double x, double y)
+{
+ return(x<y?x:y);
+}
+
+double
+fmax(double x, double y)
+{
+ return(x>y?x:y);
+}
+
+void
+clamp(double *px, double v)
+{
+ *px = (v<0?fmax:fmin)(*px,v);
+}
+
+void
+pathnames(void)
+{
+ int i;
+ char *t, *indexfile, *name;
+ FILE *f, *fx;
+ for(i=0; i<nfile; i++) {
+ name = file[i].name;
+ if(*name=='/')
+ continue;
+ indexfile = mapindex(name);
+ /* ansi equiv of unix access() call */
+ f = fopen(name, "r");
+ fx = fopen(indexfile, "r");
+ if(f) fclose(f);
+ if(fx) fclose(fx);
+ free(indexfile);
+ if(f && fx)
+ continue;
+ t = malloc(strlen(name)+strlen(mapdir)+2);
+ strcpy(t,mapdir);
+ strcat(t,"/");
+ strcat(t,name);
+ file[i].name = t;
+ }
+}
+
+void
+clipinit(void)
+{
+ int i;
+ double s,t;
+ if(nvert<=0)
+ return;
+ for(i=0; i<nvert; i++) { /*convert latlon to xy*/
+ if(normproj(v[i].x,v[i].y,&v[i].x,&v[i].y)==0)
+ error("invisible clipping vertex");
+ }
+ if(nvert==2) { /*rectangle with diag specified*/
+ nvert = 4;
+ v[2] = v[1];
+ v[1].x=v[0].x, v[1].y=v[2].y, v[3].x=v[2].x, v[3].y=v[0].y;
+ }
+ v[nvert] = v[0];
+ v[nvert+1] = v[1];
+ s = 0;
+ for(i=1; i<=nvert; i++) { /*test for convexity*/
+ t = (v[i-1].x-v[i].x)*(v[i+1].y-v[i].y) -
+ (v[i-1].y-v[i].y)*(v[i+1].x-v[i].x);
+ if(t<-FUZZ && s>=0) s = 1;
+ if(t>FUZZ && s<=0) s = -1;
+ if(-FUZZ<=t&&t<=FUZZ || t*s>0) {
+ s = 0;
+ break;
+ }
+ }
+ if(s==0)
+ error("improper clipping polygon");
+ for(i=0; i<nvert; i++) { /*edge equation ax+by=c*/
+ e[i].a = s*(v[i+1].y - v[i].y);
+ e[i].b = s*(v[i].x - v[i+1].x);
+ e[i].c = s*(v[i].x*v[i+1].y - v[i].y*v[i+1].x);
+ }
+}
+
+int
+inpoly(double x, double y)
+{
+ int i;
+ for(i=0; i<nvert; i++) {
+ register struct edge *ei = &e[i];
+ double val = x*ei->a + y*ei->b - ei->c;
+ if(val>10*FUZZ)
+ return(0);
+ }
+ return 1;
+}
+
+void
+realcut()
+{
+ struct place g;
+ double lat;
+
+ if(cut != picut) /* punt on unusual cuts */
+ return;
+ for(lat=window[0]; lat<=window[1]; lat+=grid[2]) {
+ g.wlon.l = PI;
+ sincos(&g.wlon);
+ g.nlat.l = lat*RAD;
+ sincos(&g.nlat);
+ if(!inwindow(&g)) {
+ break;
+}
+ invert(&g);
+ if(inlimits(&g)) {
+ return;
+}
+ }
+ longlines = shortlines = LONGLINES;
+ cut = nocut; /* not necessary; small eff. gain */
+}
diff --git a/src/cmd/map/map.h b/src/cmd/map/map.h
new file mode 100644
index 00000000..d96497c8
--- /dev/null
+++ b/src/cmd/map/map.h
@@ -0,0 +1,147 @@
+/*
+#pragma lib "/sys/src/cmd/map/libmap/libmap.a$O"
+#pragma src "/sys/src/cmd/map/libmap"
+*/
+
+#define index index0
+#ifndef PI
+#define PI 3.1415926535897932384626433832795028841971693993751
+#endif
+
+#define TWOPI (2*PI)
+#define RAD (PI/180)
+double hypot(double, double); /* sqrt(a*a+b*b) */
+double tan(double); /* not in K&R library */
+
+#define ECC .08227185422 /* eccentricity of earth */
+#define EC2 .006768657997
+
+#define FUZZ .0001
+#define UNUSED 0.0 /* a dummy double parameter */
+
+struct coord {
+ double l; /* lat or lon in radians*/
+ double s; /* sin */
+ double c; /* cos */
+};
+struct place {
+ struct coord nlat;
+ struct coord wlon;
+};
+
+typedef int (*proj)(struct place *, double *, double *);
+
+struct index { /* index of known projections */
+ char *name; /* name of projection */
+ proj (*prog)(double, double);
+ /* pointer to projection function */
+ int npar; /* number of params */
+ int (*cut)(struct place *, struct place *, double *);
+ /* function that handles cuts--eg longitude 180 */
+ int poles; /*1 S pole is a line, 2 N pole is, 3 both*/
+ int spheroid; /* poles must be at 90 deg if nonzero */
+ int (*limb)(double *lat, double *lon, double resolution);
+ /* get next place on limb */
+ /* return -1 if done, 0 at gap, else 1 */
+};
+
+
+proj aitoff(void);
+proj albers(double, double);
+int Xazequalarea(struct place *, double *, double *);
+proj azequalarea(void);
+int Xazequidistant(struct place *, double *, double *);
+proj azequidistant(void);
+proj bicentric(double);
+proj bonne(double);
+proj conic(double);
+proj cylequalarea(double);
+int Xcylindrical(struct place *, double *, double *);
+proj cylindrical(void);
+proj elliptic(double);
+proj fisheye(double);
+proj gall(double);
+proj gilbert(void);
+proj globular(void);
+proj gnomonic(void);
+int guycut(struct place *, struct place *, double *);
+int Xguyou(struct place *, double *, double *);
+proj guyou(void);
+proj harrison(double, double);
+int hexcut(struct place *, struct place *, double *);
+proj hex(void);
+proj homing(double);
+int hlimb(double*, double*, double resolution);
+proj lagrange(void);
+proj lambert(double, double);
+proj laue(void);
+proj lune(double, double);
+proj loxodromic(double); /* not in library */
+proj mecca(double);
+int mlimb(double*, double*, double resolution);
+proj mercator(void);
+proj mollweide(void);
+proj newyorker(double);
+proj ortelius(double, double); /* not in library */
+int Xorthographic(struct place *place, double *x, double *y);
+proj orthographic(void);
+int olimb(double*, double*, double);
+proj perspective(double);
+int plimb(double*, double*, double resolution);
+int Xpolyconic(struct place *, double *, double *);
+proj polyconic(void);
+proj rectangular(double);
+proj simpleconic(double, double);
+int Xsinusoidal(struct place *, double *, double *);
+proj sinusoidal(void);
+proj sp_albers(double, double);
+proj sp_mercator(void);
+proj square(void);
+int Xstereographic(struct place *, double *, double *);
+proj stereographic(void);
+int Xtetra(struct place *, double *, double *);
+int tetracut(struct place *, struct place *, double *);
+proj tetra(void);
+proj trapezoidal(double, double);
+proj vandergrinten(void);
+proj wreath(double, double); /* not in library */
+
+void findxy(double, double *, double *);
+void albscale(double, double, double, double);
+void invalb(double, double, double *, double *);
+
+void cdiv(double, double, double, double, double *, double *);
+void cmul(double, double, double, double, double *, double *);
+void cpow(double, double, double *, double *, double);
+void csq(double, double, double *, double *);
+void csqrt(double, double, double *, double *);
+void ccubrt(double, double, double *, double *);
+double cubrt(double);
+int elco2(double, double, double, double, double, double *, double *);
+void cdiv2(double, double, double, double, double *, double *);
+void csqr(double, double, double *, double *);
+
+void orient(double, double, double);
+void latlon(double, double, struct place *);
+void deg2rad(double, struct coord *);
+void sincos(struct coord *);
+void normalize(struct place *);
+void invert(struct place *);
+void norm(struct place *, struct place *, struct coord *);
+void printp(struct place *);
+void copyplace(struct place *, struct place *);
+
+int picut(struct place *, struct place *, double *);
+int ckcut(struct place *, struct place *, double);
+double reduce(double);
+
+void getsyms(char *);
+int putsym(struct place *, char *, double, int);
+void filerror(char *s, char *f);
+void error(char *s);
+int doproj(struct place *, int *, int *);
+int cpoint(int, int, int);
+int plotpt(struct place *, int);
+int nocut(struct place *, struct place *, double *);
+
+extern int (*projection)(struct place *, double *, double *);
diff --git a/src/cmd/map/map.rc b/src/cmd/map/map.rc
new file mode 100755
index 00000000..eaa75c91
--- /dev/null
+++ b/src/cmd/map/map.rc
@@ -0,0 +1,103 @@
+#!/bin/rc
+
+rfork en
+
+# F FEATUREs, M map files, A other arguments
+FEATURE=no
+
+if (~ $MAPPROG '')
+ MAPPROG=/bin/aux/mapd
+
+if (~ $MAPDIR '')
+ MAPDIR=/lib/map
+
+F=(); M=(); A=();
+for (i) {
+ switch ($FEATURE) {
+ case no
+ switch ($i) {
+ case -f
+ FEATURE=yes
+ F=($F)
+ case *
+ A=($A $i)
+ }
+ case yes
+ switch ($i) {
+ case -f
+ case -*
+ A=($A $i)
+ FEATURE=no
+ case riv*2
+ F=($F 201 202)
+ case riv*3
+ F=($F 201 202 203)
+ case riv*4
+ F=($F 201 202 203 204)
+ case riv*
+ F=($F 201)
+ case iriv*2
+ F=($F 206 207)
+ case iriv*[34]
+ F=($F 206 207 208)
+ case iriv*
+ F=($F 206)
+ case coast*2 shore*2 lake*2
+ F=($F 102)
+ case coast*3 shore*3 lake*3
+ F=($F 102 103)
+ case coast*4 shore*4 lake*4
+ F=($F 102 103 104)
+ case coast* shore* lake*
+ case ilake*[234] ishore*[234]
+ F=($F 106 107)
+ case ilake* ishore*
+ F=($F 106)
+ case reef*
+ F=($F 108)
+ case canal*2
+ F=($F 210 211)
+ case canal*[34]
+ F=($F 210 211 212)
+ case canal*
+ F=($F 210)
+ case glacier*
+ F=($F 115)
+ case state* province*
+ F=($F 401)
+ case countr*2
+ F=($F 301 302)
+ case countr*[34]
+ F=($F 301 302 303)
+ case countr*
+ F=($F 301)
+ case salt*[234]
+ F=($F 109 110)
+ case salt*
+ F=($F 109)
+ case ice*[234] shel*[234]
+ F=($F 113 114)
+ case ice* shel*
+ F=($F 113)
+ case *
+ echo map: unknown feature $i >[1=2]
+ exits "unknown feature"
+ }
+ }
+}
+
+for (j in $F) {
+ if (test -r $MAPDIR/$j)
+ M=($M $MAPDIR/$j)
+}
+
+if (~ $F ?*) {
+ if (test -r $MAPDIR/101)
+ M=(101 $M)
+ M=(-m $M)
+}
+
+if (~ $MAP '')
+ MAP=world
+
+MAP=$MAP MAPDIR=$MAPDIR $MAPPROG $A $M
diff --git a/src/cmd/map/mapdemo.rc b/src/cmd/map/mapdemo.rc
new file mode 100755
index 00000000..033969ab
--- /dev/null
+++ b/src/cmd/map/mapdemo.rc
@@ -0,0 +1,83 @@
+#!/bin/rc
+
+fn demo {proj=$1; shift;
+ label=$1; shift;
+ { echo 'o'
+ echo 'ra -8192 -8492 8192 8492'
+ echo 'e'
+ echo 'm -8192 8192'
+ echo t $type
+ echo 'm -8192 -8192'
+ echo t $proj - $label
+ MAP=world MAPDIR=/lib/map map $proj $* -s -d 5
+ }
+ sleep 5
+}
+
+rfork en
+{
+type='Equatorial projections centered on long. 0. Parallels are straight lines.'
+
+demo mercator 'equally spaced straight meridians, conformal, straight compass courses'
+demo sinusoidal 'equally spaced parallels, equal-area, same as bonne(0)'
+demo cylequalarea 'equally spaced straight meridians, equal-area, true scale on Eq' 0
+demo cylindrical 'central projection on tangent cylinder'
+demo rectangular 'equally spaced parallels, equally spaced straight meridians, true scale on Eq' 0
+demo gall 'parallels spaced stereographically on prime meridian, equally spaced straight meridians, true scale on Eq' 0
+demo mollweide '(homalographic) equal-area, hemisphere is a circle'
+demo gilbert 'globe mapped conformally on hemisphere, viewed orthographically'
+
+type='Azimuthal: centered on the North Pole, Parallels are concentric circles, Meridians are equally spaced radial lines'
+
+demo azequidistant 'equally spaced parallels, true distances from pole'
+demo azequalarea 'equal area'
+demo gnomonic 'central projecton on tangent plane, straight great circles'
+demo perspective 'viewed along earth''s axis 2 earth radii from center of earth' 2
+demo orthographic 'viewed from infinity'
+demo stereographic 'conformal, projected from opposite pole'
+demo laue 'radius = tan(2\(mu colatitude ), used in xray crystallography'
+demo fisheye 'fisheye view of stereographic map, index of refraction 2' 2 -o 40.75 74
+demo newyorker 'New Yorker map from viewing pedestal of radius .5' .5 -o 40.75 74
+
+type='Polar conic projections symmetric about the Prime Meridian. Parallels are segments of concentric circles.'
+
+demo conic 'central projection on cone tangent at 40' 40
+demo simpleconic 'equally spaced parallels, true scale on 20 and 50' 20 50
+demo lambert 'conformal, true scale on 20 and 50' 20 50
+demo albers 'equal-area, true scale on 20 and 50' 20 50
+demo bonne 'equally spaced parallels, equal-area, parallel 40 developed from tangent cone' 40
+
+type='Projections with bilateral symmetry about the Prime Meridian and the equator.'
+
+demo polyconic 'parallels developed from tangent cones, equally spaced along Prime Meridian'
+demo aitoff 'equal-area projection of globe onto 2-to-1 ellipse, based on azequalarea'
+demo lagrange 'conformal, maps whole sphere into a circle'
+demo bicentric 'points plotted at true azimuth from two centers on the equator at longitudes +-40, great circles are straight lines' 40
+demo elliptic 'points are plotted at true distance from two centers on the equator at longitudes +-40' 40
+demo globular 'hemisphere is circle, circular meridians and parallels'
+demo vandergrinten 'sphere is circle, meridians as in globular, circular arc parallels resemble mercator'
+
+type='Doubly periodic conformal projections.'
+
+demo guyou 'W and E hemispheres are square'
+demo square 'World is square with Poles at diagonally opposite corners'
+demo tetra 'map on tetrahedron with edge tangent to Prime Meridian at S Pole, unfolded into equilateral triangle'
+demo hex 'world is hexagon centered on N Pole, N and S hemispheres are equilateral
+triangles'
+
+type='Retroazimuthal projections. Directions to center are true.'
+
+demo mecca 'equally spaced vertical meridians' 21.4 -o 90 -39.8
+demo homing 'distances to Mecca are true' 21.4 -o 90 -39.8
+
+type='Miscellaneous projections.'
+
+demo harrison 'oblique perspective from above the North Pole, 2 earth radii from the earth, looking along the Date Line 40 degrees off vertical' 2 40
+demo trapezoidal 'equally spaced parallels, straight meridians equally spaced along parallels, true scale at 20 and 50 on Prime Meridian' 20 50
+demo lune 'conformal, polar cap above Eq is 60-degree lune' 0 60
+
+type='Maps based on the spheroid'
+
+demo sp_mercator 'equally spaced straight meridians, conformal'
+demo sp_albers 'equal-area, true scale on 20 and 50' 20 50
+} | plot
diff --git a/src/cmd/map/mkfile b/src/cmd/map/mkfile
new file mode 100644
index 00000000..4d272749
--- /dev/null
+++ b/src/cmd/map/mkfile
@@ -0,0 +1,32 @@
+<$PLAN9/src/mkhdr
+
+TARG=mapd
+LIB=libmap/libmap.a
+OFILES=map.$O\
+ symbol.$O\
+ index.$O\
+ sqrt.$O\
+
+HFILES=map.h\
+ iplot.h\
+
+<$PLAN9/src/mkone
+
+
+$O.out:V: $OFILES $LIB
+ $LD $LDFLAGS -o $target $prereq
+
+$LIB:V:
+ cd libmap
+ mk install
+
+installall:V:
+ for(objtype in $CPUS)
+ mk install
+ cp map.rc /rc/bin/map
+ cp mapdemo.rc /rc/bin/mapdemo
+
+clean nuke:V:
+ rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG
+ cd libmap; mk clean
+
diff --git a/src/cmd/map/route.c b/src/cmd/map/route.c
new file mode 100644
index 00000000..c4c67134
--- /dev/null
+++ b/src/cmd/map/route.c
@@ -0,0 +1,131 @@
+#include <u.h>
+#include <libc.h>
+#include "map.h"
+
+/* Given two lat-lon pairs, find an orientation for the
+ -o option of "map" that will place those two points
+ on the equator of a standard projection, equally spaced
+ about the prime meridian.
+
+ -w and -l options are suggested also.
+
+ Option -t prints out a series of
+ coordinates that follows the (great circle) track
+ in the original coordinate system,
+ followed by ".
+ This data is just right for map -t.
+
+ Option -i inverts the map top-to-bottom.
+*/
+struct place pole;
+struct coord twist;
+int track;
+int inv = -1;
+
+extern void doroute(double, double, double, double, double);
+
+void
+dorot(double a, double b, double *x, double *y, void (*f)(struct place *))
+{
+ struct place g;
+ deg2rad(a,&g.nlat);
+ deg2rad(b,&g.wlon);
+ (*f)(&g);
+ *x = g.nlat.l/RAD;
+ *y = g.wlon.l/RAD;
+}
+
+void
+rotate(double a, double b, double *x, double *y)
+{
+ dorot(a,b,x,y,normalize);
+}
+
+void
+rinvert(double a, double b, double *x, double *y)
+{
+ dorot(a,b,x,y,invert);
+}
+
+main(int argc, char **argv)
+{
+#pragma ref argv
+ double an,aw,bn,bw;
+ ARGBEGIN {
+ case 't':
+ track = 1;
+ break;
+
+ case 'i':
+ inv = 1;
+ break;
+
+ default:
+ exits("route: bad option");
+ } ARGEND;
+ if (argc<4) {
+ print("use route [-t] [-i] lat lon lat lon\n");
+ exits("arg count");
+ }
+ an = atof(argv[0]);
+ aw = atof(argv[1]);
+ bn = atof(argv[2]);
+ bw = atof(argv[3]);
+ doroute(inv*90.,an,aw,bn,bw);
+ return 0;
+}
+
+void
+doroute(double dir, double an, double aw, double bn, double bw)
+{
+ double an1,aw1,bn1,bw1,pn,pw;
+ double theta;
+ double cn,cw,cn1,cw1;
+ int i,n;
+ orient(an,aw,0.);
+ rotate(bn,bw,&bn1,&bw1);
+/* printf("b %f %f\n",bn1,bw1);*/
+ orient(an,aw,bw1);
+ rinvert(0.,dir,&pn,&pw);
+/* printf("p %f %f\n",pn,pw);*/
+ orient(pn,pw,0.);
+ rotate(an,aw,&an1,&aw1);
+ rotate(bn,bw,&bn1,&bw1);
+ theta = (aw1+bw1)/2;
+/* printf("a %f %f \n",an1,aw1);*/
+ orient(pn,pw,theta);
+ rotate(an,aw,&an1,&aw1);
+ rotate(bn,bw,&bn1,&bw1);
+ if(fabs(aw1-bw1)>180)
+ if(theta<0.) theta+=180;
+ else theta -= 180;
+ orient(pn,pw,theta);
+ rotate(an,aw,&an1,&aw1);
+ rotate(bn,bw,&bn1,&bw1);
+ if(!track) {
+ double dlat, dlon, t;
+ /* printf("A %.4f %.4f\n",an1,aw1); */
+ /* printf("B %.4f %.4f\n",bn1,bw1); */
+ cw1 = fabs(bw1-aw1); /* angular difference for map margins */
+ /* while (aw<0.0)
+ aw += 360.;
+ while (bw<0.0)
+ bw += 360.; */
+ dlon = fabs(aw-bw);
+ if (dlon>180)
+ dlon = 360-dlon;
+ dlat = fabs(an-bn);
+ printf("-o %.4f %.4f %.4f -w %.2f %.2f %.2f %.2f \n",
+ pn,pw,theta, -0.3*cw1, .3*cw1, -.6*cw1, .6*cw1);
+
+ } else {
+ cn1 = 0;
+ n = 1 + fabs(bw1-aw1)/.2;
+ for(i=0;i<=n;i++) {
+ cw1 = aw1 + i*(bw1-aw1)/n;
+ rinvert(cn1,cw1,&cn,&cw);
+ printf("%f %f\n",cn,cw);
+ }
+ printf("\"\n");
+ }
+}
diff --git a/src/cmd/map/sqrt.c b/src/cmd/map/sqrt.c
new file mode 100644
index 00000000..1f933384
--- /dev/null
+++ b/src/cmd/map/sqrt.c
@@ -0,0 +1,52 @@
+/*
+ sqrt returns the square root of its floating
+ point argument. Newton's method.
+
+ calls frexp
+*/
+
+#include <u.h>
+#include <libc.h>
+
+double
+sqrt(double arg)
+{
+ double x, temp;
+ int exp, i;
+
+ if(arg <= 0) {
+ if(arg < 0)
+ return 0.;
+ return 0;
+ }
+ x = frexp(arg, &exp);
+ while(x < 0.5) {
+ x *= 2;
+ exp--;
+ }
+ /*
+ * NOTE
+ * this wont work on 1's comp
+ */
+ if(exp & 1) {
+ x *= 2;
+ exp--;
+ }
+ temp = 0.5 * (1.0+x);
+
+ while(exp > 60) {
+ temp *= (1L<<30);
+ exp -= 60;
+ }
+ while(exp < -60) {
+ temp /= (1L<<30);
+ exp += 60;
+ }
+ if(exp >= 0)
+ temp *= 1L << (exp/2);
+ else
+ temp /= 1L << (-exp/2);
+ for(i=0; i<=4; i++)
+ temp = 0.5*(temp + arg/temp);
+ return temp;
+}
diff --git a/src/cmd/map/symbol.c b/src/cmd/map/symbol.c
new file mode 100644
index 00000000..e8f3c680
--- /dev/null
+++ b/src/cmd/map/symbol.c
@@ -0,0 +1,192 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "map.h"
+#include "iplot.h"
+
+#define NSYMBOL 20
+
+enum flag { POINT,ENDSEG,ENDSYM };
+struct symb {
+ double x, y;
+ char name[10+1];
+ enum flag flag;
+} *symbol[NSYMBOL];
+
+static int nsymbol;
+static double halfrange = 1;
+extern int halfwidth;
+extern int vflag;
+
+static int getrange(FILE *);
+static int getsymbol(FILE *, int);
+static void setrot(struct place *, double, int);
+static void dorot(struct symb *, double *, double *);
+
+
+void
+getsyms(char *file)
+{
+ FILE *sf = fopen(file,"r");
+ if(sf==0)
+ filerror("cannot open", file);
+ while(nsymbol<NSYMBOL-1 && getsymbol(sf,nsymbol))
+ nsymbol++;
+ fclose(sf);
+}
+
+static int
+getsymbol(FILE *sf, int n)
+{
+ double x,y;
+ char s[2];
+ int i;
+ struct symb *sp;
+ for(;;) {
+ if(fscanf(sf,"%1s",s)==EOF)
+ return 0;
+ switch(s[0]) {
+ case ':':
+ break;
+ case 'o':
+ case 'c': /* cl */
+ fscanf(sf,"%*[^\n]");
+ continue;
+ case 'r':
+ if(getrange(sf))
+ continue;
+ default:
+ error("-y file syntax error");
+ }
+ break;
+ }
+ sp = (struct symb*)malloc(sizeof(struct symb));
+ symbol[n] = sp;
+ if(fscanf(sf,"%10s",sp->name)!=1)
+ return 0;
+ i = 0;
+ while(fscanf(sf,"%1s",s)!=EOF) {
+ switch(s[0]) {
+ case 'r':
+ if(!getrange(sf))
+ break;
+ continue;
+ case 'm':
+ if(i>0)
+ symbol[n][i-1].flag = ENDSEG;
+ continue;
+ case ':':
+ ungetc(s[0],sf);
+ break;
+ default:
+ ungetc(s[0],sf);
+ case 'v':
+ if(fscanf(sf,"%lf %lf",&x,&y)!=2)
+ break;
+ sp[i].x = x*halfwidth/halfrange;
+ sp[i].y = y*halfwidth/halfrange;
+ sp[i].flag = POINT;
+ i++;
+ sp = symbol[n] = (struct symb*)realloc(symbol[n],
+ (i+1)*sizeof(struct symb));
+ continue;
+ }
+ break;
+ }
+ if(i>0)
+ symbol[n][i-1].flag = ENDSYM;
+ else
+ symbol[n] = 0;
+ return 1;
+}
+
+static int
+getrange(FILE *sf)
+{
+ double x,y,xmin,ymin;
+ if(fscanf(sf,"%*s %lf %lf %lf %lf",
+ &xmin,&ymin,&x,&y)!=4)
+ return 0;
+ x -= xmin;
+ y -= ymin;
+ halfrange = (x>y? x: y)/2;
+ if(halfrange<=0)
+ error("bad ra command in -y file");
+ return 1;
+}
+
+/* r=0 upright;=1 normal;=-1 reverse*/
+int
+putsym(struct place *p, char *name, double s, int r)
+{
+ int x,y,n;
+ struct symb *sp;
+ double dx,dy;
+ int conn = 0;
+ for(n=0; symbol[n]; n++)
+ if(strcmp(name,symbol[n]->name)==0)
+ break;
+ sp = symbol[n];
+ if(sp==0)
+ return 0;
+ if(doproj(p,&x,&y)*vflag <= 0)
+ return 1;
+ setrot(p,s,r);
+ for(;;) {
+ dorot(sp,&dx,&dy);
+ conn = cpoint(x+(int)dx,y+(int)dy,conn);
+ switch(sp->flag) {
+ case ENDSEG:
+ conn = 0;
+ case POINT:
+ sp++;
+ continue;
+ case ENDSYM:
+ break;
+ }
+ break;
+ }
+ return 1;
+}
+
+static double rot[2][2];
+
+static void
+setrot(struct place *p, double s, int r)
+{
+ double x0,y0,x1,y1;
+ struct place up;
+ up = *p;
+ up.nlat.l += .5*RAD;
+ sincos(&up.nlat);
+ if(r&&(*projection)(p,&x0,&y0)) {
+ if((*projection)(&up,&x1,&y1)<=0) {
+ up.nlat.l -= RAD;
+ sincos(&up.nlat);
+ if((*projection)(&up,&x1,&y1)<=0)
+ goto unit;
+ x1 = x0 - x1;
+ y1 = y0 - y1;
+ } else {
+ x1 -= x0;
+ y1 -= y0;
+ }
+ x1 = r*x1;
+ s /= hypot(x1,y1);
+ rot[0][0] = y1*s;
+ rot[0][1] = x1*s;
+ rot[1][0] = -x1*s;
+ rot[1][1] = y1*s;
+ } else {
+unit:
+ rot[0][0] = rot[1][1] = s;
+ rot[0][1] = rot[1][0] = 0;
+ }
+}
+
+static void
+dorot(struct symb *sp, double *px, double *py)
+{
+ *px = rot[0][0]*sp->x + rot[0][1]*sp->y;
+ *py = rot[1][0]*sp->x + rot[1][1]*sp->y;
+}