LCOV - code coverage report
Current view: top level - frmts/gif - gifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 173 250 69.2 %
Date: 2025-07-13 09:04:35 Functions: 9 10 90.0 %

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

Generated by: LCOV version 1.14