LCOV - code coverage report
Current view: top level - frmts/gif - biggifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 84 108 77.8 %
Date: 2024-11-21 22:18:42 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  BIGGIF Driver
       4             :  * Purpose:  Implement GDAL support for reading large GIF files in a
       5             :  *           streaming fashion rather than the slurp-into-memory approach
       6             :  *           of the normal GIF driver.
       7             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2001-2008, Frank Warmerdam <warmerdam@pobox.com>
      11             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_string.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "gifabstractdataset.h"
      20             : #include "gifdrivercore.h"
      21             : 
      22             : /************************************************************************/
      23             : /* ==================================================================== */
      24             : /*                          BIGGIFDataset                               */
      25             : /* ==================================================================== */
      26             : /************************************************************************/
      27             : 
      28             : class BIGGifRasterBand;
      29             : 
      30             : class BIGGIFDataset final : public GIFAbstractDataset
      31             : {
      32             :     friend class BIGGifRasterBand;
      33             : 
      34             :     int nLastLineRead;
      35             : 
      36             :     GDALDataset *poWorkDS;
      37             : 
      38             :     CPLErr ReOpen();
      39             : 
      40             :   protected:
      41             :     int CloseDependentDatasets() override;
      42             : 
      43             :   public:
      44             :     BIGGIFDataset();
      45             :     ~BIGGIFDataset() override;
      46             : 
      47             :     static GDALDataset *Open(GDALOpenInfo *);
      48             : };
      49             : 
      50             : /************************************************************************/
      51             : /* ==================================================================== */
      52             : /*                            BIGGifRasterBand                          */
      53             : /* ==================================================================== */
      54             : /************************************************************************/
      55             : 
      56             : class BIGGifRasterBand final : public GIFAbstractRasterBand
      57             : {
      58             :     friend class BIGGIFDataset;
      59             : 
      60             :   public:
      61             :     BIGGifRasterBand(BIGGIFDataset *, int);
      62             : 
      63             :     CPLErr IReadBlock(int, int, void *) override;
      64             : };
      65             : 
      66             : /************************************************************************/
      67             : /*                          BIGGifRasterBand()                          */
      68             : /************************************************************************/
      69             : 
      70           6 : BIGGifRasterBand::BIGGifRasterBand(BIGGIFDataset *poDSIn, int nBackground)
      71           6 :     : GIFAbstractRasterBand(poDSIn, 1, poDSIn->hGifFile->SavedImages,
      72           6 :                             nBackground, TRUE)
      73             : 
      74             : {
      75           6 : }
      76             : 
      77             : /************************************************************************/
      78             : /*                             IReadBlock()                             */
      79             : /************************************************************************/
      80             : 
      81        1200 : CPLErr BIGGifRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
      82             :                                     void *pImage)
      83             : {
      84        1200 :     BIGGIFDataset *poGDS = (BIGGIFDataset *)poDS;
      85             : 
      86        1200 :     CPLAssert(nBlockXOff == 0);
      87             : 
      88        1200 :     if (panInterlaceMap != nullptr)
      89        1200 :         nBlockYOff = panInterlaceMap[nBlockYOff];
      90             : 
      91             :     /* -------------------------------------------------------------------- */
      92             :     /*      Do we already have this line in the work dataset?               */
      93             :     /* -------------------------------------------------------------------- */
      94        1200 :     if (poGDS->poWorkDS != nullptr && nBlockYOff <= poGDS->nLastLineRead)
      95             :     {
      96         796 :         return poGDS->poWorkDS->RasterIO(GF_Read, 0, nBlockYOff, nBlockXSize, 1,
      97             :                                          pImage, nBlockXSize, 1, GDT_Byte, 1,
      98         796 :                                          nullptr, 0, 0, 0, nullptr);
      99             :     }
     100             : 
     101             :     /* -------------------------------------------------------------------- */
     102             :     /*      Do we need to restart from the start of the image?              */
     103             :     /* -------------------------------------------------------------------- */
     104         404 :     if (nBlockYOff <= poGDS->nLastLineRead)
     105             :     {
     106           2 :         if (poGDS->ReOpen() == CE_Failure)
     107           0 :             return CE_Failure;
     108             :     }
     109             : 
     110             :     /* -------------------------------------------------------------------- */
     111             :     /*      Read till we get our target line.                               */
     112             :     /* -------------------------------------------------------------------- */
     113         404 :     CPLErr eErr = CE_None;
     114        1606 :     while (poGDS->nLastLineRead < nBlockYOff && eErr == CE_None)
     115             :     {
     116        1202 :         if (DGifGetLine(poGDS->hGifFile, (GifPixelType *)pImage, nBlockXSize) ==
     117             :             GIF_ERROR)
     118             :         {
     119           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     120             :                      "Failure decoding scanline of GIF file.");
     121           0 :             return CE_Failure;
     122             :         }
     123             : 
     124        1202 :         poGDS->nLastLineRead++;
     125             : 
     126        1202 :         if (poGDS->poWorkDS != nullptr)
     127             :         {
     128         800 :             eErr = poGDS->poWorkDS->RasterIO(
     129             :                 GF_Write, 0, poGDS->nLastLineRead, nBlockXSize, 1, pImage,
     130             :                 nBlockXSize, 1, GDT_Byte, 1, nullptr, 0, 0, 0, nullptr);
     131             :         }
     132             :     }
     133             : 
     134         404 :     return eErr;
     135             : }
     136             : 
     137             : /************************************************************************/
     138             : /* ==================================================================== */
     139             : /*                             BIGGIFDataset                            */
     140             : /* ==================================================================== */
     141             : /************************************************************************/
     142             : 
     143             : /************************************************************************/
     144             : /*                            BIGGIFDataset()                            */
     145             : /************************************************************************/
     146             : 
     147           6 : BIGGIFDataset::BIGGIFDataset() : nLastLineRead(-1), poWorkDS(nullptr)
     148             : {
     149           6 : }
     150             : 
     151             : /************************************************************************/
     152             : /*                           ~BIGGIFDataset()                            */
     153             : /************************************************************************/
     154             : 
     155          12 : BIGGIFDataset::~BIGGIFDataset()
     156             : 
     157             : {
     158           6 :     BIGGIFDataset::FlushCache(true);
     159             : 
     160           6 :     BIGGIFDataset::CloseDependentDatasets();
     161          12 : }
     162             : 
     163             : /************************************************************************/
     164             : /*                      CloseDependentDatasets()                        */
     165             : /************************************************************************/
     166             : 
     167          16 : int BIGGIFDataset::CloseDependentDatasets()
     168             : {
     169          16 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     170             : 
     171          16 :     if (poWorkDS != nullptr)
     172             :     {
     173           2 :         bHasDroppedRef = TRUE;
     174             : 
     175           2 :         CPLString osTempFilename = poWorkDS->GetDescription();
     176           2 :         GDALDriver *poDrv = poWorkDS->GetDriver();
     177             : 
     178           2 :         GDALClose((GDALDatasetH)poWorkDS);
     179           2 :         poWorkDS = nullptr;
     180             : 
     181           2 :         if (poDrv != nullptr)
     182           2 :             poDrv->Delete(osTempFilename);
     183             : 
     184           2 :         poWorkDS = nullptr;
     185             :     }
     186             : 
     187          16 :     return bHasDroppedRef;
     188             : }
     189             : 
     190             : /************************************************************************/
     191             : /*                               ReOpen()                               */
     192             : /*                                                                      */
     193             : /*      (Re)Open the gif file and process past the first image          */
     194             : /*      descriptor.                                                     */
     195             : /************************************************************************/
     196             : 
     197           8 : CPLErr BIGGIFDataset::ReOpen()
     198             : 
     199             : {
     200             :     /* -------------------------------------------------------------------- */
     201             :     /*      If the file is already open, close it so we can restart.        */
     202             :     /* -------------------------------------------------------------------- */
     203           8 :     if (hGifFile != nullptr)
     204           2 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     205             : 
     206             :     /* -------------------------------------------------------------------- */
     207             :     /*      If we are actually reopening, then we assume that access to     */
     208             :     /*      the image data is not strictly once through sequential, and     */
     209             :     /*      we will try to create a working database in a temporary         */
     210             :     /*      directory to hold the image as we read through it the second    */
     211             :     /*      time.                                                           */
     212             :     /* -------------------------------------------------------------------- */
     213           8 :     if (hGifFile != nullptr)
     214             :     {
     215           2 :         GDALDriver *poGTiffDriver = (GDALDriver *)GDALGetDriverByName("GTiff");
     216             : 
     217           2 :         if (poGTiffDriver != nullptr)
     218             :         {
     219             :             /* Create as a sparse file to avoid filling up the whole file */
     220             :             /* while closing and then destroying this temporary dataset */
     221           2 :             const char *apszOptions[] = {"COMPRESS=LZW", "SPARSE_OK=YES",
     222             :                                          nullptr};
     223           2 :             CPLString osTempFilename = CPLGenerateTempFilename("biggif");
     224             : 
     225           2 :             osTempFilename += ".tif";
     226             : 
     227           2 :             poWorkDS = poGTiffDriver->Create(osTempFilename, nRasterXSize,
     228             :                                              nRasterYSize, 1, GDT_Byte,
     229             :                                              const_cast<char **>(apszOptions));
     230             :         }
     231             :     }
     232             : 
     233             :     /* -------------------------------------------------------------------- */
     234             :     /*      Open                                                            */
     235             :     /* -------------------------------------------------------------------- */
     236           8 :     VSIFSeekL(fp, 0, SEEK_SET);
     237             : 
     238           8 :     nLastLineRead = -1;
     239           8 :     hGifFile = GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
     240           8 :     if (hGifFile == nullptr)
     241             :     {
     242           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     243             :                  "DGifOpen() failed.  Perhaps the gif file is corrupt?\n");
     244             : 
     245           0 :         return CE_Failure;
     246             :     }
     247             : 
     248             :     /* -------------------------------------------------------------------- */
     249             :     /*      Find the first image record.                                    */
     250             :     /* -------------------------------------------------------------------- */
     251           8 :     GifRecordType RecordType = FindFirstImage(hGifFile);
     252           8 :     if (RecordType != IMAGE_DESC_RECORD_TYPE)
     253             :     {
     254           0 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     255           0 :         hGifFile = nullptr;
     256             : 
     257           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     258             :                  "Failed to find image description record in GIF file.");
     259           0 :         return CE_Failure;
     260             :     }
     261             : 
     262           8 :     if (DGifGetImageDesc(hGifFile) == GIF_ERROR)
     263             :     {
     264           0 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     265           0 :         hGifFile = nullptr;
     266             : 
     267           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     268             :                  "Image description reading failed in GIF file.");
     269           0 :         return CE_Failure;
     270             :     }
     271             : 
     272           8 :     return CE_None;
     273             : }
     274             : 
     275             : /************************************************************************/
     276             : /*                                Open()                                */
     277             : /************************************************************************/
     278             : 
     279           6 : GDALDataset *BIGGIFDataset::Open(GDALOpenInfo *poOpenInfo)
     280             : 
     281             : {
     282           6 :     if (!GIFDriverIdentify(poOpenInfo))
     283           0 :         return nullptr;
     284             : 
     285           6 :     if (poOpenInfo->eAccess == GA_Update)
     286             :     {
     287           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     288             :                  "The GIF driver does not support update access to existing"
     289             :                  " files.\n");
     290           0 :         return nullptr;
     291             :     }
     292             : 
     293             :     /* -------------------------------------------------------------------- */
     294             :     /*      Create a corresponding GDALDataset.                             */
     295             :     /* -------------------------------------------------------------------- */
     296           6 :     BIGGIFDataset *poDS = new BIGGIFDataset();
     297             : 
     298           6 :     poDS->fp = poOpenInfo->fpL;
     299           6 :     poOpenInfo->fpL = nullptr;
     300           6 :     poDS->eAccess = GA_ReadOnly;
     301           6 :     if (poDS->ReOpen() == CE_Failure)
     302             :     {
     303           0 :         delete poDS;
     304           0 :         return nullptr;
     305             :     }
     306             : 
     307             :     /* -------------------------------------------------------------------- */
     308             :     /*      Capture some information from the file that is of interest.     */
     309             :     /* -------------------------------------------------------------------- */
     310             : 
     311           6 :     poDS->nRasterXSize = poDS->hGifFile->SavedImages[0].ImageDesc.Width;
     312           6 :     poDS->nRasterYSize = poDS->hGifFile->SavedImages[0].ImageDesc.Height;
     313           6 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     314             :     {
     315           0 :         delete poDS;
     316           0 :         return nullptr;
     317             :     }
     318             : 
     319           6 :     if (poDS->hGifFile->SavedImages[0].ImageDesc.ColorMap == nullptr &&
     320           6 :         poDS->hGifFile->SColorMap == nullptr)
     321             :     {
     322           0 :         CPLDebug("GIF", "Skipping image without color table");
     323           0 :         delete poDS;
     324           0 :         return nullptr;
     325             :     }
     326             : 
     327             :     /* -------------------------------------------------------------------- */
     328             :     /*      Create band information objects.                                */
     329             :     /* -------------------------------------------------------------------- */
     330           6 :     poDS->SetBand(1,
     331           6 :                   new BIGGifRasterBand(poDS, poDS->hGifFile->SBackGroundColor));
     332             : 
     333             :     /* -------------------------------------------------------------------- */
     334             :     /*      Check for georeferencing.                                       */
     335             :     /* -------------------------------------------------------------------- */
     336           6 :     poDS->DetectGeoreferencing(poOpenInfo);
     337             : 
     338             :     /* -------------------------------------------------------------------- */
     339             :     /*      Initialize any PAM information.                                 */
     340             :     /* -------------------------------------------------------------------- */
     341           6 :     poDS->SetDescription(poOpenInfo->pszFilename);
     342           6 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     343             : 
     344             :     /* -------------------------------------------------------------------- */
     345             :     /*      Support overviews.                                              */
     346             :     /* -------------------------------------------------------------------- */
     347          12 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     348           6 :                                 poOpenInfo->GetSiblingFiles());
     349             : 
     350           6 :     return poDS;
     351             : }
     352             : 
     353             : /************************************************************************/
     354             : /*                        GDALRegister_BIGGIF()                         */
     355             : /************************************************************************/
     356             : 
     357          19 : void GDALRegister_BIGGIF()
     358             : 
     359             : {
     360          19 :     if (GDALGetDriverByName(BIGGIF_DRIVER_NAME) != nullptr)
     361           0 :         return;
     362             : 
     363          19 :     GDALDriver *poDriver = new GDALDriver();
     364          19 :     BIGGIFDriverSetCommonMetadata(poDriver);
     365             : 
     366          19 :     poDriver->pfnOpen = BIGGIFDataset::Open;
     367             : 
     368          19 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     369             : }

Generated by: LCOV version 1.14