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

Generated by: LCOV version 1.14