mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
6684 lines
247 KiB
C++
6684 lines
247 KiB
C++
/******************************************************************************
|
|
* $Id: geotiff.cpp 15792 2008-11-22 22:52:10Z rouault $
|
|
*
|
|
* Project: GeoTIFF Driver
|
|
* Purpose: GDAL GeoTIFF support.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 1998, 2002, 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 "geotiff.h"
|
|
|
|
#include "libgeotiff/gcore/gdal_pam.h"
|
|
#define CPL_SERV_H_INCLUDED
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
#define tif_int32 long
|
|
#define tif_uint32 unsigned long
|
|
#else
|
|
#define tif_int32 int
|
|
#define tif_uint32 unsigned int
|
|
#endif
|
|
|
|
#define int8 tif_int8
|
|
#define uint16 tif_uint16
|
|
#define int32 tif_int32
|
|
#define uint32 tif_uint32
|
|
#include <plugin/tif/lib/tiffio.h>
|
|
#undef int8
|
|
#undef uint16
|
|
#undef int32
|
|
#undef uint32
|
|
|
|
#include "libgeotiff/xtiffio.h"
|
|
#include "libgeotiff/geo_normalize.h"
|
|
#include "libgeotiff/geovalues.h"
|
|
#include "libgeotiff/port/cpl_string.h"
|
|
#include "libgeotiff/port/cpl_csv.h"
|
|
#include "libgeotiff/port/cpl_minixml.h"
|
|
#include "gt_overview.h"
|
|
|
|
CPL_CVSID("$Id: geotiff.cpp 15792 2008-11-22 22:52:10Z rouault $");
|
|
|
|
CPL_C_START
|
|
char CPL_DLL * GTIFGetOGISDefn( GTIF *, GTIFDefn * );
|
|
int CPL_DLL GTIFSetFromOGISDefn( GTIF *, const char * );
|
|
const char * GDALDefaultCSVFilename( const char *pszBasename );
|
|
GUInt32 HalfToFloat( GUInt16 );
|
|
GUInt32 TripleToFloat( GUInt32 );
|
|
void GTiffOneTimeInit();
|
|
CPL_C_END
|
|
|
|
#define TIFFTAG_GDAL_METADATA 42112
|
|
#define TIFFTAG_GDAL_NODATA 42113
|
|
#define TIFFTAG_RPCCOEFFICIENT 50844
|
|
|
|
TIFF* VSI_TIFFOpen(const char* name, const char* mode);
|
|
|
|
enum
|
|
{
|
|
ENDIANNESS_NATIVE,
|
|
ENDIANNESS_LITTLE,
|
|
ENDIANNESS_BIG
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffRasterBand;
|
|
class GTiffRGBABand;
|
|
class GTiffBitmapBand;
|
|
|
|
class GTiffDataset : public GDALPamDataset
|
|
{
|
|
friend class GTiffRasterBand;
|
|
friend class GTiffRGBABand;
|
|
friend class GTiffBitmapBand;
|
|
friend class GTiffSplitBitmapBand;
|
|
friend class GTiffOddBitsBand;
|
|
|
|
TIFF *hTIFF;
|
|
|
|
toff_t nDirOffset;
|
|
int bBase;
|
|
int bCloseTIFFHandle; /* usefull for closing TIFF handle opened by GTIFF_DIR: */
|
|
|
|
uint16 nPlanarConfig;
|
|
uint16 nSamplesPerPixel;
|
|
uint16 nBitsPerSample;
|
|
uint32 nRowsPerStrip;
|
|
uint16 nPhotometric;
|
|
uint16 nSampleFormat;
|
|
uint16 nCompression;
|
|
|
|
int nBlocksPerBand;
|
|
|
|
uint32 nBlockXSize;
|
|
uint32 nBlockYSize;
|
|
|
|
int nLoadedBlock; /* or tile */
|
|
int bLoadedBlockDirty;
|
|
GByte *pabyBlockBuf;
|
|
|
|
CPLErr LoadBlockBuf( int nBlockId, int bReadFromDisk = TRUE );
|
|
CPLErr FlushBlockBuf();
|
|
|
|
char *pszProjection;
|
|
double adfGeoTransform[6];
|
|
int bGeoTransformValid;
|
|
|
|
int bNewDataset; /* product of Create() */
|
|
int bTreatAsRGBA;
|
|
int bCrystalized;
|
|
|
|
void Crystalize();
|
|
|
|
GDALColorTable *poColorTable;
|
|
|
|
void WriteGeoTIFFInfo();
|
|
int SetDirectory( toff_t nDirOffset = 0 );
|
|
|
|
int nOverviewCount;
|
|
GTiffDataset **papoOverviewDS;
|
|
|
|
int nGCPCount;
|
|
GDAL_GCP *pasGCPList;
|
|
|
|
int IsBlockAvailable( int nBlockId );
|
|
|
|
int bGeoTIFFInfoChanged;
|
|
int bNoDataSet;
|
|
int bNoDataChanged;
|
|
int bColorTableChanged;
|
|
double dfNoDataValue;
|
|
|
|
int bMetadataChanged;
|
|
|
|
void ApplyPamInfo();
|
|
void PushMetadataToPam();
|
|
|
|
GDALMultiDomainMetadata oGTiffMDMD;
|
|
|
|
CPLString osProfile;
|
|
char **papszCreationOptions;
|
|
|
|
static void WriteRPCTag( TIFF *, char ** );
|
|
void ReadRPCTag();
|
|
|
|
void* pabyTempWriteBuffer;
|
|
int nTempWriteBufferSize;
|
|
int WriteEncodedTile(uint32 tile, void* data, int bPreserveDataBuffer);
|
|
int WriteEncodedStrip(uint32 strip, void* data, int bPreserveDataBuffer);
|
|
|
|
GTiffDataset* poMaskDS;
|
|
GTiffDataset* poBaseDS;
|
|
|
|
CPLString osFilename;
|
|
|
|
int bFillEmptyTiles;
|
|
void FillEmptyTiles(void);
|
|
|
|
public:
|
|
GTiffDataset();
|
|
~GTiffDataset();
|
|
|
|
virtual const char *GetProjectionRef(void);
|
|
virtual CPLErr SetProjection( const char * );
|
|
virtual CPLErr GetGeoTransform( double * );
|
|
virtual CPLErr SetGeoTransform( double * );
|
|
|
|
virtual int GetGCPCount();
|
|
virtual const char *GetGCPProjection();
|
|
virtual const GDAL_GCP *GetGCPs();
|
|
CPLErr SetGCPs( int, const GDAL_GCP *, const char * );
|
|
|
|
virtual char **GetFileList(void);
|
|
|
|
virtual CPLErr IBuildOverviews( const char *, int, int *, int, int *,
|
|
GDALProgressFunc, void * );
|
|
|
|
CPLErr OpenOffset( TIFF *, toff_t nDirOffset, int, GDALAccess, int bAllowRGBAInterface = TRUE);
|
|
|
|
static GDALDataset *OpenDir( GDALOpenInfo * );
|
|
static GDALDataset *Open( GDALOpenInfo * );
|
|
static int Identify( GDALOpenInfo * );
|
|
static GDALDataset *Create( const char * pszFilename,
|
|
int nXSize, int nYSize, int nBands,
|
|
GDALDataType eType, char ** papszParmList );
|
|
static GDALDataset *CreateCopy( const char * pszFilename,
|
|
GDALDataset *poSrcDS,
|
|
int bStrict, char ** papszOptions,
|
|
GDALProgressFunc pfnProgress,
|
|
void * pProgressData );
|
|
virtual void FlushCache( void );
|
|
|
|
virtual CPLErr SetMetadata( char **, const char * = "" );
|
|
virtual char **GetMetadata( const char * pszDomain = "" );
|
|
virtual CPLErr SetMetadataItem( const char*, const char*,
|
|
const char* = "" );
|
|
virtual const char *GetMetadataItem( const char * pszName,
|
|
const char * pszDomain = "" );
|
|
virtual void *GetInternalHandle( const char * );
|
|
|
|
virtual CPLErr CreateMaskBand( int nFlags );
|
|
|
|
// only needed by createcopy and close code.
|
|
static int WriteMetadata( GDALDataset *, TIFF *, int, const char *,
|
|
const char *, char **, int bExcludeRPBandIMGFileWriting = FALSE );
|
|
static void WriteNoDataValue( TIFF *, double );
|
|
|
|
static TIFF * CreateLL( const char * pszFilename,
|
|
int nXSize, int nYSize, int nBands,
|
|
GDALDataType eType, char **papszParmList );
|
|
|
|
CPLErr WriteEncodedTileOrStrip(uint32 tile_or_strip, void* data, int bPreserveDataBuffer);
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffRasterBand : public GDALPamRasterBand
|
|
{
|
|
friend class GTiffDataset;
|
|
|
|
GDALColorInterp eBandInterp;
|
|
|
|
int bHaveOffsetScale;
|
|
double dfOffset;
|
|
double dfScale;
|
|
|
|
protected:
|
|
GTiffDataset *poGDS;
|
|
GDALMultiDomainMetadata oGTiffMDMD;
|
|
|
|
public:
|
|
GTiffRasterBand( GTiffDataset *, int );
|
|
|
|
// should override RasterIO eventually.
|
|
|
|
virtual CPLErr IReadBlock( int, int, void * );
|
|
virtual CPLErr IWriteBlock( int, int, void * );
|
|
|
|
virtual GDALColorInterp GetColorInterpretation();
|
|
virtual GDALColorTable *GetColorTable();
|
|
virtual CPLErr SetColorTable( GDALColorTable * );
|
|
virtual double GetNoDataValue( int * );
|
|
virtual CPLErr SetNoDataValue( double );
|
|
|
|
virtual double GetOffset( int *pbSuccess = NULL );
|
|
virtual CPLErr SetOffset( double dfNewValue );
|
|
virtual double GetScale( int *pbSuccess = NULL );
|
|
virtual CPLErr SetScale( double dfNewValue );
|
|
virtual CPLErr SetColorInterpretation( GDALColorInterp );
|
|
|
|
virtual CPLErr SetMetadata( char **, const char * = "" );
|
|
virtual char **GetMetadata( const char * pszDomain = "" );
|
|
virtual CPLErr SetMetadataItem( const char*, const char*,
|
|
const char* = "" );
|
|
virtual const char *GetMetadataItem( const char * pszName,
|
|
const char * pszDomain = "" );
|
|
virtual int GetOverviewCount();
|
|
virtual GDALRasterBand *GetOverview( int );
|
|
|
|
virtual GDALRasterBand *GetMaskBand();
|
|
virtual int GetMaskFlags();
|
|
virtual CPLErr CreateMaskBand( int nFlags );
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GTiffRasterBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffRasterBand::GTiffRasterBand( GTiffDataset *poDS, int nBand )
|
|
|
|
{
|
|
poGDS = poDS;
|
|
|
|
this->poDS = poDS;
|
|
this->nBand = nBand;
|
|
|
|
bHaveOffsetScale = FALSE;
|
|
dfOffset = 0.0;
|
|
dfScale = 1.0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the GDAL data type. */
|
|
/* -------------------------------------------------------------------- */
|
|
uint16 nSampleFormat = poDS->nSampleFormat;
|
|
|
|
eDataType = GDT_Unknown;
|
|
|
|
if( poDS->nBitsPerSample <= 8 )
|
|
{
|
|
eDataType = GDT_Byte;
|
|
if( nSampleFormat == SAMPLEFORMAT_INT )
|
|
SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
|
|
|
|
}
|
|
else if( poDS->nBitsPerSample <= 16 )
|
|
{
|
|
if( nSampleFormat == SAMPLEFORMAT_INT )
|
|
eDataType = GDT_Int16;
|
|
else
|
|
eDataType = GDT_UInt16;
|
|
}
|
|
else if( poDS->nBitsPerSample == 32 )
|
|
{
|
|
if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT )
|
|
eDataType = GDT_CInt16;
|
|
else if( nSampleFormat == SAMPLEFORMAT_IEEEFP )
|
|
eDataType = GDT_Float32;
|
|
else if( nSampleFormat == SAMPLEFORMAT_INT )
|
|
eDataType = GDT_Int32;
|
|
else
|
|
eDataType = GDT_UInt32;
|
|
}
|
|
else if( poDS->nBitsPerSample == 64 )
|
|
{
|
|
if( nSampleFormat == SAMPLEFORMAT_IEEEFP )
|
|
eDataType = GDT_Float64;
|
|
else if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP )
|
|
eDataType = GDT_CFloat32;
|
|
else if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT )
|
|
eDataType = GDT_CInt32;
|
|
}
|
|
else if( poDS->nBitsPerSample == 128 )
|
|
{
|
|
if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP )
|
|
eDataType = GDT_CFloat64;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to work out band color interpretation. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poDS->poColorTable != NULL && nBand == 1 )
|
|
eBandInterp = GCI_PaletteIndex;
|
|
else if( poDS->nPhotometric == PHOTOMETRIC_RGB
|
|
|| (poDS->nPhotometric == PHOTOMETRIC_YCBCR
|
|
&& poDS->nCompression == COMPRESSION_JPEG
|
|
&& CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
|
|
"YES") )) )
|
|
{
|
|
if( nBand == 1 )
|
|
eBandInterp = GCI_RedBand;
|
|
else if( nBand == 2 )
|
|
eBandInterp = GCI_GreenBand;
|
|
else if( nBand == 3 )
|
|
eBandInterp = GCI_BlueBand;
|
|
else
|
|
{
|
|
uint16 *v;
|
|
uint16 count = 0;
|
|
|
|
if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v) )
|
|
{
|
|
if( nBand - 3 <= count && v[nBand-4] == EXTRASAMPLE_ASSOCALPHA )
|
|
eBandInterp = GCI_AlphaBand;
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
else if( nBand == 4 )
|
|
eBandInterp = GCI_AlphaBand;
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
}
|
|
else if( poDS->nPhotometric == PHOTOMETRIC_YCBCR )
|
|
{
|
|
if( nBand == 1 )
|
|
eBandInterp = GCI_YCbCr_YBand;
|
|
else if( nBand == 2 )
|
|
eBandInterp = GCI_YCbCr_CbBand;
|
|
else if( nBand == 3 )
|
|
eBandInterp = GCI_YCbCr_CrBand;
|
|
else
|
|
{
|
|
uint16 *v;
|
|
uint16 count = 0;
|
|
|
|
if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v) )
|
|
{
|
|
if( nBand - 3 <= count && v[nBand-4] == EXTRASAMPLE_ASSOCALPHA )
|
|
eBandInterp = GCI_AlphaBand;
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
else if( nBand == 4 )
|
|
eBandInterp = GCI_AlphaBand;
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
}
|
|
else if( poDS->nPhotometric == PHOTOMETRIC_SEPARATED )
|
|
{
|
|
if( nBand == 1 )
|
|
eBandInterp = GCI_CyanBand;
|
|
else if( nBand == 2 )
|
|
eBandInterp = GCI_MagentaBand;
|
|
else if( nBand == 3 )
|
|
eBandInterp = GCI_YellowBand;
|
|
else
|
|
eBandInterp = GCI_BlackBand;
|
|
}
|
|
else if( poDS->nPhotometric == PHOTOMETRIC_MINISBLACK && nBand == 1 )
|
|
eBandInterp = GCI_GrayIndex;
|
|
else
|
|
{
|
|
uint16 *v;
|
|
uint16 count = 0;
|
|
|
|
if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v ) )
|
|
{
|
|
int nBaseSamples;
|
|
nBaseSamples = poDS->nSamplesPerPixel - count;
|
|
|
|
if( nBand > nBaseSamples
|
|
&& v[nBand-nBaseSamples-1] == EXTRASAMPLE_ASSOCALPHA )
|
|
eBandInterp = GCI_AlphaBand;
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
else
|
|
eBandInterp = GCI_Undefined;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Establish block size for strip or tiles. */
|
|
/* -------------------------------------------------------------------- */
|
|
nBlockXSize = poDS->nBlockXSize;
|
|
nBlockYSize = poDS->nBlockYSize;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
int nBlockBufSize, nBlockId, nBlockIdBand0;
|
|
CPLErr eErr = CE_None;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
if( TIFFIsTiled(poGDS->hTIFF) )
|
|
nBlockBufSize = TIFFTileSize( poGDS->hTIFF );
|
|
else
|
|
{
|
|
CPLAssert( nBlockXOff == 0 );
|
|
nBlockBufSize = TIFFStripSize( poGDS->hTIFF );
|
|
}
|
|
|
|
nBlockIdBand0 = nBlockXOff + nBlockYOff * nBlocksPerRow;
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
|
|
nBlockId = nBlockIdBand0 + (nBand-1) * poGDS->nBlocksPerBand;
|
|
else
|
|
nBlockId = nBlockIdBand0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* The bottom most partial tiles and strips are sometimes only */
|
|
/* partially encoded. This code reduces the requested data so */
|
|
/* an error won't be reported in this case. (#1179) */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBlockReqSize = nBlockBufSize;
|
|
|
|
if( (nBlockYOff+1) * nBlockYSize > nRasterYSize )
|
|
{
|
|
nBlockReqSize = (nBlockBufSize / nBlockYSize)
|
|
* (nBlockYSize - (((nBlockYOff+1) * nBlockYSize) % nRasterYSize));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle the case of a strip or tile that doesn't exist yet. */
|
|
/* Just set to zeros and return. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !poGDS->IsBlockAvailable(nBlockId) )
|
|
{
|
|
memset( pImage, 0,
|
|
nBlockXSize * nBlockYSize
|
|
* (GDALGetDataTypeSize(eDataType) / 8) );
|
|
return CE_None;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle simple case (separate, onesampleperpixel) */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nBands == 1
|
|
|| poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
|
|
{
|
|
if( nBlockReqSize < nBlockBufSize )
|
|
memset( pImage, 0, nBlockBufSize );
|
|
|
|
if( TIFFIsTiled( poGDS->hTIFF ) )
|
|
{
|
|
if( TIFFReadEncodedTile( poGDS->hTIFF, nBlockId, pImage,
|
|
nBlockReqSize ) == -1 )
|
|
{
|
|
memset( pImage, 0, nBlockBufSize );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadEncodedTile() failed.\n" );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( TIFFReadEncodedStrip( poGDS->hTIFF, nBlockId, pImage,
|
|
nBlockReqSize ) == -1 )
|
|
{
|
|
memset( pImage, 0, nBlockBufSize );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadEncodedStrip() failed.\n" );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load desired block */
|
|
/* -------------------------------------------------------------------- */
|
|
eErr = poGDS->LoadBlockBuf( nBlockId );
|
|
if( eErr != CE_None )
|
|
{
|
|
memset( pImage, 0,
|
|
nBlockXSize * nBlockYSize
|
|
* (GDALGetDataTypeSize(eDataType) / 8) );
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for YCbCr subsampled data. */
|
|
/* -------------------------------------------------------------------- */
|
|
#ifdef notdef
|
|
if( (eBandInterp == GCI_YCbCr_YBand
|
|
|| eBandInterp == GCI_YCbCr_CbBand
|
|
|| eBandInterp == GCI_YCbCr_CrBand)
|
|
&& poGDS->nBitsPerSample == 8 )
|
|
{
|
|
uint16 hs, vs;
|
|
int iX, iY;
|
|
|
|
TIFFGetFieldDefaulted( poGDS->hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
|
|
&hs, &vs);
|
|
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
int iBlock = (iY / vs) * (nBlockXSize/hs) + (iX / hs);
|
|
GByte *pabySrcBlock = poGDS->pabyBlockBuf +
|
|
(vs * hs + 2) * iBlock;
|
|
|
|
if( eBandInterp == GCI_YCbCr_YBand )
|
|
((GByte *)pImage)[iY*nBlockXSize + iX] =
|
|
pabySrcBlock[(iX % hs) + (iY % vs) * hs];
|
|
else if( eBandInterp == GCI_YCbCr_CbBand )
|
|
((GByte *)pImage)[iY*nBlockXSize + iX] =
|
|
pabySrcBlock[vs * hs + 0];
|
|
else if( eBandInterp == GCI_YCbCr_CrBand )
|
|
((GByte *)pImage)[iY*nBlockXSize + iX] =
|
|
pabySrcBlock[vs * hs + 1];
|
|
}
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle simple case of eight bit data, and pixel interleaving. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nBitsPerSample == 8 )
|
|
{
|
|
int i, nBlockPixels;
|
|
GByte *pabyImage;
|
|
|
|
pabyImage = poGDS->pabyBlockBuf + nBand - 1;
|
|
|
|
nBlockPixels = nBlockXSize * nBlockYSize;
|
|
for( i = 0; i < nBlockPixels; i++ )
|
|
{
|
|
((GByte *) pImage)[i] = *pabyImage;
|
|
pabyImage += poGDS->nBands;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
int i, nBlockPixels, nWordBytes;
|
|
GByte *pabyImage;
|
|
|
|
nWordBytes = poGDS->nBitsPerSample / 8;
|
|
pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes;
|
|
|
|
nBlockPixels = nBlockXSize * nBlockYSize;
|
|
for( i = 0; i < nBlockPixels; i++ )
|
|
{
|
|
for( int j = 0; j < nWordBytes; j++ )
|
|
{
|
|
((GByte *) pImage)[i*nWordBytes + j] = pabyImage[j];
|
|
}
|
|
pabyImage += poGDS->nBands * nWordBytes;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* In the fairly common case of pixel interleaved 8bit data */
|
|
/* that is multi-band, lets push the rest of the data into the */
|
|
/* block cache too, to avoid (hopefully) having to redecode it. */
|
|
/* */
|
|
/* Our following logic actually depends on the fact that the */
|
|
/* this block is already loaded, so subsequent calls will end */
|
|
/* up back in this method and pull from the loaded block. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nBands != 1 && eErr == CE_None )
|
|
{
|
|
int iOtherBand;
|
|
|
|
for( iOtherBand = 1; iOtherBand <= poGDS->nBands; iOtherBand++ )
|
|
{
|
|
if( iOtherBand == nBand )
|
|
continue;
|
|
|
|
GDALRasterBlock *poBlock;
|
|
|
|
poBlock = poGDS->GetRasterBand(iOtherBand)->
|
|
GetLockedBlockRef(nBlockXOff,nBlockYOff);
|
|
if (poBlock == NULL)
|
|
return CE_Failure;
|
|
poBlock->DropLock();
|
|
}
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
int nBlockId;
|
|
CPLErr eErr = CE_None;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
CPLAssert( poGDS != NULL
|
|
&& nBlockXOff >= 0
|
|
&& nBlockYOff >= 0
|
|
&& pImage != NULL );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle case of "separate" images */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE
|
|
|| poGDS->nBands == 1 )
|
|
{
|
|
nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow
|
|
+ (nBand-1) * poGDS->nBlocksPerBand;
|
|
|
|
eErr = poGDS->WriteEncodedTileOrStrip(nBlockId, pImage, TRUE);
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
|
|
|
|
eErr = poGDS->LoadBlockBuf( nBlockId );
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* On write of pixel interleaved data, we might as well flush */
|
|
/* out any other bands that are dirty in our cache. This is */
|
|
/* especially helpful when writing compressed blocks. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBand;
|
|
int nWordBytes = poGDS->nBitsPerSample / 8;
|
|
|
|
for( iBand = 0; iBand < poGDS->nBands; iBand++ )
|
|
{
|
|
const GByte *pabyThisImage = NULL;
|
|
GDALRasterBlock *poBlock = NULL;
|
|
|
|
if( iBand+1 == nBand )
|
|
pabyThisImage = (GByte *) pImage;
|
|
else
|
|
{
|
|
poBlock = ((GTiffRasterBand *)poGDS->GetRasterBand( iBand+1 ))
|
|
->TryGetLockedBlockRef( nBlockXOff, nBlockYOff );
|
|
|
|
if( poBlock == NULL )
|
|
continue;
|
|
|
|
if( !poBlock->GetDirty() )
|
|
{
|
|
poBlock->DropLock();
|
|
continue;
|
|
}
|
|
|
|
pabyThisImage = (GByte *) poBlock->GetDataRef();
|
|
}
|
|
|
|
int i, nBlockPixels = nBlockXSize * nBlockYSize;
|
|
GByte *pabyOut = poGDS->pabyBlockBuf + iBand*nWordBytes;
|
|
|
|
for( i = 0; i < nBlockPixels; i++ )
|
|
{
|
|
memcpy( pabyOut, pabyThisImage, nWordBytes );
|
|
|
|
pabyOut += nWordBytes * poGDS->nBands;
|
|
pabyThisImage += nWordBytes;
|
|
}
|
|
|
|
if( poBlock != NULL )
|
|
{
|
|
poBlock->MarkClean();
|
|
poBlock->DropLock();
|
|
}
|
|
}
|
|
|
|
poGDS->bLoadedBlockDirty = TRUE;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOffset() */
|
|
/************************************************************************/
|
|
|
|
double GTiffRasterBand::GetOffset( int *pbSuccess )
|
|
|
|
{
|
|
if( pbSuccess )
|
|
*pbSuccess = bHaveOffsetScale;
|
|
return dfOffset;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetOffset() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetOffset( double dfNewValue )
|
|
|
|
{
|
|
poGDS->bMetadataChanged = TRUE;
|
|
|
|
bHaveOffsetScale = TRUE;
|
|
dfOffset = dfNewValue;
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetScale() */
|
|
/************************************************************************/
|
|
|
|
double GTiffRasterBand::GetScale( int *pbSuccess )
|
|
|
|
{
|
|
if( pbSuccess )
|
|
*pbSuccess = bHaveOffsetScale;
|
|
return dfScale;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetScale() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetScale( double dfNewValue )
|
|
|
|
{
|
|
poGDS->bMetadataChanged = TRUE;
|
|
|
|
bHaveOffsetScale = TRUE;
|
|
dfScale = dfNewValue;
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadata() */
|
|
/************************************************************************/
|
|
|
|
char **GTiffRasterBand::GetMetadata( const char * pszDomain )
|
|
|
|
{
|
|
return oGTiffMDMD.GetMetadata( pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetMetadata() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetMetadata( char ** papszMD, const char *pszDomain )
|
|
|
|
{
|
|
if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") )
|
|
poGDS->bMetadataChanged = TRUE;
|
|
|
|
return oGTiffMDMD.SetMetadata( papszMD, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
const char *GTiffRasterBand::GetMetadataItem( const char * pszName,
|
|
const char * pszDomain )
|
|
|
|
{
|
|
return oGTiffMDMD.GetMetadataItem( pszName, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetMetadataItem( const char *pszName,
|
|
const char *pszValue,
|
|
const char *pszDomain )
|
|
|
|
{
|
|
if( pszDomain == NULL || EQUAL(pszDomain,"_temporary_") )
|
|
poGDS->bMetadataChanged = TRUE;
|
|
|
|
return oGTiffMDMD.SetMetadataItem( pszName, pszValue, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp GTiffRasterBand::GetColorInterpretation()
|
|
|
|
{
|
|
return eBandInterp;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetColorInterpretation( GDALColorInterp eInterp )
|
|
|
|
{
|
|
if( eInterp == eBandInterp )
|
|
return CE_None;
|
|
|
|
if( poGDS->bCrystalized )
|
|
return GDALPamRasterBand::SetColorInterpretation( eInterp );
|
|
|
|
/* greyscale + alpha */
|
|
else if( eInterp == GCI_AlphaBand
|
|
&& nBand == 2
|
|
&& poGDS->nSamplesPerPixel == 2
|
|
&& poGDS->nPhotometric == PHOTOMETRIC_MINISBLACK )
|
|
{
|
|
uint16 v[1] = { EXTRASAMPLE_ASSOCALPHA };
|
|
|
|
TIFFSetField(poGDS->hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
|
|
eBandInterp = eInterp;
|
|
return CE_None;
|
|
}
|
|
|
|
/* RGB + alpha */
|
|
else if( eInterp == GCI_AlphaBand
|
|
&& nBand == 4
|
|
&& poGDS->nSamplesPerPixel == 4
|
|
&& poGDS->nPhotometric == PHOTOMETRIC_RGB )
|
|
{
|
|
uint16 v[1] = { EXTRASAMPLE_ASSOCALPHA };
|
|
|
|
TIFFSetField(poGDS->hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
|
|
eBandInterp = eInterp;
|
|
return CE_None;
|
|
}
|
|
|
|
else
|
|
return GDALPamRasterBand::SetColorInterpretation( eInterp );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorTable() */
|
|
/************************************************************************/
|
|
|
|
GDALColorTable *GTiffRasterBand::GetColorTable()
|
|
|
|
{
|
|
if( nBand == 1 )
|
|
return poGDS->poColorTable;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetColorTable() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetColorTable( GDALColorTable * poCT )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check if this is even a candidate for applying a PCT. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->bCrystalized && poGDS->poColorTable == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SetColorTable() not supported for existing TIFF files." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( poGDS->nSamplesPerPixel != 1 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SetColorTable() not supported for multi-sample TIFF files." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( eDataType != GDT_Byte && eDataType != GDT_UInt16 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SetColorTable() only supported for Byte or UInt16 bands in TIFF format." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write out the colortable, and update the configuration. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nColors;
|
|
|
|
if( eDataType == GDT_Byte )
|
|
nColors = 256;
|
|
else
|
|
nColors = 65536;
|
|
|
|
unsigned short *panTRed, *panTGreen, *panTBlue;
|
|
|
|
panTRed = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
panTGreen = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
panTBlue = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
|
|
for( int iColor = 0; iColor < nColors; iColor++ )
|
|
{
|
|
if( iColor < poCT->GetColorEntryCount() )
|
|
{
|
|
GDALColorEntry sRGB;
|
|
|
|
poCT->GetColorEntryAsRGB( iColor, &sRGB );
|
|
|
|
panTRed[iColor] = (unsigned short) (257 * sRGB.c1);
|
|
panTGreen[iColor] = (unsigned short) (257 * sRGB.c2);
|
|
panTBlue[iColor] = (unsigned short) (257 * sRGB.c3);
|
|
}
|
|
else
|
|
{
|
|
panTRed[iColor] = panTGreen[iColor] = panTBlue[iColor] = 0;
|
|
}
|
|
}
|
|
|
|
TIFFSetField( poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
|
|
TIFFSetField( poGDS->hTIFF, TIFFTAG_COLORMAP,
|
|
panTRed, panTGreen, panTBlue );
|
|
|
|
CPLFree( panTRed );
|
|
CPLFree( panTGreen );
|
|
CPLFree( panTBlue );
|
|
|
|
if( poGDS->poColorTable )
|
|
delete poGDS->poColorTable;
|
|
|
|
poGDS->poColorTable = poCT->Clone();
|
|
poGDS->bColorTableChanged = TRUE;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
double GTiffRasterBand::GetNoDataValue( int * pbSuccess )
|
|
|
|
{
|
|
if( poGDS->bNoDataSet )
|
|
{
|
|
if( pbSuccess )
|
|
*pbSuccess = TRUE;
|
|
|
|
return poGDS->dfNoDataValue;
|
|
}
|
|
else
|
|
return GDALPamRasterBand::GetNoDataValue( pbSuccess );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::SetNoDataValue( double dfNoData )
|
|
|
|
{
|
|
poGDS->bNoDataSet = TRUE;
|
|
poGDS->bNoDataChanged = TRUE;
|
|
poGDS->dfNoDataValue = dfNoData;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOverviewCount() */
|
|
/************************************************************************/
|
|
|
|
int GTiffRasterBand::GetOverviewCount()
|
|
|
|
{
|
|
if( poGDS->nOverviewCount > 0 )
|
|
return poGDS->nOverviewCount;
|
|
else
|
|
return GDALRasterBand::GetOverviewCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOverview() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterBand *GTiffRasterBand::GetOverview( int i )
|
|
|
|
{
|
|
if( poGDS->nOverviewCount > 0 )
|
|
{
|
|
if( i < 0 || i >= poGDS->nOverviewCount )
|
|
return NULL;
|
|
else
|
|
return poGDS->papoOverviewDS[i]->GetRasterBand(nBand);
|
|
}
|
|
else
|
|
return GDALRasterBand::GetOverview( i );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMaskFlags() */
|
|
/************************************************************************/
|
|
|
|
int GTiffRasterBand::GetMaskFlags()
|
|
{
|
|
if( poGDS->poMaskDS != NULL )
|
|
{
|
|
int iBand;
|
|
int nMaskFlag = 0;
|
|
if( poGDS->poMaskDS->GetRasterCount() == 1)
|
|
{
|
|
iBand = 1;
|
|
nMaskFlag = GMF_PER_DATASET;
|
|
}
|
|
else
|
|
{
|
|
iBand = nBand;
|
|
}
|
|
if( poGDS->poMaskDS->GetRasterBand(iBand)->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) != NULL
|
|
&& atoi(poGDS->poMaskDS->GetRasterBand(iBand)->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" )) == 1)
|
|
{
|
|
return nMaskFlag;
|
|
}
|
|
else
|
|
{
|
|
return nMaskFlag | GMF_ALPHA;
|
|
}
|
|
}
|
|
else
|
|
return GDALPamRasterBand::GetMaskFlags();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMaskBand() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterBand *GTiffRasterBand::GetMaskBand()
|
|
{
|
|
if( poGDS->poMaskDS != NULL )
|
|
{
|
|
if( poGDS->poMaskDS->GetRasterCount() == 1)
|
|
return poGDS->poMaskDS->GetRasterBand(1);
|
|
else
|
|
return poGDS->poMaskDS->GetRasterBand(nBand);
|
|
}
|
|
else
|
|
return GDALPamRasterBand::GetMaskBand();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffRGBABand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffRGBABand : public GTiffRasterBand
|
|
{
|
|
friend class GTiffDataset;
|
|
|
|
public:
|
|
|
|
GTiffRGBABand( GTiffDataset *, int );
|
|
|
|
virtual CPLErr IReadBlock( int, int, void * );
|
|
virtual CPLErr IWriteBlock( int, int, void * );
|
|
|
|
virtual GDALColorInterp GetColorInterpretation();
|
|
};
|
|
|
|
|
|
/************************************************************************/
|
|
/* GTiffRGBABand() */
|
|
/************************************************************************/
|
|
|
|
GTiffRGBABand::GTiffRGBABand( GTiffDataset *poDS, int nBand )
|
|
: GTiffRasterBand( poDS, nBand )
|
|
|
|
{
|
|
eDataType = GDT_Byte;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRGBABand::IWriteBlock( int, int, void * )
|
|
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"RGBA interpreted raster bands are read-only." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRGBABand::IReadBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
int nBlockBufSize, nBlockId;
|
|
CPLErr eErr = CE_None;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
nBlockBufSize = 4 * nBlockXSize * nBlockYSize;
|
|
nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate a temporary buffer for this strip. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->pabyBlockBuf == NULL )
|
|
{
|
|
poGDS->pabyBlockBuf = (GByte *) VSICalloc( 1, nBlockBufSize );
|
|
if( poGDS->pabyBlockBuf == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"Unable to allocate %d bytes for a temporary strip "
|
|
"buffer in GTIFF driver.",
|
|
nBlockBufSize );
|
|
|
|
return( CE_Failure );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the strip */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nLoadedBlock != nBlockId )
|
|
{
|
|
if( TIFFIsTiled( poGDS->hTIFF ) )
|
|
{
|
|
if( TIFFReadRGBATile(poGDS->hTIFF,
|
|
nBlockXOff * nBlockXSize,
|
|
nBlockYOff * nBlockYSize,
|
|
(tif_uint32 *) poGDS->pabyBlockBuf) == -1 )
|
|
{
|
|
/* Once TIFFError() is properly hooked, this can go away */
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadRGBATile() failed." );
|
|
|
|
memset( poGDS->pabyBlockBuf, 0, nBlockBufSize );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( TIFFReadRGBAStrip(poGDS->hTIFF,
|
|
nBlockId * nBlockYSize,
|
|
(tif_uint32 *) poGDS->pabyBlockBuf) == -1 )
|
|
{
|
|
/* Once TIFFError() is properly hooked, this can go away */
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadRGBAStrip() failed." );
|
|
|
|
memset( poGDS->pabyBlockBuf, 0, nBlockBufSize );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
}
|
|
|
|
poGDS->nLoadedBlock = nBlockId;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle simple case of eight bit data, and pixel interleaving. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iDestLine, nBO;
|
|
int nThisBlockYSize;
|
|
|
|
if( (nBlockYOff+1) * nBlockYSize > GetYSize()
|
|
&& !TIFFIsTiled( poGDS->hTIFF ) )
|
|
nThisBlockYSize = GetYSize() - nBlockYOff * nBlockYSize;
|
|
else
|
|
nThisBlockYSize = nBlockYSize;
|
|
|
|
#ifdef CPL_LSB
|
|
nBO = nBand - 1;
|
|
#else
|
|
nBO = 4 - nBand;
|
|
#endif
|
|
|
|
for( iDestLine = 0; iDestLine < nThisBlockYSize; iDestLine++ )
|
|
{
|
|
int nSrcOffset;
|
|
|
|
nSrcOffset = (nThisBlockYSize - iDestLine - 1) * nBlockXSize * 4;
|
|
|
|
GDALCopyWords( poGDS->pabyBlockBuf + nBO + nSrcOffset, GDT_Byte, 4,
|
|
((GByte *) pImage)+iDestLine*nBlockXSize, GDT_Byte, 1,
|
|
nBlockXSize );
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp GTiffRGBABand::GetColorInterpretation()
|
|
|
|
{
|
|
if( nBand == 1 )
|
|
return GCI_RedBand;
|
|
else if( nBand == 2 )
|
|
return GCI_GreenBand;
|
|
else if( nBand == 3 )
|
|
return GCI_BlueBand;
|
|
else
|
|
return GCI_AlphaBand;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffOddBitsBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffOddBitsBand : public GTiffRasterBand
|
|
{
|
|
friend class GTiffDataset;
|
|
public:
|
|
|
|
GTiffOddBitsBand( GTiffDataset *, int );
|
|
virtual ~GTiffOddBitsBand();
|
|
|
|
virtual CPLErr IReadBlock( int, int, void * );
|
|
virtual CPLErr IWriteBlock( int, int, void * );
|
|
};
|
|
|
|
|
|
/************************************************************************/
|
|
/* GTiffOddBitsBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffOddBitsBand::GTiffOddBitsBand( GTiffDataset *poGDS, int nBand )
|
|
: GTiffRasterBand( poGDS, nBand )
|
|
|
|
{
|
|
eDataType = GDT_Byte;
|
|
if( poGDS->nSampleFormat == SAMPLEFORMAT_IEEEFP )
|
|
eDataType = GDT_Float32;
|
|
else if( poGDS->nBitsPerSample > 8 && poGDS->nBitsPerSample < 16 )
|
|
eDataType = GDT_UInt16;
|
|
else if( poGDS->nBitsPerSample > 16 )
|
|
eDataType = GDT_UInt32;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GTiffOddBitsBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffOddBitsBand::~GTiffOddBitsBand()
|
|
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffOddBitsBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
|
|
void *pImage )
|
|
|
|
{
|
|
int nBlockId;
|
|
CPLErr eErr = CE_None;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
CPLAssert( poGDS != NULL
|
|
&& nBlockXOff >= 0
|
|
&& nBlockYOff >= 0
|
|
&& pImage != NULL );
|
|
|
|
if( eDataType == GDT_Float32 && poGDS->nBitsPerSample < 32 )
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Writing float data with nBitsPerSample < 32 is unsupported");
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load the block buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
|
|
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
|
|
nBlockId += (nBand-1) * poGDS->nBlocksPerBand;
|
|
|
|
/* Only read content from disk in the CONTIG case */
|
|
eErr = poGDS->LoadBlockBuf( nBlockId,
|
|
poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && poGDS->nBands > 1 );
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle case of "separate" images or single band images where */
|
|
/* no interleaving with other data is required. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE
|
|
|| poGDS->nBands == 1 )
|
|
{
|
|
int iBit, iPixel, iBitOffset = 0;
|
|
int iX, iY, nBitsPerLine;
|
|
|
|
// bits per line rounds up to next byte boundary.
|
|
nBitsPerLine = nBlockXSize * poGDS->nBitsPerSample;
|
|
if( (nBitsPerLine & 7) != 0 )
|
|
nBitsPerLine = (nBitsPerLine + 7) & (~7);
|
|
|
|
iPixel = 0;
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
iBitOffset = iY * nBitsPerLine;
|
|
|
|
/* Small optimization in 1 bit case */
|
|
if (poGDS->nBitsPerSample == 1)
|
|
{
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
if (((GByte *) pImage)[iPixel++])
|
|
poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7));
|
|
iBitOffset++;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
int nInWord = 0;
|
|
if( eDataType == GDT_Byte )
|
|
nInWord = ((GByte *) pImage)[iPixel++];
|
|
else if( eDataType == GDT_UInt16 )
|
|
nInWord = ((GUInt16 *) pImage)[iPixel++];
|
|
else if( eDataType == GDT_UInt32 )
|
|
nInWord = ((GUInt32 *) pImage)[iPixel++];
|
|
else
|
|
CPLAssert(0);
|
|
|
|
|
|
if (poGDS->nBitsPerSample == 24)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for 24bit data which is pre-byteswapped since */
|
|
/* the size falls on a byte boundary ... ugg (#2361). */
|
|
/* -------------------------------------------------------------------- */
|
|
#ifdef CPL_MSB
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = nInWord;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = nInWord >> 8;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = nInWord >> 16;
|
|
#else
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = nInWord >> 16;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = nInWord >> 8;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = nInWord;
|
|
#endif
|
|
iBitOffset += 24;
|
|
}
|
|
else
|
|
{
|
|
for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ )
|
|
{
|
|
if (nInWord & (1 << (poGDS->nBitsPerSample - 1 - iBit)))
|
|
poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7));
|
|
iBitOffset++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
poGDS->bLoadedBlockDirty = TRUE;
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* On write of pixel interleaved data, we might as well flush */
|
|
/* out any other bands that are dirty in our cache. This is */
|
|
/* especially helpful when writing compressed blocks. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBand;
|
|
|
|
for( iBand = 0; iBand < poGDS->nBands; iBand++ )
|
|
{
|
|
const GByte *pabyThisImage = NULL;
|
|
GDALRasterBlock *poBlock = NULL;
|
|
int iBit, iPixel, iBitOffset = 0;
|
|
int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine;
|
|
|
|
if( iBand+1 == nBand )
|
|
pabyThisImage = (GByte *) pImage;
|
|
else
|
|
{
|
|
poBlock = ((GTiffOddBitsBand *)poGDS->GetRasterBand( iBand+1 ))
|
|
->TryGetLockedBlockRef( nBlockXOff, nBlockYOff );
|
|
|
|
if( poBlock == NULL )
|
|
continue;
|
|
|
|
if( !poBlock->GetDirty() )
|
|
{
|
|
poBlock->DropLock();
|
|
continue;
|
|
}
|
|
|
|
pabyThisImage = (GByte *) poBlock->GetDataRef();
|
|
}
|
|
|
|
iPixelBitSkip = poGDS->nBitsPerSample * poGDS->nBands;
|
|
iBandBitOffset = iBand * poGDS->nBitsPerSample;
|
|
|
|
// bits per line rounds up to next byte boundary.
|
|
nBitsPerLine = nBlockXSize * iPixelBitSkip;
|
|
if( (nBitsPerLine & 7) != 0 )
|
|
nBitsPerLine = (nBitsPerLine + 7) & (~7);
|
|
|
|
iPixel = 0;
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
iBitOffset = iBandBitOffset + iY * nBitsPerLine;
|
|
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
int nInWord = 0;
|
|
if( eDataType == GDT_Byte )
|
|
nInWord = ((GByte *) pabyThisImage)[iPixel++];
|
|
else if( eDataType == GDT_UInt16 )
|
|
nInWord = ((GUInt16 *) pabyThisImage)[iPixel++];
|
|
else if( eDataType == GDT_UInt32 )
|
|
nInWord = ((GUInt32 *) pabyThisImage)[iPixel++];
|
|
else
|
|
CPLAssert(0);
|
|
|
|
|
|
if (poGDS->nBitsPerSample == 24)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for 24bit data which is pre-byteswapped since */
|
|
/* the size falls on a byte boundary ... ugg (#2361). */
|
|
/* -------------------------------------------------------------------- */
|
|
#ifdef CPL_MSB
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = nInWord;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = nInWord >> 8;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = nInWord >> 16;
|
|
#else
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = nInWord >> 16;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = nInWord >> 8;
|
|
poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = nInWord;
|
|
#endif
|
|
iBitOffset += 24;
|
|
}
|
|
else
|
|
{
|
|
for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ )
|
|
{
|
|
if (nInWord & (1 << (poGDS->nBitsPerSample - 1 - iBit)))
|
|
poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7));
|
|
iBitOffset++;
|
|
}
|
|
}
|
|
|
|
iBitOffset= iBitOffset + iPixelBitSkip - poGDS->nBitsPerSample;
|
|
}
|
|
}
|
|
|
|
if( poBlock != NULL )
|
|
{
|
|
poBlock->MarkClean();
|
|
poBlock->DropLock();
|
|
}
|
|
}
|
|
|
|
poGDS->bLoadedBlockDirty = TRUE;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffOddBitsBand::IReadBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
int nBlockId;
|
|
CPLErr eErr = CE_None;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
|
|
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE )
|
|
nBlockId += (nBand-1) * poGDS->nBlocksPerBand;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load the block buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
eErr = poGDS->LoadBlockBuf( nBlockId );
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
if ( poGDS->nBitsPerSample == 1 && (poGDS->nBands == 1 || poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) )
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Translate 1bit data to eight bit. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iDstOffset=0, iLine;
|
|
register GByte *pabyBlockBuf = poGDS->pabyBlockBuf;
|
|
|
|
for( iLine = 0; iLine < nBlockYSize; iLine++ )
|
|
{
|
|
int iSrcOffset, iPixel;
|
|
|
|
iSrcOffset = ((nBlockXSize+7) >> 3) * 8 * iLine;
|
|
|
|
for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset++ )
|
|
{
|
|
if( pabyBlockBuf[iSrcOffset >>3] & (0x80 >> (iSrcOffset & 0x7)) )
|
|
((GByte *) pImage)[iDstOffset++] = 1;
|
|
else
|
|
((GByte *) pImage)[iDstOffset++] = 0;
|
|
}
|
|
}
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle the case of 16- and 24-bit floating point data as per */
|
|
/* TIFF Technical Note 3. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( eDataType == GDT_Float32 && poGDS->nBitsPerSample < 32 )
|
|
{
|
|
int i, nBlockPixels, nWordBytes, iSkipBytes;
|
|
GByte *pabyImage;
|
|
|
|
nWordBytes = poGDS->nBitsPerSample / 8;
|
|
pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes;
|
|
iSkipBytes = ( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) ?
|
|
nWordBytes : poGDS->nBands * nWordBytes;
|
|
|
|
nBlockPixels = nBlockXSize * nBlockYSize;
|
|
if ( poGDS->nBitsPerSample == 16 )
|
|
{
|
|
for( i = 0; i < nBlockPixels; i++ )
|
|
{
|
|
((GUInt32 *) pImage)[i] =
|
|
HalfToFloat( *((GUInt16 *)pabyImage) );
|
|
pabyImage += iSkipBytes;
|
|
}
|
|
}
|
|
else if ( poGDS->nBitsPerSample == 24 )
|
|
{
|
|
for( i = 0; i < nBlockPixels; i++ )
|
|
{
|
|
#ifdef CPL_MSB
|
|
((GUInt32 *) pImage)[i] =
|
|
TripleToFloat( ((GUInt32)*(pabyImage + 0) << 16)
|
|
| ((GUInt32)*(pabyImage + 1) << 8)
|
|
| (GUInt32)*(pabyImage + 2) );
|
|
#else
|
|
((GUInt32 *) pImage)[i] =
|
|
TripleToFloat( ((GUInt32)*(pabyImage + 2) << 16)
|
|
| ((GUInt32)*(pabyImage + 1) << 8)
|
|
| (GUInt32)*pabyImage );
|
|
#endif
|
|
pabyImage += iSkipBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for moving 12bit data somewhat more efficiently. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( poGDS->nBitsPerSample == 12 )
|
|
{
|
|
int iPixel, iBitOffset = 0;
|
|
int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine;
|
|
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG )
|
|
{
|
|
iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample;
|
|
iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample;
|
|
}
|
|
else
|
|
{
|
|
iPixelBitSkip = poGDS->nBitsPerSample;
|
|
iBandBitOffset = 0;
|
|
}
|
|
|
|
// bits per line rounds up to next byte boundary.
|
|
nBitsPerLine = nBlockXSize * iPixelBitSkip;
|
|
if( (nBitsPerLine & 7) != 0 )
|
|
nBitsPerLine = (nBitsPerLine + 7) & (~7);
|
|
|
|
iPixel = 0;
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
iBitOffset = iBandBitOffset + iY * nBitsPerLine;
|
|
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
int iByte = iBitOffset>>3;
|
|
|
|
if( (iBitOffset & 0x7) == 0 )
|
|
{
|
|
/* starting on byte boundary */
|
|
|
|
((GUInt16 *) pImage)[iPixel++] =
|
|
(poGDS->pabyBlockBuf[iByte] << 4)
|
|
| (poGDS->pabyBlockBuf[iByte+1] >> 4);
|
|
}
|
|
else
|
|
{
|
|
/* starting off byte boundary */
|
|
|
|
((GUInt16 *) pImage)[iPixel++] =
|
|
((poGDS->pabyBlockBuf[iByte] & 0xf) << 8)
|
|
| (poGDS->pabyBlockBuf[iByte+1]);
|
|
}
|
|
iBitOffset += iPixelBitSkip;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for 24bit data which is pre-byteswapped since */
|
|
/* the size falls on a byte boundary ... ugg (#2361). */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( poGDS->nBitsPerSample == 24 )
|
|
{
|
|
int iPixel;
|
|
int iPixelByteSkip, iBandByteOffset, iX, iY, nBytesPerLine;
|
|
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG )
|
|
{
|
|
iPixelByteSkip = (poGDS->nBands * poGDS->nBitsPerSample) / 8;
|
|
iBandByteOffset = ((nBand-1) * poGDS->nBitsPerSample) / 8;
|
|
}
|
|
else
|
|
{
|
|
iPixelByteSkip = poGDS->nBitsPerSample / 8;
|
|
iBandByteOffset = 0;
|
|
}
|
|
|
|
nBytesPerLine = nBlockXSize * iPixelByteSkip;
|
|
|
|
iPixel = 0;
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
GByte *pabyImage =
|
|
poGDS->pabyBlockBuf + iBandByteOffset + iY * nBytesPerLine;
|
|
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
#ifdef CPL_MSB
|
|
((GUInt32 *) pImage)[iPixel++] =
|
|
((GUInt32)*(pabyImage + 2) << 16)
|
|
| ((GUInt32)*(pabyImage + 1) << 8)
|
|
| (GUInt32)*(pabyImage + 0);
|
|
#else
|
|
((GUInt32 *) pImage)[iPixel++] =
|
|
((GUInt32)*(pabyImage + 0) << 16)
|
|
| ((GUInt32)*(pabyImage + 1) << 8)
|
|
| (GUInt32)*(pabyImage + 2);
|
|
#endif
|
|
pabyImage += iPixelByteSkip;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle 1-32 bit integer data. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
int iBit, iPixel, iBitOffset = 0;
|
|
int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine;
|
|
|
|
if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG )
|
|
{
|
|
iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample;
|
|
iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample;
|
|
}
|
|
else
|
|
{
|
|
iPixelBitSkip = poGDS->nBitsPerSample;
|
|
iBandBitOffset = 0;
|
|
}
|
|
|
|
// bits per line rounds up to next byte boundary.
|
|
nBitsPerLine = nBlockXSize * iPixelBitSkip;
|
|
if( (nBitsPerLine & 7) != 0 )
|
|
nBitsPerLine = (nBitsPerLine + 7) & (~7);
|
|
|
|
register GByte *pabyBlockBuf = poGDS->pabyBlockBuf;
|
|
iPixel = 0;
|
|
|
|
for( iY = 0; iY < nBlockYSize; iY++ )
|
|
{
|
|
iBitOffset = iBandBitOffset + iY * nBitsPerLine;
|
|
|
|
for( iX = 0; iX < nBlockXSize; iX++ )
|
|
{
|
|
int nOutWord = 0;
|
|
|
|
for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ )
|
|
{
|
|
if( pabyBlockBuf[iBitOffset>>3]
|
|
& (0x80 >>(iBitOffset & 7)) )
|
|
nOutWord |= (1 << (poGDS->nBitsPerSample - 1 - iBit));
|
|
iBitOffset++;
|
|
}
|
|
|
|
iBitOffset= iBitOffset + iPixelBitSkip - poGDS->nBitsPerSample;
|
|
|
|
if( eDataType == GDT_Byte )
|
|
((GByte *) pImage)[iPixel++] = nOutWord;
|
|
else if( eDataType == GDT_UInt16 )
|
|
((GUInt16 *) pImage)[iPixel++] = nOutWord;
|
|
else if( eDataType == GDT_UInt32 )
|
|
((GUInt32 *) pImage)[iPixel++] = nOutWord;
|
|
else
|
|
CPLAssert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffBitmapBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffBitmapBand : public GTiffOddBitsBand
|
|
{
|
|
friend class GTiffDataset;
|
|
|
|
GDALColorTable *poColorTable;
|
|
|
|
public:
|
|
|
|
GTiffBitmapBand( GTiffDataset *, int );
|
|
virtual ~GTiffBitmapBand();
|
|
|
|
virtual GDALColorInterp GetColorInterpretation();
|
|
virtual GDALColorTable *GetColorTable();
|
|
};
|
|
|
|
|
|
/************************************************************************/
|
|
/* GTiffBitmapBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffBitmapBand::GTiffBitmapBand( GTiffDataset *poDS, int nBand )
|
|
: GTiffOddBitsBand( poDS, nBand )
|
|
|
|
{
|
|
eDataType = GDT_Byte;
|
|
|
|
if( poDS->poColorTable != NULL )
|
|
poColorTable = poDS->poColorTable->Clone();
|
|
else
|
|
{
|
|
GDALColorEntry oWhite, oBlack;
|
|
|
|
oWhite.c1 = 255;
|
|
oWhite.c2 = 255;
|
|
oWhite.c3 = 255;
|
|
oWhite.c4 = 255;
|
|
|
|
oBlack.c1 = 0;
|
|
oBlack.c2 = 0;
|
|
oBlack.c3 = 0;
|
|
oBlack.c4 = 255;
|
|
|
|
poColorTable = new GDALColorTable();
|
|
|
|
if( poDS->nPhotometric == PHOTOMETRIC_MINISWHITE )
|
|
{
|
|
poColorTable->SetColorEntry( 0, &oWhite );
|
|
poColorTable->SetColorEntry( 1, &oBlack );
|
|
}
|
|
else
|
|
{
|
|
poColorTable->SetColorEntry( 0, &oBlack );
|
|
poColorTable->SetColorEntry( 1, &oWhite );
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GTiffBitmapBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffBitmapBand::~GTiffBitmapBand()
|
|
|
|
{
|
|
delete poColorTable;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp GTiffBitmapBand::GetColorInterpretation()
|
|
|
|
{
|
|
return GCI_PaletteIndex;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorTable() */
|
|
/************************************************************************/
|
|
|
|
GDALColorTable *GTiffBitmapBand::GetColorTable()
|
|
|
|
{
|
|
return poColorTable;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffSplitBitmapBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class GTiffSplitBitmapBand : public GTiffBitmapBand
|
|
{
|
|
friend class GTiffDataset;
|
|
|
|
int nLastLineRead;
|
|
|
|
public:
|
|
|
|
GTiffSplitBitmapBand( GTiffDataset *, int );
|
|
virtual ~GTiffSplitBitmapBand();
|
|
|
|
virtual CPLErr IReadBlock( int, int, void * );
|
|
virtual CPLErr IWriteBlock( int, int, void * );
|
|
};
|
|
|
|
|
|
/************************************************************************/
|
|
/* GTiffBitmapBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffSplitBitmapBand::GTiffSplitBitmapBand( GTiffDataset *poDS, int nBand )
|
|
: GTiffBitmapBand( poDS, nBand )
|
|
|
|
{
|
|
nBlockXSize = poDS->GetRasterXSize();
|
|
nBlockYSize = 1;
|
|
|
|
nLastLineRead = -1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffBitmapBand() */
|
|
/************************************************************************/
|
|
|
|
GTiffSplitBitmapBand::~GTiffSplitBitmapBand()
|
|
|
|
{
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffSplitBitmapBand::IReadBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
GByte *pabyLineBuf;
|
|
|
|
poGDS->SetDirectory();
|
|
|
|
pabyLineBuf = (GByte *) CPLMalloc(TIFFScanlineSize(poGDS->hTIFF));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read through to target scanline. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nLastLineRead >= nBlockYOff )
|
|
nLastLineRead = -1;
|
|
|
|
while( nLastLineRead < nBlockYOff )
|
|
{
|
|
if( TIFFReadScanline( poGDS->hTIFF, pabyLineBuf, ++nLastLineRead, 0 ) == -1 )
|
|
{
|
|
CPLFree( pabyLineBuf );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadScanline() failed." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Translate 1bit data to eight bit. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iPixel, iSrcOffset=0, iDstOffset=0;
|
|
|
|
for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset++ )
|
|
{
|
|
if( pabyLineBuf[iSrcOffset >>3] & (0x80 >> (iSrcOffset & 0x7)) )
|
|
((GByte *) pImage)[iDstOffset++] = 1;
|
|
else
|
|
((GByte *) pImage)[iDstOffset++] = 0;
|
|
}
|
|
|
|
CPLFree( pabyLineBuf );
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffSplitBitmapBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Split bitmap bands are read-only." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GTiffDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
|
|
/************************************************************************/
|
|
/* GTiffDataset() */
|
|
/************************************************************************/
|
|
|
|
GTiffDataset::GTiffDataset()
|
|
|
|
{
|
|
nLoadedBlock = -1;
|
|
bLoadedBlockDirty = FALSE;
|
|
pabyBlockBuf = NULL;
|
|
hTIFF = NULL;
|
|
bNewDataset = FALSE;
|
|
bMetadataChanged = FALSE;
|
|
bGeoTIFFInfoChanged = FALSE;
|
|
bCrystalized = TRUE;
|
|
poColorTable = NULL;
|
|
bColorTableChanged = FALSE;
|
|
bNoDataSet = FALSE;
|
|
bNoDataChanged = FALSE;
|
|
dfNoDataValue = -9999.0;
|
|
pszProjection = CPLStrdup("");
|
|
bBase = TRUE;
|
|
bCloseTIFFHandle = FALSE;
|
|
bTreatAsRGBA = FALSE;
|
|
nOverviewCount = 0;
|
|
papoOverviewDS = NULL;
|
|
nDirOffset = 0;
|
|
|
|
bGeoTransformValid = FALSE;
|
|
adfGeoTransform[0] = 0.0;
|
|
adfGeoTransform[1] = 1.0;
|
|
adfGeoTransform[2] = 0.0;
|
|
adfGeoTransform[3] = 0.0;
|
|
adfGeoTransform[4] = 0.0;
|
|
adfGeoTransform[5] = 1.0;
|
|
|
|
nGCPCount = 0;
|
|
pasGCPList = NULL;
|
|
|
|
osProfile = "GDALGeoTIFF";
|
|
|
|
papszCreationOptions = NULL;
|
|
|
|
nTempWriteBufferSize = 0;
|
|
pabyTempWriteBuffer = NULL;
|
|
|
|
poMaskDS = NULL;
|
|
poBaseDS = NULL;
|
|
|
|
bFillEmptyTiles = FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GTiffDataset() */
|
|
/************************************************************************/
|
|
|
|
GTiffDataset::~GTiffDataset()
|
|
|
|
{
|
|
Crystalize();
|
|
|
|
FlushCache();
|
|
|
|
if( bFillEmptyTiles )
|
|
{
|
|
FillEmptyTiles();
|
|
bFillEmptyTiles = FALSE;
|
|
}
|
|
|
|
if( bBase )
|
|
{
|
|
for( int i = 0; i < nOverviewCount; i++ )
|
|
{
|
|
delete papoOverviewDS[i];
|
|
}
|
|
}
|
|
|
|
/* If we are a mask dataset, we can have overviews, but we don't */
|
|
/* own them. We can only free the array, not the overviews themselves */
|
|
CPLFree( papoOverviewDS );
|
|
|
|
/* poMaskDS is owned by the main image and the overviews */
|
|
/* so because of the latter case, we can delete it even if */
|
|
/* we are not the base image */
|
|
if (poMaskDS)
|
|
delete poMaskDS;
|
|
|
|
SetDirectory();
|
|
|
|
if( GetAccess() == GA_Update && bBase )
|
|
{
|
|
if( bNewDataset || bMetadataChanged )
|
|
WriteMetadata( this, hTIFF, TRUE, osProfile, osFilename,
|
|
papszCreationOptions );
|
|
|
|
if( bNewDataset || bGeoTIFFInfoChanged )
|
|
WriteGeoTIFFInfo();
|
|
|
|
if( bNoDataChanged )
|
|
WriteNoDataValue( hTIFF, dfNoDataValue );
|
|
|
|
if( bNewDataset || bMetadataChanged || bGeoTIFFInfoChanged
|
|
|| bNoDataChanged || bColorTableChanged )
|
|
{
|
|
#if defined(TIFFLIB_VERSION)
|
|
#if TIFFLIB_VERSION > 20010925 && TIFFLIB_VERSION != 20011807
|
|
TIFFRewriteDirectory( hTIFF );
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if( poColorTable != NULL )
|
|
delete poColorTable;
|
|
|
|
if( bBase || bCloseTIFFHandle )
|
|
{
|
|
XTIFFClose( hTIFF );
|
|
}
|
|
|
|
if( nGCPCount > 0 )
|
|
{
|
|
GDALDeinitGCPs( nGCPCount, pasGCPList );
|
|
CPLFree( pasGCPList );
|
|
}
|
|
|
|
CPLFree( pszProjection );
|
|
|
|
CSLDestroy( papszCreationOptions );
|
|
|
|
CPLFree(pabyTempWriteBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FillEmptyTiles() */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::FillEmptyTiles()
|
|
|
|
{
|
|
toff_t *panByteCounts = NULL;
|
|
int nBlockCount, iBlock;
|
|
|
|
SetDirectory();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* How many blocks are there in this file? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nPlanarConfig == PLANARCONFIG_SEPARATE )
|
|
nBlockCount = nBlocksPerBand * nBands;
|
|
else
|
|
nBlockCount = nBlocksPerBand;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch block maps. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFIsTiled( hTIFF ) )
|
|
TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts );
|
|
else
|
|
TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Prepare a blank data buffer to write for uninitialized blocks. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBlockBytes;
|
|
|
|
if( TIFFIsTiled( hTIFF ) )
|
|
nBlockBytes = TIFFTileSize(hTIFF);
|
|
else
|
|
nBlockBytes = TIFFStripSize(hTIFF);
|
|
|
|
GByte *pabyData = (GByte *) CPLCalloc(nBlockBytes,1);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check all blocks, writing out data for uninitialized blocks. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iBlock = 0; iBlock < nBlockCount; iBlock++ )
|
|
{
|
|
if( panByteCounts[iBlock] == 0 )
|
|
WriteEncodedTileOrStrip( iBlock, pabyData, FALSE );
|
|
}
|
|
|
|
CPLFree( pabyData );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteEncodedTile() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::WriteEncodedTile(uint32 tile, void* data,
|
|
int bPreserveDataBuffer)
|
|
{
|
|
/* TIFFWriteEncodedTile can alter the passed buffer if byte-swapping is necessary */
|
|
/* so we use a temporary buffer before calling it */
|
|
int cc = TIFFTileSize( hTIFF );
|
|
if (bPreserveDataBuffer && TIFFIsByteSwapped(hTIFF))
|
|
{
|
|
if (cc != nTempWriteBufferSize)
|
|
{
|
|
pabyTempWriteBuffer = CPLRealloc(pabyTempWriteBuffer, cc);
|
|
nTempWriteBufferSize = cc;
|
|
}
|
|
memcpy(pabyTempWriteBuffer, data, cc);
|
|
return TIFFWriteEncodedTile(hTIFF, tile, pabyTempWriteBuffer, cc);
|
|
}
|
|
else
|
|
return TIFFWriteEncodedTile(hTIFF, tile, data, cc);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteEncodedStrip() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::WriteEncodedStrip(uint32 strip, void* data,
|
|
int bPreserveDataBuffer)
|
|
{
|
|
/* TIFFWriteEncodedStrip can alter the passed buffer if byte-swapping is necessary */
|
|
/* so we use a temporary buffer before calling it */
|
|
int cc = TIFFStripSize( hTIFF );
|
|
if (bPreserveDataBuffer && TIFFIsByteSwapped(hTIFF))
|
|
{
|
|
if (cc != nTempWriteBufferSize)
|
|
{
|
|
pabyTempWriteBuffer = CPLRealloc(pabyTempWriteBuffer, cc);
|
|
nTempWriteBufferSize = cc;
|
|
}
|
|
memcpy(pabyTempWriteBuffer, data, cc);
|
|
return TIFFWriteEncodedStrip(hTIFF, strip, pabyTempWriteBuffer, cc);
|
|
}
|
|
else
|
|
return TIFFWriteEncodedStrip(hTIFF, strip, data, cc);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteEncodedTileOrStrip() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32 tile_or_strip, void* data,
|
|
int bPreserveDataBuffer)
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
if( TIFFIsTiled( hTIFF ) )
|
|
{
|
|
if( WriteEncodedTile(tile_or_strip, data, bPreserveDataBuffer) == -1 )
|
|
{
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( WriteEncodedStrip(tile_or_strip, data, bPreserveDataBuffer) == -1 )
|
|
{
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FlushBlockBuf() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::FlushBlockBuf()
|
|
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
if( nLoadedBlock < 0 || !bLoadedBlockDirty )
|
|
return CE_None;
|
|
|
|
bLoadedBlockDirty = FALSE;
|
|
|
|
SetDirectory();
|
|
|
|
eErr = WriteEncodedTileOrStrip(nLoadedBlock, pabyBlockBuf, TRUE);
|
|
if (eErr != CE_None)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"WriteEncodedTile/Strip() failed." );
|
|
}
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* LoadBlockBuf() */
|
|
/* */
|
|
/* Load working block buffer with request block (tile/strip). */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::LoadBlockBuf( int nBlockId, int bReadFromDisk )
|
|
|
|
{
|
|
int nBlockBufSize;
|
|
CPLErr eErr = CE_None;
|
|
|
|
if( nLoadedBlock == nBlockId )
|
|
return CE_None;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we have a dirty loaded block, flush it out first. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nLoadedBlock != -1 && bLoadedBlockDirty )
|
|
{
|
|
eErr = FlushBlockBuf();
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get block size. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFIsTiled(hTIFF) )
|
|
nBlockBufSize = TIFFTileSize( hTIFF );
|
|
else
|
|
nBlockBufSize = TIFFStripSize( hTIFF );
|
|
|
|
if ( !nBlockBufSize )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Bogus block size; unable to allocate a buffer.");
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate a temporary buffer for this strip. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pabyBlockBuf == NULL )
|
|
{
|
|
pabyBlockBuf = (GByte *) VSICalloc( 1, nBlockBufSize );
|
|
if( pabyBlockBuf == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"Unable to allocate %d bytes for a temporary strip "
|
|
"buffer in GTIFF driver.",
|
|
nBlockBufSize );
|
|
|
|
return( CE_Failure );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* The bottom most partial tiles and strips are sometimes only */
|
|
/* partially encoded. This code reduces the requested data so */
|
|
/* an error won't be reported in this case. (#1179) */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBlockReqSize = nBlockBufSize;
|
|
int nBlocksPerRow = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
|
|
int nBlockYOff = (nBlockId % nBlocksPerBand) / nBlocksPerRow;
|
|
|
|
if( (int)((nBlockYOff+1) * nBlockYSize) > nRasterYSize )
|
|
{
|
|
nBlockReqSize = (nBlockBufSize / nBlockYSize)
|
|
* (nBlockYSize - (((nBlockYOff+1) * nBlockYSize) % nRasterYSize));
|
|
memset( pabyBlockBuf, 0, nBlockBufSize );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have this block already loaded, and we know it */
|
|
/* doesn't yet exist on disk, just zero the memory buffer and */
|
|
/* pretend we loaded it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !IsBlockAvailable( nBlockId ) )
|
|
{
|
|
memset( pabyBlockBuf, 0, nBlockBufSize );
|
|
nLoadedBlock = nBlockId;
|
|
return CE_None;
|
|
}
|
|
|
|
if( !bReadFromDisk )
|
|
{
|
|
nLoadedBlock = nBlockId;
|
|
return CE_None;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load the block, if it isn't our current block. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFIsTiled( hTIFF ) )
|
|
{
|
|
if( TIFFReadEncodedTile(hTIFF, nBlockId, pabyBlockBuf,
|
|
nBlockReqSize) == -1 )
|
|
{
|
|
/* Once TIFFError() is properly hooked, this can go away */
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadEncodedTile() failed." );
|
|
|
|
memset( pabyBlockBuf, 0, nBlockBufSize );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( TIFFReadEncodedStrip(hTIFF, nBlockId, pabyBlockBuf,
|
|
nBlockReqSize) == -1 )
|
|
{
|
|
/* Once TIFFError() is properly hooked, this can go away */
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"TIFFReadEncodedStrip() failed." );
|
|
|
|
memset( pabyBlockBuf, 0, nBlockBufSize );
|
|
|
|
eErr = CE_Failure;
|
|
}
|
|
}
|
|
|
|
nLoadedBlock = nBlockId;
|
|
bLoadedBlockDirty = FALSE;
|
|
|
|
return eErr;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Crystalize() */
|
|
/* */
|
|
/* Make sure that the directory information is written out for */
|
|
/* a new file, require before writing any imagery data. */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::Crystalize()
|
|
|
|
{
|
|
if( !bCrystalized )
|
|
{
|
|
if( bNewDataset || bMetadataChanged )
|
|
WriteMetadata( this, hTIFF, TRUE, osProfile, osFilename,
|
|
papszCreationOptions );
|
|
|
|
bCrystalized = TRUE;
|
|
|
|
TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTiffDataset::Crystalize");
|
|
|
|
// Keep zip and tiff quality, and jpegcolormode which get reset when we call
|
|
// TIFFWriteDirectory
|
|
int jquality = -1, zquality = -1, nColorMode = -1;
|
|
TIFFGetField(hTIFF, TIFFTAG_JPEGQUALITY, &jquality);
|
|
TIFFGetField(hTIFF, TIFFTAG_ZIPQUALITY, &zquality);
|
|
TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode );
|
|
|
|
TIFFWriteDirectory( hTIFF );
|
|
TIFFSetDirectory( hTIFF, 0 );
|
|
|
|
// Now, reset zip and tiff quality and jpegcolormode.
|
|
if(jquality > 0)
|
|
TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, jquality);
|
|
if(zquality > 0)
|
|
TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, zquality);
|
|
if (nColorMode >= 0)
|
|
TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, nColorMode);
|
|
|
|
nDirOffset = TIFFCurrentDirOffset( hTIFF );
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* IsBlockAvailable() */
|
|
/* */
|
|
/* Return TRUE if the indicated strip/tile is available. We */
|
|
/* establish this by testing if the stripbytecount is zero. If */
|
|
/* zero then the block has never been committed to disk. */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::IsBlockAvailable( int nBlockId )
|
|
|
|
{
|
|
toff_t *panByteCounts = NULL;
|
|
|
|
if( ( TIFFIsTiled( hTIFF )
|
|
&& TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ) )
|
|
|| ( !TIFFIsTiled( hTIFF )
|
|
&& TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) ) )
|
|
{
|
|
if( panByteCounts == NULL )
|
|
return FALSE;
|
|
else
|
|
return panByteCounts[nBlockId] != 0;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FlushCache() */
|
|
/* */
|
|
/* We override this so we can also flush out local tiff strip */
|
|
/* cache if need be. */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::FlushCache()
|
|
|
|
{
|
|
GDALPamDataset::FlushCache();
|
|
|
|
if( bLoadedBlockDirty && nLoadedBlock != -1 )
|
|
FlushBlockBuf();
|
|
|
|
CPLFree( pabyBlockBuf );
|
|
pabyBlockBuf = NULL;
|
|
nLoadedBlock = -1;
|
|
bLoadedBlockDirty = FALSE;
|
|
|
|
TIFFFlush( hTIFF );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TIFF_OvLevelAdjust() */
|
|
/* */
|
|
/* Some overview levels cannot be achieved closely enough to be */
|
|
/* recognised as the desired overview level. This function */
|
|
/* will adjust an overview level to one that is achievable on */
|
|
/* the given raster size. */
|
|
/* */
|
|
/* For instance a 1200x1200 image on which a 256 level overview */
|
|
/* is request will end up generating a 5x5 overview. However, */
|
|
/* this will appear to the system be a level 240 overview. */
|
|
/* This function will adjust 256 to 240 based on knowledge of */
|
|
/* the image size. */
|
|
/* */
|
|
/* This is a copy of the GDALOvLevelAdjust() function in */
|
|
/* gdaldefaultoverviews.cpp. */
|
|
/************************************************************************/
|
|
|
|
static int TIFF_OvLevelAdjust( int nOvLevel, int nXSize )
|
|
|
|
{
|
|
int nOXSize = (nXSize + nOvLevel - 1) / nOvLevel;
|
|
|
|
return (int) (0.5 + nXSize / (double) nOXSize);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IBuildOverviews() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::IBuildOverviews(
|
|
const char * pszResampling,
|
|
int nOverviews, int * panOverviewList,
|
|
int nBands, int * panBandList,
|
|
GDALProgressFunc pfnProgress, void * pProgressData )
|
|
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
int i;
|
|
GTiffDataset *poODS;
|
|
|
|
if( !pfnProgress( 0.0, NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
return CE_Failure;
|
|
}
|
|
|
|
SetDirectory();
|
|
|
|
TIFFFlush( hTIFF );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have read access, then create the overviews externally.*/
|
|
/* -------------------------------------------------------------------- */
|
|
if( GetAccess() != GA_Update )
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"File open for read-only accessing, "
|
|
"creating overviews externally." );
|
|
|
|
return GDALDataset::IBuildOverviews(
|
|
pszResampling, nOverviews, panOverviewList,
|
|
nBands, panBandList, pfnProgress, pProgressData );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If RRD overviews requested, then invoke generic handling. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CSLTestBoolean(CPLGetConfigOption( "USE_RRD", "NO" )) )
|
|
{
|
|
return GDALDataset::IBuildOverviews(
|
|
pszResampling, nOverviews, panOverviewList,
|
|
nBands, panBandList, pfnProgress, pProgressData );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Our TIFF overview support currently only works safely if all */
|
|
/* bands are handled at the same time. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBands != GetRasterCount() )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"Generation of overviews in TIFF currently only"
|
|
" supported when operating on all bands.\n"
|
|
"Operation failed.\n" );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we are averaging bit data to grayscale we need to create */
|
|
/* 8bit overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nOvBitsPerSample = nBitsPerSample;
|
|
|
|
if( EQUALN(pszResampling,"AVERAGE_BIT2",12) )
|
|
nOvBitsPerSample = 8;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a palette? If so, create a TIFF compatible version. */
|
|
/* -------------------------------------------------------------------- */
|
|
std::vector<unsigned short> anTRed, anTGreen, anTBlue;
|
|
unsigned short *panRed=NULL, *panGreen=NULL, *panBlue=NULL;
|
|
|
|
if( nPhotometric == PHOTOMETRIC_PALETTE && poColorTable != NULL )
|
|
{
|
|
int nColors;
|
|
|
|
if( nOvBitsPerSample == 8 )
|
|
nColors = 256;
|
|
else if( nOvBitsPerSample < 8 )
|
|
nColors = 1 << nOvBitsPerSample;
|
|
else
|
|
nColors = 65536;
|
|
|
|
anTRed.resize(nColors,0);
|
|
anTGreen.resize(nColors,0);
|
|
anTBlue.resize(nColors,0);
|
|
|
|
for( int iColor = 0; iColor < nColors; iColor++ )
|
|
{
|
|
if( iColor < poColorTable->GetColorEntryCount() )
|
|
{
|
|
GDALColorEntry sRGB;
|
|
|
|
poColorTable->GetColorEntryAsRGB( iColor, &sRGB );
|
|
|
|
anTRed[iColor] = (unsigned short) (256 * sRGB.c1);
|
|
anTGreen[iColor] = (unsigned short) (256 * sRGB.c2);
|
|
anTBlue[iColor] = (unsigned short) (256 * sRGB.c3);
|
|
}
|
|
else
|
|
{
|
|
anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0;
|
|
}
|
|
}
|
|
|
|
panRed = &(anTRed[0]);
|
|
panGreen = &(anTGreen[0]);
|
|
panBlue = &(anTBlue[0]);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we need some metadata for the overviews? */
|
|
/* -------------------------------------------------------------------- */
|
|
/*CPLString */ const char *osMetadata;
|
|
|
|
GTIFFBuildOverviewMetadata( pszResampling, this, (CPLString &)osMetadata );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch extra sample tag */
|
|
/* -------------------------------------------------------------------- */
|
|
uint16 *panExtraSampleValues = NULL;
|
|
uint16 nExtraSamples = 0;
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples, &panExtraSampleValues) )
|
|
{
|
|
uint16* panExtraSampleValuesNew = (uint16*) CPLMalloc(nExtraSamples * sizeof(uint16));
|
|
memcpy(panExtraSampleValuesNew, panExtraSampleValues, nExtraSamples * sizeof(uint16));
|
|
panExtraSampleValues = panExtraSampleValuesNew;
|
|
}
|
|
else
|
|
{
|
|
panExtraSampleValues = NULL;
|
|
nExtraSamples = 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Establish which of the overview levels we already have, and */
|
|
/* which are new. We assume that band 1 of the file is */
|
|
/* representative. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; i < nOverviews && eErr == CE_None; i++ )
|
|
{
|
|
int j;
|
|
|
|
for( j = 0; j < nOverviewCount; j++ )
|
|
{
|
|
int nOvFactor;
|
|
|
|
poODS = papoOverviewDS[j];
|
|
|
|
nOvFactor = (int)
|
|
(0.5 + GetRasterXSize() / (double) poODS->GetRasterXSize());
|
|
|
|
if( nOvFactor == panOverviewList[i]
|
|
|| nOvFactor == TIFF_OvLevelAdjust( panOverviewList[i],
|
|
GetRasterXSize() ) )
|
|
panOverviewList[i] *= -1;
|
|
}
|
|
|
|
if( panOverviewList[i] > 0 )
|
|
{
|
|
toff_t nOverviewOffset;
|
|
int nOXSize, nOYSize;
|
|
|
|
nOXSize = (GetRasterXSize() + panOverviewList[i] - 1)
|
|
/ panOverviewList[i];
|
|
nOYSize = (GetRasterYSize() + panOverviewList[i] - 1)
|
|
/ panOverviewList[i];
|
|
|
|
nOverviewOffset =
|
|
GTIFFWriteDirectory(hTIFF, FILETYPE_REDUCEDIMAGE,
|
|
nOXSize, nOYSize,
|
|
nOvBitsPerSample, nPlanarConfig,
|
|
nSamplesPerPixel, 128, 128, TRUE,
|
|
nCompression, nPhotometric, nSampleFormat,
|
|
panRed, panGreen, panBlue,
|
|
nExtraSamples, panExtraSampleValues,
|
|
osMetadata );
|
|
|
|
if( nOverviewOffset == 0 )
|
|
{
|
|
eErr = CE_Failure;
|
|
continue;
|
|
}
|
|
|
|
poODS = new GTiffDataset();
|
|
if( poODS->OpenOffset( hTIFF, nOverviewOffset, FALSE,
|
|
GA_Update ) != CE_None )
|
|
{
|
|
delete poODS;
|
|
eErr = CE_Failure;
|
|
}
|
|
else
|
|
{
|
|
nOverviewCount++;
|
|
papoOverviewDS = (GTiffDataset **)
|
|
CPLRealloc(papoOverviewDS,
|
|
nOverviewCount * (sizeof(void*)));
|
|
papoOverviewDS[nOverviewCount-1] = poODS;
|
|
poODS->poBaseDS = this;
|
|
}
|
|
}
|
|
else
|
|
panOverviewList[i] *= -1;
|
|
}
|
|
|
|
CPLFree(panExtraSampleValues);
|
|
panExtraSampleValues = NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create overviews for the mask. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (poMaskDS != NULL &&
|
|
poMaskDS->GetRasterCount() == 1 &&
|
|
CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "NO")))
|
|
{
|
|
for( i = 0; i < nOverviewCount; i++ )
|
|
{
|
|
if (papoOverviewDS[i]->poMaskDS == NULL)
|
|
{
|
|
toff_t nOverviewOffset;
|
|
|
|
nOverviewOffset =
|
|
GTIFFWriteDirectory(hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK,
|
|
papoOverviewDS[i]->nRasterXSize, papoOverviewDS[i]->nRasterYSize,
|
|
1, PLANARCONFIG_CONTIG,
|
|
1, 128, 128, TRUE,
|
|
COMPRESSION_NONE, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT,
|
|
NULL, NULL, NULL, 0, NULL,
|
|
"" );
|
|
|
|
if( nOverviewOffset == 0 )
|
|
{
|
|
eErr = CE_Failure;
|
|
continue;
|
|
}
|
|
|
|
poODS = new GTiffDataset();
|
|
if( poODS->OpenOffset( hTIFF, nOverviewOffset, FALSE,
|
|
GA_Update ) != CE_None )
|
|
{
|
|
delete poODS;
|
|
eErr = CE_Failure;
|
|
}
|
|
else
|
|
{
|
|
poODS->poBaseDS = this;
|
|
papoOverviewDS[i]->poMaskDS = poODS;
|
|
poMaskDS->nOverviewCount++;
|
|
poMaskDS->papoOverviewDS = (GTiffDataset **)
|
|
CPLRealloc(poMaskDS->papoOverviewDS,
|
|
poMaskDS->nOverviewCount * (sizeof(void*)));
|
|
poMaskDS->papoOverviewDS[poMaskDS->nOverviewCount-1] = poODS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Refresh overviews for the mask */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poMaskDS != NULL &&
|
|
poMaskDS->GetRasterCount() == 1)
|
|
{
|
|
GDALRasterBand **papoOverviewBands;
|
|
int nMaskOverviews = 0;
|
|
|
|
papoOverviewBands = (GDALRasterBand **) CPLCalloc(sizeof(void*),nOverviewCount);
|
|
for( i = 0; i < nOverviewCount; i++ )
|
|
{
|
|
if (papoOverviewDS[i]->poMaskDS != NULL)
|
|
{
|
|
papoOverviewBands[nMaskOverviews ++] =
|
|
papoOverviewDS[i]->poMaskDS->GetRasterBand(1);
|
|
}
|
|
}
|
|
eErr = GDALRegenerateOverviews( (GDALRasterBandH)
|
|
poMaskDS->GetRasterBand(1),
|
|
nMaskOverviews,
|
|
(GDALRasterBandH *) papoOverviewBands,
|
|
pszResampling, GDALDummyProgress, NULL);
|
|
CPLFree(papoOverviewBands);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Refresh old overviews that were listed. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCompression != COMPRESSION_NONE &&
|
|
nPlanarConfig == PLANARCONFIG_CONTIG &&
|
|
GDALDataTypeIsComplex(GetRasterBand( panBandList[0] )->GetRasterDataType()) == FALSE &&
|
|
GetRasterBand( panBandList[0] )->GetColorTable() == NULL &&
|
|
(EQUALN(pszResampling, "NEAR", 4) || EQUAL(pszResampling, "AVERAGE") || EQUAL(pszResampling, "GAUSS")))
|
|
{
|
|
/* In the case of pixel interleaved compressed overviews, we want to generate */
|
|
/* the overviews for all the bands block by block, and not band after band, */
|
|
/* in order to write the block once and not loose space in the TIFF file */
|
|
|
|
GDALRasterBand ***papapoOverviewBands;
|
|
GDALRasterBand **papoBandList;
|
|
|
|
int nNewOverviews = 0;
|
|
int iBand;
|
|
|
|
papapoOverviewBands = (GDALRasterBand ***) CPLCalloc(sizeof(void*),nBands);
|
|
papoBandList = (GDALRasterBand **) CPLCalloc(sizeof(void*),nBands);
|
|
for( iBand = 0; iBand < nBands; iBand++ )
|
|
{
|
|
GDALRasterBand* poBand = GetRasterBand( panBandList[iBand] );
|
|
|
|
papoBandList[iBand] = poBand;
|
|
papapoOverviewBands[iBand] = (GDALRasterBand **) CPLCalloc(sizeof(void*), poBand->GetOverviewCount());
|
|
|
|
int iCurOverview = 0;
|
|
for( i = 0; i < nOverviews; i++ )
|
|
{
|
|
int j;
|
|
|
|
for( j = 0; j < poBand->GetOverviewCount(); j++ )
|
|
{
|
|
int nOvFactor;
|
|
GDALRasterBand * poOverview = poBand->GetOverview( j );
|
|
|
|
nOvFactor = (int)
|
|
(0.5 + poBand->GetXSize() / (double) poOverview->GetXSize());
|
|
|
|
int bHasNoData;
|
|
double noDataValue = poBand->GetNoDataValue(&bHasNoData);
|
|
|
|
if (bHasNoData)
|
|
poOverview->SetNoDataValue(noDataValue);
|
|
|
|
if( nOvFactor == panOverviewList[i]
|
|
|| nOvFactor == TIFF_OvLevelAdjust( panOverviewList[i],
|
|
poBand->GetXSize() ) )
|
|
{
|
|
papapoOverviewBands[iBand][iCurOverview] = poOverview;
|
|
iCurOverview++ ;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nNewOverviews == 0)
|
|
nNewOverviews = iCurOverview;
|
|
else if (nNewOverviews != iCurOverview)
|
|
{
|
|
CPLAssert(0);
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
GDALRegenerateOverviewsMultiBand(nBands, papoBandList,
|
|
nNewOverviews, papapoOverviewBands,
|
|
pszResampling, pfnProgress, pProgressData );
|
|
|
|
for( iBand = 0; iBand < nBands; iBand++ )
|
|
{
|
|
CPLFree(papapoOverviewBands[iBand]);
|
|
}
|
|
CPLFree(papapoOverviewBands);
|
|
CPLFree(papoBandList);
|
|
}
|
|
else
|
|
{
|
|
GDALRasterBand **papoOverviewBands;
|
|
|
|
papoOverviewBands = (GDALRasterBand **)
|
|
CPLCalloc(sizeof(void*),nOverviews);
|
|
|
|
for( int iBand = 0; iBand < nBands && eErr == CE_None; iBand++ )
|
|
{
|
|
GDALRasterBand *poBand;
|
|
int nNewOverviews;
|
|
|
|
poBand = GetRasterBand( panBandList[iBand] );
|
|
|
|
nNewOverviews = 0;
|
|
for( i = 0; i < nOverviews && poBand != NULL; i++ )
|
|
{
|
|
int j;
|
|
|
|
for( j = 0; j < poBand->GetOverviewCount(); j++ )
|
|
{
|
|
int nOvFactor;
|
|
GDALRasterBand * poOverview = poBand->GetOverview( j );
|
|
|
|
int bHasNoData;
|
|
double noDataValue = poBand->GetNoDataValue(&bHasNoData);
|
|
|
|
if (bHasNoData)
|
|
poOverview->SetNoDataValue(noDataValue);
|
|
|
|
nOvFactor = (int)
|
|
(0.5 + poBand->GetXSize() / (double) poOverview->GetXSize());
|
|
|
|
if( nOvFactor == panOverviewList[i]
|
|
|| nOvFactor == TIFF_OvLevelAdjust( panOverviewList[i],
|
|
poBand->GetXSize() ) )
|
|
{
|
|
papoOverviewBands[nNewOverviews++] = poOverview;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *pScaledProgressData;
|
|
|
|
pScaledProgressData =
|
|
GDALCreateScaledProgress( iBand / (double) nBands,
|
|
(iBand+1) / (double) nBands,
|
|
pfnProgress, pProgressData );
|
|
|
|
eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBand,
|
|
nNewOverviews,
|
|
(GDALRasterBandH *) papoOverviewBands,
|
|
pszResampling,
|
|
GDALScaledProgress,
|
|
pScaledProgressData);
|
|
|
|
GDALDestroyScaledProgress( pScaledProgressData );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLFree( papoOverviewBands );
|
|
}
|
|
|
|
|
|
pfnProgress( 1.0, NULL, pProgressData );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* WriteGeoTIFFInfo() */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::WriteGeoTIFFInfo()
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the geotransform is the default, don't bother writing it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
|
|
|| adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
|
|
|| adfGeoTransform[4] != 0.0 || ABS(adfGeoTransform[5]) != 1.0 )
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write the transform. If we have a normal north-up image we */
|
|
/* use the tiepoint plus pixelscale otherwise we use a matrix. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0
|
|
&& adfGeoTransform[5] < 0.0 )
|
|
{
|
|
double adfPixelScale[3], adfTiePoints[6];
|
|
|
|
adfPixelScale[0] = adfGeoTransform[1];
|
|
adfPixelScale[1] = fabs(adfGeoTransform[5]);
|
|
adfPixelScale[2] = 0.0;
|
|
|
|
if( !EQUAL(osProfile,"BASELINE") )
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
|
|
|
|
adfTiePoints[0] = 0.0;
|
|
adfTiePoints[1] = 0.0;
|
|
adfTiePoints[2] = 0.0;
|
|
adfTiePoints[3] = adfGeoTransform[0];
|
|
adfTiePoints[4] = adfGeoTransform[3];
|
|
adfTiePoints[5] = 0.0;
|
|
|
|
if( !EQUAL(osProfile,"BASELINE") )
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
|
|
}
|
|
else
|
|
{
|
|
double adfMatrix[16];
|
|
|
|
memset(adfMatrix,0,sizeof(double) * 16);
|
|
|
|
adfMatrix[0] = adfGeoTransform[1];
|
|
adfMatrix[1] = adfGeoTransform[2];
|
|
adfMatrix[3] = adfGeoTransform[0];
|
|
adfMatrix[4] = adfGeoTransform[4];
|
|
adfMatrix[5] = adfGeoTransform[5];
|
|
adfMatrix[7] = adfGeoTransform[3];
|
|
adfMatrix[15] = 1.0;
|
|
|
|
if( !EQUAL(osProfile,"BASELINE") )
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix );
|
|
}
|
|
|
|
// Do we need a world file?
|
|
if( CSLFetchBoolean( papszCreationOptions, "TFW", FALSE )
|
|
|| CSLFetchBoolean( papszCreationOptions, "WORLDFILE", FALSE ) )
|
|
{
|
|
GDALWriteWorldFile( osFilename, "tfw", adfGeoTransform );
|
|
}
|
|
}
|
|
else if( GetGCPCount() > 0 )
|
|
{
|
|
double *padfTiePoints;
|
|
int iGCP;
|
|
|
|
padfTiePoints = (double *)
|
|
CPLMalloc( 6 * sizeof(double) * GetGCPCount() );
|
|
|
|
for( iGCP = 0; iGCP < GetGCPCount(); iGCP++ )
|
|
{
|
|
|
|
padfTiePoints[iGCP*6+0] = pasGCPList[iGCP].dfGCPPixel;
|
|
padfTiePoints[iGCP*6+1] = pasGCPList[iGCP].dfGCPLine;
|
|
padfTiePoints[iGCP*6+2] = 0;
|
|
padfTiePoints[iGCP*6+3] = pasGCPList[iGCP].dfGCPX;
|
|
padfTiePoints[iGCP*6+4] = pasGCPList[iGCP].dfGCPY;
|
|
padfTiePoints[iGCP*6+5] = pasGCPList[iGCP].dfGCPZ;
|
|
}
|
|
|
|
if( !EQUAL(osProfile,"BASELINE") )
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS,
|
|
6 * GetGCPCount(), padfTiePoints );
|
|
CPLFree( padfTiePoints );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write out projection definition. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszProjection != NULL && !EQUAL( pszProjection, "" )
|
|
&& !EQUAL(osProfile,"BASELINE") )
|
|
{
|
|
GTIF *psGTIF;
|
|
|
|
// If we have existing geokeys, try to wipe them
|
|
// by writing a dummy goekey directory. (#2546)
|
|
uint16 *panVI = NULL;
|
|
uint16 nKeyCount;
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_GEOKEYDIRECTORY,
|
|
&nKeyCount, &panVI ) )
|
|
{
|
|
GUInt16 anGKVersionInfo[4] = { 1, 1, 0, 0 };
|
|
double adfDummyDoubleParams[1] = { 0.0 };
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOKEYDIRECTORY,
|
|
4, anGKVersionInfo );
|
|
TIFFSetField( hTIFF, TIFFTAG_GEODOUBLEPARAMS,
|
|
1, adfDummyDoubleParams );
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOASCIIPARAMS, "" );
|
|
}
|
|
|
|
psGTIF = GTIFNew( hTIFF );
|
|
|
|
// set according to coordinate system.
|
|
GTIFSetFromOGISDefn( psGTIF, pszProjection );
|
|
|
|
if( GetMetadataItem( GDALMD_AREA_OR_POINT )
|
|
&& EQUAL(GetMetadataItem(GDALMD_AREA_OR_POINT),
|
|
GDALMD_AOP_POINT) )
|
|
{
|
|
GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
|
|
RasterPixelIsPoint);
|
|
}
|
|
|
|
GTIFWriteKeys( psGTIF );
|
|
GTIFFree( psGTIF );
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* AppendMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
static void AppendMetadataItem( CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
|
|
const char *pszKey, const char *pszValue,
|
|
int nBand, const char *pszRole,
|
|
const char *pszDomain )
|
|
|
|
{
|
|
char szBandId[32];
|
|
CPLXMLNode *psItem;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the Item element, and subcomponents. */
|
|
/* -------------------------------------------------------------------- */
|
|
psItem = CPLCreateXMLNode( NULL, CXT_Element, "Item" );
|
|
CPLCreateXMLNode( CPLCreateXMLNode( psItem, CXT_Attribute, "name"),
|
|
CXT_Text, pszKey );
|
|
|
|
if( nBand > 0 )
|
|
{
|
|
sprintf( szBandId, "%d", nBand - 1 );
|
|
CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"sample"),
|
|
CXT_Text, szBandId );
|
|
}
|
|
|
|
if( pszRole != NULL )
|
|
CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"role"),
|
|
CXT_Text, pszRole );
|
|
|
|
if( pszDomain != NULL && strlen(pszDomain) > 0 )
|
|
CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"domain"),
|
|
CXT_Text, pszDomain );
|
|
|
|
char *pszEscapedItemValue = CPLEscapeString(pszValue,-1,CPLES_XML);
|
|
CPLCreateXMLNode( psItem, CXT_Text, pszEscapedItemValue );
|
|
CPLFree( pszEscapedItemValue );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create root, if missing. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( *ppsRoot == NULL )
|
|
*ppsRoot = CPLCreateXMLNode( NULL, CXT_Element, "GDALMetadata" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Append item to tail. We keep track of the tail to avoid */
|
|
/* O(nsquared) time as the list gets longer. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( *ppsTail == NULL )
|
|
CPLAddXMLChild( *ppsRoot, psItem );
|
|
else
|
|
CPLAddXMLSibling( *ppsTail, psItem );
|
|
|
|
*ppsTail = psItem;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteMDMDMetadata() */
|
|
/************************************************************************/
|
|
|
|
static void WriteMDMetadata( GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF,
|
|
CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail,
|
|
int nBand, const char *pszProfile )
|
|
|
|
{
|
|
int iDomain;
|
|
char **papszDomainList;
|
|
|
|
/* ==================================================================== */
|
|
/* Process each domain. */
|
|
/* ==================================================================== */
|
|
papszDomainList = poMDMD->GetDomainList();
|
|
for( iDomain = 0; papszDomainList && papszDomainList[iDomain]; iDomain++ )
|
|
{
|
|
char **papszMD = poMDMD->GetMetadata( papszDomainList[iDomain] );
|
|
int iItem;
|
|
|
|
if( EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") )
|
|
continue; // ignored
|
|
if( EQUAL(papszDomainList[iDomain], "RPC") )
|
|
continue; // handled elsewhere
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process each item in this domain. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iItem = 0; papszMD && papszMD[iItem]; iItem++ )
|
|
{
|
|
const char *pszItemValue;
|
|
char *pszItemName = NULL;
|
|
|
|
pszItemValue = CPLParseNameValue( papszMD[iItem], &pszItemName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Convert into XML item or handle as a special TIFF tag. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( strlen(papszDomainList[iDomain]) == 0
|
|
&& nBand == 0 && EQUALN(pszItemName,"TIFFTAG_",8) )
|
|
{
|
|
if( EQUAL(pszItemName,"TIFFTAG_DOCUMENTNAME") )
|
|
TIFFSetField( hTIFF, TIFFTAG_DOCUMENTNAME, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_IMAGEDESCRIPTION") )
|
|
TIFFSetField( hTIFF, TIFFTAG_IMAGEDESCRIPTION, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_SOFTWARE") )
|
|
TIFFSetField( hTIFF, TIFFTAG_SOFTWARE, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_DATETIME") )
|
|
TIFFSetField( hTIFF, TIFFTAG_DATETIME, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_ARTIST") )
|
|
TIFFSetField( hTIFF, TIFFTAG_ARTIST, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_HOSTCOMPUTER") )
|
|
TIFFSetField( hTIFF, TIFFTAG_HOSTCOMPUTER, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_COPYRIGHT") )
|
|
TIFFSetField( hTIFF, TIFFTAG_COPYRIGHT, pszItemValue );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_XRESOLUTION") )
|
|
TIFFSetField( hTIFF, TIFFTAG_XRESOLUTION, atof(pszItemValue) );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_YRESOLUTION") )
|
|
TIFFSetField( hTIFF, TIFFTAG_YRESOLUTION, atof(pszItemValue) );
|
|
else if( EQUAL(pszItemName,"TIFFTAG_RESOLUTIONUNIT") )
|
|
TIFFSetField( hTIFF, TIFFTAG_RESOLUTIONUNIT, atoi(pszItemValue) );
|
|
}
|
|
else if( nBand == 0 && EQUAL(pszItemName,GDALMD_AREA_OR_POINT) )
|
|
/* do nothing, handled elsewhere */;
|
|
else
|
|
AppendMetadataItem( ppsRoot, ppsTail,
|
|
pszItemName, pszItemValue,
|
|
nBand, NULL, papszDomainList[iDomain] );
|
|
|
|
CPLFree( pszItemName );
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteMetadata() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::WriteMetadata( GDALDataset *poSrcDS, TIFF *hTIFF,
|
|
int bSrcIsGeoTIFF,
|
|
const char *pszProfile,
|
|
const char *pszTIFFFilename,
|
|
char **papszCreationOptions,
|
|
int bExcludeRPBandIMGFileWriting)
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Convert all the remaining metadata into a simple XML */
|
|
/* format. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psRoot = NULL, *psTail = NULL;
|
|
|
|
if( bSrcIsGeoTIFF )
|
|
{
|
|
WriteMDMetadata( &(((GTiffDataset *)poSrcDS)->oGTiffMDMD),
|
|
hTIFF, &psRoot, &psTail, 0, pszProfile );
|
|
}
|
|
else
|
|
{
|
|
char **papszMD = poSrcDS->GetMetadata();
|
|
|
|
if( CSLCount(papszMD) > 0 )
|
|
{
|
|
GDALMultiDomainMetadata oMDMD;
|
|
oMDMD.SetMetadata( papszMD );
|
|
|
|
WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, 0, pszProfile );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle RPC data written to an RPB file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszRPCMD = poSrcDS->GetMetadata("RPC");
|
|
if( papszRPCMD != NULL && !bExcludeRPBandIMGFileWriting )
|
|
{
|
|
if( EQUAL(pszProfile,"GDALGeoTIFF") )
|
|
WriteRPCTag( hTIFF, papszRPCMD );
|
|
|
|
if( !EQUAL(pszProfile,"GDALGeoTIFF")
|
|
|| CSLFetchBoolean( papszCreationOptions, "RPB", FALSE ) )
|
|
{
|
|
GDALWriteRPBFile( pszTIFFFilename, papszRPCMD );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle metadata data written to an IMD file. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszIMDMD = poSrcDS->GetMetadata("IMD");
|
|
if( papszIMDMD != NULL && !bExcludeRPBandIMGFileWriting)
|
|
{
|
|
GDALWriteIMDFile( pszTIFFFilename, papszIMDMD );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We also need to address band specific metadata, and special */
|
|
/* "role" metadata. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBand;
|
|
for( nBand = 1; nBand <= poSrcDS->GetRasterCount(); nBand++ )
|
|
{
|
|
GDALRasterBand *poBand = poSrcDS->GetRasterBand( nBand );
|
|
|
|
if( bSrcIsGeoTIFF )
|
|
{
|
|
WriteMDMetadata( &(((GTiffRasterBand *)poBand)->oGTiffMDMD),
|
|
hTIFF, &psRoot, &psTail, nBand, pszProfile );
|
|
}
|
|
else
|
|
{
|
|
char **papszMD = poBand->GetMetadata();
|
|
|
|
if( CSLCount(papszMD) > 0 )
|
|
{
|
|
GDALMultiDomainMetadata oMDMD;
|
|
oMDMD.SetMetadata( papszMD );
|
|
|
|
WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, nBand,
|
|
pszProfile );
|
|
}
|
|
}
|
|
|
|
int bSuccess;
|
|
double dfOffset = poBand->GetOffset( &bSuccess );
|
|
double dfScale = poBand->GetScale();
|
|
|
|
if( bSuccess && (dfOffset != 0.0 || dfScale != 1.0) )
|
|
{
|
|
char szValue[128];
|
|
|
|
sprintf( szValue, "%.18g", dfOffset );
|
|
AppendMetadataItem( &psRoot, &psTail, "OFFSET", szValue, nBand,
|
|
"offset", "" );
|
|
sprintf( szValue, "%.18g", dfScale );
|
|
AppendMetadataItem( &psRoot, &psTail, "SCALE", szValue, nBand,
|
|
"scale", "" );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write out the generic XML metadata if there is any. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psRoot != NULL )
|
|
{
|
|
int bRet = TRUE;
|
|
|
|
if( EQUAL(pszProfile,"GDALGeoTIFF") )
|
|
{
|
|
char *pszXML_MD = CPLSerializeXMLTree( psRoot );
|
|
if( strlen(pszXML_MD) > 32000 )
|
|
{
|
|
if( bSrcIsGeoTIFF )
|
|
((GTiffDataset *) poSrcDS)->PushMetadataToPam();
|
|
else
|
|
bRet = FALSE;
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"Lost metadata writing to GeoTIFF ... too large to fit in tag." );
|
|
}
|
|
else
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD );
|
|
}
|
|
CPLFree( pszXML_MD );
|
|
}
|
|
else
|
|
{
|
|
if( bSrcIsGeoTIFF )
|
|
((GTiffDataset *) poSrcDS)->PushMetadataToPam();
|
|
else
|
|
bRet = FALSE;
|
|
}
|
|
|
|
CPLDestroyXMLNode( psRoot );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushMetadataToPam() */
|
|
/* */
|
|
/* When producing a strict profile TIFF or if our aggregate */
|
|
/* metadata is too big for a single tiff tag we may end up */
|
|
/* needing to write it via the PAM mechanisms. This method */
|
|
/* copies all the appropriate metadata into the PAM level */
|
|
/* metadata object but with special care to avoid copying */
|
|
/* metadata handled in other ways in TIFF format. */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::PushMetadataToPam()
|
|
|
|
{
|
|
int nBand;
|
|
for( nBand = 0; nBand <= GetRasterCount(); nBand++ )
|
|
{
|
|
GDALMultiDomainMetadata *poSrcMDMD;
|
|
GTiffRasterBand *poBand = NULL;
|
|
|
|
if( nBand == 0 )
|
|
poSrcMDMD = &(this->oGTiffMDMD);
|
|
else
|
|
{
|
|
poBand = (GTiffRasterBand *) GetRasterBand(nBand);
|
|
poSrcMDMD = &(poBand->oGTiffMDMD);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over the available domains. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iDomain, i;
|
|
char **papszDomainList;
|
|
|
|
papszDomainList = poSrcMDMD->GetDomainList();
|
|
for( iDomain = 0;
|
|
papszDomainList && papszDomainList[iDomain];
|
|
iDomain++ )
|
|
{
|
|
char **papszMD = poSrcMDMD->GetMetadata( papszDomainList[iDomain] );
|
|
|
|
if( EQUAL(papszDomainList[iDomain],"RPC")
|
|
|| EQUAL(papszDomainList[iDomain],"IMD")
|
|
|| EQUAL(papszDomainList[iDomain],"_temporary_")
|
|
|| EQUAL(papszDomainList[iDomain],"IMAGE_STRUCTURE") )
|
|
continue;
|
|
|
|
papszMD = CSLDuplicate(papszMD);
|
|
|
|
for( i = CSLCount(papszMD)-1; i >= 0; i-- )
|
|
{
|
|
if( EQUALN(papszMD[i],"TIFFTAG_",8)
|
|
|| EQUALN(papszMD[i],GDALMD_AREA_OR_POINT,
|
|
strlen(GDALMD_AREA_OR_POINT)) )
|
|
papszMD = CSLRemoveStrings( papszMD, i, 1, NULL );
|
|
}
|
|
|
|
if( nBand == 0 )
|
|
GDALPamDataset::SetMetadata( papszMD, papszDomainList[iDomain]);
|
|
else
|
|
poBand->GDALPamRasterBand::SetMetadata( papszMD, papszDomainList[iDomain]);
|
|
|
|
CSLDestroy( papszMD );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle some "special domain" stuff. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poBand != NULL )
|
|
{
|
|
int bSuccess;
|
|
double dfOffset = poBand->GetOffset( &bSuccess );
|
|
double dfScale = poBand->GetScale();
|
|
|
|
if( bSuccess && (dfOffset != 0.0 || dfScale != 1.0) )
|
|
{
|
|
poBand->GDALPamRasterBand::SetScale( dfScale );
|
|
poBand->GDALPamRasterBand::SetOffset( dfOffset );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteRPCTag() */
|
|
/* */
|
|
/* Format a TAG according to: */
|
|
/* */
|
|
/* http://geotiff.maptools.org/rpc_prop.html */
|
|
/************************************************************************/
|
|
|
|
/* static */
|
|
void GTiffDataset::WriteRPCTag( TIFF *hTIFF, char **papszRPCMD )
|
|
|
|
{
|
|
double adfRPCTag[92];
|
|
GDALRPCInfo sRPC;
|
|
|
|
if( !GDALExtractRPCInfo( papszRPCMD, &sRPC ) )
|
|
return;
|
|
|
|
adfRPCTag[0] = -1.0; // Error Bias
|
|
adfRPCTag[1] = -1.0; // Error Random
|
|
|
|
adfRPCTag[2] = sRPC.dfLINE_OFF;
|
|
adfRPCTag[3] = sRPC.dfSAMP_OFF;
|
|
adfRPCTag[4] = sRPC.dfLAT_OFF;
|
|
adfRPCTag[5] = sRPC.dfLONG_OFF;
|
|
adfRPCTag[6] = sRPC.dfHEIGHT_OFF;
|
|
adfRPCTag[7] = sRPC.dfLINE_SCALE;
|
|
adfRPCTag[8] = sRPC.dfSAMP_SCALE;
|
|
adfRPCTag[9] = sRPC.dfLAT_SCALE;
|
|
adfRPCTag[10] = sRPC.dfLONG_SCALE;
|
|
adfRPCTag[11] = sRPC.dfHEIGHT_SCALE;
|
|
|
|
memcpy( adfRPCTag + 12, sRPC.adfLINE_NUM_COEFF, sizeof(double) * 20 );
|
|
memcpy( adfRPCTag + 32, sRPC.adfLINE_DEN_COEFF, sizeof(double) * 20 );
|
|
memcpy( adfRPCTag + 52, sRPC.adfSAMP_NUM_COEFF, sizeof(double) * 20 );
|
|
memcpy( adfRPCTag + 72, sRPC.adfSAMP_DEN_COEFF, sizeof(double) * 20 );
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_RPCCOEFFICIENT, 92, adfRPCTag );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadRPCTag() */
|
|
/* */
|
|
/* Format a TAG according to: */
|
|
/* */
|
|
/* http://geotiff.maptools.org/rpc_prop.html */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::ReadRPCTag()
|
|
|
|
{
|
|
double *padfRPCTag;
|
|
char **papszMD = NULL;
|
|
CPLString osField;
|
|
CPLString osMultiField;
|
|
int i;
|
|
uint16 nCount;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount, &padfRPCTag )
|
|
|| nCount != 92 )
|
|
return;
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[2] );
|
|
papszMD = CSLSetNameValue( papszMD, "LINE_OFF", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[3] );
|
|
papszMD = CSLSetNameValue( papszMD, "SAMP_OFF", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[4] );
|
|
papszMD = CSLSetNameValue( papszMD, "LAT_OFF", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[5] );
|
|
papszMD = CSLSetNameValue( papszMD, "LONG_OFF", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[6] );
|
|
papszMD = CSLSetNameValue( papszMD, "HEIGHT_OFF", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[7] );
|
|
papszMD = CSLSetNameValue( papszMD, "LINE_SCALE", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[8] );
|
|
papszMD = CSLSetNameValue( papszMD, "SAMP_SCALE", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[9] );
|
|
papszMD = CSLSetNameValue( papszMD, "LAT_SCALE", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[10] );
|
|
papszMD = CSLSetNameValue( papszMD, "LONG_SCALE", osField );
|
|
|
|
osField.Printf( "%.15g", padfRPCTag[11] );
|
|
papszMD = CSLSetNameValue( papszMD, "HEIGHT_SCALE", osField );
|
|
|
|
for( i = 0; i < 20; i++ )
|
|
{
|
|
osField.Printf( "%.15g", padfRPCTag[12+i] );
|
|
if( i > 0 )
|
|
osMultiField += " ";
|
|
else
|
|
osMultiField = "";
|
|
osMultiField += osField;
|
|
}
|
|
papszMD = CSLSetNameValue( papszMD, "LINE_NUM_COEFF", osMultiField );
|
|
|
|
for( i = 0; i < 20; i++ )
|
|
{
|
|
osField.Printf( "%.15g", padfRPCTag[32+i] );
|
|
if( i > 0 )
|
|
osMultiField += " ";
|
|
else
|
|
osMultiField = "";
|
|
osMultiField += osField;
|
|
}
|
|
papszMD = CSLSetNameValue( papszMD, "LINE_DEN_COEFF", osMultiField );
|
|
|
|
for( i = 0; i < 20; i++ )
|
|
{
|
|
osField.Printf( "%.15g", padfRPCTag[52+i] );
|
|
if( i > 0 )
|
|
osMultiField += " ";
|
|
else
|
|
osMultiField = "";
|
|
osMultiField += osField;
|
|
}
|
|
papszMD = CSLSetNameValue( papszMD, "SAMP_NUM_COEFF", osMultiField );
|
|
|
|
for( i = 0; i < 20; i++ )
|
|
{
|
|
osField.Printf( "%.15g", padfRPCTag[72+i] );
|
|
if( i > 0 )
|
|
osMultiField += " ";
|
|
else
|
|
osMultiField = "";
|
|
osMultiField += osField;
|
|
}
|
|
papszMD = CSLSetNameValue( papszMD, "SAMP_DEN_COEFF", osMultiField );
|
|
|
|
oGTiffMDMD.SetMetadata( papszMD, "RPC" );
|
|
CSLDestroy( papszMD );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::WriteNoDataValue( TIFF *hTIFF, double dfNoData )
|
|
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_GDAL_NODATA,
|
|
CPLString().Printf( "%.18g", dfNoData ).c_str() );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetDirectory() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::SetDirectory( toff_t nNewOffset )
|
|
|
|
{
|
|
Crystalize();
|
|
|
|
FlushBlockBuf();
|
|
|
|
if( nNewOffset == 0 )
|
|
nNewOffset = nDirOffset;
|
|
|
|
if( nNewOffset == 0)
|
|
return TRUE;
|
|
|
|
if( TIFFCurrentDirOffset(hTIFF) == nNewOffset )
|
|
return TRUE;
|
|
|
|
if( GetAccess() == GA_Update )
|
|
TIFFFlush( hTIFF );
|
|
|
|
int nSetDirResult = TIFFSetSubDirectory( hTIFF, nNewOffset );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* YCbCr JPEG compressed images should be translated on the fly */
|
|
/* to RGB by libtiff/libjpeg unless specifically requested */
|
|
/* otherwise. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) )
|
|
nCompression = COMPRESSION_NONE;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) )
|
|
nPhotometric = PHOTOMETRIC_MINISBLACK;
|
|
|
|
if( nCompression == COMPRESSION_JPEG
|
|
&& nPhotometric == PHOTOMETRIC_YCBCR
|
|
&& CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
|
|
"YES") ) )
|
|
{
|
|
int nColorMode;
|
|
|
|
TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode );
|
|
if( nColorMode != JPEGCOLORMODE_RGB )
|
|
TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
|
|
}
|
|
|
|
return nSetDirResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Identify() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::Identify( GDALOpenInfo * poOpenInfo )
|
|
|
|
{
|
|
const char *pszFilename = poOpenInfo->pszFilename;
|
|
if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) )
|
|
{
|
|
pszFilename += strlen("GTIFF_RAW:");
|
|
GDALOpenInfo oOpenInfo( pszFilename, poOpenInfo->eAccess );
|
|
return Identify(&oOpenInfo);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We have a special hook for handling opening a specific */
|
|
/* directory of a TIFF file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) )
|
|
return TRUE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* First we check to see if the file has the expected header */
|
|
/* bytes. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poOpenInfo->nHeaderBytes < 2 )
|
|
return FALSE;
|
|
|
|
if( (poOpenInfo->pabyHeader[0] != 'I' || poOpenInfo->pabyHeader[1] != 'I')
|
|
&& (poOpenInfo->pabyHeader[0] != 'M' || poOpenInfo->pabyHeader[1] != 'M'))
|
|
return FALSE;
|
|
|
|
#ifndef BIGTIFF_SUPPORT
|
|
if( (poOpenInfo->pabyHeader[2] == 0x2B && poOpenInfo->pabyHeader[3] == 0) ||
|
|
(poOpenInfo->pabyHeader[2] == 0 && poOpenInfo->pabyHeader[3] == 0x2B) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OpenFailed,
|
|
"This is a BigTIFF file. BigTIFF is not supported by this\n"
|
|
"version of GDAL and libtiff." );
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if( (poOpenInfo->pabyHeader[2] != 0x2A || poOpenInfo->pabyHeader[3] != 0)
|
|
&& (poOpenInfo->pabyHeader[3] != 0x2A || poOpenInfo->pabyHeader[2] != 0)
|
|
&& (poOpenInfo->pabyHeader[2] != 0x2B || poOpenInfo->pabyHeader[3] != 0)
|
|
&& (poOpenInfo->pabyHeader[3] != 0x2B || poOpenInfo->pabyHeader[2] != 0))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Open() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo )
|
|
|
|
{
|
|
TIFF *hTIFF;
|
|
int bAllowRGBAInterface = TRUE;
|
|
const char *pszFilename = poOpenInfo->pszFilename;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check if it looks like a TIFF file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!Identify(poOpenInfo))
|
|
return NULL;
|
|
|
|
if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) )
|
|
{
|
|
bAllowRGBAInterface = FALSE;
|
|
pszFilename += strlen("GTIFF_RAW:");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We have a special hook for handling opening a specific */
|
|
/* directory of a TIFF file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) )
|
|
return OpenDir( poOpenInfo );
|
|
|
|
GTiffOneTimeInit();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try opening the dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poOpenInfo->eAccess == GA_ReadOnly )
|
|
hTIFF = VSI_TIFFOpen( pszFilename, "r" );
|
|
else
|
|
hTIFF = VSI_TIFFOpen( pszFilename, "r+" );
|
|
|
|
if( hTIFF == NULL )
|
|
return( NULL );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a corresponding GDALDataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
GTiffDataset *poDS;
|
|
|
|
poDS = new GTiffDataset();
|
|
poDS->SetDescription( pszFilename );
|
|
poDS->osFilename = pszFilename;
|
|
|
|
if( poDS->OpenOffset( hTIFF,TIFFCurrentDirOffset(hTIFF), TRUE,
|
|
poOpenInfo->eAccess, bAllowRGBAInterface ) != CE_None )
|
|
{
|
|
delete poDS;
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for external overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->oOvManager.Initialize( poDS, pszFilename );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize any PAM information. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->TryLoadXML();
|
|
poDS->ApplyPamInfo();
|
|
|
|
return poDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ApplyPamInfo() */
|
|
/* */
|
|
/* PAM Information, if available, overrides the GeoTIFF */
|
|
/* geotransform and projection definition. Check for them */
|
|
/* now. */
|
|
/************************************************************************/
|
|
|
|
void GTiffDataset::ApplyPamInfo()
|
|
|
|
{
|
|
double adfPamGeoTransform[6];
|
|
|
|
if( GDALPamDataset::GetGeoTransform( adfPamGeoTransform ) == CE_None )
|
|
{
|
|
memcpy( adfGeoTransform, adfPamGeoTransform, sizeof(double)*6 );
|
|
bGeoTransformValid = TRUE;
|
|
}
|
|
|
|
const char *pszPamSRS = GDALPamDataset::GetProjectionRef();
|
|
|
|
if( pszPamSRS != NULL && strlen(pszPamSRS) > 0 )
|
|
{
|
|
CPLFree( pszProjection );
|
|
pszProjection = CPLStrdup( pszPamSRS );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy any PAM metadata into our GeoTIFF context, but with the */
|
|
/* GeoTIFF context overriding the PAM info. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszPamDomains = oMDMD.GetDomainList();
|
|
|
|
for( int iDomain = 0; papszPamDomains && papszPamDomains[iDomain] != NULL; iDomain++ )
|
|
{
|
|
const char *pszDomain = papszPamDomains[iDomain];
|
|
char **papszGT_MD = oGTiffMDMD.GetMetadata( pszDomain );
|
|
char **papszPAM_MD = CSLDuplicate(oMDMD.GetMetadata( pszDomain ));
|
|
|
|
papszPAM_MD = CSLMerge( papszPAM_MD, papszGT_MD );
|
|
|
|
oGTiffMDMD.SetMetadata( papszPAM_MD, pszDomain );
|
|
CSLDestroy( papszPAM_MD );
|
|
}
|
|
|
|
for( int i = 1; i <= GetRasterCount(); i++)
|
|
{
|
|
GTiffRasterBand* poBand = (GTiffRasterBand *)GetRasterBand(i);
|
|
papszPamDomains = poBand->oMDMD.GetDomainList();
|
|
|
|
for( int iDomain = 0; papszPamDomains && papszPamDomains[iDomain] != NULL; iDomain++ )
|
|
{
|
|
const char *pszDomain = papszPamDomains[iDomain];
|
|
char **papszGT_MD = poBand->oGTiffMDMD.GetMetadata( pszDomain );
|
|
char **papszPAM_MD = CSLDuplicate(poBand->oMDMD.GetMetadata( pszDomain ));
|
|
|
|
papszPAM_MD = CSLMerge( papszPAM_MD, papszGT_MD );
|
|
|
|
poBand->oGTiffMDMD.SetMetadata( papszPAM_MD, pszDomain );
|
|
CSLDestroy( papszPAM_MD );
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenDir() */
|
|
/* */
|
|
/* Open a specific directory as encoded into a filename. */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *GTiffDataset::OpenDir( GDALOpenInfo * poOpenInfo )
|
|
|
|
{
|
|
int bAllowRGBAInterface = TRUE;
|
|
const char* pszFilename = poOpenInfo->pszFilename;
|
|
if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) )
|
|
{
|
|
bAllowRGBAInterface = FALSE;
|
|
pszFilename += strlen("GTIFF_RAW:");
|
|
}
|
|
|
|
if( !EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Split out filename, and dir#/offset. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszFilename += strlen("GTIFF_DIR:");
|
|
int bAbsolute = FALSE;
|
|
toff_t nOffset;
|
|
|
|
if( EQUALN(pszFilename,"off:",4) )
|
|
{
|
|
bAbsolute = TRUE;
|
|
pszFilename += 4;
|
|
}
|
|
|
|
nOffset = atol(pszFilename);
|
|
pszFilename += 1;
|
|
|
|
while( *pszFilename != '\0' && pszFilename[-1] != ':' )
|
|
pszFilename++;
|
|
|
|
if( *pszFilename == '\0' || nOffset == 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OpenFailed,
|
|
"Unable to extract offset or filename, should take the form\n"
|
|
"GTIFF_DIR:<dir>:filename or GTIFF_DIR:off:<dir_offset>:filename" );
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try opening the dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
TIFF *hTIFF;
|
|
|
|
GTiffOneTimeInit();
|
|
|
|
hTIFF = VSI_TIFFOpen( pszFilename, "r" );
|
|
if( hTIFF == NULL )
|
|
return( NULL );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If a directory was requested by index, advance to it now. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !bAbsolute )
|
|
{
|
|
while( nOffset > 1 )
|
|
{
|
|
if( TIFFReadDirectory( hTIFF ) == 0 )
|
|
{
|
|
XTIFFClose( hTIFF );
|
|
CPLError( CE_Failure, CPLE_OpenFailed,
|
|
"Requested directory %lu not found.", (long unsigned int)nOffset );
|
|
return NULL;
|
|
}
|
|
nOffset--;
|
|
}
|
|
|
|
nOffset = TIFFCurrentDirOffset( hTIFF );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a corresponding GDALDataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
GTiffDataset *poDS;
|
|
|
|
poDS = new GTiffDataset();
|
|
poDS->SetDescription( poOpenInfo->pszFilename );
|
|
poDS->osFilename = poOpenInfo->pszFilename;
|
|
|
|
if( !EQUAL(pszFilename,poOpenInfo->pszFilename)
|
|
&& !EQUALN(poOpenInfo->pszFilename,"GTIFF_RAW:",10) )
|
|
{
|
|
poDS->SetPhysicalFilename( pszFilename );
|
|
poDS->SetSubdatasetName( poOpenInfo->pszFilename );
|
|
poDS->osFilename = pszFilename;
|
|
}
|
|
|
|
if (poOpenInfo->eAccess == GA_Update)
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"Opening a specific TIFF directory is not supported in update mode. Switching to read-only" );
|
|
}
|
|
|
|
if( poDS->OpenOffset( hTIFF, nOffset, FALSE, GA_ReadOnly, bAllowRGBAInterface ) != CE_None )
|
|
{
|
|
delete poDS;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
poDS->bCloseTIFFHandle = TRUE;
|
|
return poDS;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenOffset() */
|
|
/* */
|
|
/* Initialize the GTiffDataset based on a passed in file */
|
|
/* handle, and directory offset to utilize. This is called for */
|
|
/* full res, and overview pages. */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::OpenOffset( TIFF *hTIFFIn, toff_t nDirOffsetIn,
|
|
int bBaseIn, GDALAccess eAccess,
|
|
int bAllowRGBAInterface)
|
|
|
|
{
|
|
uint32 nXSize, nYSize;
|
|
int bTreatAsBitmap = FALSE;
|
|
int bTreatAsOdd = FALSE;
|
|
int bTreatAsSplit = FALSE;
|
|
|
|
hTIFF = hTIFFIn;
|
|
|
|
nDirOffset = nDirOffsetIn;
|
|
|
|
SetDirectory( nDirOffsetIn );
|
|
|
|
bBase = bBaseIn;
|
|
|
|
this->eAccess = eAccess;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture some information from the file that is of interest. */
|
|
/* -------------------------------------------------------------------- */
|
|
TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize );
|
|
TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize );
|
|
nRasterXSize = nXSize;
|
|
nRasterYSize = nYSize;
|
|
|
|
if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel ) )
|
|
nBands = 1;
|
|
else
|
|
nBands = nSamplesPerPixel;
|
|
|
|
if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) )
|
|
nBitsPerSample = 1;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(nPlanarConfig) ) )
|
|
nPlanarConfig = PLANARCONFIG_CONTIG;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) )
|
|
nPhotometric = PHOTOMETRIC_MINISBLACK;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(nSampleFormat) ) )
|
|
nSampleFormat = SAMPLEFORMAT_UINT;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) )
|
|
nCompression = COMPRESSION_NONE;
|
|
|
|
#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION > 20031007 /* 3.6.0 */
|
|
if (nCompression != COMPRESSION_NONE &&
|
|
!TIFFIsCODECConfigured(nCompression))
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Cannot open TIFF file due to missing codec." );
|
|
return CE_Failure;
|
|
}
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* YCbCr JPEG compressed images should be translated on the fly */
|
|
/* to RGB by libtiff/libjpeg unless specifically requested */
|
|
/* otherwise. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nCompression == COMPRESSION_JPEG
|
|
&& nPhotometric == PHOTOMETRIC_YCBCR
|
|
&& CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
|
|
"YES") ) )
|
|
{
|
|
int nColorMode;
|
|
|
|
SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE" );
|
|
if ( !TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ) ||
|
|
nColorMode != JPEGCOLORMODE_RGB )
|
|
TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get strip/tile layout. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFIsTiled(hTIFF) )
|
|
{
|
|
TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) );
|
|
TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) );
|
|
}
|
|
else
|
|
{
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP,
|
|
&(nRowsPerStrip) ) )
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"RowsPerStrip not defined ... assuming all one strip." );
|
|
nRowsPerStrip = nYSize; /* dummy value */
|
|
}
|
|
|
|
nBlockXSize = nRasterXSize;
|
|
nBlockYSize = MIN(nRowsPerStrip,nYSize);
|
|
}
|
|
|
|
nBlocksPerBand =
|
|
((nYSize + nBlockYSize - 1) / nBlockYSize)
|
|
* ((nXSize + nBlockXSize - 1) / nBlockXSize);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we handle this using the GTiffBitmapBand? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBitsPerSample == 1 && nBands == 1 )
|
|
{
|
|
bTreatAsBitmap = TRUE;
|
|
|
|
// Lets treat large "one row" bitmaps using the scanline api.
|
|
if( !TIFFIsTiled(hTIFF)
|
|
&& nBlockYSize == nYSize
|
|
&& nYSize > 2000
|
|
&& bAllowRGBAInterface )
|
|
bTreatAsSplit = TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we treat this via the RGBA interface? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bAllowRGBAInterface &&
|
|
!bTreatAsBitmap && !(nBitsPerSample > 8)
|
|
&& (nPhotometric == PHOTOMETRIC_CIELAB ||
|
|
nPhotometric == PHOTOMETRIC_LOGL ||
|
|
nPhotometric == PHOTOMETRIC_LOGLUV ||
|
|
nPhotometric == PHOTOMETRIC_SEPARATED ||
|
|
( nPhotometric == PHOTOMETRIC_YCBCR
|
|
&& nCompression != COMPRESSION_JPEG )) )
|
|
{
|
|
char szMessage[1024];
|
|
|
|
if( TIFFRGBAImageOK( hTIFF, szMessage ) == 1 )
|
|
{
|
|
const char* pszSourceColorSpace = NULL;
|
|
switch (nPhotometric)
|
|
{
|
|
case PHOTOMETRIC_CIELAB:
|
|
pszSourceColorSpace = "CIELAB";
|
|
break;
|
|
case PHOTOMETRIC_LOGL:
|
|
pszSourceColorSpace = "LOGL";
|
|
break;
|
|
case PHOTOMETRIC_LOGLUV:
|
|
pszSourceColorSpace = "LOGLUV";
|
|
break;
|
|
case PHOTOMETRIC_SEPARATED:
|
|
pszSourceColorSpace = "CMYK";
|
|
break;
|
|
case PHOTOMETRIC_YCBCR:
|
|
pszSourceColorSpace = "YCbCr";
|
|
break;
|
|
}
|
|
if (pszSourceColorSpace)
|
|
SetMetadataItem( "SOURCE_COLOR_SPACE", pszSourceColorSpace, "IMAGE_STRUCTURE" );
|
|
bTreatAsRGBA = TRUE;
|
|
nBands = 4;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "GTiff", "TIFFRGBAImageOK says:\n%s", szMessage );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should we treat this via the odd bits interface? */
|
|
/* -------------------------------------------------------------------- */
|
|
if ( nSampleFormat == SAMPLEFORMAT_IEEEFP )
|
|
{
|
|
if ( nBitsPerSample == 16 || nBitsPerSample == 24 )
|
|
bTreatAsOdd = TRUE;
|
|
}
|
|
else if ( !bTreatAsRGBA && !bTreatAsBitmap
|
|
&& nBitsPerSample != 8
|
|
&& nBitsPerSample != 16
|
|
&& nBitsPerSample != 32
|
|
&& nBitsPerSample != 64
|
|
&& nBitsPerSample != 128 )
|
|
bTreatAsOdd = TRUE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture the color table if there is one. */
|
|
/* -------------------------------------------------------------------- */
|
|
unsigned short *panRed, *panGreen, *panBlue;
|
|
|
|
if( bTreatAsRGBA
|
|
|| TIFFGetField( hTIFF, TIFFTAG_COLORMAP,
|
|
&panRed, &panGreen, &panBlue) == 0 )
|
|
{
|
|
// Build inverted palette if we have inverted photometric.
|
|
// Pixel values remains unchanged. Avoid doing this for *deep*
|
|
// data types (per #1882)
|
|
if( nBitsPerSample <= 16 && nPhotometric == PHOTOMETRIC_MINISWHITE )
|
|
{
|
|
GDALColorEntry oEntry;
|
|
int iColor, nColorCount;
|
|
|
|
poColorTable = new GDALColorTable();
|
|
nColorCount = 1 << nBitsPerSample;
|
|
|
|
for ( iColor = 0; iColor < nColorCount; iColor++ )
|
|
{
|
|
oEntry.c1 = oEntry.c2 = oEntry.c3 =
|
|
(255 * (nColorCount - 1 - iColor)) / (nColorCount-1);
|
|
oEntry.c4 = 255;
|
|
poColorTable->SetColorEntry( iColor, &oEntry );
|
|
}
|
|
|
|
nPhotometric = PHOTOMETRIC_PALETTE;
|
|
}
|
|
else
|
|
poColorTable = NULL;
|
|
}
|
|
else
|
|
{
|
|
int nColorCount, nMaxColor = 0;
|
|
GDALColorEntry oEntry;
|
|
|
|
poColorTable = new GDALColorTable();
|
|
|
|
nColorCount = 1 << nBitsPerSample;
|
|
|
|
for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
|
|
{
|
|
oEntry.c1 = panRed[iColor] / 256;
|
|
oEntry.c2 = panGreen[iColor] / 256;
|
|
oEntry.c3 = panBlue[iColor] / 256;
|
|
oEntry.c4 = 255;
|
|
|
|
poColorTable->SetColorEntry( iColor, &oEntry );
|
|
|
|
nMaxColor = MAX(nMaxColor,panRed[iColor]);
|
|
nMaxColor = MAX(nMaxColor,panGreen[iColor]);
|
|
nMaxColor = MAX(nMaxColor,panBlue[iColor]);
|
|
}
|
|
|
|
// Bug 1384 - Some TIFF files are generated with color map entry
|
|
// values in range 0-255 instead of 0-65535 - try to handle these
|
|
// gracefully.
|
|
if( nMaxColor > 0 && nMaxColor < 256 )
|
|
{
|
|
CPLDebug( "GTiff", "TIFF ColorTable seems to be improperly scaled, fixing up." );
|
|
|
|
for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
|
|
{
|
|
oEntry.c1 = panRed[iColor];
|
|
oEntry.c2 = panGreen[iColor];
|
|
oEntry.c3 = panBlue[iColor];
|
|
oEntry.c4 = 255;
|
|
|
|
poColorTable->SetColorEntry( iColor, &oEntry );
|
|
}
|
|
}
|
|
}
|
|
|
|
bColorTableChanged = FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create band information objects. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( int iBand = 0; iBand < nBands; iBand++ )
|
|
{
|
|
if( bTreatAsRGBA )
|
|
SetBand( iBand+1, new GTiffRGBABand( this, iBand+1 ) );
|
|
else if( bTreatAsSplit )
|
|
SetBand( iBand+1, new GTiffSplitBitmapBand( this, iBand+1 ) );
|
|
else if( bTreatAsBitmap )
|
|
SetBand( iBand+1, new GTiffBitmapBand( this, iBand+1 ) );
|
|
else if( bTreatAsOdd )
|
|
SetBand( iBand+1, new GTiffOddBitsBand( this, iBand+1 ) );
|
|
else
|
|
SetBand( iBand+1, new GTiffRasterBand( this, iBand+1 ) );
|
|
}
|
|
|
|
if( GetRasterBand(1)->GetRasterDataType() == GDT_Unknown )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"Unsupported TIFF configuration." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the transform or gcps from the GeoTIFF file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bBaseIn )
|
|
{
|
|
char *pszTabWKT = NULL;
|
|
double *padfTiePoints, *padfScale, *padfMatrix;
|
|
uint16 nCount;
|
|
|
|
adfGeoTransform[0] = 0.0;
|
|
adfGeoTransform[1] = 1.0;
|
|
adfGeoTransform[2] = 0.0;
|
|
adfGeoTransform[3] = 0.0;
|
|
adfGeoTransform[4] = 0.0;
|
|
adfGeoTransform[5] = 1.0;
|
|
|
|
if( TIFFGetField(hTIFF,TIFFTAG_GEOPIXELSCALE,&nCount,&padfScale )
|
|
&& nCount >= 2
|
|
&& padfScale[0] != 0.0 && padfScale[1] != 0.0 )
|
|
{
|
|
adfGeoTransform[1] = padfScale[0];
|
|
adfGeoTransform[5] = - ABS(padfScale[1]);
|
|
|
|
if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
|
|
&& nCount >= 6 )
|
|
{
|
|
adfGeoTransform[0] =
|
|
padfTiePoints[3] - padfTiePoints[0] * adfGeoTransform[1];
|
|
adfGeoTransform[3] =
|
|
padfTiePoints[4] - padfTiePoints[1] * adfGeoTransform[5];
|
|
|
|
bGeoTransformValid = TRUE;
|
|
}
|
|
}
|
|
|
|
else if( TIFFGetField(hTIFF,TIFFTAG_GEOTRANSMATRIX,&nCount,&padfMatrix )
|
|
&& nCount == 16 )
|
|
{
|
|
adfGeoTransform[0] = padfMatrix[3];
|
|
adfGeoTransform[1] = padfMatrix[0];
|
|
adfGeoTransform[2] = padfMatrix[1];
|
|
adfGeoTransform[3] = padfMatrix[7];
|
|
adfGeoTransform[4] = padfMatrix[4];
|
|
adfGeoTransform[5] = padfMatrix[5];
|
|
bGeoTransformValid = TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise try looking for a .tfw, .tifw or .wld file. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
bGeoTransformValid =
|
|
GDALReadWorldFile( osFilename, NULL, adfGeoTransform );
|
|
|
|
if( !bGeoTransformValid )
|
|
{
|
|
bGeoTransformValid =
|
|
GDALReadWorldFile( osFilename, "wld", adfGeoTransform );
|
|
}
|
|
|
|
if( !bGeoTransformValid )
|
|
{
|
|
int bTabFileOK =
|
|
GDALReadTabFile( osFilename, adfGeoTransform,
|
|
&pszTabWKT, &nGCPCount, &pasGCPList );
|
|
|
|
if( bTabFileOK && nGCPCount == 0 )
|
|
bGeoTransformValid = TRUE;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for GCPs. Note, we will allow there to be GCPs and a */
|
|
/* transform in some circumstances. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints )
|
|
&& !bGeoTransformValid )
|
|
{
|
|
nGCPCount = nCount / 6;
|
|
pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
|
|
|
|
for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
|
|
{
|
|
char szID[32];
|
|
|
|
sprintf( szID, "%d", iGCP+1 );
|
|
pasGCPList[iGCP].pszId = CPLStrdup( szID );
|
|
pasGCPList[iGCP].pszInfo = CPLStrdup("");
|
|
pasGCPList[iGCP].dfGCPPixel = padfTiePoints[iGCP*6+0];
|
|
pasGCPList[iGCP].dfGCPLine = padfTiePoints[iGCP*6+1];
|
|
pasGCPList[iGCP].dfGCPX = padfTiePoints[iGCP*6+3];
|
|
pasGCPList[iGCP].dfGCPY = padfTiePoints[iGCP*6+4];
|
|
pasGCPList[iGCP].dfGCPZ = padfTiePoints[iGCP*6+5];
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture the GeoTIFF projection, if available. */
|
|
/* -------------------------------------------------------------------- */
|
|
GTIF *hGTIF;
|
|
GTIFDefn sGTIFDefn;
|
|
|
|
CPLFree( pszProjection );
|
|
pszProjection = NULL;
|
|
|
|
hGTIF = GTIFNew(hTIFF);
|
|
|
|
if ( !hGTIF )
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"GeoTIFF tags apparently corrupt, they are being ignored." );
|
|
}
|
|
else
|
|
{
|
|
if( GTIFGetDefn( hGTIF, &sGTIFDefn ) )
|
|
{
|
|
pszProjection = GTIFGetOGISDefn( hGTIF, &sGTIFDefn );
|
|
}
|
|
|
|
// Is this a pixel-is-point dataset?
|
|
short nRasterType;
|
|
|
|
|
|
if( GTIFKeyGet(hGTIF, GTRasterTypeGeoKey, &nRasterType,
|
|
0, 1 ) == 1 )
|
|
{
|
|
if( nRasterType == (short) RasterPixelIsPoint )
|
|
SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT );
|
|
else
|
|
SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_AREA );
|
|
}
|
|
|
|
GTIFFree( hGTIF );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we didn't get a geotiff projection definition, but we did */
|
|
/* get one from the .tab file, use that instead. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszTabWKT != NULL
|
|
&& (pszProjection == NULL || pszProjection[0] == '\0') )
|
|
{
|
|
CPLFree( pszProjection );
|
|
pszProjection = pszTabWKT;
|
|
pszTabWKT = NULL;
|
|
}
|
|
|
|
if( pszProjection == NULL )
|
|
{
|
|
pszProjection = CPLStrdup( "" );
|
|
}
|
|
|
|
if( pszTabWKT != NULL )
|
|
CPLFree( pszTabWKT );
|
|
|
|
bGeoTIFFInfoChanged = FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture some other potentially interesting information. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszText, szWorkMDI[200];
|
|
float fResolution;
|
|
uint16 nShort;
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_DOCUMENTNAME, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_DOCUMENTNAME", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_IMAGEDESCRIPTION, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_IMAGEDESCRIPTION", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_SOFTWARE, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_SOFTWARE", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_DATETIME, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_DATETIME", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_ARTIST, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_ARTIST", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_HOSTCOMPUTER, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_HOSTCOMPUTER", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_COPYRIGHT, &pszText ) )
|
|
SetMetadataItem( "TIFFTAG_COPYRIGHT", pszText );
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_XRESOLUTION, &fResolution ) )
|
|
{
|
|
sprintf( szWorkMDI, "%.8g", fResolution );
|
|
SetMetadataItem( "TIFFTAG_XRESOLUTION", szWorkMDI );
|
|
}
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_YRESOLUTION, &fResolution ) )
|
|
{
|
|
sprintf( szWorkMDI, "%.8g", fResolution );
|
|
SetMetadataItem( "TIFFTAG_YRESOLUTION", szWorkMDI );
|
|
}
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_MINSAMPLEVALUE, &nShort ) )
|
|
{
|
|
sprintf( szWorkMDI, "%d", nShort );
|
|
SetMetadataItem( "TIFFTAG_MINSAMPLEVALUE", szWorkMDI );
|
|
}
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_MAXSAMPLEVALUE, &nShort ) )
|
|
{
|
|
sprintf( szWorkMDI, "%d", nShort );
|
|
SetMetadataItem( "TIFFTAG_MAXSAMPLEVALUE", szWorkMDI );
|
|
}
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_RESOLUTIONUNIT, &nShort ) )
|
|
{
|
|
if( nShort == RESUNIT_NONE )
|
|
sprintf( szWorkMDI, "%d (unitless)", nShort );
|
|
else if( nShort == RESUNIT_INCH )
|
|
sprintf( szWorkMDI, "%d (pixels/inch)", nShort );
|
|
else if( nShort == RESUNIT_CENTIMETER )
|
|
sprintf( szWorkMDI, "%d (pixels/cm)", nShort );
|
|
else
|
|
sprintf( szWorkMDI, "%d", nShort );
|
|
SetMetadataItem( "TIFFTAG_RESOLUTIONUNIT", szWorkMDI );
|
|
}
|
|
|
|
if( nCompression == COMPRESSION_NONE )
|
|
/* no compression tag */;
|
|
else if( nCompression == COMPRESSION_CCITTRLE )
|
|
SetMetadataItem( "COMPRESSION", "CCITTRLE", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_CCITTFAX3 )
|
|
SetMetadataItem( "COMPRESSION", "CCITTFAX3", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_CCITTFAX4 )
|
|
SetMetadataItem( "COMPRESSION", "CCITTFAX4", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_LZW )
|
|
SetMetadataItem( "COMPRESSION", "LZW", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_OJPEG )
|
|
SetMetadataItem( "COMPRESSION", "OJPEG", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_JPEG )
|
|
SetMetadataItem( "COMPRESSION", "JPEG", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_NEXT )
|
|
SetMetadataItem( "COMPRESSION", "NEXT", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_CCITTRLEW )
|
|
SetMetadataItem( "COMPRESSION", "CCITTRLEW", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_PACKBITS )
|
|
SetMetadataItem( "COMPRESSION", "PACKBITS", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_THUNDERSCAN )
|
|
SetMetadataItem( "COMPRESSION", "THUNDERSCAN", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_PIXARFILM )
|
|
SetMetadataItem( "COMPRESSION", "PIXARFILM", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_PIXARLOG )
|
|
SetMetadataItem( "COMPRESSION", "PIXARLOG", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_DEFLATE )
|
|
SetMetadataItem( "COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_ADOBE_DEFLATE )
|
|
SetMetadataItem( "COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_DCS )
|
|
SetMetadataItem( "COMPRESSION", "DCS", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_JBIG )
|
|
SetMetadataItem( "COMPRESSION", "JBIG", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_SGILOG )
|
|
SetMetadataItem( "COMPRESSION", "SGILOG", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_SGILOG24 )
|
|
SetMetadataItem( "COMPRESSION", "SGILOG24", "IMAGE_STRUCTURE" );
|
|
else if( nCompression == COMPRESSION_JP2000 )
|
|
SetMetadataItem( "COMPRESSION", "JP2000", "IMAGE_STRUCTURE" );
|
|
else
|
|
{
|
|
CPLString oComp;
|
|
SetMetadataItem( "COMPRESSION",
|
|
(const char *) oComp.Printf( "%d", nCompression));
|
|
}
|
|
|
|
if( nPlanarConfig == PLANARCONFIG_CONTIG && nBands != 1 )
|
|
SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
|
|
else
|
|
SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" );
|
|
|
|
if( (GetRasterBand(1)->GetRasterDataType() == GDT_Byte && nBitsPerSample != 8 ) ||
|
|
(GetRasterBand(1)->GetRasterDataType() == GDT_UInt16 && nBitsPerSample != 16) ||
|
|
(GetRasterBand(1)->GetRasterDataType() == GDT_UInt32 && nBitsPerSample != 32) )
|
|
{
|
|
for (int i = 0; i < nBands; ++i)
|
|
GetRasterBand(i+1)->SetMetadataItem( "NBITS",
|
|
CPLString().Printf( "%d", (int)nBitsPerSample ),
|
|
"IMAGE_STRUCTURE" );
|
|
}
|
|
|
|
if( TIFFGetField( hTIFF, TIFFTAG_GDAL_METADATA, &pszText ) )
|
|
{
|
|
CPLXMLNode *psRoot = CPLParseXMLString( pszText );
|
|
CPLXMLNode *psItem = NULL;
|
|
|
|
if( psRoot != NULL && psRoot->eType == CXT_Element
|
|
&& EQUAL(psRoot->pszValue,"GDALMetadata") )
|
|
psItem = psRoot->psChild;
|
|
|
|
for( ; psItem != NULL; psItem = psItem->psNext )
|
|
{
|
|
const char *pszKey, *pszValue, *pszRole, *pszDomain;
|
|
char *pszUnescapedValue;
|
|
int nBand;
|
|
|
|
if( psItem->eType != CXT_Element
|
|
|| !EQUAL(psItem->pszValue,"Item") )
|
|
continue;
|
|
|
|
pszKey = CPLGetXMLValue( psItem, "name", NULL );
|
|
pszValue = CPLGetXMLValue( psItem, NULL, NULL );
|
|
nBand = atoi(CPLGetXMLValue( psItem, "sample", "-1" )) + 1;
|
|
pszRole = CPLGetXMLValue( psItem, "role", "" );
|
|
pszDomain = CPLGetXMLValue( psItem, "domain", "" );
|
|
|
|
if( pszKey == NULL || pszValue == NULL )
|
|
continue;
|
|
|
|
pszUnescapedValue = CPLUnescapeString( pszValue, NULL,
|
|
CPLES_XML );
|
|
if( nBand == 0 )
|
|
SetMetadataItem( pszKey, pszUnescapedValue, pszDomain );
|
|
else
|
|
{
|
|
GDALRasterBand *poBand = GetRasterBand(nBand);
|
|
if( poBand != NULL )
|
|
{
|
|
if( EQUAL(pszRole,"scale") )
|
|
poBand->SetScale( atof(pszUnescapedValue) );
|
|
else if( EQUAL(pszRole,"offset") )
|
|
poBand->SetOffset( atof(pszUnescapedValue) );
|
|
else
|
|
poBand->SetMetadataItem(pszKey,pszUnescapedValue,
|
|
pszDomain );
|
|
}
|
|
}
|
|
CPLFree( pszUnescapedValue );
|
|
}
|
|
|
|
CPLDestroyXMLNode( psRoot );
|
|
}
|
|
|
|
bMetadataChanged = FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for RPC metadata in an RPB file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bBaseIn )
|
|
{
|
|
char **papszRPCMD = GDALLoadRPBFile( osFilename, NULL );
|
|
|
|
if( papszRPCMD != NULL )
|
|
{
|
|
oGTiffMDMD.SetMetadata( papszRPCMD, "RPC" );
|
|
CSLDestroy( papszRPCMD );
|
|
bMetadataChanged = FALSE;
|
|
}
|
|
else
|
|
ReadRPCTag();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for RPC metadata in an RPB file. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bBaseIn )
|
|
{
|
|
char **papszIMDMD = GDALLoadIMDFile( osFilename, NULL );
|
|
|
|
if( papszIMDMD != NULL )
|
|
{
|
|
oGTiffMDMD.SetMetadata( papszIMDMD, "IMD" );
|
|
CSLDestroy( papszIMDMD );
|
|
bMetadataChanged = FALSE;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for NODATA */
|
|
/* -------------------------------------------------------------------- */
|
|
if( TIFFGetField( hTIFF, TIFFTAG_GDAL_NODATA, &pszText ) )
|
|
{
|
|
bNoDataSet = TRUE;
|
|
dfNoDataValue = atof( pszText );
|
|
}
|
|
|
|
bNoDataChanged = FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If this is a "base" raster, we should scan for any */
|
|
/* associated overviews, internal mask bands and subdatasets. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bBase )
|
|
{
|
|
char **papszSubdatasets = NULL;
|
|
int iDirIndex = 0;
|
|
|
|
while( !TIFFLastDirectory( hTIFF )
|
|
&& (iDirIndex == 0 || TIFFReadDirectory( hTIFF ) != 0) )
|
|
{
|
|
toff_t nThisDir = TIFFCurrentDirOffset(hTIFF);
|
|
uint32 nSubType = 0;
|
|
|
|
iDirIndex++;
|
|
|
|
if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) )
|
|
nSubType = 0;
|
|
|
|
/* Embedded overview of the main image */
|
|
if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 &&
|
|
(nSubType & FILETYPE_MASK) == 0 &&
|
|
iDirIndex != 1 )
|
|
{
|
|
GTiffDataset *poODS;
|
|
|
|
poODS = new GTiffDataset();
|
|
if( poODS->OpenOffset( hTIFF, nThisDir, FALSE,
|
|
eAccess ) != CE_None
|
|
|| poODS->GetRasterCount() != GetRasterCount() )
|
|
{
|
|
delete poODS;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "GTiff", "Opened %dx%d overview.\n",
|
|
poODS->GetRasterXSize(), poODS->GetRasterYSize());
|
|
nOverviewCount++;
|
|
papoOverviewDS = (GTiffDataset **)
|
|
CPLRealloc(papoOverviewDS,
|
|
nOverviewCount * (sizeof(void*)));
|
|
papoOverviewDS[nOverviewCount-1] = poODS;
|
|
poODS->poBaseDS = this;
|
|
}
|
|
}
|
|
|
|
/* Embedded mask of the main image */
|
|
else if ((nSubType & FILETYPE_MASK) != 0 &&
|
|
(nSubType & FILETYPE_REDUCEDIMAGE) == 0 &&
|
|
poMaskDS == NULL )
|
|
{
|
|
poMaskDS = new GTiffDataset();
|
|
|
|
/* The TIFF6 specification - page 37 - only allows 1 SamplesPerPixel and 1 BitsPerSample
|
|
Here we support either 1 or 8 bit per sample
|
|
and we support either 1 sample per pixel or as many samples as in the main image
|
|
We don't check the value of the PhotometricInterpretation tag, which should be
|
|
set to "Transparency mask" (4) according to the specification (page 36)
|
|
... But the TIFF6 specification allows image masks to have a higher resolution than
|
|
the main image, what we don't support here
|
|
*/
|
|
|
|
if( poMaskDS->OpenOffset( hTIFF, nThisDir, FALSE,
|
|
eAccess ) != CE_None
|
|
|| poMaskDS->GetRasterCount() == 0
|
|
|| !(poMaskDS->GetRasterCount() == 1 || poMaskDS->GetRasterCount() == GetRasterCount())
|
|
|| poMaskDS->GetRasterXSize() != GetRasterXSize()
|
|
|| poMaskDS->GetRasterYSize() != GetRasterYSize()
|
|
|| poMaskDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
|
|
{
|
|
delete poMaskDS;
|
|
poMaskDS = NULL;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "GTiff", "Opened band mask.\n");
|
|
poMaskDS->poBaseDS = this;
|
|
}
|
|
}
|
|
|
|
/* Embedded mask of an overview */
|
|
/* The TIFF6 specification allows the combination of the FILETYPE_xxxx masks */
|
|
else if (nSubType & (FILETYPE_REDUCEDIMAGE | FILETYPE_MASK))
|
|
{
|
|
GTiffDataset* poDS = new GTiffDataset();
|
|
if( poDS->OpenOffset( hTIFF, nThisDir, FALSE,
|
|
eAccess ) != CE_None
|
|
|| poDS->GetRasterCount() == 0
|
|
|| poDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
|
|
{
|
|
delete poDS;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
for(i=0;i<nOverviewCount;i++)
|
|
{
|
|
if (((GTiffDataset*)papoOverviewDS[i])->poMaskDS == NULL &&
|
|
poDS->GetRasterXSize() == papoOverviewDS[i]->GetRasterXSize() &&
|
|
poDS->GetRasterYSize() == papoOverviewDS[i]->GetRasterYSize() &&
|
|
(poDS->GetRasterCount() == 1 || poDS->GetRasterCount() == GetRasterCount()))
|
|
{
|
|
CPLDebug( "GTiff", "Opened band mask for %dx%d overview.\n",
|
|
poDS->GetRasterXSize(), poDS->GetRasterYSize());
|
|
((GTiffDataset*)papoOverviewDS[i])->poMaskDS = poDS;
|
|
poDS->poBaseDS = this;
|
|
break;
|
|
}
|
|
}
|
|
if (i == nOverviewCount)
|
|
{
|
|
delete poDS;
|
|
}
|
|
}
|
|
}
|
|
else if( nSubType == 0 ) {
|
|
CPLString osName, osDesc;
|
|
uint32 nXSize, nYSize;
|
|
uint16 nSPP;
|
|
|
|
TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize );
|
|
TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize );
|
|
if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSPP ) )
|
|
nSPP = 1;
|
|
|
|
osName.Printf( "SUBDATASET_%d_NAME=GTIFF_DIR:%d:%s",
|
|
iDirIndex, iDirIndex, osFilename.c_str() );
|
|
osDesc.Printf( "SUBDATASET_%d_DESC=Page %d (%dP x %dL x %dB)",
|
|
iDirIndex, iDirIndex,
|
|
nXSize, nYSize, nSPP );
|
|
|
|
papszSubdatasets =
|
|
CSLAddString( papszSubdatasets, osName );
|
|
papszSubdatasets =
|
|
CSLAddString( papszSubdatasets, osDesc );
|
|
}
|
|
|
|
SetDirectory( nThisDir );
|
|
}
|
|
|
|
/* If we have a mask for the main image, loop over the overviews, and if they */
|
|
/* have a mask, let's set this mask as an overview of the main mask... */
|
|
if (poMaskDS != NULL)
|
|
{
|
|
int i;
|
|
for(i=0;i<nOverviewCount;i++)
|
|
{
|
|
if (((GTiffDataset*)papoOverviewDS[i])->poMaskDS != NULL)
|
|
{
|
|
poMaskDS->nOverviewCount++;
|
|
poMaskDS->papoOverviewDS = (GTiffDataset **)
|
|
CPLRealloc(poMaskDS->papoOverviewDS,
|
|
poMaskDS->nOverviewCount * (sizeof(void*)));
|
|
poMaskDS->papoOverviewDS[poMaskDS->nOverviewCount-1] =
|
|
((GTiffDataset*)papoOverviewDS[i])->poMaskDS;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Only keep track of subdatasets if we have more than one */
|
|
/* subdataset (pair). */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CSLCount(papszSubdatasets) > 2 )
|
|
{
|
|
oGTiffMDMD.SetMetadata( papszSubdatasets, "SUBDATASETS" );
|
|
}
|
|
CSLDestroy( papszSubdatasets );
|
|
}
|
|
|
|
return( CE_None );
|
|
}
|
|
|
|
static int GTiffGetZLevel(char** papszOptions)
|
|
{
|
|
int nZLevel = -1;
|
|
const char* pszValue = CSLFetchNameValue( papszOptions, "ZLEVEL" );
|
|
if( pszValue != NULL )
|
|
{
|
|
nZLevel = atoi( pszValue );
|
|
if (!(nZLevel >= 1 && nZLevel <= 9))
|
|
{
|
|
CPLError( CE_Warning, CPLE_IllegalArg,
|
|
"ZLEVEL=%s value not recognised, ignoring.",
|
|
pszValue );
|
|
nZLevel = -1;
|
|
}
|
|
}
|
|
return nZLevel;
|
|
}
|
|
|
|
static int GTiffGetJpegQuality(char** papszOptions)
|
|
{
|
|
int nJpegQuality = -1;
|
|
const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" );
|
|
if( pszValue != NULL )
|
|
{
|
|
nJpegQuality = atoi( pszValue );
|
|
if (!(nJpegQuality >= 1 && nJpegQuality <= 100))
|
|
{
|
|
CPLError( CE_Warning, CPLE_IllegalArg,
|
|
"JPEG_QUALITY=%s value not recognised, ignoring.",
|
|
pszValue );
|
|
nJpegQuality = -1;
|
|
}
|
|
}
|
|
return nJpegQuality;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffCreate() */
|
|
/* */
|
|
/* Shared functionality between GTiffDataset::Create() and */
|
|
/* GTiffCreateCopy() for creating TIFF file based on a set of */
|
|
/* options and a configuration. */
|
|
/************************************************************************/
|
|
|
|
TIFF *GTiffDataset::CreateLL( const char * pszFilename,
|
|
int nXSize, int nYSize, int nBands,
|
|
GDALDataType eType,
|
|
char **papszParmList )
|
|
|
|
{
|
|
TIFF *hTIFF;
|
|
int nBlockXSize = 0, nBlockYSize = 0;
|
|
int bTiled = FALSE;
|
|
int nCompression = COMPRESSION_NONE;
|
|
int nPredictor = 1, nJpegQuality = -1, nZLevel = -1;
|
|
uint16 nSampleFormat;
|
|
int nPlanar;
|
|
const char *pszValue;
|
|
const char *pszProfile;
|
|
int bCreateBigTIFF = FALSE;
|
|
|
|
GTiffOneTimeInit();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Blow on a few errors. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nXSize < 1 || nYSize < 1 || nBands < 1 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Attempt to create %dx%dx%d TIFF file, but width, height and bands\n"
|
|
"must be positive.",
|
|
nXSize, nYSize, nBands );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup values based on options. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszProfile = CSLFetchNameValue(papszParmList,"PROFILE");
|
|
if( pszProfile == NULL )
|
|
pszProfile = "GDALGeoTIFF";
|
|
|
|
if( CSLFetchBoolean( papszParmList, "TILED", FALSE ) )
|
|
bTiled = TRUE;
|
|
|
|
pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE");
|
|
if( pszValue != NULL )
|
|
nBlockXSize = atoi( pszValue );
|
|
|
|
pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE");
|
|
if( pszValue != NULL )
|
|
nBlockYSize = atoi( pszValue );
|
|
|
|
pszValue = CSLFetchNameValue(papszParmList,"INTERLEAVE");
|
|
if( pszValue != NULL )
|
|
{
|
|
if( EQUAL( pszValue, "PIXEL" ) )
|
|
nPlanar = PLANARCONFIG_CONTIG;
|
|
else if( EQUAL( pszValue, "BAND" ) )
|
|
nPlanar = PLANARCONFIG_SEPARATE;
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"INTERLEAVE=%s unsupported, value must be PIXEL or BAND.",
|
|
pszValue );
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nPlanar = PLANARCONFIG_CONTIG;
|
|
}
|
|
|
|
pszValue = CSLFetchNameValue( papszParmList, "COMPRESS" );
|
|
if( pszValue != NULL )
|
|
{
|
|
if( EQUAL( pszValue, "NONE" ) )
|
|
nCompression = COMPRESSION_NONE;
|
|
else if( EQUAL( pszValue, "JPEG" ) )
|
|
nCompression = COMPRESSION_JPEG;
|
|
else if( EQUAL( pszValue, "LZW" ) )
|
|
nCompression = COMPRESSION_LZW;
|
|
else if( EQUAL( pszValue, "PACKBITS" ))
|
|
nCompression = COMPRESSION_PACKBITS;
|
|
else if( EQUAL( pszValue, "DEFLATE" ) || EQUAL( pszValue, "ZIP" ))
|
|
nCompression = COMPRESSION_ADOBE_DEFLATE;
|
|
else if( EQUAL( pszValue, "FAX3" )
|
|
|| EQUAL( pszValue, "CCITTFAX3" ))
|
|
nCompression = COMPRESSION_CCITTFAX3;
|
|
else if( EQUAL( pszValue, "FAX4" )
|
|
|| EQUAL( pszValue, "CCITTFAX4" ))
|
|
nCompression = COMPRESSION_CCITTFAX4;
|
|
else if( EQUAL( pszValue, "CCITTRLE" ) )
|
|
nCompression = COMPRESSION_CCITTRLE;
|
|
else
|
|
CPLError( CE_Warning, CPLE_IllegalArg,
|
|
"COMPRESS=%s value not recognised, ignoring.",
|
|
pszValue );
|
|
|
|
#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION > 20031007 /* 3.6.0 */
|
|
if (nCompression != COMPRESSION_NONE &&
|
|
!TIFFIsCODECConfigured(nCompression))
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Cannot create TIFF file due to missing codec for %s.", pszValue );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
pszValue = CSLFetchNameValue( papszParmList, "PREDICTOR" );
|
|
if( pszValue != NULL )
|
|
nPredictor = atoi( pszValue );
|
|
|
|
nZLevel = GTiffGetZLevel(papszParmList);
|
|
|
|
nJpegQuality = GTiffGetJpegQuality(papszParmList);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute the uncompressed size. */
|
|
/* -------------------------------------------------------------------- */
|
|
double dfUncompressedImageSize;
|
|
|
|
dfUncompressedImageSize =
|
|
nXSize * ((double)nYSize) * nBands * (GDALGetDataTypeSize(eType)/8);
|
|
|
|
if( nCompression == COMPRESSION_NONE
|
|
&& dfUncompressedImageSize > 4200000000.0 )
|
|
{
|
|
#ifndef BIGTIFF_SUPPORT
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"A %d pixels x %d lines x %d bands %s image would be larger than 4GB\n"
|
|
"but this is the largest size a TIFF can be, and BigTIFF is unavailable.\n"
|
|
"Creation failed.",
|
|
nXSize, nYSize, nBands, GDALGetDataTypeName(eType) );
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Should the file be created as a bigtiff file? */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszBIGTIFF = CSLFetchNameValue(papszParmList, "BIGTIFF");
|
|
|
|
if( pszBIGTIFF == NULL )
|
|
pszBIGTIFF = "IF_NEEDED";
|
|
|
|
if( EQUAL(pszBIGTIFF,"IF_NEEDED") )
|
|
{
|
|
if( nCompression == COMPRESSION_NONE
|
|
&& dfUncompressedImageSize > 4200000000.0 )
|
|
bCreateBigTIFF = TRUE;
|
|
}
|
|
else if( EQUAL(pszBIGTIFF,"IF_SAFER") )
|
|
{
|
|
if( dfUncompressedImageSize > 2000000000.0 )
|
|
bCreateBigTIFF = TRUE;
|
|
}
|
|
|
|
else
|
|
bCreateBigTIFF = CSLTestBoolean( pszBIGTIFF );
|
|
|
|
#ifndef BIGTIFF_SUPPORT
|
|
if( bCreateBigTIFF )
|
|
{
|
|
CPLError( CE_Warning, CPLE_NotSupported,
|
|
"BigTIFF requested, but GDAL built without BigTIFF\n"
|
|
"enabled libtiff, request ignored." );
|
|
bCreateBigTIFF = FALSE;
|
|
}
|
|
#endif
|
|
|
|
if( bCreateBigTIFF )
|
|
CPLDebug( "GTiff", "File being created as a BigTIFF." );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check if the user wishes a particular endianness */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
int eEndianness = ENDIANNESS_NATIVE;
|
|
pszValue = CSLFetchNameValue(papszParmList, "ENDIANNESS");
|
|
if ( pszValue == NULL )
|
|
pszValue = CPLGetConfigOption( "GDAL_TIFF_ENDIANNESS", NULL );
|
|
if ( pszValue != NULL )
|
|
{
|
|
if (EQUAL(pszValue, "LITTLE"))
|
|
eEndianness = ENDIANNESS_LITTLE;
|
|
else if (EQUAL(pszValue, "BIG"))
|
|
eEndianness = ENDIANNESS_BIG;
|
|
else if (EQUAL(pszValue, "INVERTED"))
|
|
{
|
|
#ifdef CPL_LSB
|
|
eEndianness = ENDIANNESS_BIG;
|
|
#else
|
|
eEndianness = ENDIANNESS_LITTLE;
|
|
#endif
|
|
}
|
|
else if (!EQUAL(pszValue, "NATIVE"))
|
|
{
|
|
CPLError( CE_Warning, CPLE_NotSupported,
|
|
"ENDIANNESS=%s not supported. Defaulting to NATIVE", pszValue );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try opening the dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
char szOpeningFlag[5];
|
|
strcpy(szOpeningFlag, "w+");
|
|
if (bCreateBigTIFF)
|
|
strcat(szOpeningFlag, "8");
|
|
if (eEndianness == ENDIANNESS_BIG)
|
|
strcat(szOpeningFlag, "b");
|
|
else if (eEndianness == ENDIANNESS_LITTLE)
|
|
strcat(szOpeningFlag, "l");
|
|
hTIFF = VSI_TIFFOpen( pszFilename, szOpeningFlag );
|
|
if( hTIFF == NULL )
|
|
{
|
|
if( CPLGetLastErrorNo() == 0 )
|
|
CPLError( CE_Failure, CPLE_OpenFailed,
|
|
"Attempt to create new tiff file `%s'\n"
|
|
"failed in XTIFFOpen().\n",
|
|
pszFilename );
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* How many bits per sample? We have a special case if NBITS */
|
|
/* specified for GDT_Byte, GDT_UInt16, GDT_UInt32. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBitsPerSample = GDALGetDataTypeSize(eType);
|
|
if (CSLFetchNameValue(papszParmList, "NBITS") != NULL)
|
|
{
|
|
int nMinBits = 0, nMaxBits = 0;
|
|
nBitsPerSample = atoi(CSLFetchNameValue(papszParmList, "NBITS"));
|
|
if( eType == GDT_Byte )
|
|
{
|
|
nMinBits = 1;
|
|
nMaxBits = 8;
|
|
}
|
|
else if( eType == GDT_UInt16 )
|
|
{
|
|
nMinBits = 9;
|
|
nMaxBits = 16;
|
|
}
|
|
else if( eType == GDT_UInt32 )
|
|
{
|
|
nMinBits = 17;
|
|
nMaxBits = 32;
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Warning, CPLE_NotSupported,
|
|
"NBITS is not supported for data type %s",
|
|
GDALGetDataTypeName(eType));
|
|
nBitsPerSample = GDALGetDataTypeSize(eType);
|
|
}
|
|
|
|
if (nMinBits != 0)
|
|
{
|
|
if (nBitsPerSample < nMinBits)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"NBITS=%d is invalid for data type %s. Using NBITS=%d",
|
|
nBitsPerSample, GDALGetDataTypeName(eType), nMinBits);
|
|
nBitsPerSample = nMinBits;
|
|
}
|
|
else if (nBitsPerSample > nMaxBits)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"NBITS=%d is invalid for data type %s. Using NBITS=%d",
|
|
nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits);
|
|
nBitsPerSample = nMaxBits;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a custom pixel type (just used for signed byte now). */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszPixelType = CSLFetchNameValue( papszParmList, "PIXELTYPE" );
|
|
if( pszPixelType == NULL )
|
|
pszPixelType = "";
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup some standard flags. */
|
|
/* -------------------------------------------------------------------- */
|
|
TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize );
|
|
TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize );
|
|
TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerSample );
|
|
|
|
if( (eType == GDT_Byte && EQUAL(pszPixelType,"SIGNEDBYTE"))
|
|
|| eType == GDT_Int16 || eType == GDT_Int32 )
|
|
nSampleFormat = SAMPLEFORMAT_INT;
|
|
else if( eType == GDT_CInt16 || eType == GDT_CInt32 )
|
|
nSampleFormat = SAMPLEFORMAT_COMPLEXINT;
|
|
else if( eType == GDT_Float32 || eType == GDT_Float64 )
|
|
nSampleFormat = SAMPLEFORMAT_IEEEFP;
|
|
else if( eType == GDT_CFloat32 || eType == GDT_CFloat64 )
|
|
nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP;
|
|
else
|
|
nSampleFormat = SAMPLEFORMAT_UINT;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat );
|
|
TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nBands );
|
|
TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, nPlanar );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup Photometric Interpretation. Take this value from the user */
|
|
/* passed option or guess correct value otherwise. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nSamplesAccountedFor = 1;
|
|
int bForceColorTable = FALSE;
|
|
|
|
pszValue = CSLFetchNameValue(papszParmList,"PHOTOMETRIC");
|
|
if( pszValue != NULL )
|
|
{
|
|
if( EQUAL( pszValue, "MINISBLACK" ) )
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
|
|
else if( EQUAL( pszValue, "MINISWHITE" ) )
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE );
|
|
else if( EQUAL( pszValue, "PALETTE" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
|
|
nSamplesAccountedFor = 1;
|
|
bForceColorTable = TRUE;
|
|
}
|
|
else if( EQUAL( pszValue, "RGB" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else if( EQUAL( pszValue, "CMYK" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED );
|
|
nSamplesAccountedFor = 4;
|
|
}
|
|
else if( EQUAL( pszValue, "YCBCR" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else if( EQUAL( pszValue, "CIELAB" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else if( EQUAL( pszValue, "ICCLAB" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else if( EQUAL( pszValue, "ITULAB" ))
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Warning, CPLE_IllegalArg,
|
|
"PHOTOMETRIC=%s value not recognised, ignoring.\n"
|
|
"Set the Photometric Interpretation as MINISBLACK.",
|
|
pszValue );
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
|
|
}
|
|
|
|
if ( nBands < nSamplesAccountedFor )
|
|
{
|
|
CPLError( CE_Warning, CPLE_IllegalArg,
|
|
"PHOTOMETRIC=%s value does not correspond to number "
|
|
"of bands (%d), ignoring.\n"
|
|
"Set the Photometric Interpretation as MINISBLACK.",
|
|
pszValue, nBands );
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If image contains 3 or 4 bands and datatype is Byte then we will
|
|
* assume it is RGB. In all other cases assume it is MINISBLACK.
|
|
*/
|
|
if( nBands == 3 && eType == GDT_Byte )
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
|
|
nSamplesAccountedFor = 3;
|
|
}
|
|
else if( nBands == 4 && eType == GDT_Byte )
|
|
{
|
|
uint16 v[1];
|
|
|
|
v[0] = EXTRASAMPLE_ASSOCALPHA;
|
|
TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
|
|
nSamplesAccountedFor = 4;
|
|
}
|
|
else
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
|
|
nSamplesAccountedFor = 1;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If there are extra samples, we need to mark them with an */
|
|
/* appropriate extrasamples definition here. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBands > nSamplesAccountedFor )
|
|
{
|
|
uint16 *v;
|
|
int i;
|
|
int nExtraSamples = nBands - nSamplesAccountedFor;
|
|
|
|
v = (uint16 *) CPLMalloc( sizeof(uint16) * nExtraSamples );
|
|
|
|
if( CSLFetchBoolean(papszParmList,"ALPHA",FALSE) )
|
|
v[0] = EXTRASAMPLE_ASSOCALPHA;
|
|
else
|
|
v[0] = EXTRASAMPLE_UNSPECIFIED;
|
|
|
|
for( i = 1; i < nExtraSamples; i++ )
|
|
v[i] = EXTRASAMPLE_UNSPECIFIED;
|
|
|
|
TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v );
|
|
|
|
CPLFree(v);
|
|
}
|
|
|
|
/* Set the compression method before asking the default strip size */
|
|
/* This is usefull when translating to a JPEG-In-TIFF file where */
|
|
/* the default strip size is 8 or 16 depending on the photometric value */
|
|
TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompression );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup tiling/stripping flags. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bTiled )
|
|
{
|
|
if( nBlockXSize == 0 )
|
|
nBlockXSize = 256;
|
|
|
|
if( nBlockYSize == 0 )
|
|
nBlockYSize = 256;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize );
|
|
TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize );
|
|
}
|
|
else
|
|
{
|
|
uint32 nRowsPerStrip;
|
|
|
|
if( nBlockYSize == 0 )
|
|
nRowsPerStrip = MIN(nYSize, (int)TIFFDefaultStripSize(hTIFF,0));
|
|
else
|
|
nRowsPerStrip = nBlockYSize;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nRowsPerStrip );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set compression related tags. */
|
|
/* -------------------------------------------------------------------- */
|
|
if ( nCompression == COMPRESSION_LZW ||
|
|
nCompression == COMPRESSION_ADOBE_DEFLATE )
|
|
TIFFSetField( hTIFF, TIFFTAG_PREDICTOR, nPredictor );
|
|
if (nCompression == COMPRESSION_ADOBE_DEFLATE
|
|
&& nZLevel != -1)
|
|
TIFFSetField( hTIFF, TIFFTAG_ZIPQUALITY, nZLevel );
|
|
if( nCompression == COMPRESSION_JPEG
|
|
&& nJpegQuality != -1 )
|
|
TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we forced production of a file with photometric=palette, */
|
|
/* we need to push out a default color table. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bForceColorTable )
|
|
{
|
|
int nColors;
|
|
|
|
if( eType == GDT_Byte )
|
|
nColors = 256;
|
|
else
|
|
nColors = 65536;
|
|
|
|
unsigned short *panTRed, *panTGreen, *panTBlue;
|
|
|
|
panTRed = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
panTGreen = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
panTBlue = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors);
|
|
|
|
for( int iColor = 0; iColor < nColors; iColor++ )
|
|
{
|
|
if( eType == GDT_Byte )
|
|
{
|
|
panTRed[iColor] = (unsigned short) (257 * iColor);
|
|
panTGreen[iColor] = (unsigned short) (257 * iColor);
|
|
panTBlue[iColor] = (unsigned short) (257 * iColor);
|
|
}
|
|
else
|
|
{
|
|
panTRed[iColor] = (unsigned short) iColor;
|
|
panTGreen[iColor] = (unsigned short) iColor;
|
|
panTBlue[iColor] = (unsigned short) iColor;
|
|
}
|
|
}
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_COLORMAP,
|
|
panTRed, panTGreen, panTBlue );
|
|
|
|
CPLFree( panTRed );
|
|
CPLFree( panTGreen );
|
|
CPLFree( panTBlue );
|
|
}
|
|
|
|
return( hTIFF );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create() */
|
|
/* */
|
|
/* Create a new GeoTIFF or TIFF file. */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *GTiffDataset::Create( const char * pszFilename,
|
|
int nXSize, int nYSize, int nBands,
|
|
GDALDataType eType,
|
|
char **papszParmList )
|
|
|
|
{
|
|
GTiffDataset * poDS;
|
|
TIFF *hTIFF;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the underlying TIFF file. */
|
|
/* -------------------------------------------------------------------- */
|
|
hTIFF = CreateLL( pszFilename, nXSize, nYSize, nBands,
|
|
eType, papszParmList );
|
|
|
|
if( hTIFF == NULL )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the new GTiffDataset object. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS = new GTiffDataset();
|
|
poDS->hTIFF = hTIFF;
|
|
|
|
poDS->nRasterXSize = nXSize;
|
|
poDS->nRasterYSize = nYSize;
|
|
poDS->eAccess = GA_Update;
|
|
poDS->bNewDataset = TRUE;
|
|
poDS->bCrystalized = FALSE;
|
|
poDS->nSamplesPerPixel = (uint16) nBands;
|
|
|
|
TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->nSampleFormat) );
|
|
TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->nPlanarConfig) );
|
|
TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->nPhotometric) );
|
|
TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->nBitsPerSample) );
|
|
TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(poDS->nCompression) );
|
|
|
|
if( TIFFIsTiled(hTIFF) )
|
|
{
|
|
TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(poDS->nBlockXSize) );
|
|
TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(poDS->nBlockYSize) );
|
|
}
|
|
else
|
|
{
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP,
|
|
&(poDS->nRowsPerStrip) ) )
|
|
poDS->nRowsPerStrip = 1; /* dummy value */
|
|
|
|
poDS->nBlockXSize = nXSize;
|
|
poDS->nBlockYSize = MIN((int)poDS->nRowsPerStrip,nYSize);
|
|
}
|
|
|
|
poDS->nBlocksPerBand =
|
|
((nYSize + poDS->nBlockYSize - 1) / poDS->nBlockYSize)
|
|
* ((nXSize + poDS->nBlockXSize - 1) / poDS->nBlockXSize);
|
|
|
|
if( CSLFetchNameValue( papszParmList, "PROFILE" ) != NULL )
|
|
poDS->osProfile = CSLFetchNameValue( papszParmList, "PROFILE" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* YCbCr JPEG compressed images should be translated on the fly */
|
|
/* to RGB by libtiff/libjpeg unless specifically requested */
|
|
/* otherwise. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poDS->nCompression == COMPRESSION_JPEG
|
|
&& poDS->nPhotometric == PHOTOMETRIC_YCBCR
|
|
&& CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB",
|
|
"YES") ) )
|
|
{
|
|
int nColorMode;
|
|
|
|
poDS->SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE" );
|
|
if ( !TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ) ||
|
|
nColorMode != JPEGCOLORMODE_RGB )
|
|
TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read palette back as a color table if it has one. */
|
|
/* -------------------------------------------------------------------- */
|
|
unsigned short *panRed, *panGreen, *panBlue;
|
|
|
|
if( poDS->nPhotometric == PHOTOMETRIC_PALETTE
|
|
&& TIFFGetField( hTIFF, TIFFTAG_COLORMAP,
|
|
&panRed, &panGreen, &panBlue) )
|
|
{
|
|
int nColorCount;
|
|
GDALColorEntry oEntry;
|
|
|
|
poDS->poColorTable = new GDALColorTable();
|
|
|
|
nColorCount = 1 << poDS->nBitsPerSample;
|
|
|
|
for( int iColor = nColorCount - 1; iColor >= 0; iColor-- )
|
|
{
|
|
oEntry.c1 = panRed[iColor] / 256;
|
|
oEntry.c2 = panGreen[iColor] / 256;
|
|
oEntry.c3 = panBlue[iColor] / 256;
|
|
oEntry.c4 = 255;
|
|
|
|
poDS->poColorTable->SetColorEntry( iColor, &oEntry );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we want to ensure all blocks get written out on close to */
|
|
/* avoid sparse files? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !CSLFetchBoolean( papszParmList, "SPARSE_OK", FALSE ) )
|
|
poDS->bFillEmptyTiles = TRUE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Preserve creation options for consulting later (for instance */
|
|
/* to decide if a TFW file should be written). */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->papszCreationOptions = CSLDuplicate( papszParmList );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create band information objects. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBand;
|
|
|
|
for( iBand = 0; iBand < nBands; iBand++ )
|
|
{
|
|
if( poDS->nBitsPerSample == 8 ||
|
|
poDS->nBitsPerSample == 16 ||
|
|
poDS->nBitsPerSample == 32 ||
|
|
poDS->nBitsPerSample == 64 ||
|
|
poDS->nBitsPerSample == 128)
|
|
poDS->SetBand( iBand+1, new GTiffRasterBand( poDS, iBand+1 ) );
|
|
else
|
|
poDS->SetBand( iBand+1, new GTiffOddBitsBand( poDS, iBand+1 ) );
|
|
}
|
|
|
|
return( poDS );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateCopy() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *
|
|
GTiffDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
|
|
int bStrict, char ** papszOptions,
|
|
GDALProgressFunc pfnProgress, void * pProgressData )
|
|
|
|
{
|
|
TIFF *hTIFF;
|
|
int nXSize = poSrcDS->GetRasterXSize();
|
|
int nYSize = poSrcDS->GetRasterYSize();
|
|
int nBands = poSrcDS->GetRasterCount();
|
|
int iBand;
|
|
CPLErr eErr = CE_None;
|
|
uint16 nPlanarConfig;
|
|
uint16 nBitsPerSample;
|
|
GDALRasterBand *poPBand;
|
|
|
|
if( poSrcDS->GetRasterCount() == 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unable to export GeoTIFF files with zero bands." );
|
|
return NULL;
|
|
}
|
|
|
|
poPBand = poSrcDS->GetRasterBand(1);
|
|
GDALDataType eType = poPBand->GetRasterDataType();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check, whether all bands in input dataset has the same type. */
|
|
/* -------------------------------------------------------------------- */
|
|
for ( iBand = 2; iBand <= nBands; iBand++ )
|
|
{
|
|
if ( eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType() )
|
|
{
|
|
if ( bStrict )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unable to export GeoTIFF file with different datatypes per\n"
|
|
"different bands. All bands should have the same types in TIFF." );
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"Unable to export GeoTIFF file with different datatypes per\n"
|
|
"different bands. All bands should have the same types in TIFF." );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !pfnProgress( 0.0, NULL, pProgressData ) )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture the profile. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszProfile;
|
|
int bGeoTIFF;
|
|
|
|
pszProfile = CSLFetchNameValue(papszOptions,"PROFILE");
|
|
if( pszProfile == NULL )
|
|
pszProfile = "GDALGeoTIFF";
|
|
|
|
if( !EQUAL(pszProfile,"BASELINE")
|
|
&& !EQUAL(pszProfile,"GeoTIFF")
|
|
&& !EQUAL(pszProfile,"GDALGeoTIFF") )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"PROFILE=%s not supported in GTIFF driver.",
|
|
pszProfile );
|
|
return NULL;
|
|
}
|
|
|
|
if( EQUAL(pszProfile,"BASELINE") )
|
|
bGeoTIFF = FALSE;
|
|
else
|
|
bGeoTIFF = TRUE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special handling for NBITS. Copy from band metadata if found. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszCreateOptions = CSLDuplicate( papszOptions );
|
|
|
|
if( poPBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) != NULL
|
|
&& atoi(poPBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" )) > 0
|
|
&& CSLFetchNameValue( papszCreateOptions, "NBITS") == NULL )
|
|
{
|
|
papszCreateOptions =
|
|
CSLSetNameValue( papszCreateOptions, "NBITS",
|
|
poPBand->GetMetadataItem( "NBITS",
|
|
"IMAGE_STRUCTURE" ) );
|
|
}
|
|
|
|
if( CSLFetchNameValue( papszOptions, "PIXELTYPE" ) == NULL
|
|
&& eType == GDT_Byte
|
|
&& poPBand->GetMetadataItem( "PIXELTYPE", "IMAGE_STRUCTURE" ) )
|
|
{
|
|
papszCreateOptions =
|
|
CSLSetNameValue( papszCreateOptions, "PIXELTYPE",
|
|
poPBand->GetMetadataItem(
|
|
"PIXELTYPE", "IMAGE_STRUCTURE" ) );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the file. */
|
|
/* -------------------------------------------------------------------- */
|
|
hTIFF = CreateLL( pszFilename, nXSize, nYSize, nBands,
|
|
eType, papszCreateOptions );
|
|
|
|
CSLDestroy( papszCreateOptions );
|
|
|
|
if( hTIFF == NULL )
|
|
return NULL;
|
|
|
|
TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &nPlanarConfig );
|
|
TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Are we really producing an RGBA image? If so, set the */
|
|
/* associated alpha information. */
|
|
/* -------------------------------------------------------------------- */
|
|
int bForcePhotometric =
|
|
CSLFetchNameValue(papszOptions,"PHOTOMETRIC") != NULL;
|
|
|
|
if( nBands == 4 && !bForcePhotometric
|
|
&& poSrcDS->GetRasterBand(4)->GetColorInterpretation()==GCI_AlphaBand)
|
|
{
|
|
uint16 v[1];
|
|
|
|
v[0] = EXTRASAMPLE_ASSOCALPHA;
|
|
TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v);
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the output is jpeg compressed, and the input is RGB make */
|
|
/* sure we note that. */
|
|
/* -------------------------------------------------------------------- */
|
|
uint16 nCompression;
|
|
|
|
if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) )
|
|
nCompression = COMPRESSION_NONE;
|
|
|
|
if( nCompression == COMPRESSION_JPEG )
|
|
{
|
|
if( nBands >= 3
|
|
&& (poSrcDS->GetRasterBand(1)->GetColorInterpretation()
|
|
== GCI_YCbCr_YBand)
|
|
&& (poSrcDS->GetRasterBand(2)->GetColorInterpretation()
|
|
== GCI_YCbCr_CbBand)
|
|
&& (poSrcDS->GetRasterBand(3)->GetColorInterpretation()
|
|
== GCI_YCbCr_CrBand) )
|
|
{
|
|
/* do nothing ... */
|
|
}
|
|
else
|
|
{
|
|
/* we assume RGB if it isn't explicitly YCbCr */
|
|
CPLDebug( "GTiff", "Setting JPEGCOLORMODE_RGB" );
|
|
TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does the source image consist of one band, with a palette? */
|
|
/* If so, copy over. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL
|
|
&& eType == GDT_Byte )
|
|
{
|
|
unsigned short anTRed[256], anTGreen[256], anTBlue[256];
|
|
GDALColorTable *poCT;
|
|
|
|
poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
|
|
|
|
for( int iColor = 0; iColor < 256; iColor++ )
|
|
{
|
|
if( iColor < poCT->GetColorEntryCount() )
|
|
{
|
|
GDALColorEntry sRGB;
|
|
|
|
poCT->GetColorEntryAsRGB( iColor, &sRGB );
|
|
|
|
anTRed[iColor] = (unsigned short) (257 * sRGB.c1);
|
|
anTGreen[iColor] = (unsigned short) (257 * sRGB.c2);
|
|
anTBlue[iColor] = (unsigned short) (257 * sRGB.c3);
|
|
}
|
|
else
|
|
{
|
|
anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0;
|
|
}
|
|
}
|
|
|
|
if( !bForcePhotometric )
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
|
|
TIFFSetField( hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue );
|
|
}
|
|
else if( nBands == 1
|
|
&& poSrcDS->GetRasterBand(1)->GetColorTable() != NULL
|
|
&& eType == GDT_UInt16 )
|
|
{
|
|
unsigned short *panTRed, *panTGreen, *panTBlue;
|
|
GDALColorTable *poCT;
|
|
|
|
panTRed = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short));
|
|
panTGreen = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short));
|
|
panTBlue = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short));
|
|
|
|
poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
|
|
|
|
for( int iColor = 0; iColor < 65536; iColor++ )
|
|
{
|
|
if( iColor < poCT->GetColorEntryCount() )
|
|
{
|
|
GDALColorEntry sRGB;
|
|
|
|
poCT->GetColorEntryAsRGB( iColor, &sRGB );
|
|
|
|
panTRed[iColor] = (unsigned short) (256 * sRGB.c1);
|
|
panTGreen[iColor] = (unsigned short) (256 * sRGB.c2);
|
|
panTBlue[iColor] = (unsigned short) (256 * sRGB.c3);
|
|
}
|
|
else
|
|
{
|
|
panTRed[iColor] = panTGreen[iColor] = panTBlue[iColor] = 0;
|
|
}
|
|
}
|
|
|
|
if( !bForcePhotometric )
|
|
TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE );
|
|
TIFFSetField( hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue );
|
|
|
|
CPLFree( panTRed );
|
|
CPLFree( panTGreen );
|
|
CPLFree( panTBlue );
|
|
}
|
|
else if( poSrcDS->GetRasterBand(1)->GetColorTable() != NULL )
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"Unable to export color table to GeoTIFF file. Color tables\n"
|
|
"can only be written to 1 band Byte or UInt16 GeoTIFF files." );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Transfer some TIFF specific metadata, if available. */
|
|
/* The return value will tell us if we need to try again later with*/
|
|
/* PAM because the profile doesn't allow to write some metadata */
|
|
/* as TIFF tag */
|
|
/* -------------------------------------------------------------------- */
|
|
int bHasWrittenMDInGeotiffTAG =
|
|
GTiffDataset::WriteMetadata( poSrcDS, hTIFF, FALSE, pszProfile,
|
|
pszFilename, papszOptions );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write NoData value, if exist. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(pszProfile,"GDALGeoTIFF") )
|
|
{
|
|
int bSuccess;
|
|
double dfNoData;
|
|
|
|
dfNoData = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bSuccess );
|
|
if ( bSuccess )
|
|
GTiffDataset::WriteNoDataValue( hTIFF, dfNoData );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write affine transform if it is meaningful. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszProjection = NULL;
|
|
double adfGeoTransform[6];
|
|
|
|
if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None
|
|
&& (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
|
|
|| adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
|
|
|| adfGeoTransform[4] != 0.0 || ABS(adfGeoTransform[5]) != 1.0 ))
|
|
{
|
|
if( bGeoTIFF )
|
|
{
|
|
if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0
|
|
&& adfGeoTransform[5] < 0.0 )
|
|
{
|
|
|
|
double adfPixelScale[3], adfTiePoints[6];
|
|
|
|
adfPixelScale[0] = adfGeoTransform[1];
|
|
adfPixelScale[1] = fabs(adfGeoTransform[5]);
|
|
adfPixelScale[2] = 0.0;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
|
|
|
|
adfTiePoints[0] = 0.0;
|
|
adfTiePoints[1] = 0.0;
|
|
adfTiePoints[2] = 0.0;
|
|
adfTiePoints[3] = adfGeoTransform[0];
|
|
adfTiePoints[4] = adfGeoTransform[3];
|
|
adfTiePoints[5] = 0.0;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
|
|
}
|
|
else
|
|
{
|
|
double adfMatrix[16];
|
|
|
|
memset(adfMatrix,0,sizeof(double) * 16);
|
|
|
|
adfMatrix[0] = adfGeoTransform[1];
|
|
adfMatrix[1] = adfGeoTransform[2];
|
|
adfMatrix[3] = adfGeoTransform[0];
|
|
adfMatrix[4] = adfGeoTransform[4];
|
|
adfMatrix[5] = adfGeoTransform[5];
|
|
adfMatrix[7] = adfGeoTransform[3];
|
|
adfMatrix[15] = 1.0;
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix );
|
|
}
|
|
|
|
pszProjection = poSrcDS->GetProjectionRef();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we need a TFW file? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CSLFetchBoolean( papszOptions, "TFW", FALSE ) )
|
|
GDALWriteWorldFile( pszFilename, "tfw", adfGeoTransform );
|
|
else if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
|
|
GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise write tiepoints if they are available. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( poSrcDS->GetGCPCount() > 0 && bGeoTIFF )
|
|
{
|
|
const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
|
|
double *padfTiePoints;
|
|
|
|
padfTiePoints = (double *)
|
|
CPLMalloc(6*sizeof(double)*poSrcDS->GetGCPCount());
|
|
|
|
for( int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); iGCP++ )
|
|
{
|
|
|
|
padfTiePoints[iGCP*6+0] = pasGCPs[iGCP].dfGCPPixel;
|
|
padfTiePoints[iGCP*6+1] = pasGCPs[iGCP].dfGCPLine;
|
|
padfTiePoints[iGCP*6+2] = 0;
|
|
padfTiePoints[iGCP*6+3] = pasGCPs[iGCP].dfGCPX;
|
|
padfTiePoints[iGCP*6+4] = pasGCPs[iGCP].dfGCPY;
|
|
padfTiePoints[iGCP*6+5] = pasGCPs[iGCP].dfGCPZ;
|
|
}
|
|
|
|
TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS,
|
|
6*poSrcDS->GetGCPCount(), padfTiePoints );
|
|
CPLFree( padfTiePoints );
|
|
|
|
pszProjection = poSrcDS->GetGCPProjection();
|
|
|
|
if( CSLFetchBoolean( papszOptions, "TFW", FALSE )
|
|
|| CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"TFW=ON or WORLDFILE=ON creation options are ignored when GCPs are available");
|
|
}
|
|
}
|
|
|
|
else
|
|
pszProjection = poSrcDS->GetProjectionRef();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write the projection information, if possible. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszProjection != NULL && strlen(pszProjection) > 0 && bGeoTIFF )
|
|
{
|
|
GTIF *psGTIF;
|
|
|
|
psGTIF = GTIFNew( hTIFF );
|
|
GTIFSetFromOGISDefn( psGTIF, pszProjection );
|
|
|
|
if( poSrcDS->GetMetadataItem( GDALMD_AREA_OR_POINT )
|
|
&& EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT),
|
|
GDALMD_AOP_POINT) )
|
|
{
|
|
GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
|
|
RasterPixelIsPoint);
|
|
}
|
|
|
|
GTIFWriteKeys( psGTIF );
|
|
GTIFFree( psGTIF );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTiffCreateCopy()");
|
|
TIFFWriteDirectory( hTIFF );
|
|
TIFFFlush( hTIFF );
|
|
XTIFFClose( hTIFF );
|
|
hTIFF = NULL;
|
|
|
|
if( eErr != CE_None )
|
|
{
|
|
VSIUnlink( pszFilename );
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Re-open as a dataset and copy over missing metadata using */
|
|
/* PAM facilities. */
|
|
/* -------------------------------------------------------------------- */
|
|
GTiffDataset *poDS;
|
|
CPLString osFileName("GTIFF_RAW:");
|
|
|
|
osFileName += pszFilename;
|
|
|
|
poDS = (GTiffDataset *) GDALOpen( osFileName, GA_Update );
|
|
if( poDS == NULL )
|
|
poDS = (GTiffDataset *) GDALOpen( osFileName, GA_ReadOnly );
|
|
|
|
if ( poDS == NULL )
|
|
{
|
|
VSIUnlink( pszFilename );
|
|
return NULL;
|
|
}
|
|
|
|
poDS->osProfile = pszProfile;
|
|
poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* CloneInfo() doesn't merge metadata, it just replaces it totally */
|
|
/* So we have to merge it */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
char **papszSRC_MD = poSrcDS->GetMetadata();
|
|
char **papszDST_MD = CSLDuplicate(poDS->GetMetadata());
|
|
|
|
papszDST_MD = CSLMerge( papszDST_MD, papszSRC_MD );
|
|
|
|
poDS->SetMetadata( papszDST_MD );
|
|
CSLDestroy( papszDST_MD );
|
|
|
|
/* Depending on the PHOTOMETRIC tag, the TIFF file may not have */
|
|
/* the same band count as the source. Will fail later in GDALDatasetCopyWholeRaster anyway... */
|
|
for( int nBand = 1;
|
|
nBand <= MIN(poDS->GetRasterCount(), poSrcDS->GetRasterCount()) ;
|
|
nBand++ )
|
|
{
|
|
char **papszSRC_MD = poSrcDS->GetRasterBand(nBand)->GetMetadata();
|
|
char **papszDST_MD = CSLDuplicate(poDS->GetRasterBand(nBand)->GetMetadata());
|
|
|
|
papszDST_MD = CSLMerge( papszDST_MD, papszSRC_MD );
|
|
|
|
poDS->GetRasterBand(nBand)->SetMetadata( papszDST_MD );
|
|
CSLDestroy( papszDST_MD );
|
|
}
|
|
|
|
hTIFF = (TIFF*) poDS->GetInternalHandle(NULL);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Second chance : now that we have a PAM dataset, it is possible */
|
|
/* to write metadata that we couldn't be writen as TIFF tag */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!bHasWrittenMDInGeotiffTAG)
|
|
GTiffDataset::WriteMetadata( poDS, hTIFF, TRUE, pszProfile,
|
|
pszFilename, papszOptions, TRUE /* don't write RPC and IMG file again */);
|
|
|
|
/* We must re-set the compression level at this point, since it has */
|
|
/* been lost a few lines above when closing the newly create TIFF file */
|
|
/* The TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file. */
|
|
/* They are just TIFF session parameters */
|
|
if (nCompression == COMPRESSION_ADOBE_DEFLATE)
|
|
{
|
|
int nZLevel = GTiffGetZLevel(papszOptions);
|
|
if (nZLevel != -1)
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_ZIPQUALITY, nZLevel );
|
|
}
|
|
}
|
|
else if( nCompression == COMPRESSION_JPEG)
|
|
{
|
|
int nJpegQuality = GTiffGetJpegQuality(papszOptions);
|
|
if (nJpegQuality != -1)
|
|
{
|
|
TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy actual imagery. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
char* papszCopyWholeRasterOptions[2] = { NULL, NULL };
|
|
if (nCompression != COMPRESSION_NONE)
|
|
papszCopyWholeRasterOptions[0] = (char*) "COMPRESSED=YES";
|
|
eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS,
|
|
(GDALDatasetH) poDS,
|
|
papszCopyWholeRasterOptions, pfnProgress, pProgressData );
|
|
|
|
if( eErr == CE_Failure )
|
|
{
|
|
delete poDS;
|
|
poDS = NULL;
|
|
|
|
VSIUnlink( pszFilename ); // should really delete more carefully.
|
|
}
|
|
|
|
return poDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetProjectionRef() */
|
|
/************************************************************************/
|
|
|
|
const char *GTiffDataset::GetProjectionRef()
|
|
|
|
{
|
|
if( nGCPCount == 0 )
|
|
{
|
|
if( EQUAL(pszProjection,"") )
|
|
return GDALPamDataset::GetProjectionRef();
|
|
else
|
|
return( pszProjection );
|
|
}
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetProjection() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::SetProjection( const char * pszNewProjection )
|
|
|
|
{
|
|
if( !EQUALN(pszNewProjection,"GEOGCS",6)
|
|
&& !EQUALN(pszNewProjection,"PROJCS",6)
|
|
&& !EQUALN(pszNewProjection,"LOCAL_CS",6)
|
|
&& !EQUAL(pszNewProjection,"") )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Only OGC WKT Projections supported for writing to GeoTIFF.\n"
|
|
"%s not supported.",
|
|
pszNewProjection );
|
|
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLFree( pszProjection );
|
|
pszProjection = CPLStrdup( pszNewProjection );
|
|
|
|
bGeoTIFFInfoChanged = TRUE;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::GetGeoTransform( double * padfTransform )
|
|
|
|
{
|
|
memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
|
|
|
|
if( !bGeoTransformValid )
|
|
return CE_Failure;
|
|
else
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::SetGeoTransform( double * padfTransform )
|
|
|
|
{
|
|
if( GetAccess() == GA_Update )
|
|
{
|
|
memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
|
|
bGeoTransformValid = TRUE;
|
|
bGeoTIFFInfoChanged = TRUE;
|
|
|
|
return( CE_None );
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SetGeoTransform() is only supported on newly created GeoTIFF files." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPCount() */
|
|
/************************************************************************/
|
|
|
|
int GTiffDataset::GetGCPCount()
|
|
|
|
{
|
|
return nGCPCount;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPProjection() */
|
|
/************************************************************************/
|
|
|
|
const char *GTiffDataset::GetGCPProjection()
|
|
|
|
{
|
|
if( nGCPCount > 0 )
|
|
return pszProjection;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPs() */
|
|
/************************************************************************/
|
|
|
|
const GDAL_GCP *GTiffDataset::GetGCPs()
|
|
|
|
{
|
|
return pasGCPList;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetGCPs() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
|
|
const char *pszGCPProjection )
|
|
{
|
|
if( GetAccess() == GA_Update )
|
|
{
|
|
if( this->nGCPCount > 0 )
|
|
{
|
|
GDALDeinitGCPs( this->nGCPCount, this->pasGCPList );
|
|
CPLFree( this->pasGCPList );
|
|
}
|
|
|
|
this->nGCPCount = nGCPCount;
|
|
this->pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPList);
|
|
|
|
CPLFree( this->pszProjection );
|
|
this->pszProjection = CPLStrdup( pszGCPProjection );
|
|
bGeoTIFFInfoChanged = TRUE;
|
|
|
|
return CE_None;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SetGCPs() is only supported on newly created GeoTIFF files." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadata() */
|
|
/************************************************************************/
|
|
|
|
char **GTiffDataset::GetMetadata( const char * pszDomain )
|
|
|
|
{
|
|
return oGTiffMDMD.GetMetadata( pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetMetadata() */
|
|
/************************************************************************/
|
|
CPLErr GTiffDataset::SetMetadata( char ** papszMD, const char *pszDomain )
|
|
|
|
{
|
|
if( pszDomain == NULL || EQUAL(pszDomain,"_temporary_") )
|
|
bMetadataChanged = TRUE;
|
|
|
|
return oGTiffMDMD.SetMetadata( papszMD, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
const char *GTiffDataset::GetMetadataItem( const char * pszName,
|
|
const char * pszDomain )
|
|
|
|
{
|
|
return oGTiffMDMD.GetMetadataItem( pszName, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::SetMetadataItem( const char *pszName,
|
|
const char *pszValue,
|
|
const char *pszDomain )
|
|
|
|
{
|
|
if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") )
|
|
bMetadataChanged = TRUE;
|
|
|
|
return oGTiffMDMD.SetMetadataItem( pszName, pszValue, pszDomain );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetInternalHandle() */
|
|
/************************************************************************/
|
|
|
|
void *GTiffDataset::GetInternalHandle( const char * /* pszHandleName */ )
|
|
|
|
{
|
|
return hTIFF;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetFileList() */
|
|
/************************************************************************/
|
|
|
|
char **GTiffDataset::GetFileList()
|
|
|
|
{
|
|
char **papszFileList = GDALPamDataset::GetFileList();
|
|
VSIStatBufL sStatBuf;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for .imd file. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osTarget = CPLResetExtension( osFilename, "IMD" );
|
|
if( VSIStatL( osTarget, &sStatBuf ) == 0 )
|
|
papszFileList = CSLAddString( papszFileList, osTarget );
|
|
else
|
|
{
|
|
osTarget = CPLResetExtension( osFilename, "imd" );
|
|
if( VSIStatL( osTarget, &sStatBuf ) == 0 )
|
|
papszFileList = CSLAddString( papszFileList, osTarget );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for .rpb file. */
|
|
/* -------------------------------------------------------------------- */
|
|
osTarget = CPLResetExtension( osFilename, "RPB" );
|
|
if( VSIStatL( osTarget, &sStatBuf ) == 0 )
|
|
papszFileList = CSLAddString( papszFileList, osTarget );
|
|
else
|
|
{
|
|
osTarget = CPLResetExtension( osFilename, "rpb" );
|
|
if( VSIStatL( osTarget, &sStatBuf ) == 0 )
|
|
papszFileList = CSLAddString( papszFileList, osTarget );
|
|
}
|
|
|
|
return papszFileList;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateMaskBand() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffDataset::CreateMaskBand(int nFlags)
|
|
{
|
|
if (poMaskDS != NULL)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"This TIFF dataset has already an internal mask band");
|
|
return CE_Failure;
|
|
}
|
|
else if (CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "NO")))
|
|
{
|
|
toff_t nOffset;
|
|
int bIsTiled;
|
|
int bIsOverview = FALSE;
|
|
uint32 nSubType;
|
|
|
|
if (nFlags != GMF_PER_DATASET)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"The only flag value supported for internal mask is GMF_PER_DATASET");
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have read access, then create the mask externally. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( GetAccess() != GA_Update )
|
|
{
|
|
CPLError( CE_Warning, CPLE_AppDefined,
|
|
"File open for read-only accessing, "
|
|
"creating mask externally." );
|
|
|
|
return GDALPamDataset::CreateMaskBand(nFlags);
|
|
}
|
|
|
|
if (poBaseDS)
|
|
poBaseDS->SetDirectory();
|
|
SetDirectory();
|
|
|
|
if( TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType))
|
|
{
|
|
bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0;
|
|
|
|
if ((nSubType & FILETYPE_MASK) != 0)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Cannot create a mask on a TIFF mask IFD !" );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
TIFFFlush( hTIFF );
|
|
|
|
bIsTiled = TIFFIsTiled(hTIFF);
|
|
|
|
nOffset = GTIFFWriteDirectory(hTIFF,
|
|
(bIsOverview) ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK,
|
|
nRasterXSize, nRasterYSize,
|
|
1, PLANARCONFIG_CONTIG, 1,
|
|
nBlockXSize, nBlockYSize,
|
|
bIsTiled, COMPRESSION_NONE, PHOTOMETRIC_MASK,
|
|
SAMPLEFORMAT_UINT, NULL, NULL, NULL, 0, NULL, "");
|
|
if (nOffset == 0)
|
|
return CE_Failure;
|
|
|
|
poMaskDS = new GTiffDataset();
|
|
if( poMaskDS->OpenOffset( hTIFF, nOffset, FALSE, GA_Update ) != CE_None)
|
|
{
|
|
delete poMaskDS;
|
|
poMaskDS = NULL;
|
|
return CE_Failure;
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
else
|
|
{
|
|
return GDALPamDataset::CreateMaskBand(nFlags);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateMaskBand() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GTiffRasterBand::CreateMaskBand(int nFlags)
|
|
{
|
|
if (poGDS->poMaskDS != NULL)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"This TIFF dataset has already an internal mask band");
|
|
return CE_Failure;
|
|
}
|
|
else if (CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "NO")))
|
|
{
|
|
return poGDS->CreateMaskBand(nFlags);
|
|
}
|
|
else
|
|
{
|
|
return GDALPamRasterBand::CreateMaskBand(nFlags);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PrepareTIFFErrorFormat() */
|
|
/* */
|
|
/* sometimes the "module" has stuff in it that has special */
|
|
/* meaning in a printf() style format, so we try to escape it. */
|
|
/* For now we hope the only thing we have to escape is %'s. */
|
|
/************************************************************************/
|
|
|
|
static char *PrepareTIFFErrorFormat( const char *module, const char *fmt )
|
|
|
|
{
|
|
char *pszModFmt;
|
|
int iIn, iOut;
|
|
|
|
pszModFmt = (char *) CPLMalloc( strlen(module)*2 + strlen(fmt) + 2 );
|
|
for( iOut = 0, iIn = 0; module[iIn] != '\0'; iIn++ )
|
|
{
|
|
if( module[iIn] == '%' )
|
|
{
|
|
pszModFmt[iOut++] = '%';
|
|
pszModFmt[iOut++] = '%';
|
|
}
|
|
else
|
|
pszModFmt[iOut++] = module[iIn];
|
|
}
|
|
pszModFmt[iOut] = '\0';
|
|
strcat( pszModFmt, ":" );
|
|
strcat( pszModFmt, fmt );
|
|
|
|
return pszModFmt;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffWarningHandler() */
|
|
/************************************************************************/
|
|
void
|
|
GTiffWarningHandler(const char* module, const char* fmt, va_list ap )
|
|
{
|
|
char *pszModFmt;
|
|
|
|
if( strstr(fmt,"unknown field") != NULL )
|
|
return;
|
|
|
|
pszModFmt = PrepareTIFFErrorFormat( module, fmt );
|
|
CPLErrorV( CE_Warning, CPLE_AppDefined, pszModFmt, ap );
|
|
CPLFree( pszModFmt );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffWarningHandler() */
|
|
/************************************************************************/
|
|
void
|
|
GTiffErrorHandler(const char* module, const char* fmt, va_list ap )
|
|
{
|
|
char *pszModFmt;
|
|
|
|
pszModFmt = PrepareTIFFErrorFormat( module, fmt );
|
|
CPLErrorV( CE_Failure, CPLE_AppDefined, pszModFmt, ap );
|
|
CPLFree( pszModFmt );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffTagExtender() */
|
|
/* */
|
|
/* Install tags specially known to GDAL. */
|
|
/************************************************************************/
|
|
|
|
static TIFFExtendProc _ParentExtender = NULL;
|
|
|
|
static void GTiffTagExtender(TIFF *tif)
|
|
|
|
{
|
|
static const TIFFFieldInfo xtiffFieldInfo[] = {
|
|
{ TIFFTAG_GDAL_METADATA, -1,-1, TIFF_ASCII, FIELD_CUSTOM,
|
|
TRUE, FALSE, (char*) "GDALMetadata" },
|
|
{ TIFFTAG_GDAL_NODATA, -1,-1, TIFF_ASCII, FIELD_CUSTOM,
|
|
TRUE, FALSE, (char*) "GDALNoDataValue" },
|
|
{ TIFFTAG_RPCCOEFFICIENT, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM,
|
|
TRUE, TRUE, (char*) "RPCCoefficient" }
|
|
};
|
|
|
|
if (_ParentExtender)
|
|
(*_ParentExtender)(tif);
|
|
|
|
TIFFMergeFieldInfo( tif, xtiffFieldInfo,
|
|
sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]) );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GTiffOneTimeInit() */
|
|
/* */
|
|
/* This is stuff that is initialized for the TIFF library just */
|
|
/* once. We deliberately defer the initialization till the */
|
|
/* first time we are likely to call into libtiff to avoid */
|
|
/* unnecessary paging in of the library for GDAL apps that */
|
|
/* don't use it. */
|
|
/************************************************************************/
|
|
|
|
void GTiffOneTimeInit()
|
|
|
|
{
|
|
static int bOneTimeInitDone = FALSE;
|
|
|
|
if( bOneTimeInitDone )
|
|
return;
|
|
|
|
bOneTimeInitDone = TRUE;
|
|
|
|
_ParentExtender = TIFFSetTagExtender(GTiffTagExtender);
|
|
|
|
TIFFSetWarningHandler( GTiffWarningHandler );
|
|
TIFFSetErrorHandler( GTiffErrorHandler );
|
|
|
|
// This only really needed if we are linked to an external libgeotiff
|
|
// with its own (lame) file searching logic.
|
|
SetCSVFilenameHook( GDALDefaultCSVFilename );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDeregister_GTiff() */
|
|
/************************************************************************/
|
|
|
|
void GDALDeregister_GTiff( GDALDriver * )
|
|
|
|
{
|
|
CPLDebug( "GDAL", "GDALDeregister_GTiff() called." );
|
|
CSVDeaccess( NULL );
|
|
|
|
#if defined(LIBGEOTIFF_VERSION) && LIBGEOTIFF_VERSION > 1150
|
|
GTIFDeaccessCSV();
|
|
#endif
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRegister_GTiff() */
|
|
/************************************************************************/
|
|
|
|
GDALDriver *CreateGTIFFDriver()
|
|
{
|
|
// if( GDALGetDriverByName( "GTiff" ) == NULL )
|
|
{
|
|
GDALDriver *poDriver;
|
|
char szCreateOptions[3072];
|
|
char szOptionalCompressItems[500];
|
|
|
|
poDriver = new GDALDriver();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Determine which compression codecs are available that we */
|
|
/* want to advertise. If we are using an old libtiff we won't */
|
|
/* be able to find out so we just assume all are available. */
|
|
/* -------------------------------------------------------------------- */
|
|
strcpy( szOptionalCompressItems,
|
|
" <Value>NONE</Value>" );
|
|
|
|
#if TIFFLIB_VERSION <= 20040919
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>PACKBITS</Value>"
|
|
" <Value>JPEG</Value>"
|
|
" <Value>LZW</Value>"
|
|
" <Value>DEFLATE</Value>" );
|
|
#else
|
|
TIFFCodec *c, *codecs = TIFFGetConfiguredCODECs();
|
|
|
|
for( c = codecs; c->name; c++ )
|
|
{
|
|
if( c->scheme == COMPRESSION_PACKBITS )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>PACKBITS</Value>" );
|
|
else if( c->scheme == COMPRESSION_JPEG )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>JPEG</Value>" );
|
|
else if( c->scheme == COMPRESSION_LZW )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>LZW</Value>" );
|
|
else if( c->scheme == COMPRESSION_ADOBE_DEFLATE )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>DEFLATE</Value>" );
|
|
else if( c->scheme == COMPRESSION_CCITTRLE )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>CCITTRLE</Value>" );
|
|
else if( c->scheme == COMPRESSION_CCITTFAX3 )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>CCITTFAX3</Value>" );
|
|
else if( c->scheme == COMPRESSION_CCITTFAX4 )
|
|
strcat( szOptionalCompressItems,
|
|
" <Value>CCITTFAX4</Value>" );
|
|
}
|
|
_TIFFfree( codecs );
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Build full creation option list. */
|
|
/* -------------------------------------------------------------------- */
|
|
sprintf( szCreateOptions, "%s%s%s",
|
|
"<CreationOptionList>"
|
|
" <Option name='COMPRESS' type='string-select'>",
|
|
szOptionalCompressItems,
|
|
" </Option>"
|
|
" <Option name='PREDICTOR' type='int' description='Predictor Type'/>"
|
|
" <Option name='JPEG_QUALITY' type='int' description='JPEG quality 1-100' default='75'/>"
|
|
" <Option name='ZLEVEL' type='int' description='DEFLATE compression level 1-9' default='6'/>"
|
|
" <Option name='NBITS' type='int' description='BITS for sub-byte files (1-7), sub-uint16 (9-15), sub-uint32 (17-31)'/>"
|
|
" <Option name='INTERLEAVE' type='string-select' default='PIXEL'>"
|
|
" <Value>BAND</Value>"
|
|
" <Value>PIXEL</Value>"
|
|
" </Option>"
|
|
" <Option name='TILED' type='boolean' description='Switch to tiled format'/>"
|
|
" <Option name='TFW' type='boolean' description='Write out world file'/>"
|
|
" <Option name='RPB' type='boolean' description='Write out .RPB (RPC) file'/>"
|
|
" <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
|
|
" <Option name='BLOCKYSIZE' type='int' description='Tile/Strip Height'/>"
|
|
" <Option name='PHOTOMETRIC' type='string-select'>"
|
|
" <Value>MINISBLACK</Value>"
|
|
" <Value>MINISWHITE</Value>"
|
|
" <Value>PALETTE</Value>"
|
|
" <Value>RGB</Value>"
|
|
" <Value>CMYK</Value>"
|
|
" <Value>YCBCR</Value>"
|
|
" <Value>CIELAB</Value>"
|
|
" <Value>ICCLAB</Value>"
|
|
" <Value>ITULAB</Value>"
|
|
" </Option>"
|
|
" <Option name='SPARSE_OK' type='boolean' description='Can newly created files have missing blocks?' default='FALSE'/>"
|
|
" <Option name='ALPHA' type='boolean' description='Mark first extrasample as being alpha'/>"
|
|
" <Option name='PROFILE' type='string-select' default='GDALGeoTIFF'>"
|
|
" <Value>GDALGeoTIFF</Value>"
|
|
" <Value>GeoTIFF</Value>"
|
|
" <Value>BASELINE</Value>"
|
|
" </Option>"
|
|
" <Option name='PIXELTYPE' type='string-select'>"
|
|
" <Value>DEFAULT</Value>"
|
|
" <Value>SIGNEDBYTE</Value>"
|
|
" </Option>"
|
|
#ifdef BIGTIFF_SUPPORT
|
|
" <Option name='BIGTIFF' type='string-select' description='Force creation of BigTIFF file'>"
|
|
" <Value>YES</Value>"
|
|
" <Value>NO</Value>"
|
|
" <Value>IF_NEEDED</Value>"
|
|
" <Value>IF_SAFER</Value>"
|
|
" </Option>"
|
|
#endif
|
|
" <Option name='ENDIANNESS' type='string-select' default='NATIVE' description='Force endianness of created file. For DEBUG purpose mostly'>"
|
|
" <Value>NATIVE</Value>"
|
|
" <Value>INVERTED</Value>"
|
|
" <Value>LITTLE</Value>"
|
|
" <Value>BIG</Value>"
|
|
" </Option>"
|
|
"</CreationOptionList>" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the driver details. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDriver->SetDescription( "GTiff" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "GeoTIFF" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_gtiff.html" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/tiff" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "tif" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
|
|
"Byte UInt16 Int16 UInt32 Int32 Float32 "
|
|
"Float64 CInt16 CInt32 CFloat32 CFloat64" );
|
|
poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
|
|
szCreateOptions );
|
|
|
|
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
|
|
|
|
poDriver->pfnOpen = GTiffDataset::Open;
|
|
poDriver->pfnCreate = GTiffDataset::Create;
|
|
poDriver->pfnCreateCopy = GTiffDataset::CreateCopy;
|
|
poDriver->pfnUnloadDriver = GDALDeregister_GTiff;
|
|
poDriver->pfnIdentify = GTiffDataset::Identify;
|
|
|
|
//GetGDALDriverManager()->RegisterDriver( poDriver );
|
|
return poDriver;
|
|
}
|
|
}
|