404 lines
14 KiB
C++
404 lines
14 KiB
C++
/******************************************************************************
|
|
* $Id: rawblockedimage.cpp,v 1.1 1999-08-17 01:47:59 warmerda Exp $
|
|
*
|
|
* Project: GeoTIFF Overview Builder
|
|
* Purpose: Implement the RawBlockedImage class, for holding ``under
|
|
* construction'' overviews in a temporary file.
|
|
* Author: Frank Warmerdam, warmerda@home.com
|
|
*
|
|
******************************************************************************
|
|
* 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: rawblockedimage.cpp,v $
|
|
* Revision 1.1 1999-08-17 01:47:59 warmerda
|
|
* New
|
|
*
|
|
* Revision 1.2 1999/03/12 17:29:34 warmerda
|
|
* Use _WIN32 rather than WIN32.
|
|
*
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifndef _WIN32
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include "rawblockedimage.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
|
|
|
|
/************************************************************************/
|
|
/* RawBlockedImage() */
|
|
/************************************************************************/
|
|
|
|
RawBlockedImage::RawBlockedImage( int nXSizeIn, int nYSizeIn,
|
|
int nBlockXSizeIn, int nBlockYSizeIn,
|
|
int nBitsPerPixelIn )
|
|
|
|
{
|
|
static int nTempCounter = 0;
|
|
char szFilename[128];
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize stuff. */
|
|
/* -------------------------------------------------------------------- */
|
|
nXSize = nXSizeIn;
|
|
nYSize = nYSizeIn;
|
|
nBlockXSize = nBlockXSizeIn;
|
|
nBlockYSize = nBlockYSizeIn;
|
|
nBitsPerPixel = nBitsPerPixelIn;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the raw temporary file, trying to verify first that */
|
|
/* it doesn't already exist. */
|
|
/* -------------------------------------------------------------------- */
|
|
fp = NULL;
|
|
while( fp == NULL )
|
|
{
|
|
sprintf( szFilename, "temp_%d.rbi", nTempCounter++ );
|
|
fp = fopen( szFilename, "r" );
|
|
if( fp != NULL )
|
|
fclose( fp );
|
|
else
|
|
fp = fopen( szFilename, "w+b" );
|
|
}
|
|
|
|
pszFilename = strdup( szFilename );
|
|
nCurFileSize = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize other stuff. */
|
|
/* -------------------------------------------------------------------- */
|
|
nBlocksPerRow = (nXSize + nBlockXSize - 1) / nBlockXSize;
|
|
nBlocksPerColumn = (nYSize + nBlockYSize - 1) / nBlockYSize;
|
|
nBytesPerBlock = (nBlockXSize*nBlockYSize*nBitsPerPixel + 7) / 8;
|
|
|
|
nBlocks = nBlocksPerRow * nBlocksPerColumn;
|
|
nBlocksInCache = 0;
|
|
nMaxBlocksInCache = MIN(nBlocks, 2*nBlocksPerRow);
|
|
|
|
papoBlocks = (RawBlock **) calloc(sizeof(RawBlock*),nBlocks);
|
|
|
|
poLRUHead = NULL;
|
|
poLRUTail = NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~RawBlockedImage() */
|
|
/************************************************************************/
|
|
|
|
RawBlockedImage::~RawBlockedImage()
|
|
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < nBlocks; i++ )
|
|
{
|
|
if( papoBlocks[i] != NULL )
|
|
{
|
|
if( papoBlocks[i]->pabyData != NULL )
|
|
free( papoBlocks[i]->pabyData );
|
|
|
|
delete papoBlocks[i];
|
|
}
|
|
}
|
|
|
|
if( papoBlocks != NULL)
|
|
free( papoBlocks );
|
|
|
|
fclose( fp );
|
|
|
|
unlink( pszFilename ); /* wrap this? */
|
|
|
|
free( pszFilename );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* InsertInLRUList() */
|
|
/* */
|
|
/* Insert this link at the beginning of the LRU list. First */
|
|
/* removed from it's current position if it is in the list. */
|
|
/************************************************************************/
|
|
|
|
void RawBlockedImage::InsertInLRUList( RawBlock * poBlock )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Remove from list, if it is currently in it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->poPrevLRU != NULL || poLRUHead == poBlock )
|
|
RemoveFromLRUList( poBlock );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Add at the head. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poLRUHead != NULL )
|
|
{
|
|
poLRUHead->poPrevLRU = poBlock;
|
|
}
|
|
|
|
poBlock->poNextLRU = poLRUHead;
|
|
poLRUHead = poBlock;
|
|
|
|
if( poLRUTail == NULL )
|
|
poLRUTail = poBlock;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* RemoveFromLRUList() */
|
|
/* */
|
|
/* Remove this block from the LRU list, if present. */
|
|
/************************************************************************/
|
|
|
|
void RawBlockedImage::RemoveFromLRUList( RawBlock * poBlock )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Is it even in the list? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->poPrevLRU == NULL && poLRUHead != poBlock )
|
|
return;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fix the link before this in the list (or head pointer). */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->poPrevLRU == NULL )
|
|
{
|
|
poLRUHead = poBlock->poNextLRU;
|
|
}
|
|
else
|
|
{
|
|
poBlock->poPrevLRU->poNextLRU = poBlock->poNextLRU;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fix the link after this one, or the tail pointer. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->poNextLRU == NULL )
|
|
{
|
|
poLRUTail = poBlock->poPrevLRU;
|
|
}
|
|
else
|
|
{
|
|
poBlock->poNextLRU->poPrevLRU = poBlock->poPrevLRU;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Update this link to indicate it isn't in the list now. */
|
|
/* -------------------------------------------------------------------- */
|
|
poBlock->poPrevLRU = poBlock->poNextLRU = NULL;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* FlushBlock() */
|
|
/************************************************************************/
|
|
|
|
void RawBlockedImage::FlushBlock( RawBlock * poBlock )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we aren't given a particular block to flush, then select */
|
|
/* the lest recently used one from the LRU list. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock == NULL )
|
|
{
|
|
if( poLRUTail == NULL )
|
|
return;
|
|
|
|
poBlock = poLRUTail;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Remove from the LRU list. */
|
|
/* -------------------------------------------------------------------- */
|
|
RemoveFromLRUList( poBlock );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the block has no data, then it doesn't really need to be */
|
|
/* flushed. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->pabyData == NULL )
|
|
return;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Is this block dirty? If so we will have to try and save it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBlock->nDirty )
|
|
{
|
|
if( poBlock->nPositionInFile == -1 )
|
|
poBlock->nPositionInFile = nCurFileSize;
|
|
|
|
nCurFileSize += nBytesPerBlock;
|
|
if( fseek( fp, poBlock->nPositionInFile, SEEK_SET ) != 0 )
|
|
{
|
|
fprintf( stderr,
|
|
"Seek to %d in overview spill file %s failed.\n",
|
|
poBlock->nPositionInFile, pszFilename );
|
|
exit( 1 );
|
|
}
|
|
|
|
if( fwrite( poBlock->pabyData, 1, nBytesPerBlock, fp )
|
|
!= (size_t) nBytesPerBlock )
|
|
{
|
|
fprintf( stderr,
|
|
"Write of %d bytes at %d in overview spill file %s.\n"
|
|
"Is the disk full?\n",
|
|
nBytesPerBlock, poBlock->nPositionInFile, pszFilename );
|
|
exit( 1 );
|
|
}
|
|
|
|
poBlock->nDirty = FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Free the data block, and decrement used count. */
|
|
/* -------------------------------------------------------------------- */
|
|
nBlocksInCache--;
|
|
if( poBlock->pabyData != NULL )
|
|
free( poBlock->pabyData );
|
|
poBlock->pabyData = NULL;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* GetRawBlock() */
|
|
/************************************************************************/
|
|
|
|
RawBlock *RawBlockedImage::GetRawBlock( int nXOff, int nYOff )
|
|
|
|
{
|
|
int nBlock = nXOff + nYOff * nBlocksPerRow;
|
|
RawBlock *poBlock;
|
|
|
|
assert( nBlock >= 0 && nBlock < nBlocks );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Is this the first request? If so, create the block object, */
|
|
/* initialize the data memory, and return it. */
|
|
/* -------------------------------------------------------------------- */
|
|
poBlock = papoBlocks[nBlock];
|
|
if( poBlock == NULL )
|
|
{
|
|
poBlock = papoBlocks[nBlock] = new RawBlock;
|
|
poBlock->nDirty = FALSE;
|
|
poBlock->poPrevLRU = poBlock->poNextLRU = NULL;
|
|
poBlock->nPositionInFile = -1;
|
|
poBlock->pabyData = (unsigned char *) calloc(1,nBytesPerBlock);
|
|
nBlocksInCache++;
|
|
|
|
if( poBlock->pabyData == NULL )
|
|
{
|
|
fprintf( stderr,
|
|
"RawBlockedImage::GetRawBlock() - out of memory\n" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does this block need to be read off disk? */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( poBlock->nPositionInFile >= 0 && poBlock->pabyData == NULL )
|
|
{
|
|
nBlocksInCache++;
|
|
poBlock->pabyData = (unsigned char *) calloc(1,nBytesPerBlock);
|
|
fseek( fp, poBlock->nPositionInFile, SEEK_SET );
|
|
fread( poBlock->pabyData, nBytesPerBlock, 1, fp );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does the data need to be allocated? */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( poBlock->pabyData == NULL )
|
|
{
|
|
poBlock->pabyData = (unsigned char *) calloc(1,nBytesPerBlock);
|
|
if( poBlock->pabyData == NULL )
|
|
{
|
|
fprintf( stderr,
|
|
"RawBlockedImage::GetRawBlock() - out of memory\n" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Push on the LRU stack, or pop it back to the top. */
|
|
/* -------------------------------------------------------------------- */
|
|
InsertInLRUList( poBlock );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we have exceeded our self imposed caching limit, flush */
|
|
/* one block. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBlocksInCache > nMaxBlocksInCache )
|
|
FlushBlock( NULL );
|
|
|
|
return( poBlock );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetTile() */
|
|
/************************************************************************/
|
|
|
|
unsigned char *RawBlockedImage::GetTile( int nXOff, int nYOff )
|
|
|
|
{
|
|
RawBlock *poBlock;
|
|
|
|
poBlock = GetRawBlock(nXOff,nYOff);
|
|
if( poBlock != NULL )
|
|
return poBlock->pabyData;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetTileForUpdate() */
|
|
/************************************************************************/
|
|
|
|
unsigned char *RawBlockedImage::GetTileForUpdate( int nXOff, int nYOff )
|
|
|
|
{
|
|
RawBlock *poBlock;
|
|
|
|
poBlock = GetRawBlock(nXOff,nYOff);
|
|
if( poBlock != NULL )
|
|
{
|
|
poBlock->nDirty = TRUE;
|
|
|
|
return poBlock->pabyData;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|