LCOV - code coverage report
Current view: top level - frmts/gif - gifabstractdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 172 195 88.2 %
Date: 2025-10-27 00:14:23 Functions: 20 23 87.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GIF Driver
       4             :  * Purpose:  GIF Abstract Dataset
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ****************************************************************************
       8             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gifabstractdataset.h"
      14             : 
      15             : #include "gdal_colortable.h"
      16             : #include "gdal_openinfo.h"
      17             : #include "gdal_cpp_functions.h"
      18             : 
      19             : /************************************************************************/
      20             : /* ==================================================================== */
      21             : /*                         GIFAbstractDataset                           */
      22             : /* ==================================================================== */
      23             : /************************************************************************/
      24             : 
      25             : /************************************************************************/
      26             : /*                         GIFAbstractDataset()                         */
      27             : /************************************************************************/
      28             : 
      29          36 : GIFAbstractDataset::GIFAbstractDataset()
      30             :     : fp(nullptr), hGifFile(nullptr), bGeoTransformValid(FALSE), nGCPCount(0),
      31          36 :       pasGCPList(nullptr), bHasReadXMPMetadata(FALSE)
      32             : {
      33          36 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                        ~GIFAbstractDataset()                         */
      37             : /************************************************************************/
      38             : 
      39          36 : GIFAbstractDataset::~GIFAbstractDataset()
      40             : 
      41             : {
      42          36 :     FlushCache(true);
      43             : 
      44          36 :     if (nGCPCount > 0)
      45             :     {
      46           0 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
      47           0 :         CPLFree(pasGCPList);
      48             :     }
      49             : 
      50          36 :     if (hGifFile)
      51          35 :         myDGifCloseFile(hGifFile);
      52             : 
      53          36 :     if (fp != nullptr)
      54          35 :         VSIFCloseL(fp);
      55          36 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                       GIFCollectXMPMetadata()                        */
      59             : /************************************************************************/
      60             : 
      61             : /* See ยง2.1.2 of
      62             :  * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
      63             :  */
      64             : 
      65           5 : static CPLString GIFCollectXMPMetadata(VSILFILE *fp)
      66             : 
      67             : {
      68           5 :     CPLString osXMP;
      69             : 
      70             :     /* Save current position to avoid disturbing GIF stream decoding */
      71           5 :     vsi_l_offset nCurOffset = VSIFTellL(fp);
      72             : 
      73             :     char abyBuffer[2048 + 1];
      74             : 
      75           5 :     VSIFSeekL(fp, 0, SEEK_SET);
      76             : 
      77             :     /* Loop over file */
      78             : 
      79           5 :     int iStartSearchOffset = 1024;
      80             :     while (true)
      81             :     {
      82          26 :         int nRead = static_cast<int>(VSIFReadL(abyBuffer + 1024, 1, 1024, fp));
      83          26 :         if (nRead <= 0)
      84           0 :             break;
      85          26 :         abyBuffer[1024 + nRead] = 0;
      86             : 
      87          26 :         int iFoundOffset = -1;
      88       43588 :         for (int i = iStartSearchOffset; i < 1024 + nRead - 14; i++)
      89             :         {
      90       43563 :             if (memcmp(abyBuffer + i, "\x21\xff\x0bXMP DataXMP", 14) == 0)
      91             :             {
      92           1 :                 iFoundOffset = i + 14;
      93           1 :                 break;
      94             :             }
      95             :         }
      96             : 
      97          26 :         iStartSearchOffset = 0;
      98             : 
      99          26 :         if (iFoundOffset >= 0)
     100             :         {
     101           1 :             int nSize = 1024 + nRead - iFoundOffset;
     102           1 :             char *pszXMP = (char *)VSIMalloc(nSize + 1);
     103           1 :             if (pszXMP == nullptr)
     104           0 :                 break;
     105             : 
     106           1 :             pszXMP[nSize] = 0;
     107           1 :             memcpy(pszXMP, abyBuffer + iFoundOffset, nSize);
     108             : 
     109             :             /* Read from file until we find a NUL character */
     110           1 :             int nLen = (int)strlen(pszXMP);
     111           5 :             while (nLen == nSize)
     112             :             {
     113           4 :                 char *pszNewXMP = (char *)VSIRealloc(pszXMP, nSize + 1024 + 1);
     114           4 :                 if (pszNewXMP == nullptr)
     115           0 :                     break;
     116           4 :                 pszXMP = pszNewXMP;
     117             : 
     118           4 :                 nRead =
     119           4 :                     static_cast<int>(VSIFReadL(pszXMP + nSize, 1, 1024, fp));
     120           4 :                 if (nRead <= 0)
     121           0 :                     break;
     122             : 
     123           4 :                 pszXMP[nSize + nRead] = 0;
     124           4 :                 nLen += (int)strlen(pszXMP + nSize);
     125           4 :                 nSize += nRead;
     126             :             }
     127             : 
     128           1 :             if (nLen > 256 && pszXMP[nLen - 1] == '\x01' &&
     129           1 :                 pszXMP[nLen - 2] == '\x02' && pszXMP[nLen - 255] == '\xff' &&
     130           1 :                 pszXMP[nLen - 256] == '\x01')
     131             :             {
     132           1 :                 pszXMP[nLen - 256] = 0;
     133             : 
     134           1 :                 osXMP = pszXMP;
     135             :             }
     136             : 
     137           1 :             VSIFree(pszXMP);
     138             : 
     139           1 :             break;
     140             :         }
     141             : 
     142          25 :         if (nRead != 1024)
     143           4 :             break;
     144             : 
     145          21 :         memcpy(abyBuffer, abyBuffer + 1024, 1024);
     146          21 :     }
     147             : 
     148           5 :     VSIFSeekL(fp, nCurOffset, SEEK_SET);
     149             : 
     150          10 :     return osXMP;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                       CollectXMPMetadata()                           */
     155             : /************************************************************************/
     156             : 
     157           5 : void GIFAbstractDataset::CollectXMPMetadata()
     158             : 
     159             : {
     160           5 :     if (fp == nullptr || bHasReadXMPMetadata)
     161           0 :         return;
     162             : 
     163           5 :     CPLString osXMP = GIFCollectXMPMetadata(fp);
     164           5 :     if (!osXMP.empty())
     165             :     {
     166             :         /* Avoid setting the PAM dirty bit just for that */
     167           1 :         const int nOldPamFlags = nPamFlags;
     168             : 
     169             :         char *apszMDList[2];
     170           1 :         apszMDList[0] = (char *)osXMP.c_str();
     171           1 :         apszMDList[1] = nullptr;
     172           1 :         SetMetadata(apszMDList, "xml:XMP");
     173             : 
     174             :         // cppcheck-suppress redundantAssignment
     175           1 :         nPamFlags = nOldPamFlags;
     176             :     }
     177             : 
     178           5 :     bHasReadXMPMetadata = TRUE;
     179             : }
     180             : 
     181             : /************************************************************************/
     182             : /*                      GetMetadataDomainList()                         */
     183             : /************************************************************************/
     184             : 
     185           1 : char **GIFAbstractDataset::GetMetadataDomainList()
     186             : {
     187           1 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     188           1 :                                    TRUE, "xml:XMP", nullptr);
     189             : }
     190             : 
     191             : /************************************************************************/
     192             : /*                           GetMetadata()                              */
     193             : /************************************************************************/
     194             : 
     195          64 : char **GIFAbstractDataset::GetMetadata(const char *pszDomain)
     196             : {
     197          64 :     if (fp == nullptr)
     198           0 :         return nullptr;
     199          64 :     if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
     200          53 :         (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP")))
     201           5 :         CollectXMPMetadata();
     202          64 :     return GDALPamDataset::GetMetadata(pszDomain);
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*                          GetGeoTransform()                           */
     207             : /************************************************************************/
     208             : 
     209          52 : CPLErr GIFAbstractDataset::GetGeoTransform(GDALGeoTransform &gt) const
     210             : 
     211             : {
     212          52 :     if (bGeoTransformValid)
     213             :     {
     214           0 :         gt = m_gt;
     215           0 :         return CE_None;
     216             :     }
     217             : 
     218          52 :     return GDALPamDataset::GetGeoTransform(gt);
     219             : }
     220             : 
     221             : /************************************************************************/
     222             : /*                            GetGCPCount()                             */
     223             : /************************************************************************/
     224             : 
     225          10 : int GIFAbstractDataset::GetGCPCount()
     226             : 
     227             : {
     228          10 :     if (nGCPCount > 0)
     229           0 :         return nGCPCount;
     230             : 
     231          10 :     return GDALPamDataset::GetGCPCount();
     232             : }
     233             : 
     234             : /************************************************************************/
     235             : /*                               GetGCPs()                              */
     236             : /************************************************************************/
     237             : 
     238           0 : const GDAL_GCP *GIFAbstractDataset::GetGCPs()
     239             : 
     240             : {
     241           0 :     if (nGCPCount > 0)
     242           0 :         return pasGCPList;
     243             : 
     244           0 :     return GDALPamDataset::GetGCPs();
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                            GetFileList()                             */
     249             : /************************************************************************/
     250             : 
     251          12 : char **GIFAbstractDataset::GetFileList()
     252             : 
     253             : {
     254          12 :     char **papszFileList = GDALPamDataset::GetFileList();
     255             : 
     256          12 :     if (!osWldFilename.empty() &&
     257           0 :         CSLFindString(papszFileList, osWldFilename) == -1)
     258             :     {
     259           0 :         papszFileList = CSLAddString(papszFileList, osWldFilename);
     260             :     }
     261             : 
     262          12 :     return papszFileList;
     263             : }
     264             : 
     265             : /************************************************************************/
     266             : /*                         DetectGeoreferencing()                       */
     267             : /************************************************************************/
     268             : 
     269          35 : void GIFAbstractDataset::DetectGeoreferencing(GDALOpenInfo *poOpenInfo)
     270             : {
     271          35 :     char *pszWldFilename = nullptr;
     272             : 
     273          35 :     bGeoTransformValid =
     274          70 :         GDALReadWorldFile2(poOpenInfo->pszFilename, nullptr, m_gt,
     275          35 :                            poOpenInfo->GetSiblingFiles(), &pszWldFilename);
     276          35 :     if (!bGeoTransformValid)
     277             :     {
     278          35 :         bGeoTransformValid =
     279          35 :             GDALReadWorldFile2(poOpenInfo->pszFilename, ".wld", m_gt,
     280          35 :                                poOpenInfo->GetSiblingFiles(), &pszWldFilename);
     281             :     }
     282             : 
     283          35 :     if (pszWldFilename)
     284             :     {
     285           0 :         osWldFilename = pszWldFilename;
     286           0 :         CPLFree(pszWldFilename);
     287             :     }
     288          35 : }
     289             : 
     290             : /************************************************************************/
     291             : /*                            myDGifOpen()                              */
     292             : /************************************************************************/
     293             : 
     294          68 : GifFileType *GIFAbstractDataset::myDGifOpen(void *userPtr, InputFunc readFunc)
     295             : {
     296             : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
     297             :     int nErrorCode;
     298         136 :     return DGifOpen(userPtr, readFunc, &nErrorCode);
     299             : #else
     300             :     return DGifOpen(userPtr, readFunc);
     301             : #endif
     302             : }
     303             : 
     304             : /************************************************************************/
     305             : /*                          myDGifCloseFile()                           */
     306             : /************************************************************************/
     307             : 
     308          68 : int GIFAbstractDataset::myDGifCloseFile(GifFileType *hGifFile)
     309             : {
     310             : #if defined(GIFLIB_MAJOR) &&                                                   \
     311             :     ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5)
     312             :     int nErrorCode;
     313         136 :     return DGifCloseFile(hGifFile, &nErrorCode);
     314             : #else
     315             :     return DGifCloseFile(hGifFile);
     316             : #endif
     317             : }
     318             : 
     319             : /************************************************************************/
     320             : /*                          myEGifCloseFile()                           */
     321             : /************************************************************************/
     322             : 
     323           6 : int GIFAbstractDataset::myEGifCloseFile(GifFileType *hGifFile)
     324             : {
     325             : #if defined(GIFLIB_MAJOR) &&                                                   \
     326             :     ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5)
     327             :     int nErrorCode;
     328          12 :     return EGifCloseFile(hGifFile, &nErrorCode);
     329             : #else
     330             :     return EGifCloseFile(hGifFile);
     331             : #endif
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                           VSIGIFReadFunc()                           */
     336             : /*                                                                      */
     337             : /*      Proxy function for reading from GIF file.                       */
     338             : /************************************************************************/
     339             : 
     340        9218 : int GIFAbstractDataset::ReadFunc(GifFileType *psGFile, GifByteType *pabyBuffer,
     341             :                                  int nBytesToRead)
     342             : 
     343             : {
     344             :     return static_cast<int>(
     345        9218 :         VSIFReadL(pabyBuffer, 1, nBytesToRead, (VSILFILE *)psGFile->UserData));
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*                          FindFirstImage()                            */
     350             : /************************************************************************/
     351             : 
     352          39 : GifRecordType GIFAbstractDataset::FindFirstImage(GifFileType *hGifFile)
     353             : {
     354          39 :     GifRecordType RecordType = TERMINATE_RECORD_TYPE;
     355             : 
     356          43 :     while (DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR &&
     357          86 :            RecordType != TERMINATE_RECORD_TYPE &&
     358          43 :            RecordType != IMAGE_DESC_RECORD_TYPE)
     359             :     {
     360             :         /* Skip extension records found before IMAGE_DESC_RECORD_TYPE */
     361           4 :         if (RecordType == EXTENSION_RECORD_TYPE)
     362             :         {
     363             :             int nFunction;
     364           4 :             GifByteType *pExtData = nullptr;
     365           4 :             if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR)
     366           0 :                 break;
     367          96 :             while (pExtData != nullptr)
     368             :             {
     369          92 :                 if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR)
     370           0 :                     break;
     371             :             }
     372             :         }
     373             :     }
     374             : 
     375          39 :     return RecordType;
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                        GIFAbstractRasterBand()                       */
     380             : /************************************************************************/
     381             : 
     382          36 : GIFAbstractRasterBand::GIFAbstractRasterBand(GIFAbstractDataset *poDSIn,
     383             :                                              int nBandIn,
     384             :                                              SavedImage *psSavedImage,
     385             :                                              int nBackground,
     386          36 :                                              int bAdvertiseInterlacedMDI)
     387             :     : psImage(psSavedImage), panInterlaceMap(nullptr), poColorTable(nullptr),
     388          36 :       nTransparentColor(0)
     389             : {
     390          36 :     poDS = poDSIn;
     391          36 :     nBand = nBandIn;
     392             : 
     393          36 :     eDataType = GDT_Byte;
     394             : 
     395          36 :     nBlockXSize = poDS->GetRasterXSize();
     396          36 :     nBlockYSize = 1;
     397             : 
     398          36 :     if (psImage == nullptr)
     399           1 :         return;
     400             : 
     401             :     /* -------------------------------------------------------------------- */
     402             :     /*      Setup interlacing map if required.                              */
     403             :     /* -------------------------------------------------------------------- */
     404          35 :     panInterlaceMap = nullptr;
     405          35 :     if (psImage->ImageDesc.Interlace)
     406             :     {
     407           6 :         int iLine = 0;
     408             : 
     409           6 :         if (bAdvertiseInterlacedMDI)
     410           6 :             poDS->SetMetadataItem("INTERLACED", "YES", "IMAGE_STRUCTURE");
     411             : 
     412           6 :         panInterlaceMap = (int *)CPLCalloc(poDSIn->nRasterYSize, sizeof(int));
     413             : 
     414          30 :         for (int i = 0; i < 4; i++)
     415             :         {
     416       67158 :             for (int j = InterlacedOffset[i]; j < poDSIn->nRasterYSize;
     417       67134 :                  j += InterlacedJumps[i])
     418       67134 :                 panInterlaceMap[j] = iLine++;
     419             :         }
     420             :     }
     421          29 :     else if (bAdvertiseInterlacedMDI)
     422             :     {
     423           0 :         poDS->SetMetadataItem("INTERLACED", "NO", "IMAGE_STRUCTURE");
     424             :     }
     425             : 
     426             :     /* -------------------------------------------------------------------- */
     427             :     /*      Check for transparency.  We just take the first graphic         */
     428             :     /*      control extension block we find, if any.                        */
     429             :     /* -------------------------------------------------------------------- */
     430          35 :     nTransparentColor = -1;
     431         127 :     for (int iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount;
     432             :          iExtBlock++)
     433             :     {
     434          92 :         if (psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 ||
     435           3 :             psImage->ExtensionBlocks[iExtBlock].ByteCount < 4)
     436          89 :             continue;
     437             : 
     438           3 :         unsigned char *pExtData = reinterpret_cast<unsigned char *>(
     439           3 :             psImage->ExtensionBlocks[iExtBlock].Bytes);
     440             : 
     441             :         /* check if transparent color flag is set */
     442           3 :         if (!(pExtData[0] & 0x1))
     443           0 :             continue;
     444             : 
     445           3 :         nTransparentColor = pExtData[3];
     446             :     }
     447             : 
     448             :     /* -------------------------------------------------------------------- */
     449             :     /*      Setup colormap.                                                 */
     450             :     /* -------------------------------------------------------------------- */
     451          35 :     ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap;
     452          35 :     if (psGifCT == nullptr)
     453          35 :         psGifCT = poDSIn->hGifFile->SColorMap;
     454             : 
     455          35 :     poColorTable = new GDALColorTable();
     456        3475 :     for (int iColor = 0; iColor < psGifCT->ColorCount; iColor++)
     457             :     {
     458             :         GDALColorEntry oEntry;
     459             : 
     460        3440 :         oEntry.c1 = psGifCT->Colors[iColor].Red;
     461        3440 :         oEntry.c2 = psGifCT->Colors[iColor].Green;
     462        3440 :         oEntry.c3 = psGifCT->Colors[iColor].Blue;
     463             : 
     464        3440 :         if (iColor == nTransparentColor)
     465           3 :             oEntry.c4 = 0;
     466             :         else
     467        3437 :             oEntry.c4 = 255;
     468             : 
     469        3440 :         poColorTable->SetColorEntry(iColor, &oEntry);
     470             :     }
     471             : 
     472             :     /* -------------------------------------------------------------------- */
     473             :     /*      If we have a background value, return it here.  Some            */
     474             :     /*      applications might want to treat this as transparent, but in    */
     475             :     /*      many uses this is inappropriate so we don't return it as        */
     476             :     /*      nodata or transparent.                                          */
     477             :     /* -------------------------------------------------------------------- */
     478          35 :     if (nBackground != 255)
     479             :     {
     480             :         char szBackground[10];
     481             : 
     482          20 :         snprintf(szBackground, sizeof(szBackground), "%d", nBackground);
     483          20 :         SetMetadataItem("GIF_BACKGROUND", szBackground);
     484             :     }
     485             : }
     486             : 
     487             : /************************************************************************/
     488             : /*                       ~GIFAbstractRasterBand()                       */
     489             : /************************************************************************/
     490             : 
     491          36 : GIFAbstractRasterBand::~GIFAbstractRasterBand()
     492             : 
     493             : {
     494          36 :     if (poColorTable != nullptr)
     495          35 :         delete poColorTable;
     496             : 
     497          36 :     CPLFree(panInterlaceMap);
     498          36 : }
     499             : 
     500             : /************************************************************************/
     501             : /*                       GetColorInterpretation()                       */
     502             : /************************************************************************/
     503             : 
     504          22 : GDALColorInterp GIFAbstractRasterBand::GetColorInterpretation()
     505             : 
     506             : {
     507          22 :     return GCI_PaletteIndex;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                           GetColorTable()                            */
     512             : /************************************************************************/
     513             : 
     514          42 : GDALColorTable *GIFAbstractRasterBand::GetColorTable()
     515             : 
     516             : {
     517          42 :     return poColorTable;
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                           GetNoDataValue()                           */
     522             : /************************************************************************/
     523             : 
     524          44 : double GIFAbstractRasterBand::GetNoDataValue(int *pbSuccess)
     525             : 
     526             : {
     527          44 :     if (pbSuccess != nullptr)
     528          44 :         *pbSuccess = nTransparentColor != -1;
     529             : 
     530          44 :     return nTransparentColor;
     531             : }

Generated by: LCOV version 1.14