LCOV - code coverage report
Current view: top level - frmts/tga - tgadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 280 345 81.2 %
Date: 2025-07-11 10:11:13 Functions: 10 11 90.9 %

          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             : }

Generated by: LCOV version 1.14