diff options
author | rsc <devnull@localhost> | 2004-04-21 22:19:33 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-04-21 22:19:33 +0000 |
commit | 28994509cc11ac6a5443054dfae1fedfb69039bc (patch) | |
tree | 9d5adcd11af2708db0ecc246e008c308ca0f97d4 /src/cmd/jpg | |
parent | a01e58366c54804f15f84d6e21d13f2e4080977a (diff) | |
download | plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.gz plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.tar.bz2 plan9port-28994509cc11ac6a5443054dfae1fedfb69039bc.zip |
Why not?
Diffstat (limited to 'src/cmd/jpg')
32 files changed, 9072 insertions, 0 deletions
diff --git a/src/cmd/jpg/bmp.c b/src/cmd/jpg/bmp.c new file mode 100644 index 00000000..f7e07a01 --- /dev/null +++ b/src/cmd/jpg/bmp.c @@ -0,0 +1,210 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +ulong outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readbmp(int fd, int colorspace); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "bmp: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(image->r); + r.max.y = r.min.y+Dy(image->r); + border(screen, r, -Border, nil, ZP); + draw(screen, r, image, nil, image->r.min); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + + ARGBEGIN{ + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: bmp -39cdektv [file.bmp ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, "<stdin>"); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "bmp: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i]); + close(fd); + } + if((nineflag || cflag) && argc>1 && err==nil){ + fprint(2, "bmp: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "bmp: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readbmp(fd, CRGB); + if(array == nil || array[0]==nil){ + fprint(2, "bmp: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "bmp: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "bmp: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "bmp: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "bmp: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "bmp: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/src/cmd/jpg/bmp.h b/src/cmd/jpg/bmp.h new file mode 100644 index 00000000..ca003684 --- /dev/null +++ b/src/cmd/jpg/bmp.h @@ -0,0 +1,37 @@ + +#define BMP_RGB 0 +#define BMP_RLE8 1 +#define BMP_RLE4 2 +#define BMP_BITFIELDS 3 + +typedef struct { + uchar red; + uchar green; + uchar blue; + uchar alpha; +} Rgb; + +typedef struct { + short type; + long size; + short reserved1; + short reserved2; + long offbits; +} Filehdr; + +typedef struct { + long size; /* Size of the Bitmap-file */ + long lReserved; /* Reserved */ + long dataoff; /* Picture data location */ + long hsize; /* Header-Size */ + long width; /* Picture width (pixels) */ + long height; /* Picture height (pixels) */ + short planes; /* Planes (must be 1) */ + short bpp; /* Bits per pixel (1, 4, 8 or 24) */ + long compression; /* Compression mode */ + long imagesize; /* Image size (bytes) */ + long hres; /* Horizontal Resolution (pels/meter) */ + long vres; /* Vertical Resolution (pels/meter) */ + long colours; /* Used Colours (Col-Table index) */ + long impcolours; /* Important colours (Col-Table index) */ +} Infohdr; diff --git a/src/cmd/jpg/close.c b/src/cmd/jpg/close.c new file mode 100644 index 00000000..7a260c10 --- /dev/null +++ b/src/cmd/jpg/close.c @@ -0,0 +1,121 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +float c1 = 1.402; +float c2 = 0.34414; +float c3 = 0.71414; +float c4 = 1.772; + + +int +closest(int Y, int Cb, int Cr) +{ + double r, g, b; + double diff, min; + int rgb, R, G, B, v, i; + int y1, cb1, cr1; + + Cb -= 128; + Cr -= 128; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + +//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b); + + min = 1000000.; + v = 1000; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + R = (rgb >> 16) & 0xFF; + G = (rgb >> 8) & 0xFF; + B = (rgb >> 0) & 0xFF; + diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b); +// y1 = 0.5870*G + 0.114*B + 0.299*R; +// cb1 = (B-y1)/1.772; +// cr1 = (R-y1)/1.402; + if(diff < min){ +// if(Y==0 && y1!=0) +// continue; +// if(Y==256-16 && y1<256-16) +// continue; +// if(Cb==0 && cb1!=0) +// continue; +// if(Cb==256-16 && cb1<256-16) +// continue; +// if(Cr==0 && cr1!=0) +// continue; +// if(Cr==256-16 && cr1<256-16) +// continue; +//print("%d %d %d\n", R, G, B); + min = diff; + v = i; + } + } + if(v > 255) + abort(); + return v; +} + +#define SHIFT 5 +#define INC (1<<SHIFT) + +typedef struct Color Color; + +struct Color +{ + int col; + Color *next; +}; + +Color *col[INC*INC*INC]; + +void +add(int c, int y, int cb, int cr) +{ + Color *cp; + + y >>= 8-SHIFT; + cb >>= 8-SHIFT; + cr >>= 8-SHIFT; + cp = col[cr+INC*(cb+INC*y)]; + while(cp != nil){ + if(cp->col == c) + return; + cp = cp->next; + } + cp = malloc(sizeof(Color)); + cp->col = c; + cp->next = col[cr+INC*(cb+INC*y)]; + col[cr+INC*(cb+INC*y)] = cp; +} + +void +main(void) +{ + int y, cb, cr, n; + Color *cp; + + for(y=0; y<256; y++){ + for(cb=0; cb<256; cb++) + for(cr=0;cr<256;cr++) + add(closest(y, cb, cr), y, cb, cr); + fprint(2, "%d done\n", y); + } + for(y=0; y<INC*INC*INC; y++){ + n = 0; + cp = col[y]; + while(cp != nil){ + n++; + cp = cp->next; + } + cp = col[y]; + while(cp != nil){ + n++; + print("%d ", cp->col); + cp = cp->next; + } + print("\n"); + } +} diff --git a/src/cmd/jpg/gif.c b/src/cmd/jpg/gif.c new file mode 100644 index 00000000..f9927017 --- /dev/null +++ b/src/cmd/jpg/gif.c @@ -0,0 +1,421 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +ulong outchan = CMAP8; +Image **allims; +Image **allmasks; +int which; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rectangle +imager(void) +{ + Rectangle r; + + if(allims==nil || allims[0]==nil) + return screen->r; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(allims[0]->r); + r.max.y = r.min.y+Dy(allims[0]->r); + return r; +} + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "gif: can't reattach to window\n"); + exits("resize"); + } + if(allims==nil || allims[which]==nil) + return; + r = imager(); + border(screen, r, -Border, nil, ZP); + r.min.x += allims[which]->r.min.x - allims[0]->r.min.x; + r.min.y += allims[which]->r.min.y - allims[0]->r.min.y; + drawop(screen, r, allims[which], allmasks[which], allims[which]->r.min, S); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + + ARGBEGIN{ + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: gif -39cdektv [file.gif ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, "<stdin>"); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "gif: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i]); + close(fd); + } + if(output && argc>1 && err==nil){ + fprint(2, "gif: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +Image* +transparency(Rawimage *r, char *name) +{ + Image *i; + int j, index; + uchar *pic, *mpic, *mask; + + if((r->gifflags&TRANSP) == 0) + return nil; + i = allocimage(display, r->r, GREY8, 0, 0); + if(i == nil){ + fprint(2, "gif: allocimage for mask of %s failed: %r\n", name); + return nil; + } + pic = r->chans[0]; + mask = malloc(r->chanlen); + if(mask == nil){ + fprint(2, "gif: malloc for mask of %s failed: %r\n", name); + freeimage(i); + return nil; + } + index = r->giftrindex; + mpic = mask; + for(j=0; j<r->chanlen; j++) + if(*pic++ == index) + *mpic++ = 0; + else + *mpic++ = 0xFF; + if(loadimage(i, i->r, mask, r->chanlen) < 0){ + fprint(2, "gif: loadimage for mask of %s failed: %r\n", name); + free(mask); + freeimage(i); + return nil; + } + free(mask); + return i; +} + +/* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */ +uchar* +expand(uchar *u, int chanlen, int nchan) +{ + int j, k; + uchar *v, *up, *vp; + + v = malloc(chanlen*(nchan+1)); + if(v == nil){ + fprint(2, "gif: malloc fails: %r\n"); + exits("malloc"); + } + up = u; + vp = v; + for(j=0; j<chanlen; j++){ + *vp++ = 0xFF; + for(k=0; k<nchan; k++) + *vp++ = *up++; + } + return v; +} + +void +addalpha(Rawimage *i) +{ + char buf[32]; + + switch(outchan){ + case CMAP8: + i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); + i->chanlen = 2*(i->chanlen/1); + i->chandesc = CRGBVA16; + outchan = CHAN2(CMap, 8, CAlpha, 8); + break; + + case GREY8: + i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); + i->chanlen = 2*(i->chanlen/1); + i->chandesc = CYA16; + outchan = CHAN2(CGrey, 8, CAlpha, 8); + break; + + case RGB24: + i->chans[0] = expand(i->chans[0], i->chanlen/3, 3); + i->chanlen = 4*(i->chanlen/3); + i->chandesc = CRGBA32; + outchan = RGBA32; + break; + + default: + chantostr(buf, outchan); + fprint(2, "gif: can't add alpha to type %s\n", buf); + exits("err"); + } +} + +/* + * Called only when writing output. If the output is RGBA32, + * we must write four bytes per pixel instead of two. + * There's always at least two: data plus alpha. + * r is used only for reference; the image is already in c. + */ +void +whiteout(Rawimage *r, Rawimage *c) +{ + int i, trindex; + uchar *rp, *cp; + + rp = r->chans[0]; + cp = c->chans[0]; + trindex = r->giftrindex; + if(outchan == RGBA32) + for(i=0; i<r->chanlen; i++){ + if(*rp == trindex){ + *cp++ = 0x00; + *cp++ = 0xFF; + *cp++ = 0xFF; + *cp++ = 0xFF; + }else{ + *cp++ = 0xFF; + cp += 3; + } + rp++; + } + else + for(i=0; i<r->chanlen; i++){ + if(*rp == trindex){ + *cp++ = 0x00; + *cp++ = 0xFF; + }else{ + *cp++ = 0xFF; + cp++; + } + rp++; + } +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "gif: initdraw failed: %r\n"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **images, **rgbv; + Image **ims, **masks; + int j, k, n, ch, nloop, loopcount, dt; + char *err; + char buf[32]; + + err = nil; + images = readgif(fd, CRGB); + if(images == nil){ + fprint(2, "gif: decode %s failed: %r\n", name); + return "decode"; + } + for(n=0; images[n]; n++) + ; + ims = malloc((n+1)*sizeof(Image*)); + masks = malloc((n+1)*sizeof(Image*)); + rgbv = malloc((n+1)*sizeof(Rawimage*)); + if(masks==nil || rgbv==nil || ims==nil){ + fprint(2, "gif: malloc of masks for %s failed: %r\n", name); + err = "malloc"; + goto Return; + } + memset(masks, 0, (n+1)*sizeof(Image*)); + memset(ims, 0, (n+1)*sizeof(Image*)); + memset(rgbv, 0, (n+1)*sizeof(Rawimage*)); + if(!dflag){ + if(init() < 0){ + err = "initdraw"; + goto Return; + } + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + + for(k=0; k<n; k++){ + if(outchan == CMAP8) + rgbv[k] = torgbv(images[k], !eflag); + else{ + if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0)) + rgbv[k] = totruecolor(images[k], CY); + else + rgbv[k] = totruecolor(images[k], CRGB24); + } + if(rgbv[k] == nil){ + fprint(2, "gif: converting %s to local format failed: %r\n", name); + err = "torgbv"; + goto Return; + } + if(!dflag){ + masks[k] = transparency(images[k], name); + if(rgbv[k]->chandesc == CY) + ims[k] = allocimage(display, rgbv[k]->r, GREY8, 0, 0); + else + ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0); + if(ims[k] == nil){ + fprint(2, "gif: allocimage %s failed: %r\n", name); + err = "allocimage"; + goto Return; + } + if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){ + fprint(2, "gif: loadimage %s failed: %r\n", name); + err = "loadimage"; + goto Return; + } + } + } + + allims = ims; + allmasks = masks; + loopcount = images[0]->gifloopcount; + if(!dflag){ + nloop = 0; + do{ + for(k=0; k<n; k++){ + which = k; + eresized(0); + dt = images[k]->gifdelay*10; + if(dt < 50) + dt = 50; + while(n==1 || ecankbd()){ + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) /* an odd, democratic list */ + exits(nil); + if(ch == '\n') + goto Out; + }sleep(dt); + } + /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/ + }while(loopcount==0 || ++nloop<loopcount); + /* loop count has run out */ + ekbd(); + Out: + drawop(screen, screen->clipr, display->white, nil, ZP, S); + } + if(n>1 && output) + fprint(2, "gif: warning: only writing first image in %d-image GIF %s\n", n, name); + if(nineflag){ + if(images[0]->gifflags&TRANSP){ + addalpha(rgbv[0]); + whiteout(images[0], rgbv[0]); + } + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y); + if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){ + fprint(2, "gif: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(images[0]->gifflags&TRANSP){ + addalpha(rgbv[0]); + whiteout(images[0], rgbv[0]); + } + if(writerawimage(1, rgbv[0]) < 0){ + fprint(2, "gif: %s: write error: %r\n", name); + return "write"; + } + } + + Return: + allims = nil; + allmasks = nil; + for(k=0; images[k]; k++){ + for(j=0; j<images[k]->nchans; j++) + free(images[k]->chans[j]); + free(images[k]->cmap); + if(rgbv[k]) + free(rgbv[k]->chans[0]); + freeimage(ims[k]); + freeimage(masks[k]); + free(images[k]); + free(rgbv[k]); + } + free(images); + free(masks); + free(ims); + return err; +} diff --git a/src/cmd/jpg/ico.c b/src/cmd/jpg/ico.c new file mode 100644 index 00000000..31806afb --- /dev/null +++ b/src/cmd/jpg/ico.c @@ -0,0 +1,506 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include <cursor.h> + +typedef struct Icon Icon; +struct Icon +{ + Icon *next; + + uchar w; /* icon width */ + uchar h; /* icon height */ + ushort ncolor; /* number of colors */ + ushort nplane; /* number of bit planes */ + ushort bits; /* bits per pixel */ + ulong len; /* length of data */ + ulong offset; /* file offset to data */ + + Image *img; + Image *mask; + + Rectangle r; /* relative */ + Rectangle sr; /* abs */ +}; + +typedef struct Header Header; +struct Header +{ + uint n; + Icon *first; + Icon *last; +}; + +int debug; +Mouse mouse; +Header h; +Image *background; + +ushort +gets(uchar *p) +{ + return p[0] | (p[1]<<8); +} + +ulong +getl(uchar *p) +{ + return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +} + +int +Bgetheader(Biobuf *b, Header *h) +{ + Icon *icon; + int i; + uchar buf[40]; + + memset(h, 0, sizeof(*h)); + if(Bread(b, buf, 6) != 6) + goto eof; + if(gets(&buf[0]) != 0) + goto header; + if(gets(&buf[2]) != 1) + goto header; + h->n = gets(&buf[4]); + + for(i = 0; i < h->n; i++){ + icon = mallocz(sizeof(*icon), 1); + if(icon == nil) + sysfatal("malloc: %r"); + if(Bread(b, buf, 16) != 16) + goto eof; + icon->w = buf[0]; + icon->h = buf[1]; + icon->ncolor = buf[2] == 0 ? 256 : buf[2]; + if(buf[3] != 0) + goto header; + icon->nplane = gets(&buf[4]); + icon->bits = gets(&buf[6]); + icon->len = getl(&buf[8]); + icon->offset = getl(&buf[12]); + + if(i == 0) + h->first = icon; + else + h->last->next = icon; + h->last = icon; + } + return 0; + +eof: + werrstr("unexpected EOF"); + return -1; +header: + werrstr("unknown header format"); + return -1; +} + +uchar* +transcmap(Icon *icon, uchar *map) +{ + uchar *m, *p; + int i; + + p = m = malloc(sizeof(int)*(1<<icon->bits)); + for(i = 0; i < icon->ncolor; i++){ + *p++ = rgb2cmap(map[2], map[1], map[0]); + map += 4; + } + return m; +} + +Image* +xor2img(Icon *icon, uchar *xor, uchar *map) +{ + uchar *data; + Image *img; + int inxlen; + uchar *from, *to; + int s, byte, mask; + int x, y; + + inxlen = 4*((icon->bits*icon->w+31)/32); + to = data = malloc(icon->w*icon->h); + + /* rotate around the y axis, go to 8 bits, and convert color */ + mask = (1<<icon->bits)-1; + for(y = 0; y < icon->h; y++){ + s = -1; + byte = 0; + from = xor + (icon->h - 1 - y)*inxlen; + for(x = 0; x < icon->w; x++){ + if(s < 0){ + byte = *from++; + s = 8-icon->bits; + } + *to++ = map[(byte>>s) & mask]; + s -= icon->bits; + } + } + + /* stick in an image */ + img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill); + loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w); + + free(data); + return img; +} + +Image* +and2img(Icon *icon, uchar *and) +{ + uchar *data; + Image *img; + int inxlen; + int outxlen; + uchar *from, *to; + int x, y; + + inxlen = 4*((icon->w+31)/32); + to = data = malloc(inxlen*icon->h); + + /* rotate around the y axis and invert bits */ + outxlen = (icon->w+7)/8; + for(y = 0; y < icon->h; y++){ + from = and + (icon->h - 1 - y)*inxlen; + for(x = 0; x < outxlen; x++){ + *to++ = ~(*from++); + } + } + + /* stick in an image */ + img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill); + loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen); + + free(data); + return img; +} + +int +Bgeticon(Biobuf *b, Icon *icon) +{ + ulong l; + ushort s; + uchar *xor; + uchar *and; + uchar *cm; + uchar *buf; + uchar *map2map; + Image *img; + + Bseek(b, icon->offset, 0); + buf = malloc(icon->len); + if(buf == nil) + return -1; + if(Bread(b, buf, icon->len) != icon->len){ + werrstr("unexpected EOF"); + return -1; + } + + /* this header's info takes precedence over previous one */ + if(getl(buf) != 40){ + werrstr("bad icon header"); + return -1; + } + l = getl(buf+4); + if(l != icon->w) + icon->w = l; + l = getl(buf+8); + if(l>>1 != icon->h) + icon->h = l>>1; + s = gets(buf+12); + if(s != icon->nplane) + icon->nplane = s; + s = gets(buf+14); + if(s != icon->bits) + icon->bits = s; + + /* limit what we handle */ + switch(icon->bits){ + case 1: + case 2: + case 4: + case 8: + break; + default: + werrstr("don't support %d bit pixels", icon->bits); + return -1; + } + if(icon->nplane != 1){ + werrstr("don't support %d planes", icon->nplane); + return -1; + } + + cm = buf + 40; + xor = cm + 4*icon->ncolor; + and = xor + icon->h*4*((icon->bits*icon->w+31)/32); + + /* translate the color map to a plan 9 one */ + map2map = transcmap(icon, cm); + + /* convert the images */ + icon->img = xor2img(icon, xor, map2map); + icon->mask = and2img(icon, and); + + /* so that we save an image with a white background */ + img = allocimage(display, icon->img->r, CMAP8, 0, DWhite); + draw(img, icon->img->r, icon->img, icon->mask, ZP); + icon->img = img; + + free(buf); + free(map2map); + return 0; +} + +void +usage(void) +{ + fprint(2, "usage: %s [file]\n", argv0); + exits("usage"); +} + +enum +{ + Mimage, + Mmask, + Mexit, + + Up= 1, + Down= 0, +}; + +char *menu3str[] = { + [Mimage] "write image", + [Mmask] "write mask", + [Mexit] "exit", + 0, +}; + +Menu menu3 = { + menu3str +}; + +Cursor sight = { + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} +}; + +void +buttons(int ud) +{ + while((mouse.buttons==0) != ud) + mouse = emouse(); +} + +void +mesg(char *fmt, ...) +{ + va_list arg; + char buf[1024]; + static char obuf[1024]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + string(screen, screen->r.min, background, ZP, font, obuf); + string(screen, screen->r.min, display->white, ZP, font, buf); + strcpy(obuf, buf); +} + +void +doimage(Icon *icon) +{ + int rv; + char file[256]; + int fd; + + rv = -1; + snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h); + fd = create(file, OWRITE, 0664); + if(fd >= 0){ + rv = writeimage(fd, icon->img, 0); + close(fd); + } + if(rv < 0) + mesg("error writing %s: %r", file); + else + mesg("created %s", file); +} + +void +domask(Icon *icon) +{ + int rv; + char file[64]; + int fd; + + rv = -1; + snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h); + fd = create(file, OWRITE, 0664); + if(fd >= 0){ + rv = writeimage(fd, icon->mask, 0); + close(fd); + } + if(rv < 0) + mesg("error writing %s: %r", file); + else + mesg("created %s", file); +} + +void +apply(void (*f)(Icon*)) +{ + Icon *icon; + + esetcursor(&sight); + buttons(Down); + if(mouse.buttons == 4) + for(icon = h.first; icon; icon = icon->next) + if(ptinrect(mouse.xy, icon->sr)){ + buttons(Up); + f(icon); + break; + } + buttons(Up); + esetcursor(0); +} + +void +menu(void) +{ + int sel; + + sel = emenuhit(3, &mouse, &menu3); + switch(sel){ + case Mimage: + apply(doimage); + break; + case Mmask: + apply(domask); + break; + case Mexit: + exits(0); + break; + } +} + +void +mousemoved(void) +{ + Icon *icon; + + for(icon = h.first; icon; icon = icon->next) + if(ptinrect(mouse.xy, icon->sr)){ + mesg("%dx%d", icon->w, icon->h); + return; + } + mesg(""); +} + +enum +{ + BORDER= 1, +}; + +void +eresized(int new) +{ + Icon *icon; + Rectangle r; + + if(new && getwindow(display, Refnone) < 0) + sysfatal("can't reattach to window"); + draw(screen, screen->clipr, background, nil, ZP); + r.max.x = screen->r.min.x; + r.min.y = screen->r.min.y + font->height + 2*BORDER; + for(icon = h.first; icon != nil; icon = icon->next){ + r.min.x = r.max.x + BORDER; + r.max.x = r.min.x + Dx(icon->img->r); + r.max.y = r.min.y + Dy(icon->img->r); + draw(screen, r, icon->img, nil, ZP); + border(screen, r, -BORDER, display->black, ZP); + icon->sr = r; + } + flushimage(display, 1); +} + +void +main(int argc, char **argv) +{ + Biobuf in; + Icon *icon; + int fd; + Rectangle r; + Event e; + + ARGBEGIN{ + case 'd': + debug = 1; + break; + }ARGEND; + + fd = -1; + switch(argc){ + case 0: + fd = 0; + break; + case 1: + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("opening: %r"); + break; + default: + usage(); + break; + } + + Binit(&in, fd, OREAD); + + if(Bgetheader(&in, &h) < 0) + sysfatal("reading header: %r"); + + initdraw(nil, nil, "ico"); + background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF); + + einit(Emouse|Ekeyboard); + + r.min = Pt(4, 4); + for(icon = h.first; icon != nil; icon = icon->next){ + if(Bgeticon(&in, icon) < 0){ + fprint(2, "bad rectangle: %r\n"); + continue; + } + if(debug) + fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n", + icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset); + r.max = addpt(r.min, Pt(icon->w, icon->h)); + icon->r = r; + r.min.x += r.max.x; + } + eresized(0); + + for(;;) + switch(event(&e)){ + case Ekeyboard: + break; + case Emouse: + mouse = e.mouse; + if(mouse.buttons & 4) + menu(); + else + mousemoved(); + break; + } + + exits(0); +} diff --git a/src/cmd/jpg/imagefile.h b/src/cmd/jpg/imagefile.h new file mode 100644 index 00000000..592c5a2e --- /dev/null +++ b/src/cmd/jpg/imagefile.h @@ -0,0 +1,82 @@ +typedef struct Rawimage Rawimage; + +struct Rawimage +{ + Rectangle r; + uchar *cmap; + int cmaplen; + int nchans; + uchar *chans[4]; + int chandesc; + int chanlen; + + int fields; /* defined by format */ + int gifflags; /* gif only; graphics control extension flag word */ + int gifdelay; /* gif only; graphics control extension delay in cs */ + int giftrindex; /* gif only; graphics control extension transparency index */ + int gifloopcount; /* number of times to loop in animation; 0 means forever */ +}; + +enum +{ + /* Channel descriptors */ + CRGB = 0, /* three channels, no map */ + CYCbCr = 1, /* three channels, no map, level-shifted 601 color space */ + CY = 2, /* one channel, luminance */ + CRGB1 = 3, /* one channel, map present */ + CRGBV = 4, /* one channel, map is RGBV, understood */ + CRGB24 = 5, /* one channel in correct data order for loadimage(RGB24) */ + CRGBA32 = 6, /* one channel in correct data order for loadimage(RGBA32) */ + CYA16 = 7, /* one channel in correct data order for loadimage(Grey8+Alpha8) */ + CRGBVA16= 8, /* one channel in correct data order for loadimage(CMAP8+Alpha8) */ + + /* GIF flags */ + TRANSP = 1, + INPUT = 2, + DISPMASK = 7<<2 +}; + + +enum{ /* PNG flags */ + II_GAMMA = 1 << 0, + II_COMMENT = 1 << 1, +}; + +typedef struct ImageInfo { + ulong fields_set; + double gamma; + char *comment; +} ImageInfo; + + +Rawimage** readjpg(int, int); +Rawimage** Breadjpg(Biobuf *b, int); +Rawimage** readpng(int, int); +Rawimage** Breadpng(Biobuf *b, int); +Rawimage** readgif(int, int); +Rawimage** readpixmap(int, int); +Rawimage* torgbv(Rawimage*, int); +Rawimage* totruecolor(Rawimage*, int); +int writerawimage(int, Rawimage*); +void* _remaperror(char*, ...); + +#ifndef _MEMDRAW_H_ +typedef struct Memimage Memimage; /* avoid necessity to include memdraw.h */ +#endif + +char* startgif(Biobuf*, Image*, int); +char* writegif(Biobuf*, Image*, char*, int, int); +void endgif(Biobuf*); +char* memstartgif(Biobuf*, Memimage*, int); +char* memwritegif(Biobuf*, Memimage*, char*, int, int); +void memendgif(Biobuf*); +Image* onechan(Image*); +Memimage* memonechan(Memimage*); + +char* writeppm(Biobuf*, Image*, char*); +char* memwriteppm(Biobuf*, Memimage*, char*); +Image* multichan(Image*); +Memimage* memmultichan(Memimage*); + +char* memwritepng(Biobuf*, Memimage*, ImageInfo*); +extern int drawlog2[]; diff --git a/src/cmd/jpg/jpegdump.c b/src/cmd/jpg/jpegdump.c new file mode 100644 index 00000000..0b132cb7 --- /dev/null +++ b/src/cmd/jpg/jpegdump.c @@ -0,0 +1,346 @@ +/* jpeg parser by tom szymanski */ +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <ctype.h> + +/* subroutines done by macros */ +#define min(A,B) ((A)<(B) ? (A) : (B)) +#define max(A,B) ((A)>(B) ? (A) : (B)) +#define maxeql(A,B) if (A < (B)) A = (B); +#define mineql(A,B) if (A > (B)) A = (B); +#define eatarg0 (argc--, argv++) +#define arrayLength(A) ((sizeof A)/ (sizeof A[0])) + +FILE *infile; +char *fname; + +/* Routines to print error messages of varying severity */ + +/* externally visible variables */ +int warncnt; +char *myname; + +void getname (char *arg) { + /* Save name of invoking program for use by error routines */ + register char *p; + p = strrchr (arg, '/'); + if (p == NULL) + myname = arg; + else + myname = ++p; +} + +static void introduction (void) { + warncnt++; + fflush (stdout); + if (myname != NULL) + fprintf (stderr, "%s: ", myname); +} + +void warn (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fputc ('\n', stderr); + fflush (stderr); +} + +void quit (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fputc ('\n', stderr); + fflush (stderr); + exit (1); +} + +void fatal (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, "\nbetter get help!\n"); + fflush (stderr); + abort (); +} + +int toption = 0; +int dqt[16][64]; + +int get1 (void) { + unsigned char x; + if (fread(&x, 1, 1, infile) == 0) + quit ("unexpected EOF"); + return x; +} + +int get2 (void) { + int x; + + x = get1() << 8; + return x | get1(); +} + +void eatmarker (int kind) { + int l, c; + l = get2(); + printf ("%02x len=%d\n", kind, l); + for (l -= 2; l > 0; l--) + get1(); +} + +char *sofName[16] = { + "Baseline sequential DCT - Huffman coding", + "Extended sequential DCT - Huffman coding", + "Progressive DCT - Huffman coding", + "Lossless - Huffman coding", + "4 is otherwise used", + "Sequential DCT - differential Huffman coding", + "Progressive DCT - differential Huffman coding", + "Lossless - differential Huffman coding", + "8 is reserved", + "Extended Sequential DCT - arithmetic coding", + "Progressive DCT - arithmetic coding", + "Lossless - arithmetic coding", + "c is otherwise used", + "Sequential DCT - differential arithmetic coding", + "Progressive DCT - differential arithmetic coding", + "Lossless - differential arithmetic coding", +}; + +void get_sof (int kind) { + int i, length, height, width, precision, ncomponents; + int id, sf, tab; + length = get2(); + precision = get1(); + height = get2(); + width = get2(); + ncomponents = get1(); + printf ("SOF%d:\t%s\n", kind - 0xc0, sofName[kind - 0xc0]); + printf ("\t%d wide, %d high, %d deep, %d components\n", + width, height, precision, ncomponents); + for (i = 0; i < ncomponents; i++) { + id = get1(); + sf = get1(); + tab = get1(); + printf ("\tcomponent %d: %d hsample, %d vsample, quantization table %d\n", + id, sf >> 4, sf & 0xf, tab); + } +} + +void get_com (int kind) { + int l, c; + l = get2(); + printf ("COM len=%d '", l); + for (l -= 2; l > 0; l--) + putchar (c = get1()); + printf ("'\n"); +} + +void get_app (int kind) { + int l, c, first; + char buf[6]; + int nbuf, nok; + l = get2(); + printf ("APP%d len=%d\n", kind - 0xe0, l); + nbuf = 0; + nok = 0; + first = 1; + /* dump printable strings in comment */ + for (l -= 2; l > 0; l--){ + c = get1(); + if(isprint(c)){ + if(nbuf >= sizeof buf){ + if(!first && nbuf == nok) + printf(" "); + printf("%.*s", nbuf, buf); + nbuf = 0; + first = 0; + } + buf[nbuf++] = c; + nok++; + }else{ + if(nok >= sizeof buf) + if(nbuf > 0) + printf("%.*s", nbuf, buf); + nbuf = 0; + nok = 0; + } + } + if(nok >= sizeof buf) + if(nbuf > 0){ + if(!first && nbuf == nok) + printf(" "); + printf("%.*s", nbuf, buf); + } +} + +void get_dac (int kind) { + eatmarker (kind); +} + +int get1dqt (void) { + int t, p, i, *tab; + t = get1(); + p = t >> 4; + t = t & 0xf; + printf ("DQT:\tp = %d, table = %d\n", p, t); + tab = &dqt[t][0]; + for (i = 0; i < 64; i++) + tab[i] = p ? get2() : get1(); + if (toption) { + for (i = 0; i < 64; i++) + printf ("\t%q[%02d] = %d\n", i, tab[i]); + } + return p ? 65 : 129; +} + +void get_dqt (int kind) { + int length; + length = get2() - 2; + while (length > 0) + length -= get1dqt(); +} + +int get1dht (void) { + int l, tcth, p, i, j, v[16], vv[16][256]; + tcth = get1(); + printf ("DHT:\tclass = %d, table = %d\n", tcth >> 4, tcth & 0xf); + for (i = 0; i < 16; i++) + v[i] = get1(); + l = 17; + for (i = 0; i < 16; i++) + for (j = 0; j < v[i]; j++) { + vv[i][j] = get1(); + l += 1; + } + if (toption) { + for (i = 0; i < 16; i++) + printf ("\t%l[%02d] = %d\n", i+1, v[i]); + for (i = 0; i < 16; i++) + for (j = 0; j < v[i]; j++) + printf ("\t%v[%02d,%02d] = %d\n", i+1, j+1, vv[i][j]); + } + return l; +} + +void get_dht (int kind) { + int length; + length = get2() - 2; + while (length > 0) + length -= get1dht(); +} + +void get_sos (int kind) { + int i, length, ncomponents, id, dcac, ahal; + length = get2(); + ncomponents = get1(); + printf ("SOS:\t%d components\n", ncomponents); + for (i = 0; i < ncomponents; i++) { + id = get1(); + dcac = get1(); + printf ("\tcomponent %d: %d DC, %d AC\n", id, dcac >> 4, dcac & 0xf); + } + printf ("\tstart spectral %d\n", get1()); + printf ("\tend spectral %d\n", get1()); + ahal = get1(); + printf ("\tah = %d, al = %d\n", ahal >> 4, ahal &0xf); +} + +main (int argc, char *argv[]) { + int l, stuff, i, j, c; + while (argc > 1 && argv[1][0] == '-') { + switch (argv[1][1]) { + case 't': + toption = 1; + break; + default: + warn ("bad option '%c'", argv[1][1]); + } + eatarg0; + } + fname = argv[1]; + infile = fopen (fname, "r"); + if (infile == NULL) + quit ("can't open %s\n", fname); + Start: +// if (get1() != 0xff || get1() != 0xd8) +// quit ("not JFIF"); +// printf ("SOI\n"); +// get_app (0xe0); + for (;;) { + c = get1(); + if (c != 0xff) + quit ("expected marker, got %2x", c); + do { + c = get1(); + } while (c == 0xff); +marker: + switch (c) { + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcd: case 0xce: case 0xcf: + get_sof (c); + break; + case 0xc4: + get_dht (c); + break; + case 0xcc: + get_dac (c); + break; + case 0xd8: + printf ("SOI\n"); + break; + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + get_app(c); + break; + case 0xda: + get_sos (c); + goto newentropy; + case 0xdb: + get_dqt (c); + break; + case 0xfe: + get_com (c); + break; + case 0xd9: + printf ("EOI\n"); + if((c=getc(infile)) == EOF) + exit(0); + ungetc(c, infile); + goto Start; + default: + eatmarker (c); + } + continue; +newentropy: + l = stuff = 0; +entropy: + while ((c = get1()) != 0xff) + l += 1; + while (c == 0xff) + c = get1(); + if (c == 0) { + stuff += 1; + goto entropy; + } + printf ("sequence length %d with %d stuffs\n", l, stuff); + if (0xd0 <= c && c <= 0xd7) { + printf ("restart %d\n", c - 0xd0); + goto newentropy; + } + goto marker; + } +} diff --git a/src/cmd/jpg/jpg.c b/src/cmd/jpg/jpg.c new file mode 100644 index 00000000..cf438a93 --- /dev/null +++ b/src/cmd/jpg/jpg.c @@ -0,0 +1,342 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int jflag = 0; +int fflag = 0; +int Fflag = 0; +int nineflag = 0; +int threeflag = 0; +int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */ +int output = 0; +ulong outchan = CMAP8; +Image *image; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*, int); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "jpg: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(image->r); + r.max.y = r.min.y+Dy(image->r); + border(screen, r, -Border, nil, ZP); + draw(screen, r, image, nil, image->r.min); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i, yflag; + char *err; + char buf[12+1]; + + yflag = 0; + ARGBEGIN{ + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'F': + Fflag++; /* make a movie */ + fflag++; /* merge two fields per image */ + break; + case 'f': + fflag++; /* merge two fields per image */ + break; + case 'J': /* decode jpeg only; no display or remap (for debugging, etc.) */ + jflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'r': + colorspace = CRGB; + break; + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case 'y': /* leave it in CYCbCr; for debugging only */ + yflag = 1; + colorspace = CYCbCr; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: jpg -39cdefFkJrtv [file.jpg ...]\n"); + exits("usage"); + }ARGEND; + + if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */ + fd = open("/dev/screen", OREAD); + if(fd > 0){ + buf[12] = '\0'; + if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8) + colorspace = CRGB; + close(fd); + } + } + + err = nil; + if(argc == 0) + err = show(0, "<stdin>", outchan); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "jpg: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i], outchan); + close(fd); + } + if((nineflag || cflag) && argc>1 && err==nil){ + fprint(2, "jpg: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +Rawimage** +vidmerge(Rawimage **aa1, Rawimage **aa2) +{ + Rawimage **aao, *ao, *a1, *a2; + int i, c, row, col; + + aao = nil; + for (i = 0; aa1[i]; i++) { + + a1 = aa1[i]; + a2 = aa2[i]; + if (a2 == nil){ + fprint(2, "jpg: vidmerge: unequal lengths\n"); + return nil; + } + aao = realloc(aao, (i+2)*sizeof(Rawimage *)); + if (aao == nil){ + fprint(2, "jpg: vidmerge: realloc\n"); + return nil; + } + aao[i+1] = nil; + ao = aao[i] = malloc(sizeof(Rawimage)); + if (ao == nil){ + fprint(2, "jpg: vidmerge: realloc\n"); + return nil; + } + memcpy(ao, a1, sizeof(Rawimage)); + if (!eqrect(a1->r , a2->r)){ + fprint(2, "jpg: vidmerge: rects different in img %d\n", i); + return nil; + } + if (a1->cmaplen != a2->cmaplen){ + fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i); + return nil; + } + if (a1->nchans != a2->nchans){ + fprint(2, "jpg: vidmerge: nchans different in img %d\n", i); + return nil; + } + if (a1->fields != a2->fields){ + fprint(2, "jpg: vidmerge: fields different in img %d\n", i); + return nil; + } + ao->r.max.y += Dy(ao->r); + ao->chanlen += ao->chanlen; + if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){ + fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n", + ao->chanlen, Dx(ao->r), Dy(ao->r)); + return nil; + } + row = Dx(a1->r); + for (c = 0; c < ao->nchans; c++) { + uchar *po, *p1, *p2; + + ao->chans[c] = malloc(ao->chanlen); + po = ao->chans[c]; + p1 = a1->chans[c]; + p2 = a2->chans[c]; + for (col = 0; col < Dy(a1->r); col++) { + memcpy(po, p1, row); + po += row, p1 += row; + memcpy(po, p2, row); + po += row, p2 += row; + } + free(a1->chans[c]); + free(a2->chans[c]); + } + if(a2->cmap != nil) + free(a2->cmap); + free(a1); + free(a2); + } + if (aa2[i] != nil) + fprint(2, "jpg: vidmerge: unequal lengths\n"); + free(aa1); + free(aa2); + return aao; +} + +char* +show(int fd, char *name, int outc) +{ + Rawimage **array, *r, *c; + static int inited; + Image *i; + int j, ch, outchan; + Biobuf b; + char buf[32]; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + outchan = outc; +rpt: array = Breadjpg(&b, colorspace); + if(array == nil || array[0]==nil){ + fprint(2, "jpg: decode %s failed: %r\n", name); + return "decode"; + } + if (fflag) { + Rawimage **a; + + a = Breadjpg(&b, colorspace); + if(a == nil || a[0]==nil){ + fprint(2, "jpg: decode %s-2 failed: %r\n", name); + return "decode"; + } + array = vidmerge(a, array); + } else + Bterm(&b); + + r = array[0]; + c = nil; + if(jflag) + goto Return; + if(!dflag && !inited){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "jpg: initdraw failed: %r\n"); + return "initdraw"; + } + if(Fflag == 0) + einit(Ekeyboard|Emouse); + if(defaultcolor && screen->depth>8 && outchan==CMAP8) + outchan = RGB24; + inited++; + } + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){ + c = totruecolor(r, CY); + outchan = GREY8; + }else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "jpg: conversion of %s failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(c->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "jpg: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "jpg: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if (Fflag) { + freeimage(i); + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + goto rpt; + } + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "jpg: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "jpg: %s: write error: %r\n", name); + return "write"; + } + } + Return: + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + if (Fflag) goto rpt; + return nil; +} diff --git a/src/cmd/jpg/mkfile b/src/cmd/jpg/mkfile new file mode 100644 index 00000000..198f8e2f --- /dev/null +++ b/src/cmd/jpg/mkfile @@ -0,0 +1,52 @@ +<$PLAN9/src/mkhdr + +TARG=jpg\ + gif\ + togif\ + ppm\ + toppm\ + png\ + topng\ + yuv\ + ico\ + toico\ + bmp\ + +IMFILES=\ + torgbv.$O\ + totruecolor.$O\ + writerawimage.$O\ + +HFILES=imagefile.h\ + +SHORTLIB=draw flate bio 9 +LDFLAGS=$LDFLAGS -L$X11/lib -lX11 + +<$PLAN9/src/mkmany + +$O.jpg: $IMFILES readjpg.$O jpg.$O +$O.gif: $IMFILES readgif.$O gif.$O +$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O multichan.$O +$O.ppm: $IMFILES readppm.$O ppm.$O +$O.toppm: writeppm.$O multichan.$O toppm.$O +$O.png: $IMFILES readpng.$O png.$O multichan.$O +$O.topng: writepng.$O topng.$O +$O.yuv: $IMFILES readyuv.$O yuv.$O +$O.bmp: $IMFILES readbmp.$O bmp.$O + +torgbv.$O: ycbcr.h rgbv.h + +ycbcr.h: rgbycc.c + 9c rgbycc.c + 9l -o o.rgbycc rgbycc.c + ./o.rgbycc >ycbcr.h + +rgbv.h: rgbrgbv.c + 9c rgbrgbv.c + 9l -o o.rgbrgbv rgbrgbv.c + ./o.rgbrgbv >rgbv.h + +nuke:V: nuke-headers + +nuke-headers:V: + rm -f rgbv.h ycbcr.h diff --git a/src/cmd/jpg/multichan.c b/src/cmd/jpg/multichan.c new file mode 100644 index 00000000..03971215 --- /dev/null +++ b/src/cmd/jpg/multichan.c @@ -0,0 +1,60 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <bio.h> +#include "imagefile.h" + +/* Separate colors, if not a grey scale or bitmap, into one byte per color per pixel, no alpha or X */ +/* Result is GREY[1248] or RGB24 */ + +int drawlog2[] = { + 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5 +}; + +static +int +notrans(ulong chan) +{ + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + case GREY8: + case RGB24: + return 1; + } + return 0; +} + +Image* +multichan(Image *i) +{ + Image *ni; + + if(notrans(i->chan)) + return i; + + ni = allocimage(display, i->r, RGB24, 0, DNofill); + if(ni == nil) + return ni; + draw(ni, ni->r, i, nil, i->r.min); + return ni; +} + +Memimage* +memmultichan(Memimage *i) +{ + Memimage *ni; + + if(notrans(i->chan)) + return i; + + ni = allocmemimage(i->r, RGB24); + if(ni == nil) + return ni; + memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); + return ni; +} diff --git a/src/cmd/jpg/onechan.c b/src/cmd/jpg/onechan.c new file mode 100644 index 00000000..ea1c489b --- /dev/null +++ b/src/cmd/jpg/onechan.c @@ -0,0 +1,229 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <bio.h> +#include "imagefile.h" + +/* Convert image to a single channel, one byte per pixel */ + +static +int +notrans(ulong chan) +{ + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + case CMAP8: + case GREY8: + return 1; + } + return 0; +} + +static +int +easycase(ulong chan) +{ + switch(chan){ + case RGB16: + case RGB24: + case RGBA32: + case ARGB32: + return 1; + } + return 0; +} + +/* + * Convert to one byte per pixel, RGBV or grey, depending + */ + +static +uchar* +load(Image *image, Memimage *memimage) +{ + uchar *data, *p, *q0, *q1, *q2; + uchar *rgbv; + int depth, ndata, dx, dy, i, v; + ulong chan, pixel; + Rectangle r; + Rawimage ri, *nri; + + if(memimage == nil){ + r = image->r; + depth = image->depth; + chan = image->chan; + }else{ + r = memimage->r; + depth = memimage->depth; + chan = memimage->chan; + } + dx = Dx(r); + dy = Dy(r); + + /* + * Read image data into memory + * potentially one extra byte on each end of each scan line. + */ + ndata = dy*(2+bytesperline(r, depth)); + data = malloc(ndata); + if(data == nil) + return nil; + if(memimage != nil) + ndata = unloadmemimage(memimage, r, data, ndata); + else + ndata = unloadimage(image, r, data, ndata); + if(ndata < 0){ + werrstr("onechan: %r"); + free(data); + return nil; + } + + /* + * Repack + */ + memset(&ri, 0, sizeof(ri)); + ri.r = r; + ri.cmap = nil; + ri.cmaplen = 0; + ri.nchans = 3; + ri.chanlen = dx*dy; + ri.chans[0] = malloc(ri.chanlen); + ri.chans[1] = malloc(ri.chanlen); + ri.chans[2] = malloc(ri.chanlen); + if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){ + Err: + free(ri.chans[0]); + free(ri.chans[1]); + free(ri.chans[2]); + free(data); + return nil; + } + ri.chandesc = CRGB; + + p = data; + q0 = ri.chans[0]; + q1 = ri.chans[1]; + q2 = ri.chans[2]; + + switch(chan){ + default: + werrstr("can't handle image type 0x%lux", chan); + goto Err; + case RGB16: + for(i=0; i<ri.chanlen; i++, p+=2){ + pixel = (p[1]<<8)|p[0]; /* rrrrrggg gggbbbbb */ + v = (pixel & 0xF800) >> 8; + *q0++ = v | (v>>5); + v = (pixel & 0x07E0) >> 3; + *q1++ = v | (v>>6); + v = (pixel & 0x001F) << 3; + *q2++ = v | (v>>5); + } + break; + case RGB24: + for(i=0; i<ri.chanlen; i++){ + *q2++ = *p++; + *q1++ = *p++; + *q0++ = *p++; + } + break; + case RGBA32: + for(i=0; i<ri.chanlen; i++){ + *q2++ = *p++; + *q1++ = *p++; + *q0++ = *p++; + p++; + } + break; + case ARGB32: + for(i=0; i<ri.chanlen; i++){ + p++; + *q2++ = *p++; + *q1++ = *p++; + *q0++ = *p++; + } + break; + } + + rgbv = nil; + nri = torgbv(&ri, 1); + if(nri != nil){ + rgbv = nri->chans[0]; + free(nri); + } + + free(ri.chans[0]); + free(ri.chans[1]); + free(ri.chans[2]); + free(data); + return rgbv; +} + +Image* +onechan(Image *i) +{ + uchar *data; + Image *ni; + + if(notrans(i->chan)) + return i; + + if(easycase(i->chan)) + data = load(i, nil); + else{ + ni = allocimage(display, i->r, RGB24, 0, DNofill); + if(ni == nil) + return ni; + draw(ni, ni->r, i, nil, i->r.min); + data = load(ni, nil); + freeimage(ni); + } + + if(data == nil) + return nil; + + ni = allocimage(display, i->r, CMAP8, 0, DNofill); + if(ni != nil) + if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){ + freeimage(ni); + ni = nil; + } + free(data); + return ni; +} + +Memimage* +memonechan(Memimage *i) +{ + uchar *data; + Memimage *ni; + + if(notrans(i->chan)) + return i; + + if(easycase(i->chan)) + data = load(nil, i); + else{ + ni = allocmemimage(i->r, RGB24); + if(ni == nil) + return ni; + memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S); + data = load(nil, ni); + freememimage(ni); + } + + if(data == nil) + return nil; + + ni = allocmemimage(i->r, CMAP8); + if(ni != nil) + if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){ + freememimage(ni); + ni = nil; + } + free(data); + return ni; +} diff --git a/src/cmd/jpg/png.c b/src/cmd/jpg/png.c new file mode 100644 index 00000000..d653fe6a --- /dev/null +++ b/src/cmd/jpg/png.c @@ -0,0 +1,224 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +extern int debug; +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int colorspace = CRGB; +int output = 0; +ulong outchan = CMAP8; +Image *image; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*, int); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "png: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(image->r); + r.max.y = r.min.y+Dy(image->r); + border(screen, r, -Border, nil, ZP); + draw(screen, r, image, nil, image->r.min); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + char buf[12+1]; + + ARGBEGIN{ + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'D': + debug++; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'r': + colorspace = CRGB; + break; + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: png -39cdekrtv [file.png ...]\n"); + exits("usage"); + }ARGEND; + + if(dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */ + fd = open("/dev/screen", OREAD); + if(fd > 0){ + buf[12] = '\0'; + if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8) + colorspace = CRGB; + close(fd); + } + } + + err = nil; + if(argc == 0) + err = show(0, "<stdin>", outchan); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "png: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i], outchan); + close(fd); + } + if((nineflag || cflag) && argc>1 && err==nil){ + fprint(2, "png: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +char* +show(int fd, char *name, int outc) +{ + Rawimage **array, *r, *c; + static int inited; + Image *i; + int j, ch, outchan; + Biobuf b; + char buf[32]; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + outchan = outc; + array = Breadpng(&b, colorspace); + if(array == nil || array[0]==nil){ + fprint(2, "png: decode %s failed: %r\n", name); + return "decode"; + } + Bterm(&b); + + r = array[0]; + if(!dflag && !inited){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "png: initdraw failed: %r\n"); + return "initdraw"; + } + einit(Ekeyboard|Emouse); + if(defaultcolor && screen->depth>8 && outchan==CMAP8) + outchan = RGB24; + inited++; + } + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){ + c = totruecolor(r, CY); + outchan = GREY8; + }else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "png: conversion of %s failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(c->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "png: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "png: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "png: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "png: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/src/cmd/jpg/ppm.c b/src/cmd/jpg/ppm.c new file mode 100644 index 00000000..24019dfa --- /dev/null +++ b/src/cmd/jpg/ppm.c @@ -0,0 +1,209 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +ulong outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "ppm: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(image->r); + r.max.y = r.min.y+Dy(image->r); + border(screen, r, -Border, nil, ZP); + draw(screen, r, image, nil, image->r.min); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + + ARGBEGIN{ + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: ppm -39cdektv [file.ppm ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, "<stdin>"); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "ppm: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i]); + close(fd); + } + if((nineflag || cflag) && argc>1 && err==nil){ + fprint(2, "ppm: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "ppm: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readpixmap(fd, CRGB); + if(array == nil || array[0]==nil){ + fprint(2, "ppm: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "ppm: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "ppm: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "ppm: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "ppm: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "ppm: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/src/cmd/jpg/readbmp.c b/src/cmd/jpg/readbmp.c new file mode 100644 index 00000000..154cb48d --- /dev/null +++ b/src/cmd/jpg/readbmp.c @@ -0,0 +1,626 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" +#include "bmp.h" + +/* + MS-BMP file reader + (c) 2003, I.P.Keller + + aims to decode *all* valid bitmap formats, although some of the + flavours couldn't be verified due to lack of suitable test-files. + the following flavours are supported: + + Bit/Pix Orientation Compression Tested? + 1 top->bottom n/a yes + 1 bottom->top n/a yes + 4 top->bottom no yes + 4 bottom->top no yes + 4 top->bottom RLE4 yes, but not with displacement + 8 top->bottom no yes + 8 bottom->top no yes + 8 top->bottom RLE8 yes, but not with displacement + 16 top->bottom no no + 16 bottom->top no no + 16 top->bottom BITMASK no + 16 bottom->top BITMASK no + 24 top->bottom n/a yes + 24 bottom->top n/a yes + 32 top->bottom no no + 32 bottom->top no no + 32 top->bottom BITMASK no + 32 bottom->top BITMASK no + + OS/2 1.x bmp files are recognised as well, but testing was very limited. + + verifying was done with a number of test files, generated by + different tools. nevertheless, the tests were in no way exhaustive + enough to guarantee bug-free decoding. caveat emptor! +*/ + +static short +r16(Biobuf*b) +{ + short s; + + s = Bgetc(b); + s |= ((short)Bgetc(b)) << 8; + return s; +} + + +static long +r32(Biobuf*b) +{ + long l; + + l = Bgetc(b); + l |= ((long)Bgetc(b)) << 8; + l |= ((long)Bgetc(b)) << 16; + l |= ((long)Bgetc(b)) << 24; + return l; +} + + +/* get highest bit set */ +static int +msb(ulong x) +{ + int i; + for(i = 32; i; i--, x <<= 1) + if(x & 0x80000000L) + return i; + return 0; +} + +/* Load a 1-Bit encoded BMP file (uncompressed) */ +static int +load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32; + int val = 0, n; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0, n = 0; ix < padded_width; ix++, n--) { + if(!n) { + val = Bgetc(b); + n = 8; + } + if(ix < width) { + buf[i] = clut[val & 0x80 ? 1 : 0]; + i++; + } + val <<= 1; + } + return 0; +} + +/* Load a 4-Bit encoded BMP file (uncompressed) */ +static int +load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3; + uint valH, valL; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ) { + valH = valL = Bgetc(b) & 0xff; + valH >>= 4; + + buf[i] = clut[valH]; + i++; ix++; + + if(ix < width) { + valL &= 0xf; + buf[i] = clut[valL]; + i++; ix++; + } + } + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 4-Bit encoded BMP file (RLE4-compressed) */ +static int +load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy = height -1; + uint val, valS, skip; + Rgb* p; + + while(iy >= 0) { + ix = 0; + while(ix < width) { + val = Bgetc(b); + + if(0 != val) { + valS = (uint)Bgetc(b); + p = &buf[ix + iy * width]; + while(val--) { + *p = clut[0xf & (valS >> 4)]; + p++; + ix++; + if(0 < val) { + *p = clut[0xf & valS]; + p++; + ix++; + val--; + } + } + } else { + /* Special modes... */ + val = Bgetc(b); + switch(val) { + case 0: /* End-Of-Line detected */ + ix = width; + iy--; + break; + case 1: /* End-Of-Picture detected -->> abort */ + ix = width; + iy = -1; + break; + case 2: /* Position change detected */ + val = Bgetc(b); + ix += val; + val = Bgetc(b); + iy -= val; + break; + + default:/* Transparent data sequence detected */ + p = &buf[ix + iy * width]; + if((1 == (val & 3)) || (2 == (val & 3))) + skip = 1; + else + skip = 0; + + while(val--) { + valS = (uint)Bgetc(b); + *p = clut[0xf & (valS >> 4)]; + p++; + ix++; + if(0 < val) { + *p = clut[0xf & valS]; + p++; + ix++; + val--; + } + } + if(skip) + Bgetc(b); + break; + } + } + } + } + return 0; +} + +/* Load a 8-Bit encoded BMP file (uncompressed) */ +static int +load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ix++, i++) + buf[i] = clut[Bgetc(b) & 0xff]; + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 8-Bit encoded BMP file (RLE8-compressed) */ +static int +load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + long ix, iy = height -1; + int val, valS, skip; + Rgb* p; + + while(iy >= 0) { + ix = 0; + while(ix < width) { + val = Bgetc(b); + + if(0 != val) { + valS = Bgetc(b); + p = &buf[ix + iy * width]; + while(val--) { + *p = clut[valS]; + p++; + ix++; + } + } else { + /* Special modes... */ + val = Bgetc(b); + switch(val) { + case 0: /* End-Of-Line detected */ + ix = width; + iy--; + break; + case 1: /* End-Of-Picture detected */ + ix = width; + iy = -1; + break; + case 2: /* Position change detected */ + val = Bgetc(b); + ix += val; + val = Bgetc(b); + iy -= val; + break; + default: /* Transparent (not compressed) sequence detected */ + p = &buf[ix + iy * width]; + if(val & 1) + skip = 1; + else + skip = 0; + + while(val--) { + valS = Bgetc(b); + *p = clut[valS]; + p++; + ix++; + } + if(skip) + /* Align data stream */ + Bgetc(b); + break; + } + } + } + } + return 0; +} + +/* Load a 16-Bit encoded BMP file (uncompressed) */ +static int +load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + uchar c[2]; + long ix, iy, i = 0, step_up = 0; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + if(clut) { + unsigned mask_blue = (unsigned)clut[0].blue + + ((unsigned)clut[0].green << 8); + unsigned mask_green = (unsigned)clut[1].blue + + ((unsigned)clut[1].green << 8); + unsigned mask_red = (unsigned)clut[2].blue + + ((unsigned)clut[2].green << 8); + int shft_blue = msb((ulong)mask_blue) - 8; + int shft_green = msb((ulong)mask_green) - 8; + int shft_red = msb((ulong)mask_red) - 8; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + unsigned val; + Bread(b, c, sizeof(c)); + val = (unsigned)c[0] + ((unsigned)c[1] << 8); + + buf[i].alpha = 0; + if(shft_blue >= 0) + buf[i].blue = (uchar)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (uchar)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (uchar)((val & mask_green) >> shft_green); + else + buf[i].green = (uchar)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (uchar)((val & mask_red) >> shft_red); + else + buf[i].red = (uchar)((val & mask_red) << -shft_red); + } + } else + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + Bread(b, c, sizeof(c)); + buf[i].blue = (uchar)((c[0] << 3) & 0xf8); + buf[i].green = (uchar)(((((unsigned)c[1] << 6) + + (((unsigned)c[0]) >> 2))) & 0xf8); + buf[i].red = (uchar)((c[1] << 1) & 0xf8); + } + return 0; +} + +/* Load a 24-Bit encoded BMP file (uncompressed) */ +static int +load_24T(Biobuf* b, long width, long height, Rgb* buf) +{ + long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + for(iy = height; iy; iy--, i += step_up) { + for(ix = 0; ix < width; ix++, i++) { + buf[i].alpha = 0; + buf[i].blue = Bgetc(b); + buf[i].green = Bgetc(b); + buf[i].red = Bgetc(b); + } + Bseek(b, skip, 1); + } + return 0; +} + +/* Load a 32-Bit encoded BMP file (uncompressed) */ +static int +load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut) +{ + uchar c[4]; + long ix, iy, i = 0, step_up = 0; + + if(height > 0) { /* bottom-up */ + i = (height - 1) * width; + step_up = -2 * width; + } else + height = -height; + + if(clut) { + ulong mask_blue = (ulong)clut[0].blue + + ((ulong)clut[0].green << 8) + + ((ulong)clut[0].red << 16) + + ((ulong)clut[0].alpha << 24); + ulong mask_green = (ulong)clut[1].blue + + ((ulong)clut[1].green << 8) + + ((ulong)clut[1].red << 16) + + ((ulong)clut[1].alpha << 24); + ulong mask_red = (ulong)clut[2].blue + + ((ulong)clut[2].green << 8) + + ((ulong)clut[2].red << 16) + + ((ulong)clut[2].alpha << 24); + int shft_blue = msb(mask_blue) - 8; + int shft_green = msb(mask_green) - 8; + int shft_red = msb(mask_red) - 8; + + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + ulong val; + Bread(b, c, sizeof(c)); + val = (ulong)c[0] + ((ulong)c[1] << 8) + + ((ulong)c[2] << 16) + ((ulong)c[1] << 24); + + buf[i].alpha = 0; + if(shft_blue >= 0) + buf[i].blue = (uchar)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (uchar)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (uchar)((val & mask_green) >> shft_green); + else + buf[i].green = (uchar)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (uchar)((val & mask_red) >> shft_red); + else + buf[i].red = (uchar)((val & mask_red) << -shft_red); + } + } else + for(iy = height; iy; iy--, i += step_up) + for(ix = 0; ix < width; ix++, i++) { + Bread(b, c, nelem(c)); + buf[i].blue = c[0]; + buf[i].green = c[1]; + buf[i].red = c[2]; + } + return 0; +} + + +static Rgb* +ReadBMP(Biobuf *b, int *width, int *height) +{ + int colours, num_coltab = 0; + Filehdr bmfh; + Infohdr bmih; + Rgb clut[256]; + Rgb* buf; + + bmfh.type = r16(b); + if(bmfh.type != 0x4d42) /* signature must be 'BM' */ + sysfatal("bad magic number, not a BMP file"); + + bmfh.size = r32(b); + bmfh.reserved1 = r16(b); + bmfh.reserved2 = r16(b); + bmfh.offbits = r32(b); + + memset(&bmih, 0, sizeof(bmih)); + bmih.size = r32(b); + + if(bmih.size == 0x0c) { /* OS/2 1.x version */ + bmih.width = r16(b); + bmih.height = r16(b); + bmih.planes = r16(b); + bmih.bpp = r16(b); + bmih.compression = BMP_RGB; + } else { /* Windows */ + bmih.width = r32(b); + bmih.height = r32(b); + bmih.planes = r16(b); + bmih.bpp = r16(b); + bmih.compression = r32(b); + bmih.imagesize = r32(b); + bmih.hres = r32(b); + bmih.vres = r32(b); + bmih.colours = r32(b); + bmih.impcolours = r32(b); + } + + if(bmih.bpp < 16) { + /* load colour table */ + if(bmih.impcolours) + num_coltab = (int)bmih.impcolours; + else + num_coltab = 1 << bmih.bpp; + } else if(bmih.compression == BMP_BITFIELDS && + (bmih.bpp == 16 || bmih.bpp == 32)) + /* load bitmasks */ + num_coltab = 3; + + if(num_coltab) { + int i; + Bseek(b, bmih.size + sizeof(Infohdr), 0); + + for(i = 0; i < num_coltab; i++) { + clut[i].blue = (uchar)Bgetc(b); + clut[i].green = (uchar)Bgetc(b); + clut[i].red = (uchar)Bgetc(b); + clut[i].alpha = (uchar)Bgetc(b); + } + } + + *width = bmih.width; + *height = bmih.height; + colours = bmih.bpp; + + Bseek(b, bmfh.offbits, 0); + + if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil) + sysfatal("no memory"); + + switch(colours) { + case 1: + load_1T(b, *width, *height, buf, clut); + break; + case 4: + if(bmih.compression == BMP_RLE4) + load_4C(b, *width, *height, buf, clut); + else + load_4T(b, *width, *height, buf, clut); + break; + case 8: + if(bmih.compression == BMP_RLE8) + load_8C(b, *width, *height, buf, clut); + else + load_8T(b, *width, *height, buf, clut); + break; + case 16: + load_16(b, *width, *height, buf, + bmih.compression == BMP_BITFIELDS ? clut : nil); + break; + case 24: + load_24T(b, *width, *height, buf); + break; + case 32: + load_32(b, *width, *height, buf, + bmih.compression == BMP_BITFIELDS ? clut : nil); + break; + } + return buf; +} + +Rawimage** +Breadbmp(Biobuf *bp, int colourspace) +{ + Rawimage *a, **array; + int c, width, height; + uchar *r, *g, *b; + Rgb *s, *e; + Rgb *bmp; + char ebuf[128]; + + a = nil; + bmp = nil; + array = nil; + USED(a); + USED(bmp); + if (colourspace != CRGB) { + errstr(ebuf, sizeof ebuf); /* throw it away */ + werrstr("ReadRGB: unknown colour space %d", colourspace); + return nil; + } + + if ((bmp = ReadBMP(bp, &width, &height)) == nil) + return nil; + + if ((a = calloc(sizeof(Rawimage), 1)) == nil) + goto Error; + + for (c = 0; c < 3; c++) + if ((a->chans[c] = calloc(width, height)) == nil) + goto Error; + + if ((array = calloc(sizeof(Rawimage *), 2)) == nil) + goto Error; + array[0] = a; + array[1] = nil; + + a->nchans = 3; + a->chandesc = CRGB; + a->chanlen = width * height; + a->r = Rect(0, 0, width, height); + + s = bmp; + e = s + width * height; + r = a->chans[0]; + g = a->chans[1]; + b = a->chans[2]; + + do { + *r++ = s->red; + *g++ = s->green; + *b++ = s->blue; + }while(++s < e); + + free(bmp); + return array; + +Error: + if (a) + for (c = 0; c < 3; c++) + if (a->chans[c]) + free(a->chans[c]); + if (a) + free(a); + if (array) + free(array); + if (bmp) + free(bmp); + return nil; + +} + +Rawimage** +readbmp(int fd, int colorspace) +{ + Rawimage * *a; + Biobuf b; + + if (Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadbmp(&b, colorspace); + Bterm(&b); + return a; +} + + diff --git a/src/cmd/jpg/readgif.c b/src/cmd/jpg/readgif.c new file mode 100644 index 00000000..22a0b68c --- /dev/null +++ b/src/cmd/jpg/readgif.c @@ -0,0 +1,535 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" + +typedef struct Entry Entry; +typedef struct Header Header; + +struct Entry{ + int prefix; + int exten; +}; + + +struct Header{ + Biobuf *fd; + char err[256]; + jmp_buf errlab; + uchar buf[3*256]; + char vers[8]; + uchar *globalcmap; + int screenw; + int screenh; + int fields; + int bgrnd; + int aspect; + int flags; + int delay; + int trindex; + int loopcount; + Entry tbl[4096]; + Rawimage **array; + Rawimage *new; + + uchar *pic; +}; + +static char readerr[] = "ReadGIF: read error: %r"; +static char extreaderr[] = "ReadGIF: can't read extension: %r"; +static char memerr[] = "ReadGIF: malloc failed: %r"; + +static Rawimage** readarray(Header*); +static Rawimage* readone(Header*); +static void readheader(Header*); +static void skipextension(Header*); +static uchar* readcmap(Header*, int); +static uchar* decode(Header*, Rawimage*, Entry*); +static void interlace(Header*, Rawimage*); + +static +void +clear(void *pp) +{ + void **p = (void**)pp; + + if(*p){ + free(*p); + *p = nil; + } +} + +static +void +giffreeall(Header *h, int freeimage) +{ + int i; + + if(h->fd){ + Bterm(h->fd); + h->fd = nil; + } + clear(&h->pic); + if(h->new){ + clear(&h->new->cmap); + clear(&h->new->chans[0]); + clear(&h->new); + } + clear(&h->globalcmap); + if(freeimage && h->array!=nil){ + for(i=0; h->array[i]; i++){ + clear(&h->array[i]->cmap); + clear(&h->array[i]->chans[0]); + } + clear(&h->array); + } +} + +static +void +giferror(Header *h, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vseprint(h->err, h->err+sizeof h->err, fmt, arg); + va_end(arg); + + werrstr(h->err); + giffreeall(h, 1); + longjmp(h->errlab, 1); +} + + +Rawimage** +readgif(int fd, int colorspace) +{ + Rawimage **a; + Biobuf b; + Header *h; + char buf[ERRMAX]; + + buf[0] = '\0'; + USED(colorspace); + if(Binit(&b, fd, OREAD) < 0) + return nil; + h = malloc(sizeof(Header)); + if(h == nil){ + Bterm(&b); + return nil; + } + memset(h, 0, sizeof(Header)); + h->fd = &b; + errstr(buf, sizeof buf); /* throw it away */ + if(setjmp(h->errlab)) + a = nil; + else + a = readarray(h); + giffreeall(h, 0); + free(h); + return a; +} + +static +void +inittbl(Header *h) +{ + int i; + Entry *tbl; + + tbl = h->tbl; + for(i=0; i<258; i++) { + tbl[i].prefix = -1; + tbl[i].exten = i; + } +} + +static +Rawimage** +readarray(Header *h) +{ + Entry *tbl; + Rawimage *new, **array; + int c, nimages; + + tbl = h->tbl; + + readheader(h); + + if(h->fields & 0x80) + h->globalcmap = readcmap(h, (h->fields&7)+1); + + array = malloc(sizeof(Rawimage**)); + if(array == nil) + giferror(h, memerr); + nimages = 0; + array[0] = nil; + h->array = array; + + for(;;){ + switch(c = Bgetc(h->fd)){ + case Beof: + goto Return; + + case 0x21: /* Extension (ignored) */ + skipextension(h); + break; + + case 0x2C: /* Image Descriptor */ + inittbl(h); + new = readone(h); + if(new->fields & 0x80){ + new->cmaplen = 3*(1<<((new->fields&7)+1)); + new->cmap = readcmap(h, (new->fields&7)+1); + }else{ + new->cmaplen = 3*(1<<((h->fields&7)+1)); + new->cmap = malloc(new->cmaplen); + memmove(new->cmap, h->globalcmap, new->cmaplen); + } + h->new = new; + new->chans[0] = decode(h, new, tbl); + if(new->fields & 0x40) + interlace(h, new); + new->gifflags = h->flags; + new->gifdelay = h->delay; + new->giftrindex = h->trindex; + new->gifloopcount = h->loopcount; + array = realloc(h->array, (nimages+2)*sizeof(Rawimage*)); + if(array == nil) + giferror(h, memerr); + array[nimages++] = new; + array[nimages] = nil; + h->array = array; + h->new = nil; + break; + + case 0x3B: /* Trailer */ + goto Return; + + default: + fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c); + goto Return; + } + } + + Return: + if(array[0]==nil || array[0]->chans[0] == nil) + giferror(h, "ReadGIF: no picture in file"); + + return array; +} + +static +void +readheader(Header *h) +{ + if(Bread(h->fd, h->buf, 13) != 13) + giferror(h, "ReadGIF: can't read header: %r"); + memmove(h->vers, h->buf, 6); + if(strcmp(h->vers, "GIF87a")!=0 && strcmp(h->vers, "GIF89a")!=0) + giferror(h, "ReadGIF: can't recognize format %s", h->vers); + h->screenw = h->buf[6]+(h->buf[7]<<8); + h->screenh = h->buf[8]+(h->buf[9]<<8); + h->fields = h->buf[10]; + h->bgrnd = h->buf[11]; + h->aspect = h->buf[12]; + h->flags = 0; + h->delay = 0; + h->trindex = 0; + h->loopcount = -1; +} + +static +uchar* +readcmap(Header *h, int size) +{ + uchar *map; + + if(size > 8) + giferror(h, "ReadGIF: can't handles %d bits per pixel", size); + size = 3*(1<<size); + if(Bread(h->fd, h->buf, size) != size) + giferror(h, "ReadGIF: short read on color map"); + map = malloc(size); + if(map == nil) + giferror(h, memerr); + memmove(map, h->buf, size); + return map; +} + +static +Rawimage* +readone(Header *h) +{ + Rawimage *i; + int left, top, width, height; + + if(Bread(h->fd, h->buf, 9) != 9) + giferror(h, "ReadGIF: can't read image descriptor: %r"); + i = malloc(sizeof(Rawimage)); + if(i == nil) + giferror(h, memerr); + left = h->buf[0]+(h->buf[1]<<8); + top = h->buf[2]+(h->buf[3]<<8); + width = h->buf[4]+(h->buf[5]<<8); + height = h->buf[6]+(h->buf[7]<<8); + i->fields = h->buf[8]; + i->r.min.x = left; + i->r.min.y = top; + i->r.max.x = left+width; + i->r.max.y = top+height; + i->nchans = 1; + i->chandesc = CRGB1; + return i; +} + + +static +int +readdata(Header *h, uchar *data) +{ + int nbytes, n; + + nbytes = Bgetc(h->fd); + if(nbytes < 0) + giferror(h, "ReadGIF: can't read data: %r"); + if(nbytes == 0) + return 0; + n = Bread(h->fd, data, nbytes); + if(n < 0) + giferror(h, "ReadGIF: can't read data: %r"); + if(n != nbytes) + fprint(2, "ReadGIF: short data subblock\n"); + return n; +} + +static +void +graphiccontrol(Header *h) +{ + if(Bread(h->fd, h->buf, 5+1) != 5+1) + giferror(h, readerr); + h->flags = h->buf[1]; + h->delay = h->buf[2]+(h->buf[3]<<8); + h->trindex = h->buf[4]; +} + +static +void +skipextension(Header *h) +{ + int type, hsize, hasdata, n; + uchar data[256]; + + hsize = 0; + hasdata = 0; + + type = Bgetc(h->fd); + switch(type){ + case Beof: + giferror(h, extreaderr); + break; + case 0x01: /* Plain Text Extension */ + hsize = 13; + hasdata = 1; + break; + case 0xF9: /* Graphic Control Extension */ + graphiccontrol(h); + return; + case 0xFE: /* Comment Extension */ + hasdata = 1; + break; + case 0xFF: /* Application Extension */ + hsize = Bgetc(h->fd); + /* standard says this must be 11, but Adobe likes to put out 10-byte ones, + * so we pay attention to the field. */ + hasdata = 1; + break; + default: + giferror(h, "ReadGIF: unknown extension"); + } + if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize) + giferror(h, extreaderr); + if(!hasdata){ + if(h->buf[hsize-1] != 0) + giferror(h, "ReadGIF: bad extension format"); + return; + } + + /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */ + if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){ + n = readdata(h, data); + if(n == 0) + return; + if(n==3 && data[0]==1) + h->loopcount = data[1] | (data[2]<<8); + } + while(readdata(h, data) != 0) + ; +} + +static +uchar* +decode(Header *h, Rawimage *i, Entry *tbl) +{ + int c, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen; + int csize, nentry, maxentry, first, ocode, ndata, nb; + uchar *pic; + uchar stack[4096], data[256]; + + if(Bread(h->fd, h->buf, 1) != 1) + giferror(h, "ReadGIF: can't read data: %r"); + codesize = h->buf[0]; + if(codesize>8 || 0>codesize) + giferror(h, "ReadGIF: can't handle codesize %d", codesize); + if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize) + && (codesize!=2 || i->cmaplen!=3*2)) /* peculiar GIF bitmap files... */ + giferror(h, "ReadGIF: codesize %d doesn't match color map 3*%d", codesize, i->cmaplen/3); + + CTM =1<<codesize; + EOD = CTM+1; + + piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y); + i->chanlen = piclen; + pic = malloc(piclen); + if(pic == nil) + giferror(h, memerr); + h->pic = pic; + pici = 0; + ndata = 0; + datai = 0; + nbits = 0; + sreg = 0; + fc = 0; + + Loop: + for(;;){ + csize = codesize+1; + nentry = EOD+1; + maxentry = (1<<csize)-1; + first = 1; + ocode = -1; + + for(;; ocode = incode) { + while(nbits < csize) { + if(datai == ndata){ + ndata = readdata(h, data); + if(ndata == 0) + goto Return; + datai = 0; + } + c = data[datai++]; + sreg |= c<<nbits; + nbits += 8; + } + code = sreg & ((1<<csize) - 1); + sreg >>= csize; + nbits -= csize; + + if(code == EOD){ + ndata = readdata(h, data); + if(ndata != 0) + fprint(2, "ReadGIF: unexpected data past EOD"); + goto Return; + } + + if(code == CTM) + goto Loop; + + stacki = (sizeof stack)-1; + + incode = code; + + /* special case for KwKwK */ + if(code == nentry) { + stack[stacki--] = fc; + code = ocode; + } + + if(code > nentry) + giferror(h, "ReadGIF: bad code %x %x", code, nentry); + + for(c=code; c>=0; c=tbl[c].prefix) + stack[stacki--] = tbl[c].exten; + + nb = (sizeof stack)-(stacki+1); + if(pici+nb > piclen){ + /* this common error is harmless + * we have to keep reading to keep the blocks in sync */ + ; + }else{ + memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1)); + pici += nb; + } + + fc = stack[stacki+1]; + + if(first){ + first = 0; + continue; + } + #define early 0 /* peculiar tiff feature here for reference */ + if(nentry == maxentry-early) { + if(csize >= 12) + continue; + csize++; + maxentry = (1<<csize); + if(csize < 12) + maxentry--; + } + tbl[nentry].prefix = ocode; + tbl[nentry].exten = fc; + nentry++; + } + } + +Return: + h->pic = nil; + return pic; +} + +static +void +interlace(Header *h, Rawimage *image) +{ + uchar *pic; + Rectangle r; + int dx, yy, y; + uchar *ipic; + + pic = image->chans[0]; + r = image->r; + dx = r.max.x-r.min.x; + ipic = malloc(dx*(r.max.y-r.min.y)); + if(ipic == nil) + giferror(h, nil); + + /* Group 1: every 8th row, starting with row 0 */ + yy = 0; + for(y=r.min.y; y<r.max.y; y+=8){ + memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx); + yy++; + } + + /* Group 2: every 8th row, starting with row 4 */ + for(y=r.min.y+4; y<r.max.y; y+=8){ + memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx); + yy++; + } + + /* Group 3: every 4th row, starting with row 2 */ + for(y=r.min.y+2; y<r.max.y; y+=4){ + memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx); + yy++; + } + + /* Group 4: every 2nd row, starting with row 1 */ + for(y=r.min.y+1; y<r.max.y; y+=2){ + memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx); + yy++; + } + + free(image->chans[0]); + image->chans[0] = ipic; +} diff --git a/src/cmd/jpg/readjpg.c b/src/cmd/jpg/readjpg.c new file mode 100644 index 00000000..b154e75b --- /dev/null +++ b/src/cmd/jpg/readjpg.c @@ -0,0 +1,1661 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" + +enum { + /* Constants, all preceded by byte 0xFF */ + SOF =0xC0, /* Start of Frame */ + SOF2=0xC2, /* Start of Frame; progressive Huffman */ + JPG =0xC8, /* Reserved for JPEG extensions */ + DHT =0xC4, /* Define Huffman Tables */ + DAC =0xCC, /* Arithmetic coding conditioning */ + RST =0xD0, /* Restart interval termination */ + RST7 =0xD7, /* Restart interval termination (highest value) */ + SOI =0xD8, /* Start of Image */ + EOI =0xD9, /* End of Image */ + SOS =0xDA, /* Start of Scan */ + DQT =0xDB, /* Define quantization tables */ + DNL =0xDC, /* Define number of lines */ + DRI =0xDD, /* Define restart interval */ + DHP =0xDE, /* Define hierarchical progression */ + EXP =0xDF, /* Expand reference components */ + APPn =0xE0, /* Reserved for application segments */ + JPGn =0xF0, /* Reserved for JPEG extensions */ + COM =0xFE, /* Comment */ + + CLAMPOFF = 300, + NCLAMP = CLAMPOFF+700 +}; + +typedef struct Framecomp Framecomp; +typedef struct Header Header; +typedef struct Huffman Huffman; + +struct Framecomp /* Frame component specifier from SOF marker */ +{ + int C; + int H; + int V; + int Tq; +}; + +struct Huffman +{ + int *size; /* malloc'ed */ + int *code; /* malloc'ed */ + int *val; /* malloc'ed */ + int mincode[17]; + int maxcode[17]; + int valptr[17]; + /* fast lookup */ + int value[256]; + int shift[256]; +}; + + +struct Header +{ + Biobuf *fd; + char err[256]; + jmp_buf errlab; + /* variables in i/o routines */ + int sr; /* shift register, right aligned */ + int cnt; /* # bits in right part of sr */ + uchar *buf; + int nbuf; + int peek; + + int Nf; + + Framecomp comp[3]; + uchar mode; + int X; + int Y; + int qt[4][64]; /* quantization tables */ + Huffman dcht[4]; + Huffman acht[4]; + int **data[3]; + int ndata[3]; + + uchar *sf; /* start of frame; do better later */ + uchar *ss; /* start of scan; do better later */ + int ri; /* restart interval */ + + /* progressive scan */ + Rawimage *image; + Rawimage **array; + int *dccoeff[3]; + int **accoeff[3]; /* only need 8 bits plus quantization */ + int naccoeff[3]; + int nblock[3]; + int nacross; + int ndown; + int Hmax; + int Vmax; +}; + +static uchar clamp[NCLAMP]; + +static Rawimage *readslave(Header*, int); +static int readsegment(Header*, int*); +static void quanttables(Header*, uchar*, int); +static void huffmantables(Header*, uchar*, int); +static void soiheader(Header*); +static int nextbyte(Header*, int); +static int int2(uchar*, int); +static void nibbles(int, int*, int*); +static int receive(Header*, int); +static int receiveEOB(Header*, int); +static int receivebit(Header*); +static void restart(Header*, int); +static int decode(Header*, Huffman*); +static Rawimage* baselinescan(Header*, int); +static void progressivescan(Header*, int); +static Rawimage* progressiveIDCT(Header*, int); +static void idct(int*); +static void colormap1(Header*, int, Rawimage*, int*, int, int); +static void colormapall1(Header*, int, Rawimage*, int*, int*, int*, int, int); +static void colormap(Header*, int, Rawimage*, int**, int**, int**, int, int, int, int, int*, int*); +static void jpgerror(Header*, char*, ...); + +static char readerr[] = "ReadJPG: read error: %r"; +static char memerr[] = "ReadJPG: malloc failed: %r"; + +static int zig[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, 17, /* 0-7 */ + 24, 32, 25, 18, 11, 4, 5, /* 8-15 */ + 12, 19, 26, 33, 40, 48, 41, 34, /* 16-23 */ + 27, 20, 13, 6, 7, 14, 21, 28, /* 24-31 */ + 35, 42, 49, 56, 57, 50, 43, 36, /* 32-39 */ + 29, 22, 15, 23, 30, 37, 44, 51, /* 40-47 */ + 58, 59, 52, 45, 38, 31, 39, 46, /* 48-55 */ + 53, 60, 61, 54, 47, 55, 62, 63 /* 56-63 */ +}; + +static +void +jpginit(void) +{ + int k; + static int inited; + + if(inited) + return; + inited = 1; + for(k=0; k<CLAMPOFF; k++) + clamp[k] = 0; + for(; k<CLAMPOFF+256; k++) + clamp[k] = k-CLAMPOFF; + for(; k<NCLAMP; k++) + clamp[k] = 255; +} + +static +void* +jpgmalloc(Header *h, int n, int clear) +{ + void *p; + + p = malloc(n); + if(p == nil) + jpgerror(h, memerr); + if(clear) + memset(p, 0, n); + return p; +} + +static +void +clear(void *pp) +{ + void **p = (void**)pp; + + if(*p){ + free(*p); + *p = nil; + } +} + +static +void +jpgfreeall(Header *h, int freeimage) +{ + int i, j; + + clear(&h->buf); + if(h->dccoeff[0]) + for(i=0; i<3; i++) + clear(&h->dccoeff[i]); + if(h->accoeff[0]) + for(i=0; i<3; i++){ + if(h->accoeff[i]) + for(j=0; j<h->naccoeff[i]; j++) + clear(&h->accoeff[i][j]); + clear(&h->accoeff[i]); + } + for(i=0; i<4; i++){ + clear(&h->dcht[i].size); + clear(&h->acht[i].size); + clear(&h->dcht[i].code); + clear(&h->acht[i].code); + clear(&h->dcht[i].val); + clear(&h->acht[i].val); + } + if(h->data[0]) + for(i=0; i<3; i++){ + if(h->data[i]) + for(j=0; j<h->ndata[i]; j++) + clear(&h->data[i][j]); + clear(&h->data[i]); + } + if(freeimage && h->image!=nil){ + clear(&h->array); + clear(&h->image->cmap); + for(i=0; i<3; i++) + clear(&h->image->chans[i]); + clear(&h->image); + } +} + +static +void +jpgerror(Header *h, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vseprint(h->err, h->err+sizeof h->err, fmt, arg); + va_end(arg); + + werrstr(h->err); + jpgfreeall(h, 1); + longjmp(h->errlab, 1); +} + +Rawimage** +Breadjpg(Biobuf *b, int colorspace) +{ + Rawimage *r, **array; + Header *h; + char buf[ERRMAX]; + + buf[0] = '\0'; + if(colorspace!=CYCbCr && colorspace!=CRGB){ + errstr(buf, sizeof buf); /* throw it away */ + werrstr("ReadJPG: unknown color space"); + return nil; + } + jpginit(); + h = malloc(sizeof(Header)); + array = malloc(sizeof(Header)); + if(h==nil || array==nil){ + free(h); + free(array); + return nil; + } + h->array = array; + memset(h, 0, sizeof(Header)); + h->fd = b; + errstr(buf, sizeof buf); /* throw it away */ + if(setjmp(h->errlab)) + r = nil; + else + r = readslave(h, colorspace); + jpgfreeall(h, 0); + free(h); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage** +readjpg(int fd, int colorspace) +{ + Rawimage** a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadjpg(&b, colorspace); + Bterm(&b); + return a; +} + +static +Rawimage* +readslave(Header *header, int colorspace) +{ + Rawimage *image; + int nseg, i, H, V, m, n; + uchar *b; + + soiheader(header); + nseg = 0; + image = nil; + + header->buf = jpgmalloc(header, 4096, 0); + header->nbuf = 4096; + while(header->err[0] == '\0'){ + nseg++; + n = readsegment(header, &m); + b = header->buf; + switch(m){ + case -1: + return image; + + case APPn+0: + if(nseg==1 && strncmp((char*)b, "JFIF", 4)==0) /* JFIF header; check version */ + if(b[5]>1 || b[6]>2) + sprint(header->err, "ReadJPG: can't handle JFIF version %d.%2d", b[5], b[6]); + break; + + case APPn+1: case APPn+2: case APPn+3: case APPn+4: case APPn+5: + case APPn+6: case APPn+7: case APPn+8: case APPn+9: case APPn+10: + case APPn+11: case APPn+12: case APPn+13: case APPn+14: case APPn+15: + break; + + case DQT: + quanttables(header, b, n); + break; + + case SOF: + case SOF2: + header->Y = int2(b, 1); + header->X = int2(b, 3); + header->Nf =b[5]; + for(i=0; i<header->Nf; i++){ + header->comp[i].C = b[6+3*i+0]; + nibbles(b[6+3*i+1], &H, &V); + if(H<=0 || V<=0) + jpgerror(header, "non-positive sampling factor (Hsamp or Vsamp)"); + header->comp[i].H = H; + header->comp[i].V = V; + header->comp[i].Tq = b[6+3*i+2]; + } + header->mode = m; + header->sf = b; + break; + + case SOS: + header->ss = b; + switch(header->mode){ + case SOF: + image = baselinescan(header, colorspace); + break; + case SOF2: + progressivescan(header, colorspace); + break; + default: + sprint(header->err, "unrecognized or unspecified encoding %d", header->mode); + break; + } + break; + + case DHT: + huffmantables(header, b, n); + break; + + case DRI: + header->ri = int2(b, 0); + break; + + case COM: + break; + + case EOI: + if(header->mode == SOF2) + image = progressiveIDCT(header, colorspace); + return image; + + default: + sprint(header->err, "ReadJPG: unknown marker %.2x", m); + break; + } + } + return image; +} + +/* readsegment is called after reading scan, which can have */ +/* read ahead a byte. so we must check peek here */ +static +int +readbyte(Header *h) +{ + uchar x; + + if(h->peek >= 0){ + x = h->peek; + h->peek = -1; + }else if(Bread(h->fd, &x, 1) != 1) + jpgerror(h, readerr); + return x; +} + +static +int +marker(Header *h) +{ + int c; + + while((c=readbyte(h)) == 0) + fprint(2, "ReadJPG: skipping zero byte at offset %lld\n", Boffset(h->fd)); + if(c != 0xFF) + jpgerror(h, "ReadJPG: expecting marker; found 0x%x at offset %lld\n", c, Boffset(h->fd)); + while(c == 0xFF) + c = readbyte(h); + return c; +} + +static +int +int2(uchar *buf, int n) +{ + return (buf[n]<<8) + buf[n+1]; +} + +static +void +nibbles(int b, int *p0, int *p1) +{ + *p0 = (b>>4) & 0xF; + *p1 = b & 0xF; +} + +static +void +soiheader(Header *h) +{ + h->peek = -1; + if(marker(h) != SOI) + jpgerror(h, "ReadJPG: unrecognized marker in header"); + h->err[0] = '\0'; + h->mode = 0; + h->ri = 0; +} + +static +int +readsegment(Header *h, int *markerp) +{ + int m, n; + uchar tmp[2]; + + m = marker(h); + switch(m){ + case EOI: + *markerp = m; + return 0; + case 0: + jpgerror(h, "ReadJPG: expecting marker; saw %.2x at offset %lld", m, Boffset(h->fd)); + } + if(Bread(h->fd, tmp, 2) != 2) + Readerr: + jpgerror(h, readerr); + n = int2(tmp, 0); + if(n < 2) + goto Readerr; + n -= 2; + if(n > h->nbuf){ + free(h->buf); + h->buf = jpgmalloc(h, n+1, 0); /* +1 for sentinel */ + h->nbuf = n; + } + if(Bread(h->fd, h->buf, n) != n) + goto Readerr; + *markerp = m; + return n; +} + +static +int +huffmantable(Header *h, uchar *b) +{ + Huffman *t; + int Tc, th, n, nsize, i, j, k, v, cnt, code, si, sr, m; + int *maxcode; + + nibbles(b[0], &Tc, &th); + if(Tc > 1) + jpgerror(h, "ReadJPG: unknown Huffman table class %d", Tc); + if(th>3 || (h->mode==SOF && th>1)) + jpgerror(h, "ReadJPG: unknown Huffman table index %d", th); + if(Tc == 0) + t = &h->dcht[th]; + else + t = &h->acht[th]; + + /* flow chart C-2 */ + nsize = 0; + for(i=0; i<16; i++) + nsize += b[1+i]; + t->size = jpgmalloc(h, (nsize+1)*sizeof(int), 1); + k = 0; + for(i=1; i<=16; i++){ + n = b[i]; + for(j=0; j<n; j++) + t->size[k++] = i; + } + t->size[k] = 0; + + /* initialize HUFFVAL */ + t->val = jpgmalloc(h, nsize*sizeof(int), 1); + for(i=0; i<nsize; i++) + t->val[i] = b[17+i]; + + /* flow chart C-3 */ + t->code = jpgmalloc(h, (nsize+1)*sizeof(int), 1); + k = 0; + code = 0; + si = t->size[0]; + for(;;){ + do + t->code[k++] = code++; + while(t->size[k] == si); + if(t->size[k] == 0) + break; + do{ + code <<= 1; + si++; + }while(t->size[k] != si); + } + + /* flow chart F-25 */ + i = 0; + j = 0; + for(;;){ + for(;;){ + i++; + if(i > 16) + goto outF25; + if(b[i] != 0) + break; + t->maxcode[i] = -1; + } + t->valptr[i] = j; + t->mincode[i] = t->code[j]; + j += b[i]-1; + t->maxcode[i] = t->code[j]; + j++; + } +outF25: + + /* create byte-indexed fast path tables */ + maxcode = t->maxcode; + /* stupid startup algorithm: just run machine for each byte value */ + for(v=0; v<256; ){ + cnt = 7; + m = 1<<7; + code = 0; + sr = v; + i = 1; + for(;;i++){ + if(sr & m) + code |= 1; + if(code <= maxcode[i]) + break; + code <<= 1; + m >>= 1; + if(m == 0){ + t->shift[v] = 0; + t->value[v] = -1; + goto continueBytes; + } + cnt--; + } + t->shift[v] = 8-cnt; + t->value[v] = t->val[t->valptr[i]+(code-t->mincode[i])]; + + continueBytes: + v++; + } + + return nsize; +} + +static +void +huffmantables(Header *h, uchar *b, int n) +{ + int l, mt; + + for(l=0; l<n; l+=17+mt) + mt = huffmantable(h, &b[l]); +} + +static +int +quanttable(Header *h, uchar *b) +{ + int i, pq, tq, *q; + + nibbles(b[0], &pq, &tq); + if(pq > 1) + jpgerror(h, "ReadJPG: unknown quantization table class %d", pq); + if(tq > 3) + jpgerror(h, "ReadJPG: unknown quantization table index %d", tq); + q = h->qt[tq]; + for(i=0; i<64; i++){ + if(pq == 0) + q[i] = b[1+i]; + else + q[i] = int2(b, 1+2*i); + } + return 64*(1+pq); +} + +static +void +quanttables(Header *h, uchar *b, int n) +{ + int l, m; + + for(l=0; l<n; l+=1+m) + m = quanttable(h, &b[l]); +} + +static +Rawimage* +baselinescan(Header *h, int colorspace) +{ + int Ns, z, k, m, Hmax, Vmax, comp; + int allHV1, nblock, ri, mcu, nacross, nmcu; + Huffman *dcht, *acht; + int block, t, diff, *qt; + uchar *ss; + Rawimage *image; + int Td[3], Ta[3], H[3], V[3], DC[3]; + int ***data, *zz; + + ss = h->ss; + Ns = ss[0]; + if((Ns!=3 && Ns!=1) || Ns!=h->Nf) + jpgerror(h, "ReadJPG: can't handle scan not 3 components"); + + image = jpgmalloc(h, sizeof(Rawimage), 1); + h->image = image; + image->r = Rect(0, 0, h->X, h->Y); + image->cmap = nil; + image->cmaplen = 0; + image->chanlen = h->X*h->Y; + image->fields = 0; + image->gifflags = 0; + image->gifdelay = 0; + image->giftrindex = 0; + if(Ns == 3) + image->chandesc = colorspace; + else + image->chandesc = CY; + image->nchans = h->Nf; + for(k=0; k<h->Nf; k++) + image->chans[k] = jpgmalloc(h, h->X*h->Y, 0); + + /* compute maximum H and V */ + Hmax = 0; + Vmax = 0; + for(comp=0; comp<Ns; comp++){ + if(h->comp[comp].H > Hmax) + Hmax = h->comp[comp].H; + if(h->comp[comp].V > Vmax) + Vmax = h->comp[comp].V; + } + + /* initialize data structures */ + allHV1 = 1; + data = h->data; + for(comp=0; comp<Ns; comp++){ + /* JPEG requires scan components to be in same order as in frame, */ + /* so if both have 3 we know scan is Y Cb Cr and there's no need to */ + /* reorder */ + nibbles(ss[2+2*comp], &Td[comp], &Ta[comp]); + H[comp] = h->comp[comp].H; + V[comp] = h->comp[comp].V; + nblock = H[comp]*V[comp]; + if(nblock != 1) + allHV1 = 0; + data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0); + h->ndata[comp] = nblock; + DC[comp] = 0; + for(m=0; m<nblock; m++) + data[comp][m] = jpgmalloc(h, 8*8*sizeof(int), 0); + } + + ri = h->ri; + + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nacross = ((h->X+(8*Hmax-1))/(8*Hmax)); + nmcu = ((h->Y+(8*Vmax-1))/(8*Vmax))*nacross; + for(mcu=0; mcu<nmcu; ){ + for(comp=0; comp<Ns; comp++){ + dcht = &h->dcht[Td[comp]]; + acht = &h->acht[Ta[comp]]; + qt = h->qt[h->comp[comp].Tq]; + + for(block=0; block<H[comp]*V[comp]; block++){ + /* F-22 */ + t = decode(h, dcht); + diff = receive(h, t); + DC[comp] += diff; + + /* F-23 */ + zz = data[comp][block]; + memset(zz, 0, 8*8*sizeof(int)); + zz[0] = qt[0]*DC[comp]; + k = 1; + + for(;;){ + t = decode(h, acht); + if((t&0x0F) == 0){ + if((t&0xF0) != 0xF0) + break; + k += 16; + }else{ + k += t>>4; + z = receive(h, t&0xF); + zz[zig[k]] = z*qt[k]; + if(k == 63) + break; + k++; + } + } + + idct(zz); + } + } + + /* rotate colors to RGB and assign to bytes */ + if(Ns == 1) /* very easy */ + colormap1(h, colorspace, image, data[0][0], mcu, nacross); + else if(allHV1) /* fairly easy */ + colormapall1(h, colorspace, image, data[0][0], data[1][0], data[2][0], mcu, nacross); + else /* miserable general case */ + colormap(h, colorspace, image, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V); + /* process restart marker, if present */ + mcu++; + if(ri>0 && mcu<nmcu && mcu%ri==0){ + restart(h, mcu); + for(comp=0; comp<Ns; comp++) + DC[comp] = 0; + } + } + return image; +} + +static +void +restart(Header *h, int mcu) +{ + int rest, rst, nskip; + + rest = mcu/h->ri-1; + nskip = 0; + do{ + do{ + rst = nextbyte(h, 1); + nskip++; + }while(rst>=0 && rst!=0xFF); + if(rst == 0xFF){ + rst = nextbyte(h, 1); + nskip++; + } + }while(rst>=0 && (rst&~7)!=RST); + if(nskip != 2) + sprint(h->err, "ReadJPG: skipped %d bytes at restart %d\n", nskip-2, rest); + if(rst < 0) + jpgerror(h, readerr); + if((rst&7) != (rest&7)) + jpgerror(h, "ReadJPG: expected RST%d got %d", rest&7, rst&7); + h->cnt = 0; + h->sr = 0; +} + +static +Rawimage* +progressiveIDCT(Header *h, int colorspace) +{ + int k, m, comp, block, Nf, bn; + int allHV1, nblock, mcu, nmcu; + int H[3], V[3], blockno[3]; + int *dccoeff, **accoeff; + int ***data, *zz; + + Nf = h->Nf; + allHV1 = 1; + data = h->data; + + for(comp=0; comp<Nf; comp++){ + H[comp] = h->comp[comp].H; + V[comp] = h->comp[comp].V; + nblock = h->nblock[comp]; + if(nblock != 1) + allHV1 = 0; + h->ndata[comp] = nblock; + data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0); + for(m=0; m<nblock; m++) + data[comp][m] = jpgmalloc(h, 8*8*sizeof(int), 0); + } + + memset(blockno, 0, sizeof blockno); + nmcu = h->nacross*h->ndown; + for(mcu=0; mcu<nmcu; mcu++){ + for(comp=0; comp<Nf; comp++){ + dccoeff = h->dccoeff[comp]; + accoeff = h->accoeff[comp]; + bn = blockno[comp]; + for(block=0; block<h->nblock[comp]; block++){ + zz = data[comp][block]; + memset(zz, 0, 8*8*sizeof(int)); + zz[0] = dccoeff[bn]; + + for(k=1; k<64; k++) + zz[zig[k]] = accoeff[bn][k]; + + idct(zz); + bn++; + } + blockno[comp] = bn; + } + + /* rotate colors to RGB and assign to bytes */ + if(Nf == 1) /* very easy */ + colormap1(h, colorspace, h->image, data[0][0], mcu, h->nacross); + else if(allHV1) /* fairly easy */ + colormapall1(h, colorspace, h->image, data[0][0], data[1][0], data[2][0], mcu, h->nacross); + else /* miserable general case */ + colormap(h, colorspace, h->image, data[0], data[1], data[2], mcu, h->nacross, h->Hmax, h->Vmax, H, V); + } + + return h->image; +} + +static +void +progressiveinit(Header *h, int colorspace) +{ + int Nf, Ns, j, k, nmcu, comp; + uchar *ss; + Rawimage *image; + + ss = h->ss; + Ns = ss[0]; + Nf = h->Nf; + if((Ns!=3 && Ns!=1) || Ns!=Nf) + jpgerror(h, "ReadJPG: image must have 1 or 3 components"); + + image = jpgmalloc(h, sizeof(Rawimage), 1); + h->image = image; + image->r = Rect(0, 0, h->X, h->Y); + image->cmap = nil; + image->cmaplen = 0; + image->chanlen = h->X*h->Y; + image->fields = 0; + image->gifflags = 0; + image->gifdelay = 0; + image->giftrindex = 0; + if(Nf == 3) + image->chandesc = colorspace; + else + image->chandesc = CY; + image->nchans = h->Nf; + for(k=0; k<Nf; k++){ + image->chans[k] = jpgmalloc(h, h->X*h->Y, 0); + h->nblock[k] = h->comp[k].H*h->comp[k].V; + } + + /* compute maximum H and V */ + h->Hmax = 0; + h->Vmax = 0; + for(comp=0; comp<Nf; comp++){ + if(h->comp[comp].H > h->Hmax) + h->Hmax = h->comp[comp].H; + if(h->comp[comp].V > h->Vmax) + h->Vmax = h->comp[comp].V; + } + h->nacross = ((h->X+(8*h->Hmax-1))/(8*h->Hmax)); + h->ndown = ((h->Y+(8*h->Vmax-1))/(8*h->Vmax)); + nmcu = h->nacross*h->ndown; + + for(k=0; k<Nf; k++){ + h->dccoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int), 1); + h->accoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int*), 1); + h->naccoeff[k] = h->nblock[k]*nmcu; + for(j=0; j<h->nblock[k]*nmcu; j++) + h->accoeff[k][j] = jpgmalloc(h, 64*sizeof(int), 1); + } + +} + +static +void +progressivedc(Header *h, int comp, int Ah, int Al) +{ + int Ns, z, ri, mcu, nmcu; + int block, t, diff, qt, *dc, bn; + Huffman *dcht; + uchar *ss; + int Td[3], DC[3], blockno[3]; + + ss= h->ss; + Ns = ss[0]; + if(Ns!=h->Nf) + jpgerror(h, "ReadJPG: can't handle progressive with Nf!=Ns in DC scan"); + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + for(comp=0; comp<Ns; comp++){ + /* + * JPEG requires scan components to be in same order as in frame, + * so if both have 3 we know scan is Y Cb Cr and there's no need to + * reorder + */ + nibbles(ss[2+2*comp], &Td[comp], &z); /* z is ignored */ + DC[comp] = 0; + } + + ri = h->ri; + + nmcu = h->nacross*h->ndown; + memset(blockno, 0, sizeof blockno); + for(mcu=0; mcu<nmcu; ){ + for(comp=0; comp<Ns; comp++){ + dcht = &h->dcht[Td[comp]]; + qt = h->qt[h->comp[comp].Tq][0]; + dc = h->dccoeff[comp]; + bn = blockno[comp]; + + for(block=0; block<h->nblock[comp]; block++){ + if(Ah == 0){ + t = decode(h, dcht); + diff = receive(h, t); + DC[comp] += diff; + dc[bn] = qt*DC[comp]<<Al; + }else + dc[bn] |= qt*receivebit(h)<<Al; + bn++; + } + blockno[comp] = bn; + } + + /* process restart marker, if present */ + mcu++; + if(ri>0 && mcu<nmcu && mcu%ri==0){ + restart(h, mcu); + for(comp=0; comp<Ns; comp++) + DC[comp] = 0; + } + } +} + +static +void +progressiveac(Header *h, int comp, int Al) +{ + int Ns, Ss, Se, z, k, eobrun, x, y, nver, tmcu, blockno, *acc, rs; + int ri, mcu, nacross, ndown, nmcu, nhor; + Huffman *acht; + int *qt, rrrr, ssss, q; + uchar *ss; + int Ta, H, V; + + ss = h->ss; + Ns = ss[0]; + if(Ns != 1) + jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan"); + Ss = ss[1+2]; + Se = ss[2+2]; + H = h->comp[comp].H; + V = h->comp[comp].V; + + nacross = h->nacross*H; + ndown = h->ndown*V; + q = 8*h->Hmax/H; + nhor = (h->X+q-1)/q; + q = 8*h->Vmax/V; + nver = (h->Y+q-1)/q; + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nibbles(ss[1+1], &z, &Ta); /* z is thrown away */ + + ri = h->ri; + + eobrun = 0; + acht = &h->acht[Ta]; + qt = h->qt[h->comp[comp].Tq]; + nmcu = nacross*ndown; + mcu = 0; + for(y=0; y<nver; y++){ + for(x=0; x<nhor; x++){ + /* Figure G-3 */ + if(eobrun > 0){ + --eobrun; + continue; + } + + /* arrange blockno to be in same sequence as original scan calculation. */ + tmcu = x/H + (nacross/H)*(y/V); + blockno = tmcu*H*V + H*(y%V) + x%H; + acc = h->accoeff[comp][blockno]; + k = Ss; + for(;;){ + rs = decode(h, acht); + /* XXX remove rrrr ssss as in baselinescan */ + nibbles(rs, &rrrr, &ssss); + if(ssss == 0){ + if(rrrr < 15){ + eobrun = 0; + if(rrrr > 0) + eobrun = receiveEOB(h, rrrr)-1; + break; + } + k += 16; + }else{ + k += rrrr; + z = receive(h, ssss); + acc[k] = z*qt[k]<<Al; + if(k == Se) + break; + k++; + } + } + } + + /* process restart marker, if present */ + mcu++; + if(ri>0 && mcu<nmcu && mcu%ri==0){ + restart(h, mcu); + eobrun = 0; + } + } +} + +static +void +increment(Header *h, int acc[], int k, int Pt) +{ + if(acc[k] == 0) + return; + if(receivebit(h) != 0) + if(acc[k] < 0) + acc[k] -= Pt; + else + acc[k] += Pt; +} + +static +void +progressiveacinc(Header *h, int comp, int Al) +{ + int Ns, i, z, k, Ss, Se, Ta, **ac, H, V; + int ri, mcu, nacross, ndown, nhor, nver, eobrun, nzeros, pending, x, y, tmcu, blockno, q, nmcu; + Huffman *acht; + int *qt, rrrr, ssss, *acc, rs; + uchar *ss; + + ss = h->ss; + Ns = ss[0]; + if(Ns != 1) + jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan"); + Ss = ss[1+2]; + Se = ss[2+2]; + H = h->comp[comp].H; + V = h->comp[comp].V; + + nacross = h->nacross*H; + ndown = h->ndown*V; + q = 8*h->Hmax/H; + nhor = (h->X+q-1)/q; + q = 8*h->Vmax/V; + nver = (h->Y+q-1)/q; + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nibbles(ss[1+1], &z, &Ta); /* z is thrown away */ + ri = h->ri; + + eobrun = 0; + ac = h->accoeff[comp]; + acht = &h->acht[Ta]; + qt = h->qt[h->comp[comp].Tq]; + nmcu = nacross*ndown; + mcu = 0; + pending = 0; + nzeros = -1; + for(y=0; y<nver; y++){ + for(x=0; x<nhor; x++){ + /* Figure G-7 */ + + /* arrange blockno to be in same sequence as original scan calculation. */ + tmcu = x/H + (nacross/H)*(y/V); + blockno = tmcu*H*V + H*(y%V) + x%H; + acc = ac[blockno]; + if(eobrun > 0){ + if(nzeros > 0) + jpgerror(h, "ReadJPG: zeros pending at block start"); + for(k=Ss; k<=Se; k++) + increment(h, acc, k, qt[k]<<Al); + --eobrun; + continue; + } + + for(k=Ss; k<=Se; ){ + if(nzeros >= 0){ + if(acc[k] != 0) + increment(h, acc, k, qt[k]<<Al); + else if(nzeros-- == 0) + acc[k] = pending; + k++; + continue; + } + rs = decode(h, acht); + nibbles(rs, &rrrr, &ssss); + if(ssss == 0){ + if(rrrr < 15){ + eobrun = 0; + if(rrrr > 0) + eobrun = receiveEOB(h, rrrr)-1; + while(k <= Se){ + increment(h, acc, k, qt[k]<<Al); + k++; + } + break; + } + for(i=0; i<16; k++){ + increment(h, acc, k, qt[k]<<Al); + if(acc[k] == 0) + i++; + } + continue; + }else if(ssss != 1) + jpgerror(h, "ReadJPG: ssss!=1 in progressive increment"); + nzeros = rrrr; + pending = receivebit(h); + if(pending == 0) + pending = -1; + pending *= qt[k]<<Al; + } + } + + /* process restart marker, if present */ + mcu++; + if(ri>0 && mcu<nmcu && mcu%ri==0){ + restart(h, mcu); + eobrun = 0; + nzeros = -1; + } + } +} + +static +void +progressivescan(Header *h, int colorspace) +{ + uchar *ss; + int Ns, Ss, Ah, Al, c, comp, i; + + if(h->dccoeff[0] == nil) + progressiveinit(h, colorspace); + + ss = h->ss; + Ns = ss[0]; + Ss = ss[1+2*Ns]; + nibbles(ss[3+2*Ns], &Ah, &Al); + c = ss[1]; + comp = -1; + for(i=0; i<h->Nf; i++) + if(h->comp[i].C == c) + comp = i; + if(comp == -1) + jpgerror(h, "ReadJPG: bad component index in scan header"); + + if(Ss == 0){ + progressivedc(h, comp, Ah, Al); + return; + } + if(Ah == 0){ + progressiveac(h, comp, Al); + return; + } + progressiveacinc(h, comp, Al); +} + +enum { + c1 = 2871, /* 1.402 * 2048 */ + c2 = 705, /* 0.34414 * 2048 */ + c3 = 1463, /* 0.71414 * 2048 */ + c4 = 3629, /* 1.772 * 2048 */ +}; + +static +void +colormap1(Header *h, int colorspace, Rawimage *image, int data[8*8], int mcu, int nacross) +{ + uchar *pic; + int x, y, dx, dy, minx, miny; + int r, k, pici; + + USED(colorspace); + pic = image->chans[0]; + minx = 8*(mcu%nacross); + dx = 8; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*(mcu/nacross); + dy = 8; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + k = 0; + for(y=0; y<dy; y++){ + for(x=0; x<dx; x++){ + r = clamp[(data[k+x]+128)+CLAMPOFF]; + pic[pici+x] = r; + } + pici += h->X; + k += 8; + } +} + +static +void +colormapall1(Header *h, int colorspace, Rawimage *image, int data0[8*8], int data1[8*8], int data2[8*8], int mcu, int nacross) +{ + uchar *rpic, *gpic, *bpic, *rp, *gp, *bp; + int *p0, *p1, *p2; + int x, y, dx, dy, minx, miny; + int r, g, b, k, pici; + int Y, Cr, Cb; + + rpic = image->chans[0]; + gpic = image->chans[1]; + bpic = image->chans[2]; + minx = 8*(mcu%nacross); + dx = 8; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*(mcu/nacross); + dy = 8; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + k = 0; + for(y=0; y<dy; y++){ + p0 = data0+k; + p1 = data1+k; + p2 = data2+k; + rp = rpic+pici; + gp = gpic+pici; + bp = bpic+pici; + if(colorspace == CYCbCr) + for(x=0; x<dx; x++){ + *rp++ = clamp[*p0++ + 128 + CLAMPOFF]; + *gp++ = clamp[*p1++ + 128 + CLAMPOFF]; + *bp++ = clamp[*p2++ + 128 + CLAMPOFF]; + } + else + for(x=0; x<dx; x++){ + Y = (*p0++ + 128) << 11; + Cb = *p1++; + Cr = *p2++; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + *rp++ = clamp[(r>>11)+CLAMPOFF]; + *gp++ = clamp[(g>>11)+CLAMPOFF]; + *bp++ = clamp[(b>>11)+CLAMPOFF]; + } + pici += h->X; + k += 8; + } +} + +static +void +colormap(Header *h, int colorspace, Rawimage *image, int *data0[8*8], int *data1[8*8], int *data2[8*8], int mcu, int nacross, int Hmax, int Vmax, int *H, int *V) +{ + uchar *rpic, *gpic, *bpic; + int x, y, dx, dy, minx, miny; + int r, g, b, pici, H0, H1, H2; + int t, b0, b1, b2, y0, y1, y2, x0, x1, x2; + int Y, Cr, Cb; + + rpic = image->chans[0]; + gpic = image->chans[1]; + bpic = image->chans[2]; + minx = 8*Hmax*(mcu%nacross); + dx = 8*Hmax; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*Vmax*(mcu/nacross); + dy = 8*Vmax; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + H0 = H[0]; + H1 = H[1]; + H2 = H[2]; + for(y=0; y<dy; y++){ + t = y*V[0]; + b0 = H0*(t/(8*Vmax)); + y0 = 8*((t/Vmax)&7); + t = y*V[1]; + b1 = H1*(t/(8*Vmax)); + y1 = 8*((t/Vmax)&7); + t = y*V[2]; + b2 = H2*(t/(8*Vmax)); + y2 = 8*((t/Vmax)&7); + x0 = 0; + x1 = 0; + x2 = 0; + for(x=0; x<dx; x++){ + if(colorspace == CYCbCr){ + rpic[pici+x] = clamp[data0[b0][y0+x0++*H0/Hmax] + 128 + CLAMPOFF]; + gpic[pici+x] = clamp[data1[b1][y1+x1++*H1/Hmax] + 128 + CLAMPOFF]; + bpic[pici+x] = clamp[data2[b2][y2+x2++*H2/Hmax] + 128 + CLAMPOFF]; + }else{ + Y = (data0[b0][y0+x0++*H0/Hmax]+128)<<11; + Cb = data1[b1][y1+x1++*H1/Hmax]; + Cr = data2[b2][y2+x2++*H2/Hmax]; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + rpic[pici+x] = clamp[(r>>11)+CLAMPOFF]; + gpic[pici+x] = clamp[(g>>11)+CLAMPOFF]; + bpic[pici+x] = clamp[(b>>11)+CLAMPOFF]; + } + if(x0*H0/Hmax >= 8){ + x0 = 0; + b0++; + } + if(x1*H1/Hmax >= 8){ + x1 = 0; + b1++; + } + if(x2*H2/Hmax >= 8){ + x2 = 0; + b2++; + } + } + pici += h->X; + } +} + +/* + * decode next 8-bit value from entropy-coded input. chart F-26 + */ +static +int +decode(Header *h, Huffman *t) +{ + int code, v, cnt, m, sr, i; + int *maxcode; + static int badcode; + + maxcode = t->maxcode; + if(h->cnt < 8) + nextbyte(h, 0); + /* fast lookup */ + code = (h->sr>>(h->cnt-8))&0xFF; + v = t->value[code]; + if(v >= 0){ + h->cnt -= t->shift[code]; + return v; + } + + h->cnt -= 8; + if(h->cnt == 0) + nextbyte(h, 0); + h->cnt--; + cnt = h->cnt; + m = 1<<cnt; + sr = h->sr; + code <<= 1; + i = 9; + for(;;i++){ + if(sr & m) + code |= 1; + if(code <= maxcode[i]) + break; + code <<= 1; + m >>= 1; + if(m == 0){ + sr = nextbyte(h, 0); + m = 0x80; + cnt = 8; + } + cnt--; + } + if(i >= 17){ + if(badcode == 0) + fprint(2, "badly encoded %dx%d JPEG file; ignoring bad value\n", h->X, h->Y); + badcode = 1; + i = 0; + } + h->cnt = cnt; + return t->val[t->valptr[i]+(code-t->mincode[i])]; +} + +/* + * load next byte of input + */ +static +int +nextbyte(Header *h, int marker) +{ + int b, b2; + + if(h->peek >= 0){ + b = h->peek; + h->peek = -1; + }else{ + b = Bgetc(h->fd); + if(b == Beof) + jpgerror(h, "truncated file"); + b &= 0xFF; + } + + if(b == 0xFF){ + if(marker) + return b; + b2 = Bgetc(h->fd); + if(b2 != 0){ + if(b2 == Beof) + jpgerror(h, "truncated file"); + b2 &= 0xFF; + if(b2 == DNL) + jpgerror(h, "ReadJPG: DNL marker unimplemented"); + /* decoder is reading into marker; satisfy it and restore state */ + Bungetc(h->fd); + h->peek = b; + } + } + h->cnt += 8; + h->sr = (h->sr<<8) | b; + return b; +} + +/* + * return next s bits of input, MSB first, and level shift it + */ +static +int +receive(Header *h, int s) +{ + int v, m; + + while(h->cnt < s) + nextbyte(h, 0); + h->cnt -= s; + v = h->sr >> h->cnt; + m = (1<<s); + v &= m-1; + /* level shift */ + if(v < (m>>1)) + v += ~(m-1)+1; + return v; +} + +/* + * return next s bits of input, decode as EOB + */ +static +int +receiveEOB(Header *h, int s) +{ + int v, m; + + while(h->cnt < s) + nextbyte(h, 0); + h->cnt -= s; + v = h->sr >> h->cnt; + m = (1<<s); + v &= m-1; + /* level shift */ + v += m; + return v; +} + +/* + * return next bit of input + */ +static +int +receivebit(Header *h) +{ + if(h->cnt < 1) + nextbyte(h, 0); + h->cnt--; + return (h->sr >> h->cnt) & 1; +} + +/* + * Scaled integer implementation. + * inverse two dimensional DCT, Chen-Wang algorithm + * (IEEE ASSP-32, pp. 803-816, Aug. 1984) + * 32-bit integer arithmetic (8 bit coefficients) + * 11 mults, 29 adds per DCT + * + * coefficients extended to 12 bit for IEEE1180-1990 compliance + */ + +enum { + W1 = 2841, /* 2048*sqrt(2)*cos(1*pi/16)*/ + W2 = 2676, /* 2048*sqrt(2)*cos(2*pi/16)*/ + W3 = 2408, /* 2048*sqrt(2)*cos(3*pi/16)*/ + W5 = 1609, /* 2048*sqrt(2)*cos(5*pi/16)*/ + W6 = 1108, /* 2048*sqrt(2)*cos(6*pi/16)*/ + W7 = 565, /* 2048*sqrt(2)*cos(7*pi/16)*/ + + W1pW7 = 3406, /* W1+W7*/ + W1mW7 = 2276, /* W1-W7*/ + W3pW5 = 4017, /* W3+W5*/ + W3mW5 = 799, /* W3-W5*/ + W2pW6 = 3784, /* W2+W6*/ + W2mW6 = 1567, /* W2-W6*/ + + R2 = 181 /* 256/sqrt(2)*/ +}; + +static +void +idct(int b[8*8]) +{ + int x, y, eighty, v; + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + int *p; + + /* transform horizontally*/ + for(y=0; y<8; y++){ + eighty = y<<3; + /* if all non-DC components are zero, just propagate the DC term*/ + p = b+eighty; + if(p[1]==0) + if(p[2]==0 && p[3]==0) + if(p[4]==0 && p[5]==0) + if(p[6]==0 && p[7]==0){ + v = p[0]<<3; + p[0] = v; + p[1] = v; + p[2] = v; + p[3] = v; + p[4] = v; + p[5] = v; + p[6] = v; + p[7] = v; + continue; + } + /* prescale*/ + x0 = (p[0]<<11)+128; + x1 = p[4]<<11; + x2 = p[6]; + x3 = p[2]; + x4 = p[1]; + x5 = p[7]; + x6 = p[5]; + x7 = p[3]; + /* first stage*/ + x8 = W7*(x4+x5); + x4 = x8 + W1mW7*x4; + x5 = x8 - W1pW7*x5; + x8 = W3*(x6+x7); + x6 = x8 - W3mW5*x6; + x7 = x8 - W3pW5*x7; + /* second stage*/ + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2); + x2 = x1 - W2pW6*x2; + x3 = x1 + W2mW6*x3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + /* third stage*/ + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + /* fourth stage*/ + p[0] = (x7+x1)>>8; + p[1] = (x3+x2)>>8; + p[2] = (x0+x4)>>8; + p[3] = (x8+x6)>>8; + p[4] = (x8-x6)>>8; + p[5] = (x0-x4)>>8; + p[6] = (x3-x2)>>8; + p[7] = (x7-x1)>>8; + } + /* transform vertically*/ + for(x=0; x<8; x++){ + /* if all non-DC components are zero, just propagate the DC term*/ + p = b+x; + if(p[8*1]==0) + if(p[8*2]==0 && p[8*3]==0) + if(p[8*4]==0 && p[8*5]==0) + if(p[8*6]==0 && p[8*7]==0){ + v = (p[8*0]+32)>>6; + p[8*0] = v; + p[8*1] = v; + p[8*2] = v; + p[8*3] = v; + p[8*4] = v; + p[8*5] = v; + p[8*6] = v; + p[8*7] = v; + continue; + } + /* prescale*/ + x0 = (p[8*0]<<8)+8192; + x1 = p[8*4]<<8; + x2 = p[8*6]; + x3 = p[8*2]; + x4 = p[8*1]; + x5 = p[8*7]; + x6 = p[8*5]; + x7 = p[8*3]; + /* first stage*/ + x8 = W7*(x4+x5) + 4; + x4 = (x8+W1mW7*x4)>>3; + x5 = (x8-W1pW7*x5)>>3; + x8 = W3*(x6+x7) + 4; + x6 = (x8-W3mW5*x6)>>3; + x7 = (x8-W3pW5*x7)>>3; + /* second stage*/ + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2) + 4; + x2 = (x1-W2pW6*x2)>>3; + x3 = (x1+W2mW6*x3)>>3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + /* third stage*/ + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + /* fourth stage*/ + p[8*0] = (x7+x1)>>14; + p[8*1] = (x3+x2)>>14; + p[8*2] = (x0+x4)>>14; + p[8*3] = (x8+x6)>>14; + p[8*4] = (x8-x6)>>14; + p[8*5] = (x0-x4)>>14; + p[8*6] = (x3-x2)>>14; + p[8*7] = (x7-x1)>>14; + } +} diff --git a/src/cmd/jpg/readpng.c b/src/cmd/jpg/readpng.c new file mode 100644 index 00000000..1cb85946 --- /dev/null +++ b/src/cmd/jpg/readpng.c @@ -0,0 +1,334 @@ +// work in progress... this version only good enough to read +// non-interleaved, 24bit RGB images + +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <bio.h> +#include <flate.h> +#include <draw.h> +#include "imagefile.h" + +int debug; + +enum{ IDATSIZE=1000000, + /* filtering algorithms, supposedly increase compression */ + FilterNone = 0, /* new[x][y] = buf[x][y] */ + FilterSub = 1, /* new[x][y] = buf[x][y] + new[x-1][y] */ + FilterUp = 2, /* new[x][y] = buf[x][y] + new[x][y-1] */ + FilterAvg = 3, /* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */ + FilterPaeth= 4, /* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */ + FilterLast = 5, + PropertyBit = 1<<5, +}; + +typedef struct ZlibR{ + Biobuf *bi; + uchar *buf; + uchar *b; // next byte to decompress + uchar *e; // past end of buf +} ZlibR; + +typedef struct ZlibW{ + uchar *r, *g, *b; // Rawimage channels + int chan; // next channel to write + int col; // column index of current pixel + // -1 = one-byte pseudo-column for filter spec + int row; // row index of current pixel + int ncol, nrow; // image width, height + int filter; // algorithm for current scanline +} ZlibW; + +static ulong *crctab; +static uchar PNGmagic[] = {137,80,78,71,13,10,26,10}; +static char memerr[] = "ReadPNG: malloc failed: %r"; + +static ulong +get4(uchar *a) +{ + return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; +} + +static +void +pnginit(void) +{ + static int inited; + + if(inited) + return; + inited = 1; + crctab = mkcrctab(0xedb88320); + if(crctab == nil) + sysfatal("mkcrctab error"); + inflateinit(); +} + +static +void* +pngmalloc(ulong n, int clear) +{ + void *p; + + p = malloc(n); + if(p == nil) + sysfatal(memerr); + if(clear) + memset(p, 0, n); + return p; +} + +static int +getchunk(Biobuf *b, char *type, uchar *d, int m) +{ + uchar buf[8]; + ulong crc = 0, crc2; + int n, nr; + + if(Bread(b, buf, 8) != 8) + return -1; + n = get4(buf); + memmove(type, buf+4, 4); + type[4] = 0; + if(n > m) + sysfatal("getchunk needed %d, had %d", n, m); + nr = Bread(b, d, n); + if(nr != n) + sysfatal("getchunk read %d, expected %d", nr, n); + crc = blockcrc(crctab, crc, type, 4); + crc = blockcrc(crctab, crc, d, n); + if(Bread(b, buf, 4) != 4) + sysfatal("getchunk tlr failed"); + crc2 = get4(buf); + if(crc != crc2) + sysfatal("getchunk crc failed"); + return n; +} + +static int +zread(void *va) +{ + ZlibR *z = va; + char type[5]; + int n; + + if(z->b >= z->e){ +refill_buffer: + z->b = z->buf; + n = getchunk(z->bi, type, z->b, IDATSIZE); + if(n < 0 || strcmp(type, "IEND") == 0) + return -1; + z->e = z->b + n; + if(type[0] & PropertyBit) + goto refill_buffer; /* skip auxiliary chunks for now */ + if(strcmp(type,"IDAT") != 0) + sysfatal("unrecognized mandatory chunk %s", type); + } + return *z->b++; +} + +static uchar +paeth(uchar a, uchar b, uchar c) +{ + int p, pa, pb, pc; + + p = (int)a + (int)b - (int)c; + pa = abs(p - (int)a); + pb = abs(p - (int)b); + pc = abs(p - (int)c); + + if(pa <= pb && pa <= pc) + return a; + else if(pb <= pc) + return b; + return c; +} + +static void +unfilter(int alg, uchar *buf, uchar *ebuf, int up) +{ + switch(alg){ + case FilterSub: + while (++buf < ebuf) + *buf += buf[-1]; + break; + case FilterUp: + if (up != 0) + do + *buf += buf[up]; + while (++buf < ebuf); + break; + case FilterAvg: + if (up == 0) + while (++buf < ebuf) + *buf += buf[-1]/2; + else{ + *buf += buf[up]/2; + while (++buf < ebuf) + *buf += (buf[-1]+buf[up])/2; + } + break; + case FilterPaeth: + if (up == 0) + while (++buf < ebuf) + *buf += buf[-1]; + else{ + *buf += paeth(0, buf[up], 0); + while (++buf < ebuf) + *buf += paeth(buf[-1], buf[up], buf[up-1]); + } + break; + } +} + +static int +zwrite(void *va, void *vb, int n) +{ + ZlibW *z = va; + uchar *buf = vb; + int i, up; + for(i=0; i<n; i++){ + if(z->col == -1){ + // set filter byte + z->filter = *buf++; + if (z->filter >= FilterLast) + sysfatal("unknown filter algorithm %d for row %d", z->row, z->filter); + z->col++; + continue; + } + switch(z->chan){ + case 0: + *z->r++ = *buf++; + z->chan = 1; + break; + case 1: + *z->g++ = *buf++; + z->chan = 2; + break; + case 2: + *z->b++ = *buf++; + z->chan = 0; + z->col++; + if(z->col == z->ncol){ + if (z->filter){ + if(z->row == 0) + up = 0; + else + up = -z->ncol; + unfilter(z->filter, z->r - z->col, z->r, up); + unfilter(z->filter, z->g - z->col, z->g, up); + unfilter(z->filter, z->b - z->col, z->b, up); + } + z->col = -1; + z->row++; + if((z->row >= z->nrow) && (i < n-1) ) + sysfatal("header said %d rows; data goes further", z->nrow); + } + break; + } + } + return n; +} + +static Rawimage* +readslave(Biobuf *b) +{ + ZlibR zr; + ZlibW zw; + Rawimage *image; + char type[5]; + uchar *buf, *h; + int k, n, nrow, ncol, err; + + buf = pngmalloc(IDATSIZE, 0); + Bread(b, buf, sizeof PNGmagic); + if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0) + sysfatal("bad PNGmagic"); + + n = getchunk(b, type, buf, IDATSIZE); + if(n < 13 || strcmp(type,"IHDR") != 0) + sysfatal("missing IHDR chunk"); + h = buf; + ncol = get4(h); h += 4; + nrow = get4(h); h += 4; + if(ncol <= 0 || nrow <= 0) + sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol); + if(debug) + fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol); + if(*h++ != 8) + sysfatal("only 24 bit per pixel supported for now [%d]", h[-1]); + if(*h++ != 2) + sysfatal("only rgb supported for now [%d]", h[-1]); + if(*h++ != 0) + sysfatal("only deflate supported for now [%d]", h[-1]); + if(*h++ != FilterNone) + sysfatal("only FilterNone supported for now [%d]", h[-1]); + if(*h != 0) + sysfatal("only non-interlaced supported for now [%d]", h[-1]); + + image = pngmalloc(sizeof(Rawimage), 1); + image->r = Rect(0, 0, ncol, nrow); + image->cmap = nil; + image->cmaplen = 0; + image->chanlen = ncol*nrow; + image->fields = 0; + image->gifflags = 0; + image->gifdelay = 0; + image->giftrindex = 0; + image->chandesc = CRGB; + image->nchans = 3; + for(k=0; k<3; k++) + image->chans[k] = pngmalloc(ncol*nrow, 0); + zr.bi = b; + zr.buf = buf; + zr.b = zr.e = buf + IDATSIZE; + zw.r = image->chans[0]; + zw.g = image->chans[1]; + zw.b = image->chans[2]; + zw.chan = 0; + zw.col = -1; + zw.row = 0; + zw.ncol = ncol; + zw.nrow = nrow; + err = inflatezlib(&zw, zwrite, &zr, zread); + if(err) + sysfatal("inflatezlib %s\n", flateerr(err)); + free(buf); + return image; +} + +Rawimage** +Breadpng(Biobuf *b, int colorspace) +{ + Rawimage *r, **array; + char buf[ERRMAX]; + + buf[0] = '\0'; + if(colorspace != CRGB){ + errstr(buf, sizeof buf); /* throw it away */ + werrstr("ReadPNG: unknown color space %d", colorspace); + return nil; + } + pnginit(); + array = malloc(2*sizeof(*array)); + if(array==nil) + return nil; + errstr(buf, sizeof buf); /* throw it away */ + r = readslave(b); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage** +readpng(int fd, int colorspace) +{ + Rawimage** a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadpng(&b, colorspace); + Bterm(&b); + return a; +} diff --git a/src/cmd/jpg/readppm.c b/src/cmd/jpg/readppm.c new file mode 100644 index 00000000..28b7f4ea --- /dev/null +++ b/src/cmd/jpg/readppm.c @@ -0,0 +1,238 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <ctype.h> +#include "imagefile.h" + +Rawimage *readppm(Biobuf*, Rawimage*); + +/* + * fetch a non-comment character. + */ +static +int +Bgetch(Biobuf *b) +{ + int c; + + for(;;) { + c = Bgetc(b); + if(c == '#') { + while((c = Bgetc(b)) != Beof && c != '\n') + ; + } + return c; + } +} + +/* + * fetch a nonnegative decimal integer. + */ +static +int +Bgetint(Biobuf *b) +{ + int c; + int i; + + while((c = Bgetch(b)) != Beof && !isdigit(c)) + ; + if(c == Beof) + return -1; + + i = 0; + do { + i = i*10 + (c-'0'); + } while((c = Bgetch(b)) != Beof && isdigit(c)); + + return i; +} + +static +int +Bgetdecimalbit(Biobuf *b) +{ + int c; + while((c = Bgetch(b)) != Beof && c != '0' && c != '1') + ; + if(c == Beof) + return -1; + return c == '1'; +} + +static int bitc, nbit; + +static +int +Bgetbit(Biobuf *b) +{ + if(nbit == 0) { + nbit = 8; + bitc = Bgetc(b); + if(bitc == -1) + return -1; + } + nbit--; + return (bitc >> (nbit-1)) & 0x1; +} + +static +void +Bflushbit(Biobuf *b) +{ + USED(b); + nbit = 0; +} + + +Rawimage** +readpixmap(int fd, int colorspace) +{ + Rawimage **array, *a; + Biobuf b; + char buf[ERRMAX]; + int i; + char *e; + + USED(colorspace); + if(Binit(&b, fd, OREAD) < 0) + return nil; + + werrstr(""); + e = "out of memory"; + if((array = malloc(sizeof *array)) == nil) + goto Error; + if((array[0] = malloc(sizeof *array[0])) == nil) + goto Error; + memset(array[0], 0, sizeof *array[0]); + + for(i=0; i<3; i++) + array[0]->chans[i] = nil; + + e = "bad file format"; + switch(Bgetc(&b)) { + case 'P': + Bungetc(&b); + a = readppm(&b, array[0]); + break; + default: + a = nil; + break; + } + if(a == nil) + goto Error; + array[0] = a; + + return array; + +Error: + if(array) + free(array[0]); + free(array); + + errstr(buf, sizeof buf); + if(buf[0] == 0) + strcpy(buf, e); + errstr(buf, sizeof buf); + + return nil; +} + +typedef struct Pix Pix; +struct Pix { + char magic; + int maxcol; + int (*fetch)(Biobuf*); + int nchan; + int chandesc; + int invert; + void (*flush)(Biobuf*); +}; + +static Pix pix[] = { + { '1', 1, Bgetdecimalbit, 1, CY, 1, nil }, /* portable bitmap */ + { '4', 1, Bgetbit, 1, CY, 1, Bflushbit }, /* raw portable bitmap */ + { '2', 0, Bgetint, 1, CY, 0, nil }, /* portable greymap */ + { '5', 0, Bgetc, 1, CY, 0, nil }, /* raw portable greymap */ + { '3', 0, Bgetint, 3, CRGB, 0, nil }, /* portable pixmap */ + { '6', 0, Bgetc, 3, CRGB, 0, nil }, /* raw portable pixmap */ + { 0 }, +}; + +Rawimage* +readppm(Biobuf *b, Rawimage *a) +{ + int i, ch, wid, ht, r, c; + int maxcol, nchan, invert; + int (*fetch)(Biobuf*); + uchar *rgb[3]; + char buf[ERRMAX]; + char *e; + Pix *p; + + e = "bad file format"; + if(Bgetc(b) != 'P') + goto Error; + + c = Bgetc(b); + for(p=pix; p->magic; p++) + if(p->magic == c) + break; + if(p->magic == 0) + goto Error; + + + wid = Bgetint(b); + ht = Bgetint(b); + if(wid <= 0 || ht <= 0) + goto Error; + a->r = Rect(0,0,wid,ht); + + maxcol = p->maxcol; + if(maxcol == 0) { + maxcol = Bgetint(b); + if(maxcol <= 0) + goto Error; + } + + e = "out of memory"; + for(i=0; i<p->nchan; i++) + if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil) + goto Error; + a->nchans = p->nchan; + a->chanlen = wid*ht; + a->chandesc = p->chandesc; + + e = "error reading file"; + + fetch = p->fetch; + nchan = p->nchan; + invert = p->invert; + for(r=0; r<ht; r++) { + for(c=0; c<wid; c++) { + for(i=0; i<nchan; i++) { + if((ch = (*fetch)(b)) < 0) + goto Error; + if(invert) + ch = maxcol - ch; + *rgb[i]++ = (ch * 255)/maxcol; + } + } + if(p->flush) + (*p->flush)(b); + } + + return a; + +Error: + errstr(buf, sizeof buf); + if(buf[0] == 0) + strcpy(buf, e); + errstr(buf, sizeof buf); + + for(i=0; i<3; i++) + free(a->chans[i]); + free(a->cmap); + return nil; +} diff --git a/src/cmd/jpg/readyuv.c b/src/cmd/jpg/readyuv.c new file mode 100644 index 00000000..eae2764e --- /dev/null +++ b/src/cmd/jpg/readyuv.c @@ -0,0 +1,190 @@ +/* readyuv.c - read an Abekas A66 style image file. Steve Simon, 2003 */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <ctype.h> +#include "imagefile.h" + +/* + * ITU/CCIR Rec601 states: + * + * R = y + 1.402 * Cr + * B = Y + 1.77305 * Cb + * G = Y - 0.72414 * Cr - 0.34414 * Cb + * + * using 8 bit traffic + * Y = 16 + 219 * Y + * Cr = 128 + 224 * Cr + * Cb = 128 + 224 * Cb + * or, if 10bit is used + * Y = 64 + 876 * Y + * Cr = 512 + 896 * Cr + * Cb = 512 + 896 * Cb + */ + +enum { + PAL = 576, NTSC = 486 }; + + +static int lsbtab[] = { 6, 4, 2, 0}; + +static int +clip(int x) +{ + x >>= 18; + + if (x > 255) + return 0xff; + if (x <= 0) + return 0; + return x; +} + + +Rawimage** +Breadyuv(Biobuf *bp, int colourspace) +{ + Dir * d; + Rawimage * a, **array; + char *e, ebuf[128]; + ushort * mux, *end, *frm; + uchar buf[720 * 2], *r, *g, *b; + int y1, y2, cb, cr, sz, c, l, w, base, bits, lines; + + frm = 0; + if (colourspace != CYCbCr) { + errstr(ebuf, sizeof ebuf); /* throw it away */ + werrstr("ReadYUV: unknown colour space %d", colourspace); + return nil; + } + + if ((a = calloc(sizeof(Rawimage), 1)) == nil) + sysfatal("no memory"); + + if ((array = calloc(sizeof(Rawimage * ), 2)) == nil) + sysfatal("no memory"); + array[0] = a; + array[1] = nil; + + if ((d = dirfstat(Bfildes(bp))) != nil) { + sz = d->length; + free(d); + } else { + fprint(2, "cannot stat input, assuming 720x576x10bit\n"); + sz = 720 * PAL * 2L + (720 * PAL / 2L); + } + + switch (sz) { + case 720 * PAL * 2: // 625 x 8bit + bits = 8; + lines = PAL; + break; + case 720 * NTSC * 2: // 525 x 8bit + bits = 8; + lines = NTSC; + break; + case 720 * PAL * 2 + (720 * PAL / 2) : // 625 x 10bit + bits = 10; + lines = PAL; + break; + case 720 * NTSC * 2 + (720 * NTSC / 2) : // 525 x 10bit + bits = 10; + lines = NTSC; + break; + default: + e = "unknown file size"; + goto Error; + } + + // print("bits=%d pixels=%d lines=%d\n", bits, 720, lines); + // + a->nchans = 3; + a->chandesc = CRGB; + a->chanlen = 720 * lines; + a->r = Rect(0, 0, 720, lines); + + e = "no memory"; + if ((frm = malloc(720 * 2 * lines * sizeof(ushort))) == nil) + goto Error; + + for (c = 0; c < 3; c++) + if ((a->chans[c] = malloc(720 * lines)) == nil) + goto Error; + + e = "read file"; + for (l = 0; l < lines; l++) { + if (Bread(bp, buf, 720 * 2) == -1) + goto Error; + + base = l * 720 * 2; + for (w = 0; w < 720 * 2; w++) + frm[base + w] = ((ushort)buf[w]) << 2; + } + + + if (bits == 10) + for (l = 0; l < lines; l++) { + if (Bread(bp, buf, 720 / 2) == -1) + goto Error; + + + base = l * 720 * 2; + for (w = 0; w < 720 * 2; w++) + frm[base + w] |= buf[w / 4] >> lsbtab[w % 4]; + } + + mux = frm; + end = frm + 720 * lines * 2; + r = a->chans[0]; + g = a->chans[1]; + b = a->chans[2]; + + while (mux < end) { + cb = *mux++ - 512; + y1 = (*mux++ - 64) * 76310; + cr = *mux++ - 512; + y2 = (*mux++ - 64) * 76310; + + *r++ = clip((104635 * cr) + y1); + *g++ = clip((-25690 * cb + -53294 * cr) + y1); + *b++ = clip((132278 * cb) + y1); + + *r++ = clip((104635 * cr) + y2); + *g++ = clip((-25690 * cb + -53294 * cr) + y2); + *b++ = clip((132278 * cb) + y2); + } + free(frm); + return array; + +Error: + + errstr(ebuf, sizeof ebuf); + if (ebuf[0] == 0) + strcpy(ebuf, e); + errstr(ebuf, sizeof ebuf); + + for (c = 0; c < 3; c++) + free(a->chans[c]); + free(a->cmap); + free(array[0]); + free(array); + free(frm); + return nil; +} + + +Rawimage** +readyuv(int fd, int colorspace) +{ + Rawimage * *a; + Biobuf b; + + if (Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadyuv(&b, colorspace); + Bterm(&b); + return a; +} + + diff --git a/src/cmd/jpg/rgbrgbv.c b/src/cmd/jpg/rgbrgbv.c new file mode 100644 index 00000000..bc55947a --- /dev/null +++ b/src/cmd/jpg/rgbrgbv.c @@ -0,0 +1,69 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +/* + * This version of closest() is now (feb 20, 2001) installed as rgb2cmap in libdraw + */ + +int +closest(int cr, int cg, int cb) +{ + int i, r, g, b, sq; + ulong rgb; + int best, bestsq; + + best = 0; + bestsq = 0x7FFFFFFF; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + r = (rgb>>16) & 0xFF; + g = (rgb>>8) & 0xFF; + b = (rgb>>0) & 0xFF; + sq = (r-cr)*(r-cr)+(g-cg)*(g-cg)+(b-cb)*(b-cb); + if(sq < bestsq){ + bestsq = sq; + best = i; + } + } + return best; +} + +void +main(int argc, char *argv[]) +{ + int i, rgb; + int r, g, b; + uchar close[16*16*16]; + + /* rgbmap */ + print("uint rgbmap[256] = {\n"); + for(i=0; i<256; i++){ + if(i%8 == 0) + print("\t"); + rgb = cmap2rgb(i); + r = (rgb>>16) & 0xFF; + g = (rgb>>8) & 0xFF; + b = (rgb>>0) & 0xFF; + print("0x%.6ulX, ", (r<<16) | (g<<8) | b); + if(i%8 == 7) + print("\n"); + } + print("};\n\n"); + + /* closestrgb */ + print("uchar closestrgb[16*16*16] = {\n"); + for(r=0; r<256; r+=16) + for(g=0; g<256; g+=16) + for(b=0; b<256; b+=16) + close[(b/16)+16*((g/16)+16*(r/16))] = closest(r+8, g+8, b+8); + for(i=0; i<16*16*16; i++){ + if(i%16 == 0) + print("\t"); + print("%d,", close[i]); + if(i%16 == 15) + print("\n"); + } + print("};\n\n"); + exits(nil); +} diff --git a/src/cmd/jpg/rgbycc.c b/src/cmd/jpg/rgbycc.c new file mode 100644 index 00000000..3654a2d2 --- /dev/null +++ b/src/cmd/jpg/rgbycc.c @@ -0,0 +1,120 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +float c1 = 1.402; +float c2 = 0.34414; +float c3 = 0.71414; +float c4 = 1.772; + +int +closest(int Y, int Cb, int Cr) +{ + double r, g, b; + double diff, min; + int rgb, R, G, B, v, i; + int y1, cb1, cr1; + + Cb -= 128; + Cr -= 128; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + +//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b); + + min = 1000000.; + v = 1000; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + R = (rgb >> 16) & 0xFF; + G = (rgb >> 8) & 0xFF; + B = (rgb >> 0) & 0xFF; + diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b); + y1 = 0.5870*G + 0.114*B + 0.299*R; + cb1 = (B-y1)/1.772; + cr1 = (R-y1)/1.402; + if(diff < min){ +// if(Y==0 && y1!=0) +// continue; + if(Y==256-16 && y1<256-16) + continue; +// if(Cb==0 && cb1!=0) +// continue; + if(Cb==256-16 && cb1<256-16) + continue; +// if(Cr==0 && cr1!=0) +// continue; + if(Cr==256-16 && cr1<256-16) + continue; +//print("%d %d %d\n", R, G, B); + min = diff; + v = i; + } + } + if(v > 255) + abort(); + return v; +} + +void +main(int argc, char *argv[]) +{ + int i, rgb; + int r, g, b; + double Y, Cr, Cb; + int y, cb, cr; + uchar close[16*16*16]; + +//print("%d\n", closest(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]))); +//exits("X"); + + /* ycbcrmap */ + print("uint ycbcrmap[256] = {\n"); + for(i=0; i<256; i++){ + if(i%8 == 0) + print("\t"); + rgb = cmap2rgb(i); + r = (rgb>>16) & 0xFF; + g = (rgb>>8) & 0xFF; + b = (rgb>>0) & 0xFF; + Y = 0.5870*g + 0.114*b + 0.299*r; + Cr = (r-Y)/1.402 + 128.; + Cb = (b-Y)/1.772 + 128.; + if(Y<0. || Y>=256. || Cr<0. || Cr>=256. || Cb<0. || Cb>=256.) + print("bad at %d: %d %d %d; %g %g %g\n", i, r, g, b, Y, Cb, Cr); + r = Y; + g = Cb; + b = Cr; + print("0x%.6ulX, ", (r<<16) | (g<<8) | b); + if(i%8 == 7) + print("\n"); + } + print("};\n\n"); + + /* closestycbcr */ + print("uchar closestycbcr[16*16*16] = {\n"); + for(y=0; y<256; y+=16) + for(cb=0; cb<256; cb+=16) + for(cr=0; cr<256; cr+=16) + close[(cr/16)+16*((cb/16)+16*(y/16))] = closest(y, cb, cr); +if(0){ + /*weird: set white for nearly white */ + for(cb=128-32; cb<=128+32; cb+=16) + for(cr=128-32; cr<=128+32; cr+=16) + close[(cr/16)+16*((cb/16)+16*(255/16))] = 0; + /*weird: set black for nearly black */ + for(cb=128-32; cb<=128+32; cb+=16) + for(cr=128-32; cr<=128+32; cr+=16) + close[(cr/16)+16*((cb/16)+16*(0/16))] = 255; +} + for(i=0; i<16*16*16; i++){ + if(i%16 == 0) + print("\t"); + print("%d,", close[i]); + if(i%16 == 15) + print("\n"); + } + print("};\n\n"); + exits(nil); +} diff --git a/src/cmd/jpg/togif.c b/src/cmd/jpg/togif.c new file mode 100644 index 00000000..35336883 --- /dev/null +++ b/src/cmd/jpg/togif.c @@ -0,0 +1,147 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <ctype.h> +#include <bio.h> +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: togif [-l loopcount] [-c 'comment'] [-d Δt (ms)] [-t transparency-index] [file ... [-d Δt] file ...]\n"); + exits("usage"); +} + +#define UNSET (-12345678) + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd, j, dt, trans, loop; + char buf[256]; + char *err, *comment, *s; + + comment = nil; + dt = -1; + trans = -1; + loop = UNSET; + ARGBEGIN{ + case 'l': + s = ARGF(); + if(s==nil || (!isdigit(s[0]) && s[0]!='-')) + usage(); + loop = atoi(s); + break; + case 'c': + comment = ARGF(); + if(comment == nil) + usage(); + break; + case 'd': + s = ARGF(); + if(s==nil || !isdigit(s[0])) + usage(); + dt = atoi(s); + break; + case 't': + s = ARGF(); + if(s==nil || !isdigit(s[0])) + usage(); + trans = atoi(s); + if(trans > 255) + usage(); + break; + default: + usage(); + }ARGEND + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + + memimageinit(); + + err = nil; + + if(argc == 0){ + i = readmemimage(0); + if(i == nil) + sysfatal("reading input: %r"); + ni = memonechan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + err = memstartgif(&bout, i, -1); + if(err == nil){ + if(comment) + err = memwritegif(&bout, i, comment, dt, trans); + else{ + snprint(buf, sizeof buf, "Converted by Plan 9 from <stdin>"); + err = memwritegif(&bout, i, buf, dt, trans); + } + } + }else{ + if(loop == UNSET){ + if(argc == 1) + loop = -1; /* no loop for single image */ + else + loop = 0; /* the default case: 0 means infinite loop */ + } + for(j=0; j<argc; j++){ + if(argv[j][0] == '-' && argv[j][1]=='d'){ + /* time change */ + if(argv[j][2] == '\0'){ + s = argv[++j]; + if(j == argc) + usage(); + }else + s = &argv[j][2]; + if(!isdigit(s[0])) + usage(); + dt = atoi(s); + if(j == argc-1) /* last argument must be file */ + usage(); + continue; + } + fd = open(argv[j], OREAD); + if(fd < 0) + sysfatal("can't open %s: %r", argv[j]); + i = readmemimage(fd); + if(i == nil) + sysfatal("can't readimage %s: %r", argv[j]); + close(fd); + ni = memonechan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + if(j == 0){ + err = memstartgif(&bout, i, loop); + if(err != nil) + break; + } + if(comment) + err = memwritegif(&bout, i, comment, dt, trans); + else{ + snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[j]); + err = memwritegif(&bout, i, buf, dt, trans); + } + if(err != nil) + break; + freememimage(i); + comment = nil; + } + } + memendgif(&bout); + + if(err != nil) + fprint(2, "togif: %s\n", err); + exits(err); +} diff --git a/src/cmd/jpg/toico.c b/src/cmd/jpg/toico.c new file mode 100644 index 00000000..059e018b --- /dev/null +++ b/src/cmd/jpg/toico.c @@ -0,0 +1,322 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> + +enum +{ + FileHdrLen= 6, + IconDescrLen= 16, + IconHdrLen= 40, +}; + +typedef struct Icon Icon; +struct Icon +{ + Icon *next; + char *file; + + uchar w; /* icon width */ + uchar h; /* icon height */ + ushort ncolor; /* number of colors */ + ushort nplane; /* number of bit planes */ + ushort bits; /* bits per pixel */ + ulong len; /* length of data */ + ulong offset; /* file offset to data */ + uchar map[4*256]; /* color map */ + + Image *img; + + uchar *xor; + int xorlen; + uchar *and; + int andlen; +}; + +typedef struct Header Header; +struct Header +{ + uint n; + Icon *first; + Icon *last; +}; + +void +Bputs(Biobuf *b, ushort x) +{ + Bputc(b, x&0xff); + Bputc(b, x>>8); +} + +void +Bputl(Biobuf *b, ulong x) +{ + Bputs(b, x&0xffff); + Bputs(b, x>>16); +} + +Header h; + +void* emalloc(int); +void mk8bit(Icon*, int); +void mkxorand(Icon*, int); +void readicon(char*); + +void +main(int argc, char **argv) +{ + int i; + Biobuf *b, out; + Icon *icon; + ulong offset; + ulong len; + + ARGBEGIN{ + }ARGEND; + + /* read in all the images */ + initdraw(nil, nil, nil); + if(argc < 1){ + readicon("/fd/0"); + } else { + for(i = 0; i < argc; i++) + readicon(argv[i]); + } + + /* create the .ico file */ + b = &out; + Binit(b, 1, OWRITE); + + /* offset to first icon */ + offset = FileHdrLen + h.n*IconDescrLen; + + /* file header is */ + Bputs(b, 0); + Bputs(b, 1); + Bputs(b, h.n); + + /* icon description */ + for(icon = h.first; icon != nil; icon = icon->next){ + Bputc(b, icon->w); + Bputc(b, icon->h); + Bputc(b, icon->ncolor); + Bputc(b, 0); + Bputs(b, icon->nplane); + Bputs(b, icon->bits); + len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen; + Bputl(b, len); + Bputl(b, offset); + offset += len; + } + + /* icons */ + for(icon = h.first; icon != nil; icon = icon->next){ + /* icon header (BMP like) */ + Bputl(b, IconHdrLen); + Bputl(b, icon->w); + Bputl(b, 2*icon->h); + Bputs(b, icon->nplane); + Bputs(b, icon->bits); + Bputl(b, 0); /* compression info */ + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + + /* color map */ + if(Bwrite(b, icon->map, 4*icon->ncolor) < 0) + sysfatal("writing color map: %r"); + + /* xor bits */ + if(Bwrite(b, icon->xor, icon->xorlen) < 0) + sysfatal("writing xor bits: %r"); + + /* and bits */ + if(Bwrite(b, icon->and, icon->andlen) < 0) + sysfatal("writing and bits: %r"); + } + + Bterm(b); + exits(0); +} + +void +readicon(char *file) +{ + int fd; + Icon *icon; + + fd = open(file, OREAD); + if(fd < 0) + sysfatal("opening %s: %r", file); + icon = emalloc(sizeof(Icon)); + icon->img = readimage(display, fd, 0); + if(icon->img == nil) + sysfatal("reading image %s: %r", file); + close(fd); + + if(h.first) + h.last->next = icon; + else + h.first = icon; + h.last = icon; + h.n++; + + icon->h = Dy(icon->img->r); + icon->w = Dx(icon->img->r); + icon->bits = 1<<icon->img->depth; + icon->nplane = 1; + + /* convert to 8 bits per pixel */ + switch(icon->img->chan){ + case GREY8: + case CMAP8: + break; + case GREY1: + case GREY2: + case GREY4: + mk8bit(icon, 1); + break; + default: + mk8bit(icon, 0); + break; + } + icon->bits = 8; + icon->file = file; + + /* create xor/and masks, minimizing bits per pixel */ + mkxorand(icon, icon->img->chan == GREY8); +} + +void* +emalloc(int len) +{ + void *x; + + x = mallocz(len, 1); + if(x == nil) + sysfatal("memory: %r"); + return x; +} + +/* convert to 8 bit */ +void +mk8bit(Icon *icon, int grey) +{ + Image *img; + + img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill); + if(img == nil) + sysfatal("can't allocimage: %r"); + draw(img, img->r, icon->img, nil, ZP); + freeimage(icon->img); + icon->img = img; +} + +/* make xor and and mask */ +void +mkxorand(Icon *icon, int grey) +{ + int i, x, y, s, sa; + uchar xx[256]; + uchar *data, *p, *e; + int ndata; + uchar *mp; + int ncolor; + ulong color; + int bits; + uchar andbyte, xorbyte; + uchar *ato, *xto; + int xorrl, andrl; + + ndata = icon->h * icon->w; + data = emalloc(ndata); + if(unloadimage(icon->img, icon->img->r, data, ndata) < 0) + sysfatal("can't unload %s: %r", icon->file); + e = data + ndata; + + /* find colors used */ + memset(xx, 0, sizeof xx); + for(p = data; p < e; p++) + xx[*p]++; + + /* count the colors and create a mapping from plan 9 */ + mp = icon->map; + ncolor = 0; + for(i = 0; i < 256; i++){ + if(xx[i] == 0) + continue; + if(grey){ + *mp++ = i; + *mp++ = i; + *mp++ = i; + *mp++ = 0; + } else { + color = cmap2rgb(i); + *mp++ = color; + *mp++ = color>>8; + *mp++ = color>>16; + *mp++ = 0; + } + xx[i] = ncolor; + ncolor++; + } + + /* get minimum number of pixels per bit (with a color map) */ + if(ncolor <= 2){ + ncolor = 2; + bits = 1; + } else if(ncolor <= 4){ + ncolor = 4; + bits = 2; + } else if(ncolor <= 16){ + ncolor = 16; + bits = 4; + } else { + ncolor = 256; + bits = 8; + } + icon->bits = bits; + icon->ncolor = ncolor; + + /* the xor mask rows are justified to a 32 bit boundary */ + /* the and mask is 1 bit grey */ + xorrl = 4*((bits*icon->w + 31)/32); + andrl = 4*((icon->w + 31)/32); + icon->xor = emalloc(xorrl * icon->h); + icon->and = emalloc(andrl * icon->h); + icon->xorlen = xorrl*icon->h; + icon->andlen = andrl*icon->h; + + /* make both masks. they're upside down relative to plan9 ones */ + p = data; + for(y = 0; y < icon->h; y++){ + andbyte = 0; + xorbyte = 0; + sa = s = 0; + xto = icon->xor + (icon->h-1-y)*xorrl; + ato = icon->and + (icon->h-1-y)*andrl; + for(x = 0; x < icon->w; x++){ + xorbyte <<= bits; + xorbyte |= xx[*p]; + s += bits; + if(s == 8){ + *xto++ = xorbyte; + xorbyte = 0; + s = 0; + } + andbyte <<= 1; + if(*p == 0xff) + andbyte |= 1; + sa++; + if(sa == 0){ + *ato++ = andbyte; + sa = 0; + andbyte = 0; + } + p++; + } + } + free(data); +} diff --git a/src/cmd/jpg/topng.c b/src/cmd/jpg/topng.c new file mode 100644 index 00000000..09c00613 --- /dev/null +++ b/src/cmd/jpg/topng.c @@ -0,0 +1,70 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <ctype.h> +#include <bio.h> +#include <flate.h> +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: topng [-c 'comment'] [-g 'gamma'] [file]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i; + int fd; + char *err, *filename; + ImageInfo II; + + ARGBEGIN{ + case 'c': + II.comment = ARGF(); + if(II.comment == nil) + usage(); + II.fields_set |= II_COMMENT; + break; + case 'g': + II.gamma = atof(ARGF()); + if(II.gamma == 0.) + usage(); + II.fields_set |= II_GAMMA; + break; + case 't': + break; + default: + usage(); + }ARGEND + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + memimageinit(); + + if(argc == 0){ + fd = 0; + filename = "<stdin>"; + }else{ + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("can't open %s: %r", argv[0]); + filename = argv[0]; + } + + i = readmemimage(fd); + if(i == nil) + sysfatal("can't readimage %s: %r", filename); + close(fd); + + err = memwritepng(&bout, i, &II); + freememimage(i); + + if(err != nil) + fprint(2, "topng: %s\n", err); + exits(err); +} diff --git a/src/cmd/jpg/toppm.c b/src/cmd/jpg/toppm.c new file mode 100644 index 00000000..e3094655 --- /dev/null +++ b/src/cmd/jpg/toppm.c @@ -0,0 +1,90 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <ctype.h> +#include <bio.h> +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: toppm [-c 'comment'] [file]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd; + char buf[256]; + char *err, *comment; + + comment = nil; + ARGBEGIN{ + case 'c': + comment = ARGF(); + if(comment == nil) + usage(); + if(strchr(comment, '\n') != nil){ + fprint(2, "ppm: comment cannot contain newlines\n"); + usage(); + } + break; + default: + usage(); + }ARGEND + + if(argc > 1) + usage(); + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + + memimageinit(); + + err = nil; + + if(argc == 0){ + i = readmemimage(0); + if(i == nil) + sysfatal("reading input: %r"); + ni = memmultichan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + if(err == nil) + err = memwriteppm(&bout, i, comment); + }else{ + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("can't open %s: %r", argv[0]); + i = readmemimage(fd); + if(i == nil) + sysfatal("can't readimage %s: %r", argv[0]); + close(fd); + ni = memmultichan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + if(comment) + err = memwriteppm(&bout, i, comment); + else{ + snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[0]); + err = memwriteppm(&bout, i, buf); + } + freememimage(i); + } + + if(err != nil) + fprint(2, "toppm: %s\n", err); + exits(err); +} diff --git a/src/cmd/jpg/torgbv.c b/src/cmd/jpg/torgbv.c new file mode 100644 index 00000000..e83f82cd --- /dev/null +++ b/src/cmd/jpg/torgbv.c @@ -0,0 +1,299 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" + +#include "rgbv.h" +#include "ycbcr.h" + +#define CLAMPOFF 128 + +static int clamp[CLAMPOFF+256+CLAMPOFF]; +static int inited; + +void* +_remaperror(char *fmt, ...) +{ + va_list arg; + char buf[256]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + + werrstr(buf); + return nil; +} + +Rawimage* +torgbv(Rawimage *i, int errdiff) +{ + int j, k, rgb, x, y, er, eg, eb, col, t; + int r, g, b, r1, g1, b1; + int *ered, *egrn, *eblu, *rp, *gp, *bp; + uint *map3; + uchar *closest; + Rawimage *im; + int dx, dy; + char err[ERRMAX]; + uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic; + + err[0] = '\0'; + errstr(err, sizeof err); /* throw it away */ + im = malloc(sizeof(Rawimage)); + if(im == nil) + return nil; + memset(im, 0, sizeof(Rawimage)); + im->chans[0] = malloc(i->chanlen); + if(im->chans[0] == nil){ + free(im); + return nil; + } + im->r = i->r; + im->nchans = 1; + im->chandesc = CRGBV; + im->chanlen = i->chanlen; + + dx = i->r.max.x-i->r.min.x; + dy = i->r.max.y-i->r.min.y; + cmap = i->cmap; + + if(inited == 0){ + inited = 1; + for(j=0; j<CLAMPOFF; j++) + clamp[j] = 0; + for(j=0; j<256; j++) + clamp[CLAMPOFF+j] = (j>>4); + for(j=0; j<CLAMPOFF; j++) + clamp[CLAMPOFF+256+j] = (255>>4); + } + + in = i->chans[0]; + inp = in; + out = im->chans[0]; + outp = out; + + ered = malloc((dx+1)*sizeof(int)); + egrn = malloc((dx+1)*sizeof(int)); + eblu = malloc((dx+1)*sizeof(int)); + if(ered==nil || egrn==nil || eblu==nil){ + free(im->chans[0]); + free(im); + free(ered); + free(egrn); + free(eblu); + return _remaperror("remap: malloc failed: %r"); + } + memset(ered, 0, (dx+1)*sizeof(int)); + memset(egrn, 0, (dx+1)*sizeof(int)); + memset(eblu, 0, (dx+1)*sizeof(int)); + + switch(i->chandesc){ + default: + return _remaperror("remap: can't recognize channel type %d", i->chandesc); + case CRGB1: + if(cmap == nil) + return _remaperror("remap: image has no color map"); + if(i->nchans != 1) + return _remaperror("remap: can't handle nchans %d", i->nchans); + for(j=1; j<=8; j++) + if(i->cmaplen == 3*(1<<j)) + break; + if(j > 8) + return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3); + if(i->cmaplen != 3*256){ + /* to avoid a range check in inner loop below, make a full-size cmap */ + memmove(cmap1, cmap, i->cmaplen); + cmap = cmap1; + } + if(errdiff == 0){ + k = 0; + for(j=0; j<256; j++){ + r = cmap[k]>>4; + g = cmap[k+1]>>4; + b = cmap[k+2]>>4; + k += 3; + map[j] = closestrgb[b+16*(g+16*r)]; + } + for(j=0; j<i->chanlen; j++) + out[j] = map[in[j]]; + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y<dy; y++){ + er = 0; + eg = 0; + eb = 0; + rp = ered; + gp = egrn; + bp = eblu; + for(x=0; x<dx; x++){ + cm = &cmap[3 * *inp++]; + r = cm[0] +*rp; + g = cm[1] +*gp; + b = cm[2] +*bp; + + /* sanity checks are new */ + if(r >= 256+CLAMPOFF) + r = 0; + if(g >= 256+CLAMPOFF) + g = 0; + if(b >= 256+CLAMPOFF) + b = 0; + r1 = clamp[r+CLAMPOFF]; + g1 = clamp[g+CLAMPOFF]; + b1 = clamp[b+CLAMPOFF]; + if(r1 >= 16 || g1 >= 16 || b1 >= 16) + col = 0; + else + col = closestrgb[b1+16*(g1+16*r1)]; + *outp++ = col; + + rgb = rgbmap[col]; + r -= (rgb>>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + + g -= (rgb>>8) & 0xFF; + t = (3*g)>>4; + *gp++ = t+eg; + *gp += t; + eg = g-3*t; + + b -= rgb & 0xFF; + t = (3*b)>>4; + *bp++ = t+eb; + *bp += t; + eb = b-3*t; + } + } + } + break; + + case CYCbCr: + closest = closestycbcr; + map3 = ycbcrmap; + goto Threecolor; + + case CRGB: + closest = closestrgb; + map3 = rgbmap; + + Threecolor: + if(i->nchans != 3) + return _remaperror("remap: RGB image has %d channels", i->nchans); + rpic = i->chans[0]; + gpic = i->chans[1]; + bpic = i->chans[2]; + if(errdiff == 0){ + for(j=0; j<i->chanlen; j++){ + r = rpic[j]>>4; + g = gpic[j]>>4; + b = bpic[j]>>4; + out[j] = closest[b+16*(g+16*r)]; + } + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y<dy; y++){ + er = 0; + eg = 0; + eb = 0; + rp = ered; + gp = egrn; + bp = eblu; + for(x=0; x<dx; x++){ + r = *rpic++ + *rp; + g = *gpic++ + *gp; + b = *bpic++ + *bp; + /* + * Errors can be uncorrectable if converting from YCbCr, + * since we can't guarantee that an extremal value of one of + * the components selects a color with an extremal value. + * If we don't, the errors accumulate without bound. This + * doesn't happen in RGB because the closest table can guarantee + * a color on the edge of the gamut, producing a zero error in + * that component. For the rotation YCbCr space, there may be + * no color that can guarantee zero error at the edge. + * Therefore we must clamp explicitly rather than by assuming + * an upper error bound of CLAMPOFF. The performance difference + * is miniscule anyway. + */ + if(r < 0) + r = 0; + else if(r > 255) + r = 255; + if(g < 0) + g = 0; + else if(g > 255) + g = 255; + if(b < 0) + b = 0; + else if(b > 255) + b = 255; + r1 = r>>4; + g1 = g>>4; + b1 = b>>4; + col = closest[b1+16*(g1+16*r1)]; + *outp++ = col; + + rgb = map3[col]; + r -= (rgb>>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + + g -= (rgb>>8) & 0xFF; + t = (3*g)>>4; + *gp++ = t+eg; + *gp += t; + eg = g-3*t; + + b -= rgb & 0xFF; + t = (3*b)>>4; + *bp++ = t+eb; + *bp += t; + eb = b-3*t; + } + } + } + break; + + case CY: + if(i->nchans != 1) + return _remaperror("remap: Y image has %d chans", i->nchans); + rpic = i->chans[0]; + if(errdiff == 0){ + for(j=0; j<i->chanlen; j++){ + r = rpic[j]>>4; + *outp++ = closestrgb[r+16*(r+16*r)]; + } + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y<dy; y++){ + er = 0; + rp = ered; + for(x=0; x<dx; x++){ + r = *inp++ + *rp; + r1 = clamp[r+CLAMPOFF]; + col = closestrgb[r1+16*(r1+16*r1)]; + *outp++ = col; + + rgb = rgbmap[col]; + r -= (rgb>>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + } + } + } + break; + } + free(ered); + free(egrn); + free(eblu); + return im; +} diff --git a/src/cmd/jpg/totruecolor.c b/src/cmd/jpg/totruecolor.c new file mode 100644 index 00000000..0da5d9a3 --- /dev/null +++ b/src/cmd/jpg/totruecolor.c @@ -0,0 +1,163 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" + +enum { + c1 = 2871, /* 1.402 * 2048 */ + c2 = 705, /* 0.34414 * 2048 */ + c3 = 1463, /* 0.71414 * 2048 */ + c4 = 3629, /* 1.772 * 2048 */ +}; + +Rawimage* +totruecolor(Rawimage *i, int chandesc) +{ + int j, k; + Rawimage *im; + char err[ERRMAX]; + uchar *rp, *gp, *bp, *cmap, *inp, *outp, cmap1[3*256]; + int r, g, b, Y, Cr, Cb; + + if(chandesc!=CY && chandesc!=CRGB24) + return _remaperror("remap: can't convert to chandesc %d", chandesc); + + err[0] = '\0'; + errstr(err, sizeof err); /* throw it away */ + im = malloc(sizeof(Rawimage)); + if(im == nil) + return nil; + memset(im, 0, sizeof(Rawimage)); + if(chandesc == CY) + im->chanlen = i->chanlen; + else + im->chanlen = 3*i->chanlen; + im->chandesc = chandesc; + im->chans[0] = malloc(im->chanlen); + if(im->chans[0] == nil){ + free(im); + return nil; + } + im->r = i->r; + im->nchans = 1; + + cmap = i->cmap; + + outp = im->chans[0]; + + switch(i->chandesc){ + default: + return _remaperror("remap: can't recognize channel type %d", i->chandesc); + case CY: + if(i->nchans != 1) + return _remaperror("remap: Y image has %d chans", i->nchans); + if(chandesc == CY){ + memmove(im->chans[0], i->chans[0], i->chanlen); + break; + } + /* convert to three color */ + inp = i->chans[0]; + for(j=0; j<i->chanlen; j++){ + k = *inp++; + *outp++ = k; + *outp++ = k; + *outp++ = k; + } + break; + + case CRGB1: + if(cmap == nil) + return _remaperror("remap: image has no color map"); + if(i->nchans != 1) + return _remaperror("remap: can't handle nchans %d", i->nchans); + for(j=1; j<=8; j++) + if(i->cmaplen == 3*(1<<j)) + break; + if(j > 8) + return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3); + if(i->cmaplen != 3*256){ + /* to avoid a range check in loop below, make a full-size cmap */ + memmove(cmap1, cmap, i->cmaplen); + cmap = cmap1; + } + inp = i->chans[0]; + if(chandesc == CY){ + for(j=0; j<i->chanlen; j++){ + k = *inp++; + r = cmap[3*k+2]; + g = cmap[3*k+1]; + b = cmap[3*k+0]; + r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */ + *outp++ = r; + } + }else{ + for(j=0; j<i->chanlen; j++){ + k = *inp++; + *outp++ = cmap[3*k+2]; + *outp++ = cmap[3*k+1]; + *outp++ = cmap[3*k+0]; + } + } + break; + + case CRGB: + if(i->nchans != 3) + return _remaperror("remap: can't handle nchans %d", i->nchans); + rp = i->chans[0]; + gp = i->chans[1]; + bp = i->chans[2]; + if(chandesc == CY){ + for(j=0; j<i->chanlen; j++){ + r = *bp++; + g = *gp++; + b = *rp++; + r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */ + *outp++ = r; + } + }else + for(j=0; j<i->chanlen; j++){ + *outp++ = *bp++; + *outp++ = *gp++; + *outp++ = *rp++; + } + break; + + case CYCbCr: + if(i->nchans != 3) + return _remaperror("remap: can't handle nchans %d", i->nchans); + rp = i->chans[0]; + gp = i->chans[1]; + bp = i->chans[2]; + for(j=0; j<i->chanlen; j++){ + Y = *rp++ << 11; + Cb = *gp++ - 128; + Cr = *bp++ - 128; + r = (Y+c1*Cr) >> 11; + g = (Y-c2*Cb-c3*Cr) >> 11; + b = (Y+c4*Cb) >> 11; + if(r < 0) + r = 0; + if(r > 255) + r = 255; + if(g < 0) + g = 0; + if(g > 255) + g = 255; + if(b < 0) + b = 0; + if(b > 255) + b = 255; + if(chandesc == CY){ + r = (2125*r + 7154*g + 721*b)/10000; + *outp++ = r; + }else{ + *outp++ = b; + *outp++ = g; + *outp++ = r; + } + } + break; + } + return im; +} diff --git a/src/cmd/jpg/writegif.c b/src/cmd/jpg/writegif.c new file mode 100644 index 00000000..16402ef8 --- /dev/null +++ b/src/cmd/jpg/writegif.c @@ -0,0 +1,568 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <bio.h> +#include "imagefile.h" + +enum +{ + Nhash = 4001, + Nbuf = 300, +}; + +typedef struct Entry Entry; +typedef struct IO IO; + + +struct Entry +{ + int index; + int prefix; + int exten; + Entry *next; +}; + +struct IO +{ + Biobuf *fd; + uchar buf[Nbuf]; + int i; + int nbits; /* bits in right side of shift register */ + int sreg; /* shift register */ +}; + +static Rectangle mainrect; +static Entry tbl[4096]; +static uchar *colormap[5]; /* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */ +#define GREYMAP 4 +static int colormapsize[] = { 2, 4, 16, 256, 256 }; /* 2 for zero is an odd property of GIF */ + +static void writeheader(Biobuf*, Rectangle, int, ulong, int); +static void writedescriptor(Biobuf*, Rectangle); +static char* writedata(Biobuf*, Image*, Memimage*); +static void writecomment(Biobuf *fd, char*); +static void writegraphiccontrol(Biobuf *fd, int, int); +static void* gifmalloc(ulong); +static void encode(Biobuf*, Rectangle, int, uchar*, uint); + +static +char* +startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount) +{ + int i; + + for(i=0; i<nelem(tbl); i++) + tbl[i] = (Entry){i, -1, i, nil}; + + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + case CMAP8: + case GREY8: + break; + default: + return "WriteGIF: can't handle channel type"; + } + + mainrect = r; + writeheader(fd, r, depth, chan, loopcount); + return nil; +} + +char* +startgif(Biobuf *fd, Image *image, int loopcount) +{ + return startgif0(fd, image->chan, image->r, image->depth, loopcount); +} + +char* +memstartgif(Biobuf *fd, Memimage *memimage, int loopcount) +{ + return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount); +} + +static +char* +writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans) +{ + char *err; + + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + case CMAP8: + case GREY8: + break; + default: + return "WriteGIF: can't handle channel type"; + } + + writecomment(fd, comment); + writegraphiccontrol(fd, dt, trans); + writedescriptor(fd, r); + + err = writedata(fd, image, memimage); + if(err != nil) + return err; + + return nil; +} + +char* +writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans) +{ + return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans); +} + +char* +memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans) +{ + return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans); +} + +/* + * Write little-endian 16-bit integer + */ +static +void +put2(Biobuf *fd, int i) +{ + Bputc(fd, i); + Bputc(fd, i>>8); +} + +/* + * Get color map for all ldepths, in format suitable for writing out + */ +static +void +getcolormap(void) +{ + int i, col; + ulong rgb; + uchar *c; + + if(colormap[0] != nil) + return; + for(i=0; i<nelem(colormap); i++) + colormap[i] = gifmalloc(3* colormapsize[i]); + c = colormap[GREYMAP]; /* GREY8 */ + for(i=0; i<256; i++){ + c[3*i+0] = i; /* red */ + c[3*i+1] = i; /* green */ + c[3*i+2] = i; /* blue */ + } + c = colormap[3]; /* RGBV */ + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[2]; /* GREY4 */ + for(i=0; i<16; i++){ + col = (i<<4)|i; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[1]; /* GREY2 */ + for(i=0; i<4; i++){ + col = (i<<6)|(i<<4)|(i<<2)|i; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[0]; /* GREY1 */ + for(i=0; i<2; i++){ + if(i == 0) + col = 0; + else + col = 0xFF; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } +} + +/* + * Write header, logical screen descriptor, and color map + */ +static +void +writeheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount) +{ + /* Header */ + Bprint(fd, "%s", "GIF89a"); + + /* Logical Screen Descriptor */ + put2(fd, Dx(r)); + put2(fd, Dy(r)); + + /* Color table present, 4 bits per color (for RGBV best case), size of color map */ + Bputc(fd, (1<<7)|(3<<4)|(depth-1)); /* not right for GREY8, but GIF doesn't let us specify enough bits */ + Bputc(fd, 0xFF); /* white background (doesn't matter anyway) */ + Bputc(fd, 0); /* pixel aspect ratio - unused */ + + /* Global Color Table */ + getcolormap(); + if(chan == GREY8) + depth = GREYMAP; + else + depth = drawlog2[depth]; + Bwrite(fd, colormap[depth], 3*colormapsize[depth]); + + if(loopcount >= 0){ /* hard-to-discover way to force cycled animation */ + /* Application Extension with (1 loopcountlo loopcounthi) as data */ + Bputc(fd, 0x21); + Bputc(fd, 0xFF); + Bputc(fd, 11); + Bwrite(fd, "NETSCAPE2.0", 11); + Bputc(fd, 3); + Bputc(fd, 1); + put2(fd, loopcount); + Bputc(fd, 0); + } +} + +/* + * Write optional comment block + */ +static +void +writecomment(Biobuf *fd, char *comment) +{ + int n; + + if(comment==nil || comment[0]=='\0') + return; + + /* Comment extension and label */ + Bputc(fd, 0x21); + Bputc(fd, 0xFE); + + /* Comment data */ + n = strlen(comment); + if(n > 255) + n = 255; + Bputc(fd, n); + Bwrite(fd, comment, n); + + /* Block terminator */ + Bputc(fd, 0x00); +} + +/* + * Write optional control block (sets Delay Time) + */ +static +void +writegraphiccontrol(Biobuf *fd, int dt, int trans) +{ + if(dt < 0 && trans < 0) + return; + + /* Comment extension and label and block size*/ + Bputc(fd, 0x21); + Bputc(fd, 0xF9); + Bputc(fd, 0x04); + + /* Disposal method and other flags (none) */ + if(trans >= 0) + Bputc(fd, 0x01); + else + Bputc(fd, 0x00); + + /* Delay time, in centisec (argument is millisec for sanity) */ + if(dt < 0) + dt = 0; + else if(dt < 10) + dt = 1; + else + dt = (dt+5)/10; + put2(fd, dt); + + /* Transparency index */ + if(trans < 0) + trans = 0; + Bputc(fd, trans); + + /* Block terminator */ + Bputc(fd, 0x00); +} + +/* + * Write image descriptor + */ +static +void +writedescriptor(Biobuf *fd, Rectangle r) +{ + /* Image Separator */ + Bputc(fd, 0x2C); + + /* Left, top, width, height */ + put2(fd, r.min.x-mainrect.min.x); + put2(fd, r.min.y-mainrect.min.y); + put2(fd, Dx(r)); + put2(fd, Dy(r)); + /* no special processing */ + Bputc(fd, 0); +} + +/* + * Write data + */ +static +char* +writedata(Biobuf *fd, Image *image, Memimage *memimage) +{ + char *err; + uchar *data; + int ndata, depth; + Rectangle r; + + if(memimage != nil){ + r = memimage->r; + depth = memimage->depth; + }else{ + r = image->r; + depth = image->depth; + } + + /* LZW Minimum code size */ + if(depth == 1) + Bputc(fd, 2); + else + Bputc(fd, depth); + + /* + * Read image data into memory + * potentially one extra byte on each end of each scan line + */ + ndata = Dy(r)*(2+(Dx(r)>>(3-drawlog2[depth]))); + data = gifmalloc(ndata); + if(memimage != nil) + ndata = unloadmemimage(memimage, r, data, ndata); + else + ndata = unloadimage(image, r, data, ndata); + if(ndata < 0){ + err = gifmalloc(ERRMAX); + snprint(err, ERRMAX, "WriteGIF: %r"); + free(data); + return err; + } + + /* Encode and emit the data */ + encode(fd, r, depth, data, ndata); + free(data); + + /* Block Terminator */ + Bputc(fd, 0); + return nil; +} + +/* + * Write trailer + */ +void +endgif(Biobuf *fd) +{ + Bputc(fd, 0x3B); + Bflush(fd); +} + +void +memendgif(Biobuf *fd) +{ + endgif(fd); +} + +/* + * Put n bits of c into output at io.buf[i]; + */ +static +void +output(IO *io, int c, int n) +{ + if(c < 0){ + if(io->nbits != 0) + io->buf[io->i++] = io->sreg; + Bputc(io->fd, io->i); + Bwrite(io->fd, io->buf, io->i); + io->nbits = 0; + return; + } + + if(io->nbits+n >= 31){ + fprint(2, "panic: WriteGIF sr overflow\n"); + exits("WriteGIF panic"); + } + io->sreg |= c<<io->nbits; + io->nbits += n; + + while(io->nbits >= 8){ + io->buf[io->i++] = io->sreg; + io->sreg >>= 8; + io->nbits -= 8; + } + + if(io->i >= 255){ + Bputc(io->fd, 255); + Bwrite(io->fd, io->buf, 255); + memmove(io->buf, io->buf+255, io->i-255); + io->i -= 255; + } +} + +/* + * LZW encoder + */ +static +void +encode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata) +{ + int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel; + int CTM, EOD, codesize, ld0, datai, x, ld, pm; + int nentry, maxentry, early; + Entry *e, *oe; + IO *io; + Entry **hash; + + first = 1; + ld = drawlog2[depth]; + /* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */ + ld0 = ld; + if(ld0 == 0) + ld0 = 1; + codesize = (1<<ld0); + CTM = 1<<codesize; + EOD = CTM+1; + + io = gifmalloc(sizeof(IO)); + io->fd = fd; + sreg = 0; + nbits = 0; + bitsperpixel = 1<<ld; + pm = (1<<bitsperpixel)-1; + + datai = 0; + x = r.min.x; + hash = gifmalloc(Nhash*sizeof(Entry*)); + +Init: + memset(hash, 0, Nhash*sizeof(Entry*)); + csize = codesize+1; + nentry = EOD+1; + maxentry = (1<<csize); + for(i = 0; i<nentry; i++){ + e = &tbl[i]; + h = (e->prefix<<24) | (e->exten<<8); + h %= Nhash; + if(h < 0) + h += Nhash; + e->next = hash[h]; + hash[h] = e; + } + prefix = -1; + if(first) + output(io, CTM, csize); + first = 0; + + /* + * Scan over pixels. Because of partially filled bytes on ends of scan lines, + * which must be ignored in the data stream passed to GIF, this is more + * complex than we'd like. + */ +Next: + for(;;){ + if(ld != 3){ + /* beginning of scan line is difficult; prime the shift register */ + if(x == r.min.x){ + if(datai == ndata) + break; + sreg = data[datai++]; + nbits = 8-((x&(7>>ld))<<ld); + } + x++; + if(x == r.max.x) + x = r.min.x; + } + if(nbits == 0){ + if(datai == ndata) + break; + sreg = data[datai++]; + nbits = 8; + } + nbits -= bitsperpixel; + c = sreg>>nbits & pm; + h = prefix<<24 | c<<8; + h %= Nhash; + if(h < 0) + h += Nhash; + oe = nil; + for(e = hash[h]; e!=nil; e=e->next){ + if(e->prefix == prefix && e->exten == c){ + if(oe != nil){ + oe->next = e->next; + e->next = hash[h]; + hash[h] = e; + } + prefix = e->index; + goto Next; + } + oe = e; + } + + output(io, prefix, csize); + early = 0; /* peculiar tiff feature here for reference */ + if(nentry == maxentry-early){ + if(csize == 12){ + nbits += bitsperpixel; /* unget pixel */ + x--; + if(ld != 3 && x == r.min.x) + datai--; + output(io, CTM, csize); + goto Init; + } + csize++; + maxentry = (1<<csize); + } + + e = &tbl[nentry]; + e->prefix = prefix; + e->exten = c; + e->next = hash[h]; + hash[h] = e; + + prefix = c; + nentry++; + } + + output(io, prefix, csize); + output(io, EOD, csize); + output(io, -1, csize); + free(io); + free(hash); +} + +static +void* +gifmalloc(ulong sz) +{ + void *v; + v = malloc(sz); + if(v == nil) { + fprint(2, "WriteGIF: out of memory allocating %ld\n", sz); +abort(); + exits("mem"); + } + memset(v, 0, sz); + return v; +} diff --git a/src/cmd/jpg/writepng.c b/src/cmd/jpg/writepng.c new file mode 100644 index 00000000..44bfe10c --- /dev/null +++ b/src/cmd/jpg/writepng.c @@ -0,0 +1,220 @@ +// based on PNG 1.2 specification, July 1999 (see also rfc2083) +// Alpha is not supported yet because of lack of industry acceptance and +// because Plan9 Image uses premultiplied alpha, so png can't be lossless. +// Only 24bit color supported, because 8bit may as well use GIF. + +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <ctype.h> +#include <bio.h> +#include <flate.h> +#include "imagefile.h" + +enum{ IDATSIZE = 20000, + FilterNone = 0, +}; + +typedef struct ZlibR{ + uchar *data; + int width; + int nrow, ncol; + int row, col; // next pixel to send +} ZlibR; + +typedef struct ZlibW{ + Biobuf *bo; + uchar *buf; + uchar *b; // next place to write + uchar *e; // past end of buf +} ZlibW; + +static ulong *crctab; +static uchar PNGmagic[] = {137,80,78,71,13,10,26,10}; + +static void +put4(uchar *a, ulong v) +{ + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +static void +chunk(Biobuf *bo, char *type, uchar *d, int n) +{ + uchar buf[4]; + ulong crc = 0; + + if(strlen(type) != 4) + return; + put4(buf, n); + Bwrite(bo, buf, 4); + Bwrite(bo, type, 4); + Bwrite(bo, d, n); + crc = blockcrc(crctab, crc, type, 4); + crc = blockcrc(crctab, crc, d, n); + put4(buf, crc); + Bwrite(bo, buf, 4); +} + +static int +zread(void *va, void *buf, int n) +{ + ZlibR *z = va; + int nrow = z->nrow; + int ncol = z->ncol; + uchar *b = buf, *e = b+n, *img; + int i, pixels; // number of pixels in row that can be sent now + + while(b+3 <= e){ // loop over image rows + if(z->row >= nrow) + break; + if(z->col==0) + *b++ = FilterNone; + pixels = (e-b)/3; + if(pixels > ncol - z->col) + pixels = ncol - z->col; + img = z->data + z->width * z->row + 3 * z->col; + + // Plan 9 image format is BGR?!!! + // memmove(b, img, 3*pixels); + // b += 3*pixels; + for(i=0; i<pixels; i++, img += 3){ + *b++ = img[2]; + *b++ = img[1]; + *b++ = img[0]; + } + + z->col += pixels; + if(z->col >= ncol){ + z->col = 0; + z->row++; + } + } + return b - (uchar*)buf; +} + +static void +IDAT(ZlibW *z) +{ + chunk(z->bo, "IDAT", z->buf, z->b - z->buf); + z->b = z->buf; +} + +static int +zwrite(void *va, void *buf, int n) +{ + ZlibW *z = va; + uchar *b = buf, *e = b+n; + int m; + + while(b < e){ // loop over IDAT chunks + m = z->e - z->b; + if(m > e - b) + m = e - b; + memmove(z->b, b, m); + z->b += m; + b += m; + if(z->b >= z->e) + IDAT(z); + } + return n; +} + +static Memimage* +memRGB(Memimage *i) +{ + Memimage *ni; + + if(i->chan == RGB24) + return i; + + ni = allocmemimage(i->r, RGB24); + if(ni == nil) + return ni; + memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); + return ni; +} + +char* +memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II) +{ + uchar buf[200], *h; + ulong vgamma; + int err, n; + ZlibR zr; + ZlibW zw; + int nrow = r->r.max.y - r->r.min.y; + int ncol = r->r.max.x - r->r.min.x; + Tm *tm; + Memimage *rgb; + + rgb = memRGB(r); + if(rgb == nil) + return "allocmemimage nil"; + crctab = mkcrctab(0xedb88320); + if(crctab == nil) + sysfatal("mkcrctab error"); + deflateinit(); + + Bwrite(bo, PNGmagic, sizeof PNGmagic); + // IHDR chunk + h = buf; + put4(h, ncol); h += 4; + put4(h, nrow); h += 4; + *h++ = 8; // bit depth = 24 bit per pixel + *h++ = 2; // color type = rgb + *h++ = 0; // compression method = deflate + *h++ = 0; // filter method + *h++ = 0; // interlace method = no interlace + chunk(bo, "IHDR", buf, h-buf); + + tm = gmtime(time(0)); + h = buf; + *h++ = (tm->year + 1900)>>8; + *h++ = (tm->year + 1900)&0xff; + *h++ = tm->mon + 1; + *h++ = tm->mday; + *h++ = tm->hour; + *h++ = tm->min; + *h++ = tm->sec; + chunk(bo, "tIME", buf, h-buf); + + if(II->fields_set & II_GAMMA){ + vgamma = II->gamma*100000; + put4(buf, vgamma); + chunk(bo, "gAMA", buf, 4); + } + + if(II->fields_set & II_COMMENT){ + strncpy((char*)buf, "Comment", sizeof buf); + n = strlen((char*)buf)+1; // leave null between Comment and text + strncpy((char*)(buf+n), II->comment, sizeof buf - n); + chunk(bo, "tEXt", buf, n+strlen((char*)buf+n)); + } + + // image chunks + zr.nrow = nrow; + zr.ncol = ncol; + zr.width = rgb->width * sizeof(ulong); + zr.data = rgb->data->bdata; + zr.row = zr.col = 0; + zw.bo = bo; + zw.buf = malloc(IDATSIZE); + zw.b = zw.buf; + zw.e = zw.b + IDATSIZE; + err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0); + if(zw.b > zw.buf) + IDAT(&zw); + free(zw.buf); + if(err) + sysfatal("deflatezlib %s\n", flateerr(err)); + chunk(bo, "IEND", nil, 0); + + if(r != rgb) + freememimage(rgb); + return nil; +} diff --git a/src/cmd/jpg/writeppm.c b/src/cmd/jpg/writeppm.c new file mode 100644 index 00000000..c8378652 --- /dev/null +++ b/src/cmd/jpg/writeppm.c @@ -0,0 +1,164 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <bio.h> +#include "imagefile.h" + +#define MAXLINE 70 + +/* + * Write data + */ +static +char* +writedata(Biobuf *fd, Image *image, Memimage *memimage) +{ + char *err; + uchar *data; + int i, x, y, ndata, depth, col, pix, xmask, pmask; + ulong chan; + Rectangle r; + + if(memimage != nil){ + r = memimage->r; + depth = memimage->depth; + chan = memimage->chan; + }else{ + r = image->r; + depth = image->depth; + chan = image->chan; + } + + /* + * Read image data into memory + * potentially one extra byte on each end of each scan line + */ + ndata = Dy(r)*(2+Dx(r)*depth/8); + data = malloc(ndata); + if(data == nil) + return "WritePPM: malloc failed"; + if(memimage != nil) + ndata = unloadmemimage(memimage, r, data, ndata); + else + ndata = unloadimage(image, r, data, ndata); + if(ndata < 0){ + err = malloc(ERRMAX); + if(err == nil) + return "WritePPM: malloc failed"; + snprint(err, ERRMAX, "WriteGIF: %r"); + free(data); + return err; + } + + /* Encode and emit the data */ + col = 0; + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + pmask = (1<<depth)-1; + xmask = 7>>drawlog2[depth]; + for(y=r.min.y; y<r.max.y; y++){ + i = (y-r.min.y)*bytesperline(r, depth); + for(x=r.min.x; x<r.max.x; x++){ + pix = (data[i]>>depth*((xmask-x)&xmask))&pmask; + if(((x+1)&xmask) == 0) + i++; + col += Bprint(fd, "%d ", pix); + if(col >= MAXLINE-(2+1)){ + Bprint(fd, "\n"); + col = 0; + }else + col += Bprint(fd, " "); + } + } + break; + case GREY8: + for(i=0; i<ndata; i++){ + col += Bprint(fd, "%d ", data[i]); + if(col >= MAXLINE-(4+1)){ + Bprint(fd, "\n"); + col = 0; + }else + col += Bprint(fd, " "); + } + break; + case RGB24: + for(i=0; i<ndata; i+=3){ + col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]); + if(col >= MAXLINE-(4+4+4+1)){ + Bprint(fd, "\n"); + col = 0; + }else + col += Bprint(fd, " "); + } + break; + default: + return "WritePPM: can't handle channel type"; + } + + return nil; +} + +static +char* +writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment) +{ + char *err; + + switch(chan){ + case GREY1: + Bprint(fd, "P1\n"); + break; + case GREY2: + case GREY4: + case GREY8: + Bprint(fd, "P2\n"); + break; + case RGB24: + Bprint(fd, "P3\n"); + break; + default: + return "WritePPM: can't handle channel type"; + } + + if(comment!=nil && comment[0]!='\0'){ + Bprint(fd, "# %s", comment); + if(comment[strlen(comment)-1] != '\n') + Bprint(fd, "\n"); + } + Bprint(fd, "%d %d\n", Dx(r), Dy(r)); + + /* maximum pixel value */ + switch(chan){ + case GREY2: + Bprint(fd, "%d\n", 3); + break; + case GREY4: + Bprint(fd, "%d\n", 15); + break; + case GREY8: + case RGB24: + Bprint(fd, "%d\n", 255); + break; + } + + err = writedata(fd, image, memimage); + + Bprint(fd, "\n"); + Bflush(fd); + return err; +} + +char* +writeppm(Biobuf *fd, Image *image, char *comment) +{ + return writeppm0(fd, image, nil, image->r, image->chan, comment); +} + +char* +memwriteppm(Biobuf *fd, Memimage *memimage, char *comment) +{ + return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment); +} diff --git a/src/cmd/jpg/writerawimage.c b/src/cmd/jpg/writerawimage.c new file mode 100644 index 00000000..26e0cd16 --- /dev/null +++ b/src/cmd/jpg/writerawimage.c @@ -0,0 +1,206 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include "imagefile.h" + +/* + * Hacked version for writing from Rawimage to file. + * Assumes 8 bits per component. + */ + +#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ +#define NHASH (1<<(HSHIFT*NMATCH)) +#define HMASK (NHASH-1) +#define hupdate(h, c) ((((h)<<HSHIFT)^(c))&HMASK) +typedef struct Hlist Hlist; +struct Hlist{ + uchar *s; + Hlist *next, *prev; +}; + +int +writerawimage(int fd, Rawimage *i) +{ + uchar *outbuf, *outp, *eout; /* encoded data, pointer, end */ + uchar *loutp; /* start of encoded line */ + Hlist *hash; /* heads of hash chains of past strings */ + Hlist *chain, *hp; /* hash chain members, pointer */ + Hlist *cp; /* next Hlist to fall out of window */ + int h; /* hash value */ + uchar *line, *eline; /* input line, end pointer */ + uchar *data, *edata; /* input buffer, end pointer */ + ulong n; /* length of input buffer */ + int bpl; /* input line length */ + int offs, runlen; /* offset, length of consumed data */ + uchar dumpbuf[NDUMP]; /* dump accumulator */ + int ndump; /* length of dump accumulator */ + int ncblock; /* size of buffer */ + Rectangle r; + uchar *p, *q, *s, *es, *t; + char hdr[11+5*12+1], buf[16]; + ulong desc; + + r = i->r; + switch(i->chandesc){ + default: + werrstr("can't handle chandesc %d", i->chandesc); + return -1; + case CY: + bpl = Dx(r); + desc = GREY8; + break; + case CYA16: + bpl = 2*Dx(r); + desc = CHAN2(CGrey, 8, CAlpha, 8); + break; + case CRGBV: + bpl = Dx(r); + desc = CMAP8; + break; + case CRGBVA16: + bpl = 2*Dx(r); + desc = CHAN2(CMap, 8, CAlpha, 8); + break; + case CRGB24: + bpl = 3*Dx(r); + desc = RGB24; + break; + case CRGBA32: + bpl = 4*Dx(r); + desc = RGBA32; + break; + } + ncblock = _compblocksize(r, bpl/Dx(r)); + outbuf = malloc(ncblock); + hash = malloc(NHASH*sizeof(Hlist)); + chain = malloc(NMEM*sizeof(Hlist)); + if(outbuf == 0 || hash == 0 || chain == 0){ + ErrOut: + free(outbuf); + free(hash); + free(chain); + return -1; + } + n = Dy(r)*bpl; + data = i->chans[0]; + sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ", + chantostr(buf, desc), r.min.x, r.min.y, r.max.x, r.max.y); + if(write(fd, hdr, 11+5*12) != 11+5*12){ + werrstr("i/o error writing header"); + goto ErrOut; + } + edata = data+n; + eout = outbuf+ncblock; + line = data; + r.max.y = r.min.y; + while(line != edata){ + memset(hash, 0, NHASH*sizeof(Hlist)); + memset(chain, 0, NMEM*sizeof(Hlist)); + cp = chain; + h = 0; + outp = outbuf; + for(n = 0; n != NMATCH; n++) + h = hupdate(h, line[n]); + loutp = outbuf; + while(line != edata){ + ndump = 0; + eline = line+bpl; + for(p = line; p != eline; ){ + if(eline-p < NRUN) + es = eline; + else + es = p+NRUN; + q = 0; + runlen = 0; + for(hp = hash[h].next; hp; hp = hp->next){ + s = p + runlen; + if(s >= es) + continue; + t = hp->s + runlen; + for(; s >= p; s--) + if(*s != *t--) + goto matchloop; + t += runlen+2; + s += runlen+2; + for(; s < es; s++) + if(*s != *t++) + break; + n = s-p; + if(n > runlen){ + runlen = n; + q = hp->s; + if(n == NRUN) + break; + } + matchloop: ; + } + if(runlen < NMATCH){ + if(ndump == NDUMP){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + dumpbuf[ndump++] = *p; + runlen = 1; + } + else{ + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + offs = p-q-1; + if(eout-outp < 2) + goto Bfull; + *outp++ = ((runlen-NMATCH)<<2) + (offs>>8); + *outp++ = offs&255; + } + for(q = p+runlen; p != q; p++){ + if(cp->prev) + cp->prev->next = 0; + cp->next = hash[h].next; + cp->prev = &hash[h]; + if(cp->next) + cp->next->prev = cp; + cp->prev->next = cp; + cp->s = p; + if(++cp == &chain[NMEM]) + cp = chain; + if(edata-p > NMATCH) + h = hupdate(h, p[NMATCH]); + } + } + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + } + line = eline; + loutp = outp; + r.max.y++; + } + Bfull: + if(loutp == outbuf){ + werrstr("compressor out of sync"); + goto ErrOut; + } + n = loutp-outbuf; + sprint(hdr, "%11d %11ld ", r.max.y, n); + write(fd, hdr, 2*12); + write(fd, outbuf, n); + r.min.y = r.max.y; + } + free(outbuf); + free(hash); + free(chain); + return 0; +} diff --git a/src/cmd/jpg/yuv.c b/src/cmd/jpg/yuv.c new file mode 100644 index 00000000..929ccf39 --- /dev/null +++ b/src/cmd/jpg/yuv.c @@ -0,0 +1,211 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <event.h> +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +ulong outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readyuv(int fd, int colorspace); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "yuv: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x+Dx(image->r); + r.max.y = r.min.y+Dy(image->r); + border(screen, r, -Border, nil, ZP); + draw(screen, r, image, nil, image->r.min); + flushimage(display, 1); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + + ARGBEGIN{ + case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ + threeflag++; + /* fall through */ + case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + case 'c': /* produce encoded, compressed, bitmap file; no display by default */ + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + case 'd': /* suppress display of image */ + dflag++; + break; + case 'e': /* disable floyd-steinberg error diffusion */ + eflag++; + break; + case 'k': /* force black and white */ + defaultcolor = 0; + outchan = GREY8; + break; + case 'v': /* force RGBV */ + defaultcolor = 0; + outchan = CMAP8; + break; + case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + fprint(2, "usage: yuv -39cdektv [file.yuv ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, "<stdin>"); + else{ + for(i=0; i<argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "yuv: can't open %s: %r\n", argv[i]); + err = "open"; + }else{ + err = show(fd, argv[i]); + close(fd); + } + if((nineflag || cflag) && argc>1 && err==nil){ + fprint(2, "yuv: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "yuv: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readyuv(fd, CYCbCr); + if(array == nil || array[0]==nil){ + fprint(2, "yuv: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "yuv: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "yuv: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "yuv: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "yuv: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "yuv: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; j<r->nchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} |