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