1424 строки
50 KiB
C++
1424 строки
50 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: FIT Driver
|
|
* Purpose: Implement FIT Support - not using the SGI iflFIT library.
|
|
* Author: Philip Nemec, nemec@keyholecorp.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2001, Keyhole, Inc.
|
|
* Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.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 "cpl_string.h"
|
|
#include "fit.h"
|
|
#include "gdal_frmts.h"
|
|
#include "gdal_pam.h"
|
|
#include "gstEndian.h"
|
|
#include "cpl_safemaths.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
|
|
constexpr size_t FIT_PAGE_SIZE = 128;
|
|
|
|
using namespace gstEndian;
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* FITDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class FITRasterBand;
|
|
|
|
class FITDataset final : public GDALPamDataset
|
|
{
|
|
friend class FITRasterBand;
|
|
|
|
VSILFILE *fp;
|
|
FITinfo *info;
|
|
double adfGeoTransform[6];
|
|
|
|
public:
|
|
FITDataset();
|
|
~FITDataset();
|
|
static GDALDataset *Open(GDALOpenInfo *);
|
|
// virtual CPLErr GetGeoTransform( double * );
|
|
};
|
|
|
|
static GDALDataset *FITCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
|
|
int bStrict, char **papszOptions,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData);
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* FITRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class FITRasterBand final : public GDALPamRasterBand
|
|
{
|
|
friend class FITDataset;
|
|
|
|
unsigned long recordSize; // number of bytes of a single page/block/record
|
|
unsigned long numXBlocks; // number of pages in the X direction
|
|
unsigned long numYBlocks; // number of pages in the Y direction
|
|
unsigned long bytesPerComponent;
|
|
unsigned long bytesPerPixel;
|
|
char *tmpImage;
|
|
|
|
public:
|
|
FITRasterBand(FITDataset *, int nBandIn, int nBandsIn);
|
|
~FITRasterBand() override;
|
|
|
|
// should override RasterIO eventually.
|
|
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
// virtual CPLErr WriteBlock( int, int, void * );
|
|
double GetMinimum(int *pbSuccess) override;
|
|
double GetMaximum(int *pbSuccess) override;
|
|
GDALColorInterp GetColorInterpretation() override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* FITRasterBand() */
|
|
/************************************************************************/
|
|
|
|
FITRasterBand::FITRasterBand(FITDataset *poDSIn, int nBandIn, int nBandsIn)
|
|
: recordSize(0), numXBlocks(0), numYBlocks(0), bytesPerComponent(0),
|
|
bytesPerPixel(0), tmpImage(nullptr)
|
|
{
|
|
poDS = poDSIn;
|
|
nBand = nBandIn;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the GDAL data type. */
|
|
/* -------------------------------------------------------------------- */
|
|
eDataType = fitDataType(poDSIn->info->dtype);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the page sizes. */
|
|
/* -------------------------------------------------------------------- */
|
|
nBlockXSize = poDSIn->info->xPageSize;
|
|
nBlockYSize = poDSIn->info->yPageSize;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Calculate the values for record offset calculations. */
|
|
/* -------------------------------------------------------------------- */
|
|
bytesPerComponent = GDALGetDataTypeSizeBytes(eDataType);
|
|
if (bytesPerComponent == 0)
|
|
return;
|
|
bytesPerPixel = nBandsIn * bytesPerComponent;
|
|
const auto knIntMax = std::numeric_limits<int>::max();
|
|
if (nBlockXSize <= 0 || nBlockYSize <= 0 ||
|
|
nBlockXSize > knIntMax / static_cast<int>(bytesPerPixel) ||
|
|
nBlockYSize >
|
|
knIntMax / (nBlockXSize * static_cast<int>(bytesPerPixel)))
|
|
return;
|
|
recordSize = bytesPerPixel * nBlockXSize * nBlockYSize;
|
|
numXBlocks = (unsigned long)ceil((double)poDSIn->info->xSize / nBlockXSize);
|
|
numYBlocks = (unsigned long)ceil((double)poDSIn->info->ySize / nBlockYSize);
|
|
|
|
tmpImage = (char *)VSI_MALLOC_VERBOSE(recordSize);
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the access flag. For now we set it the same as the */
|
|
/* whole dataset, but eventually this should take account of */
|
|
/* locked channels, or read-only secondary data files. */
|
|
/* -------------------------------------------------------------------- */
|
|
/* ... */
|
|
}
|
|
|
|
FITRasterBand::~FITRasterBand()
|
|
{
|
|
VSIFree(tmpImage);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
#define COPY_XFIRST(t) \
|
|
{ \
|
|
t *dstp = (t *)pImage; \
|
|
t *srcp = (t *)tmpImage; \
|
|
srcp += nBand - 1; \
|
|
long imacro = 0; \
|
|
for (long y = ystart; y != ystop; y += yinc) \
|
|
for (long x = xstart; x != xstop; x += xinc, imacro++) \
|
|
{ \
|
|
dstp[imacro] = srcp[(y * nBlockXSize + x) * poFIT_DS->nBands]; \
|
|
} \
|
|
}
|
|
|
|
#define COPY_YFIRST(t) \
|
|
{ \
|
|
t *dstp = (t *)pImage; \
|
|
t *srcp = (t *)tmpImage; \
|
|
srcp += nBand - 1; \
|
|
long imacro = 0; \
|
|
for (long x = xstart; x != xstop; x += xinc, imacro++) \
|
|
for (long y = ystart; y != ystop; y += yinc) \
|
|
{ \
|
|
dstp[imacro] = srcp[(x * nBlockYSize + y) * poFIT_DS->nBands]; \
|
|
} \
|
|
}
|
|
|
|
CPLErr FITRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
|
|
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *)poDS;
|
|
|
|
uint64 tilenum = 0;
|
|
|
|
switch (poFIT_DS->info->space)
|
|
{
|
|
case 1:
|
|
// iflUpperLeftOrigin - from upper left corner
|
|
// scan right then down
|
|
tilenum = nBlockYOff * numXBlocks + nBlockXOff;
|
|
break;
|
|
case 2:
|
|
// iflUpperRightOrigin - from upper right corner
|
|
// scan left then down
|
|
tilenum = numYBlocks * numXBlocks + (numXBlocks - 1 - nBlockXOff);
|
|
break;
|
|
case 3:
|
|
// iflLowerRightOrigin - from lower right corner
|
|
// scan left then up
|
|
tilenum = (numYBlocks - 1 - nBlockYOff) * numXBlocks +
|
|
(numXBlocks - 1 - nBlockXOff);
|
|
break;
|
|
case 4:
|
|
// iflLowerLeftOrigin - from lower left corner
|
|
// scan right then up
|
|
tilenum = (numYBlocks - 1 - nBlockYOff) * numXBlocks + nBlockXOff;
|
|
break;
|
|
case 5:
|
|
// iflLeftUpperOrigin -* from upper left corner
|
|
// scan down then right
|
|
tilenum = nBlockXOff * numYBlocks + nBlockYOff;
|
|
break;
|
|
case 6:
|
|
// iflRightUpperOrigin - from upper right corner
|
|
// scan down then left
|
|
tilenum = (numXBlocks - 1 - nBlockXOff) * numYBlocks + nBlockYOff;
|
|
break;
|
|
case 7:
|
|
// iflRightLowerOrigin - from lower right corner
|
|
// scan up then left
|
|
tilenum = nBlockXOff * numYBlocks + (numYBlocks - 1 - nBlockYOff);
|
|
break;
|
|
case 8:
|
|
// iflLeftLowerOrigin -* from lower left corner
|
|
// scan up then right
|
|
tilenum = (numXBlocks - 1 - nBlockXOff) * numYBlocks +
|
|
(numYBlocks - 1 - nBlockYOff);
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - unrecognized image space %i",
|
|
poFIT_DS->info->space);
|
|
return CE_Failure;
|
|
} // switch
|
|
|
|
uint64 offset = poFIT_DS->info->dataOffset + recordSize * tilenum;
|
|
// CPLDebug("FIT", "%i RasterBand::IReadBlock %i %i (out of %i %i) --
|
|
// %i",
|
|
// poFIT_DS->info->space,
|
|
// nBlockXOff, nBlockYOff, numXBlocks, numYBlocks, tilenum);
|
|
|
|
if (VSIFSeekL(poFIT_DS->fp, offset, SEEK_SET) == -1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - 64bit file seek failure, handle=%p", poFIT_DS->fp);
|
|
return CE_Failure;
|
|
}
|
|
|
|
// XXX - should handle status
|
|
// fast path is single component (ll?) - no copy needed
|
|
int fastpath = FALSE;
|
|
|
|
if ((poFIT_DS->nBands == 1) && (poFIT_DS->info->space == 1)) // upper left
|
|
fastpath = TRUE;
|
|
|
|
size_t nRead = 0;
|
|
char *p = nullptr;
|
|
if (!fastpath)
|
|
{
|
|
nRead = VSIFReadL(tmpImage, recordSize, 1, poFIT_DS->fp);
|
|
// offset to correct component to swap
|
|
p = (char *)tmpImage + nBand - 1;
|
|
}
|
|
else
|
|
{
|
|
nRead = VSIFReadL(pImage, recordSize, 1, poFIT_DS->fp);
|
|
p = (char *)pImage;
|
|
}
|
|
if (nRead != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO, "Cannot read record");
|
|
return CE_Failure;
|
|
}
|
|
|
|
#ifdef swapping
|
|
unsigned long i = 0;
|
|
|
|
switch (bytesPerComponent)
|
|
{
|
|
case 1:
|
|
// do nothing
|
|
break;
|
|
case 2:
|
|
for (i = 0; i < recordSize; i += bytesPerPixel)
|
|
gst_swap16(p + i);
|
|
break;
|
|
case 4:
|
|
for (i = 0; i < recordSize; i += bytesPerPixel)
|
|
gst_swap32(p + i);
|
|
break;
|
|
case 8:
|
|
for (i = 0; i < recordSize; i += bytesPerPixel)
|
|
gst_swap64(p + i);
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FITRasterBand::IReadBlock unsupported bytesPerPixel %lu",
|
|
bytesPerComponent);
|
|
} // switch
|
|
#else
|
|
(void)p; // avoid warnings.
|
|
#endif // swapping
|
|
|
|
if (!fastpath)
|
|
{
|
|
long xinc, yinc, xstart, ystart, xstop, ystop;
|
|
if (poFIT_DS->info->space <= 4)
|
|
{
|
|
// scan left/right first
|
|
|
|
switch (poFIT_DS->info->space)
|
|
{
|
|
case 1:
|
|
// iflUpperLeftOrigin - from upper left corner
|
|
// scan right then down
|
|
xinc = 1;
|
|
yinc = 1;
|
|
break;
|
|
case 2:
|
|
// iflUpperRightOrigin - from upper right corner
|
|
// scan left then down
|
|
xinc = -1;
|
|
yinc = 1;
|
|
break;
|
|
case 3:
|
|
// iflLowerRightOrigin - from lower right corner
|
|
// scan left then up
|
|
xinc = -1;
|
|
yinc = -1;
|
|
break;
|
|
case 4:
|
|
// iflLowerLeftOrigin - from lower left corner
|
|
// scan right then up
|
|
xinc = 1;
|
|
yinc = -1;
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - unrecognized image space %i",
|
|
poFIT_DS->info->space);
|
|
xinc = 1;
|
|
yinc = 1;
|
|
} // switch
|
|
|
|
if (xinc == 1)
|
|
{
|
|
xstart = 0;
|
|
xstop = nBlockXSize;
|
|
}
|
|
else
|
|
{
|
|
xstart = nBlockXSize - 1;
|
|
xstop = -1;
|
|
}
|
|
if (yinc == 1)
|
|
{
|
|
ystart = 0;
|
|
ystop = nBlockYSize;
|
|
}
|
|
else
|
|
{
|
|
int localBlockYSize = nBlockYSize;
|
|
long maxy_full =
|
|
(long)floor(poFIT_DS->info->ySize / (double)nBlockYSize);
|
|
if (nBlockYOff >= maxy_full)
|
|
localBlockYSize = poFIT_DS->info->ySize % nBlockYSize;
|
|
ystart = localBlockYSize - 1;
|
|
ystop = -1;
|
|
}
|
|
|
|
switch (bytesPerComponent)
|
|
{
|
|
case 1:
|
|
COPY_XFIRST(char);
|
|
break;
|
|
case 2:
|
|
COPY_XFIRST(uint16);
|
|
break;
|
|
case 4:
|
|
COPY_XFIRST(uint32);
|
|
break;
|
|
case 8:
|
|
COPY_XFIRST(uint64);
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FITRasterBand::IReadBlock unsupported "
|
|
"bytesPerComponent %lu",
|
|
bytesPerComponent);
|
|
} // switch
|
|
} // Scan left/right first.
|
|
else
|
|
{
|
|
// Scan up/down first.
|
|
switch (poFIT_DS->info->space)
|
|
{
|
|
case 5:
|
|
// iflLeftUpperOrigin -* from upper left corner
|
|
// scan down then right
|
|
xinc = 1;
|
|
yinc = 1;
|
|
break;
|
|
case 6:
|
|
// iflRightUpperOrigin - from upper right corner
|
|
// scan down then left
|
|
xinc = -1;
|
|
yinc = 1;
|
|
break;
|
|
case 7:
|
|
// iflRightLowerOrigin - from lower right corner
|
|
// scan up then left
|
|
xinc = -1;
|
|
yinc = -1;
|
|
break;
|
|
case 8:
|
|
// iflLeftLowerOrigin -* from lower left corner
|
|
// scan up then right
|
|
xinc = 1;
|
|
yinc = -1;
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - unrecognized image space %i",
|
|
poFIT_DS->info->space);
|
|
xinc = 1;
|
|
yinc = 1;
|
|
} // switch
|
|
|
|
if (xinc == 1)
|
|
{
|
|
xstart = 0;
|
|
xstop = nBlockXSize;
|
|
}
|
|
else
|
|
{
|
|
int localBlockXSize = nBlockXSize;
|
|
long maxx_full =
|
|
(long)floor(poFIT_DS->info->xSize / (double)nBlockXSize);
|
|
if (nBlockXOff >= maxx_full)
|
|
localBlockXSize = poFIT_DS->info->xSize % nBlockXSize;
|
|
xstart = localBlockXSize - 1;
|
|
xstop = -1;
|
|
}
|
|
if (yinc == 1)
|
|
{
|
|
ystart = 0;
|
|
ystop = nBlockYSize;
|
|
}
|
|
else
|
|
{
|
|
ystart = nBlockYSize - 1;
|
|
ystop = -1;
|
|
}
|
|
|
|
switch (bytesPerComponent)
|
|
{
|
|
case 1:
|
|
COPY_YFIRST(char);
|
|
break;
|
|
case 2:
|
|
COPY_YFIRST(uint16);
|
|
break;
|
|
case 4:
|
|
COPY_YFIRST(uint32);
|
|
break;
|
|
case 8:
|
|
COPY_YFIRST(uint64);
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FITRasterBand::IReadBlock unsupported "
|
|
"bytesPerComponent %lu",
|
|
bytesPerComponent);
|
|
} // switch
|
|
} // Scan up/down first.
|
|
} // !fastpath
|
|
return CE_None;
|
|
}
|
|
|
|
#if 0
|
|
/************************************************************************/
|
|
/* ReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr FITRasterBand::ReadBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *) poDS;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr FITRasterBand::WriteBlock( int nBlockXOff, int nBlockYOff,
|
|
void * pImage )
|
|
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *) poDS;
|
|
|
|
return CE_None;
|
|
}
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* GetMinimum() */
|
|
/************************************************************************/
|
|
|
|
double FITRasterBand::GetMinimum(int *pbSuccess)
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *)poDS;
|
|
|
|
if ((!poFIT_DS) || (!poFIT_DS->info))
|
|
return GDALRasterBand::GetMinimum(pbSuccess);
|
|
|
|
if (pbSuccess)
|
|
*pbSuccess = TRUE;
|
|
|
|
if (poFIT_DS->info->version &&
|
|
STARTS_WITH_CI((const char *)&(poFIT_DS->info->version), "02"))
|
|
{
|
|
return poFIT_DS->info->minValue;
|
|
}
|
|
|
|
return GDALRasterBand::GetMinimum(pbSuccess);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMaximum() */
|
|
/************************************************************************/
|
|
|
|
double FITRasterBand::GetMaximum(int *pbSuccess)
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *)poDS;
|
|
|
|
if ((!poFIT_DS) || (!poFIT_DS->info))
|
|
return GDALRasterBand::GetMaximum(pbSuccess);
|
|
|
|
if (pbSuccess)
|
|
*pbSuccess = TRUE;
|
|
|
|
if (STARTS_WITH_CI((const char *)&poFIT_DS->info->version, "02"))
|
|
{
|
|
return poFIT_DS->info->maxValue;
|
|
}
|
|
|
|
return GDALRasterBand::GetMaximum(pbSuccess);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp FITRasterBand::GetColorInterpretation()
|
|
{
|
|
FITDataset *poFIT_DS = (FITDataset *)poDS;
|
|
|
|
if ((!poFIT_DS) || (!poFIT_DS->info))
|
|
return GCI_Undefined;
|
|
|
|
switch (poFIT_DS->info->cm)
|
|
{
|
|
case 1: // iflNegative - inverted luminance (min value is white)
|
|
CPLError(
|
|
CE_Warning, CPLE_NotSupported,
|
|
"FIT - color model Negative not supported - ignoring model");
|
|
return GCI_Undefined;
|
|
|
|
case 2: // iflLuminance - luminance
|
|
if (poFIT_DS->nBands != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model Luminance mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_GrayIndex;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model Luminance unknown band %i",
|
|
nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 3: // iflRGB - full color (Red, Green, Blue triplets)
|
|
if (poFIT_DS->nBands != 3)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model RGB mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_RedBand;
|
|
case 2:
|
|
return GCI_GreenBand;
|
|
case 3:
|
|
return GCI_BlueBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model RGB unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 4: // iflRGBPalette - color mapped values
|
|
CPLError(CE_Warning, CPLE_NotSupported,
|
|
"FIT - color model RGBPalette not supported - "
|
|
"ignoring model");
|
|
return GCI_Undefined;
|
|
|
|
case 5: // iflRGBA - full color with transparency (alpha channel)
|
|
if (poFIT_DS->nBands != 4)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model RGBA mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_RedBand;
|
|
case 2:
|
|
return GCI_GreenBand;
|
|
case 3:
|
|
return GCI_BlueBand;
|
|
case 4:
|
|
return GCI_AlphaBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model RGBA unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 6: // iflHSV - Hue, Saturation, Value
|
|
if (poFIT_DS->nBands != 3)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model HSV mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_HueBand;
|
|
case 2:
|
|
return GCI_SaturationBand;
|
|
case 3:
|
|
return GCI_LightnessBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model HSV unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 7: // iflCMY - Cyan, Magenta, Yellow
|
|
if (poFIT_DS->nBands != 3)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model CMY mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_CyanBand;
|
|
case 2:
|
|
return GCI_MagentaBand;
|
|
case 3:
|
|
return GCI_YellowBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model CMY unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 8: // iflCMYK - Cyan, Magenta, Yellow, Black
|
|
if (poFIT_DS->nBands != 4)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model CMYK mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_CyanBand;
|
|
case 2:
|
|
return GCI_MagentaBand;
|
|
case 3:
|
|
return GCI_YellowBand;
|
|
case 4:
|
|
return GCI_BlackBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model CMYK unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 9: // iflBGR - full color (ordered Blue, Green, Red)
|
|
if (poFIT_DS->nBands != 3)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model BGR mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_BlueBand;
|
|
case 2:
|
|
return GCI_GreenBand;
|
|
case 3:
|
|
return GCI_RedBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model BGR unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 10: // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers)
|
|
if (poFIT_DS->nBands != 4)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model ABGR mismatch with %i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_AlphaBand;
|
|
case 2:
|
|
return GCI_BlueBand;
|
|
case 3:
|
|
return GCI_GreenBand;
|
|
case 4:
|
|
return GCI_RedBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model ABGR unknown band %i", nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
case 11: // iflMultiSpectral - multi-spectral data, arbitrary number of
|
|
// chans
|
|
return GCI_Undefined;
|
|
|
|
case 12: // iflYCC PhotoCD color model (Luminance, Chrominance)
|
|
CPLError(CE_Warning, CPLE_NotSupported,
|
|
"FIT - color model YCC not supported - ignoring model");
|
|
return GCI_Undefined;
|
|
|
|
case 13: // iflLuminanceAlpha - Luminance plus alpha
|
|
if (poFIT_DS->nBands != 2)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model LuminanceAlpha mismatch with "
|
|
"%i bands",
|
|
poFIT_DS->nBands);
|
|
return GCI_Undefined;
|
|
}
|
|
switch (nBand)
|
|
{
|
|
case 1:
|
|
return GCI_GrayIndex;
|
|
case 2:
|
|
return GCI_AlphaBand;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - color model LuminanceAlpha unknown band %i",
|
|
nBand);
|
|
return GCI_Undefined;
|
|
} // switch nBand
|
|
|
|
default:
|
|
CPLError(CE_Warning, CPLE_NotSupported,
|
|
"FIT - unrecognized color model %i - ignoring model",
|
|
poFIT_DS->info->cm);
|
|
return GCI_Undefined;
|
|
} // switch
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FITDataset() */
|
|
/************************************************************************/
|
|
|
|
FITDataset::FITDataset() : fp(nullptr), info(nullptr)
|
|
{
|
|
adfGeoTransform[0] = 0.0; // x origin (top left corner)
|
|
adfGeoTransform[1] = 1.0; // x pixel size
|
|
adfGeoTransform[2] = 0.0;
|
|
adfGeoTransform[3] = 0.0; // y origin (top left corner)
|
|
adfGeoTransform[4] = 0.0;
|
|
adfGeoTransform[5] = 1.0; // y pixel size
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~FITDataset() */
|
|
/************************************************************************/
|
|
|
|
FITDataset::~FITDataset()
|
|
{
|
|
FlushCache(true);
|
|
if (info)
|
|
delete (info);
|
|
if (fp)
|
|
{
|
|
if (VSIFCloseL(fp) != 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO, "I/O error");
|
|
}
|
|
}
|
|
}
|
|
|
|
// simple guard object to delete memory
|
|
// when the guard goes out of scope
|
|
template <class T> class DeleteGuard
|
|
{
|
|
public:
|
|
explicit DeleteGuard(T *p) : _ptr(p)
|
|
{
|
|
}
|
|
~DeleteGuard()
|
|
{
|
|
delete _ptr;
|
|
}
|
|
|
|
T *take()
|
|
{
|
|
T *tmp = _ptr;
|
|
_ptr = nullptr;
|
|
return tmp;
|
|
}
|
|
|
|
private:
|
|
T *_ptr;
|
|
// prevent default copy constructor and assignment operator
|
|
DeleteGuard(const DeleteGuard &);
|
|
DeleteGuard &operator=(const DeleteGuard &);
|
|
};
|
|
|
|
// simple guard object to free memory
|
|
// when the guard goes out of scope
|
|
template <class T> class FreeGuard
|
|
{
|
|
public:
|
|
explicit FreeGuard(T *p) : _ptr(p)
|
|
{
|
|
}
|
|
~FreeGuard()
|
|
{
|
|
if (_ptr)
|
|
free(_ptr);
|
|
}
|
|
|
|
T *take()
|
|
{
|
|
T *tmp = _ptr;
|
|
_ptr = NULL;
|
|
return tmp;
|
|
}
|
|
|
|
private:
|
|
T *_ptr;
|
|
// prevent default copy constructor and assignment operator
|
|
FreeGuard(const FreeGuard &);
|
|
FreeGuard &operator=(const FreeGuard &);
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* Open() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *FITDataset::Open(GDALOpenInfo *poOpenInfo)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* First we check to see if the file has the expected header */
|
|
/* bytes. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (poOpenInfo->nHeaderBytes < 5 || poOpenInfo->fpL == nullptr)
|
|
return nullptr;
|
|
|
|
if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "IT01") &&
|
|
!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "IT02"))
|
|
return nullptr;
|
|
|
|
if (poOpenInfo->eAccess == GA_Update)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"The FIT driver does not support update access to existing"
|
|
" files.\n");
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a corresponding GDALDataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
FITDataset *poDS = new FITDataset();
|
|
DeleteGuard<FITDataset> guard(poDS);
|
|
poDS->eAccess = poOpenInfo->eAccess;
|
|
poDS->fp = poOpenInfo->fpL;
|
|
poOpenInfo->fpL = nullptr;
|
|
|
|
poDS->info = new FITinfo;
|
|
FITinfo *info = poDS->info;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read other header values. */
|
|
/* -------------------------------------------------------------------- */
|
|
FIThead02 *head = (FIThead02 *)poOpenInfo->pabyHeader;
|
|
|
|
// extract the image attributes from the file header
|
|
if (STARTS_WITH_CI((const char *)&head->version, "02"))
|
|
{
|
|
// incomplete header
|
|
if (poOpenInfo->nHeaderBytes < (signed)sizeof(FIThead02))
|
|
return nullptr;
|
|
|
|
CPLDebug("FIT", "Loading file with header version 02");
|
|
|
|
gst_swapb(head->minValue);
|
|
info->minValue = head->minValue;
|
|
gst_swapb(head->maxValue);
|
|
info->maxValue = head->maxValue;
|
|
gst_swapb(head->dataOffset);
|
|
info->dataOffset = head->dataOffset;
|
|
|
|
info->userOffset = sizeof(FIThead02);
|
|
}
|
|
else if (STARTS_WITH_CI((const char *)&head->version, "01"))
|
|
{
|
|
// incomplete header
|
|
if (poOpenInfo->nHeaderBytes < (signed)sizeof(FIThead01))
|
|
return nullptr;
|
|
|
|
CPLDebug("FIT", "Loading file with header version 01");
|
|
|
|
// map old style header into new header structure
|
|
FIThead01 *head01 = (FIThead01 *)head;
|
|
gst_swapb(head->dataOffset);
|
|
info->dataOffset = head01->dataOffset;
|
|
|
|
info->userOffset = sizeof(FIThead01);
|
|
}
|
|
else
|
|
{
|
|
// unrecognized header version
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT - unsupported header version %.2s\n",
|
|
(const char *)&head->version);
|
|
return nullptr;
|
|
}
|
|
|
|
CPLDebug("FIT", "userOffset %i, dataOffset %i", info->userOffset,
|
|
info->dataOffset);
|
|
|
|
info->magic = head->magic;
|
|
info->version = head->version;
|
|
|
|
gst_swapb(head->xSize);
|
|
info->xSize = head->xSize;
|
|
gst_swapb(head->ySize);
|
|
info->ySize = head->ySize;
|
|
gst_swapb(head->zSize);
|
|
info->zSize = head->zSize;
|
|
gst_swapb(head->cSize);
|
|
info->cSize = head->cSize;
|
|
gst_swapb(head->dtype);
|
|
info->dtype = head->dtype;
|
|
gst_swapb(head->order);
|
|
info->order = head->order;
|
|
gst_swapb(head->space);
|
|
info->space = head->space;
|
|
gst_swapb(head->cm);
|
|
info->cm = head->cm;
|
|
gst_swapb(head->xPageSize);
|
|
info->xPageSize = head->xPageSize;
|
|
gst_swapb(head->yPageSize);
|
|
info->yPageSize = head->yPageSize;
|
|
gst_swapb(head->zPageSize);
|
|
info->zPageSize = head->zPageSize;
|
|
gst_swapb(head->cPageSize);
|
|
info->cPageSize = head->cPageSize;
|
|
|
|
CPLDebug("FIT", "size %i %i %i %i, pageSize %i %i %i %i", info->xSize,
|
|
info->ySize, info->zSize, info->cSize, info->xPageSize,
|
|
info->yPageSize, info->zPageSize, info->cPageSize);
|
|
|
|
CPLDebug("FIT", "dtype %i order %i space %i cm %i", info->dtype,
|
|
info->order, info->space, info->cm);
|
|
|
|
/**************************/
|
|
|
|
poDS->nRasterXSize = head->xSize;
|
|
poDS->nRasterYSize = head->ySize;
|
|
|
|
if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
|
|
!GDALCheckBandCount(head->cSize, FALSE) || head->xPageSize == 0 ||
|
|
head->yPageSize == 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify all "unused" header values. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (info->zSize != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT driver - unsupported zSize %i\n", info->zSize);
|
|
return nullptr;
|
|
}
|
|
|
|
if (info->order != 1) // interleaved - RGBRGB
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT driver - unsupported order %i\n", info->order);
|
|
return nullptr;
|
|
}
|
|
|
|
if (info->zPageSize != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT driver - unsupported zPageSize %i\n", info->zPageSize);
|
|
return nullptr;
|
|
}
|
|
|
|
if (info->cPageSize != info->cSize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT driver - unsupported cPageSize %i (!= %i)\n",
|
|
info->cPageSize, info->cSize);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create band information objects. */
|
|
/* -------------------------------------------------------------------- */
|
|
// Verified by above GDALCheckBandCount()
|
|
// coverity[tainted_data]
|
|
for (int i = 0; i < (int)head->cSize; i++)
|
|
{
|
|
FITRasterBand *poBand =
|
|
new FITRasterBand(poDS, i + 1, (int)head->cSize);
|
|
poDS->SetBand(i + 1, poBand);
|
|
if (poBand->tmpImage == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize any PAM information. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->SetDescription(poOpenInfo->pszFilename);
|
|
poDS->TryLoadXML();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for external overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
|
|
poOpenInfo->GetSiblingFiles());
|
|
|
|
return guard.take();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FITCreateCopy() */
|
|
/************************************************************************/
|
|
|
|
static GDALDataset *FITCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
|
|
int bStrict, char **papszOptions,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
CPLDebug("FIT", "CreateCopy %s - %i", pszFilename, bStrict);
|
|
|
|
int nBands = poSrcDS->GetRasterCount();
|
|
if (nBands == 0)
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"FIT driver does not support source dataset with zero band.\n");
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!pfnProgress(0.0, nullptr, pProgressData))
|
|
{
|
|
CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
VSILFILE *fpImage = VSIFOpenL(pszFilename, "wb");
|
|
if (fpImage == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"FIT - unable to create file %s.\n", pszFilename);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Generate header. */
|
|
/* -------------------------------------------------------------------- */
|
|
// XXX - should FIT_PAGE_SIZE be based on file page size ??
|
|
|
|
const size_t size = std::max(sizeof(FIThead02), FIT_PAGE_SIZE);
|
|
FIThead02 *head = (FIThead02 *)malloc(size);
|
|
FreeGuard<FIThead02> guardHead(head);
|
|
|
|
// clean header so padding (past real header) is all zeros
|
|
memset(head, 0, size);
|
|
|
|
memcpy((char *)&head->magic, "IT", 2);
|
|
memcpy((char *)&head->version, "02", 2);
|
|
|
|
head->xSize = poSrcDS->GetRasterXSize();
|
|
gst_swapb(head->xSize);
|
|
head->ySize = poSrcDS->GetRasterYSize();
|
|
gst_swapb(head->ySize);
|
|
head->zSize = 1;
|
|
gst_swapb(head->zSize);
|
|
|
|
head->cSize = nBands;
|
|
gst_swapb(head->cSize);
|
|
|
|
GDALRasterBand *firstBand = poSrcDS->GetRasterBand(1);
|
|
if (!firstBand)
|
|
{
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
return nullptr;
|
|
}
|
|
|
|
head->dtype = fitGetDataType(firstBand->GetRasterDataType());
|
|
if (!head->dtype)
|
|
{
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
return nullptr;
|
|
}
|
|
gst_swapb(head->dtype);
|
|
head->order = 1; // interleaved - RGBRGB
|
|
gst_swapb(head->order);
|
|
head->space = 1; // upper left
|
|
gst_swapb(head->space);
|
|
|
|
// XXX - need to check all bands
|
|
head->cm = fitGetColorModel(firstBand->GetColorInterpretation(), nBands);
|
|
gst_swapb(head->cm);
|
|
|
|
int blockX, blockY;
|
|
firstBand->GetBlockSize(&blockX, &blockY);
|
|
blockX = std::min(blockX, poSrcDS->GetRasterXSize());
|
|
blockY = std::min(blockY, poSrcDS->GetRasterYSize());
|
|
int nDTSize = GDALGetDataTypeSizeBytes(firstBand->GetRasterDataType());
|
|
try
|
|
{
|
|
CPL_IGNORE_RET_VAL(CPLSM(blockX) * CPLSM(blockY) * CPLSM(nDTSize) *
|
|
CPLSM(nBands));
|
|
CPLDebug("FIT write", "inherited block size %ix%i", blockX, blockY);
|
|
}
|
|
catch (...)
|
|
{
|
|
blockX = std::min(256, poSrcDS->GetRasterXSize());
|
|
blockY = std::min(256, poSrcDS->GetRasterYSize());
|
|
}
|
|
|
|
if (CSLFetchNameValue(papszOptions, "PAGESIZE") != nullptr)
|
|
{
|
|
const char *str = CSLFetchNameValue(papszOptions, "PAGESIZE");
|
|
int newBlockX, newBlockY;
|
|
sscanf(str, "%i,%i", &newBlockX, &newBlockY);
|
|
if (newBlockX > 0 && newBlockY > 0)
|
|
{
|
|
blockX = newBlockX;
|
|
blockY = newBlockY;
|
|
try
|
|
{
|
|
CPL_IGNORE_RET_VAL(CPLSM(blockX) * CPLSM(blockY) *
|
|
CPLSM(nDTSize) * CPLSM(nBands));
|
|
}
|
|
catch (...)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Too big values in PAGESIZE");
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"FIT - Unable to parse option PAGESIZE values [%s]", str);
|
|
}
|
|
}
|
|
|
|
// XXX - need to do lots of checking of block size
|
|
// * provide ability to override block size with options
|
|
// * handle non-square block size (like scanline)
|
|
// - probably default from non-tiled image - have default block size
|
|
// * handle block size bigger than image size
|
|
// * undesirable block size (non power of 2, others?)
|
|
// * mismatched block sizes for different bands
|
|
// * image that isn't even pages (i.e. partially empty pages at edge)
|
|
CPLDebug("FIT write", "using block size %ix%i", blockX, blockY);
|
|
|
|
head->xPageSize = blockX;
|
|
gst_swapb(head->xPageSize);
|
|
head->yPageSize = blockY;
|
|
gst_swapb(head->yPageSize);
|
|
head->zPageSize = 1;
|
|
gst_swapb(head->zPageSize);
|
|
head->cPageSize = nBands;
|
|
gst_swapb(head->cPageSize);
|
|
|
|
// XXX - need to check all bands
|
|
head->minValue = firstBand->GetMinimum();
|
|
gst_swapb(head->minValue);
|
|
// XXX - need to check all bands
|
|
head->maxValue = firstBand->GetMaximum();
|
|
gst_swapb(head->maxValue);
|
|
head->dataOffset = static_cast<unsigned int>(size);
|
|
gst_swapb(head->dataOffset);
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFWriteL(head, size, 1, fpImage));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over image, copying image data. */
|
|
/* -------------------------------------------------------------------- */
|
|
unsigned long bytesPerPixel = nBands * nDTSize;
|
|
|
|
size_t pageBytes = blockX * blockY * bytesPerPixel;
|
|
char *output = (char *)calloc(1, pageBytes);
|
|
if (!output)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory,
|
|
"FITRasterBand couldn't allocate %lu bytes",
|
|
static_cast<unsigned long>(pageBytes));
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
return nullptr;
|
|
}
|
|
FreeGuard<char> guardOutput(output);
|
|
|
|
long maxx = (long)ceil(poSrcDS->GetRasterXSize() / (double)blockX);
|
|
long maxy = (long)ceil(poSrcDS->GetRasterYSize() / (double)blockY);
|
|
long maxx_full = (long)floor(poSrcDS->GetRasterXSize() / (double)blockX);
|
|
long maxy_full = (long)floor(poSrcDS->GetRasterYSize() / (double)blockY);
|
|
|
|
CPLDebug("FIT", "about to write %ld x %ld blocks", maxx, maxy);
|
|
|
|
for (long y = 0; y < maxy; y++)
|
|
for (long x = 0; x < maxx; x++)
|
|
{
|
|
long readX = blockX;
|
|
long readY = blockY;
|
|
int do_clean = FALSE;
|
|
|
|
// handle cases where image size isn't an exact multiple
|
|
// of page size
|
|
if (x >= maxx_full)
|
|
{
|
|
readX = poSrcDS->GetRasterXSize() % blockX;
|
|
do_clean = TRUE;
|
|
}
|
|
if (y >= maxy_full)
|
|
{
|
|
readY = poSrcDS->GetRasterYSize() % blockY;
|
|
do_clean = TRUE;
|
|
}
|
|
|
|
// clean out image if only doing partial reads
|
|
if (do_clean)
|
|
memset(output, 0, pageBytes);
|
|
|
|
for (int iBand = 0; iBand < nBands; iBand++)
|
|
{
|
|
GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
|
|
CPLErr eErr = poBand->RasterIO(
|
|
GF_Read, // eRWFlag
|
|
static_cast<int>(x * blockX), // nXOff
|
|
static_cast<int>(y * blockY), // nYOff
|
|
static_cast<int>(readX), // nXSize
|
|
static_cast<int>(readY), // nYSize
|
|
output + iBand * nDTSize,
|
|
// pData
|
|
blockX, // nBufXSize
|
|
blockY, // nBufYSize
|
|
firstBand->GetRasterDataType(),
|
|
// eBufType
|
|
bytesPerPixel, // nPixelSpace
|
|
bytesPerPixel * blockX, nullptr); // nLineSpace
|
|
if (eErr != CE_None)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO,
|
|
"FIT write - CreateCopy got read error %i", eErr);
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
VSIUnlink(pszFilename);
|
|
return nullptr;
|
|
}
|
|
} // for iBand
|
|
|
|
#ifdef swapping
|
|
char *p = output;
|
|
unsigned long i;
|
|
switch (nDTSize)
|
|
{
|
|
case 1:
|
|
// do nothing
|
|
break;
|
|
case 2:
|
|
for (i = 0; i < pageBytes; i += nDTSize)
|
|
gst_swap16(p + i);
|
|
break;
|
|
case 4:
|
|
for (i = 0; i < pageBytes; i += nDTSize)
|
|
gst_swap32(p + i);
|
|
break;
|
|
case 8:
|
|
for (i = 0; i < pageBytes; i += nDTSize)
|
|
gst_swap64(p + i);
|
|
break;
|
|
default:
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"FIT write - unsupported bytesPerPixel %d",
|
|
nDTSize);
|
|
} // switch
|
|
#endif // swapping
|
|
|
|
if (VSIFWriteL(output, 1, pageBytes, fpImage) != pageBytes)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO, "Write failed");
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
VSIUnlink(pszFilename);
|
|
return nullptr;
|
|
}
|
|
|
|
double perc = ((double)(y * maxx + x)) / (maxx * maxy);
|
|
if (!pfnProgress(perc, nullptr, pProgressData))
|
|
{
|
|
CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
|
|
// free(output);
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
VSIUnlink(pszFilename);
|
|
return nullptr;
|
|
}
|
|
} // for x
|
|
|
|
// free(output);
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
|
|
pfnProgress(1.0, nullptr, pProgressData);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Re-open dataset, and copy any auxiliary pam information. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_ReadOnly);
|
|
|
|
if (poDS)
|
|
poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
|
|
|
|
return poDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
// CPLErr FITDataset::GetGeoTransform( double * padfTransform )
|
|
// {
|
|
// CPLDebug("FIT", "FITDataset::GetGeoTransform");
|
|
// memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
|
|
// return CE_None;
|
|
// }
|
|
|
|
/************************************************************************/
|
|
/* GDALRegister_FIT() */
|
|
/************************************************************************/
|
|
|
|
void GDALRegister_FIT()
|
|
|
|
{
|
|
if (GDALGetDriverByName("FIT") != nullptr)
|
|
return;
|
|
|
|
GDALDriver *poDriver = new GDALDriver();
|
|
|
|
poDriver->SetDescription("FIT");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
|
|
poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "FIT Image");
|
|
poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/fit.html");
|
|
poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
|
|
|
|
poDriver->pfnOpen = FITDataset::Open;
|
|
poDriver->pfnCreateCopy = FITCreateCopy;
|
|
poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
|
|
"Byte UInt16 Int16 UInt32 Int32 "
|
|
"Float32 Float64");
|
|
|
|
GetGDALDriverManager()->RegisterDriver(poDriver);
|
|
}
|