1 : /******************************************************************************
2 : * $Id: biggifdataset.cpp 22684 2011-07-10 19:30:49Z rouault $
3 : *
4 : * Project: BIGGIF Driver
5 : * Purpose: Implement GDAL support for reading large GIF files in a
6 : * streaming fashion rather than the slurp-into-memory approach
7 : * of the normal GIF driver.
8 : * Author: Frank Warmerdam, warmerdam@pobox.com
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2001-2008, Frank Warmerdam <warmerdam@pobox.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 "gdal_pam.h"
33 : #include "cpl_string.h"
34 : #include "gifabstractdataset.h"
35 :
36 : CPL_CVSID("$Id: biggifdataset.cpp 22684 2011-07-10 19:30:49Z rouault $");
37 :
38 : CPL_C_START
39 : void GDALRegister_BIGGIF(void);
40 : CPL_C_END
41 :
42 : static const int InterlacedOffset[] = { 0, 4, 2, 1 };
43 : static const int InterlacedJumps[] = { 8, 8, 4, 2 };
44 :
45 : static int VSIGIFReadFunc( GifFileType *, GifByteType *, int);
46 :
47 : /************************************************************************/
48 : /* ==================================================================== */
49 : /* BIGGIFDataset */
50 : /* ==================================================================== */
51 : /************************************************************************/
52 :
53 : class BIGGifRasterBand;
54 :
55 : class BIGGIFDataset : public GIFAbstractDataset
56 : {
57 : friend class BIGGifRasterBand;
58 :
59 : int nLastLineRead;
60 :
61 : GDALDataset *poWorkDS;
62 :
63 : CPLErr ReOpen();
64 :
65 : protected:
66 : virtual int CloseDependentDatasets();
67 :
68 : public:
69 : BIGGIFDataset();
70 : ~BIGGIFDataset();
71 :
72 : static GDALDataset *Open( GDALOpenInfo * );
73 : };
74 :
75 : /************************************************************************/
76 : /* ==================================================================== */
77 : /* BIGGifRasterBand */
78 : /* ==================================================================== */
79 : /************************************************************************/
80 :
81 : class BIGGifRasterBand : public GDALPamRasterBand
82 : {
83 : friend class BIGGIFDataset;
84 :
85 : int *panInterlaceMap;
86 :
87 : GDALColorTable *poColorTable;
88 :
89 : public:
90 :
91 : BIGGifRasterBand( BIGGIFDataset *, int );
92 : virtual ~BIGGifRasterBand();
93 :
94 : virtual CPLErr IReadBlock( int, int, void * );
95 :
96 : virtual GDALColorInterp GetColorInterpretation();
97 : virtual GDALColorTable *GetColorTable();
98 : };
99 :
100 : /************************************************************************/
101 : /* BIGGifRasterBand() */
102 : /************************************************************************/
103 :
104 3 : BIGGifRasterBand::BIGGifRasterBand( BIGGIFDataset *poDS, int nBackground )
105 :
106 : {
107 3 : SavedImage *psImage = poDS->hGifFile->SavedImages + 0;
108 :
109 3 : this->poDS = poDS;
110 3 : this->nBand = 1;
111 :
112 3 : eDataType = GDT_Byte;
113 :
114 3 : nBlockXSize = poDS->nRasterXSize;
115 3 : nBlockYSize = 1;
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Setup interlacing map if required. */
119 : /* -------------------------------------------------------------------- */
120 3 : panInterlaceMap = NULL;
121 3 : if( psImage->ImageDesc.Interlace )
122 : {
123 3 : int i, j, iLine = 0;
124 :
125 3 : poDS->SetMetadataItem( "INTERLACED", "YES", "IMAGE_STRUCTURE" );
126 :
127 3 : panInterlaceMap = (int *) CPLCalloc(poDS->nRasterYSize,sizeof(int));
128 :
129 15 : for (i = 0; i < 4; i++)
130 : {
131 67146 : for (j = InterlacedOffset[i];
132 : j < poDS->nRasterYSize;
133 33567 : j += InterlacedJumps[i])
134 33567 : panInterlaceMap[j] = iLine++;
135 : }
136 : }
137 : else
138 0 : poDS->SetMetadataItem( "INTERLACED", "NO", "IMAGE_STRUCTURE" );
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Setup colormap. */
142 : /* -------------------------------------------------------------------- */
143 3 : ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap;
144 3 : if( psGifCT == NULL )
145 3 : psGifCT = poDS->hGifFile->SColorMap;
146 :
147 3 : poColorTable = new GDALColorTable();
148 51 : for( int iColor = 0; iColor < psGifCT->ColorCount; iColor++ )
149 : {
150 : GDALColorEntry oEntry;
151 :
152 48 : oEntry.c1 = psGifCT->Colors[iColor].Red;
153 48 : oEntry.c2 = psGifCT->Colors[iColor].Green;
154 48 : oEntry.c3 = psGifCT->Colors[iColor].Blue;
155 48 : oEntry.c4 = 255;
156 :
157 48 : poColorTable->SetColorEntry( iColor, &oEntry );
158 : }
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* If we have a background value, return it here. Some */
162 : /* applications might want to treat this as transparent, but in */
163 : /* many uses this is inappropriate so we don't return it as */
164 : /* nodata or transparent. */
165 : /* -------------------------------------------------------------------- */
166 3 : if( nBackground != 255 )
167 : {
168 : char szBackground[10];
169 :
170 3 : sprintf( szBackground, "%d", nBackground );
171 3 : SetMetadataItem( "GIF_BACKGROUND", szBackground );
172 : }
173 3 : }
174 :
175 : /************************************************************************/
176 : /* ~BIGGifRasterBand() */
177 : /************************************************************************/
178 :
179 3 : BIGGifRasterBand::~BIGGifRasterBand()
180 :
181 : {
182 3 : if( poColorTable != NULL )
183 3 : delete poColorTable;
184 :
185 3 : CPLFree( panInterlaceMap );
186 3 : }
187 :
188 : /************************************************************************/
189 : /* IReadBlock() */
190 : /************************************************************************/
191 :
192 400 : CPLErr BIGGifRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
193 : void * pImage )
194 :
195 : {
196 400 : BIGGIFDataset *poGDS = (BIGGIFDataset *) poDS;
197 :
198 400 : CPLAssert( nBlockXOff == 0 );
199 :
200 400 : if( panInterlaceMap != NULL )
201 400 : nBlockYOff = panInterlaceMap[nBlockYOff];
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Do we already have this line in the work dataset? */
205 : /* -------------------------------------------------------------------- */
206 400 : if( poGDS->poWorkDS != NULL && nBlockYOff <= poGDS->nLastLineRead )
207 : {
208 : return poGDS->poWorkDS->
209 : RasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1,
210 : pImage, nBlockXSize, 1, GDT_Byte,
211 198 : 1, NULL, 0, 0, 0 );
212 : }
213 :
214 : /* -------------------------------------------------------------------- */
215 : /* Do we need to restart from the start of the image? */
216 : /* -------------------------------------------------------------------- */
217 202 : if( nBlockYOff <= poGDS->nLastLineRead )
218 : {
219 1 : if( poGDS->ReOpen() == CE_Failure )
220 0 : return CE_Failure;
221 : }
222 :
223 : /* -------------------------------------------------------------------- */
224 : /* Read till we get our target line. */
225 : /* -------------------------------------------------------------------- */
226 1005 : while( poGDS->nLastLineRead < nBlockYOff )
227 : {
228 601 : if( DGifGetLine( poGDS->hGifFile, (GifPixelType*)pImage,
229 : nBlockXSize ) == GIF_ERROR )
230 : {
231 : CPLError( CE_Failure, CPLE_AppDefined,
232 0 : "Failure decoding scanline of GIF file." );
233 0 : return CE_Failure;
234 : }
235 :
236 601 : poGDS->nLastLineRead++;
237 :
238 601 : if( poGDS->poWorkDS != NULL )
239 : {
240 : poGDS->poWorkDS->RasterIO( GF_Write,
241 : 0, poGDS->nLastLineRead, nBlockXSize, 1,
242 : pImage, nBlockXSize, 1, GDT_Byte,
243 400 : 1, NULL, 0, 0, 0 );
244 : }
245 : }
246 :
247 202 : return CE_None;
248 : }
249 :
250 : /************************************************************************/
251 : /* GetColorInterpretation() */
252 : /************************************************************************/
253 :
254 0 : GDALColorInterp BIGGifRasterBand::GetColorInterpretation()
255 :
256 : {
257 0 : return GCI_PaletteIndex;
258 : }
259 :
260 : /************************************************************************/
261 : /* GetColorTable() */
262 : /************************************************************************/
263 :
264 0 : GDALColorTable *BIGGifRasterBand::GetColorTable()
265 :
266 : {
267 0 : return poColorTable;
268 : }
269 :
270 : /************************************************************************/
271 : /* ==================================================================== */
272 : /* BIGGIFDataset */
273 : /* ==================================================================== */
274 : /************************************************************************/
275 :
276 :
277 : /************************************************************************/
278 : /* BIGGIFDataset() */
279 : /************************************************************************/
280 :
281 3 : BIGGIFDataset::BIGGIFDataset()
282 :
283 : {
284 3 : nLastLineRead = -1;
285 3 : poWorkDS = NULL;
286 3 : }
287 :
288 : /************************************************************************/
289 : /* ~BIGGIFDataset() */
290 : /************************************************************************/
291 :
292 3 : BIGGIFDataset::~BIGGIFDataset()
293 :
294 : {
295 3 : FlushCache();
296 :
297 3 : CloseDependentDatasets();
298 3 : }
299 :
300 : /************************************************************************/
301 : /* CloseDependentDatasets() */
302 : /************************************************************************/
303 :
304 3 : int BIGGIFDataset::CloseDependentDatasets()
305 : {
306 3 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
307 :
308 3 : if( poWorkDS != NULL )
309 : {
310 1 : bHasDroppedRef = TRUE;
311 :
312 1 : CPLString osTempFilename = poWorkDS->GetDescription();
313 :
314 1 : GDALClose( (GDALDatasetH) poWorkDS );
315 1 : poWorkDS = NULL;
316 :
317 1 : GDALDriver *poGTiff = (GDALDriver *) GDALGetDriverByName( "GTiff" );
318 1 : poGTiff->Delete( osTempFilename );
319 :
320 1 : poWorkDS = NULL;
321 : }
322 :
323 3 : return bHasDroppedRef;
324 : }
325 :
326 : /************************************************************************/
327 : /* ReOpen() */
328 : /* */
329 : /* (Re)Open the gif file and process past the first image */
330 : /* descriptor. */
331 : /************************************************************************/
332 :
333 4 : CPLErr BIGGIFDataset::ReOpen()
334 :
335 : {
336 : /* -------------------------------------------------------------------- */
337 : /* If the file is already open, close it so we can restart. */
338 : /* -------------------------------------------------------------------- */
339 4 : if( hGifFile != NULL )
340 1 : DGifCloseFile( hGifFile );
341 :
342 : /* -------------------------------------------------------------------- */
343 : /* If we are actually reopening, then we assume that access to */
344 : /* the image data is not strictly once through sequential, and */
345 : /* we will try to create a working database in a temporary */
346 : /* directory to hold the image as we read through it the second */
347 : /* time. */
348 : /* -------------------------------------------------------------------- */
349 4 : if( hGifFile != NULL )
350 : {
351 1 : GDALDriver *poGTiffDriver = (GDALDriver*) GDALGetDriverByName("GTiff");
352 :
353 1 : if( poGTiffDriver != NULL )
354 : {
355 : /* Create as a sparse file to avoid filling up the whole file */
356 : /* while closing and then destroying this temporary dataset */
357 1 : const char* apszOptions[] = { "COMPRESS=LZW", "SPARSE_OK=YES", NULL };
358 1 : CPLString osTempFilename = CPLGenerateTempFilename("biggif");
359 :
360 1 : osTempFilename += ".tif";
361 :
362 : poWorkDS = poGTiffDriver->Create( osTempFilename,
363 : nRasterXSize, nRasterYSize, 1,
364 1 : GDT_Byte, const_cast<char**>(apszOptions));
365 : }
366 : }
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Open */
370 : /* -------------------------------------------------------------------- */
371 4 : VSIFSeekL( fp, 0, SEEK_SET );
372 :
373 4 : nLastLineRead = -1;
374 4 : hGifFile = DGifOpen( fp, VSIGIFReadFunc );
375 4 : if( hGifFile == NULL )
376 : {
377 : CPLError( CE_Failure, CPLE_OpenFailed,
378 0 : "DGifOpen() failed. Perhaps the gif file is corrupt?\n" );
379 :
380 0 : return CE_Failure;
381 : }
382 :
383 : /* -------------------------------------------------------------------- */
384 : /* Find the first image record. */
385 : /* -------------------------------------------------------------------- */
386 4 : GifRecordType RecordType = TERMINATE_RECORD_TYPE;
387 :
388 8 : while( DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR
389 : && RecordType != TERMINATE_RECORD_TYPE
390 : && RecordType != IMAGE_DESC_RECORD_TYPE )
391 : {
392 : /* Skip extension records found before IMAGE_DESC_RECORD_TYPE */
393 0 : if (RecordType == EXTENSION_RECORD_TYPE)
394 : {
395 : int nFunction;
396 : GifByteType *pExtData;
397 0 : if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR)
398 0 : break;
399 0 : while (pExtData != NULL)
400 : {
401 0 : if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR)
402 0 : break;
403 : }
404 : }
405 : }
406 :
407 4 : if( RecordType != IMAGE_DESC_RECORD_TYPE )
408 : {
409 0 : DGifCloseFile( hGifFile );
410 0 : hGifFile = NULL;
411 :
412 : CPLError( CE_Failure, CPLE_OpenFailed,
413 0 : "Failed to find image description record in GIF file." );
414 0 : return CE_Failure;
415 : }
416 :
417 4 : if (DGifGetImageDesc(hGifFile) == GIF_ERROR)
418 : {
419 0 : DGifCloseFile( hGifFile );
420 0 : hGifFile = NULL;
421 :
422 : CPLError( CE_Failure, CPLE_OpenFailed,
423 0 : "Image description reading failed in GIF file." );
424 0 : return CE_Failure;
425 : }
426 :
427 4 : return CE_None;
428 : }
429 :
430 :
431 : /************************************************************************/
432 : /* Open() */
433 : /************************************************************************/
434 :
435 2463 : GDALDataset *BIGGIFDataset::Open( GDALOpenInfo * poOpenInfo )
436 :
437 : {
438 2463 : if( !Identify( poOpenInfo ) )
439 2460 : return NULL;
440 :
441 3 : if( poOpenInfo->eAccess == GA_Update )
442 : {
443 : CPLError( CE_Failure, CPLE_NotSupported,
444 : "The GIF driver does not support update access to existing"
445 0 : " files.\n" );
446 0 : return NULL;
447 : }
448 :
449 : /* -------------------------------------------------------------------- */
450 : /* Open the file. */
451 : /* -------------------------------------------------------------------- */
452 : VSILFILE *fp;
453 :
454 3 : fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
455 3 : if( fp == NULL )
456 0 : return NULL;
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Create a corresponding GDALDataset. */
460 : /* -------------------------------------------------------------------- */
461 : BIGGIFDataset *poDS;
462 :
463 3 : poDS = new BIGGIFDataset();
464 :
465 3 : poDS->fp = fp;
466 3 : poDS->eAccess = GA_ReadOnly;
467 3 : if( poDS->ReOpen() == CE_Failure )
468 : {
469 0 : delete poDS;
470 0 : return NULL;
471 : }
472 :
473 : /* -------------------------------------------------------------------- */
474 : /* Capture some information from the file that is of interest. */
475 : /* -------------------------------------------------------------------- */
476 :
477 3 : poDS->nRasterXSize = poDS->hGifFile->SavedImages[0].ImageDesc.Width;
478 3 : poDS->nRasterYSize = poDS->hGifFile->SavedImages[0].ImageDesc.Height;
479 :
480 : /* -------------------------------------------------------------------- */
481 : /* Create band information objects. */
482 : /* -------------------------------------------------------------------- */
483 : poDS->SetBand( 1,
484 : new BIGGifRasterBand( poDS,
485 3 : poDS->hGifFile->SBackGroundColor ));
486 :
487 : /* -------------------------------------------------------------------- */
488 : /* Check for georeferencing. */
489 : /* -------------------------------------------------------------------- */
490 3 : poDS->DetectGeoreferencing(poOpenInfo);
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Initialize any PAM information. */
494 : /* -------------------------------------------------------------------- */
495 3 : poDS->SetDescription( poOpenInfo->pszFilename );
496 3 : poDS->TryLoadXML();
497 :
498 : /* -------------------------------------------------------------------- */
499 : /* Support overviews. */
500 : /* -------------------------------------------------------------------- */
501 3 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
502 :
503 3 : return poDS;
504 : }
505 :
506 : /************************************************************************/
507 : /* VSIGIFReadFunc() */
508 : /* */
509 : /* Proxy function for reading from GIF file. */
510 : /************************************************************************/
511 :
512 195 : static int VSIGIFReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer,
513 : int nBytesToRead )
514 :
515 : {
516 : return VSIFReadL( pabyBuffer, 1, nBytesToRead,
517 195 : (VSILFILE *) psGFile->UserData );
518 : }
519 :
520 : /************************************************************************/
521 : /* GDALRegister_BIGGIF() */
522 : /************************************************************************/
523 :
524 558 : void GDALRegister_BIGGIF()
525 :
526 : {
527 : GDALDriver *poDriver;
528 :
529 558 : if( GDALGetDriverByName( "BIGGIF" ) == NULL )
530 : {
531 537 : poDriver = new GDALDriver();
532 :
533 537 : poDriver->SetDescription( "BIGGIF" );
534 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
535 537 : "Graphics Interchange Format (.gif)" );
536 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
537 537 : "frmt_gif.html" );
538 537 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" );
539 537 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" );
540 537 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
541 :
542 537 : poDriver->pfnOpen = BIGGIFDataset::Open;
543 537 : poDriver->pfnIdentify = GIFAbstractDataset::Identify;
544 :
545 537 : GetGDALDriverManager()->RegisterDriver( poDriver );
546 : }
547 558 : }
548 :
|