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

Generated by: LCOV version 1.14