gdal/frmts/sgi/sgidataset.cpp

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);
}