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