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 /sys/src/cmd/jpg/writetif.c | |
parent | 24a5720bec7a90e94ab13d3c32b27f39585a1039 (diff) |
readtif: fix many bugs
totif: add tiff encoder
Diffstat (limited to 'sys/src/cmd/jpg/writetif.c')
-rw-r--r-- | sys/src/cmd/jpg/writetif.c | 1341 |
1 files changed, 1341 insertions, 0 deletions
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); +} |