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

Generated by: LCOV version 1.14