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