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