837 строки
29 KiB
C++
837 строки
29 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: SGI Image Driver
|
|
* Purpose: Implement SGI Image Support based on Paul Bourke's SGI Image code.
|
|
* http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
|
|
* ftp://ftp.sgi.com/graphics/SGIIMAGESPEC
|
|
* Authors: Mike Mazzella (GDAL driver)
|
|
* Paul Bourke (original SGI format code)
|
|
* Frank Warmerdam (write support)
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
|
|
* Copyright (c) 2008-2010, 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_port.h"
|
|
#include "cpl_string.h"
|
|
#include "gdal_frmts.h"
|
|
#include "gdal_pam.h"
|
|
|
|
#include <algorithm>
|
|
|
|
struct ImageRec
|
|
{
|
|
GUInt16 imagic;
|
|
GByte type;
|
|
GByte bpc;
|
|
GUInt16 dim;
|
|
GUInt16 xsize;
|
|
GUInt16 ysize;
|
|
GUInt16 zsize;
|
|
GUInt32 min;
|
|
GUInt32 max;
|
|
char wasteBytes[4];
|
|
char name[80];
|
|
GUInt32 colorMap;
|
|
|
|
VSILFILE *file;
|
|
std::string fileName;
|
|
int tmpSize;
|
|
unsigned char *tmp;
|
|
GUInt32 rleEnd;
|
|
int rleTableDirty;
|
|
GUInt32 *rowStart;
|
|
GInt32 *rowSize;
|
|
|
|
ImageRec()
|
|
: imagic(0), type(0), bpc(1), dim(0), xsize(0), ysize(0), zsize(0),
|
|
min(0), max(0), colorMap(0), file(nullptr), fileName(""), tmpSize(0),
|
|
tmp(nullptr), rleEnd(0), rleTableDirty(FALSE), rowStart(nullptr),
|
|
rowSize(nullptr)
|
|
{
|
|
memset(wasteBytes, 0, 4);
|
|
memset(name, 0, 80);
|
|
}
|
|
|
|
void Swap()
|
|
{
|
|
#ifdef CPL_LSB
|
|
CPL_SWAP16PTR(&imagic);
|
|
CPL_SWAP16PTR(&dim);
|
|
CPL_SWAP16PTR(&xsize);
|
|
CPL_SWAP16PTR(&ysize);
|
|
CPL_SWAP16PTR(&zsize);
|
|
CPL_SWAP32PTR(&min);
|
|
CPL_SWAP32PTR(&max);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ConvertLong() */
|
|
/************************************************************************/
|
|
#ifdef CPL_LSB
|
|
static void ConvertLong(GUInt32 *array, GInt32 length)
|
|
{
|
|
GUInt32 *ptr = reinterpret_cast<GUInt32 *>(array);
|
|
while (length--)
|
|
{
|
|
CPL_SWAP32PTR(ptr);
|
|
ptr++;
|
|
}
|
|
}
|
|
#else
|
|
static void ConvertLong(GUInt32 * /*array*/, GInt32 /*length */)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* ImageGetRow() */
|
|
/************************************************************************/
|
|
static CPLErr ImageGetRow(ImageRec *image, unsigned char *buf, int y, int z)
|
|
{
|
|
y = image->ysize - 1 - y;
|
|
|
|
if (static_cast<int>(image->type) != 1)
|
|
{
|
|
VSIFSeekL(image->file,
|
|
512 + (y * static_cast<vsi_l_offset>(image->xsize)) +
|
|
(z * static_cast<vsi_l_offset>(image->xsize) *
|
|
static_cast<vsi_l_offset>(image->ysize)),
|
|
SEEK_SET);
|
|
if (VSIFReadL(buf, 1, image->xsize, image->file) != image->xsize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file read error: row (%d) of (%s)\n", y,
|
|
image->fileName.empty() ? "none"
|
|
: image->fileName.c_str());
|
|
return CE_Failure;
|
|
}
|
|
return CE_None;
|
|
}
|
|
|
|
// Image type 1.
|
|
|
|
// reads row
|
|
if (image->rowSize[y + z * image->ysize] < 0 ||
|
|
image->rowSize[y + z * image->ysize] > image->tmpSize)
|
|
{
|
|
return CE_Failure;
|
|
}
|
|
VSIFSeekL(image->file,
|
|
static_cast<long>(image->rowStart[y + z * image->ysize]),
|
|
SEEK_SET);
|
|
if (VSIFReadL(image->tmp, 1,
|
|
static_cast<GUInt32>(image->rowSize[y + z * image->ysize]),
|
|
image->file) !=
|
|
static_cast<GUInt32>(image->rowSize[y + z * image->ysize]))
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file read error: row (%d) of (%s)\n", y,
|
|
image->fileName.empty() ? "none" : image->fileName.c_str());
|
|
return CE_Failure;
|
|
}
|
|
|
|
// expands row
|
|
unsigned char *iPtr = image->tmp;
|
|
unsigned char *oPtr = buf;
|
|
int xsizeCount = 0;
|
|
for (;;)
|
|
{
|
|
unsigned char pixel = *iPtr++;
|
|
int count = static_cast<int>(pixel & 0x7F);
|
|
if (!count)
|
|
{
|
|
if (xsizeCount != image->xsize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file read error: row (%d) of (%s)\n", y,
|
|
image->fileName.empty() ? "none"
|
|
: image->fileName.c_str());
|
|
return CE_Failure;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (xsizeCount + count > image->xsize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Wrong repetition number that would overflow data "
|
|
"at line %d",
|
|
y);
|
|
return CE_Failure;
|
|
}
|
|
|
|
if (pixel & 0x80)
|
|
{
|
|
memcpy(oPtr, iPtr, count);
|
|
iPtr += count;
|
|
}
|
|
else
|
|
{
|
|
pixel = *iPtr++;
|
|
memset(oPtr, pixel, count);
|
|
}
|
|
oPtr += count;
|
|
xsizeCount += count;
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SGIDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class SGIRasterBand;
|
|
|
|
class SGIDataset final : public GDALPamDataset
|
|
{
|
|
friend class SGIRasterBand;
|
|
|
|
VSILFILE *fpImage;
|
|
|
|
int bGeoTransformValid;
|
|
double adfGeoTransform[6];
|
|
|
|
ImageRec image;
|
|
|
|
public:
|
|
SGIDataset();
|
|
virtual ~SGIDataset();
|
|
|
|
virtual CPLErr GetGeoTransform(double *) override;
|
|
static GDALDataset *Open(GDALOpenInfo *);
|
|
static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
|
|
int nBandsIn, GDALDataType eType,
|
|
char **papszOptions);
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SGIRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class SGIRasterBand final : public GDALPamRasterBand
|
|
{
|
|
friend class SGIDataset;
|
|
|
|
public:
|
|
SGIRasterBand(SGIDataset *, int);
|
|
|
|
virtual CPLErr IReadBlock(int, int, void *) override;
|
|
virtual CPLErr IWriteBlock(int, int, void *) override;
|
|
virtual GDALColorInterp GetColorInterpretation() override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* SGIRasterBand() */
|
|
/************************************************************************/
|
|
|
|
SGIRasterBand::SGIRasterBand(SGIDataset *poDSIn, int nBandIn)
|
|
|
|
{
|
|
poDS = poDSIn;
|
|
nBand = nBandIn;
|
|
|
|
if (static_cast<int>(poDSIn->image.bpc) == 1)
|
|
eDataType = GDT_Byte;
|
|
else
|
|
eDataType = GDT_Int16;
|
|
|
|
nBlockXSize = poDSIn->nRasterXSize;
|
|
nBlockYSize = 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr SGIRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
|
|
|
|
CPLAssert(nBlockXOff == 0);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load the desired data into the working buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
return ImageGetRow(&(poGDS->image),
|
|
reinterpret_cast<unsigned char *>(pImage), nBlockYOff,
|
|
nBand - 1);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWritelock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr SGIRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
CPLAssert(nBlockXOff == 0);
|
|
|
|
SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
|
|
ImageRec *image = &(poGDS->image);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle the fairly trivial non-RLE case. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (image->type == 0)
|
|
{
|
|
VSIFSeekL(image->file,
|
|
512 + (nBlockYOff * static_cast<vsi_l_offset>(image->xsize)) +
|
|
((nBand - 1) * static_cast<vsi_l_offset>(image->xsize) *
|
|
static_cast<vsi_l_offset>(image->ysize)),
|
|
SEEK_SET);
|
|
if (VSIFWriteL(pImage, 1, image->xsize, image->file) != image->xsize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file write error: row (%d)\n", nBlockYOff);
|
|
return CE_Failure;
|
|
}
|
|
return CE_None;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle RLE case. */
|
|
/* -------------------------------------------------------------------- */
|
|
const GByte *pabyRawBuf = reinterpret_cast<const GByte *>(pImage);
|
|
GByte *pabyRLEBuf =
|
|
reinterpret_cast<GByte *>(CPLMalloc(image->xsize * 2 + 6));
|
|
|
|
int iX = 0;
|
|
int nRLEBytes = 0;
|
|
|
|
while (iX < image->xsize)
|
|
{
|
|
int nRepeatCount = 1;
|
|
|
|
while (iX + nRepeatCount < image->xsize && nRepeatCount < 127 &&
|
|
pabyRawBuf[iX + nRepeatCount] == pabyRawBuf[iX])
|
|
nRepeatCount++;
|
|
|
|
if (nRepeatCount > 2 || iX + nRepeatCount == image->xsize ||
|
|
(iX + nRepeatCount < image->xsize - 3 &&
|
|
pabyRawBuf[iX + nRepeatCount + 1] ==
|
|
pabyRawBuf[iX + nRepeatCount + 2] &&
|
|
pabyRawBuf[iX + nRepeatCount + 1] ==
|
|
pabyRawBuf[iX + nRepeatCount + 3]))
|
|
{ // encode a constant run.
|
|
pabyRLEBuf[nRLEBytes++] = static_cast<GByte>(nRepeatCount);
|
|
pabyRLEBuf[nRLEBytes++] = pabyRawBuf[iX];
|
|
iX += nRepeatCount;
|
|
}
|
|
else
|
|
{ // copy over mixed data.
|
|
for (nRepeatCount = 1;
|
|
iX + nRepeatCount < image->xsize && nRepeatCount < 127;
|
|
nRepeatCount++)
|
|
{
|
|
if (iX + nRepeatCount + 3 >= image->xsize)
|
|
continue;
|
|
|
|
// quit if the next 3 pixels match
|
|
if (pabyRawBuf[iX + nRepeatCount] ==
|
|
pabyRawBuf[iX + nRepeatCount + 1] &&
|
|
pabyRawBuf[iX + nRepeatCount] ==
|
|
pabyRawBuf[iX + nRepeatCount + 2])
|
|
break;
|
|
}
|
|
|
|
pabyRLEBuf[nRLEBytes++] = static_cast<GByte>(0x80 | nRepeatCount);
|
|
memcpy(pabyRLEBuf + nRLEBytes, pabyRawBuf + iX, nRepeatCount);
|
|
|
|
nRLEBytes += nRepeatCount;
|
|
iX += nRepeatCount;
|
|
}
|
|
}
|
|
|
|
// EOL marker.
|
|
pabyRLEBuf[nRLEBytes++] = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write RLE Buffer at end of file. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int row =
|
|
(image->ysize - nBlockYOff - 1) + (nBand - 1) * image->ysize;
|
|
|
|
VSIFSeekL(image->file, 0, SEEK_END);
|
|
|
|
image->rowStart[row] = static_cast<GUInt32>(VSIFTellL(image->file));
|
|
image->rowSize[row] = nRLEBytes;
|
|
image->rleTableDirty = TRUE;
|
|
|
|
if (static_cast<int>(VSIFWriteL(pabyRLEBuf, 1, nRLEBytes, image->file)) !=
|
|
nRLEBytes)
|
|
{
|
|
CPLFree(pabyRLEBuf);
|
|
CPLError(CE_Failure, CPLE_OpenFailed, "file write error: row (%d)\n",
|
|
nBlockYOff);
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLFree(pabyRLEBuf);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp SGIRasterBand::GetColorInterpretation()
|
|
|
|
{
|
|
SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
|
|
|
|
if (poGDS->nBands == 1)
|
|
return GCI_GrayIndex;
|
|
else if (poGDS->nBands == 2)
|
|
{
|
|
if (nBand == 1)
|
|
return GCI_GrayIndex;
|
|
else
|
|
return GCI_AlphaBand;
|
|
}
|
|
else if (poGDS->nBands == 3)
|
|
{
|
|
if (nBand == 1)
|
|
return GCI_RedBand;
|
|
else if (nBand == 2)
|
|
return GCI_GreenBand;
|
|
else
|
|
return GCI_BlueBand;
|
|
}
|
|
else if (poGDS->nBands == 4)
|
|
{
|
|
if (nBand == 1)
|
|
return GCI_RedBand;
|
|
else if (nBand == 2)
|
|
return GCI_GreenBand;
|
|
else if (nBand == 3)
|
|
return GCI_BlueBand;
|
|
else
|
|
return GCI_AlphaBand;
|
|
}
|
|
return GCI_Undefined;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SGIDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* SGIDataset() */
|
|
/************************************************************************/
|
|
|
|
SGIDataset::SGIDataset() : fpImage(nullptr), 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;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~SGIDataset() */
|
|
/************************************************************************/
|
|
|
|
SGIDataset::~SGIDataset()
|
|
|
|
{
|
|
FlushCache(true);
|
|
|
|
// Do we need to write out rle table?
|
|
if (image.rleTableDirty)
|
|
{
|
|
CPLDebug("SGI", "Flushing RLE offset table.");
|
|
ConvertLong(image.rowStart, image.ysize * image.zsize);
|
|
ConvertLong(reinterpret_cast<GUInt32 *>(image.rowSize),
|
|
image.ysize * image.zsize);
|
|
|
|
VSIFSeekL(fpImage, 512, SEEK_SET);
|
|
size_t nSize =
|
|
static_cast<size_t>(image.ysize) * static_cast<size_t>(image.zsize);
|
|
VSIFWriteL(image.rowStart, 4, nSize, fpImage);
|
|
VSIFWriteL(image.rowSize, 4, nSize, fpImage);
|
|
image.rleTableDirty = FALSE;
|
|
}
|
|
|
|
if (fpImage != nullptr)
|
|
VSIFCloseL(fpImage);
|
|
|
|
CPLFree(image.tmp);
|
|
CPLFree(image.rowSize);
|
|
CPLFree(image.rowStart);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
CPLErr SGIDataset::GetGeoTransform(double *padfTransform)
|
|
|
|
{
|
|
if (bGeoTransformValid)
|
|
{
|
|
memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
|
|
return CE_None;
|
|
}
|
|
|
|
return GDALPamDataset::GetGeoTransform(padfTransform);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Open() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *SGIDataset::Open(GDALOpenInfo *poOpenInfo)
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* First we check to see if the file has the expected header */
|
|
/* bytes. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
|
|
return nullptr;
|
|
|
|
ImageRec tmpImage;
|
|
memcpy(&tmpImage.imagic, poOpenInfo->pabyHeader + 0, 2);
|
|
memcpy(&tmpImage.type, poOpenInfo->pabyHeader + 2, 1);
|
|
memcpy(&tmpImage.bpc, poOpenInfo->pabyHeader + 3, 1);
|
|
memcpy(&tmpImage.dim, poOpenInfo->pabyHeader + 4, 2);
|
|
memcpy(&tmpImage.xsize, poOpenInfo->pabyHeader + 6, 2);
|
|
memcpy(&tmpImage.ysize, poOpenInfo->pabyHeader + 8, 2);
|
|
memcpy(&tmpImage.zsize, poOpenInfo->pabyHeader + 10, 2);
|
|
tmpImage.Swap();
|
|
|
|
if (tmpImage.imagic != 474)
|
|
return nullptr;
|
|
|
|
if (tmpImage.type != 0 && tmpImage.type != 1)
|
|
return nullptr;
|
|
|
|
if (tmpImage.bpc != 1 && tmpImage.bpc != 2)
|
|
return nullptr;
|
|
|
|
if (tmpImage.dim != 1 && tmpImage.dim != 2 && tmpImage.dim != 3)
|
|
return nullptr;
|
|
|
|
if (tmpImage.bpc != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"The SGI driver only supports 1 byte channel values.\n");
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a corresponding GDALDataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
SGIDataset *poDS = new SGIDataset();
|
|
poDS->eAccess = poOpenInfo->eAccess;
|
|
poDS->fpImage = poOpenInfo->fpL;
|
|
poOpenInfo->fpL = nullptr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read pre-image data after ensuring the file is rewound. */
|
|
/* -------------------------------------------------------------------- */
|
|
VSIFSeekL(poDS->fpImage, 0, SEEK_SET);
|
|
if (VSIFReadL(reinterpret_cast<void *>(&(poDS->image)), 1, 12,
|
|
poDS->fpImage) != 12)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file read error while reading header in sgidataset.cpp");
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
poDS->image.Swap();
|
|
poDS->image.file = poDS->fpImage;
|
|
poDS->image.fileName = poOpenInfo->pszFilename;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture some information from the file that is of interest. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->nRasterXSize = poDS->image.xsize;
|
|
poDS->nRasterYSize = poDS->image.ysize;
|
|
if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"Invalid image dimensions : %d x %d", poDS->nRasterXSize,
|
|
poDS->nRasterYSize);
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
poDS->nBands = std::max(static_cast<GUInt16>(1), poDS->image.zsize);
|
|
if (poDS->nBands > 256)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed, "Too many bands : %d",
|
|
poDS->nBands);
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
|
|
const int numItems = (static_cast<int>(poDS->image.bpc) == 1) ? 256 : 65536;
|
|
if (poDS->image.xsize > INT_MAX / numItems)
|
|
{
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
poDS->image.tmpSize = poDS->image.xsize * numItems;
|
|
poDS->image.tmp =
|
|
(unsigned char *)VSI_CALLOC_VERBOSE(poDS->image.xsize, numItems);
|
|
if (poDS->image.tmp == nullptr)
|
|
{
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read RLE Pointer tables. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (static_cast<int>(poDS->image.type) == 1) // RLE compressed
|
|
{
|
|
const size_t x = static_cast<size_t>(poDS->image.ysize) * poDS->nBands *
|
|
sizeof(GUInt32);
|
|
poDS->image.rowStart = reinterpret_cast<GUInt32 *>(VSI_MALLOC2_VERBOSE(
|
|
poDS->image.ysize, poDS->nBands * sizeof(GUInt32)));
|
|
poDS->image.rowSize = reinterpret_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(
|
|
poDS->image.ysize, poDS->nBands * sizeof(GUInt32)));
|
|
if (poDS->image.rowStart == nullptr || poDS->image.rowSize == nullptr)
|
|
{
|
|
delete poDS;
|
|
return nullptr;
|
|
}
|
|
memset(poDS->image.rowStart, 0, x);
|
|
memset(poDS->image.rowSize, 0, x);
|
|
poDS->image.rleEnd = static_cast<GUInt32>(512 + (2 * x));
|
|
VSIFSeekL(poDS->fpImage, 512, SEEK_SET);
|
|
if (VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file) != x)
|
|
{
|
|
delete poDS;
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"file read error while reading start positions in "
|
|
"sgidataset.cpp");
|
|
return nullptr;
|
|
}
|
|
if (VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x)
|
|
{
|
|
delete poDS;
|
|
CPLError(
|
|
CE_Failure, CPLE_OpenFailed,
|
|
"file read error while reading row lengths in sgidataset.cpp");
|
|
return nullptr;
|
|
}
|
|
ConvertLong(poDS->image.rowStart,
|
|
static_cast<int>(x / static_cast<int>(sizeof(GUInt32))));
|
|
ConvertLong(reinterpret_cast<GUInt32 *>(poDS->image.rowSize),
|
|
static_cast<int>(x / static_cast<int>(sizeof(GInt32))));
|
|
}
|
|
else // uncompressed.
|
|
{
|
|
poDS->image.rowStart = nullptr;
|
|
poDS->image.rowSize = nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create band information objects. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int iBand = 0; iBand < poDS->nBands; iBand++)
|
|
poDS->SetBand(iBand + 1, new SGIRasterBand(poDS, iBand + 1));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for world file. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename,
|
|
".wld", poDS->adfGeoTransform);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize any PAM information. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->SetDescription(poOpenInfo->pszFilename);
|
|
poDS->TryLoadXML();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
|
|
|
|
return poDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *SGIDataset::Create(const char *pszFilename, int nXSize, int nYSize,
|
|
int nBandsIn, GDALDataType eType,
|
|
CPL_UNUSED char **papszOptions)
|
|
{
|
|
if (eType != GDT_Byte)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Attempt to create SGI dataset with an illegal\n"
|
|
"data type (%s), only Byte supported by the format.\n",
|
|
GDALGetDataTypeName(eType));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Open the file for output. */
|
|
/* -------------------------------------------------------------------- */
|
|
VSILFILE *fp = VSIFOpenL(pszFilename, "w");
|
|
if (fp == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create file '%s': %s",
|
|
pszFilename, VSIStrerror(errno));
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Prepare and write 512 byte header. */
|
|
/* -------------------------------------------------------------------- */
|
|
GByte abyHeader[512];
|
|
|
|
memset(abyHeader, 0, 512);
|
|
|
|
abyHeader[0] = 1;
|
|
abyHeader[1] = 218;
|
|
abyHeader[2] = 1; // RLE
|
|
abyHeader[3] = 1; // 8bit
|
|
|
|
GInt16 nShortValue;
|
|
if (nBandsIn == 1)
|
|
nShortValue = CPL_MSBWORD16(2);
|
|
else
|
|
nShortValue = CPL_MSBWORD16(3);
|
|
memcpy(abyHeader + 4, &nShortValue, 2);
|
|
|
|
nShortValue = CPL_MSBWORD16(nXSize);
|
|
memcpy(abyHeader + 6, &nShortValue, 2);
|
|
|
|
nShortValue = CPL_MSBWORD16(nYSize);
|
|
memcpy(abyHeader + 8, &nShortValue, 2);
|
|
|
|
nShortValue = CPL_MSBWORD16(nBandsIn);
|
|
memcpy(abyHeader + 10, &nShortValue, 2);
|
|
|
|
GInt32 nIntValue = CPL_MSBWORD32(0);
|
|
memcpy(abyHeader + 12, &nIntValue, 4);
|
|
|
|
GUInt32 nUIntValue = CPL_MSBWORD32(255);
|
|
memcpy(abyHeader + 16, &nUIntValue, 4);
|
|
|
|
VSIFWriteL(abyHeader, 1, 512, fp);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create our RLE compressed zero-ed dummy line. */
|
|
/* -------------------------------------------------------------------- */
|
|
GByte *pabyRLELine =
|
|
reinterpret_cast<GByte *>(CPLMalloc((nXSize / 127) * 2 + 4));
|
|
|
|
int nPixelsRemaining = nXSize;
|
|
GInt32 nRLEBytes = 0;
|
|
while (nPixelsRemaining > 0)
|
|
{
|
|
pabyRLELine[nRLEBytes] =
|
|
static_cast<GByte>(std::min(127, nPixelsRemaining));
|
|
pabyRLELine[nRLEBytes + 1] = 0;
|
|
nPixelsRemaining -= pabyRLELine[nRLEBytes];
|
|
|
|
nRLEBytes += 2;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Prepare and write RLE offset/size tables with everything */
|
|
/* zeroed indicating dummy lines. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int nTableLen = nYSize * nBandsIn;
|
|
GInt32 nDummyRLEOffset = 512 + 4 * nTableLen * 2;
|
|
|
|
CPL_MSBPTR32(&nRLEBytes);
|
|
CPL_MSBPTR32(&nDummyRLEOffset);
|
|
|
|
for (int i = 0; i < nTableLen; i++)
|
|
VSIFWriteL(&nDummyRLEOffset, 1, 4, fp);
|
|
|
|
for (int i = 0; i < nTableLen; i++)
|
|
VSIFWriteL(&nRLEBytes, 1, 4, fp);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* write the dummy RLE blank line. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPL_MSBPTR32(&nRLEBytes);
|
|
if (static_cast<GInt32>(VSIFWriteL(pabyRLELine, 1, nRLEBytes, fp)) !=
|
|
nRLEBytes)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO, "Failure writing SGI file '%s'.\n%s",
|
|
pszFilename, VSIStrerror(errno));
|
|
VSIFCloseL(fp);
|
|
CPLFree(pabyRLELine);
|
|
return nullptr;
|
|
}
|
|
|
|
VSIFCloseL(fp);
|
|
CPLFree(pabyRLELine);
|
|
|
|
return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRegister_SGI() */
|
|
/************************************************************************/
|
|
|
|
void GDALRegister_SGI()
|
|
|
|
{
|
|
if (GDALGetDriverByName("SGI") != nullptr)
|
|
return;
|
|
|
|
GDALDriver *poDriver = new GDALDriver();
|
|
|
|
poDriver->SetDescription("SGI");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
|
|
poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SGI Image File Format 1.0");
|
|
poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rgb");
|
|
poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/rgb");
|
|
poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/sgi.html");
|
|
poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
|
|
|
|
poDriver->pfnOpen = SGIDataset::Open;
|
|
poDriver->pfnCreate = SGIDataset::Create;
|
|
|
|
GetGDALDriverManager()->RegisterDriver(poDriver);
|
|
}
|