Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SGI Image Driver
4 : * Purpose: Implement SGI Image Support based on Paul Bourke's SGI Image code.
5 : * http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
6 : * ftp://ftp.sgi.com/graphics/SGIIMAGESPEC
7 : * Authors: Mike Mazzella (GDAL driver)
8 : * Paul Bourke (original SGI format code)
9 : * Frank Warmerdam (write support)
10 : *
11 : ******************************************************************************
12 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
13 : * Copyright (c) 2008-2010, Even Rouault <even dot rouault at spatialys.com>
14 : *
15 : * SPDX-License-Identifier: MIT
16 : ****************************************************************************/
17 :
18 : #include "cpl_port.h"
19 : #include "cpl_string.h"
20 : #include "gdal_frmts.h"
21 : #include "gdal_pam.h"
22 :
23 : #include <algorithm>
24 :
25 : struct ImageRec
26 : {
27 : GUInt16 imagic;
28 : GByte type;
29 : GByte bpc;
30 : GUInt16 dim;
31 : GUInt16 xsize;
32 : GUInt16 ysize;
33 : GUInt16 zsize;
34 : GUInt32 min;
35 : GUInt32 max;
36 : char wasteBytes[4];
37 : char name[80];
38 : GUInt32 colorMap;
39 :
40 : VSILFILE *file;
41 : std::string fileName;
42 : int tmpSize;
43 : unsigned char *tmp;
44 : GUInt32 rleEnd;
45 : int rleTableDirty;
46 : GUInt32 *rowStart;
47 : GInt32 *rowSize;
48 :
49 5734 : ImageRec()
50 5734 : : imagic(0), type(0), bpc(1), dim(0), xsize(0), ysize(0), zsize(0),
51 : min(0), max(0), colorMap(0), file(nullptr), fileName(""), tmpSize(0),
52 : tmp(nullptr), rleEnd(0), rleTableDirty(FALSE), rowStart(nullptr),
53 5734 : rowSize(nullptr)
54 : {
55 5734 : memset(wasteBytes, 0, 4);
56 5734 : memset(name, 0, 80);
57 5734 : }
58 :
59 5734 : void Swap()
60 : {
61 : #ifdef CPL_LSB
62 5734 : CPL_SWAP16PTR(&imagic);
63 5734 : CPL_SWAP16PTR(&dim);
64 5734 : CPL_SWAP16PTR(&xsize);
65 5734 : CPL_SWAP16PTR(&ysize);
66 5734 : CPL_SWAP16PTR(&zsize);
67 5734 : CPL_SWAP32PTR(&min);
68 5734 : CPL_SWAP32PTR(&max);
69 : #endif
70 5734 : }
71 : };
72 :
73 : /************************************************************************/
74 : /* ConvertLong() */
75 : /************************************************************************/
76 : #ifdef CPL_LSB
77 60 : static void ConvertLong(GUInt32 *array, GInt32 length)
78 : {
79 60 : GUInt32 *ptr = reinterpret_cast<GUInt32 *>(array);
80 7300 : while (length--)
81 : {
82 7240 : CPL_SWAP32PTR(ptr);
83 7240 : ptr++;
84 : }
85 60 : }
86 : #else
87 : static void ConvertLong(GUInt32 * /*array*/, GInt32 /*length */)
88 : {
89 : }
90 : #endif
91 :
92 : /************************************************************************/
93 : /* ImageGetRow() */
94 : /************************************************************************/
95 90 : static CPLErr ImageGetRow(ImageRec *image, unsigned char *buf, int y, int z)
96 : {
97 90 : y = image->ysize - 1 - y;
98 :
99 90 : if (static_cast<int>(image->type) != 1)
100 : {
101 0 : VSIFSeekL(image->file,
102 0 : 512 + (y * static_cast<vsi_l_offset>(image->xsize)) +
103 0 : (z * static_cast<vsi_l_offset>(image->xsize) *
104 0 : static_cast<vsi_l_offset>(image->ysize)),
105 : SEEK_SET);
106 0 : if (VSIFReadL(buf, 1, image->xsize, image->file) != image->xsize)
107 : {
108 0 : CPLError(CE_Failure, CPLE_OpenFailed,
109 : "file read error: row (%d) of (%s)\n", y,
110 0 : image->fileName.empty() ? "none"
111 0 : : image->fileName.c_str());
112 0 : return CE_Failure;
113 : }
114 0 : return CE_None;
115 : }
116 :
117 : // Image type 1.
118 :
119 : // reads row
120 90 : if (image->rowSize[y + z * image->ysize] < 0 ||
121 90 : image->rowSize[y + z * image->ysize] > image->tmpSize)
122 : {
123 0 : return CE_Failure;
124 : }
125 90 : VSIFSeekL(image->file,
126 90 : static_cast<long>(image->rowStart[y + z * image->ysize]),
127 : SEEK_SET);
128 180 : if (VSIFReadL(image->tmp, 1,
129 90 : static_cast<GUInt32>(image->rowSize[y + z * image->ysize]),
130 90 : image->file) !=
131 90 : static_cast<GUInt32>(image->rowSize[y + z * image->ysize]))
132 : {
133 0 : CPLError(CE_Failure, CPLE_OpenFailed,
134 : "file read error: row (%d) of (%s)\n", y,
135 0 : image->fileName.empty() ? "none" : image->fileName.c_str());
136 0 : return CE_Failure;
137 : }
138 :
139 : // expands row
140 90 : unsigned char *iPtr = image->tmp;
141 90 : unsigned char *oPtr = buf;
142 90 : int xsizeCount = 0;
143 : for (;;)
144 : {
145 288 : unsigned char pixel = *iPtr++;
146 288 : int count = static_cast<int>(pixel & 0x7F);
147 288 : if (!count)
148 : {
149 90 : if (xsizeCount != image->xsize)
150 : {
151 0 : CPLError(CE_Failure, CPLE_OpenFailed,
152 : "file read error: row (%d) of (%s)\n", y,
153 0 : image->fileName.empty() ? "none"
154 0 : : image->fileName.c_str());
155 0 : return CE_Failure;
156 : }
157 : else
158 : {
159 90 : break;
160 : }
161 : }
162 :
163 198 : if (xsizeCount + count > image->xsize)
164 : {
165 0 : CPLError(CE_Failure, CPLE_AppDefined,
166 : "Wrong repetition number that would overflow data "
167 : "at line %d",
168 : y);
169 0 : return CE_Failure;
170 : }
171 :
172 198 : if (pixel & 0x80)
173 : {
174 110 : memcpy(oPtr, iPtr, count);
175 110 : iPtr += count;
176 : }
177 : else
178 : {
179 88 : pixel = *iPtr++;
180 88 : memset(oPtr, pixel, count);
181 : }
182 198 : oPtr += count;
183 198 : xsizeCount += count;
184 198 : }
185 :
186 90 : return CE_None;
187 : }
188 :
189 : /************************************************************************/
190 : /* ==================================================================== */
191 : /* SGIDataset */
192 : /* ==================================================================== */
193 : /************************************************************************/
194 :
195 : class SGIRasterBand;
196 :
197 : class SGIDataset final : public GDALPamDataset
198 : {
199 : friend class SGIRasterBand;
200 :
201 : VSILFILE *fpImage;
202 :
203 : int bGeoTransformValid;
204 : double adfGeoTransform[6];
205 :
206 : ImageRec image;
207 :
208 : public:
209 : SGIDataset();
210 : virtual ~SGIDataset();
211 :
212 : virtual CPLErr GetGeoTransform(double *) override;
213 : static GDALDataset *Open(GDALOpenInfo *);
214 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
215 : int nBandsIn, GDALDataType eType,
216 : char **papszOptions);
217 : };
218 :
219 : /************************************************************************/
220 : /* ==================================================================== */
221 : /* SGIRasterBand */
222 : /* ==================================================================== */
223 : /************************************************************************/
224 :
225 : class SGIRasterBand final : public GDALPamRasterBand
226 : {
227 : friend class SGIDataset;
228 :
229 : public:
230 : SGIRasterBand(SGIDataset *, int);
231 :
232 : virtual CPLErr IReadBlock(int, int, void *) override;
233 : virtual CPLErr IWriteBlock(int, int, void *) override;
234 : virtual GDALColorInterp GetColorInterpretation() override;
235 : };
236 :
237 : /************************************************************************/
238 : /* SGIRasterBand() */
239 : /************************************************************************/
240 :
241 53 : SGIRasterBand::SGIRasterBand(SGIDataset *poDSIn, int nBandIn)
242 :
243 : {
244 53 : poDS = poDSIn;
245 53 : nBand = nBandIn;
246 :
247 53 : if (static_cast<int>(poDSIn->image.bpc) == 1)
248 53 : eDataType = GDT_Byte;
249 : else
250 0 : eDataType = GDT_Int16;
251 :
252 53 : nBlockXSize = poDSIn->nRasterXSize;
253 53 : nBlockYSize = 1;
254 53 : }
255 :
256 : /************************************************************************/
257 : /* IReadBlock() */
258 : /************************************************************************/
259 :
260 90 : CPLErr SGIRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
261 : void *pImage)
262 : {
263 90 : SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
264 :
265 90 : CPLAssert(nBlockXOff == 0);
266 :
267 : /* -------------------------------------------------------------------- */
268 : /* Load the desired data into the working buffer. */
269 : /* -------------------------------------------------------------------- */
270 180 : return ImageGetRow(&(poGDS->image),
271 : reinterpret_cast<unsigned char *>(pImage), nBlockYOff,
272 90 : nBand - 1);
273 : }
274 :
275 : /************************************************************************/
276 : /* IWritelock() */
277 : /************************************************************************/
278 :
279 220 : CPLErr SGIRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
280 : void *pImage)
281 : {
282 220 : CPLAssert(nBlockXOff == 0);
283 :
284 220 : SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
285 220 : ImageRec *image = &(poGDS->image);
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Handle the fairly trivial non-RLE case. */
289 : /* -------------------------------------------------------------------- */
290 220 : if (image->type == 0)
291 : {
292 0 : VSIFSeekL(image->file,
293 0 : 512 + (nBlockYOff * static_cast<vsi_l_offset>(image->xsize)) +
294 0 : ((nBand - 1) * static_cast<vsi_l_offset>(image->xsize) *
295 0 : static_cast<vsi_l_offset>(image->ysize)),
296 : SEEK_SET);
297 0 : if (VSIFWriteL(pImage, 1, image->xsize, image->file) != image->xsize)
298 : {
299 0 : CPLError(CE_Failure, CPLE_OpenFailed,
300 : "file write error: row (%d)\n", nBlockYOff);
301 0 : return CE_Failure;
302 : }
303 0 : return CE_None;
304 : }
305 :
306 : /* -------------------------------------------------------------------- */
307 : /* Handle RLE case. */
308 : /* -------------------------------------------------------------------- */
309 220 : const GByte *pabyRawBuf = reinterpret_cast<const GByte *>(pImage);
310 : GByte *pabyRLEBuf =
311 220 : reinterpret_cast<GByte *>(CPLMalloc(image->xsize * 2 + 6));
312 :
313 220 : int iX = 0;
314 220 : int nRLEBytes = 0;
315 :
316 528 : while (iX < image->xsize)
317 : {
318 308 : int nRepeatCount = 1;
319 :
320 2216 : while (iX + nRepeatCount < image->xsize && nRepeatCount < 127 &&
321 2065 : pabyRawBuf[iX + nRepeatCount] == pabyRawBuf[iX])
322 1908 : nRepeatCount++;
323 :
324 308 : if (nRepeatCount > 2 || iX + nRepeatCount == image->xsize ||
325 90 : (iX + nRepeatCount < image->xsize - 3 &&
326 87 : pabyRawBuf[iX + nRepeatCount + 1] ==
327 87 : pabyRawBuf[iX + nRepeatCount + 2] &&
328 16 : pabyRawBuf[iX + nRepeatCount + 1] ==
329 16 : pabyRawBuf[iX + nRepeatCount + 3]))
330 : { // encode a constant run.
331 226 : pabyRLEBuf[nRLEBytes++] = static_cast<GByte>(nRepeatCount);
332 226 : pabyRLEBuf[nRLEBytes++] = pabyRawBuf[iX];
333 226 : iX += nRepeatCount;
334 : }
335 : else
336 : { // copy over mixed data.
337 2272 : for (nRepeatCount = 1;
338 2272 : iX + nRepeatCount < image->xsize && nRepeatCount < 127;
339 : nRepeatCount++)
340 : {
341 2203 : if (iX + nRepeatCount + 3 >= image->xsize)
342 204 : continue;
343 :
344 : // quit if the next 3 pixels match
345 1999 : if (pabyRawBuf[iX + nRepeatCount] ==
346 1999 : pabyRawBuf[iX + nRepeatCount + 1] &&
347 91 : pabyRawBuf[iX + nRepeatCount] ==
348 91 : pabyRawBuf[iX + nRepeatCount + 2])
349 13 : break;
350 : }
351 :
352 82 : pabyRLEBuf[nRLEBytes++] = static_cast<GByte>(0x80 | nRepeatCount);
353 82 : memcpy(pabyRLEBuf + nRLEBytes, pabyRawBuf + iX, nRepeatCount);
354 :
355 82 : nRLEBytes += nRepeatCount;
356 82 : iX += nRepeatCount;
357 : }
358 : }
359 :
360 : // EOL marker.
361 220 : pabyRLEBuf[nRLEBytes++] = 0;
362 :
363 : /* -------------------------------------------------------------------- */
364 : /* Write RLE Buffer at end of file. */
365 : /* -------------------------------------------------------------------- */
366 220 : const int row =
367 220 : (image->ysize - nBlockYOff - 1) + (nBand - 1) * image->ysize;
368 :
369 220 : VSIFSeekL(image->file, 0, SEEK_END);
370 :
371 220 : image->rowStart[row] = static_cast<GUInt32>(VSIFTellL(image->file));
372 220 : image->rowSize[row] = nRLEBytes;
373 220 : image->rleTableDirty = TRUE;
374 :
375 220 : if (static_cast<int>(VSIFWriteL(pabyRLEBuf, 1, nRLEBytes, image->file)) !=
376 : nRLEBytes)
377 : {
378 0 : CPLFree(pabyRLEBuf);
379 0 : CPLError(CE_Failure, CPLE_OpenFailed, "file write error: row (%d)\n",
380 : nBlockYOff);
381 0 : return CE_Failure;
382 : }
383 :
384 220 : CPLFree(pabyRLEBuf);
385 :
386 220 : return CE_None;
387 : }
388 :
389 : /************************************************************************/
390 : /* GetColorInterpretation() */
391 : /************************************************************************/
392 :
393 0 : GDALColorInterp SGIRasterBand::GetColorInterpretation()
394 :
395 : {
396 0 : SGIDataset *poGDS = reinterpret_cast<SGIDataset *>(poDS);
397 :
398 0 : if (poGDS->nBands == 1)
399 0 : return GCI_GrayIndex;
400 0 : else if (poGDS->nBands == 2)
401 : {
402 0 : if (nBand == 1)
403 0 : return GCI_GrayIndex;
404 : else
405 0 : return GCI_AlphaBand;
406 : }
407 0 : else if (poGDS->nBands == 3)
408 : {
409 0 : if (nBand == 1)
410 0 : return GCI_RedBand;
411 0 : else if (nBand == 2)
412 0 : return GCI_GreenBand;
413 : else
414 0 : return GCI_BlueBand;
415 : }
416 0 : else if (poGDS->nBands == 4)
417 : {
418 0 : if (nBand == 1)
419 0 : return GCI_RedBand;
420 0 : else if (nBand == 2)
421 0 : return GCI_GreenBand;
422 0 : else if (nBand == 3)
423 0 : return GCI_BlueBand;
424 : else
425 0 : return GCI_AlphaBand;
426 : }
427 0 : return GCI_Undefined;
428 : }
429 :
430 : /************************************************************************/
431 : /* ==================================================================== */
432 : /* SGIDataset */
433 : /* ==================================================================== */
434 : /************************************************************************/
435 :
436 : /************************************************************************/
437 : /* SGIDataset() */
438 : /************************************************************************/
439 :
440 25 : SGIDataset::SGIDataset() : fpImage(nullptr), bGeoTransformValid(FALSE)
441 : {
442 25 : adfGeoTransform[0] = 0.0;
443 25 : adfGeoTransform[1] = 1.0;
444 25 : adfGeoTransform[2] = 0.0;
445 25 : adfGeoTransform[3] = 0.0;
446 25 : adfGeoTransform[4] = 0.0;
447 25 : adfGeoTransform[5] = 1.0;
448 25 : }
449 :
450 : /************************************************************************/
451 : /* ~SGIDataset() */
452 : /************************************************************************/
453 :
454 50 : SGIDataset::~SGIDataset()
455 :
456 : {
457 25 : FlushCache(true);
458 :
459 : // Do we need to write out rle table?
460 25 : if (image.rleTableDirty)
461 : {
462 7 : CPLDebug("SGI", "Flushing RLE offset table.");
463 7 : ConvertLong(image.rowStart, image.ysize * image.zsize);
464 7 : ConvertLong(reinterpret_cast<GUInt32 *>(image.rowSize),
465 7 : image.ysize * image.zsize);
466 :
467 7 : VSIFSeekL(fpImage, 512, SEEK_SET);
468 7 : size_t nSize =
469 7 : static_cast<size_t>(image.ysize) * static_cast<size_t>(image.zsize);
470 7 : VSIFWriteL(image.rowStart, 4, nSize, fpImage);
471 7 : VSIFWriteL(image.rowSize, 4, nSize, fpImage);
472 7 : image.rleTableDirty = FALSE;
473 : }
474 :
475 25 : if (fpImage != nullptr)
476 25 : VSIFCloseL(fpImage);
477 :
478 25 : CPLFree(image.tmp);
479 25 : CPLFree(image.rowSize);
480 25 : CPLFree(image.rowStart);
481 50 : }
482 :
483 : /************************************************************************/
484 : /* GetGeoTransform() */
485 : /************************************************************************/
486 :
487 5 : CPLErr SGIDataset::GetGeoTransform(double *padfTransform)
488 :
489 : {
490 5 : if (bGeoTransformValid)
491 : {
492 0 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
493 0 : return CE_None;
494 : }
495 :
496 5 : return GDALPamDataset::GetGeoTransform(padfTransform);
497 : }
498 :
499 : /************************************************************************/
500 : /* Open() */
501 : /************************************************************************/
502 :
503 34338 : GDALDataset *SGIDataset::Open(GDALOpenInfo *poOpenInfo)
504 :
505 : {
506 : /* -------------------------------------------------------------------- */
507 : /* First we check to see if the file has the expected header */
508 : /* bytes. */
509 : /* -------------------------------------------------------------------- */
510 34338 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
511 28629 : return nullptr;
512 :
513 11418 : ImageRec tmpImage;
514 5709 : memcpy(&tmpImage.imagic, poOpenInfo->pabyHeader + 0, 2);
515 5709 : memcpy(&tmpImage.type, poOpenInfo->pabyHeader + 2, 1);
516 5709 : memcpy(&tmpImage.bpc, poOpenInfo->pabyHeader + 3, 1);
517 5709 : memcpy(&tmpImage.dim, poOpenInfo->pabyHeader + 4, 2);
518 5709 : memcpy(&tmpImage.xsize, poOpenInfo->pabyHeader + 6, 2);
519 5709 : memcpy(&tmpImage.ysize, poOpenInfo->pabyHeader + 8, 2);
520 5709 : memcpy(&tmpImage.zsize, poOpenInfo->pabyHeader + 10, 2);
521 5709 : tmpImage.Swap();
522 :
523 5709 : if (tmpImage.imagic != 474)
524 5684 : return nullptr;
525 :
526 25 : if (tmpImage.type != 0 && tmpImage.type != 1)
527 0 : return nullptr;
528 :
529 25 : if (tmpImage.bpc != 1 && tmpImage.bpc != 2)
530 0 : return nullptr;
531 :
532 25 : if (tmpImage.dim != 1 && tmpImage.dim != 2 && tmpImage.dim != 3)
533 0 : return nullptr;
534 :
535 25 : if (tmpImage.bpc != 1)
536 : {
537 0 : CPLError(CE_Failure, CPLE_NotSupported,
538 : "The SGI driver only supports 1 byte channel values.\n");
539 0 : return nullptr;
540 : }
541 :
542 : /* -------------------------------------------------------------------- */
543 : /* Create a corresponding GDALDataset. */
544 : /* -------------------------------------------------------------------- */
545 25 : SGIDataset *poDS = new SGIDataset();
546 25 : poDS->eAccess = poOpenInfo->eAccess;
547 25 : poDS->fpImage = poOpenInfo->fpL;
548 25 : poOpenInfo->fpL = nullptr;
549 :
550 : /* -------------------------------------------------------------------- */
551 : /* Read pre-image data after ensuring the file is rewound. */
552 : /* -------------------------------------------------------------------- */
553 25 : VSIFSeekL(poDS->fpImage, 0, SEEK_SET);
554 25 : if (VSIFReadL(reinterpret_cast<void *>(&(poDS->image)), 1, 12,
555 25 : poDS->fpImage) != 12)
556 : {
557 0 : CPLError(CE_Failure, CPLE_OpenFailed,
558 : "file read error while reading header in sgidataset.cpp");
559 0 : delete poDS;
560 0 : return nullptr;
561 : }
562 25 : poDS->image.Swap();
563 25 : poDS->image.file = poDS->fpImage;
564 25 : poDS->image.fileName = poOpenInfo->pszFilename;
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* Capture some information from the file that is of interest. */
568 : /* -------------------------------------------------------------------- */
569 25 : poDS->nRasterXSize = poDS->image.xsize;
570 25 : poDS->nRasterYSize = poDS->image.ysize;
571 25 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
572 : {
573 0 : CPLError(CE_Failure, CPLE_OpenFailed,
574 : "Invalid image dimensions : %d x %d", poDS->nRasterXSize,
575 : poDS->nRasterYSize);
576 0 : delete poDS;
577 0 : return nullptr;
578 : }
579 25 : poDS->nBands = std::max(static_cast<GUInt16>(1), poDS->image.zsize);
580 25 : if (poDS->nBands > 256)
581 : {
582 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Too many bands : %d",
583 : poDS->nBands);
584 0 : delete poDS;
585 0 : return nullptr;
586 : }
587 :
588 25 : const int numItems = (static_cast<int>(poDS->image.bpc) == 1) ? 256 : 65536;
589 25 : if (poDS->image.xsize > INT_MAX / numItems)
590 : {
591 0 : delete poDS;
592 0 : return nullptr;
593 : }
594 25 : poDS->image.tmpSize = poDS->image.xsize * numItems;
595 25 : poDS->image.tmp =
596 25 : (unsigned char *)VSI_CALLOC_VERBOSE(poDS->image.xsize, numItems);
597 25 : if (poDS->image.tmp == nullptr)
598 : {
599 0 : delete poDS;
600 0 : return nullptr;
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Read RLE Pointer tables. */
605 : /* -------------------------------------------------------------------- */
606 25 : if (static_cast<int>(poDS->image.type) == 1) // RLE compressed
607 : {
608 25 : const size_t x = static_cast<size_t>(poDS->image.ysize) * poDS->nBands *
609 : sizeof(GUInt32);
610 25 : poDS->image.rowStart = reinterpret_cast<GUInt32 *>(VSI_MALLOC2_VERBOSE(
611 : poDS->image.ysize, poDS->nBands * sizeof(GUInt32)));
612 25 : poDS->image.rowSize = reinterpret_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(
613 : poDS->image.ysize, poDS->nBands * sizeof(GUInt32)));
614 25 : if (poDS->image.rowStart == nullptr || poDS->image.rowSize == nullptr)
615 : {
616 0 : delete poDS;
617 0 : return nullptr;
618 : }
619 25 : memset(poDS->image.rowStart, 0, x);
620 25 : memset(poDS->image.rowSize, 0, x);
621 25 : poDS->image.rleEnd = static_cast<GUInt32>(512 + (2 * x));
622 25 : VSIFSeekL(poDS->fpImage, 512, SEEK_SET);
623 25 : if (VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file) != x)
624 : {
625 2 : delete poDS;
626 2 : CPLError(CE_Failure, CPLE_OpenFailed,
627 : "file read error while reading start positions in "
628 : "sgidataset.cpp");
629 2 : return nullptr;
630 : }
631 23 : if (VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x)
632 : {
633 0 : delete poDS;
634 0 : CPLError(
635 : CE_Failure, CPLE_OpenFailed,
636 : "file read error while reading row lengths in sgidataset.cpp");
637 0 : return nullptr;
638 : }
639 23 : ConvertLong(poDS->image.rowStart,
640 23 : static_cast<int>(x / static_cast<int>(sizeof(GUInt32))));
641 23 : ConvertLong(reinterpret_cast<GUInt32 *>(poDS->image.rowSize),
642 23 : static_cast<int>(x / static_cast<int>(sizeof(GInt32))));
643 : }
644 : else // uncompressed.
645 : {
646 0 : poDS->image.rowStart = nullptr;
647 0 : poDS->image.rowSize = nullptr;
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Create band information objects. */
652 : /* -------------------------------------------------------------------- */
653 76 : for (int iBand = 0; iBand < poDS->nBands; iBand++)
654 53 : poDS->SetBand(iBand + 1, new SGIRasterBand(poDS, iBand + 1));
655 :
656 : /* -------------------------------------------------------------------- */
657 : /* Check for world file. */
658 : /* -------------------------------------------------------------------- */
659 46 : poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename,
660 23 : ".wld", poDS->adfGeoTransform);
661 :
662 : /* -------------------------------------------------------------------- */
663 : /* Initialize any PAM information. */
664 : /* -------------------------------------------------------------------- */
665 23 : poDS->SetDescription(poOpenInfo->pszFilename);
666 23 : poDS->TryLoadXML();
667 :
668 : /* -------------------------------------------------------------------- */
669 : /* Check for overviews. */
670 : /* -------------------------------------------------------------------- */
671 23 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
672 :
673 23 : return poDS;
674 : }
675 :
676 : /************************************************************************/
677 : /* Create() */
678 : /************************************************************************/
679 :
680 62 : GDALDataset *SGIDataset::Create(const char *pszFilename, int nXSize, int nYSize,
681 : int nBandsIn, GDALDataType eType,
682 : CPL_UNUSED char **papszOptions)
683 : {
684 62 : if (eType != GDT_Byte)
685 : {
686 36 : CPLError(CE_Failure, CPLE_AppDefined,
687 : "Attempt to create SGI dataset with an illegal\n"
688 : "data type (%s), only Byte supported by the format.\n",
689 : GDALGetDataTypeName(eType));
690 :
691 36 : return nullptr;
692 : }
693 :
694 : /* -------------------------------------------------------------------- */
695 : /* Open the file for output. */
696 : /* -------------------------------------------------------------------- */
697 26 : VSILFILE *fp = VSIFOpenL(pszFilename, "w");
698 26 : if (fp == nullptr)
699 : {
700 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create file '%s': %s",
701 3 : pszFilename, VSIStrerror(errno));
702 3 : return nullptr;
703 : }
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* Prepare and write 512 byte header. */
707 : /* -------------------------------------------------------------------- */
708 : GByte abyHeader[512];
709 :
710 23 : memset(abyHeader, 0, 512);
711 :
712 23 : abyHeader[0] = 1;
713 23 : abyHeader[1] = 218;
714 23 : abyHeader[2] = 1; // RLE
715 23 : abyHeader[3] = 1; // 8bit
716 :
717 : GInt16 nShortValue;
718 23 : if (nBandsIn == 1)
719 14 : nShortValue = CPL_MSBWORD16(2);
720 : else
721 9 : nShortValue = CPL_MSBWORD16(3);
722 23 : memcpy(abyHeader + 4, &nShortValue, 2);
723 :
724 23 : nShortValue = CPL_MSBWORD16(nXSize);
725 23 : memcpy(abyHeader + 6, &nShortValue, 2);
726 :
727 23 : nShortValue = CPL_MSBWORD16(nYSize);
728 23 : memcpy(abyHeader + 8, &nShortValue, 2);
729 :
730 23 : nShortValue = CPL_MSBWORD16(nBandsIn);
731 23 : memcpy(abyHeader + 10, &nShortValue, 2);
732 :
733 23 : GInt32 nIntValue = CPL_MSBWORD32(0);
734 23 : memcpy(abyHeader + 12, &nIntValue, 4);
735 :
736 23 : GUInt32 nUIntValue = CPL_MSBWORD32(255);
737 23 : memcpy(abyHeader + 16, &nUIntValue, 4);
738 :
739 23 : VSIFWriteL(abyHeader, 1, 512, fp);
740 :
741 : /* -------------------------------------------------------------------- */
742 : /* Create our RLE compressed zero-ed dummy line. */
743 : /* -------------------------------------------------------------------- */
744 : GByte *pabyRLELine =
745 23 : reinterpret_cast<GByte *>(CPLMalloc((nXSize / 127) * 2 + 4));
746 :
747 23 : int nPixelsRemaining = nXSize;
748 23 : GInt32 nRLEBytes = 0;
749 46 : while (nPixelsRemaining > 0)
750 : {
751 23 : pabyRLELine[nRLEBytes] =
752 23 : static_cast<GByte>(std::min(127, nPixelsRemaining));
753 23 : pabyRLELine[nRLEBytes + 1] = 0;
754 23 : nPixelsRemaining -= pabyRLELine[nRLEBytes];
755 :
756 23 : nRLEBytes += 2;
757 : }
758 :
759 : /* -------------------------------------------------------------------- */
760 : /* Prepare and write RLE offset/size tables with everything */
761 : /* zeroed indicating dummy lines. */
762 : /* -------------------------------------------------------------------- */
763 23 : const int nTableLen = nYSize * nBandsIn;
764 23 : GInt32 nDummyRLEOffset = 512 + 4 * nTableLen * 2;
765 :
766 23 : CPL_MSBPTR32(&nRLEBytes);
767 23 : CPL_MSBPTR32(&nDummyRLEOffset);
768 :
769 1843 : for (int i = 0; i < nTableLen; i++)
770 1820 : VSIFWriteL(&nDummyRLEOffset, 1, 4, fp);
771 :
772 1843 : for (int i = 0; i < nTableLen; i++)
773 1820 : VSIFWriteL(&nRLEBytes, 1, 4, fp);
774 :
775 : /* -------------------------------------------------------------------- */
776 : /* write the dummy RLE blank line. */
777 : /* -------------------------------------------------------------------- */
778 23 : CPL_MSBPTR32(&nRLEBytes);
779 23 : if (static_cast<GInt32>(VSIFWriteL(pabyRLELine, 1, nRLEBytes, fp)) !=
780 : nRLEBytes)
781 : {
782 2 : CPLError(CE_Failure, CPLE_FileIO, "Failure writing SGI file '%s'.\n%s",
783 2 : pszFilename, VSIStrerror(errno));
784 2 : VSIFCloseL(fp);
785 2 : CPLFree(pabyRLELine);
786 2 : return nullptr;
787 : }
788 :
789 21 : VSIFCloseL(fp);
790 21 : CPLFree(pabyRLELine);
791 :
792 21 : return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
793 : }
794 :
795 : /************************************************************************/
796 : /* GDALRegister_SGI() */
797 : /************************************************************************/
798 :
799 1595 : void GDALRegister_SGI()
800 :
801 : {
802 1595 : if (GDALGetDriverByName("SGI") != nullptr)
803 302 : return;
804 :
805 1293 : GDALDriver *poDriver = new GDALDriver();
806 :
807 1293 : poDriver->SetDescription("SGI");
808 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
809 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SGI Image File Format 1.0");
810 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rgb");
811 1293 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/rgb");
812 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/sgi.html");
813 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
814 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
815 :
816 1293 : poDriver->pfnOpen = SGIDataset::Open;
817 1293 : poDriver->pfnCreate = SGIDataset::Create;
818 :
819 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
820 : }
|