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