tif images. the bulk of the structure comes from ppatience0, but many of the operations were rewritten or taken from earlier work. this allows for more image types to be decoded. ycbcr images are not working right. Reference: /n/atom/patch/applied2013/tif Date: Mon Aug 5 22:13:28 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/jpg/totruecolor.c Mon Aug 5 22:10:59 2013 +++ /sys/src/cmd/jpg/totruecolor.c Mon Aug 5 22:26:47 2013 @@ -11,6 +11,13 @@ c4 = 3629, /* 1.772 * 2048 */ }; +enum { + Cyan, + Magenta, + Yellow, + Kblack, +}; + Rawimage* totruecolor(Rawimage *i, int chandesc) { @@ -157,6 +164,22 @@ *outp++ = r; } } + break; + case CMYK: + if(i->nchans != 1) + return _remaperror("remap: can't handle nchans %d", i->nchans); + for(j=0; jchanlen; j += 4){ + inp = i->chans[0] + j; + k = 255 - inp[Kblack]; + r = (255 - inp[Cyan]) * k/255; + g = (255 - inp[Magenta]) * k/255; + b = (255 - inp[Yellow]) * k/255; + *outp++ = b; + *outp++ = g; + *outp++ = r; + } + i->chanlen *= 3; + i->chanlen /= 4; break; } return im; --- /sys/src/cmd/jpg/readtif.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jpg/readtif.c Mon Aug 5 22:11:01 2013 @@ -0,0 +1,2485 @@ +/* +* 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 +* +* fax codes and lzw: +* http://www.remotesensing.org/libtiff/ +*/ +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum { + II = 0x4949, /* little-endian */ + MM = 0x4d4d, /* big-endian */ + TIF = 0x002a /* tiff magic number */ +}; + +enum { + /* ifd types */ + Ibyte = 1, + Iascii = 2, + Ishort = 3, + Ilong = 4, + Irat = 5, + Isbyte = 6, + Iundef = 7, + Isshort = 8, + Islong = 9, + Israt = 10, + Ifloat = 11, + Idbl = 12, +}; + +enum { + Znone = 1, + Zhuff = 2, + Zt4 = 3, + Zt6 = 4, + Zlzw = 5, + Zjpg = 6, + Zdeflateadobe = 8, + Znext = 32766, + Zccittrlew = 32771, + Zpackbits = 32773, // 0x8005, + Zdeflate = 32946, // 0x80b2, + Zthunderscan = 32809, + Zit8ctpad = 32895, + Zit8lw = 32896, + Zit8mp = 32897, + Zit8bl = 32898, + Zpixarfilm = 32908, + Zpixarlog = 32909, + Zdcs = 32947, + Zjbig = 34661, + Zsgilog = 34676, + Zsgilog24 = 34677, + Zjpg2000 = 34712, + + /* photometric interpretation */ + Pwhitezero = 0, + Pblackzero = 1, + Prgb = 2, + Ppalette = 3, + Ptransparency = 4, + Pseparated = 5, + Pycbcr = 6, + Pcielab = 8, + Picclab = 9, + Pitulab = 10, + Pcolorfiltarray = 32803, /* dng */ + Plogl = 32844, + Plogluv = 32845, + Plinearraw = 34892, + + Pchunky = 1, + Pplanar = 2, + + T42d = 1<<0, + T4noZ = 1<<1, + T4fileol = 1<<2, + + T6noZ = 1<<1, + + Shorizontal = 0, + Svertical = 1, +}; + +enum { + Tsubfiletype = 0x0fe, /* long */ + Twidth = 0x100, + Tlength = 0x101, + Tbitspersample = 0x102, + Tcompression = 0x103, + Tphotometric = 0x106, + Tthresholding = 0x107, + Tcellwidth = 0x108, + Tcelllength = 0x109, + Thwmodel = 0x110, + Tfillorder = 0x10a, + Tdocname = 0x10d, + Timagedesc = 0x10e, + Tstripoffsets = 0x111, + Torientation = 0x112, + Tsamplespp = 0x115, + Trowsperstrip = 0x116, + Tstripbytecounts = 0x117, /* after compression */ + Tminsampleval = 0x118, + Tmaxsampleval = 0x119, + Txresolution = 0x11a, /* Rational */ + Tyresolution = 0x11b, + Tplanarconf = 0x11c, + Txpos = 0x11e, + Typos = 0x11f, + T4opts = 0x124, + T6opts = 0x125, + Tresolutionunit = 0x128, + Tpageno = 0x129, + Tsoftware = 0x131, + Tdatetime = 0x132, + Tartist = 0x13b, + Tcomputer = 0x13c, + Tpredictor = 0x13d, + Tcolormap = 0x140, + Thalftonehint = 0x141, + Ttilewidth = 0x142, + Ttilelength = 0x143, + Ttileoffsets = 0x144, + Tbadfaxlines = 0x146, + Tcleanfax = 0x147, + Tbadfaxlinesrun = 0x148, + Textrasamples = 0x152, /* 1 == premult α, 2 matte */ + Tsamplefmt = 0x153, + Tclippath = 0x157, /* wierd adobe */ + Txclippathunits = 0x158, + Tyclippathunits = 0x159, + Tindexed = 0x15a, /* extends pallate images to any color space */ + + Tjpegproc = 0x200, /* 1 == sequential, 14 == huffman */ + Tjpegichgfmt = 0x201, + Tjpegichgfmtlen = 0x202, + Tjpegrstartival = 0x203, + Tjpegpredictor = 0x204, + Tjpegxform = 0x205, + Tjpegqtable = 0x207, + Tjpegdctable = 0x208, + Tjpegactable = 0x209, + Tycbcrcoeff = 0x211, + Tycbcrsubsamp = 0x212, + Tycbcrpos = 0x213, + Tycbcrrefbw = 0x214, + Tstriprowcounts = 0x22c, + Txmp = 0x2bc, + + Tcopyright = 0x8298, + Tphotoshop = 0x8649, + Texifid = 0x8769, + Ticcprofile = 0x8773, + Tgps = 0x8825, +}; + +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 { + uint n; + int m; + int st; /* state */ + Tab *tab[2]; + int ntab; /* position in tab */ + Tab *eol; + int (*getbit)(Fax *); + int *l1; + int *l2; + uint nl; + uchar *data; + uint next; /* next strip offset in data */ +}; + +struct Code { + uchar val; + Code *next; +}; + +struct Lzw { + Code tab[Tabsz]; + int ntab; + int len; /* code length */ + uint n; + int m; + uchar *data; + uint next; /* next strip offset in data */ + /* remaining allocated codes */ + Code *first; + Code *last; +}; + +struct Fld { + uint tag; + uint typ; + uint cnt; + uint off; /* value offset */ + uint *val; + uint nval; +}; + +struct Tif { + Biobuf *fd; + uint end; /* endianness */ + uchar tmp[4]; + uchar *buf; + uint nbuf; + int eof; + uint n; /* offset in buf array */ + uint off; + uint nfld; + Fld *fld; + uint (*byte2)(uchar *); + uint (*byte4)(uchar *); + + /* field data */ + uint dx; + uint dy; + uint depth; + char cdepth[8]; + int ncolors; + uint comp; + uchar *(*uncompress)(Tif *); + uint orientation; + uint photo; + int (*decode)(Tif *, Rawimage *, uchar *); + uint fill; + uint *strips; + uint nstrips; + uint samples; + uint rows; + uint *counts; + uint ncounts; + uint planar; + uint *colormap; + uint ncolormap; + uint t4; + uint t6; + uint predictor; + uint samplefmt; + uint subsample[2]; + + /* image data */ + uchar *data; + uint ndata; +}; + +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[] = { +[Ibyte] 1, +[Iascii] -1, +[Ishort] 2, +[Ilong] 4, +[Irat] 8, +[Isbyte] 1, +[Iundef] 0, +[Isshort] 2, +[Islong] 4, +[Israt] 8, +[Ifloat] 4, +[Idbl] 8, +}; + +static int vcodeval[] = {0, 0, 0, 1, 0, 0, 2, 3}; + +static uint byte2le(uchar *); +static uint byte4le(uchar *); +static uint byte2be(uchar *); +static uint byte4be(uchar *); +static void readdata(Tif *, uint); +static void readnbytes(Tif *, uint); +static uint readbyte(Tif *); +static uint readshort(Tif *); +static uint readlong(Tif *); +static int gototif(Tif *, ulong); +static int readheader(Tif *); +static uchar *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 *, uchar *, uint, uint *, uint *, uint, int); +static void fillbits(Fax *); +static int faxalloclines(Fax *); +static Tab *getfax1d(Fax *, uchar *, uint, uint *, uint *, uint); +static Tab *getfax2d(Fax *, uchar *, uint, uint *, uint *, uint); +static int faxstrip(Tif *, Fax *, uchar *, uint, uint *); +static uchar *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(uchar *, ulong, ulong *, Code *, long *); +static void predict(Tif *, uchar *); +static int lzwstrip(Lzw *, uchar *, ulong, ulong *, long); +static uchar *lzw(Tif *); +static uchar *packbits(Tif *); +static int faxdecode(Tif *, Rawimage *, uchar *); +static int greydecode(Tif *, Rawimage *, uchar *); +static int rgbdecode(Tif *, Rawimage *, uchar *); +static int paldecode(Tif *, Rawimage *, uchar *); +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 uint +byte2le(uchar *buf) +{ + return (buf[1] << 8) | buf[0]; +} + +static uint +byte4le(uchar *buf) +{ + return (byte2le(buf+2) << 16) | byte2le(buf); +} + +static uint +byte2be(uchar *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static uint +byte4be(uchar *buf) +{ + return (byte2be(buf) << 16) | byte2be(buf+2); +} + +static void +readdata(Tif *t, uint offset) +{ + long n, m; + ulong size; + + if(offset < t->nbuf) + offset = t->nbuf; + m = offset + 4096 - t->nbuf; + size = m + t->nbuf; + if((t->buf = realloc(t->buf, size)) == nil) + sysfatal("readtif: realloc: %r"); + if((n = Bread(t->fd, t->buf+t->nbuf, m)) < 0) + sysfatal("readtif: Bread: %r"); + if(n != m) + t->eof = 1; + t->nbuf += n; +} + +static void +readnbytes(Tif *t, uint n) +{ + if(n ==0 || n > sizeof t->tmp) + sysfatal("readtif: cannot read %ud bytes", n); + if(t->n+n > t->nbuf) { + if(t->eof) + sysfatal("readtif: reached end of file"); + readdata(t, 0); + } + memmove(t->tmp, t->buf+t->n, n); + t->n += n; +} + +static uint +readbyte(Tif *t) +{ + readnbytes(t, 1); + return t->tmp[0]; +} + +static uint +readshort(Tif *t) +{ + readnbytes(t, 2); + return (*t->byte2)(t->tmp); +} + +static uint +readlong(Tif *t) +{ + readnbytes(t, 4); + return (*t->byte4)(t->tmp); +} + +static int +gototif(Tif *t, ulong n) +{ + if(n < 8) { + werrstr("readtif: 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("readtif: illegal byte order: %#.4x", t->end); + return -1; + } + if((n = readshort(t)) != TIF) { + werrstr("readtif: illegal tiff magic: %#.4x", n); + return -1; + } + t->off = readlong(t); + return gototif(t, t->off); +} + +static uchar * +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("readtif: faxdecode: code not found"); + return nil; +} + +static Tab * +geteol(Fax *f) +{ + int i, bit; + Tab *p; + + if(f->eol == nil) { + if((p = gettab(f, 0)) == nil || p->run >= 0) { + werrstr("readtif: faxdecode: first eol not found"); + 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("readtif: faxdecode: eol not found"); + return nil; + } + return f->eol; +} + +static int +faxfill(Fax *f, uchar *data, uint size, uint *i, uint *x, uint dx, int n) +{ + if((*x += n) > dx) { + werrstr("readtif: fax row overflow"); + return -1; + } + if((*i += n) >= size) { + werrstr("readtif: fax data overflow"); + 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, uchar *data, uint size, uint *i, uint *x, uint dx) +{ + int j, n; + Tab *p; + + for(j = 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); + } + return nil; +} + +static Tab * +getfax2d(Fax *f, uchar *data, uint size, uint *i, uint *x, uint dx) +{ + int j, k, n, code, len, a0, a1, b1, b2, v; + 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; + } + 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; + n = p->run; + 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("readtif: fax2d: bad 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; + } + if(j >= f->nl) + faxalloclines(f); + a0 = *x; + } + memmove(f->l1, f->l2, j*sizeof *f->l1); + return nil; +} + +static int +faxstrip(Tif *t, Fax *f, uchar *data, uint size, uint *i) +{ + int d1; + uint x, y; + Tab *p; + + d1 = t->comp != Zt6; + p = nil; + for(x = y = 0; x < t->dx || y < t->rows;) { + f->st = 0; + if(t->comp == Zt4) { + if(p == nil && geteol(f) == nil) { + if(f->st >= 0) + return -1; + break; + } + if(y > 0) + *i += t->dx - x; + 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 == Zhuff) + fillbits(f); + if(p == nil && x != t->dx) { + if(f->st >= 0 || x > t->dx) + return -1; + break; + } + } + return 0; +} + +/* +* the t4 fax test images i decoded did not follow the +* spec. in particular, they did not have rtcs. +*/ +static uchar * +fax(Tif *t) +{ + int m; + uint i, j, datasz, linesz; + uchar *data; + Fax f; + + datasz = t->dx * t->dy; + data = malloc(datasz); + f.nl = t->dx; + linesz = f.nl * sizeof *f.l1; + f.l1 = malloc(linesz); + f.l2 = malloc(linesz); + if(data == nil || f.l1 == nil || f.l2 == nil) { + free(t->data); + free(data); + free(f.l1); + 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; + } else { + f.getbit = getbit2; + m = 0; + } + f.tab[0] = faxwhite; + f.tab[1] = faxblack; + f.ntab = 0; + f.eol = nil; + f.data = t->data; + j = 0; + for(i = 0; 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; + if(faxstrip(t, &f, data, datasz, &j) < 0) + break; + } + 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; + + if(l->ntab >= Tabsz) { + werrstr("readtif: lzw: table full"); + return nil; + } + r = s = &l->tab[l->ntab++]; + switch(l->ntab) { + case 511: + case 1023: + case 2047: + l->len++; + break; + default: + break; + } + 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) { + werrstr("readtif: lzw: input overflow"); + 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->n++; + l->m = 7; + } + } + return code; +} + +static int +wstr(uchar *data, ulong size, ulong *i, Code *p, long *striplen) +{ + for(; p != nil; p = p->next){ + if(*i >= size || *striplen < 0) { + werrstr("readtif: lzw overflow %ld %ld %ld", *i, size, *striplen); + return -1; + } + data[*i] = p->val; + ++*i; + --*striplen; + } + return 0; +} + +static void +predict(Tif *t, uchar *data) +{ + char a, b; + uint y, x, i, j, k, s; + + 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); + } + } + } +} + +static int +lzwstrip(Lzw *l, uchar *data, ulong size, ulong *i, long striplen) +{ + int c, oc; + Code *p, *q; + + if((c = getcode(l)) != Clrcode) { + werrstr("readtif: lzw: missing 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(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 { + q = &l->tab[oc]; + if((p = tabadd(l, q, q)) == nil) + return -1; + if(wstr(data, size, i, p, &striplen) < 0) + return -1; + } + if(striplen <= 0) + break; + oc = c; + c = getcode(l); + } + return 0; +} + +static uchar * +lzw(Tif *t) +{ + ulong i, j, size; + long striplen; + uchar *data; + Lzw l; + Code *p, *q; + + i = t->dx * t->rows * t->depth; + striplen = i+7>>3; + + size = t->nstrips * striplen; + 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; + j = 0; + for(i = 0; 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; + if(lzwstrip(&l, data, size, &j, striplen) < 0) + break; + j = i*striplen; /* BUG: j can be off by one unless reset */ + } + 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) + predict(t, data); + return data; +} + +static uchar* +compflate(Tif *t) +{ + uchar *data; + uint i, oo, io, size; + int striplen, r; + + i = t->dx * t->rows * t->depth; + striplen = i%8 == 0? i/8: i/8+1; + size = t->nstrips * striplen; + if((data = malloc(size)) == nil) { + free(t->data); + return nil; + } + inflateinit(); + io = 0; + oo = 0; + for(i = 0; i < t->nstrips; i++){ + r = inflateblock(data+oo, size-oo, t->data+io+2, t->counts[i]-2); + if(r < 0){ + werrstr("readtif: inflateblock failed: %d", r); + break; + } + oo += r; + io += t->counts[i]; + } + if(i < t->nstrips) { + free(data); + data = nil; + } + return data; +} + +static uchar * +packbits(Tif *t) +{ + uchar *data, *p, c; + int n, l; + + l = t->dx * t->dy * t->samples; + if((data = malloc(l)) == nil) { + free(t->data); + return nil; + } + p = t->data; + for(n = 0; n != l; ){ + c = *p++; + if(c < 128){ + memcpy(data+n, p, c+1); + p += c+1; + }else if(c > 128){ + c = 256-c; + memset(data+n, *p++, c+1); + }else + continue; + n += c+1; + } + free(t->data); + return data; +} + +static int +faxdecode(Tif *t, Rawimage *im, uchar *data) +{ + ulong n; + + for(n = 0; n < im->chanlen; n++) { + if(t->photo == Pwhitezero) + data[n] ^= 1; + im->chans[0][n] = data[n] * 0xff; + } + return 0; +} + +typedef struct Bit Bit; +struct Bit { + uint r0; /* register */ + uint r0bits; + uchar *data; + uint ndata; + uint i; + int err; +}; + +static void +bitinit(Bit *b, uchar *data, uint ndata) +{ + memset(b, 0, sizeof *b); + b->data = data; + b->ndata = ndata; +} + +static uint masks[33] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, + 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, + 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, + 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff, +}; + +static uint +bitget(Bit *b, int nbits) +{ + uint d, r, m; + + /* load enough bits */ + for(; b->r0bits < nbits; b->r0bits += 8){ + if(b->i == b->ndata){ + b->err = 1; + return 0; + } + b->r0 = b->r0<<8 | b->data[b->i++]; + } + + /* unload top bits */ + m = masks[nbits]; + d = b->r0bits - nbits; + r = (b->r0 >> d) & m; + b->r0 &= masks[32-nbits]; + b->r0bits = d; + + return r; +} + +static void +bitnewline(Bit *b, uint idx) +{ + b->i = idx; + b->r0 = 0; + b->r0bits = 0; +} + +static void +bitput(Bit *b, int nbits, uint bits) +{ + if(nbits == 8 && b->r0bits == 0){ + b->data[b->i++] = bits; + return; + } + abort(); +} + +static void +bitputtail(Bit *b) +{ + if(b->r0bits == 0) + return; + assert(b->r0bits < 8); + b->data[b->i++] = b->r0; + b->r0 = 0; + b->r0bits = 0; +} + +static int +greydecode(Tif *t, Rawimage *im, uchar *data) +{ + uint pix, x, y, dlen; + Bit b, o; + + if(t->depth > 32){ + werrstr("readtif: greydecode: depth > 32"); + return -1; + } + + dlen = bytesperline(im->r, t->depth)*im->r.max.y; /* should pass in */ + bitinit(&b, data, dlen); + bitinit(&o, im->chans[0], im->chanlen); + + for(y = 0; y < t->dy; y++) { + bitnewline(&b, y * bytesperline(im->r, t->depth)); /* odd-length line? */ + for(x = 0; x < t->dx; x++) { + pix = bitget(&b, t->depth); + if(b.err){ + werrstr("readtif: grey overflow"); + return -1; + } + if(t->photo == Pwhitezero) + pix ^= masks[t->depth]; + if(t->depth >= 8) + pix >>= t->depth - 8; + else + pix = (pix * 0xff) / masks[t->depth]; + bitput(&o, 8, pix); + } + bitputtail(&o); + } + return 0; +} + +static void +colordump(Tif *t) +{ + int i; + + fprint(2, "rgbdecode %d; ", t->depth); + for(i = 0; i < t->ncolors; i++) + fprint(2, "%d ", t->cdepth[i]); + fprint(2, "\n"); +} + +/* don't abort if depth > 32 */ +static int +mybytesperline(Rectangle r, int d) +{ + ulong l, t; + + if(r.min.x >= 0){ + l = (r.max.x*d+7)/8; + l -= (r.min.x*d)/8; + }else{ /* make positive before divide */ + t = (-r.min.x*d+7)/8; + l = t+(r.max.x*d+7)/8; + } + return l; +} + +static int +rgbdecode(Tif *t, Rawimage *im, uchar *data) +{ + uint pix[4], x, y, i, d, ochan, dlen, *map; + Bit b, o; + static uint rgbmap[] = {2, 1, 0}; + static uint cmykmap[] = {0, 1, 2, 3}; + +// colordump(t); + + ochan = 3; + map = rgbmap; + if(im->chandesc == CMYK){ + ochan = 4; + map = cmykmap; + } + + if(t->dy*t->dx*ochan != im->chanlen){ + werrstr("readtif: rgbdecode: rawimage has incorrect size"); + return -1; + } + + dlen = mybytesperline(im->r, t->depth)*im->r.max.y; /* should pass in */ + bitinit(&b, data, dlen); + bitinit(&o, im->chans[0], im->chanlen); + + for(y = 0; y < t->dy; y++) { + bitnewline(&b, y * mybytesperline(im->r, t->depth)); /* odd-length line? */ + for(x = 0; x < t->dx; x++) { + for(i = 0; i < t->ncolors; i++){ + d = t->cdepth[i]; + pix[i] = bitget(&b, d); + if(b.err){ + werrstr("readtif: rgb overflow"); + return -1; + } + if(d > 8) + pix[i] >>= d - 8; + else + pix[i] <<= 8 - d; + } + for(i = 0; i < ochan; i++) + bitput(&o, 8, pix[map[i]]); + } + bitputtail(&o); + } + return 0; +} + +static int +paldecode(Tif *t, Rawimage *im, uchar *data) +{ + uchar m[3*65536]; + uint pix, dlen, csamp, i, x, y, *s; + Bit b, o; + static uint rgbmap[] = {2, 1, 0}; + + if(t->depth > 16){ + werrstr("readtif: paldecode: colordepth %d to large", t->depth); + return -1; + } + if(t->dy*t->dx*3 != im->chanlen){ + werrstr("readtif: paldecode: rawimage has incorrect size"); + return -1; + } + + csamp = 1<depth; + + /* downsample to 8 bit colormap */ + s = t->colormap; + for(i = 0; i < 3*csamp; i++) + m[i] = (s[i]>>8) + ((s[i]&128)>>7); + + dlen = mybytesperline(im->r, t->depth)*im->r.max.y; /* should pass in */ + bitinit(&b, data, dlen); + bitinit(&o, im->chans[0], im->chanlen); + + for(y = 0; y < t->dy; y++) { + bitnewline(&b, y * mybytesperline(im->r, t->depth)); /* odd-length line? */ + for(x = 0; x < t->dx; x++) { + pix = bitget(&b, t->depth); + if(b.err){ + werrstr("readtif: pallate overflow"); + return -1; + } + for(i = 0; i < 3; i++) + bitput(&o, 8, m[pix + rgbmap[i]*csamp]); + } + bitputtail(&o); + } + return 0; +} + +/* + * totruecolor() needs to be extended to handle + * n-bit color maps that map to m-bits, for m != + * component color depth to use this version. + */ +static int +xxpaldecode(Tif *t, Rawimage *im, uchar *data) +{ + uchar m[3*65536]; + int i; + uint *s, csamp; + Rawimage *im2; + + csamp = 1<depth; + if(csamp*3 > sizeof m){ + werrstr("readtif: paldecode: pallette too deep: %d bits", csamp); + return -1; + } + if(t->dy*t->dx*3 != im->chanlen){ + werrstr("readtif: paldecode: rawimage has incorrect size"); + return -1; + } + + /* + * downsample to 8 bit colormap, and convert from striped + * to interleaved colors. + */ + s = t->colormap; + for(i = 0; i < csamp; i++) { + m[3*i + 0] = s[i + 0*csamp]>>8; + m[3*i + 1] = s[i + 1*csamp]>>8; + m[3*i + 2] = s[i + 2*csamp]>>8; + } + + im->cmap = m; + im->cmaplen = 3*csamp; + im->chans[0] = data; + im->chandesc = CRGB1; /* why isn't this set already? */ + im2 = totruecolor(im, CRGB24); + if(im2 == nil){ + sysfatal("readtif: decode fails: %r"); + return -1; + } + im->cmap = nil; + im->cmaplen = 0; + im->chanlen = im2->chanlen; + im->chans[0] = im2->chans[0]; + + return 0; +} + +static int +parsefield(Tif *t, Fld *f) +{ + ulong v; + int i; + + v = f->val[0]; + switch(f->tag) { + case Twidth: + t->dx = v; + break; + case Tlength: + t->dy = v; + break; + case Tbitspersample: + t->depth = 0; + t->ncolors = f->cnt; + for(i = 0; i < f->cnt; i++){ + t->cdepth[i] = f->val[i]; + t->depth += f->val[i]; + } + break; + case Tcompression: + t->comp = v; + break; + case Tphotometric: + t->photo = v; + switch(t->photo) { + case Pwhitezero: + case Pblackzero: + t->decode = greydecode; + break; + case Pseparated: + t->planar = Pplanar; + case Prgb: + case Pycbcr: + t->decode = rgbdecode; + break; + case Ppalette: + t->decode = paldecode; + break; + default: + fprint(2, "readtif: unknown photometric %d\n", t->photo); + break; + } + break; + case Tstripoffsets: + t->strips = f->val; + t->nstrips = f->cnt; + break; + case Tfillorder: + t->fill = v; + break; + case Torientation: + t->orientation = v; + break; + case Tsamplespp: + t->samples = v; + break; + case Trowsperstrip: + t->rows = v; + break; + case Tstripbytecounts: + t->counts = f->val; + t->ncounts = f->cnt; + break; + case Tplanarconf: + t->planar = v; + break; + case T4opts: + t->t4 = v; + break; + case T6opts: + t->t6 = v; + break; + case Tpredictor: + t->predictor = v; + break; + case Tcolormap: + t->colormap = f->val; + t->ncolormap = f->cnt; + break; + case Tsamplefmt: + t->samplefmt = v; + for(i = 0; i < f->cnt; i++){ + if( f->val[i] != v){ + werrstr("readtif: mixed sample formats"); + return -1; + } + } + break; + case Tycbcrsubsamp: + if(f->cnt != 2){ + werrstr("readtif: ycbcr: bad subsampling"); + return -1; + } +fprint(2, "f->val[0] %d f->val[1] %d\n", f->val[0], f->val[1]); + t->subsample[0] = f->val[0]; + t->subsample[1] = f->val[1]; + break; + default: + werrstr("readtif: shouldn't reach"); + return -1; + } + return 0; +} + +static int +readfield(Tif *t, Fld *f) +{ + int size; + uint i, j, n, off; + uint (*readval)(Tif *); + + f->tag = readshort(t); + f->typ = readshort(t); + f->cnt = readlong(t); + f->val = nil; + switch(f->tag) { + case Twidth: + case Tlength: + case Tcompression: + case Tphotometric: + case Tfillorder: + case Torientation: + case Tsamplespp: + case Trowsperstrip: + case Tplanarconf: + case T4opts: + case T6opts: + case Tpredictor: + if(f->cnt != 1) { + werrstr("readtif: field count not 1: %d cnt %d", f->tag, f->cnt); + return -1; + } + break; + case Tbitspersample: + switch(f->cnt){ + case 1: + case 3: + case 4: + break; + default: + werrstr("readtif: too many channels: bitspersample=%d", f->cnt); + return -1; + } + break; + case Tsamplefmt: + break; + case Ttilewidth: + case Ttilelength: + case Ttileoffsets: + werrstr("readtif: tiled images not supported"); + return -1; + case Tstripoffsets: + case Tstripbytecounts: + case Tcolormap: + case Tycbcrsubsamp: + break; + case Tsubfiletype: + case Tthresholding: + case Tcellwidth: + case Tcelllength: + case Thwmodel: + case Tdocname: + case Thalftonehint: + case Tbadfaxlines: + case Tcleanfax: + case Tbadfaxlinesrun: + case Timagedesc: + case Tminsampleval: + case Tmaxsampleval: + case Txresolution: + case Tyresolution: + case Txpos: + case Typos: + case Tresolutionunit: + case Tpageno: + case Tsoftware: + case Tdatetime: + case Tartist: + case Tcomputer: + case Textrasamples: + case Tclippath: + case Txclippathunits: + case Tyclippathunits: + case Tjpegproc: + case Tjpegichgfmt: + case Tjpegichgfmtlen: + case Tjpegrstartival: + case Tjpegpredictor: + case Tjpegxform: + case Tjpegqtable: + case Tjpegdctable: + case Tjpegactable: + case Tycbcrcoeff: + case Tycbcrpos: + case Tycbcrrefbw: + case Tstriprowcounts: + case Txmp: + case Tcopyright: + case Tphotoshop: + case Texifid: + case Ticcprofile: + case Tgps: + readlong(t); + return 0; + default: +fprint(2, "f->tag %ux\n", f->tag); + readlong(t); + return 0; + } + switch(f->typ) { + case Ibyte: + readval = readbyte; + break; + case Ishort: + readval = readshort; + break; + case Ilong: + readval = readlong; + break; + default: + werrstr("readtif: readfield: unsupported type %d", f->typ); + 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 = 0; + 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) +{ + double a, b; + uint n, size; + + if(t->dx == 0 || t->dy == 0){ + werrstr("readtif: bad rectangle: %d.%d", t->dx, t->dy); + return -1; + } + switch(t->comp) { + case Znone: + t->uncompress = nocomp; + break; + case Zhuff: + case Zt4: + case Zt6: + t->uncompress = fax; + if(t->decode != nil) + t->decode = faxdecode; + if((t->comp == Zt4 && t->t4 & T4noZ) || + (t->comp == Zt6 && t->t6 & T6noZ)) { + werrstr("readtif: t4/t6]: can't decode uncompressed images"); + return -1; + } + break; + case Zlzw: + t->uncompress = lzw; + break; + case Zpackbits: + t->uncompress = packbits; + break; + case Zdeflate: + case Zdeflateadobe: + t->uncompress = compflate; + break; + default: + werrstr("readtif: compression type unknown: %d", t->comp); + return -1; + } + if(t->decode == nil) { + werrstr("readtif: photometric interpretation unknown"); + return -1; + } + if(t->depth > 1 && (t->comp == Zhuff || t->comp == Zt4 || t->comp == Zt6)) { + werrstr("readtif: compression Zhuff/Zt4/Zt6 illegal when depth>1"); + return -1; + } + if(t->fill != 1 && t->fill != 2) { + werrstr("readtif: unknown fill order %d", t->fill); + return -1; + } + if(t->fill == 2 && t->depth != 1) { + werrstr("readtif: depth should be 1 with fill order 2"); + return -1; + } + if(t->orientation != 1) { + werrstr("readtif: orientation != 1"); + return -1; + } + if(t->rows == 0) { + werrstr("readtif: zero rows per strip"); + return -1; + } + if(t->planar != Pchunky && t->planar != Pplanar){ + werrstr("readtif: planar configuration %d unsupported", t->planar); + return -1; + } + if(t->planar == Pplanar) { + if(t->strips == nil || t->nstrips % t->ncolors){ + werrstr("readtif: strip offsets %d != %d (planar 2)", t->nstrips, t->ncolors); + return -1; + } + }else{ + /* could reasonably be a multiple */ + a = (double)t->dy; + b = (double)t->rows; + n = (uint)floor((a+b-1)/b); + if(t->strips == nil || t->nstrips != n) { + werrstr("readtif: strip offsets %d != %d", n, t->nstrips); + return -1; + } + } + if(t->photo == Prgb || t->photo == Pycbcr || t->photo == Pseparated){ + if(t->samples < 3){ + werrstr("readtif: not enough samples per pixel"); + return -1; + } + }else{ + if(t->samples != 1){ + werrstr("readtif: too many 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 == Znone && + t->nstrips == 1 && + (t->counts = malloc(size)) != nil) { + n = t->dx * t->dy * t->depth; + t->counts[0] = n%8 == 0? n/8: n/8+1; + t->ncounts = t->nstrips; + } + if(t->counts == nil || t->ncounts != t->nstrips) { + werrstr("readtif: bad strip byte counts"); + return -1; + } + if(t->photo == Ppalette) + if((t->colormap == nil || t->ncolormap != 3*(1<depth))) { + werrstr("readtif: bad color map"); + return -1; + } + if(t->predictor == 2 && t->depth == 1) { + werrstr("readtif: depth too low for predictor 2"); + return -1; + } + return 0; +} + +static int +readstrips(Tif *t) +{ + int i, j, n; + ulong off; + + t->ndata = 0; + for(i = 0; i < t->nstrips; i++) + t->ndata += t->counts[i]; + if((t->data = malloc(t->ndata)) == nil) + return -1; + off = t->n; + n = 0; + for(i = 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++) + t->data[n++] = readbyte(t); + } + return gototif(t, off); +} + +static Rawimage* +cvterr(Rawimage *im, char *err) +{ + werrstr(err); + free(im->chans[0]); + free(im); + return nil; +} + +static Rawimage* +chanswizzle(Rawimage *im, int ncolors) +{ + uchar *c, *p[4]; + int i, j, npix, *cmap; + static int maptab[9] = {0, 0, 0, 1, 0, 0, 2, 1, 0}; + static int cmykmap[] = {0, 1, 2, 3}; + + if(ncolors == 1) + return im; + if(im->chandesc == CMYK && ncolors == 4) + cmap = cmykmap; + else if(ncolors > 3) + return cvterr(im, "readtif: chanswizzle: too many channels to swizzle"); + else + cmap = maptab + 3*(ncolors - 1); + + npix = im->r.max.x * im->r.max.y; + c = malloc(npix * ncolors); + if(c == nil) + return cvterr(im, "readtif: chanswizzle: malloc: %r"); + for(i = 0; i < ncolors; i++) + p[i] = im->chans[0] + i*npix; + for(i = 0; i < npix; i++) + for(j = 0; j < ncolors; j++) + c[i*ncolors + cmap[j]] = p[j][i]; + + free(im->chans[0]); + im->chans[0] = c; + return im; +} + +static Rawimage* +xxycbcrify(Tif*, Rawimage *im) +{ + uchar *y, *cb, *cr, *p; + int i, npix; + + fprint(2, "readtiff: warning: YCbCr: output incorrect\n"); + npix = im->r.max.x * im->r.max.y; + y = malloc(npix * 3); + if(y == nil){ + free(im->chans[0]); + free(im); + return nil; + } + cb = y + 1*npix; + cr = y + 2*npix; + + p = im->chans[0]; + for(i = 0; i < npix; i++){ + y[i] = p[3*i + 0]; + cb[i] = p[3*i + 1]; + cr[i] = p[3*i + 2]; + } + + free(im->chans[0]); + im->nchans *= 3; + im->chanlen /= 3; + im->chans[2] = y; + im->chans[1] = cb; + im->chans[0] = cr; + return totruecolor(im, CRGB24); +} + +static Rawimage* +ycbcrify(Tif *t, Rawimage *im) +{ + uchar *y, *cb, *cr, *p; + int i, npix; + + fprint(2, "readtiff: subsample %d %d\n", t->subsample[0], t->subsample[1]); + npix = im->r.max.x * im->r.max.y; + y = malloc(npix * 3); + if(y == nil){ + free(im->chans[0]); + free(im); + return nil; + } + memset(y, 0, npix*3); + cb = y + 1*npix; + cr = y + 2*npix; + + p = im->chans[0]; + if(t->subsample[0] == 1 && t->subsample[1] == 1){ + for(i = 0; i < npix; i++){ + y[i] = p[3*i + 0]; + cb[i] = p[3*i + 1]; + cr[i] = p[3*i + 2]; + } + }else{ +//#ifdef notdef + uint h, v, ysz, bsz, nblock, bcb, bcr, linesz, n; + + ysz = t->subsample[Svertical]*t->subsample[Shorizontal]; + nblock = (npix+ysz-1)/ysz; + bsz = ysz + 2; +fprint(2, "ysz %d bsz %d\n", ysz, bsz); + linesz = bsz*im->r.max.y/ysz; + n = 0; +fprint(2, "npix %d\n", npix); + for(i = 0; i < nblock*bsz; i += bsz){ + bcb = p[i + ysz + 0]; + bcr = p[i + ysz + 1]; + + for(v = 0; v < t->subsample[Svertical]; v += linesz){ + for(h = 0; h < t->subsample[Shorizontal]; h++){ + y[n] = p[i + v + h]; + cb[n] = bcb; + cr[n] = bcr; + n++; + } + } + } +//#endif +#ifdef notdef + uint bcb, bcr, yy, x, block, ssx, ssy, n, by2ch2line, y2block; + + y2block = t->subsample[Shorizontal]*t->subsample[Svertical]; + by2ch2line = bytesperline(im->r, 8); /* separated channels */ + block = 0; +// goto test; +// goto test1; + for(yy = 0; yy < t->dy; yy += t->subsample[Svertical]){ + for(x = 0; x < t->dx; x += t->subsample[Shorizontal]){ + bcb = p[block + y2block +0]; + bcr = p[block + y2block + 1]; + n = 0; + for(ssy = 0; ssy < t->subsample[Svertical]; ssy++){ + for(ssx = 0; ssx < t->subsample[Shorizontal]; ssx++){ + y[(yy+ssy)*by2ch2line + (x+ssx)] = p[block + n]; + cb[(yy+ssy)*by2ch2line + (x+ssx)] = bcb; + cr[(yy+ssy)*by2ch2line + (x+ssx)] = bcr; + n++; + } + } + block += y2block + 2; + } + } + goto done; + test1: +fprint(2, "nbox/y2block %g\n", (double)npix/y2block); + for(block = 0; block < (npix/y2block)*(y2block+2); block += y2block+2){ + } + goto done; + + test: + ; + // int green[] = {200, 0, 0}; + // int red[] = {200, 0, 255}; + // int blue[] = {200, 255, 0}; + int rgb[] = {255, 0, 0, 200, 0, 255, 200, 255, 0}; + int rgbi; + + rgbi = 6; + + for(yy = 0; yy < t->dy; yy += t->subsample[Svertical]){ + for(x = 0; x < t->dx; x += t->subsample[Shorizontal]){ + // bcb = p[block + y2block + x + 0]; + // bcr = p[block + y2block + x + 1]; + n = 0; + for(ssy = 0; ssy < t->subsample[Svertical]; ssy++){ + for(ssx = 0; ssx < t->subsample[Shorizontal]; ssx++){ + y[(yy+ssy)*by2ch2line + (x+ssx)] = rgb[rgbi]; + cb[(yy+ssy)*by2ch2line + (x+ssx)] = rgb[rgbi+1]; + cr[(yy+ssy)*by2ch2line + (x+ssx)] = rgb[rgbi+2]; + n++; + } + } + block += y2block + 2; + } + rgbi += 3; + if(rgbi >= nelem(rgb)) + rgbi = 0; + } + done: ; +#endif + } + + free(im->chans[0]); + im->nchans *= 3; + im->chanlen /= 3; + im->chans[2] = y; + im->chans[1] = cb; + im->chans[0] = cr; + return totruecolor(im, CRGB24); +} + +/* + * convert 64-bit floating point to 8-bit values. + * botch: floating point values need to be checked to avoid + * floating point exceptions. + */ +static int +floatcvt(Tif *t, Rawimage *im, uchar *data) +{ + int i, nsamp; + uvlong *u; + double g, min, max, *h; + + USED(im); + + for(i = 0; i < t->ncolors; i++) + if(t->cdepth[i] != 64){ + werrstr("readtif: floatcvt: can't handle %d-bit channel", t->cdepth[i]); + return -1; + } + + nsamp = t->dx * t->dy*t->ncolors; + + /* byteswap */ + u = (uvlong*)data; + for(i = 0; i < nsamp; i++) + if(t->end == II) + u[i] = getle(data+8*i, 8); + else + u[i] = getbe(data+8*i, 8); + + /* scale */ + h = (double*)(data); + min = h[0]; + max = h[0]; + for(i = 1; i < nsamp; i++){ + g = h[i]; + if(g > max) + max = g; + else if(g < min) + min = g; + } + + /* + * if we have minsampleval or maxsampleval, then use that + * we currently do not because we can't cleanly handle fields + * with either integer or non-integer types. + if(t->minsampleval != nil){ + } + if(t->maxsampleval != nil){ + } + */ + + for(i = 0; i < nsamp; i++) + data[i] = (uchar)((h[i] - min)/max * 255.); + for(i = 0; i < t->ncolors; i++) + t->cdepth[i] = 8; + t->depth = t->ncolors*8; + return 0; +} + +static int +resample(Tif *t, Rawimage *im, uchar *data) +{ + switch(t->samplefmt){ + case 0: + case 1: + case 2: + return 0; + case 3: + return floatcvt(t, im, data); + default: + werrstr("readtif: unknown sample fmt %d", t->samplefmt); + return -1; + } +} + +static Rawimage * +decode(Tif *t) +{ + uchar *data; + uint size; + Rawimage *im; + + if((im = malloc(sizeof *im)) == nil) + return nil; + memset(im, 0, sizeof *im); + im->r = Rect(0, 0, t->dx, t->dy); + im->cmap = nil; + im->cmaplen = 0; + im->chanlen = t->dx * t->dy; + if(t->photo == Prgb || t->photo == Ppalette) { + im->chandesc = CRGB24; + im->chanlen *= 3; + }else if(t->photo == Pseparated){ + im->chandesc = CMYK; + im->chanlen *= 4; + }else if(t->photo == Pycbcr){ + im->chandesc = CYCbCr; /* torgbv expects 3 chans; requires makeup */ + im->chanlen *= 3; + }else + im->chandesc = CY; + im->nchans = 1; + size = im->chanlen; + if((im->chans[0] = malloc(size)) == nil){ + free(im); + return nil; + } + if((data = (*t->uncompress)(t)) == nil + || resample(t, im, data) == -1 + || t->decode(t, im, data) == -1){ + free(im); + free(im->chans[0]); + im = nil; + } + if(im != nil && t->planar == Pplanar) + im = chanswizzle(im, t->ncolors); + if(im != nil && im->chandesc == CMYK){ + im = totruecolor(im, CRGB24); + t->ncolors = 3; + } + if(im != nil && im->chandesc == CYCbCr) + im = ycbcrify(t, im); + + free(data); + return im; +} + +static void +freefields(Tif *t) +{ + uint i; + + for(i = 0; i < t->nfld; i++) + free(t->fld[i].val); + free(t->fld); +} + +static Rawimage * +doread(Tif *t) +{ + uint i, j; + Rawimage *r; + + if(readheader(t) < 0) + return nil; + if((t->nfld = readshort(t)) <= 0) { + werrstr("readtif: 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) { + free(t->fld[i].val); + break; + } + } + if(i < t->nfld) { + for(j = 0; j < i; j++) + free(t->fld[j].val); + free(t->fld); + return nil; + } + readlong(t); + if(checkfields(t) < 0 || readstrips(t) < 0) { + freefields(t); + free(t->data); + t->data = nil; + return nil; + } + free(t->buf); + r = decode(t); + freefields(t); + return r; +} + +Rawimage ** +Breadtif(Biobuf *b) +{ + Rawimage **array, *r; + Tif *t; + + if((t = malloc(sizeof *t)) == nil) + return nil; + memset(t, 0, sizeof *t); + + 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 = Pchunky; + t->t4 = 0; + t->t6 = 0; + t->predictor = 1; + t->colormap = nil; + t->ncolormap = 0; + t->samplefmt = 0; + r = doread(t); + free(t); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage ** +readtif(int fd) +{ + Rawimage **a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadtif(&b); + Bterm(&b); + return a; +} --- /sys/src/cmd/jpg/tif.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jpg/tif.c Mon Aug 5 22:11:02 2013 @@ -0,0 +1,251 @@ +#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); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0) + sysfatal("getwindow: %r"); + if(image == nil) + return; + r = insetrect(screen->clipr, Edge+Border); + r.max.x = r.min.x + Dx(image->r); + r.max.y = r.min.y + Dy(image->r); + border(screen, r, -Border, nil, ZP); + 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; + ulong 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); + 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"; +/* fixme: ppm doesn't check for outchan==CMAP8 */ + if(defaultcolor && screen->depth > 8 && + outchan == CMAP8) + 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 == 0x7f || ch == 0x04) + exits(nil); + draw(screen, screen->clipr, display->white, + nil, ZP); + image = nil; + freeimage(i); + } + if(nineflag) { + chantostr(buf, outchan); + print("%11s %11d %11d %11d %11d ", buf, + c->r.min.x, c->r.min.y, + c->r.max.x, c->r.max.y); + if(write(1, c->chans[0], c->chanlen) != + c->chanlen) { + fprint(2, "%s: %s: write error: %r\n", + argv0, name); + return "write"; + } + } else if(cflag) { + if(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; +} --- /sys/src/cmd/page/gfx.c Mon Aug 5 22:11:03 2013 +++ /sys/src/cmd/page/gfx.c Mon Aug 5 22:11:03 2013 @@ -52,7 +52,7 @@ */ Convert cvt[] = { [Ipic] { "plan9", "fb/3to1 rgbv %a |fb/pcp -tplan9" }, -[Itiff] { "tiff", "fb/tiff2pic %a | fb/3to1 rgbv | fb/pcp -tplan9" }, +[Itiff] { "tiff", "tif -9 %a", "tif -t9 %a" }, [Iplan9bm] { "plan9bm", nil }, [Ijpeg] { "jpeg", "jpg -9 %a", "jpg -t9 %a" }, [Igif] { "gif", "gif -9 %a", "gif -t9 %a" }, --- /sys/src/cmd/imgtype.c Mon Aug 5 22:11:04 2013 +++ /sys/src/cmd/imgtype.c Mon Aug 5 22:11:05 2013 @@ -41,7 +41,7 @@ */ Convert cvt[] = { [Ipic] { "plan9", "fb/3to1 rgbv %a |fb/pcp -tplan9" }, -[Itiff] { "tiff", "fb/tiff2pic %a | fb/3to1 rgbv | fb/pcp -tplan9" }, +[Itiff] { "tiff", "tif -9 %a", "tif -t9 %a" }, [Iplan9bm] { "plan9bm", "cat %a", "cat %a" }, [Ijpeg] { "jpeg", "jpg -9 %a", "jpg -t9 %a" }, [Igif] { "gif", "gif -9 %a", "gif -t9 %a" }, --- /sys/man/1/jpg Mon Aug 5 22:11:06 2013 +++ /sys/man/1/jpg Mon Aug 5 22:11:06 2013 @@ -1,6 +1,6 @@ .TH JPG 1 .SH NAME -jpg, gif, png, ppm, bmp, v210, tga, wbmp, yuv, ico, togif, tojpg, toppm, topng, toico, towbmp \- view and convert pictures +jpg, gif, png, ppm, bmp, v210, tga, tif, wbmp, yuv, ico, togif, tojpg, toppm, topng, toico, towbmp \- view and convert pictures .SH SYNOPSIS .B jpg [ @@ -37,6 +37,13 @@ .I file ... ] .br +.B tif +[ +.B -39cdektv +] [ +.I file ... +] +.br .B v210 [ .B -39cdektv @@ -131,6 +138,7 @@ .IR ppm , .IR bmp , .IR tga , +.IR tif , and .IR v210 , .IR yuv @@ -147,6 +155,7 @@ The default behavior of .IR jpg , .IR gif , +.IR tif , and .IR ppm is to display the @@ -289,6 +298,8 @@ .B http://www.w3.org/TR/2003/REC-PNG-20031110 .br .B http://netpbm.sourceforge.net/doc/ppm.html +.br +,B http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf .br .B http://en.wikipedia.org/wiki/Windows_bitmap .br