LCOV - code coverage report
Current view: top level - frmts/gif - gifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 172 249 69.1 %
Date: 2024-11-21 22:18:42 Functions: 7 8 87.5 %

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