1 : /******************************************************************************
2 : * $Id: gifdataset.cpp 17689 2009-09-25 17:23:37Z rouault $
3 : *
4 : * Project: GIF Driver
5 : * Purpose: Implement GDAL GIF Support using libungif code.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal_pam.h"
31 : #include "cpl_string.h"
32 :
33 : CPL_CVSID("$Id: gifdataset.cpp 17689 2009-09-25 17:23:37Z rouault $");
34 :
35 : CPL_C_START
36 : #include "gif_lib.h"
37 : CPL_C_END
38 :
39 : CPL_C_START
40 : void GDALRegister_GIF(void);
41 :
42 : // This prototype seems to have been messed up!
43 : GifFileType * EGifOpen(void* userData, OutputFunc writeFunc);
44 : CPL_C_END
45 :
46 : static const int InterlacedOffset[] = { 0, 4, 2, 1 };
47 : static const int InterlacedJumps[] = { 8, 8, 4, 2 };
48 :
49 : static int VSIGIFReadFunc( GifFileType *, GifByteType *, int);
50 : static int VSIGIFWriteFunc( GifFileType *, const GifByteType *, int );
51 :
52 : /************************************************************************/
53 : /* ==================================================================== */
54 : /* GIFDataset */
55 : /* ==================================================================== */
56 : /************************************************************************/
57 :
58 : class GIFRasterBand;
59 :
60 : class GIFDataset : public GDALPamDataset
61 : {
62 : friend class GIFRasterBand;
63 :
64 : FILE *fp;
65 :
66 : GifFileType *hGifFile;
67 :
68 : char *pszProjection;
69 : int bGeoTransformValid;
70 : double adfGeoTransform[6];
71 :
72 : int nGCPCount;
73 : GDAL_GCP *pasGCPList;
74 :
75 : public:
76 : GIFDataset();
77 : ~GIFDataset();
78 :
79 : virtual const char *GetProjectionRef();
80 : virtual CPLErr GetGeoTransform( double * );
81 : virtual int GetGCPCount();
82 : virtual const char *GetGCPProjection();
83 : virtual const GDAL_GCP *GetGCPs();
84 : static GDALDataset *Open( GDALOpenInfo * );
85 : static int Identify( GDALOpenInfo * );
86 : };
87 :
88 : /************************************************************************/
89 : /* ==================================================================== */
90 : /* GIFRasterBand */
91 : /* ==================================================================== */
92 : /************************************************************************/
93 :
94 : class GIFRasterBand : public GDALPamRasterBand
95 : {
96 : friend class GIFDataset;
97 :
98 : SavedImage *psImage;
99 :
100 : int *panInterlaceMap;
101 :
102 : GDALColorTable *poColorTable;
103 :
104 : int nTransparentColor;
105 :
106 : public:
107 :
108 : GIFRasterBand( GIFDataset *, int, SavedImage *, int );
109 : virtual ~GIFRasterBand();
110 :
111 : virtual CPLErr IReadBlock( int, int, void * );
112 :
113 : virtual double GetNoDataValue( int *pbSuccess = NULL );
114 : virtual GDALColorInterp GetColorInterpretation();
115 : virtual GDALColorTable *GetColorTable();
116 : };
117 :
118 : /************************************************************************/
119 : /* GIFRasterBand() */
120 : /************************************************************************/
121 :
122 23 : GIFRasterBand::GIFRasterBand( GIFDataset *poDS, int nBand,
123 23 : SavedImage *psSavedImage, int nBackground )
124 :
125 : {
126 23 : this->poDS = poDS;
127 23 : this->nBand = nBand;
128 :
129 23 : eDataType = GDT_Byte;
130 :
131 23 : nBlockXSize = poDS->nRasterXSize;
132 23 : nBlockYSize = 1;
133 :
134 23 : psImage = psSavedImage;
135 :
136 : /* -------------------------------------------------------------------- */
137 : /* Setup interlacing map if required. */
138 : /* -------------------------------------------------------------------- */
139 23 : panInterlaceMap = NULL;
140 23 : if( psImage->ImageDesc.Interlace )
141 : {
142 8 : int i, j, iLine = 0;
143 :
144 8 : panInterlaceMap = (int *) CPLCalloc(poDS->nRasterYSize,sizeof(int));
145 :
146 40 : for (i = 0; i < 4; i++)
147 : {
148 6432 : for (j = InterlacedOffset[i];
149 : j < poDS->nRasterYSize;
150 3200 : j += InterlacedJumps[i])
151 3200 : panInterlaceMap[j] = iLine++;
152 : }
153 : }
154 :
155 : /* -------------------------------------------------------------------- */
156 : /* Check for transparency. We just take the first graphic */
157 : /* control extension block we find, if any. */
158 : /* -------------------------------------------------------------------- */
159 : int iExtBlock;
160 :
161 23 : nTransparentColor = -1;
162 26 : for( iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount; iExtBlock++ )
163 : {
164 : unsigned char *pExtData;
165 :
166 3 : if( psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 )
167 0 : continue;
168 :
169 3 : pExtData = (unsigned char *) psImage->ExtensionBlocks[iExtBlock].Bytes;
170 :
171 : /* check if transparent color flag is set */
172 3 : if( !(pExtData[0] & 0x1) )
173 0 : continue;
174 :
175 3 : nTransparentColor = pExtData[3];
176 : }
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Setup colormap. */
180 : /* -------------------------------------------------------------------- */
181 23 : ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap;
182 23 : if( psGifCT == NULL )
183 23 : psGifCT = poDS->hGifFile->SColorMap;
184 :
185 23 : poColorTable = new GDALColorTable();
186 3143 : for( int iColor = 0; iColor < psGifCT->ColorCount; iColor++ )
187 : {
188 : GDALColorEntry oEntry;
189 :
190 3120 : oEntry.c1 = psGifCT->Colors[iColor].Red;
191 3120 : oEntry.c2 = psGifCT->Colors[iColor].Green;
192 3120 : oEntry.c3 = psGifCT->Colors[iColor].Blue;
193 :
194 3120 : if( iColor == nTransparentColor )
195 3 : oEntry.c4 = 0;
196 : else
197 3117 : oEntry.c4 = 255;
198 :
199 3120 : poColorTable->SetColorEntry( iColor, &oEntry );
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* If we have a background value, return it here. Some */
204 : /* applications might want to treat this as transparent, but in */
205 : /* many uses this is inappropriate so we don't return it as */
206 : /* nodata or transparent. */
207 : /* -------------------------------------------------------------------- */
208 23 : if( nBackground != 255 )
209 : {
210 : char szBackground[10];
211 :
212 13 : sprintf( szBackground, "%d", nBackground );
213 13 : SetMetadataItem( "GIF_BACKGROUND", szBackground );
214 : }
215 23 : }
216 :
217 : /************************************************************************/
218 : /* ~GIFRasterBand() */
219 : /************************************************************************/
220 :
221 46 : GIFRasterBand::~GIFRasterBand()
222 :
223 : {
224 23 : if( poColorTable != NULL )
225 23 : delete poColorTable;
226 :
227 23 : CPLFree( panInterlaceMap );
228 46 : }
229 :
230 : /************************************************************************/
231 : /* IReadBlock() */
232 : /************************************************************************/
233 :
234 3089 : CPLErr GIFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
235 : void * pImage )
236 :
237 : {
238 : CPLAssert( nBlockXOff == 0 );
239 :
240 3089 : if( panInterlaceMap != NULL )
241 1600 : nBlockYOff = panInterlaceMap[nBlockYOff];
242 :
243 : memcpy( pImage, psImage->RasterBits + nBlockYOff * nBlockXSize,
244 3089 : nBlockXSize );
245 :
246 3089 : return CE_None;
247 : }
248 :
249 : /************************************************************************/
250 : /* GetColorInterpretation() */
251 : /************************************************************************/
252 :
253 11 : GDALColorInterp GIFRasterBand::GetColorInterpretation()
254 :
255 : {
256 11 : return GCI_PaletteIndex;
257 : }
258 :
259 : /************************************************************************/
260 : /* GetColorTable() */
261 : /************************************************************************/
262 :
263 1622 : GDALColorTable *GIFRasterBand::GetColorTable()
264 :
265 : {
266 1622 : return poColorTable;
267 : }
268 :
269 : /************************************************************************/
270 : /* GetNoDataValue() */
271 : /************************************************************************/
272 :
273 16 : double GIFRasterBand::GetNoDataValue( int *pbSuccess )
274 :
275 : {
276 16 : if( pbSuccess != NULL )
277 16 : *pbSuccess = nTransparentColor != -1;
278 :
279 16 : return nTransparentColor;
280 : }
281 :
282 : /************************************************************************/
283 : /* ==================================================================== */
284 : /* GIFDataset */
285 : /* ==================================================================== */
286 : /************************************************************************/
287 :
288 :
289 : /************************************************************************/
290 : /* GIFDataset() */
291 : /************************************************************************/
292 :
293 23 : GIFDataset::GIFDataset()
294 :
295 : {
296 23 : hGifFile = NULL;
297 23 : fp = NULL;
298 :
299 23 : pszProjection = NULL;
300 23 : bGeoTransformValid = FALSE;
301 23 : adfGeoTransform[0] = 0.0;
302 23 : adfGeoTransform[1] = 1.0;
303 23 : adfGeoTransform[2] = 0.0;
304 23 : adfGeoTransform[3] = 0.0;
305 23 : adfGeoTransform[4] = 0.0;
306 23 : adfGeoTransform[5] = 1.0;
307 :
308 23 : nGCPCount = 0;
309 23 : pasGCPList = NULL;
310 23 : }
311 :
312 : /************************************************************************/
313 : /* ~GIFDataset() */
314 : /************************************************************************/
315 :
316 46 : GIFDataset::~GIFDataset()
317 :
318 : {
319 23 : FlushCache();
320 :
321 23 : if ( pszProjection )
322 0 : CPLFree( pszProjection );
323 :
324 23 : if ( nGCPCount > 0 )
325 : {
326 0 : GDALDeinitGCPs( nGCPCount, pasGCPList );
327 0 : CPLFree( pasGCPList );
328 : }
329 :
330 23 : if( hGifFile )
331 23 : DGifCloseFile( hGifFile );
332 :
333 23 : if( fp != NULL )
334 23 : VSIFCloseL( fp );
335 46 : }
336 :
337 : /************************************************************************/
338 : /* GetProjectionRef() */
339 : /************************************************************************/
340 :
341 16 : const char *GIFDataset::GetProjectionRef()
342 :
343 : {
344 16 : if ( pszProjection && bGeoTransformValid )
345 0 : return pszProjection;
346 : else
347 16 : return GDALPamDataset::GetProjectionRef();
348 : }
349 :
350 : /************************************************************************/
351 : /* GetGeoTransform() */
352 : /************************************************************************/
353 :
354 21 : CPLErr GIFDataset::GetGeoTransform( double * padfTransform )
355 :
356 : {
357 21 : if( bGeoTransformValid )
358 : {
359 0 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
360 0 : return CE_None;
361 : }
362 : else
363 21 : return GDALPamDataset::GetGeoTransform( padfTransform );
364 : }
365 :
366 : /************************************************************************/
367 : /* GetGCPCount() */
368 : /************************************************************************/
369 :
370 5 : int GIFDataset::GetGCPCount()
371 :
372 : {
373 5 : if (nGCPCount > 0)
374 0 : return nGCPCount;
375 : else
376 5 : return GDALPamDataset::GetGCPCount();
377 : }
378 :
379 : /************************************************************************/
380 : /* GetGCPProjection() */
381 : /************************************************************************/
382 :
383 0 : const char *GIFDataset::GetGCPProjection()
384 :
385 : {
386 0 : if ( pszProjection && nGCPCount > 0 )
387 0 : return pszProjection;
388 : else
389 0 : return GDALPamDataset::GetGCPProjection();
390 : }
391 :
392 : /************************************************************************/
393 : /* GetGCPs() */
394 : /************************************************************************/
395 :
396 0 : const GDAL_GCP *GIFDataset::GetGCPs()
397 :
398 : {
399 0 : if (nGCPCount > 0)
400 0 : return pasGCPList;
401 : else
402 0 : return GDALPamDataset::GetGCPs();
403 : }
404 :
405 : /************************************************************************/
406 : /* Open() */
407 : /************************************************************************/
408 :
409 9381 : int GIFDataset::Identify( GDALOpenInfo * poOpenInfo )
410 :
411 : {
412 9381 : if( poOpenInfo->nHeaderBytes < 8 )
413 8589 : return FALSE;
414 :
415 792 : if( strncmp((const char *) poOpenInfo->pabyHeader, "GIF87a",5) != 0
416 : && strncmp((const char *) poOpenInfo->pabyHeader, "GIF89a",5) != 0 )
417 768 : return FALSE;
418 :
419 24 : return TRUE;
420 : }
421 :
422 : /************************************************************************/
423 : /* Open() */
424 : /************************************************************************/
425 :
426 1658 : GDALDataset *GIFDataset::Open( GDALOpenInfo * poOpenInfo )
427 :
428 : {
429 1658 : if( !Identify( poOpenInfo ) )
430 1634 : return NULL;
431 :
432 24 : if( poOpenInfo->eAccess == GA_Update )
433 : {
434 : CPLError( CE_Failure, CPLE_NotSupported,
435 : "The GIF driver does not support update access to existing"
436 0 : " files.\n" );
437 0 : return NULL;
438 : }
439 :
440 : /* -------------------------------------------------------------------- */
441 : /* Open the file and ingest. */
442 : /* -------------------------------------------------------------------- */
443 : GifFileType *hGifFile;
444 : FILE *fp;
445 : int nGifErr;
446 :
447 24 : fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
448 24 : if( fp == NULL )
449 0 : return NULL;
450 :
451 24 : hGifFile = DGifOpen( fp, VSIGIFReadFunc );
452 24 : if( hGifFile == NULL )
453 : {
454 0 : VSIFCloseL( fp );
455 : CPLError( CE_Failure, CPLE_OpenFailed,
456 : "DGifOpen() failed for %s.\n"
457 : "Perhaps the gif file is corrupt?\n",
458 0 : poOpenInfo->pszFilename );
459 :
460 0 : return NULL;
461 : }
462 :
463 : /* The following code enables us to detect GIF datasets eligible */
464 : /* for BIGGIF driver even with an unpatched giflib */
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* Find the first image record. */
468 : /* -------------------------------------------------------------------- */
469 24 : GifRecordType RecordType = TERMINATE_RECORD_TYPE;
470 :
471 27 : while( DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR
472 : && RecordType != TERMINATE_RECORD_TYPE
473 : && RecordType != IMAGE_DESC_RECORD_TYPE ) {}
474 :
475 24 : if( RecordType == IMAGE_DESC_RECORD_TYPE &&
476 : DGifGetImageDesc(hGifFile) != GIF_ERROR)
477 : {
478 21 : int width = hGifFile->SavedImages[0].ImageDesc.Width;
479 21 : int height = hGifFile->SavedImages[0].ImageDesc.Height;
480 21 : if ((double) width * (double) height > 100000000.0 )
481 : {
482 : CPLDebug( "GIF",
483 : "Due to limitations of the GDAL GIF driver we deliberately avoid\n"
484 1 : "opening large GIF files (larger than 100 megapixels).");
485 1 : DGifCloseFile( hGifFile );
486 1 : VSIFCloseL( fp );
487 1 : return NULL;
488 : }
489 : }
490 :
491 23 : DGifCloseFile( hGifFile );
492 :
493 23 : VSIFSeekL( fp, 0, SEEK_SET);
494 23 : hGifFile = DGifOpen( fp, VSIGIFReadFunc );
495 23 : if( hGifFile == NULL )
496 : {
497 0 : VSIFCloseL( fp );
498 : CPLError( CE_Failure, CPLE_OpenFailed,
499 : "DGifOpen() failed for %s.\n"
500 : "Perhaps the gif file is corrupt?\n",
501 0 : poOpenInfo->pszFilename );
502 :
503 0 : return NULL;
504 : }
505 :
506 23 : nGifErr = DGifSlurp( hGifFile );
507 :
508 23 : if( nGifErr != GIF_OK )
509 : {
510 0 : VSIFCloseL( fp );
511 0 : DGifCloseFile(hGifFile);
512 :
513 0 : if( nGifErr == D_GIF_ERR_DATA_TOO_BIG )
514 : {
515 : CPLDebug( "GIF",
516 : "DGifSlurp() failed for %s because it was too large.\n"
517 : "Due to limitations of the GDAL GIF driver we deliberately avoid\n"
518 : "opening large GIF files (larger than 100 megapixels).",
519 0 : poOpenInfo->pszFilename );
520 0 : return NULL;
521 : }
522 : else
523 : CPLError( CE_Failure, CPLE_OpenFailed,
524 : "DGifSlurp() failed for %s.\n"
525 : "Perhaps the gif file is corrupt?\n",
526 0 : poOpenInfo->pszFilename );
527 :
528 0 : return NULL;
529 : }
530 :
531 : /* -------------------------------------------------------------------- */
532 : /* Create a corresponding GDALDataset. */
533 : /* -------------------------------------------------------------------- */
534 : GIFDataset *poDS;
535 :
536 23 : poDS = new GIFDataset();
537 :
538 23 : poDS->fp = fp;
539 23 : poDS->eAccess = GA_ReadOnly;
540 23 : poDS->hGifFile = hGifFile;
541 :
542 : /* -------------------------------------------------------------------- */
543 : /* Capture some information from the file that is of interest. */
544 : /* -------------------------------------------------------------------- */
545 23 : poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
546 23 : poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Create band information objects. */
550 : /* -------------------------------------------------------------------- */
551 46 : for( int iImage = 0; iImage < hGifFile->ImageCount; iImage++ )
552 : {
553 23 : SavedImage *psImage = hGifFile->SavedImages + iImage;
554 :
555 23 : if( psImage->ImageDesc.Width != poDS->nRasterXSize
556 : || psImage->ImageDesc.Height != poDS->nRasterYSize )
557 0 : continue;
558 :
559 : poDS->SetBand( poDS->nBands+1,
560 : new GIFRasterBand( poDS, poDS->nBands+1, psImage,
561 23 : hGifFile->SBackGroundColor ));
562 : }
563 :
564 : /* -------------------------------------------------------------------- */
565 : /* Check for world file. */
566 : /* -------------------------------------------------------------------- */
567 : poDS->bGeoTransformValid =
568 : GDALReadWorldFile( poOpenInfo->pszFilename, NULL,
569 23 : poDS->adfGeoTransform );
570 23 : if ( !poDS->bGeoTransformValid )
571 : {
572 : poDS->bGeoTransformValid =
573 : GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
574 23 : poDS->adfGeoTransform );
575 :
576 23 : if ( !poDS->bGeoTransformValid )
577 : {
578 : int bOziFileOK =
579 : GDALReadOziMapFile( poOpenInfo->pszFilename,
580 : poDS->adfGeoTransform,
581 : &poDS->pszProjection,
582 23 : &poDS->nGCPCount, &poDS->pasGCPList );
583 :
584 23 : if ( bOziFileOK && poDS->nGCPCount == 0 )
585 0 : poDS->bGeoTransformValid = TRUE;
586 : }
587 : }
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Initialize any PAM information. */
591 : /* -------------------------------------------------------------------- */
592 23 : poDS->SetDescription( poOpenInfo->pszFilename );
593 23 : poDS->TryLoadXML();
594 :
595 : /* -------------------------------------------------------------------- */
596 : /* Support overviews. */
597 : /* -------------------------------------------------------------------- */
598 23 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
599 :
600 23 : return poDS;
601 : }
602 :
603 : /************************************************************************/
604 : /* GIFCreateCopy() */
605 : /************************************************************************/
606 :
607 : static GDALDataset *
608 19 : GIFCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
609 : int bStrict, char ** papszOptions,
610 : GDALProgressFunc pfnProgress, void * pProgressData )
611 :
612 : {
613 19 : int nBands = poSrcDS->GetRasterCount();
614 19 : int nXSize = poSrcDS->GetRasterXSize();
615 19 : int nYSize = poSrcDS->GetRasterYSize();
616 19 : int bInterlace = FALSE;
617 :
618 : /* -------------------------------------------------------------------- */
619 : /* Check for interlaced option. */
620 : /* -------------------------------------------------------------------- */
621 19 : bInterlace = CSLFetchBoolean(papszOptions, "INTERLACING", FALSE);
622 :
623 : /* -------------------------------------------------------------------- */
624 : /* Some some rudimentary checks */
625 : /* -------------------------------------------------------------------- */
626 19 : if( nBands != 1 )
627 : {
628 : CPLError( CE_Failure, CPLE_NotSupported,
629 5 : "GIF driver only supports one band images.\n" );
630 :
631 5 : return NULL;
632 : }
633 :
634 14 : if (nXSize > 65535 || nYSize > 65535)
635 : {
636 : CPLError( CE_Failure, CPLE_NotSupported,
637 0 : "GIF driver only supports datasets up to 65535x65535 size.\n" );
638 :
639 0 : return NULL;
640 : }
641 :
642 14 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
643 : && bStrict )
644 : {
645 : CPLError( CE_Failure, CPLE_NotSupported,
646 : "GIF driver doesn't support data type %s. "
647 : "Only eight bit bands supported.\n",
648 : GDALGetDataTypeName(
649 10 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
650 :
651 10 : return NULL;
652 : }
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Open the output file. */
656 : /* -------------------------------------------------------------------- */
657 : GifFileType *hGifFile;
658 : FILE *fp;
659 :
660 4 : fp = VSIFOpenL( pszFilename, "w" );
661 4 : if( fp == NULL )
662 : {
663 : CPLError( CE_Failure, CPLE_OpenFailed,
664 : "Failed to create %s:\n%s",
665 0 : pszFilename, VSIStrerror( errno ) );
666 0 : return NULL;
667 : }
668 :
669 4 : hGifFile = EGifOpen( fp, VSIGIFWriteFunc );
670 4 : if( hGifFile == NULL )
671 : {
672 0 : VSIFCloseL( fp );
673 : CPLError( CE_Failure, CPLE_OpenFailed,
674 : "EGifOpenFilename(%s) failed. Does file already exist?",
675 0 : pszFilename );
676 :
677 0 : return NULL;
678 : }
679 :
680 : /* -------------------------------------------------------------------- */
681 : /* Prepare colortable. */
682 : /* -------------------------------------------------------------------- */
683 4 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
684 : ColorMapObject *psGifCT;
685 : int iColor;
686 :
687 4 : if( poBand->GetColorTable() == NULL )
688 : {
689 3 : psGifCT = MakeMapObject( 256, NULL );
690 771 : for( iColor = 0; iColor < 256; iColor++ )
691 : {
692 768 : psGifCT->Colors[iColor].Red = (GifByteType) iColor;
693 768 : psGifCT->Colors[iColor].Green = (GifByteType) iColor;
694 768 : psGifCT->Colors[iColor].Blue = (GifByteType) iColor;
695 : }
696 : }
697 : else
698 : {
699 1 : GDALColorTable *poCT = poBand->GetColorTable();
700 1 : int nFullCount = 1;
701 :
702 6 : while( nFullCount < poCT->GetColorEntryCount() )
703 4 : nFullCount = nFullCount * 2;
704 :
705 1 : psGifCT = MakeMapObject( nFullCount, NULL );
706 17 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
707 : {
708 : GDALColorEntry sEntry;
709 :
710 16 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
711 16 : psGifCT->Colors[iColor].Red = (GifByteType) sEntry.c1;
712 16 : psGifCT->Colors[iColor].Green = (GifByteType) sEntry.c2;
713 16 : psGifCT->Colors[iColor].Blue = (GifByteType) sEntry.c3;
714 : }
715 1 : for( ; iColor < nFullCount; iColor++ )
716 : {
717 0 : psGifCT->Colors[iColor].Red = 0;
718 0 : psGifCT->Colors[iColor].Green = 0;
719 0 : psGifCT->Colors[iColor].Blue = 0;
720 : }
721 : }
722 :
723 : /* -------------------------------------------------------------------- */
724 : /* Setup parameters. */
725 : /* -------------------------------------------------------------------- */
726 4 : if (EGifPutScreenDesc(hGifFile, nXSize, nYSize,
727 : psGifCT->ColorCount, 255, psGifCT) == GIF_ERROR)
728 : {
729 0 : FreeMapObject(psGifCT);
730 0 : PrintGifError();
731 : CPLError( CE_Failure, CPLE_AppDefined,
732 0 : "Error writing gif file." );
733 0 : EGifCloseFile(hGifFile);
734 0 : VSIFCloseL( fp );
735 0 : return NULL;
736 : }
737 :
738 4 : FreeMapObject(psGifCT);
739 4 : psGifCT = NULL;
740 :
741 : /* Support for transparency */
742 : int bNoDataValue;
743 4 : double noDataValue = poBand->GetNoDataValue(&bNoDataValue);
744 4 : if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255)
745 : {
746 : unsigned char extensionData[4];
747 1 : extensionData[0] = 1; /* Transparent Color Flag */
748 1 : extensionData[1] = 0;
749 1 : extensionData[2] = 0;
750 1 : extensionData[3] = (unsigned char)noDataValue;
751 1 : EGifPutExtension(hGifFile, 0xf9, 4, extensionData);
752 : }
753 :
754 4 : if (EGifPutImageDesc(hGifFile, 0, 0, nXSize, nYSize, bInterlace, NULL) == GIF_ERROR )
755 : {
756 0 : PrintGifError();
757 : CPLError( CE_Failure, CPLE_AppDefined,
758 0 : "Error writing gif file." );
759 0 : EGifCloseFile(hGifFile);
760 0 : VSIFCloseL( fp );
761 0 : return NULL;
762 : }
763 :
764 : /* -------------------------------------------------------------------- */
765 : /* Loop over image, copying image data. */
766 : /* -------------------------------------------------------------------- */
767 : CPLErr eErr;
768 : GDALPamDataset *poDS;
769 : GByte *pabyScanline;
770 :
771 4 : pabyScanline = (GByte *) CPLMalloc( nXSize );
772 :
773 4 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
774 0 : eErr = CE_Failure;
775 :
776 4 : if( !bInterlace )
777 : {
778 454 : for( int iLine = 0; iLine < nYSize; iLine++ )
779 : {
780 : eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
781 : pabyScanline, nXSize, 1, GDT_Byte,
782 450 : nBands, nBands * nXSize );
783 :
784 450 : if( eErr != CE_None || EGifPutLine( hGifFile, pabyScanline, nXSize ) == GIF_ERROR )
785 : {
786 : CPLError( CE_Failure, CPLE_AppDefined,
787 0 : "Error writing gif file." );
788 0 : goto error;
789 : }
790 :
791 450 : if( !pfnProgress( (iLine + 1) * 1.0 / nYSize, NULL, pProgressData ) )
792 : {
793 0 : goto error;
794 : }
795 :
796 : }
797 : }
798 : else
799 : {
800 : int i, j;
801 0 : int nLinesRead = 0;
802 0 : int nLinesToRead = 0;
803 0 : for ( i = 0; i < 4; i++)
804 : {
805 0 : for (j = InterlacedOffset[i]; j < nYSize; j += InterlacedJumps[i])
806 : {
807 0 : nLinesToRead ++;
808 : }
809 : }
810 :
811 : /* Need to perform 4 passes on the images: */
812 0 : for ( i = 0; i < 4; i++)
813 : {
814 0 : for (j = InterlacedOffset[i]; j < nYSize; j += InterlacedJumps[i])
815 : {
816 : eErr= poBand->RasterIO( GF_Read, 0, j, nXSize, 1,
817 : pabyScanline, nXSize, 1, GDT_Byte,
818 0 : 1, nXSize );
819 :
820 0 : if (eErr != CE_None || EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR)
821 : {
822 : CPLError( CE_Failure, CPLE_AppDefined,
823 0 : "Error writing gif file." );
824 0 : goto error;
825 : }
826 :
827 0 : nLinesRead ++;
828 0 : if( !pfnProgress( nLinesRead * 1.0 / nYSize, NULL, pProgressData ) )
829 : {
830 0 : goto error;
831 : }
832 : }
833 : }
834 : }
835 :
836 4 : CPLFree( pabyScanline );
837 4 : pabyScanline = NULL;
838 :
839 : /* -------------------------------------------------------------------- */
840 : /* cleanup */
841 : /* -------------------------------------------------------------------- */
842 4 : if (EGifCloseFile(hGifFile) == GIF_ERROR)
843 : {
844 : CPLError( CE_Failure, CPLE_AppDefined,
845 0 : "EGifCloseFile() failed.\n" );
846 0 : hGifFile = NULL;
847 0 : goto error;
848 : }
849 4 : hGifFile = NULL;
850 :
851 : /* This is a hack to write a GIF89a instead of GIF87a */
852 : /* (we have to, since we are using graphical extension block) */
853 : /* EGifSpew would write GIF89a when it detects an extension block if we were using it */
854 : /* As we don't, we could have used EGifSetGifVersion instead, but the version of libungif */
855 : /* in GDAL has a bug : it writes on read-only memory ! */
856 : /* (this is a well-known problem. Just google for "EGifSetGifVersion segfault") */
857 : /* Most readers don't even care if it is GIF87a or GIF89a, but it is */
858 : /* better to write the right version */
859 :
860 4 : VSIFSeekL(fp, 0, SEEK_SET);
861 4 : if (VSIFWriteL("GIF89a", 1, 6, fp) != 6)
862 : {
863 : CPLError( CE_Failure, CPLE_AppDefined,
864 0 : "Error writing gif file." );
865 0 : goto error;
866 : }
867 :
868 4 : VSIFCloseL( fp );
869 4 : fp = NULL;
870 :
871 : /* -------------------------------------------------------------------- */
872 : /* Do we need a world file? */
873 : /* -------------------------------------------------------------------- */
874 4 : if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
875 : {
876 : double adfGeoTransform[6];
877 :
878 0 : if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
879 0 : GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform );
880 : }
881 :
882 : /* -------------------------------------------------------------------- */
883 : /* Re-open dataset, and copy any auxilary pam information. */
884 : /* -------------------------------------------------------------------- */
885 : poDS = (GDALPamDataset *)
886 4 : GDALOpen( pszFilename, GA_ReadOnly );
887 :
888 4 : if( poDS )
889 4 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
890 :
891 4 : return poDS;
892 :
893 : error:
894 0 : if (hGifFile)
895 0 : EGifCloseFile(hGifFile);
896 0 : if (fp)
897 0 : VSIFCloseL( fp );
898 0 : if (pabyScanline)
899 0 : CPLFree( pabyScanline );
900 0 : return NULL;
901 : }
902 :
903 : /************************************************************************/
904 : /* VSIGIFReadFunc() */
905 : /* */
906 : /* Proxy function for reading from GIF file. */
907 : /************************************************************************/
908 :
909 8513 : static int VSIGIFReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer,
910 : int nBytesToRead )
911 :
912 : {
913 : return VSIFReadL( pabyBuffer, 1, nBytesToRead,
914 8513 : (FILE *) psGFile->UserData );
915 : }
916 :
917 : /************************************************************************/
918 : /* VSIGIFWriteFunc() */
919 : /* */
920 : /* Proxy write function. */
921 : /************************************************************************/
922 :
923 872 : static int VSIGIFWriteFunc( GifFileType *psGFile,
924 : const GifByteType *pabyBuffer, int nBytesToWrite )
925 :
926 : {
927 : return VSIFWriteL( (void *) pabyBuffer, 1, nBytesToWrite,
928 872 : (FILE *) psGFile->UserData );
929 : }
930 :
931 : /************************************************************************/
932 : /* GDALRegister_GIF() */
933 : /************************************************************************/
934 :
935 338 : void GDALRegister_GIF()
936 :
937 : {
938 : GDALDriver *poDriver;
939 :
940 338 : if( GDALGetDriverByName( "GIF" ) == NULL )
941 : {
942 336 : poDriver = new GDALDriver();
943 :
944 336 : poDriver->SetDescription( "GIF" );
945 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
946 336 : "Graphics Interchange Format (.gif)" );
947 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
948 336 : "frmt_gif.html" );
949 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" );
950 336 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" );
951 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
952 336 : "Byte" );
953 :
954 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
955 : "<CreationOptionList>\n"
956 : " <Option name='INTERLACING' type='boolean'/>\n"
957 : " <Option name='WORLDFILE' type='boolean'/>\n"
958 336 : "</CreationOptionList>\n" );
959 :
960 336 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
961 :
962 336 : poDriver->pfnOpen = GIFDataset::Open;
963 336 : poDriver->pfnCreateCopy = GIFCreateCopy;
964 336 : poDriver->pfnIdentify = GIFDataset::Identify;
965 :
966 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
967 : }
968 338 : }
969 :
|