/* ====================================================================== read.c Functions for reading an FPBM image file. Ernie Wright 4 Dec 00 ====================================================================== */ #include #include #include #include "flex.h" /* ====================================================================== flexFree() Free memory used by a Flex structure. ====================================================================== */ void flexFree( Flex *flex ) { int i; if ( flex ) { if ( flex->frame ) { for ( i = 0; i < flex->hdr.numFrames; i++ ) if ( flex->frame[ i ].layer ) free( flex->frame[ i ].layer ); free( flex->frame ); } free( flex ); } } /* ====================================================================== flexReadBegin() Opens and reads an FPBM image file. Returns a pointer to a Flex structure, or NULL if the file couldn't be loaded. On failure, failID and failpos can be used to diagnose the cause. 1. If the file isn't an FPBM, failpos will contain 12 and failID will be unchanged. 2. If an error occurs while reading, failID will contain the most recently read IFF chunk ID, and failpos will contain the value returned by ftell() at the time of the failure. 3. If the file couldn't be opened, or an error occurs while reading the first 12 bytes, both failID and failpos will be unchanged. If you don't need this information, failID and failpos can be NULL. ====================================================================== */ Flex *flexReadBegin( char *filename, unsigned int *failID, int *failpos ) { FILE *fp = NULL; Flex *flex; Frame *frame = NULL; Layer *layer = NULL; unsigned int id, formsize, type, cksize; int rlen, nframes = 0, nlayers = 0; /* open the file */ fp = fopen( filename, "rb" ); if ( !fp ) return NULL; /* read the first 12 bytes */ set_flen( 0 ); id = getU4( fp ); formsize = getU4( fp ); type = getU4( fp ); if ( 12 != get_flen() ) { fclose( fp ); return NULL; } /* is this a flex image? */ if ( id != ID_FORM ) { fclose( fp ); if ( failpos ) *failpos = 12; return NULL; } if ( type != ID_FPBM ) { fclose( fp ); if ( failpos ) *failpos = 12; return NULL; } /* allocate a Flex */ flex = calloc( 1, sizeof( Flex )); if ( !flex ) goto Fail; /* get the first chunk header */ id = getU4( fp ); cksize = getU4( fp ); if ( 0 > get_flen() ) goto Fail; /* process chunks as they're encountered */ while ( 1 ) { set_flen( 0 ); switch ( id ) { case ID_FPHD: flex->hdr.width = getI2( fp ); flex->hdr.height = getI2( fp ); flex->hdr.numLayers = getI2( fp ); flex->hdr.numFrames = getI2( fp ); flex->hdr.numBuffers = getI2( fp ); flex->hdr.flags = getI2( fp ); flex->hdr.srcLayerDepth = getI2( fp ); skipbytes( fp, 2 ); flex->hdr.pixelAspect = getF4( fp ); flex->hdr.pixelWidth = getF4( fp ); flex->hdr.framesPerSecond = getF4( fp ); frame = calloc( flex->hdr.numFrames, sizeof( Frame )); if ( !frame ) goto Fail; flex->frame = frame; break; case ID_FLEX: if ( !frame ) goto Fail; if ( nframes >= flex->hdr.numFrames ) goto Fail; nlayers = getI2( fp ); layer = calloc( nlayers, sizeof( Layer )); if ( !layer ) goto Fail; frame[ nframes ].layer = layer; frame[ nframes ].hdr.numLayers = nlayers; nlayers = 0; ++nframes; break; case ID_LYHD: if ( !layer ) goto Fail; if ( nlayers >= frame[ nframes - 1 ].hdr.numLayers ) goto Fail; layer[ nlayers ].w = flex->hdr.width; layer[ nlayers ].h = flex->hdr.height; layer[ nlayers ].hdr.flags = getI2( fp ); layer[ nlayers ].hdr.layerType = getI2( fp ); layer[ nlayers ].hdr.layerDepth = getI2( fp ); layer[ nlayers ].hdr.compression = getI2( fp ); layer[ nlayers ].hdr.blackPoint = getF4( fp ); layer[ nlayers ].hdr.whitePoint = getF4( fp ); layer[ nlayers ].hdr.gamma = getF4( fp ); break; case ID_LAYR: if ( !layer ) goto Fail; layer[ nlayers ].offset = ftell( fp ); layer[ nlayers ].size = cksize; ++nlayers; break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > cksize ) goto Fail; /* skip unread parts of the current subchunk */ cksize += cksize & 1; if ( rlen < cksize ) fseek( fp, cksize - rlen, SEEK_CUR ); /* end of the file? */ if ( formsize <= ftell( fp ) - 8 ) break; /* get the next chunk header */ set_flen( 0 ); id = getU4( fp ); cksize = getU4( fp ); if ( 8 != get_flen() ) goto Fail; } flex->fp = fp; return flex; Fail: if ( failID ) *failID = id; if ( fp ) { if ( failpos ) *failpos = ftell( fp ); fclose( fp ); } flexFree( flex ); return NULL; } /* ====================================================================== flexFindLayer() Return the layer that matches the frame number and layer type. ====================================================================== */ Layer *flexFindLayer( Flex *flex, int fnum, int layerType ) { Layer *layer = NULL; int i; if ( fnum < 0 || fnum > flex->hdr.numFrames - 1 ) return NULL; for ( i = 0; i < flex->frame[ fnum ].hdr.numLayers; i++ ) if ( flex->frame[ fnum ].layer[ i ].hdr.layerType == layerType ) { layer = &flex->frame[ fnum ].layer[ i ]; break; } return layer; } /* ====================================================================== flexLayerSize() Return the size in bytes of the uncompressed layer data. ====================================================================== */ int flexLayerSize( Flex *flex, int fnum, int layerType ) { Layer *layer; layer = flexFindLayer( flex, fnum, layerType ); if ( layer ) return layer->hdr.layerDepth * layer->w * layer->h; else return 0; } /* ====================================================================== unpackRLE() Decompress run-length encoded bytes. ====================================================================== */ static int unpackRLE( char **psrc, char *dst, int size, int step ) { int c, n; char *src = *psrc; while ( size > 0 ) { n = *src++; if ( n >= 0 ) { ++n; size -= n; if ( size < 0 ) return 0; while ( n-- ) { *dst = *src++; dst += step; } } else { n = -n + 1; size -= n; if ( size < 0 ) return 0; c = *src++; while ( n-- ) { *dst = c; dst += step; } } } *psrc = src; return 1; } /* ====================================================================== unpackDelta() Decompress delta encoded bytes. ====================================================================== */ static int unpackDelta( char *src, char *dst, int size, int vstep, int hstep ) { int n, nn; while ( size > 0 ) { n = *src++; --size; if ( n < 0 ) dst += -n * vstep; else { for ( ; n >= 0; n-- ) { nn = *src++; --size; if ( nn < 0 ) nn = -nn; else { ++nn; if ( !unpackRLE( &src, dst, nn, hstep )) return 0; } dst += nn * hstep; } } } return 1; } /* ====================================================================== flexReadLayer() Fill in the dst buffer with pixels from the layer matching the frame number and layer type. The destination buffer must be at least as large as the number of bytes returned by flexLayerSize(). ====================================================================== */ Layer *flexReadLayer( Flex *flex, int fnum, int layerType, char *dst ) { Layer *layer; char *buf, *src, *dst0; int i, rowbytes, ok; layer = flexFindLayer( flex, fnum, layerType ); if ( !layer ) return NULL; if ( !flex->fp ) return NULL; fseek( flex->fp, layer->offset, SEEK_SET ); if ( layer->hdr.compression == NoCompression ) { ok = fread( dst, layer->size, 1, flex->fp ); if ( ok && ( layer->hdr.layerDepth > 1 )) revbytes( dst, layer->hdr.layerDepth, layer->w * layer->h ); return layer; } buf = malloc( layer->size ); if ( !buf ) return NULL; if ( 1 != fread( buf, layer->size, 1, flex->fp )) { free( buf ); return NULL; } src = buf; dst0 = dst; rowbytes = layer->hdr.layerDepth * layer->w; switch ( layer->hdr.compression ) { case HorizontalRLE: for ( i = 0; i < layer->h; i++ ) { ok = unpackRLE( &src, dst, rowbytes, 1 ); if ( !ok ) break; dst += rowbytes; } break; case VerticalRLE: for ( i = 0; i < rowbytes; i++ ) { ok = unpackRLE( &src, dst, layer->h, rowbytes ); if ( !ok ) break; dst++; } break; case HorizontalDelta: revbytes( dst, layer->hdr.layerDepth, layer->w * layer->h ); ok = unpackDelta( src, dst, rowbytes * layer->h, 1, rowbytes ); break; case VerticalDelta: revbytes( dst, layer->hdr.layerDepth, layer->w * layer->h ); ok = unpackDelta( src, dst, rowbytes * layer->h, rowbytes, 1 ); break; default: ok = 0; break; } free( buf ); if ( ok ) { revbytes( dst0, layer->hdr.layerDepth, layer->w * layer->h ); return layer; } return NULL; } /* ====================================================================== flexReadDone() Free resources obtained by flexReadBegin(). ====================================================================== */ void flexReadDone( Flex *flex ) { if ( flex->fp ) fclose( flex->fp ); flexFree( flex ); } /* ====================================================================== flexLayerToByte() Convert layer data to byte levels. This is useful when you need to display the layer. This converts only a few common layer data types. INPUTS layer the layer to convert src the layer values dst buffer to receive converted layer data drowbytes number of bytes in one scanline of dst dstep bytes between successive layer values in dst For example, a 24-bit Windows DIB stores pixels as B G R B G R B G R ... The raster is longword-aligned, so drowbytes would be 3 * w, rounded up to the next multiple of 4. dstep would be 3, since each B (blue) byte, for example, is offset from the previous one by 3 bytes. The dst pointer would be offset by 1 for the G (green) layer and by 2 for the red layer. A final complication is that DIBs are "upside-down," storing the bottom scanline first. So set dst to the start of the last (top) scanline (+1 for G, +2 for R), and make drowbytes negative. When you don't have to worry about scanline alignment, upside-down scanlines, or interleaved color channels, the conversion is quite straightforward. Set drowbytes = layer->w, and dstep = 1. When the layer is float-valued, the black and white points are used to define the range, and the value is gamma corrected if the layer gamma isn't 1.0. ====================================================================== */ void flexLayerToByte( Layer *layer, void *src, unsigned char *dst, int drowbytes, int dstep ) { int x, y, c; float r, *pf, f, g; unsigned short *pw; unsigned char *pb, *qb; switch ( layer->hdr.layerDepth ) { case 1: pb = ( unsigned char * ) src; for ( y = 0; y < layer->h; y++ ) { qb = dst + y * drowbytes; for ( x = 0; x < layer->w; x++, pb++, qb += dstep ) *qb = *pb; } break; case 2: pw = ( unsigned short * ) src; for ( y = 0; y < layer->h; y++ ) { qb = dst + y * drowbytes; for ( x = 0; x < layer->w; x++, pw++, qb += dstep ) *qb = *pw >> 8; } break; case 4: if ( layer->hdr.flags & Layer_FP ) { pf = ( float * ) src; r = layer->hdr.whitePoint - layer->hdr.blackPoint; if ( layer->hdr.gamma > 0.0f && layer->hdr.gamma != 1.0f ) g = 1.0f / layer->hdr.gamma; else g = 0.0f; for ( y = 0; y < layer->h; y++ ) { qb = dst + y * drowbytes; for ( x = 0; x < layer->w; x++, pf++, qb += dstep ) { f = ( *pf - layer->hdr.blackPoint ) / r; if ( g != 0.0f ) f = ( float ) pow( f, g ); c = ( int )( 255.0f * f ); if ( c > 255 ) c = 255; if ( c < 0 ) c = 0; *qb = c; } } } break; } } /* ====================================================================== flexLayerToWord() Like flexLayerToByte, but converts to word levels. The destination is an array of words, and the destination row width and step are word counts. Adobe Photoshop and Apple QuickDraw support 16 bits per image channel, for example. ====================================================================== */ void flexLayerToWord( Layer *layer, void *src, unsigned short *dst, int drowwords, int dstep ) { int x, y, c; float r, *pf, f, g; unsigned short *pw, *qw; unsigned char *pb; switch ( layer->hdr.layerDepth ) { case 1: pb = ( unsigned char * ) src; for ( y = 0; y < layer->h; y++ ) { qw = dst + y * drowwords; for ( x = 0; x < layer->w; x++, pb++, qw += dstep ) *qw = *pb * 257; } break; case 2: pw = ( unsigned short * ) src; for ( y = 0; y < layer->h; y++ ) { qw = dst + y * drowwords; for ( x = 0; x < layer->w; x++, pw++, qw += dstep ) *qw = *pw; } break; case 4: if ( layer->hdr.flags & Layer_FP ) { pf = ( float * ) src; r = layer->hdr.whitePoint - layer->hdr.blackPoint; if ( layer->hdr.gamma > 0.0f && layer->hdr.gamma != 1.0f ) g = 1.0f / layer->hdr.gamma; else g = 0.0f; for ( y = 0; y < layer->h; y++ ) { qw = dst + y * drowwords; for ( x = 0; x < layer->w; x++, pf++, qw += dstep ) { f = ( *pf - layer->hdr.blackPoint ) / r; if ( g != 0.0f ) f = ( float ) pow( f, g ); c = ( int )( 65535.0f * f ); if ( c > 65535 ) c = 65535; if ( c < 0 ) c = 0; *qw = c; } } } break; } }