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

Generated by: LCOV version 1.14