LCOV - code coverage report
Current view: top level - frmts/webp - webpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 379 513 73.9 %
Date: 2025-09-10 17:48:50 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.14