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

Generated by: LCOV version 1.14