Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL WEBP Driver
4 : * Purpose: Implement GDAL WEBP Support based on libwebp
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 "cpl_string.h"
14 : #include "cpl_vsi_virtual.h"
15 : #include "gdal_frmts.h"
16 : #include "gdal_pam.h"
17 :
18 : #include "webp_headers.h"
19 : #include "webpdrivercore.h"
20 :
21 : #include <limits>
22 :
23 : /************************************************************************/
24 : /* ==================================================================== */
25 : /* WEBPDataset */
26 : /* ==================================================================== */
27 : /************************************************************************/
28 :
29 : class WEBPRasterBand;
30 :
31 : class WEBPDataset final : public GDALPamDataset
32 : {
33 : friend class WEBPRasterBand;
34 :
35 : VSILFILE *fpImage;
36 : GByte *pabyUncompressed;
37 : int bHasBeenUncompressed;
38 : CPLErr eUncompressErrRet;
39 : CPLErr Uncompress();
40 :
41 : int bHasReadXMPMetadata;
42 :
43 : CPL_DISALLOW_COPY_ASSIGN(WEBPDataset)
44 :
45 : CPLErr GetGeoTransform(GDALGeoTransform >,
46 : std::string &osWorldFilename) const;
47 :
48 : public:
49 : WEBPDataset();
50 : ~WEBPDataset() override;
51 :
52 : char **GetFileList() override;
53 :
54 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
55 : CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
56 : GDALDataType, int, BANDMAP_TYPE, GSpacing nPixelSpace,
57 : GSpacing nLineSpace, GSpacing nBandSpace,
58 : GDALRasterIOExtraArg *psExtraArg) override;
59 :
60 : char **GetMetadataDomainList() override;
61 : char **GetMetadata(const char *pszDomain = "") override;
62 :
63 : CPLStringList GetCompressionFormats(int nXOff, int nYOff, int nXSize,
64 : int nYSize, int nBandCount,
65 : const int *panBandList) override;
66 : CPLErr ReadCompressedData(const char *pszFormat, int nXOff, int nYOff,
67 : int nXSize, int nYSize, int nBandCount,
68 : const int *panBandList, void **ppBuffer,
69 : size_t *pnBufferSize,
70 : char **ppszDetailedFormat) override;
71 :
72 : static GDALPamDataset *OpenPAM(GDALOpenInfo *poOpenInfo);
73 : static GDALDataset *Open(GDALOpenInfo *);
74 : static GDALDataset *CreateCopy(const char *pszFilename,
75 : GDALDataset *poSrcDS, int bStrict,
76 : char **papszOptions,
77 : GDALProgressFunc pfnProgress,
78 : void *pProgressData);
79 : };
80 :
81 : /************************************************************************/
82 : /* ==================================================================== */
83 : /* WEBPRasterBand */
84 : /* ==================================================================== */
85 : /************************************************************************/
86 :
87 : class WEBPRasterBand final : public GDALPamRasterBand
88 : {
89 : friend class WEBPDataset;
90 :
91 : public:
92 : WEBPRasterBand(WEBPDataset *, int);
93 :
94 : CPLErr IReadBlock(int, int, void *) override;
95 : GDALColorInterp GetColorInterpretation() override;
96 : };
97 :
98 : /************************************************************************/
99 : /* WEBPRasterBand() */
100 : /************************************************************************/
101 :
102 1784 : WEBPRasterBand::WEBPRasterBand(WEBPDataset *poDSIn, int)
103 : {
104 1784 : poDS = poDSIn;
105 :
106 1784 : eDataType = GDT_Byte;
107 :
108 1784 : nBlockXSize = poDSIn->nRasterXSize;
109 1784 : nBlockYSize = 1;
110 1784 : }
111 :
112 : /************************************************************************/
113 : /* IReadBlock() */
114 : /************************************************************************/
115 :
116 11676 : CPLErr WEBPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
117 : void *pImage)
118 : {
119 11676 : WEBPDataset *poGDS = cpl::down_cast<WEBPDataset *>(poDS);
120 :
121 11676 : if (poGDS->Uncompress() != CE_None)
122 0 : return CE_Failure;
123 :
124 11676 : GByte *pabyUncompressed =
125 11676 : &poGDS->pabyUncompressed[nBlockYOff * nRasterXSize * poGDS->nBands +
126 11676 : nBand - 1];
127 2885230 : for (int i = 0; i < nRasterXSize; i++)
128 2873560 : reinterpret_cast<GByte *>(pImage)[i] =
129 2873560 : pabyUncompressed[poGDS->nBands * i];
130 :
131 11676 : return CE_None;
132 : }
133 :
134 : /************************************************************************/
135 : /* GetColorInterpretation() */
136 : /************************************************************************/
137 :
138 84 : GDALColorInterp WEBPRasterBand::GetColorInterpretation()
139 :
140 : {
141 84 : if (nBand == 1)
142 27 : return GCI_RedBand;
143 :
144 57 : else if (nBand == 2)
145 27 : return GCI_GreenBand;
146 :
147 30 : else if (nBand == 3)
148 27 : return GCI_BlueBand;
149 :
150 3 : return GCI_AlphaBand;
151 : }
152 :
153 : /************************************************************************/
154 : /* ==================================================================== */
155 : /* WEBPDataset */
156 : /* ==================================================================== */
157 : /************************************************************************/
158 :
159 : /************************************************************************/
160 : /* WEBPDataset() */
161 : /************************************************************************/
162 :
163 490 : WEBPDataset::WEBPDataset()
164 : : fpImage(nullptr), pabyUncompressed(nullptr), bHasBeenUncompressed(FALSE),
165 490 : eUncompressErrRet(CE_None), bHasReadXMPMetadata(FALSE)
166 : {
167 490 : }
168 :
169 : /************************************************************************/
170 : /* ~WEBPDataset() */
171 : /************************************************************************/
172 :
173 980 : WEBPDataset::~WEBPDataset()
174 :
175 : {
176 490 : FlushCache(true);
177 490 : if (fpImage)
178 490 : VSIFCloseL(fpImage);
179 490 : VSIFree(pabyUncompressed);
180 980 : }
181 :
182 : /************************************************************************/
183 : /* GetFileList() */
184 : /************************************************************************/
185 :
186 3 : char **WEBPDataset::GetFileList()
187 : {
188 3 : char **papszFileList = GDALPamDataset::GetFileList();
189 3 : GDALGeoTransform gt;
190 3 : std::string osWorldFilename;
191 3 : CPL_IGNORE_RET_VAL(GetGeoTransform(gt, osWorldFilename));
192 3 : if (!osWorldFilename.empty())
193 : {
194 1 : papszFileList = CSLAddString(papszFileList, osWorldFilename.c_str());
195 : }
196 6 : return papszFileList;
197 : }
198 :
199 : /************************************************************************/
200 : /* GetGeoTransform() */
201 : /************************************************************************/
202 :
203 18 : CPLErr WEBPDataset::GetGeoTransform(GDALGeoTransform >) const
204 : {
205 36 : std::string osWorldFilename;
206 36 : return GetGeoTransform(gt, osWorldFilename);
207 : }
208 :
209 21 : CPLErr WEBPDataset::GetGeoTransform(GDALGeoTransform >,
210 : std::string &osWorldFilename) const
211 : {
212 21 : bool bGeoTransformValid = GDALPamDataset::GetGeoTransform(gt) == CE_None;
213 21 : if (!bGeoTransformValid)
214 : {
215 21 : char *pszWldFilename = nullptr;
216 21 : bGeoTransformValid =
217 21 : GDALReadWorldFile2(GetDescription(), ".wld", gt,
218 18 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
219 18 : GDALReadWorldFile2(GetDescription(), ".wpw", gt,
220 39 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
221 18 : GDALReadWorldFile2(GetDescription(), ".webpw", gt,
222 : oOvManager.GetSiblingFiles(), &pszWldFilename);
223 21 : if (bGeoTransformValid)
224 3 : osWorldFilename = pszWldFilename;
225 21 : CPLFree(pszWldFilename);
226 : }
227 21 : return bGeoTransformValid ? CE_None : CE_Failure;
228 : }
229 :
230 : /************************************************************************/
231 : /* GetMetadataDomainList() */
232 : /************************************************************************/
233 :
234 1 : char **WEBPDataset::GetMetadataDomainList()
235 : {
236 1 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
237 1 : TRUE, "xml:XMP", nullptr);
238 : }
239 :
240 : /************************************************************************/
241 : /* GetMetadata() */
242 : /************************************************************************/
243 :
244 54 : char **WEBPDataset::GetMetadata(const char *pszDomain)
245 : {
246 54 : if ((pszDomain != nullptr && EQUAL(pszDomain, "xml:XMP")) &&
247 4 : !bHasReadXMPMetadata)
248 : {
249 4 : bHasReadXMPMetadata = TRUE;
250 :
251 4 : VSIFSeekL(fpImage, 12, SEEK_SET);
252 :
253 4 : bool bFirst = true;
254 : while (true)
255 : {
256 : char szHeader[5];
257 : GUInt32 nChunkSize;
258 :
259 16 : if (VSIFReadL(szHeader, 1, 4, fpImage) != 4 ||
260 8 : VSIFReadL(&nChunkSize, 1, 4, fpImage) != 4)
261 4 : break;
262 :
263 8 : szHeader[4] = '\0';
264 8 : CPL_LSBPTR32(&nChunkSize);
265 :
266 8 : if (bFirst)
267 : {
268 4 : if (strcmp(szHeader, "VP8X") != 0 || nChunkSize < 10)
269 : break;
270 :
271 : int l_nFlags;
272 2 : if (VSIFReadL(&l_nFlags, 1, 4, fpImage) != 4)
273 0 : break;
274 2 : CPL_LSBPTR32(&l_nFlags);
275 2 : if ((l_nFlags & 8) == 0)
276 0 : break;
277 :
278 2 : VSIFSeekL(fpImage, nChunkSize - 4, SEEK_CUR);
279 :
280 2 : bFirst = false;
281 : }
282 4 : else if (strcmp(szHeader, "META") == 0)
283 : {
284 2 : if (nChunkSize > 1024 * 1024)
285 0 : break;
286 :
287 : char *pszXMP =
288 2 : reinterpret_cast<char *>(VSIMalloc(nChunkSize + 1));
289 2 : if (pszXMP == nullptr)
290 0 : break;
291 :
292 2 : if (static_cast<GUInt32>(VSIFReadL(pszXMP, 1, nChunkSize,
293 2 : fpImage)) != nChunkSize)
294 : {
295 0 : VSIFree(pszXMP);
296 0 : break;
297 : }
298 2 : pszXMP[nChunkSize] = '\0';
299 :
300 : /* Avoid setting the PAM dirty bit just for that */
301 2 : const int nOldPamFlags = nPamFlags;
302 :
303 2 : char *apszMDList[2] = {pszXMP, nullptr};
304 2 : SetMetadata(apszMDList, "xml:XMP");
305 :
306 : // cppcheck-suppress redundantAssignment
307 2 : nPamFlags = nOldPamFlags;
308 :
309 2 : VSIFree(pszXMP);
310 2 : break;
311 : }
312 : else
313 2 : VSIFSeekL(fpImage, nChunkSize, SEEK_CUR);
314 4 : }
315 : }
316 :
317 54 : return GDALPamDataset::GetMetadata(pszDomain);
318 : }
319 :
320 : /************************************************************************/
321 : /* Uncompress() */
322 : /************************************************************************/
323 :
324 12011 : CPLErr WEBPDataset::Uncompress()
325 : {
326 12011 : if (bHasBeenUncompressed)
327 11654 : return eUncompressErrRet;
328 :
329 357 : bHasBeenUncompressed = TRUE;
330 357 : eUncompressErrRet = CE_Failure;
331 :
332 : // To avoid excessive memory allocation attempts
333 : // Normally WebP images are no larger than 16383x16383*4 ~= 1 GB
334 357 : if (nRasterXSize > INT_MAX / (nRasterYSize * nBands))
335 : {
336 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large image");
337 0 : return CE_Failure;
338 : }
339 :
340 357 : pabyUncompressed = reinterpret_cast<GByte *>(
341 357 : VSIMalloc3(nRasterXSize, nRasterYSize, nBands));
342 357 : if (pabyUncompressed == nullptr)
343 0 : return CE_Failure;
344 :
345 357 : VSIFSeekL(fpImage, 0, SEEK_END);
346 357 : vsi_l_offset nSizeLarge = VSIFTellL(fpImage);
347 357 : if (nSizeLarge !=
348 357 : static_cast<vsi_l_offset>(static_cast<uint32_t>(nSizeLarge)))
349 0 : return CE_Failure;
350 357 : VSIFSeekL(fpImage, 0, SEEK_SET);
351 357 : uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
352 357 : uint8_t *pabyCompressed = reinterpret_cast<uint8_t *>(VSIMalloc(nSize));
353 357 : if (pabyCompressed == nullptr)
354 0 : return CE_Failure;
355 357 : VSIFReadL(pabyCompressed, 1, nSize, fpImage);
356 : uint8_t *pRet;
357 :
358 357 : if (nBands == 4)
359 234 : pRet = WebPDecodeRGBAInto(pabyCompressed, static_cast<uint32_t>(nSize),
360 234 : static_cast<uint8_t *>(pabyUncompressed),
361 234 : static_cast<size_t>(nRasterXSize) *
362 234 : nRasterYSize * nBands,
363 234 : nRasterXSize * nBands);
364 : else
365 123 : pRet = WebPDecodeRGBInto(pabyCompressed, static_cast<uint32_t>(nSize),
366 123 : static_cast<uint8_t *>(pabyUncompressed),
367 123 : static_cast<size_t>(nRasterXSize) *
368 123 : nRasterYSize * nBands,
369 123 : nRasterXSize * nBands);
370 :
371 357 : VSIFree(pabyCompressed);
372 357 : if (pRet == nullptr)
373 : {
374 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPDecodeRGBInto() failed");
375 0 : return CE_Failure;
376 : }
377 357 : eUncompressErrRet = CE_None;
378 :
379 357 : return CE_None;
380 : }
381 :
382 : /************************************************************************/
383 : /* IRasterIO() */
384 : /************************************************************************/
385 :
386 339 : CPLErr WEBPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
387 : int nXSize, int nYSize, void *pData,
388 : int nBufXSize, int nBufYSize,
389 : GDALDataType eBufType, int nBandCount,
390 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
391 : GSpacing nLineSpace, GSpacing nBandSpace,
392 : GDALRasterIOExtraArg *psExtraArg)
393 :
394 : {
395 339 : if ((eRWFlag == GF_Read) && (nBandCount == nBands) && (nXOff == 0) &&
396 339 : (nYOff == 0) && (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
397 335 : (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
398 1013 : (eBufType == GDT_Byte) && (pData != nullptr) &&
399 335 : IsAllBands(nBandCount, panBandMap))
400 : {
401 335 : if (Uncompress() != CE_None)
402 0 : return CE_Failure;
403 335 : if (nPixelSpace == nBands && nLineSpace == (nPixelSpace * nXSize) &&
404 : nBandSpace == 1)
405 : {
406 95 : memcpy(pData, pabyUncompressed,
407 95 : static_cast<size_t>(nBands) * nXSize * nYSize);
408 : }
409 : else
410 : {
411 8024 : for (int y = 0; y < nYSize; ++y)
412 : {
413 7784 : GByte *pabyScanline = pabyUncompressed + y * nBands * nXSize;
414 1101290 : for (int x = 0; x < nXSize; ++x)
415 : {
416 5276670 : for (int iBand = 0; iBand < nBands; iBand++)
417 : reinterpret_cast<GByte *>(
418 4183170 : pData)[(y * nLineSpace) + (x * nPixelSpace) +
419 4183170 : iBand * nBandSpace] =
420 4183170 : pabyScanline[x * nBands + iBand];
421 : }
422 : }
423 : }
424 :
425 335 : return CE_None;
426 : }
427 :
428 4 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
429 : pData, nBufXSize, nBufYSize, eBufType,
430 : nBandCount, panBandMap, nPixelSpace,
431 4 : nLineSpace, nBandSpace, psExtraArg);
432 : }
433 :
434 : /************************************************************************/
435 : /* GetCompressionFormats() */
436 : /************************************************************************/
437 :
438 0 : CPLStringList WEBPDataset::GetCompressionFormats(int nXOff, int nYOff,
439 : int nXSize, int nYSize,
440 : int nBandCount,
441 : const int *panBandList)
442 : {
443 0 : CPLStringList aosRet;
444 0 : if (nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
445 0 : nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
446 : {
447 0 : aosRet.AddString("WEBP");
448 : }
449 0 : return aosRet;
450 : }
451 :
452 : /************************************************************************/
453 : /* ReadCompressedData() */
454 : /************************************************************************/
455 :
456 2 : CPLErr WEBPDataset::ReadCompressedData(const char *pszFormat, int nXOff,
457 : int nYOff, int nXSize, int nYSize,
458 : int nBandCount, const int *panBandList,
459 : void **ppBuffer, size_t *pnBufferSize,
460 : char **ppszDetailedFormat)
461 : {
462 2 : if (nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
463 4 : nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
464 : {
465 2 : const CPLStringList aosTokens(CSLTokenizeString2(pszFormat, ";", 0));
466 2 : if (aosTokens.size() != 1)
467 0 : return CE_Failure;
468 :
469 2 : if (EQUAL(aosTokens[0], "WEBP"))
470 : {
471 2 : if (ppszDetailedFormat)
472 0 : *ppszDetailedFormat = VSIStrdup("WEBP");
473 2 : VSIFSeekL(fpImage, 0, SEEK_END);
474 2 : const auto nFileSize = VSIFTellL(fpImage);
475 2 : if (nFileSize > std::numeric_limits<uint32_t>::max())
476 0 : return CE_Failure;
477 2 : auto nSize = static_cast<uint32_t>(nFileSize);
478 2 : if (ppBuffer)
479 : {
480 2 : if (!pnBufferSize)
481 0 : return CE_Failure;
482 2 : bool bFreeOnError = false;
483 2 : if (*ppBuffer)
484 : {
485 0 : if (*pnBufferSize < nSize)
486 0 : return CE_Failure;
487 : }
488 : else
489 : {
490 2 : *ppBuffer = VSI_MALLOC_VERBOSE(nSize);
491 2 : if (*ppBuffer == nullptr)
492 0 : return CE_Failure;
493 2 : bFreeOnError = true;
494 : }
495 2 : VSIFSeekL(fpImage, 0, SEEK_SET);
496 2 : if (VSIFReadL(*ppBuffer, nSize, 1, fpImage) != 1)
497 : {
498 0 : if (bFreeOnError)
499 : {
500 0 : VSIFree(*ppBuffer);
501 0 : *ppBuffer = nullptr;
502 : }
503 0 : return CE_Failure;
504 : }
505 :
506 : // Remove META box
507 2 : if (nSize > 12 && memcmp(*ppBuffer, "RIFF", 4) == 0)
508 : {
509 2 : size_t nPos = 12;
510 2 : GByte *pabyData = static_cast<GByte *>(*ppBuffer);
511 6 : while (nPos <= nSize - 8)
512 : {
513 4 : char szBoxName[5] = {0, 0, 0, 0, 0};
514 4 : memcpy(szBoxName, pabyData + nPos, 4);
515 : uint32_t nChunkSize;
516 4 : memcpy(&nChunkSize, pabyData + nPos + 4, 4);
517 4 : CPL_LSBPTR32(&nChunkSize);
518 4 : if (nChunkSize % 2) // Payload padding if needed
519 1 : nChunkSize++;
520 4 : if (nChunkSize > nSize - (nPos + 8))
521 0 : break;
522 4 : if (memcmp(szBoxName, "META", 4) == 0)
523 : {
524 1 : CPLDebug("WEBP",
525 : "Remove existing %s box from "
526 : "source compressed data",
527 : szBoxName);
528 1 : if (nPos + 8 + nChunkSize < nSize)
529 : {
530 0 : memmove(pabyData + nPos,
531 0 : pabyData + nPos + 8 + nChunkSize,
532 0 : nSize - (nPos + 8 + nChunkSize));
533 : }
534 1 : nSize -= 8 + nChunkSize;
535 : }
536 : else
537 : {
538 3 : nPos += 8 + nChunkSize;
539 : }
540 : }
541 :
542 : // Patch size of RIFF
543 2 : uint32_t nSize32 = nSize - 8;
544 2 : CPL_LSBPTR32(&nSize32);
545 2 : memcpy(pabyData + 4, &nSize32, 4);
546 : }
547 : }
548 2 : if (pnBufferSize)
549 2 : *pnBufferSize = nSize;
550 2 : return CE_None;
551 : }
552 : }
553 0 : return CE_Failure;
554 : }
555 :
556 : /************************************************************************/
557 : /* OpenPAM() */
558 : /************************************************************************/
559 :
560 490 : GDALPamDataset *WEBPDataset::OpenPAM(GDALOpenInfo *poOpenInfo)
561 :
562 : {
563 490 : if (!WEBPDriverIdentify(poOpenInfo) || poOpenInfo->fpL == nullptr)
564 0 : return nullptr;
565 :
566 : int nWidth, nHeight;
567 490 : if (!WebPGetInfo(reinterpret_cast<const uint8_t *>(poOpenInfo->pabyHeader),
568 490 : static_cast<uint32_t>(poOpenInfo->nHeaderBytes), &nWidth,
569 : &nHeight))
570 0 : return nullptr;
571 :
572 490 : int nBands = 3;
573 :
574 980 : auto poDS = std::make_unique<WEBPDataset>();
575 :
576 : #if WEBP_DECODER_ABI_VERSION >= 0x0002
577 : WebPDecoderConfig config;
578 490 : if (!WebPInitDecoderConfig(&config))
579 0 : return nullptr;
580 :
581 : const bool bOK =
582 490 : WebPGetFeatures(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes,
583 490 : &config.input) == VP8_STATUS_OK;
584 :
585 : // Cf commit https://github.com/webmproject/libwebp/commit/86c0031eb2c24f78d4dcfc5dab752ebc9f511607#diff-859d219dccb3163cc11cd538effed461ff0145135070abfe70bd263f16408023
586 : // Added in webp 0.4.0
587 : #if WEBP_DECODER_ABI_VERSION >= 0x0202
588 980 : poDS->GDALDataset::SetMetadataItem(
589 : "COMPRESSION_REVERSIBILITY",
590 490 : config.input.format == 2 ? "LOSSLESS" : "LOSSY", "IMAGE_STRUCTURE");
591 : #endif
592 :
593 684 : if (config.input.has_alpha ||
594 194 : CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
595 : "FORCE_4BANDS", "NO")))
596 314 : nBands = 4;
597 :
598 490 : WebPFreeDecBuffer(&config.output);
599 :
600 490 : if (!bOK)
601 0 : return nullptr;
602 :
603 : #endif
604 :
605 490 : if (poOpenInfo->eAccess == GA_Update)
606 : {
607 0 : ReportUpdateNotSupportedByDriver("WEBP");
608 0 : return nullptr;
609 : }
610 :
611 : /* -------------------------------------------------------------------- */
612 : /* Create a corresponding GDALDataset. */
613 : /* -------------------------------------------------------------------- */
614 490 : poDS->nRasterXSize = nWidth;
615 490 : poDS->nRasterYSize = nHeight;
616 490 : poDS->fpImage = poOpenInfo->fpL;
617 490 : poOpenInfo->fpL = nullptr;
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Create band information objects. */
621 : /* -------------------------------------------------------------------- */
622 2274 : for (int iBand = 0; iBand < nBands; iBand++)
623 1784 : poDS->SetBand(iBand + 1, new WEBPRasterBand(poDS.get(), iBand + 1));
624 :
625 : /* -------------------------------------------------------------------- */
626 : /* Initialize any PAM information. */
627 : /* -------------------------------------------------------------------- */
628 490 : poDS->SetDescription(poOpenInfo->pszFilename);
629 :
630 490 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
631 :
632 : /* -------------------------------------------------------------------- */
633 : /* Open overviews. */
634 : /* -------------------------------------------------------------------- */
635 980 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
636 490 : poOpenInfo->GetSiblingFiles());
637 :
638 490 : return poDS.release();
639 : }
640 :
641 : /************************************************************************/
642 : /* Open() */
643 : /************************************************************************/
644 :
645 383 : GDALDataset *WEBPDataset::Open(GDALOpenInfo *poOpenInfo)
646 :
647 : {
648 383 : return OpenPAM(poOpenInfo);
649 : }
650 :
651 : /************************************************************************/
652 : /* WebPUserData */
653 : /************************************************************************/
654 :
655 : typedef struct
656 : {
657 : VSILFILE *fp;
658 : GDALProgressFunc pfnProgress;
659 : void *pProgressData;
660 : } WebPUserData;
661 :
662 : /************************************************************************/
663 : /* WEBPDatasetWriter() */
664 : /************************************************************************/
665 :
666 855 : static int WEBPDatasetWriter(const uint8_t *data, size_t data_size,
667 : const WebPPicture *const picture)
668 : {
669 855 : WebPUserData *pUserData =
670 : reinterpret_cast<WebPUserData *>(picture->custom_ptr);
671 855 : return VSIFWriteL(data, 1, data_size, pUserData->fp) == data_size;
672 : }
673 :
674 : /************************************************************************/
675 : /* WEBPDatasetProgressHook() */
676 : /************************************************************************/
677 :
678 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
679 1038 : static int WEBPDatasetProgressHook(int percent,
680 : const WebPPicture *const picture)
681 : {
682 1038 : WebPUserData *pUserData =
683 : reinterpret_cast<WebPUserData *>(picture->custom_ptr);
684 1038 : return pUserData->pfnProgress(percent / 100.0, nullptr,
685 1038 : pUserData->pProgressData);
686 : }
687 : #endif
688 :
689 : /************************************************************************/
690 : /* CreateCopy() */
691 : /************************************************************************/
692 :
693 134 : GDALDataset *WEBPDataset::CreateCopy(const char *pszFilename,
694 : GDALDataset *poSrcDS, int bStrict,
695 : char **papszOptions,
696 : GDALProgressFunc pfnProgress,
697 : void *pProgressData)
698 :
699 : {
700 : const char *pszLossLessCopy =
701 134 : CSLFetchNameValueDef(papszOptions, "LOSSLESS_COPY", "AUTO");
702 134 : if (EQUAL(pszLossLessCopy, "AUTO") || CPLTestBool(pszLossLessCopy))
703 : {
704 134 : void *pWEBPContent = nullptr;
705 134 : size_t nWEBPContent = 0;
706 134 : if (poSrcDS->ReadCompressedData(
707 : "WEBP", 0, 0, poSrcDS->GetRasterXSize(),
708 : poSrcDS->GetRasterYSize(), poSrcDS->GetRasterCount(), nullptr,
709 268 : &pWEBPContent, &nWEBPContent, nullptr) == CE_None)
710 : {
711 2 : CPLDebug("WEBP", "Lossless copy from source dataset");
712 2 : std::vector<GByte> abyData;
713 : try
714 : {
715 2 : abyData.assign(static_cast<const GByte *>(pWEBPContent),
716 2 : static_cast<const GByte *>(pWEBPContent) +
717 2 : nWEBPContent);
718 :
719 2 : char **papszXMP = poSrcDS->GetMetadata("xml:XMP");
720 2 : if (papszXMP && papszXMP[0])
721 : {
722 : GByte abyChunkHeader[8];
723 1 : memcpy(abyChunkHeader, "META", 4);
724 1 : const size_t nXMPSize = strlen(papszXMP[0]);
725 1 : uint32_t nChunkSize = static_cast<uint32_t>(nXMPSize);
726 1 : CPL_LSBPTR32(&nChunkSize);
727 1 : memcpy(abyChunkHeader + 4, &nChunkSize, 4);
728 0 : abyData.insert(abyData.end(), abyChunkHeader,
729 1 : abyChunkHeader + sizeof(abyChunkHeader));
730 : abyData.insert(
731 0 : abyData.end(), reinterpret_cast<GByte *>(papszXMP[0]),
732 1 : reinterpret_cast<GByte *>(papszXMP[0]) + nXMPSize);
733 1 : if ((abyData.size() % 2) != 0) // Payload padding if needed
734 1 : abyData.push_back(0);
735 :
736 : // Patch size of RIFF
737 : uint32_t nSize32 =
738 1 : static_cast<uint32_t>(abyData.size()) - 8;
739 1 : CPL_LSBPTR32(&nSize32);
740 1 : memcpy(abyData.data() + 4, &nSize32, 4);
741 : }
742 : }
743 0 : catch (const std::exception &e)
744 : {
745 0 : CPLError(CE_Failure, CPLE_AppDefined, "Exception occurred: %s",
746 0 : e.what());
747 0 : abyData.clear();
748 : }
749 2 : VSIFree(pWEBPContent);
750 :
751 2 : if (!abyData.empty())
752 : {
753 : auto fpImage(
754 2 : CPLTestBool(CSLFetchNameValueDef(
755 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME",
756 : "NO"))
757 0 : ? VSIFileManager::GetHandler(pszFilename)
758 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true,
759 0 : nullptr)
760 4 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
761 2 : if (fpImage == nullptr)
762 : {
763 0 : CPLError(CE_Failure, CPLE_OpenFailed,
764 : "Unable to create jpeg file %s.", pszFilename);
765 :
766 0 : return nullptr;
767 : }
768 4 : if (fpImage->Write(abyData.data(), 1, abyData.size()) !=
769 2 : abyData.size())
770 : {
771 0 : CPLError(CE_Failure, CPLE_FileIO,
772 0 : "Failure writing data: %s", VSIStrerror(errno));
773 0 : fpImage->CancelCreation();
774 0 : return nullptr;
775 : }
776 :
777 2 : if (fpImage->Close() != 0)
778 : {
779 0 : CPLError(CE_Failure, CPLE_FileIO,
780 : "Error at file closing of '%s': %s", pszFilename,
781 0 : VSIStrerror(errno));
782 0 : return nullptr;
783 : }
784 :
785 2 : pfnProgress(1.0, nullptr, pProgressData);
786 :
787 : // Re-open file and clone missing info to PAM
788 2 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
789 2 : auto poDS = OpenPAM(&oOpenInfo);
790 2 : if (poDS)
791 : {
792 2 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
793 : }
794 :
795 2 : return poDS;
796 : }
797 : }
798 : }
799 :
800 132 : const bool bLossless = CPLFetchBool(papszOptions, "LOSSLESS", false);
801 255 : if (!bLossless &&
802 123 : (!EQUAL(pszLossLessCopy, "AUTO") && CPLTestBool(pszLossLessCopy)))
803 : {
804 0 : CPLError(CE_Failure, CPLE_AppDefined,
805 : "LOSSLESS_COPY=YES requested but not possible");
806 0 : return nullptr;
807 : }
808 :
809 : /* -------------------------------------------------------------------- */
810 : /* WEBP library initialization */
811 : /* -------------------------------------------------------------------- */
812 :
813 : WebPPicture sPicture;
814 132 : if (!WebPPictureInit(&sPicture))
815 : {
816 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed");
817 0 : return nullptr;
818 : }
819 :
820 : /* -------------------------------------------------------------------- */
821 : /* Some some rudimentary checks */
822 : /* -------------------------------------------------------------------- */
823 :
824 132 : const int nXSize = poSrcDS->GetRasterXSize();
825 132 : const int nYSize = poSrcDS->GetRasterYSize();
826 132 : if (nXSize > 16383 || nYSize > 16383)
827 : {
828 0 : CPLError(CE_Failure, CPLE_NotSupported,
829 : "WEBP maximum image dimensions are 16383 x 16383.");
830 :
831 0 : return nullptr;
832 : }
833 :
834 132 : const int nBands = poSrcDS->GetRasterCount();
835 132 : if (nBands != 3
836 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
837 96 : && nBands != 4
838 : #endif
839 : )
840 : {
841 14 : CPLError(CE_Failure, CPLE_NotSupported,
842 : "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
843 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
844 : "or 4 (RGBA) "
845 : #endif
846 : "bands.",
847 : nBands);
848 :
849 14 : return nullptr;
850 : }
851 :
852 118 : const GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
853 :
854 118 : if (eDT != GDT_Byte)
855 : {
856 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
857 : "WEBP driver doesn't support data type %s. "
858 : "Only eight bit byte bands supported.",
859 : GDALGetDataTypeName(
860 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
861 :
862 0 : if (bStrict)
863 0 : return nullptr;
864 : }
865 :
866 : /* -------------------------------------------------------------------- */
867 : /* What options has the user selected? */
868 : /* -------------------------------------------------------------------- */
869 118 : float fQuality = 75.0f;
870 118 : const char *pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY");
871 118 : if (pszQUALITY != nullptr)
872 : {
873 91 : fQuality = static_cast<float>(CPLAtof(pszQUALITY));
874 91 : if (fQuality < 0.0f || fQuality > 100.0f)
875 : {
876 0 : CPLError(CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.",
877 : "QUALITY", pszQUALITY);
878 0 : return nullptr;
879 : }
880 : }
881 :
882 118 : WebPPreset nPreset = WEBP_PRESET_DEFAULT;
883 : const char *pszPRESET =
884 118 : CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT");
885 118 : if (EQUAL(pszPRESET, "DEFAULT"))
886 118 : nPreset = WEBP_PRESET_DEFAULT;
887 0 : else if (EQUAL(pszPRESET, "PICTURE"))
888 0 : nPreset = WEBP_PRESET_PICTURE;
889 0 : else if (EQUAL(pszPRESET, "PHOTO"))
890 0 : nPreset = WEBP_PRESET_PHOTO;
891 0 : else if (EQUAL(pszPRESET, "PICTURE"))
892 0 : nPreset = WEBP_PRESET_PICTURE;
893 0 : else if (EQUAL(pszPRESET, "DRAWING"))
894 0 : nPreset = WEBP_PRESET_DRAWING;
895 0 : else if (EQUAL(pszPRESET, "ICON"))
896 0 : nPreset = WEBP_PRESET_ICON;
897 0 : else if (EQUAL(pszPRESET, "TEXT"))
898 0 : nPreset = WEBP_PRESET_TEXT;
899 : else
900 : {
901 0 : CPLError(CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.",
902 : "PRESET", pszPRESET);
903 0 : return nullptr;
904 : }
905 :
906 : WebPConfig sConfig;
907 118 : if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality,
908 : WEBP_ENCODER_ABI_VERSION))
909 : {
910 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed");
911 0 : return nullptr;
912 : }
913 :
914 : // TODO: Get rid of this macro in a reasonable way.
915 : #define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \
916 : { \
917 : const char *pszVal = CSLFetchNameValue(papszOptions, name); \
918 : if (pszVal != nullptr) \
919 : { \
920 : sConfig.fieldname = atoi(pszVal); \
921 : if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \
922 : { \
923 : CPLError(CE_Failure, CPLE_IllegalArg, \
924 : "%s=%s is not a legal value.", name, pszVal); \
925 : return nullptr; \
926 : } \
927 : } \
928 : }
929 :
930 118 : FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX - 1);
931 :
932 118 : const char *pszPSNR = CSLFetchNameValue(papszOptions, "PSNR");
933 118 : if (pszPSNR)
934 : {
935 0 : sConfig.target_PSNR = static_cast<float>(CPLAtof(pszPSNR));
936 0 : if (sConfig.target_PSNR < 0)
937 : {
938 0 : CPLError(CE_Failure, CPLE_IllegalArg,
939 : "PSNR=%s is not a legal value.", pszPSNR);
940 0 : return nullptr;
941 : }
942 : }
943 :
944 118 : FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6);
945 118 : FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4);
946 118 : FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100);
947 118 : FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100);
948 118 : FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7);
949 118 : FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1);
950 118 : FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1);
951 118 : FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10);
952 118 : FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1);
953 118 : FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3);
954 : #if WEBP_ENCODER_ABI_VERSION >= 0x0002
955 118 : FETCH_AND_SET_OPTION_INT("PARTITION_LIMIT", partition_limit, 0, 100);
956 : #endif
957 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
958 118 : sConfig.lossless = bLossless;
959 118 : if (sConfig.lossless)
960 9 : sPicture.use_argb = 1;
961 : #endif
962 : #if WEBP_ENCODER_ABI_VERSION >= 0x0209
963 118 : FETCH_AND_SET_OPTION_INT("EXACT", exact, 0, 1);
964 : #endif
965 :
966 118 : if (!WebPValidateConfig(&sConfig))
967 : {
968 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed");
969 0 : return nullptr;
970 : }
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Allocate memory */
974 : /* -------------------------------------------------------------------- */
975 : GByte *pabyBuffer =
976 118 : static_cast<GByte *>(VSI_MALLOC3_VERBOSE(nBands, nXSize, nYSize));
977 118 : if (pabyBuffer == nullptr)
978 : {
979 0 : return nullptr;
980 : }
981 :
982 : /* -------------------------------------------------------------------- */
983 : /* Create the dataset. */
984 : /* -------------------------------------------------------------------- */
985 : auto fpImage(
986 118 : CPLTestBool(CSLFetchNameValueDef(
987 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"))
988 1 : ? VSIFileManager::GetHandler(pszFilename)
989 1 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
990 237 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
991 118 : if (fpImage == nullptr)
992 : {
993 3 : CPLError(CE_Failure, CPLE_OpenFailed,
994 : "Unable to create WEBP file %s.\n", pszFilename);
995 3 : VSIFree(pabyBuffer);
996 3 : return nullptr;
997 : }
998 :
999 : WebPUserData sUserData;
1000 115 : sUserData.fp = fpImage.get();
1001 115 : sUserData.pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1002 115 : sUserData.pProgressData = pProgressData;
1003 :
1004 : /* -------------------------------------------------------------------- */
1005 : /* WEBP library settings */
1006 : /* -------------------------------------------------------------------- */
1007 :
1008 115 : sPicture.width = nXSize;
1009 115 : sPicture.height = nYSize;
1010 115 : sPicture.writer = WEBPDatasetWriter;
1011 115 : sPicture.custom_ptr = &sUserData;
1012 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1013 115 : sPicture.progress_hook = WEBPDatasetProgressHook;
1014 : #endif
1015 115 : if (!WebPPictureAlloc(&sPicture))
1016 : {
1017 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed");
1018 0 : VSIFree(pabyBuffer);
1019 0 : fpImage->CancelCreation();
1020 0 : return nullptr;
1021 : }
1022 :
1023 : /* -------------------------------------------------------------------- */
1024 : /* Acquire source imagery. */
1025 : /* -------------------------------------------------------------------- */
1026 : CPLErr eErr =
1027 230 : poSrcDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, pabyBuffer, nXSize,
1028 : nYSize, GDT_Byte, nBands, nullptr, nBands,
1029 115 : static_cast<GSpacing>(nBands) * nXSize, 1, nullptr);
1030 :
1031 : /* -------------------------------------------------------------------- */
1032 : /* Import and write to file */
1033 : /* -------------------------------------------------------------------- */
1034 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1035 115 : if (eErr == CE_None && nBands == 4)
1036 : {
1037 82 : if (!WebPPictureImportRGBA(&sPicture, pabyBuffer, nBands * nXSize))
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_AppDefined,
1040 : "WebPPictureImportRGBA() failed");
1041 0 : eErr = CE_Failure;
1042 : }
1043 : }
1044 : else
1045 : #endif
1046 66 : if (eErr == CE_None &&
1047 33 : !WebPPictureImportRGB(&sPicture, pabyBuffer, nBands * nXSize))
1048 : {
1049 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed");
1050 0 : eErr = CE_Failure;
1051 : }
1052 :
1053 115 : if (pfnProgress)
1054 115 : pfnProgress(0.5, "", pProgressData);
1055 :
1056 115 : if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture))
1057 : {
1058 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1059 10 : const char *pszErrorMsg = nullptr;
1060 10 : switch (sPicture.error_code)
1061 : {
1062 0 : case VP8_ENC_ERROR_OUT_OF_MEMORY:
1063 0 : pszErrorMsg = "Out of memory";
1064 0 : break;
1065 0 : case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
1066 0 : pszErrorMsg = "Out of memory while flushing bits";
1067 0 : break;
1068 0 : case VP8_ENC_ERROR_NULL_PARAMETER:
1069 0 : pszErrorMsg = "A pointer parameter is NULL";
1070 0 : break;
1071 0 : case VP8_ENC_ERROR_INVALID_CONFIGURATION:
1072 0 : pszErrorMsg = "Configuration is invalid";
1073 0 : break;
1074 0 : case VP8_ENC_ERROR_BAD_DIMENSION:
1075 0 : pszErrorMsg = "Picture has invalid width/height";
1076 0 : break;
1077 0 : case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
1078 0 : pszErrorMsg = "Partition is bigger than 512k. Try using less "
1079 : "SEGMENTS, or increase PARTITION_LIMIT value";
1080 0 : break;
1081 0 : case VP8_ENC_ERROR_PARTITION_OVERFLOW:
1082 0 : pszErrorMsg = "Partition is bigger than 16M";
1083 0 : break;
1084 5 : case VP8_ENC_ERROR_BAD_WRITE:
1085 5 : pszErrorMsg = "Error while flushing bytes";
1086 5 : break;
1087 0 : case VP8_ENC_ERROR_FILE_TOO_BIG:
1088 0 : pszErrorMsg = "File is bigger than 4G";
1089 0 : break;
1090 0 : case VP8_ENC_ERROR_USER_ABORT:
1091 0 : pszErrorMsg = "User interrupted";
1092 0 : break;
1093 5 : default:
1094 5 : CPLError(CE_Failure, CPLE_AppDefined,
1095 : "WebPEncode returned an unknown error code: %d",
1096 5 : sPicture.error_code);
1097 5 : pszErrorMsg = "Unknown WebP error type.";
1098 5 : break;
1099 : }
1100 10 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed : %s",
1101 : pszErrorMsg);
1102 : #else
1103 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed");
1104 : #endif
1105 10 : eErr = CE_Failure;
1106 : }
1107 :
1108 : /* -------------------------------------------------------------------- */
1109 : /* Cleanup and close. */
1110 : /* -------------------------------------------------------------------- */
1111 115 : CPLFree(pabyBuffer);
1112 :
1113 115 : WebPPictureFree(&sPicture);
1114 :
1115 115 : if (eErr == CE_None)
1116 : {
1117 105 : if (fpImage->Close() != 0)
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_FileIO,
1120 : "Error at file closing of '%s': %s", pszFilename,
1121 0 : VSIStrerror(errno));
1122 0 : eErr = CE_Failure;
1123 : }
1124 : }
1125 : else
1126 : {
1127 10 : fpImage->CancelCreation();
1128 10 : fpImage.reset();
1129 : }
1130 :
1131 115 : if (pfnProgress)
1132 115 : pfnProgress(1.0, "", pProgressData);
1133 :
1134 115 : if (eErr != CE_None)
1135 : {
1136 10 : VSIUnlink(pszFilename);
1137 10 : return nullptr;
1138 : }
1139 :
1140 : // Do we need a world file?
1141 105 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
1142 : {
1143 1 : GDALGeoTransform gt;
1144 1 : poSrcDS->GetGeoTransform(gt);
1145 1 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
1146 : }
1147 :
1148 : /* -------------------------------------------------------------------- */
1149 : /* Re-open dataset, and copy any auxiliary pam information. */
1150 : /* -------------------------------------------------------------------- */
1151 210 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1152 :
1153 : /* If writing to stdout, we can't reopen it, so return */
1154 : /* a fake dataset to make the caller happy */
1155 105 : CPLPushErrorHandler(CPLQuietErrorHandler);
1156 105 : auto poDS = WEBPDataset::OpenPAM(&oOpenInfo);
1157 105 : CPLPopErrorHandler();
1158 105 : if (poDS)
1159 : {
1160 105 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1161 105 : return poDS;
1162 : }
1163 :
1164 0 : return nullptr;
1165 : }
1166 :
1167 : /************************************************************************/
1168 : /* GDALRegister_WEBP() */
1169 : /************************************************************************/
1170 :
1171 12 : void GDALRegister_WEBP()
1172 :
1173 : {
1174 12 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
1175 0 : return;
1176 :
1177 12 : GDALDriver *poDriver = new GDALDriver();
1178 12 : WEBPDriverSetCommonMetadata(poDriver);
1179 :
1180 12 : poDriver->pfnOpen = WEBPDataset::Open;
1181 12 : poDriver->pfnCreateCopy = WEBPDataset::CreateCopy;
1182 :
1183 12 : GetGDALDriverManager()->RegisterDriver(poDriver);
1184 : }
|