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

Generated by: LCOV version 1.14