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

Generated by: LCOV version 1.14