/*********************************************************/ /* program 29: TIFF read/write functions */ /*********************************************************/ #include #define II 1 /* intel byte order */ #define MM 2 /* motorola byte order */ #define BYTE 1 /* tag IFD field types */ #define ASCII 2 #define SHORT 3 #define LONG 4 #define RATIONAL 5 typedef struct /* header structure */ { short int byte_order; short int version; long int ifd_offset; } TIFF_HEADER; typedef struct /* pointer structure */ { shortint tag; shortint type; longint length; longint voff; } TIFF_POINTER; typedef struct /* file information structure */ { long int iimm; long int subfile_flags; long int photo_interp; long int planar_config; short int bits_sample[3]; short int sample_pixel; long int rows_strip; long int strip_cnt_length; long int strip_cnt_offset; long int strip_off_length; long int strip_off_offset; } TIFF_DATA; unsigned char *make_tag(); short int ii2mms(); long int ii2mml(); long int strip_read(); /*********************************************************/ /* function: read_tiff_header */ /*********************************************************/ void read_tiff_header(fp,xs,ys,pl,td) long int fp; /* file pointer */ long int *xs,*ys; /* x,y size */ long int *pl; /* plane count */ TIFF_DATA *td; /* file information */ { TIFF_HEADER header; TIFF_POINTER pointer; long int error; short int n,c,cnt; long int next_ifd; long int next_pointer; char *pbyte; short int *pshort; long int *plong; /* initialize variables */ *xs = 0; *ys = 0; *pl = 0; td->iimm = 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 file header */ read(fp,&header,sizeof(header)); if (header.byte_order == 0x4d4d) td->iimm = MM; else if (header.byte_order == 0x4949) td->iimm = II; else printf("!! INVALID TIFF BYTE ORDER: 0x%04x\n",header.byte_order); /* verify header information */ if (td->iimm == II) ii2mms(&header.version); if (header.version != 42) printf("!! INVALID TIFF VERSION: %d\n",header.version); /* get pointer to first IFD */ if (td->iimm == II) ii2mml(&header.ifd_offset); next_ifd = header.ifd_offset; /* process all IFDs */ while (next_ifd != 0) { lseek(fp,next_ifd,SEEK_SET); next_pointer = next_ifd; read(fp,&cnt,2); next_pointer = next_pointer + 2; if (td->iimm == II) ii2mms(&cnt); /* process all tagged pointers in this IFD */ for (c = 1; c <= cnt; c++) { read(fp,&pointer,sizeof(pointer)); next_pointer = next_pointer + sizeof(pointer); if (td->iimm == II) { ii2mms(&pointer.tag); ii2mms(&pointer.type); ii2mml(&pointer.length); 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("!! UNKNOWN TIFF POINTER TYPE: %d\n",pointer.type); break; } error = NULL; switch(pointer.tag) { case 254: /* new subfile type */ if ((pointer.type == LONG) && (pointer.length == 1)) td->subfile_flags = pointer.voff; else error++; break; case 256: /* image width */ if (pointer.length == 1) if (pointer.type == LONG) *xs = *plong; else if (pointer.type == SHORT) *xs = (long int)*pshort; else error++; else error++; break; case 257: /* image length */ if (pointer.length == 1) if (pointer.type == LONG) *ys = *plong; else if (pointer.type == SHORT) *ys = (long int)*pshort; else error++; else error++; break; case 258: /* bits per sample */ if (pointer.type == SHORT) { if (pointer.length == 1) { td->bits_sample[0] = *pshort; } else { lseek(fp,pointer.voff,SEEK_SET); for (n = 0; n < pointer.length; n++) { read(fp,&td->bits_sample[n],2); if (td->iimm == II) ii2mms(&td->bits_sample[n]); } lseek(fp,next_pointer,SEEK_SET); } } else { error++; } break; case 259: /* compression */ if ((pointer.type == SHORT) && (pointer.length == 1)) { if ((pointer.voff >> 16) != 1) printf("!! CANNOT DECODE TIFF COMPRESION: %d\n",pointer.voff); } else { error++; } break; case 262: /* photometric interpretation */ 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("!! CANNOT DECODE PHOTOMETRIC INTERP: %d\n", td->photo_interp); else error++; break; case 273: /* strip offsets */ 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; case 277: /* samples per pixel */ if (pointer.length == 1) if (pointer.type == SHORT) td->sample_pixel = *pshort; else error++; else error++; break; case 278: /* rows per strip */ 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; case 279: /* strip byte counts */ 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; case 284: /* planar configuration */ 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("!! UNKNOWN TIFF POINTER TAG: %d\n",pointer.tag); break; } if (error != NULL) printf("!! INVALID TIFF POINTER STRUCTURE: %d %d %d %d\n", pointer.tag,pointer.type,pointer.length,pointer.voff); } read(fp,&next_ifd,4); if (td->iimm == II) ii2mml(&next_ifd); } if (td->rows_strip == 0) td->rows_strip = *ys; return; } /*********************************************************/ /* function: read_tiff_data */ /*********************************************************/ void read_tiff_data(fp,xs,ys,pl,td,r,g,b) long int fp; /* file pointer */ long int xs,ys; /* x,y size */ long int pl; /* plane count */ TIFF_DATA td; /* file information */ unsigned char *r,*g,*b; /* data buffer pointers */ { unsigned char *dr,*dg,*db; unsigned char *line; long int sco,scf; long int soo,sof; long int c,l; /* 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 + (l / pl); dg = dg + (l / pl); db = db + (l / 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("!! ERROR, TIFF strip count/offset mismatch = %d,%d\n", td.strip_cnt_length,td.strip_off_length); } } else { printf("!! ERROR, TIFF bits/sample = %d\n",td.bits_sample[0]); } } else { printf("!! ERROR, TIFF planar config = %d\n",td.planar_config); } } else { printf("!! ERROR, TIFF photometric interp = %d\n",td.photo_interp); } return; } /*********************************************************/ /* function: write_tiff_header */ /*********************************************************/ void write_tiff_header(fp,xs,ys,pl) long int fp; /* file pointer */ long int xs,ys; /* x,y size */ long int pl; /* plane count */ { char cnt; unsigned char buf[512]; unsigned char *b; long int n; for (n = 0; n < 512; n++) buf[n] = 0; buf[0] = 0x4d; /* tiff file header */ buf[1] = 0x4d; buf[3] = 42; buf[7] = 10; cnt = 0; b = buf + 12; b = make_tag(254,LONG, 1,0,b,&cnt); /* new subfile type */ b = make_tag(256,SHORT,1,(xs<<16),b,&cnt); /* image width */ b = make_tag(257,SHORT,1,(ys<<16),b,&cnt); /* image length */ if (pl == 3) /* bits per sample */ b = make_tag(258,SHORT,3,256,b,&cnt); else b = make_tag(258,SHORT,1,(8<<16),b,&cnt); b = make_tag(259,SHORT,1,(1<<16),b,&cnt); /* compression */ if (pl == 3) /* photometric interpretation */ b = make_tag(262,SHORT,1,(2<<16),b,&cnt); else b = make_tag(262,SHORT,1,(1<<16),b,&cnt); b = make_tag(273,LONG, 1,512,b,&cnt); /* strip offset */ b = make_tag(277,SHORT,1,(pl<<16),b,&cnt); /* samples per pixel */ b = make_tag(278,SHORT,1,(ys<<16),b,&cnt); /* rows per strip */ b = make_tag(279,LONG, 1,(ys*xs*pl),b,&cnt); /* strip byte count */ b = make_tag(284,SHORT,1,(1<<16),b,&cnt); /* planar configuration */ b = make_tag(0,0,0,0,b,&cnt); /* next IFD */ cnt--; buf[11] = cnt; if (pl == 3) { buf[257] = 8; buf[259] = 8; buf[261] = 8; } write(fp,buf,512); return; } /*********************************************************/ /* function: write_tiff_data */ /*********************************************************/ void write_tiff_data(fp,xs,ys,pl,r,g,b) long int fp; /* file pointer */ long int xs,ys; /* x,y size */ long int pl; /* plane count */ unsigned char *r,*g,*b; /* data buffer pointers */ { long int x,y,z; unsigned char *dr,*dg,*db; int error; long int *p,*s; unsigned char *l,*line; 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++; } } write(fp,line,(xs*pl)); } free(line); return; } /*********************************************************/ /* utility functions */ /*********************************************************/ unsigned char *make_tag(tag,type,lng,fld,b,cnt) 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 ii2mms(value) short int *value; { short temp; temp = *value; *value = ((temp >> 8) & 0x00ff) + ((temp << 8) & 0xff00); return; } long ii2mml(value) long int *value; { long temp; temp = *value; *value = ((temp >> 24) & 0x000000ff) + ((temp >> 8) & 0x0000ff00) + ((temp << 8) & 0x00ff0000) + ((temp << 24) & 0xff000000); return; } long strip_read(fp,im,xs,pl,sco,scf,soo,sof,line,r,g,b,lng) long int fp; long int im; long int xs,pl; long int sco,scf; long int soo,sof; unsigned char *line; unsigned char *r,*g,*b; long int *lng; { long int lcnt,loff; short int scnt,soff; long int x; unsigned char *l; lseek(fp,sco,SEEK_SET); if (scf == LONG) { read(fp,&lcnt,4); if (im == II) ii2mml(&lcnt); } else { read(fp,&scnt,2); if (im == II) ii2mms(&scnt); lcnt = (long int)scnt; } *lng = lcnt; lseek(fp,soo,SEEK_SET); if (sof == LONG) { read(fp,&loff,4); if (im == II) ii2mml(&loff); } else { read(fp,&soff,2); if (im == II) ii2mms(&soff); loff = (long int)soff; } lseek(fp,loff,SEEK_SET); while (lcnt > 0) { read(fp,line,(xs*pl)); l = line; for (x = 0; x < xs; x++) { *r++ = *l++; if (pl == 3) { *g++ = *l++; *b++ = *l++; } } lcnt = lcnt - (xs * pl); } return; }