From e8562ff537a562727de5ad9a15c3387e7d2ab7bd Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Tue, 3 Jan 2017 22:31:09 +0100 Subject: [PATCH] cmd/pict: import 9front's graphical tools --- sys/src/cmd/pict/README.txt | 8 + sys/src/cmd/pict/bmp.c | 219 ++++ sys/src/cmd/pict/bmp.h | 39 + sys/src/cmd/pict/build.json | 47 + sys/src/cmd/pict/close.c | 121 ++ sys/src/cmd/pict/gif.c | 437 +++++++ sys/src/cmd/pict/ico.c | 627 ++++++++++ sys/src/cmd/pict/imagefile.h | 86 ++ sys/src/cmd/pict/jpegdump.c | 346 ++++++ sys/src/cmd/pict/jpg.c | 357 ++++++ sys/src/cmd/pict/multichan.c | 54 + sys/src/cmd/pict/onechan.c | 229 ++++ sys/src/cmd/pict/png.c | 250 ++++ sys/src/cmd/pict/ppm.c | 219 ++++ sys/src/cmd/pict/readbmp.c | 626 ++++++++++ sys/src/cmd/pict/readgif.c | 545 +++++++++ sys/src/cmd/pict/readjpg.c | 1659 ++++++++++++++++++++++++++ sys/src/cmd/pict/readpng.c | 531 +++++++++ sys/src/cmd/pict/readppm.c | 237 ++++ sys/src/cmd/pict/readtga.c | 510 ++++++++ sys/src/cmd/pict/readtif.c | 1862 ++++++++++++++++++++++++++++++ sys/src/cmd/pict/readv210.c | 203 ++++ sys/src/cmd/pict/readyuv.c | 215 ++++ sys/src/cmd/pict/rgbrgbv.c | 70 ++ sys/src/cmd/pict/rgbv.h | 295 +++++ sys/src/cmd/pict/rgbycc.c | 121 ++ sys/src/cmd/pict/tga.c | 223 ++++ sys/src/cmd/pict/tif.c | 256 ++++ sys/src/cmd/pict/togif.c | 178 +++ sys/src/cmd/pict/toico.c | 322 ++++++ sys/src/cmd/pict/tojpg.c | 70 ++ sys/src/cmd/pict/topng.c | 70 ++ sys/src/cmd/pict/toppm.c | 91 ++ sys/src/cmd/pict/torgbv.c | 339 ++++++ sys/src/cmd/pict/totif.c | 157 +++ sys/src/cmd/pict/totruecolor.c | 162 +++ sys/src/cmd/pict/v210.c | 220 ++++ sys/src/cmd/pict/writegif.c | 574 +++++++++ sys/src/cmd/pict/writejpg.c | 915 +++++++++++++++ sys/src/cmd/pict/writepng.c | 267 +++++ sys/src/cmd/pict/writeppm.c | 214 ++++ sys/src/cmd/pict/writerawimage.c | 206 ++++ sys/src/cmd/pict/writetif.c | 1333 +++++++++++++++++++++ sys/src/cmd/pict/ycbcr.h | 294 +++++ sys/src/cmd/pict/yuv.c | 220 ++++ 45 files changed, 16024 insertions(+) create mode 100644 sys/src/cmd/pict/README.txt create mode 100644 sys/src/cmd/pict/bmp.c create mode 100644 sys/src/cmd/pict/bmp.h create mode 100644 sys/src/cmd/pict/build.json create mode 100644 sys/src/cmd/pict/close.c create mode 100644 sys/src/cmd/pict/gif.c create mode 100644 sys/src/cmd/pict/ico.c create mode 100644 sys/src/cmd/pict/imagefile.h create mode 100644 sys/src/cmd/pict/jpegdump.c create mode 100644 sys/src/cmd/pict/jpg.c create mode 100644 sys/src/cmd/pict/multichan.c create mode 100644 sys/src/cmd/pict/onechan.c create mode 100644 sys/src/cmd/pict/png.c create mode 100644 sys/src/cmd/pict/ppm.c create mode 100644 sys/src/cmd/pict/readbmp.c create mode 100644 sys/src/cmd/pict/readgif.c create mode 100644 sys/src/cmd/pict/readjpg.c create mode 100644 sys/src/cmd/pict/readpng.c create mode 100644 sys/src/cmd/pict/readppm.c create mode 100644 sys/src/cmd/pict/readtga.c create mode 100644 sys/src/cmd/pict/readtif.c create mode 100644 sys/src/cmd/pict/readv210.c create mode 100644 sys/src/cmd/pict/readyuv.c create mode 100644 sys/src/cmd/pict/rgbrgbv.c create mode 100644 sys/src/cmd/pict/rgbv.h create mode 100644 sys/src/cmd/pict/rgbycc.c create mode 100644 sys/src/cmd/pict/tga.c create mode 100644 sys/src/cmd/pict/tif.c create mode 100644 sys/src/cmd/pict/togif.c create mode 100644 sys/src/cmd/pict/toico.c create mode 100644 sys/src/cmd/pict/tojpg.c create mode 100644 sys/src/cmd/pict/topng.c create mode 100644 sys/src/cmd/pict/toppm.c create mode 100644 sys/src/cmd/pict/torgbv.c create mode 100644 sys/src/cmd/pict/totif.c create mode 100644 sys/src/cmd/pict/totruecolor.c create mode 100644 sys/src/cmd/pict/v210.c create mode 100644 sys/src/cmd/pict/writegif.c create mode 100644 sys/src/cmd/pict/writejpg.c create mode 100644 sys/src/cmd/pict/writepng.c create mode 100644 sys/src/cmd/pict/writeppm.c create mode 100644 sys/src/cmd/pict/writerawimage.c create mode 100644 sys/src/cmd/pict/writetif.c create mode 100644 sys/src/cmd/pict/ycbcr.h create mode 100644 sys/src/cmd/pict/yuv.c diff --git a/sys/src/cmd/pict/README.txt b/sys/src/cmd/pict/README.txt new file mode 100644 index 0000000..66cb4a3 --- /dev/null +++ b/sys/src/cmd/pict/README.txt @@ -0,0 +1,8 @@ +These are the graphic tools from 9front as documented at + + http://man.cat-v.org/9front/1/jpg + +The whole pict/ folder should be an external package as it's not +actually required for a minimal system. +It's included in the main Jehanne's source tree because we don't have +a package management system yet. diff --git a/sys/src/cmd/pict/bmp.c b/sys/src/cmd/pict/bmp.c new file mode 100644 index 0000000..2d75790 --- /dev/null +++ b/sys/src/cmd/pict/bmp.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readbmp(int fd, int colorspace); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +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 = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->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: bmp -39cdektv [file.bmp ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "bmp: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "bmp: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readbmp(fd, CRGB); + if(array == nil || array[0]==nil){ + fprint(2, "bmp: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "bmp: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "bmp: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "bmp: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "bmp: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "bmp: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/sys/src/cmd/pict/bmp.h b/sys/src/cmd/pict/bmp.h new file mode 100644 index 0000000..eaa7d56 --- /dev/null +++ b/sys/src/cmd/pict/bmp.h @@ -0,0 +1,39 @@ + +#define BMP_RGB 0 +#define BMP_RLE8 1 +#define BMP_RLE4 2 +#define BMP_BITFIELDS 3 + +typedef struct { + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +} Rgb; + +#define Filehdrsz 14 + +typedef struct { + short type; + long size; /* file size, not structure 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/sys/src/cmd/pict/build.json b/sys/src/cmd/pict/build.json new file mode 100644 index 0000000..2eb363c --- /dev/null +++ b/sys/src/cmd/pict/build.json @@ -0,0 +1,47 @@ +{ + "Cmd": { + "Include": [ + "../cmd.json" + ], + "Install": "/arch/$ARCH/cmd/pict/", + "SourceFiles": [ + "torgbv.c", + "totruecolor.c", + "writerawimage.c", + "readjpg.c", + "writejpg.c", + "multichan.c", + "readgif.c", + "writegif.c", + "onechan.c", + "readpng.c", + "writepng.c", + "readppm.c", + "writeppm.c", + "readtif.c", + "writetif.c", + "readyuv.c", + "readbmp.c", + "readtga.c", + "readv210.c" + ], + "SourceFilesCmd": [ + "jpg.c", + "tojpg.c", + "gif.c", + "togif.c", + "ppm.c", + "toppm.c", + "png.c", + "topng.c", + "tif.c", + "totif.c", + "yuv.c", + "ico.c", + "toico.c", + "bmp.c", + "tga.c", + "v210.c" + ] + } +} diff --git a/sys/src/cmd/pict/close.c b/sys/src/cmd/pict/close.c new file mode 100644 index 0000000..7a260c1 --- /dev/null +++ b/sys/src/cmd/pict/close.c @@ -0,0 +1,121 @@ +#include +#include +#include + +float c1 = 1.402; +float c2 = 0.34414; +float c3 = 0.71414; +float c4 = 1.772; + + +int +closest(int Y, int Cb, int Cr) +{ + double r, g, b; + double diff, min; + int rgb, R, G, B, v, i; + int y1, cb1, cr1; + + Cb -= 128; + Cr -= 128; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + +//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b); + + min = 1000000.; + v = 1000; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + R = (rgb >> 16) & 0xFF; + G = (rgb >> 8) & 0xFF; + B = (rgb >> 0) & 0xFF; + diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b); +// y1 = 0.5870*G + 0.114*B + 0.299*R; +// cb1 = (B-y1)/1.772; +// cr1 = (R-y1)/1.402; + if(diff < min){ +// if(Y==0 && y1!=0) +// continue; +// if(Y==256-16 && y1<256-16) +// continue; +// if(Cb==0 && cb1!=0) +// continue; +// if(Cb==256-16 && cb1<256-16) +// continue; +// if(Cr==0 && cr1!=0) +// continue; +// if(Cr==256-16 && cr1<256-16) +// continue; +//print("%d %d %d\n", R, G, B); + min = diff; + v = i; + } + } + if(v > 255) + abort(); + return v; +} + +#define SHIFT 5 +#define INC (1<>= 8-SHIFT; + cb >>= 8-SHIFT; + cr >>= 8-SHIFT; + cp = col[cr+INC*(cb+INC*y)]; + while(cp != nil){ + if(cp->col == c) + return; + cp = cp->next; + } + cp = malloc(sizeof(Color)); + cp->col = c; + cp->next = col[cr+INC*(cb+INC*y)]; + col[cr+INC*(cb+INC*y)] = cp; +} + +void +main(void) +{ + int y, cb, cr, n; + Color *cp; + + for(y=0; y<256; y++){ + for(cb=0; cb<256; cb++) + for(cr=0;cr<256;cr++) + add(closest(y, cb, cr), y, cb, cr); + fprint(2, "%d done\n", y); + } + for(y=0; ynext; + } + cp = col[y]; + while(cp != nil){ + n++; + print("%d ", cp->col); + cp = cp->next; + } + print("\n"); + } +} diff --git a/sys/src/cmd/pict/gif.c b/sys/src/cmd/pict/gif.c new file mode 100644 index 0000000..1b6fdbf --- /dev/null +++ b/sys/src/cmd/pict/gif.c @@ -0,0 +1,437 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +Image **allims; +int which; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rectangle +imager(void) +{ + Point p1, p2; + + if(allims==nil || allims[0]==nil) + return screen->r; + + p1 = addpt(divpt(subpt(allims[0]->r.max, allims[0]->r.min), 2), allims[0]->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(allims[0]->r, subpt(p2, p1)); +} + +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); + draw(screen, r, allims[which], nil, allims[which]->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: gif -39cdektv [file.gif ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "gif: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +Image* +transparency(Rawimage *r, char *name) +{ + Image *i; + int j, index; + unsigned char *pic, *mpic, *mask; + + if((r->gifflags&TRANSP) == 0) + return nil; + i = allocimage(display, r->r, GREY8, 0, 0); + if(i == nil){ + fprint(2, "gif: allocimage for mask of %s failed: %r\n", name); + return nil; + } + pic = r->chans[0]; + mask = malloc(r->chanlen); + if(mask == nil){ + fprint(2, "gif: malloc for mask of %s failed: %r\n", name); + freeimage(i); + return nil; + } + index = r->giftrindex; + mpic = mask; + for(j=0; jchanlen; j++) + if(*pic++ == index) + *mpic++ = 0; + else + *mpic++ = 0xFF; + if(loadimage(i, i->r, mask, r->chanlen) < 0){ + fprint(2, "gif: loadimage for mask of %s failed: %r\n", name); + free(mask); + freeimage(i); + return nil; + } + free(mask); + return i; +} + +/* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */ +unsigned char* +expand(unsigned char *u, int chanlen, int nchan) +{ + int j, k; + unsigned char *v, *up, *vp; + + v = malloc(chanlen*(nchan+1)); + if(v == nil){ + fprint(2, "gif: malloc fails: %r\n"); + exits("malloc"); + } + up = u; + vp = v; + for(j=0; jchans[0] = expand(i->chans[0], i->chanlen/1, 1); + i->chanlen = 2*(i->chanlen/1); + i->chandesc = CRGBVA16; + outchan = CHAN2(CMap, 8, CAlpha, 8); + break; + + case GREY8: + i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); + i->chanlen = 2*(i->chanlen/1); + i->chandesc = CYA16; + outchan = CHAN2(CGrey, 8, CAlpha, 8); + break; + + case RGB24: + i->chans[0] = expand(i->chans[0], i->chanlen/3, 3); + i->chanlen = 4*(i->chanlen/3); + i->chandesc = CRGBA32; + outchan = RGBA32; + break; + + default: + chantostr(buf, outchan); + fprint(2, "gif: can't add alpha to type %s\n", buf); + exits("err"); + } +} + +/* + * Called only when writing output. If the output is RGBA32, + * we must write four bytes per pixel instead of two. + * There's always at least two: data plus alpha. + * r is used only for reference; the image is already in c. + */ +void +blackout(Rawimage *r, Rawimage *c) +{ + int i, trindex; + unsigned char *rp, *cp; + + rp = r->chans[0]; + cp = c->chans[0]; + trindex = r->giftrindex; + if(outchan == RGBA32) + for(i=0; ichanlen; i++){ + if(*rp == trindex){ + *cp++ = 0x00; + *cp++ = 0x00; + *cp++ = 0x00; + *cp++ = 0x00; + }else{ + *cp++ = 0xFF; + cp += 3; + } + rp++; + } + else + for(i=0; ichanlen; i++){ + if(*rp == trindex){ + *cp++ = 0x00; + *cp++ = 0x00; + }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 *tmp, *msk, *img, *dst, **ims; + Rectangle r; + int j, k, n, ch, nloop, loopcount, dt; + char *err; + char buf[32]; + + err = nil; + images = readgif(fd, CRGB, dflag); + if(images == nil){ + fprint(2, "gif: decode %s failed: %r\n", name); + return "decode"; + } + for(n=0; images[n]; n++) + if(n == 0) + r = images[n]->r; + else + combinerect(&r, images[n]->r); + tmp = nil; + ims = malloc((n+1)*sizeof(Image*)); + rgbv = malloc((n+1)*sizeof(Rawimage*)); + if(rgbv==nil || ims==nil){ + fprint(2, "gif: malloc of masks for %s failed: %r\n", name); + err = "malloc"; + goto Return; + } + memset(ims, 0, (n+1)*sizeof(Image*)); + memset(rgbv, 0, (n+1)*sizeof(Rawimage*)); + if(!dflag){ + if(init() < 0){ + err = "initdraw"; + goto Return; + } + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + + for(k=0; kchandesc==CY && threeflag==0)) + rgbv[k] = totruecolor(images[k], CY); + else + rgbv[k] = totruecolor(images[k], CRGB24); + } + if(rgbv[k] == nil){ + fprint(2, "gif: converting %s to local format failed: %r\n", name); + err = "torgbv"; + goto Return; + } + if(!dflag){ + msk = transparency(images[k], name); + if(rgbv[k]->chandesc == CY) + img = allocimage(display, rgbv[k]->r, GREY8, 0, 0); + else + img = allocimage(display, rgbv[k]->r, outchan, 0, 0); + if(tmp == nil) + tmp = allocimage(display, r, img->chan, 0, DWhite); + ims[k]= dst = allocimage(display, r, tmp->chan, 0, DWhite); + if(tmp == nil || img == nil || dst == nil){ + fprint(2, "gif: allocimage %s failed: %r\n", name); + err = "allocimage"; + goto Return; + } + if(loadimage(img, img->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){ + fprint(2, "gif: loadimage %s failed: %r\n", name); + err = "loadimage"; + goto Return; + } + switch((images[k]->gifflags>>2)&7){ + default: + draw(tmp, img->r, img, msk, img->r.min); + draw(dst, tmp->r, tmp, nil, tmp->r.min); + break; + case 2: + draw(tmp, img->r, display->white, msk, img->r.min); + /* no break */ + case 3: + draw(dst, tmp->r, tmp, nil, tmp->r.min); + draw(dst, img->r, img, msk, img->r.min); + break; + } + freeimage(msk); + freeimage(img); + } + } + if(tmp) + freeimage(tmp); + + allims = ims; + loopcount = images[0]->gifloopcount; + if(!dflag){ + nloop = 0; + do{ + for(k=0; kgifdelay*10; + if(dt < 50) + dt = 50; + while(n==1 || ecankbd()){ + /* an odd, democratic list */ + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + exits(nil); + if(ch == '\n') + goto Out; + }sleep(dt); + } + /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/ + }while(loopcount==0 || ++nloopclipr, display->white, nil, ZP); + } + if(nineflag){ + if(images[0]->gifflags&TRANSP){ + addalpha(rgbv[0]); + blackout(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]); + blackout(images[0], rgbv[0]); + } + if(writerawimage(1, rgbv[0]) < 0){ + fprint(2, "gif: %s: write error: %r\n", name); + return "write"; + } + } + + Return: + allims = nil; + for(k=0; images[k]; k++){ + for(j=0; jnchans; j++) + free(images[k]->chans[j]); + free(images[k]->cmap); + if(rgbv[k]) + free(rgbv[k]->chans[0]); + freeimage(ims[k]); + free(images[k]); + free(rgbv[k]); + } + free(images); + free(ims); + return err; +} diff --git a/sys/src/cmd/pict/ico.c b/sys/src/cmd/pict/ico.c new file mode 100644 index 0000000..6125ca7 --- /dev/null +++ b/sys/src/cmd/pict/ico.c @@ -0,0 +1,627 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "imagefile.h" + +typedef struct Icon Icon; +struct Icon +{ + Icon *next; + + unsigned char w; /* icon width */ + unsigned char h; /* icon height */ + unsigned short ncolor; /* number of colors */ + unsigned short nplane; /* number of bit planes */ + unsigned short bits; /* bits per pixel */ + uint32_t len; /* length of data */ + uint32_t offset; /* file offset to data */ + + Memimage *img; + Memimage *mask; + + Rectangle r; /* relative */ + Rectangle sr; /* abs */ +}; + +typedef struct Header Header; +struct Header +{ + uint n; + Icon *first; + Icon *last; +}; + +int debug; +int cflag; +Mouse mouse; +Header h; +Image *background; + +unsigned short +gets(unsigned char *p) +{ + return p[0] | (p[1]<<8); +} + +uint32_t +getl(unsigned char *p) +{ + return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +} + +int +Bgetheader(Biobuf *b, Header *h) +{ + unsigned char buf[40]; + Icon *icon; + int i; + + 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] == 0 ? 256 : buf[0]; + icon->h = buf[1] == 0 ? 256 : buf[1]; + icon->ncolor = buf[2] == 0 ? 256 : buf[2]; + 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; +} + +unsigned char* +transcmap(Icon *icon, int ncolor, unsigned char *map) +{ + unsigned char *m, *p; + int i; + + p = m = mallocz(sizeof(int)*(1<bits), 1); + if(m == nil) + sysfatal("malloc: %r"); + for(i = 0; i < ncolor; i++){ + *p++ = rgb2cmap(map[2], map[1], map[0]); + map += 4; + } + return m; +} + +Memimage* +xor2img(Icon *icon, long chan, unsigned char *xor, unsigned char *map) +{ + unsigned char *data; + Memimage *img; + int inxlen; + unsigned char *from, *to; + int s, byte, mask; + int x, y; + + inxlen = 4*((icon->bits*icon->w+31)/32); + img = allocmemimage(Rect(0,0,icon->w,icon->h), chan); + if(img == nil) + return nil; + + if(chan != CMAP8){ + from = xor + icon->h*inxlen; + for(y = 0; y < icon->h; y++){ + from -= inxlen; + loadmemimage(img, Rect(0,y,icon->w,y+1), from, inxlen); + } + return img; + } + + to = data = malloc(icon->w*icon->h); + if(data == nil){ + freememimage(img); + return nil; + } + + /* rotate around the y axis, go to 8 bits, and convert color */ + mask = (1<bits)-1; + for(y = 0; y < icon->h; y++){ + s = -1; + byte = 0; + from = xor + (icon->h - 1 - y)*inxlen; + for(x = 0; x < icon->w; x++){ + if(s < 0){ + byte = *from++; + s = 8-icon->bits; + } + *to++ = map[(byte>>s) & mask]; + s -= icon->bits; + } + } + /* stick in an image */ + loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w); + free(data); + return img; +} + +Memimage* +and2img(Icon *icon, unsigned char *and) +{ + unsigned char *data; + Memimage *img; + int inxlen; + int outxlen; + unsigned char *from, *to; + int x, y; + + inxlen = 4*((icon->w+31)/32); + to = data = malloc(inxlen*icon->h); + if(data == nil) + return nil; + + /* 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 */ + if(img = allocmemimage(Rect(0,0,icon->w,icon->h), GREY1)) + loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen); + + free(data); + return img; +} + +int +Bgeticon(Biobuf *b, Icon *icon) +{ + unsigned char *end; + unsigned char *xor; + unsigned char *and; + unsigned char *cm; + unsigned char *buf; + unsigned char *map2map; + Memimage *img; + unsigned char magic[4]; + int ncolor; + long chan; + + Bseek(b, icon->offset, 0); + if(Bread(b, magic, 4) != 4){ + werrstr("unexpected EOF"); + return -1; + } + if(magic[0] == 137 && memcmp(magic+1, "PNG", 3) == 0){ + Rawimage **png; + + Bseek(b, -4, 1); + png = Breadpng(b, CRGB); + if(png == nil || png[0] == nil) + return -1; + switch(png[0]->chandesc){ + case CY: + chan = GREY8; + break; + case CYA16: + chan = CHAN2(CGrey, 8, CAlpha, 8); + break; + case CRGB24: + chan = RGB24; + break; + case CRGBA32: + chan = RGBA32; + break; + default: + werrstr("bad icon png channel descriptor"); + return -1; + } + icon->mask = nil; + icon->img = allocmemimage(png[0]->r, chan); + loadmemimage(icon->img, icon->img->r, png[0]->chans[0], png[0]->chanlen); + return 0; + } + + if(getl(magic) != 40){ + werrstr("bad icon bmp header"); + return -1; + } + if(icon->len < 40){ + werrstr("bad icon bmp header length"); + return -1; + } + buf = malloc(icon->len); + if(buf == nil) + return -1; + memmove(buf, magic, 4); + if(Bread(b, buf+4, icon->len-4) != icon->len-4){ + werrstr("unexpected EOF"); + return -1; + } + + /* this header's info takes precedence over previous one */ + ncolor = 0; + icon->w = getl(buf+4); + icon->h = getl(buf+8)>>1; + icon->nplane = gets(buf+12); + icon->bits = gets(buf+14); + + /* limit what we handle */ + switch(icon->bits){ + case 1: + case 2: + case 4: + case 8: + ncolor = icon->ncolor; + if(ncolor > (1<bits)) + ncolor = 1<bits; + chan = CMAP8; + break; + case 15: + case 16: + chan = RGB16; + break; + case 24: + chan = RGB24; + break; + case 32: + chan = ARGB32; + 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; + } + + xor = cm = buf + 40; + if(chan == CMAP8) + xor += 4*ncolor; + end = xor + icon->h*4*((icon->bits*icon->w+31)/32); + if(end < buf || end > buf+icon->len){ + werrstr("bad icon length %zux != %lux", end - buf, icon->len); + return -1; + } + + /* translate the color map to a plan 9 one */ + map2map = nil; + if(chan == CMAP8) + map2map = transcmap(icon, ncolor, cm); + + /* convert the images */ + icon->img = xor2img(icon, chan, xor, map2map); + if(icon->img == nil){ + werrstr("xor2img: %r"); + return -1; + } + icon->mask = nil; + + /* check for and mask */ + and = end; + end += icon->h*4*((icon->w+31)/32); + if(end <= buf+icon->len) + icon->mask = and2img(icon, and); + + /* so that we save an image with a white background */ + if(img = allocmemimage(icon->img->r, icon->img->chan)){ + memfillcolor(img, DWhite); + memimagedraw(img, icon->img->r, icon->img, ZP, icon->mask, ZP, SoverD); + freememimage(icon->img); + icon->img = img; + } + + free(buf); + free(map2map); + return 0; +} + +void +usage(void) +{ + fprint(2, "usage: %s [ -c ] [ 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 = writememimage(fd, icon->img); + 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; + + if(icon->mask == nil) + return; + + rv = -1; + snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h); + fd = create(file, OWRITE, 0664); + if(fd >= 0){ + rv = writememimage(fd, icon->mask); + 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, +}; + +Image* +screenimage(Memimage *m) +{ + Rectangle r; + Image *i; + + if(i = allocimage(display, m->r, m->chan, 0, DNofill)){ + r = m->r; + while(r.min.y < m->r.max.y){ + r.max.y = r.min.y+1; + loadimage(i, r, byteaddr(m, r.min), bytesperline(r, m->depth)); + r.min.y++; + } + } + return i; +} + +void +eresized(int new) +{ + Icon *icon; + Image *i; + 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); + if(i = screenimage(icon->img)){ + draw(screen, r, i, nil, ZP); + freeimage(i); + } + border(screen, r, -BORDER, display->black, ZP); + icon->sr = r; + } + flushimage(display, 1); +} + +void +main(int argc, char **argv) +{ + Biobuf in; + Icon *icon; + int num, fd; + Rectangle r; + Event e; + + ARGBEGIN{ + case 'd': + debug = 1; + break; + case 'c': + cflag = 1; + break; + default: + usage(); + }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; + } + + memimageinit(); + Binit(&in, fd, OREAD); + + if(Bgetheader(&in, &h) < 0) + sysfatal("reading header: %r"); + + num = 0; + r.min = Pt(4, 4); + for(icon = h.first; icon != nil; icon = icon->next){ + if(Bgeticon(&in, icon) < 0){ + fprint(2, "%s: read fail: %r\n", argv0); + 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; + if(cflag){ + writememimage(1, icon->img); + exits(0); + } + r.min.x += r.max.x; + num++; + } + + if(num == 0 || cflag) + sysfatal("no images"); + + initdraw(nil, nil, "ico"); + background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF); + eresized(0); + einit(Emouse|Ekeyboard); + for(;;) + switch(event(&e)){ + case Ekeyboard: + break; + case Emouse: + mouse = e.mouse; + if(mouse.buttons & 4) + menu(); + else + mousemoved(); + break; + } + /* not reached */ +} diff --git a/sys/src/cmd/pict/imagefile.h b/sys/src/cmd/pict/imagefile.h new file mode 100644 index 0000000..9cb9432 --- /dev/null +++ b/sys/src/cmd/pict/imagefile.h @@ -0,0 +1,86 @@ +typedef struct Rawimage Rawimage; + +struct Rawimage +{ + Rectangle r; + unsigned char *cmap; + int cmaplen; + int nchans; + unsigned char *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 { + uint32_t fields_set; + double gamma; + char *comment; +} ImageInfo; + + +Rawimage** readjpg(int, int); +Rawimage** Breadjpg(Biobuf*, int); +Rawimage** readpng(int, int); +Rawimage** Breadpng(Biobuf*, int); +Rawimage** readtif(int, int); +Rawimage** Breadtif(Biobuf*, int); +Rawimage** readgif(int, int, int); +Rawimage** readpixmap(int, int); +Rawimage* torgbv(Rawimage*, int); +Rawimage* totruecolor(Rawimage*, int); +int writerawimage(int, Rawimage*); +void* _remaperror(char*, ...); + +typedef struct Memimage Memimage; /* avoid necessity to include memdraw.h */ + +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*, int); +char* memwriteppm(Biobuf*, Memimage*, char*, int); +char* writejpg(Biobuf*, Image*, char*, int, int); +char* memwritejpg(Biobuf*, Memimage*, char*, int, int); +Image* multichan(Image*); +Memimage* memmultichan(Memimage*); + +char* memwritepng(Biobuf*, Memimage*, ImageInfo*); + +char* writetif(Biobuf*, Image*, char*, int, int); +char* memwritetif(Biobuf*, Memimage*, char*, int, int); diff --git a/sys/src/cmd/pict/jpegdump.c b/sys/src/cmd/pict/jpegdump.c new file mode 100644 index 0000000..0b132cb --- /dev/null +++ b/sys/src/cmd/pict/jpegdump.c @@ -0,0 +1,346 @@ +/* jpeg parser by tom szymanski */ +#include +#include +#include +#include +#include +#include + +/* subroutines done by macros */ +#define min(A,B) ((A)<(B) ? (A) : (B)) +#define max(A,B) ((A)>(B) ? (A) : (B)) +#define maxeql(A,B) if (A < (B)) A = (B); +#define mineql(A,B) if (A > (B)) A = (B); +#define eatarg0 (argc--, argv++) +#define arrayLength(A) ((sizeof A)/ (sizeof A[0])) + +FILE *infile; +char *fname; + +/* Routines to print error messages of varying severity */ + +/* externally visible variables */ +int warncnt; +char *myname; + +void getname (char *arg) { + /* Save name of invoking program for use by error routines */ + register char *p; + p = strrchr (arg, '/'); + if (p == NULL) + myname = arg; + else + myname = ++p; +} + +static void introduction (void) { + warncnt++; + fflush (stdout); + if (myname != NULL) + fprintf (stderr, "%s: ", myname); +} + +void warn (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fputc ('\n', stderr); + fflush (stderr); +} + +void quit (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fputc ('\n', stderr); + fflush (stderr); + exit (1); +} + +void fatal (char *fmt, ...) { + va_list args; + introduction (); + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, "\nbetter get help!\n"); + fflush (stderr); + abort (); +} + +int toption = 0; +int dqt[16][64]; + +int get1 (void) { + unsigned char x; + if (fread(&x, 1, 1, infile) == 0) + quit ("unexpected EOF"); + return x; +} + +int get2 (void) { + int x; + + x = get1() << 8; + return x | get1(); +} + +void eatmarker (int kind) { + int l, c; + l = get2(); + printf ("%02x len=%d\n", kind, l); + for (l -= 2; l > 0; l--) + get1(); +} + +char *sofName[16] = { + "Baseline sequential DCT - Huffman coding", + "Extended sequential DCT - Huffman coding", + "Progressive DCT - Huffman coding", + "Lossless - Huffman coding", + "4 is otherwise used", + "Sequential DCT - differential Huffman coding", + "Progressive DCT - differential Huffman coding", + "Lossless - differential Huffman coding", + "8 is reserved", + "Extended Sequential DCT - arithmetic coding", + "Progressive DCT - arithmetic coding", + "Lossless - arithmetic coding", + "c is otherwise used", + "Sequential DCT - differential arithmetic coding", + "Progressive DCT - differential arithmetic coding", + "Lossless - differential arithmetic coding", +}; + +void get_sof (int kind) { + int i, length, height, width, precision, ncomponents; + int id, sf, tab; + length = get2(); + precision = get1(); + height = get2(); + width = get2(); + ncomponents = get1(); + printf ("SOF%d:\t%s\n", kind - 0xc0, sofName[kind - 0xc0]); + printf ("\t%d wide, %d high, %d deep, %d components\n", + width, height, precision, ncomponents); + for (i = 0; i < ncomponents; i++) { + id = get1(); + sf = get1(); + tab = get1(); + printf ("\tcomponent %d: %d hsample, %d vsample, quantization table %d\n", + id, sf >> 4, sf & 0xf, tab); + } +} + +void get_com (int kind) { + int l, c; + l = get2(); + printf ("COM len=%d '", l); + for (l -= 2; l > 0; l--) + putchar (c = get1()); + printf ("'\n"); +} + +void get_app (int kind) { + int l, c, first; + char buf[6]; + int nbuf, nok; + l = get2(); + printf ("APP%d len=%d\n", kind - 0xe0, l); + nbuf = 0; + nok = 0; + first = 1; + /* dump printable strings in comment */ + for (l -= 2; l > 0; l--){ + c = get1(); + if(isprint(c)){ + if(nbuf >= sizeof buf){ + if(!first && nbuf == nok) + printf(" "); + printf("%.*s", nbuf, buf); + nbuf = 0; + first = 0; + } + buf[nbuf++] = c; + nok++; + }else{ + if(nok >= sizeof buf) + if(nbuf > 0) + printf("%.*s", nbuf, buf); + nbuf = 0; + nok = 0; + } + } + if(nok >= sizeof buf) + if(nbuf > 0){ + if(!first && nbuf == nok) + printf(" "); + printf("%.*s", nbuf, buf); + } +} + +void get_dac (int kind) { + eatmarker (kind); +} + +int get1dqt (void) { + int t, p, i, *tab; + t = get1(); + p = t >> 4; + t = t & 0xf; + printf ("DQT:\tp = %d, table = %d\n", p, t); + tab = &dqt[t][0]; + for (i = 0; i < 64; i++) + tab[i] = p ? get2() : get1(); + if (toption) { + for (i = 0; i < 64; i++) + printf ("\t%q[%02d] = %d\n", i, tab[i]); + } + return p ? 65 : 129; +} + +void get_dqt (int kind) { + int length; + length = get2() - 2; + while (length > 0) + length -= get1dqt(); +} + +int get1dht (void) { + int l, tcth, p, i, j, v[16], vv[16][256]; + tcth = get1(); + printf ("DHT:\tclass = %d, table = %d\n", tcth >> 4, tcth & 0xf); + for (i = 0; i < 16; i++) + v[i] = get1(); + l = 17; + for (i = 0; i < 16; i++) + for (j = 0; j < v[i]; j++) { + vv[i][j] = get1(); + l += 1; + } + if (toption) { + for (i = 0; i < 16; i++) + printf ("\t%l[%02d] = %d\n", i+1, v[i]); + for (i = 0; i < 16; i++) + for (j = 0; j < v[i]; j++) + printf ("\t%v[%02d,%02d] = %d\n", i+1, j+1, vv[i][j]); + } + return l; +} + +void get_dht (int kind) { + int length; + length = get2() - 2; + while (length > 0) + length -= get1dht(); +} + +void get_sos (int kind) { + int i, length, ncomponents, id, dcac, ahal; + length = get2(); + ncomponents = get1(); + printf ("SOS:\t%d components\n", ncomponents); + for (i = 0; i < ncomponents; i++) { + id = get1(); + dcac = get1(); + printf ("\tcomponent %d: %d DC, %d AC\n", id, dcac >> 4, dcac & 0xf); + } + printf ("\tstart spectral %d\n", get1()); + printf ("\tend spectral %d\n", get1()); + ahal = get1(); + printf ("\tah = %d, al = %d\n", ahal >> 4, ahal &0xf); +} + +main (int argc, char *argv[]) { + int l, stuff, i, j, c; + while (argc > 1 && argv[1][0] == '-') { + switch (argv[1][1]) { + case 't': + toption = 1; + break; + default: + warn ("bad option '%c'", argv[1][1]); + } + eatarg0; + } + fname = argv[1]; + infile = fopen (fname, "r"); + if (infile == NULL) + quit ("can't open %s\n", fname); + Start: +// if (get1() != 0xff || get1() != 0xd8) +// quit ("not JFIF"); +// printf ("SOI\n"); +// get_app (0xe0); + for (;;) { + c = get1(); + if (c != 0xff) + quit ("expected marker, got %2x", c); + do { + c = get1(); + } while (c == 0xff); +marker: + switch (c) { + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcd: case 0xce: case 0xcf: + get_sof (c); + break; + case 0xc4: + get_dht (c); + break; + case 0xcc: + get_dac (c); + break; + case 0xd8: + printf ("SOI\n"); + break; + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + get_app(c); + break; + case 0xda: + get_sos (c); + goto newentropy; + case 0xdb: + get_dqt (c); + break; + case 0xfe: + get_com (c); + break; + case 0xd9: + printf ("EOI\n"); + if((c=getc(infile)) == EOF) + exit(0); + ungetc(c, infile); + goto Start; + default: + eatmarker (c); + } + continue; +newentropy: + l = stuff = 0; +entropy: + while ((c = get1()) != 0xff) + l += 1; + while (c == 0xff) + c = get1(); + if (c == 0) { + stuff += 1; + goto entropy; + } + printf ("sequence length %d with %d stuffs\n", l, stuff); + if (0xd0 <= c && c <= 0xd7) { + printf ("restart %d\n", c - 0xd0); + goto newentropy; + } + goto marker; + } +} diff --git a/sys/src/cmd/pict/jpg.c b/sys/src/cmd/pict/jpg.c new file mode 100644 index 0000000..7179dc5 --- /dev/null +++ b/sys/src/cmd/pict/jpg.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int jflag = 0; +int fflag = 0; +int Fflag = 0; +int nineflag = 0; +int threeflag = 0; +int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */ +int output = 0; +uint32_t outchan = CMAP8; +Image *image; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*, int); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +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 = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->r.min, S); + 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 -39cdefFkJrtvy [file.jpg ...]\n"); + exits("usage"); + }ARGEND; + + if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */ + fd = open("/dev/screen", OREAD); + if(fd >= 0){ + buf[12] = '\0'; + if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8) + colorspace = CRGB; + close(fd); + } + } + + err = nil; + if(argc == 0) + err = show(0, "", outchan); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "jpg: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +Rawimage** +vidmerge(Rawimage **aa1, Rawimage **aa2) +{ + Rawimage **aao, *ao, *a1, *a2; + int i, c, row, col; + + aao = nil; + for (i = 0; aa1[i]; i++) { + + a1 = aa1[i]; + a2 = aa2[i]; + if (a2 == nil){ + fprint(2, "jpg: vidmerge: unequal lengths\n"); + return nil; + } + aao = realloc(aao, (i+2)*sizeof(Rawimage *)); + if (aao == nil){ + fprint(2, "jpg: vidmerge: realloc\n"); + return nil; + } + aao[i+1] = nil; + ao = aao[i] = malloc(sizeof(Rawimage)); + if (ao == nil){ + fprint(2, "jpg: vidmerge: malloc\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++) { + unsigned char *po, *p1, *p2; + + ao->chans[c] = malloc(ao->chanlen); + if (ao->chans[c] == nil){ + fprint(2, "jpg: vidmerge: malloc chan\n"); + return nil; + } + 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){ + if (!inited) { + if(initdraw(0, 0, 0) < 0){ + fprint(2, "jpg: initdraw failed: %r\n"); + return "initdraw"; + } + if(Fflag == 0) + einit(Ekeyboard|Emouse); + inited++; + } + if(defaultcolor && screen->depth>8 && outchan==CMAP8) + outchan = RGB24; + } + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){ + c = totruecolor(r, CY); + outchan = GREY8; + }else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "jpg: conversion of %s failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(c->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "jpg: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "jpg: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if (Fflag) { + freeimage(i); + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + goto rpt; + } + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "jpg: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "jpg: %s: write error: %r\n", name); + return "write"; + } + } + Return: + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + if (Fflag) goto rpt; + return nil; +} diff --git a/sys/src/cmd/pict/multichan.c b/sys/src/cmd/pict/multichan.c new file mode 100644 index 0000000..6245687 --- /dev/null +++ b/sys/src/cmd/pict/multichan.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include "imagefile.h" + +/* Separate colors, if not a grey scale or bitmap, into one byte per color per pixel, no alpha or X */ +/* Result is GREY[1248] or RGB24 */ + +static +int +notrans(uint32_t 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/sys/src/cmd/pict/onechan.c b/sys/src/cmd/pict/onechan.c new file mode 100644 index 0000000..aee1dc7 --- /dev/null +++ b/sys/src/cmd/pict/onechan.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include "imagefile.h" + +/* Convert image to a single channel, one byte per pixel */ + +static +int +notrans(uint32_t chan) +{ + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + case CMAP8: + case GREY8: + return 1; + } + return 0; +} + +static +int +easycase(uint32_t 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 +unsigned char* +load(Image *image, Memimage *memimage) +{ + unsigned char *data, *p, *q0, *q1, *q2; + unsigned char *rgbv; + int depth, ndata, dx, dy, i, v; + uint32_t chan, pixel; + Rectangle r; + Rawimage ri, *nri; + + if(memimage == nil){ + r = image->r; + depth = image->depth; + chan = image->chan; + }else{ + r = memimage->r; + depth = memimage->depth; + chan = memimage->chan; + } + dx = Dx(r); + dy = Dy(r); + + /* + * Read image data into memory + * potentially one extra byte on each end of each scan line. + */ + ndata = dy*(2+bytesperline(r, depth)); + data = malloc(ndata); + if(data == nil) + return nil; + if(memimage != nil) + ndata = unloadmemimage(memimage, r, data, ndata); + else + ndata = unloadimage(image, r, data, ndata); + if(ndata < 0){ + werrstr("onechan: %r"); + free(data); + return nil; + } + + /* + * Repack + */ + memset(&ri, 0, sizeof(ri)); + ri.r = r; + ri.cmap = nil; + ri.cmaplen = 0; + ri.nchans = 3; + ri.chanlen = dx*dy; + ri.chans[0] = malloc(ri.chanlen); + ri.chans[1] = malloc(ri.chanlen); + ri.chans[2] = malloc(ri.chanlen); + if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){ + Err: + free(ri.chans[0]); + free(ri.chans[1]); + free(ri.chans[2]); + free(data); + return nil; + } + ri.chandesc = CRGB; + + p = data; + q0 = ri.chans[0]; + q1 = ri.chans[1]; + q2 = ri.chans[2]; + + switch(chan){ + default: + werrstr("can't handle image type 0x%lux", chan); + goto Err; + case RGB16: + for(i=0; i> 8; + *q0++ = v | (v>>5); + v = (pixel & 0x07E0) >> 3; + *q1++ = v | (v>>6); + v = (pixel & 0x001F) << 3; + *q2++ = v | (v>>5); + } + break; + case RGB24: + for(i=0; ichans[0]; + free(nri); + } + + free(ri.chans[0]); + free(ri.chans[1]); + free(ri.chans[2]); + free(data); + return rgbv; +} + +Image* +onechan(Image *i) +{ + unsigned char *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) +{ + unsigned char *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/sys/src/cmd/pict/png.c b/sys/src/cmd/pict/png.c new file mode 100644 index 0000000..5340b6f --- /dev/null +++ b/sys/src/cmd/pict/png.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +extern int debug; +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +Image *image; +int defaultcolor = 1; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*, int); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +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 = imager(image); + 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 '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 '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; + + err = nil; + if(argc == 0) + err = show(0, "", outchan); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "png: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +char* +show(int fd, char *name, int outc) +{ + Rawimage **array, *r, *c; + Image *i, *i2; + int j, ch, outchan; + long len; + Biobuf b; + char buf[32]; + static int inited; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + outchan = outc; + array = Breadpng(&b, CRGB); + if(array == nil || array[0]==nil){ + fprint(2, "png: decode %s failed: %r\n", name); + return "decode"; + } + Bterm(&b); + + r = array[0]; + if(!dflag){ + if (!inited) { + if(initdraw(0, 0, 0) < 0){ + fprint(2, "png: initdraw failed: %r\n"); + return "initdraw"; + } + einit(Ekeyboard|Emouse); + inited++; + } + if(defaultcolor && screen->depth>8 && outchan==CMAP8) + outchan = RGB24; + } + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + switch(r->chandesc){ + case CY: + outchan = GREY8; + break; + case CYA16: + outchan = CHAN2(CGrey, 8, CAlpha, 8); + break; + case CRGB24: + outchan = RGB24; + break; + case CRGBA32: + outchan = RGBA32; + break; + } + c = r; + } + if(c == nil){ + fprint(2, "png: conversion of %s failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + 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 of %d bytes failed: %r\n", + name, c->chanlen); + return "loadimage"; + } + i2 = allocimage(display, c->r, outchan, 0, 0); + draw(i2, i2->r, display->black, nil, ZP); + draw(i2, i2->r, i, nil, i->r.min); + image = i2; + eresized(0); + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + len = (c->r.max.x - c->r.min.x) * (c->r.max.y - c->r.min.y); + switch(c->chandesc){ + case CY: + // len *= 1; + break; + case CYA16: + len *= 2; + break; + case CRGB24: + len *= 3; + break; + case CRGBA32: + len *= 4; + break; + } + if(c->chanlen != len) + fprint(2, "%s: writing %d bytes for len %ld chan %s\n", + argv0, c->chanlen, len, buf); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "png: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "png: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c && c != r){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/sys/src/cmd/pict/ppm.c b/sys/src/cmd/pict/ppm.c new file mode 100644 index 0000000..5d845a9 --- /dev/null +++ b/sys/src/cmd/pict/ppm.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +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 = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->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: ppm -39cdektv [file.ppm ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "ppm: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "ppm: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readpixmap(fd, CRGB); + if(array == nil || array[0]==nil){ + fprint(2, "ppm: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "ppm: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + outchan = GREY8; + 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==Kdel || ch==Keof) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + if(r->chandesc == CY) + outchan = GREY8; + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "ppm: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "ppm: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/sys/src/cmd/pict/readbmp.c b/sys/src/cmd/pict/readbmp.c new file mode 100644 index 0000000..5892d3f --- /dev/null +++ b/sys/src/cmd/pict/readbmp.c @@ -0,0 +1,626 @@ +#include +#include +#include +#include +#include "imagefile.h" +#include "bmp.h" + +/* + MS-BMP file reader + (c) 2003, I.P.Keller + + aims to decode *all* valid bitmap formats, although some of the + flavours couldn't be verified due to lack of suitable test-files. + the following flavours are supported: + + Bit/Pix Orientation Compression Tested? + 1 top->bottom n/a yes + 1 bottom->top n/a yes + 4 top->bottom no yes + 4 bottom->top no yes + 4 top->bottom RLE4 yes, but not with displacement + 8 top->bottom no yes + 8 bottom->top no yes + 8 top->bottom RLE8 yes, but not with displacement + 16 top->bottom no no + 16 bottom->top no no + 16 top->bottom BITMASK no + 16 bottom->top BITMASK no + 24 top->bottom n/a yes + 24 bottom->top n/a yes + 32 top->bottom no no + 32 bottom->top no no + 32 top->bottom BITMASK no + 32 bottom->top BITMASK no + + OS/2 1.x bmp files are recognised as well, but testing was very limited. + + verifying was done with a number of test files, generated by + different tools. nevertheless, the tests were in no way exhaustive + enough to guarantee bug-free decoding. caveat emptor! +*/ + +static short +r16(Biobuf*b) +{ + short s; + + s = Bgetc(b); + s |= ((short)Bgetc(b)) << 8; + return s; +} + + +static long +r32(Biobuf*b) +{ + long l; + + l = Bgetc(b); + l |= ((long)Bgetc(b)) << 8; + l |= ((long)Bgetc(b)) << 16; + l |= ((long)Bgetc(b)) << 24; + return l; +} + + +/* get highest bit set */ +static int +msb(uint32_t 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 = (uint)Bgetc(b); + + if(0 != val) { + valS = (uint)Bgetc(b); + p = &buf[ix + iy * width]; + while(val--) { + *p = clut[0xf & (valS >> 4)]; + p++; + ix++; + if(val != 0) { + *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 = (uint)Bgetc(b); + ix += val; + val = (uint)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(val != 0) { + *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) +{ + unsigned char 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((uint32_t)mask_blue) - 8; + int shft_green = msb((uint32_t)mask_green) - 8; + int shft_red = msb((uint32_t)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 = (unsigned char)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (unsigned char)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (unsigned char)((val & mask_green) >> shft_green); + else + buf[i].green = (unsigned char)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (unsigned char)((val & mask_red) >> shft_red); + else + buf[i].red = (unsigned char)((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 = (unsigned char)((c[0] << 3) & 0xf8); + buf[i].green = (unsigned char)(((((unsigned)c[1] << 6) + + (((unsigned)c[0]) >> 2))) & 0xf8); + buf[i].red = (unsigned char)((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) +{ + unsigned char 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) { + uint32_t mask_blue = (uint32_t)clut[0].blue + + ((uint32_t)clut[0].green << 8) + + ((uint32_t)clut[0].red << 16) + + ((uint32_t)clut[0].alpha << 24); + uint32_t mask_green = (uint32_t)clut[1].blue + + ((uint32_t)clut[1].green << 8) + + ((uint32_t)clut[1].red << 16) + + ((uint32_t)clut[1].alpha << 24); + uint32_t mask_red = (uint32_t)clut[2].blue + + ((uint32_t)clut[2].green << 8) + + ((uint32_t)clut[2].red << 16) + + ((uint32_t)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++) { + uint32_t val; + Bread(b, c, sizeof(c)); + val = (uint32_t)c[0] + ((uint32_t)c[1] << 8) + + ((uint32_t)c[2] << 16) + ((uint32_t)c[1] << 24); + + buf[i].alpha = 0; + if(shft_blue >= 0) + buf[i].blue = (unsigned char)((val & mask_blue) >> shft_blue); + else + buf[i].blue = (unsigned char)((val & mask_blue) << -shft_blue); + if(shft_green >= 0) + buf[i].green = (unsigned char)((val & mask_green) >> shft_green); + else + buf[i].green = (unsigned char)((val & mask_green) << -shft_green); + if(shft_red >= 0) + buf[i].red = (unsigned char)((val & mask_red) >> shft_red); + else + buf[i].red = (unsigned char)((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 + Filehdrsz, 0); + + for(i = 0; i < num_coltab; i++) { + clut[i].blue = (unsigned char)Bgetc(b); + clut[i].green = (unsigned char)Bgetc(b); + clut[i].red = (unsigned char)Bgetc(b); + clut[i].alpha = (unsigned char)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; + unsigned char *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/sys/src/cmd/pict/readgif.c b/sys/src/cmd/pict/readgif.c new file mode 100644 index 0000000..b00495c --- /dev/null +++ b/sys/src/cmd/pict/readgif.c @@ -0,0 +1,545 @@ +#include +#include +#include +#include +#include "imagefile.h" + +typedef struct Entry Entry; +typedef struct Header Header; + +struct Entry{ + int prefix; + int exten; +}; + +struct Header{ + Biobuf *fd; + char err[256]; + jmp_buf errlab; + unsigned char buf[3*256]; + char vers[8]; + unsigned char *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; + + unsigned char *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*, int); +static Rawimage* readone(Header*); +static void readheader(Header*); +static void skipextension(Header*); +static unsigned char* readcmap(Header*, int); +static unsigned char* decode(Header*, Rawimage*, Entry*); +static void interlace(Header*, Rawimage*); + +static +void +_clear(void **p) +{ + if(*p){ + free(*p); + *p = nil; + } +} +#define clear(p) _clear((void**)p) + +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("%s", h->err); + giffreeall(h, 1); + longjmp(h->errlab, 1); +} + + +Rawimage** +readgif(int fd, int colorspace, int justone) +{ + 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, justone); + 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, int justone) +{ + 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{ + if(h->globalcmap == nil) + giferror(h, "ReadGIF: globalcmap missing"); + new->cmaplen = 3*(1<<((h->fields&7)+1)); + new->cmap = malloc(new->cmaplen); + if(new->cmap == nil) + giferror(h, memerr); + 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; + if(justone) + goto Return; + 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 +unsigned char* +readcmap(Header *h, int size) +{ + unsigned char *map; + + if(size > 8) + giferror(h, "ReadGIF: can't handles %d bits per pixel", size); + size = 3*(1<fd, h->buf, size) != size) + giferror(h, "ReadGIF: short read on color map"); + map = malloc(size); + if(map == nil) + giferror(h, memerr); + memmove(map, h->buf, size); + return map; +} + +static +Rawimage* +readone(Header *h) +{ + Rawimage *i; + int left, top, width, height; + + if(Bread(h->fd, h->buf, 9) != 9) + giferror(h, "ReadGIF: can't read image descriptor: %r"); + i = malloc(sizeof(Rawimage)); + if(i == nil) + giferror(h, memerr); + left = h->buf[0]+(h->buf[1]<<8); + top = h->buf[2]+(h->buf[3]<<8); + width = h->buf[4]+(h->buf[5]<<8); + height = h->buf[6]+(h->buf[7]<<8); + i->fields = h->buf[8]; + i->r.min.x = left; + i->r.min.y = top; + i->r.max.x = left+width; + i->r.max.y = top+height; + i->nchans = 1; + i->chandesc = CRGB1; + memset(i->chans, 0, sizeof(i->chans)); + return i; +} + + +static +int +readdata(Header *h, unsigned char *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; + unsigned char 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){ + /* + * This code used to check h->buf[hsize-1] != 0 + * and giferror if so, but if !hasdata, hsize == 0. + */ + return; + } + + /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 in data */ + if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){ + n = readdata(h, data); + if(n == 0) + return; + if(n==3 && data[0]==1) + h->loopcount = data[1] | (data[2]<<8); + } + while(readdata(h, data) != 0) + ; +} + +static +unsigned char* +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; + unsigned char clip, *p, *pic; + unsigned char 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); + + CTM =1<r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y); + i->chanlen = piclen; + pic = malloc(piclen); + if(pic == nil) + giferror(h, memerr); + h->pic = pic; + pici = 0; + ndata = 0; + datai = 0; + nbits = 0; + sreg = 0; + fc = 0; + + Loop: + for(;;){ + csize = codesize+1; + nentry = EOD+1; + maxentry = (1<>= csize; + nbits -= csize; + + if(code == EOD){ + ndata = readdata(h, data); + if(ndata != 0) + fprint(2, "ReadGIF: unexpected data past EOD\n"); + 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){ + fprint(2, "ReadGIF: GIF invalid, code out of range, %x > %x\n", code, nentry); + code = nentry; + } + for(c=code; stacki>0 && 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<cmap!=nil && i->cmaplen!=3*256){ + clip = (i->cmaplen/3)-1; + for(p = pic; p < pic+piclen; p++) + if(*p > clip) + *p = clip; + } + h->pic = nil; + return pic; +} + +static +void +interlace(Header *h, Rawimage *image) +{ + unsigned char *pic; + Rectangle r; + int dx, yy, y; + unsigned char *ipic; + + pic = image->chans[0]; + r = image->r; + dx = r.max.x-r.min.x; + ipic = malloc(dx*(r.max.y-r.min.y)); + if(ipic == nil) + giferror(h, nil); + + /* Group 1: every 8th row, starting with row 0 */ + yy = 0; + for(y=r.min.y; ychans[0]); + image->chans[0] = ipic; +} diff --git a/sys/src/cmd/pict/readjpg.c b/sys/src/cmd/pict/readjpg.c new file mode 100644 index 0000000..5bceee6 --- /dev/null +++ b/sys/src/cmd/pict/readjpg.c @@ -0,0 +1,1659 @@ +#include +#include +#include +#include +#include "imagefile.h" + +enum { + /* Constants, all preceded by byte 0xFF */ + SOF =0xC0, /* Start of Frame */ + SOF2 =0xC2, /* Start of Frame; progressive Huffman */ + JPG =0xC8, /* Reserved for JPEG extensions */ + DHT =0xC4, /* Define Huffman Tables */ + DAC =0xCC, /* Arithmetic coding conditioning */ + RST =0xD0, /* Restart interval termination */ + RST7 =0xD7, /* Restart interval termination (highest value) */ + SOI =0xD8, /* Start of Image */ + EOI =0xD9, /* End of Image */ + SOS =0xDA, /* Start of Scan */ + DQT =0xDB, /* Define quantization tables */ + DNL =0xDC, /* Define number of lines */ + DRI =0xDD, /* Define restart interval */ + DHP =0xDE, /* Define hierarchical progression */ + EXP =0xDF, /* Expand reference components */ + APPn =0xE0, /* Reserved for application segments */ + JPGn =0xF0, /* Reserved for JPEG extensions */ + COM =0xFE, /* Comment */ + + CLAMPOFF = 300, + NCLAMP = CLAMPOFF+700 +}; + +typedef struct Framecomp Framecomp; +typedef struct Header Header; +typedef struct Huffman Huffman; + +struct Framecomp /* Frame component specifier from SOF marker */ +{ + int C; + int H; + int V; + int Tq; +}; + +struct Huffman +{ + int *size; /* malloc'ed */ + int *code; /* malloc'ed */ + int *val; /* malloc'ed */ + int mincode[17]; + int maxcode[17]; + int valptr[17]; + /* fast lookup */ + int value[256]; + int shift[256]; +}; + + +struct Header +{ + Biobuf *fd; + char err[256]; + jmp_buf errlab; + /* variables in i/o routines */ + int sr; /* shift register, right aligned */ + int cnt; /* # bits in right part of sr */ + unsigned char *buf; + int nbuf; + int peek; + + int Nf; + + Framecomp comp[3]; + unsigned char mode; + int X; + int Y; + int qt[4][64]; /* quantization tables */ + Huffman dcht[4]; + Huffman acht[4]; + int **data[3]; + int ndata[3]; + + unsigned char *sf; /* start of frame; do better later */ + unsigned char *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 unsigned char clamp[NCLAMP]; + +static Rawimage *readslave(Header*, int); +static int readsegment(Header*, int*); +static void quanttables(Header*, unsigned char*, int); +static void huffmantables(Header*, unsigned char*, int); +static void soiheader(Header*); +static int nextbyte(Header*, int); +static int int2(unsigned char*, int); +static void nibbles(int, int*, int*); +static int receive(Header*, int); +static int receiveEOB(Header*, int); +static int receivebit(Header*); +static void restart(Header*, int); +static int decode(Header*, Huffman*); +static Rawimage* baselinescan(Header*, int); +static void progressivescan(Header*, int); +static Rawimage* progressiveIDCT(Header*, int); +static void idct(int*); +static void colormap1(Header*, int, Rawimage*, int*, int, int); +static void colormapall1(Header*, int, Rawimage*, int*, int*, int*, int, int); +static void colormap(Header*, int, Rawimage*, int**, int**, int**, int, int, int, int, int*, int*); +static void jpgerror(Header*, char*, ...); + +static char readerr[] = "ReadJPG: read error: %r"; +static char memerr[] = "ReadJPG: malloc failed: %r"; + +static int zig[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, 17, /* 0-7 */ + 24, 32, 25, 18, 11, 4, 5, /* 8-15 */ + 12, 19, 26, 33, 40, 48, 41, 34, /* 16-23 */ + 27, 20, 13, 6, 7, 14, 21, 28, /* 24-31 */ + 35, 42, 49, 56, 57, 50, 43, 36, /* 32-39 */ + 29, 22, 15, 23, 30, 37, 44, 51, /* 40-47 */ + 58, 59, 52, 45, 38, 31, 39, 46, /* 48-55 */ + 53, 60, 61, 54, 47, 55, 62, 63 /* 56-63 */ +}; + +static +void +jpginit(void) +{ + int k; + static int inited; + + if(inited) + return; + inited = 1; + for(k=0; kbuf); + if(h->dccoeff[0]) + for(i=0; i<3; i++) + clear(&h->dccoeff[i]); + if(h->accoeff[0]) + for(i=0; i<3; i++){ + if(h->accoeff[i]) + for(j=0; jnaccoeff[i]; j++) + clear(&h->accoeff[i][j]); + clear(&h->accoeff[i]); + } + for(i=0; i<4; i++){ + clear(&h->dcht[i].size); + clear(&h->acht[i].size); + clear(&h->dcht[i].code); + clear(&h->acht[i].code); + clear(&h->dcht[i].val); + clear(&h->acht[i].val); + } + if(h->data[0]) + for(i=0; i<3; i++){ + if(h->data[i]) + for(j=0; jndata[i]; j++) + clear(&h->data[i][j]); + clear(&h->data[i]); + } + if(freeimage && h->image!=nil){ + clear(&h->array); + clear(&h->image->cmap); + for(i=0; i<3; i++) + clear(&h->image->chans[i]); + clear(&h->image); + } +} + +static +void +jpgerror(Header *h, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vseprint(h->err, h->err+sizeof h->err, fmt, arg); + va_end(arg); + if(h->image != nil) + fprint(2, "jpg: partial image: %s\n", h->err); + werrstr("%s", h->err); + 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(2*sizeof(Rawimage*)); + 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 = h->image; + 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; + unsigned char *b; + + soiheader(header); + nseg = 0; + image = nil; + + header->buf = jpgmalloc(header, 4096, 0); + header->nbuf = 4096; + while(header->err[0] == '\0'){ + nseg++; + n = readsegment(header, &m); + b = header->buf; + switch(m){ + case -1: + return image; + + case APPn+0: + if(nseg==1 && strncmp((char*)b, "JFIF", 4)==0) /* JFIF header; check version */ + if(b[5]>1 || b[6]>2) + sprint(header->err, "ReadJPG: can't handle JFIF version %d.%2d", b[5], b[6]); + break; + + case APPn+1: case APPn+2: case APPn+3: case APPn+4: case APPn+5: + case APPn+6: case APPn+7: case APPn+8: case APPn+9: case APPn+10: + case APPn+11: case APPn+12: case APPn+13: case APPn+14: case APPn+15: + break; + + case DQT: + quanttables(header, b, n); + break; + + case SOF: + case SOF2: + header->Y = int2(b, 1); + header->X = int2(b, 3); + header->Nf = b[5]; + for(i=0; iNf; i++){ + header->comp[i].C = b[6+3*i+0]; + nibbles(b[6+3*i+1], &H, &V); + if(H<=0 || V<=0) + jpgerror(header, "non-positive sampling factor (Hsamp or Vsamp)"); + /* hack: colormap1() doesnt handle resampling */ + if(header->Nf == 1) + H = V = 1; + 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) +{ + unsigned char 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; + +Again: + while((c=readbyte(h)) == 0) + ; + if(c != 0xFF) + goto Again; + while(c == 0xFF) + c = readbyte(h); + if(c == 0) + goto Again; + return c; +} + +static +int +int2(unsigned char *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; + unsigned char tmp[2]; + + if((m = marker(h)) == EOI){ + *markerp = m; + return 0; + } + 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); + /* zero in case of short read later */ + h->buf = jpgmalloc(h, n+1, 1); /* +1 for sentinel */ + h->nbuf = n; + } + /* accept short reads to cope with some real-world jpegs */ + if(Bread(h->fd, h->buf, n) < 0) + goto Readerr; + *markerp = m; + return n; +} + +static +int +huffmantable(Header *h, unsigned char *b) +{ + Huffman *t; + int Tc, th, n, nsize, i, j, k, v, cnt, code, si, sr; + 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=1; i<=16; i++) + nsize += b[i]; + if(nsize == 0) + return 0; + t->size = jpgmalloc(h, (nsize+1)*sizeof(int), 1); + k = 0; + for(i=1; i<=16; i++){ + n = b[i]; + for(j=0; jsize[k++] = i; + } + t->size[k] = 0; + + /* initialize HUFFVAL */ + t->val = jpgmalloc(h, nsize*sizeof(int), 1); + for(i=0; ival[i] = b[17+i]; + + /* flow chart C-3 */ + t->code = jpgmalloc(h, (nsize+1)*sizeof(int), 1); + k = 0; + code = 0; + si = t->size[0]; + for(;;){ + do + t->code[k++] = code++; + while(t->size[k] == si); + if(t->size[k] == 0) + break; + do{ + code <<= 1; + si++; + }while(t->size[k] != si); + } + + /* flow chart F-25 */ + i = 0; + j = 0; + for(;;){ + for(;;){ + i++; + if(i > 16) + goto outF25; + if(b[i] != 0) + break; + t->maxcode[i] = -1; + } + t->valptr[i] = j; + t->mincode[i] = t->code[j]; + j += b[i]-1; + t->maxcode[i] = t->code[j]; + j++; + } +outF25: + + /* create byte-indexed fast path tables */ + maxcode = t->maxcode; + /* stupid startup algorithm: just run machine for each byte value */ + for(v=0; v<256; ){ + code = 0; + cnt = 8; + sr = v; + for(i=1;;i++){ + cnt--; + if(sr & (1<shift[v] = 0; + t->value[v] = -1; + goto continueBytes; + } + } + 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, unsigned char *b, int n) +{ + int l, mt; + + for(l=0; l 1) + jpgerror(h, "ReadJPG: unknown quantization table class %d", pq); + if(tq > 3) + jpgerror(h, "ReadJPG: unknown quantization table index %d", tq); + q = h->qt[tq]; + for(i=0; i<64; i++){ + if(pq == 0) + q[i] = b[1+i]; + else + q[i] = int2(b, 1+2*i); + } + return 64*(1+pq); +} + +static +void +quanttables(Header *h, unsigned char *b, int n) +{ + int l, m; + + for(l=0; lss; + Ns = ss[0]; + if((Ns!=3 && Ns!=1) || Ns!=h->Nf) + jpgerror(h, "ReadJPG: can't handle scan not 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(Ns == 3) + image->chandesc = colorspace; + else + image->chandesc = CY; + image->nchans = h->Nf; + for(k=0; kNf; k++) + image->chans[k] = jpgmalloc(h, h->X*h->Y, 0); + + /* compute maximum H and V */ + Hmax = 0; + Vmax = 0; + for(comp=0; compcomp[comp].H > Hmax) + Hmax = h->comp[comp].H; + if(h->comp[comp].V > Vmax) + Vmax = h->comp[comp].V; + } + + /* initialize data structures */ + allHV1 = 1; + data = h->data; + for(comp=0; compcomp[comp].H; + V[comp] = h->comp[comp].V; + nblock = H[comp]*V[comp]; + if(nblock != 1) + allHV1 = 0; + data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0); + h->ndata[comp] = nblock; + DC[comp] = 0; + for(m=0; mri; + + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nacross = ((h->X+(8*Hmax-1))/(8*Hmax)); + nmcu = ((h->Y+(8*Vmax-1))/(8*Vmax))*nacross; + for(mcu=0; mcudcht[Td[comp]]; + acht = &h->acht[Ta[comp]]; + qt = h->qt[h->comp[comp].Tq]; + + for(block=0; block= 0); + if((t&0x0F) == 0){ + if((t&0xF0) != 0xF0) + break; + k += 16; + }else{ + z = receive(h, t&0xF); + k += t>>4; + if(k >= 64) + break; + zz[zig[k]] = z*qt[k]; + k++; + } + } while(k < 64); + + idct(zz); + } + } + + /* rotate colors to RGB and assign to bytes */ + if(Ns == 1) /* very easy */ + colormap1(h, colorspace, image, data[0][0], mcu, nacross); + else if(allHV1) /* fairly easy */ + colormapall1(h, colorspace, image, data[0][0], data[1][0], data[2][0], mcu, nacross); + else /* miserable general case */ + colormap(h, colorspace, image, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V); + /* process restart marker, if present */ + mcu++; + if(ri>0 && mcuri-1; + nskip = 0; + do{ + do{ + rst = nextbyte(h, 1); + nskip++; + }while(rst>=0 && rst!=0xFF); + if(rst == 0xFF){ + rst = nextbyte(h, 1); + nskip++; + } + }while(rst>=0 && (rst&~7)!=RST); + if(nskip != 2) + sprint(h->err, "ReadJPG: skipped %d bytes at restart %d\n", nskip-2, rest); + if(rst < 0) + jpgerror(h, readerr); + if((rst&7) != (rest&7)) + jpgerror(h, "ReadJPG: expected RST%d got %d", rest&7, rst&7); + h->cnt = 0; + h->sr = 0; +} + +static +Rawimage* +progressiveIDCT(Header *h, int colorspace) +{ + int k, m, comp, block, Nf, bn; + int allHV1, nblock, mcu, nmcu; + int H[3], V[3], blockno[3]; + int *dccoeff, **accoeff; + int ***data, *zz; + + Nf = h->Nf; + allHV1 = 1; + data = h->data; + + for(comp=0; compcomp[comp].H; + V[comp] = h->comp[comp].V; + nblock = h->nblock[comp]; + if(nblock != 1) + allHV1 = 0; + h->ndata[comp] = nblock; + data[comp] = jpgmalloc(h, nblock*sizeof(int*), 0); + for(m=0; mnacross*h->ndown; + for(mcu=0; mcudccoeff[comp]; + accoeff = h->accoeff[comp]; + bn = blockno[comp]; + for(block=0; blocknblock[comp]; block++){ + zz = data[comp][block]; + memset(zz, 0, 8*8*sizeof(int)); + zz[0] = dccoeff[bn]; + + for(k=1; k<64; k++) + zz[zig[k]] = accoeff[bn][k]; + + idct(zz); + bn++; + } + blockno[comp] = bn; + } + + /* rotate colors to RGB and assign to bytes */ + if(Nf == 1) /* very easy */ + colormap1(h, colorspace, h->image, data[0][0], mcu, h->nacross); + else if(allHV1) /* fairly easy */ + colormapall1(h, colorspace, h->image, data[0][0], data[1][0], data[2][0], mcu, h->nacross); + else /* miserable general case */ + colormap(h, colorspace, h->image, data[0], data[1], data[2], mcu, h->nacross, h->Hmax, h->Vmax, H, V); + } + + return h->image; +} + +static +void +progressiveinit(Header *h, int colorspace) +{ + int Nf, Ns, j, k, nmcu, comp; + unsigned char *ss; + Rawimage *image; + + ss = h->ss; + Ns = ss[0]; + Nf = h->Nf; + if(Ns!=3 && Ns!=1) + jpgerror(h, "ReadJPG: image must have 1 or 3 components"); + + image = jpgmalloc(h, sizeof(Rawimage), 1); + h->image = image; + image->r = Rect(0, 0, h->X, h->Y); + image->cmap = nil; + image->cmaplen = 0; + image->chanlen = h->X*h->Y; + image->fields = 0; + image->gifflags = 0; + image->gifdelay = 0; + image->giftrindex = 0; + if(Nf == 3) + image->chandesc = colorspace; + else + image->chandesc = CY; + image->nchans = h->Nf; + for(k=0; kchans[k] = jpgmalloc(h, h->X*h->Y, 0); + h->nblock[k] = h->comp[k].H*h->comp[k].V; + } + + /* compute maximum H and V */ + h->Hmax = 0; + h->Vmax = 0; + for(comp=0; compcomp[comp].H > h->Hmax) + h->Hmax = h->comp[comp].H; + if(h->comp[comp].V > h->Vmax) + h->Vmax = h->comp[comp].V; + } + h->nacross = ((h->X+(8*h->Hmax-1))/(8*h->Hmax)); + h->ndown = ((h->Y+(8*h->Vmax-1))/(8*h->Vmax)); + nmcu = h->nacross*h->ndown; + + for(k=0; kdccoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int), 1); + h->accoeff[k] = jpgmalloc(h, h->nblock[k]*nmcu * sizeof(int*), 1); + h->naccoeff[k] = h->nblock[k]*nmcu; + for(j=0; jnblock[k]*nmcu; j++) + h->accoeff[k][j] = jpgmalloc(h, 64*sizeof(int), 1); + } + +} + +static +void +progressivedc(Header *h, int comp, int Ah, int Al) +{ + int Ns, z, ri, mcu, nmcu; + int block, t, diff, qt, *dc, bn; + Huffman *dcht; + unsigned char *ss; + int i, Td[3], DC[3], blockno[3]; + + ss= h->ss; + Ns = ss[0]; + if(Ns!=1 && Ns!=h->Nf) + jpgerror(h, "ReadJPG: can't handle progressive with Ns!=1 and Nf!=Ns in DC scan"); + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + + for(i=0; iri; + + nmcu = h->nacross*h->ndown; + memset(blockno, 0, sizeof blockno); + for(mcu=0; mcudcht[Td[i]]; + qt = h->qt[h->comp[comp].Tq][0]; + dc = h->dccoeff[comp]; + bn = blockno[i]; + + for(block=0; blocknblock[comp]; block++){ + if(Ah == 0){ + t = decode(h, dcht); + diff = receive(h, t); + DC[i] += diff; + dc[bn] = qt*DC[i]<0 && mcuss; + Ns = ss[0]; + if(Ns != 1) + jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan"); + Ss = ss[1+2]; + Se = ss[2+2]; + H = h->comp[comp].H; + V = h->comp[comp].V; + + nacross = h->nacross*H; + ndown = h->ndown*V; + q = 8*h->Hmax/H; + nhor = (h->X+q-1)/q; + q = 8*h->Vmax/V; + nver = (h->Y+q-1)/q; + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nibbles(ss[1+1], &z, &Ta); /* z is thrown away */ + + ri = h->ri; + + eobrun = 0; + acht = &h->acht[Ta]; + qt = h->qt[h->comp[comp].Tq]; + nmcu = nacross*ndown; + mcu = 0; + for(y=0; y 0){ + --eobrun; + continue; + } + + /* arrange blockno to be in same sequence as original scan calculation. */ + tmcu = x/H + (nacross/H)*(y/V); + blockno = tmcu*H*V + H*(y%V) + x%H; + acc = h->accoeff[comp][blockno]; + k = Ss; + do { + 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{ + z = receive(h, ssss); + k += rrrr; + if(k > Se) + break; + acc[k] = z*qt[k]<0 && mcuss; + Ns = ss[0]; + if(Ns != 1) + jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan"); + Ss = ss[1+2]; + Se = ss[2+2]; + H = h->comp[comp].H; + V = h->comp[comp].V; + + nacross = h->nacross*H; + ndown = h->ndown*V; + q = 8*h->Hmax/H; + nhor = (h->X+q-1)/q; + q = 8*h->Vmax/V; + nver = (h->Y+q-1)/q; + + /* initialize data structures */ + h->cnt = 0; + h->sr = 0; + h->peek = -1; + nibbles(ss[1+1], &z, &Ta); /* z is thrown away */ + ri = h->ri; + + eobrun = 0; + ac = h->accoeff[comp]; + acht = &h->acht[Ta]; + qt = h->qt[h->comp[comp].Tq]; + nmcu = nacross*ndown; + mcu = 0; + pending = 0; + nzeros = -1; + for(y=0; y 0){ + if(nzeros > 0) + jpgerror(h, "ReadJPG: zeros pending at block start"); + for(k=Ss; k<=Se; k++) + increment(h, acc, k, qt[k]<= 0){ + if(acc[k] != 0) + increment(h, acc, k, qt[k]< 0) + eobrun = receiveEOB(h, rrrr)-1; + while(k <= Se){ + increment(h, acc, k, qt[k]<0 && mcudccoeff[0] == nil) + progressiveinit(h, colorspace); + + ss = h->ss; + Ns = ss[0]; + Ss = ss[1+2*Ns]; + nibbles(ss[3+2*Ns], &Ah, &Al); + c = ss[1]; + comp = -1; + for(i=0; iNf; i++) + if(h->comp[i].C == c) + comp = i; + if(comp == -1) + jpgerror(h, "ReadJPG: bad component index in scan header"); + + if(Ss == 0){ + progressivedc(h, comp, Ah, Al); + return; + } + if(Ah == 0){ + progressiveac(h, comp, Al); + return; + } + progressiveacinc(h, comp, Al); +} + +enum { + c1 = 2871, /* 1.402 * 2048 */ + c2 = 705, /* 0.34414 * 2048 */ + c3 = 1463, /* 0.71414 * 2048 */ + c4 = 3629, /* 1.772 * 2048 */ +}; + +static +void +colormap1(Header *h, int colorspace, Rawimage *image, int data[8*8], int mcu, int nacross) +{ + unsigned char *pic; + int x, y, dx, dy, minx, miny; + int r, k, pici; + + USED(colorspace); + pic = image->chans[0]; + minx = 8*(mcu%nacross); + dx = 8; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*(mcu/nacross); + dy = 8; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + k = 0; + for(y=0; yX; + k += 8; + } +} + +static +void +colormapall1(Header *h, int colorspace, Rawimage *image, int data0[8*8], int data1[8*8], int data2[8*8], int mcu, int nacross) +{ + unsigned char *rpic, *gpic, *bpic, *rp, *gp, *bp; + int *p0, *p1, *p2; + int x, y, dx, dy, minx, miny; + int r, g, b, k, pici; + int Y, Cr, Cb; + + rpic = image->chans[0]; + gpic = image->chans[1]; + bpic = image->chans[2]; + minx = 8*(mcu%nacross); + dx = 8; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*(mcu/nacross); + dy = 8; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + k = 0; + for(y=0; y>11)+CLAMPOFF]; + *gp++ = clamp[(g>>11)+CLAMPOFF]; + *bp++ = clamp[(b>>11)+CLAMPOFF]; + } + pici += h->X; + k += 8; + } +} + +static +void +colormap(Header *h, int colorspace, Rawimage *image, int *data0[8*8], int *data1[8*8], int *data2[8*8], int mcu, int nacross, int Hmax, int Vmax, int *H, int *V) +{ + unsigned char *rpic, *gpic, *bpic; + int x, y, dx, dy, minx, miny; + int r, g, b, pici, H0, H1, H2; + int t, b0, b1, b2, y0, y1, y2, x0, x1, x2; + int Y, Cr, Cb; + + rpic = image->chans[0]; + gpic = image->chans[1]; + bpic = image->chans[2]; + minx = 8*Hmax*(mcu%nacross); + dx = 8*Hmax; + if(minx+dx > h->X) + dx = h->X-minx; + miny = 8*Vmax*(mcu/nacross); + dy = 8*Vmax; + if(miny+dy > h->Y) + dy = h->Y-miny; + pici = miny*h->X+minx; + H0 = H[0]; + H1 = H[1]; + H2 = H[2]; + for(y=0; y>11)+CLAMPOFF]; + gpic[pici+x] = clamp[(g>>11)+CLAMPOFF]; + bpic[pici+x] = clamp[(b>>11)+CLAMPOFF]; + } + if(x0*H0/Hmax >= 8){ + x0 = 0; + b0++; + } + if(x1*H1/Hmax >= 8){ + x1 = 0; + b1++; + } + if(x2*H2/Hmax >= 8){ + x2 = 0; + b2++; + } + } + pici += h->X; + } +} + +/* + * decode next 8-bit value from entropy-coded input. chart F-26 + */ +static +int +decode(Header *h, Huffman *t) +{ + int code, v, cnt, sr, i; + int *maxcode; + + 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); + cnt = h->cnt; + sr = h->sr; + code <<= 1; + for(i = 9; i<17; i++){ + cnt--; + if(sr & (1<= 17){ + /* bad code */ + code = 0; + i = 0; + } + h->cnt = cnt; + return t->val[t->valptr[i]+(code-t->mincode[i])]; +} + +/* + * load next byte of input + */ +static +int +nextbyte(Header *h, int marker) +{ + int b, b2; + + if(h->peek >= 0){ + b = h->peek; + h->peek = -1; + }else{ + b = Bgetc(h->fd); + if(b == Beof) + jpgerror(h, "truncated file"); + b &= 0xFF; + } + + if(b == 0xFF){ + if(marker) + return b; + b2 = Bgetc(h->fd); + if(b2 != 0){ + if(b2 == Beof) + jpgerror(h, "truncated file"); + b2 &= 0xFF; + if(b2 == DNL) + jpgerror(h, "ReadJPG: DNL marker unimplemented"); + /* decoder is reading into marker; satisfy it and restore state */ + Bungetc(h->fd); + h->peek = b; + } + } + h->cnt += 8; + h->sr = (h->sr<<8) | b; + return b; +} + +/* + * return next s bits of input, MSB first, and level shift it + */ +static +int +receive(Header *h, int s) +{ + int v, m; + + while(h->cnt < s) + nextbyte(h, 0); + h->cnt -= s; + v = h->sr >> h->cnt; + m = (1<>1)) + v += ~(m-1)+1; + return v; +} + +/* + * return next s bits of input, decode as EOB + */ +static +int +receiveEOB(Header *h, int s) +{ + int v, m; + + while(h->cnt < s) + nextbyte(h, 0); + h->cnt -= s; + v = h->sr >> h->cnt; + m = (1<cnt < 1) + nextbyte(h, 0); + h->cnt--; + return (h->sr >> h->cnt) & 1; +} + +/* + * Scaled integer implementation. + * inverse two dimensional DCT, Chen-Wang algorithm + * (IEEE ASSP-32, pp. 803-816, Aug. 1984) + * 32-bit integer arithmetic (8 bit coefficients) + * 11 mults, 29 adds per DCT + * + * coefficients extended to 12 bit for IEEE1180-1990 compliance + */ + +enum { + W1 = 2841, /* 2048*sqrt(2)*cos(1*pi/16)*/ + W2 = 2676, /* 2048*sqrt(2)*cos(2*pi/16)*/ + W3 = 2408, /* 2048*sqrt(2)*cos(3*pi/16)*/ + W5 = 1609, /* 2048*sqrt(2)*cos(5*pi/16)*/ + W6 = 1108, /* 2048*sqrt(2)*cos(6*pi/16)*/ + W7 = 565, /* 2048*sqrt(2)*cos(7*pi/16)*/ + + W1pW7 = 3406, /* W1+W7*/ + W1mW7 = 2276, /* W1-W7*/ + W3pW5 = 4017, /* W3+W5*/ + W3mW5 = 799, /* W3-W5*/ + W2pW6 = 3784, /* W2+W6*/ + W2mW6 = 1567, /* W2-W6*/ + + R2 = 181 /* 256/sqrt(2)*/ +}; + +static +void +idct(int b[8*8]) +{ + int x, y, eighty, v; + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + int *p; + + /* transform horizontally*/ + for(y=0; y<8; y++){ + eighty = y<<3; + /* if all non-DC components are zero, just propagate the DC term*/ + p = b+eighty; + if(p[1]==0) + if(p[2]==0 && p[3]==0) + if(p[4]==0 && p[5]==0) + if(p[6]==0 && p[7]==0){ + v = p[0]<<3; + p[0] = v; + p[1] = v; + p[2] = v; + p[3] = v; + p[4] = v; + p[5] = v; + p[6] = v; + p[7] = v; + continue; + } + /* prescale*/ + x0 = (p[0]<<11)+128; + x1 = p[4]<<11; + x2 = p[6]; + x3 = p[2]; + x4 = p[1]; + x5 = p[7]; + x6 = p[5]; + x7 = p[3]; + /* first stage*/ + x8 = W7*(x4+x5); + x4 = x8 + W1mW7*x4; + x5 = x8 - W1pW7*x5; + x8 = W3*(x6+x7); + x6 = x8 - W3mW5*x6; + x7 = x8 - W3pW5*x7; + /* second stage*/ + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2); + x2 = x1 - W2pW6*x2; + x3 = x1 + W2mW6*x3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + /* third stage*/ + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + /* fourth stage*/ + p[0] = (x7+x1)>>8; + p[1] = (x3+x2)>>8; + p[2] = (x0+x4)>>8; + p[3] = (x8+x6)>>8; + p[4] = (x8-x6)>>8; + p[5] = (x0-x4)>>8; + p[6] = (x3-x2)>>8; + p[7] = (x7-x1)>>8; + } + /* transform vertically*/ + for(x=0; x<8; x++){ + /* if all non-DC components are zero, just propagate the DC term*/ + p = b+x; + if(p[8*1]==0) + if(p[8*2]==0 && p[8*3]==0) + if(p[8*4]==0 && p[8*5]==0) + if(p[8*6]==0 && p[8*7]==0){ + v = (p[8*0]+32)>>6; + p[8*0] = v; + p[8*1] = v; + p[8*2] = v; + p[8*3] = v; + p[8*4] = v; + p[8*5] = v; + p[8*6] = v; + p[8*7] = v; + continue; + } + /* prescale*/ + x0 = (p[8*0]<<8)+8192; + x1 = p[8*4]<<8; + x2 = p[8*6]; + x3 = p[8*2]; + x4 = p[8*1]; + x5 = p[8*7]; + x6 = p[8*5]; + x7 = p[8*3]; + /* first stage*/ + x8 = W7*(x4+x5) + 4; + x4 = (x8+W1mW7*x4)>>3; + x5 = (x8-W1pW7*x5)>>3; + x8 = W3*(x6+x7) + 4; + x6 = (x8-W3mW5*x6)>>3; + x7 = (x8-W3pW5*x7)>>3; + /* second stage*/ + x8 = x0 + x1; + x0 -= x1; + x1 = W6*(x3+x2) + 4; + x2 = (x1-W2pW6*x2)>>3; + x3 = (x1+W2mW6*x3)>>3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + /* third stage*/ + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (R2*(x4+x5)+128)>>8; + x4 = (R2*(x4-x5)+128)>>8; + /* fourth stage*/ + p[8*0] = (x7+x1)>>14; + p[8*1] = (x3+x2)>>14; + p[8*2] = (x0+x4)>>14; + p[8*3] = (x8+x6)>>14; + p[8*4] = (x8-x6)>>14; + p[8*5] = (x0-x4)>>14; + p[8*6] = (x3-x2)>>14; + p[8*7] = (x7-x1)>>14; + } +} diff --git a/sys/src/cmd/pict/readpng.c b/sys/src/cmd/pict/readpng.c new file mode 100644 index 0000000..bbf3b70 --- /dev/null +++ b/sys/src/cmd/pict/readpng.c @@ -0,0 +1,531 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int debug; + +enum +{ + IDATSIZE = 8*1024*1024, + + /* filtering algorithms */ + 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 ZlibR; +typedef struct ZlibW ZlibW; + +struct ZlibW +{ + unsigned char *data; /* Rawimage data */ + int ndata; + int noutchan; + int chandesc; + int nchan; + + unsigned char *scan; /* new scanline */ + unsigned char *lastscan; /* previous scan line */ + int scanlen; /* scan line length */ + int scanpos; /* scan position */ + + int dx; /* width of image */ + int dy; /* height of image */ + int bpc; /* bits per channel (per pixel) */ + int y; /* current scan line */ + int pass; /* adam7 pass#; 0 means no adam7 */ + unsigned char palette[3*256]; /* color palette */ + int palsize; /* number of palette entries */ +}; + +struct ZlibR +{ + Biobuf *io; /* input buffer */ + unsigned char *buf; /* malloc'ed staging buffer */ + unsigned char *p; /* next byte to decompress */ + unsigned char *e; /* end of buffer */ + ZlibW *w; +}; + +static uint32_t *crctab; +static unsigned char PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'}; + +static uint32_t +get4(unsigned char *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(uint32_t n, int clear) +{ + void *p; + + p = mallocz(n, clear); + if(p == nil) + sysfatal("malloc: %r"); + return p; +} + +static int +getchunk(Biobuf *b, char *type, unsigned char *d, int m) +{ + unsigned char buf[8]; + uint32_t 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->p >= z->e){ + Again: + z->p = z->buf; + z->e = z->p; + n = getchunk(z->io, type, z->p, IDATSIZE); + if(n < 0 || strcmp(type, "IEND") == 0) + return -1; + z->e = z->p + n; + if(!strcmp(type,"PLTE")){ + if(n < 3 || n > 3*256 || n%3) + sysfatal("invalid PLTE chunk len %d", n); + memcpy(z->w->palette, z->p, n); + z->w->palsize = 256; + goto Again; + } + if(type[0] & PropertyBit) + goto Again; /* skip auxiliary chunks fornow */ + if(strcmp(type,"IDAT")){ + sysfatal("unrecognized mandatory chunk %s", type); + goto Again; + } + } + return *z->p++; +} + +static unsigned char +paeth(unsigned char a, unsigned char b, unsigned char c) +{ + int p, pa, pb, pc; + + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + + if(pa <= pb && pa <= pc) + return a; + else if(pb <= pc) + return b; + return c; +} + +static void +unfilter(int alg, unsigned char *buf, unsigned char *up, int len, int bypp) +{ + int i; + + switch(alg){ + default: + fprint(2, "unknown filtering scheme %d\n", alg); + case FilterNone: + break; + + case FilterSub: + for(i = bypp; i < len; ++i) + buf[i] += buf[i-bypp]; + break; + + case FilterUp: + for(i = 0; i < len; ++i) + buf[i] += up[i]; + break; + + case FilterAvg: + for(i = 0; i < bypp; ++i) + buf[i] += (0+up[i])/2; + for(; i < len; ++i) + buf[i] += (buf[i-bypp]+up[i])/2; + break; + + case FilterPaeth: + for(i = 0; i < bypp; ++i) + buf[i] += paeth(0, up[i], 0); + for(; i < len; ++i) + buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]); + break; + } +} + +struct { + int x; + int y; + int dx; + int dy; +} adam7[] = { + {0,0,1,1}, /* eve alone */ + {0,0,8,8}, /* pass 1 */ + {4,0,8,8}, /* pass 2 */ + {0,4,4,8}, /* pass 3 */ + {2,0,4,4}, /* pass 4 */ + {0,2,2,4}, /* pass 5 */ + {1,0,2,2}, /* pass 6 */ + {0,1,1,2}, /* pass 7 */ +}; + +static void +scan(int len, ZlibW *z) +{ + int chan, i, j, nbit, off, val; + unsigned char pixel[4], *p, *w; + + unfilter(z->scan[0], z->scan+1, z->lastscan+1, len-1, (z->nchan*z->bpc+7)/8); + + /* + * loop over raw bits extracting pixel values and converting to 8-bit + */ + nbit = 0; + chan = 0; + val = 0; + off = z->y*z->dx + adam7[z->pass].x; + w = z->data + z->noutchan*off; + p = z->scan+1; /* skip alg byte */ + len--; + for(i=0; i>3] & (1<<(7-(i&7)))) + val++; + if(++nbit == z->bpc){ + /* finished the value */ + pixel[chan++] = (val*255)/((1<bpc)-1); + val = 0; + nbit = 0; + if(chan == z->nchan){ + /* finished the pixel */ + if(off < z->dx*z->dy){ + if(z->nchan < 3 && z->palsize){ + j = pixel[0]; + if(z->bpc < 8) + j >>= 8-z->bpc; + if(j >= z->palsize) + sysfatal("index %d >= palette size %d", j, z->palsize); + pixel[3] = pixel[1]; /* alpha */ + pixel[0] = z->palette[3*j]; + pixel[1] = z->palette[3*j+1]; + pixel[2] = z->palette[3*j+2]; + } + switch(z->chandesc){ + case CYA16: + // print("%.2x%.2x ", pixel[0], pixel[1]); + *w++ = pixel[1]; + *w++ += (pixel[0]*pixel[1])/255; + break; + case CRGBA32: + // print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]); + *w++ += pixel[3]; + *w++ += (pixel[2]*pixel[3])/255; + *w++ += (pixel[1]*pixel[3])/255; + *w++ += (pixel[0]*pixel[3])/255; + break; + case CRGB24: + *w++ = pixel[2]; + *w++ = pixel[1]; + case CY: + *w++ = pixel[0]; + break; + } + w += (adam7[z->pass].dx-1)*z->noutchan; + } + off += adam7[z->pass].dx; + if(off >= (z->y+1)*z->dx){ + /* finished the line */ + return; + } + chan = 0; + } + } + } + sysfatal("scan line too short"); +} + +static int +scanbytes(ZlibW *z) +{ + int bits, n, adx, dx; + + if(adam7[z->pass].y >= z->dy || adam7[z->pass].x >= z->dx) + return 0; + adx = adam7[z->pass].dx; + dx = z->dx - adam7[z->pass].x; + if(dx <= 0) + n = 1; + else + n = (dx+adx-1)/adx; + if(n != 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx){ + fprint(2, "%d/%d != 1+(%d-1)/%d = %d\n", + z->dx - adam7[z->pass].x - 1 + adx, adx, + z->dx - (adam7[z->pass].x+1), adam7[z->pass].dx, + 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx); + } + bits = n*z->bpc*z->nchan; + return 1 + (bits+7)/8; +} + +static int +nextpass(ZlibW *z) +{ + int len; + + memset(z->lastscan, 0, z->scanlen); + do{ + z->pass = (z->pass+1)%8; + z->y = adam7[z->pass].y; + len = scanbytes(z); + }while(len < 2); + return len; +} + +static int +zwrite(void *vz, void *vbuf, int n) +{ + int oldn, m, len; + unsigned char *buf, *t; + ZlibW *z; + + z = vz; + buf = vbuf; + oldn = n; + + len = scanbytes(z); + if(len < 2) + len = nextpass(z); + + while(n > 0){ + m = len - z->scanpos; + if(m > n){ + /* save final partial line */ + memmove(z->scan+z->scanpos, buf, n); + z->scanpos += n; + break; + } + + /* fill line */ + memmove(z->scan+z->scanpos, buf, m); + buf += m; + n -= m; + + /* process line */ + scan(len, z); + t = z->scan; + z->scan = z->lastscan; + z->lastscan = t; + + z->scanpos = 0; + z->y += adam7[z->pass].dy; + if(z->y >= z->dy) + len = nextpass(z); + } + return oldn; +} + +static Rawimage* +readslave(Biobuf *b) +{ + char type[5]; + int bpc, colorfmt, dx, dy, err, n, nchan, nout, useadam7; + unsigned char *buf, *h; + Rawimage *image; + ZlibR zr; + ZlibW zw; + + buf = pngmalloc(IDATSIZE, 0); + if(Bread(b, buf, sizeof PNGmagic) != sizeof PNGmagic + || 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; + dx = get4(h); + h += 4; + dy = get4(h); + h += 4; + if(dx <= 0 || dy <= 0) + sysfatal("impossible image size %dx%d", dx, dy); + if(debug) + fprint(2, "readpng %dx%d\n", dx, dy); + + bpc = *h++; + colorfmt = *h++; + nchan = 0; + if(*h++ != 0) + sysfatal("only deflate supported for now [%d]", h[-1]); + if(*h++ != FilterNone) + sysfatal("only FilterNone supported for now [%d]", h[-1]); + useadam7 = *h++; + USED(h); + + image = pngmalloc(sizeof(Rawimage), 1); + image->r = Rect(0, 0, dx, dy); + nout = 0; + switch(colorfmt){ + case 0: /* grey */ + if(bpc != 1 && bpc != 2 && bpc != 4 && bpc != 8 && bpc != 16) + sysfatal("invalid greyscale bpc %d", bpc); + image->nchans = 1; + image->chandesc = CY; + nout = 1; + nchan = 1; + break; + case 2: /* rgb */ + if(bpc != 8 && bpc != 16) + sysfatal("invalid rgb bpc %d", bpc); + image->nchans = 1; + image->chandesc = CRGB24; + nout = 3; + nchan = 3; + break; + case 3: /* indexed rgb with PLTE */ + if(bpc != 1 && bpc != 2 && bpc != 4 && bpc != 8) + sysfatal("invalid indexed rgb bpc %d", bpc); + image->nchans = 1; + image->chandesc = CRGB24; + nout = 3; + nchan = 1; + break; + case 4: /* grey+alpha */ + if(bpc != 8 && bpc != 16) + sysfatal("invalid grey+alpha bpc %d", bpc); + image->nchans = 1; + image->chandesc = CYA16; + nout = 2; + nchan = 2; + break; + case 6: /* rgb+alpha */ + if(bpc != 8 && bpc != 16) + sysfatal("invalid rgb+alpha bpc %d", bpc); + image->nchans = 1; + image->chandesc = CRGBA32; + nout = 4; + nchan = 4; + break; + default: + sysfatal("unsupported color scheme %d", h[-1]); + } + image->chanlen = dx*dy*nout; + image->chans[0] = pngmalloc(image->chanlen, 0); + memset(image->chans[0], 0, image->chanlen); + + memset(&zr, 0, sizeof zr); + zr.w = &zw; + zr.io = b; + zr.buf = buf; + + memset(&zw, 0, sizeof zw); + if(useadam7) + zw.pass = 1; + zw.data = image->chans[0]; + zw.ndata = image->chanlen; + zw.chandesc = image->chandesc; + zw.noutchan = nout; + + zw.dx = dx; + zw.dy = dy; + zw.scanlen = (nchan*dx*bpc+7)/8+1; + zw.scan = pngmalloc(zw.scanlen, 1); + zw.lastscan = pngmalloc(zw.scanlen, 1); + zw.nchan = nchan; + zw.bpc = bpc; + + err = inflatezlib(&zw, zwrite, &zr, zread); + + if(err) + sysfatal("inflatezlib %s\n", flateerr(err)); + + free(buf); + free(zw.scan); + free(zw.lastscan); + return image; +} + +Rawimage** +Breadpng(Biobuf *b, int colorspace) +{ + Rawimage **array, *r; + + if(colorspace != CRGB){ + werrstr("ReadPNG: unknown color space %d", colorspace); + return nil; + } + pnginit(); + array = malloc(2*sizeof(*array)); + if(array==nil) + return nil; + r = readslave(b); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage** +readpng(int fd, int colorspace) +{ + Biobuf b; + Rawimage **a; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadpng(&b, colorspace); + Bterm(&b); + return a; +} diff --git a/sys/src/cmd/pict/readppm.c b/sys/src/cmd/pict/readppm.c new file mode 100644 index 0000000..f1a15e9 --- /dev/null +++ b/sys/src/cmd/pict/readppm.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include "imagefile.h" + +Rawimage *readppm(Biobuf*, Rawimage*); + +/* + * fetch a non-comment character. + */ +static +int +Bgetch(Biobufhdr *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(Biobufhdr *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(Biobufhdr *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(Biobufhdr *b) +{ + if(nbit == 0) { + nbit = 8; + bitc = Bgetc(b); + if(bitc == -1) + return -1; + } + nbit--; + return (bitc >> nbit) & 0x1; +} + +static +void +Bflushbit(Biobufhdr* _) +{ + 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)(Biobufhdr*); + int nchan; + int chandesc; + int invert; + void (*flush)(Biobufhdr*); +}; + +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)(Biobufhdr*); + unsigned char *rgb[3]; + char buf[ERRMAX]; + char *e; + Pix *p; + + e = "bad file format"; + if(Bgetc(b) != 'P') + goto Error; + + c = Bgetc(b); + for(p=pix; p->magic; p++) + if(p->magic == c) + break; + if(p->magic == 0) + goto Error; + + + wid = Bgetint(b); + ht = Bgetint(b); + if(wid <= 0 || ht <= 0) + goto Error; + a->r = Rect(0,0,wid,ht); + + maxcol = p->maxcol; + if(maxcol == 0) { + maxcol = Bgetint(b); + if(maxcol <= 0) + goto Error; + } + + e = "out of memory"; + for(i=0; inchan; i++) + if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil) + goto Error; + a->nchans = p->nchan; + a->chanlen = wid*ht; + a->chandesc = p->chandesc; + + e = "error reading file"; + + fetch = p->fetch; + nchan = p->nchan; + invert = p->invert; + for(r=0; rflush) + (*p->flush)(b); + } + + return a; + +Error: + errstr(buf, sizeof buf); + if(buf[0] == 0) + strcpy(buf, e); + errstr(buf, sizeof buf); + + for(i=0; i<3; i++) + free(a->chans[i]); + free(a->cmap); + return nil; +} diff --git a/sys/src/cmd/pict/readtga.c b/sys/src/cmd/pict/readtga.c new file mode 100644 index 0000000..faf170c --- /dev/null +++ b/sys/src/cmd/pict/readtga.c @@ -0,0 +1,510 @@ +/* + * TGA is a fairly dead standard, however in the video industry + * it is still used a little for test patterns and the like. + * + * Thus we ignore any alpha channels. + */ + +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum { + HdrLen = 18, +}; + +typedef struct { + int idlen; /* length of string after header */ + int cmaptype; /* 1 => datatype = 1 => colourmapped */ + int datatype; /* see below */ + int cmaporigin; /* index of first entry in colour map */ + int cmaplen; /* length of colour map */ + int cmapbpp; /* bits per pixel of colour map: 16, 24, or 32 */ + int xorigin; /* source image origin */ + int yorigin; + int width; + int height; + int bpp; /* bits per pixel of image: 16, 24, or 32 */ + int descriptor; + unsigned char *cmap; /* colour map (optional) */ +} Tga; + +/* + * descriptor: + * d0-3 = number of attribute bits per pixel + * d4 = reserved, always zero + * d6-7 = origin: 0=lower left, 1=upper left, 2=lower right, 3=upper right + * d8-9 = interleave: 0=progressive, 1=2 way, 3=4 way, 4=reserved. + */ + +char *datatype[] = { + [0] "No image data", + [1] "Color-mapped", + [2] "RGB", + [3] "B&W", + [9] "RLE color-mapped", + [10] "RLE RGB", + [11] "RLE B&W", + [32] "Compressed color", + [33] "Quadtree compressed color", +}; + +static int +Bgeti(Biobuf *bp) +{ + int x, y; + + if((x = Bgetc(bp)) < 0) + return -1; + if((y = Bgetc(bp)) < 0) + return -1; + return (y<<8)|x; +} + +static int +fixcmap(unsigned char *cmap, int *cmapbpp, int cmaplen) +{ + int i; + unsigned short x; + unsigned char tmp; + + switch(*cmapbpp){ + case 32: + /* swap B with R */ + for(i = 0; i < cmaplen; i++){ + tmp = cmap[4*i+0]; + cmap[4*i+0] = cmap[4*i+2]; + cmap[4*i+2] = tmp; + } + break; + case 24: + /* swap B with R */ + for(i = 0; i < cmaplen; i++){ + tmp = cmap[3*i+0]; + cmap[3*i+0] = cmap[3*i+2]; + cmap[3*i+2] = tmp; + } + break; + case 16: + case 15: + /* convert to 24-bit colormap */ + if((cmap = realloc(cmap, 3*cmaplen)) == nil) + return -1; + for(i = cmaplen-1; i >= 0; i--){ + x = (cmap[2*i+1]<<8) | cmap[2*i+0]; + tmp = (x>>0)&0x1f; + cmap[3*i+2] = (tmp<<3) | (tmp>>2); + tmp = (x>>5)&0x1f; + cmap[3*i+1] = (tmp<<3) | (tmp>>2); + tmp = (x>>10)&0x1f; + cmap[3*i+0] = (tmp<<3) | (tmp>>2); + } + *cmapbpp = 24; + break; + default: + break; + } + + return 0; +} + +static Tga * +rdhdr(Biobuf *bp) +{ + int n; + Tga *h; + + if((h = malloc(sizeof(Tga))) == nil) + return nil; + if((h->idlen = Bgetc(bp)) == -1) + return nil; + if((h->cmaptype = Bgetc(bp)) == -1) + return nil; + if((h->datatype = Bgetc(bp)) == -1) + return nil; + if((h->cmaporigin = Bgeti(bp)) == -1) + return nil; + if((h->cmaplen = Bgeti(bp)) == -1) + return nil; + if((h->cmapbpp = Bgetc(bp)) == -1) + return nil; + if((h->xorigin = Bgeti(bp)) == -1) + return nil; + if((h->yorigin = Bgeti(bp)) == -1) + return nil; + if((h->width = Bgeti(bp)) == -1) + return nil; + if((h->height = Bgeti(bp)) == -1) + return nil; + if((h->bpp = Bgetc(bp)) == -1) + return nil; + if((h->descriptor = Bgetc(bp)) == -1) + return nil; + + /* skip over ID, usually empty anyway */ + if(Bseek(bp, h->idlen, 1) < 0){ + free(h); + return nil; + } + + if(h->cmaptype == 0){ + h->cmap = 0; + return h; + } + + /* skip over unused color map data */ + n = (h->cmapbpp/8)*h->cmaporigin; + if(Bseek(bp, n, 1) < 0){ + free(h); + return nil; + } + h->cmaplen -= h->cmaporigin; + + n = (h->cmapbpp/8)*h->cmaplen; + if((h->cmap = malloc(n)) == nil){ + free(h); + return nil; + } + if(Bread(bp, h->cmap, n) != n){ + free(h); + free(h->cmap); + return nil; + } + if(fixcmap(h->cmap, &h->cmapbpp, h->cmaplen) != 0){ + free(h); + free(h->cmap); + return nil; + } + return h; +} + +static int +cmap(Biobuf *bp, unsigned char *l, int num) +{ + return Bread(bp, l, num); +} + +static int +luma(Biobuf *bp, int bpp, unsigned char *l, int num) +{ + char tmp[2]; + int got; + + if(bpp == 8){ + got = Bread(bp, l, num); + } + else{ + for(got = 0; got < num; got++){ + if(Bread(bp, tmp, 2) != 2) + break; + *l++ = tmp[0]; + } + } + return got; +} + +static int +luma_rle(Biobuf *bp, int bpp, unsigned char *l, int num) +{ + unsigned char len, p; + int got; + + for(got = 0; got < num;){ + if(Bread(bp, &len, 1) != 1) + break; + if(len & 0x80){ + len &= 0x7f; + if(luma(bp, bpp, &p, 1) != 1) + break; + for(len++; len > 0 && got < num; len--, got++) + *l++ = p; + } + else{ + for(len++; len > 0 && got < num; len--, got++) + if(luma(bp, bpp, l++, 1) != 1) + return got; + } + } + return got; +} + +static int +cmap_rle(Biobuf *bp, unsigned char *l, int num) +{ + return luma_rle(bp, 8, l, num); +} + +static int +rgba(Biobuf *bp, int bpp, unsigned char *r, unsigned char *g, unsigned char *b, int num) +{ + int i; + unsigned char buf[4], tmp; + unsigned short x; + + switch(bpp){ + case 16: + case 15: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 2) != 2) + break; + x = (buf[1]<<8) | buf[0]; + tmp = (x>>0)&0x1f; + *b++ = (tmp<<3) | (tmp>>2); + tmp = (x>>5)&0x1f; + *g++ = (tmp<<3) | (tmp>>2); + tmp = (x>>10)&0x1f; + *r++ = (tmp<<3) | (tmp>>2); + } + break; + case 24: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 3) != 3) + break; + *b++ = buf[0]; + *g++ = buf[1]; + *r++ = buf[2]; + } + break; + case 32: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 4) != 4) + break; + *b++ = buf[0]; + *g++ = buf[1]; + *r++ = buf[2]; + } + break; + default: + i = 0; + break; + } + return i; +} + +static int +rgba_rle(Biobuf *bp, int bpp, unsigned char *r, unsigned char *g, unsigned char *b, int num) +{ + unsigned char len; + int i, got; + + for(got = 0; got < num; got += len){ + if(Bread(bp, &len, 1) != 1) + break; + if(len & 0x80){ + len &= 0x7f; + len += 1; /* run of zero is meaningless */ + if(rgba(bp, bpp, r, g, b, 1) != 1) + break; + for(i = 1; i < len && got+i < num; i++){ + r[i] = *r; + g[i] = *g; + b[i] = *b; + } + len = i; + } + else{ + len += 1; /* raw block of zero is meaningless */ + if(rgba(bp, bpp, r, g, b, len) != len) + break; + } + r += len; + g += len; + b += len; + } + return got; +} + +int +flip(Rawimage *ar) +{ + int w, h, c, l; + unsigned char *t, *s, *d; + + w = Dx(ar->r); + h = Dy(ar->r); + if((t = malloc(w)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + return -1; + } + + for(c = 0; c < ar->nchans; c++){ + s = ar->chans[c]; + d = ar->chans[c] + ar->chanlen - w; + for(l = 0; l < (h/2); l++){ + memcpy(t, s, w); + memcpy(s, d, w); + memcpy(d, t, w); + s += w; + d -= w; + } + } + free(t); + return 0; +} + +int +reflect(Rawimage *ar) +{ + int w, h, c, l, p; + unsigned char t, *sol, *eol, *s, *d; + + w = Dx(ar->r); + h = Dy(ar->r); + + for(c = 0; c < ar->nchans; c++){ + sol = ar->chans[c]; + eol = ar->chans[c] +w -1; + for(l = 0; l < h; l++){ + s = sol; + d = eol; + for(p = 0; p < w/2; p++){ + t = *s; + *s = *d; + *d = t; + s++; + d--; + } + sol += w; + eol += w; + } + } + return 0; +} + + +Rawimage** +Breadtga(Biobuf *bp) +{ + Tga *h; + int n, c, num; + unsigned char *r, *g, *b; + Rawimage *ar, **array; + + if((h = rdhdr(bp)) == nil){ + werrstr("ReadTGA: bad header %r"); + return nil; + } + + if(0){ + fprint(2, "idlen=%d\n", h->idlen); + fprint(2, "cmaptype=%d\n", h->cmaptype); + fprint(2, "datatype=%s\n", datatype[h->datatype]); + fprint(2, "cmaporigin=%d\n", h->cmaporigin); + fprint(2, "cmaplen=%d\n", h->cmaplen); + fprint(2, "cmapbpp=%d\n", h->cmapbpp); + fprint(2, "xorigin=%d\n", h->xorigin); + fprint(2, "yorigin=%d\n", h->yorigin); + fprint(2, "width=%d\n", h->width); + fprint(2, "height=%d\n", h->height); + fprint(2, "bpp=%d\n", h->bpp); + fprint(2, "descriptor=%d\n", h->descriptor); + } + + array = nil; + if((ar = calloc(1, sizeof(Rawimage))) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + + if((array = calloc(2, sizeof(Rawimage *))) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + array[0] = ar; + array[1] = nil; + + if(h->datatype == 3 || h->datatype == 11){ + ar->nchans = 1; + ar->chandesc = CY; + } + else if(h->datatype == 1){ + ar->nchans = 1; + ar->chandesc = CRGB1; + } + else if(h->datatype == 9){ + ar->nchans = 1; + ar->chandesc = (h->cmapbpp == 32) ? CRGBV : CRGB1; + } + else{ + ar->nchans = 3; + ar->chandesc = CRGB; + } + + ar->cmap = h->cmap; + ar->cmaplen = (h->cmapbpp/8)*h->cmaplen; + ar->chanlen = h->width*h->height; + ar->r = Rect(0, 0, h->width, h->height); + for(c = 0; c < ar->nchans; c++) + if((ar->chans[c] = malloc(h->width*h->height)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + r = ar->chans[0]; + g = ar->chans[1]; + b = ar->chans[2]; + + num = h->width*h->height; + switch(h->datatype){ + case 1: + n = cmap(bp, r, num); + break; + case 2: + n = rgba(bp, h->bpp, r, g, b, num); + break; + case 3: + n = luma(bp, h->bpp, r, num); + break; + case 9: + n = cmap_rle(bp, r, num); + break; + case 10: + n = rgba_rle(bp, h->bpp, r, g, b, num); + break; + case 11: + n = luma_rle(bp, h->bpp, r, num); + break; + default: + werrstr("ReadTGA: type=%d (%s) unsupported\n", h->datatype, datatype[h->datatype]); + goto Error; + } + + if(n != num){ + werrstr("ReadTGA: decode fail (%d!=%d) - %r\n", n, num); + goto Error; + } + if((h->descriptor&(1<<4)) != 0) + reflect(ar); + if((h->descriptor&(1<<5)) == 0) + flip(ar); + + free(h); + return array; +Error: + + if(ar) + for (c = 0; c < ar->nchans; c++) + free(ar->chans[c]); + free(ar); + free(array); + free(h->cmap); + free(h); + return nil; +} + +Rawimage** +readtga(int fd) +{ + Rawimage * *a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadtga(&b); + Bterm(&b); + return a; +} + + diff --git a/sys/src/cmd/pict/readtif.c b/sys/src/cmd/pict/readtif.c new file mode 100644 index 0000000..186626e --- /dev/null +++ b/sys/src/cmd/pict/readtif.c @@ -0,0 +1,1862 @@ +/* +* code/documentation: +* http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +* http://www.fileformat.info/format/tiff/egff.htm +* http://www.fileformat.info/mirror/egff/ch09_05.htm +* http://www.itu.int/rec/T-REC-T.4-199904-S/en +* http://www.itu.int/rec/T-REC-T.6-198811-I/en +* +* many thanks to paul bourke for a simple description of tiff: +* http://paulbourke.net/dataformats/tiff/ +* +* copy-pasted fax codes and lzw help: +* http://www.remotesensing.org/libtiff/ +*/ +#include +#include +#include +#include +#include "imagefile.h" + +enum { + II = 0x4949, /* little-endian */ + MM = 0x4d4d, /* big-endian */ + TIF = 0x002a /* tiff magic number */ +}; + +enum { + Byte = 1, + Short = 3, + Long = 4 +}; + +enum { + Width = 0x0100, + Length = 0x0101, + Bits = 0x0102, + + Compression = 0x0103, + Nocomp = 0x0001, + Huffman = 0x0002, + T4enc = 0x0003, + T6enc = 0x0004, + Lzwenc = 0x0005, + Packbits = 0x8005, + + Photometric = 0x0106, + Whitezero = 0x0000, + Blackzero = 0x0001, + Rgb = 0x0002, + Palette = 0x0003, + + Fill = 0x010a, + Strips = 0x0111, + Orientation = 0x0112, + Samples = 0x0115, + Rows = 0x0116, + Counts = 0x0117, + Planar = 0x011c, + T4opts = 0x0124, + T6opts = 0x0125, + Predictor = 0x13d, + Color = 0x0140 +}; + +enum { + Nfaxcodes = 10, + Nfaxtab = 105 +}; + +enum { + Clrcode = 256, + Eoicode = 257, + Tabsz = 1<<12 +}; + +typedef struct Tab Tab; +typedef struct Fax Fax; +typedef struct Code Code; +typedef struct Lzw Lzw; +typedef struct Fld Fld; +typedef struct Tif Tif; + +struct Tab { + int len; + int code; + int run; /* run length */ +}; + +struct Fax { + uint32_t n; + int m; + int st; /* state */ + Tab *tab[2]; + int ntab; /* position in tab */ + Tab *eol; + int eolfill; + int (*getbit)(Fax *); + uint32_t *l1; + uint32_t *l2; + uint32_t nl; + unsigned char *data; + uint32_t next; /* next strip offset in data */ +}; + +struct Code { + unsigned char val; + Code *next; +}; + +struct Lzw { + Code tab[Tabsz]; + int ntab; + int len; /* code length */ + uint32_t n; + int m; + unsigned char *data; + uint32_t next; /* next strip offset in data */ + /* remaining allocated codes */ + Code *first; + Code *last; +}; + +struct Fld { + uint tag; + uint typ; + uint32_t cnt; + uint32_t off; /* value offset */ + uint32_t *val; + uint32_t nval; +}; + +struct Tif { + Biobuf *fd; + uint end; /* endianness */ + unsigned char tmp[4]; + unsigned char *buf; + uint32_t nbuf; + int eof; /* reached end of image */ + uint32_t n; /* offset in buf array */ + uint32_t off; + uint nfld; + Fld *fld; + uint32_t (*byte2)(unsigned char *); + uint32_t (*byte4)(unsigned char *); + + /* field data */ + uint32_t dx; + uint32_t dy; + uint32_t depth; + uint32_t comp; + unsigned char *(*uncompress)(Tif *); + uint32_t orientation; + uint32_t photo; + int (*decode)(Tif *, Rawimage *, unsigned char *); + uint32_t fill; + uint32_t *strips; + uint32_t nstrips; + uint32_t samples; + uint32_t rows; + uint32_t *counts; + uint32_t ncounts; + uint32_t planar; + uint32_t *color; /* color map */ + uint32_t ncolor; + uint32_t t4; + uint32_t t6; + uint32_t predictor; + + /* image data */ + unsigned char *data; + uint32_t ndata; +}; + +/* +* imported from libdraw/arith.c to permit +* extern log2 function +*/ +static int log2[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, + -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, + -1, -1, -1, -1, -1, -1, -1, 5 +}; + +static Tab faxwhite[Nfaxtab] = { + {4, 0x7, 2}, /* 0111 */ + {4, 0x8, 3}, /* 1000 */ + {4, 0xb, 4}, /* 1011 */ + {4, 0xc, 5}, /* 1100 */ + {4, 0xe, 6}, /* 1110 */ + {4, 0xf, 7}, /* 1111 */ + {5, 0x12, 128}, /* 1001 0 */ + {5, 0x13, 8}, /* 1001 1 */ + {5, 0x14, 9}, /* 1010 0 */ + {5, 0x1b, 64}, /* 1101 1 */ + {5, 0x7, 10}, /* 0011 1 */ + {5, 0x8, 11}, /* 0100 0 */ + {6, 0x17, 192}, /* 0101 11 */ + {6, 0x18, 1664}, /* 0110 00 */ + {6, 0x2a, 16}, /* 1010 10 */ + {6, 0x2b, 17}, /* 1010 11 */ + {6, 0x3, 13}, /* 0000 11 */ + {6, 0x34, 14}, /* 1101 00 */ + {6, 0x35, 15}, /* 1101 01 */ + {6, 0x7, 1}, /* 0001 11 */ + {6, 0x8, 12}, /* 0010 00 */ + {7, 0x13, 26}, /* 0010 011 */ + {7, 0x17, 21}, /* 0010 111 */ + {7, 0x18, 28}, /* 0011 000 */ + {7, 0x24, 27}, /* 0100 100 */ + {7, 0x27, 18}, /* 0100 111 */ + {7, 0x28, 24}, /* 0101 000 */ + {7, 0x2b, 25}, /* 0101 011 */ + {7, 0x3, 22}, /* 0000 011 */ + {7, 0x37, 256}, /* 0110 111 */ + {7, 0x4, 23}, /* 0000 100 */ + {7, 0x8, 20}, /* 0001 000 */ + {7, 0xc, 19}, /* 0001 100 */ + {8, 0x12, 33}, /* 0001 0010 */ + {8, 0x13, 34}, /* 0001 0011 */ + {8, 0x14, 35}, /* 0001 0100 */ + {8, 0x15, 36}, /* 0001 0101 */ + {8, 0x16, 37}, /* 0001 0110 */ + {8, 0x17, 38}, /* 0001 0111 */ + {8, 0x1a, 31}, /* 0001 1010 */ + {8, 0x1b, 32}, /* 0001 1011 */ + {8, 0x2, 29}, /* 0000 0010 */ + {8, 0x24, 53}, /* 0010 0100 */ + {8, 0x25, 54}, /* 0010 0101 */ + {8, 0x28, 39}, /* 0010 1000 */ + {8, 0x29, 40}, /* 0010 1001 */ + {8, 0x2a, 41}, /* 0010 1010 */ + {8, 0x2b, 42}, /* 0010 1011 */ + {8, 0x2c, 43}, /* 0010 1100 */ + {8, 0x2d, 44}, /* 0010 1101 */ + {8, 0x3, 30}, /* 0000 0011 */ + {8, 0x32, 61}, /* 0011 0010 */ + {8, 0x33, 62}, /* 0011 0011 */ + {8, 0x34, 63}, /* 0011 0100 */ + {8, 0x35, 0}, /* 0011 0101 */ + {8, 0x36, 320}, /* 0011 0110 */ + {8, 0x37, 384}, /* 0011 0111 */ + {8, 0x4, 45}, /* 0000 0100 */ + {8, 0x4a, 59}, /* 0100 1010 */ + {8, 0x4b, 60}, /* 0100 1011 */ + {8, 0x5, 46}, /* 0000 0101 */ + {8, 0x52, 49}, /* 0101 0010 */ + {8, 0x53, 50}, /* 0101 0011 */ + {8, 0x54, 51}, /* 0101 0100 */ + {8, 0x55, 52}, /* 0101 0101 */ + {8, 0x58, 55}, /* 0101 1000 */ + {8, 0x59, 56}, /* 0101 1001 */ + {8, 0x5a, 57}, /* 0101 1010 */ + {8, 0x5b, 58}, /* 0101 1011 */ + {8, 0x64, 448}, /* 0110 0100 */ + {8, 0x65, 512}, /* 0110 0101 */ + {8, 0x67, 640}, /* 0110 0111 */ + {8, 0x68, 576}, /* 0110 1000 */ + {8, 0xa, 47}, /* 0000 1010 */ + {8, 0xb, 48}, /* 0000 1011 */ + {9, 0x98, 1472}, /* 0100 1100 0 */ + {9, 0x99, 1536}, /* 0100 1100 1 */ + {9, 0x9a, 1600}, /* 0100 1101 0 */ + {9, 0x9b, 1728}, /* 0100 1101 1 */ + {9, 0xcc, 704}, /* 0110 0110 0 */ + {9, 0xcd, 768}, /* 0110 0110 1 */ + {9, 0xd2, 832}, /* 0110 1001 0 */ + {9, 0xd3, 896}, /* 0110 1001 1 */ + {9, 0xd4, 960}, /* 0110 1010 0 */ + {9, 0xd5, 1024}, /* 0110 1010 1 */ + {9, 0xd6, 1088}, /* 0110 1011 0 */ + {9, 0xd7, 1152}, /* 0110 1011 1 */ + {9, 0xd8, 1216}, /* 0110 1100 0 */ + {9, 0xd9, 1280}, /* 0110 1100 1 */ + {9, 0xda, 1344}, /* 0110 1101 0 */ + {9, 0xdb, 1408}, /* 0110 1101 1 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x1, -1}, /* 0000 0000 0001 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560} /* 0000 0001 1111 */ +}; + +static Tab faxblack[Nfaxtab] = { + {2, 0x2, 3}, /* 10 */ + {2, 0x3, 2}, /* 11 */ + {3, 0x2, 1}, /* 010 */ + {3, 0x3, 4}, /* 011 */ + {4, 0x2, 6}, /* 0010 */ + {4, 0x3, 5}, /* 0011 */ + {5, 0x3, 7}, /* 0001 1 */ + {6, 0x4, 9}, /* 0001 00 */ + {6, 0x5, 8}, /* 0001 01 */ + {7, 0x4, 10}, /* 0000 100 */ + {7, 0x5, 11}, /* 0000 101 */ + {7, 0x7, 12}, /* 0000 111 */ + {8, 0x4, 13}, /* 0000 0100 */ + {8, 0x7, 14}, /* 0000 0111 */ + {9, 0x18, 15}, /* 0000 1100 0 */ + {10, 0x17, 16}, /* 0000 0101 11 */ + {10, 0x18, 17}, /* 0000 0110 00 */ + {10, 0x37, 0}, /* 0000 1101 11 */ + {10, 0x8, 18}, /* 0000 0010 00 */ + {10, 0xf, 64}, /* 0000 0011 11 */ + {11, 0x17, 24}, /* 0000 0010 111 */ + {11, 0x18, 25}, /* 0000 0011 000 */ + {11, 0x28, 23}, /* 0000 0101 000 */ + {11, 0x37, 22}, /* 0000 0110 111 */ + {11, 0x67, 19}, /* 0000 1100 111 */ + {11, 0x68, 20}, /* 0000 1101 000 */ + {11, 0x6c, 21}, /* 0000 1101 100 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x1, -1}, /* 0000 0000 0001 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560}, /* 0000 0001 1111 */ + {12, 0x24, 52}, /* 0000 0010 0100 */ + {12, 0x27, 55}, /* 0000 0010 0111 */ + {12, 0x28, 56}, /* 0000 0010 1000 */ + {12, 0x2b, 59}, /* 0000 0010 1011 */ + {12, 0x2c, 60}, /* 0000 0010 1100 */ + {12, 0x33, 320}, /* 0000 0011 0011 */ + {12, 0x34, 384}, /* 0000 0011 0100 */ + {12, 0x35, 448}, /* 0000 0011 0101 */ + {12, 0x37, 53}, /* 0000 0011 0111 */ + {12, 0x38, 54}, /* 0000 0011 1000 */ + {12, 0x52, 50}, /* 0000 0101 0010 */ + {12, 0x53, 51}, /* 0000 0101 0011 */ + {12, 0x54, 44}, /* 0000 0101 0100 */ + {12, 0x55, 45}, /* 0000 0101 0101 */ + {12, 0x56, 46}, /* 0000 0101 0110 */ + {12, 0x57, 47}, /* 0000 0101 0111 */ + {12, 0x58, 57}, /* 0000 0101 1000 */ + {12, 0x59, 58}, /* 0000 0101 1001 */ + {12, 0x5a, 61}, /* 0000 0101 1010 */ + {12, 0x5b, 256}, /* 0000 0101 1011 */ + {12, 0x64, 48}, /* 0000 0110 0100 */ + {12, 0x65, 49}, /* 0000 0110 0101 */ + {12, 0x66, 62}, /* 0000 0110 0110 */ + {12, 0x67, 63}, /* 0000 0110 0111 */ + {12, 0x68, 30}, /* 0000 0110 1000 */ + {12, 0x69, 31}, /* 0000 0110 1001 */ + {12, 0x6a, 32}, /* 0000 0110 1010 */ + {12, 0x6b, 33}, /* 0000 0110 1011 */ + {12, 0x6c, 40}, /* 0000 0110 1100 */ + {12, 0x6d, 41}, /* 0000 0110 1101 */ + {12, 0xc8, 128}, /* 0000 1100 1000 */ + {12, 0xc9, 192}, /* 0000 1100 1001 */ + {12, 0xca, 26}, /* 0000 1100 1010 */ + {12, 0xcb, 27}, /* 0000 1100 1011 */ + {12, 0xcc, 28}, /* 0000 1100 1100 */ + {12, 0xcd, 29}, /* 0000 1100 1101 */ + {12, 0xd2, 34}, /* 0000 1101 0010 */ + {12, 0xd3, 35}, /* 0000 1101 0011 */ + {12, 0xd4, 36}, /* 0000 1101 0100 */ + {12, 0xd5, 37}, /* 0000 1101 0101 */ + {12, 0xd6, 38}, /* 0000 1101 0110 */ + {12, 0xd7, 39}, /* 0000 1101 0111 */ + {12, 0xda, 42}, /* 0000 1101 1010 */ + {12, 0xdb, 43}, /* 0000 1101 1011 */ + {13, 0x4a, 640}, /* 0000 0010 0101 0 */ + {13, 0x4b, 704}, /* 0000 0010 0101 1 */ + {13, 0x4c, 768}, /* 0000 0010 0110 0 */ + {13, 0x4d, 832}, /* 0000 0010 0110 1 */ + {13, 0x52, 1280}, /* 0000 0010 1001 0 */ + {13, 0x53, 1344}, /* 0000 0010 1001 1 */ + {13, 0x54, 1408}, /* 0000 0010 1010 0 */ + {13, 0x55, 1472}, /* 0000 0010 1010 1 */ + {13, 0x5a, 1536}, /* 0000 0010 1101 0 */ + {13, 0x5b, 1600}, /* 0000 0010 1101 1 */ + {13, 0x64, 1664}, /* 0000 0011 0010 0 */ + {13, 0x65, 1728}, /* 0000 0011 0010 1 */ + {13, 0x6c, 512}, /* 0000 0011 0110 0 */ + {13, 0x6d, 576}, /* 0000 0011 0110 1 */ + {13, 0x72, 896}, /* 0000 0011 1001 0 */ + {13, 0x73, 960}, /* 0000 0011 1001 1 */ + {13, 0x74, 1024}, /* 0000 0011 1010 0 */ + {13, 0x75, 1088}, /* 0000 0011 1010 1 */ + {13, 0x76, 1152}, /* 0000 0011 1011 0 */ + {13, 0x77, 1216} /* 0000 0011 1011 1 */ +}; + +static Tab faxcodes[Nfaxcodes] = { + {1, 0x1, 0}, /* 1 */ + {3, 0x1, 0}, /* 001 */ + {3, 0x2, 0}, /* 010 */ + {3, 0x3, 0}, /* 011 */ + {4, 0x1, 0}, /* 0001 */ + {6, 0x2, 0}, /* 0000 10 */ + {6, 0x3, 0}, /* 0000 11 */ + {7, 0x2, 0}, /* 0000 010 */ + {7, 0x3, 0}, /* 0000 011 */ + {12, 0x1, -1} /* 0000 0000 0001 */ +}; + +static int typesizes[] = {0, 1, 0, 2, 4}; +static int vcodeval[] = {0, 0, 0, 1, 0, 0, 2, 3}; + +static uint32_t byte2le(unsigned char *); +static uint32_t byte4le(unsigned char *); +static uint32_t byte2be(unsigned char *); +static uint32_t byte4be(unsigned char *); +static void readdata(Tif *, uint32_t); +static void readnbytes(Tif *, uint32_t); +static uint32_t readbyte(Tif *); +static uint32_t readshort(Tif *); +static uint32_t readlong(Tif *); +static int gototif(Tif *, uint32_t); +static int readheader(Tif *); +static unsigned char *nocomp(Tif *); +static int getbit1(Fax *); +static int getbit2(Fax *); +static Tab *findtab(Fax *, int, int, Tab *, int); +static Tab *gettab(Fax *, int); +static Tab *geteol(Fax *); +static int faxfill(Fax *, unsigned char *, uint32_t, uint32_t *, uint32_t *, uint32_t, int); +static void fillbits(Fax *); +static int faxalloclines(Fax *); +static Tab *getfax1d(Fax *, unsigned char *, uint32_t, uint32_t *, uint32_t *, uint32_t); +static Tab *getfax2d(Fax *, unsigned char *, uint32_t, uint32_t *, uint32_t *, uint32_t); +static int faxstrip(Tif *, Fax *, unsigned char *, uint32_t, uint32_t, uint32_t *); +static unsigned char *fax(Tif *); +static void tabinit(Lzw *); +static Code *newcode(Lzw *, Code *); +static void listadd(Lzw *, Code *); +static Code *tabadd(Lzw *, Code *, Code *); +static int getcode(Lzw *); +static int wstr(unsigned char *, uint32_t, uint32_t *, Code *, long *); +static void predict1(Tif *, unsigned char *); +static void predict8(Tif *, unsigned char *); +static int lzwstrip(Lzw *, unsigned char *, uint32_t, uint32_t *, long); +static unsigned char *lzw(Tif *); +static unsigned char *packbits(Tif *); +static int faxdecode(Tif *, Rawimage *, unsigned char *); +static int greydecode(Tif *, Rawimage *, unsigned char *); +static int rgbdecode(Tif *, Rawimage *, unsigned char *); +static int paldecode(Tif *, Rawimage *, unsigned char *); +static int parsefield(Tif *, Fld *); +static int readfield(Tif *, Fld *); +static int checkfields(Tif *); +static int readstrips(Tif *); +static Rawimage *decode(Tif *); +static void freefields(Tif *); +static Rawimage *readslave(Tif *); + +static uint32_t +byte2le(unsigned char *buf) +{ + return (buf[1] << 8) | buf[0]; +} + +static uint32_t +byte4le(unsigned char *buf) +{ + return (byte2le(buf+2) << 16) | byte2le(buf); +} + +static uint32_t +byte2be(unsigned char *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static uint32_t +byte4be(unsigned char *buf) +{ + return (byte2be(buf) << 16) | byte2be(buf+2); +} + +static void +readdata(Tif *t, uint32_t offset) +{ + long n, m; + uint32_t size; + + if(offset < t->nbuf) + offset = t->nbuf; + m = offset + 4096 - t->nbuf; + size = (m + t->nbuf) * sizeof *t->buf; + if(t->buf == nil) { + if((t->buf = malloc(size)) == nil) + sysfatal("malloc: %r"); + } else { + if((t->buf = realloc(t->buf, size)) == nil) + sysfatal("realloc: %r"); + } + if((n = Bread(t->fd, t->buf+t->nbuf, m)) < 0) + sysfatal("Bread: %r"); + if(n != m) + t->eof = 1; + t->nbuf += n; +} + +static void +readnbytes(Tif *t, uint32_t n) +{ + if(n <= 0 || n > 4) + sysfatal("cannot read %lud bytes", n); + if(t->n+n > t->nbuf) { + if(t->eof) + sysfatal("reached end of file"); + readdata(t, 0); + } + memmove(t->tmp, t->buf+t->n, n); + t->n += n; +} + +static uint32_t +readbyte(Tif *t) +{ + readnbytes(t, 1); + return t->tmp[0]; +} + +static uint32_t +readshort(Tif *t) +{ + readnbytes(t, 2); + return (*t->byte2)(t->tmp); +} + +static uint32_t +readlong(Tif *t) +{ + readnbytes(t, 4); + return (*t->byte4)(t->tmp); +} + +static int +gototif(Tif *t, uint32_t n) +{ + if(n < 8) { + werrstr("offset pointing to header"); + return -1; + } + if(n > t->nbuf) + readdata(t, n); + t->n = n; + return 0; +} + +static int +readheader(Tif *t) +{ + uint n; + + t->end = readshort(t); + switch(t->end) { + case II: + break; + case MM: + t->byte2 = byte2be; + t->byte4 = byte4be; + break; + default: + werrstr("illegal byte order: %#.4x", t->end); + return -1; + } + if((n = readshort(t)) != TIF) { + werrstr("illegal tiff magic: %#.4x", n); + return -1; + } + t->off = readlong(t); + return gototif(t, t->off); +} + +static unsigned char * +nocomp(Tif *t) +{ + return t->data; +} + +static int +getbit1(Fax *f) +{ + int bit; + + if(f->n >= f->next) + return -1; + bit = (f->data[f->n] >> f->m) & 0x1; + f->m--; + if(f->m < 0) { + f->n++; + f->m = 7; + } + return bit; +} + +static int +getbit2(Fax *f) +{ + int bit; + + if(f->n >= f->next) + return -1; + bit = (f->data[f->n] >> f->m) & 0x1; + f->m++; + if(f->m >= 8) { + f->n++; + f->m = 0; + } + return bit; +} + +static Tab * +findtab(Fax *f, int code, int len, Tab *tab, int max) +{ + Tab *p; + + while(f->ntab < max) { + p = &tab[f->ntab]; + if(p->len > len) + break; + if(p->code == code) { + f->ntab = 0; + return p; + } + f->ntab++; + } + return nil; +} + +static Tab * +gettab(Fax *f, int mode) +{ + int i, n, maxlen, bit, code; + Tab *p, *tab; + + code = 0; + if(mode) { + n = Nfaxcodes; + tab = faxcodes; + } else { + n = Nfaxtab; + tab = f->tab[f->st]; + } + maxlen = tab[n-1].len; + for(i = 1; i <= maxlen; i++) { + if((bit = (*f->getbit)(f)) < 0) { + f->st = -1; + return nil; + } + code = (code << 1) | bit; + if((p = findtab(f, code, i, tab, n)) != nil) + return p; + } + werrstr("code not found"); + return nil; +} + +static Tab * +geteol(Fax *f) +{ + int i, bit; + Tab *p; + + if(f->eol == nil) { + if(f->eolfill) { + for(i = 0; i < 4; i++) { + if((*f->getbit)(f) < 0) { + f->st = -1; + return nil; + } + } + } + if((p = gettab(f, 0)) == nil || p->run >= 0) { + werrstr("first eol"); + return nil; + } + f->eol = p; + return p; + } + for(i = 0; (bit = (*f->getbit)(f)) == 0; i++) + ; + if(bit < 0) { + f->st = -1; + return nil; + } + if(i < 11) { + werrstr("eol"); + return nil; + } + return f->eol; +} + +static int +faxfill(Fax *f, unsigned char *data, uint32_t size, uint32_t *i, uint32_t *x, uint32_t dx, + int n) +{ + if((*x += n) > dx) { + werrstr("fax row overflow"); + return -1; + } + if((*i += n) > size) + return -1; + if(f->st != 0) + memset(data+*i-n, f->st, n); + return 0; +} + +static void +fillbits(Fax *f) +{ + if(f->getbit == getbit1) { + if(f->m != 7) { + f->n++; + f->m = 7; + } + } else { + if(f->m != 0) { + f->n++; + f->m = 0; + } + } +} + +static int +faxalloclines(Fax *f) +{ + f->nl *= 2; + f->l1 = realloc(f->l1, f->nl*sizeof *f->l1); + if(f->l1 == nil) + return -1; + f->l2 = realloc(f->l2, f->nl*sizeof *f->l2); + if(f->l2 == nil) { + free(f->l1); + return -1; + } + return 0; +} + +static Tab * +getfax1d(Fax *f, unsigned char *data, uint32_t size, uint32_t *i, uint32_t *x, + uint32_t dx) +{ + int j, n; + Tab *p; + + for(j = n = 0; *x < dx;) { + if((p = gettab(f, 0)) == nil) + return nil; + if((n = p->run) < 0) { + f->l1[j] = dx; + return f->eol; + } + if(faxfill(f, data, size, i, x, dx, n) < 0) + return nil; + if(n < 64) { + f->l1[j++] = *x; + f->st ^= 1; + } + if(j >= f->nl) + faxalloclines(f); + } + if(n >= 64) { + f->l1[j] = dx; + if((p = gettab(f, 0)) == nil) + return nil; + if((n = p->run) < 0) + return f->eol; + if(n != 0) { + werrstr("no terminating code"); + return nil; + } + } + return nil; +} + +static Tab * +getfax2d(Fax *f, unsigned char *data, uint32_t size, uint32_t *i, uint32_t *x, + uint32_t dx) +{ + int j, k, n, code, len, v; + long a0, a1, b1, b2; + Tab *p; + + a0 = -1; + for(j = 0; *x < dx;) { + for(k = 0;; k++) { + b1 = f->l1[k]; + if(b1 > a0 && f->st == k%2) + break; + if(b1 >= dx) + break; + } + if((b2 = b1) < dx) + b2 = f->l1[k+1]; + if((p = gettab(f, 1)) == nil) + return nil; + /* early eofb */ + if(p->run < 0) { + f->st = -1; + return nil; + } + if(j+1 >= f->nl) + faxalloclines(f); + len = p->len; + code = p->code; + if(code == 1 && len == 3) { + /* horizontal */ + for(k = 0; k < 2;) { + if((p = gettab(f, 0)) == nil) + return nil; + if((n = p->run) < 0) { + werrstr("2d eol"); + return nil; + } + if(faxfill(f, data, size, i, x, + dx, n) < 0) + return nil; + if(n < 64) { + f->l2[j++] = *x; + f->st ^= 1; + k++; + } + } + } else if(code == 1 && len == 4) { + /* pass */ + n = b2 - *x; + if(faxfill(f, data, size, i, x, dx, n) < 0) + return nil; + if(*x == dx) + f->l2[j++] = *x; + } else { + /* vertical */ + switch(code) { + case 1: + case 2: + v = -vcodeval[len]; + break; + case 3: + v = vcodeval[len]; + break; + default: + werrstr("mode"); + return nil; + } + a1 = b1 + v; + n = a1 - *x; + if(faxfill(f, data, size, i, x, dx, n) < 0) + return nil; + f->l2[j++] = *x; + f->st ^= 1; + } + a0 = *x; + } + memmove(f->l1, f->l2, j*sizeof *f->l1); + return nil; +} + +static int +faxstrip(Tif *t, Fax *f, unsigned char *data, uint32_t size, uint32_t rows, + uint32_t *i) +{ + int d1; + uint32_t x, y; + Tab *p; + + d1 = t->comp != T6enc; + p = nil; + for(x = y = 0; x < t->dx || y < rows;) { + f->st = 0; + if(t->comp == T4enc) { + if(p == nil && geteol(f) == nil) { + if(f->st >= 0) + return -1; + break; + } + if(y > 0) { + *i += t->dx - x; + if(*i > size) + break; + } + if(t->t4 & 1) { + d1 = (*f->getbit)(f); + if(d1 < 0) + break; + } + } + x = 0; + y++; + if(d1) { + p = getfax1d(f, data, size, i, + &x, t->dx); + } else { + p = getfax2d(f, data, size, i, + &x, t->dx); + } + if(t->comp == Huffman) + fillbits(f); + if(p == nil && x != t->dx) { + if(f->st >= 0) + return -1; + if(x > t->dx) + return -1; + break; + } + } + if(*i > size) { + werrstr("fax data overflow"); + return -1; + } + return 0; +} + +/* i've encountered t4 images that did not have rtcs */ +static unsigned char * +fax(Tif *t) +{ + int m; + uint32_t i, j, datasz, r, dy; + unsigned char *data; + Fax f; + + datasz = t->dx * t->dy * sizeof *data; + data = mallocz(datasz, 1); + f.nl = t->dx + 1; + f.l1 = mallocz(f.nl*sizeof *f.l1, 1); + f.l2 = mallocz(f.nl*sizeof *f.l2, 1); + if(data == nil || f.l1 == nil || f.l2 == nil) { + free(t->data); + if(data != nil) + free(data); + if(f.l1 != nil) + free(f.l1); + if(f.l2 != nil) + free(f.l2); + return nil; + } + if(t->fill == 1) { + f.getbit = getbit1; + m = 7; + } else { + f.getbit = getbit2; + m = 0; + } + f.tab[0] = faxwhite; + f.tab[1] = faxblack; + f.ntab = 0; + f.eol = nil; + if(t->comp == T4enc && t->t4 & (1<<1)) + f.eolfill = 1; + else + f.eolfill = 0; + f.data = t->data; + for(i = j = 0, dy = t->dy; i < t->nstrips; i++) { + f.l1[0] = t->dx; + f.n = t->strips[i]; + f.m = m; + if(i < t->nstrips-1) + f.next = t->strips[i+1]; + else + f.next = t->ndata; + r = dy < t->rows? dy: t->rows; + if(faxstrip(t, &f, data, datasz, r, &j) < 0) + break; + dy -= t->rows; + } + if(i < t->nstrips) { + free(data); + data = nil; + } + free(t->data); + free(f.l1); + free(f.l2); + return data; +} + +static void +tabinit(Lzw *l) +{ + l->ntab = Eoicode + 1; + l->len = 9; +} + +static Code * +newcode(Lzw *l, Code *p) +{ + Code *q; + + if(p == nil) + return nil; + if(l->first != nil) { + q = l->first; + if((l->first = l->first->next) == nil) + l->last = nil; + } else if((q = malloc(sizeof *q)) == nil) + return nil; + q->val = p->val; + q->next = nil; + return q; +} + +static void +listadd(Lzw *l, Code *p) +{ + if(p == nil) + return; + if(l->last != nil) + l->last->next = p; + else + l->first = l->last = p; + while(l->last->next != nil) + l->last = l->last->next; +} + +static Code * +tabadd(Lzw *l, Code *p, Code *q) +{ + Code *r, *s; + + r = s = &l->tab[l->ntab]; + switch(l->ntab) { + case 510: + case 1022: + case 2046: + l->len++; + break; + default: + break; + } + if(l->ntab++ >= Tabsz-3) { + werrstr("lzw table full"); + return nil; + } + s->val = p->val; + while((p = p->next) != nil) { + if(s->next != nil) + s->next->val = p->val; + else if((s->next = newcode(l, p)) == nil) + return nil; + s = s->next; + } + if(s->next != nil) { + s->next->val = q->val; + s = s->next; + if(s->next != nil) { + listadd(l, s->next); + s->next = nil; + } + } else if((s->next = newcode(l, q)) == nil) + return nil; + return r; +} + +static int +getcode(Lzw *l) +{ + int i, c, code; + + if(l->n >= l->next) { +eof: + werrstr("lzw eof"); + return -1; + } + code = 0; + for(i = l->len-1; i >= 0; i--) { + c = (l->data[l->n] >> l->m) & 0x1; + code |= c << i; + l->m--; + if(l->m < 0) { + l->m = 7; + if(++l->n >= l->next && i > 0) + goto eof; + } + } + return code; +} + +static int +wstr(unsigned char *data, uint32_t size, uint32_t *i, Code *p, long *striplen) +{ + for(; p != nil; p = p->next, ++*i, --*striplen) { + if(*i >= size || *striplen < 0) { + werrstr("lzw overflow"); + return -1; + } + data[*i] = p->val; + } + return 0; +} + +static void +predict1(Tif *t, unsigned char *data) +{ + int bpl, pix, b[8], d, m, n, j; + uint32_t x, y; + + d = t->depth; + bpl = bytesperline(Rect(0, 0, t->dx, t->dy), d); + m = (1 << d) - 1; + n = 8 / d; + for(y = 0; y < t->dy; y++) { + for(x = 0; x < bpl; x++, data++) { + pix = *data; + b[n-1] = (pix >> d*(n-1)) & m; + if(x > 0) + b[n-1] += *(data-1) & m; + for(j = n-2; j >= 0; j--) { + b[j] = (pix >> d*j) & m; + b[j] += b[j+1]; + } + for(j = pix = 0; j < n; j++) + pix |= (b[j] & m) << d*j; + *data = pix; + } + } +} + +static void +predict8(Tif *t, unsigned char *data) +{ + uint32_t j, s, x, y; + + s = t->samples; + for(y = 0; y < t->dy; y++) { + for(x = 1, data += s; x < t->dx; x++) { + for(j = 0; j < s; j++, data++) + *data += *(data-s); + } + } +} + +static int +lzwstrip(Lzw *l, unsigned char *data, uint32_t size, uint32_t *i, long striplen) +{ + int c, oc; + Code *p, *q; + + if((c = getcode(l)) != Clrcode) { + werrstr("clear code"); + return -1; + } + for(oc = -1; c != Eoicode;) { + if(c < 0) + return -1; + if(c == Clrcode) { + if(oc >= 0) + tabinit(l); + if((c = getcode(l)) == Eoicode) + break; + if(c < 0) + return -1; + if(c >= l->ntab) { + werrstr("table overflow"); + return -1; + } + if(wstr(data, size, i, &l->tab[c], + &striplen) < 0) + return -1; + } else if(c < l->ntab) { + p = &l->tab[c]; + if(wstr(data, size, i, p, + &striplen) < 0) + return -1; + q = &l->tab[oc]; + if(tabadd(l, q, p) == nil) + return -1; + } else if(c == l->ntab) { + q = &l->tab[oc]; + if((p = tabadd(l, q, q)) == nil) + return -1; + if(wstr(data, size, i, p, + &striplen) < 0) + return -1; + } else { + werrstr("table overflow"); + return -1; + } + if(striplen <= 0) + break; + oc = c; + c = getcode(l); + } + return 0; +} + +static unsigned char * +lzw(Tif *t) +{ + uint32_t i, j, size, r, dy; + long striplen; + unsigned char *data; + Lzw l; + Code *p, *q; + + size = ((t->dx*t->dy*t->depth + 7) / 8) * sizeof *data; + if((data = malloc(size)) == nil) { + free(t->data); + return nil; + } + for(i = 0; i < Tabsz; i++) { + l.tab[i].val = i; + l.tab[i].next = nil; + } + l.data = t->data; + l.first = l.last = nil; + for(i = j = 0, dy = t->dy; i < t->nstrips; i++) { + tabinit(&l); + l.n = t->strips[i]; + l.m = 7; + if(i < t->nstrips-1) + l.next = t->strips[i+1]; + else + l.next = t->ndata; + r = dy < t->rows? dy: t->rows; + striplen = (t->dx*r*t->depth + 7) / 8; + if(lzwstrip(&l, data, size, &j, striplen) < 0) + break; + dy -= t->rows; + } + if(i < t->nstrips) { + free(data); + data = nil; + } + for(i = 0; i < Tabsz; i++) { + for(p = l.tab[i].next; (q = p) != nil;) { + p = p->next; + free(q); + } + } + for(p = l.first; (q = p) != nil;) { + p = p->next; + free(q); + } + free(t->data); + if(data != nil && t->predictor == 2) { + if(t->depth < 8) + predict1(t, data); + else + predict8(t, data); + } + return data; +} + +static unsigned char * +packbits(Tif *t) +{ + char n; + uint32_t i, j, k, size; + unsigned char *data; + + size = ((t->dx*t->dy*t->depth + 7) / 8) * sizeof *data; + if((data = malloc(size)) == nil) { + free(t->data); + return nil; + } + for(i = 0, j = 0; i < t->ndata;) { + n = (char)t->data[i++]; + if(n >= 0) { + k = n + 1; + if((j += k) > size || (i += k) > t->ndata) + break; + memmove(data+j-k, t->data+i-k, k); + } else if(n > -128 && n < 0) { + k = j - n + 1; + if(k > size || i >= t->ndata) + break; + for(; j < k; j++) + data[j] = t->data[i]; + i++; + } + } + if(i < t->ndata) { + werrstr("packbits overflow"); + free(data); + data = nil; + } + free(t->data); + return data; +} + +static int +faxdecode(Tif *t, Rawimage *im, unsigned char *data) +{ + uint32_t n; + + for(n = 0; n < im->chanlen; n++) { + if(t->photo == Whitezero) + data[n] ^= 1; + im->chans[0][n] = data[n] * 0xff; + } + return 0; +} + +static int +greydecode(Tif *t, Rawimage *im, unsigned char *data) +{ + int pix, pmask, xmask; + uint32_t i, n, x, y; + + pmask = (1 << t->depth) - 1; + xmask = 7 >> log2[t->depth]; + for(y = 0, n = 0; y < t->dy; y++) { + i = y * bytesperline(im->r, t->depth); + for(x = 0; x < t->dx; x++, n++) { + if(n >= im->chanlen) { + werrstr("grey overflow"); + return -1; + } + pix = (data[i] >> t->depth*((xmask - + x) & xmask)) & pmask; + if(((x + 1) & xmask) == 0) + i++; + if(t->photo == Whitezero) + pix ^= pmask; + pix = (pix * 0xff) / pmask; + im->chans[0][n] = pix; + } + } + return 0; +} + +static int +rgbdecode(Tif *t, Rawimage *im, unsigned char *data) +{ + uint32_t i, n, x, y; + + for(y = 0, n = 0; y < t->dy; y++) { + for(x = 0; x < t->dx; x++, n += 3) { + if(n >= im->chanlen) { + werrstr("rgb overflow"); + return -1; + } + i = (y*t->dx + x) * 3; + im->chans[0][n] = data[i+2]; + im->chans[0][n+1] = data[i+1]; + im->chans[0][n+2] = data[i]; + } + } + return 0; +} + +static int +paldecode(Tif *t, Rawimage *im, unsigned char *data) +{ + int pix, pmask, xmask; + uint32_t i, n, x, y, *r, *g, *b, max; + + pmask = (1 << t->depth) - 1; + xmask = 7 >> log2[t->depth]; + for(i = 0, max = 1; i < t->ncolor; i++) { + if(t->color[i] > max) + max = t->color[i]; + } + for(i = 0; i < t->ncolor; i++) + t->color[i] = (t->color[i] * 0xff) / max; + r = t->color; + g = r + pmask + 1; + b = g + pmask + 1; + for(y = 0, n = 0; y < t->dy; y++) { + i = y * bytesperline(im->r, t->depth); + for(x = 0; x < t->dx; x++, n += 3) { + if(n >= im->chanlen) { + werrstr("palette overflow"); + return -1; + } + pix = (data[i] >> t->depth*((xmask - + x) & xmask)) & pmask; + if(((x + 1) & xmask) == 0) + i++; + im->chans[0][n] = b[pix]; + im->chans[0][n+1] = g[pix]; + im->chans[0][n+2] = r[pix]; + } + } + return 0; +} + +static int +parsefield(Tif *t, Fld *f) +{ + uint32_t v; + + v = f->val[0]; + switch(f->tag) { + case Width: + t->dx = v; + break; + case Length: + t->dy = v; + break; + case Bits: + t->depth = v; + if(f->cnt == 3) + t->depth += f->val[1] + f->val[2]; + break; + case Compression: + t->comp = v; + break; + case Photometric: + t->photo = v; + switch(t->photo) { + case Whitezero: + case Blackzero: + t->decode = greydecode; + break; + case Rgb: + t->decode = rgbdecode; + break; + case Palette: + t->decode = paldecode; + break; + default: + break; + } + break; + case Strips: + t->strips = f->val; + t->nstrips = f->cnt; + break; + case Fill: + t->fill = v; + break; + case Orientation: + t->orientation = v; + break; + case Samples: + t->samples = v; + break; + case Rows: + t->rows = v; + break; + case Counts: + t->counts = f->val; + t->ncounts = f->cnt; + break; + case Planar: + t->planar = v; + break; + case T4opts: + t->t4 = v; + break; + case T6opts: + t->t6 = v; + break; + case Predictor: + t->predictor = v; + break; + case Color: + t->color = f->val; + t->ncolor = f->cnt; + break; + default: + werrstr("shouldn't reach"); + return -1; + } + return 0; +} + +static int +readfield(Tif *t, Fld *f) +{ + int size; + uint32_t i, j, n, off; + uint32_t (*readval)(Tif *); + + f->tag = readshort(t); + f->typ = readshort(t); + f->cnt = readlong(t); + f->val = nil; + switch(f->tag) { + case Width: + case Length: + case Compression: + case Photometric: + case Fill: + case Orientation: + case Samples: + case Rows: + case Planar: + case T4opts: + case T6opts: + case Predictor: + if(f->cnt != 1) { + werrstr("field count"); + return -1; + } + break; + case Bits: + if(f->cnt != 1 && f->cnt != 3) { + werrstr("field count"); + return -1; + } + break; + case Strips: + case Counts: + case Color: + break; + default: + readlong(t); + return 0; + } + switch(f->typ) { + case Byte: + readval = readbyte; + break; + case Short: + readval = readshort; + break; + case Long: + readval = readlong; + break; + default: + werrstr("unsupported type\n"); + return -1; + } + if((f->val = malloc(f->cnt*sizeof *f->val)) == nil) + return -1; + size = typesizes[f->typ]; + if((n = size*f->cnt) <= 4) { + for(i = 0; i < f->cnt; i++) + f->val[i] = (*readval)(t); + f->off = 0x0; + f->nval = i; + for(j = n; j < 4; j += size) + (*readval)(t); + } else { + f->off = readlong(t); + off = t->n; + if(gototif(t, f->off) < 0) + return -1; + for(i = 0; i < f->cnt; i++) + f->val[i] = (*readval)(t); + f->nval = i; + if(gototif(t, off) < 0) + return -1; + } + return parsefield(t, f); +} + +static int +checkfields(Tif *t) +{ + uint32_t n, size; + + if(t->dx == 0) { + werrstr("image width"); + return -1; + } + if(t->dy == 0) { + werrstr("image length"); + return -1; + } + switch(t->depth) { + case 1: + case 4: + case 8: + case 24: + break; + default: + werrstr("bits per sample"); + return -1; + } + switch(t->comp) { + case Nocomp: + t->uncompress = nocomp; + break; + case Huffman: + case T4enc: + case T6enc: + t->uncompress = fax; + if(t->decode != nil) + t->decode = faxdecode; + if((t->comp == T4enc && t->t4 & (1<<1)) || + (t->comp == T6enc && + t->t6 & (1<<1))) { + werrstr("uncompressed mode"); + return -1; + } + break; + case Lzwenc: + t->uncompress = lzw; + break; + case Packbits: + t->uncompress = packbits; + break; + default: + werrstr("compression"); + return -1; + } + if(t->decode == nil) { + werrstr("photometric interpretation"); + return -1; + } + if(t->depth > 1 && (t->comp == Huffman || + t->comp == T4enc || t->comp == T6enc)) { + werrstr("compression"); + return -1; + } + if(t->fill != 1 && t->fill != 2) { + werrstr("fill order"); + return -1; + } + if(t->fill == 2 && t->depth != 1) { + werrstr("depth should be 1 with fill order 2"); + return -1; + } + if(t->orientation != 1) { + werrstr("orientation"); + return -1; + } + if(t->rows == 0) { + werrstr("rows per strip"); + return -1; + } + n = (t->dy + t->rows - 1) / t->rows; + if(t->strips == nil || t->nstrips != n) { + werrstr("strip offsets"); + return -1; + } + if(t->samples == 1 && t->photo == Rgb) { + werrstr("not enough samples per pixel"); + return -1; + } + if(t->samples == 3 && t->photo != Rgb) { + werrstr("too many samples per pixel"); + return -1; + } + if(t->samples != 1 && t->samples != 3) { + werrstr("samples per pixel"); + return -1; + } + /* + * strip byte counts should not be missing, + * but we can guess correctly in this case + */ + size = sizeof *t->counts; + if(t->counts == nil && t->comp == Nocomp && + t->nstrips == 1 && + (t->counts = malloc(size)) != nil) { + t->counts[0] = (t->dx*t->dy*t->depth + 7) / 8; + t->ncounts = t->nstrips; + } + if(t->counts == nil || t->ncounts != t->nstrips) { + werrstr("strip byte counts"); + return -1; + } + if(t->planar != 1) { + werrstr("planar configuration"); + return -1; + } + if(t->photo == Palette && (t->color == nil || + t->ncolor != 3*(1<depth))) { + werrstr("color map"); + return -1; + } + return 0; +} + +static int +readstrips(Tif *t) +{ + int i, j, n; + uint32_t off; + + t->data = nil; + t->ndata = 0; + for(i = 0; i < t->nstrips; i++) + t->ndata += t->counts[i]; + if(t->ndata == 0) { + werrstr("no image data"); + return -1; + } + if((t->data = malloc(t->ndata*sizeof *t->data)) == nil) + return -1; + off = t->n; + for(i = n = 0; i < t->nstrips; i++) { + if(gototif(t, t->strips[i]) < 0) + return -1; + /* + * we store each strip's offset in t->data + * in order to skip the final rtc or eofb + * during fax decoding. t->strips is used + * to save on memory allocation. these + * offsets are also used in lzw as a + * preventive measure. + */ + t->strips[i] = n; + for(j = 0; j < t->counts[i]; j++, n++) + t->data[n] = readbyte(t); + } + return gototif(t, off); +} + +static Rawimage * +decode(Tif *t) +{ + uint32_t size; + unsigned char *data; + Rawimage *im; + + if((im = malloc(sizeof *im)) == nil) + return nil; + im->r = Rect(0, 0, t->dx, t->dy); + im->cmap = nil; + im->cmaplen = 0; + im->chanlen = t->dx * t->dy; + if(t->photo == Rgb || t->photo == Palette) { + im->chandesc = CRGB24; + im->chanlen *= 3; + } else + im->chandesc = CY; + im->nchans = 1; + size = im->chanlen * sizeof *im->chans[0]; + if((im->chans[0] = malloc(size)) == nil) + return nil; + /* unused members */ + im->fields = 0; + im->gifflags = 0; + im->gifdelay = 0; + im->giftrindex = 0; + im->gifloopcount = 1; + if((data = (*t->uncompress)(t)) == nil) + return nil; + if((*t->decode)(t, im, data) < 0) { + free(im->chans[0]); + free(im); + im = nil; + } + free(data); + return im; +} + +static void +freefields(Tif *t) +{ + uint i; + + for(i = 0; i < t->nfld; i++) { + if(t->fld[i].val != nil) + free(t->fld[i].val); + } + free(t->fld); +} + +static Rawimage * +readslave(Tif *t) +{ + uint i, j; + Rawimage *r; + + if(readheader(t) < 0) + return nil; + if((t->nfld = readshort(t)) <= 0) { + werrstr("illegal field number: %#.4x", t->nfld); + return nil; + } + if((t->fld = malloc(t->nfld*sizeof *t->fld)) == nil) + return nil; + for(i = 0; i < t->nfld; i++) { + if(readfield(t, &t->fld[i]) < 0) { + if(t->fld[i].val != nil) + free(t->fld[i].val); + break; + } + } + if(i < t->nfld) { + for(j = 0; j < i; j++) { + if(t->fld[j].val != nil) + free(t->fld[j].val); + } + free(t->fld); + return nil; + } + readlong(t); + if(checkfields(t) < 0) { + freefields(t); + return nil; + } + if(readstrips(t) < 0) { + freefields(t); + if(t->data != nil) + free(t->data); + return nil; + } + free(t->buf); + r = decode(t); + freefields(t); + return r; +} + +Rawimage ** +Breadtif(Biobuf *b, int colorspace) +{ + Rawimage **array, *r; + Tif *t; + + if(colorspace != CRGB24) { + werrstr("unknown color space: %d", + colorspace); + return nil; + } + if((t = malloc(sizeof *t)) == nil) + return nil; + if((array = malloc(2*sizeof *array)) == nil) + return nil; + t->fd = b; + t->buf = nil; + t->nbuf = t->eof = t->n = 0; + /* order doesn't matter for the first two bytes */ + t->byte2 = byte2le; + t->byte4 = byte4le; + /* defaults */ + t->dx = 0; + t->dy = 0; + t->depth = 1; + t->comp = 1; + t->uncompress = nil; + t->photo = 0; + t->decode = nil; + t->fill = 1; + t->orientation = 1; + t->strips = nil; + t->nstrips = 0; + t->samples = 1; + t->rows = 0xffffffff; /* entire image is one strip */ + t->counts = nil; + t->ncounts = 0; + t->planar = 1; + t->t4 = 0; + t->t6 = 0; + t->predictor = 1; + t->color = nil; + t->ncolor = 0; + r = readslave(t); + free(t); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage ** +readtif(int fd, int colorspace) +{ + Rawimage **a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadtif(&b, colorspace); + Bterm(&b); + return a; +} diff --git a/sys/src/cmd/pict/readv210.c b/sys/src/cmd/pict/readv210.c new file mode 100644 index 0000000..6df21ce --- /dev/null +++ b/sys/src/cmd/pict/readv210.c @@ -0,0 +1,203 @@ +/* + * readV210.c - read single uncompressed Quicktime YUV image. + * http://developer.apple.com/quicktime/icefloe/dispatch019.html#v210 + * Steve Simon, 2009 + */ +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum { + Pixels = 720, + R601pal = 576, + R601ntsc = 486, + Shift = 13 +}; + +static int +looksize(char *file, long size, int *pixels, int *lines, int *chunk) +{ + Biobuf *bp; + unsigned long l, p, c; + char *s, *a[12]; + + /* + * This may not always work, there could be an alias between file + * sizes of different standards stored in 8bits and 10 bits. + */ + if((bp = Bopen(file, OREAD)) == nil) + return -1; + while((s = Brdstr(bp, '\n', 1)) != nil){ + if(tokenize(s, a, nelem(a)) < 3) + continue; + if(a[0][0] == '#') + continue; + p = atoll(a[3]); + l = atoll(a[5]); + l += atoll(a[7]); + c = 128 * ceil(p/48); + if(l*c == size){ + *pixels = p; + *lines = l; + *chunk = c; + break; + } + } + Bterm(bp); + if(s == nil) + return -1; + return 0; +} + +static int +clip(int x) +{ + x >>= Shift + 2; /* +2 as we assume all input images are 10 bit */ + if(x > 255) + return 0xff; + if(x <= 0) + return 0; + return x; +} + +Rawimage** +BreadV210(Biobuf *bp, int colourspace) +{ + Dir *d; + unsigned long sz; + Rawimage *a, **array; + unsigned short *mux, *end, *frm, *wr; + unsigned char *buf, *r, *g, *b; + uint i, t; + int y1, y2, cb, cr, c, l, rd; + int chunk, lines, pixels; + int F1, F2, F3, F4; + + buf = nil; + if(colourspace != CYCbCr){ + werrstr("BreadV210: unknown colour space %d", colourspace); + return nil; + } + + if((d = dirfstat(Bfildes(bp))) != nil){ + sz = d->length; + free(d); + } + else { + fprint(2, "cannot stat input, assuming pixelsx576x10bit\n"); + sz = Pixels * R601pal * 2L + (pixels * R601pal / 2L); + } + + if(looksize("/lib/video.specs", sz, &pixels, &lines, &chunk) == -1){ + werrstr("file spec not in /lib/video.specs\n"); + 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; + + a->nchans = 3; + a->chandesc = CRGB; + a->chanlen = pixels * lines; + a->r = Rect(0, 0, pixels, lines); + + if((frm = malloc(pixels*2*lines*sizeof(unsigned short))) == nil) + goto Error; + + for(c = 0; c < 3; c++) + if((a->chans[c] = malloc(pixels*lines)) == nil) + goto Error; + + if((buf = malloc(chunk)) == nil) + goto Error; + + for(l = 0; l < lines; l++){ + if(Bread(bp, buf, chunk) == -1) + goto Error; + + rd = 0; + wr = &frm[l*pixels*2]; + end = &frm[(l+1)*pixels*2]; + while(wr < end){ + t = 0; + for(i = 0; i < 4; i++) + t += buf[rd+i] << 8*i; + *wr++ = t & 0x3ff; + *wr++ = t>>10 & 0x3ff; + *wr++ = t>>20 & 0x3ff; + rd += 4; + } + } + + mux = frm; + end = frm + pixels * lines * 2; + r = a->chans[0]; + g = a->chans[1]; + b = a->chans[2]; + + if(pixels == Pixels && lines != R601pal){ // 625 + F1 = floor(1.402 * (1 << Shift)); + F2 = floor(0.34414 * (1 << Shift)); + F3 = floor(0.71414 * (1 << Shift)); + F4 = floor(1.772 * (1 << Shift)); + } + else{ // 525 and HD + F1 = floor(1.5748 * (1 << Shift)); + F2 = floor(0.1874 * (1 << Shift)); + F3 = floor(0.4681 * (1 << Shift)); + F4 = floor(1.8560 * (1 << Shift)); + } + + /* + * Fixme: fixed colourspace conversion at present + */ + while(mux < end){ + + cb = *mux++ - 512; + y1 = (int)*mux++ << Shift; + cr = *mux++ - 512; + y2 = (int)*mux++ << Shift; + + *r++ = clip(y1 + F1*cr); + *g++ = clip(y1 - F2*cb - F3*cr); + *b++ = clip((y1 + F4*cb)); + + *r++ = clip(y2 + F1*cr); + *g++ = clip(y2 - F2*cb - F3*cr); + *b++ = clip((y2 + F4*cb)); + } + free(frm); + free(buf); + return array; + +Error: + for(c = 0; c < 3; c++) + free(a->chans[c]); + free(a->cmap); + free(array[0]); + free(array); + free(frm); + free(buf); + return nil; +} + +Rawimage** +readV210(int fd, int colorspace) +{ + Rawimage * *a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = BreadV210(&b, colorspace); + Bterm(&b); + return a; +} diff --git a/sys/src/cmd/pict/readyuv.c b/sys/src/cmd/pict/readyuv.c new file mode 100644 index 0000000..7d08515 --- /dev/null +++ b/sys/src/cmd/pict/readyuv.c @@ -0,0 +1,215 @@ +/* readyuv.c - read an Abekas A66 style image file. Steve Simon, 2003 */ +#include +#include +#include +#include +#include +#include "imagefile.h" + + +enum { + Pixels = 720, + R601pal = 576, + R601ntsc = 486, + Shift = 13 +}; + + +static int lsbtab[] = { 6, 4, 2, 0}; + +static int +looksize(char *file, long size, int *pixels, int *lines, int *bits) +{ + Biobuf *bp; + unsigned long l, p; + char *s, *a[12]; + + /* + * This may not always work, there could be an alias between file + * sizes of different standards stored in 8bits and 10 bits. + */ + if ((bp = Bopen(file, OREAD)) == nil) + return -1; + while((s = Brdstr(bp, '\n', 1)) != nil){ + if (tokenize(s, a, nelem(a)) < 3) + continue; + if (a[0][0] == '#') + continue; + p = atoll(a[3]); + l = atoll(a[5]); + l += atoll(a[7]); + if (l*p*2 == size){ + *pixels = p; + *lines = l; + *bits = 8; + break; + } + if ((l*p*20)/8 == size){ + *pixels = p; + *lines = l; + *bits = 10; + break; + } + } + Bterm(bp); + if (s == nil) + return -1; + return 0; +} + + +static int +clip(int x) +{ + x >>= (Shift+2); // +2 as we assume all input images are 10 bit + + if (x > 255) + return 0xff; + if (x <= 0) + return 0; + return x; +} + +Rawimage** +Breadyuv(Biobuf *bp, int colourspace) +{ + Dir *d; + unsigned long sz; + Rawimage *a, **array; + unsigned short * mux, *end, *frm; + unsigned char *buf, *r, *g, *b; + int y1, y2, cb, cr, c, l, w, base; + int bits, lines, pixels; + int F1, F2, F3, F4; + + if ((d = dirfstat(Bfildes(bp))) != nil){ + sz = d->length; + free(d); + } + else{ + fprint(2, "cannot stat input, assuming pixelsx576x10bit\n"); + sz = Pixels * R601pal * 2L + (Pixels * R601pal / 2L); + } + + if (looksize("/lib/video.specs", sz, &pixels, &lines, &bits) == -1){ + werrstr("file size not listed in /lib/video.specs"); + return nil; + } + + buf = nil; + if (colourspace != CYCbCr) { + 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; + + a->nchans = 3; + a->chandesc = CRGB; + a->chanlen = pixels * lines; + a->r = Rect(0, 0, pixels, lines); + + if ((frm = malloc(pixels*2*lines*sizeof(unsigned short))) == nil) + goto Error; + + for (c = 0; c < 3; c++) + if ((a->chans[c] = malloc(pixels*lines)) == nil) + goto Error; + + if ((buf = malloc(pixels*2)) == nil) + goto Error; + + for (l = 0; l < lines; l++) { + if (Bread(bp, buf, pixels *2) == -1) + goto Error; + + base = l*pixels*2; + for (w = 0; w < pixels *2; w++) + frm[base + w] = ((unsigned short)buf[w]) << 2; + } + + + if (bits == 10) + for (l = 0; l < lines; l++) { + if (Bread(bp, buf, pixels / 2) == -1) + goto Error; + + + base = l * pixels * 2; + for (w = 0; w < pixels * 2; w++) + frm[base + w] |= (buf[w / 4] >> lsbtab[w % 4]) & 3; + } + + mux = frm; + end = frm + pixels * lines * 2; + r = a->chans[0]; + g = a->chans[1]; + b = a->chans[2]; + + if(pixels == Pixels && lines != R601pal){ // 625 + F1 = floor(1.402 * (1 << Shift)); + F2 = floor(0.34414 * (1 << Shift)); + F3 = floor(0.71414 * (1 << Shift)); + F4 = floor(1.772 * (1 << Shift)); + } + else{ // 525 + F1 = floor(1.5748 * (1 << Shift)); + F2 = floor(0.1874 * (1 << Shift)); + F3 = floor(0.4681 * (1 << Shift)); + F4 = floor(1.8560 * (1 << Shift)); + } + + /* + * Fixme: fixed colourspace conversion at present + */ + while (mux < end) { + + cb = *mux++ - 512; + y1 = (int)*mux++ << Shift; + cr = *mux++ - 512; + y2 = (int)*mux++ << Shift; + + *r++ = clip(y1 + F1*cr); + *g++ = clip(y1 - F2*cb - F3*cr); + *b++ = clip((y1 + F4*cb)); + + *r++ = clip(y2 + F1*cr); + *g++ = clip(y2 - F2*cb - F3*cr); + *b++ = clip((y2 + F4*cb)); + } + free(frm); + free(buf); + return array; + +Error: + for (c = 0; c < 3; c++) + free(a->chans[c]); + free(a->cmap); + free(array[0]); + free(array); + free(frm); + free(buf); + 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/sys/src/cmd/pict/rgbrgbv.c b/sys/src/cmd/pict/rgbrgbv.c new file mode 100644 index 0000000..ed4de79 --- /dev/null +++ b/sys/src/cmd/pict/rgbrgbv.c @@ -0,0 +1,70 @@ +#include +#include +#include + +/* + * This version of closest() is now (feb 20, 2001) installed as rgb2cmap in libdraw + */ + +int +closest(int cr, int cg, int cb) +{ + int i, r, g, b, sq; + uint32_t 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; + unsigned char close[16*16*16]; + + print("/* This file has been generated by rgbrgbv */\n"); + /* rgbmap */ + print("unsigned int 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("unsigned char 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/sys/src/cmd/pict/rgbv.h b/sys/src/cmd/pict/rgbv.h new file mode 100644 index 0000000..195d68e --- /dev/null +++ b/sys/src/cmd/pict/rgbv.h @@ -0,0 +1,295 @@ +/* This file has been generated by rgbrgbv */ +unsigned int rgbmap[256] = { + 0x000000, 0x000044, 0x000088, 0x0000CC, 0x004400, 0x004444, 0x004488, 0x0044CC, + 0x008800, 0x008844, 0x008888, 0x0088CC, 0x00CC00, 0x00CC44, 0x00CC88, 0x00CCCC, + 0x00DDDD, 0x111111, 0x000055, 0x000099, 0x0000DD, 0x005500, 0x005555, 0x004C99, + 0x0049DD, 0x009900, 0x00994C, 0x009999, 0x0093DD, 0x00DD00, 0x00DD49, 0x00DD93, + 0x00EE9E, 0x00EEEE, 0x222222, 0x000066, 0x0000AA, 0x0000EE, 0x006600, 0x006666, + 0x0055AA, 0x004FEE, 0x00AA00, 0x00AA55, 0x00AAAA, 0x009EEE, 0x00EE00, 0x00EE4F, + 0x00FF55, 0x00FFAA, 0x00FFFF, 0x333333, 0x000077, 0x0000BB, 0x0000FF, 0x007700, + 0x007777, 0x005DBB, 0x0055FF, 0x00BB00, 0x00BB5D, 0x00BBBB, 0x00AAFF, 0x00FF00, + 0x440044, 0x440088, 0x4400CC, 0x444400, 0x444444, 0x444488, 0x4444CC, 0x448800, + 0x448844, 0x448888, 0x4488CC, 0x44CC00, 0x44CC44, 0x44CC88, 0x44CCCC, 0x440000, + 0x550000, 0x550055, 0x4C0099, 0x4900DD, 0x555500, 0x555555, 0x4C4C99, 0x4949DD, + 0x4C9900, 0x4C994C, 0x4C9999, 0x4993DD, 0x49DD00, 0x49DD49, 0x49DD93, 0x49DDDD, + 0x4FEEEE, 0x660000, 0x660066, 0x5500AA, 0x4F00EE, 0x666600, 0x666666, 0x5555AA, + 0x4F4FEE, 0x55AA00, 0x55AA55, 0x55AAAA, 0x4F9EEE, 0x4FEE00, 0x4FEE4F, 0x4FEE9E, + 0x55FFAA, 0x55FFFF, 0x770000, 0x770077, 0x5D00BB, 0x5500FF, 0x777700, 0x777777, + 0x5D5DBB, 0x5555FF, 0x5DBB00, 0x5DBB5D, 0x5DBBBB, 0x55AAFF, 0x55FF00, 0x55FF55, + 0x880088, 0x8800CC, 0x884400, 0x884444, 0x884488, 0x8844CC, 0x888800, 0x888844, + 0x888888, 0x8888CC, 0x88CC00, 0x88CC44, 0x88CC88, 0x88CCCC, 0x880000, 0x880044, + 0x99004C, 0x990099, 0x9300DD, 0x994C00, 0x994C4C, 0x994C99, 0x9349DD, 0x999900, + 0x99994C, 0x999999, 0x9393DD, 0x93DD00, 0x93DD49, 0x93DD93, 0x93DDDD, 0x990000, + 0xAA0000, 0xAA0055, 0xAA00AA, 0x9E00EE, 0xAA5500, 0xAA5555, 0xAA55AA, 0x9E4FEE, + 0xAAAA00, 0xAAAA55, 0xAAAAAA, 0x9E9EEE, 0x9EEE00, 0x9EEE4F, 0x9EEE9E, 0x9EEEEE, + 0xAAFFFF, 0xBB0000, 0xBB005D, 0xBB00BB, 0xAA00FF, 0xBB5D00, 0xBB5D5D, 0xBB5DBB, + 0xAA55FF, 0xBBBB00, 0xBBBB5D, 0xBBBBBB, 0xAAAAFF, 0xAAFF00, 0xAAFF55, 0xAAFFAA, + 0xCC00CC, 0xCC4400, 0xCC4444, 0xCC4488, 0xCC44CC, 0xCC8800, 0xCC8844, 0xCC8888, + 0xCC88CC, 0xCCCC00, 0xCCCC44, 0xCCCC88, 0xCCCCCC, 0xCC0000, 0xCC0044, 0xCC0088, + 0xDD0093, 0xDD00DD, 0xDD4900, 0xDD4949, 0xDD4993, 0xDD49DD, 0xDD9300, 0xDD9349, + 0xDD9393, 0xDD93DD, 0xDDDD00, 0xDDDD49, 0xDDDD93, 0xDDDDDD, 0xDD0000, 0xDD0049, + 0xEE004F, 0xEE009E, 0xEE00EE, 0xEE4F00, 0xEE4F4F, 0xEE4F9E, 0xEE4FEE, 0xEE9E00, + 0xEE9E4F, 0xEE9E9E, 0xEE9EEE, 0xEEEE00, 0xEEEE4F, 0xEEEE9E, 0xEEEEEE, 0xEE0000, + 0xFF0000, 0xFF0055, 0xFF00AA, 0xFF00FF, 0xFF5500, 0xFF5555, 0xFF55AA, 0xFF55FF, + 0xFFAA00, 0xFFAA55, 0xFFAAAA, 0xFFAAFF, 0xFFFF00, 0xFFFF55, 0xFFFFAA, 0xFFFFFF, +}; + +unsigned char closestrgb[16*16*16] = { + 0,17,17,1,1,18,35,52,2,19,36,53,3,20,37,54, + 17,17,17,1,1,18,35,52,2,19,36,53,3,20,37,54, + 17,17,34,5,5,5,35,6,6,6,23,7,7,7,24,41, + 4,4,5,5,5,5,6,6,6,6,23,7,7,7,24,41, + 4,4,5,5,5,22,22,6,6,23,40,40,7,24,41,41, + 21,21,5,5,22,22,39,39,6,23,40,57,57,24,41,58, + 38,38,38,9,22,39,39,56,56,40,40,57,57,57,41,58, + 55,55,9,9,9,39,56,56,10,10,57,11,11,11,28,58, + 8,8,9,9,9,9,56,10,10,10,27,11,11,11,28,45, + 25,25,9,9,26,26,43,10,10,27,27,44,11,28,45,45, + 42,42,26,26,43,43,43,60,27,27,44,44,61,28,45,62, + 59,59,13,13,43,60,60,14,14,44,44,61,61,15,45,62, + 12,12,13,13,13,60,60,14,14,14,61,61,15,15,16,62, + 29,29,13,13,30,30,60,14,14,31,31,15,15,16,16,33, + 46,46,30,30,47,47,47,31,31,32,32,32,16,16,33,33, + 63,63,47,47,47,48,48,48,32,32,49,49,49,33,33,50, + 17,17,17,1,1,18,35,52,2,19,36,53,3,20,37,54, + 17,17,34,34,1,18,35,52,2,19,36,53,3,20,37,54, + 17,34,34,34,51,5,35,6,6,6,23,7,7,7,24,41, + 4,34,34,51,5,5,6,6,6,6,23,7,7,7,24,41, + 4,4,51,5,5,22,22,6,6,23,40,40,7,24,41,41, + 21,21,5,5,22,22,39,39,6,23,40,57,57,24,41,58, + 38,38,38,9,22,39,39,56,56,40,40,57,57,57,41,58, + 55,55,9,9,9,39,56,56,10,10,57,11,11,11,28,58, + 8,8,9,9,9,9,56,10,10,10,27,11,11,11,28,45, + 25,25,9,9,26,26,43,10,10,27,27,44,11,28,45,45, + 42,42,26,26,43,43,43,60,27,27,44,44,61,28,45,62, + 59,59,13,13,43,60,60,14,14,44,44,61,61,15,45,62, + 12,12,13,13,13,60,60,14,14,14,61,61,15,15,16,62, + 29,29,13,13,30,30,60,14,14,31,31,15,15,16,16,33, + 46,46,30,30,47,47,47,31,31,32,32,32,16,16,33,33, + 63,63,47,47,47,48,48,48,32,32,49,49,49,33,33,50, + 17,17,34,64,64,64,35,65,65,65,82,66,66,66,83,100, + 17,34,34,34,51,64,35,65,65,65,82,66,66,66,83,100, + 34,34,34,51,51,51,69,69,69,69,69,70,70,70,87,87, + 67,34,51,51,51,68,69,69,69,69,86,70,70,70,87,87, + 67,51,51,51,68,68,69,69,69,69,86,70,70,70,87,104, + 67,67,51,68,68,68,39,69,69,69,40,70,70,70,87,58, + 38,38,72,72,72,39,39,56,73,73,40,57,74,74,87,58, + 71,71,72,72,72,72,56,73,73,73,73,74,74,74,74,91, + 71,71,72,72,72,72,73,73,73,73,73,74,74,74,91,91, + 71,71,72,72,72,72,73,73,73,73,90,74,74,91,91,108, + 88,88,72,89,89,43,43,73,73,90,44,44,74,91,91,62, + 75,75,76,76,76,76,60,77,77,77,44,78,78,78,78,62, + 75,75,76,76,76,76,77,77,77,77,77,78,78,78,78,95, + 75,75,76,76,76,76,77,77,77,94,94,78,78,78,95,95, + 92,92,93,93,93,93,93,77,94,94,94,78,78,95,95,96, + 109,109,93,93,110,48,48,94,94,111,49,49,95,95,96,50, + 79,79,64,64,64,64,65,65,65,65,82,66,66,66,83,100, + 79,34,34,51,64,64,65,65,65,65,82,66,66,66,83,100, + 67,34,51,51,51,68,69,69,69,69,86,70,70,70,87,87, + 67,51,51,51,68,68,69,69,69,69,86,70,70,70,87,104, + 67,67,51,68,68,68,69,69,69,69,86,70,70,70,87,104, + 67,67,68,68,68,85,85,69,69,86,86,70,70,87,87,104, + 71,71,72,72,72,85,85,73,73,86,103,103,74,87,104,121, + 71,71,72,72,72,72,73,73,73,73,73,74,74,74,91,91, + 71,71,72,72,72,72,73,73,73,73,90,74,74,74,91,108, + 71,71,72,72,72,89,89,73,73,90,90,74,74,91,91,108, + 88,88,89,89,89,89,106,73,90,90,107,107,91,91,108,108, + 75,75,76,76,76,76,106,77,77,77,107,78,78,78,108,125, + 75,75,76,76,76,76,77,77,77,77,94,78,78,78,95,95, + 75,75,76,76,76,93,93,77,77,94,94,78,78,95,95,95, + 92,92,93,93,93,93,110,94,94,94,111,111,95,95,95,96, + 109,109,93,110,110,110,127,94,111,111,111,112,95,95,96,96, + 79,79,64,64,64,81,81,65,65,82,99,99,66,83,100,100, + 79,79,51,64,64,81,81,65,65,82,99,99,66,83,100,100, + 67,51,51,51,68,68,69,69,69,69,86,70,70,70,87,104, + 67,67,51,68,68,68,69,69,69,69,86,70,70,70,87,104, + 67,67,68,68,68,85,85,69,69,86,86,70,70,87,87,104, + 84,84,68,68,85,85,85,69,69,86,103,103,70,87,104,121, + 84,84,72,72,85,85,85,102,73,86,103,120,120,87,104,121, + 71,71,72,72,72,72,102,73,73,73,73,74,74,74,91,121, + 71,71,72,72,72,72,73,73,73,73,90,74,74,91,91,108, + 88,88,72,72,89,89,89,73,73,90,90,74,74,91,108,108, + 105,105,89,89,89,106,106,73,90,90,107,107,91,91,108,125, + 105,105,76,76,76,106,123,77,77,77,107,124,78,78,108,125, + 75,75,76,76,76,76,123,77,77,77,94,78,78,78,95,125, + 92,92,76,76,93,93,93,77,94,94,94,78,78,95,95,96, + 109,109,93,93,93,110,110,94,94,111,111,111,95,95,96,96, + 109,109,110,110,110,127,127,127,111,111,112,112,112,96,96,96, + 80,80,64,64,81,81,98,98,65,82,99,116,116,83,100,117, + 80,80,64,64,81,81,98,98,65,82,99,116,116,83,100,117, + 67,67,51,68,68,68,98,69,69,69,99,70,70,70,87,117, + 67,67,68,68,68,85,85,69,69,86,86,70,70,87,87,104, + 84,84,68,68,85,85,85,69,69,86,103,103,70,87,104,121, + 84,84,68,85,85,85,85,102,86,86,103,120,120,87,104,121, + 101,101,101,85,85,85,102,102,86,103,103,120,120,120,104,121, + 101,101,72,72,72,102,102,102,73,73,120,120,74,74,91,121, + 71,71,72,72,72,89,89,73,73,90,90,74,74,91,91,108, + 88,88,72,89,89,89,106,73,90,90,107,107,74,91,108,108, + 105,105,105,89,106,106,106,123,90,107,107,107,124,108,108,125, + 122,122,76,76,106,123,123,123,77,107,107,124,124,124,125,125, + 122,122,76,76,76,123,123,77,77,77,124,124,124,78,95,125, + 92,92,76,93,93,93,123,77,94,94,111,124,78,95,95,96, + 109,109,93,93,110,110,110,94,94,111,111,112,95,95,96,96, + 126,126,126,110,127,127,127,127,111,111,112,112,112,96,96,113, + 97,97,97,143,81,98,98,115,115,99,99,116,116,116,100,117, + 97,97,97,143,81,98,98,115,115,99,99,116,116,116,100,117, + 97,97,131,131,131,98,98,115,132,132,99,116,133,133,87,117, + 130,130,131,131,131,85,85,132,132,86,103,103,133,87,104,121, + 84,84,131,131,85,85,85,102,132,86,103,120,120,87,104,121, + 101,101,101,85,85,85,102,102,86,103,103,120,120,120,104,121, + 101,101,101,85,85,102,102,102,119,103,120,120,120,120,121,121, + 118,118,118,135,102,102,102,119,119,119,120,120,120,137,91,121, + 118,118,135,135,135,89,119,119,119,90,90,137,137,91,108,108, + 105,105,135,89,89,106,106,119,90,90,107,107,137,91,108,125, + 105,105,105,106,106,106,123,123,90,107,107,124,124,108,108,125, + 122,122,122,106,123,123,123,123,140,107,124,124,124,124,125,125, + 122,122,139,139,123,123,123,123,140,140,124,124,124,124,95,125, + 122,122,139,93,93,123,123,140,94,94,111,124,124,95,95,96, + 109,109,93,110,110,110,127,94,111,111,111,112,95,95,96,96, + 126,126,126,127,127,127,127,127,111,112,112,112,112,96,96,113, + 114,114,143,143,143,98,115,115,128,128,116,129,129,129,146,117, + 114,114,143,143,143,98,115,115,128,128,116,129,129,129,146,117, + 130,130,131,131,131,131,115,132,132,132,132,133,133,133,133,150, + 130,130,131,131,131,131,132,132,132,132,132,133,133,133,150,150, + 130,130,131,131,131,131,102,132,132,132,132,133,133,133,150,121, + 101,101,131,131,131,102,102,102,132,132,120,120,133,133,150,121, + 118,118,118,135,102,102,102,119,119,119,120,120,120,137,150,121, + 118,118,135,135,135,102,119,119,119,136,136,137,137,137,137,154, + 134,134,135,135,135,135,119,119,136,136,136,137,137,137,154,154, + 134,134,135,135,135,135,119,136,136,136,153,137,137,137,154,154, + 122,122,135,135,135,123,123,136,136,153,107,124,124,154,154,125, + 138,138,139,139,139,123,123,140,140,140,124,124,141,141,141,125, + 138,138,139,139,139,139,123,140,140,140,124,141,141,141,141,158, + 138,138,139,139,139,139,140,140,140,140,157,141,141,141,158,158, + 155,155,139,156,156,156,156,140,157,157,157,141,141,158,158,158, + 126,126,156,156,127,127,127,157,157,157,112,112,158,158,158,113, + 142,142,143,143,143,143,115,128,128,128,145,129,129,129,146,163, + 142,142,143,143,143,143,115,128,128,128,145,129,129,129,146,163, + 130,130,131,131,131,131,132,132,132,132,132,133,133,133,150,150, + 130,130,131,131,131,131,132,132,132,132,149,133,133,133,150,167, + 130,130,131,131,131,131,132,132,132,132,149,133,133,150,150,167, + 130,130,131,131,131,148,148,132,132,149,149,133,133,150,150,167, + 118,118,135,135,135,148,119,119,119,149,149,137,137,150,167,167, + 134,134,135,135,135,135,119,119,136,136,136,137,137,137,154,154, + 134,134,135,135,135,135,119,136,136,136,153,137,137,137,154,154, + 134,134,135,135,135,152,152,136,136,153,153,137,137,154,154,171, + 151,151,135,152,152,152,152,136,153,153,153,170,154,154,171,171, + 138,138,139,139,139,139,140,140,140,140,170,141,141,141,141,171, + 138,138,139,139,139,139,140,140,140,140,157,141,141,141,158,158, + 138,138,139,139,156,156,156,140,140,157,157,141,141,158,158,158, + 155,155,156,156,156,156,173,157,157,157,174,141,158,158,158,175, + 172,172,156,173,173,173,173,157,157,174,174,174,158,158,175,175, + 159,159,143,143,144,144,161,128,128,145,145,162,129,146,163,163, + 159,159,143,143,144,144,161,128,128,145,145,162,129,146,163,163, + 130,130,131,131,131,131,132,132,132,132,149,133,133,150,150,167, + 130,130,131,131,131,148,148,132,132,149,149,133,133,150,150,167, + 147,147,131,131,148,148,148,132,132,149,149,133,133,150,167,167, + 147,147,131,148,148,148,165,132,149,149,166,166,133,150,167,167, + 164,164,135,148,148,165,165,119,149,149,166,166,137,150,167,184, + 134,134,135,135,135,135,119,136,136,136,153,137,137,137,154,154, + 134,134,135,135,135,152,152,136,136,153,153,137,137,154,154,171, + 151,151,135,152,152,152,152,136,153,153,153,170,154,154,171,171, + 151,151,152,152,152,169,169,153,153,153,170,170,154,154,171,171, + 168,168,139,139,139,169,169,140,140,170,170,170,141,141,171,188, + 138,138,139,139,139,139,140,140,140,157,157,141,141,141,158,158, + 155,155,156,156,156,156,156,140,157,157,157,141,141,158,158,175, + 172,172,156,156,173,173,173,157,157,174,174,174,158,158,175,175, + 172,172,173,173,173,173,190,157,174,174,174,191,158,175,175,175, + 160,160,144,144,161,161,161,178,145,145,162,162,179,146,163,180, + 160,160,144,144,161,161,161,178,145,145,162,162,179,146,163,180, + 147,147,131,148,148,161,161,132,132,149,162,162,133,150,150,180, + 147,147,148,148,148,148,165,132,149,149,166,166,150,150,167,167, + 164,164,148,148,148,165,165,132,149,149,166,166,150,150,167,184, + 164,164,164,148,165,165,165,182,149,166,166,166,183,167,167,184, + 164,164,164,165,165,165,182,182,149,166,166,183,183,167,167,184, + 181,181,135,135,135,182,182,136,136,153,166,183,183,154,154,184, + 151,151,135,152,152,152,152,136,153,153,153,170,154,154,171,171, + 151,151,152,152,152,169,169,153,153,153,170,170,154,154,171,171, + 168,168,168,169,169,169,169,169,153,170,170,170,187,171,171,188, + 168,168,168,169,169,169,186,186,170,170,170,187,187,187,188,188, + 185,185,139,156,156,186,186,186,157,157,187,187,187,158,158,188, + 155,155,156,156,156,173,173,157,157,157,174,187,158,158,158,175, + 172,172,156,173,173,173,173,157,174,174,174,191,158,158,175,175, + 189,189,189,173,190,190,190,190,174,174,191,191,191,175,175,176, + 177,177,206,206,161,178,178,207,207,162,162,179,179,192,163,180, + 177,177,206,206,161,178,178,207,207,162,162,179,179,192,163,180, + 193,193,194,194,194,194,178,195,195,195,162,196,196,196,196,180, + 193,193,194,194,194,194,165,195,195,195,166,196,196,196,167,184, + 164,164,194,194,194,165,182,195,195,195,166,183,196,196,167,184, + 181,181,194,194,165,182,182,182,195,166,166,183,183,183,184,184, + 181,181,181,165,182,182,182,182,199,166,183,183,183,183,184,184, + 197,197,198,198,198,182,182,199,199,199,183,183,200,200,200,184, + 197,197,198,198,198,198,199,199,199,199,170,200,200,200,200,171, + 168,168,198,198,198,169,169,199,199,170,170,170,200,200,171,188, + 168,168,168,169,169,169,186,186,170,170,170,187,187,187,188,188, + 185,185,202,202,186,186,186,186,203,170,187,187,187,187,188,188, + 185,185,202,202,202,186,186,203,203,203,187,187,187,204,204,188, + 201,201,202,202,202,186,186,203,203,203,187,187,204,204,175,175, + 172,172,202,173,173,190,190,203,203,174,191,191,204,175,175,176, + 189,189,189,190,190,190,190,190,174,191,191,191,191,175,176,176, + 205,205,206,206,206,178,178,207,207,207,179,179,192,192,209,180, + 205,205,206,206,206,178,178,207,207,207,179,179,192,192,209,180, + 193,193,194,194,194,194,195,195,195,195,195,196,196,196,196,213, + 193,193,194,194,194,194,195,195,195,195,212,196,196,196,213,213, + 193,193,194,194,194,194,182,195,195,195,212,196,196,196,213,184, + 181,181,194,194,194,182,182,195,195,195,183,183,183,196,213,184, + 181,181,198,198,182,182,182,182,199,199,183,183,183,183,213,184, + 197,197,198,198,198,198,182,199,199,199,183,200,200,200,200,217, + 197,197,198,198,198,198,199,199,199,199,216,200,200,200,217,217, + 197,197,198,198,198,198,199,199,199,216,216,200,200,200,217,217, + 185,185,198,215,215,186,186,186,216,216,187,187,187,217,217,188, + 185,185,202,202,202,186,186,203,203,203,187,187,187,204,204,188, + 201,201,202,202,202,186,186,203,203,203,187,187,204,204,204,221, + 201,201,202,202,202,202,186,203,203,203,220,204,204,204,221,221, + 218,218,202,219,219,219,219,203,220,220,220,204,204,221,221,221, + 189,189,219,219,190,190,190,220,220,220,191,191,221,221,221,176, + 222,222,206,206,223,223,178,207,207,208,208,192,192,209,209,226, + 222,222,206,206,223,223,178,207,207,208,208,192,192,209,209,226, + 193,193,194,194,194,194,195,195,195,212,212,196,196,196,213,213, + 193,193,194,194,194,211,211,195,195,212,212,196,196,213,213,213, + 210,210,194,194,211,211,211,195,212,212,212,196,196,213,213,230, + 210,210,194,211,211,211,182,195,212,212,229,183,196,213,213,230, + 181,181,198,211,211,182,182,199,212,212,229,183,183,213,213,230, + 197,197,198,198,198,198,199,199,199,199,216,200,200,200,217,217, + 197,197,198,198,215,215,215,199,199,216,216,200,200,217,217,217, + 214,214,215,215,215,215,215,199,216,216,216,200,200,217,217,234, + 214,214,215,215,215,232,232,216,216,216,233,187,217,217,217,234, + 201,201,202,202,202,186,186,203,203,203,187,187,204,204,234,234, + 201,201,202,202,202,202,186,203,203,203,220,204,204,204,221,221, + 218,218,202,219,219,219,219,203,220,220,220,204,204,221,221,221, + 218,218,219,219,219,219,219,220,220,220,220,237,221,221,221,238, + 235,235,219,219,236,236,236,220,220,237,237,237,221,221,238,238, + 239,239,223,223,224,224,224,208,208,225,225,225,209,209,226,226, + 239,239,223,223,224,224,224,208,208,225,225,225,209,209,226,226, + 210,210,211,211,211,211,211,195,212,212,212,196,196,213,213,230, + 210,210,211,211,211,211,228,212,212,212,229,229,213,213,213,230, + 227,227,211,211,211,228,228,212,212,229,229,229,213,213,230,230, + 227,227,211,211,228,228,228,212,212,229,229,246,213,213,230,230, + 227,227,211,228,228,228,245,212,229,229,229,246,213,213,230,230, + 214,214,198,215,215,215,215,199,216,216,216,200,200,217,217,217, + 214,214,215,215,215,215,232,216,216,216,233,200,217,217,217,234, + 231,231,215,215,232,232,232,216,216,233,233,233,217,217,234,234, + 231,231,215,232,232,232,232,216,233,233,233,250,217,217,234,234, + 231,231,202,232,232,249,249,203,203,233,250,250,204,234,234,251, + 218,218,202,219,219,219,219,203,220,220,220,204,204,221,221,221, + 218,218,219,219,219,219,219,220,220,220,220,237,221,221,221,238, + 235,235,219,219,236,236,236,220,220,237,237,237,221,221,238,238, + 235,235,236,236,236,236,236,220,237,237,237,254,221,238,238,238, + 240,240,224,224,224,241,241,241,225,225,242,242,242,226,226,243, + 240,240,224,224,224,241,241,241,225,225,242,242,242,226,226,243, + 227,227,211,211,228,241,241,212,212,229,242,242,213,213,230,243, + 227,227,211,228,228,228,245,212,229,229,229,246,213,213,230,230, + 227,227,228,228,228,245,245,245,229,229,246,246,246,230,230,230, + 244,244,244,228,245,245,245,245,229,229,246,246,246,230,230,247, + 244,244,244,245,245,245,245,245,229,246,246,246,246,230,230,247, + 244,244,215,215,245,245,245,216,216,216,246,246,217,217,217,247, + 231,231,215,232,232,232,232,216,216,233,233,233,217,217,234,234, + 231,231,232,232,232,232,249,216,233,233,233,250,217,234,234,234, + 248,248,248,232,249,249,249,249,233,233,250,250,250,234,234,251, + 248,248,248,249,249,249,249,249,233,250,250,250,250,234,251,251, + 248,248,219,219,249,249,249,220,220,220,250,250,221,221,221,251, + 235,235,219,219,236,236,236,220,220,237,237,237,221,221,238,238, + 235,235,236,236,236,236,236,220,237,237,237,254,221,238,238,238, + 252,252,252,236,236,253,253,253,237,237,254,254,254,238,238,255, +}; + diff --git a/sys/src/cmd/pict/rgbycc.c b/sys/src/cmd/pict/rgbycc.c new file mode 100644 index 0000000..f0c8756 --- /dev/null +++ b/sys/src/cmd/pict/rgbycc.c @@ -0,0 +1,121 @@ +#include +#include +#include + +float c1 = 1.402; +float c2 = 0.34414; +float c3 = 0.71414; +float c4 = 1.772; + +int +closest(int Y, int Cb, int Cr) +{ + double r, g, b; + double diff, min; + int rgb, R, G, B, v, i; + int y1, cb1, cr1; + + Cb -= 128; + Cr -= 128; + r = Y+c1*Cr; + g = Y-c2*Cb-c3*Cr; + b = Y+c4*Cb; + +//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b); + + min = 1000000.; + v = 1000; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + R = (rgb >> 16) & 0xFF; + G = (rgb >> 8) & 0xFF; + B = (rgb >> 0) & 0xFF; + diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b); + y1 = 0.5870*G + 0.114*B + 0.299*R; + cb1 = (B-y1)/1.772; + cr1 = (R-y1)/1.402; + if(diff < min){ +// if(Y==0 && y1!=0) +// continue; + if(Y==256-16 && y1<256-16) + continue; +// if(Cb==0 && cb1!=0) +// continue; + if(Cb==256-16 && cb1<256-16) + continue; +// if(Cr==0 && cr1!=0) +// continue; + if(Cr==256-16 && cr1<256-16) + continue; +//print("%d %d %d\n", R, G, B); + min = diff; + v = i; + } + } + if(v > 255) + abort(); + return v; +} + +void +main(int argc, char *argv[]) +{ + int i, rgb; + int r, g, b; + double Y, Cr, Cb; + int y, cb, cr; + unsigned char close[16*16*16]; + +//print("%d\n", closest(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]))); +//exits("X"); + + print("/* This file has been generated by rgbycc */\n"); + /* 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("unsigned char 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/sys/src/cmd/pict/tga.c b/sys/src/cmd/pict/tga.c new file mode 100644 index 0000000..ef0d18c --- /dev/null +++ b/sys/src/cmd/pict/tga.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readtga(int fd); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "tga: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->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: tga -39cdektv [file ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "tga: 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, "tga: 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 = readtga(fd); + if(array == nil || array[0]==nil){ + fprint(2, "tga: 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); + outchan = GREY8; + }else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "tga: 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, "tga: allocimage %s failed: %r\n", name); + return "allocimage"; + } + + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "tga: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + 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, "tga: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "tga: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/sys/src/cmd/pict/tif.c b/sys/src/cmd/pict/tif.c new file mode 100644 index 0000000..28adc0f --- /dev/null +++ b/sys/src/cmd/pict/tif.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +Image *image; +int defaultcolor = 1; + +enum { + Border = 2, + Edge = 5 +}; + +int init(void); +char *show(int, char *, int); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0) + sysfatal("getwindow: %r"); + if(image == nil) + return; + r = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->r.min, S); + flushimage(display, 1); +} + +void +usage(void) +{ + fprint(2, "usage: %s [-39cdektv] [file.tif ...]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int fd, i; + char *err; + uint32_t outchan; + + outchan = CMAP8; + ARGBEGIN { + /* + * produce encoded, compressed, bitmap file; + * no display by default + */ + case 'c': + cflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + /* suppress display of image */ + case 'd': + dflag++; + break; + /* disable floyd-steinberg error diffusion */ + case 'e': + eflag++; + break; + /* force black and white */ + case 'k': + defaultcolor = 0; + outchan = GREY8; + break; + /* + * produce encoded, compressed, three-color + * bitmap file; no display by default + */ + case '3': + threeflag++; + /* fall through */ + /* + * produce encoded, compressed, true-color + * bitmap file; no display by default + */ + case 't': + cflag++; + dflag++; + output++; + defaultcolor = 0; + outchan = RGB24; + break; + /* force RGBV */ + case 'v': + defaultcolor = 0; + outchan = CMAP8; + break; + /* + * produce plan 9, uncompressed, bitmap file; + * no display by default + */ + case '9': + nineflag++; + dflag++; + output++; + if(defaultcolor) + outchan = CMAP8; + break; + default: + usage(); + } ARGEND + + if(argc <= 0) + exits(show(0, "", outchan)); + err = nil; + for(i = 0; i < argc; i++) { + if((fd = open(argv[i], OREAD)) < 0) { + fprint(2, "%s: open %s: %r\n", + argv0, argv[i]); + err = "open"; + } else { + err = show(fd, argv[i], outchan); + close(fd); + } + if((nineflag || cflag) && argc > 1 && err == nil) { + fprint(2, "%s: exiting after one file\n", + argv0); + break; + } + } + exits(err); +} + +int +init(void) +{ + static int inited = 0; + + if(!inited) { + if(initdraw(0, 0, 0) < 0) { + fprint(2, "%s: initdraw: %r", argv0); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 0; +} + +char * +show(int fd, char *name, int outchan) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + Biobuf b; + char buf[32]; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + array = Breadtif(&b, CRGB24); + if(array == nil || array[0] == nil) { + if(array != nil) + free(array); + fprint(2, "%s: decode %s failed: %r\n", + argv0, name); + return "decode"; + } + Bterm(&b); + if(!dflag) { + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth > 8) + outchan = RGB24; + } + r = array[0]; + if(outchan != CMAP8) { + switch(r->chandesc) { + case CY: + outchan = GREY8; + break; + case CRGB24: + outchan = RGB24; + break; + } + c = r; + } else if((c = torgbv(r, !eflag)) == nil) { + fprint(2, "%s: conversion of %s failed: %r\n", + argv0, name); + return "torgbv"; + } + if(!dflag) { + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil) { + fprint(2, "%s: allocimage %s: %r\n", + argv0, name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], + c->chanlen) < 0) { + fprint(2, "%s: loadimage %s: %r\n", + argv0, name); + return "loadimage"; + } + image = i; + eresized(0); + ch = ekbd(); + if(ch == 'q' || ch == Kdel || ch == Keof) + 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, "%s: %s: write error: %r\n", + argv0, name); + return "write"; + } + } else if(cflag && writerawimage(1, c) < 0) { + fprint(2, "%s: %s: write error: %r\n", + argv0, name); + return "write"; + } + if(c != nil && c != r) { + free(c->chans[0]); + free(c); + } + for(j = 0; j < r->nchans; j++) + free(r->chans[j]); + free(r); + free(array); + return nil; +} diff --git a/sys/src/cmd/pict/togif.c b/sys/src/cmd/pict/togif.c new file mode 100644 index 0000000..2a5e186 --- /dev/null +++ b/sys/src/cmd/pict/togif.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: togif [-l loopcount] [-c 'comment'] [-d Δt (ms)] [-t transparency-index] [file ... [-d Δt] file ... | -E]\n"); + exits("usage"); +} + +#define UNSET (-12345678) + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd, j, dt, trans, loop, eof; + char buf[256]; + char *err, *comment, *s; + + comment = nil; + dt = -1; + trans = -1; + loop = UNSET; + eof = 0; + 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; + case 'E': + eof++; + break; + default: + usage(); + }ARGEND + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + + memimageinit(); + + err = nil; + + if(eof){ + if(argc != 0) usage(); + for(j = 0;;j++){ + i = readmemimage(0); + if(i == nil) break; + ni = memonechan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != nil){ + 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 "); + err = memwritegif(&bout, i, buf, dt, trans); + } + if(err != nil) break; + freememimage(i); + comment = nil; + } + }else if(argc == 0){ + i = readmemimage(0); + if(i == nil) + sysfatal("reading input: %r"); + ni = memonechan(i); + if(ni == nil) + sysfatal("converting image to RGBV: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + err = memstartgif(&bout, i, -1); + if(err == nil){ + if(comment) + err = memwritegif(&bout, i, comment, dt, trans); + else{ + snprint(buf, sizeof buf, "Converted by Plan 9 from "); + err = memwritegif(&bout, i, buf, dt, trans); + } + } + }else{ + if(loop == UNSET){ + if(argc == 1) + loop = -1; /* no loop for single image */ + else + loop = 0; /* the default case: 0 means infinite loop */ + } + for(j=0; j +#include +#include +#include + +enum +{ + FileHdrLen= 6, + IconDescrLen= 16, + IconHdrLen= 40, +}; + +typedef struct Icon Icon; +struct Icon +{ + Icon *next; + char *file; + + unsigned char w; /* icon width */ + unsigned char h; /* icon height */ + unsigned short ncolor; /* number of colors */ + unsigned short nplane; /* number of bit planes */ + unsigned short bits; /* bits per pixel */ + uint32_t len; /* length of data */ + uint32_t offset; /* file offset to data */ + unsigned char map[4*256]; /* color map */ + + Image *img; + + unsigned char *xor; + int xorlen; + unsigned char *and; + int andlen; +}; + +typedef struct Header Header; +struct Header +{ + uint n; + Icon *first; + Icon *last; +}; + +void +Bputs(Biobuf *b, unsigned short x) +{ + Bputc(b, x&0xff); + Bputc(b, x>>8); +} + +void +Bputl(Biobuf *b, uint32_t 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; + uint32_t offset; + uint32_t len; + + ARGBEGIN{ + }ARGEND; + + /* read in all the images */ + display = initdisplay(nil, nil, nil); + if(argc < 1){ + readicon("/fd/0"); + } else { + for(i = 0; i < argc; i++) + readicon(argv[i]); + } + + /* create the .ico file */ + b = &out; + Binit(b, 1, OWRITE); + + /* offset to first icon */ + offset = FileHdrLen + h.n*IconDescrLen; + + /* file header is */ + Bputs(b, 0); + Bputs(b, 1); + Bputs(b, h.n); + + /* icon description */ + for(icon = h.first; icon != nil; icon = icon->next){ + Bputc(b, icon->w); + Bputc(b, icon->h); + Bputc(b, icon->ncolor); + Bputc(b, 0); + Bputs(b, icon->nplane); + Bputs(b, icon->bits); + len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen; + Bputl(b, len); + Bputl(b, offset); + offset += len; + } + + /* icons */ + for(icon = h.first; icon != nil; icon = icon->next){ + /* icon header (BMP like) */ + Bputl(b, IconHdrLen); + Bputl(b, icon->w); + Bputl(b, 2*icon->h); + Bputs(b, icon->nplane); + Bputs(b, icon->bits); + Bputl(b, 0); /* compression info */ + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + Bputl(b, 0); + + /* color map */ + if(Bwrite(b, icon->map, 4*icon->ncolor) < 0) + sysfatal("writing color map: %r"); + + /* xor bits */ + if(Bwrite(b, icon->xor, icon->xorlen) < 0) + sysfatal("writing xor bits: %r"); + + /* and bits */ + if(Bwrite(b, icon->and, icon->andlen) < 0) + sysfatal("writing and bits: %r"); + } + + Bterm(b); + exits(0); +} + +void +readicon(char *file) +{ + int fd; + Icon *icon; + + fd = open(file, OREAD); + if(fd < 0) + sysfatal("opening %s: %r", file); + icon = emalloc(sizeof(Icon)); + icon->img = readimage(display, fd, 0); + if(icon->img == nil) + sysfatal("reading image %s: %r", file); + close(fd); + + if(h.first) + h.last->next = icon; + else + h.first = icon; + h.last = icon; + h.n++; + + icon->h = Dy(icon->img->r); + icon->w = Dx(icon->img->r); + icon->bits = 1<img->depth; + icon->nplane = 1; + + /* convert to 8 bits per pixel */ + switch(icon->img->chan){ + case GREY8: + case CMAP8: + break; + case GREY1: + case GREY2: + case GREY4: + mk8bit(icon, 1); + break; + default: + mk8bit(icon, 0); + break; + } + icon->bits = 8; + icon->file = file; + + /* create xor/and masks, minimizing bits per pixel */ + mkxorand(icon, icon->img->chan == GREY8); +} + +void* +emalloc(int len) +{ + void *x; + + x = mallocz(len, 1); + if(x == nil) + sysfatal("memory: %r"); + return x; +} + +/* convert to 8 bit */ +void +mk8bit(Icon *icon, int grey) +{ + Image *img; + + img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill); + if(img == nil) + sysfatal("can't allocimage: %r"); + draw(img, img->r, icon->img, nil, ZP); + freeimage(icon->img); + icon->img = img; +} + +/* make xor and and mask */ +void +mkxorand(Icon *icon, int grey) +{ + int i, x, y, s, sa; + unsigned char xx[256]; + unsigned char *data, *p, *e; + int ndata; + unsigned char *mp; + int ncolor; + uint32_t color; + int bits; + unsigned char andbyte, xorbyte; + unsigned char *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/sys/src/cmd/pict/tojpg.c b/sys/src/cmd/pict/tojpg.c new file mode 100644 index 0000000..79a3e9f --- /dev/null +++ b/sys/src/cmd/pict/tojpg.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: %s [-c 'comment'] [-ks] [file]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd, kflag, sflag; + char *err, *file, *com; + + kflag = sflag = 0; + com = nil; + ARGBEGIN { + case 'c': + com = EARGF(usage()); + break; + case 'k': + kflag = 1; + break; + case 's': + sflag = 1; + break; + default: + usage(); + } ARGEND + + if(argc > 1) + usage(); + if(argc == 0) { + file = ""; + fd = 0; + } else { + file = argv[0]; + if((fd = open(file, OREAD)) < 0) + sysfatal("open %s: %r", file); + } + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit: %r"); + memimageinit(); + + if((i = readmemimage(fd)) == nil) + sysfatal("readimage %s: %r", file); + close(fd); + if((ni = memmultichan(i)) == nil) + sysfatal("converting image to RGB24: %r"); + if(i != ni) { + freememimage(i); + i = ni; + } + err = memwritejpg(&bout, i, com, kflag, sflag); + freememimage(i); + + if(err != nil) + fprint(2, "%s: %s\n", argv0, err); + exits(err); +} diff --git a/sys/src/cmd/pict/topng.c b/sys/src/cmd/pict/topng.c new file mode 100644 index 0000000..09c0061 --- /dev/null +++ b/sys/src/cmd/pict/topng.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: topng [-c 'comment'] [-g 'gamma'] [file]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i; + int fd; + char *err, *filename; + ImageInfo II; + + ARGBEGIN{ + case 'c': + II.comment = ARGF(); + if(II.comment == nil) + usage(); + II.fields_set |= II_COMMENT; + break; + case 'g': + II.gamma = atof(ARGF()); + if(II.gamma == 0.) + usage(); + II.fields_set |= II_GAMMA; + break; + case 't': + break; + default: + usage(); + }ARGEND + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + memimageinit(); + + if(argc == 0){ + fd = 0; + filename = ""; + }else{ + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("can't open %s: %r", argv[0]); + filename = argv[0]; + } + + i = readmemimage(fd); + if(i == nil) + sysfatal("can't readimage %s: %r", filename); + close(fd); + + err = memwritepng(&bout, i, &II); + freememimage(i); + + if(err != nil) + fprint(2, "topng: %s\n", err); + exits(err); +} diff --git a/sys/src/cmd/pict/toppm.c b/sys/src/cmd/pict/toppm.c new file mode 100644 index 0000000..724854e --- /dev/null +++ b/sys/src/cmd/pict/toppm.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +void +usage(void) +{ + fprint(2, "usage: toppm [-c 'comment'] [-r] [file]\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd, rflag; + char buf[256]; + char *err, *comment; + + rflag = 0; + 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; + case 'r': + rflag = 1; + break; + default: + usage(); + }ARGEND + + if(argc > 1) + usage(); + + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit failed: %r"); + + memimageinit(); + + 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; + } + err = memwriteppm(&bout, i, comment, rflag); + }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 RGB24: %r"); + if(i != ni){ + freememimage(i); + i = ni; + } + if(comment) + err = memwriteppm(&bout, i, comment, rflag); + else{ + snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[0]); + err = memwriteppm(&bout, i, buf, rflag); + } + freememimage(i); + } + + if(err != nil) + fprint(2, "toppm: %s\n", err); + exits(err); +} diff --git a/sys/src/cmd/pict/torgbv.c b/sys/src/cmd/pict/torgbv.c new file mode 100644 index 0000000..6a58d03 --- /dev/null +++ b/sys/src/cmd/pict/torgbv.c @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include "imagefile.h" + +#include "rgbv.h" +#include "ycbcr.h" + +#define CLAMPOFF 128 + +static int clamp[CLAMPOFF+256+CLAMPOFF]; +static int inited; + +void* +_remaperror(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + + errstr(buf, sizeof 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; + int bpc; + uint *map3; + unsigned char *closest; + Rawimage *im; + int dx, dy; + char err[ERRMAX]; + unsigned char *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic; + + err[0] = '\0'; + errstr(err, sizeof err); /* throw it away */ + im = malloc(sizeof(Rawimage)); + if(im == nil) + return nil; + memset(im, 0, sizeof(Rawimage)); + im->chans[0] = malloc(i->chanlen); + if(im->chans[0] == nil){ + free(im); + return nil; + } + im->r = i->r; + im->nchans = 1; + im->chandesc = CRGBV; + im->chanlen = i->chanlen; + + dx = i->r.max.x-i->r.min.x; + dy = i->r.max.y-i->r.min.y; + cmap = i->cmap; + + if(inited == 0){ + inited = 1; + for(j=0; j>4); + for(j=0; j>4); + } + + in = i->chans[0]; + inp = in; + out = im->chans[0]; + outp = out; + + ered = malloc((dx+1)*sizeof(int)); + egrn = malloc((dx+1)*sizeof(int)); + eblu = malloc((dx+1)*sizeof(int)); + if(ered==nil || egrn==nil || eblu==nil){ + free(im->chans[0]); + free(im); + free(ered); + free(egrn); + free(eblu); + return _remaperror("remap: malloc failed: %r"); + } + memset(ered, 0, (dx+1)*sizeof(int)); + memset(egrn, 0, (dx+1)*sizeof(int)); + memset(eblu, 0, (dx+1)*sizeof(int)); + + switch(i->chandesc){ + default: + return _remaperror("remap: can't recognize channel type %d", i->chandesc); + case CRGB1: + if(cmap == nil) + return _remaperror("remap: image has no color map"); + if(i->nchans != 1) + return _remaperror("remap: can't handle nchans %d", i->nchans); + for(j=1; j<=8; j++) + if(i->cmaplen == 3*(1< 8) + return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3); + if(i->cmaplen != 3*256){ + /* to avoid a range check in inner loop below, make a full-size cmap */ + memmove(cmap1, cmap, i->cmaplen); + cmap = cmap1; + } + if(errdiff == 0){ + k = 0; + for(j=0; j<256; j++){ + r = cmap[k]>>4; + g = cmap[k+1]>>4; + b = cmap[k+2]>>4; + k += 3; + map[j] = closestrgb[b+16*(g+16*r)]; + } + for(j=0; jchanlen; j++) + out[j] = map[in[j]]; + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y= 256+CLAMPOFF) + r = 0; + if(g >= 256+CLAMPOFF) + g = 0; + if(b >= 256+CLAMPOFF) + b = 0; + r1 = clamp[r+CLAMPOFF]; + g1 = clamp[g+CLAMPOFF]; + b1 = clamp[b+CLAMPOFF]; + if(r1 >= 16 || g1 >= 16 || b1 >= 16) + col = 0; + else + col = closestrgb[b1+16*(g1+16*r1)]; + *outp++ = col; + + rgb = rgbmap[col]; + r -= (rgb>>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + + g -= (rgb>>8) & 0xFF; + t = (3*g)>>4; + *gp++ = t+eg; + *gp += t; + eg = g-3*t; + + b -= rgb & 0xFF; + t = (3*b)>>4; + *bp++ = t+eb; + *bp += t; + eb = b-3*t; + } + } + } + break; + + case CYCbCr: + bpc = 1; + rpic = i->chans[0]; + gpic = i->chans[1]; + bpic = i->chans[2]; + closest = closestycbcr; + map3 = ycbcrmap; + if(i->nchans != 3) + return _remaperror("remap: RGB image has %d channels", i->nchans); + goto Threecolor; + + case CRGB: + bpc = 1; + rpic = i->chans[0]; + gpic = i->chans[1]; + bpic = i->chans[2]; + if(i->nchans != 3) + return _remaperror("remap: RGB image has %d channels", i->nchans); + goto rgbgen; + + case CRGB24: + bpc = 3; + bpic = i->chans[0]; + gpic = i->chans[0] + 1; + rpic = i->chans[0] + 2; + goto rgbgen; + + case CRGBA32: + bpc = 4; + /* i->chans[0]+0 is alpha */ + bpic = i->chans[0] + 1; + gpic = i->chans[0] + 2; + rpic = i->chans[0] + 3; + + rgbgen: + closest = closestrgb; + map3 = rgbmap; + + Threecolor: + + if(errdiff == 0){ + outp = out; + for(j=0; jchanlen; j+=bpc){ + r = rpic[j]>>4; + g = gpic[j]>>4; + b = bpic[j]>>4; + *outp++ = closest[b+16*(g+16*r)]; + } + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y 255) + r = 255; + if(g < 0) + g = 0; + else if(g > 255) + g = 255; + if(b < 0) + b = 0; + else if(b > 255) + b = 255; + r1 = r>>4; + g1 = g>>4; + b1 = b>>4; + col = closest[b1+16*(g1+16*r1)]; + *outp++ = col; + + rgb = map3[col]; + r -= (rgb>>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + + g -= (rgb>>8) & 0xFF; + t = (3*g)>>4; + *gp++ = t+eg; + *gp += t; + eg = g-3*t; + + b -= rgb & 0xFF; + t = (3*b)>>4; + *bp++ = t+eb; + *bp += t; + eb = b-3*t; + } + } + } + break; + + case CYA16: + bpc = 2; + /* i->chans[0] + 0 is alpha */ + rpic = i->chans[0] + 1; + goto greygen; + + case CY: + bpc = 1; + rpic = i->chans[0]; + if(i->nchans != 1) + return _remaperror("remap: Y image has %d chans", i->nchans); + + greygen: + if(errdiff == 0){ + for(j=0; jchanlen; j+=bpc){ + r = rpic[j]>>4; + *outp++ = closestrgb[r+16*(r+16*r)]; + } + }else{ + /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ + for(y=0; y>16) & 0xFF; + t = (3*r)>>4; + *rp++ = t+er; + *rp += t; + er = r-3*t; + } + } + } + break; + } + free(ered); + free(egrn); + free(eblu); + return im; +} diff --git a/sys/src/cmd/pict/totif.c b/sys/src/cmd/pict/totif.c new file mode 100644 index 0000000..2e440b9 --- /dev/null +++ b/sys/src/cmd/pict/totif.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include "imagefile.h" + +static Memimage *memtochan(Memimage *, uint32_t); + +void +usage(void) +{ + fprint(2, "usage: %s [-c 'comment'] " + "[-3bgGhklLptvyY] [file]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Biobuf bout; + Memimage *i, *ni; + int fd, chanflag, comp, opt; + char *err, *file, *c; + uint32_t chan; + + chan = BGR24; + chanflag = opt = 0; + comp = 1; + c = nil; + ARGBEGIN { + case '3': /* force RGB */ + chan = BGR24; + chanflag = 1; + break; + case 'b': + chan = GREY1; + chanflag = 1; + break; + case 'c': + c = EARGF(usage()); + break; + case 'g': /* t4 */ + comp = 3; + opt = 0; + break; + case 'G': /* t4 two-dimensional */ + comp = 3; + opt = 1; + break; + case 'h': /* huffman */ + comp = 2; + break; + case 'k': + chan = GREY8; + chanflag = 1; + break; + case 'l': /* lzw */ + comp = 5; + opt = 0; + break; + case 'L': /* lzw, horizontal differencing */ + comp = 5; + opt = 1; + break; + case 'p': /* packbits */ + comp = 0x8005; + break; + case 't': /* t6 */ + comp = 4; + break; + case 'v': /* RGBV */ + chan = CMAP8; + chanflag = 1; + break; + case 'y': + chan = GREY2; + chanflag = 1; + break; + case 'Y': + chan = GREY4; + chanflag = 1; + break; + default: + usage(); + } ARGEND + if(argc > 1) + usage(); + if(argc == 0) { + file = ""; + fd = 0; + } else { + file = argv[0]; + if((fd = open(file, OREAD)) < 0) + sysfatal("open %s: %r", file); + } + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit: %r"); + memimageinit(); + if((i = readmemimage(fd)) == nil) + sysfatal("readmemimage %s: %r", file); + close(fd); + if(comp >= 2 && comp <= 4) { + chan = GREY1; + chanflag = 1; + } else if(chan == GREY2) { + if((ni = memtochan(i, chan)) == nil) + sysfatal("memtochan: %r"); + if(i != ni) { + freememimage(i); + i = ni; + } + chan = GREY4; + } + if(!chanflag) { + switch(i->chan) { + case GREY1: + case GREY4: + case GREY8: + case CMAP8: + case BGR24: + break; + case GREY2: + chan = GREY4; + chanflag = 1; + break; + default: + chanflag = 1; + break; + } + } + if(chanflag) { + if((ni = memtochan(i, chan)) == nil) + sysfatal("memtochan: %r"); + if(i != ni) { + freememimage(i); + i = ni; + } + } + if((err = memwritetif(&bout, i, c, comp, opt)) != nil) + fprint(2, "%s: %s\n", argv0, err); + freememimage(i); + exits(err); +} + +static Memimage * +memtochan(Memimage *i, uint32_t chan) +{ + Memimage *ni; + + if(i->chan == chan) + return i; + if((ni = allocmemimage(i->r, chan)) == nil) + return nil; + memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); + return ni; +} diff --git a/sys/src/cmd/pict/totruecolor.c b/sys/src/cmd/pict/totruecolor.c new file mode 100644 index 0000000..5dead68 --- /dev/null +++ b/sys/src/cmd/pict/totruecolor.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include "imagefile.h" + +enum { + c1 = 2871, /* 1.402 * 2048 */ + c2 = 705, /* 0.34414 * 2048 */ + c3 = 1463, /* 0.71414 * 2048 */ + c4 = 3629, /* 1.772 * 2048 */ +}; + +Rawimage* +totruecolor(Rawimage *i, int chandesc) +{ + int j, k; + Rawimage *im; + char err[ERRMAX]; + unsigned char *rp, *gp, *bp, *cmap, *inp, *outp, cmap1[4*256]; + int r, g, b, Y, Cr, Cb, psize; + + if(chandesc!=CY && chandesc!=CRGB24) + return _remaperror("remap: can't convert to chandesc %d", chandesc); + + err[0] = '\0'; + errstr(err, sizeof err); /* throw it away */ + im = malloc(sizeof(Rawimage)); + if(im == nil) + return nil; + memset(im, 0, sizeof(Rawimage)); + if(chandesc == CY) + im->chanlen = i->chanlen; + else + im->chanlen = 3*i->chanlen; + im->chandesc = chandesc; + im->chans[0] = malloc(im->chanlen); + if(im->chans[0] == nil){ + free(im); + return nil; + } + im->r = i->r; + im->nchans = 1; + + cmap = i->cmap; + + outp = im->chans[0]; + + switch(i->chandesc){ + default: + return _remaperror("remap: can't recognize channel type %d", i->chandesc); + case CY: + if(i->nchans != 1) + return _remaperror("remap: Y image has %d chans", i->nchans); + if(chandesc == CY){ + memmove(im->chans[0], i->chans[0], i->chanlen); + break; + } + /* convert to three color */ + inp = i->chans[0]; + for(j=0; jchanlen; j++){ + k = *inp++; + *outp++ = k; + *outp++ = k; + *outp++ = k; + } + break; + + case CRGB1: + case CRGBV: + psize = (i->chandesc == CRGB1) ? 3 : 4; + 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); + if(i->cmaplen > psize*256) + return _remaperror("remap: can't do colormap size %d*%d", psize, i->cmaplen/psize); + if(i->cmaplen != psize*256){ + /* to avoid a range check in loop below, make a full-size cmap */ + memmove(cmap1, cmap, i->cmaplen); + cmap = cmap1; + } + inp = i->chans[0]; + if(chandesc == CY){ + for(j=0; jchanlen; j++){ + k = psize*(*inp++); + r = cmap[k+2]; + g = cmap[k+1]; + b = cmap[k+0]; + r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */ + *outp++ = r; + } + }else{ + for(j=0; jchanlen; j++){ + k = psize*(*inp++); + *outp++ = cmap[k+2]; + *outp++ = cmap[k+1]; + *outp++ = cmap[k+0]; + } + } + break; + + case CRGB: + if(i->nchans != 3) + return _remaperror("remap: can't handle nchans %d", i->nchans); + rp = i->chans[0]; + gp = i->chans[1]; + bp = i->chans[2]; + if(chandesc == CY){ + for(j=0; jchanlen; j++){ + r = *bp++; + g = *gp++; + b = *rp++; + r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */ + *outp++ = r; + } + }else + for(j=0; jchanlen; j++){ + *outp++ = *bp++; + *outp++ = *gp++; + *outp++ = *rp++; + } + break; + + case CYCbCr: + if(i->nchans != 3) + return _remaperror("remap: can't handle nchans %d", i->nchans); + rp = i->chans[0]; + gp = i->chans[1]; + bp = i->chans[2]; + for(j=0; jchanlen; j++){ + Y = *rp++ << 11; + Cb = *gp++ - 128; + Cr = *bp++ - 128; + r = (Y+c1*Cr) >> 11; + g = (Y-c2*Cb-c3*Cr) >> 11; + b = (Y+c4*Cb) >> 11; + if(r < 0) + r = 0; + if(r > 255) + r = 255; + if(g < 0) + g = 0; + if(g > 255) + g = 255; + if(b < 0) + b = 0; + if(b > 255) + b = 255; + if(chandesc == CY){ + r = (2125*r + 7154*g + 721*b)/10000; + *outp++ = r; + }else{ + *outp++ = b; + *outp++ = g; + *outp++ = r; + } + } + break; + } + return im; +} diff --git a/sys/src/cmd/pict/v210.c b/sys/src/cmd/pict/v210.c new file mode 100644 index 0000000..0614c2a --- /dev/null +++ b/sys/src/cmd/pict/v210.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readV210(int fd, int colorspace); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "readV210: can't reattach to window\n"); + exits("resize"); + } + if(image == nil) + return; + r = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->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: %s -39cdektv [file.yuv ...]\n", argv0); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "readV210: 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, "readV210: 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 = readV210(fd, CYCbCr); + if(array == nil || array[0]==nil){ + fprint(2, "readV210: 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, "readV210: 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, "readV210: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "readV210: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + 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, "readV210: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "readV210: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} diff --git a/sys/src/cmd/pict/writegif.c b/sys/src/cmd/pict/writegif.c new file mode 100644 index 0000000..346bc18 --- /dev/null +++ b/sys/src/cmd/pict/writegif.c @@ -0,0 +1,574 @@ +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum +{ + Nhash = 4001, + Nbuf = 300, +}; + +typedef struct Entry Entry; +typedef struct IO IO; + + +struct Entry +{ + int index; + int prefix; + int exten; + Entry *next; +}; + +struct IO +{ + Biobuf *fd; + unsigned char 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 unsigned char *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, uint32_t, 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(uint32_t); +static void encode(Biobuf*, Rectangle, int, unsigned char*, uint); + +static +char* +startgif0(Biobuf *fd, uint32_t chan, Rectangle r, int depth, int loopcount) +{ + int i; + + for(i=0; ichan, image->r, image->depth, loopcount); +} + +char* +memstartgif(Biobuf *fd, Memimage *memimage, int loopcount) +{ + return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount); +} + +static +char* +writegif0(Biobuf *fd, Image *image, Memimage *memimage, uint32_t 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; + uint32_t rgb; + unsigned char *c; + + if(colormap[0] != nil) + return; + for(i=0; i>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[2]; /* GREY4 */ + for(i=0; i<16; i++){ + col = (i<<4)|i; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[1]; /* GREY2 */ + for(i=0; i<4; i++){ + col = (i<<6)|(i<<4)|(i<<2)|i; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } + c = colormap[0]; /* GREY1 */ + for(i=0; i<2; i++){ + if(i == 0) + col = 0; + else + col = 0xFF; + rgb = cmap2rgb(col); + c[3*i+0] = (rgb>>16) & 0xFF; /* red */ + c[3*i+1] = (rgb>> 8) & 0xFF; /* green */ + c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */ + } +} + +/* imported from libdraw/arith.c to permit an extern log2 function */ +static int log2[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 +}; + +/* + * Write header, logical screen descriptor, and color map + */ +static +void +writeheader(Biobuf *fd, Rectangle r, int depth, uint32_t 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 = log2[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; + unsigned char *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-log2[depth]))); + data = gifmalloc(ndata); + if(memimage != nil) + ndata = unloadmemimage(memimage, r, data, ndata); + else + ndata = unloadimage(image, r, data, ndata); + if(ndata < 0){ + err = gifmalloc(ERRMAX); + snprint(err, ERRMAX, "WriteGIF: %r"); + free(data); + return err; + } + + /* Encode and emit the data */ + encode(fd, r, depth, data, ndata); + free(data); + + /* Block Terminator */ + Bputc(fd, 0); + return nil; +} + +/* + * Write trailer + */ +void +endgif(Biobuf *fd) +{ + Bputc(fd, 0x3B); + Bflush(fd); +} + +void +memendgif(Biobuf *fd) +{ + endgif(fd); +} + +/* + * Put n bits of c into output at io.buf[i]; + */ +static +void +output(IO *io, int c, int n) +{ + if(c < 0){ + if(io->nbits != 0) + io->buf[io->i++] = io->sreg; + Bputc(io->fd, io->i); + Bwrite(io->fd, io->buf, io->i); + io->nbits = 0; + return; + } + + if(io->nbits+n >= 31){ + fprint(2, "panic: WriteGIF sr overflow\n"); + exits("WriteGIF panic"); + } + io->sreg |= c<nbits; + io->nbits += n; + + while(io->nbits >= 8){ + io->buf[io->i++] = io->sreg; + io->sreg >>= 8; + io->nbits -= 8; + } + + if(io->i >= 255){ + Bputc(io->fd, 255); + Bwrite(io->fd, io->buf, 255); + memmove(io->buf, io->buf+255, io->i-255); + io->i -= 255; + } +} + +/* + * LZW encoder + */ +static +void +encode(Biobuf *fd, Rectangle r, int depth, unsigned char *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 = log2[depth]; + /* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */ + ld0 = ld; + if(ld0 == 0) + ld0 = 1; + codesize = (1<fd = fd; + sreg = 0; + nbits = 0; + bitsperpixel = 1<prefix<<24) | (e->exten<<8); + h %= Nhash; + if(h < 0) + h += Nhash; + e->next = hash[h]; + hash[h] = e; + } + prefix = -1; + if(first) + output(io, CTM, csize); + first = 0; + + /* + * Scan over pixels. Because of partially filled bytes on ends of scan lines, + * which must be ignored in the data stream passed to GIF, this is more + * complex than we'd like. + */ +Next: + for(;;){ + if(ld != 3){ + /* beginning of scan line is difficult; prime the shift register */ + if(x == r.min.x){ + if(datai == ndata) + break; + sreg = data[datai++]; + nbits = 8-((x&(7>>ld))<>nbits & pm; + h = prefix<<24 | c<<8; + h %= Nhash; + if(h < 0) + h += Nhash; + oe = nil; + for(e = hash[h]; e!=nil; e=e->next){ + if(e->prefix == prefix && e->exten == c){ + if(oe != nil){ + oe->next = e->next; + e->next = hash[h]; + hash[h] = e; + } + prefix = e->index; + goto Next; + } + oe = e; + } + + output(io, prefix, csize); + early = 0; /* peculiar tiff feature here for reference */ + if(nentry == maxentry-early){ + if(csize == 12){ + nbits += bitsperpixel; /* unget pixel */ + x--; + if(ld != 3 && x == r.min.x) + datai--; + output(io, CTM, csize); + goto Init; + } + csize++; + maxentry = (1<prefix = prefix; + e->exten = c; + e->next = hash[h]; + hash[h] = e; + + prefix = c; + nentry++; + } + + output(io, prefix, csize); + output(io, EOD, csize); + output(io, -1, csize); + free(io); + free(hash); +} + +static +void* +gifmalloc(uint32_t 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/sys/src/cmd/pict/writejpg.c b/sys/src/cmd/pict/writejpg.c new file mode 100644 index 0000000..e764d9f --- /dev/null +++ b/sys/src/cmd/pict/writejpg.c @@ -0,0 +1,915 @@ +/* +* code/documentation from: +* http://www.w3.org/Graphics/JPEG/jfif3.pdf +* http://www.w3.org/Graphics/JPEG/itu-t81.pdf +* http://en.wikipedia.org/wiki/JPEG +* http://en.wikibooks.org/wiki/JPEG_-_Idea_and_Practice +* http://code.google.com/p/go/source/browse/src/pkg/image/jpeg/writer.go +* /sys/src/cmd/jpg +* +* fdct code from: +* http://www.ijg.org/files/jpegsrc.v8c.tar.gz +* http://code.google.com/p/go/source/browse/src/pkg/image/jpeg/fdct.go +*/ +#include +#include +#include +#include +#include +#include "imagefile.h" + +/* +* imported from libdraw/arith.c to permit +* extern log2 function +*/ +static int log2[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, + -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, + -1, -1, -1, -1, -1, -1, -1, 5 +}; + +/* fdct constants */ +enum { + Fix02 = 2446, /* 0.298631336 */ + Fix03 = 3196, /* 0.390180644 */ + Fix05 = 4433, /* 0.541196100 */ + Fix07 = 6270, /* 0.765366865 */ + Fix08 = 7373, /* 0.899976223 */ + Fix11 = 9633, /* 1.175875602 */ + Fix15 = 12299, /* 1.501321110 */ + Fix18 = 15137, /* 1.847759065 */ + Fix19 = 16069, /* 1.961570560 */ + Fix20 = 16819, /* 2.053119869 */ + Fix25 = 20995, /* 2.562915447 */ + Fix30 = 25172 /* 3.072711026 */ +}; + +static int zigzag[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +static int invzigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/* section K.1 for quantization tables */ +static int qt[2][64] = { + /* luminance */ + {16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99}, + + /* chrominance */ + {17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99} +}; + +/* section K.3.3 for huffman tables */ +static int dcbits[2][16] = { + /* luminance */ + {0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + /* chrominance */ + {0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +static int dchuffval[2][12] = { + /* luminance */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}, + + /* chrominance */ + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b} +}; + +static int acbits[2][16] = { + /* luminance */ + {0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d}, + + /* chrominance */ + {0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77} +}; + +static int achuffval[2][162] = { + /* luminance */ + {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, + 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, + 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa}, + + /* chrominance */ + {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, + 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa} +}; + +static int ehufcod[2][12]; /* dc codes */ +static int ehufsid[2][12]; /* dc sizes */ +static int ehufcoa[2][251]; /* ac codes */ +static int ehufsia[2][251]; /* ac sizes */ + +static int byte; +static int nbyte; + +/* utilities */ +static int Bputs(Biobuf *, int); +static int min(int, int); + +/* encoding */ +static void grey2rgb(int *, int *, int *, int, int); +static void rgb2ycc(int *, int *, int *, int, int, int); +static void fdct(int *, int); +static int csize(int, int); +static void writebyte(Biobuf *); +static void writebits(Biobuf *, int, int); +static void writecode(Biobuf *, int, int); +static int huf(Biobuf *, int *, int, int, int); +static char *toycc1(int *, int *, int *, int, int, int, int, int, + unsigned char *, int, int); +static char *toycc2(int *, int *, int *, int, int, int, int, int, + unsigned char *, int, int); +static char *encode(Biobuf *, Rectangle, unsigned char *, uint32_t, int, + int, int); + +/* huffman tables */ +static void makehuf(int *, int *, int *, int *, int); + +/* tables, markers, headers, trailers */ +static void writejfif(Biobuf *, int, int); +static void writecomment(Biobuf *, char *); +static void writequant(Biobuf *, int, int); +static void writehuffman(Biobuf *, int, int); +static void writeframe(Biobuf *, int, int, int); +static void writescan(Biobuf *, int); +static void writeheader(Biobuf *, int, int, char *, int, int); +static void writetrailer(Biobuf *); +static char *writedata(Biobuf *, Image *, Memimage *, int, int); +static char *writejpg0(Biobuf *, Image *, Memimage *, + Rectangle, uint32_t, char *, int, int); + +static int +Bputs(Biobuf *b, int s) +{ + if(Bputc(b, s>>8) < 0) + return -1; + return Bputc(b, s); +} + +static int +min(int a, int b) +{ + return a < b? a: b; +} + +static void +grey2rgb(int *r, int *g, int *b, int c, int depth) +{ + if(depth == 1) { + if(c != 0) + c = 0xff; + } else if(depth == 2) + c = (c << 6) | (c << 4) | (c << 2) | c; + else + c = (c << 4) | c; + c = cmap2rgb(c); + *r = (c >> 16) & 0xff; + *g = (c >> 8) & 0xff; + *b = c & 0xff; +} + +static void +rgb2ycc(int *y, int *cb, int *cr, int r, int g, int b) +{ + *y = (int)(0.299*r + 0.587*g + 0.114*b); + *cb = (int)(128.0 - 0.1687*r - 0.3313*g + 0.5*b); + *cr = (int)(128.0 + 0.5*r - 0.4187*g - 0.0813*b); +} + +/* coefficients remain scaled up by 8 at the end */ +static void +fdct(int *b, int sflag) +{ + int x, y, z, tmp0, tmp1, tmp2, tmp3; + int tmp10, tmp12, tmp11, tmp13; + + /* rows */ + for(y = 0; y < 8; y++) { + tmp0 = b[y*8+0] + b[y*8+7]; + tmp1 = b[y*8+1] + b[y*8+6]; + tmp2 = b[y*8+2] + b[y*8+5]; + tmp3 = b[y*8+3] + b[y*8+4]; + + tmp10 = tmp0 + tmp3; + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = b[y*8+0] - b[y*8+7]; + tmp1 = b[y*8+1] - b[y*8+6]; + tmp2 = b[y*8+2] - b[y*8+5]; + tmp3 = b[y*8+3] - b[y*8+4]; + + b[y*8+0] = (tmp10 + tmp11 - 8*128) << 2; + b[y*8+4] = (tmp10 - tmp11) << 2; + + z = (tmp12 + tmp13) * Fix05; + z += 1 << 10; + b[y*8+2] = (z + tmp12*Fix07) >> 11; + b[y*8+6] = (z - tmp13*Fix18) >> 11; + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z = (tmp12 + tmp13) * Fix11; + z += 1 << 10; + + tmp0 *= Fix15; + tmp1 *= Fix30; + tmp2 *= Fix20; + tmp3 *= Fix02; + tmp10 *= -Fix08; + tmp11 *= -Fix25; + tmp12 *= -Fix03; + tmp13 *= -Fix19; + + tmp12 += z; + tmp13 += z; + + b[y*8+1] = (tmp0 + tmp10 + tmp12) >> 11; + b[y*8+3] = (tmp1 + tmp11 + tmp13) >> 11; + b[y*8+5] = (tmp2 + tmp11 + tmp12) >> 11; + b[y*8+7] = (tmp3 + tmp10 + tmp13) >> 11; + } + /* columns */ + for(x = 0; x < 8; x++) { + tmp0 = b[0*8+x] + b[7*8+x]; + tmp1 = b[1*8+x] + b[6*8+x]; + tmp2 = b[2*8+x] + b[5*8+x]; + tmp3 = b[3*8+x] + b[4*8+x]; + + if(sflag) + tmp10 = (tmp0 + tmp3 + 1) << 1; + else + tmp10 = tmp0 + tmp3 + (1 << 1); + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = b[0*8+x] - b[7*8+x]; + tmp1 = b[1*8+x] - b[6*8+x]; + tmp2 = b[2*8+x] - b[5*8+x]; + tmp3 = b[3*8+x] - b[4*8+x]; + + b[0*8+x] = (tmp10 + tmp11) >> 2; + b[4*8+x] = (tmp10 - tmp11) >> 2; + + z = (tmp12 + tmp13) * Fix05; + z += 1 << 14; + b[2*8+x] = (z + tmp12*Fix07) >> 15; + b[6*8+x] = (z - tmp13*Fix18) >> 15; + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z = (tmp12 + tmp13) * Fix11; + z += 1 << 14; + + tmp0 *= Fix15; + tmp1 *= Fix30; + tmp2 *= Fix20; + tmp3 *= Fix02; + tmp10 *= -Fix08; + tmp11 *= -Fix25; + tmp12 *= -Fix03; + tmp13 *= -Fix19; + + tmp12 += z; + tmp13 += z; + + b[1*8+x] = (tmp0 + tmp10 + tmp12) >> 15; + b[3*8+x] = (tmp1 + tmp11 + tmp13) >> 15; + b[5*8+x] = (tmp2 + tmp11 + tmp12) >> 15; + b[7*8+x] = (tmp3 + tmp10 + tmp13) >> 15; + } +} + +static int +csize(int coeff, int ac) +{ + int i, max; + + max = 1 << 10; + if(!ac) + max <<= 1; + if(coeff < 0) + coeff *= -1; + if(coeff > max) + sysfatal("csize: coeff too big: %d", coeff); + i = ac? 1: 0; + while(coeff >= (1< 10)) + sysfatal("csize: invalid ac ssss: %d", i); + if(!ac && (i < 0 || i > 11)) + sysfatal("csize: invalid dc ssss: %d", i); + return i; +} + +static void +writebyte(Biobuf *fd) +{ + Bputc(fd, byte); + if(byte == 0xff) /* byte stuffing */ + Bputc(fd, 0x00); + byte = 0; + nbyte = 7; +} + +static void +writebits(Biobuf *fd, int co, int si) +{ + int i, bit; + + for(i = si-1; i >= 0; i--) { + bit = (co >> i) & 0x1; + byte |= bit << nbyte; + nbyte--; + if(nbyte < 0) + writebyte(fd); + } +} + +static void +writecode(Biobuf *fd, int co, int si) +{ + if(si > 8) { + writebits(fd, co>>8, si-8); + si = 8; + } + writebits(fd, co, si); +} + +static int +huf(Biobuf *fd, int *b, int pred, int chr, int sflag) +{ + int k, r, s, rs, si, co, dc, diff, zz[64], p, q, z; + + if(sflag) { + for(k = 0; k < 64; k++) { + p = b[zigzag[k]]; + q = qt[chr][zigzag[k]]; + zz[k] = p / q; + } + } else { + for(k = 0; k < 64; k++) { + p = b[k]; + q = (qt[chr][k] << 3); + /* rounding */ + if(p >= 0) + z = (p + (q >> 1)) / q; + else + z = -(-p + (q >> 1)) / q; + zz[zigzag[k]] = z; + } + } + + /* dc coefficient */ + dc = zz[0]; + zz[0] = diff = dc - pred; + + s = csize(diff, 0); + si = ehufsid[chr][s]; + co = ehufcod[chr][s]; + writecode(fd, co, si); + if(diff < 0) + diff -= 1; + writecode(fd, diff, s); + + /* figure F.2 */ + for(k = 1, r = 0; k < 64; k++) { + if(zz[k] == 0) { + if(k < 63) + r++; + else { + si = ehufsia[chr][0x00]; + co = ehufcoa[chr][0x00]; + writecode(fd, co, si); + } + } else { + while(r > 15) { + si = ehufsia[chr][0xf0]; + co = ehufcoa[chr][0xf0]; + writecode(fd, co, si); + r -= 16; + } + /* figure F.3 */ + s = csize(zz[k], 1); + rs = (r << 4) | s; + si = ehufsia[chr][rs]; + co = ehufcoa[chr][rs]; + writecode(fd, co, si); + if(zz[k] < 0) + zz[k] -= 1; + writecode(fd, zz[k], s); + r = 0; + } + } + return dc; +} + +static char * +toycc1(int *y, int *cb, int *cr, int jx, int jy, int dx, int dy, + int bpl, unsigned char *data, int ndata, int depth) +{ + int i, j, k, l, m, n, u, v, pos, pmask, nmask, pix; + int r, g, b; + + m = 8 / depth; + pmask = (1 << depth) - 1; + nmask = 7 >> log2[depth]; + for(i = jy, k = 0; i < jy+8; i++) { + v = min(i, dy-1); + for(l = 0, j = jx/m; l < 8; l++, k++) { + u = min(j, (dx-1)/m); + n = l+jx >= dx? dx-jx-1: l; + pos = v*bpl + u; + if(pos >= ndata) + return "WriteJPG: overflow"; + /* thanks writeppm */ + pix = (data[pos] >> depth*((nmask - n) & + nmask)) & pmask; + if(((n + 1) & nmask) == 0) + j++; + grey2rgb(&r, &g, &b, pix, depth); + rgb2ycc(&y[k], &cb[k], &cr[k], r, g, b); + } + } + return nil; +} + +static char * +toycc2(int *y, int *cb, int *cr, int jx, int jy, int dx, int dy, + int bpl, unsigned char *data, int ndata, int depth) +{ + int i, j, k, m, u, v, pos; + + m = depth / 8; + for(i = jy, k = 0; i < jy+8; i++) { + v = min(i, dy-1); + for(j = jx*m; j < (jx+8)*m; j+=m, k++) { + u = min(j, (dx-1)*m); + pos = v*bpl + u; + if(pos+m-1 >= ndata) + return "WriteJPG: overflow"; + rgb2ycc(&y[k], &cb[k], &cr[k], + data[pos+2*m/3], + data[pos+m/3], + data[pos]); + } + } + return nil; +} + +static char * +encode(Biobuf *fd, Rectangle r, unsigned char *data, uint32_t chan, + int ndata, int kflag, int sflag) +{ + int k, x, y, dx, dy, depth, bpl, ncomp; + int b[3][64], pred[3]; + char *err; + char *(*toycc)(int *, int *, int *, int, int, int, int, + int, unsigned char *, int, int); + + byte = 0; + nbyte = 7; + + switch(chan) { + case GREY1: + case GREY2: + case GREY4: + toycc = toycc1; + break; + case GREY8: + case RGB24: + toycc = toycc2; + break; + default: + return "WriteJPG: can't handle channel type"; + } + + /* + * if dx or dy is not a multiple of 8, + * the decoder should continue until reaching + * the last mcu, even if the extra pixels go beyond + * 0xffff. they are not shown anyway. + */ + dx = min(Dx(r), 0xffff); + dy = min(Dy(r), 0xffff); + depth = chantodepth(chan); + bpl = bytesperline(r, depth); + ncomp = kflag? 1: 3; + memset(pred, 0, sizeof pred); + for(x = 0, y = 0;;) { + err = (*toycc)(b[0], b[1], b[2], x, y, dx, dy, + bpl, data, ndata, depth); + if(err != nil) + return err; + for(k = 0; k < ncomp; k++) { + fdct(b[k], sflag); + pred[k] = huf(fd, b[k], pred[k], + k>0, sflag); + } + if((x += 8) >= dx) { + if((y += 8) >= dy) + break; + x = 0; + } + } + if(nbyte < 7) { /* bit padding */ + for(; nbyte >= 0; nbyte--) + byte |= 0x1 << nbyte; + writebyte(fd); + } + return err; +} + +static void +makehuf(int *ehufco, int *ehufsi, int *bits, int *huffval, int n) +{ + int i, j, k, code, si, lastk, *huffcode, *huffsize; + + /* n+1 for lastk */ + if((huffcode = malloc((n+1)*sizeof *huffcode)) == nil) + sysfatal("malloc: %r"); + if((huffsize = malloc((n+1)*sizeof *huffsize)) == nil) + sysfatal("malloc: %r"); + /* figure C.1 */ + for(k = 0, i = 1, j = 1; i <= 16;) { + if(j > bits[i-1]) { /* bits[i] in T.81: bug? */ + i++; + j = 1; + } else { + huffsize[k++] = i; + j++; + } + } + huffsize[k] = 0; + lastk = k; + /* figure C.2 */ + for(k = 0, code = 0, si = huffsize[0];;) { + do { + huffcode[k++] = code++; + } while(huffsize[k] == si); + if(huffsize[k] == 0) + break; + while(huffsize[k] != si) { + code <<= 1; + si++; + } + } + /* figure C.3 */ + for(k = 0; k < lastk; k++) { + i = huffval[k]; + ehufco[i] = huffcode[k]; + ehufsi[i] = huffsize[k]; + } + free(huffcode); + free(huffsize); +} + +static void +writejfif(Biobuf *fd, int dx, int dy) +{ + if(dx > 0xffff || dy > 0xffff) + sysfatal("writejfif: dx or dy too big"); + Bputs(fd, 0xffe0); + Bputs(fd, 0x0010); + Bputc(fd, 0x4a); + Bputc(fd, 0x46); + Bputc(fd, 0x49); + Bputc(fd, 0x46); + Bputc(fd, 0x00); + Bputs(fd, 0x0102); + Bputc(fd, 0x01); + Bputs(fd, dx); + Bputs(fd, dy); + Bputc(fd, 0x00); + Bputc(fd, 0x00); +} + +static void +writecomment(Biobuf *fd, char *com) +{ + int n; + + if(com != nil && com[0] != '\0') { + n = min(strlen(com)+2, 0xffff); + Bputs(fd, 0xfffe); + Bputs(fd, n); + Bwrite(fd, com, n-2); + } +} + +static void +writequant(Biobuf *fd, int tq, int sflag) +{ + int i, *p, *q; + + if(tq != 0x0 && tq != 0x1) + sysfatal("writequant: invalid Tq"); + q = qt[tq]; + Bputs(fd, 0xffdb); + Bputs(fd, 0x0043); + Bputc(fd, (0x0<<4)|tq); + p = sflag? zigzag: invzigzag; + for(i = 0; i < 64; i++) + Bputc(fd, q[p[i]]); +} + +static void +writehuffman(Biobuf *fd, int tc, int th) +{ + int i, n, m, *b, *hv; + + if((tc != 0x0 && tc != 0x1) || (th != 0x0 && th != 0x1)) + sysfatal("writehuffman: invalid Tc or Th"); + n = 0x0013; + if(tc == 0x0) { + b = dcbits[th]; + hv = dchuffval[th]; + m = nelem(dchuffval[th]); + } else { + b = acbits[th]; + hv = achuffval[th]; + m = nelem(achuffval[th]); + } + Bputs(fd, 0xffc4); + Bputs(fd, n+m); + Bputc(fd, (tc<<4)|th); + for(i = 0; i < 16; i++) + Bputc(fd, b[i]); + for(i = 0; i < m; i++) + Bputc(fd, hv[i]); +} + +static void +writeframe(Biobuf *fd, int y, int x, int kflag) +{ + int n, nf; + + nf = kflag? 0x01: 0x03; + n = 0x0008 + 0x0003*nf; + + Bputs(fd, 0xffc0); + Bputs(fd, n); + Bputc(fd, 0x08); + Bputs(fd, y); + Bputs(fd, x); + Bputc(fd, nf); + + /* Y component */ + Bputc(fd, 0x00); + Bputc(fd, (0x1<<4)|0x1); + Bputc(fd, 0x00); + + if(!kflag) { + /* Cb component */ + Bputc(fd, 0x01); + Bputc(fd, (0x1<<4)|0x1); + Bputc(fd, 0x01); + + /* Cr component */ + Bputc(fd, 0x02); + Bputc(fd, (0x1<<4)|0x1); + Bputc(fd, 0x01); + } +} + +static void +writescan(Biobuf *fd, int kflag) +{ + int n, ns; + + ns = kflag? 0x01: 0x03; + n = 0x0006 + 0x0002*ns; + + Bputs(fd, 0xffda); + Bputs(fd, n); + Bputc(fd, ns); + + /* Y component */ + Bputc(fd, 0x00); + Bputc(fd, (0x0<<4)|0x0); + + if(!kflag) { + /* Cb component */ + Bputc(fd, 0x01); + Bputc(fd, (0x1<<4)|0x1); + + /* Cr component */ + Bputc(fd, 0x02); + Bputc(fd, (0x1<<4)|0x1); + } + + Bputc(fd, 0x00); + Bputc(fd, 0x3f); + Bputc(fd, (0x0<<4)|0x0); +} + +static void +writeheader(Biobuf *fd, int dx, int dy, char *s, int kflag, int sflag) +{ + int i; + + dx = min(dx, 0xffff); + dy = min(dy, 0xffff); + + Bputs(fd, 0xffd8); + writejfif(fd, dx, dy); + writecomment(fd, s); + writequant(fd, 0, sflag); + if(!kflag) + writequant(fd, 1, sflag); + writeframe(fd, dy, dx, kflag); + for(i = 0; i < 2; i++) { + writehuffman(fd, i, 0); + if(!kflag) + writehuffman(fd, i, 1); + } + writescan(fd, kflag); +} + +static void +writetrailer(Biobuf *fd) +{ + Bputs(fd, 0xffd9); +} + +static char * +writedata(Biobuf *fd, Image *i, Memimage *m, int kflag, int sflag) +{ + char *err; + unsigned char *data; + int ndata, depth; + uint32_t chan; + Rectangle r; + + if(m != nil) { + r = m->r; + depth = m->depth; + chan = m->chan; + } else { + r = i->r; + depth = i->depth; + chan = i->chan; + } + + /* + * potentially one extra byte on each + * end of each scan line + */ + ndata = Dy(r) * (2 + Dx(r)*depth/8); + if((data = malloc(ndata)) == nil) + return "WriteJPG: malloc failed"; + if(m != nil) + ndata = unloadmemimage(m, r, data, ndata); + else + ndata = unloadimage(i, r, data, ndata); + if(ndata < 0) { + if((err = malloc(ERRMAX)) == nil) { + free(data); + return "WriteJPG: malloc failed"; + } + snprint(err, ERRMAX, "WriteJPG: %r"); + } else + err = encode(fd, r, data, chan, ndata, kflag, sflag); + free(data); + return err; +} + +static char * +writejpg0(Biobuf *fd, Image *image, Memimage *memimage, + Rectangle r, uint32_t chan, char *s, int kflag, int sflag) +{ + int i; + char *err; + + switch(chan) { + case GREY1: + case GREY2: + case GREY4: + case GREY8: + kflag = 1; + break; + case RGB24: + break; + default: + return "WriteJPG: can't handle channel type"; + } + for(i = 0; i < 2; i++) { + memset(ehufcod[i], 0, sizeof ehufcod[i]); + memset(ehufsid[i], 0, sizeof ehufsid[i]); + memset(ehufcoa[i], 0, sizeof ehufcoa[i]); + memset(ehufsia[i], 0, sizeof ehufsia[i]); + makehuf(ehufcod[i], ehufsid[i], dcbits[i], + dchuffval[i], nelem(dchuffval[i])); + makehuf(ehufcoa[i], ehufsia[i], acbits[i], + achuffval[i], nelem(achuffval[i])); + } + writeheader(fd, Dx(r), Dy(r), s, kflag, sflag); + err = writedata(fd, image, memimage, kflag, sflag); + writetrailer(fd); + return err; +} + +char * +writejpg(Biobuf *fd, Image *i, char *s, int kflag, int sflag) +{ + return writejpg0(fd, i, nil, i->r, i->chan, s, kflag, sflag); +} + +char * +memwritejpg(Biobuf *fd, Memimage *m, char *s, int kflag, int sflag) +{ + return writejpg0(fd, nil, m, m->r, m->chan, s, kflag, sflag); +} diff --git a/sys/src/cmd/pict/writepng.c b/sys/src/cmd/pict/writepng.c new file mode 100644 index 0000000..ad03601 --- /dev/null +++ b/sys/src/cmd/pict/writepng.c @@ -0,0 +1,267 @@ +/* + * See PNG 1.2 spec, also RFC 2083. + */ +#include +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum +{ + IDATSIZE = 20000, + FilterNone = 0, +}; + +typedef struct ZlibR ZlibR; +typedef struct ZlibW ZlibW; + +struct ZlibR +{ + unsigned char *data; + int width; + int dx; + int dy; + int x; + int y; + int pixwid; +}; + +struct ZlibW +{ + Biobuf *io; + unsigned char *buf; + unsigned char *b; + unsigned char *e; +}; + +static uint32_t *crctab; +static unsigned char PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'}; + +static void +put4(unsigned char *a, uint32_t v) +{ + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +static void +chunk(Biobuf *bo, char *type, unsigned char *d, int n) +{ + unsigned char buf[4]; + uint32_t 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) +{ + int a, i, pixels, pixwid; + unsigned char *b, *e, *img; + ZlibR *z; + + z = va; + pixwid = z->pixwid; + b = buf; + e = b+n; + while(b+pixwid < e){ /* one less for filter alg byte */ + if(z->y >= z->dy) + break; + if(z->x == 0) + *b++ = FilterNone; + pixels = (e-b)/pixwid; + if(pixels > z->dx - z->x) + pixels = z->dx - z->x; + img = z->data + z->width*z->y + pixwid*z->x; + memmove(b, img, pixwid*pixels); + if(pixwid == 4){ + /* + * Convert to non-premultiplied alpha. + */ + for(i=0; i= a) + b[0] = a; + b[0] = (b[0]*255)/a; + if(b[1] >= a) + b[1] = a; + b[1] = (b[1]*255)/a; + if(b[2] >= a) + b[2] = a; + b[2] = (b[2]*255)/a; + } + } + }else + b += pixwid*pixels; + + z->x += pixels; + if(z->x >= z->dx){ + z->x = 0; + z->y++; + } + } + return b - (unsigned char*)buf; +} + +static void +IDAT(ZlibW *z) +{ + chunk(z->io, "IDAT", z->buf, z->b - z->buf); + z->b = z->buf; +} + +static int +zwrite(void *va, void *buf, int n) +{ + int m; + unsigned char *b, *e; + ZlibW *z; + + z = va; + b = buf; + e = b+n; + + while(b < e){ + 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* +memRGBA(Memimage *i) +{ + Memimage *ni; + char buf[32]; + uint32_t dst; + + /* + * [A]BGR because we want R,G,B,[A] in big-endian order. Sigh. + */ + chantostr(buf, i->chan); + if(strchr(buf, 'a')) + dst = ABGR32; + else + dst = BGR24; + + if(i->chan == dst) + return i; + + ni = allocmemimage(i->r, dst); + if(ni == nil) + return ni; + memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); + return ni; +} + +char* +memwritepng(Biobuf *io, Memimage *m, ImageInfo *II) +{ + int err, n; + unsigned char buf[200], *h; + uint32_t vgamma; + Tm *tm; + Memimage *rgb; + ZlibR zr; + ZlibW zw; + + crctab = mkcrctab(0xedb88320); + if(crctab == nil) + sysfatal("mkcrctab error"); + deflateinit(); + + rgb = memRGBA(m); + if(rgb == nil) + return "allocmemimage nil"; + + Bwrite(io, PNGmagic, sizeof PNGmagic); + + /* IHDR chunk */ + h = buf; + put4(h, Dx(m->r)); h += 4; + put4(h, Dy(m->r)); h += 4; + *h++ = 8; /* 8 bits per channel */ + if(rgb->chan == BGR24) + *h++ = 2; /* RGB */ + else + *h++ = 6; /* RGBA */ + *h++ = 0; /* compression - deflate */ + *h++ = 0; /* filter - none */ + *h++ = 0; /* interlace - none */ + chunk(io, "IHDR", buf, h-buf); + + /* time - using now is suspect */ + 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(io, "tIME", buf, h-buf); + + /* gamma */ + if(II->fields_set & II_GAMMA){ + vgamma = II->gamma*100000; + put4(buf, vgamma); + chunk(io, "gAMA", buf, 4); + } + + /* comment */ + 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(io, "tEXt", buf, n+strlen((char*)buf+n)); + } + + /* image data */ + zr.dx = Dx(m->r); + zr.dy = Dy(m->r); + zr.width = rgb->width * sizeof(uint32_t); + zr.data = rgb->data->bdata; + zr.x = 0; + zr.y = 0; + zr.pixwid = chantodepth(rgb->chan)/8; + zw.io = io; + zw.buf = malloc(IDATSIZE); + if(zw.buf == nil) + sysfatal("malloc: %r"); + zw.b = zw.buf; + zw.e = zw.b + IDATSIZE; + if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0) + sysfatal("deflatezlib %s", flateerr(err)); + if(zw.b > zw.buf) + IDAT(&zw); + free(zw.buf); + chunk(io, "IEND", nil, 0); + if(m != rgb) + freememimage(rgb); + return nil; +} diff --git a/sys/src/cmd/pict/writeppm.c b/sys/src/cmd/pict/writeppm.c new file mode 100644 index 0000000..34b74fe --- /dev/null +++ b/sys/src/cmd/pict/writeppm.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include + +#define MAXLINE 70 + +/* imported from libdraw/arith.c to permit an extern log2 function */ +static int log2[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 +}; + +static int bitc = 0; +static int nbit = 0; + +static +void +Bputbit(Biobufhdr *b, int c) +{ + if(c >= 0x0){ + bitc = (bitc << 1) | (c & 0x1); + nbit++; + }else if(nbit > 0){ + for(; nbit < 8; nbit++) + bitc <<= 1; + } + if(nbit == 8){ + Bputc(b, bitc); + bitc = nbit = 0; + } +} + +/* + * Write data + */ +static +char* +writedata(Biobuf *fd, Image *image, Memimage *memimage, int rflag) +{ + char *err; + unsigned char *data; + int i, x, y, ndata, depth, col, pix, xmask, pmask; + uint32_t 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, "WritePPM: %r"); + free(data); + return err; + } + + /* Encode and emit the data */ + col = 0; + switch(chan){ + case GREY1: + case GREY2: + case GREY4: + pmask = (1<>log2[depth]; + for(y=r.min.y; y>depth*((xmask-x)&xmask))&pmask; + if(((x+1)&xmask) == 0) + i++; + if(chan == GREY1){ + pix ^= 1; + if(rflag){ + Bputbit(fd, pix); + continue; + } + } else { + if(rflag){ + Bputc(fd, pix); + continue; + } + } + col += Bprint(fd, "%d", pix); + if(col >= MAXLINE-(2+1)){ + Bprint(fd, "\n"); + col = 0; + }else if(y < r.max.y-1 || x < r.max.x-1) + col += Bprint(fd, " "); + } + if(rflag) + Bputbit(fd, -1); + } + break; + case GREY8: + for(i=0; i= MAXLINE-(4+1)){ + Bprint(fd, "\n"); + col = 0; + }else if(i < ndata-1) + col += Bprint(fd, " "); + } + break; + case RGB24: + for(i=0; i= MAXLINE-(4+4+4+1)){ + Bprint(fd, "\n"); + col = 0; + }else if(i < ndata-3) + 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, int rflag) +{ + char *err; + + switch(chan){ + case GREY1: + Bprint(fd, "%s\n", rflag? "P4": "P1"); + break; + case GREY2: + case GREY4: + case GREY8: + Bprint(fd, "%s\n", rflag? "P5": "P2"); + break; + case RGB24: + Bprint(fd, "%s\n", rflag? "P6": "P3"); + 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, rflag); + + if(!rflag) + Bprint(fd, "\n"); + Bflush(fd); + return err; +} + +char* +writeppm(Biobuf *fd, Image *image, char *comment, int rflag) +{ + return writeppm0(fd, image, nil, image->r, image->chan, comment, rflag); +} + +char* +memwriteppm(Biobuf *fd, Memimage *memimage, char *comment, int rflag) +{ + return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment, rflag); +} diff --git a/sys/src/cmd/pict/writerawimage.c b/sys/src/cmd/pict/writerawimage.c new file mode 100644 index 0000000..0ed4028 --- /dev/null +++ b/sys/src/cmd/pict/writerawimage.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include "imagefile.h" + +/* + * Hacked version for writing from Rawimage to file. + * Assumes 8 bits per component. + */ + +#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ +#define NHASH (1<<(HSHIFT*NMATCH)) +#define HMASK (NHASH-1) +#define hupdate(h, c) ((((h)<r; + switch(i->chandesc){ + default: + werrstr("can't handle chandesc %d", i->chandesc); + return -1; + case CY: + bpl = Dx(r); + desc = GREY8; + break; + case CYA16: + bpl = 2*Dx(r); + desc = CHAN2(CGrey, 8, CAlpha, 8); + break; + case CRGBV: + bpl = Dx(r); + desc = CMAP8; + break; + case CRGBVA16: + bpl = 2*Dx(r); + desc = CHAN2(CMap, 8, CAlpha, 8); + break; + case CRGB24: + bpl = 3*Dx(r); + desc = RGB24; + break; + case CRGBA32: + bpl = 4*Dx(r); + desc = RGBA32; + break; + } + ncblock = _compblocksize(r, bpl/Dx(r)); + outbuf = malloc(ncblock); + hash = malloc(NHASH*sizeof(Hlist)); + chain = malloc(NMEM*sizeof(Hlist)); + if(outbuf == 0 || hash == 0 || chain == 0){ + ErrOut: + free(outbuf); + free(hash); + free(chain); + return -1; + } + n = Dy(r)*bpl; + data = i->chans[0]; + sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ", + chantostr(buf, desc), r.min.x, r.min.y, r.max.x, r.max.y); + if(write(fd, hdr, 11+5*12) != 11+5*12){ + werrstr("i/o error writing header"); + goto ErrOut; + } + edata = data+n; + eout = outbuf+ncblock; + line = data; + r.max.y = r.min.y; + while(line != edata){ + memset(hash, 0, NHASH*sizeof(Hlist)); + memset(chain, 0, NMEM*sizeof(Hlist)); + cp = chain; + h = 0; + outp = outbuf; + for(n = 0; n != NMATCH; n++) + h = hupdate(h, line[n]); + loutp = outbuf; + while(line != edata){ + ndump = 0; + eline = line+bpl; + for(p = line; p != eline; ){ + if(eline-p < NRUN) + es = eline; + else + es = p+NRUN; + q = 0; + runlen = 0; + for(hp = hash[h].next; hp; hp = hp->next){ + s = p + runlen; + if(s >= es) + continue; + t = hp->s + runlen; + for(; s >= p; s--) + if(*s != *t--) + goto matchloop; + t += runlen+2; + s += runlen+2; + for(; s < es; s++) + if(*s != *t++) + break; + n = s-p; + if(n > runlen){ + runlen = n; + q = hp->s; + if(n == NRUN) + break; + } + matchloop: ; + } + if(runlen < NMATCH){ + if(ndump == NDUMP){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + dumpbuf[ndump++] = *p; + runlen = 1; + } + else{ + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + offs = p-q-1; + if(eout-outp < 2) + goto Bfull; + *outp++ = ((runlen-NMATCH)<<2) + (offs>>8); + *outp++ = offs&255; + } + for(q = p+runlen; p != q; p++){ + if(cp->prev) + cp->prev->next = 0; + cp->next = hash[h].next; + cp->prev = &hash[h]; + if(cp->next) + cp->next->prev = cp; + cp->prev->next = cp; + cp->s = p; + if(++cp == &chain[NMEM]) + cp = chain; + if(edata-p > NMATCH) + h = hupdate(h, p[NMATCH]); + } + } + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + } + line = eline; + loutp = outp; + r.max.y++; + } + Bfull: + if(loutp == outbuf){ + werrstr("compressor out of sync"); + goto ErrOut; + } + n = loutp-outbuf; + sprint(hdr, "%11d %11ld ", r.max.y, n); + write(fd, hdr, 2*12); + write(fd, outbuf, n); + r.min.y = r.max.y; + } + free(outbuf); + free(hash); + free(chain); + return 0; +} diff --git a/sys/src/cmd/pict/writetif.c b/sys/src/cmd/pict/writetif.c new file mode 100644 index 0000000..c413cbb --- /dev/null +++ b/sys/src/cmd/pict/writetif.c @@ -0,0 +1,1333 @@ +/* +* code/documentation: +* http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +* http://www.fileformat.info/format/tiff/egff.htm +* http://www.fileformat.info/mirror/egff/ch09_05.htm +* http://www.itu.int/rec/T-REC-T.4-199904-S/en +* http://www.itu.int/rec/T-REC-T.6-198811-I/en +* +* many thanks to paul bourke for a simple description of tiff: +* http://paulbourke.net/dataformats/tiff/ +* +* copy-pasted fax codes and copy-pasted lzw encoding +* hash table implementation: +* http://www.remotesensing.org/libtiff/ +*/ +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum { + Tbyte = 0x0001, + Tascii = 0x0002, + Tshort = 0x0003, + Tlong = 0x0004, + Trational = 0x0005, + Tnocomp = 0x0001, + Thuffman = 0x0002, + Tt4enc = 0x0003, + Tt6enc = 0x0004, + Tlzw = 0x0005, + Tpackbits = 0x8005 +}; + +enum { + Twidth, + Tlength, + Tbits, + Tcomp, + Tphoto, + Tfill, + Tdesc, + Tstrips, + Tsamples, + Trows, + Tcounts, + Txres, + Tyres, + T4opt, + Tresunit, + Tpredictor, + Tcolor +}; + +enum { + Kpar = 2, + Nfaxtab = 105 +}; + +enum { + Clrcode = 256, + Eoicode = 257, + Tabsz = 1<<12, + Hshift = 13 - 8, + Hsize = 9001L +}; + +typedef struct Tab Tab; +typedef struct Fax Fax; +typedef struct Hash Hash; +typedef struct Lzw Lzw; +typedef struct Pkb Pkb; +typedef struct Fld Fld; +typedef struct Tif Tif; + +struct Tab { + int len; + int code; + int run; +}; + +struct Fax { + int st; + Tab *tab[2]; + int byte; + int nbyte; + uint32_t *l1; + uint32_t *l2; + unsigned char *data; + uint32_t ndata; + uint32_t n; +}; + +struct Hash { + long hash; + unsigned short code; +}; + +struct Lzw { + Hash hash[Hsize]; + int ntab; + int len; + int byte; + int nbyte; + unsigned char *data; + uint32_t ndata; + uint32_t n; +}; + +struct Pkb { + unsigned char *data; + uint32_t ndata; + uint32_t n; +}; + +struct Fld { + unsigned short tag; + unsigned short typ; +}; + +struct Tif { + uint32_t dx; + uint32_t dy; + unsigned short depth[3]; + unsigned short comp; + uint32_t opt; + char *(*compress)(Tif *); + unsigned short photo; + char *desc; + uint32_t *strips; + uint32_t nstrips; + unsigned short samples; + uint32_t rows; + uint32_t *counts; + unsigned short *color; + uint32_t ncolor; + unsigned char *data; + uint32_t ndata; + unsigned short nfld; + int bpl; +}; + +static Fld flds[] = { + [Twidth] {0x0100, Tlong}, + [Tlength] {0x0101, Tlong}, + [Tbits] {0x0102, Tshort}, + [Tcomp] {0x0103, Tshort}, + [Tphoto] {0x0106, Tshort}, + [Tfill] {0x010a, Tshort}, + [Tdesc] {0x010e, Tascii}, + [Tstrips] {0x0111, Tlong}, + [Tsamples] {0x0115, Tshort}, + [Trows] {0x0116, Tlong}, + [Tcounts] {0x0117, Tlong}, + [Txres] {0x011a, Trational}, + [Tyres] {0x011b, Trational}, + [T4opt] {0x0124, Tlong}, + [Tresunit] {0x0128, Tshort}, + [Tpredictor] {0x013d, Tshort}, + [Tcolor] {0x0140, Tshort} +}; + +static Tab faxwhite[Nfaxtab] = { + {8, 0x35, 0}, /* 0011 0101 */ + {6, 0x7, 1}, /* 0001 11 */ + {4, 0x7, 2}, /* 0111 */ + {4, 0x8, 3}, /* 1000 */ + {4, 0xb, 4}, /* 1011 */ + {4, 0xc, 5}, /* 1100 */ + {4, 0xe, 6}, /* 1110 */ + {4, 0xf, 7}, /* 1111 */ + {5, 0x13, 8}, /* 1001 1 */ + {5, 0x14, 9}, /* 1010 0 */ + {5, 0x7, 10}, /* 0011 1 */ + {5, 0x8, 11}, /* 0100 0 */ + {6, 0x8, 12}, /* 0010 00 */ + {6, 0x3, 13}, /* 0000 11 */ + {6, 0x34, 14}, /* 1101 00 */ + {6, 0x35, 15}, /* 1101 01 */ + {6, 0x2a, 16}, /* 1010 10 */ + {6, 0x2b, 17}, /* 1010 11 */ + {7, 0x27, 18}, /* 0100 111 */ + {7, 0xc, 19}, /* 0001 100 */ + {7, 0x8, 20}, /* 0001 000 */ + {7, 0x17, 21}, /* 0010 111 */ + {7, 0x3, 22}, /* 0000 011 */ + {7, 0x4, 23}, /* 0000 100 */ + {7, 0x28, 24}, /* 0101 000 */ + {7, 0x2b, 25}, /* 0101 011 */ + {7, 0x13, 26}, /* 0010 011 */ + {7, 0x24, 27}, /* 0100 100 */ + {7, 0x18, 28}, /* 0011 000 */ + {8, 0x2, 29}, /* 0000 0010 */ + {8, 0x3, 30}, /* 0000 0011 */ + {8, 0x1a, 31}, /* 0001 1010 */ + {8, 0x1b, 32}, /* 0001 1011 */ + {8, 0x12, 33}, /* 0001 0010 */ + {8, 0x13, 34}, /* 0001 0011 */ + {8, 0x14, 35}, /* 0001 0100 */ + {8, 0x15, 36}, /* 0001 0101 */ + {8, 0x16, 37}, /* 0001 0110 */ + {8, 0x17, 38}, /* 0001 0111 */ + {8, 0x28, 39}, /* 0010 1000 */ + {8, 0x29, 40}, /* 0010 1001 */ + {8, 0x2a, 41}, /* 0010 1010 */ + {8, 0x2b, 42}, /* 0010 1011 */ + {8, 0x2c, 43}, /* 0010 1100 */ + {8, 0x2d, 44}, /* 0010 1101 */ + {8, 0x4, 45}, /* 0000 0100 */ + {8, 0x5, 46}, /* 0000 0101 */ + {8, 0xa, 47}, /* 0000 1010 */ + {8, 0xb, 48}, /* 0000 1011 */ + {8, 0x52, 49}, /* 0101 0010 */ + {8, 0x53, 50}, /* 0101 0011 */ + {8, 0x54, 51}, /* 0101 0100 */ + {8, 0x55, 52}, /* 0101 0101 */ + {8, 0x24, 53}, /* 0010 0100 */ + {8, 0x25, 54}, /* 0010 0101 */ + {8, 0x58, 55}, /* 0101 1000 */ + {8, 0x59, 56}, /* 0101 1001 */ + {8, 0x5a, 57}, /* 0101 1010 */ + {8, 0x5b, 58}, /* 0101 1011 */ + {8, 0x4a, 59}, /* 0100 1010 */ + {8, 0x4b, 60}, /* 0100 1011 */ + {8, 0x32, 61}, /* 0011 0010 */ + {8, 0x33, 62}, /* 0011 0011 */ + {8, 0x34, 63}, /* 0011 0100 */ + {5, 0x1b, 64}, /* 1101 1 */ + {5, 0x12, 128}, /* 1001 0 */ + {6, 0x17, 192}, /* 0101 11 */ + {7, 0x37, 256}, /* 0110 111 */ + {8, 0x36, 320}, /* 0011 0110 */ + {8, 0x37, 384}, /* 0011 0111 */ + {8, 0x64, 448}, /* 0110 0100 */ + {8, 0x65, 512}, /* 0110 0101 */ + {8, 0x68, 576}, /* 0110 1000 */ + {8, 0x67, 640}, /* 0110 0111 */ + {9, 0xcc, 704}, /* 0110 0110 0 */ + {9, 0xcd, 768}, /* 0110 0110 1 */ + {9, 0xd2, 832}, /* 0110 1001 0 */ + {9, 0xd3, 896}, /* 0110 1001 1 */ + {9, 0xd4, 960}, /* 0110 1010 0 */ + {9, 0xd5, 1024}, /* 0110 1010 1 */ + {9, 0xd6, 1088}, /* 0110 1011 0 */ + {9, 0xd7, 1152}, /* 0110 1011 1 */ + {9, 0xd8, 1216}, /* 0110 1100 0 */ + {9, 0xd9, 1280}, /* 0110 1100 1 */ + {9, 0xda, 1344}, /* 0110 1101 0 */ + {9, 0xdb, 1408}, /* 0110 1101 1 */ + {9, 0x98, 1472}, /* 0100 1100 0 */ + {9, 0x99, 1536}, /* 0100 1100 1 */ + {9, 0x9a, 1600}, /* 0100 1101 0 */ + {6, 0x18, 1664}, /* 0110 00 */ + {9, 0x9b, 1728}, /* 0100 1101 1 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560}, /* 0000 0001 1111 */ + {12, 0x1, -1} /* 0000 0000 0001 */ +}; + +static Tab faxblack[Nfaxtab] = { + {10, 0x37, 0}, /* 0000 1101 11 */ + {3, 0x2, 1}, /* 010 */ + {2, 0x3, 2}, /* 11 */ + {2, 0x2, 3}, /* 10 */ + {3, 0x3, 4}, /* 011 */ + {4, 0x3, 5}, /* 0011 */ + {4, 0x2, 6}, /* 0010 */ + {5, 0x3, 7}, /* 0001 1 */ + {6, 0x5, 8}, /* 0001 01 */ + {6, 0x4, 9}, /* 0001 00 */ + {7, 0x4, 10}, /* 0000 100 */ + {7, 0x5, 11}, /* 0000 101 */ + {7, 0x7, 12}, /* 0000 111 */ + {8, 0x4, 13}, /* 0000 0100 */ + {8, 0x7, 14}, /* 0000 0111 */ + {9, 0x18, 15}, /* 0000 1100 0 */ + {10, 0x17, 16}, /* 0000 0101 11 */ + {10, 0x18, 17}, /* 0000 0110 00 */ + {10, 0x8, 18}, /* 0000 0010 00 */ + {11, 0x67, 19}, /* 0000 1100 111 */ + {11, 0x68, 20}, /* 0000 1101 000 */ + {11, 0x6c, 21}, /* 0000 1101 100 */ + {11, 0x37, 22}, /* 0000 0110 111 */ + {11, 0x28, 23}, /* 0000 0101 000 */ + {11, 0x17, 24}, /* 0000 0010 111 */ + {11, 0x18, 25}, /* 0000 0011 000 */ + {12, 0xca, 26}, /* 0000 1100 1010 */ + {12, 0xcb, 27}, /* 0000 1100 1011 */ + {12, 0xcc, 28}, /* 0000 1100 1100 */ + {12, 0xcd, 29}, /* 0000 1100 1101 */ + {12, 0x68, 30}, /* 0000 0110 1000 */ + {12, 0x69, 31}, /* 0000 0110 1001 */ + {12, 0x6a, 32}, /* 0000 0110 1010 */ + {12, 0x6b, 33}, /* 0000 0110 1011 */ + {12, 0xd2, 34}, /* 0000 1101 0010 */ + {12, 0xd3, 35}, /* 0000 1101 0011 */ + {12, 0xd4, 36}, /* 0000 1101 0100 */ + {12, 0xd5, 37}, /* 0000 1101 0101 */ + {12, 0xd6, 38}, /* 0000 1101 0110 */ + {12, 0xd7, 39}, /* 0000 1101 0111 */ + {12, 0x6c, 40}, /* 0000 0110 1100 */ + {12, 0x6d, 41}, /* 0000 0110 1101 */ + {12, 0xda, 42}, /* 0000 1101 1010 */ + {12, 0xdb, 43}, /* 0000 1101 1011 */ + {12, 0x54, 44}, /* 0000 0101 0100 */ + {12, 0x55, 45}, /* 0000 0101 0101 */ + {12, 0x56, 46}, /* 0000 0101 0110 */ + {12, 0x57, 47}, /* 0000 0101 0111 */ + {12, 0x64, 48}, /* 0000 0110 0100 */ + {12, 0x65, 49}, /* 0000 0110 0101 */ + {12, 0x52, 50}, /* 0000 0101 0010 */ + {12, 0x53, 51}, /* 0000 0101 0011 */ + {12, 0x24, 52}, /* 0000 0010 0100 */ + {12, 0x37, 53}, /* 0000 0011 0111 */ + {12, 0x38, 54}, /* 0000 0011 1000 */ + {12, 0x27, 55}, /* 0000 0010 0111 */ + {12, 0x28, 56}, /* 0000 0010 1000 */ + {12, 0x58, 57}, /* 0000 0101 1000 */ + {12, 0x59, 58}, /* 0000 0101 1001 */ + {12, 0x2b, 59}, /* 0000 0010 1011 */ + {12, 0x2c, 60}, /* 0000 0010 1100 */ + {12, 0x5a, 61}, /* 0000 0101 1010 */ + {12, 0x66, 62}, /* 0000 0110 0110 */ + {12, 0x67, 63}, /* 0000 0110 0111 */ + {10, 0xf, 64}, /* 0000 0011 11 */ + {12, 0xc8, 128}, /* 0000 1100 1000 */ + {12, 0xc9, 192}, /* 0000 1100 1001 */ + {12, 0x5b, 256}, /* 0000 0101 1011 */ + {12, 0x33, 320}, /* 0000 0011 0011 */ + {12, 0x34, 384}, /* 0000 0011 0100 */ + {12, 0x35, 448}, /* 0000 0011 0101 */ + {13, 0x6c, 512}, /* 0000 0011 0110 0 */ + {13, 0x6d, 576}, /* 0000 0011 0110 1 */ + {13, 0x4a, 640}, /* 0000 0010 0101 0 */ + {13, 0x4b, 704}, /* 0000 0010 0101 1 */ + {13, 0x4c, 768}, /* 0000 0010 0110 0 */ + {13, 0x4d, 832}, /* 0000 0010 0110 1 */ + {13, 0x72, 896}, /* 0000 0011 1001 0 */ + {13, 0x73, 960}, /* 0000 0011 1001 1 */ + {13, 0x74, 1024}, /* 0000 0011 1010 0 */ + {13, 0x75, 1088}, /* 0000 0011 1010 1 */ + {13, 0x76, 1152}, /* 0000 0011 1011 0 */ + {13, 0x77, 1216}, /* 0000 0011 1011 1 */ + {13, 0x52, 1280}, /* 0000 0010 1001 0 */ + {13, 0x53, 1344}, /* 0000 0010 1001 1 */ + {13, 0x54, 1408}, /* 0000 0010 1010 0 */ + {13, 0x55, 1472}, /* 0000 0010 1010 1 */ + {13, 0x5a, 1536}, /* 0000 0010 1101 0 */ + {13, 0x5b, 1600}, /* 0000 0010 1101 1 */ + {13, 0x64, 1664}, /* 0000 0011 0010 0 */ + {13, 0x65, 1728}, /* 0000 0011 0010 1 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560}, /* 0000 0001 1111 */ + {12, 0x1, -1} /* 0000 0000 0001 */ +}; + +static Tab faxcodes[] = { + {4, 0x1, 0}, /* 0001 */ + {3, 0x1, 0}, /* 001 */ + {1, 0x1, 0}, /* 1 */ + {3, 0x2, 0}, /* 010 */ + {6, 0x2, 0}, /* 0000 10 */ + {7, 0x2, 0}, /* 0000 010 */ + {3, 0x3, 0}, /* 011 */ + {6, 0x3, 0}, /* 0000 11 */ + {7, 0x3, 0} /* 0000 011 */ +}; + +static int typesizes[] = {0, 1, 1, 2, 4, 8}; +static char memerr[] = "WriteTIF: malloc failed"; + +static int +put1(Biobuf *b, unsigned char c) +{ + return Bputc(b, c); +} + +static int +put2(Biobuf *b, uint s) +{ + if(put1(b, s>>8) < 0) + return -1; + return put1(b, s); +} + +static int +put4(Biobuf *b, uint32_t l) +{ + if(put2(b, l>>16) < 0) + return -1; + return put2(b, l); +} + +static char * +nocomp(Tif * _) +{ + return nil; +} + +static char * +faxputbyte(Fax *f) +{ + if(f->n >= f->ndata) { + f->ndata *= 2; + f->data = realloc(f->data, + f->ndata*sizeof *f->data); + if(f->data == nil) + return memerr; + } + f->data[f->n++] = f->byte; + f->byte = f->nbyte = 0; + return nil; +} + +static char * +faxputbit(Fax *f, int bit) +{ + f->byte = (f->byte << 1) | bit; + f->nbyte++; + return f->nbyte >= 8? faxputbyte(f): nil; +} + +static char * +faxbytealign(Fax *f) +{ + char *err; + + err = nil; + if(f->nbyte != 0) { + f->byte <<= 8 - f->nbyte; + err = faxputbyte(f); + } + return err; +} + +static char * +faxputcode(Fax *f, Tab *tab) +{ + int i, bit; + char *err; + + for(i = tab->len-1; i >= 0; i--) { + bit = (tab->code >> i) & 0x1; + if((err = faxputbit(f, bit)) != nil) + return err; + } + return nil; +} + +static int +faxgettab(int run) +{ + if(run >= 0) { + if(run <= 64) + return run; + if(run <= 2560) + return 64 + run/64 - 1; + } + return Nfaxtab - 1; +} + +static char * +faxputrun(Fax *f, long run) +{ + char *err; + Tab *tab, *p; + + tab = f->tab[f->st]; + p = &tab[faxgettab(2560)]; + while(run >= 2624) { + if((err = faxputcode(f, p)) != nil) + return err; + run -= 2560; + } + if(run >= 64) { + p = &tab[faxgettab(run)]; + if((err = faxputcode(f, p)) != nil) + return err; + run -= p->run; + } + p = &tab[faxgettab(run)]; + err = faxputcode(f, p); + f->st ^= 1; + return err; +} + +static char * +faxputeol(Fax *f) +{ + return faxputcode(f, &f->tab[0][faxgettab(-1)]); +} + +static char * +fax1d(Fax *f, uint32_t dx) +{ + uint32_t i; + long run; + char *err; + + f->st = 0; + run = f->l2[0]; + for(i = 0;;) { + if((err = faxputrun(f, run)) != nil) + return err; + if(f->l2[i++] >= dx) + break; + run = f->l2[i] - f->l2[i-1]; + } + memmove(f->l1, f->l2, i*sizeof *f->l1); + return nil; +} + +static char * +fax2d(Fax *f, uint32_t dx) +{ + int j, v; + uint32_t i; + long a0, a1, a2, b1, b2; + char *err; + Tab *tab, *p; + + f->st = 0; + a0 = a1 = -1; + tab = faxcodes; + for(i = 0, err = nil; err == nil;) { + while(a1 <= a0) + a1 = f->l2[i++]; + for(j = 0;; j++) { + b1 = f->l1[j]; + if(b1 > a0 && f->st == j%2) + break; + if(b1 >= dx) + break; + } + if((b2 = b1) < dx) + b2 = f->l1[j+1]; + if(b2 < a1) { + /* pass */ + p = &tab[0]; + err = faxputcode(f, p); + a0 = b2; + } else if(abs(v = a1-b1) < 3) { + /* vertical */ + p = &tab[2+(v>0?3:0)+abs(v)]; + err = faxputcode(f, p); + f->st ^= 1; + a0 = a1; + } else { + /* horizontal */ + if(a0 < 0) + a0 = 0; + p = &tab[1]; + if((err = faxputcode(f, p)) != nil) + return err; + a2 = a1 < dx? f->l2[i++]: a1; + if((err = faxputrun(f, a1-a0)) != nil) + return err; + err = faxputrun(f, a2-a1); + a0 = a2; + } + if(a0 >= dx) + break; + } + memmove(f->l1, f->l2, i*sizeof *f->l1); + return err; +} + +static char * +faxstrip(Tif *t, Fax *f, unsigned char *data, uint32_t n, uint32_t dx) +{ + int k, s, d1, two; + uint32_t i, j, x; + char *err; + + d1 = t->comp != Tt6enc; + two = 0; + if(t->comp == Tt4enc) { + if((err = faxputeol(f)) != nil) + return err; + if(t->opt && (err = faxputbit(f, 1)) != nil) + return err; + } + for(i = j = x = 0; i < n;) { + s = 7 - x++%8; + k = ((data[i] >> s) & 0x1) ^ 0x1; + if(s == 0) + i++; + if(k != f->st) { + f->l2[j++] = x - 1; + f->st ^= 1; + } + if(x == dx) { + f->l2[j] = dx; + if(d1) { + err = fax1d(f, dx); + if(t->comp == Tt4enc && + t->opt) { + two = Kpar - 1; + d1 = 0; + } + } else { + err = fax2d(f, dx); + if(two > 0 && --two <= 0) + d1 = 1; + } + if(err != nil) + return err; + if(t->comp == Thuffman) + err = faxbytealign(f); + else if(t->comp == Tt4enc && + t->opt) { + if((err = faxputeol(f)) != nil) + return err; + err = faxputbit(f, d1); + } else if(t->comp == Tt4enc) + err = faxputeol(f); + if(err != nil) + return err; + f->st = 0; + if(s != 0) + i++; + x = 0; + j = 0; + } + } + if(t->comp == Tt4enc || t->comp == Tt6enc) { + i = t->comp == Tt4enc? 5: 2; + for(; i > 0; i--) { + if((err = faxputeol(f)) != nil) + return err; + if(t->comp == Tt4enc && t->opt) { + err = faxputbit(f, 1); + if(err != nil) + return err; + } + } + } + return faxbytealign(f); +} + +static char * +fax(Tif *t) +{ + uint32_t i, m, n; + char *err; + unsigned char *data; + Fax f; + + f.ndata = t->ndata; + if((f.data = malloc(f.ndata*sizeof *f.data)) == nil) + return memerr; + f.l1 = mallocz((t->dx+1)*sizeof *f.l1, 1); + f.l2 = mallocz((t->dx+1)*sizeof *f.l2, 1); + if(f.l1 == nil || f.l2 == nil) { + free(f.data); + if(f.l1 != nil) + free(f.l1); + if(f.l2 != nil) + free(f.l2); + return memerr; + } + f.tab[0] = faxwhite; + f.tab[1] = faxblack; + f.n = f.byte = f.nbyte = 0; + for(i = n = 0, data = t->data; i < t->nstrips; i++) { + f.st = 0; + f.l1[0] = t->dx; + m = t->counts[i]; + if((err = faxstrip(t, &f, data, m, t->dx)) != nil) { + if(f.data != nil) + free(f.data); + return err; + } + data += m; + t->counts[i] = f.n - n; + n = f.n; + } + free(t->data); + free(f.l1); + free(f.l2); + t->data = f.data; + t->ndata = f.n; + return nil; +} + +static void +lzwtabinit(Lzw *l) +{ + long i; + Hash *hp; + + l->ntab = Eoicode + 1; + l->len = 9; + hp = &l->hash[Hsize-1]; + i = Hsize - 8; + do { + i -= 8; + hp[-7].hash = -1; + hp[-6].hash = -1; + hp[-5].hash = -1; + hp[-4].hash = -1; + hp[-3].hash = -1; + hp[-2].hash = -1; + hp[-1].hash = -1; + hp[0].hash = -1; + hp -= 8; + } while(i >= 0); + for(i += 8; i > 0; i--, hp--) + hp->hash = -1; +} + +static char * +lzwputbyte(Lzw *l) +{ + if(l->n >= l->ndata) { + l->ndata *= 2; + l->data = realloc(l->data, + l->ndata*sizeof *l->data); + if(l->data == nil) + return memerr; + } + l->data[l->n++] = l->byte; + l->byte = l->nbyte = 0; + return nil; +} + +static char * +lzwbytealign(Lzw *l) +{ + char *err; + + err = nil; + if(l->nbyte != 0) { + l->byte <<= 8 - l->nbyte; + err = lzwputbyte(l); + } + return err; +} + +static char * +lzwputcode(Lzw *l, int code) +{ + int i, c; + char *err; + + for(i = l->len-1; i >= 0; i--) { + c = (code >> i) & 0x1; + l->byte = (l->byte << 1) | c; + l->nbyte++; + if(l->nbyte >= 8) { + if((err = lzwputbyte(l)) != nil) + return err; + } + } + return nil; +} + +static void +predict1(Tif *t) +{ + int pix, b[8], d, m, n, j; + uint32_t x, y; + unsigned char *data, *p; + + p = t->data; + d = *t->depth; + m = (1 << d) - 1; + n = 8 / d; + for(y = 0; y < t->dy; y++) { + data = p += t->bpl; + for(x = t->bpl; x > 0; x--) { + pix = *--data; + for(j = 0; j < n; j++) { + b[j] = (pix >> d*j) & m; + if(j > 0) + b[j-1] -= b[j]; + } + if(x > 1) + b[n-1] -= *(data-1) & m; + for(j = pix = 0; j < n; j++) + pix |= (b[j] & m) << d*j; + *data = pix; + } + } +} + +static void +predict8(Tif *t) +{ + uint32_t j, s, x, y; + unsigned char *data, *p; + + p = t->data; + s = t->samples; + for(y = 0; y < t->dy; y++) { + data = p += t->dx * s; + for(x = t->dx; x > 1; x--) { + for(j = 0; j < s; j++) { + data--; + *data -= *(data-s); + } + } + } +} + +static char * +lzwstrip(Lzw *l, unsigned char *data, uint32_t n) +{ + int k, h; + long fcode, disp; + uint32_t i; + char *err; + Hash *hp; + unsigned short ent; + + if((err = lzwputcode(l, Clrcode)) != nil) + return err; + i = 0; + ent = data[i++]; + for(; i < n; i++) { + k = data[i]; + fcode = ((long)k << 12) + ent; + h = (k << Hshift) ^ ent; + hp = &l->hash[h]; + if(hp->hash == fcode) { +hit: + ent = hp->code; + continue; + } + if(hp->hash >= 0) { + disp = h == 0? 1: Hsize - h; + do { + if((h -= disp) < 0) + h += Hsize; + hp = &l->hash[h]; + if(hp->hash == fcode) + goto hit; + } while(hp->hash >= 0); + } + if((err = lzwputcode(l, ent)) != nil) + return err; + ent = k; + hp->hash = fcode; + switch(hp->code = l->ntab) { + case 511: + case 1023: + case 2047: + l->len++; + break; + default: + break; + } + if(l->ntab++ >= Tabsz-2) { + err = lzwputcode(l, Clrcode); + if(err != nil) + return err; + lzwtabinit(l); + } + } + if((err = lzwputcode(l, ent)) != nil) + return err; + if((err = lzwputcode(l, Eoicode)) != nil) + return err; + return lzwbytealign(l); +} + +static char * +lzw(Tif *t) +{ + uint32_t i, m, n; + char *err; + unsigned char *data; + Lzw l; + + if(t->opt) + *t->depth < 8? predict1(t): predict8(t); + l.ndata = t->ndata; + if((l.data = malloc(l.ndata*sizeof *l.data)) == nil) + return memerr; + l.n = l.byte = l.nbyte = 0; + err = nil; + for(i = n = 0, data = t->data; i < t->nstrips; i++) { + lzwtabinit(&l); + m = t->counts[i]; + if((err = lzwstrip(&l, data, m)) != nil) + break; + data += m; + t->counts[i] = l.n - n; + n = l.n; + } + if(err != nil) { + if(l.data != nil) + free(l.data); + return err; + } + free(t->data); + t->data = l.data; + t->ndata = l.n; + return nil; +} + +static char * +pkbrow(Pkb *p, unsigned char *data, int ndata, long *buf) +{ + int b, repl; + long i, j, k, n; + uint32_t m; + + i = n = 0; + buf[n++] = i; + b = data[i++]; + if(i < ndata) + repl = b == data[i]? 1: 0; + else + repl = 0; + for(; i < ndata; i++) { + k = data[i]; + j = labs(buf[n-1]); + if(repl) { + if(b != k) { + repl ^= 1; + buf[n++] = -i; + } + } else { + if(b == k) { + repl ^= 1; + if(i-j > 1) + buf[n++] = i - 1; + } + } + b = k; + } + buf[n++] = repl? -i: i; + for(i = 1; i < n;) { + k = buf[i]; + j = labs(buf[i-1]); + if(i < n-2 && k > 0 && buf[i+1] < 0 && + buf[i+2] > 0 && -buf[i+1]-k <= 2) { + buf[i] = buf[i+1] = buf[i+2]; + continue; + } + if((b = labs(k) - j) > 128) { + b = 128; + buf[i-1] += buf[i-1] < 0? -b: b; + } else + i++; + if(b == 0) + continue; + m = 1 + (k < 0? 1: b); + if(p->n+m > p->ndata) { + p->ndata = (p->n + m) * 2; + p->data = realloc(p->data, + p->ndata*sizeof *p->data); + if(p->data == nil) + return memerr; + } + if(k < 0) { + p->data[p->n++] = 1 - b; + p->data[p->n++] = data[j]; + } else { + p->data[p->n++] = b - 1; + memmove(p->data+p->n, data+j, b); + p->n += b; + } + } + return nil; +} + +static char * +packbits(Tif *t) +{ + uint32_t i, j, n; + char *err; + unsigned char *data; + long *buf; + Pkb p; + + p.ndata = t->ndata; + if((p.data = malloc(p.ndata*sizeof *p.data)) == nil) + return memerr; + if((buf = malloc((t->bpl+1)*sizeof *buf)) == nil) { + free(p.data); + return memerr; + } + p.n = 0; + data = t->data; + for(i = j = n = 0, err = nil; i < t->dy; i++) { + if((err = pkbrow(&p, data, t->bpl, buf)) != nil) + break; + data += t->bpl; + if(i%t->rows == t->rows-1) { + t->counts[j++] = p.n - n; + n = p.n; + } + } + free(buf); + if(err != nil) { + if(p.data != nil) + free(p.data); + return err; + } + if(j < t->nstrips) + t->counts[j] = p.n - n; + free(t->data); + t->data = p.data; + t->ndata = p.n; + return nil; +} + +static char * +alloctif(Tif *t) +{ + int rgb; + uint32_t i, count, n; + + count = t->ndata < 0x2000? t->ndata: 0x2000; + t->rows = (count + t->bpl - 1) / t->bpl; + if(t->comp == Tt4enc && t->opt) { + if((n = t->rows%Kpar) != 0) + t->rows += Kpar - n; + } + t->nstrips = (t->dy + t->rows - 1) / t->rows; + t->strips = malloc(t->nstrips*sizeof *t->strips); + if(t->strips == nil) + return memerr; + t->counts = malloc(t->nstrips*sizeof *t->counts); + if(t->counts == nil) { + free(t->strips); + return memerr; + } + if(t->ncolor > 0) { + t->color = malloc(t->ncolor*sizeof *t->color); + if(t->color == nil) { + free(t->strips); + free(t->counts); + return memerr; + } + for(i = 0; i < 256; i++) { + rgb = cmap2rgb(i); + t->color[i] = (rgb >> 16) & 0xff; + t->color[i+256] = (rgb >> 8) & 0xff; + t->color[i+256*2] = rgb & 0xff; + } + } + count = t->rows * t->bpl; + for(i = 0, n = t->ndata; i < t->nstrips-1; i++) { + t->counts[i] = count; + n -= count; + } + t->counts[i] = n; + return nil; +} + +static void +freetif(Tif *t) +{ + free(t->strips); + free(t->counts); + if(t->color != nil) + free(t->color); + free(t->data); +} + +static int +typesize(int fld) +{ + return typesizes[flds[fld].typ]; +} + +static void +writefld(Biobuf *fd, int fld, uint32_t cnt, uint32_t val) +{ + put2(fd, flds[fld].tag); + put2(fd, flds[fld].typ); + put4(fd, cnt); + put4(fd, val); +} + +static void +writeflds(Biobuf *fd, Tif *t) +{ + int n; + uint32_t i, off, slen, s, offs[7]; + + slen = t->desc == nil? 0: strlen(t->desc) + 1; + put2(fd, 0x4d4d); + put2(fd, 0x002a); + off = 0x00000008; + memset(offs, 0, sizeof offs); + n = 0; + offs[n++] = off; + if(t->samples > 1) { + off += t->samples * typesize(Tbits); + offs[n++] = off; + } + if(slen > 4) { + off += slen * typesize(Tdesc); + offs[n++] = off; + } + if(t->nstrips > 1) { + off += t->nstrips * typesize(Tstrips); + offs[n++] = off; + off += t->nstrips * typesize(Tcounts); + offs[n++] = off; + } + off += typesize(Txres); + offs[n++] = off; + off += typesize(Tyres); + offs[n] = off; + if(t->color != nil) + off += t->ncolor * typesize(Tcolor); + for(i = 0; i < t->nstrips-1; i++) { + t->strips[i] = off; + off += t->counts[i]; + } + t->strips[i] = off; + off += t->counts[i]; + put4(fd, off); + if(t->samples > 1) { + for(i = 0; i < t->samples; i++) + put2(fd, t->depth[i]); + } + if(slen > 4) { + Bwrite(fd, t->desc, slen-1); + put1(fd, 0x00); + } + if(t->nstrips > 1) { + for(i = 0; i < t->nstrips; i++) + put4(fd, t->strips[i]); + for(i = 0; i < t->nstrips; i++) + put4(fd, t->counts[i]); + } + put4(fd, t->dx); + put4(fd, 0x00000004); + put4(fd, t->dy); + put4(fd, 0x00000004); + if(t->color != nil) { + for(i = 0; i < t->ncolor; i++) + put2(fd, t->color[i]); + } + Bwrite(fd, t->data, t->ndata); + n = 0; + put2(fd, t->nfld); + writefld(fd, Twidth, 1, t->dx); + writefld(fd, Tlength, 1, t->dy); + if(t->samples > 1) + writefld(fd, Tbits, t->samples, offs[n++]); + else + writefld(fd, Tbits, t->samples, *t->depth<<16); + writefld(fd, Tcomp, 1, t->comp<<16); + writefld(fd, Tphoto, 1, t->photo<<16); + if(t->comp >= 2 && t->comp <= 4) + writefld(fd, Tfill, 1, 1<<16); + if(slen > 1) { + if(slen <= 4) { + for(i = s = 0; i < slen-1; i++) + s = (s << 8) | t->desc[i]; + s <<= 8; + writefld(fd, Tdesc, slen, s); + } else + writefld(fd, Tdesc, slen, offs[n++]); + } + if(t->nstrips > 1) + writefld(fd, Tstrips, t->nstrips, offs[n++]); + else + writefld(fd, Tstrips, t->nstrips, *t->strips); + if(t->samples > 1) + writefld(fd, Tsamples, 1, t->samples<<16); + writefld(fd, Trows, 1, t->rows); + if(t->nstrips > 1) + writefld(fd, Tcounts, t->nstrips, offs[n++]); + else + writefld(fd, Tcounts, t->nstrips, *t->counts); + writefld(fd, Txres, 1, offs[n++]); + writefld(fd, Tyres, 1, offs[n++]); + if(t->comp == Tt4enc && t->opt) + writefld(fd, T4opt, 1, 1); + writefld(fd, Tresunit, 1, 2<<16); + if(t->comp == Tlzw && t->opt) + writefld(fd, Tpredictor, 1, 2<<16); + if(t->color != nil) + writefld(fd, Tcolor, t->ncolor, offs[n]); + put4(fd, 0x00000000); +} + +static char * +writedata(Biobuf *fd, Image *i, Memimage *m, Tif *t) +{ + char *err; + unsigned char *data; + int j, ndata, depth; + Rectangle r; + + if(m != nil) { + r = m->r; + depth = m->depth; + } else { + r = i->r; + depth = i->depth; + } + t->dx = Dx(r); + t->dy = Dy(r); + for(j = 0; j < t->samples; j++) + t->depth[j] = depth / t->samples; + /* + * potentially one extra byte on each + * end of each scan line + */ + ndata = t->dy * (2 + t->dx*depth/8); + if((data = malloc(ndata)) == nil) + return memerr; + if(m != nil) + ndata = unloadmemimage(m, r, data, ndata); + else + ndata = unloadimage(i, r, data, ndata); + if(ndata < 0) { + free(data); + if((err = malloc(ERRMAX*sizeof *err)) == nil) + return memerr; + snprint(err, ERRMAX, "WriteTIF: %r"); + } else { + t->data = data; + t->ndata = ndata; + t->bpl = bytesperline(r, depth); + err = alloctif(t); + if(err != nil) { + freetif(t); + return err; + } + if((err = (*t->compress)(t)) == nil) + writeflds(fd, t); + freetif(t); + } + return err; +} + +static char * +writetif0(Biobuf *fd, Image *image, Memimage *memimage, + uint32_t chan, char *s, int comp, int opt) +{ + Tif t; + + t.nfld = 11; + t.color = nil; + if((t.desc = s) != nil) + t.nfld++; + t.opt = opt; + t.comp = comp; + switch(chan) { + case GREY1: + case GREY4: + case GREY8: + t.photo = 1; + t.samples = 1; + t.ncolor = 0; + break; + case CMAP8: + t.photo = 3; + t.samples = 1; + t.ncolor = 3 * 256; + t.nfld++; + break; + case BGR24: + t.photo = 2; + t.samples = 3; + t.ncolor = 0; + t.nfld++; + break; + default: + return "WriteTIF: can't handle channel type"; + } + switch(t.comp) { + case Tnocomp: + t.compress = nocomp; + break; + case Thuffman: + case Tt4enc: + case Tt6enc: + t.photo = 0; + t.nfld++; + if(t.comp == Tt4enc && t.opt) + t.nfld++; + t.compress = fax; + break; + case Tlzw: + t.compress = lzw; + if(t.opt) + t.nfld++; + break; + case Tpackbits: + t.compress = packbits; + break; + default: + return "WriteTIF: unknown compression"; + } + return writedata(fd, image, memimage, &t); +} + +char * +writetif(Biobuf *fd, Image *i, char *s, int comp, int opt) +{ + return writetif0(fd, i, nil, i->chan, s, comp, opt); +} + +char * +memwritetif(Biobuf *fd, Memimage *m, char *s, int comp, int opt) +{ + return writetif0(fd, nil, m, m->chan, s, comp, opt); +} diff --git a/sys/src/cmd/pict/ycbcr.h b/sys/src/cmd/pict/ycbcr.h new file mode 100644 index 0000000..2bbac71 --- /dev/null +++ b/sys/src/cmd/pict/ycbcr.h @@ -0,0 +1,294 @@ +unsigned int ycbcrmap[256] = { + 0x008080, 0x07A27A, 0x0FC474, 0x17E66F, 0x276963, 0x2F8B5E, 0x37AD58, 0x3FCF52, + 0x4F5247, 0x577441, 0x5F963C, 0x67B836, 0x773C2A, 0x7F5E25, 0x87801F, 0x8FA21A, + 0x9AA511, 0x118080, 0x09AA79, 0x11CC73, 0x19EE6E, 0x31635C, 0x3B8E55, 0x3EB353, + 0x44D64F, 0x594D3F, 0x627339, 0x6B9933, 0x6FBD30, 0x813623, 0x8A5B1D, 0x928017, + 0x9D800F, 0xA6A809, 0x228080, 0x0BB377, 0x13D572, 0x1BF76C, 0x3B5E55, 0x47914D, + 0x45B84E, 0x49DC4B, 0x634738, 0x6D7231, 0x779C2B, 0x77C22A, 0x8B311C, 0x945815, + 0x9F560E, 0xA98007, 0xB2AB00, 0x338080, 0x0DBB76, 0x15DD70, 0x1DFF6B, 0x45584E, + 0x539444, 0x4BBE49, 0x4EE347, 0x6D4231, 0x78702A, 0x839F22, 0x80C724, 0x952B15, + 0x1C969C, 0x23B896, 0x2BDA91, 0x3C5E85, 0x448080, 0x4BA27A, 0x53C474, 0x644769, + 0x6B6963, 0x738B5E, 0x7BAD58, 0x8C304C, 0x935247, 0x9B7441, 0xA3963C, 0x1474A2, + 0x1971AA, 0x239CA3, 0x28BF99, 0x2FE292, 0x4B5586, 0x558080, 0x54A679, 0x59CA73, + 0x704065, 0x79665F, 0x818C59, 0x85B154, 0x972A47, 0x9F4E42, 0xA8733C, 0xB09836, + 0xBE9A30, 0x1E6EB3, 0x2AA1AA, 0x2CC69C, 0x32E994, 0x5A4D88, 0x668080, 0x5EAA79, + 0x61CF73, 0x7D3963, 0x86635C, 0x908E55, 0x8FB551, 0xA32343, 0xAC4B3D, 0xB57237, + 0xC27231, 0xCC9C2B, 0x236BBB, 0x31A7B1, 0x31CD9F, 0x36F195, 0x694489, 0x778080, + 0x67AF78, 0x68D572, 0x893260, 0x946058, 0x9E8F51, 0x9AB84E, 0xAF1D3F, 0xB84738, + 0x38ADB8, 0x3FCFB3, 0x5052A7, 0x5874A2, 0x60969C, 0x67B896, 0x783C8B, 0x805E85, + 0x888080, 0x8FA27A, 0xA0256E, 0xA84769, 0xAF6963, 0xB78B5E, 0x2869C4, 0x308BBE, + 0x368CC6, 0x3FB2C0, 0x45D5B7, 0x5A4DAC, 0x6373A6, 0x6B99A0, 0x6FBD98, 0x87338C, + 0x905986, 0x998080, 0x9BA579, 0xAD1D6C, 0xB64267, 0xBE6761, 0xC68C5B, 0x2D66CC, + 0x3263D5, 0x3C8DCE, 0x46B8C7, 0x4ADCBB, 0x6447B1, 0x6E71AA, 0x789CA3, 0x78C29A, + 0x962B8D, 0xA05586, 0xAA8080, 0xA7A879, 0xBA166B, 0xC33D64, 0xCC655E, 0xD68D58, + 0xE58E55, 0x3760DD, 0x428ED5, 0x4DBDCE, 0x4FE2C0, 0x6E41B6, 0x7970AF, 0x839FA7, + 0x81C69C, 0xA5228F, 0xB05187, 0xBB8080, 0xB3AA79, 0xC80E6A, 0xD23963, 0xDB635C, + 0x54C3D5, 0x6447C9, 0x6C69C4, 0x748BBE, 0x7CADB8, 0x8C30AD, 0x9452A7, 0x9C74A2, + 0xA4969C, 0xB41A90, 0xBC3C8B, 0xC45E85, 0xCC8080, 0x3C5DE6, 0x447FE0, 0x4CA1DA, + 0x52A4E2, 0x5BC9DC, 0x6C42CF, 0x7567CA, 0x7D8CC3, 0x86B1BD, 0x982AB0, 0xA04EAB, + 0xA973A5, 0xB1989E, 0xC31191, 0xCC368C, 0xD45A86, 0xDD8080, 0x425AEE, 0x4A7FE8, + 0x507FF0, 0x59A6EA, 0x62CEE3, 0x753DD5, 0x7E65CF, 0x878CC9, 0x90B4C2, 0xA323B4, + 0xAC4BAE, 0xB572A8, 0xBF9AA1, 0xD20993, 0xDB308C, 0xE45886, 0xEE8080, 0x4757F7, + 0x4C54FF, 0x557FF8, 0x5FA9F1, 0x69D4EA, 0x7E38DB, 0x8763D5, 0x918DCE, 0x9BB8C7, + 0xB01CB8, 0xB947B1, 0xC371AA, 0xCD9CA3, 0xE10094, 0xEB2B8D, 0xF55586, 0xFF8080, +}; + +unsigned char closestycbcr[16*16*16] = { + 8,55,55,38,21,21,4,4,4,0,79,79,80,114,142,159, + 8,55,38,38,21,4,4,4,4,0,79,79,80,114,142,159, + 55,55,38,21,21,4,4,4,0,0,79,79,80,114,142,159, + 55,38,38,21,4,4,4,4,0,0,79,79,80,114,142,159, + 55,38,21,21,4,4,4,0,0,0,79,79,80,114,142,159, + 38,38,21,4,4,4,4,0,0,0,79,79,80,114,142,159, + 38,21,21,4,4,4,0,0,0,0,79,79,80,114,142,159, + 38,21,4,4,4,4,0,0,0,0,79,79,80,114,142,159, + 21,21,4,4,4,4,0,0,0,0,79,79,80,114,142,159, + 21,4,4,4,4,0,0,0,0,17,79,79,80,114,142,159, + 5,5,5,5,5,1,1,1,1,1,64,64,64,143,143,144, + 22,5,5,5,18,18,18,18,18,18,64,64,81,98,143,144, + 6,6,6,52,52,52,52,52,52,52,65,65,98,115,115,128, + 6,6,6,2,2,2,2,2,2,2,65,65,82,115,128,145, + 23,23,36,36,36,36,36,36,36,36,82,99,99,116,145,162, + 7,7,3,3,3,3,3,3,3,3,66,66,116,116,129,129, + 25,8,8,55,38,38,21,4,4,67,67,80,97,142,159,160, + 25,8,55,55,38,21,21,4,4,67,79,80,97,142,159,160, + 8,8,55,38,38,21,4,4,4,67,79,80,97,142,159,160, + 8,55,55,38,21,21,4,4,4,79,79,80,97,142,159,160, + 8,55,38,38,21,4,4,4,4,79,79,80,97,142,159,160, + 55,55,38,21,21,4,4,4,0,79,79,80,97,142,159,160, + 55,38,38,21,4,4,4,4,0,79,79,80,97,142,159,160, + 55,38,21,21,4,4,4,0,0,79,79,80,97,142,159,160, + 38,38,21,4,4,4,4,17,17,17,79,80,97,142,159,160, + 5,5,5,5,5,5,1,1,17,34,64,64,143,143,143,144, + 22,22,5,5,5,5,1,1,1,64,64,81,81,143,144,161, + 39,22,22,22,5,35,35,35,35,35,81,81,98,115,144,178, + 6,6,6,6,6,2,2,2,2,65,65,65,115,128,128,145, + 23,23,23,23,19,19,19,19,19,65,82,82,99,128,145,162, + 57,7,7,7,53,53,53,53,53,66,66,116,116,129,129,179, + 24,7,7,7,20,20,20,20,20,66,66,83,116,129,146,146, + 42,25,25,8,55,38,38,21,4,67,67,67,114,142,160,177, + 42,25,8,8,55,38,21,21,4,67,67,97,114,142,160,177, + 25,25,8,55,55,38,21,4,4,67,67,97,114,142,160,177, + 25,8,8,55,38,38,21,4,4,67,67,97,114,142,160,177, + 25,8,55,55,38,21,21,4,4,67,80,97,114,142,160,177, + 8,8,55,38,38,21,4,4,4,67,80,97,114,142,160,177, + 8,55,55,38,21,21,4,4,4,79,80,97,114,142,160,177, + 8,55,38,38,21,4,4,4,17,79,80,97,114,142,160,177, + 55,55,38,21,21,4,4,34,34,34,80,97,114,142,160,177, + 9,9,22,5,5,5,5,5,34,64,64,81,143,143,144,206, + 39,39,22,22,22,5,5,18,18,64,81,98,98,144,161,178, + 56,39,39,6,6,6,6,52,52,65,65,98,115,128,178,178, + 23,23,23,6,6,6,19,19,19,65,82,82,128,145,145,207, + 40,40,40,40,23,36,36,36,36,82,99,116,116,145,162,179, + 57,7,7,7,7,3,3,3,3,66,66,116,129,129,179,192, + 41,41,24,24,24,37,37,37,37,83,100,100,146,146,163,192, + 59,42,25,25,8,55,55,38,67,67,84,130,130,159,177,205, + 42,42,25,8,8,55,38,38,67,67,84,130,130,159,177,205, + 42,25,25,8,55,55,38,21,67,67,67,130,130,159,177,205, + 42,25,8,8,55,38,38,21,67,67,67,130,142,159,177,205, + 25,25,8,55,55,38,21,21,67,67,67,114,142,159,177,205, + 25,25,8,55,38,38,21,4,67,67,67,114,142,159,177,205, + 25,8,8,55,38,21,21,4,67,67,80,114,142,159,177,205, + 25,8,55,55,38,21,4,4,34,67,80,114,142,159,177,205, + 9,9,9,9,5,5,5,51,51,51,64,143,143,144,206,206, + 9,9,9,22,22,22,5,5,51,64,81,143,143,144,161,206, + 56,56,39,39,39,22,22,6,69,81,98,98,115,161,178,178, + 10,56,56,6,6,6,6,6,65,65,65,115,128,145,207,207, + 10,40,40,40,23,23,23,19,82,82,99,99,145,145,162,208, + 57,57,57,57,7,7,7,53,66,66,116,116,129,162,179,192, + 11,24,24,24,24,7,7,20,66,83,83,129,146,146,192,192, + 58,58,41,41,41,41,37,37,100,100,117,117,163,163,180,209, + 12,59,42,42,25,8,8,71,71,84,101,130,147,193,193,222, + 59,59,42,25,25,8,55,71,84,84,101,130,147,193,205,222, + 59,42,42,25,8,8,55,71,84,84,84,130,130,193,205,222, + 59,42,25,25,8,55,55,38,84,84,130,130,130,160,205,222, + 42,42,25,8,8,55,38,67,67,84,130,130,130,160,205,222, + 42,25,25,8,55,55,38,67,67,84,130,130,159,160,205,222, + 42,25,8,8,55,38,38,67,67,67,130,130,159,160,205,222, + 25,9,9,9,55,38,21,51,51,68,131,131,159,160,206,222, + 26,9,9,9,9,22,5,68,68,68,131,143,144,161,206,223, + 26,26,9,39,39,22,22,68,68,68,98,143,144,161,178,223, + 10,56,56,56,39,39,6,69,69,69,115,115,128,178,207,207, + 10,10,10,40,23,23,23,69,69,82,128,128,145,162,207,208, + 11,11,57,57,40,40,40,70,70,99,116,129,162,162,179,225, + 11,11,57,57,7,7,7,70,70,66,116,129,146,179,192,209, + 28,41,41,41,41,41,24,87,100,100,100,146,163,163,209,209, + 58,58,58,58,58,58,58,54,117,117,117,163,180,180,180,226, + 29,12,59,59,42,25,71,71,71,101,118,147,164,193,193,210, + 12,12,59,42,42,25,71,71,71,101,101,147,164,193,193,210, + 12,59,59,42,25,25,71,71,101,101,130,147,164,193,193,239, + 12,59,42,42,25,8,71,71,101,101,130,147,147,193,193,239, + 59,59,42,25,25,8,71,71,84,101,130,147,147,193,222,239, + 59,42,42,25,8,8,55,71,84,101,130,130,147,193,222,239, + 59,42,25,25,8,55,55,84,84,84,130,130,147,177,222,239, + 13,26,9,9,9,9,72,68,68,131,131,131,148,206,206,223, + 43,26,26,9,9,9,22,68,85,85,131,131,161,178,223,224, + 43,43,56,56,56,39,39,69,85,85,132,132,178,178,207,224, + 27,10,10,10,56,56,69,69,69,132,132,132,145,207,207,208, + 27,27,27,10,40,40,40,86,86,103,132,145,162,179,208,225, + 11,11,11,57,57,57,70,70,70,133,133,129,179,179,192,242, + 28,28,11,11,41,24,24,87,87,87,129,146,163,192,209,209, + 45,45,58,58,58,58,41,104,104,117,163,163,180,180,226,226, + 62,58,58,58,58,58,58,121,117,117,117,180,180,180,243,243, + 46,29,12,12,59,42,88,88,88,118,134,164,181,193,210,227, + 29,29,12,59,59,42,88,88,88,118,118,164,181,193,210,227, + 29,12,12,59,42,42,71,71,118,118,118,164,181,193,210,227, + 29,12,59,59,42,25,71,71,101,118,147,164,181,193,210,227, + 12,12,59,42,42,25,71,71,101,118,147,164,193,193,210,240, + 12,59,59,42,25,25,71,71,101,101,147,164,193,193,210,240, + 13,13,42,26,9,9,72,72,101,101,131,148,194,194,194,240, + 13,43,43,26,26,9,72,72,85,131,131,148,194,194,223,241, + 60,60,43,26,26,56,72,85,102,102,148,165,194,194,224,241, + 14,27,10,10,10,56,73,102,102,132,132,132,195,207,208,225, + 44,27,27,27,10,10,73,86,103,132,132,149,195,208,225,225, + 44,44,44,11,11,57,103,103,120,133,133,166,179,179,225,242, + 28,28,11,11,11,57,70,87,87,133,133,150,192,192,209,226, + 45,45,28,28,58,41,104,104,104,150,150,163,180,209,226,226, + 62,62,45,58,58,58,121,121,121,121,180,180,180,180,243,243, + 62,62,58,58,58,58,121,121,121,121,180,180,180,180,243,243, + 63,46,29,29,12,75,105,105,105,134,151,197,181,227,244,244, + 46,46,29,12,12,75,105,105,134,134,134,181,181,227,244,244, + 46,29,29,12,59,75,88,88,134,134,134,181,181,210,227,244, + 46,29,12,12,59,88,88,88,118,134,134,181,181,210,227,244, + 29,29,12,59,59,88,88,88,118,134,164,181,181,210,227,244, + 29,12,12,59,42,88,88,88,118,118,164,181,193,210,227,244, + 13,13,13,13,26,72,72,72,135,135,148,165,194,211,228,228, + 30,60,60,43,43,72,72,72,102,135,165,165,194,211,228,241, + 14,60,60,60,43,73,73,102,119,119,165,182,195,195,228,241, + 14,14,27,27,27,73,73,73,119,132,149,195,195,212,225,242, + 61,44,44,44,27,90,90,120,120,149,166,166,212,212,242,242, + 61,61,61,11,11,74,74,120,120,133,133,196,196,196,242,242, + 45,45,28,28,28,74,74,104,104,150,150,167,196,209,226,243, + 62,62,45,45,45,91,121,121,121,167,167,184,180,226,243,243, + 62,62,62,62,58,121,121,121,121,184,184,184,180,243,243,243, + 62,62,62,62,58,121,121,121,121,184,184,180,180,243,243,243, + 63,63,46,29,75,75,122,122,151,151,151,197,197,244,244,244, + 63,63,46,29,75,75,122,122,151,151,151,197,197,244,244,244, + 63,46,46,29,75,75,122,122,151,151,151,197,197,244,244,244, + 63,46,29,29,75,75,105,105,134,151,151,197,197,227,244,244, + 46,46,29,12,75,75,105,105,134,151,197,197,210,227,244,244, + 30,30,13,13,76,76,89,135,135,135,198,198,211,228,245,245, + 30,30,13,13,76,89,89,89,135,135,165,182,211,228,245,245, + 47,60,60,60,60,106,106,119,119,119,182,182,228,228,245,245, + 14,14,14,14,77,73,73,119,136,136,182,195,195,212,229,246, + 31,14,14,44,44,90,90,90,136,136,166,166,212,229,246,246, + 15,61,61,61,44,74,74,137,137,166,183,183,196,246,246,242, + 15,15,15,28,91,91,74,137,137,150,183,196,213,213,230,243, + 62,62,62,45,45,91,91,121,167,167,184,184,213,230,243,243, + 62,62,62,62,62,108,121,121,184,184,184,184,230,247,243,243, + 62,62,62,62,62,125,121,121,184,184,184,184,247,243,243,243, + 62,62,62,62,62,121,121,121,184,184,184,184,247,243,243,243, + 63,63,63,46,92,92,122,138,138,168,168,214,214,231,244,244, + 63,63,63,46,92,92,122,138,138,168,168,214,214,231,244,244, + 63,63,46,46,92,92,122,138,151,168,197,197,214,244,244,244, + 63,63,46,29,75,75,122,138,151,168,197,197,214,244,244,244, + 63,46,46,29,75,75,122,122,151,151,197,197,214,244,244,244, + 47,47,30,30,76,76,106,139,152,152,198,198,215,245,245,245, + 48,47,30,30,76,123,123,123,152,152,198,198,228,245,245,245, + 48,31,14,14,77,123,123,136,136,136,199,199,245,245,245,245, + 32,31,31,14,77,90,90,136,136,153,199,199,229,229,246,246, + 32,32,61,61,107,107,107,153,153,153,183,183,229,246,246,246, + 16,15,15,61,78,91,91,137,137,137,183,183,213,246,246,246, + 16,16,16,45,108,108,108,154,154,154,184,213,230,230,247,247, + 33,62,62,62,125,125,125,154,171,184,184,230,230,247,247,243, + 62,62,62,62,125,125,125,125,184,184,184,184,247,247,247,243, + 62,62,62,62,125,125,125,121,184,184,184,184,247,247,243,243, + 62,62,62,62,125,125,125,121,184,184,184,184,247,247,243,243, + 63,63,63,126,109,109,138,155,155,185,185,231,231,248,248,244, + 63,63,63,109,109,109,138,138,138,185,185,231,231,248,248,244, + 63,63,63,109,109,109,138,138,168,185,185,231,231,248,244,244, + 63,63,63,109,109,92,138,138,168,185,214,214,231,248,244,244, + 48,48,47,93,93,93,139,139,139,169,198,215,215,232,245,245, + 48,48,47,93,93,93,139,139,169,169,215,215,232,245,245,245, + 48,48,48,93,93,123,123,140,169,169,215,215,232,245,245,245, + 32,32,31,77,77,77,140,140,153,199,199,216,216,246,246,246, + 49,32,32,94,94,107,107,153,153,170,216,216,246,246,246,246, + 49,49,15,78,78,124,124,137,170,200,200,200,246,246,246,246, + 16,16,16,78,78,124,124,154,154,200,200,217,230,230,247,247, + 33,33,16,95,125,125,125,171,171,171,217,230,247,247,247,247, + 50,33,62,125,125,125,125,171,188,184,184,247,247,247,247,247, + 50,62,62,125,125,125,125,188,188,184,184,247,247,247,247,247, + 50,62,62,125,125,125,125,188,184,184,184,247,247,247,247,247, + 50,62,62,125,125,125,125,188,184,184,184,247,247,247,247,243, + 63,63,63,126,126,126,155,172,172,201,201,248,248,248,248,248, + 63,63,63,126,126,126,155,155,172,201,201,248,248,248,248,248, + 63,63,63,126,126,126,155,155,185,201,201,248,248,248,248,248, + 63,63,63,126,126,126,155,155,185,202,202,248,248,248,248,244, + 48,48,48,110,110,110,139,156,202,202,232,232,249,249,249,245, + 48,48,48,110,110,110,156,156,186,186,232,232,249,249,245,245, + 48,48,48,94,94,140,140,140,186,186,216,216,249,249,245,245, + 49,49,111,111,94,140,140,140,170,216,216,233,233,246,246,246, + 49,49,111,111,111,124,141,170,170,187,233,233,250,246,246,246, + 49,16,95,95,95,141,141,141,187,200,217,217,217,246,246,246, + 33,33,95,95,95,141,141,171,171,217,217,234,247,247,247,247, + 50,50,96,96,125,125,188,188,188,188,234,234,247,247,247,247, + 50,50,50,125,125,125,188,188,188,188,234,247,247,247,247,247, + 50,50,50,125,125,125,188,188,188,188,247,247,247,247,247,247, + 50,50,62,125,125,125,188,188,188,188,247,247,247,247,247,247, + 50,50,62,125,125,125,188,188,188,184,247,247,247,247,247,247, + 63,63,126,126,126,126,189,189,189,218,218,248,248,248,248,248, + 63,63,126,126,126,126,172,172,201,218,218,248,248,248,248,248, + 63,63,126,126,126,172,172,172,201,218,218,248,248,248,248,248, + 48,48,127,127,127,156,173,173,202,202,219,249,249,249,249,249, + 48,48,127,127,127,173,173,173,202,202,249,249,249,249,249,249, + 48,48,127,127,127,173,173,173,186,203,249,249,249,249,249,249, + 49,49,111,111,111,157,157,157,203,203,233,233,250,250,250,246, + 49,49,112,112,112,157,157,157,187,203,233,250,250,250,250,246, + 49,49,112,112,112,141,141,187,187,204,250,250,250,250,246,246, + 50,33,96,96,96,158,158,158,204,217,234,234,234,251,247,247, + 50,50,96,96,96,158,158,188,188,234,234,251,251,247,247,247, + 50,50,113,113,113,175,188,188,188,234,251,251,251,247,247,247, + 50,50,113,113,113,125,188,188,188,251,251,251,247,247,247,247, + 50,50,113,113,125,125,188,188,188,251,251,251,247,247,247,247, + 50,50,113,113,125,125,188,188,188,251,251,247,247,247,247,247, + 50,50,113,125,125,125,188,188,188,251,251,247,247,247,247,247, + 63,126,126,126,126,189,189,189,235,235,235,252,248,248,248,248, + 63,126,126,126,126,189,189,189,218,235,235,235,248,248,248,248, + 63,126,126,126,126,189,189,189,219,219,235,248,248,248,248,248, + 48,127,127,127,127,190,190,190,219,219,236,249,249,249,249,249, + 48,127,127,127,127,190,190,190,219,219,236,249,249,249,249,249, + 48,127,127,127,127,174,174,203,203,220,220,249,249,249,249,249, + 49,112,112,112,112,174,174,174,220,220,250,250,250,250,250,250, + 49,112,112,112,112,174,191,191,204,204,250,250,250,250,250,250, + 49,112,112,112,158,158,158,204,204,204,250,250,250,250,250,250, + 50,113,113,113,113,175,175,175,221,234,251,251,251,251,251,247, + 50,113,113,113,113,175,175,188,188,251,251,251,251,251,251,247, + 50,113,113,113,113,176,176,188,188,251,251,251,251,251,247,247, + 50,113,113,113,113,176,188,188,188,251,251,251,251,251,247,247, + 50,113,113,113,113,176,188,188,188,251,251,251,251,247,247,247, + 50,113,113,113,113,188,188,188,188,251,251,251,251,247,247,247, + 50,113,113,113,113,188,188,188,188,251,251,251,247,247,247,247, + 126,126,126,126,189,189,189,189,235,252,252,252,252,248,248,248, + 126,126,126,126,189,189,189,189,235,252,252,252,252,248,248,248, + 127,127,127,127,190,190,190,190,236,236,253,253,249,249,249,249, + 127,127,127,127,190,190,190,190,236,236,253,253,249,249,249,249, + 127,127,127,127,190,190,190,190,236,236,253,249,249,249,249,249, + 112,112,112,112,191,191,191,220,220,237,237,250,250,250,250,250, + 112,112,112,112,191,191,191,191,237,237,237,250,250,250,250,250, + 112,112,112,112,191,191,191,221,221,221,250,250,250,250,250,250, + 113,113,113,113,175,175,175,221,221,221,251,251,251,251,251,251, + 113,113,113,113,176,176,176,221,238,238,251,251,251,251,251,251, + 113,113,113,113,176,176,176,176,238,251,251,251,251,251,251,251, + 113,113,113,113,176,176,176,176,251,251,251,251,251,251,251,247, + 113,113,113,113,176,176,176,176,251,251,251,251,251,251,251,247, + 113,113,113,113,176,176,176,188,251,251,251,251,251,251,251,247, + 113,113,113,113,176,176,176,188,251,251,251,251,251,251,247,247, + 113,113,113,113,176,176,188,188,251,251,251,251,251,251,247,247, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +}; + diff --git a/sys/src/cmd/pict/yuv.c b/sys/src/cmd/pict/yuv.c new file mode 100644 index 0000000..25d7820 --- /dev/null +++ b/sys/src/cmd/pict/yuv.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include "imagefile.h" + +int cflag = 0; +int dflag = 0; +int eflag = 0; +int nineflag = 0; +int threeflag = 0; +int output = 0; +uint32_t outchan = CMAP8; +int defaultcolor = 1; +Image *image; + +enum{ + Border = 2, + Edge = 5 +}; + +char *show(int, char*); + +Rawimage** readyuv(int fd, int colorspace); + +Rectangle +imager(Image *i) +{ + Point p1, p2; + + p1 = addpt(divpt(subpt(i->r.max, i->r.min), 2), i->r.min); + p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); + return rectaddpt(i->r, subpt(p2, p1)); +} + +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 = imager(image); + border(screen, r, -Border, nil, ZP); + drawop(screen, r, image, nil, image->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: yuv -39cdektv [file.yuv ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "yuv: exiting after one file\n"); + break; + } + } + } + exits(err); +} + +int +init(void) +{ + static int inited; + + if(inited == 0){ + if(initdraw(0, 0, 0) < 0){ + fprint(2, "yuv: initdraw failed: %r"); + return -1; + } + einit(Ekeyboard|Emouse); + inited++; + } + return 1; +} + +char* +show(int fd, char *name) +{ + Rawimage **array, *r, *c; + Image *i; + int j, ch; + char buf[32]; + + array = readyuv(fd, CYCbCr); + if(array == nil || array[0]==nil){ + fprint(2, "yuv: decode %s failed: %r\n", name); + return "decode"; + } + if(!dflag){ + if(init() < 0) + return "initdraw"; + if(defaultcolor && screen->depth>8) + outchan = RGB24; + } + r = array[0]; + if(outchan == CMAP8) + c = torgbv(r, !eflag); + else{ + if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)) + c = totruecolor(r, CY); + else + c = totruecolor(r, CRGB24); + } + if(c == nil){ + fprint(2, "yuv: converting %s to local format failed: %r\n", name); + return "torgbv"; + } + if(!dflag){ + if(r->chandesc == CY) + i = allocimage(display, c->r, GREY8, 0, 0); + else + i = allocimage(display, c->r, outchan, 0, 0); + if(i == nil){ + fprint(2, "yuv: allocimage %s failed: %r\n", name); + return "allocimage"; + } + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "yuv: loadimage %s failed: %r\n", name); + return "loadimage"; + } + image = i; + eresized(0); + if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) + exits(nil); + draw(screen, screen->clipr, display->white, nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag){ + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != c->chanlen){ + fprint(2, "yuv: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "yuv: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +}