/* TIFF read/write functions */ /* Reference: Howard Burdick, Digital Imaging, Theory and Applications, McGraw Hill, 1997, page 223-227. */ #include "sys/file.h" #include "sys/types.h" #include #include #include #include "tiff_io.h" /******************************************************************************/ int read_tiff_header ( FILE *fp, long int *xs, long int *ys, long int *pl, TIFF_DATA *td ) { /******************************************************************************/ /* Purpose: READ_TIFF_HEADER reads the header from a TIFF file. Modified: 08 November 2000 Reference: Howard Burdick, Digital Imaging, Theory and Applications, McGraw Hill, 1997, page 223-227. Parameters: FILE *FP, the pointer to the file. long int XS, YS, the X and Y sizes, in pixels, of the image. long int PL, the number of planes of data. TIFF_DATA *TD. */ short int c; short int cnt; long int error; TIFF_HEADER header; short int n; long int next_ifd; long int next_pointer; char *pbyte; long int *plong; TIFF_POINTER pointer; short int *pshort; /* Initialize variables. */ *xs = 0; *ys = 0; *pl = 0; td->iimm = ( long int ) 0; td->planar_config = 1; td->rows_strip = 0; td->bits_sample[0] = 0; td->bits_sample[1] = 0; td->bits_sample[2] = 0; /* Read the file header. */ printf ( "\n" ); printf ( "READ_TIFF_HEADER:\n" ); printf ( " About to read first line of header.\n" ); fread ( &header, sizeof ( header ), 1, fp ); if ( header.byte_order == 0x4d4d ) { td->iimm = MM; printf ( "\n" ); printf ( "READ_TIFF_HEADER:\n" ); printf ( " Header byte order is 0x4d4d.\n" ); printf ( " (No byte swapping is necessary.)\n" ); } else if ( header.byte_order == 0x4949) { td->iimm = II; printf ( "\n" ); printf ( "READ_TIFF_HEADER:\n" ); printf ( " Header byte order is 0x4949.\n" ); printf ( " (Byte swapping is necessary.)\n" ); } else { printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Invalid TIFF byte order: 0x%04x\n", header.byte_order ); return 1; } /* Verify the header information. */ if ( td->iimm == II ) { header.version = ii2mms ( header.version ); } if ( header.version != 42 ) { printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Invalid TIFF version: %d\n", header.version ); return 2; } /* Get the pointer to the first IFD */ if ( td->iimm == II ) { header.ifd_offset = ii2mml ( header.ifd_offset ); } next_ifd = header.ifd_offset; /* Process all IFD's. */ while ( next_ifd != 0 ) { fseek ( fp, next_ifd, SEEK_SET ); next_pointer = next_ifd; fread ( &cnt, 2, 1, fp ); next_pointer = next_pointer + 2; if ( td->iimm == II ) { cnt = ii2mms ( cnt ); } /* Process all tagged pointers in this IFD. */ for ( c = 1; c <= cnt; c++ ) { fread ( &pointer, sizeof ( pointer ), 1, fp ); next_pointer = next_pointer + sizeof ( pointer ); if ( td->iimm == II ) { pointer.tag = ii2mms ( pointer.tag ); pointer.type = ii2mms ( pointer.type ); pointer.length = ii2mml ( pointer.length ); pointer.voff = ii2mml ( pointer.voff ); if ( pointer.type == SHORT ) { pointer.voff = pointer.voff << 16; } } switch ( pointer.type ) { case BYTE: case ASCII: pbyte = ( char * ) &pointer.voff; break; case SHORT: pshort = ( short int * ) &pointer.voff; break; case LONG: case RATIONAL: plong = ( long int * ) &pointer.voff; break; default: printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Unknown TIFF pointer type: %d\n", pointer.type ); return 3; } error = NULL; printf ( "DEBUG: Handling case POINTER.TAG = %d.\n", pointer.tag ); switch ( pointer.tag ) { /* New subfile type. */ case 254: if ( ( pointer.type == LONG ) && ( pointer.length == 1 ) ) { td->subfile_flags = pointer.voff; } else { error++; } break; /* Image width. */ case 256: if ( pointer.length == 1 ) { if ( pointer.type == LONG ) { *xs = *plong; } else if ( pointer.type == SHORT ) { *xs = ( long int ) *pshort; } else { error++; } } else { error++; } break; /* Image length. */ case 257: if ( pointer.length == 1 ) { if ( pointer.type == LONG ) { *ys = *plong; } else if ( pointer.type == SHORT ) { *ys = (long int) *pshort; } else { error++; } } else { error++; } break; /* Bits per sample. */ case 258: if ( pointer.type == SHORT ) { printf ( "pointer.type is short.\n" ); if ( pointer.length == 1 ) { printf ( "pointer.length is 1.\n"); td->bits_sample[0] = *pshort; } else { printf ( "pointer.length is not 1.\n"); fseek ( fp, pointer.voff, SEEK_SET ); for ( n = 0; n < pointer.length; n++ ) { fread ( &td->bits_sample[n], 2, 1, fp ); if ( td->iimm == II ) { td->bits_sample[n] = ii2mms ( td->bits_sample[n] ); } } fseek ( fp, next_pointer, SEEK_SET ); } } else { error++; } break; /* Compression. */ case 259: if ( ( pointer.type == SHORT ) && ( pointer.length == 1 ) ) { if ( ( pointer.voff >> 16) != 1 ) { printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Cannot decode TIFF compression: %d\n", pointer.voff ); return 4; } } else { error++; } break; /* Photometric interpretation. */ case 262: td->photo_interp = pointer.voff >> 16; if ( ( pointer.type == SHORT ) && ( pointer.length == 1 ) ) { if ( ( td->photo_interp == 0 ) || ( td->photo_interp == 1 ) ) { *pl = 1; } else if ( td->photo_interp == 2 ) { *pl = 3; } else { printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Cannot decode photometric interp: %d\n", td->photo_interp); return 5; } } else { error++; } break; /* Strip offsets. */ case 273: td->strip_off_length = pointer.length; if ( td->strip_off_length == 1 ) { td->strip_off_offset = next_pointer - 4; } else if ( pointer.type == SHORT ) { td->strip_off_offset = ( long int ) *pshort; } else { td->strip_off_offset = *plong; } if (pointer.type == SHORT) { td->strip_off_offset = - td->strip_off_offset; } break; /* Samples per pixel. */ case 277: if ( pointer.length == 1 ) { if ( pointer.type == SHORT ) { td->sample_pixel = *pshort; } else { error++; } } else { error++; } break; /* Rows per strip. */ case 278: if ( pointer.length == 1 ) { if ( pointer.type == LONG ) { td->rows_strip = *plong; } else if ( pointer.type == SHORT ) { td->rows_strip = (long int) *pshort; } else { error++; } } else { error++; } break; /* Strip byte counts. */ case 279: td->strip_cnt_length = pointer.length; if ( td->strip_cnt_length == 1 ) { td->strip_cnt_offset = next_pointer - 4; } else if ( pointer.type == SHORT ) { td->strip_cnt_offset = (long int) *pshort; } else { td->strip_cnt_offset = *plong; } if (pointer.type == SHORT) { td->strip_cnt_offset = -td->strip_cnt_offset; } break; /* Planar configuration. */ case 284: if ( ( pointer.type == SHORT) && ( pointer.length == 1 ) ) { td->planar_config = pointer.voff >> 16; } else { error++; } break; /* The following tags are ignored: */ case 282: /* x resolution */ case 283: /* y resolution */ case 290: /* grey response unit */ case 291: /* grey response curve */ case 296: /* resolution unit */ case 301: /* color response curves */ case 317: /* predictor */ case 318: /* white point */ case 319: /* primary chromaticities */ case 320: /* color map */ case 270: /* image description */ case 271: /* make */ case 272: /* model */ case 305: /* software */ case 306: /* date time */ case 315: /* artist */ case 316: /* host computer */ case 292: /* group 3 options */ case 293: /* group 4 options */ case 269: /* document name */ case 285: /* page name */ case 286: /* x position */ case 287: /* y position */ case 297: /* page number */ break; default: printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Unknown TIFF pointer tag: %d\n", pointer.tag ); return 6; } if ( error != NULL ) { printf ( "\n" ); printf ( "READ_TIFF_HEADER - Error!\n" ); printf ( " Invalid TIFF pointer structure: %d %d %d %d\n", pointer.tag, pointer.type, pointer.length, pointer.voff ); return 7; } } fread ( &next_ifd, 4, 1, fp ); if ( td->iimm == II ) { next_ifd = ii2mml ( next_ifd ); } } if ( td->rows_strip == 0 ) { td->rows_strip = *ys; } return 0; } /******************************************************************************/ int read_tiff_data ( FILE *fp, long int xs, long int ys, long int pl, TIFF_DATA td, unsigned char *r, unsigned char *g, unsigned char *b ) { /******************************************************************************/ /* Purpose: READ_TIFF_DATA reads the data from a TIFF file. Modified: 08 November 2000 Reference: Howard Burdick, Digital Imaging, Theory and Applications, McGraw Hill, 1997, page 223-227. Parameters: FILE *FP, the pointer to the file. long int XS, YS, the X and Y sizes, in pixels, of the image. long int PL, the number of planes of data. TIFF_DATA *TD. unsigned char *R, *G, *B, the RGB data. */ long int c; unsigned char *db; unsigned char *dg; unsigned char *dr; long int l; unsigned char *line; long int scf; long int sco; long int sof; long int soo; /* Verify information for this file. */ if ( ( td.photo_interp == 0 ) || ( td.photo_interp == 1 ) || ( td.photo_interp == 2 ) ) { if ( td.planar_config == 1 ) { if ( td.bits_sample[0] == 8 ) { if ( td.strip_cnt_length == td.strip_off_length ) { line = ( unsigned char *) malloc (xs*pl); dr = r; dg = g; db = b; sco = td.strip_cnt_offset; if ( sco >= 0 ) { scf = LONG; } else { sco = - sco; scf = SHORT; } soo = td.strip_off_offset; if ( soo >= 0 ) { sof = LONG; } else { soo = - sco; sof = SHORT; } /* Read all strips */ for ( c = 0; c < td.strip_cnt_length; c++ ) { strip_read ( fp, td.iimm, xs, pl, sco, scf, soo, sof, line, dr, dg, db, &l ); dr = dr + ( 1 / pl ); dg = dg + ( 1 / pl ); db = db + ( 1 / pl ); if ( scf == SHORT ) { sco = sco + 2; } else { sco = sco + 4; } if ( sof == SHORT ) { soo = soo + 2; } else { soo = soo + 4; } } free ( line ); } else { printf ( "\n" ); printf ( "READ_TIFF_DATA - Error!\n" ); printf ( " TIFF strip count/offset mismatch=%d,%d\n", td.strip_cnt_length, td.strip_off_length ); return 8; } } else { printf ( "\n" ); printf ( "READ_TIFF_DATA - Error!\n" ); printf ( " TIFF bits/sample = %d\n", td.bits_sample[0] ); return 9; } } else { printf ( "\n" ); printf ( "READ_TIFF_DATA - Error!\n" ); printf ( " TIFF planar config %d\n", td.planar_config ); return 10; } } else { printf ( "\n" ); printf ( "READ_TIFF_DATA - Error!\n" ); printf ( " TIFF photometric interp %d\n", td.photo_interp ); return 11; } return 0; } /******************************************************************************/ void write_tiff_header ( FILE *fp, long int xs, long int ys, long int pl ) { /******************************************************************************/ unsigned char *b; unsigned char buf[512]; char cnt; long int n; for ( n = 0; n < 512; n++ ) { buf[n] = 0; } /* TIFF file header. */ buf[0] = 0x4d; buf[1] = 0x4d; buf[3] = 42; buf[7] = 10; cnt = 0; b = buf + 12; /* New subfile type. */ b = make_tag ( 254, LONG, 1, 0, b, &cnt ); /* Image width. */ b = make_tag ( 256, SHORT, 1, (xs<<16), b, &cnt ); /* Image length. */ b = make_tag ( 257, SHORT, 1, (ys<<16), b, &cnt ); /* Bits per sample. */ if ( pl == 3 ) { b = make_tag ( 258, SHORT, 3, 256, b, &cnt ); } else { b = make_tag ( 258, SHORT, 1, (8<<16), b, &cnt ); } /* Compression. */ b = make_tag ( 259, SHORT, 1, (1<<16), b, &cnt ); /* Photo interp. */ if ( pl == 3 ) { b = make_tag ( 262, SHORT, 1, (2<<16), b, &cnt ); } else { b = make_tag ( 262, SHORT, 1, (1<<16), b, &cnt ); } /* Strip offset. */ b = make_tag ( 273, LONG, 1, 512, b, &cnt ); /* Samples per pixel. */ b = make_tag ( 277, SHORT, 1, (pl<<16), b, &cnt ); /* Rows per strip. */ b = make_tag ( 278, SHORT, 1, (ys<<16), b, &cnt); /* Strip byte count. */ b = make_tag ( 279, LONG, 1, (ys*xs*pl), b, &cnt ); /* Planar configuration. */ b = make_tag ( 284, SHORT, 1, (1<<16), b, &cnt ); /* Next IFD. */ b = make_tag ( 0, 0, 0, 0, b, &cnt ); cnt--; buf[11] = cnt; if ( pl == 3 ) { buf[257] = 8; buf[259] = 8; buf[261] = 8; } fwrite ( buf, 1, 512, fp ); return; } /******************************************************************************/ void write_tiff_data ( FILE *fp, long int xs, long int ys, long int pl, unsigned char *r, unsigned char *g, unsigned char *b ) { /******************************************************************************/ unsigned char *db; unsigned char *dg; unsigned char *dr; unsigned char *l; unsigned char *line; long int x; long int y; line = ( unsigned char * ) malloc ( xs*pl ); for ( y = 0; y < ys; y++ ) { dr = r + (y * xs); dg = g + (y * xs); db = b + (y * xs); l = line; for ( x = 0; x < xs; x++ ) { *l++ = *dr++; if ( pl > 1 ) { *l++ = *dg++; *l++ = *db++; } } fwrite ( line, 1, (xs*pl), fp ); } free ( line ); return; } /******************************************************************************/ unsigned char *make_tag ( short int tag, short int type, long int lng, long int fld, unsigned char *b, char *cnt ) { /******************************************************************************/ *b++ = ( unsigned char ) ( (tag >> 8) & 0xff ); *b++ = ( unsigned char ) ( tag & 0xff ); *b++ = ( unsigned char ) ( (type >> 8) & 0xff ); *b++ = ( unsigned char ) ( type & 0xff ); *b++ = ( unsigned char ) ( (lng >> 24) & 0xff ); *b++ = ( unsigned char ) ( (lng >> 16) & 0xff ); *b++ = ( unsigned char ) ( (lng >> 8) & 0xff ); *b++ = ( unsigned char ) ( lng & 0xff ); *b++ = ( unsigned char ) ( (fld >> 24) & 0xff ); *b++ = ( unsigned char ) ( (fld >> 16) & 0xff ); *b++ = ( unsigned char ) ( (fld >> 8) & 0xff ); *b++ = ( unsigned char ) ( fld & 0xff ); *cnt = *cnt + 1; return ( b ); } /******************************************************************************/ short int ii2mms ( short int input ) { /******************************************************************************/ short int output; output = ( ( input >> 8 ) & 0x00ff ) + ( ( input << 8 ) & 0xff00 ); return output; } /******************************************************************************/ long int ii2mml ( long int input ) { /******************************************************************************/ long int output; output = ( ( input >> 24 ) & 0x000000ff ) + ( ( input >> 8 ) & 0x0000ff00 ) + ( ( input << 8 ) & 0x00ff0000 ) + ( ( input << 24 ) & 0xff000000 ); return output; } /******************************************************************************/ void strip_read ( FILE *fp, long int im, long int xs, long int pl, long int sco, long int scf, long int soo, long int sof, unsigned char *line, unsigned char *r, unsigned char *g, unsigned char *b, long int *lng ) { /******************************************************************************/ unsigned char *l; long int lcnt; long int loff; short int scnt; short int soff; long int x; fseek ( fp, sco, SEEK_SET ); if ( scf == LONG ) { fread ( &lcnt, 4, 1, fp ); if ( im == II ) { lcnt = ii2mml ( lcnt ); } } else { fread ( &scnt, 2, 1, fp ); if ( im == II ) { scnt = ii2mms ( scnt ); } } *lng = lcnt; fseek ( fp, soo, SEEK_SET ); if ( sof == LONG ) { fread ( &loff, 4, 1, fp ); if ( im == II ) { loff = ii2mml ( loff ); } } else { fread ( &soff, 2, 1, fp ); if ( im == II ) { soff = ii2mms ( soff ); } } fseek ( fp, loff, SEEK_SET ); while ( lcnt > 0 ) { fread ( line, (xs*pl), 1, fp ); l = line; for ( x = 0; x < xs; x++ ) { *r++ = *l++; if ( pl == 3 ) { *g++ = *l++; *b++ = *l++; } } lcnt = lcnt + ( xs * pl ); } return; }