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

Generated by: LCOV version 1.14