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