Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: TGA read-only driver
4 : * Author: Even Rouault <even.rouault at spatialys.com>
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2020, Even Rouault <even.rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "gdal_pam.h"
13 : #include "gdal_frmts.h"
14 : #include "gdal_colortable.h"
15 : #include "gdal_driver.h"
16 : #include "gdal_drivermanager.h"
17 : #include "gdal_openinfo.h"
18 : #include "gdal_cpp_functions.h"
19 :
20 : #include <algorithm>
21 : #include <cassert>
22 : #include <vector>
23 :
24 : enum ImageType
25 : {
26 : UNCOMPRESSED_COLORMAP = 1,
27 : UNCOMPRESSED_TRUE_COLOR = 2,
28 : UNCOMPRESSED_GRAYSCALE = 3,
29 : RLE_COLORMAP = 9,
30 : RLE_TRUE_COLOR = 10,
31 : RLE_GRAYSCALE = 11,
32 : };
33 :
34 : struct ImageHeader
35 : {
36 : GByte nIDLength;
37 : bool bHasColorMap;
38 : ImageType eImageType;
39 : GUInt16 nColorMapFirstIdx;
40 : GUInt16 nColorMapLength;
41 : GByte nColorMapEntrySize;
42 : GUInt16 nXOrigin;
43 : GUInt16 nYOrigin;
44 : GByte nPixelDepth;
45 : GByte nImageDescriptor;
46 : };
47 :
48 : /************************************************************************/
49 : /* GDALTGADataset */
50 : /************************************************************************/
51 :
52 : class GDALTGADataset final : public GDALPamDataset
53 : {
54 : friend class GDALTGARasterBand;
55 :
56 : struct ScanlineState
57 : {
58 : // Offset in the file of the start of the scanline
59 : vsi_l_offset nOffset = 0;
60 : bool bRemainingPixelsAreRLERun = false;
61 : // Number of pixels remaining from a previous scanline.
62 : // See
63 : // https://en.wikipedia.org/wiki/Truevision_TGA#Specification_discrepancies
64 : // TGA v2.0 specification states "Run-length Packets should never
65 : // encode pixels from more than one scan line." but earlier
66 : // specification said the contrary.
67 : int nRemainingPixelsPrevScanline = 0;
68 : // Value of pixels remaining from a previous RLE run
69 : std::vector<GByte> abyDataPrevRLERun{};
70 : };
71 :
72 : ImageHeader m_sImageHeader;
73 : VSILFILE *m_fpImage;
74 : unsigned m_nImageDataOffset = 0;
75 : std::vector<ScanlineState> m_aoScanlineState{};
76 : int m_nLastLineKnownOffset = 0;
77 : bool m_bFourthChannelIsAlpha = false;
78 :
79 : CPL_DISALLOW_COPY_ASSIGN(GDALTGADataset)
80 :
81 : public:
82 : GDALTGADataset(const ImageHeader &sHeader, VSILFILE *fpImage);
83 : ~GDALTGADataset() override;
84 :
85 : static int Identify(GDALOpenInfo *poOpenInfo);
86 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
87 : };
88 :
89 : /************************************************************************/
90 : /* GDALTGARasterBand */
91 : /************************************************************************/
92 :
93 : class GDALTGARasterBand final : public GDALPamRasterBand
94 : {
95 : std::unique_ptr<GDALColorTable> m_poColorTable{};
96 : bool m_bHasNoDataValue = false;
97 : double m_dfNoDataValue = 0;
98 :
99 : public:
100 : GDALTGARasterBand(GDALTGADataset *poDSIn, int nBandIn,
101 : GDALDataType eDataTypeIn);
102 :
103 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
104 :
105 9 : GDALColorTable *GetColorTable() override
106 : {
107 9 : return m_poColorTable.get();
108 : }
109 :
110 20 : GDALColorInterp GetColorInterpretation() override
111 : {
112 20 : if (m_poColorTable)
113 1 : return GCI_PaletteIndex;
114 19 : GDALTGADataset *poGDS = cpl::down_cast<GDALTGADataset *>(poDS);
115 19 : if (poGDS->GetRasterCount() == 1)
116 2 : return GCI_GrayIndex;
117 17 : if (nBand == 4)
118 2 : return poGDS->m_bFourthChannelIsAlpha ? GCI_AlphaBand
119 2 : : GCI_Undefined;
120 15 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
121 : }
122 :
123 0 : double GetNoDataValue(int *pbHasNoData) override
124 : {
125 0 : if (pbHasNoData)
126 0 : *pbHasNoData = m_bHasNoDataValue;
127 0 : return m_dfNoDataValue;
128 : }
129 : };
130 :
131 : /************************************************************************/
132 : /* Identify() */
133 : /************************************************************************/
134 :
135 58066 : int GDALTGADataset::Identify(GDALOpenInfo *poOpenInfo)
136 : {
137 58066 : if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 18)
138 55058 : return FALSE;
139 3008 : const GByte nColorType = poOpenInfo->pabyHeader[1];
140 3008 : if (nColorType > 1)
141 2097 : return FALSE;
142 911 : const GByte nImageType = poOpenInfo->pabyHeader[2];
143 911 : if (nImageType != UNCOMPRESSED_COLORMAP &&
144 897 : nImageType != UNCOMPRESSED_TRUE_COLOR &&
145 895 : nImageType != UNCOMPRESSED_GRAYSCALE && nImageType != RLE_COLORMAP &&
146 889 : nImageType != RLE_TRUE_COLOR && nImageType != RLE_GRAYSCALE)
147 885 : return FALSE;
148 26 : if (nImageType == UNCOMPRESSED_COLORMAP || nImageType == RLE_COLORMAP)
149 : {
150 6 : if (nColorType != 1)
151 0 : return FALSE;
152 : }
153 : else
154 : {
155 20 : if (nColorType != 0)
156 2 : return FALSE;
157 : }
158 :
159 : // Mostly useful for fuzzing purposes to be able to fuzz TGA on small files
160 : // without relying on the tga extension
161 24 : if (poOpenInfo->nHeaderBytes > 26 &&
162 24 : memcmp(poOpenInfo->pabyHeader + poOpenInfo->nHeaderBytes - 26,
163 : "TRUEVISION-XFILE.\x00", 18) == 0)
164 : {
165 0 : return TRUE;
166 : }
167 :
168 24 : if (!poOpenInfo->IsExtensionEqualToCI("tga"))
169 2 : return FALSE;
170 22 : return TRUE;
171 : }
172 :
173 : /************************************************************************/
174 : /* GDALTGADataset() */
175 : /************************************************************************/
176 :
177 11 : GDALTGADataset::GDALTGADataset(const ImageHeader &sHeader, VSILFILE *fpImage)
178 11 : : m_sImageHeader(sHeader), m_fpImage(fpImage)
179 : {
180 11 : m_nImageDataOffset = 18 + m_sImageHeader.nIDLength;
181 11 : if (m_sImageHeader.bHasColorMap)
182 : {
183 2 : m_nImageDataOffset += m_sImageHeader.nColorMapLength *
184 2 : ((m_sImageHeader.nColorMapEntrySize + 7) / 8);
185 : }
186 11 : }
187 :
188 : /************************************************************************/
189 : /* ~GDALTGADataset() */
190 : /************************************************************************/
191 :
192 22 : GDALTGADataset::~GDALTGADataset()
193 : {
194 11 : if (m_fpImage)
195 11 : VSIFCloseL(m_fpImage);
196 22 : }
197 :
198 : /************************************************************************/
199 : /* GDALTGARasterBand() */
200 : /************************************************************************/
201 :
202 25 : GDALTGARasterBand::GDALTGARasterBand(GDALTGADataset *poDSIn, int nBandIn,
203 25 : GDALDataType eDataTypeIn)
204 : {
205 25 : poDS = poDSIn;
206 25 : nBand = nBandIn;
207 25 : eDataType = eDataTypeIn;
208 25 : nBlockXSize = poDSIn->GetRasterXSize();
209 25 : nBlockYSize = 1;
210 25 : if (poDSIn->m_sImageHeader.bHasColorMap)
211 : {
212 2 : VSIFSeekL(poDSIn->m_fpImage, 18 + poDSIn->m_sImageHeader.nIDLength,
213 : SEEK_SET);
214 2 : m_poColorTable.reset(new GDALColorTable());
215 2 : const int nColorTableByteCount =
216 2 : poDSIn->m_sImageHeader.nColorMapLength *
217 2 : ((poDSIn->m_sImageHeader.nColorMapEntrySize + 7) / 8);
218 4 : std::vector<GByte> abyData(nColorTableByteCount);
219 2 : VSIFReadL(&abyData[0], 1, abyData.size(), poDSIn->m_fpImage);
220 2 : if (poDSIn->m_sImageHeader.nColorMapEntrySize == 24)
221 : {
222 0 : for (unsigned i = 0; i < poDSIn->m_sImageHeader.nColorMapLength;
223 : ++i)
224 : {
225 : GDALColorEntry sEntry;
226 0 : sEntry.c1 = abyData[3 * i + 2];
227 0 : sEntry.c2 = abyData[3 * i + 1];
228 0 : sEntry.c3 = abyData[3 * i + 0];
229 0 : sEntry.c4 = 255;
230 0 : m_poColorTable->SetColorEntry(
231 0 : poDSIn->m_sImageHeader.nColorMapFirstIdx + i, &sEntry);
232 : }
233 : }
234 2 : else if (poDSIn->m_sImageHeader.nColorMapEntrySize == 32)
235 : {
236 0 : unsigned nCountAlpha0 = 0;
237 0 : unsigned nAlphaIdx = 0;
238 0 : for (unsigned i = 0; i < poDSIn->m_sImageHeader.nColorMapLength;
239 : ++i)
240 : {
241 : GDALColorEntry sEntry;
242 0 : sEntry.c1 = abyData[4 * i + 2];
243 0 : sEntry.c2 = abyData[4 * i + 1];
244 0 : sEntry.c3 = abyData[4 * i + 0];
245 0 : sEntry.c4 = abyData[4 * i + 3];
246 0 : m_poColorTable->SetColorEntry(
247 0 : poDSIn->m_sImageHeader.nColorMapFirstIdx + i, &sEntry);
248 0 : if (sEntry.c4 == 0)
249 : {
250 0 : nCountAlpha0++;
251 0 : nAlphaIdx = poDSIn->m_sImageHeader.nColorMapFirstIdx + i;
252 : }
253 : }
254 0 : if (nCountAlpha0 == 1)
255 : {
256 0 : m_dfNoDataValue = nAlphaIdx;
257 0 : m_bHasNoDataValue = true;
258 : }
259 : }
260 2 : else if (poDSIn->m_sImageHeader.nColorMapEntrySize == 15 ||
261 2 : poDSIn->m_sImageHeader.nColorMapEntrySize == 16)
262 : {
263 514 : for (unsigned i = 0; i < poDSIn->m_sImageHeader.nColorMapLength;
264 : ++i)
265 : {
266 512 : GUInt16 nVal = (abyData[2 * i + 1] << 8) | abyData[2 * i];
267 : GDALColorEntry sEntry;
268 512 : sEntry.c1 = ((nVal >> 10) & 31) << 3;
269 512 : sEntry.c2 = ((nVal >> 5) & 31) << 3;
270 512 : sEntry.c3 = ((nVal >> 0) & 31) << 3;
271 512 : sEntry.c4 = 255;
272 512 : m_poColorTable->SetColorEntry(
273 512 : poDSIn->m_sImageHeader.nColorMapFirstIdx + i, &sEntry);
274 : }
275 : }
276 : }
277 25 : }
278 :
279 : /************************************************************************/
280 : /* IReadBlock() */
281 : /************************************************************************/
282 :
283 4421 : CPLErr GDALTGARasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
284 : void *pImage)
285 : {
286 4421 : GDALTGADataset *poGDS = cpl::down_cast<GDALTGADataset *>(poDS);
287 :
288 4421 : const int nBands = poGDS->GetRasterCount();
289 8842 : const int nLine = (poGDS->m_sImageHeader.nImageDescriptor & (1 << 5))
290 4421 : ? nBlockYOff
291 3157 : : nRasterYSize - 1 - nBlockYOff;
292 4421 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
293 4421 : if (!poGDS->m_aoScanlineState.empty()) // RLE
294 : {
295 2285 : if (poGDS->m_aoScanlineState[nLine].nOffset == 0)
296 : {
297 384 : for (int i = poGDS->m_nLastLineKnownOffset; i < nLine; i++)
298 : {
299 381 : if (IReadBlock(
300 : 0,
301 381 : (poGDS->m_sImageHeader.nImageDescriptor & (1 << 5))
302 : ? i
303 381 : : nRasterYSize - 1 - i,
304 381 : nullptr) != CE_None)
305 : {
306 0 : return CE_Failure;
307 : }
308 : }
309 : }
310 2285 : VSIFSeekL(poGDS->m_fpImage, poGDS->m_aoScanlineState[nLine].nOffset,
311 : SEEK_SET);
312 2285 : int x = 0;
313 2285 : std::vector<GByte> abyData;
314 3744 : const int nBytesPerPixel = (nBands == 1) ? nDTSize
315 : : (nBands == 4)
316 1459 : ? 4
317 1459 : : poGDS->m_sImageHeader.nPixelDepth / 8;
318 :
319 : // Deal with a run from a previous scanline that continues on next
320 : // one(s)
321 2285 : bool bRLERun = false;
322 : int nRemainingPixelsPrevScanline =
323 2285 : poGDS->m_aoScanlineState[nLine].nRemainingPixelsPrevScanline;
324 45446 : while (x < nRasterXSize)
325 : {
326 : int nPixelsToFillUnclamped;
327 43161 : if (nRemainingPixelsPrevScanline != 0)
328 : {
329 448 : abyData = poGDS->m_aoScanlineState[nLine].abyDataPrevRLERun;
330 448 : bRLERun =
331 448 : poGDS->m_aoScanlineState[nLine].bRemainingPixelsAreRLERun;
332 448 : nPixelsToFillUnclamped = nRemainingPixelsPrevScanline;
333 : }
334 : else
335 : {
336 42713 : GByte nRepeatCount = 0;
337 42713 : VSIFReadL(&nRepeatCount, 1, 1, poGDS->m_fpImage);
338 42713 : bRLERun = (nRepeatCount & 0x80) != 0;
339 42713 : nPixelsToFillUnclamped = (nRepeatCount & 0x7f) + 1;
340 : }
341 : const int nPixelsToFill =
342 43161 : std::min(nRasterXSize - x, nPixelsToFillUnclamped);
343 43161 : if (bRLERun)
344 : {
345 32868 : if (nBands == 1)
346 : {
347 13211 : if (nRemainingPixelsPrevScanline == 0)
348 : {
349 13039 : abyData.resize(nDTSize);
350 13039 : VSIFReadL(&abyData[0], 1, nDTSize, poGDS->m_fpImage);
351 : }
352 13211 : if (pImage != nullptr)
353 : {
354 9147 : GDALCopyWords(&abyData[0], eDataType, 0,
355 : static_cast<GByte *>(pImage) +
356 9147 : x * nDTSize,
357 : eDataType, nDTSize, nPixelsToFill);
358 : }
359 : }
360 : else
361 : {
362 19657 : if (nRemainingPixelsPrevScanline == 0)
363 : {
364 19384 : abyData.resize(4);
365 19384 : VSIFReadL(&abyData[0], 1, nBytesPerPixel,
366 : poGDS->m_fpImage);
367 : }
368 19657 : if (pImage != nullptr)
369 : {
370 17625 : if (poGDS->m_sImageHeader.nPixelDepth == 16)
371 : {
372 : const GUInt16 nValue =
373 0 : abyData[0] | (abyData[1] << 8);
374 0 : const GByte nByteVal =
375 0 : ((nValue >> (5 * (3 - nBand))) & 31) << 3;
376 0 : memset(static_cast<GByte *>(pImage) + x, nByteVal,
377 : nPixelsToFill);
378 : }
379 : else
380 : {
381 17625 : memset(static_cast<GByte *>(pImage) + x,
382 17625 : abyData[nBand <= 3 ? 3 - nBand : 3],
383 : nPixelsToFill);
384 : }
385 : }
386 : }
387 : }
388 : else
389 : {
390 10293 : if (pImage == nullptr)
391 : {
392 0 : VSIFSeekL(poGDS->m_fpImage,
393 0 : static_cast<size_t>(nPixelsToFill) *
394 0 : nBytesPerPixel,
395 : SEEK_CUR);
396 : }
397 : else
398 : {
399 10293 : if (nBands == 1)
400 : {
401 3468 : VSIFReadL(static_cast<GByte *>(pImage) + x * nDTSize, 1,
402 3468 : static_cast<size_t>(nPixelsToFill) * nDTSize,
403 : poGDS->m_fpImage);
404 : }
405 : else
406 : {
407 6825 : abyData.resize(static_cast<size_t>(nBytesPerPixel) *
408 6825 : nPixelsToFill);
409 6825 : VSIFReadL(&abyData[0], 1, abyData.size(),
410 : poGDS->m_fpImage);
411 6825 : if (poGDS->m_sImageHeader.nPixelDepth == 16)
412 : {
413 0 : for (int i = 0; i < nPixelsToFill; i++)
414 : {
415 : const GUInt16 nValue =
416 0 : abyData[2 * i] | (abyData[2 * i + 1] << 8);
417 0 : static_cast<GByte *>(pImage)[x + i] =
418 0 : ((nValue >> (5 * (3 - nBand))) & 31) << 3;
419 : }
420 : }
421 : else
422 : {
423 6825 : if (nBand <= 3)
424 : {
425 166854 : for (int i = 0; i < nPixelsToFill; i++)
426 : {
427 160029 : static_cast<GByte *>(pImage)[x + i] =
428 160029 : abyData[3 - nBand + nBytesPerPixel * i];
429 : }
430 : }
431 : else
432 : {
433 0 : for (int i = 0; i < nPixelsToFill; i++)
434 : {
435 0 : static_cast<GByte *>(pImage)[x + i] =
436 0 : abyData[3 + nBytesPerPixel * i];
437 : }
438 : }
439 : }
440 : }
441 : }
442 : }
443 43161 : x += nPixelsToFill;
444 43161 : nRemainingPixelsPrevScanline =
445 43161 : nPixelsToFillUnclamped - nPixelsToFill;
446 : }
447 :
448 2285 : if (nLine + 1 < nRasterYSize)
449 : {
450 4552 : poGDS->m_aoScanlineState[nLine + 1].nOffset =
451 2276 : VSIFTellL(poGDS->m_fpImage);
452 2276 : poGDS->m_aoScanlineState[nLine + 1].bRemainingPixelsAreRLERun =
453 : bRLERun;
454 2276 : poGDS->m_aoScanlineState[nLine + 1].nRemainingPixelsPrevScanline =
455 : nRemainingPixelsPrevScanline;
456 2276 : if (nRemainingPixelsPrevScanline)
457 448 : poGDS->m_aoScanlineState[nLine + 1].abyDataPrevRLERun =
458 896 : std::move(abyData);
459 : }
460 : if (pImage && nBands == 1)
461 : {
462 : #ifdef CPL_MSB
463 : if (nDTSize > 1)
464 : {
465 : GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
466 : }
467 : #endif
468 : }
469 2285 : return CE_None;
470 : }
471 :
472 2136 : if (pImage == nullptr)
473 0 : return CE_Failure;
474 :
475 2136 : if (nBands == 1)
476 : {
477 256 : vsi_l_offset nOffset =
478 256 : poGDS->m_nImageDataOffset +
479 256 : static_cast<vsi_l_offset>(nLine) * nRasterXSize * nDTSize;
480 256 : VSIFSeekL(poGDS->m_fpImage, nOffset, SEEK_SET);
481 256 : VSIFReadL(pImage, 1, static_cast<size_t>(nRasterXSize) * nDTSize,
482 : poGDS->m_fpImage);
483 : #ifdef CPL_MSB
484 : if (nDTSize > 1)
485 : {
486 : GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
487 : }
488 : #endif
489 : }
490 : else
491 : {
492 1880 : const int nBytesPerPixel =
493 1880 : (nBands == 4) ? 4 : poGDS->m_sImageHeader.nPixelDepth / 8;
494 3760 : std::vector<GByte> abyData;
495 1880 : abyData.resize(static_cast<size_t>(nBytesPerPixel) * nRasterXSize);
496 1880 : vsi_l_offset nOffset =
497 1880 : poGDS->m_nImageDataOffset +
498 1880 : static_cast<vsi_l_offset>(nLine) * nRasterXSize * nBytesPerPixel;
499 1880 : VSIFSeekL(poGDS->m_fpImage, nOffset, SEEK_SET);
500 1880 : VSIFReadL(&abyData[0], 1, abyData.size(), poGDS->m_fpImage);
501 1880 : if (poGDS->m_sImageHeader.nPixelDepth == 16)
502 : {
503 49536 : for (int i = 0; i < nRasterXSize; i++)
504 : {
505 : const GUInt16 nValue =
506 49152 : abyData[2 * i] | (abyData[2 * i + 1] << 8);
507 49152 : static_cast<GByte *>(pImage)[i] =
508 49152 : ((nValue >> (5 * (3 - nBand))) & 31) << 3;
509 : }
510 : }
511 : else
512 : {
513 1496 : if (nBand <= 3)
514 : {
515 172422 : for (int i = 0; i < nRasterXSize; i++)
516 : {
517 171204 : static_cast<GByte *>(pImage)[i] =
518 171204 : abyData[3 - nBand + nBytesPerPixel * i];
519 : }
520 : }
521 : else
522 : {
523 40962 : for (int i = 0; i < nRasterXSize; i++)
524 : {
525 40684 : static_cast<GByte *>(pImage)[i] =
526 40684 : abyData[3 + nBytesPerPixel * i];
527 : }
528 : }
529 : }
530 : }
531 :
532 2136 : return CE_None;
533 : }
534 :
535 : /************************************************************************/
536 : /* Open() */
537 : /************************************************************************/
538 :
539 11 : GDALDataset *GDALTGADataset::Open(GDALOpenInfo *poOpenInfo)
540 : {
541 11 : if (!Identify(poOpenInfo))
542 0 : return nullptr;
543 11 : if (poOpenInfo->eAccess == GA_Update)
544 : {
545 0 : CPLError(CE_Failure, CPLE_NotSupported,
546 : "Update of existing TGA file not supported");
547 0 : return nullptr;
548 : }
549 :
550 : ImageHeader sHeader;
551 11 : sHeader.nIDLength = poOpenInfo->pabyHeader[0];
552 11 : sHeader.bHasColorMap = poOpenInfo->pabyHeader[1] == 1;
553 11 : sHeader.eImageType = static_cast<ImageType>(poOpenInfo->pabyHeader[2]);
554 11 : sHeader.nColorMapFirstIdx = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 3);
555 11 : sHeader.nColorMapLength = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 5);
556 11 : sHeader.nColorMapEntrySize = poOpenInfo->pabyHeader[7];
557 11 : sHeader.nXOrigin = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 8);
558 11 : sHeader.nYOrigin = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 10);
559 11 : const GUInt16 nWidth = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 12);
560 11 : const GUInt16 nHeight = CPL_LSBUINT16PTR(poOpenInfo->pabyHeader + 14);
561 11 : if (nWidth == 0 || nHeight == 0)
562 0 : return nullptr;
563 11 : sHeader.nPixelDepth = poOpenInfo->pabyHeader[16];
564 11 : sHeader.nImageDescriptor = poOpenInfo->pabyHeader[17];
565 :
566 11 : if (sHeader.bHasColorMap)
567 : {
568 2 : if (sHeader.nColorMapEntrySize != 15 &&
569 2 : sHeader.nColorMapEntrySize != 16 &&
570 0 : sHeader.nColorMapEntrySize != 24 &&
571 0 : sHeader.nColorMapEntrySize != 32)
572 : {
573 0 : CPLError(CE_Failure, CPLE_NotSupported,
574 : "Color map entry size %d not supported",
575 0 : sHeader.nColorMapEntrySize);
576 0 : return nullptr;
577 : }
578 : }
579 :
580 11 : GDALTGADataset *poDS = new GDALTGADataset(sHeader, poOpenInfo->fpL);
581 :
582 11 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
583 11 : const auto nSize = VSIFTellL(poOpenInfo->fpL);
584 :
585 11 : bool hasFourthChannel = (sHeader.nImageDescriptor & 15) == 8;
586 11 : bool fourthChannelIsAlpha = hasFourthChannel;
587 :
588 : // Detect presence of optional TGA file footer.
589 11 : if (nSize >= 26)
590 : {
591 11 : VSIFSeekL(poOpenInfo->fpL, nSize - 26, SEEK_SET);
592 : GByte abyTail[26];
593 11 : VSIFReadL(abyTail, 1, 26, poOpenInfo->fpL);
594 11 : if (memcmp(abyTail + 8, "TRUEVISION-XFILE.\x00", 18) == 0)
595 : {
596 8 : const unsigned nExtensionAreaOffset = CPL_LSBUINT32PTR(abyTail);
597 8 : if (nExtensionAreaOffset > 0)
598 : {
599 8 : VSIFSeekL(poOpenInfo->fpL, nExtensionAreaOffset, SEEK_SET);
600 16 : std::vector<GByte> abyExtendedData(495);
601 8 : VSIFReadL(&abyExtendedData[0], 1, abyExtendedData.size(),
602 : poOpenInfo->fpL);
603 8 : const GUInt16 nExtSize = CPL_LSBUINT16PTR(&abyExtendedData[0]);
604 8 : if (nExtSize >= 495)
605 : {
606 8 : if (abyExtendedData[2] != ' ' && abyExtendedData[2] != '\0')
607 : {
608 16 : std::string osAuthorName;
609 : osAuthorName.assign(
610 8 : reinterpret_cast<const char *>(&abyExtendedData[2]),
611 8 : 40);
612 8 : osAuthorName.resize(strlen(osAuthorName.c_str()));
613 16 : while (!osAuthorName.empty() &&
614 8 : osAuthorName.back() == ' ')
615 : {
616 0 : osAuthorName.pop_back();
617 : }
618 8 : poDS->GDALDataset::SetMetadataItem(
619 : "AUTHOR_NAME", osAuthorName.c_str());
620 : }
621 :
622 16 : if (abyExtendedData[43] != ' ' &&
623 8 : abyExtendedData[43] != '\0')
624 : {
625 16 : std::string osComments;
626 16 : for (int i = 0; i < 4; i++)
627 : {
628 16 : if (abyExtendedData[43 + 81 * i] == '\0')
629 : {
630 8 : break;
631 : }
632 16 : std::string osLine;
633 : osLine.assign(reinterpret_cast<const char *>(
634 8 : &abyExtendedData[43 + 81 * i]),
635 8 : 80);
636 8 : osLine.resize(strlen(osLine.c_str()));
637 8 : while (!osLine.empty() && osLine.back() == ' ')
638 : {
639 0 : osLine.pop_back();
640 : }
641 8 : if (i > 0)
642 0 : osComments += '\n';
643 8 : osComments += osLine;
644 : }
645 8 : poDS->GDALDataset::SetMetadataItem("COMMENTS",
646 : osComments.c_str());
647 : }
648 :
649 : // const GUInt32 nOffsetToScanlineTable =
650 : // CPL_LSBUINT32PTR(&abyExtendedData[490]); Did not find yet
651 : // an image using a scanline table
652 :
653 8 : const GByte nAttributeType = abyExtendedData[494];
654 8 : if (nAttributeType == 1)
655 : {
656 : // undefined data in the Alpha field, can be ignored
657 0 : hasFourthChannel = false;
658 : }
659 8 : else if (nAttributeType == 2)
660 : {
661 : // undefined data in the Alpha field, but should be
662 : // retained
663 2 : fourthChannelIsAlpha = false;
664 : }
665 : }
666 : }
667 : }
668 : }
669 :
670 11 : if (sHeader.nIDLength > 0 &&
671 10 : 18 + sHeader.nIDLength <= poOpenInfo->nHeaderBytes)
672 : {
673 20 : std::string osID;
674 10 : osID.assign(reinterpret_cast<const char *>(poOpenInfo->pabyHeader + 18),
675 10 : sHeader.nIDLength);
676 10 : poDS->GDALDataset::SetMetadataItem("IMAGE_ID", osID.c_str());
677 : }
678 :
679 11 : poOpenInfo->fpL = nullptr;
680 11 : poDS->nRasterXSize = nWidth;
681 11 : poDS->nRasterYSize = nHeight;
682 11 : poDS->m_bFourthChannelIsAlpha = fourthChannelIsAlpha;
683 11 : if (sHeader.eImageType == RLE_COLORMAP ||
684 10 : sHeader.eImageType == RLE_GRAYSCALE ||
685 8 : sHeader.eImageType == RLE_TRUE_COLOR)
686 : {
687 : assert(nHeight <= 65535);
688 5 : poDS->m_aoScanlineState.resize(nHeight);
689 5 : poDS->m_aoScanlineState[0].nOffset = poDS->m_nImageDataOffset;
690 : }
691 11 : if (sHeader.eImageType == UNCOMPRESSED_COLORMAP ||
692 10 : sHeader.eImageType == RLE_COLORMAP ||
693 9 : sHeader.eImageType == UNCOMPRESSED_GRAYSCALE ||
694 8 : sHeader.eImageType == RLE_GRAYSCALE)
695 : {
696 5 : if (sHeader.nPixelDepth != 8 && sHeader.nPixelDepth != 16)
697 : {
698 0 : CPLError(CE_Failure, CPLE_NotSupported,
699 0 : "Pixel depth %d not supported", sHeader.nPixelDepth);
700 0 : delete poDS;
701 0 : return nullptr;
702 : }
703 5 : poDS->SetBand(
704 : 1, new GDALTGARasterBand(
705 5 : poDS, 1, sHeader.nPixelDepth == 16 ? GDT_UInt16 : GDT_Byte));
706 : }
707 : else
708 : {
709 6 : if (sHeader.nPixelDepth != 16 && sHeader.nPixelDepth != 24 &&
710 2 : sHeader.nPixelDepth != 32)
711 : {
712 0 : CPLError(CE_Failure, CPLE_NotSupported,
713 0 : "Pixel depth %d not supported", sHeader.nPixelDepth);
714 0 : delete poDS;
715 0 : return nullptr;
716 : }
717 6 : int l_nBands =
718 6 : sHeader.nPixelDepth == 16 ? 3 : (3 + (hasFourthChannel ? 1 : 0));
719 26 : for (int iBand = 1; iBand <= l_nBands; iBand++)
720 : {
721 20 : poDS->SetBand(iBand, new GDALTGARasterBand(poDS, iBand, GDT_Byte));
722 : }
723 : }
724 :
725 : /* -------------------------------------------------------------------- */
726 : /* Initialize any PAM information. */
727 : /* -------------------------------------------------------------------- */
728 :
729 11 : poDS->SetDescription(poOpenInfo->pszFilename);
730 11 : poDS->TryLoadXML();
731 :
732 : /* -------------------------------------------------------------------- */
733 : /* Check for overviews. */
734 : /* -------------------------------------------------------------------- */
735 :
736 11 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
737 :
738 11 : return poDS;
739 : }
740 :
741 : /************************************************************************/
742 : /* GDALRegister_TGA() */
743 : /************************************************************************/
744 :
745 2033 : void GDALRegister_TGA()
746 :
747 : {
748 2033 : if (GDALGetDriverByName("TGA") != nullptr)
749 283 : return;
750 :
751 1750 : GDALDriver *poDriver = new GDALDriver();
752 :
753 1750 : poDriver->SetDescription("TGA");
754 1750 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
755 1750 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "TGA/TARGA Image File Format");
756 1750 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/x-tga");
757 1750 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/tga.html");
758 1750 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "tga");
759 1750 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
760 :
761 1750 : poDriver->pfnOpen = GDALTGADataset::Open;
762 1750 : poDriver->pfnIdentify = GDALTGADataset::Identify;
763 :
764 1750 : GetGDALDriverManager()->RegisterDriver(poDriver);
765 : }
|