Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GIF Driver
4 : * Purpose: Implement GDAL GIF Support using libungif code.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam
9 : * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gifabstractdataset.h"
15 :
16 : #include "cpl_string.h"
17 : #include "gdal_frmts.h"
18 : #include "gdal_pam.h"
19 : #include "gifdrivercore.h"
20 :
21 : CPL_C_START
22 : #if !(defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5)
23 :
24 : // This prototype seems to have been messed up!
25 : GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
26 :
27 : // Define alias compatible with giflib >= 5.0.0
28 : #define GifMakeMapObject MakeMapObject
29 : #define GifFreeMapObject FreeMapObject
30 :
31 : #endif // defined(GIFLIB_MAJOR) && GIFLIB_MAJOR < 5
32 :
33 : CPL_C_END
34 :
35 : /************************************************************************/
36 : /* VSIGIFWriteFunc() */
37 : /* */
38 : /* Proxy write function. */
39 : /************************************************************************/
40 :
41 1414 : static int VSIGIFWriteFunc(GifFileType *psGFile, const GifByteType *pabyBuffer,
42 : int nBytesToWrite)
43 :
44 : {
45 1414 : VSILFILE *fp = static_cast<VSILFILE *>(psGFile->UserData);
46 1420 : if (VSIFTellL(fp) == 0 && nBytesToWrite >= 6 &&
47 6 : memcmp(pabyBuffer, "GIF87a", 6) == 0)
48 : {
49 : // This is a hack to write a GIF89a instead of GIF87a (we have to, since
50 : // we are using graphical extension block). EGifSpew would write GIF89a
51 : // when it detects an extension block if we were using it As we don't,
52 : // we could have used EGifSetGifVersion instead, but the version of
53 : // libungif in GDAL has a bug: it writes on read-only memory!
54 : // This is a well-known problem. Just google for "EGifSetGifVersion
55 : // segfault".
56 : // Most readers don't even care if it is GIF87a or GIF89a, but it is
57 : // better to write the right version.
58 :
59 6 : size_t nRet = VSIFWriteL("GIF89a", 1, 6, fp);
60 12 : nRet += VSIFWriteL(reinterpret_cast<const char *>(pabyBuffer) + 6, 1,
61 6 : nBytesToWrite - 6, fp);
62 6 : return static_cast<int>(nRet);
63 : }
64 :
65 1408 : return static_cast<int>(VSIFWriteL(pabyBuffer, 1, nBytesToWrite, fp));
66 : }
67 :
68 : /************************************************************************/
69 : /* ==================================================================== */
70 : /* GIFDataset */
71 : /* ==================================================================== */
72 : /************************************************************************/
73 :
74 : class GIFRasterBand;
75 :
76 60 : class GIFDataset final : public GIFAbstractDataset
77 : {
78 : friend class GIFRasterBand;
79 :
80 : public:
81 : GIFDataset();
82 : ~GIFDataset() override;
83 :
84 : static GDALDataset *Open(GDALOpenInfo *);
85 : static GDALPamDataset *OpenPAM(GDALOpenInfo *);
86 :
87 : static GDALDataset *CreateCopy(const char *pszFilename,
88 : GDALDataset *poSrcDS, int bStrict,
89 : CSLConstList papszOptions,
90 : GDALProgressFunc pfnProgress,
91 : void *pProgressData);
92 : };
93 :
94 : GIFDataset::~GIFDataset() = default;
95 :
96 : /************************************************************************/
97 : /* ==================================================================== */
98 : /* GIFRasterBand */
99 : /* ==================================================================== */
100 : /************************************************************************/
101 :
102 : class GIFRasterBand final : public GIFAbstractRasterBand
103 : {
104 : public:
105 : GIFRasterBand(GIFDataset *, int, SavedImage *, int);
106 : CPLErr IReadBlock(int, int, void *) override;
107 : };
108 :
109 : /************************************************************************/
110 : /* GIFRasterBand() */
111 : /************************************************************************/
112 :
113 30 : GIFRasterBand::GIFRasterBand(GIFDataset *poDSIn, int nBandIn,
114 30 : SavedImage *psSavedImage, int nBackground)
115 30 : : GIFAbstractRasterBand(poDSIn, nBandIn, psSavedImage, nBackground, FALSE)
116 : {
117 30 : }
118 :
119 : /************************************************************************/
120 : /* IReadBlock() */
121 : /************************************************************************/
122 :
123 2960 : CPLErr GIFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
124 : void *pImage)
125 : {
126 2960 : CPLAssert(nBlockXOff == 0);
127 :
128 2960 : if (psImage == nullptr)
129 : {
130 20 : memset(pImage, 0, nBlockXSize);
131 20 : return CE_None;
132 : }
133 :
134 2940 : if (panInterlaceMap != nullptr)
135 0 : nBlockYOff = panInterlaceMap[nBlockYOff];
136 :
137 2940 : memcpy(pImage, psImage->RasterBits + nBlockYOff * nBlockXSize, nBlockXSize);
138 :
139 2940 : return CE_None;
140 : }
141 :
142 : /************************************************************************/
143 : /* ==================================================================== */
144 : /* GIFDataset */
145 : /* ==================================================================== */
146 : /************************************************************************/
147 :
148 : /************************************************************************/
149 : /* GIFDataset() */
150 : /************************************************************************/
151 :
152 30 : GIFDataset::GIFDataset()
153 : {
154 30 : }
155 :
156 : /************************************************************************/
157 : /* Open() */
158 : /************************************************************************/
159 :
160 26 : GDALDataset *GIFDataset::Open(GDALOpenInfo *poOpenInfo)
161 : {
162 26 : return OpenPAM(poOpenInfo);
163 : }
164 :
165 32 : GDALPamDataset *GIFDataset::OpenPAM(GDALOpenInfo *poOpenInfo)
166 :
167 : {
168 32 : if (!GIFDriverIdentify(poOpenInfo))
169 1 : return nullptr;
170 :
171 31 : if (poOpenInfo->eAccess == GA_Update)
172 : {
173 0 : ReportUpdateNotSupportedByDriver("GIF");
174 0 : return nullptr;
175 : }
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Ingest. */
179 : /* -------------------------------------------------------------------- */
180 31 : VSILFILE *fp = poOpenInfo->fpL;
181 31 : poOpenInfo->fpL = nullptr;
182 :
183 : GifFileType *hGifFile =
184 31 : GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
185 31 : if (hGifFile == nullptr)
186 : {
187 0 : VSIFCloseL(fp);
188 0 : CPLError(CE_Failure, CPLE_OpenFailed,
189 : "DGifOpen() failed for %s. "
190 : "Perhaps the gif file is corrupt?",
191 : poOpenInfo->pszFilename);
192 :
193 0 : return nullptr;
194 : }
195 :
196 : // The following code enables us to detect GIF datasets eligible
197 : // for BIGGIF driver even with an unpatched giflib.
198 :
199 : /* -------------------------------------------------------------------- */
200 : /* Find the first image record. */
201 : /* -------------------------------------------------------------------- */
202 31 : GifRecordType RecordType = FindFirstImage(hGifFile);
203 62 : if (RecordType == IMAGE_DESC_RECORD_TYPE &&
204 31 : DGifGetImageDesc(hGifFile) != GIF_ERROR)
205 : {
206 31 : const int width = hGifFile->SavedImages[0].ImageDesc.Width;
207 31 : const int height = hGifFile->SavedImages[0].ImageDesc.Height;
208 31 : if (static_cast<double>(width) * height > 100000000.0)
209 : {
210 2 : CPLDebug("GIF", "Due to limitations of the GDAL GIF driver we "
211 : "deliberately avoid opening large GIF files "
212 : "(larger than 100 megapixels).");
213 2 : GIFAbstractDataset::myDGifCloseFile(hGifFile);
214 : // Reset poOpenInfo->fpL since BIGGIF may need it.
215 2 : poOpenInfo->fpL = fp;
216 2 : VSIFSeekL(fp, 0, SEEK_SET);
217 2 : return nullptr;
218 : }
219 : }
220 :
221 29 : GIFAbstractDataset::myDGifCloseFile(hGifFile);
222 :
223 29 : VSIFSeekL(fp, 0, SEEK_SET);
224 :
225 29 : hGifFile = GIFAbstractDataset::myDGifOpen(fp, GIFAbstractDataset::ReadFunc);
226 29 : if (hGifFile == nullptr)
227 : {
228 0 : VSIFCloseL(fp);
229 0 : CPLError(CE_Failure, CPLE_OpenFailed,
230 : "DGifOpen() failed for %s. "
231 : "Perhaps the gif file is corrupt?",
232 : poOpenInfo->pszFilename);
233 :
234 0 : return nullptr;
235 : }
236 :
237 29 : const int nGifErr = DGifSlurp(hGifFile);
238 :
239 29 : if (nGifErr != GIF_OK || hGifFile->SavedImages == nullptr)
240 : {
241 0 : VSIFCloseL(fp);
242 0 : GIFAbstractDataset::myDGifCloseFile(hGifFile);
243 :
244 0 : if (nGifErr == D_GIF_ERR_DATA_TOO_BIG)
245 : {
246 0 : CPLDebug("GIF",
247 : "DGifSlurp() failed for %s because it was too large. "
248 : "Due to limitations of the GDAL GIF driver we "
249 : "deliberately avoid opening large GIF files "
250 : "(larger than 100 megapixels).",
251 : poOpenInfo->pszFilename);
252 0 : return nullptr;
253 : }
254 :
255 0 : CPLError(CE_Failure, CPLE_OpenFailed,
256 : "DGifSlurp() failed for %s. "
257 : "Perhaps the gif file is corrupt?",
258 : poOpenInfo->pszFilename);
259 :
260 0 : return nullptr;
261 : }
262 :
263 : /* -------------------------------------------------------------------- */
264 : /* Create a corresponding GDALDataset. */
265 : /* -------------------------------------------------------------------- */
266 29 : GIFDataset *poDS = new GIFDataset();
267 :
268 29 : poDS->fp = fp;
269 29 : poDS->eAccess = GA_ReadOnly;
270 29 : poDS->hGifFile = hGifFile;
271 :
272 : /* -------------------------------------------------------------------- */
273 : /* Capture some information from the file that is of interest. */
274 : /* -------------------------------------------------------------------- */
275 29 : poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
276 29 : poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
277 29 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
278 : {
279 0 : delete poDS;
280 0 : return nullptr;
281 : }
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* Create band information objects. */
285 : /* -------------------------------------------------------------------- */
286 58 : for (int iImage = 0; iImage < hGifFile->ImageCount; iImage++)
287 : {
288 29 : SavedImage *psImage = hGifFile->SavedImages + iImage;
289 :
290 29 : if (psImage->ImageDesc.Width != poDS->nRasterXSize ||
291 29 : psImage->ImageDesc.Height != poDS->nRasterYSize)
292 0 : continue;
293 :
294 29 : if (psImage->ImageDesc.ColorMap == nullptr &&
295 29 : poDS->hGifFile->SColorMap == nullptr)
296 : {
297 0 : CPLDebug("GIF", "Skipping image without color table");
298 0 : continue;
299 : }
300 : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
301 : // Since giflib 5, de-interlacing is done by DGifSlurp().
302 29 : psImage->ImageDesc.Interlace = false;
303 : #endif
304 29 : poDS->SetBand(poDS->nBands + 1,
305 29 : new GIFRasterBand(poDS, poDS->nBands + 1, psImage,
306 29 : hGifFile->SBackGroundColor));
307 : }
308 29 : if (poDS->nBands == 0)
309 : {
310 0 : delete poDS;
311 0 : return nullptr;
312 : }
313 :
314 : /* -------------------------------------------------------------------- */
315 : /* Check for georeferencing. */
316 : /* -------------------------------------------------------------------- */
317 29 : poDS->DetectGeoreferencing(poOpenInfo);
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Initialize any PAM information. */
321 : /* -------------------------------------------------------------------- */
322 29 : poDS->SetDescription(poOpenInfo->pszFilename);
323 29 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Support overviews. */
327 : /* -------------------------------------------------------------------- */
328 58 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
329 29 : poOpenInfo->GetSiblingFiles());
330 :
331 29 : return poDS;
332 : }
333 :
334 : /************************************************************************/
335 : /* GDALPrintGifError() */
336 : /************************************************************************/
337 :
338 0 : static void GDALPrintGifError(CPL_UNUSED GifFileType *hGifFile,
339 : const char *pszMsg)
340 : {
341 : // GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
342 : // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
343 : #if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \
344 : ((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
345 : // Static string actually, hence the const char* cast.
346 :
347 : #if GIFLIB_MAJOR >= 5
348 0 : const char *pszGIFLIBError = GifErrorString(hGifFile->Error);
349 : #else
350 : // TODO(schwehr): Can we remove the cast for older libgif?
351 : const char *pszGIFLIBError = (const char *)GifErrorString();
352 : #endif
353 0 : if (pszGIFLIBError == nullptr)
354 0 : pszGIFLIBError = "Unknown error";
355 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s. GIFLib Error : %s", pszMsg,
356 : pszGIFLIBError);
357 : #else
358 : PrintGifError();
359 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMsg);
360 : #endif
361 0 : }
362 :
363 : /************************************************************************/
364 : /* CreateCopy() */
365 : /************************************************************************/
366 :
367 24 : GDALDataset *GIFDataset::CreateCopy(const char *pszFilename,
368 : GDALDataset *poSrcDS, int bStrict,
369 : CSLConstList papszOptions,
370 : GDALProgressFunc pfnProgress,
371 : void *pProgressData)
372 :
373 : {
374 : /* -------------------------------------------------------------------- */
375 : /* Check for interlaced option. */
376 : /* -------------------------------------------------------------------- */
377 24 : const bool bInterlace = CPLFetchBool(papszOptions, "INTERLACING", false);
378 :
379 : /* -------------------------------------------------------------------- */
380 : /* Some some rudimentary checks */
381 : /* -------------------------------------------------------------------- */
382 24 : const int nBands = poSrcDS->GetRasterCount();
383 24 : if (nBands != 1)
384 : {
385 5 : CPLError(CE_Failure, CPLE_NotSupported,
386 : "GIF driver only supports one band images.");
387 :
388 5 : return nullptr;
389 : }
390 :
391 19 : const int nXSize = poSrcDS->GetRasterXSize();
392 19 : const int nYSize = poSrcDS->GetRasterYSize();
393 19 : if (nXSize > 65535 || nYSize > 65535)
394 : {
395 0 : CPLError(CE_Failure, CPLE_NotSupported,
396 : "GIF driver only supports datasets up to 65535x65535 size.");
397 :
398 0 : return nullptr;
399 : }
400 :
401 19 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt8 && bStrict)
402 : {
403 10 : CPLError(CE_Failure, CPLE_NotSupported,
404 : "GIF driver doesn't support data type %s. "
405 : "Only eight bit bands supported.",
406 : GDALGetDataTypeName(
407 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
408 :
409 10 : return nullptr;
410 : }
411 :
412 : /* -------------------------------------------------------------------- */
413 : /* Open the output file. */
414 : /* -------------------------------------------------------------------- */
415 9 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
416 9 : if (fp == nullptr)
417 : {
418 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s:\n%s",
419 3 : pszFilename, VSIStrerror(errno));
420 3 : return nullptr;
421 : }
422 :
423 : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
424 6 : int nError = 0;
425 6 : GifFileType *hGifFile = EGifOpen(fp, VSIGIFWriteFunc, &nError);
426 : #else
427 : GifFileType *hGifFile = EGifOpen(fp, VSIGIFWriteFunc);
428 : #endif
429 6 : if (hGifFile == nullptr)
430 : {
431 0 : VSIFCloseL(fp);
432 0 : CPLError(CE_Failure, CPLE_OpenFailed,
433 : "EGifOpenFilename(%s) failed. Does file already exist?",
434 : pszFilename);
435 :
436 0 : return nullptr;
437 : }
438 :
439 : /* -------------------------------------------------------------------- */
440 : /* Prepare colortable. */
441 : /* -------------------------------------------------------------------- */
442 6 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
443 6 : ColorMapObject *psGifCT = nullptr;
444 :
445 6 : if (poBand->GetColorTable() == nullptr)
446 : {
447 5 : psGifCT = GifMakeMapObject(256, nullptr);
448 5 : if (psGifCT == nullptr)
449 : {
450 0 : CPLError(CE_Failure, CPLE_AppDefined,
451 : "Cannot allocate color table");
452 0 : GIFAbstractDataset::myEGifCloseFile(hGifFile);
453 0 : VSIFCloseL(fp);
454 0 : return nullptr;
455 : }
456 1285 : for (int iColor = 0; iColor < 256; iColor++)
457 : {
458 1280 : psGifCT->Colors[iColor].Red = static_cast<GifByteType>(iColor);
459 1280 : psGifCT->Colors[iColor].Green = static_cast<GifByteType>(iColor);
460 1280 : psGifCT->Colors[iColor].Blue = static_cast<GifByteType>(iColor);
461 : }
462 : }
463 : else
464 : {
465 1 : GDALColorTable *poCT = poBand->GetColorTable();
466 1 : int nFullCount = 2;
467 :
468 4 : while (nFullCount < poCT->GetColorEntryCount())
469 3 : nFullCount = nFullCount * 2;
470 :
471 1 : psGifCT = GifMakeMapObject(nFullCount, nullptr);
472 1 : if (psGifCT == nullptr)
473 : {
474 0 : CPLError(CE_Failure, CPLE_AppDefined,
475 : "Cannot allocate color table");
476 0 : GIFAbstractDataset::myEGifCloseFile(hGifFile);
477 0 : VSIFCloseL(fp);
478 0 : return nullptr;
479 : }
480 1 : int iColor = 0;
481 17 : for (; iColor < poCT->GetColorEntryCount(); iColor++)
482 : {
483 : GDALColorEntry sEntry;
484 :
485 16 : poCT->GetColorEntryAsRGB(iColor, &sEntry);
486 16 : psGifCT->Colors[iColor].Red = static_cast<GifByteType>(sEntry.c1);
487 16 : psGifCT->Colors[iColor].Green = static_cast<GifByteType>(sEntry.c2);
488 16 : psGifCT->Colors[iColor].Blue = static_cast<GifByteType>(sEntry.c3);
489 : }
490 1 : for (; iColor < nFullCount; iColor++)
491 : {
492 0 : psGifCT->Colors[iColor].Red = 0;
493 0 : psGifCT->Colors[iColor].Green = 0;
494 0 : psGifCT->Colors[iColor].Blue = 0;
495 : }
496 : }
497 :
498 : /* -------------------------------------------------------------------- */
499 : /* Setup parameters. */
500 : /* -------------------------------------------------------------------- */
501 6 : if (EGifPutScreenDesc(hGifFile, nXSize, nYSize, 8, /* ColorRes */
502 : 255, /* Background */
503 6 : psGifCT) == GIF_ERROR)
504 : {
505 0 : GifFreeMapObject(psGifCT);
506 0 : GDALPrintGifError(hGifFile, "Error writing gif file.");
507 0 : GIFAbstractDataset::myEGifCloseFile(hGifFile);
508 0 : VSIFCloseL(fp);
509 0 : return nullptr;
510 : }
511 :
512 6 : GifFreeMapObject(psGifCT);
513 6 : psGifCT = nullptr;
514 :
515 : // Support for transparency.
516 6 : int bNoDataValue = 0;
517 6 : double noDataValue = poBand->GetNoDataValue(&bNoDataValue);
518 6 : if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255)
519 : {
520 1 : unsigned char extensionData[4] = {
521 : 1, // Transparent Color Flag.
522 1 : 0, 0, static_cast<unsigned char>(noDataValue)};
523 1 : EGifPutExtension(hGifFile, 0xf9, 4, extensionData);
524 : }
525 :
526 6 : if (EGifPutImageDesc(hGifFile, 0, 0, nXSize, nYSize, bInterlace, nullptr) ==
527 : GIF_ERROR)
528 : {
529 0 : GDALPrintGifError(hGifFile, "Error writing gif file.");
530 0 : GIFAbstractDataset::myEGifCloseFile(hGifFile);
531 0 : VSIFCloseL(fp);
532 0 : return nullptr;
533 : }
534 :
535 : /* -------------------------------------------------------------------- */
536 : /* Loop over image, copying image data. */
537 : /* -------------------------------------------------------------------- */
538 6 : GDALPamDataset *poDS = nullptr;
539 6 : GByte *pabyScanline = static_cast<GByte *>(CPLMalloc(nXSize));
540 :
541 6 : if (!pfnProgress(0.0, nullptr, pProgressData))
542 : {
543 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unable to setup progress.");
544 : }
545 :
546 6 : if (!bInterlace)
547 : {
548 475 : for (int iLine = 0; iLine < nYSize; iLine++)
549 : {
550 940 : const CPLErr eErr = poBand->RasterIO(
551 : GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1,
552 470 : GDT_UInt8, nBands, static_cast<GSpacing>(nBands) * nXSize,
553 : nullptr);
554 :
555 940 : if (eErr != CE_None ||
556 470 : EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR)
557 : {
558 0 : CPLError(CE_Failure, CPLE_AppDefined,
559 : "Error writing gif file.");
560 0 : goto error;
561 : }
562 :
563 470 : if (!pfnProgress((iLine + 1) * 1.0 / nYSize, nullptr,
564 : pProgressData))
565 : {
566 0 : goto error;
567 : }
568 : }
569 : }
570 : else
571 : {
572 1 : int nLinesRead = 0;
573 : // Need to perform 4 passes on the images:
574 5 : for (int i = 0; i < 4; i++)
575 : {
576 24 : for (int j = InterlacedOffset[i]; j < nYSize;
577 20 : j += InterlacedJumps[i])
578 : {
579 : const CPLErr eErr =
580 20 : poBand->RasterIO(GF_Read, 0, j, nXSize, 1, pabyScanline,
581 : nXSize, 1, GDT_UInt8, 1, nXSize, nullptr);
582 :
583 40 : if (eErr != CE_None ||
584 20 : EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR)
585 : {
586 0 : CPLError(CE_Failure, CPLE_AppDefined,
587 : "Error writing gif file.");
588 0 : goto error;
589 : }
590 :
591 20 : nLinesRead++;
592 20 : if (!pfnProgress(nLinesRead * 1.0 / nYSize, nullptr,
593 : pProgressData))
594 : {
595 0 : goto error;
596 : }
597 : }
598 : }
599 : }
600 :
601 6 : CPLFree(pabyScanline);
602 6 : pabyScanline = nullptr;
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* cleanup */
606 : /* -------------------------------------------------------------------- */
607 6 : if (GIFAbstractDataset::myEGifCloseFile(hGifFile) == GIF_ERROR)
608 : {
609 0 : CPLError(CE_Failure, CPLE_AppDefined, "EGifCloseFile() failed.");
610 0 : hGifFile = nullptr;
611 0 : goto error;
612 : }
613 6 : hGifFile = nullptr;
614 :
615 6 : VSIFCloseL(fp);
616 6 : fp = nullptr;
617 :
618 : /* -------------------------------------------------------------------- */
619 : /* Do we need a world file? */
620 : /* -------------------------------------------------------------------- */
621 6 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
622 : {
623 0 : GDALGeoTransform gt;
624 0 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
625 0 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
626 : }
627 :
628 : /* -------------------------------------------------------------------- */
629 : /* Re-open dataset, and copy any auxiliary pam information. */
630 : /* -------------------------------------------------------------------- */
631 :
632 : // If writing to stdout, we can't reopen it, so return
633 : // a fake dataset to make the caller happy.
634 : {
635 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
636 6 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
637 6 : poDS = OpenPAM(&oOpenInfo);
638 : }
639 6 : if (poDS)
640 : {
641 5 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
642 5 : return poDS;
643 : }
644 : else
645 : {
646 1 : CPLErrorReset();
647 :
648 1 : GIFDataset *poGIF_DS = new GIFDataset();
649 1 : poGIF_DS->nRasterXSize = nXSize;
650 1 : poGIF_DS->nRasterYSize = nYSize;
651 2 : for (int i = 0; i < nBands; i++)
652 1 : poGIF_DS->SetBand(i + 1,
653 1 : new GIFRasterBand(poGIF_DS, i + 1, nullptr, 0));
654 1 : return poGIF_DS;
655 : }
656 :
657 0 : error:
658 0 : if (hGifFile)
659 0 : GIFAbstractDataset::myEGifCloseFile(hGifFile);
660 0 : if (fp)
661 0 : VSIFCloseL(fp);
662 0 : if (pabyScanline)
663 0 : CPLFree(pabyScanline);
664 0 : return nullptr;
665 : }
666 :
667 : /************************************************************************/
668 : /* GDALRegister_GIF() */
669 : /************************************************************************/
670 :
671 19 : void GDALRegister_GIF()
672 :
673 : {
674 19 : if (GDALGetDriverByName(GIF_DRIVER_NAME) != nullptr)
675 0 : return;
676 :
677 19 : GDALDriver *poDriver = new GDALDriver();
678 :
679 19 : GIFDriverSetCommonMetadata(poDriver);
680 19 : poDriver->pfnOpen = GIFDataset::Open;
681 19 : poDriver->pfnCreateCopy = GIFDataset::CreateCopy;
682 :
683 19 : GetGDALDriverManager()->RegisterDriver(poDriver);
684 :
685 : #ifdef GIF_PLUGIN
686 19 : GDALRegister_BIGGIF();
687 : #endif
688 : }
|