Coverage Report

Created: 2026-08-14 09:29

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