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