libtiff/contrib/addtiffo/tif_overview.c

648 lines
25 KiB
C
Raw Normal View History

2000-01-28 10:03:32 -05:00
/******************************************************************************
2000-04-18 18:48:31 -04:00
* $Id: tif_overview.c,v 1.3 2000-04-18 22:48:31 warmerda Exp $
2000-01-28 10:03:32 -05:00
*
* Project: TIFF Overview Builder
* Purpose: Library function for building overviews in a TIFF file.
* Author: Frank Warmerdam, warmerda@home.com
*
* Notes:
* o Currently only images with bits_per_sample of a multiple of eight
* will work.
*
* o The downsampler currently just takes the top left pixel from the
* source rectangle. Eventually sampling options of averaging, mode, and
* ``center pixel'' should be offered.
*
* o The code will attempt to use the same kind of compression,
* photometric interpretation, and organization as the source image, but
* it doesn't copy geotiff tags to the reduced resolution images.
*
* o Reduced resolution overviews for multi-sample files will currently
* always be generated as PLANARCONFIG_SEPARATE. This could be fixed
* reasonable easily if needed to improve compatibility with other
* packages. Many don't properly support PLANARCONFIG_SEPARATE.
*
******************************************************************************
* Copyright (c) 1999, Frank Warmerdam
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
******************************************************************************
*
* $Log: tif_overview.c,v $
2000-04-18 18:48:31 -04:00
* Revision 1.3 2000-04-18 22:48:31 warmerda
* Added support for averaging resampling
*
* Revision 1.2 2000/01/28 15:36:38 warmerda
* pass TIFF handle instead of filename to overview builder
*
* Revision 1.1 2000/01/28 15:04:03 warmerda
2000-01-28 10:03:32 -05:00
* New
*
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "tiffio.h"
#include "tif_ovrcache.h"
#ifndef FALSE
# define FALSE 0
# define TRUE 1
#endif
#ifndef MAX
# define MIN(a,b) ((a<b) ? a : b)
# define MAX(a,b) ((a>b) ? a : b)
#endif
2000-04-18 18:48:31 -04:00
void TIFFBuildOverviews( TIFF *, int, int *, int, const char *,
int (*)(double,void*), void * );
2000-01-28 10:03:32 -05:00
/************************************************************************/
/* TIFF_WriteOverview() */
/* */
/* Create a new directory, without any image data for an overview. */
/* Returns offset of newly created overview directory, but the */
/* current directory is reset to be the one in used when this */
/* function is called. */
/********************************* ***************************************/
static
uint32 TIFF_WriteOverview( TIFF *hTIFF, int nXSize, int nYSize,
int nBitsPerPixel, int nSamples,
int nBlockXSize, int nBlockYSize,
int bTiled, int nCompressFlag, int nPhotometric,
int nSampleFormat,
unsigned short *panRed,
unsigned short *panGreen,
unsigned short *panBlue,
int bUseSubIFDs )
{
uint32 nBaseDirOffset;
uint32 nOffset;
nBaseDirOffset = TIFFCurrentDirOffset( hTIFF );
TIFFCreateDirectory( hTIFF );
/* -------------------------------------------------------------------- */
/* Setup TIFF fields. */
/* -------------------------------------------------------------------- */
TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize );
TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize );
if( nSamples == 1 )
TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
else
TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_SEPARATE );
TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel );
TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples );
TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompressFlag );
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric );
TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat );
if( bTiled )
{
TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize );
TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize );
}
else
TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize );
TIFFSetField( hTIFF, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
/* -------------------------------------------------------------------- */
/* Write color table if one is present. */
/* -------------------------------------------------------------------- */
if( panRed != NULL )
{
TIFFSetField( hTIFF, TIFFTAG_COLORMAP, panRed, panGreen, panBlue );
}
/* -------------------------------------------------------------------- */
/* Write directory, and return byte offset. */
/* -------------------------------------------------------------------- */
TIFFWriteCheck( hTIFF, bTiled, "TIFFBuildOverviews" );
TIFFWriteDirectory( hTIFF );
TIFFSetDirectory( hTIFF, TIFFNumberOfDirectories(hTIFF)-1 );
nOffset = TIFFCurrentDirOffset( hTIFF );
TIFFSetSubDirectory( hTIFF, nBaseDirOffset );
return nOffset;
}
2000-04-18 18:48:31 -04:00
/************************************************************************/
/* TIFF_GetSourceSamples() */
/************************************************************************/
static void
TIFF_GetSourceSamples( double * padfSamples, unsigned char *pabySrc,
int nPixelBytes, int nSampleFormat,
int nXSize, int nYSize,
int nPixelOffset, int nLineOffset )
{
int iXOff, iYOff, iSample;
iSample = 0;
for( iYOff = 0; iYOff < nYSize; iYOff++ )
{
for( iXOff = 0; iXOff < nXSize; iXOff++ )
{
unsigned char *pabyData;
pabyData = pabySrc + iYOff * nLineOffset + iXOff * nPixelOffset;
if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 1 )
{
padfSamples[iSample++] = *pabyData;
}
else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 2 )
{
padfSamples[iSample++] = ((uint16 *) pabyData)[0];
}
else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 4 )
{
padfSamples[iSample++] = ((uint32 *) pabyData)[0];
}
else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 2 )
{
padfSamples[iSample++] = ((int16 *) pabyData)[0];
}
else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 32 )
{
padfSamples[iSample++] = ((int32 *) pabyData)[0];
}
else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 4 )
{
padfSamples[iSample++] = ((float *) pabyData)[0];
}
else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 8 )
{
padfSamples[iSample++] = ((double *) pabyData)[0];
}
}
}
}
/************************************************************************/
/* TIFF_SetSample() */
/************************************************************************/
static void
TIFF_SetSample( unsigned char * pabyData, int nPixelBytes, int nSampleFormat,
double dfValue )
{
if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 1 )
{
*pabyData = (unsigned char) MAX(0,MIN(255,dfValue));
}
else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 2 )
{
*((uint16 *)pabyData) = (uint16) MAX(0,MIN(65535,dfValue));
}
else if( nSampleFormat == SAMPLEFORMAT_UINT && nPixelBytes == 4 )
{
*((uint32 *)pabyData) = (uint32) dfValue;
}
else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 2 )
{
*((int16 *)pabyData) = (int16) MAX(-32768,MIN(32767,dfValue));
}
else if( nSampleFormat == SAMPLEFORMAT_INT && nPixelBytes == 32 )
{
*((int32 *)pabyData) = (int32) dfValue;
}
else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 4 )
{
*((float *)pabyData) = (float) dfValue;
}
else if( nSampleFormat == SAMPLEFORMAT_IEEEFP && nPixelBytes == 8 )
{
*((double *)pabyData) = dfValue;
}
}
2000-01-28 10:03:32 -05:00
/************************************************************************/
/* TIFF_DownSample() */
/* */
/* Down sample a tile of full res data into a window of a tile */
/* of downsampled data. */
/************************************************************************/
static
void TIFF_DownSample( unsigned char *pabySrcTile,
int nBlockXSize, int nBlockYSize,
int nPixelSkewBits, int nBitsPerPixel,
unsigned char * pabyOTile,
int nOBlockXSize, int nOBlockYSize,
2000-04-18 18:48:31 -04:00
int nTXOff, int nTYOff, int nOMult,
int nSampleFormat, const char * pszResampling )
2000-01-28 10:03:32 -05:00
{
int i, j, k, nPixelBytes = (nBitsPerPixel) / 8;
int nPixelGroupBytes = (nBitsPerPixel+nPixelSkewBits)/8;
unsigned char *pabySrc, *pabyDst;
2000-04-18 18:48:31 -04:00
double *padfSamples;
2000-01-28 10:03:32 -05:00
assert( nBitsPerPixel >= 8 );
2000-04-18 18:48:31 -04:00
padfSamples = (double *) malloc(sizeof(double) * nOMult * nOMult);
/* ==================================================================== */
/* Loop over scanline chunks to process, establishing where the */
/* data is going. */
/* ==================================================================== */
2000-01-28 10:03:32 -05:00
for( j = 0; j*nOMult < nBlockYSize; j++ )
{
if( j + nTYOff >= nOBlockYSize )
break;
pabyDst = pabyOTile
+ ((j+nTYOff)*nOBlockXSize + nTXOff) * nPixelBytes;
2000-04-18 18:48:31 -04:00
/* -------------------------------------------------------------------- */
/* Handler nearest resampling ... we don't even care about the */
/* data type, we just do a bytewise copy. */
/* -------------------------------------------------------------------- */
if( strncmp(pszResampling,"nearest",4) == 0
|| strncmp(pszResampling,"NEAR",4) == 0 )
2000-01-28 10:03:32 -05:00
{
2000-04-18 18:48:31 -04:00
pabySrc = pabySrcTile + j*nOMult*nBlockXSize * nPixelGroupBytes;
for( i = 0; i*nOMult < nBlockXSize; i++ )
{
if( i + nTXOff >= nOBlockXSize )
break;
2000-01-28 10:03:32 -05:00
2000-04-18 18:48:31 -04:00
/*
* For now use simple subsampling, from the top left corner
* of the source block of pixels.
*/
for( k = 0; k < nPixelBytes; k++ )
{
*(pabyDst++) = pabySrc[k];
}
pabySrc += nOMult * nPixelGroupBytes;
}
}
2000-01-28 10:03:32 -05:00
2000-04-18 18:48:31 -04:00
/* -------------------------------------------------------------------- */
/* Handle the case of averaging. For this we also have to */
/* handle each sample format we are concerned with. */
/* -------------------------------------------------------------------- */
else if( strncmp(pszResampling,"averag",6) == 0
|| strncmp(pszResampling,"AVERAG",6) == 0 )
{
pabySrc = pabySrcTile + j*nOMult*nBlockXSize * nPixelGroupBytes;
for( i = 0; i*nOMult < nBlockXSize; i++ )
2000-01-28 10:03:32 -05:00
{
2000-04-18 18:48:31 -04:00
double dfTotal;
int iSample;
int nXSize, nYSize;
if( i + nTXOff >= nOBlockXSize )
break;
nXSize = MIN(nOMult,nBlockXSize-i);
nYSize = MIN(nOMult,nBlockYSize-j);
TIFF_GetSourceSamples( padfSamples, pabySrc,
nPixelBytes, nSampleFormat,
nXSize, nYSize,
nPixelGroupBytes,
nPixelGroupBytes * nBlockXSize );
dfTotal = 0;
for( iSample = 0; iSample < nXSize*nYSize; iSample++ )
{
dfTotal += padfSamples[iSample];
}
TIFF_SetSample( pabyDst, nPixelBytes, nSampleFormat,
dfTotal / (nXSize*nYSize) );
pabySrc += nOMult * nPixelGroupBytes;
pabyDst += nPixelBytes;
2000-01-28 10:03:32 -05:00
}
}
}
2000-04-18 18:48:31 -04:00
free( padfSamples );
2000-01-28 10:03:32 -05:00
}
/************************************************************************/
/* TIFF_ProcessFullResBlock() */
/* */
/* Process one block of full res data, downsampling into each */
/* of the overviews. */
/************************************************************************/
void TIFF_ProcessFullResBlock( TIFF *hTIFF, int nPlanarConfig,
int nOverviews, int * panOvList,
int nBitsPerPixel,
int nSamples, TIFFOvrCache ** papoRawBIs,
int nSXOff, int nSYOff,
unsigned char *pabySrcTile,
2000-04-18 18:48:31 -04:00
int nBlockXSize, int nBlockYSize,
int nSampleFormat, const char * pszResampling )
2000-01-28 10:03:32 -05:00
{
int iOverview, iSample;
for( iSample = 0; iSample < nSamples; iSample++ )
{
/*
* We have to read a tile/strip for each sample for
* PLANARCONFIG_SEPARATE. Otherwise, we just read all the samples
* at once when handling the first sample.
*/
if( nPlanarConfig == PLANARCONFIG_SEPARATE || iSample == 0 )
{
if( TIFFIsTiled(hTIFF) )
{
TIFFReadEncodedTile( hTIFF,
TIFFComputeTile(hTIFF, nSXOff, nSYOff,
0, iSample ),
pabySrcTile,
TIFFTileSize(hTIFF));
}
else
{
TIFFReadEncodedStrip( hTIFF,
TIFFComputeStrip(hTIFF, nSYOff, iSample),
pabySrcTile,
TIFFStripSize(hTIFF) );
}
}
/*
* Loop over destination overview layers
*/
for( iOverview = 0; iOverview < nOverviews; iOverview++ )
{
TIFFOvrCache *poRBI = papoRawBIs[iOverview];
unsigned char *pabyOTile;
int nTXOff, nTYOff, nOXOff, nOYOff, nOMult;
int nOBlockXSize = poRBI->nBlockXSize;
int nOBlockYSize = poRBI->nBlockYSize;
int nSkewBits, nSampleByteOffset;
/*
* Fetch the destination overview tile
*/
nOMult = panOvList[iOverview];
nOXOff = (nSXOff/nOMult) / nOBlockXSize;
nOYOff = (nSYOff/nOMult) / nOBlockYSize;
pabyOTile = TIFFGetOvrBlock( poRBI, nOXOff, nOYOff, iSample );
/*
* Establish the offset into this tile at which we should
* start placing data.
*/
nTXOff = (nSXOff - nOXOff*nOMult*nOBlockXSize) / nOMult;
nTYOff = (nSYOff - nOYOff*nOMult*nOBlockYSize) / nOMult;
/*
* Figure out the skew (extra space between ``our samples'') and
* the byte offset to the first sample.
*/
assert( (nBitsPerPixel % 8) == 0 );
if( nPlanarConfig == PLANARCONFIG_SEPARATE )
{
nSkewBits = 0;
nSampleByteOffset = 0;
}
else
{
nSkewBits = nBitsPerPixel * (nSamples-1);
nSampleByteOffset = (nBitsPerPixel/8) * iSample;
}
/*
* Perform the downsampling.
*/
#ifdef DBMALLOC
malloc_chain_check( 1 );
#endif
TIFF_DownSample( pabySrcTile + nSampleByteOffset,
nBlockXSize, nBlockYSize,
nSkewBits, nBitsPerPixel, pabyOTile,
poRBI->nBlockXSize, poRBI->nBlockYSize,
nTXOff, nTYOff,
2000-04-18 18:48:31 -04:00
nOMult, nSampleFormat, pszResampling );
2000-01-28 10:03:32 -05:00
#ifdef DBMALLOC
malloc_chain_check( 1 );
#endif
}
}
}
/************************************************************************/
/* TIFF_BuildOverviews() */
/* */
/* Build the requested list of overviews. Overviews are */
/* maintained in a bunch of temporary files and then these are */
/* written back to the TIFF file. Only one pass through the */
/* source TIFF file is made for any number of output */
/* overviews. */
/************************************************************************/
void TIFFBuildOverviews( TIFF *hTIFF, int nOverviews, int * panOvList,
2000-04-18 18:48:31 -04:00
int bUseSubIFDs, const char *pszResampleMethod,
int (*pfnProgress)( double, void * ),
void * pProgressData )
2000-01-28 10:03:32 -05:00
{
TIFFOvrCache **papoRawBIs;
uint32 nXSize, nYSize, nBlockXSize, nBlockYSize;
uint16 nBitsPerPixel, nPhotometric, nCompressFlag, nSamples,
nPlanarConfig, nSampleFormat;
int bTiled, nSXOff, nSYOff, i;
unsigned char *pabySrcTile;
uint16 *panRedMap, *panGreenMap, *panBlueMap;
TIFFErrorHandler pfnWarning;
/* -------------------------------------------------------------------- */
/* Get the base raster size. */
/* -------------------------------------------------------------------- */
TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize );
TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize );
TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerPixel );
TIFFGetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamples );
TIFFGetFieldDefaulted( hTIFF, TIFFTAG_PLANARCONFIG, &nPlanarConfig );
TIFFGetFieldDefaulted( hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric );
TIFFGetFieldDefaulted( hTIFF, TIFFTAG_COMPRESSION, &nCompressFlag );
TIFFGetFieldDefaulted( hTIFF, TIFFTAG_SAMPLEFORMAT, &nSampleFormat );
if( nBitsPerPixel < 8 )
{
TIFFError( "TIFFBuildOverviews",
"File `%s' has samples of %d bits per sample. Sample\n"
"sizes of less than 8 bits per sample are not supported.\n",
TIFFFileName(hTIFF), nBitsPerPixel );
2000-01-28 10:03:32 -05:00
return;
}
/* -------------------------------------------------------------------- */
/* Turn off warnings to avoid alot of repeated warnings while */
/* rereading directories. */
/* -------------------------------------------------------------------- */
pfnWarning = TIFFSetWarningHandler( NULL );
/* -------------------------------------------------------------------- */
/* Get the base raster block size. */
/* -------------------------------------------------------------------- */
if( TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(nBlockYSize) ) )
{
nBlockXSize = nXSize;
bTiled = FALSE;
}
else
{
TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &nBlockXSize );
TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &nBlockYSize );
bTiled = TRUE;
}
/* -------------------------------------------------------------------- */
/* Capture the pallette if there is one. */
/* -------------------------------------------------------------------- */
if( TIFFGetField( hTIFF, TIFFTAG_COLORMAP,
&panRedMap, &panGreenMap, &panBlueMap ) )
{
uint16 *panRed2, *panGreen2, *panBlue2;
panRed2 = (uint16 *) _TIFFmalloc(2*256);
panGreen2 = (uint16 *) _TIFFmalloc(2*256);
panBlue2 = (uint16 *) _TIFFmalloc(2*256);
memcpy( panRed2, panRedMap, 512 );
memcpy( panGreen2, panGreenMap, 512 );
memcpy( panBlue2, panBlueMap, 512 );
panRedMap = panRed2;
panGreenMap = panGreen2;
panBlueMap = panBlue2;
}
else
{
panRedMap = panGreenMap = panBlueMap = NULL;
}
/* -------------------------------------------------------------------- */
/* Initialize overviews. */
/* -------------------------------------------------------------------- */
papoRawBIs = (TIFFOvrCache **) _TIFFmalloc(nOverviews*sizeof(void*));
for( i = 0; i < nOverviews; i++ )
{
int nOXSize, nOYSize, nOBlockXSize, nOBlockYSize;
uint32 nDirOffset;
nOXSize = (nXSize + panOvList[i] - 1) / panOvList[i];
nOYSize = (nYSize + panOvList[i] - 1) / panOvList[i];
nOBlockXSize = MIN((int)nBlockXSize,nOXSize);
nOBlockYSize = MIN((int)nBlockYSize,nOYSize);
if( bTiled )
{
if( (nOBlockXSize % 16) != 0 )
nOBlockXSize = nOBlockXSize + 16 - (nOBlockXSize % 16);
if( (nOBlockYSize % 16) != 0 )
nOBlockYSize = nOBlockYSize + 16 - (nOBlockYSize % 16);
}
nDirOffset = TIFF_WriteOverview( hTIFF, nOXSize, nOYSize,
nBitsPerPixel, nSamples,
nOBlockXSize, nOBlockYSize,
bTiled, nCompressFlag, nPhotometric,
nSampleFormat,
panRedMap, panGreenMap, panBlueMap,
bUseSubIFDs );
papoRawBIs[i] = TIFFCreateOvrCache( hTIFF, nDirOffset );
}
if( panRedMap != NULL )
{
_TIFFfree( panRedMap );
_TIFFfree( panGreenMap );
_TIFFfree( panBlueMap );
}
/* -------------------------------------------------------------------- */
/* Allocate a buffer to hold a source block. */
/* -------------------------------------------------------------------- */
if( bTiled )
pabySrcTile = (unsigned char *) _TIFFmalloc(TIFFTileSize(hTIFF));
else
pabySrcTile = (unsigned char *) _TIFFmalloc(TIFFStripSize(hTIFF));
/* -------------------------------------------------------------------- */
/* Loop over the source raster, applying data to the */
/* destination raster. */
/* -------------------------------------------------------------------- */
for( nSYOff = 0; nSYOff < (int) nYSize; nSYOff += nBlockYSize )
{
for( nSXOff = 0; nSXOff < (int) nXSize; nSXOff += nBlockXSize )
{
/*
* Read and resample into the various overview images.
*/
TIFF_ProcessFullResBlock( hTIFF, nPlanarConfig,
nOverviews, panOvList,
nBitsPerPixel, nSamples, papoRawBIs,
nSXOff, nSYOff, pabySrcTile,
2000-04-18 18:48:31 -04:00
nBlockXSize, nBlockYSize,
nSampleFormat, pszResampleMethod );
2000-01-28 10:03:32 -05:00
}
}
_TIFFfree( pabySrcTile );
/* -------------------------------------------------------------------- */
/* Cleanup the rawblockedimage files. */
/* -------------------------------------------------------------------- */
for( i = 0; i < nOverviews; i++ )
{
TIFFDestroyOvrCache( papoRawBIs[i] );
}
if( papoRawBIs != NULL )
_TIFFfree( papoRawBIs );
TIFFSetWarningHandler( pfnWarning );
}