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