1 : /******************************************************************************
2 : * $Id: gifdataset.cpp 23621 2011-12-20 23:26:56Z 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 23621 2011-12-20 23:26:56Z 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 58 : {
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 58 : GIFRasterBand::GIFRasterBand( GIFDataset *poDS, int nBand,
109 58 : SavedImage *psSavedImage, int nBackground )
110 :
111 : {
112 58 : this->poDS = poDS;
113 58 : this->nBand = nBand;
114 :
115 58 : eDataType = GDT_Byte;
116 :
117 58 : nBlockXSize = poDS->nRasterXSize;
118 58 : nBlockYSize = 1;
119 :
120 58 : psImage = psSavedImage;
121 :
122 58 : poColorTable = NULL;
123 58 : panInterlaceMap = NULL;
124 58 : nTransparentColor = 0;
125 :
126 58 : if (psImage == NULL)
127 2 : return;
128 :
129 : /* -------------------------------------------------------------------- */
130 : /* Setup interlacing map if required. */
131 : /* -------------------------------------------------------------------- */
132 56 : panInterlaceMap = NULL;
133 56 : if( psImage->ImageDesc.Interlace )
134 : {
135 22 : int i, j, iLine = 0;
136 :
137 22 : panInterlaceMap = (int *) CPLCalloc(poDS->nRasterYSize,sizeof(int));
138 :
139 110 : for (i = 0; i < 4; i++)
140 : {
141 17688 : for (j = InterlacedOffset[i];
142 : j < poDS->nRasterYSize;
143 8800 : j += InterlacedJumps[i])
144 8800 : 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 56 : nTransparentColor = -1;
155 240 : for( iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount; iExtBlock++ )
156 : {
157 : unsigned char *pExtData;
158 :
159 184 : if( psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 )
160 178 : continue;
161 :
162 6 : pExtData = (unsigned char *) psImage->ExtensionBlocks[iExtBlock].Bytes;
163 :
164 : /* check if transparent color flag is set */
165 6 : if( !(pExtData[0] & 0x1) )
166 0 : continue;
167 :
168 6 : nTransparentColor = pExtData[3];
169 : }
170 :
171 : /* -------------------------------------------------------------------- */
172 : /* Setup colormap. */
173 : /* -------------------------------------------------------------------- */
174 56 : ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap;
175 56 : if( psGifCT == NULL )
176 56 : psGifCT = poDS->hGifFile->SColorMap;
177 :
178 56 : poColorTable = new GDALColorTable();
179 7416 : for( int iColor = 0; iColor < psGifCT->ColorCount; iColor++ )
180 : {
181 : GDALColorEntry oEntry;
182 :
183 7360 : oEntry.c1 = psGifCT->Colors[iColor].Red;
184 7360 : oEntry.c2 = psGifCT->Colors[iColor].Green;
185 7360 : oEntry.c3 = psGifCT->Colors[iColor].Blue;
186 :
187 7360 : if( iColor == nTransparentColor )
188 6 : oEntry.c4 = 0;
189 : else
190 7354 : oEntry.c4 = 255;
191 :
192 7360 : 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 56 : if( nBackground != 255 )
202 : {
203 : char szBackground[10];
204 :
205 32 : sprintf( szBackground, "%d", nBackground );
206 32 : SetMetadataItem( "GIF_BACKGROUND", szBackground );
207 : }
208 0 : }
209 :
210 : /************************************************************************/
211 : /* ~GIFRasterBand() */
212 : /************************************************************************/
213 :
214 58 : GIFRasterBand::~GIFRasterBand()
215 :
216 : {
217 58 : if( poColorTable != NULL )
218 56 : delete poColorTable;
219 :
220 58 : CPLFree( panInterlaceMap );
221 58 : }
222 :
223 : /************************************************************************/
224 : /* IReadBlock() */
225 : /************************************************************************/
226 :
227 7058 : CPLErr GIFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
228 : void * pImage )
229 :
230 : {
231 7058 : CPLAssert( nBlockXOff == 0 );
232 :
233 7058 : if (psImage == NULL)
234 : {
235 40 : memset(pImage, 0, nBlockXSize);
236 40 : return CE_None;
237 : }
238 :
239 7018 : if( panInterlaceMap != NULL )
240 4000 : nBlockYOff = panInterlaceMap[nBlockYOff];
241 :
242 : memcpy( pImage, psImage->RasterBits + nBlockYOff * nBlockXSize,
243 7018 : nBlockXSize );
244 :
245 7018 : return CE_None;
246 : }
247 :
248 : /************************************************************************/
249 : /* GetColorInterpretation() */
250 : /************************************************************************/
251 :
252 30 : GDALColorInterp GIFRasterBand::GetColorInterpretation()
253 :
254 : {
255 30 : return GCI_PaletteIndex;
256 : }
257 :
258 : /************************************************************************/
259 : /* GetColorTable() */
260 : /************************************************************************/
261 :
262 3242 : GDALColorTable *GIFRasterBand::GetColorTable()
263 :
264 : {
265 3242 : return poColorTable;
266 : }
267 :
268 : /************************************************************************/
269 : /* GetNoDataValue() */
270 : /************************************************************************/
271 :
272 46 : double GIFRasterBand::GetNoDataValue( int *pbSuccess )
273 :
274 : {
275 46 : if( pbSuccess != NULL )
276 46 : *pbSuccess = nTransparentColor != -1;
277 :
278 46 : return nTransparentColor;
279 : }
280 :
281 : /************************************************************************/
282 : /* ==================================================================== */
283 : /* GIFDataset */
284 : /* ==================================================================== */
285 : /************************************************************************/
286 :
287 :
288 : /************************************************************************/
289 : /* GIFDataset() */
290 : /************************************************************************/
291 :
292 58 : GIFDataset::GIFDataset()
293 : {
294 58 : }
295 :
296 : /************************************************************************/
297 : /* Open() */
298 : /************************************************************************/
299 :
300 5108 : GDALDataset *GIFDataset::Open( GDALOpenInfo * poOpenInfo )
301 :
302 : {
303 5108 : if( !Identify( poOpenInfo ) )
304 5050 : return NULL;
305 :
306 58 : 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 58 : fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
322 58 : if( fp == NULL )
323 0 : return NULL;
324 :
325 58 : hGifFile = DGifOpen( fp, VSIGIFReadFunc );
326 58 : 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 58 : GifRecordType RecordType = TERMINATE_RECORD_TYPE;
344 :
345 124 : 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 8 : if (RecordType == EXTENSION_RECORD_TYPE)
351 : {
352 : int nFunction;
353 : GifByteType *pExtData;
354 8 : if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR)
355 0 : break;
356 200 : while (pExtData != NULL)
357 : {
358 184 : if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR)
359 0 : break;
360 : }
361 : }
362 : }
363 :
364 58 : if( RecordType == IMAGE_DESC_RECORD_TYPE &&
365 : DGifGetImageDesc(hGifFile) != GIF_ERROR)
366 : {
367 58 : int width = hGifFile->SavedImages[0].ImageDesc.Width;
368 58 : int height = hGifFile->SavedImages[0].ImageDesc.Height;
369 58 : 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 2 : "opening large GIF files (larger than 100 megapixels).");
374 2 : DGifCloseFile( hGifFile );
375 2 : VSIFCloseL( fp );
376 2 : return NULL;
377 : }
378 : }
379 :
380 56 : DGifCloseFile( hGifFile );
381 :
382 56 : VSIFSeekL( fp, 0, SEEK_SET);
383 56 : hGifFile = DGifOpen( fp, VSIGIFReadFunc );
384 56 : 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 56 : nGifErr = DGifSlurp( hGifFile );
396 :
397 56 : if( nGifErr != GIF_OK || hGifFile->SavedImages == NULL )
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 56 : poDS = new GIFDataset();
426 :
427 56 : poDS->fp = fp;
428 56 : poDS->eAccess = GA_ReadOnly;
429 56 : poDS->hGifFile = hGifFile;
430 :
431 : /* -------------------------------------------------------------------- */
432 : /* Capture some information from the file that is of interest. */
433 : /* -------------------------------------------------------------------- */
434 56 : poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
435 56 : poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
436 :
437 : /* -------------------------------------------------------------------- */
438 : /* Create band information objects. */
439 : /* -------------------------------------------------------------------- */
440 112 : for( int iImage = 0; iImage < hGifFile->ImageCount; iImage++ )
441 : {
442 56 : SavedImage *psImage = hGifFile->SavedImages + iImage;
443 :
444 56 : 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 56 : hGifFile->SBackGroundColor ));
451 : }
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Check for georeferencing. */
455 : /* -------------------------------------------------------------------- */
456 56 : poDS->DetectGeoreferencing(poOpenInfo);
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Initialize any PAM information. */
460 : /* -------------------------------------------------------------------- */
461 56 : poDS->SetDescription( poOpenInfo->pszFilename );
462 56 : poDS->TryLoadXML();
463 :
464 : /* -------------------------------------------------------------------- */
465 : /* Support overviews. */
466 : /* -------------------------------------------------------------------- */
467 56 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
468 :
469 56 : return poDS;
470 : }
471 :
472 : /************************************************************************/
473 : /* CreateCopy() */
474 : /************************************************************************/
475 :
476 : GDALDataset *
477 44 : GIFDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
478 : int bStrict, char ** papszOptions,
479 : GDALProgressFunc pfnProgress, void * pProgressData )
480 :
481 : {
482 44 : int nBands = poSrcDS->GetRasterCount();
483 44 : int nXSize = poSrcDS->GetRasterXSize();
484 44 : int nYSize = poSrcDS->GetRasterYSize();
485 44 : int bInterlace = FALSE;
486 :
487 : /* -------------------------------------------------------------------- */
488 : /* Check for interlaced option. */
489 : /* -------------------------------------------------------------------- */
490 44 : bInterlace = CSLFetchBoolean(papszOptions, "INTERLACING", FALSE);
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Some some rudimentary checks */
494 : /* -------------------------------------------------------------------- */
495 44 : if( nBands != 1 )
496 : {
497 : CPLError( CE_Failure, CPLE_NotSupported,
498 10 : "GIF driver only supports one band images.\n" );
499 :
500 10 : return NULL;
501 : }
502 :
503 34 : 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 34 : 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 20 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
519 :
520 20 : return NULL;
521 : }
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Open the output file. */
525 : /* -------------------------------------------------------------------- */
526 : GifFileType *hGifFile;
527 : VSILFILE *fp;
528 :
529 14 : fp = VSIFOpenL( pszFilename, "wb" );
530 14 : if( fp == NULL )
531 : {
532 : CPLError( CE_Failure, CPLE_OpenFailed,
533 : "Failed to create %s:\n%s",
534 4 : pszFilename, VSIStrerror( errno ) );
535 4 : return NULL;
536 : }
537 :
538 10 : hGifFile = EGifOpen( fp, VSIGIFWriteFunc );
539 10 : 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 10 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
553 : ColorMapObject *psGifCT;
554 : int iColor;
555 :
556 10 : if( poBand->GetColorTable() == NULL )
557 : {
558 8 : psGifCT = MakeMapObject( 256, NULL );
559 2056 : for( iColor = 0; iColor < 256; iColor++ )
560 : {
561 2048 : psGifCT->Colors[iColor].Red = (GifByteType) iColor;
562 2048 : psGifCT->Colors[iColor].Green = (GifByteType) iColor;
563 2048 : psGifCT->Colors[iColor].Blue = (GifByteType) iColor;
564 : }
565 : }
566 : else
567 : {
568 2 : GDALColorTable *poCT = poBand->GetColorTable();
569 2 : int nFullCount = 1;
570 :
571 12 : while( nFullCount < poCT->GetColorEntryCount() )
572 8 : nFullCount = nFullCount * 2;
573 :
574 2 : psGifCT = MakeMapObject( nFullCount, NULL );
575 34 : for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ )
576 : {
577 : GDALColorEntry sEntry;
578 :
579 32 : poCT->GetColorEntryAsRGB( iColor, &sEntry );
580 32 : psGifCT->Colors[iColor].Red = (GifByteType) sEntry.c1;
581 32 : psGifCT->Colors[iColor].Green = (GifByteType) sEntry.c2;
582 32 : psGifCT->Colors[iColor].Blue = (GifByteType) sEntry.c3;
583 : }
584 2 : 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 10 : 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 10 : FreeMapObject(psGifCT);
608 10 : psGifCT = NULL;
609 :
610 : /* Support for transparency */
611 : int bNoDataValue;
612 10 : double noDataValue = poBand->GetNoDataValue(&bNoDataValue);
613 10 : if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255)
614 : {
615 : unsigned char extensionData[4];
616 2 : extensionData[0] = 1; /* Transparent Color Flag */
617 2 : extensionData[1] = 0;
618 2 : extensionData[2] = 0;
619 2 : extensionData[3] = (unsigned char)noDataValue;
620 2 : EGifPutExtension(hGifFile, 0xf9, 4, extensionData);
621 : }
622 :
623 10 : 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 10 : pabyScanline = (GByte *) CPLMalloc( nXSize );
641 :
642 10 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
643 0 : eErr = CE_Failure;
644 :
645 10 : if( !bInterlace )
646 : {
647 950 : 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 940 : nBands, nBands * nXSize );
652 :
653 940 : 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 940 : 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 10 : CPLFree( pabyScanline );
706 10 : pabyScanline = NULL;
707 :
708 : /* -------------------------------------------------------------------- */
709 : /* cleanup */
710 : /* -------------------------------------------------------------------- */
711 10 : 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 10 : hGifFile = NULL;
719 :
720 10 : VSIFCloseL( fp );
721 10 : fp = NULL;
722 :
723 : /* -------------------------------------------------------------------- */
724 : /* Do we need a world file? */
725 : /* -------------------------------------------------------------------- */
726 10 : 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 10 : CPLPushErrorHandler(CPLQuietErrorHandler);
741 10 : poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly);
742 10 : CPLPopErrorHandler();
743 10 : if (poDS)
744 : {
745 8 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
746 8 : return poDS;
747 : }
748 : else
749 : {
750 2 : CPLErrorReset();
751 :
752 2 : GIFDataset* poGIF_DS = new GIFDataset();
753 2 : poGIF_DS->nRasterXSize = nXSize;
754 2 : poGIF_DS->nRasterYSize = nYSize;
755 8 : for(int i=0;i<nBands;i++)
756 2 : poGIF_DS->SetBand( i+1, new GIFRasterBand( poGIF_DS, i+1, NULL, 0 ) );
757 2 : 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 20642 : static int VSIGIFReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer,
777 : int nBytesToRead )
778 :
779 : {
780 : return VSIFReadL( pabyBuffer, 1, nBytesToRead,
781 20642 : (VSILFILE *) psGFile->UserData );
782 : }
783 :
784 : /************************************************************************/
785 : /* VSIGIFWriteFunc() */
786 : /* */
787 : /* Proxy write function. */
788 : /************************************************************************/
789 :
790 2286 : static int VSIGIFWriteFunc( GifFileType *psGFile,
791 : const GifByteType *pabyBuffer, int nBytesToWrite )
792 :
793 : {
794 2286 : VSILFILE* fp = (VSILFILE *) psGFile->UserData;
795 2286 : 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 10 : int nRet = VSIFWriteL("GIF89a", 1, 6, fp);
808 10 : nRet += VSIFWriteL( (char *) pabyBuffer + 6, 1, nBytesToWrite - 6, fp );
809 10 : return nRet;
810 : }
811 : else
812 2276 : return VSIFWriteL( (void *) pabyBuffer, 1, nBytesToWrite, fp );
813 : }
814 :
815 : /************************************************************************/
816 : /* GDALRegister_GIF() */
817 : /************************************************************************/
818 :
819 1135 : void GDALRegister_GIF()
820 :
821 : {
822 : GDALDriver *poDriver;
823 :
824 1135 : if( GDALGetDriverByName( "GIF" ) == NULL )
825 : {
826 1093 : poDriver = new GDALDriver();
827 :
828 1093 : poDriver->SetDescription( "GIF" );
829 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
830 1093 : "Graphics Interchange Format (.gif)" );
831 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
832 1093 : "frmt_gif.html" );
833 1093 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" );
834 1093 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" );
835 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
836 1093 : "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 1093 : "</CreationOptionList>\n" );
843 :
844 1093 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
845 :
846 1093 : poDriver->pfnOpen = GIFDataset::Open;
847 1093 : poDriver->pfnCreateCopy = GIFDataset::CreateCopy;
848 1093 : poDriver->pfnIdentify = GIFAbstractDataset::Identify;
849 :
850 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
851 : }
852 1135 : }
853 :
|