/********************************************************************** * TIFFWRITE.C * Julian Fong * August 1995 * * ***WARNING*** this code is a hack! I'm not responsible for nausea, * dizziness, vomiting or chest pains caused by perusing this code. * * That being said, this is simply code to write a 24 bit RGB TIFF file, * based on the Aldus/Microsoft Tech Memorandum/TIFF Revision 5.0/Aug 1988, * available from * gopher://palimpsest.stanford.edu:70/00/ByTopic/standards/tiff.5.0.txt * (If Adobe had been considerate enough to provide Acrobat for SGI I would * have read the Rev 6.0 document instead) * * The RGB file produced is 24 bit, uncompressed, and has the minimum 14 * tags necessary for TIFF RGB images according to Rev 6.0 (at least, * as far as I could tell from the Image Format Encyclopedia). * * The code is very hackish since I'm using precomputed file offsets as * follows: * 0: 8 bit header * 8: Start of IFD: 2 bytes count (14) * 10: NewSubFileType tag * 22: ImageWidth tag * 34: ImageLength tag * 46: BitsPerSamples (points to 182) * 58: Compression * 70: PhotometricInterpretation * 82: StripOffsets (points to 204) * 94: SamplesPerPixel *106: RowsPerStrip *118: StripByteCounts (points to 204 + 4 * height) *130: XResolution (points to 188) *142: YResolution (points to 196) *154: PlanarConfiguration *166: ResolutionUnit *178: 4 Null bytes (End of IFD) *182: 6 bytes of BitsPerSample info *188: X Resolution Information (RATIONAL, so two longs = 8 bytes) *196: Y Resolution Information (RATIONAL, so two longs = 8 bytes) *204: The StripOffsets information. *204 + 4 * height: The StripByteCounts information. *204 + 8 * height: The Image. * **********************************************************************/ #include #include /* comment this out if you're not using OpenGL. It only affects the image being upside down */ #ifndef OPENGL #define OPENGL #endif /* bit of a hack which gives me "I" or "M" depending on byte order. This used to be a function, hence the parentheses */ #define end_type() ((*(short *) "IM") & 255) /* weird TIFF defines */ #define TIFF_BYTE 1 #define TIFF_ASCII 2 #define TIFF_SHORT 3 #define TIFF_LONG 4 #define TIFF_RATIONAL 5 #define NewSubfileType 254 #define ImageWidth 256 #define ImageLength 257 #define BitsPerSample 258 #define Compression 259 #define PhotometricInterpretation 262 #define StripOffsets 273 #define SamplesPerPixel 277 #define RowsPerStrip 278 #define StripByteCounts 279 #define XResolution 282 #define YResolution 283 #define PlanarConfiguration 284 #define ResolutionUnit 296 /* function for writing a tag which has its value pointing elsewhere in the file */ static void write_pointer_tag(FILE *fp, short tagnum, short size, long elements, long pointer) { fwrite(&tagnum, sizeof(short), 1, fp); fwrite(&size, sizeof(short), 1, fp); fwrite(&elements, sizeof(long), 1, fp); fwrite(&pointer, sizeof(long), 1, fp); } /* function which writes a tag with a short value */ static void write_short_tag(FILE *fp, short tagnum, long elements, short value) { long templong; short size = TIFF_SHORT; /* short */ /* The spec doc says "values which fit in less than 4 bytes are left justified". Thus, due to byte order I need this brain-damaged code. On Intel systems, left justified is the default */ if (end_type() == 'I') templong = (long) value; /* on Motorola systems, left justified for shorts is shifted left 16 bits */ else templong = (long)value << 16; fwrite(&tagnum, sizeof(short), 1, fp); fwrite(&size, sizeof(short), 1, fp); fwrite(&elements, sizeof(long), 1, fp); fwrite(&templong, sizeof(long), 1, fp); } /* function which writes a tag with a long value */ static void write_long_tag(FILE *fp, short tagnum, long elements, long value) { short size = TIFF_LONG; /* long */ fwrite(&tagnum, sizeof(short), 1, fp); fwrite(&size, sizeof(short), 1, fp); fwrite(&elements, sizeof(long), 1, fp); fwrite(&value, sizeof(long), 1, fp); } /* main function */ void tiffwrite(char *filename, unsigned char *rgb, unsigned long int width, unsigned long int height) { FILE *fp; char tempchar; short int tempshort; long int templong; long int counter; long num_bytes_per_row; long int byte_counter = 0; if ((fp = fopen(filename, "w+")) == NULL) { fprintf(stderr, "Error in opening file %s!\n", filename); return; } /* write two byte order chars */ tempchar = end_type(); fwrite(&tempchar,1,1,fp); fwrite(&tempchar,1,1,fp); /* write the 42, the answer to life, the universe, and everything */ tempshort = 42; fwrite(&tempshort,sizeof(short),1,fp); /* IFD address is at 8 */ templong = 8; fwrite(&templong,sizeof(long),1,fp); /****************************************************/ /********************* BEGIN IFD ********************/ /****************************************************/ /* RGB TIFF has minimum 14 tags*/ tempshort = 14; fwrite(&tempshort,sizeof(short), 1, fp); /* NewSubfileType is 0, meaning this image is a simple image */ write_long_tag(fp, NewSubfileType, 1, 0); /* Image width and height */ write_long_tag(fp, ImageWidth, 1, width); write_long_tag(fp, ImageLength, 1, height); /* BitsPerSample is 8, 8, 8 for RGB */ write_pointer_tag(fp, BitsPerSample, TIFF_SHORT, 3, 182); /* Compression = 1, means no compression */ write_short_tag(fp, Compression, 1, 1); /* PhotometricInterpretation = 2, means Image is an RGB */ write_short_tag(fp, PhotometricInterpretation, 1, 2); /* StripOffsets begin at 204 */ write_pointer_tag(fp, StripOffsets, TIFF_LONG, height, 204); /* RGB image has 3 samples per pixel */ write_short_tag(fp, SamplesPerPixel, 1, 3); /* Each strip represents one row */ write_long_tag(fp, RowsPerStrip, 1, 1); /* StripByteCounts begin at 204 + 4 * height (StripOffsets were longs) */ write_pointer_tag(fp, StripByteCounts, TIFF_LONG, height, 204 + 4 * height); /* pointer to two rational tags */ write_pointer_tag(fp, XResolution, TIFF_RATIONAL, 1, 188); write_pointer_tag(fp, YResolution, TIFF_RATIONAL, 1, 196); /* Image is stored as RGBRGBRGBRGB */ write_short_tag(fp, PlanarConfiguration, 1, 1); /* ResUnit = inch.. again, arbitrary */ write_short_tag(fp, ResolutionUnit, 1, 2); /* write 4 null bytes */ templong = 0; fwrite(&templong, sizeof(long), 1, fp); /****************************************************/ /********************* END IFD ********************/ /****************************************************/ /* write bitspersample info */ tempshort = 8; fwrite(&tempshort, sizeof(short), 1, fp); fwrite(&tempshort, sizeof(short), 1, fp); fwrite(&tempshort, sizeof(short), 1, fp); /* write xres and yres info. Arbitrary chosen to be 72 dpi, since that's close to what the monitor is */ templong = 72; fwrite(&templong, sizeof(long), 1, fp); templong = 1; fwrite(&templong, sizeof(long), 1, fp); templong = 72; fwrite(&templong, sizeof(long), 1, fp); templong = 1; fwrite(&templong, sizeof(long), 1, fp); num_bytes_per_row = width * 3; /* strips start at 204 + 8 * height. */ byte_counter = 204 + 8 * height; for (counter = 0; counter < height; counter++) { fwrite(&byte_counter, sizeof(long), 1, fp); byte_counter += num_bytes_per_row; } /* strip byte counter info */ for (counter = 0; counter < height; counter++) fwrite(&num_bytes_per_row, sizeof(long), 1, fp); #ifdef OPENGL /* write image */ for (counter = height - 1; counter >= 0; counter--) { /* the image from OpenGL is upside down. We have to write rows backwards */ fwrite(rgb + (counter * num_bytes_per_row), sizeof(unsigned char), (3 * width), fp); } #else /* just dump entire array to disk */ fwrite(rgb, sizeof(unsigned char), (3 * width * height), fp); #endif /* end */ fclose(fp); }