LCOV - code coverage report
Current view: top level - frmts/webp - webpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 386 521 74.1 %
Date: 2026-01-23 20:24:11 Functions: 21 22 95.5 %

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

Generated by: LCOV version 1.14