LCOV - code coverage report
Current view: top level - frmts/gif - biggifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 83 107 77.6 %
Date: 2024-05-04 12:52:34 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      14             :  * copy of this software and associated documentation files (the "Software"),
      15             :  * to deal in the Software without restriction, including without limitation
      16             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17             :  * and/or sell copies of the Software, and to permit persons to whom the
      18             :  * Software is furnished to do so, subject to the following conditions:
      19             :  *
      20             :  * The above copyright notice and this permission notice shall be included
      21             :  * in all copies or substantial portions of the Software.
      22             :  *
      23             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29             :  * DEALINGS IN THE SOFTWARE.
      30             :  ****************************************************************************/
      31             : 
      32             : #include "cpl_string.h"
      33             : #include "gdal_frmts.h"
      34             : #include "gdal_pam.h"
      35             : #include "gifabstractdataset.h"
      36             : #include "gifdrivercore.h"
      37             : 
      38             : /************************************************************************/
      39             : /* ==================================================================== */
      40             : /*                          BIGGIFDataset                               */
      41             : /* ==================================================================== */
      42             : /************************************************************************/
      43             : 
      44             : class BIGGifRasterBand;
      45             : 
      46             : class BIGGIFDataset final : public GIFAbstractDataset
      47             : {
      48             :     friend class BIGGifRasterBand;
      49             : 
      50             :     int nLastLineRead;
      51             : 
      52             :     GDALDataset *poWorkDS;
      53             : 
      54             :     CPLErr ReOpen();
      55             : 
      56             :   protected:
      57             :     int CloseDependentDatasets() override;
      58             : 
      59             :   public:
      60             :     BIGGIFDataset();
      61             :     ~BIGGIFDataset() override;
      62             : 
      63             :     static GDALDataset *Open(GDALOpenInfo *);
      64             : };
      65             : 
      66             : /************************************************************************/
      67             : /* ==================================================================== */
      68             : /*                            BIGGifRasterBand                          */
      69             : /* ==================================================================== */
      70             : /************************************************************************/
      71             : 
      72             : class BIGGifRasterBand final : public GIFAbstractRasterBand
      73             : {
      74             :     friend class BIGGIFDataset;
      75             : 
      76             :   public:
      77             :     BIGGifRasterBand(BIGGIFDataset *, int);
      78             : 
      79             :     CPLErr IReadBlock(int, int, void *) override;
      80             : };
      81             : 
      82             : /************************************************************************/
      83             : /*                          BIGGifRasterBand()                          */
      84             : /************************************************************************/
      85             : 
      86           6 : BIGGifRasterBand::BIGGifRasterBand(BIGGIFDataset *poDSIn, int nBackground)
      87           6 :     : GIFAbstractRasterBand(poDSIn, 1, poDSIn->hGifFile->SavedImages,
      88           6 :                             nBackground, TRUE)
      89             : 
      90             : {
      91           6 : }
      92             : 
      93             : /************************************************************************/
      94             : /*                             IReadBlock()                             */
      95             : /************************************************************************/
      96             : 
      97        1200 : CPLErr BIGGifRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
      98             :                                     void *pImage)
      99             : {
     100        1200 :     BIGGIFDataset *poGDS = (BIGGIFDataset *)poDS;
     101             : 
     102        1200 :     CPLAssert(nBlockXOff == 0);
     103             : 
     104        1200 :     if (panInterlaceMap != nullptr)
     105        1200 :         nBlockYOff = panInterlaceMap[nBlockYOff];
     106             : 
     107             :     /* -------------------------------------------------------------------- */
     108             :     /*      Do we already have this line in the work dataset?               */
     109             :     /* -------------------------------------------------------------------- */
     110        1200 :     if (poGDS->poWorkDS != nullptr && nBlockYOff <= poGDS->nLastLineRead)
     111             :     {
     112         796 :         return poGDS->poWorkDS->RasterIO(GF_Read, 0, nBlockYOff, nBlockXSize, 1,
     113             :                                          pImage, nBlockXSize, 1, GDT_Byte, 1,
     114         796 :                                          nullptr, 0, 0, 0, nullptr);
     115             :     }
     116             : 
     117             :     /* -------------------------------------------------------------------- */
     118             :     /*      Do we need to restart from the start of the image?              */
     119             :     /* -------------------------------------------------------------------- */
     120         404 :     if (nBlockYOff <= poGDS->nLastLineRead)
     121             :     {
     122           2 :         if (poGDS->ReOpen() == CE_Failure)
     123           0 :             return CE_Failure;
     124             :     }
     125             : 
     126             :     /* -------------------------------------------------------------------- */
     127             :     /*      Read till we get our target line.                               */
     128             :     /* -------------------------------------------------------------------- */
     129         404 :     CPLErr eErr = CE_None;
     130        1606 :     while (poGDS->nLastLineRead < nBlockYOff && eErr == CE_None)
     131             :     {
     132        1202 :         if (DGifGetLine(poGDS->hGifFile, (GifPixelType *)pImage, nBlockXSize) ==
     133             :             GIF_ERROR)
     134             :         {
     135           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     136             :                      "Failure decoding scanline of GIF file.");
     137           0 :             return CE_Failure;
     138             :         }
     139             : 
     140        1202 :         poGDS->nLastLineRead++;
     141             : 
     142        1202 :         if (poGDS->poWorkDS != nullptr)
     143             :         {
     144         800 :             eErr = poGDS->poWorkDS->RasterIO(
     145             :                 GF_Write, 0, poGDS->nLastLineRead, nBlockXSize, 1, pImage,
     146             :                 nBlockXSize, 1, GDT_Byte, 1, nullptr, 0, 0, 0, nullptr);
     147             :         }
     148             :     }
     149             : 
     150         404 :     return eErr;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /* ==================================================================== */
     155             : /*                             BIGGIFDataset                            */
     156             : /* ==================================================================== */
     157             : /************************************************************************/
     158             : 
     159             : /************************************************************************/
     160             : /*                            BIGGIFDataset()                            */
     161             : /************************************************************************/
     162             : 
     163           6 : BIGGIFDataset::BIGGIFDataset() : nLastLineRead(-1), poWorkDS(nullptr)
     164             : {
     165           6 : }
     166             : 
     167             : /************************************************************************/
     168             : /*                           ~BIGGIFDataset()                            */
     169             : /************************************************************************/
     170             : 
     171          12 : BIGGIFDataset::~BIGGIFDataset()
     172             : 
     173             : {
     174           6 :     BIGGIFDataset::FlushCache(true);
     175             : 
     176           6 :     BIGGIFDataset::CloseDependentDatasets();
     177          12 : }
     178             : 
     179             : /************************************************************************/
     180             : /*                      CloseDependentDatasets()                        */
     181             : /************************************************************************/
     182             : 
     183          24 : int BIGGIFDataset::CloseDependentDatasets()
     184             : {
     185          24 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     186             : 
     187          24 :     if (poWorkDS != nullptr)
     188             :     {
     189           2 :         bHasDroppedRef = TRUE;
     190             : 
     191           2 :         CPLString osTempFilename = poWorkDS->GetDescription();
     192           2 :         GDALDriver *poDrv = poWorkDS->GetDriver();
     193             : 
     194           2 :         GDALClose((GDALDatasetH)poWorkDS);
     195           2 :         poWorkDS = nullptr;
     196             : 
     197           2 :         if (poDrv != nullptr)
     198           2 :             poDrv->Delete(osTempFilename);
     199             : 
     200           2 :         poWorkDS = nullptr;
     201             :     }
     202             : 
     203          24 :     return bHasDroppedRef;
     204             : }
     205             : 
     206             : /************************************************************************/
     207             : /*                               ReOpen()                               */
     208             : /*                                                                      */
     209             : /*      (Re)Open the gif file and process past the first image          */
     210             : /*      descriptor.                                                     */
     211             : /************************************************************************/
     212             : 
     213           8 : CPLErr BIGGIFDataset::ReOpen()
     214             : 
     215             : {
     216             :     /* -------------------------------------------------------------------- */
     217             :     /*      If the file is already open, close it so we can restart.        */
     218             :     /* -------------------------------------------------------------------- */
     219           8 :     if (hGifFile != nullptr)
     220           2 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     221             : 
     222             :     /* -------------------------------------------------------------------- */
     223             :     /*      If we are actually reopening, then we assume that access to     */
     224             :     /*      the image data is not strictly once through sequential, and     */
     225             :     /*      we will try to create a working database in a temporary         */
     226             :     /*      directory to hold the image as we read through it the second    */
     227             :     /*      time.                                                           */
     228             :     /* -------------------------------------------------------------------- */
     229           8 :     if (hGifFile != nullptr)
     230             :     {
     231           2 :         GDALDriver *poGTiffDriver = (GDALDriver *)GDALGetDriverByName("GTiff");
     232             : 
     233           2 :         if (poGTiffDriver != nullptr)
     234             :         {
     235             :             /* Create as a sparse file to avoid filling up the whole file */
     236             :             /* while closing and then destroying this temporary dataset */
     237           2 :             const char *apszOptions[] = {"COMPRESS=LZW", "SPARSE_OK=YES",
     238             :                                          nullptr};
     239           2 :             CPLString osTempFilename = CPLGenerateTempFilename("biggif");
     240             : 
     241           2 :             osTempFilename += ".tif";
     242             : 
     243           2 :             poWorkDS = poGTiffDriver->Create(osTempFilename, nRasterXSize,
     244             :                                              nRasterYSize, 1, GDT_Byte,
     245             :                                              const_cast<char **>(apszOptions));
     246             :         }
     247             :     }
     248             : 
     249             :     /* -------------------------------------------------------------------- */
     250             :     /*      Open                                                            */
     251             :     /* -------------------------------------------------------------------- */
     252           8 :     VSIFSeekL(fp, 0, SEEK_SET);
     253             : 
     254           8 :     nLastLineRead = -1;
     255           8 :     hGifFile = GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
     256           8 :     if (hGifFile == nullptr)
     257             :     {
     258           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     259             :                  "DGifOpen() failed.  Perhaps the gif file is corrupt?\n");
     260             : 
     261           0 :         return CE_Failure;
     262             :     }
     263             : 
     264             :     /* -------------------------------------------------------------------- */
     265             :     /*      Find the first image record.                                    */
     266             :     /* -------------------------------------------------------------------- */
     267           8 :     GifRecordType RecordType = FindFirstImage(hGifFile);
     268           8 :     if (RecordType != IMAGE_DESC_RECORD_TYPE)
     269             :     {
     270           0 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     271           0 :         hGifFile = nullptr;
     272             : 
     273           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     274             :                  "Failed to find image description record in GIF file.");
     275           0 :         return CE_Failure;
     276             :     }
     277             : 
     278           8 :     if (DGifGetImageDesc(hGifFile) == GIF_ERROR)
     279             :     {
     280           0 :         GIFAbstractDataset::myDGifCloseFile(hGifFile);
     281           0 :         hGifFile = nullptr;
     282             : 
     283           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     284             :                  "Image description reading failed in GIF file.");
     285           0 :         return CE_Failure;
     286             :     }
     287             : 
     288           8 :     return CE_None;
     289             : }
     290             : 
     291             : /************************************************************************/
     292             : /*                                Open()                                */
     293             : /************************************************************************/
     294             : 
     295           6 : GDALDataset *BIGGIFDataset::Open(GDALOpenInfo *poOpenInfo)
     296             : 
     297             : {
     298           6 :     if (!GIFDriverIdentify(poOpenInfo))
     299           0 :         return nullptr;
     300             : 
     301           6 :     if (poOpenInfo->eAccess == GA_Update)
     302             :     {
     303           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     304             :                  "The GIF driver does not support update access to existing"
     305             :                  " files.\n");
     306           0 :         return nullptr;
     307             :     }
     308             : 
     309             :     /* -------------------------------------------------------------------- */
     310             :     /*      Create a corresponding GDALDataset.                             */
     311             :     /* -------------------------------------------------------------------- */
     312           6 :     BIGGIFDataset *poDS = new BIGGIFDataset();
     313             : 
     314           6 :     poDS->fp = poOpenInfo->fpL;
     315           6 :     poOpenInfo->fpL = nullptr;
     316           6 :     poDS->eAccess = GA_ReadOnly;
     317           6 :     if (poDS->ReOpen() == CE_Failure)
     318             :     {
     319           0 :         delete poDS;
     320           0 :         return nullptr;
     321             :     }
     322             : 
     323             :     /* -------------------------------------------------------------------- */
     324             :     /*      Capture some information from the file that is of interest.     */
     325             :     /* -------------------------------------------------------------------- */
     326             : 
     327           6 :     poDS->nRasterXSize = poDS->hGifFile->SavedImages[0].ImageDesc.Width;
     328           6 :     poDS->nRasterYSize = poDS->hGifFile->SavedImages[0].ImageDesc.Height;
     329           6 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     330             :     {
     331           0 :         delete poDS;
     332           0 :         return nullptr;
     333             :     }
     334             : 
     335           6 :     if (poDS->hGifFile->SavedImages[0].ImageDesc.ColorMap == nullptr &&
     336           6 :         poDS->hGifFile->SColorMap == nullptr)
     337             :     {
     338           0 :         CPLDebug("GIF", "Skipping image without color table");
     339           0 :         delete poDS;
     340           0 :         return nullptr;
     341             :     }
     342             : 
     343             :     /* -------------------------------------------------------------------- */
     344             :     /*      Create band information objects.                                */
     345             :     /* -------------------------------------------------------------------- */
     346           6 :     poDS->SetBand(1,
     347           6 :                   new BIGGifRasterBand(poDS, poDS->hGifFile->SBackGroundColor));
     348             : 
     349             :     /* -------------------------------------------------------------------- */
     350             :     /*      Check for georeferencing.                                       */
     351             :     /* -------------------------------------------------------------------- */
     352           6 :     poDS->DetectGeoreferencing(poOpenInfo);
     353             : 
     354             :     /* -------------------------------------------------------------------- */
     355             :     /*      Initialize any PAM information.                                 */
     356             :     /* -------------------------------------------------------------------- */
     357           6 :     poDS->SetDescription(poOpenInfo->pszFilename);
     358           6 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     359             : 
     360             :     /* -------------------------------------------------------------------- */
     361             :     /*      Support overviews.                                              */
     362             :     /* -------------------------------------------------------------------- */
     363           6 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     364             :                                 poOpenInfo->GetSiblingFiles());
     365             : 
     366           6 :     return poDS;
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                        GDALRegister_BIGGIF()                         */
     371             : /************************************************************************/
     372             : 
     373          17 : void GDALRegister_BIGGIF()
     374             : 
     375             : {
     376          17 :     if (GDALGetDriverByName(BIGGIF_DRIVER_NAME) != nullptr)
     377           0 :         return;
     378             : 
     379          17 :     GDALDriver *poDriver = new GDALDriver();
     380          17 :     BIGGIFDriverSetCommonMetadata(poDriver);
     381             : 
     382          17 :     poDriver->pfnOpen = BIGGIFDataset::Open;
     383             : 
     384          17 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     385             : }

Generated by: LCOV version 1.14