tWhy not? - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 28994509cc11ac6a5443054dfae1fedfb69039bc
 (DIR) parent a01e58366c54804f15f84d6e21d13f2e4080977a
 (HTM) Author: rsc <devnull@localhost>
       Date:   Wed, 21 Apr 2004 22:19:33 +0000
       
       Why not?
       
       Diffstat:
         A src/cmd/jpg/bmp.c                   |     210 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/bmp.h                   |      37 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/close.c                 |     121 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/gif.c                   |     421 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/ico.c                   |     506 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/imagefile.h             |      82 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/jpegdump.c              |     346 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/jpg.c                   |     342 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/mkfile                  |      52 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/multichan.c             |      60 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/onechan.c               |     229 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/png.c                   |     224 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/ppm.c                   |     209 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readbmp.c               |     626 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readgif.c               |     535 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readjpg.c               |    1661 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readpng.c               |     334 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readppm.c               |     238 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/readyuv.c               |     190 ++++++++++++++++++++++++++++++
         A src/cmd/jpg/rgbrgbv.c               |      69 ++++++++++++++++++++++++++++++
         A src/cmd/jpg/rgbycc.c                |     120 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/togif.c                 |     147 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/toico.c                 |     322 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/topng.c                 |      70 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/toppm.c                 |      90 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/torgbv.c                |     299 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/totruecolor.c           |     163 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/writegif.c              |     568 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/writepng.c              |     220 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/writeppm.c              |     164 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/writerawimage.c         |     206 +++++++++++++++++++++++++++++++
         A src/cmd/jpg/yuv.c                   |     211 +++++++++++++++++++++++++++++++
         A src/cmd/map/index.c                 |      88 +++++++++++++++++++++++++++++++
         A src/cmd/map/iplot.h                 |      51 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/aitoff.c         |      26 ++++++++++++++++++++++++++
         A src/cmd/map/libmap/albers.c         |     117 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/azequalarea.c    |      19 +++++++++++++++++++
         A src/cmd/map/libmap/azequidist.c     |      19 +++++++++++++++++++
         A src/cmd/map/libmap/bicentric.c      |      25 +++++++++++++++++++++++++
         A src/cmd/map/libmap/bonne.c          |      36 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/ccubrt.c         |      13 +++++++++++++
         A src/cmd/map/libmap/complex.c        |      85 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/conic.c          |      27 +++++++++++++++++++++++++++
         A src/cmd/map/libmap/cubrt.c          |      30 ++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/cuts.c           |      39 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/cylequalarea.c   |      24 ++++++++++++++++++++++++
         A src/cmd/map/libmap/cylindrical.c    |      19 +++++++++++++++++++
         A src/cmd/map/libmap/elco2.c          |     132 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/elliptic.c       |      35 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/fisheye.c        |      26 ++++++++++++++++++++++++++
         A src/cmd/map/libmap/gall.c           |      29 +++++++++++++++++++++++++++++
         A src/cmd/map/libmap/gilbert.c        |      51 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/guyou.c          |     101 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/harrison.c       |      40 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/hex.c            |     122 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/homing.c         |     121 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/lagrange.c       |      30 ++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/lambert.c        |      46 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/laue.c           |      24 ++++++++++++++++++++++++
         A src/cmd/map/libmap/lune.c           |      62 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/mercator.c       |      36 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/mkfile           |      50 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/mollweide.c      |      25 +++++++++++++++++++++++++
         A src/cmd/map/libmap/newyorker.c      |      28 ++++++++++++++++++++++++++++
         A src/cmd/map/libmap/orthographic.c   |      35 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/perspective.c    |      84 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/polyconic.c      |      28 ++++++++++++++++++++++++++++
         A src/cmd/map/libmap/rectangular.c    |      22 ++++++++++++++++++++++
         A src/cmd/map/libmap/simpleconic.c    |      34 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/sinusoidal.c     |      17 +++++++++++++++++
         A src/cmd/map/libmap/tetra.c          |     206 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/trapezoidal.c    |      30 ++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/twocirc.c        |      80 +++++++++++++++++++++++++++++++
         A src/cmd/map/libmap/zcoord.c         |     143 +++++++++++++++++++++++++++++++
         A src/cmd/map/map.c                   |    1226 +++++++++++++++++++++++++++++++
         A src/cmd/map/map.h                   |     147 +++++++++++++++++++++++++++++++
         A src/cmd/map/map.rc                  |     103 +++++++++++++++++++++++++++++++
         A src/cmd/map/mapdemo.rc              |      83 +++++++++++++++++++++++++++++++
         A src/cmd/map/mkfile                  |      32 +++++++++++++++++++++++++++++++
         A src/cmd/map/route.c                 |     131 +++++++++++++++++++++++++++++++
         A src/cmd/map/sqrt.c                  |      52 +++++++++++++++++++++++++++++++
         A src/cmd/map/symbol.c                |     192 +++++++++++++++++++++++++++++++
       
       82 files changed, 13293 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/jpg/bmp.c b/src/cmd/jpg/bmp.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/bmp.h b/src/cmd/jpg/bmp.h
       t@@ -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;
 (DIR) diff --git a/src/cmd/jpg/close.c b/src/cmd/jpg/close.c
       t@@ -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");
       +        }
       +}
 (DIR) diff --git a/src/cmd/jpg/gif.c b/src/cmd/jpg/gif.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/ico.c b/src/cmd/jpg/ico.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/imagefile.h b/src/cmd/jpg/imagefile.h
       t@@ -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[];
 (DIR) diff --git a/src/cmd/jpg/jpegdump.c b/src/cmd/jpg/jpegdump.c
       t@@ -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;
       +        }
       +}
 (DIR) diff --git a/src/cmd/jpg/jpg.c b/src/cmd/jpg/jpg.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/mkfile b/src/cmd/jpg/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/jpg/multichan.c b/src/cmd/jpg/multichan.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/onechan.c b/src/cmd/jpg/onechan.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/png.c b/src/cmd/jpg/png.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/ppm.c b/src/cmd/jpg/ppm.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/readbmp.c b/src/cmd/jpg/readbmp.c
       t@@ -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;
       +}
       +
       +
 (DIR) diff --git a/src/cmd/jpg/readgif.c b/src/cmd/jpg/readgif.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/readjpg.c b/src/cmd/jpg/readjpg.c
       t@@ -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;
       +        }
       +}
 (DIR) diff --git a/src/cmd/jpg/readpng.c b/src/cmd/jpg/readpng.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/readppm.c b/src/cmd/jpg/readppm.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/readyuv.c b/src/cmd/jpg/readyuv.c
       t@@ -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;
       +}
       +
       +
 (DIR) diff --git a/src/cmd/jpg/rgbrgbv.c b/src/cmd/jpg/rgbrgbv.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/rgbycc.c b/src/cmd/jpg/rgbycc.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/togif.c b/src/cmd/jpg/togif.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/toico.c b/src/cmd/jpg/toico.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/topng.c b/src/cmd/jpg/topng.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/toppm.c b/src/cmd/jpg/toppm.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/torgbv.c b/src/cmd/jpg/torgbv.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/totruecolor.c b/src/cmd/jpg/totruecolor.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/writegif.c b/src/cmd/jpg/writegif.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/writepng.c b/src/cmd/jpg/writepng.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/writeppm.c b/src/cmd/jpg/writeppm.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/jpg/writerawimage.c b/src/cmd/jpg/writerawimage.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/jpg/yuv.c b/src/cmd/jpg/yuv.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/index.c b/src/cmd/map/index.c
       t@@ -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
       +};
 (DIR) diff --git a/src/cmd/map/iplot.h b/src/cmd/map/iplot.h
       t@@ -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)
 (DIR) diff --git a/src/cmd/map/libmap/aitoff.c b/src/cmd/map/libmap/aitoff.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/albers.c b/src/cmd/map/libmap/albers.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/azequalarea.c b/src/cmd/map/libmap/azequalarea.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/azequidist.c b/src/cmd/map/libmap/azequidist.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/bicentric.c b/src/cmd/map/libmap/bicentric.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/bonne.c b/src/cmd/map/libmap/bonne.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/ccubrt.c b/src/cmd/map/libmap/ccubrt.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/complex.c b/src/cmd/map/libmap/complex.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/conic.c b/src/cmd/map/libmap/conic.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/cubrt.c b/src/cmd/map/libmap/cubrt.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/cuts.c b/src/cmd/map/libmap/cuts.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/cylequalarea.c b/src/cmd/map/libmap/cylequalarea.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/cylindrical.c b/src/cmd/map/libmap/cylindrical.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/elco2.c b/src/cmd/map/libmap/elco2.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/elliptic.c b/src/cmd/map/libmap/elliptic.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/fisheye.c b/src/cmd/map/libmap/fisheye.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/gall.c b/src/cmd/map/libmap/gall.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/gilbert.c b/src/cmd/map/libmap/gilbert.c
       t@@ -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)
       +*/
       +
       +
 (DIR) diff --git a/src/cmd/map/libmap/guyou.c b/src/cmd/map/libmap/guyou.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/harrison.c b/src/cmd/map/libmap/harrison.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/hex.c b/src/cmd/map/libmap/hex.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/homing.c b/src/cmd/map/libmap/homing.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/lagrange.c b/src/cmd/map/libmap/lagrange.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/lambert.c b/src/cmd/map/libmap/lambert.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/laue.c b/src/cmd/map/libmap/laue.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/lune.c b/src/cmd/map/libmap/lune.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/mercator.c b/src/cmd/map/libmap/mercator.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/mkfile b/src/cmd/map/libmap/mkfile
       t@@ -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]
 (DIR) diff --git a/src/cmd/map/libmap/mollweide.c b/src/cmd/map/libmap/mollweide.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/newyorker.c b/src/cmd/map/libmap/newyorker.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/orthographic.c b/src/cmd/map/libmap/orthographic.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/perspective.c b/src/cmd/map/libmap/perspective.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/polyconic.c b/src/cmd/map/libmap/polyconic.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/rectangular.c b/src/cmd/map/libmap/rectangular.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/simpleconic.c b/src/cmd/map/libmap/simpleconic.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/sinusoidal.c b/src/cmd/map/libmap/sinusoidal.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/map/libmap/tetra.c b/src/cmd/map/libmap/tetra.c
       t@@ -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);
       +}
       +
 (DIR) diff --git a/src/cmd/map/libmap/trapezoidal.c b/src/cmd/map/libmap/trapezoidal.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/twocirc.c b/src/cmd/map/libmap/twocirc.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/libmap/zcoord.c b/src/cmd/map/libmap/zcoord.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/map.c b/src/cmd/map/map.c
       t@@ -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 */
       +}
 (DIR) diff --git a/src/cmd/map/map.h b/src/cmd/map/map.h
       t@@ -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 *);
 (DIR) diff --git a/src/cmd/map/map.rc b/src/cmd/map/map.rc
       t@@ -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
 (DIR) diff --git a/src/cmd/map/mapdemo.rc b/src/cmd/map/mapdemo.rc
       t@@ -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
 (DIR) diff --git a/src/cmd/map/mkfile b/src/cmd/map/mkfile
       t@@ -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
       +
 (DIR) diff --git a/src/cmd/map/route.c b/src/cmd/map/route.c
       t@@ -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");
       +        }
       +}
 (DIR) diff --git a/src/cmd/map/sqrt.c b/src/cmd/map/sqrt.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/map/symbol.c b/src/cmd/map/symbol.c
       t@@ -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;
       +}