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