LCOV - code coverage report
Current view: top level - frmts/gif - gifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 176 252 69.8 %
Date: 2026-06-19 21:24:00 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GIF Driver
       4             :  * Purpose:  Implement GDAL GIF Support using libungif code.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam
       9             :  * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gifabstractdataset.h"
      15             : 
      16             : #include "cpl_string.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "gifdrivercore.h"
      20             : 
      21             : CPL_C_START
      22             : #if !(defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5)
      23             : 
      24             : // This prototype seems to have been messed up!
      25             : GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
      26             : 
      27             : // Define alias compatible with giflib >= 5.0.0
      28             : #define GifMakeMapObject MakeMapObject
      29             : #define GifFreeMapObject FreeMapObject
      30             : 
      31             : #endif  // defined(GIFLIB_MAJOR) && GIFLIB_MAJOR < 5
      32             : 
      33             : CPL_C_END
      34             : 
      35             : /************************************************************************/
      36             : /*                          VSIGIFWriteFunc()                           */
      37             : /*                                                                      */
      38             : /*      Proxy write function.                                           */
      39             : /************************************************************************/
      40             : 
      41        1414 : static int VSIGIFWriteFunc(GifFileType *psGFile, const GifByteType *pabyBuffer,
      42             :                            int nBytesToWrite)
      43             : 
      44             : {
      45        1414 :     VSILFILE *fp = static_cast<VSILFILE *>(psGFile->UserData);
      46        1420 :     if (VSIFTellL(fp) == 0 && nBytesToWrite >= 6 &&
      47           6 :         memcmp(pabyBuffer, "GIF87a", 6) == 0)
      48             :     {
      49             :         // This is a hack to write a GIF89a instead of GIF87a (we have to, since
      50             :         // we are using graphical extension block).  EGifSpew would write GIF89a
      51             :         // when it detects an extension block if we were using it As we don't,
      52             :         // we could have used EGifSetGifVersion instead, but the version of
      53             :         // libungif in GDAL has a bug: it writes on read-only memory!
      54             :         // This is a well-known problem. Just google for "EGifSetGifVersion
      55             :         // segfault".
      56             :         // Most readers don't even care if it is GIF87a or GIF89a, but it is
      57             :         // better to write the right version.
      58             : 
      59           6 :         size_t nRet = VSIFWriteL("GIF89a", 1, 6, fp);
      60          12 :         nRet += VSIFWriteL(reinterpret_cast<const char *>(pabyBuffer) + 6, 1,
      61           6 :                            nBytesToWrite - 6, fp);
      62           6 :         return static_cast<int>(nRet);
      63             :     }
      64             : 
      65        1408 :     return static_cast<int>(VSIFWriteL(pabyBuffer, 1, nBytesToWrite, fp));
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /* ==================================================================== */
      70             : /*                                  GIFDataset                          */
      71             : /* ==================================================================== */
      72             : /************************************************************************/
      73             : 
      74             : class GIFRasterBand;
      75             : 
      76          60 : class GIFDataset final : public GIFAbstractDataset
      77             : {
      78             :     friend class GIFRasterBand;
      79             : 
      80             :   public:
      81             :     GIFDataset();
      82             :     ~GIFDataset() override;
      83             : 
      84             :     static GDALDataset *Open(GDALOpenInfo *);
      85             :     static GDALPamDataset *OpenPAM(GDALOpenInfo *);
      86             : 
      87             :     static GDALDataset *CreateCopy(const char *pszFilename,
      88             :                                    GDALDataset *poSrcDS, int bStrict,
      89             :                                    CSLConstList papszOptions,
      90             :                                    GDALProgressFunc pfnProgress,
      91             :                                    void *pProgressData);
      92             : };
      93             : 
      94             : GIFDataset::~GIFDataset() = default;
      95             : 
      96             : /************************************************************************/
      97             : /* ==================================================================== */
      98             : /*                            GIFRasterBand                             */
      99             : /* ==================================================================== */
     100             : /************************************************************************/
     101             : 
     102             : class GIFRasterBand final : public GIFAbstractRasterBand
     103             : {
     104             :   public:
     105             :     GIFRasterBand(GIFDataset *, int, SavedImage *, int);
     106             :     CPLErr IReadBlock(int, int, void *) override;
     107             : };
     108             : 
     109             : /************************************************************************/
     110             : /*                           GIFRasterBand()                            */
     111             : /************************************************************************/
     112             : 
     113          30 : GIFRasterBand::GIFRasterBand(GIFDataset *poDSIn, int nBandIn,
     114          30 :                              SavedImage *psSavedImage, int nBackground)
     115          30 :     : GIFAbstractRasterBand(poDSIn, nBandIn, psSavedImage, nBackground, FALSE)
     116             : {
     117          30 : }
     118             : 
     119             : /************************************************************************/
     120             : /*                             IReadBlock()                             */
     121             : /************************************************************************/
     122             : 
     123        2960 : CPLErr GIFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     124             :                                  void *pImage)
     125             : {
     126        2960 :     CPLAssert(nBlockXOff == 0);
     127             : 
     128        2960 :     if (psImage == nullptr)
     129             :     {
     130          20 :         memset(pImage, 0, nBlockXSize);
     131          20 :         return CE_None;
     132             :     }
     133             : 
     134        2940 :     if (panInterlaceMap != nullptr)
     135           0 :         nBlockYOff = panInterlaceMap[nBlockYOff];
     136             : 
     137        2940 :     memcpy(pImage, psImage->RasterBits + nBlockYOff * nBlockXSize, nBlockXSize);
     138             : 
     139        2940 :     return CE_None;
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /* ==================================================================== */
     144             : /*                             GIFDataset                               */
     145             : /* ==================================================================== */
     146             : /************************************************************************/
     147             : 
     148             : /************************************************************************/
     149             : /*                             GIFDataset()                             */
     150             : /************************************************************************/
     151             : 
     152          30 : GIFDataset::GIFDataset()
     153             : {
     154          30 : }
     155             : 
     156             : /************************************************************************/
     157             : /*                                Open()                                */
     158             : /************************************************************************/
     159             : 
     160          26 : GDALDataset *GIFDataset::Open(GDALOpenInfo *poOpenInfo)
     161             : {
     162          26 :     return OpenPAM(poOpenInfo);
     163             : }
     164             : 
     165          32 : GDALPamDataset *GIFDataset::OpenPAM(GDALOpenInfo *poOpenInfo)
     166             : 
     167             : {
     168          32 :     if (!GIFDriverIdentify(poOpenInfo))
     169           1 :         return nullptr;
     170             : 
     171          31 :     if (poOpenInfo->eAccess == GA_Update)
     172             :     {
     173           0 :         ReportUpdateNotSupportedByDriver("GIF");
     174           0 :         return nullptr;
     175             :     }
     176             : 
     177             :     /* -------------------------------------------------------------------- */
     178             :     /*      Ingest.                                                         */
     179             :     /* -------------------------------------------------------------------- */
     180          31 :     VSILFILE *fp = poOpenInfo->fpL;
     181          31 :     poOpenInfo->fpL = nullptr;
     182             : 
     183             :     GifFileType *hGifFile =
     184          31 :         GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
     185          31 :     if (hGifFile == nullptr)
     186             :     {
     187           0 :         VSIFCloseL(fp);
     188           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     189             :                  "DGifOpen() failed for %s.  "
     190             :                  "Perhaps the gif file is corrupt?",
     191             :                  poOpenInfo->pszFilename);
     192             : 
     193           0 :         return nullptr;
     194             :     }
     195             : 
     196             :     // The following code enables us to detect GIF datasets eligible
     197             :     // for BIGGIF driver even with an unpatched giflib.
     198             : 
     199             :     /* -------------------------------------------------------------------- */
     200             :     /*      Find the first image record.                                    */
     201             :     /* -------------------------------------------------------------------- */
     202          31 :     GifRecordType RecordType = FindFirstImage(hGifFile);
     203          62 :     if (RecordType == IMAGE_DESC_RECORD_TYPE &&
     204          31 :         DGifGetImageDesc(hGifFile) != GIF_ERROR)
     205             :     {
     206          31 :         const int width = hGifFile->SavedImages[0].ImageDesc.Width;
     207          31 :         const int height = hGifFile->SavedImages[0].ImageDesc.Height;
     208          31 :         if (static_cast<double>(width) * height > 100000000.0)
     209             :         {
     210           2 :             CPLDebug("GIF", "Due to limitations of the GDAL GIF driver we "
     211             :                             "deliberately avoid opening large GIF files "
     212             :                             "(larger than 100 megapixels).");
     213           2 :             GIFAbstractDataset::myDGifCloseFile(hGifFile);
     214             :             // Reset poOpenInfo->fpL since BIGGIF may need it.
     215           2 :             poOpenInfo->fpL = fp;
     216           2 :             VSIFSeekL(fp, 0, SEEK_SET);
     217           2 :             return nullptr;
     218             :         }
     219             :     }
     220             : 
     221          29 :     GIFAbstractDataset::myDGifCloseFile(hGifFile);
     222             : 
     223          29 :     VSIFSeekL(fp, 0, SEEK_SET);
     224             : 
     225          29 :     hGifFile = GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
     226          29 :     if (hGifFile == nullptr)
     227             :     {
     228           0 :         VSIFCloseL(fp);
     229           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     230             :                  "DGifOpen() failed for %s.  "
     231             :                  "Perhaps the gif file is corrupt?",
     232             :                  poOpenInfo->pszFilename);
     233             : 
     234           0 :         return nullptr;
     235             :     }
     236             : 
     237          29 :     const int nGifErr = DGifSlurp(hGifFile);
     238             : 
     239          29 :     if (nGifErr != GIF_OK || hGifFile->SavedImages == nullptr)
     240             :     {
     241           0 :         VSIFCloseL(fp);
     242           0 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     243             : 
     244           0 :         if (nGifErr == D_GIF_ERR_DATA_TOO_BIG)
     245             :         {
     246           0 :             CPLDebug("GIF",
     247             :                      "DGifSlurp() failed for %s because it was too large.  "
     248             :                      "Due to limitations of the GDAL GIF driver we "
     249             :                      "deliberately avoid opening large GIF files "
     250             :                      "(larger than 100 megapixels).",
     251             :                      poOpenInfo->pszFilename);
     252           0 :             return nullptr;
     253             :         }
     254             : 
     255           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     256             :                  "DGifSlurp() failed for %s.  "
     257             :                  "Perhaps the gif file is corrupt?",
     258             :                  poOpenInfo->pszFilename);
     259             : 
     260           0 :         return nullptr;
     261             :     }
     262             : 
     263             :     /* -------------------------------------------------------------------- */
     264             :     /*      Create a corresponding GDALDataset.                             */
     265             :     /* -------------------------------------------------------------------- */
     266          29 :     GIFDataset *poDS = new GIFDataset();
     267             : 
     268          29 :     poDS->fp = fp;
     269          29 :     poDS->eAccess = GA_ReadOnly;
     270          29 :     poDS->hGifFile = hGifFile;
     271             : 
     272             :     /* -------------------------------------------------------------------- */
     273             :     /*      Capture some information from the file that is of interest.     */
     274             :     /* -------------------------------------------------------------------- */
     275          29 :     poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
     276          29 :     poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
     277          29 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     278             :     {
     279           0 :         delete poDS;
     280           0 :         return nullptr;
     281             :     }
     282             : 
     283             :     /* -------------------------------------------------------------------- */
     284             :     /*      Create band information objects.                                */
     285             :     /* -------------------------------------------------------------------- */
     286          58 :     for (int iImage = 0; iImage < hGifFile->ImageCount; iImage++)
     287             :     {
     288          29 :         SavedImage *psImage = hGifFile->SavedImages + iImage;
     289             : 
     290          29 :         if (psImage->ImageDesc.Width != poDS->nRasterXSize ||
     291          29 :             psImage->ImageDesc.Height != poDS->nRasterYSize)
     292           0 :             continue;
     293             : 
     294          29 :         if (psImage->ImageDesc.ColorMap == nullptr &&
     295          29 :             poDS->hGifFile->SColorMap == nullptr)
     296             :         {
     297           0 :             CPLDebug("GIF", "Skipping image without color table");
     298           0 :             continue;
     299             :         }
     300             : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
     301             :         // Since giflib 5, de-interlacing is done by DGifSlurp().
     302          29 :         psImage->ImageDesc.Interlace = false;
     303             : #endif
     304          29 :         poDS->SetBand(poDS->nBands + 1,
     305          29 :                       new GIFRasterBand(poDS, poDS->nBands + 1, psImage,
     306          29 :                                         hGifFile->SBackGroundColor));
     307             :     }
     308          29 :     if (poDS->nBands == 0)
     309             :     {
     310           0 :         delete poDS;
     311           0 :         return nullptr;
     312             :     }
     313             : 
     314             :     /* -------------------------------------------------------------------- */
     315             :     /*      Check for georeferencing.                                       */
     316             :     /* -------------------------------------------------------------------- */
     317          29 :     poDS->DetectGeoreferencing(poOpenInfo);
     318             : 
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      Initialize any PAM information.                                 */
     321             :     /* -------------------------------------------------------------------- */
     322          29 :     poDS->SetDescription(poOpenInfo->pszFilename);
     323          29 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     324             : 
     325             :     /* -------------------------------------------------------------------- */
     326             :     /*      Support overviews.                                              */
     327             :     /* -------------------------------------------------------------------- */
     328          58 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     329          29 :                                 poOpenInfo->GetSiblingFiles());
     330             : 
     331          29 :     return poDS;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                         GDALPrintGifError()                          */
     336             : /************************************************************************/
     337             : 
     338           0 : static void GDALPrintGifError(CPL_UNUSED GifFileType *hGifFile,
     339             :                               const char *pszMsg)
     340             : {
     341             :     // GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
     342             :     // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
     343             : #if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) &&                          \
     344             :     ((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
     345             :     // Static string actually, hence the const char* cast.
     346             : 
     347             : #if GIFLIB_MAJOR >= 5
     348           0 :     const char *pszGIFLIBError = GifErrorString(hGifFile->Error);
     349             : #else
     350             :     // TODO(schwehr): Can we remove the cast for older libgif?
     351             :     const char *pszGIFLIBError = (const char *)GifErrorString();
     352             : #endif
     353           0 :     if (pszGIFLIBError == nullptr)
     354           0 :         pszGIFLIBError = "Unknown error";
     355           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s. GIFLib Error : %s", pszMsg,
     356             :              pszGIFLIBError);
     357             : #else
     358             :     PrintGifError();
     359             :     CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMsg);
     360             : #endif
     361           0 : }
     362             : 
     363             : /************************************************************************/
     364             : /*                             CreateCopy()                             */
     365             : /************************************************************************/
     366             : 
     367          24 : GDALDataset *GIFDataset::CreateCopy(const char *pszFilename,
     368             :                                     GDALDataset *poSrcDS, int bStrict,
     369             :                                     CSLConstList papszOptions,
     370             :                                     GDALProgressFunc pfnProgress,
     371             :                                     void *pProgressData)
     372             : 
     373             : {
     374             :     /* -------------------------------------------------------------------- */
     375             :     /*      Check for interlaced option.                                    */
     376             :     /* -------------------------------------------------------------------- */
     377          24 :     const bool bInterlace = CPLFetchBool(papszOptions, "INTERLACING", false);
     378             : 
     379             :     /* -------------------------------------------------------------------- */
     380             :     /*      Some some rudimentary checks                                    */
     381             :     /* -------------------------------------------------------------------- */
     382          24 :     const int nBands = poSrcDS->GetRasterCount();
     383          24 :     if (nBands != 1)
     384             :     {
     385           5 :         CPLError(CE_Failure, CPLE_NotSupported,
     386             :                  "GIF driver only supports one band images.");
     387             : 
     388           5 :         return nullptr;
     389             :     }
     390             : 
     391          19 :     const int nXSize = poSrcDS->GetRasterXSize();
     392          19 :     const int nYSize = poSrcDS->GetRasterYSize();
     393          19 :     if (nXSize > 65535 || nYSize > 65535)
     394             :     {
     395           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     396             :                  "GIF driver only supports datasets up to 65535x65535 size.");
     397             : 
     398           0 :         return nullptr;
     399             :     }
     400             : 
     401          19 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt8 && bStrict)
     402             :     {
     403          10 :         CPLError(CE_Failure, CPLE_NotSupported,
     404             :                  "GIF driver doesn't support data type %s. "
     405             :                  "Only eight bit bands supported.",
     406             :                  GDALGetDataTypeName(
     407             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
     408             : 
     409          10 :         return nullptr;
     410             :     }
     411             : 
     412             :     /* -------------------------------------------------------------------- */
     413             :     /*      Open the output file.                                           */
     414             :     /* -------------------------------------------------------------------- */
     415           9 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
     416           9 :     if (fp == nullptr)
     417             :     {
     418           3 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s:\n%s",
     419           3 :                  pszFilename, VSIStrerror(errno));
     420           3 :         return nullptr;
     421             :     }
     422             : 
     423             : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
     424           6 :     int nError = 0;
     425           6 :     GifFileType *hGifFile = EGifOpen(fp, VSIGIFWriteFunc, &nError);
     426             : #else
     427             :     GifFileType *hGifFile = EGifOpen(fp, VSIGIFWriteFunc);
     428             : #endif
     429           6 :     if (hGifFile == nullptr)
     430             :     {
     431           0 :         VSIFCloseL(fp);
     432           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     433             :                  "EGifOpenFilename(%s) failed.  Does file already exist?",
     434             :                  pszFilename);
     435             : 
     436           0 :         return nullptr;
     437             :     }
     438             : 
     439             :     /* -------------------------------------------------------------------- */
     440             :     /*      Prepare colortable.                                             */
     441             :     /* -------------------------------------------------------------------- */
     442           6 :     GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
     443           6 :     ColorMapObject *psGifCT = nullptr;
     444             : 
     445           6 :     if (poBand->GetColorTable() == nullptr)
     446             :     {
     447           5 :         psGifCT = GifMakeMapObject(256, nullptr);
     448           5 :         if (psGifCT == nullptr)
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     451             :                      "Cannot allocate color table");
     452           0 :             GIFAbstractDataset::myEGifCloseFile(hGifFile);
     453           0 :             VSIFCloseL(fp);
     454           0 :             return nullptr;
     455             :         }
     456        1285 :         for (int iColor = 0; iColor < 256; iColor++)
     457             :         {
     458        1280 :             psGifCT->Colors[iColor].Red = static_cast<GifByteType>(iColor);
     459        1280 :             psGifCT->Colors[iColor].Green = static_cast<GifByteType>(iColor);
     460        1280 :             psGifCT->Colors[iColor].Blue = static_cast<GifByteType>(iColor);
     461             :         }
     462             :     }
     463             :     else
     464             :     {
     465           1 :         GDALColorTable *poCT = poBand->GetColorTable();
     466           1 :         int nFullCount = 2;
     467             : 
     468           4 :         while (nFullCount < poCT->GetColorEntryCount())
     469           3 :             nFullCount = nFullCount * 2;
     470             : 
     471           1 :         psGifCT = GifMakeMapObject(nFullCount, nullptr);
     472           1 :         if (psGifCT == nullptr)
     473             :         {
     474           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     475             :                      "Cannot allocate color table");
     476           0 :             GIFAbstractDataset::myEGifCloseFile(hGifFile);
     477           0 :             VSIFCloseL(fp);
     478           0 :             return nullptr;
     479             :         }
     480           1 :         int iColor = 0;
     481          17 :         for (; iColor < poCT->GetColorEntryCount(); iColor++)
     482             :         {
     483             :             GDALColorEntry sEntry;
     484             : 
     485          16 :             poCT->GetColorEntryAsRGB(iColor, &sEntry);
     486          16 :             psGifCT->Colors[iColor].Red = static_cast<GifByteType>(sEntry.c1);
     487          16 :             psGifCT->Colors[iColor].Green = static_cast<GifByteType>(sEntry.c2);
     488          16 :             psGifCT->Colors[iColor].Blue = static_cast<GifByteType>(sEntry.c3);
     489             :         }
     490           1 :         for (; iColor < nFullCount; iColor++)
     491             :         {
     492           0 :             psGifCT->Colors[iColor].Red = 0;
     493           0 :             psGifCT->Colors[iColor].Green = 0;
     494           0 :             psGifCT->Colors[iColor].Blue = 0;
     495             :         }
     496             :     }
     497             : 
     498             :     /* -------------------------------------------------------------------- */
     499             :     /*      Setup parameters.                                               */
     500             :     /* -------------------------------------------------------------------- */
     501           6 :     if (EGifPutScreenDesc(hGifFile, nXSize, nYSize, 8, /* ColorRes */
     502             :                           255,                         /* Background */
     503           6 :                           psGifCT) == GIF_ERROR)
     504             :     {
     505           0 :         GifFreeMapObject(psGifCT);
     506           0 :         GDALPrintGifError(hGifFile, "Error writing gif file.");
     507           0 :         GIFAbstractDataset::myEGifCloseFile(hGifFile);
     508           0 :         VSIFCloseL(fp);
     509           0 :         return nullptr;
     510             :     }
     511             : 
     512           6 :     GifFreeMapObject(psGifCT);
     513           6 :     psGifCT = nullptr;
     514             : 
     515             :     // Support for transparency.
     516           6 :     int bNoDataValue = 0;
     517           6 :     double noDataValue = poBand->GetNoDataValue(&bNoDataValue);
     518           6 :     if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255)
     519             :     {
     520           1 :         unsigned char extensionData[4] = {
     521             :             1,  // Transparent Color Flag.
     522           1 :             0, 0, static_cast<unsigned char>(noDataValue)};
     523           1 :         EGifPutExtension(hGifFile, 0xf9, 4, extensionData);
     524             :     }
     525             : 
     526           6 :     if (EGifPutImageDesc(hGifFile, 0, 0, nXSize, nYSize, bInterlace, nullptr) ==
     527             :         GIF_ERROR)
     528             :     {
     529           0 :         GDALPrintGifError(hGifFile, "Error writing gif file.");
     530           0 :         GIFAbstractDataset::myEGifCloseFile(hGifFile);
     531           0 :         VSIFCloseL(fp);
     532           0 :         return nullptr;
     533             :     }
     534             : 
     535             :     /* -------------------------------------------------------------------- */
     536             :     /*      Loop over image, copying image data.                            */
     537             :     /* -------------------------------------------------------------------- */
     538           6 :     GDALPamDataset *poDS = nullptr;
     539           6 :     GByte *pabyScanline = static_cast<GByte *>(CPLMalloc(nXSize));
     540             : 
     541           6 :     if (!pfnProgress(0.0, nullptr, pProgressData))
     542             :     {
     543           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to setup progress.");
     544             :     }
     545             : 
     546           6 :     if (!bInterlace)
     547             :     {
     548         475 :         for (int iLine = 0; iLine < nYSize; iLine++)
     549             :         {
     550         940 :             const CPLErr eErr = poBand->RasterIO(
     551             :                 GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1,
     552         470 :                 GDT_UInt8, nBands, static_cast<GSpacing>(nBands) * nXSize,
     553             :                 nullptr);
     554             : 
     555         940 :             if (eErr != CE_None ||
     556         470 :                 EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR)
     557             :             {
     558           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     559             :                          "Error writing gif file.");
     560           0 :                 goto error;
     561             :             }
     562             : 
     563         470 :             if (!pfnProgress((iLine + 1) * 1.0 / nYSize, nullptr,
     564             :                              pProgressData))
     565             :             {
     566           0 :                 goto error;
     567             :             }
     568             :         }
     569             :     }
     570             :     else
     571             :     {
     572           1 :         int nLinesRead = 0;
     573             :         // Need to perform 4 passes on the images:
     574           5 :         for (int i = 0; i < 4; i++)
     575             :         {
     576          24 :             for (int j = InterlacedOffset[i]; j < nYSize;
     577          20 :                  j += InterlacedJumps[i])
     578             :             {
     579             :                 const CPLErr eErr =
     580          20 :                     poBand->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
     581             :                                      nXSize, 1, GDT_UInt8, 1, nXSize, nullptr);
     582             : 
     583          40 :                 if (eErr != CE_None ||
     584          20 :                     EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR)
     585             :                 {
     586           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     587             :                              "Error writing gif file.");
     588           0 :                     goto error;
     589             :                 }
     590             : 
     591          20 :                 nLinesRead++;
     592          20 :                 if (!pfnProgress(nLinesRead * 1.0 / nYSize, nullptr,
     593             :                                  pProgressData))
     594             :                 {
     595           0 :                     goto error;
     596             :                 }
     597             :             }
     598             :         }
     599             :     }
     600             : 
     601           6 :     CPLFree(pabyScanline);
     602           6 :     pabyScanline = nullptr;
     603             : 
     604             :     /* -------------------------------------------------------------------- */
     605             :     /*      cleanup                                                         */
     606             :     /* -------------------------------------------------------------------- */
     607           6 :     if (GIFAbstractDataset::myEGifCloseFile(hGifFile) == GIF_ERROR)
     608             :     {
     609           0 :         CPLError(CE_Failure, CPLE_AppDefined, "EGifCloseFile() failed.");
     610           0 :         hGifFile = nullptr;
     611           0 :         goto error;
     612             :     }
     613           6 :     hGifFile = nullptr;
     614             : 
     615           6 :     VSIFCloseL(fp);
     616           6 :     fp = nullptr;
     617             : 
     618             :     /* -------------------------------------------------------------------- */
     619             :     /*      Do we need a world file?                                        */
     620             :     /* -------------------------------------------------------------------- */
     621           6 :     if (CPLFetchBool(papszOptions, "WORLDFILE", false))
     622             :     {
     623           0 :         GDALGeoTransform gt;
     624           0 :         if (poSrcDS->GetGeoTransform(gt) == CE_None)
     625           0 :             GDALWriteWorldFile(pszFilename, "wld", gt.data());
     626             :     }
     627             : 
     628             :     /* -------------------------------------------------------------------- */
     629             :     /*      Re-open dataset, and copy any auxiliary pam information.         */
     630             :     /* -------------------------------------------------------------------- */
     631             : 
     632             :     // If writing to stdout, we can't reopen it, so return
     633             :     // a fake dataset to make the caller happy.
     634             :     {
     635          12 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     636           6 :         GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
     637           6 :         poDS = OpenPAM(&oOpenInfo);
     638             :     }
     639           6 :     if (poDS)
     640             :     {
     641           5 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
     642           5 :         return poDS;
     643             :     }
     644             :     else
     645             :     {
     646           1 :         CPLErrorReset();
     647             : 
     648           1 :         GIFDataset *poGIF_DS = new GIFDataset();
     649           1 :         poGIF_DS->nRasterXSize = nXSize;
     650           1 :         poGIF_DS->nRasterYSize = nYSize;
     651           2 :         for (int i = 0; i < nBands; i++)
     652           1 :             poGIF_DS->SetBand(i + 1,
     653           1 :                               new GIFRasterBand(poGIF_DS, i + 1, nullptr, 0));
     654           1 :         return poGIF_DS;
     655             :     }
     656             : 
     657           0 : error:
     658           0 :     if (hGifFile)
     659           0 :         GIFAbstractDataset::myEGifCloseFile(hGifFile);
     660           0 :     if (fp)
     661           0 :         VSIFCloseL(fp);
     662           0 :     if (pabyScanline)
     663           0 :         CPLFree(pabyScanline);
     664           0 :     return nullptr;
     665             : }
     666             : 
     667             : /************************************************************************/
     668             : /*                          GDALRegister_GIF()                          */
     669             : /************************************************************************/
     670             : 
     671          19 : void GDALRegister_GIF()
     672             : 
     673             : {
     674          19 :     if (GDALGetDriverByName(GIF_DRIVER_NAME) != nullptr)
     675           0 :         return;
     676             : 
     677          19 :     GDALDriver *poDriver = new GDALDriver();
     678             : 
     679          19 :     GIFDriverSetCommonMetadata(poDriver);
     680          19 :     poDriver->pfnOpen = GIFDataset::Open;
     681          19 :     poDriver->pfnCreateCopy = GIFDataset::CreateCopy;
     682             : 
     683          19 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     684             : 
     685             : #ifdef GIF_PLUGIN
     686          19 :     GDALRegister_BIGGIF();
     687             : #endif
     688             : }

Generated by: LCOV version 1.14