diff options
author | ppatience0 <ppatience0@gmail.com> | 2013-07-19 02:16:43 -0400 |
---|---|---|
committer | ppatience0 <ppatience0@gmail.com> | 2013-07-19 02:16:43 -0400 |
commit | 360cabb85846e4d344d6f9aaf4082cf42683a91c (patch) | |
tree | d96b55c941abaf3d872c6753692caaafc8d66827 | |
parent | 24a5720bec7a90e94ab13d3c32b27f39585a1039 (diff) |
readtif: fix many bugs
totif: add tiff encoder
-rw-r--r-- | sys/man/1/jpg | 66 | ||||
-rw-r--r-- | sys/src/cmd/jpg/imagefile.h | 6 | ||||
-rw-r--r-- | sys/src/cmd/jpg/mkfile | 6 | ||||
-rw-r--r-- | sys/src/cmd/jpg/readtif.c | 224 | ||||
-rw-r--r-- | sys/src/cmd/jpg/tif.c | 18 | ||||
-rw-r--r-- | sys/src/cmd/jpg/totif.c | 157 | ||||
-rw-r--r-- | sys/src/cmd/jpg/writetif.c | 1341 |
7 files changed, 1731 insertions, 87 deletions
diff --git a/sys/man/1/jpg b/sys/man/1/jpg index e242fa6e8..1b23aad72 100644 --- a/sys/man/1/jpg +++ b/sys/man/1/jpg @@ -1,6 +1,6 @@ .TH JPG 1 .SH NAME -jpg, gif, png, tif, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, topng, toico \- view and convert pictures +jpg, gif, png, tif, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, topng, totif, toico \- view and convert pictures .SH SYNOPSIS .B jpg [ @@ -127,6 +127,16 @@ jpg, gif, png, tif, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm ] [ .I file ] +.br +.B totif +[ +.B -c +.I comment +] [ +.B -bBgGhHlLmprtT +] [ +.I file +] .PP .B ico [ @@ -157,9 +167,11 @@ to Plan 9 image format and write them to standard output. .IR Tojpg , .IR togif , .IR toppm , +.IR topng , and -.I topng -read Plan 9 images files, convert them to JPEG, GIF, PPM, or PNG, and write them to standard output. +.I totif +read Plan 9 images files, convert them to JPEG, GIF, PPM, +PNG, or TIFF and write them to standard output. .PP The default behavior of .IR jpg , @@ -246,7 +258,7 @@ The and .IR topng programs go the other way: they convert from Plan 9 images to JPEG, GIF, -PPM and PNG and have no display capability. +PPM, PNG, and TIFF and have no display capability. They all accept an option .B -c to set the comment field of the resulting file. @@ -270,6 +282,52 @@ is an script that invokes .B tojpg .BR -s . +.I Totif +accepts many options. +Choosing Huffman, T4, or T6 compression +forces the output to be a bilevel image. +.TP +.B -b +Output a bilevel (GREY1) image. +.TP +.B -B +Output a grayscale (GREY2) image. +.TP +.B -g +Output a grayscale (GREY4) image. +.TP +.B -G +Output a grayscale (GREY8) image. +.TP +.B -h +Use Huffman compression. +.TP +.B -H +Use T4 one-dimensional compression. +.TP +.B -l +Use LZW compression. +.TP +.B -L +Use LZW compression with horizontal differencing. +Note that some TIFF decoders may not accept horizontal +differencing applied to images with depths less than eight. +.TP +.B -m +Output a color (CMAP8) image. +.TP +.B -p +Use Packbits compression. +.TP +.B -r +Output a color (BGR24) image. +.TP +.B -t +Use T4 two-dimensional compression. +.TP +.B -T +Use T6 compression. +.PP If there is only one input picture, .I togif converts the image to GIF format. diff --git a/sys/src/cmd/jpg/imagefile.h b/sys/src/cmd/jpg/imagefile.h index 04600e364..0030559a9 100644 --- a/sys/src/cmd/jpg/imagefile.h +++ b/sys/src/cmd/jpg/imagefile.h @@ -75,10 +75,12 @@ 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* writejpg(Biobuf*, Image*, char*, int, int); -char* memwritejpg(Biobuf*, Memimage*, char*, int, int); +char* writetif(Biobuf*, Image*, char*, int, int); +char* memwritetif(Biobuf*, Memimage*, char*, int, int); diff --git a/sys/src/cmd/jpg/mkfile b/sys/src/cmd/jpg/mkfile index 0455f2cc9..5a8fc085f 100644 --- a/sys/src/cmd/jpg/mkfile +++ b/sys/src/cmd/jpg/mkfile @@ -10,6 +10,7 @@ TARG=\ png\ topng\ tif\ + totif\ yuv\ ico\ toico\ @@ -54,9 +55,10 @@ $O.gif: $IMFILES readgif.$O gif.$O $O.togif: writegif.$O onechan.$O togif.$O torgbv.$O $O.ppm: $IMFILES readppm.$O ppm.$O $O.toppm: writeppm.$O multichan.$O toppm.$O -$O.png: $IMFILES readpng.$O png.$O +$O.png: torgbv.$O writerawimage.$O readpng.$O png.$O $O.topng: writepng.$O topng.$O -$O.tif: $IMFILES readtif.$O tif.$O +$O.tif: torgbv.$O writerawimage.$O readtif.$O tif.$O +$O.totif: writetif.$O totif.$O $O.yuv: $IMFILES readyuv.$O yuv.$O $O.bmp: $IMFILES readbmp.$O bmp.$O $O.v210: $IMFILES readv210.$O v210.$O diff --git a/sys/src/cmd/jpg/readtif.c b/sys/src/cmd/jpg/readtif.c index 51b15424d..f4c9b9daa 100644 --- a/sys/src/cmd/jpg/readtif.c +++ b/sys/src/cmd/jpg/readtif.c @@ -7,7 +7,7 @@ * http://www.itu.int/rec/T-REC-T.4-199904-S/en * http://www.itu.int/rec/T-REC-T.6-198811-I/en * -* fax codes and lzw: +* copy-pasted fax codes and lzw help: * http://www.remotesensing.org/libtiff/ */ #include <u.h> @@ -91,9 +91,10 @@ struct Fax { Tab *tab[2]; int ntab; /* position in tab */ Tab *eol; + int eolfill; int (*getbit)(Fax *); - int *l1; - int *l2; + ulong *l1; + ulong *l2; ulong nl; uchar *data; ulong next; /* next strip offset in data */ @@ -433,7 +434,7 @@ static void fillbits(Fax *); static int faxalloclines(Fax *); static Tab *getfax1d(Fax *, uchar *, ulong, ulong *, ulong *, ulong); static Tab *getfax2d(Fax *, uchar *, ulong, ulong *, ulong *, ulong); -static int faxstrip(Tif *, Fax *, uchar *, ulong, ulong *); +static int faxstrip(Tif *, Fax *, uchar *, ulong, ulong, ulong *); static uchar *fax(Tif *); static void tabinit(Lzw *); static Code *newcode(Lzw *, Code *); @@ -441,7 +442,8 @@ static void listadd(Lzw *, Code *); static Code *tabadd(Lzw *, Code *, Code *); static int getcode(Lzw *); static int wstr(uchar *, ulong, ulong *, Code *, long *); -static void predict(Tif *, uchar *); +static int predict1(Tif *, uchar *, ulong); +static int predict8(Tif *, uchar *, ulong); static int lzwstrip(Lzw *, uchar *, ulong, ulong *, long); static uchar *lzw(Tif *); static uchar *packbits(Tif *); @@ -669,6 +671,10 @@ geteol(Fax *f) Tab *p; if(f->eol == nil) { + if(f->eolfill) { + for(i = 0; i < 4; i++) + (*f->getbit)(f); + } if((p = gettab(f, 0)) == nil || p->run >= 0) { werrstr("first eol"); return nil; @@ -697,10 +703,8 @@ faxfill(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, ulong dx, werrstr("fax row overflow"); return -1; } - if((*i += n) >= size) { - werrstr("fax data overflow"); + if((*i += n) > size) return -1; - } if(f->st != 0) memset(data+*i-n, f->st, n); return 0; @@ -744,7 +748,7 @@ getfax1d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, int j, n; Tab *p; - for(j = 0; *x < dx;) { + for(j = n = 0; *x < dx;) { if((p = gettab(f, 0)) == nil) return nil; if((n = p->run) < 0) { @@ -760,6 +764,17 @@ getfax1d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, 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; } @@ -767,7 +782,8 @@ static Tab * getfax2d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, ulong dx) { - int j, k, n, code, len, a0, a1, b1, b2, v; + int j, k, n, code, len, v; + long a0, a1, b1, b2; Tab *p; a0 = -1; @@ -795,7 +811,10 @@ getfax2d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, for(k = 0; k < 2;) { if((p = gettab(f, 0)) == nil) return nil; - n = p->run; + if((n = p->run) < 0) { + werrstr("2d eol"); + return nil; + } if(faxfill(f, data, size, i, x, dx, n) < 0) return nil; @@ -842,7 +861,8 @@ getfax2d(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, } static int -faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong *i) +faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong rows, + ulong *i) { int d1; ulong x, y; @@ -850,7 +870,7 @@ faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong *i) d1 = t->comp != T6enc; p = nil; - for(x = y = 0; x < t->dx || y < t->rows;) { + for(x = y = 0; x < t->dx || y < rows;) { f->st = 0; if(t->comp == T4enc) { if(p == nil && geteol(f) == nil) { @@ -858,8 +878,11 @@ faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong *i) return -1; break; } - if(y > 0) + if(y > 0) { *i += t->dx - x; + if(*i > size) + break; + } if(t->t4 & 1) { d1 = (*f->getbit)(f); if(d1 < 0) @@ -878,32 +901,34 @@ faxstrip(Tif *t, Fax *f, uchar *data, ulong size, ulong *i) if(t->comp == Huffman) fillbits(f); if(p == nil && x != t->dx) { - if(f->st >= 0 || 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; } -/* -* the t4 fax test images i decoded did not follow the -* spec. in particular, they did not have rtcs. -*/ +/* i've encountered t4 images that did not have rtcs */ static uchar * fax(Tif *t) { int m; - ulong i, j, datasz, linesz; + ulong i, j, datasz, r, dy; uchar *data; Fax f; datasz = t->dx * t->dy * sizeof *data; - data = malloc(datasz); - f.nl = t->dx; - linesz = f.nl * sizeof *f.l1; - f.l1 = malloc(linesz); - f.l2 = malloc(linesz); + 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) @@ -914,9 +939,6 @@ fax(Tif *t) free(f.l2); return nil; } - memset(data, 0, datasz); - memset(f.l1, 0, linesz); - memset(f.l2, 0, linesz); if(t->fill == 1) { f.getbit = getbit1; m = 7; @@ -928,8 +950,12 @@ fax(Tif *t) 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; i < t->nstrips; i++) { + for(i = j = 0, dy = t->dy; i < t->nstrips; i++) { f.l1[0] = t->dx; f.n = t->strips[i]; f.m = m; @@ -937,8 +963,10 @@ fax(Tif *t) f.next = t->strips[i+1]; else f.next = t->ndata; - if(faxstrip(t, &f, data, datasz, &j) < 0) + 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); @@ -993,20 +1021,20 @@ tabadd(Lzw *l, Code *p, Code *q) { Code *r, *s; - if(l->ntab >= Tabsz) { - werrstr("lzw table full"); - return nil; - } - r = s = &l->tab[l->ntab++]; + r = s = &l->tab[l->ntab]; switch(l->ntab) { - case 511: - case 1023: - case 2047: + 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) @@ -1033,6 +1061,7 @@ getcode(Lzw *l) int i, c, code; if(l->n >= l->next) { +eof: werrstr("lzw eof"); return -1; } @@ -1042,8 +1071,9 @@ getcode(Lzw *l) code |= c << i; l->m--; if(l->m < 0) { - l->n++; l->m = 7; + if(++l->n >= l->next && i > 0) + goto eof; } } return code; @@ -1062,24 +1092,61 @@ wstr(uchar *data, ulong size, ulong *i, Code *p, long *striplen) return 0; } -static void -predict(Tif *t, uchar *data) +static int +predict1(Tif *t, uchar *data, ulong ndata) +{ + int bpl, pix, b[8], d, m, n, j; + ulong i, x, y; + + bpl = bytesperline(Rect(0, 0, t->dx, t->dy), t->depth); + d = t->depth; + m = (1 << d) - 1; + n = 8 / d; + for(y = 0; y < t->dy; y++) { + for(x = 0; x < bpl; x++) { + i = y*bpl + x; + if(i >= ndata) { + werrstr("pred4 overflow"); + return -1; + } + pix = data[i]; + b[n-1] = (pix >> d*(n-1)) & m; + if(x > 0) + b[n-1] += data[i-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[i] = pix; + } + } + return 0; +} + +static int +predict8(Tif *t, uchar *data, ulong ndata) { char a, b; - ulong y, x, i, j, k, s; + ulong i, j, s, x, y; s = t->samples; for(y = 0; y < t->dy; y++) { for(x = 1; x < t->dx; x++) { - i = y*t->dx + x; - for(j = 0; j < s; j++) { - k = i*s + j; - a = (char)data[k]; - b = (char)data[k-s]; - data[k] = (uchar)(a + b); + i = (y*t->dx + x) * s; + if(i+s-1 >= ndata) { + werrstr("pred8 overflow"); + return -1; + } + for(j = 0; j < s; i++, j++) { + a = (char)data[i]; + b = (char)data[i-s]; + data[i] = (uchar)(a + b); } } } + return 0; } static int @@ -1102,6 +1169,10 @@ lzwstrip(Lzw *l, uchar *data, ulong size, ulong *i, long striplen) 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; @@ -1113,13 +1184,16 @@ lzwstrip(Lzw *l, uchar *data, ulong size, ulong *i, long striplen) q = &l->tab[oc]; if(tabadd(l, q, p) == nil) return -1; - } else { + } 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; @@ -1132,15 +1206,16 @@ lzwstrip(Lzw *l, uchar *data, ulong size, ulong *i, long striplen) static uchar * lzw(Tif *t) { - ulong i, j, size; + ulong i, j, size, r, dy, n; long striplen; uchar *data; Lzw l; Code *p, *q; + int (*predict)(Tif *, uchar *, ulong); - i = t->dx * t->rows * t->depth; - striplen = i%8 == 0? i/8: i/8+1; - size = t->nstrips * striplen * sizeof *data; + n = t->dx * t->dy * t->depth; + n = n%8 == 0? n/8: n/8+1; + size = n * sizeof *data; if((data = malloc(size)) == nil) { free(t->data); return nil; @@ -1151,7 +1226,7 @@ lzw(Tif *t) } l.data = t->data; l.first = l.last = nil; - for(i = j = 0; i < t->nstrips; i++) { + for(i = j = 0, dy = t->dy; i < t->nstrips; i++) { tabinit(&l); l.n = t->strips[i]; l.m = 7; @@ -1159,8 +1234,12 @@ lzw(Tif *t) l.next = t->strips[i+1]; else l.next = t->ndata; + r = dy < t->rows? dy: t->rows; + n = t->dx * r * t->depth; + striplen = n%8 == 0? n/8: n/8+1; if(lzwstrip(&l, data, size, &j, striplen) < 0) break; + dy -= t->rows; } if(i < t->nstrips) { free(data); @@ -1177,8 +1256,16 @@ lzw(Tif *t) free(q); } free(t->data); - if(data != nil && t->predictor == 2) - predict(t, data); + if(data != nil && t->predictor == 2) { + if(t->depth < 8) + predict = predict1; + else + predict = predict8; + if((*predict)(t, data, size) < 0) { + free(data); + return nil; + } + } return data; } @@ -1189,7 +1276,9 @@ packbits(Tif *t) ulong i, j, k, size; uchar *data; - size = t->dx * t->dy * t->samples * sizeof *data; + i = t->dx * t->dy * t->depth; + i = i%8 == 0? i/8: i/8+1; + size = i * sizeof *data; if((data = malloc(size)) == nil) { free(t->data); return nil; @@ -1198,11 +1287,9 @@ packbits(Tif *t) n = (char)t->data[i++]; if(n >= 0) { k = n + 1; - if(j+k >= size || i+k >= t->ndata) + if((j += k) > size || (i += k) > t->ndata) break; - memmove(data+j, t->data+i, k); - i += k; - j += k; + 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) @@ -1461,18 +1548,18 @@ readfield(Tif *t, Fld *f) size = typesizes[f->typ]; if((n = size*f->cnt) <= 4) { for(i = 0; i < f->cnt; i++) - f->val[i] = readval(t); + f->val[i] = (*readval)(t); f->off = 0x0; f->nval = i; for(j = n; j < 4; j += size) - readval(t); + (*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->val[i] = (*readval)(t); f->nval = i; if(gototif(t, off) < 0) return -1; @@ -1600,10 +1687,6 @@ checkfields(Tif *t) werrstr("color map"); return -1; } - if(t->predictor == 2 && t->depth == 1) { - werrstr("depth too low for predictor 2"); - return -1; - } return 0; } @@ -1613,9 +1696,14 @@ readstrips(Tif *t) int i, j, n; ulong 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; diff --git a/sys/src/cmd/jpg/tif.c b/sys/src/cmd/jpg/tif.c index 6607efb93..91d77abc6 100644 --- a/sys/src/cmd/jpg/tif.c +++ b/sys/src/cmd/jpg/tif.c @@ -169,17 +169,15 @@ show(int fd, char *name, int outchan) if(array == nil || array[0] == nil) { if(array != nil) free(array); - fprint(2, "%s: decode %s failed: %r\n", argv0, - name); + fprint(2, "%s: decode %s failed: %r\n", + argv0, name); return "decode"; } Bterm(&b); if(!dflag) { if(init() < 0) return "initdraw"; -/* fixme: ppm doesn't check for outchan==CMAP8 */ - if(defaultcolor && screen->depth > 8 && - outchan == CMAP8) + if(defaultcolor && screen->depth > 8) outchan = RGB24; } r = array[0]; @@ -232,12 +230,10 @@ show(int fd, char *name, int outchan) argv0, name); return "write"; } - } else if(cflag) { - if(writerawimage(1, c) < 0) { - 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]); diff --git a/sys/src/cmd/jpg/totif.c b/sys/src/cmd/jpg/totif.c new file mode 100644 index 000000000..4d0ea8b1e --- /dev/null +++ b/sys/src/cmd/jpg/totif.c @@ -0,0 +1,157 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <memdraw.h> +#include "imagefile.h" + +static Memimage *memtochan(Memimage *, ulong); + +void +usage(void) +{ + fprint(2, "usage: %s [-c 'comment'] " + "[-bBgGhHlLmprtT] [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; + ulong chan; + + chan = BGR24; + chanflag = opt = 0; + comp = 1; + c = nil; + ARGBEGIN { + case 'b': + chan = GREY1; + chanflag = 1; + break; + case 'B': + chan = GREY2; + chanflag = 1; + break; + case 'c': + c = EARGF(usage()); + break; + case 'g': + chan = GREY4; + chanflag = 1; + break; + case 'G': + chan = GREY8; + chanflag = 1; + break; + case 'h': /* huffman */ + comp = 2; + break; + case 'H': /* t4 */ + comp = 3; + opt = 0; + break; + case 'l': /* lzw */ + comp = 5; + opt = 0; + break; + case 'L': /* lzw, horizontal differencing */ + comp = 5; + opt = 1; + break; + case 'm': /* palette */ + chan = CMAP8; + chanflag = 1; + break; + case 'p': /* packbits */ + comp = 0x8005; + break; + case 'r': /* force BGR24 */ + chan = BGR24; + chanflag = 1; + break; + case 't': /* t4 two-dimensional */ + comp = 3; + opt = 1; + break; + case 'T': /* t6 */ + comp = 4; + break; + default: + usage(); + } ARGEND + if(argc > 1) + usage(); + if(argc == 0) { + file = "<stdin>"; + 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, ulong 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/jpg/writetif.c b/sys/src/cmd/jpg/writetif.c new file mode 100644 index 000000000..2c02b5d44 --- /dev/null +++ b/sys/src/cmd/jpg/writetif.c @@ -0,0 +1,1341 @@ +/* +* code/documentation: +* http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +* http://paulbourke.net/dataformats/tiff/ +* 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 +* +* copy-pasted fax codes and copy-pasted lzw encoding +* hash table implementation: +* http://www.remotesensing.org/libtiff/ +*/ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <draw.h> +#include <memdraw.h> +#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; + ulong *l1; + ulong *l2; + uchar *data; + ulong ndata; + ulong n; +}; + +struct Hash { + long hash; + u16int code; +}; + +struct Lzw { + Hash hash[Hsize]; + int ntab; + int len; + int byte; + int nbyte; + uchar *data; + ulong ndata; + ulong n; +}; + +struct Pkb { + uchar *data; + ulong ndata; + ulong n; +}; + +struct Fld { + ushort tag; + ushort typ; +}; + +struct Tif { + ulong dx; + ulong dy; + ushort depth[3]; + ushort comp; + ulong opt; + char *(*compress)(Tif *); + ushort photo; + char *desc; + ulong *strips; + ulong nstrips; + ushort samples; + ulong rows; + ulong *counts; + ushort *color; + ulong ncolor; + uchar *data; + ulong ndata; + ushort 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} +}; + +/* +* 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] = { + {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, uchar 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, ulong 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, ulong dx) +{ + ulong 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, ulong dx) +{ + int j, v; + ulong 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, uchar *data, ulong n, ulong dx) +{ + int k, s, d1, two; + ulong 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) +{ + ulong i, m, n; + char *err; + uchar *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; + ulong i, x, y; + + d = *t->depth; + m = (1 << d) - 1; + n = 8 / d; + for(y = 0; y < t->dy; y++) { + for(x = t->bpl-1;; x--) { + i = y*t->bpl + x; + pix = t->data[i]; + for(j = 0; j < n; j++) { + b[j] = (pix >> d*j) & m; + if(j > 0) + b[j-1] -= b[j]; + } + if(x > 0) + b[n-1] -= t->data[i-1] & m; + for(j = pix = 0; j < n; j++) + pix |= (b[j] & m) << d*j; + t->data[i] = pix; + if(x == 0) + break; + } + } +} + +static void +predict8(Tif *t) +{ + ulong i, j, s, x, y; + + s = t->samples; + for(y = 0; y < t->dy; y++) { + for(x = t->dx-1; x >= 1; x--) { + i = (y*t->dx + x) * s; + for(j = 0; j < s; i++, j++) + t->data[i] -= t->data[i-s]; + } + } +} + +static char * +lzwstrip(Lzw *l, uchar *data, ulong n) +{ + int k, h; + long fcode, disp; + ulong i; + char *err; + Hash *hp; + u16int 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) +{ + ulong i, m, n; + char *err; + uchar *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, uchar *data, int ndata, long *buf) +{ + int b, repl; + long i, j, k, n; + + 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; + if(p->n+1+(k<0?1:b) > p->ndata) { + p->ndata *= 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) +{ + ulong i, j, n; + char *err; + uchar *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; + ulong i, count, n; + double a, b; + + count = t->ndata < 0x2000? t->ndata: 0x2000; + t->rows = count / t->bpl; + if(count%t->bpl != 0) + t->rows++; + if(t->comp == Tt4enc && t->opt) { + if((n = t->rows%Kpar) != 0) + t->rows += Kpar - n; + } + a = (double)t->dy; + b = (double)t->rows; + t->nstrips = (ulong)floor((a+b-1)/b); + 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, ulong cnt, ulong 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; + ulong 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; + uchar *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, + ulong 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); +} |