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 : CSLConstList 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 1898 : WEBPRasterBand::WEBPRasterBand(WEBPDataset *poDSIn, int)
105 : {
106 1898 : poDS = poDSIn;
107 :
108 1898 : eDataType = GDT_UInt8;
109 :
110 1898 : nBlockXSize = poDSIn->nRasterXSize;
111 1898 : nBlockYSize = 1;
112 1898 : }
113 :
114 : /************************************************************************/
115 : /* IReadBlock() */
116 : /************************************************************************/
117 :
118 14840 : CPLErr WEBPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
119 : void *pImage)
120 : {
121 14840 : WEBPDataset *poGDS = cpl::down_cast<WEBPDataset *>(poDS);
122 :
123 14840 : if (poGDS->Uncompress() != CE_None)
124 0 : return CE_Failure;
125 :
126 14840 : GByte *pabyUncompressed =
127 14840 : &poGDS->pabyUncompressed[nBlockYOff * nRasterXSize * poGDS->nBands +
128 14840 : nBand - 1];
129 3698380 : for (int i = 0; i < nRasterXSize; i++)
130 3683540 : reinterpret_cast<GByte *>(pImage)[i] =
131 3683540 : pabyUncompressed[poGDS->nBands * i];
132 :
133 14840 : return CE_None;
134 : }
135 :
136 : /************************************************************************/
137 : /* GetColorInterpretation() */
138 : /************************************************************************/
139 :
140 173 : GDALColorInterp WEBPRasterBand::GetColorInterpretation()
141 :
142 : {
143 173 : if (nBand == 1)
144 43 : return GCI_RedBand;
145 :
146 130 : else if (nBand == 2)
147 43 : return GCI_GreenBand;
148 :
149 87 : else if (nBand == 3)
150 43 : return GCI_BlueBand;
151 :
152 44 : return GCI_AlphaBand;
153 : }
154 :
155 : /************************************************************************/
156 : /* ==================================================================== */
157 : /* WEBPDataset */
158 : /* ==================================================================== */
159 : /************************************************************************/
160 :
161 : /************************************************************************/
162 : /* WEBPDataset() */
163 : /************************************************************************/
164 :
165 521 : WEBPDataset::WEBPDataset()
166 : : fpImage(nullptr), pabyUncompressed(nullptr), bHasBeenUncompressed(FALSE),
167 521 : eUncompressErrRet(CE_None), bHasReadXMPMetadata(FALSE)
168 : {
169 521 : }
170 :
171 : /************************************************************************/
172 : /* ~WEBPDataset() */
173 : /************************************************************************/
174 :
175 1042 : WEBPDataset::~WEBPDataset()
176 :
177 : {
178 521 : WEBPDataset::Close();
179 521 : VSIFree(pabyUncompressed);
180 1042 : }
181 :
182 : /************************************************************************/
183 : /* Close() */
184 : /************************************************************************/
185 :
186 670 : CPLErr WEBPDataset::Close(GDALProgressFunc, void *)
187 : {
188 670 : CPLErr eErr = CE_None;
189 :
190 670 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
191 : {
192 521 : eErr = WEBPDataset::FlushCache(true);
193 :
194 521 : if (fpImage != nullptr && VSIFCloseL(fpImage) != 0)
195 0 : eErr = CE_Failure;
196 521 : fpImage = nullptr;
197 :
198 521 : eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
199 : }
200 670 : 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 65 : CPLErr WEBPDataset::GetGeoTransform(GDALGeoTransform >) const
225 : {
226 130 : std::string osWorldFilename;
227 130 : return GetGeoTransform(gt, osWorldFilename);
228 : }
229 :
230 68 : CPLErr WEBPDataset::GetGeoTransform(GDALGeoTransform >,
231 : std::string &osWorldFilename) const
232 : {
233 68 : bool bGeoTransformValid = GDALPamDataset::GetGeoTransform(gt) == CE_None;
234 68 : if (!bGeoTransformValid)
235 : {
236 68 : char *pszWldFilename = nullptr;
237 68 : bGeoTransformValid =
238 68 : GDALReadWorldFile2(GetDescription(), ".wld", gt,
239 65 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
240 65 : GDALReadWorldFile2(GetDescription(), ".wpw", gt,
241 133 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
242 65 : GDALReadWorldFile2(GetDescription(), ".webpw", gt,
243 : oOvManager.GetSiblingFiles(), &pszWldFilename);
244 68 : if (bGeoTransformValid)
245 3 : osWorldFilename = pszWldFilename;
246 68 : CPLFree(pszWldFilename);
247 : }
248 68 : 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 15175 : CPLErr WEBPDataset::Uncompress()
348 : {
349 15175 : if (bHasBeenUncompressed)
350 14804 : return eUncompressErrRet;
351 :
352 371 : bHasBeenUncompressed = TRUE;
353 371 : eUncompressErrRet = CE_Failure;
354 :
355 : // To avoid excessive memory allocation attempts
356 : // Normally WebP images are no larger than 16383x16383*4 ~= 1 GB
357 371 : 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 371 : pabyUncompressed = reinterpret_cast<GByte *>(
364 371 : VSIMalloc3(nRasterXSize, nRasterYSize, nBands));
365 371 : if (pabyUncompressed == nullptr)
366 0 : return CE_Failure;
367 :
368 371 : VSIFSeekL(fpImage, 0, SEEK_END);
369 371 : vsi_l_offset nSizeLarge = VSIFTellL(fpImage);
370 371 : if (nSizeLarge !=
371 371 : static_cast<vsi_l_offset>(static_cast<uint32_t>(nSizeLarge)))
372 0 : return CE_Failure;
373 371 : VSIFSeekL(fpImage, 0, SEEK_SET);
374 371 : uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
375 371 : uint8_t *pabyCompressed = reinterpret_cast<uint8_t *>(VSIMalloc(nSize));
376 371 : if (pabyCompressed == nullptr)
377 0 : return CE_Failure;
378 371 : VSIFReadL(pabyCompressed, 1, nSize, fpImage);
379 : uint8_t *pRet;
380 :
381 371 : if (nBands == 4)
382 244 : pRet = WebPDecodeRGBAInto(pabyCompressed, static_cast<uint32_t>(nSize),
383 244 : static_cast<uint8_t *>(pabyUncompressed),
384 244 : static_cast<size_t>(nRasterXSize) *
385 244 : nRasterYSize * nBands,
386 244 : nRasterXSize * nBands);
387 : else
388 127 : pRet = WebPDecodeRGBInto(pabyCompressed, static_cast<uint32_t>(nSize),
389 127 : static_cast<uint8_t *>(pabyUncompressed),
390 127 : static_cast<size_t>(nRasterXSize) *
391 127 : nRasterYSize * nBands,
392 127 : nRasterXSize * nBands);
393 :
394 371 : VSIFree(pabyCompressed);
395 371 : if (pRet == nullptr)
396 : {
397 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPDecodeRGBInto() failed");
398 0 : return CE_Failure;
399 : }
400 371 : eUncompressErrRet = CE_None;
401 :
402 371 : return CE_None;
403 : }
404 :
405 : /************************************************************************/
406 : /* IRasterIO() */
407 : /************************************************************************/
408 :
409 349 : 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 349 : if ((eRWFlag == GF_Read) && (nBandCount == nBands) && (nXOff == 0) &&
419 339 : (nYOff == 0) && (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
420 335 : (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
421 1033 : (eBufType == GDT_UInt8) && (pData != nullptr) &&
422 335 : IsAllBands(nBandCount, panBandMap))
423 : {
424 335 : if (Uncompress() != CE_None)
425 0 : return CE_Failure;
426 335 : if (nPixelSpace == nBands && nLineSpace == (nPixelSpace * nXSize) &&
427 : nBandSpace == 1)
428 : {
429 95 : memcpy(pData, pabyUncompressed,
430 95 : 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 335 : return CE_None;
449 : }
450 :
451 14 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
452 : pData, nBufXSize, nBufYSize, eBufType,
453 : nBandCount, panBandMap, nPixelSpace,
454 14 : 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 521 : GDALPamDataset *WEBPDataset::OpenPAM(GDALOpenInfo *poOpenInfo)
584 :
585 : {
586 521 : if (!WEBPDriverIdentify(poOpenInfo) || poOpenInfo->fpL == nullptr)
587 0 : return nullptr;
588 :
589 : int nWidth, nHeight;
590 521 : if (!WebPGetInfo(reinterpret_cast<const uint8_t *>(poOpenInfo->pabyHeader),
591 521 : static_cast<uint32_t>(poOpenInfo->nHeaderBytes), &nWidth,
592 : &nHeight))
593 0 : return nullptr;
594 :
595 521 : int nBands = 3;
596 :
597 1042 : auto poDS = std::make_unique<WEBPDataset>();
598 :
599 : #if WEBP_DECODER_ABI_VERSION >= 0x0002
600 : WebPDecoderConfig config;
601 521 : if (!WebPInitDecoderConfig(&config))
602 0 : return nullptr;
603 :
604 : const bool bOK =
605 521 : WebPGetFeatures(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes,
606 521 : &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 1042 : poDS->GDALDataset::SetMetadataItem("COMPRESSION_REVERSIBILITY",
612 521 : config.input.format == 2 ? "LOSSLESS"
613 : : "LOSSY",
614 : GDAL_MDD_IMAGE_STRUCTURE);
615 : #endif
616 :
617 725 : if (config.input.has_alpha ||
618 204 : CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
619 : "FORCE_4BANDS", "NO")))
620 335 : nBands = 4;
621 :
622 521 : WebPFreeDecBuffer(&config.output);
623 :
624 521 : if (!bOK)
625 0 : return nullptr;
626 :
627 : #endif
628 :
629 521 : if (poOpenInfo->eAccess == GA_Update)
630 : {
631 0 : ReportUpdateNotSupportedByDriver("WEBP");
632 0 : return nullptr;
633 : }
634 :
635 : /* -------------------------------------------------------------------- */
636 : /* Create a corresponding GDALDataset. */
637 : /* -------------------------------------------------------------------- */
638 521 : poDS->nRasterXSize = nWidth;
639 521 : poDS->nRasterYSize = nHeight;
640 521 : poDS->fpImage = poOpenInfo->fpL;
641 521 : poOpenInfo->fpL = nullptr;
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Create band information objects. */
645 : /* -------------------------------------------------------------------- */
646 2419 : for (int iBand = 0; iBand < nBands; iBand++)
647 1898 : poDS->SetBand(iBand + 1, new WEBPRasterBand(poDS.get(), iBand + 1));
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Initialize any PAM information. */
651 : /* -------------------------------------------------------------------- */
652 521 : poDS->SetDescription(poOpenInfo->pszFilename);
653 :
654 521 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
655 :
656 : /* -------------------------------------------------------------------- */
657 : /* Open overviews. */
658 : /* -------------------------------------------------------------------- */
659 1042 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
660 521 : poOpenInfo->GetSiblingFiles());
661 :
662 521 : return poDS.release();
663 : }
664 :
665 : /************************************************************************/
666 : /* Open() */
667 : /************************************************************************/
668 :
669 405 : GDALDataset *WEBPDataset::Open(GDALOpenInfo *poOpenInfo)
670 :
671 : {
672 405 : return OpenPAM(poOpenInfo);
673 : }
674 :
675 : /************************************************************************/
676 : /* WebPUserData */
677 : /************************************************************************/
678 :
679 : typedef struct
680 : {
681 : VSILFILE *fp;
682 : GDALProgressFunc pfnProgress;
683 : void *pProgressData;
684 : } WebPUserData;
685 :
686 : /************************************************************************/
687 : /* WEBPDatasetWriter() */
688 : /************************************************************************/
689 :
690 899 : static int WEBPDatasetWriter(const uint8_t *data, size_t data_size,
691 : const WebPPicture *const picture)
692 : {
693 899 : WebPUserData *pUserData =
694 : reinterpret_cast<WebPUserData *>(picture->custom_ptr);
695 899 : return VSIFWriteL(data, 1, data_size, pUserData->fp) == data_size;
696 : }
697 :
698 : /************************************************************************/
699 : /* WEBPDatasetProgressHook() */
700 : /************************************************************************/
701 :
702 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
703 1214 : static int WEBPDatasetProgressHook(int percent,
704 : const WebPPicture *const picture)
705 : {
706 1214 : WebPUserData *pUserData =
707 : reinterpret_cast<WebPUserData *>(picture->custom_ptr);
708 1214 : return pUserData->pfnProgress(percent / 100.0, nullptr,
709 1214 : pUserData->pProgressData);
710 : }
711 : #endif
712 :
713 : /************************************************************************/
714 : /* CreateCopy() */
715 : /************************************************************************/
716 :
717 143 : GDALDataset *WEBPDataset::CreateCopy(const char *pszFilename,
718 : GDALDataset *poSrcDS, int bStrict,
719 : CSLConstList papszOptions,
720 : GDALProgressFunc pfnProgress,
721 : void *pProgressData)
722 :
723 : {
724 : const char *pszLossLessCopy =
725 143 : CSLFetchNameValueDef(papszOptions, "LOSSLESS_COPY", "AUTO");
726 143 : if (EQUAL(pszLossLessCopy, "AUTO") || CPLTestBool(pszLossLessCopy))
727 : {
728 143 : void *pWEBPContent = nullptr;
729 143 : size_t nWEBPContent = 0;
730 143 : if (poSrcDS->ReadCompressedData(
731 : "WEBP", 0, 0, poSrcDS->GetRasterXSize(),
732 : poSrcDS->GetRasterYSize(), poSrcDS->GetRasterCount(), nullptr,
733 286 : &pWEBPContent, &nWEBPContent, nullptr) == CE_None)
734 : {
735 2 : CPLDebug("WEBP", "Lossless copy from source dataset");
736 2 : std::vector<GByte> abyData;
737 : try
738 : {
739 2 : abyData.assign(static_cast<const GByte *>(pWEBPContent),
740 2 : static_cast<const GByte *>(pWEBPContent) +
741 2 : nWEBPContent);
742 :
743 2 : CSLConstList papszXMP = poSrcDS->GetMetadata("xml:XMP");
744 2 : if (papszXMP && papszXMP[0])
745 : {
746 : GByte abyChunkHeader[8];
747 1 : memcpy(abyChunkHeader, "META", 4);
748 1 : const size_t nXMPSize = strlen(papszXMP[0]);
749 1 : uint32_t nChunkSize = static_cast<uint32_t>(nXMPSize);
750 1 : CPL_LSBPTR32(&nChunkSize);
751 1 : memcpy(abyChunkHeader + 4, &nChunkSize, 4);
752 0 : abyData.insert(abyData.end(), abyChunkHeader,
753 1 : abyChunkHeader + sizeof(abyChunkHeader));
754 : abyData.insert(
755 0 : abyData.end(),
756 : reinterpret_cast<const GByte *>(papszXMP[0]),
757 1 : reinterpret_cast<const GByte *>(papszXMP[0]) +
758 1 : nXMPSize);
759 1 : if ((abyData.size() % 2) != 0) // Payload padding if needed
760 1 : abyData.push_back(0);
761 :
762 : // Patch size of RIFF
763 : uint32_t nSize32 =
764 1 : static_cast<uint32_t>(abyData.size()) - 8;
765 1 : CPL_LSBPTR32(&nSize32);
766 1 : memcpy(abyData.data() + 4, &nSize32, 4);
767 : }
768 : }
769 0 : catch (const std::exception &e)
770 : {
771 0 : CPLError(CE_Failure, CPLE_AppDefined, "Exception occurred: %s",
772 0 : e.what());
773 0 : abyData.clear();
774 : }
775 2 : VSIFree(pWEBPContent);
776 :
777 2 : if (!abyData.empty())
778 : {
779 : auto fpImage(
780 2 : CPLTestBool(CSLFetchNameValueDef(
781 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME",
782 : "NO"))
783 0 : ? VSIFileManager::GetHandler(pszFilename)
784 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true,
785 0 : nullptr)
786 4 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
787 2 : if (fpImage == nullptr)
788 : {
789 0 : CPLError(CE_Failure, CPLE_OpenFailed,
790 : "Unable to create jpeg file %s.", pszFilename);
791 :
792 0 : return nullptr;
793 : }
794 4 : if (fpImage->Write(abyData.data(), 1, abyData.size()) !=
795 2 : abyData.size())
796 : {
797 0 : CPLError(CE_Failure, CPLE_FileIO,
798 0 : "Failure writing data: %s", VSIStrerror(errno));
799 0 : fpImage->CancelCreation();
800 0 : return nullptr;
801 : }
802 :
803 2 : if (fpImage->Close() != 0)
804 : {
805 0 : CPLError(CE_Failure, CPLE_FileIO,
806 : "Error at file closing of '%s': %s", pszFilename,
807 0 : VSIStrerror(errno));
808 0 : return nullptr;
809 : }
810 :
811 2 : pfnProgress(1.0, nullptr, pProgressData);
812 :
813 : // Re-open file and clone missing info to PAM
814 2 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
815 2 : auto poDS = OpenPAM(&oOpenInfo);
816 2 : if (poDS)
817 : {
818 2 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
819 : }
820 :
821 2 : return poDS;
822 : }
823 : }
824 : }
825 :
826 141 : const bool bLossless = CPLFetchBool(papszOptions, "LOSSLESS", false);
827 268 : if (!bLossless &&
828 127 : (!EQUAL(pszLossLessCopy, "AUTO") && CPLTestBool(pszLossLessCopy)))
829 : {
830 0 : CPLError(CE_Failure, CPLE_AppDefined,
831 : "LOSSLESS_COPY=YES requested but not possible");
832 0 : return nullptr;
833 : }
834 :
835 : /* -------------------------------------------------------------------- */
836 : /* WEBP library initialization */
837 : /* -------------------------------------------------------------------- */
838 :
839 : WebPPicture sPicture;
840 141 : if (!WebPPictureInit(&sPicture))
841 : {
842 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed");
843 0 : return nullptr;
844 : }
845 :
846 : /* -------------------------------------------------------------------- */
847 : /* Some some rudimentary checks */
848 : /* -------------------------------------------------------------------- */
849 :
850 141 : const int nXSize = poSrcDS->GetRasterXSize();
851 141 : const int nYSize = poSrcDS->GetRasterYSize();
852 141 : if (nXSize > 16383 || nYSize > 16383)
853 : {
854 0 : CPLError(CE_Failure, CPLE_NotSupported,
855 : "WEBP maximum image dimensions are 16383 x 16383.");
856 :
857 0 : return nullptr;
858 : }
859 :
860 141 : const int nBands = poSrcDS->GetRasterCount();
861 141 : if (nBands != 3
862 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
863 94 : && nBands != 4
864 : #endif
865 : )
866 : {
867 14 : CPLError(CE_Failure, CPLE_NotSupported,
868 : "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
869 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
870 : "or 4 (RGBA) "
871 : #endif
872 : "bands.",
873 : nBands);
874 :
875 14 : return nullptr;
876 : }
877 :
878 127 : const GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
879 :
880 127 : if (eDT != GDT_UInt8)
881 : {
882 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
883 : "WEBP driver doesn't support data type %s. "
884 : "Only UInt8 bands supported.",
885 : GDALGetDataTypeName(
886 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
887 :
888 0 : if (bStrict)
889 0 : return nullptr;
890 : }
891 :
892 : /* -------------------------------------------------------------------- */
893 : /* What options has the user selected? */
894 : /* -------------------------------------------------------------------- */
895 127 : float fQuality = 75.0f;
896 127 : const char *pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY");
897 127 : if (pszQUALITY != nullptr)
898 : {
899 98 : fQuality = static_cast<float>(CPLAtof(pszQUALITY));
900 98 : if (fQuality < 0.0f || fQuality > 100.0f)
901 : {
902 0 : CPLError(CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.",
903 : "QUALITY", pszQUALITY);
904 0 : return nullptr;
905 : }
906 : }
907 :
908 127 : WebPPreset nPreset = WEBP_PRESET_DEFAULT;
909 : const char *pszPRESET =
910 127 : CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT");
911 127 : if (EQUAL(pszPRESET, "DEFAULT"))
912 127 : nPreset = WEBP_PRESET_DEFAULT;
913 0 : else if (EQUAL(pszPRESET, "PICTURE"))
914 0 : nPreset = WEBP_PRESET_PICTURE;
915 0 : else if (EQUAL(pszPRESET, "PHOTO"))
916 0 : nPreset = WEBP_PRESET_PHOTO;
917 0 : else if (EQUAL(pszPRESET, "PICTURE"))
918 0 : nPreset = WEBP_PRESET_PICTURE;
919 0 : else if (EQUAL(pszPRESET, "DRAWING"))
920 0 : nPreset = WEBP_PRESET_DRAWING;
921 0 : else if (EQUAL(pszPRESET, "ICON"))
922 0 : nPreset = WEBP_PRESET_ICON;
923 0 : else if (EQUAL(pszPRESET, "TEXT"))
924 0 : nPreset = WEBP_PRESET_TEXT;
925 : else
926 : {
927 0 : CPLError(CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.",
928 : "PRESET", pszPRESET);
929 0 : return nullptr;
930 : }
931 :
932 : WebPConfig sConfig;
933 127 : if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality,
934 : WEBP_ENCODER_ABI_VERSION))
935 : {
936 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed");
937 0 : return nullptr;
938 : }
939 :
940 : // TODO: Get rid of this macro in a reasonable way.
941 : #define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \
942 : { \
943 : const char *pszVal = CSLFetchNameValue(papszOptions, name); \
944 : if (pszVal != nullptr) \
945 : { \
946 : sConfig.fieldname = atoi(pszVal); \
947 : if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \
948 : { \
949 : CPLError(CE_Failure, CPLE_IllegalArg, \
950 : "%s=%s is not a legal value.", name, pszVal); \
951 : return nullptr; \
952 : } \
953 : } \
954 : }
955 :
956 127 : FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX - 1);
957 :
958 127 : const char *pszPSNR = CSLFetchNameValue(papszOptions, "PSNR");
959 127 : if (pszPSNR)
960 : {
961 0 : sConfig.target_PSNR = static_cast<float>(CPLAtof(pszPSNR));
962 0 : if (sConfig.target_PSNR < 0)
963 : {
964 0 : CPLError(CE_Failure, CPLE_IllegalArg,
965 : "PSNR=%s is not a legal value.", pszPSNR);
966 0 : return nullptr;
967 : }
968 : }
969 :
970 127 : FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6);
971 127 : FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4);
972 127 : FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100);
973 127 : FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100);
974 127 : FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7);
975 127 : FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1);
976 127 : FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1);
977 127 : FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10);
978 127 : FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1);
979 127 : FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3);
980 : #if WEBP_ENCODER_ABI_VERSION >= 0x0002
981 127 : FETCH_AND_SET_OPTION_INT("PARTITION_LIMIT", partition_limit, 0, 100);
982 : #endif
983 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
984 127 : sConfig.lossless = bLossless;
985 127 : if (sConfig.lossless)
986 14 : sPicture.use_argb = 1;
987 : #endif
988 : #if WEBP_ENCODER_ABI_VERSION >= 0x0209
989 127 : FETCH_AND_SET_OPTION_INT("EXACT", exact, 0, 1);
990 : #endif
991 :
992 127 : if (!WebPValidateConfig(&sConfig))
993 : {
994 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed");
995 0 : return nullptr;
996 : }
997 :
998 : /* -------------------------------------------------------------------- */
999 : /* Allocate memory */
1000 : /* -------------------------------------------------------------------- */
1001 : GByte *pabyBuffer =
1002 127 : static_cast<GByte *>(VSI_MALLOC3_VERBOSE(nBands, nXSize, nYSize));
1003 127 : if (pabyBuffer == nullptr)
1004 : {
1005 0 : return nullptr;
1006 : }
1007 :
1008 : /* -------------------------------------------------------------------- */
1009 : /* Create the dataset. */
1010 : /* -------------------------------------------------------------------- */
1011 : auto fpImage(
1012 127 : CPLTestBool(CSLFetchNameValueDef(
1013 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"))
1014 7 : ? VSIFileManager::GetHandler(pszFilename)
1015 7 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
1016 261 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
1017 127 : if (fpImage == nullptr)
1018 : {
1019 3 : CPLError(CE_Failure, CPLE_OpenFailed,
1020 : "Unable to create WEBP file %s.\n", pszFilename);
1021 3 : VSIFree(pabyBuffer);
1022 3 : return nullptr;
1023 : }
1024 :
1025 : WebPUserData sUserData;
1026 124 : sUserData.fp = fpImage.get();
1027 124 : sUserData.pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1028 124 : sUserData.pProgressData = pProgressData;
1029 :
1030 : /* -------------------------------------------------------------------- */
1031 : /* WEBP library settings */
1032 : /* -------------------------------------------------------------------- */
1033 :
1034 124 : sPicture.width = nXSize;
1035 124 : sPicture.height = nYSize;
1036 124 : sPicture.writer = WEBPDatasetWriter;
1037 124 : sPicture.custom_ptr = &sUserData;
1038 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1039 124 : sPicture.progress_hook = WEBPDatasetProgressHook;
1040 : #endif
1041 124 : if (!WebPPictureAlloc(&sPicture))
1042 : {
1043 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed");
1044 0 : VSIFree(pabyBuffer);
1045 0 : fpImage->CancelCreation();
1046 0 : return nullptr;
1047 : }
1048 :
1049 : /* -------------------------------------------------------------------- */
1050 : /* Acquire source imagery. */
1051 : /* -------------------------------------------------------------------- */
1052 : CPLErr eErr =
1053 248 : poSrcDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, pabyBuffer, nXSize,
1054 : nYSize, GDT_UInt8, nBands, nullptr, nBands,
1055 124 : static_cast<GSpacing>(nBands) * nXSize, 1, nullptr);
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* Import and write to file */
1059 : /* -------------------------------------------------------------------- */
1060 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1061 124 : if (eErr == CE_None && nBands == 4)
1062 : {
1063 80 : if (!WebPPictureImportRGBA(&sPicture, pabyBuffer, nBands * nXSize))
1064 : {
1065 0 : CPLError(CE_Failure, CPLE_AppDefined,
1066 : "WebPPictureImportRGBA() failed");
1067 0 : eErr = CE_Failure;
1068 : }
1069 : }
1070 : else
1071 : #endif
1072 88 : if (eErr == CE_None &&
1073 44 : !WebPPictureImportRGB(&sPicture, pabyBuffer, nBands * nXSize))
1074 : {
1075 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed");
1076 0 : eErr = CE_Failure;
1077 : }
1078 :
1079 124 : if (pfnProgress)
1080 124 : pfnProgress(0.5, "", pProgressData);
1081 :
1082 124 : if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture))
1083 : {
1084 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
1085 10 : const char *pszErrorMsg = nullptr;
1086 10 : switch (sPicture.error_code)
1087 : {
1088 0 : case VP8_ENC_ERROR_OUT_OF_MEMORY:
1089 0 : pszErrorMsg = "Out of memory";
1090 0 : break;
1091 0 : case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
1092 0 : pszErrorMsg = "Out of memory while flushing bits";
1093 0 : break;
1094 0 : case VP8_ENC_ERROR_NULL_PARAMETER:
1095 0 : pszErrorMsg = "A pointer parameter is NULL";
1096 0 : break;
1097 0 : case VP8_ENC_ERROR_INVALID_CONFIGURATION:
1098 0 : pszErrorMsg = "Configuration is invalid";
1099 0 : break;
1100 0 : case VP8_ENC_ERROR_BAD_DIMENSION:
1101 0 : pszErrorMsg = "Picture has invalid width/height";
1102 0 : break;
1103 0 : case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
1104 0 : pszErrorMsg = "Partition is bigger than 512k. Try using less "
1105 : "SEGMENTS, or increase PARTITION_LIMIT value";
1106 0 : break;
1107 0 : case VP8_ENC_ERROR_PARTITION_OVERFLOW:
1108 0 : pszErrorMsg = "Partition is bigger than 16M";
1109 0 : break;
1110 5 : case VP8_ENC_ERROR_BAD_WRITE:
1111 5 : pszErrorMsg = "Error while flushing bytes";
1112 5 : break;
1113 0 : case VP8_ENC_ERROR_FILE_TOO_BIG:
1114 0 : pszErrorMsg = "File is bigger than 4G";
1115 0 : break;
1116 0 : case VP8_ENC_ERROR_USER_ABORT:
1117 0 : pszErrorMsg = "User interrupted";
1118 0 : break;
1119 5 : default:
1120 5 : CPLError(CE_Failure, CPLE_AppDefined,
1121 : "WebPEncode returned an unknown error code: %d",
1122 5 : sPicture.error_code);
1123 5 : pszErrorMsg = "Unknown WebP error type.";
1124 5 : break;
1125 : }
1126 10 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed : %s",
1127 : pszErrorMsg);
1128 : #else
1129 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed");
1130 : #endif
1131 10 : eErr = CE_Failure;
1132 : }
1133 :
1134 : /* -------------------------------------------------------------------- */
1135 : /* Cleanup and close. */
1136 : /* -------------------------------------------------------------------- */
1137 124 : CPLFree(pabyBuffer);
1138 :
1139 124 : WebPPictureFree(&sPicture);
1140 :
1141 124 : if (eErr == CE_None)
1142 : {
1143 114 : if (fpImage->Close() != 0)
1144 : {
1145 0 : CPLError(CE_Failure, CPLE_FileIO,
1146 : "Error at file closing of '%s': %s", pszFilename,
1147 0 : VSIStrerror(errno));
1148 0 : eErr = CE_Failure;
1149 : }
1150 : }
1151 : else
1152 : {
1153 10 : fpImage->CancelCreation();
1154 10 : fpImage.reset();
1155 : }
1156 :
1157 124 : if (pfnProgress)
1158 124 : pfnProgress(1.0, "", pProgressData);
1159 :
1160 124 : if (eErr != CE_None)
1161 : {
1162 10 : VSIUnlink(pszFilename);
1163 10 : return nullptr;
1164 : }
1165 :
1166 : // Do we need a world file?
1167 114 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
1168 : {
1169 1 : GDALGeoTransform gt;
1170 1 : poSrcDS->GetGeoTransform(gt);
1171 1 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
1172 : }
1173 :
1174 : /* -------------------------------------------------------------------- */
1175 : /* Re-open dataset, and copy any auxiliary pam information. */
1176 : /* -------------------------------------------------------------------- */
1177 228 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1178 :
1179 : /* If writing to stdout, we can't reopen it, so return */
1180 : /* a fake dataset to make the caller happy */
1181 114 : CPLPushErrorHandler(CPLQuietErrorHandler);
1182 114 : auto poDS = WEBPDataset::OpenPAM(&oOpenInfo);
1183 114 : CPLPopErrorHandler();
1184 114 : if (poDS)
1185 : {
1186 114 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1187 114 : return poDS;
1188 : }
1189 :
1190 0 : return nullptr;
1191 : }
1192 :
1193 : /************************************************************************/
1194 : /* GDALRegister_WEBP() */
1195 : /************************************************************************/
1196 :
1197 13 : void GDALRegister_WEBP()
1198 :
1199 : {
1200 13 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
1201 0 : return;
1202 :
1203 13 : GDALDriver *poDriver = new GDALDriver();
1204 13 : WEBPDriverSetCommonMetadata(poDriver);
1205 :
1206 13 : poDriver->pfnOpen = WEBPDataset::Open;
1207 13 : poDriver->pfnCreateCopy = WEBPDataset::CreateCopy;
1208 :
1209 13 : GetGDALDriverManager()->RegisterDriver(poDriver);
1210 : }
|