commit 28994509cc11ac6a5443054dfae1fedfb69039bc from: rsc date: Wed Apr 21 22:19:33 2004 UTC Why not? commit - a01e58366c54804f15f84d6e21d13f2e4080977a commit + 28994509cc11ac6a5443054dfae1fedfb69039bc blob - /dev/null blob + f7e07a01525aad3e432f846f7f77988fdc2f3aab (mode 644) --- /dev/null +++ src/cmd/jpg/bmp.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#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, ""); + else{ + for(i=0; i1 && 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; jnchans; j++) + free(r->chans[j]); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} blob - /dev/null blob + ca00368465b62652e0e878cf2c06bd4fed50fc09 (mode 644) --- /dev/null +++ src/cmd/jpg/bmp.h @@ -0,0 +1,37 @@ + +#define BMP_RGB 0 +#define BMP_RLE8 1 +#define BMP_RLE4 2 +#define BMP_BITFIELDS 3 + +typedef struct { + uchar red; + uchar green; + uchar blue; + uchar alpha; +} Rgb; + +typedef struct { + short type; + long size; + short reserved1; + short reserved2; + long offbits; +} Filehdr; + +typedef struct { + long size; /* Size of the Bitmap-file */ + long lReserved; /* Reserved */ + long dataoff; /* Picture data location */ + long hsize; /* Header-Size */ + long width; /* Picture width (pixels) */ + long height; /* Picture height (pixels) */ + short planes; /* Planes (must be 1) */ + short bpp; /* Bits per pixel (1, 4, 8 or 24) */ + long compression; /* Compression mode */ + long imagesize; /* Image size (bytes) */ + long hres; /* Horizontal Resolution (pels/meter) */ + long vres; /* Vertical Resolution (pels/meter) */ + long colours; /* Used Colours (Col-Table index) */ + long impcolours; /* Important colours (Col-Table index) */ +} Infohdr; blob - /dev/null blob + 7a260c1087bc8f9579576860e32b6df52f35c583 (mode 644) --- /dev/null +++ src/cmd/jpg/close.c @@ -0,0 +1,121 @@ +#include +#include +#include + +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<>= 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; ynext; + } + cp = col[y]; + while(cp != nil){ + n++; + print("%d ", cp->col); + cp = cp->next; + } + print("\n"); + } +} blob - /dev/null blob + f9927017601f89b6ae348443c40561311e90bfa4 (mode 644) --- /dev/null +++ src/cmd/jpg/gif.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include +#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, ""); + else{ + for(i=0; i1 && 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; jchanlen; 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; jchans[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; ichanlen; i++){ + if(*rp == trindex){ + *cp++ = 0x00; + *cp++ = 0xFF; + *cp++ = 0xFF; + *cp++ = 0xFF; + }else{ + *cp++ = 0xFF; + cp += 3; + } + rp++; + } + else + for(i=0; ichanlen; 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; kchandesc==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; kgifdelay*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 || ++nloopclipr, 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; jnchans; 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; +} blob - /dev/null blob + 31806afb869b7166e1a731fdc1eacb274573a8aa (mode 644) --- /dev/null +++ src/cmd/jpg/ico.c @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include +#include + +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<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<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); +} blob - /dev/null blob + 592c5a2e2cb499f1832750452bc52ba83abde592 (mode 644) --- /dev/null +++ src/cmd/jpg/imagefile.h @@ -0,0 +1,82 @@ +typedef struct Rawimage Rawimage; + +struct Rawimage +{ + Rectangle r; + uchar *cmap; + int cmaplen; + int nchans; + uchar *chans[4]; + int chandesc; + int chanlen; + + int fields; /* defined by format */ + int gifflags; /* gif only; graphics control extension flag word */ + int gifdelay; /* gif only; graphics control extension delay in cs */ + int giftrindex; /* gif only; graphics control extension transparency index */ + int gifloopcount; /* number of times to loop in animation; 0 means forever */ +}; + +enum +{ + /* Channel descriptors */ + CRGB = 0, /* three channels, no map */ + CYCbCr = 1, /* three channels, no map, level-shifted 601 color space */ + CY = 2, /* one channel, luminance */ + CRGB1 = 3, /* one channel, map present */ + CRGBV = 4, /* one channel, map is RGBV, understood */ + CRGB24 = 5, /* one channel in correct data order for loadimage(RGB24) */ + CRGBA32 = 6, /* one channel in correct data order for loadimage(RGBA32) */ + CYA16 = 7, /* one channel in correct data order for loadimage(Grey8+Alpha8) */ + CRGBVA16= 8, /* one channel in correct data order for loadimage(CMAP8+Alpha8) */ + + /* GIF flags */ + TRANSP = 1, + INPUT = 2, + DISPMASK = 7<<2 +}; + + +enum{ /* PNG flags */ + II_GAMMA = 1 << 0, + II_COMMENT = 1 << 1, +}; + +typedef struct ImageInfo { + ulong fields_set; + double gamma; + char *comment; +} ImageInfo; + + +Rawimage** readjpg(int, int); +Rawimage** Breadjpg(Biobuf *b, int); +Rawimage** readpng(int, int); +Rawimage** Breadpng(Biobuf *b, int); +Rawimage** readgif(int, int); +Rawimage** readpixmap(int, int); +Rawimage* torgbv(Rawimage*, int); +Rawimage* totruecolor(Rawimage*, int); +int writerawimage(int, Rawimage*); +void* _remaperror(char*, ...); + +#ifndef _MEMDRAW_H_ +typedef struct Memimage Memimage; /* avoid necessity to include memdraw.h */ +#endif + +char* startgif(Biobuf*, Image*, int); +char* writegif(Biobuf*, Image*, char*, int, int); +void endgif(Biobuf*); +char* memstartgif(Biobuf*, Memimage*, int); +char* memwritegif(Biobuf*, Memimage*, char*, int, int); +void memendgif(Biobuf*); +Image* onechan(Image*); +Memimage* memonechan(Memimage*); + +char* writeppm(Biobuf*, Image*, char*); +char* memwriteppm(Biobuf*, Memimage*, char*); +Image* multichan(Image*); +Memimage* memmultichan(Memimage*); + +char* memwritepng(Biobuf*, Memimage*, ImageInfo*); +extern int drawlog2[]; blob - /dev/null blob + 0b132cb738ad3ab89d33adad9a1a8040e4e13451 (mode 644) --- /dev/null +++ src/cmd/jpg/jpegdump.c @@ -0,0 +1,346 @@ +/* jpeg parser by tom szymanski */ +#include +#include +#include +#include +#include +#include + +/* 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; + } +} blob - /dev/null blob + cf438a93cd5ca6912cecf6a6a82989aad296fd2f (mode 644) --- /dev/null +++ src/cmd/jpg/jpg.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#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, "", outchan); + else{ + for(i=0; i1 && 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; jnchans; 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; jnchans; 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; +} blob - /dev/null blob + 198f8e2fb1a4ca1f9d851337f569d63c8df7f860 (mode 644) --- /dev/null +++ src/cmd/jpg/mkfile @@ -0,0 +1,52 @@ +<$PLAN9/src/mkhdr + +TARG=jpg\ + gif\ + togif\ + ppm\ + toppm\ + png\ + topng\ + yuv\ + ico\ + toico\ + bmp\ + +IMFILES=\ + torgbv.$O\ + totruecolor.$O\ + writerawimage.$O\ + +HFILES=imagefile.h\ + +SHORTLIB=draw flate bio 9 +LDFLAGS=$LDFLAGS -L$X11/lib -lX11 + +<$PLAN9/src/mkmany + +$O.jpg: $IMFILES readjpg.$O jpg.$O +$O.gif: $IMFILES readgif.$O gif.$O +$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O multichan.$O +$O.ppm: $IMFILES readppm.$O ppm.$O +$O.toppm: writeppm.$O multichan.$O toppm.$O +$O.png: $IMFILES readpng.$O png.$O multichan.$O +$O.topng: writepng.$O topng.$O +$O.yuv: $IMFILES readyuv.$O yuv.$O +$O.bmp: $IMFILES readbmp.$O bmp.$O + +torgbv.$O: ycbcr.h rgbv.h + +ycbcr.h: rgbycc.c + 9c rgbycc.c + 9l -o o.rgbycc rgbycc.c + ./o.rgbycc >ycbcr.h + +rgbv.h: rgbrgbv.c + 9c rgbrgbv.c + 9l -o o.rgbrgbv rgbrgbv.c + ./o.rgbrgbv >rgbv.h + +nuke:V: nuke-headers + +nuke-headers:V: + rm -f rgbv.h ycbcr.h blob - /dev/null blob + 039712152df1e4fd94721b69041a145ba52707d2 (mode 644) --- /dev/null +++ src/cmd/jpg/multichan.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#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; +} blob - /dev/null blob + ea1c489bec696fabcf1c1b03393488953743a7fb (mode 644) --- /dev/null +++ src/cmd/jpg/onechan.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#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> 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; ichans[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; +} blob - /dev/null blob + d653fe6a9c2838c4064b06734b61b4875b4ce6fd (mode 644) --- /dev/null +++ src/cmd/jpg/png.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#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, "", outchan); + else{ + for(i=0; i1 && 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; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} blob - /dev/null blob + 24019dfa1cd06e0fa05c3f264bd27e5c1cd46175 (mode 644) --- /dev/null +++ src/cmd/jpg/ppm.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#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, ""); + else{ + for(i=0; i1 && 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; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} blob - /dev/null blob + 154cb48d0531d4a846bcef722be0c69c817be102 (mode 644) --- /dev/null +++ src/cmd/jpg/readbmp.c @@ -0,0 +1,626 @@ +#include +#include +#include +#include +#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; +} + + blob - /dev/null blob + 22a0b68cf071bb4e8b6542f76ca1709479ef0041 (mode 644) --- /dev/null +++ src/cmd/jpg/readgif.c @@ -0,0 +1,535 @@ +#include +#include +#include +#include +#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<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 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<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<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; + 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<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; ychans[0]); + image->chans[0] = ipic; +} blob - /dev/null blob + b154e75b18615836bf075ca51ddc22296a99c462 (mode 644) --- /dev/null +++ src/cmd/jpg/readjpg.c @@ -0,0 +1,1661 @@ +#include +#include +#include +#include +#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; kbuf); + 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; jnaccoeff[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; jndata[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; iNf; 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; jsize[k++] = i; + } + t->size[k] = 0; + + /* initialize HUFFVAL */ + t->val = jpgmalloc(h, nsize*sizeof(int), 1); + for(i=0; ival[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 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; lss; + 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; kNf; k++) + image->chans[k] = jpgmalloc(h, h->X*h->Y, 0); + + /* compute maximum H and V */ + Hmax = 0; + Vmax = 0; + for(comp=0; compcomp[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; compcomp[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; mri; + + 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; mcudcht[Td[comp]]; + acht = &h->acht[Ta[comp]]; + qt = h->qt[h->comp[comp].Tq]; + + for(block=0; block>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 && mcuri-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; compcomp[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; mnacross*h->ndown; + for(mcu=0; mcudccoeff[comp]; + accoeff = h->accoeff[comp]; + bn = blockno[comp]; + for(block=0; blocknblock[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; kchans[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; compcomp[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; kdccoeff[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; jnblock[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; compri; + + nmcu = h->nacross*h->ndown; + memset(blockno, 0, sizeof blockno); + for(mcu=0; mcudcht[Td[comp]]; + qt = h->qt[h->comp[comp].Tq][0]; + dc = h->dccoeff[comp]; + bn = blockno[comp]; + + for(block=0; blocknblock[comp]; block++){ + if(Ah == 0){ + t = decode(h, dcht); + diff = receive(h, t); + DC[comp] += diff; + dc[bn] = qt*DC[comp]<0 && mcuss; + 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 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]<0 && mcuss; + 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 0){ + if(nzeros > 0) + jpgerror(h, "ReadJPG: zeros pending at block start"); + for(k=Ss; k<=Se; k++) + increment(h, acc, k, qt[k]<= 0){ + if(acc[k] != 0) + increment(h, acc, k, qt[k]< 0) + eobrun = receiveEOB(h, rrrr)-1; + while(k <= Se){ + increment(h, acc, k, qt[k]<0 && mcudccoeff[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; iNf; 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; yX; + 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>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>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<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<>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<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; + } +} blob - /dev/null blob + 1cb859466140273d2bbaba2764dabae7e7065376 (mode 644) --- /dev/null +++ src/cmd/jpg/readpng.c @@ -0,0 +1,334 @@ +// work in progress... this version only good enough to read +// non-interleaved, 24bit RGB images + +#include +#include +#include +#include +#include +#include +#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; icol == -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; +} blob - /dev/null blob + 28b7f4ea0978142831ba5382b917932a35150d8d (mode 644) --- /dev/null +++ src/cmd/jpg/readppm.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#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; inchan; 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; rflush) + (*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; +} blob - /dev/null blob + eae2764e7d6d19dca0d02781bb1bffacef6592f6 (mode 644) --- /dev/null +++ src/cmd/jpg/readyuv.c @@ -0,0 +1,190 @@ +/* readyuv.c - read an Abekas A66 style image file. Steve Simon, 2003 */ +#include +#include +#include +#include +#include +#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; +} + + blob - /dev/null blob + bc55947a06aaef921d5bf27ca77031a63d12a3ba (mode 644) --- /dev/null +++ src/cmd/jpg/rgbrgbv.c @@ -0,0 +1,69 @@ +#include +#include +#include + +/* + * 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); +} blob - /dev/null blob + 3654a2d20e16cf7e3de55776ecc4b965340e54b5 (mode 644) --- /dev/null +++ src/cmd/jpg/rgbycc.c @@ -0,0 +1,120 @@ +#include +#include +#include + +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); +} blob - /dev/null blob + 353368830bde7fb757e2a4d8c17597defbe08f7a (mode 644) --- /dev/null +++ src/cmd/jpg/togif.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#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 "); + 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 +#include +#include +#include + +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<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); +} blob - /dev/null blob + 09c0061377a8603c8efd88e4acd1731c66bc4d5a (mode 644) --- /dev/null +++ src/cmd/jpg/topng.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#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 = ""; + }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); +} blob - /dev/null blob + e3094655562749b640ee187525d582dfc5e6e366 (mode 644) --- /dev/null +++ src/cmd/jpg/toppm.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#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); +} blob - /dev/null blob + e83f82cd639180d49c8a34d2f118c05c8eb7917a (mode 644) --- /dev/null +++ src/cmd/jpg/torgbv.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#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>4); + for(j=0; j>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< 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; jchanlen; 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= 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; jchanlen; 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 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; jchanlen; 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>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + } + } + } + break; + } + free(ered); + free(egrn); + free(eblu); + return im; +} blob - /dev/null blob + 0da5d9a3f4339fd61d6bcada3a4b5edc22d3ab37 (mode 644) --- /dev/null +++ src/cmd/jpg/totruecolor.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#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; jchanlen; 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< 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; jchanlen; 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; jchanlen; 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; jchanlen; j++){ + r = *bp++; + g = *gp++; + b = *rp++; + r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */ + *outp++ = r; + } + }else + for(j=0; jchanlen; 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; jchanlen; 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; +} blob - /dev/null blob + 16402ef8e14f55283dfb6677801ed8b5f6539890 (mode 644) --- /dev/null +++ src/cmd/jpg/writegif.c @@ -0,0 +1,568 @@ +#include +#include +#include +#include +#include +#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; ichan, 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>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<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<fd = fd; + sreg = 0; + nbits = 0; + bitsperpixel = 1<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))<>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<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; +} blob - /dev/null blob + 44bfe10c64b59131962c4640d349aabf03471564 (mode 644) --- /dev/null +++ src/cmd/jpg/writepng.c @@ -0,0 +1,220 @@ +// based on PNG 1.2 specification, July 1999 (see also rfc2083) +// Alpha is not supported yet because of lack of industry acceptance and +// because Plan9 Image uses premultiplied alpha, so png can't be lossless. +// Only 24bit color supported, because 8bit may as well use GIF. + +#include +#include +#include +#include +#include +#include +#include +#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; icol += 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; +} blob - /dev/null blob + c837865242810f8d735070990ccb2a3b605c1e08 (mode 644) --- /dev/null +++ src/cmd/jpg/writeppm.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#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<>drawlog2[depth]; + for(y=r.min.y; y>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= MAXLINE-(4+1)){ + Bprint(fd, "\n"); + col = 0; + }else + col += Bprint(fd, " "); + } + break; + case RGB24: + for(i=0; i= 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); +} blob - /dev/null blob + 26e0cd16e102c97220dfb0ecd5f94e70ca328a91 (mode 644) --- /dev/null +++ src/cmd/jpg/writerawimage.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#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)<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; +} blob - /dev/null blob + 929ccf39fd547c080d5e48599ab8fc2c4c3f70ef (mode 644) --- /dev/null +++ src/cmd/jpg/yuv.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#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, ""); + else{ + for(i=0; i1 && 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; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} blob - /dev/null blob + 0ff75f5dc51701947e0c9bf82c7c14aec6c911ff (mode 644) --- /dev/null +++ src/cmd/map/index.c @@ -0,0 +1,88 @@ +#include +#include +#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 +}; blob - /dev/null blob + be472fff8478ce7159e411fcb4f780a5a94637bc (mode 644) --- /dev/null +++ src/cmd/map/iplot.h @@ -0,0 +1,51 @@ +/* Plotting functions for v8 and v9 systems */ +/* This file is an alternative to plot.h */ + +/* open the plotting output */ +#define openpl() print("o\n") + +/* close the plotting output */ +#define closepl() print("cl\n") + +/* make sure the page or screen is clear */ +#define erase() print("e\n") + +/* plot a point at _x,_y, which becomes current */ +#define point(_x,_y) print("poi %d %d\n", _x,_y) + +/* coordinates to be assigned to lower left and upper right + corners of (square) plotting area */ +#define range(_x,_y,_X,_Y) print("ra %d %d %d %d\n", _x,_y,_X,_Y) + +/* place text, first letter at current point, which does not change */ +#define text(_s) {if(*(_s) == ' ')print("t \"%s\"\n",_s); else print("t %s\n", _s); } + +/* draw line from current point to _x,_y, which becomes current */ +#define vec(_x,_y) print("v %d %d\n", _x,_y) + +/* _x,_y becomes current point */ +#define move(_x, _y) print("m %d %d\n", _x, _y) + +/* specify style for drawing lines */ + +#define SOLID "solid" +#define DOTTED "dotted" +#define DASHED "dashed" +#define DOTDASH "dotdash" + +#define pen(_s) print("pe %s\n", _s) + +#define BLACK "z" +#define RED "r" +#define YELLOW "y" +#define GREEN "g" +#define BLUE "b" +#define CYAN "c" +#define MAGENTA "m" +#define WHITE "w" + +#define colorcode(_s) ((strcmp(_s,"black")==0)?BLACK:_s) + +#define colorx(_s) print("co %s\n", _s); /* funny name is all ken's fault */ + +#define comment(s,f) blob - /dev/null blob + 83b777fb3a1e7de7db4e890839bf6a96af914e9a (mode 644) --- /dev/null +++ src/cmd/map/libmap/aitoff.c @@ -0,0 +1,26 @@ +#include +#include +#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); +} blob - /dev/null blob + 56e3e9f591b5b0c84633cd4db0da07f6b66835aa (mode 644) --- /dev/null +++ src/cmd/map/libmap/albers.c @@ -0,0 +1,117 @@ +#include +#include +#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; +} blob - /dev/null blob + 6bae893df472f44106d63443518c7cbbe129b59a (mode 644) --- /dev/null +++ src/cmd/map/libmap/azequalarea.c @@ -0,0 +1,19 @@ +#include +#include +#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); +} blob - /dev/null blob + d26d33d4b3f6d21d9515df1bb3c5a19d2f6a4aee (mode 644) --- /dev/null +++ src/cmd/map/libmap/azequidist.c @@ -0,0 +1,19 @@ +#include +#include +#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); +} blob - /dev/null blob + 33fd8d198f53ed231234b7bc9db6916d00b96e8e (mode 644) --- /dev/null +++ src/cmd/map/libmap/bicentric.c @@ -0,0 +1,25 @@ +#include +#include +#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,¢er); + return(Xbicentric); +} blob - /dev/null blob + 858f0d69c7dcb7e6a28bcdabda61115fea429326 (mode 644) --- /dev/null +++ src/cmd/map/libmap/bonne.c @@ -0,0 +1,36 @@ +#include +#include +#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); +} blob - /dev/null blob + 8a7af566182cfe06601aef4a1b2afa1462b32006 (mode 644) --- /dev/null +++ src/cmd/map/libmap/ccubrt.c @@ -0,0 +1,13 @@ +#include +#include +#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); +} blob - /dev/null blob + b3099fd4ef2fe8ed5159c792d416ae830f50455d (mode 644) --- /dev/null +++ src/cmd/map/libmap/complex.c @@ -0,0 +1,85 @@ +#include +#include +#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)=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); +} blob - /dev/null blob + ba4430c68ea2a3a6b36619048d7968db2417b192 (mode 644) --- /dev/null +++ src/cmd/map/libmap/conic.c @@ -0,0 +1,27 @@ +#include +#include +#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); +} blob - /dev/null blob + fd508d2baae56d34302020f5099bc4901aee0a64 (mode 644) --- /dev/null +++ src/cmd/map/libmap/cubrt.c @@ -0,0 +1,30 @@ +#include +#include +#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); +} blob - /dev/null blob + ad9d6dc35a2c37fda20d2ee989a496ffd8766b85 (mode 644) --- /dev/null +++ src/cmd/map/libmap/cuts.c @@ -0,0 +1,39 @@ +#include +#include +#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; +} blob - /dev/null blob + 3cf222ff32c76a94812203254e48bb485c08e96a (mode 644) --- /dev/null +++ src/cmd/map/libmap/cylequalarea.c @@ -0,0 +1,24 @@ +#include +#include +#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); +} blob - /dev/null blob + 4d01bc23885336a3a932c6be0bc2736aa68d21a1 (mode 644) --- /dev/null +++ src/cmd/map/libmap/cylindrical.c @@ -0,0 +1,19 @@ +#include +#include +#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); +} blob - /dev/null blob + b4c9bbf695dbddec867071bc33680cb7c02e7f4b (mode 644) --- /dev/null +++ src/cmd/map/libmap/elco2.c @@ -0,0 +1,132 @@ +#include +#include +#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); +} blob - /dev/null blob + 3f3b1d572da909c94572d749647f3607e8126073 (mode 644) --- /dev/null +++ src/cmd/map/libmap/elliptic.c @@ -0,0 +1,35 @@ +#include +#include +#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,¢er); + return(Xelliptic); +} blob - /dev/null blob + 412d65e7dde2bc6c6b4073cd662d0419c1d06377 (mode 644) --- /dev/null +++ src/cmd/map/libmap/fisheye.c @@ -0,0 +1,26 @@ +#include +#include +#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; +} blob - /dev/null blob + 84da651b256f959f1919bade12600ae40ac1c95f (mode 644) --- /dev/null +++ src/cmd/map/libmap/gall.c @@ -0,0 +1,29 @@ +#include +#include +#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; +} blob - /dev/null blob + 173ffcd34a7ccbb670a27c05f552efa3abcf44e1 (mode 644) --- /dev/null +++ src/cmd/map/libmap/gilbert.c @@ -0,0 +1,51 @@ +#include +#include +#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) +*/ + + blob - /dev/null blob + b37736f240d33b5d75ae59fb85626a84b775e068 (mode 644) --- /dev/null +++ src/cmd/map/libmap/guyou.c @@ -0,0 +1,101 @@ +#include +#include +#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.lPI-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); +} blob - /dev/null blob + 6b6003c2c3615263e6c0fad44c1e2d1dcc0a750f (mode 644) --- /dev/null +++ src/cmd/map/libmap/harrison.c @@ -0,0 +1,40 @@ +#include +#include +#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 +#include +#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.lplace->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); +} blob - /dev/null blob + 366f69fe440bbf95918ce28734151675c986599b (mode 644) --- /dev/null +++ src/cmd/map/libmap/homing.c @@ -0,0 +1,121 @@ +#include +#include +#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; +} blob - /dev/null blob + 02dd29eb46b654334dd79672cecaab970405c7a8 (mode 644) --- /dev/null +++ src/cmd/map/libmap/lagrange.c @@ -0,0 +1,30 @@ +#include +#include +#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); +} blob - /dev/null blob + 6b688aa3d0309b98464f2d5c1afaa97a766037c8 (mode 644) --- /dev/null +++ src/cmd/map/libmap/lambert.c @@ -0,0 +1,46 @@ +#include +#include +#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); +} blob - /dev/null blob + 06c5f3a7f33da200138bb4e0dbf88878c14b354d (mode 644) --- /dev/null +++ src/cmd/map/libmap/laue.c @@ -0,0 +1,24 @@ +#include +#include +#include "map.h" + + +static int +Xlaue(struct place *place, double *x, double *y) +{ + double r; + if(place->nlat.lnlat.l); + if(r>3) + return(-1); + *x = - r * place->wlon.s; + *y = - r * place->wlon.c; + return(1); +} + +proj +laue(void) +{ + return(Xlaue); +} blob - /dev/null blob + dc58c6f05045d1db2ce452f5fcee4f66fe979240 (mode 644) --- /dev/null +++ src/cmd/map/libmap/lune.c @@ -0,0 +1,62 @@ +#include +#include +#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; +} blob - /dev/null blob + 0ab636530fa551877155dc15ee3df9297e0cbdd6 (mode 644) --- /dev/null +++ src/cmd/map/libmap/mercator.c @@ -0,0 +1,36 @@ +#include +#include +#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); +} blob - /dev/null blob + e903124610a1f3aea59850a7be86b01243a845ab (mode 644) --- /dev/null +++ src/cmd/map/libmap/mkfile @@ -0,0 +1,50 @@ +<$PLAN9/src/mkhdr + +LIB=libmap.a +OFILES=aitoff.$O\ + albers.$O\ + azequalarea.$O\ + azequidist.$O\ + bicentric.$O\ + bonne.$O\ + ccubrt.$O\ + complex.$O\ + conic.$O\ + cubrt.$O\ + cylequalarea.$O\ + cylindrical.$O\ + elco2.$O\ + elliptic.$O\ + fisheye.$O\ + gall.$O\ + gilbert.$O\ + guyou.$O\ + harrison.$O\ + hex.$O\ + homing.$O\ + lagrange.$O\ + lambert.$O\ + laue.$O\ + lune.$O\ + mercator.$O\ + mollweide.$O\ + newyorker.$O\ + orthographic.$O\ + perspective.$O\ + polyconic.$O\ + rectangular.$O\ + simpleconic.$O\ + sinusoidal.$O\ + tetra.$O\ + trapezoidal.$O\ + twocirc.$O\ + zcoord.$O\ + +HFILES=../map.h\ + +<$PLAN9/src/mklib +CFLAGS=$CFLAGS -I.. + +nuke:V: + mk clean + rm -f libmap.a[$OS] blob - /dev/null blob + 3284c495298735feb847737bbe49a28549ebe922 (mode 644) --- /dev/null +++ src/cmd/map/libmap/mollweide.c @@ -0,0 +1,25 @@ +#include +#include +#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); +} blob - /dev/null blob + 370e3b37d35c297bf690e3c49a698a4c0cd0654f (mode 644) --- /dev/null +++ src/cmd/map/libmap/newyorker.c @@ -0,0 +1,28 @@ +#include +#include +#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(rwlon.s; + *y = -s * place->wlon.c; + return(1); +} + +proj +newyorker(double a0) +{ + a = a0*RAD; + return(Xnewyorker); +} blob - /dev/null blob + 7aac5b15d6eba9d9f17b6682df529877cf398d93 (mode 644) --- /dev/null +++ src/cmd/map/libmap/orthographic.c @@ -0,0 +1,35 @@ +#include +#include +#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; +} blob - /dev/null blob + 7ce6b0d6f8b386322acd93e282e2c21be1c9b82a (mode 644) --- /dev/null +++ src/cmd/map/libmap/perspective.c @@ -0,0 +1,84 @@ +#include +#include +#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= 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; +} blob - /dev/null blob + a07c97e367ec1c1b95d2b93f7950114b01219bcf (mode 644) --- /dev/null +++ src/cmd/map/libmap/polyconic.c @@ -0,0 +1,28 @@ +#include +#include +#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); +} blob - /dev/null blob + d4a86c98a398bda226fe3a800e654c4e098d44f4 (mode 644) --- /dev/null +++ src/cmd/map/libmap/rectangular.c @@ -0,0 +1,22 @@ +#include +#include +#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); +} blob - /dev/null blob + 1ed1d1aae45967b509748cd0b922bc5fa728ca7a (mode 644) --- /dev/null +++ src/cmd/map/libmap/simpleconic.c @@ -0,0 +1,34 @@ +#include +#include +#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; +} blob - /dev/null blob + 6a79706ea57da327ca471309b6204e7d3294f0c0 (mode 644) --- /dev/null +++ src/cmd/map/libmap/sinusoidal.c @@ -0,0 +1,17 @@ +#include +#include +#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); +} blob - /dev/null blob + 6bdef49b9e6299c9a3e2388fed0f006cf9d714ba (mode 644) --- /dev/null +++ src/cmd/map/libmap/tetra.c @@ -0,0 +1,206 @@ +#include +#include +#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); +} + blob - /dev/null blob + 977cf60c43780e2153a574f97b00c80889b46667 (mode 644) --- /dev/null +++ src/cmd/map/libmap/trapezoidal.c @@ -0,0 +1,30 @@ +#include +#include +#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; +} blob - /dev/null blob + 0d0e48a4e30419581a27f1e6238053335b9ad648 (mode 644) --- /dev/null +++ src/cmd/map/libmap/twocirc.c @@ -0,0 +1,80 @@ +#include +#include +#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; +} blob - /dev/null blob + 7c3d3ad75fc6429613b7ad5510cfbf3a4ea84663 (mode 644) --- /dev/null +++ src/cmd/map/libmap/zcoord.c @@ -0,0 +1,143 @@ +#include +#include +#include +#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; +} blob - /dev/null blob + 47109d43e874e9273938f62da61e889c03aa8c83 (mode 644) --- /dev/null +++ src/cmd/map/map.c @@ -0,0 +1,1226 @@ +#include +#include +#include +#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=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=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&&i0&&!option(argv[0])) { + delta = atoi(argv[0]); + argv++; + argc--; + } + break; + case 'w': + bordcolor = currcolor; + windowed++; + for(i=0;ii&&!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];latklimits[1]) + lat = klimits[1]; + for(lon=klimits[2];lonxmax) xmax = x; + if(yymax) ymax = y; + } + } + } else { + for(i=0; ixmax) xmax = x; + if(yymax) 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;inlat.lnlat.l>rwindow[1]+FUZZ|| + geog->wlon.lwlon.l>rwindow[3]+FUZZ) + return(0); + else return(1); +} + +int +inlimits(struct place *g) +{ + if(rlimits[0]-FUZZ>g->nlat.l|| + rlimits[1]+FUZZnlat.l) + return(0); + switch(limcase) { + case 0: + if(rlimits[2]+TWOPI-FUZZ>g->wlon.l&& + rlimits[3]+FUZZwlon.l) + return(0); + break; + case 1: + if(rlimits[2]-FUZZ>g->wlon.l|| + rlimits[3]+FUZZwlon.l) + return(0); + break; + case 2: + if(rlimits[2]>g->wlon.l&& + rlimits[3]-TWOPI+FUZZwlon.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=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;k0) + 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=right || 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))0) + cpoint(kx,ky,0); + } + if(f1PI*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(lat0tetrapt) { + 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 16 bits */ + return r; +} + +double +fmin(double x, double y) +{ + return(xy?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=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; ia + 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 */ +} blob - /dev/null blob + d96497c857957298e6a05c61a3307623e718289e (mode 644) --- /dev/null +++ src/cmd/map/map.h @@ -0,0 +1,147 @@ +/* +#pragma lib "/sys/src/cmd/map/libmap/libmap.a$O" +#pragma src "/sys/src/cmd/map/libmap" +*/ + +#define index index0 +#ifndef PI +#define PI 3.1415926535897932384626433832795028841971693993751 +#endif + +#define TWOPI (2*PI) +#define RAD (PI/180) +double hypot(double, double); /* sqrt(a*a+b*b) */ +double tan(double); /* not in K&R library */ + +#define ECC .08227185422 /* eccentricity of earth */ +#define EC2 .006768657997 + +#define FUZZ .0001 +#define UNUSED 0.0 /* a dummy double parameter */ + +struct coord { + double l; /* lat or lon in radians*/ + double s; /* sin */ + double c; /* cos */ +}; +struct place { + struct coord nlat; + struct coord wlon; +}; + +typedef int (*proj)(struct place *, double *, double *); + +struct index { /* index of known projections */ + char *name; /* name of projection */ + proj (*prog)(double, double); + /* pointer to projection function */ + int npar; /* number of params */ + int (*cut)(struct place *, struct place *, double *); + /* function that handles cuts--eg longitude 180 */ + int poles; /*1 S pole is a line, 2 N pole is, 3 both*/ + int spheroid; /* poles must be at 90 deg if nonzero */ + int (*limb)(double *lat, double *lon, double resolution); + /* get next place on limb */ + /* return -1 if done, 0 at gap, else 1 */ +}; + + +proj aitoff(void); +proj albers(double, double); +int Xazequalarea(struct place *, double *, double *); +proj azequalarea(void); +int Xazequidistant(struct place *, double *, double *); +proj azequidistant(void); +proj bicentric(double); +proj bonne(double); +proj conic(double); +proj cylequalarea(double); +int Xcylindrical(struct place *, double *, double *); +proj cylindrical(void); +proj elliptic(double); +proj fisheye(double); +proj gall(double); +proj gilbert(void); +proj globular(void); +proj gnomonic(void); +int guycut(struct place *, struct place *, double *); +int Xguyou(struct place *, double *, double *); +proj guyou(void); +proj harrison(double, double); +int hexcut(struct place *, struct place *, double *); +proj hex(void); +proj homing(double); +int hlimb(double*, double*, double resolution); +proj lagrange(void); +proj lambert(double, double); +proj laue(void); +proj lune(double, double); +proj loxodromic(double); /* not in library */ +proj mecca(double); +int mlimb(double*, double*, double resolution); +proj mercator(void); +proj mollweide(void); +proj newyorker(double); +proj ortelius(double, double); /* not in library */ +int Xorthographic(struct place *place, double *x, double *y); +proj orthographic(void); +int olimb(double*, double*, double); +proj perspective(double); +int plimb(double*, double*, double resolution); +int Xpolyconic(struct place *, double *, double *); +proj polyconic(void); +proj rectangular(double); +proj simpleconic(double, double); +int Xsinusoidal(struct place *, double *, double *); +proj sinusoidal(void); +proj sp_albers(double, double); +proj sp_mercator(void); +proj square(void); +int Xstereographic(struct place *, double *, double *); +proj stereographic(void); +int Xtetra(struct place *, double *, double *); +int tetracut(struct place *, struct place *, double *); +proj tetra(void); +proj trapezoidal(double, double); +proj vandergrinten(void); +proj wreath(double, double); /* not in library */ + +void findxy(double, double *, double *); +void albscale(double, double, double, double); +void invalb(double, double, double *, double *); + +void cdiv(double, double, double, double, double *, double *); +void cmul(double, double, double, double, double *, double *); +void cpow(double, double, double *, double *, double); +void csq(double, double, double *, double *); +void csqrt(double, double, double *, double *); +void ccubrt(double, double, double *, double *); +double cubrt(double); +int elco2(double, double, double, double, double, double *, double *); +void cdiv2(double, double, double, double, double *, double *); +void csqr(double, double, double *, double *); + +void orient(double, double, double); +void latlon(double, double, struct place *); +void deg2rad(double, struct coord *); +void sincos(struct coord *); +void normalize(struct place *); +void invert(struct place *); +void norm(struct place *, struct place *, struct coord *); +void printp(struct place *); +void copyplace(struct place *, struct place *); + +int picut(struct place *, struct place *, double *); +int ckcut(struct place *, struct place *, double); +double reduce(double); + +void getsyms(char *); +int putsym(struct place *, char *, double, int); +void filerror(char *s, char *f); +void error(char *s); +int doproj(struct place *, int *, int *); +int cpoint(int, int, int); +int plotpt(struct place *, int); +int nocut(struct place *, struct place *, double *); + +extern int (*projection)(struct place *, double *, double *); blob - /dev/null blob + eaa75c91afa4a8c98e09f6aebb94def00989626b (mode 755) --- /dev/null +++ src/cmd/map/map.rc @@ -0,0 +1,103 @@ +#!/bin/rc + +rfork en + +# F FEATUREs, M map files, A other arguments +FEATURE=no + +if (~ $MAPPROG '') + MAPPROG=/bin/aux/mapd + +if (~ $MAPDIR '') + MAPDIR=/lib/map + +F=(); M=(); A=(); +for (i) { + switch ($FEATURE) { + case no + switch ($i) { + case -f + FEATURE=yes + F=($F) + case * + A=($A $i) + } + case yes + switch ($i) { + case -f + case -* + A=($A $i) + FEATURE=no + case riv*2 + F=($F 201 202) + case riv*3 + F=($F 201 202 203) + case riv*4 + F=($F 201 202 203 204) + case riv* + F=($F 201) + case iriv*2 + F=($F 206 207) + case iriv*[34] + F=($F 206 207 208) + case iriv* + F=($F 206) + case coast*2 shore*2 lake*2 + F=($F 102) + case coast*3 shore*3 lake*3 + F=($F 102 103) + case coast*4 shore*4 lake*4 + F=($F 102 103 104) + case coast* shore* lake* + case ilake*[234] ishore*[234] + F=($F 106 107) + case ilake* ishore* + F=($F 106) + case reef* + F=($F 108) + case canal*2 + F=($F 210 211) + case canal*[34] + F=($F 210 211 212) + case canal* + F=($F 210) + case glacier* + F=($F 115) + case state* province* + F=($F 401) + case countr*2 + F=($F 301 302) + case countr*[34] + F=($F 301 302 303) + case countr* + F=($F 301) + case salt*[234] + F=($F 109 110) + case salt* + F=($F 109) + case ice*[234] shel*[234] + F=($F 113 114) + case ice* shel* + F=($F 113) + case * + echo map: unknown feature $i >[1=2] + exits "unknown feature" + } + } +} + +for (j in $F) { + if (test -r $MAPDIR/$j) + M=($M $MAPDIR/$j) +} + +if (~ $F ?*) { + if (test -r $MAPDIR/101) + M=(101 $M) + M=(-m $M) +} + +if (~ $MAP '') + MAP=world + +MAP=$MAP MAPDIR=$MAPDIR $MAPPROG $A $M blob - /dev/null blob + 033969ab184b0913b9eb0d156df52d2a5dccf9f1 (mode 755) --- /dev/null +++ src/cmd/map/mapdemo.rc @@ -0,0 +1,83 @@ +#!/bin/rc + +fn demo {proj=$1; shift; + label=$1; shift; + { echo 'o' + echo 'ra -8192 -8492 8192 8492' + echo 'e' + echo 'm -8192 8192' + echo t $type + echo 'm -8192 -8192' + echo t $proj - $label + MAP=world MAPDIR=/lib/map map $proj $* -s -d 5 + } + sleep 5 +} + +rfork en +{ +type='Equatorial projections centered on long. 0. Parallels are straight lines.' + +demo mercator 'equally spaced straight meridians, conformal, straight compass courses' +demo sinusoidal 'equally spaced parallels, equal-area, same as bonne(0)' +demo cylequalarea 'equally spaced straight meridians, equal-area, true scale on Eq' 0 +demo cylindrical 'central projection on tangent cylinder' +demo rectangular 'equally spaced parallels, equally spaced straight meridians, true scale on Eq' 0 +demo gall 'parallels spaced stereographically on prime meridian, equally spaced straight meridians, true scale on Eq' 0 +demo mollweide '(homalographic) equal-area, hemisphere is a circle' +demo gilbert 'globe mapped conformally on hemisphere, viewed orthographically' + +type='Azimuthal: centered on the North Pole, Parallels are concentric circles, Meridians are equally spaced radial lines' + +demo azequidistant 'equally spaced parallels, true distances from pole' +demo azequalarea 'equal area' +demo gnomonic 'central projecton on tangent plane, straight great circles' +demo perspective 'viewed along earth''s axis 2 earth radii from center of earth' 2 +demo orthographic 'viewed from infinity' +demo stereographic 'conformal, projected from opposite pole' +demo laue 'radius = tan(2\(mu colatitude ), used in xray crystallography' +demo fisheye 'fisheye view of stereographic map, index of refraction 2' 2 -o 40.75 74 +demo newyorker 'New Yorker map from viewing pedestal of radius .5' .5 -o 40.75 74 + +type='Polar conic projections symmetric about the Prime Meridian. Parallels are segments of concentric circles.' + +demo conic 'central projection on cone tangent at 40' 40 +demo simpleconic 'equally spaced parallels, true scale on 20 and 50' 20 50 +demo lambert 'conformal, true scale on 20 and 50' 20 50 +demo albers 'equal-area, true scale on 20 and 50' 20 50 +demo bonne 'equally spaced parallels, equal-area, parallel 40 developed from tangent cone' 40 + +type='Projections with bilateral symmetry about the Prime Meridian and the equator.' + +demo polyconic 'parallels developed from tangent cones, equally spaced along Prime Meridian' +demo aitoff 'equal-area projection of globe onto 2-to-1 ellipse, based on azequalarea' +demo lagrange 'conformal, maps whole sphere into a circle' +demo bicentric 'points plotted at true azimuth from two centers on the equator at longitudes +-40, great circles are straight lines' 40 +demo elliptic 'points are plotted at true distance from two centers on the equator at longitudes +-40' 40 +demo globular 'hemisphere is circle, circular meridians and parallels' +demo vandergrinten 'sphere is circle, meridians as in globular, circular arc parallels resemble mercator' + +type='Doubly periodic conformal projections.' + +demo guyou 'W and E hemispheres are square' +demo square 'World is square with Poles at diagonally opposite corners' +demo tetra 'map on tetrahedron with edge tangent to Prime Meridian at S Pole, unfolded into equilateral triangle' +demo hex 'world is hexagon centered on N Pole, N and S hemispheres are equilateral +triangles' + +type='Retroazimuthal projections. Directions to center are true.' + +demo mecca 'equally spaced vertical meridians' 21.4 -o 90 -39.8 +demo homing 'distances to Mecca are true' 21.4 -o 90 -39.8 + +type='Miscellaneous projections.' + +demo harrison 'oblique perspective from above the North Pole, 2 earth radii from the earth, looking along the Date Line 40 degrees off vertical' 2 40 +demo trapezoidal 'equally spaced parallels, straight meridians equally spaced along parallels, true scale at 20 and 50 on Prime Meridian' 20 50 +demo lune 'conformal, polar cap above Eq is 60-degree lune' 0 60 + +type='Maps based on the spheroid' + +demo sp_mercator 'equally spaced straight meridians, conformal' +demo sp_albers 'equal-area, true scale on 20 and 50' 20 50 +} | plot blob - /dev/null blob + 4d272749c2365bea830e5188c0e44d57bbe704cd (mode 644) --- /dev/null +++ src/cmd/map/mkfile @@ -0,0 +1,32 @@ +<$PLAN9/src/mkhdr + +TARG=mapd +LIB=libmap/libmap.a +OFILES=map.$O\ + symbol.$O\ + index.$O\ + sqrt.$O\ + +HFILES=map.h\ + iplot.h\ + +<$PLAN9/src/mkone + + +$O.out:V: $OFILES $LIB + $LD $LDFLAGS -o $target $prereq + +$LIB:V: + cd libmap + mk install + +installall:V: + for(objtype in $CPUS) + mk install + cp map.rc /rc/bin/map + cp mapdemo.rc /rc/bin/mapdemo + +clean nuke:V: + rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG + cd libmap; mk clean + blob - /dev/null blob + c4c67134de7528f8ccc9e00a1bab45787da1c244 (mode 644) --- /dev/null +++ src/cmd/map/route.c @@ -0,0 +1,131 @@ +#include +#include +#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"); + } +} blob - /dev/null blob + 1f9333847b9b56a86521c31ade386b9e9fbbb621 (mode 644) --- /dev/null +++ src/cmd/map/sqrt.c @@ -0,0 +1,52 @@ +/* + sqrt returns the square root of its floating + point argument. Newton's method. + + calls frexp +*/ + +#include +#include + +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; +} blob - /dev/null blob + e8f3c680b9103261e3da1459fa1979512fb45249 (mode 644) --- /dev/null +++ src/cmd/map/symbol.c @@ -0,0 +1,192 @@ +#include +#include +#include +#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(nsymbolname)!=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; +}