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 : }
|