ultimatepp/bazaar/plugin/gdal/frmts/pcidsk/pcidsktiledrasterband.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

365 lines
12 KiB
C++

/******************************************************************************
* $Id: pcidsktiledrasterband.cpp 17688 2009-09-25 16:01:19Z dron $
*
* Project: PCIDSK Database File
* Purpose: Implementation of PCIDSKTiledRasterBand
* Author: Frank Warmerdam <warmerdam@pobox.com>
*
******************************************************************************
* Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
*
* 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.
****************************************************************************/
#include "gdal_pcidsk.h"
CPL_CVSID("$Id: pcidsktiledrasterband.cpp 17688 2009-09-25 16:01:19Z dron $");
/************************************************************************/
/* PCIDSKRasterBand() */
/************************************************************************/
PCIDSKTiledRasterBand::PCIDSKTiledRasterBand( PCIDSKDataset *poDS,
int nBand, int nImage )
{
poPDS = poDS;
this->poDS = poDS;
this->nBand = nBand;
this->nImage = nImage;
nOverviewCount = 0;
papoOverviews = NULL;
nBlocks = 0;
panBlockOffset = NULL;
if( !BuildBlockMap() )
return;
/* -------------------------------------------------------------------- */
/* Load and parse image header. This is the image header */
/* within the tiled image data. */
/* -------------------------------------------------------------------- */
char achBData[128];
SysRead( 0, 128, achBData );
nRasterXSize = (int) CPLScanLong(achBData + 0, 8);
nRasterYSize = (int) CPLScanLong(achBData + 8, 8);
nBlockXSize = (int) CPLScanLong(achBData + 16, 8);
nBlockYSize = (int) CPLScanLong(achBData + 24, 8);
int nBPR = (nBlockXSize) ? (nRasterXSize + nBlockXSize - 1) / nBlockXSize : 0;
int nBPC = (nBlockYSize) ? (nRasterYSize + nBlockYSize - 1) / nBlockYSize : 0;
/* nBPR * nBPC * 20 must fit on an int. See BuildTileMap() */
if ( nRasterXSize <= 0 || nRasterYSize <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0
|| nRasterXSize > INT_MAX - (nBlockXSize - 1)
|| nRasterYSize > INT_MAX - (nBlockYSize - 1)
|| (double)nBPR * (double)nBPC * 20 > INT_MAX )
{
CPLError( CE_Failure, CPLE_AppDefined, "Invalid raster or block dimensions" );
nRasterXSize = 0;
nRasterYSize = 0;
nBlockXSize = 0;
nBlockYSize = 0;
}
eDataType = poPDS->PCIDSKTypeToGDAL( achBData + 32 );
szCompression[8] = '\0';
memcpy( szCompression, achBData+54, 8 );
}
/************************************************************************/
/* ~PCIDSKTiledRasterBand() */
/************************************************************************/
PCIDSKTiledRasterBand::~PCIDSKTiledRasterBand()
{
FlushCache();
int i;
for( i = 0; i < nOverviewCount; i++ )
delete papoOverviews[i];
CPLFree( papoOverviews );
CPLFree( panBlockOffset );
CPLFree( panTileOffset );
CPLFree( panTileSize );
}
/************************************************************************/
/* BuildBlockMap() */
/************************************************************************/
int PCIDSKTiledRasterBand::BuildBlockMap()
{
nBlocks = 0;
panBlockOffset = NULL;
nTileCount = 0;
panTileOffset = NULL;
panTileSize = NULL;
/* -------------------------------------------------------------------- */
/* Read the whole block map segment. */
/* -------------------------------------------------------------------- */
int nBMapSize;
char *pachBMap;
if( poPDS->nBlockMapSeg < 1 )
return FALSE;
nBMapSize = (int) poPDS->panSegSize[poPDS->nBlockMapSeg-1];
pachBMap = (char *) CPLCalloc(nBMapSize+1,1);
if( !poPDS->SegRead( poPDS->nBlockMapSeg, 0, nBMapSize, pachBMap ) )
return FALSE;
/* -------------------------------------------------------------------- */
/* Parse the header. */
/* -------------------------------------------------------------------- */
int nMaxBlocks = (int) CPLScanLong(pachBMap + 18,8);
if( !EQUALN(pachBMap,"VERSION",7) )
return FALSE;
/* -------------------------------------------------------------------- */
/* Build a "back link" map for this image's blocks. We need */
/* this to positively identify the first block in the chain. */
/* -------------------------------------------------------------------- */
int *panBackLink;
int i, nLastBlock = -1;
panBackLink = (int *) VSICalloc( sizeof(int), nMaxBlocks );
if ( panBackLink == NULL )
{
VSIFree( pachBMap );
VSIFree( panBackLink );
return FALSE;
}
for( i = 0; i < nMaxBlocks; i++ )
panBackLink[i] = -1;
for( i = 0; i < nMaxBlocks; i++ )
{
char *pachEntry = pachBMap + i * 28 + 512;
int nThisImage = (int) CPLScanLong(pachEntry + 12,8);
int nNextBlock = (int) CPLScanLong(pachEntry + 20,8);
if ( nThisImage != nImage || nNextBlock >= nMaxBlocks )
continue;
if( nNextBlock == -1 )
nLastBlock = i;
else
panBackLink[nNextBlock] = i;
}
/* -------------------------------------------------------------------- */
/* Track back through chain to identify the first entry (while */
/* counting). */
/* -------------------------------------------------------------------- */
int iBlock = nLastBlock;
nBlocks = 1;
while( panBackLink[iBlock] != -1 )
{
nBlocks++;
iBlock = panBackLink[iBlock];
}
CPLFree( panBackLink );
panBlockOffset = (vsi_l_offset *) VSIMalloc(sizeof(vsi_l_offset)*nBlocks);
if ( panBlockOffset == NULL )
{
CPLFree( pachBMap );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Process blocks, transforming to absolute offsets in the */
/* PCIDSK file. */
/* -------------------------------------------------------------------- */
for( i = 0; i < nBlocks; i++ )
{
char *pachEntry = pachBMap + iBlock * 28 + 512;
int nBDataSeg = CPLScanLong( pachEntry + 0, 4 );
int nBDataBlock = CPLScanLong( pachEntry + 4, 8 );
if ( nBDataSeg <= 0 || nBDataSeg > poPDS->nSegCount
|| poPDS->panSegType[nBDataSeg-1] != 182)
{
CPLFree( pachBMap );
return FALSE;
}
panBlockOffset[i] =
((vsi_l_offset) nBDataBlock) * 8192
+ poPDS->panSegOffset[nBDataSeg-1] + 1024;
iBlock = (int) CPLScanLong( pachEntry + 20, 8 );
}
CPLFree( pachBMap );
return TRUE;
}
/************************************************************************/
/* BuildTileMap() */
/************************************************************************/
int PCIDSKTiledRasterBand::BuildTileMap()
{
if ( nTileCount < 0 )
return FALSE;
if( nTileCount > 0)
return TRUE;
int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
int nBPC = (nRasterYSize + nBlockYSize - 1) / nBlockYSize;
nTileCount = nBPR * nBPC;
panTileOffset = (vsi_l_offset *)
VSICalloc( sizeof(vsi_l_offset), nTileCount );
panTileSize = (int *) VSICalloc( sizeof(int), nTileCount );
char *pachTileInfo = (char *) VSIMalloc( 20 * nTileCount );
if ( panTileOffset == NULL || panTileSize == NULL || pachTileInfo == NULL )
{
CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory" );
nTileCount = -1;
CPLFree( pachTileInfo );
return FALSE;
}
if( !SysRead( 128, 20 * nTileCount, pachTileInfo ) )
{
nTileCount = -1;
CPLFree( pachTileInfo );
return FALSE;
}
for( int iTile = 0; iTile < nTileCount; iTile++ )
{
panTileOffset[iTile] = (vsi_l_offset)
CPLScanUIntBig( pachTileInfo+12*iTile, 12 );
panTileSize[iTile] = (int)
CPLScanLong( pachTileInfo+12*nTileCount+8*iTile, 8 );
}
CPLFree( pachTileInfo );
return TRUE;
}
/************************************************************************/
/* IReadBlock() */
/************************************************************************/
CPLErr PCIDSKTiledRasterBand::IReadBlock( int nBlockX, int nBlockY,
void *pData )
{
int iTile;
if( !EQUALN(szCompression,"NONE",4) )
{
CPLError( CE_Failure, CPLE_NotSupported,
"Compression '%s' not supported by GDAL.",
szCompression );
return CE_Failure;
}
if( !BuildTileMap() )
return CE_Failure;
int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
iTile = nBlockX + nBlockY * nBPR;
if( panTileOffset[iTile] == (vsi_l_offset) -1 )
{
int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
memset( pData, 0, nBlockXSize*nBlockYSize*nWordSize );
}
else
{
if( !SysRead( panTileOffset[iTile], panTileSize[iTile], pData ) )
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* PCIDSK multibyte data is always big endian. Swap if needed. */
/* -------------------------------------------------------------------- */
#ifdef CPL_LSB
int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
GDALSwapWords( pData, nWordSize, nBlockXSize * nBlockYSize, nWordSize );
#endif
return CE_None;
}
/************************************************************************/
/* SysRead() */
/************************************************************************/
int PCIDSKTiledRasterBand::SysRead( vsi_l_offset nOffset,
int nSize,
void *pData )
{
int iReadSoFar = 0;
while( iReadSoFar < nSize )
{
int iBlock;
vsi_l_offset nNextOffset = nOffset + iReadSoFar;
vsi_l_offset nRealOffset;
int nOffsetInBlock, nThisReadBytes;
iBlock = (int) (nNextOffset >> 13);
nOffsetInBlock = (int) (nNextOffset & 0x1fff);
if ( iBlock >= nBlocks )
return 0;
nRealOffset = panBlockOffset[iBlock] + nOffsetInBlock;
nThisReadBytes = MIN(nSize - iReadSoFar,8192 - nOffsetInBlock);
if( VSIFSeekL( poPDS->fp, nRealOffset, SEEK_SET ) != 0 )
return 0;
if( VSIFReadL( ((char *) pData) + iReadSoFar, 1, nThisReadBytes,
poPDS->fp ) != (size_t) nThisReadBytes )
return 0;
iReadSoFar += nThisReadBytes;
}
return nSize;
}