diff --git a/sys/man/1/jpg b/sys/man/1/jpg index 9ec0153fc..e242fa6e8 100644 --- a/sys/man/1/jpg +++ b/sys/man/1/jpg @@ -1,6 +1,6 @@ .TH JPG 1 .SH NAME -jpg, gif, png, 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, toico \- view and convert pictures .SH SYNOPSIS .B jpg [ @@ -23,6 +23,13 @@ jpg, gif, png, ppm, bmp, v210, yuv, ico, tga, tojpg, togeordi, togif, toppm, top .I file ... ] .br +.B tif +[ +.B -39cdektv +] [ +.I file ... +] +.br .B ppm [ .B -39cdektv @@ -137,6 +144,7 @@ These programs read, display, and write image files in public formats. .IR Jpg , .IR gif , .IR png , +.IR tif , .IR ppm , .IR bmp , .IR tga , @@ -156,8 +164,14 @@ read Plan 9 images files, convert them to JPEG, GIF, PPM, or PNG, and write them The default behavior of .IR jpg , .IR gif , +.IR png , +.IR tif , +.IR ppm , +.IR bmp , +.IR tga , +.IR v210 , and -.IR ppm +.IR yuv is to display the .IR file , or standard input if no file is named. @@ -227,11 +241,12 @@ The input is a motion JPEG file, with multiple images representing frames of the .PP The .IR tojpg , -.IR togif -and +.IR togif , .IR toppm -programs go the other way: they convert from Plan 9 images to JPEG, GIF and PPM, -and have no display capability. +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. They all accept an option .B -c to set the comment field of the resulting file. @@ -323,6 +338,8 @@ space in the image. The icon file is written to standard output. .br .B http://www.w3.org/TR/2003/REC-PNG-20031110 .br +.B http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf +.br .B http://netpbm.sourceforge.net/doc/ppm.html .br .B http://en.wikipedia.org/wiki/Windows_bitmap diff --git a/sys/src/cmd/jpg/imagefile.h b/sys/src/cmd/jpg/imagefile.h index bab62e84e..04600e364 100644 --- a/sys/src/cmd/jpg/imagefile.h +++ b/sys/src/cmd/jpg/imagefile.h @@ -50,9 +50,11 @@ typedef struct ImageInfo { Rawimage** readjpg(int, int); -Rawimage** Breadjpg(Biobuf *b, int); +Rawimage** Breadjpg(Biobuf*, int); Rawimage** readpng(int, int); -Rawimage** Breadpng(Biobuf *b, int); +Rawimage** Breadpng(Biobuf*, int); +Rawimage** readtif(int); +Rawimage** Breadtif(Biobuf*); Rawimage** readgif(int, int); Rawimage** readpixmap(int, int); Rawimage* torgbv(Rawimage*, int); diff --git a/sys/src/cmd/jpg/mkfile b/sys/src/cmd/jpg/mkfile index 3671856e9..0455f2cc9 100644 --- a/sys/src/cmd/jpg/mkfile +++ b/sys/src/cmd/jpg/mkfile @@ -9,6 +9,7 @@ TARG=\ toppm\ png\ topng\ + tif\ yuv\ ico\ toico\ @@ -55,6 +56,7 @@ $O.ppm: $IMFILES readppm.$O ppm.$O $O.toppm: writeppm.$O multichan.$O toppm.$O $O.png: $IMFILES readpng.$O png.$O $O.topng: writepng.$O topng.$O +$O.tif: $IMFILES readtif.$O tif.$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 new file mode 100644 index 000000000..75c80f27f --- /dev/null +++ b/sys/src/cmd/jpg/readtif.c @@ -0,0 +1,1787 @@ +/* +* 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 "imagefile.h" + +enum { + II = 0x4949, /* little-endian */ + MM = 0x4d4d, /* big-endian */ + TIF = 0x002a /* tiff magic number */ +}; + +enum { + Byte = 1, + Short = 3, + Long = 4 +}; + +enum { + Width = 0x0100, + Length = 0x0101, + Bits = 0x0102, + + Compression = 0x0103, + Nocomp = 0x0001, + Huffman = 0x0002, + T4enc = 0x0003, + T6enc = 0x0004, + Lzwenc = 0x0005, + Packbits = 0x8005, + + Photometric = 0x0106, + Whitezero = 0x0000, + Blackzero = 0x0001, + Rgb = 0x0002, + Palette = 0x0003, + + Fill = 0x010a, + Strips = 0x0111, + Orientation = 0x0112, + Samples = 0x0115, + Rows = 0x0116, + Counts = 0x0117, + Planar = 0x011c, + T4opts = 0x0124, + T6opts = 0x0125, + Predictor = 0x13d, + Color = 0x0140 +}; + +enum { + Nfaxcodes = 10, + Nfaxtab = 105 +}; + +enum { + Clrcode = 256, + Eoicode = 257, + Tabsz = 1<<12 +}; + +typedef struct Tab Tab; +typedef struct Fax Fax; +typedef struct Code Code; +typedef struct Lzw Lzw; +typedef struct Fld Fld; +typedef struct Tif Tif; + +struct Tab { + int len; + int code; + int run; /* run length */ +}; + +struct Fax { + ulong n; + int m; + int st; /* state */ + Tab *tab[2]; + int ntab; /* position in tab */ + Tab *eol; + int (*getbit)(Fax *); + int *l1; + int *l2; + ulong nl; + uchar *data; + ulong next; /* next strip offset in data */ +}; + +struct Code { + uchar val; + Code *next; +}; + +struct Lzw { + Code tab[Tabsz]; + int ntab; + int len; /* code length */ + ulong n; + int m; + uchar *data; + ulong next; /* next strip offset in data */ + /* remaining allocated codes */ + Code *first; + Code *last; +}; + +struct Fld { + uint tag; + uint typ; + ulong cnt; + ulong off; /* value offset */ + ulong *val; + ulong nval; +}; + +struct Tif { + Biobuf *fd; + uint end; /* endianness */ + uchar tmp[4]; + uchar *buf; + ulong nbuf; + int eof; /* reached end of image */ + ulong n; /* offset in buf array */ + ulong off; + uint nfld; + Fld *fld; + ulong (*byte2)(uchar *); + ulong (*byte4)(uchar *); + + /* field data */ + ulong dx; + ulong dy; + ulong depth; + ulong comp; + uchar *(*uncompress)(Tif *); + ulong orientation; + ulong photo; + int (*decode)(Tif *, Rawimage *, uchar *); + ulong fill; + ulong *strips; + ulong nstrips; + ulong samples; + ulong rows; + ulong *counts; + ulong ncounts; + ulong planar; + ulong *color; /* color map */ + ulong ncolor; + ulong t4; + ulong t6; + ulong predictor; + + /* image data */ + uchar *data; + ulong ndata; +}; + +/* +* imported from libdraw/arith.c to permit +* extern log2 function +*/ +static int log2[] = { + -1, 0, 1, -1, 2, -1, -1, -1, 3, + -1, -1, -1, -1, -1, -1, -1, 4, + -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, + -1, -1, -1, -1, -1, -1, -1, 5 +}; + +static Tab faxwhite[Nfaxtab] = { + {4, 0x7, 2}, /* 0111 */ + {4, 0x8, 3}, /* 1000 */ + {4, 0xb, 4}, /* 1011 */ + {4, 0xc, 5}, /* 1100 */ + {4, 0xe, 6}, /* 1110 */ + {4, 0xf, 7}, /* 1111 */ + {5, 0x12, 128}, /* 1001 0 */ + {5, 0x13, 8}, /* 1001 1 */ + {5, 0x14, 9}, /* 1010 0 */ + {5, 0x1b, 64}, /* 1101 1 */ + {5, 0x7, 10}, /* 0011 1 */ + {5, 0x8, 11}, /* 0100 0 */ + {6, 0x17, 192}, /* 0101 11 */ + {6, 0x18, 1664}, /* 0110 00 */ + {6, 0x2a, 16}, /* 1010 10 */ + {6, 0x2b, 17}, /* 1010 11 */ + {6, 0x3, 13}, /* 0000 11 */ + {6, 0x34, 14}, /* 1101 00 */ + {6, 0x35, 15}, /* 1101 01 */ + {6, 0x7, 1}, /* 0001 11 */ + {6, 0x8, 12}, /* 0010 00 */ + {7, 0x13, 26}, /* 0010 011 */ + {7, 0x17, 21}, /* 0010 111 */ + {7, 0x18, 28}, /* 0011 000 */ + {7, 0x24, 27}, /* 0100 100 */ + {7, 0x27, 18}, /* 0100 111 */ + {7, 0x28, 24}, /* 0101 000 */ + {7, 0x2b, 25}, /* 0101 011 */ + {7, 0x3, 22}, /* 0000 011 */ + {7, 0x37, 256}, /* 0110 111 */ + {7, 0x4, 23}, /* 0000 100 */ + {7, 0x8, 20}, /* 0001 000 */ + {7, 0xc, 19}, /* 0001 100 */ + {8, 0x12, 33}, /* 0001 0010 */ + {8, 0x13, 34}, /* 0001 0011 */ + {8, 0x14, 35}, /* 0001 0100 */ + {8, 0x15, 36}, /* 0001 0101 */ + {8, 0x16, 37}, /* 0001 0110 */ + {8, 0x17, 38}, /* 0001 0111 */ + {8, 0x1a, 31}, /* 0001 1010 */ + {8, 0x1b, 32}, /* 0001 1011 */ + {8, 0x2, 29}, /* 0000 0010 */ + {8, 0x24, 53}, /* 0010 0100 */ + {8, 0x25, 54}, /* 0010 0101 */ + {8, 0x28, 39}, /* 0010 1000 */ + {8, 0x29, 40}, /* 0010 1001 */ + {8, 0x2a, 41}, /* 0010 1010 */ + {8, 0x2b, 42}, /* 0010 1011 */ + {8, 0x2c, 43}, /* 0010 1100 */ + {8, 0x2d, 44}, /* 0010 1101 */ + {8, 0x3, 30}, /* 0000 0011 */ + {8, 0x32, 61}, /* 0011 0010 */ + {8, 0x33, 62}, /* 0011 0011 */ + {8, 0x34, 63}, /* 0011 0100 */ + {8, 0x35, 0}, /* 0011 0101 */ + {8, 0x36, 320}, /* 0011 0110 */ + {8, 0x37, 384}, /* 0011 0111 */ + {8, 0x4, 45}, /* 0000 0100 */ + {8, 0x4a, 59}, /* 0100 1010 */ + {8, 0x4b, 60}, /* 0100 1011 */ + {8, 0x5, 46}, /* 0000 0101 */ + {8, 0x52, 49}, /* 0101 0010 */ + {8, 0x53, 50}, /* 0101 0011 */ + {8, 0x54, 51}, /* 0101 0100 */ + {8, 0x55, 52}, /* 0101 0101 */ + {8, 0x58, 55}, /* 0101 1000 */ + {8, 0x59, 56}, /* 0101 1001 */ + {8, 0x5a, 57}, /* 0101 1010 */ + {8, 0x5b, 58}, /* 0101 1011 */ + {8, 0x64, 448}, /* 0110 0100 */ + {8, 0x65, 512}, /* 0110 0101 */ + {8, 0x67, 640}, /* 0110 0111 */ + {8, 0x68, 576}, /* 0110 1000 */ + {8, 0xa, 47}, /* 0000 1010 */ + {8, 0xb, 48}, /* 0000 1011 */ + {9, 0x98, 1472}, /* 0100 1100 0 */ + {9, 0x99, 1536}, /* 0100 1100 1 */ + {9, 0x9a, 1600}, /* 0100 1101 0 */ + {9, 0x9b, 1728}, /* 0100 1101 1 */ + {9, 0xcc, 704}, /* 0110 0110 0 */ + {9, 0xcd, 768}, /* 0110 0110 1 */ + {9, 0xd2, 832}, /* 0110 1001 0 */ + {9, 0xd3, 896}, /* 0110 1001 1 */ + {9, 0xd4, 960}, /* 0110 1010 0 */ + {9, 0xd5, 1024}, /* 0110 1010 1 */ + {9, 0xd6, 1088}, /* 0110 1011 0 */ + {9, 0xd7, 1152}, /* 0110 1011 1 */ + {9, 0xd8, 1216}, /* 0110 1100 0 */ + {9, 0xd9, 1280}, /* 0110 1100 1 */ + {9, 0xda, 1344}, /* 0110 1101 0 */ + {9, 0xdb, 1408}, /* 0110 1101 1 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x1, -1}, /* 0000 0000 0001 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560} /* 0000 0001 1111 */ +}; + +static Tab faxblack[Nfaxtab] = { + {2, 0x2, 3}, /* 10 */ + {2, 0x3, 2}, /* 11 */ + {3, 0x2, 1}, /* 010 */ + {3, 0x3, 4}, /* 011 */ + {4, 0x2, 6}, /* 0010 */ + {4, 0x3, 5}, /* 0011 */ + {5, 0x3, 7}, /* 0001 1 */ + {6, 0x4, 9}, /* 0001 00 */ + {6, 0x5, 8}, /* 0001 01 */ + {7, 0x4, 10}, /* 0000 100 */ + {7, 0x5, 11}, /* 0000 101 */ + {7, 0x7, 12}, /* 0000 111 */ + {8, 0x4, 13}, /* 0000 0100 */ + {8, 0x7, 14}, /* 0000 0111 */ + {9, 0x18, 15}, /* 0000 1100 0 */ + {10, 0x17, 16}, /* 0000 0101 11 */ + {10, 0x18, 17}, /* 0000 0110 00 */ + {10, 0x37, 0}, /* 0000 1101 11 */ + {10, 0x8, 18}, /* 0000 0010 00 */ + {10, 0xf, 64}, /* 0000 0011 11 */ + {11, 0x17, 24}, /* 0000 0010 111 */ + {11, 0x18, 25}, /* 0000 0011 000 */ + {11, 0x28, 23}, /* 0000 0101 000 */ + {11, 0x37, 22}, /* 0000 0110 111 */ + {11, 0x67, 19}, /* 0000 1100 111 */ + {11, 0x68, 20}, /* 0000 1101 000 */ + {11, 0x6c, 21}, /* 0000 1101 100 */ + {11, 0x8, 1792}, /* 0000 0001 000 */ + {11, 0xc, 1856}, /* 0000 0001 100 */ + {11, 0xd, 1920}, /* 0000 0001 101 */ + {12, 0x1, -1}, /* 0000 0000 0001 */ + {12, 0x12, 1984}, /* 0000 0001 0010 */ + {12, 0x13, 2048}, /* 0000 0001 0011 */ + {12, 0x14, 2112}, /* 0000 0001 0100 */ + {12, 0x15, 2176}, /* 0000 0001 0101 */ + {12, 0x16, 2240}, /* 0000 0001 0110 */ + {12, 0x17, 2304}, /* 0000 0001 0111 */ + {12, 0x1c, 2368}, /* 0000 0001 1100 */ + {12, 0x1d, 2432}, /* 0000 0001 1101 */ + {12, 0x1e, 2496}, /* 0000 0001 1110 */ + {12, 0x1f, 2560}, /* 0000 0001 1111 */ + {12, 0x24, 52}, /* 0000 0010 0100 */ + {12, 0x27, 55}, /* 0000 0010 0111 */ + {12, 0x28, 56}, /* 0000 0010 1000 */ + {12, 0x2b, 59}, /* 0000 0010 1011 */ + {12, 0x2c, 60}, /* 0000 0010 1100 */ + {12, 0x33, 320}, /* 0000 0011 0011 */ + {12, 0x34, 384}, /* 0000 0011 0100 */ + {12, 0x35, 448}, /* 0000 0011 0101 */ + {12, 0x37, 53}, /* 0000 0011 0111 */ + {12, 0x38, 54}, /* 0000 0011 1000 */ + {12, 0x52, 50}, /* 0000 0101 0010 */ + {12, 0x53, 51}, /* 0000 0101 0011 */ + {12, 0x54, 44}, /* 0000 0101 0100 */ + {12, 0x55, 45}, /* 0000 0101 0101 */ + {12, 0x56, 46}, /* 0000 0101 0110 */ + {12, 0x57, 47}, /* 0000 0101 0111 */ + {12, 0x58, 57}, /* 0000 0101 1000 */ + {12, 0x59, 58}, /* 0000 0101 1001 */ + {12, 0x5a, 61}, /* 0000 0101 1010 */ + {12, 0x5b, 256}, /* 0000 0101 1011 */ + {12, 0x64, 48}, /* 0000 0110 0100 */ + {12, 0x65, 49}, /* 0000 0110 0101 */ + {12, 0x66, 62}, /* 0000 0110 0110 */ + {12, 0x67, 63}, /* 0000 0110 0111 */ + {12, 0x68, 30}, /* 0000 0110 1000 */ + {12, 0x69, 31}, /* 0000 0110 1001 */ + {12, 0x6a, 32}, /* 0000 0110 1010 */ + {12, 0x6b, 33}, /* 0000 0110 1011 */ + {12, 0x6c, 40}, /* 0000 0110 1100 */ + {12, 0x6d, 41}, /* 0000 0110 1101 */ + {12, 0xc8, 128}, /* 0000 1100 1000 */ + {12, 0xc9, 192}, /* 0000 1100 1001 */ + {12, 0xca, 26}, /* 0000 1100 1010 */ + {12, 0xcb, 27}, /* 0000 1100 1011 */ + {12, 0xcc, 28}, /* 0000 1100 1100 */ + {12, 0xcd, 29}, /* 0000 1100 1101 */ + {12, 0xd2, 34}, /* 0000 1101 0010 */ + {12, 0xd3, 35}, /* 0000 1101 0011 */ + {12, 0xd4, 36}, /* 0000 1101 0100 */ + {12, 0xd5, 37}, /* 0000 1101 0101 */ + {12, 0xd6, 38}, /* 0000 1101 0110 */ + {12, 0xd7, 39}, /* 0000 1101 0111 */ + {12, 0xda, 42}, /* 0000 1101 1010 */ + {12, 0xdb, 43}, /* 0000 1101 1011 */ + {13, 0x4a, 640}, /* 0000 0010 0101 0 */ + {13, 0x4b, 704}, /* 0000 0010 0101 1 */ + {13, 0x4c, 768}, /* 0000 0010 0110 0 */ + {13, 0x4d, 832}, /* 0000 0010 0110 1 */ + {13, 0x52, 1280}, /* 0000 0010 1001 0 */ + {13, 0x53, 1344}, /* 0000 0010 1001 1 */ + {13, 0x54, 1408}, /* 0000 0010 1010 0 */ + {13, 0x55, 1472}, /* 0000 0010 1010 1 */ + {13, 0x5a, 1536}, /* 0000 0010 1101 0 */ + {13, 0x5b, 1600}, /* 0000 0010 1101 1 */ + {13, 0x64, 1664}, /* 0000 0011 0010 0 */ + {13, 0x65, 1728}, /* 0000 0011 0010 1 */ + {13, 0x6c, 512}, /* 0000 0011 0110 0 */ + {13, 0x6d, 576}, /* 0000 0011 0110 1 */ + {13, 0x72, 896}, /* 0000 0011 1001 0 */ + {13, 0x73, 960}, /* 0000 0011 1001 1 */ + {13, 0x74, 1024}, /* 0000 0011 1010 0 */ + {13, 0x75, 1088}, /* 0000 0011 1010 1 */ + {13, 0x76, 1152}, /* 0000 0011 1011 0 */ + {13, 0x77, 1216} /* 0000 0011 1011 1 */ +}; + +static Tab faxcodes[Nfaxcodes] = { + {1, 0x1, 0}, /* 1 */ + {3, 0x1, 0}, /* 001 */ + {3, 0x2, 0}, /* 010 */ + {3, 0x3, 0}, /* 011 */ + {4, 0x1, 0}, /* 0001 */ + {6, 0x2, 0}, /* 0000 10 */ + {6, 0x3, 0}, /* 0000 11 */ + {7, 0x2, 0}, /* 0000 010 */ + {7, 0x3, 0}, /* 0000 011 */ + {12, 0x1, -1} /* 0000 0000 0001 */ +}; + +static int typesizes[] = {0, 1, 0, 2, 4}; +static int vcodeval[] = {0, 0, 0, 1, 0, 0, 2, 3}; + +static ulong byte2le(uchar *); +static ulong byte4le(uchar *); +static ulong byte2be(uchar *); +static ulong byte4be(uchar *); +static void readdata(Tif *, ulong); +static void readnbytes(Tif *, ulong); +static ulong readbyte(Tif *); +static ulong readshort(Tif *); +static ulong 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 *, ulong, ulong *, ulong *, ulong, int); +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 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 Rawimage *readslave(Tif *); + +static ulong +byte2le(uchar *buf) +{ + return (buf[1] << 8) | buf[0]; +} + +static ulong +byte4le(uchar *buf) +{ + return (byte2le(buf+2) << 16) | byte2le(buf); +} + +static ulong +byte2be(uchar *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static ulong +byte4be(uchar *buf) +{ + return (byte2be(buf) << 16) | byte2be(buf+2); +} + +static void +readdata(Tif *t, ulong offset) +{ + long n, m; + ulong size; + + if(offset < t->nbuf) + offset = t->nbuf; + m = offset + 4096 - t->nbuf; + size = (m + t->nbuf) * sizeof *t->buf; + if(t->buf == nil) { + if((t->buf = malloc(size)) == nil) + sysfatal("malloc: %r"); + } else { + if((t->buf = realloc(t->buf, size)) == nil) + sysfatal("realloc: %r"); + } + if((n = Bread(t->fd, t->buf+t->nbuf, m)) < 0) + sysfatal("Bread: %r"); + if(n != m) + t->eof = 1; + t->nbuf += n; +} + +static void +readnbytes(Tif *t, ulong n) +{ + if(n <= 0 || n > 4) + sysfatal("cannot read %lud bytes", n); + if(t->n+n > t->nbuf) { + if(t->eof) + sysfatal("reached end of file"); + readdata(t, 0); + } + memmove(t->tmp, t->buf+t->n, n); + t->n += n; +} + +static ulong +readbyte(Tif *t) +{ + readnbytes(t, 1); + return t->tmp[0]; +} + +static ulong +readshort(Tif *t) +{ + readnbytes(t, 2); + return (*t->byte2)(t->tmp); +} + +static ulong +readlong(Tif *t) +{ + readnbytes(t, 4); + return (*t->byte4)(t->tmp); +} + +static int +gototif(Tif *t, ulong n) +{ + if(n < 8) { + werrstr("offset pointing to header"); + return -1; + } + if(n > t->nbuf) + readdata(t, n); + t->n = n; + return 0; +} + +static int +readheader(Tif *t) +{ + uint n; + + t->end = readshort(t); + switch(t->end) { + case II: + break; + case MM: + t->byte2 = byte2be; + t->byte4 = byte4be; + break; + default: + werrstr("illegal byte order: %#.4x", t->end); + return -1; + } + if((n = readshort(t)) != TIF) { + werrstr("illegal tiff magic: %#.4x", n); + return -1; + } + t->off = readlong(t); + return gototif(t, t->off); +} + +static 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("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("first eol"); + return nil; + } + f->eol = p; + return p; + } + for(i = 0; (bit = (*f->getbit)(f)) == 0; i++) + ; + if(bit < 0) { + f->st = -1; + return nil; + } + if(i < 11) { + werrstr("eol"); + return nil; + } + return f->eol; +} + +static int +faxfill(Fax *f, uchar *data, ulong size, ulong *i, ulong *x, ulong dx, + int n) +{ + if((*x += n) > dx) { + werrstr("fax row overflow"); + return -1; + } + if((*i += n) >= size) { + werrstr("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, ulong size, ulong *i, ulong *x, + ulong 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, ulong size, ulong *i, ulong *x, + ulong 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("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, ulong size, ulong *i) +{ + int d1; + ulong x, y; + Tab *p; + + d1 = t->comp != T6enc; + p = nil; + for(x = y = 0; x < t->dx || y < t->rows;) { + f->st = 0; + if(t->comp == T4enc) { + if(p == nil && geteol(f) == nil) { + if(f->st >= 0) + return -1; + break; + } + if(y > 0) + *i += t->dx - x; + if(t->t4 & 1) { + d1 = (*f->getbit)(f); + if(d1 < 0) + break; + } + } + x = 0; + y++; + if(d1) { + p = getfax1d(f, data, size, i, + &x, t->dx); + } else { + p = getfax2d(f, data, size, i, + &x, t->dx); + } + if(t->comp == Huffman) + fillbits(f); + if(p == nil && x != t->dx) { + if(f->st >= 0 || 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; + ulong i, j, datasz, linesz; + 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); + if(data == nil || f.l1 == nil || f.l2 == nil) { + free(t->data); + if(data != nil) + free(data); + if(f.l1 != nil) + free(f.l1); + if(f.l2 != nil) + free(f.l2); + return nil; + } + 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; + for(i = j = 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("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("lzw eof"); + return -1; + } + code = 0; + for(i = l->len-1; i >= 0; i--) { + c = (l->data[l->n] >> l->m) & 0x1; + code |= c << i; + l->m--; + if(l->m < 0) { + l->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, ++*i, --*striplen) { + if(*i >= size || *striplen < 0) { + werrstr("lzw overflow"); + return -1; + } + data[*i] = p->val; + } + return 0; +} + +static void +predict(Tif *t, uchar *data) +{ + char a, b; + ulong 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("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%8 == 0? i/8: i/8+1; + size = t->nstrips * striplen * sizeof *data; + if((data = malloc(size)) == nil) { + free(t->data); + return nil; + } + for(i = 0; i < Tabsz; i++) { + l.tab[i].val = i; + l.tab[i].next = nil; + } + l.data = t->data; + l.first = l.last = nil; + for(i = j = 0; 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; + } + 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 * +packbits(Tif *t) +{ + char n; + ulong i, j, k, size; + uchar *data; + + size = t->dx * t->dy * t->samples * sizeof *data; + if((data = malloc(size)) == nil) { + free(t->data); + return nil; + } + for(i = 0, j = 0; i < t->ndata;) { + n = (char)t->data[i++]; + if(n >= 0) { + k = n + 1; + if(j+k >= size || i+k >= t->ndata) + break; + memmove(data+j, t->data+i, k); + i += k; + j += k; + } else if(n > -128 && n < 0) { + k = j - n + 1; + if(k > size || i >= t->ndata) + break; + for(; j < k; j++) + data[j] = t->data[i]; + i++; + } + } + if(i < t->ndata) { + werrstr("packbits overflow"); + free(data); + data = nil; + } + free(t->data); + return data; +} + +static int +faxdecode(Tif *t, Rawimage *im, uchar *data) +{ + ulong n; + + for(n = 0; n < im->chanlen; n++) { + if(t->photo == Whitezero) + data[n] ^= 1; + im->chans[0][n] = data[n] * 0xff; + } + return 0; +} + +static int +greydecode(Tif *t, Rawimage *im, uchar *data) +{ + int pix, pmask, xmask; + ulong i, n, x, y; + + pmask = (1 << t->depth) - 1; + xmask = 7 >> log2[t->depth]; + for(y = 0, n = 0; y < t->dy; y++) { + i = y * bytesperline(im->r, t->depth); + for(x = 0; x < t->dx; x++, n++) { + if(n >= im->chanlen) { + werrstr("grey overflow"); + return -1; + } + pix = (data[i] >> t->depth*((xmask - + x) & xmask)) & pmask; + if(((x + 1) & xmask) == 0) + i++; + if(t->photo == Whitezero) + pix ^= pmask; + pix = (pix * 0xff) / pmask; + im->chans[0][n] = pix; + } + } + return 0; +} + +static int +rgbdecode(Tif *t, Rawimage *im, uchar *data) +{ + ulong i, n, x, y; + + for(y = 0, n = 0; y < t->dy; y++) { + for(x = 0; x < t->dx; x++, n += 3) { + if(n >= im->chanlen) { + werrstr("rgb overflow"); + return -1; + } + i = (y*t->dx + x) * 3; + im->chans[0][n] = data[i+2]; + im->chans[0][n+1] = data[i+1]; + im->chans[0][n+2] = data[i]; + } + } + return 0; +} + +static int +paldecode(Tif *t, Rawimage *im, uchar *data) +{ + int pix, pmask, xmask; + ulong i, n, x, y, *r, *g, *b; + + pmask = (1 << t->depth) - 1; + xmask = 7 >> log2[t->depth]; + r = t->color; + g = r + pmask + 1; + b = g + pmask + 1; + for(y = 0, n = 0; y < t->dy; y++) { + i = y * bytesperline(im->r, t->depth); + for(x = 0; x < t->dx; x++, n += 3) { + if(n >= im->chanlen) { + werrstr("palette overflow"); + return -1; + } + pix = (data[i] >> t->depth*((xmask - + x) & xmask)) & pmask; + if(((x + 1) & xmask) == 0) + i++; + im->chans[0][n] = b[pix]; + im->chans[0][n+1] = g[pix]; + im->chans[0][n+2] = r[pix]; + } + } + return 0; +} + +static int +parsefield(Tif *t, Fld *f) +{ + ulong v; + + v = f->val[0]; + switch(f->tag) { + case Width: + t->dx = v; + break; + case Length: + t->dy = v; + break; + case Bits: + t->depth = v; + if(f->cnt == 3) + t->depth += f->val[1] + f->val[2]; + break; + case Compression: + t->comp = v; + break; + case Photometric: + t->photo = v; + switch(t->photo) { + case Whitezero: + case Blackzero: + t->decode = greydecode; + break; + case Rgb: + t->decode = rgbdecode; + break; + case Palette: + t->decode = paldecode; + break; + default: + break; + } + break; + case Strips: + t->strips = f->val; + t->nstrips = f->cnt; + break; + case Fill: + t->fill = v; + break; + case Orientation: + t->orientation = v; + break; + case Samples: + t->samples = v; + break; + case Rows: + t->rows = v; + break; + case Counts: + t->counts = f->val; + t->ncounts = f->cnt; + break; + case Planar: + t->planar = v; + break; + case T4opts: + t->t4 = v; + break; + case T6opts: + t->t6 = v; + break; + case Predictor: + t->predictor = v; + break; + case Color: + t->color = f->val; + t->ncolor = f->cnt; + break; + default: + werrstr("shouldn't reach"); + return -1; + } + return 0; +} + +static int +readfield(Tif *t, Fld *f) +{ + int size; + ulong i, j, n, off; + ulong (*readval)(Tif *); + + f->tag = readshort(t); + f->typ = readshort(t); + f->cnt = readlong(t); + f->val = nil; + switch(f->tag) { + case Width: + case Length: + case Compression: + case Photometric: + case Fill: + case Orientation: + case Samples: + case Rows: + case Planar: + case T4opts: + case T6opts: + case Predictor: + if(f->cnt != 1) { + werrstr("field count"); + return -1; + } + break; + case Bits: + if(f->cnt != 1 && f->cnt != 3) { + werrstr("field count"); + return -1; + } + break; + case Strips: + case Counts: + case Color: + break; + default: + readlong(t); + return 0; + } + switch(f->typ) { + case Byte: + readval = readbyte; + break; + case Short: + readval = readshort; + break; + case Long: + readval = readlong; + break; + default: + werrstr("unsupported type\n"); + return -1; + } + if((f->val = malloc(f->cnt*sizeof *f->val)) == nil) + return -1; + size = typesizes[f->typ]; + if((n = size*f->cnt) <= 4) { + for(i = 0; i < f->cnt; i++) + f->val[i] = readval(t); + f->off = 0x0; + f->nval = i; + for(j = n; j < 4; j += size) + readval(t); + } else { + f->off = readlong(t); + off = t->n; + if(gototif(t, f->off) < 0) + return -1; + for(i = 0; i < f->cnt; i++) + f->val[i] = readval(t); + f->nval = i; + if(gototif(t, off) < 0) + return -1; + } + return parsefield(t, f); +} + +static int +checkfields(Tif *t) +{ + double a, b; + ulong n, size; + + if(t->dx == 0) { + werrstr("image width"); + return -1; + } + if(t->dy == 0) { + werrstr("image length"); + return -1; + } + switch(t->depth) { + case 1: + case 4: + case 8: + case 24: + break; + default: + werrstr("bits per sample"); + return -1; + } + switch(t->comp) { + case Nocomp: + t->uncompress = nocomp; + break; + case Huffman: + case T4enc: + case T6enc: + t->uncompress = fax; + if(t->decode != nil) + t->decode = faxdecode; + if((t->comp == T4enc && t->t4 & (1<<1)) || + (t->comp == T6enc && + t->t6 & (1<<1))) { + werrstr("uncompressed mode"); + return -1; + } + break; + case Lzwenc: + t->uncompress = lzw; + break; + case Packbits: + t->uncompress = packbits; + break; + default: + werrstr("compression"); + return -1; + } + if(t->decode == nil) { + werrstr("photometric interpretation"); + return -1; + } + if(t->depth > 1 && (t->comp == Huffman || + t->comp == T4enc || t->comp == T6enc)) { + werrstr("compression"); + return -1; + } + if(t->fill != 1 && t->fill != 2) { + werrstr("fill order"); + return -1; + } + if(t->fill == 2 && t->depth != 1) { + werrstr("depth should be 1 with fill order 2"); + return -1; + } + if(t->orientation != 1) { + werrstr("orientation"); + return -1; + } + if(t->rows == 0) { + werrstr("rows per strip"); + return -1; + } + a = (double)t->dy; + b = (double)t->rows; + n = (ulong)floor((a+b-1)/b); + if(t->strips == nil || t->nstrips != n) { + werrstr("strip offsets"); + return -1; + } + if(t->samples == 1 && t->photo == Rgb) { + werrstr("not enough samples per pixel"); + return -1; + } + if(t->samples == 3 && t->photo != Rgb) { + werrstr("too many samples per pixel"); + return -1; + } + if(t->samples != 1 && t->samples != 3) { + werrstr("samples per pixel"); + return -1; + } + /* + * strip byte counts should not be missing, + * but we can guess correctly in this case + */ + size = sizeof *t->counts; + if(t->counts == nil && t->comp == Nocomp && + t->nstrips == 1 && + (t->counts = malloc(size)) != nil) { + 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("strip byte counts"); + return -1; + } + if(t->planar != 1) { + werrstr("planar configuration"); + return -1; + } + if(t->photo == Palette && (t->color == nil || + t->ncolor != 3*(1<depth))) { + werrstr("color map"); + return -1; + } + if(t->predictor == 2 && t->depth == 1) { + werrstr("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*sizeof *t->data)) == nil) + return -1; + off = t->n; + for(i = n = 0; i < t->nstrips; i++) { + if(gototif(t, t->strips[i]) < 0) + return -1; + /* + * we store each strip's offset in t->data + * in order to skip the final rtc or eofb + * during fax decoding. t->strips is used + * to save on memory allocation. these + * offsets are also used in lzw as a + * preventive measure. + */ + t->strips[i] = n; + for(j = 0; j < t->counts[i]; j++, n++) + t->data[n] = readbyte(t); + } + return gototif(t, off); +} + +static Rawimage * +decode(Tif *t) +{ + ulong size; + uchar *data; + Rawimage *im; + + if((im = malloc(sizeof *im)) == nil) + return nil; + im->r = Rect(0, 0, t->dx, t->dy); + im->cmap = nil; + im->cmaplen = 0; + im->chanlen = t->dx * t->dy; + if(t->photo == Rgb || t->photo == Palette) { + im->chandesc = CRGB24; + im->chanlen *= 3; + } else + im->chandesc = CY; + im->nchans = 1; + size = im->chanlen * sizeof *im->chans[0]; + if((im->chans[0] = malloc(size)) == nil) + return nil; + /* unused members */ + im->fields = 0; + im->gifflags = 0; + im->gifdelay = 0; + im->giftrindex = 0; + im->gifloopcount = 1; + if((data = (*t->uncompress)(t)) == nil) + return nil; + if((*t->decode)(t, im, data) < 0) { + free(im->chans[0]); + free(im); + im = nil; + } + free(data); + return im; +} + +static void +freefields(Tif *t) +{ + uint i; + + for(i = 0; i < t->nfld; i++) { + if(t->fld[i].val != nil) + free(t->fld[i].val); + } + free(t->fld); +} + +static Rawimage * +readslave(Tif *t) +{ + uint i, j; + Rawimage *r; + + if(readheader(t) < 0) + return nil; + if((t->nfld = readshort(t)) <= 0) { + werrstr("illegal field number: %#.4x", t->nfld); + return nil; + } + if((t->fld = malloc(t->nfld*sizeof *t->fld)) == nil) + return nil; + for(i = 0; i < t->nfld; i++) { + if(readfield(t, &t->fld[i]) < 0) { + if(t->fld[i].val != nil) + free(t->fld[i].val); + break; + } + } + if(i < t->nfld) { + for(j = 0; j < i; j++) { + if(t->fld[j].val != nil) + free(t->fld[j].val); + } + free(t->fld); + return nil; + } + readlong(t); + if(checkfields(t) < 0) { + freefields(t); + return nil; + } + if(readstrips(t) < 0) { + freefields(t); + if(t->data != nil) + free(t->data); + return nil; + } + free(t->buf); + r = decode(t); + freefields(t); + return r; +} + +Rawimage ** +Breadtif(Biobuf *b) +{ + Rawimage **array, *r; + Tif *t; + + if((t = malloc(sizeof *t)) == nil) + return nil; + if((array = malloc(2*sizeof *array)) == nil) + return nil; + t->fd = b; + t->buf = nil; + t->nbuf = t->eof = t->n = 0; + /* order doesn't matter for the first two bytes */ + t->byte2 = byte2le; + t->byte4 = byte4le; + /* defaults */ + t->dx = 0; + t->dy = 0; + t->depth = 1; + t->comp = 1; + t->uncompress = nil; + t->photo = 0; + t->decode = nil; + t->fill = 1; + t->orientation = 1; + t->strips = nil; + t->nstrips = 0; + t->samples = 1; + t->rows = 0xffffffff; /* entire image is one strip */ + t->counts = nil; + t->ncounts = 0; + t->planar = 1; + t->t4 = 0; + t->t6 = 0; + t->predictor = 1; + t->color = nil; + t->ncolor = 0; + r = readslave(t); + free(t); + array[0] = r; + array[1] = nil; + return array; +} + +Rawimage ** +readtif(int fd) +{ + Rawimage **a; + Biobuf b; + + if(Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadtif(&b); + Bterm(&b); + return a; +} diff --git a/sys/src/cmd/jpg/tif.c b/sys/src/cmd/jpg/tif.c new file mode 100644 index 000000000..6607efb93 --- /dev/null +++ b/sys/src/cmd/jpg/tif.c @@ -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; +} diff --git a/sys/src/cmd/page.c b/sys/src/cmd/page.c index 0926b8486..aea34facd 100644 --- a/sys/src/cmd/page.c +++ b/sys/src/cmd/page.c @@ -680,6 +680,7 @@ popenfile(Page *p) "image/gif", popenimg, "gif", "image/jpeg", popenimg, "jpg", "image/png", popenimg, "png", + "image/tiff", popenimg, "tif", "image/ppm", popenimg, "ppm", "image/bmp", popenimg, "bmp", "image/tga", popenimg, "tga",