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