// iffReader.C //////////////////////////////////////////////////////////////// /* * Maya IFF image reading and writing code * Copyright (C) 1997-1999 Mike Taylor * (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor) * Copyright (C) 2003 Luke Tokheim * Copyright (C) 2003-2009 The Foundry Visionmongers Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Note: this license applies only to this plugin, and not to the rest * of DDImage, Nuke, etc. */ //////////////////////////////////////////////////////////////// #include #include "DDImage/Reader.h" #include "DDImage/LUT.h" //#define __IFF_DEBUG_ typedef struct _iff_image { unsigned width; //!< Image width. unsigned height; //!< Image height. unsigned depth; //!< Color format, bytes per color pixel. unsigned datatype; //!< 0=byte, 1=short, 3=float? unsigned char* rgba; //!< The color data of size width*height*depth. unsigned short* srgba; //!< data if datatype==1 float* frgba; //!< data if datatype==3 float znear; //!< The near clipping plane for the z buffer data. float zfar; //!< The far clipping plane for the z buffer data. /*! Access pixel x,y as zbuffer[width*y + x]. (Starting from the top left.) The stored values now are -1/z components in light eye space. When reading a z depth value back, one thus need to compute (-1.0 / z) to get the real z component in eye space. This format is the same as the camera depth format. This format is always used, whatever the light type is. In order to be able to compute real 3D distances of the shadow samples there's an IFF tag: 'ESXY' 'Eye Space X Y' values are two float values, the first one is the width size, the second one is the height size matching the map width and map height. When reading the shadow map buffer, one can thus convert from the pixel coordinates to the light eye space xy coords. If the pixel coordinates are considered to be normalized in the [-1.0, 1.0] range, the eye space xy are given by: X_light_eye_space = 3D normalized_pixel_x * (width / 2.0) Y_light_eye_space = 3D normalized_pixel_y * (heigth / 2.0) Once one get the (X,Y,Z) light eye space coordinates, the true 3D distance from the light source is: true_distance = 3D sqrt(X=B2 + Y=B2 + Z=B2) (This was copied from the Alias|Wavefront API Knowledge Base.) */ float* zbuffer; //!< The z buffer data of size width*height. /*! Eye x-ratio. This is a depth map specific field used to compute the xy eye coordinates from the normalized pixel. */ float zesx; /*! Eye y-ratio. This is a depth map specific field used to compute the xy eye coordinates from the normalized pixel. */ float zesy; /*! This does not work right now! I do not know how to interpret these vectors. If you can figure it out, please let me know. */ float* blurvec; //!< The packed xy motion blur vectors of size 2*width*height. } iff_image; iff_image* iff_load( const char* filename ); void iff_free( iff_image* image ); unsigned iff_get_error(); /*////////////////////////////////////////////////////////////// */ #include "DDImage/DDWindows.h" #include #include #include /// The original IFF reader code used global variables so was non-re-entrant and hence not thread-safe. /// By wrapping up its state and functions in a class, multiple instances of the API are enabled to operate /// concurrently without interfering with each-other. Any individual instance is still not thread-safe. /// /// This isn't the prettiest possible solution but it does stop issues with multi-threaded decoding in Hiero. class IFFApi { private: typedef unsigned char uByte; typedef unsigned int uInt32; typedef unsigned short uInt16; typedef float Float32; #define RGB_FLAG (1) #define ALPHA_FLAG (2) #define ZBUFFER_FLAG (4) #define CHUNK_STACK_SIZE (32) /* Error code definitions */ #define IFF_NO_ERROR (0) #define IFF_OPEN_FAILS (1) #define IFF_READ_FAILS (2) #define IFF_BAD_TAG (3) #define IFF_BAD_COMPRESS (4) #define IFF_BAD_STACK (5) #define IFF_BAD_CHUNK (6) /* Define the IFF tags we are looking for in the file. */ static const uInt32 IFF_TAG_CIMG = ('C' << 24) | ('I' << 16) | ('M' << 8) | ('G'); static const uInt32 IFF_TAG_FOR4 = ('F' << 24) | ('O' << 16) | ('R' << 8) | ('4'); static const uInt32 IFF_TAG_TBHD = ('T' << 24) | ('B' << 16) | ('H' << 8) | ('D'); static const uInt32 IFF_TAG_TBMP = ('T' << 24) | ('B' << 16) | ('M' << 8) | ('P'); static const uInt32 IFF_TAG_RGBA = ('R' << 24) | ('G' << 16) | ('B' << 8) | ('A'); static const uInt32 IFF_TAG_CLPZ = ('C' << 24) | ('L' << 16) | ('P' << 8) | ('Z'); static const uInt32 IFF_TAG_ESXY = ('E' << 24) | ('S' << 16) | ('X' << 8) | ('Y'); static const uInt32 IFF_TAG_ZBUF = ('Z' << 24) | ('B' << 16) | ('U' << 8) | ('F'); static const uInt32 IFF_TAG_BLUR = ('B' << 24) | ('L' << 16) | ('U' << 8) | ('R'); static const uInt32 IFF_TAG_BLRT = ('B' << 24) | ('L' << 16) | ('R' << 8) | ('T'); //const uInt32 IFF_TAG_HIST = ('H' << 24) | ('I' << 16) | ('S' << 8) | ('T'); /* For the stack of chunks */ typedef struct _iff_chunk { uInt32 tag; uInt32 start; uInt32 size; uInt32 chunkType; } iff_chunk; iff_chunk chunkStack[CHUNK_STACK_SIZE]; int chunkDepth; /* The current error state. */ unsigned iff_error; /* * -- Basic input functions. * */ uInt16 iff_get_short( FILE* file ) { uByte buf[2]; size_t result = 0; result = fread( buf, 2, 1, file); if ( result != 1 ) { if ( iff_error == IFF_NO_ERROR ) { assert(false); iff_error = IFF_READ_FAILS; } return 0 ; } return ( buf[0] << 8 ) + ( buf[1] ) ; } uInt32 iff_get_long( FILE* file ) { uByte buffer[4]; size_t result = fread( buffer, 4, 1, file ); if ( result != 1 ) { if ( iff_error == IFF_NO_ERROR ) { assert(false); iff_error = IFF_READ_FAILS; } return 0 ; } return ( buffer[0] << 24 ) + ( buffer[1] << 16 ) + ( buffer[2] << 8 ) + ( buffer[3] << 0 ) ; } Float32 iff_get_float( FILE* file ) { uByte buffer[4]; uInt32 value; size_t result = fread( buffer, 4, 1, file ); if ( result != 1 ) { if ( iff_error == IFF_NO_ERROR ) { assert(false); iff_error = IFF_READ_FAILS; } return 0.0 ; } value = ( buffer[3] << 24 ) + ( buffer[2] << 16 ) + ( buffer[1] << 8 ) + ( buffer[0] << 0 ); return *((Float32*)&value) ; } /* * IFF Chunking Routines. * */ iff_chunk iff_begin_read_chunk( FILE* file ) { chunkDepth++; if ( (chunkDepth >= CHUNK_STACK_SIZE) || (chunkDepth < 0) ) { if ( iff_error == IFF_NO_ERROR ) { iff_error = IFF_BAD_STACK; } return chunkStack[0] ; } chunkStack[chunkDepth].start = ftell( file ); chunkStack[chunkDepth].tag = iff_get_long( file ); chunkStack[chunkDepth].size = iff_get_long( file ); if ( chunkStack[chunkDepth].tag == IFF_TAG_FOR4 ) { // -- We have a form, so read the form type tag as well. chunkStack[chunkDepth].chunkType = iff_get_long( file ); } else { chunkStack[chunkDepth].chunkType = 0; } #if 0 //def __IFF_DEBUG_ printf( "Beginning Chunk: %c%c%c%c", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF)); printf(" start: %u", chunkStack[chunkDepth].start ); printf(" size: %u", chunkStack[chunkDepth].size ); if ( chunkStack[chunkDepth].chunkType != 0 ) { printf(" type: %c%c%c%c", (((&chunkStack[chunkDepth].chunkType)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].chunkType)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].chunkType)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].chunkType)[0] >> 0) & 0xFF)); } printf( " depth: %d\n", chunkDepth ); #endif return chunkStack[chunkDepth] ; } void iff_end_read_chunk( FILE* file ) { uInt32 end = chunkStack[chunkDepth].start + chunkStack[chunkDepth].size + 8; int part; if ( chunkStack[chunkDepth].chunkType != 0 ) { end += 4; } // Add padding part = end % 4; if ( part != 0 ) { end += 4 - part; } fseek( file, end, SEEK_SET ); #if 0 //def __IFF_DEBUG_ printf( "Closing Chunk: %c%c%c%c\n\n", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF) ); #endif chunkDepth--; } /* * Compression Routines * */ static void decompress_rle(uByte* data, uInt32 delta, uInt32 numBytes, uByte* compressedData, uInt32 compressedDataSize, uInt32* compressedIndex ) { #if 0 //def __IFF_DEBUG_ printf( "Decompressing data %d\n", numBytes ); #endif uInt32 FROM = *compressedIndex; uInt32 TO = 0; while (TO < numBytes) { if (FROM >= compressedDataSize) { printf("NOT ENOUGH COMPRESSED DATA\n"); while (TO < numBytes) data[delta * TO++] = 0; break; } uByte nextChar = compressedData[FROM++]; unsigned count = (nextChar & 0x7f) + 1; if ( ( TO + count ) > numBytes ) { printf("COUNT BAD, %d+%d > %d\n", TO, count, numBytes); count = numBytes - TO; // printf("Start at %d of %d: ", *compressedIndex, compressedDataSize); // for (uInt32 i = *compressedIndex; i <= FROM;) { // uByte n = compressedData[i++]; // printf("%02x ", n); // if (n & 0x80) i++; // else {i += (n&0x7f) + 1;} // } // printf("\n"); } if ( nextChar & 0x80 ) { // We have a duplication run nextChar = compressedData[FROM++]; for (uInt32 i = 0; i < count; ++i ) data[delta * TO++] = nextChar; } else { // We have a verbatim run for (uInt32 i = 0; i < count; ++i ) data[delta * TO++] = compressedData[FROM++]; } } *compressedIndex = FROM; } static uByte* read_tile(FILE* file, int size, int depth, int datasize, int* offsets) { uByte* result = (uByte*)malloc( size * depth ); if (datasize >= size * depth) { fread(result, 1, size * depth, file); } else { // compressed tile uByte* data = (uByte*)malloc(datasize); datasize = int(fread(data, 1, datasize, file)); uInt32 index = 0; for (int i = 0; i < depth; i++) decompress_rle(result + offsets[i], depth, size, data, datasize, &index); free(data); } return result; } public: iff_image* iff_load( const char* filename ) { iff_chunk chunkInfo; iff_image* image; // -- Header info. uInt32 width, height, depth, npixels; uInt32 flags, compress; uInt16 tiles; uInt16 tbmpfound; uInt16 datatype; uInt16 x1, x2, y1, y2, tile_width, tile_height; uInt32 tile; uInt32 ztile; uInt32 i; uInt32 remainingDataSize; uInt32 oldSpot; uInt32 fileLength; FILE* file; // -- Initialize the top of the chunk stack. chunkDepth = -1; image = nullptr; // -- Open the file. file = fopen( filename, "rb" ); if ( !file ) { if ( iff_error == IFF_NO_ERROR ) { iff_error = IFF_OPEN_FAILS; } return nullptr ; } // -- File should begin with a FOR4 chunk of type CIMG chunkInfo = iff_begin_read_chunk( file ); if ( chunkInfo.chunkType != IFF_TAG_CIMG ) { if ( iff_error == IFF_NO_ERROR ) { // -- This is not a CIMG, it is not an IFF Image. iff_error = IFF_BAD_TAG; } return nullptr ; } /* * Read the image header * OK, we have a FOR4 of type CIMG, look for the following tags * FVER * TBHD bitmap header, definition of size, etc. * AUTH * DATE */ while ( 1 ) { chunkInfo = iff_begin_read_chunk( file ); // -- Right now, the only info we need about the image is in TBHD // -- so search this level until we find it. if ( chunkInfo.tag == IFF_TAG_TBHD ) { // -- Header chunk found width = iff_get_long( file ); height = iff_get_long( file ); iff_get_short( file ); // -- Don't support iff_get_short( file ); // -- Don't support flags = iff_get_long( file ); datatype = iff_get_short( file ); tiles = iff_get_short( file ); compress = iff_get_long( file ); #ifdef __IFF_DEBUG_ printf( "****************************************\n" ); printf( "Width: %u\n", width); printf( "Height: %u\n", height); printf( "flags: 0x%X\n", flags); printf( "datatype: %d\n", datatype); printf( "tiles: %hu\n", tiles); printf( "compress: %u\n", compress); printf( "****************************************\n" ); #endif iff_end_read_chunk( file ); if ( compress > 1 ) { if ( iff_error == IFF_NO_ERROR ) { iff_error = IFF_BAD_COMPRESS; } return nullptr ; } break; } else { #ifdef __IFF_DEBUG_ // Skipping unused data at FOR4 CIMG depth printf("Skipping Chunk: %c%c%c%c\n", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF)); #endif iff_end_read_chunk( file ); } } /* END find TBHD while loop */ // -- Number of channels. depth = 0; if ( flags & RGB_FLAG ) { depth += 3; } if ( flags & ALPHA_FLAG ) { depth += 1; } npixels = width * height; // -- Allocate the image struct. image = (iff_image*)malloc( sizeof( iff_image ) ); image->width = width; image->height = height; image->depth = depth; image->datatype = datatype; image->znear = 0.0; image->zfar = 0.0; image->zesx = 0.0; image->zesy = 0.0; image->rgba = nullptr; image->srgba = nullptr; image->frgba = nullptr; image->zbuffer = nullptr; image->blurvec = nullptr; if (datatype == 3) { image->frgba = (float*)malloc( npixels * depth * 4 ); memset( image->frgba, 0, npixels * depth * 4 ); } else if (datatype == 1) { image->srgba = (uInt16*)malloc( npixels * depth * 2 ); memset( image->srgba, 0, npixels * depth * 2 ); } else { image->rgba = (uByte*)malloc( npixels * depth); memset( image->rgba, 0, npixels * depth); } if ( flags & ZBUFFER_FLAG ) { image->zbuffer = (Float32*)malloc( npixels * sizeof( Float32 ) ); memset( image->zbuffer, 0, npixels * sizeof( Float32 ) ); } // -- Assume the next FOR4 of type TBMP tbmpfound = 0; // Read the tiled image data while ( !tbmpfound ) { chunkInfo = iff_begin_read_chunk( file ); /* * OK, we have a FOR4 of type TBMP, (embedded FOR4) * look for the following tags * RGBA color data, RLE compressed tiles of 32 bbp data * ZBUF z-buffer data, 32 bit float values * CLPZ depth map specific, clipping planes, 2 float values * ESXY depth map specific, eye x-y ratios, 2 float values * HIST * VERS * FOR4 BLUR (twice embedded FOR4) */ if ( chunkInfo.chunkType == IFF_TAG_TBMP ) { tbmpfound = 1; // Image data found tile = 0; ztile = 0; #if 0 //def __IFF_DEBUG_ printf( "Reading image tiles\n" ); #endif if ( !(flags & ZBUFFER_FLAG) ) { ztile = tiles; } if ( depth == 0 ) { tile = tiles; } // -- Read tiles while ( ( tile < tiles ) || ( ztile < tiles ) ) { chunkInfo = iff_begin_read_chunk( file ); if ( !(chunkInfo.tag == IFF_TAG_RGBA) && !(chunkInfo.tag == IFF_TAG_ZBUF) ) { if ( iff_error == IFF_NO_ERROR ) { iff_error = IFF_BAD_CHUNK; } iff_free( image ); return nullptr ; } // Get tile size and location info x1 = iff_get_short( file ); y1 = iff_get_short( file ); x2 = iff_get_short( file ); y2 = iff_get_short( file ); remainingDataSize = chunkInfo.size - 8; tile_width = x2 - x1 + 1; tile_height = y2 - y1 + 1; #if 0 //def __IFF_DEBUG_ printf( "Tile x1: %hu ", x1 ); printf( "y1: %hu ", y1 ); printf( "x2: %hu ", x2 ); printf( "y2: %hu\n", y2 ); #endif // -- OK, we found an RGBA chunk, eat it. if ( chunkInfo.tag == IFF_TAG_RGBA ) { if ( depth == 0 ) { iff_end_read_chunk( file ); continue; } if (datatype == 3) { static int offsets[4][16] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 0, 4, 1, 5, 2, 6, 3, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11, 12, 13, 14, 15 }, { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 } }; float* tileData = (float*)read_tile(file, tile_width * tile_height, 4 * depth, remainingDataSize, offsets[depth - 1]); if ( tileData ) { DD::Image::Reader::frommsb((unsigned*)tileData, tile_width * tile_height * depth); float* from = tileData; for (unsigned i = 0; i < tile_height; i++) { float* to = image->frgba + depth * (width * (y1 + i) + x1); for (unsigned j = 0; j < tile_width; j++) { for (unsigned k = 0; k < depth; k++) to[k] = from[depth - k - 1]; to += depth; from += depth; } } free( tileData ); } } else if (datatype == 1) { static int offsets[4][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 2, 1, 3, 4, 5, 6, 7 }, { 0, 2, 4, 1, 3, 5, 6, 7 }, { 0, 2, 4, 6, 1, 3, 5, 7 } }; uInt16* tileData = (uInt16*)read_tile(file, tile_width * tile_height, 2 * depth, remainingDataSize, offsets[depth - 1]); if ( tileData ) { DD::Image::Reader::frommsb(tileData, tile_width * tile_height * depth); uInt16* from = tileData; for (unsigned i = 0; i < tile_height; i++) { uInt16* to = image->srgba + depth * (width * (y1 + i) + x1); for (unsigned j = 0; j < tile_width; j++) { for (unsigned k = 0; k < depth; k++) to[k] = from[depth - k - 1]; to += depth; from += depth; } } free( tileData ); } } else { static int offsets[] = { 0, 1, 2, 3 }; uByte* tileData = read_tile(file, tile_width * tile_height, depth, remainingDataSize, offsets); if ( tileData ) { uByte* from = tileData; for (unsigned i = 0; i < tile_height; i++) { uByte* to = image->rgba + depth * (width * (y1 + i) + x1); for (unsigned j = 0; j < tile_width; j++) { for (unsigned k = 0; k < depth; k++) to[k] = from[depth - k - 1]; to += depth; from += depth; } } free( tileData ); } } iff_end_read_chunk( file ); tile++; } /* END RGBA chunk */ // -- OK, we found a ZBUF chunk, eat it....hmmm, tasty else if ( chunkInfo.tag == IFF_TAG_ZBUF ) { static int offsets[] = { 0, 1, 2, 3 }; float* tileData = (float*)read_tile(file, tile_width * tile_height, 4, remainingDataSize, offsets); if ( tileData ) { DD::Image::Reader::frommsb((unsigned*)tileData, tile_width * tile_height); const int base = y1 * width + x1; for (int i = 0; i < tile_height; i++) { for (int j = 0; j < tile_width; j++) { image->zbuffer[base + i * width + j] = tileData[i * tile_width + j]; } /* End DEPTH dump */ } free( tileData ); } iff_end_read_chunk( file ); ztile++; } /* END ZBUF chunk */ } /* END while TBMP tiles */ } /* END if TBMP */ else { #ifdef __IFF_DEBUG_ // Skipping unused data IN THE BEGINNING OF THE FILE printf( "Skipping Chunk in search of TBMP: %c%c%c%c\n", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF)); #endif iff_end_read_chunk( file ); } } oldSpot = ftell( file ) ; fileLength = 0 ; fseek( file, 0, SEEK_END ) ; fileLength = ftell( file ) ; fseek( file, oldSpot, SEEK_SET ); while ( 1 ) { if ( (width * height + ftell( file )) > fileLength ) { #ifdef __IFF_DEBUG_ printf ( "End of parsable data, time to quit\n" ); #endif break ; } chunkInfo = iff_begin_read_chunk( file ); if ( chunkInfo.tag == IFF_TAG_CLPZ ) { image->znear = iff_get_float( file ); image->zfar = iff_get_float( file ); #ifdef __IFF_DEBUG_ printf( "Got clipping info: %f %f\n", image->znear, image->zfar ); #endif iff_end_read_chunk( file ); } /* END CLPZ chunk */ else if ( chunkInfo.tag == IFF_TAG_ESXY ) { image->zesx = iff_get_float( file ); image->zesy = iff_get_float( file ); #ifdef __IFF_DEBUG_ printf( "Got esxy info: %f %f\n", image->zesx, image->zesy ); #endif iff_end_read_chunk( file ); } /* END ESXY chunk */ else if ( chunkInfo.tag == IFF_TAG_FOR4 ) { if ( chunkInfo.chunkType == IFF_TAG_BLUR ) { // -- FIXME: GET THE BLUR INFO HERE if ( image->blurvec ) { free( image->blurvec ); } while ( 1 ) { chunkInfo = iff_begin_read_chunk( file ); if ( chunkInfo.tag == IFF_TAG_BLRT ) { // read in values, uncompressed and in a linear sort // of manner, uh huh... /* printf( "%d\n", iff_get_long( file ) ); printf( "%d\n", iff_get_long( file ) ); printf( "%d\n", iff_get_long( file ) ); printf( "%d\n", iff_get_long( file ) ); */ iff_get_long( file ); // -- Don't know what these are iff_get_long( file ); iff_get_long( file ); iff_get_long( file ); image->blurvec = (Float32*)malloc( npixels * 2 * sizeof( Float32 ) ); for ( i = 0; i < npixels; i++ ) { image->blurvec[2 * i] = iff_get_float( file ); image->blurvec[2 * i + 1] = iff_get_float( file ); } iff_end_read_chunk( file ); break; } else { #ifdef __IFF_DEBUG_ printf( "Skipping Chunk in search of BLRT: %c%c%c%c\n", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF) ); #endif iff_end_read_chunk( file ); } } #ifdef __IFF_DEBUG_ printf("Found FOR4 BLUR\n"); #endif } iff_end_read_chunk( file ); } else { #ifdef __IFF_DEBUG_ // Skipping unused data IN THE BEGINNING OF THE FILE printf("Skipping Chunk in search of CLPZ: %c%c%c%c\n", (((&chunkStack[chunkDepth].tag)[0] >> 24) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 16) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 8) & 0xFF), (((&chunkStack[chunkDepth].tag)[0] >> 0) & 0xFF)); #endif iff_end_read_chunk( file ); } } fclose( file ); return image ; } static void iff_free( iff_image* image ) { if ( image ) { if ( image->rgba ) { free( image->rgba ); } if ( image->frgba ) { free( image->frgba ); } if ( image->srgba ) { free( image->srgba ); } if ( image->zbuffer ) { free( image->zbuffer ); } if ( image->blurvec ) { free( image->blurvec ); } free( image ); image = nullptr; } } unsigned iff_get_error() { unsigned err = iff_error; iff_error = IFF_NO_ERROR; return err ; } static const char* iff_error_string( unsigned errorNumber ) { switch ( errorNumber ) { case ( IFF_NO_ERROR ): return "no error" ; case ( IFF_OPEN_FAILS ): return "cannot open file" ; case ( IFF_READ_FAILS ): return "cannot read file" ; case ( IFF_BAD_TAG ): return "unexpected tag" ; case ( IFF_BAD_COMPRESS ): return "unknown compression format" ; case ( IFF_BAD_STACK ): return "tag stack corrupt" ; case ( IFF_BAD_CHUNK ): return "unexpected chunk" ; } return "" ; } IFFApi() : chunkDepth(-1) , iff_error(IFF_NO_ERROR) {} }; //////////////////////////////////////////////////////////////// #include "DDImage/Row.h" #include #ifndef _WIN32 #include #else #include #endif #include using namespace DD::Image; class iffReader : public Reader { iff_image* image; IFFApi _api; /// Encapsulation of IFF reading code for use by this reader, making it safe for multiple readers to operate concurrently. public: iffReader(Read*, int fd); ~iffReader() override; void engine(int y, int x, int r, ChannelMask, Row &) override; void open() override; static const Description d; MetaData::Bundle _meta; const MetaData::Bundle& fetchMetaData(const char* key) override { return _meta; } }; /** Check for the correct magic numbers at the start of the file */ static bool test(int fd, const unsigned char* block, int length) { if (block[0] != 'F' || block[1] != 'O' // letter oh || block[2] != 'R' || block[3] != '4' ) return false; // check if this is a picture file if (block[8] != 'C' || block[9] != 'I' || block[10] != 'M' || block[11] != 'G' ) { printf("Block is type %c%c%c%c\n", block[8], block[9], block[10], block[11]); return false; } // great, we passed all tests. return true; } static Reader* build(Read* iop, int fd, const unsigned char* b, int n) { return new iffReader(iop, fd); } const Reader::Description iffReader::d("iff\0iff16\0", build, test); iffReader::iffReader(Read* r, int fd) : Reader(r) { close(fd); image = _api.iff_load(filename()); int err = _api.iff_get_error(); // printf("err %s, width %d, height %d, depth %d\n", // iff_error_string(err), image->width, image->height, image->depth); if (err != IFF_NO_ERROR) { iop->error(IFFApi::iff_error_string(err)); return; } switch (image->datatype) { case 1: _meta.setData(MetaData::DEPTH, MetaData::DEPTH_16); break; case 3: _meta.setData(MetaData::DEPTH, MetaData::DEPTH_FLOAT); break; default: _meta.setData(MetaData::DEPTH, MetaData::DEPTH_8); } set_info(image->width, image->height, image->depth); if (image->zbuffer) info_.turn_on(Mask_Z); if (image->blurvec) info_.turn_on(Mask_UV); lut_ = LUT::GetLut( image->datatype == 3 ? LUT::FLOAT : image->datatype == 1 ? LUT::INT16 : LUT::INT8, this ); } // delay anything unneeded for info_ until this is called: void iffReader::open() { Reader::open(); } iffReader::~iffReader() { IFFApi::iff_free(image); } void iffReader::engine(int y, int x, int r, ChannelMask channels, Row& row) { const int depth = image->depth; int offset = y * image->width + x; if (image->datatype == 3) { const float* pixel = image->frgba + offset * depth; const float* alpha = depth > 3 ? pixel + 3 : nullptr; if (channels & Mask_Red) from_float(Chan_Red, row.writable(Chan_Red) + x, pixel + 0, alpha, r - x, depth); if (channels & Mask_Green) from_float(Chan_Green, row.writable(Chan_Green) + x, pixel + 1, alpha, r - x, depth); if (channels & Mask_Blue) from_float(Chan_Blue, row.writable(Chan_Blue) + x, pixel + 2, alpha, r - x, depth); if (channels & Mask_Alpha) from_float(Chan_Alpha, row.writable(Chan_Alpha) + x, pixel + 3, alpha, r - x, depth); } else if (image->datatype == 1) { const U16* pixel = image->srgba + offset * depth; const U16* alpha = depth > 3 ? pixel + 3 : nullptr; if (channels & Mask_Red) from_short(Chan_Red, row.writable(Chan_Red) + x, pixel + 0, alpha, r - x, 16, depth); if (channels & Mask_Green) from_short(Chan_Green, row.writable(Chan_Green) + x, pixel + 1, alpha, r - x, 16, depth); if (channels & Mask_Blue) from_short(Chan_Blue, row.writable(Chan_Blue) + x, pixel + 2, alpha, r - x, 16, depth); if (channels & Mask_Alpha) from_short(Chan_Alpha, row.writable(Chan_Alpha) + x, pixel + 3, alpha, r - x, 16, depth); } else { const uchar* pixel = image->rgba + offset * depth; const uchar* alpha = depth > 3 ? pixel + 3 : nullptr; if (channels & Mask_Red) from_byte(Chan_Red, row.writable(Chan_Red) + x, pixel + 0, alpha, r - x, depth); if (channels & Mask_Green) from_byte(Chan_Green, row.writable(Chan_Green) + x, pixel + 1, alpha, r - x, depth); if (channels & Mask_Blue) from_byte(Chan_Blue, row.writable(Chan_Blue) + x, pixel + 2, alpha, r - x, depth); if (channels & Mask_Alpha) from_byte(Chan_Alpha, row.writable(Chan_Alpha) + x, pixel + 3, alpha, r - x, depth); } if (channels & Mask_Z) from_float(Chan_Z, row.writable(Chan_Z) + x, image->zbuffer + offset, nullptr, r - x, 1); if (channels & Mask_U) from_float(Chan_Z, row.writable(Chan_U) + x, image->blurvec + 2 * offset, nullptr, r - x, 2); if (channels & Mask_V) from_float(Chan_Z, row.writable(Chan_V) + x, image->blurvec + 2 * offset + 1, nullptr, r - x, 2); }