Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GIF Driver
4 : * Purpose: GIF Abstract Dataset
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ****************************************************************************
8 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gifabstractdataset.h"
14 :
15 : /************************************************************************/
16 : /* ==================================================================== */
17 : /* GIFAbstractDataset */
18 : /* ==================================================================== */
19 : /************************************************************************/
20 :
21 : /************************************************************************/
22 : /* GIFAbstractDataset() */
23 : /************************************************************************/
24 :
25 40 : GIFAbstractDataset::GIFAbstractDataset()
26 : : fp(nullptr), hGifFile(nullptr), bGeoTransformValid(FALSE), nGCPCount(0),
27 40 : pasGCPList(nullptr), bHasReadXMPMetadata(FALSE)
28 : {
29 40 : adfGeoTransform[0] = 0.0;
30 40 : adfGeoTransform[1] = 1.0;
31 40 : adfGeoTransform[2] = 0.0;
32 40 : adfGeoTransform[3] = 0.0;
33 40 : adfGeoTransform[4] = 0.0;
34 40 : adfGeoTransform[5] = 1.0;
35 40 : }
36 :
37 : /************************************************************************/
38 : /* ~GIFAbstractDataset() */
39 : /************************************************************************/
40 :
41 40 : GIFAbstractDataset::~GIFAbstractDataset()
42 :
43 : {
44 40 : FlushCache(true);
45 :
46 40 : if (nGCPCount > 0)
47 : {
48 0 : GDALDeinitGCPs(nGCPCount, pasGCPList);
49 0 : CPLFree(pasGCPList);
50 : }
51 :
52 40 : if (hGifFile)
53 39 : myDGifCloseFile(hGifFile);
54 :
55 40 : if (fp != nullptr)
56 39 : VSIFCloseL(fp);
57 40 : }
58 :
59 : /************************************************************************/
60 : /* GIFCollectXMPMetadata() */
61 : /************************************************************************/
62 :
63 : /* See ยง2.1.2 of
64 : * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
65 : */
66 :
67 5 : static CPLString GIFCollectXMPMetadata(VSILFILE *fp)
68 :
69 : {
70 5 : CPLString osXMP;
71 :
72 : /* Save current position to avoid disturbing GIF stream decoding */
73 5 : vsi_l_offset nCurOffset = VSIFTellL(fp);
74 :
75 : char abyBuffer[2048 + 1];
76 :
77 5 : VSIFSeekL(fp, 0, SEEK_SET);
78 :
79 : /* Loop over file */
80 :
81 5 : int iStartSearchOffset = 1024;
82 : while (true)
83 : {
84 26 : int nRead = static_cast<int>(VSIFReadL(abyBuffer + 1024, 1, 1024, fp));
85 26 : if (nRead <= 0)
86 0 : break;
87 26 : abyBuffer[1024 + nRead] = 0;
88 :
89 26 : int iFoundOffset = -1;
90 43588 : for (int i = iStartSearchOffset; i < 1024 + nRead - 14; i++)
91 : {
92 43563 : if (memcmp(abyBuffer + i, "\x21\xff\x0bXMP DataXMP", 14) == 0)
93 : {
94 1 : iFoundOffset = i + 14;
95 1 : break;
96 : }
97 : }
98 :
99 26 : iStartSearchOffset = 0;
100 :
101 26 : if (iFoundOffset >= 0)
102 : {
103 1 : int nSize = 1024 + nRead - iFoundOffset;
104 1 : char *pszXMP = (char *)VSIMalloc(nSize + 1);
105 1 : if (pszXMP == nullptr)
106 0 : break;
107 :
108 1 : pszXMP[nSize] = 0;
109 1 : memcpy(pszXMP, abyBuffer + iFoundOffset, nSize);
110 :
111 : /* Read from file until we find a NUL character */
112 1 : int nLen = (int)strlen(pszXMP);
113 5 : while (nLen == nSize)
114 : {
115 4 : char *pszNewXMP = (char *)VSIRealloc(pszXMP, nSize + 1024 + 1);
116 4 : if (pszNewXMP == nullptr)
117 0 : break;
118 4 : pszXMP = pszNewXMP;
119 :
120 4 : nRead =
121 4 : static_cast<int>(VSIFReadL(pszXMP + nSize, 1, 1024, fp));
122 4 : if (nRead <= 0)
123 0 : break;
124 :
125 4 : pszXMP[nSize + nRead] = 0;
126 4 : nLen += (int)strlen(pszXMP + nSize);
127 4 : nSize += nRead;
128 : }
129 :
130 1 : if (nLen > 256 && pszXMP[nLen - 1] == '\x01' &&
131 1 : pszXMP[nLen - 2] == '\x02' && pszXMP[nLen - 255] == '\xff' &&
132 1 : pszXMP[nLen - 256] == '\x01')
133 : {
134 1 : pszXMP[nLen - 256] = 0;
135 :
136 1 : osXMP = pszXMP;
137 : }
138 :
139 1 : VSIFree(pszXMP);
140 :
141 1 : break;
142 : }
143 :
144 25 : if (nRead != 1024)
145 4 : break;
146 :
147 21 : memcpy(abyBuffer, abyBuffer + 1024, 1024);
148 21 : }
149 :
150 5 : VSIFSeekL(fp, nCurOffset, SEEK_SET);
151 :
152 10 : return osXMP;
153 : }
154 :
155 : /************************************************************************/
156 : /* CollectXMPMetadata() */
157 : /************************************************************************/
158 :
159 5 : void GIFAbstractDataset::CollectXMPMetadata()
160 :
161 : {
162 5 : if (fp == nullptr || bHasReadXMPMetadata)
163 0 : return;
164 :
165 5 : CPLString osXMP = GIFCollectXMPMetadata(fp);
166 5 : if (!osXMP.empty())
167 : {
168 : /* Avoid setting the PAM dirty bit just for that */
169 1 : const int nOldPamFlags = nPamFlags;
170 :
171 : char *apszMDList[2];
172 1 : apszMDList[0] = (char *)osXMP.c_str();
173 1 : apszMDList[1] = nullptr;
174 1 : SetMetadata(apszMDList, "xml:XMP");
175 :
176 : // cppcheck-suppress redundantAssignment
177 1 : nPamFlags = nOldPamFlags;
178 : }
179 :
180 5 : bHasReadXMPMetadata = TRUE;
181 : }
182 :
183 : /************************************************************************/
184 : /* GetMetadataDomainList() */
185 : /************************************************************************/
186 :
187 1 : char **GIFAbstractDataset::GetMetadataDomainList()
188 : {
189 1 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
190 1 : TRUE, "xml:XMP", nullptr);
191 : }
192 :
193 : /************************************************************************/
194 : /* GetMetadata() */
195 : /************************************************************************/
196 :
197 64 : char **GIFAbstractDataset::GetMetadata(const char *pszDomain)
198 : {
199 64 : if (fp == nullptr)
200 0 : return nullptr;
201 64 : if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata &&
202 53 : (pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP")))
203 5 : CollectXMPMetadata();
204 64 : return GDALPamDataset::GetMetadata(pszDomain);
205 : }
206 :
207 : /************************************************************************/
208 : /* GetGeoTransform() */
209 : /************************************************************************/
210 :
211 52 : CPLErr GIFAbstractDataset::GetGeoTransform(double *padfTransform)
212 :
213 : {
214 52 : if (bGeoTransformValid)
215 : {
216 0 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
217 0 : return CE_None;
218 : }
219 :
220 52 : return GDALPamDataset::GetGeoTransform(padfTransform);
221 : }
222 :
223 : /************************************************************************/
224 : /* GetGCPCount() */
225 : /************************************************************************/
226 :
227 10 : int GIFAbstractDataset::GetGCPCount()
228 :
229 : {
230 10 : if (nGCPCount > 0)
231 0 : return nGCPCount;
232 :
233 10 : return GDALPamDataset::GetGCPCount();
234 : }
235 :
236 : /************************************************************************/
237 : /* GetGCPs() */
238 : /************************************************************************/
239 :
240 0 : const GDAL_GCP *GIFAbstractDataset::GetGCPs()
241 :
242 : {
243 0 : if (nGCPCount > 0)
244 0 : return pasGCPList;
245 :
246 0 : return GDALPamDataset::GetGCPs();
247 : }
248 :
249 : /************************************************************************/
250 : /* GetFileList() */
251 : /************************************************************************/
252 :
253 12 : char **GIFAbstractDataset::GetFileList()
254 :
255 : {
256 12 : char **papszFileList = GDALPamDataset::GetFileList();
257 :
258 12 : if (!osWldFilename.empty() &&
259 0 : CSLFindString(papszFileList, osWldFilename) == -1)
260 : {
261 0 : papszFileList = CSLAddString(papszFileList, osWldFilename);
262 : }
263 :
264 12 : return papszFileList;
265 : }
266 :
267 : /************************************************************************/
268 : /* DetectGeoreferencing() */
269 : /************************************************************************/
270 :
271 39 : void GIFAbstractDataset::DetectGeoreferencing(GDALOpenInfo *poOpenInfo)
272 : {
273 39 : char *pszWldFilename = nullptr;
274 :
275 39 : bGeoTransformValid =
276 78 : GDALReadWorldFile2(poOpenInfo->pszFilename, nullptr, adfGeoTransform,
277 39 : poOpenInfo->GetSiblingFiles(), &pszWldFilename);
278 39 : if (!bGeoTransformValid)
279 : {
280 39 : bGeoTransformValid =
281 39 : GDALReadWorldFile2(poOpenInfo->pszFilename, ".wld", adfGeoTransform,
282 39 : poOpenInfo->GetSiblingFiles(), &pszWldFilename);
283 : }
284 :
285 39 : if (pszWldFilename)
286 : {
287 0 : osWldFilename = pszWldFilename;
288 0 : CPLFree(pszWldFilename);
289 : }
290 39 : }
291 :
292 : /************************************************************************/
293 : /* myDGifOpen() */
294 : /************************************************************************/
295 :
296 76 : GifFileType *GIFAbstractDataset::myDGifOpen(void *userPtr, InputFunc readFunc)
297 : {
298 : #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
299 : int nErrorCode;
300 152 : return DGifOpen(userPtr, readFunc, &nErrorCode);
301 : #else
302 : return DGifOpen(userPtr, readFunc);
303 : #endif
304 : }
305 :
306 : /************************************************************************/
307 : /* myDGifCloseFile() */
308 : /************************************************************************/
309 :
310 76 : int GIFAbstractDataset::myDGifCloseFile(GifFileType *hGifFile)
311 : {
312 : #if defined(GIFLIB_MAJOR) && \
313 : ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5)
314 : int nErrorCode;
315 152 : return DGifCloseFile(hGifFile, &nErrorCode);
316 : #else
317 : return DGifCloseFile(hGifFile);
318 : #endif
319 : }
320 :
321 : /************************************************************************/
322 : /* myEGifCloseFile() */
323 : /************************************************************************/
324 :
325 6 : int GIFAbstractDataset::myEGifCloseFile(GifFileType *hGifFile)
326 : {
327 : #if defined(GIFLIB_MAJOR) && \
328 : ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5)
329 : int nErrorCode;
330 12 : return EGifCloseFile(hGifFile, &nErrorCode);
331 : #else
332 : return EGifCloseFile(hGifFile);
333 : #endif
334 : }
335 :
336 : /************************************************************************/
337 : /* VSIGIFReadFunc() */
338 : /* */
339 : /* Proxy function for reading from GIF file. */
340 : /************************************************************************/
341 :
342 12146 : int GIFAbstractDataset::ReadFunc(GifFileType *psGFile, GifByteType *pabyBuffer,
343 : int nBytesToRead)
344 :
345 : {
346 : return static_cast<int>(
347 12146 : VSIFReadL(pabyBuffer, 1, nBytesToRead, (VSILFILE *)psGFile->UserData));
348 : }
349 :
350 : /************************************************************************/
351 : /* FindFirstImage() */
352 : /************************************************************************/
353 :
354 43 : GifRecordType GIFAbstractDataset::FindFirstImage(GifFileType *hGifFile)
355 : {
356 43 : GifRecordType RecordType = TERMINATE_RECORD_TYPE;
357 :
358 47 : while (DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR &&
359 94 : RecordType != TERMINATE_RECORD_TYPE &&
360 47 : RecordType != IMAGE_DESC_RECORD_TYPE)
361 : {
362 : /* Skip extension records found before IMAGE_DESC_RECORD_TYPE */
363 4 : if (RecordType == EXTENSION_RECORD_TYPE)
364 : {
365 : int nFunction;
366 4 : GifByteType *pExtData = nullptr;
367 4 : if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR)
368 0 : break;
369 96 : while (pExtData != nullptr)
370 : {
371 92 : if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR)
372 0 : break;
373 : }
374 : }
375 : }
376 :
377 43 : return RecordType;
378 : }
379 :
380 : /************************************************************************/
381 : /* GIFAbstractRasterBand() */
382 : /************************************************************************/
383 :
384 40 : GIFAbstractRasterBand::GIFAbstractRasterBand(GIFAbstractDataset *poDSIn,
385 : int nBandIn,
386 : SavedImage *psSavedImage,
387 : int nBackground,
388 40 : int bAdvertiseInterlacedMDI)
389 : : psImage(psSavedImage), panInterlaceMap(nullptr), poColorTable(nullptr),
390 40 : nTransparentColor(0)
391 : {
392 40 : poDS = poDSIn;
393 40 : nBand = nBandIn;
394 :
395 40 : eDataType = GDT_Byte;
396 :
397 40 : nBlockXSize = poDS->GetRasterXSize();
398 40 : nBlockYSize = 1;
399 :
400 40 : if (psImage == nullptr)
401 1 : return;
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Setup interlacing map if required. */
405 : /* -------------------------------------------------------------------- */
406 39 : panInterlaceMap = nullptr;
407 39 : if (psImage->ImageDesc.Interlace)
408 : {
409 6 : int iLine = 0;
410 :
411 6 : if (bAdvertiseInterlacedMDI)
412 6 : poDS->SetMetadataItem("INTERLACED", "YES", "IMAGE_STRUCTURE");
413 :
414 6 : panInterlaceMap = (int *)CPLCalloc(poDSIn->nRasterYSize, sizeof(int));
415 :
416 30 : for (int i = 0; i < 4; i++)
417 : {
418 67158 : for (int j = InterlacedOffset[i]; j < poDSIn->nRasterYSize;
419 67134 : j += InterlacedJumps[i])
420 67134 : panInterlaceMap[j] = iLine++;
421 : }
422 : }
423 33 : else if (bAdvertiseInterlacedMDI)
424 : {
425 0 : poDS->SetMetadataItem("INTERLACED", "NO", "IMAGE_STRUCTURE");
426 : }
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Check for transparency. We just take the first graphic */
430 : /* control extension block we find, if any. */
431 : /* -------------------------------------------------------------------- */
432 39 : nTransparentColor = -1;
433 131 : for (int iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount;
434 : iExtBlock++)
435 : {
436 92 : if (psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 ||
437 3 : psImage->ExtensionBlocks[iExtBlock].ByteCount < 4)
438 89 : continue;
439 :
440 3 : unsigned char *pExtData = reinterpret_cast<unsigned char *>(
441 3 : psImage->ExtensionBlocks[iExtBlock].Bytes);
442 :
443 : /* check if transparent color flag is set */
444 3 : if (!(pExtData[0] & 0x1))
445 0 : continue;
446 :
447 3 : nTransparentColor = pExtData[3];
448 : }
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* Setup colormap. */
452 : /* -------------------------------------------------------------------- */
453 39 : ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap;
454 39 : if (psGifCT == nullptr)
455 39 : psGifCT = poDSIn->hGifFile->SColorMap;
456 :
457 39 : poColorTable = new GDALColorTable();
458 4503 : for (int iColor = 0; iColor < psGifCT->ColorCount; iColor++)
459 : {
460 : GDALColorEntry oEntry;
461 :
462 4464 : oEntry.c1 = psGifCT->Colors[iColor].Red;
463 4464 : oEntry.c2 = psGifCT->Colors[iColor].Green;
464 4464 : oEntry.c3 = psGifCT->Colors[iColor].Blue;
465 :
466 4464 : if (iColor == nTransparentColor)
467 3 : oEntry.c4 = 0;
468 : else
469 4461 : oEntry.c4 = 255;
470 :
471 4464 : poColorTable->SetColorEntry(iColor, &oEntry);
472 : }
473 :
474 : /* -------------------------------------------------------------------- */
475 : /* If we have a background value, return it here. Some */
476 : /* applications might want to treat this as transparent, but in */
477 : /* many uses this is inappropriate so we don't return it as */
478 : /* nodata or transparent. */
479 : /* -------------------------------------------------------------------- */
480 39 : if (nBackground != 255)
481 : {
482 : char szBackground[10];
483 :
484 24 : snprintf(szBackground, sizeof(szBackground), "%d", nBackground);
485 24 : SetMetadataItem("GIF_BACKGROUND", szBackground);
486 : }
487 : }
488 :
489 : /************************************************************************/
490 : /* ~GIFAbstractRasterBand() */
491 : /************************************************************************/
492 :
493 40 : GIFAbstractRasterBand::~GIFAbstractRasterBand()
494 :
495 : {
496 40 : if (poColorTable != nullptr)
497 39 : delete poColorTable;
498 :
499 40 : CPLFree(panInterlaceMap);
500 40 : }
501 :
502 : /************************************************************************/
503 : /* GetColorInterpretation() */
504 : /************************************************************************/
505 :
506 23 : GDALColorInterp GIFAbstractRasterBand::GetColorInterpretation()
507 :
508 : {
509 23 : return GCI_PaletteIndex;
510 : }
511 :
512 : /************************************************************************/
513 : /* GetColorTable() */
514 : /************************************************************************/
515 :
516 47 : GDALColorTable *GIFAbstractRasterBand::GetColorTable()
517 :
518 : {
519 47 : return poColorTable;
520 : }
521 :
522 : /************************************************************************/
523 : /* GetNoDataValue() */
524 : /************************************************************************/
525 :
526 45 : double GIFAbstractRasterBand::GetNoDataValue(int *pbSuccess)
527 :
528 : {
529 45 : if (pbSuccess != nullptr)
530 45 : *pbSuccess = nTransparentColor != -1;
531 :
532 45 : return nTransparentColor;
533 : }
|