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