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