LCOV - code coverage report
Current view: top level - frmts/exr - exrdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 690 951 72.6 %
Date: 2024-11-21 22:18:42 Functions: 45 57 78.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  EXR read/write Driver
       4             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2020, Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "gdal_pam.h"
      13             : #include "ogr_spatialref.h"
      14             : 
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <mutex>
      18             : 
      19             : #include "openexr_headers.h"
      20             : #include "exrdrivercore.h"
      21             : 
      22             : using namespace OPENEXR_IMF_NAMESPACE;
      23             : using namespace IMATH_NAMESPACE;
      24             : 
      25             : extern "C" CPL_DLL void GDALRegister_EXR();
      26             : 
      27             : static const char *const apszCompressions[] = {
      28             :     "NONE", "RLE", "ZIPS", "ZIP", "PIZ", "PXR24", "B44", "B44A", "DWAA", "DWAB",
      29             : };
      30             : 
      31             : /************************************************************************/
      32             : /*                       GDALEXRDataset()                               */
      33             : /************************************************************************/
      34             : 
      35         182 : class GDALEXRDataset final : public GDALPamDataset
      36             : {
      37             :     friend class GDALEXRRasterBand;
      38             :     friend class GDALEXRPreviewRasterBand;
      39             :     friend class GDALEXRRGBARasterBand;
      40             : 
      41             :     // Keep stream before others, so that it is destroyed last
      42             :     std::unique_ptr<IStream> m_pIStream{};
      43             : 
      44             :     std::unique_ptr<TiledInputPart> m_pTiledIP{};
      45             :     std::unique_ptr<InputPart> m_pIP{};
      46             : 
      47             :     std::unique_ptr<MultiPartInputFile> m_pMPIF{};
      48             :     std::unique_ptr<RgbaInputFile> m_pRGBAIF{};
      49             : 
      50             :     std::vector<Rgba> m_rgbaBuffer{};
      51             :     int m_nRGBABufferLine = -1;
      52             :     int m_iPart = 0;
      53             :     int m_nDWMinX = 0;
      54             :     int m_nDWMinY = 0;
      55             :     GDALEXRDataset *m_poParent = nullptr;
      56             :     int m_iLevel = 0;
      57             :     std::vector<std::unique_ptr<GDALEXRDataset>> m_apoOvrDS{};
      58             :     OGRSpatialReference m_oSRS{};
      59             :     double m_adfGT[6] = {0, 1, 0, 0, 0, 1};
      60             :     bool m_bHasGT = false;
      61             : 
      62             :   public:
      63          91 :     GDALEXRDataset() = default;
      64             :     ~GDALEXRDataset();
      65             : 
      66             :     const OGRSpatialReference *GetSpatialRef() const override;
      67             :     CPLErr GetGeoTransform(double *adfGT) override;
      68             : 
      69             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      70             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      71             :                                int nBandsIn, GDALDataType eType,
      72             :                                char **papszOptions);
      73             :     static GDALDataset *CreateCopy(const char *pszFilename,
      74             :                                    GDALDataset *poSrcDS, int bStrict,
      75             :                                    char **papszOptions,
      76             :                                    GDALProgressFunc pfnProgress,
      77             :                                    void *pProgressData);
      78             : };
      79             : 
      80             : /************************************************************************/
      81             : /*                       GDALEXRRasterBand()                            */
      82             : /************************************************************************/
      83             : 
      84             : class GDALEXRRasterBand final : public GDALPamRasterBand
      85             : {
      86             :     friend class GDALEXRDataset;
      87             : 
      88             :     GDALColorInterp m_eInterp = GCI_Undefined;
      89             :     std::string m_osChannelName;
      90             : 
      91             :   protected:
      92             :     CPLErr IReadBlock(int, int, void *) override;
      93             : 
      94             :   public:
      95             :     GDALEXRRasterBand(GDALEXRDataset *poDSIn, int nBandIn,
      96             :                       const std::string &channelName, PixelType pixelType,
      97             :                       int nBlockXSizeIn, int nBlockYSizeIn);
      98             : 
      99           0 :     GDALColorInterp GetColorInterpretation() override
     100             :     {
     101           0 :         return m_eInterp;
     102             :     }
     103             : 
     104             :     int GetOverviewCount() override;
     105             :     GDALRasterBand *GetOverview(int) override;
     106             : };
     107             : 
     108             : /************************************************************************/
     109             : /*                         GDALEXRRasterBand()                          */
     110             : /************************************************************************/
     111             : 
     112         148 : GDALEXRRasterBand::GDALEXRRasterBand(GDALEXRDataset *poDSIn, int nBandIn,
     113             :                                      const std::string &channelName,
     114             :                                      PixelType pixelType, int nBlockXSizeIn,
     115         148 :                                      int nBlockYSizeIn)
     116         148 :     : m_osChannelName(channelName)
     117             : {
     118         148 :     poDS = poDSIn;
     119         148 :     nBand = nBandIn;
     120         148 :     nRasterXSize = poDSIn->GetRasterXSize();
     121         148 :     nRasterYSize = poDSIn->GetRasterYSize();
     122         148 :     nBlockXSize = nBlockXSizeIn;
     123         148 :     nBlockYSize = nBlockYSizeIn;
     124         148 :     eDataType = (pixelType == UINT) ? GDT_UInt32 : GDT_Float32;
     125         148 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                          IReadBlock()                                */
     129             : /************************************************************************/
     130             : 
     131         184 : CPLErr GDALEXRRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     132             :                                      void *pImage)
     133             : {
     134         184 :     auto poGDS = cpl::down_cast<GDALEXRDataset *>(poDS);
     135             :     try
     136             :     {
     137         184 :         FrameBuffer fb;
     138         184 :         const size_t sizeOfElt = sizeof(float);  // sizeof(uint32) as well
     139             :         const auto slice =
     140         184 :             Slice(eDataType == GDT_Float32 ? FLOAT : UINT,
     141         184 :                   static_cast<char *>(pImage) -
     142         184 :                       (poGDS->m_nDWMinX + nBlockXOff * nBlockXSize +
     143         184 :                        static_cast<size_t>(poGDS->m_nDWMinY +
     144         184 :                                            nBlockYOff * nBlockYSize) *
     145         184 :                            nBlockXSize) *
     146             :                           sizeOfElt,
     147         184 :                   sizeOfElt, sizeOfElt * nBlockXSize);
     148         184 :         fb.insert(m_osChannelName, slice);
     149             : 
     150         184 :         if (poGDS->m_pIP)
     151             :         {
     152           0 :             poGDS->m_pIP->setFrameBuffer(fb);
     153           0 :             poGDS->m_pIP->readPixels(poGDS->m_nDWMinY + nBlockYOff);
     154             :         }
     155             :         else
     156             :         {
     157         184 :             auto tiledIP = poGDS->m_poParent
     158         184 :                                ? poGDS->m_poParent->m_pTiledIP.get()
     159         183 :                                : poGDS->m_pTiledIP.get();
     160         184 :             CPLAssert(tiledIP);
     161         184 :             tiledIP->setFrameBuffer(fb);
     162         184 :             tiledIP->readTile(nBlockXOff, nBlockYOff, poGDS->m_iLevel);
     163             :         }
     164         184 :         return CE_None;
     165             :     }
     166           0 :     catch (const std::exception &e)
     167             :     {
     168           0 :         if (strstr(e.what(), "is missing"))
     169             :         {
     170           0 :             CPLDebug("EXR", "%s", e.what());
     171           0 :             memset(pImage, 0,
     172           0 :                    static_cast<size_t>(nBlockXSize) * nBlockYSize *
     173           0 :                        GDALGetDataTypeSizeBytes(eDataType));
     174           0 :             return CE_None;
     175             :         }
     176           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
     177             :     }
     178             : 
     179           0 :     return CE_Failure;
     180             : }
     181             : 
     182             : /************************************************************************/
     183             : /*                          GetOverviewCount()                          */
     184             : /************************************************************************/
     185             : 
     186           3 : int GDALEXRRasterBand::GetOverviewCount()
     187             : {
     188           3 :     auto poGDS = cpl::down_cast<GDALEXRDataset *>(poDS);
     189           3 :     return static_cast<int>(poGDS->m_apoOvrDS.size());
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                            GetOverview()                             */
     194             : /************************************************************************/
     195             : 
     196           3 : GDALRasterBand *GDALEXRRasterBand::GetOverview(int iOvr)
     197             : {
     198           3 :     if (iOvr < 0 || iOvr >= GetOverviewCount())
     199           2 :         return nullptr;
     200           1 :     auto poGDS = cpl::down_cast<GDALEXRDataset *>(poDS);
     201           1 :     return poGDS->m_apoOvrDS[iOvr]->GetRasterBand(nBand);
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                      GDALEXRPreviewRasterBand                        */
     206             : /************************************************************************/
     207             : 
     208             : class GDALEXRPreviewRasterBand final : public GDALPamRasterBand
     209             : {
     210             :     friend class GDALEXRDataset;
     211             : 
     212             :   protected:
     213             :     CPLErr IReadBlock(int, int, void *) override;
     214             : 
     215           0 :     GDALColorInterp GetColorInterpretation() override
     216             :     {
     217           0 :         return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
     218             :     }
     219             : 
     220             :   public:
     221             :     GDALEXRPreviewRasterBand(GDALEXRDataset *poDSIn, int nBandIn);
     222             : };
     223             : 
     224             : /************************************************************************/
     225             : /*                      GDALEXRPreviewRasterBand()                      */
     226             : /************************************************************************/
     227             : 
     228           4 : GDALEXRPreviewRasterBand::GDALEXRPreviewRasterBand(GDALEXRDataset *poDSIn,
     229           4 :                                                    int nBandIn)
     230             : {
     231           4 :     poDS = poDSIn;
     232           4 :     nBand = nBandIn;
     233           4 :     nRasterXSize = poDSIn->GetRasterXSize();
     234           4 :     nRasterYSize = poDSIn->GetRasterYSize();
     235           4 :     nBlockXSize = nRasterXSize;
     236           4 :     nBlockYSize = 1;
     237           4 :     eDataType = GDT_Byte;
     238           4 : }
     239             : 
     240             : /************************************************************************/
     241             : /*                          IReadBlock()                                */
     242             : /************************************************************************/
     243             : 
     244         200 : CPLErr GDALEXRPreviewRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
     245             : {
     246         200 :     auto poGDS = cpl::down_cast<GDALEXRDataset *>(poDS);
     247             :     try
     248             :     {
     249         200 :         const auto &header = poGDS->m_pMPIF->header(poGDS->m_iPart);
     250         200 :         const auto &preview = header.previewImage();
     251         400 :         GDALCopyWords(reinterpret_cast<const GByte *>(
     252         200 :                           preview.pixels() + nBlockYOff * nRasterXSize) +
     253         200 :                           nBand - 1,
     254             :                       GDT_Byte, 4, pImage, GDT_Byte, 1, nRasterXSize);
     255         200 :         return CE_None;
     256             :     }
     257           0 :     catch (const std::exception &e)
     258             :     {
     259           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
     260             :     }
     261             : 
     262           0 :     return CE_Failure;
     263             : }
     264             : 
     265             : /************************************************************************/
     266             : /*                      GDALEXRRGBARasterBand                        */
     267             : /************************************************************************/
     268             : 
     269             : class GDALEXRRGBARasterBand final : public GDALPamRasterBand
     270             : {
     271             :     friend class GDALEXRDataset;
     272             : 
     273             :   protected:
     274             :     CPLErr IReadBlock(int, int, void *) override;
     275             : 
     276           0 :     GDALColorInterp GetColorInterpretation() override
     277             :     {
     278           0 :         return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
     279             :     }
     280             : 
     281             :   public:
     282             :     GDALEXRRGBARasterBand(GDALEXRDataset *poDSIn, int nBandIn);
     283             : };
     284             : 
     285             : /************************************************************************/
     286             : /*                      GDALEXRRGBARasterBand()                      */
     287             : /************************************************************************/
     288             : 
     289           0 : GDALEXRRGBARasterBand::GDALEXRRGBARasterBand(GDALEXRDataset *poDSIn,
     290           0 :                                              int nBandIn)
     291             : {
     292           0 :     poDS = poDSIn;
     293           0 :     nBand = nBandIn;
     294           0 :     nRasterXSize = poDSIn->GetRasterXSize();
     295           0 :     nRasterYSize = poDSIn->GetRasterYSize();
     296           0 :     nBlockXSize = nRasterXSize;
     297           0 :     nBlockYSize = 1;
     298           0 :     eDataType = GDT_Float32;
     299           0 : }
     300             : 
     301             : /************************************************************************/
     302             : /*                          IReadBlock()                                */
     303             : /************************************************************************/
     304             : 
     305           0 : CPLErr GDALEXRRGBARasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
     306             : {
     307           0 :     auto poGDS = cpl::down_cast<GDALEXRDataset *>(poDS);
     308             :     try
     309             :     {
     310           0 :         if (nBlockYOff != poGDS->m_nRGBABufferLine)
     311             :         {
     312           0 :             poGDS->m_rgbaBuffer.resize(nRasterXSize);
     313           0 :             poGDS->m_pRGBAIF->setFrameBuffer(
     314           0 :                 poGDS->m_rgbaBuffer.data() -
     315           0 :                     ((poGDS->m_nDWMinY + nBlockYOff) *
     316           0 :                          static_cast<size_t>(nRasterXSize) +
     317           0 :                      poGDS->m_nDWMinX),
     318           0 :                 1, nRasterXSize);
     319           0 :             poGDS->m_pRGBAIF->readPixels(poGDS->m_nDWMinY + nBlockYOff);
     320             :         }
     321           0 :         if (nBand == 1)
     322             :         {
     323           0 :             for (int i = 0; i < nRasterXSize; i++)
     324             :             {
     325           0 :                 static_cast<float *>(pImage)[i] = poGDS->m_rgbaBuffer[i].r;
     326             :             }
     327             :         }
     328           0 :         else if (nBand == 2)
     329             :         {
     330           0 :             for (int i = 0; i < nRasterXSize; i++)
     331             :             {
     332           0 :                 static_cast<float *>(pImage)[i] = poGDS->m_rgbaBuffer[i].g;
     333             :             }
     334             :         }
     335           0 :         else if (nBand == 3)
     336             :         {
     337           0 :             for (int i = 0; i < nRasterXSize; i++)
     338             :             {
     339           0 :                 static_cast<float *>(pImage)[i] = poGDS->m_rgbaBuffer[i].b;
     340             :             }
     341             :         }
     342             : #ifdef unused
     343             :         else
     344             :         {
     345             :             for (int i = 0; i < nRasterXSize; i++)
     346             :             {
     347             :                 static_cast<float *>(pImage)[i] = poGDS->m_rgbaBuffer[i].a;
     348             :             }
     349             :         }
     350             : #endif
     351           0 :         poGDS->m_nRGBABufferLine = nBlockYOff;
     352           0 :         return CE_None;
     353             :     }
     354           0 :     catch (const std::exception &e)
     355             :     {
     356           0 :         if (strstr(e.what(), "is missing"))
     357             :         {
     358           0 :             CPLDebug("EXR", "%s", e.what());
     359           0 :             memset(pImage, 0,
     360           0 :                    static_cast<size_t>(nBlockXSize) * nBlockYSize *
     361           0 :                        GDALGetDataTypeSizeBytes(eDataType));
     362           0 :             return CE_None;
     363             :         }
     364           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
     365             :     }
     366             : 
     367           0 :     return CE_Failure;
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                         ~GDALEXRDataset()                            */
     372             : /************************************************************************/
     373             : 
     374             : GDALEXRDataset::~GDALEXRDataset() = default;
     375             : 
     376             : /************************************************************************/
     377             : /*                          GetSpatialRef()                             */
     378             : /************************************************************************/
     379             : 
     380           0 : const OGRSpatialReference *GDALEXRDataset::GetSpatialRef() const
     381             : {
     382           0 :     const auto *poPamSRS = GDALPamDataset::GetSpatialRef();
     383           0 :     if (poPamSRS)
     384           0 :         return poPamSRS;
     385           0 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     386             : }
     387             : 
     388             : /************************************************************************/
     389             : /*                         GetGeoTransform()                            */
     390             : /************************************************************************/
     391             : 
     392           0 : CPLErr GDALEXRDataset::GetGeoTransform(double *adfGT)
     393             : {
     394           0 :     if (GDALPamDataset::GetGeoTransform(adfGT) == CE_None)
     395             :     {
     396           0 :         return CE_None;
     397             :     }
     398           0 :     memcpy(adfGT, m_adfGT, 6 * sizeof(double));
     399           0 :     return m_bHasGT ? CE_None : CE_Failure;
     400             : }
     401             : 
     402             : /************************************************************************/
     403             : /*                           GDALEXRIOStream                            */
     404             : /************************************************************************/
     405             : 
     406             : class GDALEXRIOStreamException final : public std::exception
     407             : {
     408             :     std::string m_msg;
     409             : 
     410             :   public:
     411          41 :     explicit GDALEXRIOStreamException(const std::string &msg) : m_msg(msg)
     412             :     {
     413          41 :     }
     414             : 
     415          10 :     const char *what() const noexcept override
     416             :     {
     417          10 :         return m_msg.c_str();
     418             :     }
     419             : };
     420             : 
     421             : #if OPENEXR_VERSION_MAJOR < 3
     422             : typedef Int64 IoInt64Type;
     423             : #else
     424             : typedef uint64_t IoInt64Type;
     425             : #endif
     426             : 
     427             : class GDALEXRIOStream final : public IStream, public OStream
     428             : {
     429             :   public:
     430         168 :     GDALEXRIOStream(VSILFILE *fp, const char *filename)
     431         168 :         : IStream(filename), OStream(filename), m_fp(fp)
     432             :     {
     433         168 :     }
     434             : 
     435         295 :     ~GDALEXRIOStream()
     436         168 :     {
     437         168 :         VSIFCloseL(m_fp);
     438         295 :     }
     439             : 
     440             :     virtual bool read(char c[/*n*/], int n) override;
     441             :     virtual void write(const char c[/*n*/], int n) override;
     442             :     virtual IoInt64Type tellg() override;
     443             : 
     444         310 :     virtual IoInt64Type tellp() override
     445             :     {
     446         310 :         return tellg();
     447             :     }
     448             : 
     449             :     virtual void seekg(IoInt64Type pos) override;
     450             : 
     451         136 :     virtual void seekp(IoInt64Type pos) override
     452             :     {
     453         136 :         return seekg(pos);
     454             :     }
     455             : 
     456             :   private:
     457             :     VSILFILE *m_fp;
     458             : };
     459             : 
     460      114977 : bool GDALEXRIOStream::read(char c[/*n*/], int n)
     461             : {
     462      114977 :     if (static_cast<int>(VSIFReadL(c, 1, n, m_fp)) != n)
     463             :     {
     464          31 :         if (VSIFEofL(m_fp))
     465             :         {
     466             :             throw GDALEXRIOStreamException(
     467          31 :                 CPLSPrintf("Unexpected end of file. Cannot read %d bytes", n));
     468             :         }
     469             :         else
     470             :         {
     471             :             throw GDALEXRIOStreamException(
     472           0 :                 CPLSPrintf("cannot read %d bytes", n));
     473             :         }
     474             :     }
     475      114946 :     return VSIFEofL(m_fp) != 0;
     476             : }
     477             : 
     478       20116 : void GDALEXRIOStream::write(const char c[/*n*/], int n)
     479             : {
     480       20116 :     if (static_cast<int>(VSIFWriteL(c, 1, n, m_fp)) != n)
     481             :     {
     482          10 :         throw GDALEXRIOStreamException(CPLSPrintf("cannot write %d bytes", n));
     483             :     }
     484       20106 : }
     485             : 
     486         430 : IoInt64Type GDALEXRIOStream::tellg()
     487             : {
     488         430 :     return static_cast<IoInt64Type>(VSIFTellL(m_fp));
     489             : }
     490             : 
     491         172 : void GDALEXRIOStream::seekg(IoInt64Type pos)
     492             : {
     493         172 :     VSIFSeekL(m_fp, static_cast<vsi_l_offset>(pos), SEEK_SET);
     494         172 : }
     495             : 
     496             : /************************************************************************/
     497             : /*                           setNumThreads()                            */
     498             : /************************************************************************/
     499             : 
     500          44 : static void setNumThreads()
     501             : {
     502             :     static std::mutex mutex;
     503          88 :     std::lock_guard<std::mutex> oLock(mutex);
     504             :     static bool bSet = false;
     505          44 :     if (!bSet)
     506             :     {
     507           3 :         bSet = true;
     508           3 :         setGlobalThreadCount(CPLGetNumCPUs());
     509             :     }
     510          44 : }
     511             : 
     512             : /************************************************************************/
     513             : /*                               Open()                                 */
     514             : /************************************************************************/
     515             : 
     516          90 : GDALDataset *GDALEXRDataset::Open(GDALOpenInfo *poOpenInfo)
     517             : {
     518          90 :     if (!EXRDriverIdentify(poOpenInfo))
     519           0 :         return nullptr;
     520          90 :     if (poOpenInfo->eAccess == GA_Update)
     521             :     {
     522           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     523             :                  "Update of existing EXR file not supported");
     524           0 :         return nullptr;
     525             :     }
     526             : 
     527         180 :     CPLString osFilename(poOpenInfo->pszFilename);
     528          90 :     int iPart = 0;
     529          90 :     bool bIsPreview = false;
     530             :     VSILFILE *fp;
     531          90 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "EXR:"))
     532             :     {
     533           1 :         bIsPreview = STARTS_WITH_CI(poOpenInfo->pszFilename, "EXR:PREVIEW:");
     534           1 :         const char *pszPartPos =
     535           1 :             bIsPreview ? poOpenInfo->pszFilename + strlen("EXR:PREVIEW:")
     536           0 :                        : poOpenInfo->pszFilename + strlen("EXR:");
     537           1 :         const char *pszNextColumn = strchr(pszPartPos, ':');
     538           1 :         if (pszNextColumn == nullptr)
     539           0 :             return nullptr;
     540           1 :         iPart = atoi(pszPartPos);
     541           1 :         if (iPart <= 0)
     542           0 :             return nullptr;
     543           1 :         osFilename = pszNextColumn + 1;
     544           1 :         fp = VSIFOpenL(osFilename, "rb");
     545           1 :         if (fp == nullptr)
     546           0 :             return nullptr;
     547             :     }
     548             :     else
     549             :     {
     550          89 :         fp = poOpenInfo->fpL;
     551          89 :         poOpenInfo->fpL = nullptr;
     552             :     }
     553             : 
     554             :     try
     555             :     {
     556         180 :         auto poDS = std::make_unique<GDALEXRDataset>();
     557          90 :         poDS->m_pIStream.reset(new GDALEXRIOStream(fp, osFilename));
     558          90 :         poDS->m_pMPIF.reset(new MultiPartInputFile(*poDS->m_pIStream));
     559          90 :         if (iPart > 0 && iPart > poDS->m_pMPIF->parts())
     560           0 :             return nullptr;
     561             : 
     562          90 :         if (iPart > 0 || poDS->m_pMPIF->parts() == 1)
     563             :         {
     564          90 :             iPart = iPart > 0 ? iPart - 1 : 0;
     565          90 :             poDS->m_iPart = iPart;
     566             : 
     567          90 :             const auto &header = poDS->m_pMPIF->header(iPart);
     568          90 :             if (bIsPreview)
     569             :             {
     570           1 :                 if (!header.hasPreviewImage())
     571           0 :                     return nullptr;
     572           5 :                 for (int i = 1; i <= 4; i++)
     573             :                 {
     574           4 :                     const auto &preview = header.previewImage();
     575           4 :                     poDS->nRasterXSize = preview.width();
     576           4 :                     poDS->nRasterYSize = preview.height();
     577           8 :                     poDS->SetBand(i,
     578           4 :                                   new GDALEXRPreviewRasterBand(poDS.get(), i));
     579             :                 }
     580           1 :                 return poDS.release();
     581             :             }
     582             : 
     583          89 :             const auto &dataWindow = header.dataWindow();
     584          89 :             poDS->m_nDWMinX = dataWindow.min.x;
     585          89 :             poDS->m_nDWMinY = dataWindow.min.y;
     586          89 :             poDS->nRasterXSize = 1 + dataWindow.max.x - dataWindow.min.x;
     587          89 :             poDS->nRasterYSize = 1 + dataWindow.max.y - dataWindow.min.y;
     588          89 :             const auto &channels = header.channels();
     589          89 :             int i = 0;
     590          89 :             bool BGR = true;
     591          89 :             bool ABGR = true;
     592          89 :             bool BYRYY = true;
     593          89 :             PixelType samePixelType = NUM_PIXELTYPES;
     594         234 :             for (auto iter = channels.begin(); iter != channels.end();
     595         145 :                  ++iter, ++i)
     596             :             {
     597         145 :                 const Channel &channel = iter.channel();
     598         290 :                 const std::string name(iter.name());
     599         145 :                 if (i == 0)
     600          89 :                     samePixelType = channel.type;
     601          56 :                 else if (samePixelType != channel.type)
     602             :                 {
     603           0 :                     ABGR = false;
     604           0 :                     BGR = false;
     605             :                 }
     606             : 
     607         145 :                 if (i == 0 && name != "B")
     608          84 :                     BGR = false;
     609          61 :                 else if (i == 1 && name != "G")
     610          21 :                     BGR = false;
     611          40 :                 else if (i == 2 && name != "R")
     612          19 :                     BGR = false;
     613             : 
     614         145 :                 if (i == 0 && name != "A")
     615          89 :                     ABGR = false;
     616          56 :                 else if (i == 1 && name != "B")
     617          26 :                     ABGR = false;
     618          30 :                 else if (i == 2 && name != "G")
     619          24 :                     ABGR = false;
     620           6 :                 else if (i == 3 && name != "R")
     621           4 :                     ABGR = false;
     622             : 
     623         145 :                 if (i == 0 && name != "BY")
     624          89 :                     BYRYY = false;
     625          56 :                 else if (i == 1 && name != "RY")
     626          26 :                     BYRYY = false;
     627          30 :                 else if (i == 2 && name != "Y")
     628          24 :                     BYRYY = false;
     629             :             }
     630          89 :             BGR &= (i == 3);
     631          89 :             ABGR &= (i == 4);
     632          89 :             BYRYY &= iPart == 0 && (i == 3);
     633          89 :             int nBlockXSize = poDS->nRasterXSize;
     634          89 :             int nBlockYSize = 1;
     635          89 :             if (header.hasTileDescription())
     636             :             {
     637          89 :                 const auto &tileDesc = header.tileDescription();
     638          89 :                 nBlockXSize = tileDesc.xSize;
     639          89 :                 nBlockYSize = tileDesc.ySize;
     640         178 :                 poDS->m_pTiledIP.reset(
     641          89 :                     new TiledInputPart(*poDS->m_pMPIF, iPart));
     642             :             }
     643           0 :             else if (BYRYY)
     644             :             {
     645           0 :                 poDS->m_pIStream->seekg(0);
     646           0 :                 poDS->m_pRGBAIF.reset(new RgbaInputFile(*poDS->m_pIStream));
     647             :             }
     648             :             else
     649             :             {
     650           0 :                 poDS->m_pIP.reset(new InputPart(*poDS->m_pMPIF, iPart));
     651             :             }
     652          89 :             if (BYRYY)
     653             :             {
     654           0 :                 for (i = 1; i <= 3; i++)
     655             :                 {
     656           0 :                     poDS->SetBand(i, new GDALEXRRGBARasterBand(poDS.get(), i));
     657             :                 }
     658           0 :                 poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     659           0 :                 poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
     660           0 :                                       "IMAGE_STRUCTURE");
     661             :             }
     662          89 :             else if (BGR || ABGR)
     663             :             {
     664           5 :                 const int nBands = i;
     665           5 :                 i = 0;
     666          20 :                 for (auto iter = channels.begin(); iter != channels.end();
     667          15 :                      ++iter, ++i)
     668             :                 {
     669             :                     auto poBand = new GDALEXRRasterBand(
     670          15 :                         poDS.get(), nBands - i, iter.name(), samePixelType,
     671          15 :                         nBlockXSize, nBlockYSize);
     672          15 :                     poBand->m_eInterp = static_cast<GDALColorInterp>(
     673          15 :                         GCI_RedBand + nBands - 1 - i);
     674          15 :                     poDS->SetBand(nBands - i, poBand);
     675           5 :                 }
     676             :             }
     677             :             else
     678             :             {
     679          84 :                 i = 0;
     680         214 :                 for (auto iter = channels.begin(); iter != channels.end();
     681         130 :                      ++iter, ++i)
     682             :                 {
     683         130 :                     const Channel &channel = iter.channel();
     684             :                     auto poBand = new GDALEXRRasterBand(
     685         130 :                         poDS.get(), i + 1, iter.name(), channel.type,
     686         130 :                         nBlockXSize, nBlockYSize);
     687         260 :                     const std::string name(iter.name());
     688         130 :                     if (name != CPLSPrintf("Band%d", i + 1))
     689           0 :                         poBand->SetDescription(name.c_str());
     690         130 :                     if (name == "B")
     691           0 :                         poBand->m_eInterp = GCI_BlueBand;
     692         130 :                     else if (name == "G")
     693           0 :                         poBand->m_eInterp = GCI_GreenBand;
     694         130 :                     else if (name == "R")
     695           0 :                         poBand->m_eInterp = GCI_RedBand;
     696         130 :                     else if (name == "A")
     697           0 :                         poBand->m_eInterp = GCI_AlphaBand;
     698         130 :                     else if (name == "Y")
     699           0 :                         poBand->m_eInterp = GCI_GrayIndex;
     700         130 :                     poDS->SetBand(i + 1, poBand);
     701             :                 }
     702             :             }
     703             : 
     704         178 :             if (poDS->m_pTiledIP && !BYRYY &&
     705             :                 // Not completely clear on tiling & overviews would work
     706             :                 // on dataWindow.min != 0, so exclude that for now
     707         178 :                 dataWindow.min.x == 0 && dataWindow.min.y == 0)
     708             :             {
     709          89 :                 int nLevels = std::min(poDS->m_pTiledIP->numXLevels(),
     710         178 :                                        poDS->m_pTiledIP->numYLevels());
     711          90 :                 for (int iLevel = 1; iLevel < nLevels; iLevel++)
     712             :                 {
     713           2 :                     const int nOvrWidth = poDS->m_pTiledIP->levelWidth(iLevel);
     714             :                     const int nOvrHeight =
     715           2 :                         poDS->m_pTiledIP->levelHeight(iLevel);
     716           2 :                     if (nOvrWidth < 128 && nOvrHeight < 128)
     717             :                     {
     718           1 :                         break;
     719             :                     }
     720           2 :                     auto poOvrDS = std::make_unique<GDALEXRDataset>();
     721             :                     // coverity[escape]
     722           1 :                     poOvrDS->m_poParent = poDS.get();
     723           1 :                     poOvrDS->m_iLevel = iLevel;
     724           1 :                     poOvrDS->nRasterXSize = nOvrWidth;
     725           1 :                     poOvrDS->nRasterYSize = nOvrHeight;
     726           1 :                     poDS->m_apoOvrDS.push_back(std::move(poOvrDS));
     727           1 :                     i = 0;
     728           4 :                     for (auto iter = channels.begin(); iter != channels.end();
     729           3 :                          ++iter, ++i)
     730             :                     {
     731           3 :                         const Channel &channel = iter.channel();
     732             :                         auto poBand = new GDALEXRRasterBand(
     733           3 :                             poDS->m_apoOvrDS.back().get(), i + 1, iter.name(),
     734           3 :                             channel.type, nBlockXSize, nBlockYSize);
     735           3 :                         poDS->m_apoOvrDS.back()->SetBand(i + 1, poBand);
     736             :                     }
     737             :                 }
     738             :             }
     739             : 
     740        1169 :             for (auto iter = header.begin(); iter != header.end(); ++iter)
     741             :             {
     742        1080 :                 const Attribute *attr = &iter.attribute();
     743             :                 const StringAttribute *stringAttr =
     744        1080 :                     dynamic_cast<const StringAttribute *>(attr);
     745             :                 const M33dAttribute *m33DAttr =
     746        1080 :                     dynamic_cast<const M33dAttribute *>(attr);
     747        1080 :                 if (stringAttr && strcmp(iter.name(), "gdal:crsWkt") == 0)
     748             :                 {
     749          78 :                     poDS->m_oSRS.SetAxisMappingStrategy(
     750             :                         OAMS_TRADITIONAL_GIS_ORDER);
     751          78 :                     poDS->m_oSRS.importFromWkt(stringAttr->value().c_str());
     752             :                 }
     753        1080 :                 else if (m33DAttr &&
     754          78 :                          strcmp(iter.name(), "gdal:geoTransform") == 0)
     755             :                 {
     756          78 :                     poDS->m_bHasGT = true;
     757          78 :                     poDS->m_adfGT[0] = m33DAttr->value()[0][2];
     758          78 :                     poDS->m_adfGT[1] = m33DAttr->value()[0][0];
     759          78 :                     poDS->m_adfGT[2] = m33DAttr->value()[0][1];
     760          78 :                     poDS->m_adfGT[3] = m33DAttr->value()[1][2];
     761          78 :                     poDS->m_adfGT[4] = m33DAttr->value()[1][0];
     762          78 :                     poDS->m_adfGT[5] = m33DAttr->value()[1][1];
     763             :                 }
     764         924 :                 else if (stringAttr && STARTS_WITH(iter.name(), "gdal:"))
     765             :                 {
     766          64 :                     poDS->SetMetadataItem(iter.name() + strlen("gdal:"),
     767          32 :                                           stringAttr->value().c_str());
     768             :                 }
     769         892 :                 else if (stringAttr && strcmp(iter.name(), "type") != 0)
     770             :                 {
     771           0 :                     poDS->SetMetadataItem(iter.name(),
     772           0 :                                           stringAttr->value().c_str());
     773             :                 }
     774             :             }
     775             : 
     776          89 :             const auto &compression = header.compression();
     777          89 :             if (compression == NO_COMPRESSION)
     778             :             {
     779             :                 // nothing
     780             :             }
     781          89 :             else if (compression < CPL_ARRAYSIZE(apszCompressions))
     782             :             {
     783          89 :                 poDS->SetMetadataItem("COMPRESSION",
     784          89 :                                       apszCompressions[compression],
     785          89 :                                       "IMAGE_STRUCTURE");
     786             :             }
     787             :             else
     788             :             {
     789           0 :                 CPLDebug("EXR", "Unknown compression method: %d", compression);
     790             :             }
     791             : 
     792          89 :             if (header.hasPreviewImage())
     793             :             {
     794           2 :                 CPLStringList aosSubDS;
     795             :                 aosSubDS.SetNameValue("SUBDATASET_1_NAME",
     796             :                                       CPLSPrintf("EXR:PREVIEW:%d:%s", iPart + 1,
     797           1 :                                                  osFilename.c_str()));
     798           1 :                 aosSubDS.SetNameValue("SUBDATASET_1_DESC", "Preview image");
     799           1 :                 poDS->SetMetadata(aosSubDS.List(), "SUBDATASETS");
     800             :             }
     801             :         }
     802             :         else
     803             :         {
     804           0 :             CPLStringList aosSubDS;
     805           0 :             for (int i = 0; i < poDS->m_pMPIF->parts(); i++)
     806             :             {
     807           0 :                 const auto &header = poDS->m_pMPIF->header(i);
     808             :                 aosSubDS.SetNameValue(
     809             :                     CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
     810           0 :                     CPLSPrintf("EXR:%d:%s", i + 1, poOpenInfo->pszFilename));
     811             :                 aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
     812           0 :                                       header.name().c_str());
     813             :             }
     814           0 :             poDS->SetMetadata(aosSubDS.List(), "SUBDATASETS");
     815             :         }
     816             : 
     817          89 :         poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
     818             : 
     819             :         // Initialize any PAM information.
     820          89 :         poDS->SetDescription(poOpenInfo->pszFilename);
     821          89 :         poDS->TryLoadXML();
     822             : 
     823          89 :         return poDS.release();
     824             :     }
     825           0 :     catch (const std::exception &e)
     826             :     {
     827           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
     828           0 :         return nullptr;
     829             :     }
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                          getPixelType()                              */
     834             : /************************************************************************/
     835             : 
     836          81 : static PixelType getPixelType(GDALDataType eSrcDT, char **papszOptions)
     837             : {
     838          81 :     PixelType pixelType =
     839         125 :         (eSrcDT == GDT_Byte) ? HALF
     840          41 :         : (eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16 || eSrcDT == GDT_UInt32)
     841          85 :             ? UINT
     842             :             : FLOAT;
     843             :     const char *pszPixelType =
     844          81 :         CSLFetchNameValueDef(papszOptions, "PIXEL_TYPE", "");
     845          81 :     if (EQUAL(pszPixelType, "HALF"))
     846           1 :         pixelType = HALF;
     847          80 :     else if (EQUAL(pszPixelType, "FLOAT"))
     848           1 :         pixelType = FLOAT;
     849          79 :     else if (EQUAL(pszPixelType, "UINT"))
     850           1 :         pixelType = UINT;
     851          81 :     return pixelType;
     852             : }
     853             : 
     854          72 : static void WriteSRSInHeader(Header &header, const OGRSpatialReference *poSRS)
     855             : {
     856          72 :     char *pszWKT = nullptr;
     857          72 :     const char *apszOptions[] = {"FORMAT=WKT2_2018", nullptr};
     858          72 :     poSRS->exportToWkt(&pszWKT, apszOptions);
     859          72 :     if (pszWKT)
     860             :     {
     861          72 :         header.insert("gdal:crsWkt", StringAttribute(pszWKT));
     862          72 :         CPLFree(pszWKT);
     863             :     }
     864          72 : }
     865             : 
     866          72 : static void WriteGeoTransformInHeader(Header &header, const double *padfGT)
     867             : {
     868          72 :     M33d gt;
     869          72 :     gt[0][0] = padfGT[1];
     870          72 :     gt[0][1] = padfGT[2];
     871          72 :     gt[0][2] = padfGT[0];
     872          72 :     gt[1][0] = padfGT[4];
     873          72 :     gt[1][1] = padfGT[5];
     874          72 :     gt[1][2] = padfGT[3];
     875          72 :     gt[2][0] = 0;
     876          72 :     gt[2][1] = 0;
     877          72 :     gt[2][2] = 1;
     878          72 :     header.insert("gdal:geoTransform", M33dAttribute(gt));
     879          72 : }
     880             : 
     881          78 : static void WriteMetadataInHeader(Header &header, CSLConstList papszMD)
     882             : {
     883         119 :     for (CSLConstList papszIter = papszMD; papszIter && *papszIter; ++papszIter)
     884             :     {
     885          41 :         char *pszKey = nullptr;
     886          41 :         const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
     887          41 :         if (pszKey && pszValue)
     888             :         {
     889          16 :             header.insert((std::string("gdal:") + pszKey).c_str(),
     890          32 :                           StringAttribute(pszValue));
     891             :         }
     892          41 :         CPLFree(pszKey);
     893             :     }
     894          78 : }
     895             : 
     896          78 : static void FillHeaderFromDataset(Header &header, GDALDataset *poDS)
     897             : {
     898          78 :     const auto poSRS = poDS->GetSpatialRef();
     899          78 :     if (poSRS)
     900             :     {
     901          72 :         WriteSRSInHeader(header, poSRS);
     902             :     }
     903             : 
     904             :     double adfGT[6];
     905          78 :     if (poDS->GetGeoTransform(adfGT) == CE_None)
     906             :     {
     907          72 :         WriteGeoTransformInHeader(header, adfGT);
     908             :     }
     909             : 
     910          78 :     WriteMetadataInHeader(header, poDS->GetMetadata());
     911          78 : }
     912             : 
     913          78 : static void FillHeaderFromOptions(Header &header, CSLConstList papszOptions)
     914             : {
     915             :     const char *pszDWACompressLevel =
     916          78 :         CSLFetchNameValue(papszOptions, "DWA_COMPRESSION_LEVEL");
     917          78 :     if (pszDWACompressLevel)
     918             :     {
     919           1 :         header.insert(
     920             :             "dwaCompressionLevel",
     921           2 :             FloatAttribute(static_cast<float>(CPLAtof(pszDWACompressLevel))));
     922             :     }
     923          78 : }
     924             : 
     925             : /************************************************************************/
     926             : /*                             CreateCopy()                             */
     927             : /************************************************************************/
     928             : 
     929          45 : GDALDataset *GDALEXRDataset::CreateCopy(const char *pszFilename,
     930             :                                         GDALDataset *poSrcDS, int,
     931             :                                         char **papszOptions,
     932             :                                         GDALProgressFunc pfnProgress,
     933             :                                         void *pProgressData)
     934             : {
     935          45 :     const int nBands = poSrcDS->GetRasterCount();
     936          45 :     const int nXSize = poSrcDS->GetRasterXSize();
     937          45 :     const int nYSize = poSrcDS->GetRasterYSize();
     938          45 :     if (nBands == 0)
     939           1 :         return nullptr;
     940             : 
     941          44 :     bool bRGB_or_RGBA = false;
     942          44 :     if ((nBands == 3 || nBands == 4))
     943             :     {
     944           7 :         bRGB_or_RGBA = true;
     945          29 :         for (int iBand = 0; iBand < nBands; iBand++)
     946             :         {
     947          22 :             bRGB_or_RGBA &=
     948          22 :                 (poSrcDS->GetRasterBand(iBand + 1)->GetColorInterpretation() ==
     949          22 :                  GCI_RedBand + iBand);
     950             :         }
     951             :     }
     952             : 
     953             :     const bool bPreview =
     954          45 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "PREVIEW", "NO")) &&
     955           1 :         (nXSize > 100 || nYSize > 100);
     956          44 :     const GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
     957          44 :     if (bPreview && !(bRGB_or_RGBA && eSrcDT == GDT_Byte))
     958             :     {
     959           0 :         CPLError(
     960             :             CE_Failure, CPLE_NotSupported,
     961             :             "Preview creation only supported on RGB/RGBA images of type Byte");
     962           0 :         return nullptr;
     963             :     }
     964          44 :     const PixelType pixelType = getPixelType(eSrcDT, papszOptions);
     965             :     const bool bRescaleDiv255 =
     966          49 :         pixelType == HALF && bRGB_or_RGBA && eSrcDT == GDT_Byte &&
     967           5 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "AUTO_RESCALE", "YES"));
     968             : 
     969          44 :     setNumThreads();
     970             : 
     971          88 :     CPLString osTmpOvrFile;
     972             :     try
     973             :     {
     974          44 :         VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
     975          44 :         if (fp == nullptr)
     976             :         {
     977           3 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
     978           3 :             return nullptr;
     979             :         }
     980          51 :         GDALEXRIOStream ostream(fp, pszFilename);
     981             : 
     982          51 :         std::vector<std::string> channelNames;
     983          41 :         if (bRGB_or_RGBA)
     984             :         {
     985           5 :             channelNames.push_back("R");
     986           5 :             channelNames.push_back("G");
     987           5 :             channelNames.push_back("B");
     988           5 :             if (nBands == 4)
     989             :             {
     990           0 :                 channelNames.push_back("A");
     991             :             }
     992             :         }
     993             :         else
     994             :         {
     995          82 :             for (int iBand = 0; iBand < nBands; iBand++)
     996             :             {
     997          46 :                 channelNames.push_back(CPLSPrintf("Band%d", iBand + 1));
     998             :             }
     999             :         }
    1000             : 
    1001          51 :         Header header(nXSize, nYSize);
    1002             : 
    1003          41 :         if (bPreview)
    1004             :         {
    1005           1 :             const int previewWidth = 100;
    1006             :             const int previewHeight = std::max(
    1007           2 :                 1, static_cast<int>(static_cast<GIntBig>(previewWidth) *
    1008           1 :                                     nYSize / nXSize));
    1009           2 :             std::vector<PreviewRgba> pixels(previewWidth * previewHeight);
    1010           1 :             if (poSrcDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, &pixels[0],
    1011             :                                   previewWidth, previewHeight, GDT_Byte, nBands,
    1012             :                                   nullptr, 4, 4 * previewWidth, 1,
    1013           1 :                                   nullptr) == CE_None)
    1014             :             {
    1015           1 :                 header.setPreviewImage(
    1016           2 :                     PreviewImage(previewWidth, previewHeight, &pixels[0]));
    1017             :             }
    1018             :         }
    1019             : 
    1020          41 :         FillHeaderFromDataset(header, poSrcDS);
    1021             : 
    1022             :         const char *pszCompress =
    1023          41 :             CSLFetchNameValueDef(papszOptions, "COMPRESS", "");
    1024          41 :         if (pszCompress[0] != '\0')
    1025             :         {
    1026           2 :             bool bFound = false;
    1027          12 :             for (size_t i = 0; i < CPL_ARRAYSIZE(apszCompressions); i++)
    1028             :             {
    1029          12 :                 if (EQUAL(pszCompress, apszCompressions[i]))
    1030             :                 {
    1031           2 :                     bFound = true;
    1032           2 :                     header.compression() = static_cast<Compression>(i);
    1033           2 :                     break;
    1034             :                 }
    1035             :             }
    1036           2 :             if (!bFound)
    1037             :             {
    1038           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Unknown compression %s",
    1039             :                          pszCompress);
    1040           0 :                 return nullptr;
    1041             :             }
    1042             :         }
    1043             : 
    1044          41 :         FillHeaderFromOptions(header, papszOptions);
    1045             : 
    1046          51 :         std::vector<half> bufferHalf;
    1047          51 :         std::vector<float> bufferFloat;
    1048          51 :         std::vector<GUInt32> bufferUInt;
    1049          41 :         const size_t pixelTypeSize = (pixelType == HALF) ? 2 : 4;
    1050          41 :         const GDALDataType eDT = (pixelType == UINT) ? GDT_UInt32 : GDT_Float32;
    1051          41 :         const GSpacing nDTSize = GDALGetDataTypeSizeBytes(eDT);
    1052             : 
    1053             :         const bool bTiled =
    1054          41 :             CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "YES"));
    1055             : 
    1056             :         int nChunkXSize;
    1057             :         int nChunkYSize;
    1058             :         const int nBlockXSize =
    1059          41 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256"));
    1060             :         const int nBlockYSize =
    1061          41 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256"));
    1062          41 :         if (nBlockXSize <= 8 || nBlockYSize <= 8 || nBlockXSize >= 8192 ||
    1063             :             nBlockYSize >= 8192)
    1064             :         {
    1065           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
    1066           0 :             return nullptr;
    1067             :         }
    1068          41 :         constexpr int MAX_BUFFER_SIZE = 10 * 1024 * 1024;
    1069             : 
    1070             :         const bool bBuildOvr =
    1071          41 :             CPLTestBool(CSLFetchNameValueDef(papszOptions, "OVERVIEWS", "NO"));
    1072          41 :         if (bBuildOvr && !bTiled)
    1073             :         {
    1074           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1075             :                      "Overviews only supported on tiled images");
    1076           0 :             return nullptr;
    1077             :         }
    1078             : 
    1079          41 :         if (bTiled)
    1080             :         {
    1081          41 :             header.setType(TILEDIMAGE);
    1082          41 :             header.setTileDescription(TileDescription(
    1083             :                 nBlockXSize, nBlockYSize, bBuildOvr ? MIPMAP_LEVELS : ONE_LEVEL,
    1084             :                 ROUND_UP));
    1085          41 :             nChunkYSize = nBlockYSize;
    1086          41 :             nChunkXSize =
    1087             :                 std::min(std::max(nBlockXSize,
    1088          41 :                                   static_cast<int>(
    1089          41 :                                       MAX_BUFFER_SIZE /
    1090          41 :                                       (pixelTypeSize * nBands * nBlockYSize) /
    1091          41 :                                       nBlockXSize * nBlockXSize)),
    1092          41 :                          nXSize);
    1093             :         }
    1094             :         else
    1095             :         {
    1096           0 :             header.setType(SCANLINEIMAGE);
    1097           0 :             nChunkXSize = nXSize;
    1098           0 :             nChunkYSize = std::min(
    1099           0 :                 std::max(1,
    1100           0 :                          static_cast<int>(MAX_BUFFER_SIZE /
    1101           0 :                                           (pixelTypeSize * nBands * nXSize))),
    1102           0 :                 nYSize);
    1103             :         }
    1104             :         char *sliceBuffer;
    1105          41 :         if (pixelType == UINT)
    1106             :         {
    1107           6 :             bufferUInt.resize(static_cast<size_t>(nBands) * nChunkXSize *
    1108           6 :                               nChunkYSize);
    1109           6 :             sliceBuffer = reinterpret_cast<char *>(bufferUInt.data());
    1110             :         }
    1111             :         else
    1112             :         {
    1113          35 :             bufferFloat.resize(static_cast<size_t>(nBands) * nChunkXSize *
    1114          35 :                                nChunkYSize);
    1115          35 :             if (pixelType == HALF)
    1116             :             {
    1117          25 :                 bufferHalf.resize(static_cast<size_t>(nBands) * nChunkXSize *
    1118          25 :                                   nChunkYSize);
    1119          25 :                 sliceBuffer = reinterpret_cast<char *>(bufferHalf.data());
    1120             :             }
    1121             :             else
    1122             :             {
    1123          10 :                 sliceBuffer = reinterpret_cast<char *>(bufferFloat.data());
    1124             :             }
    1125             :         }
    1126             : 
    1127         102 :         for (const auto &channelName : channelNames)
    1128             :         {
    1129          61 :             header.channels().insert(channelName, Channel(pixelType));
    1130             :         }
    1131             : 
    1132          41 :         MultiPartOutputFile mpof(ostream, &header, 1);
    1133          31 :         if (bTiled)
    1134             :         {
    1135          31 :             TiledOutputPart op(mpof, 0);
    1136             : 
    1137          31 :             if (bBuildOvr)
    1138             :             {
    1139           1 :                 if (nBlockXSize != nBlockYSize)
    1140             :                 {
    1141           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1142             :                              "Overview building only works if "
    1143             :                              "BLOCKXSIZE=BLOCKYSIZE");
    1144           0 :                     return nullptr;
    1145             :                 }
    1146           2 :                 if (nBlockXSize < 64 || nBlockXSize > 4096 ||
    1147           1 :                     !CPLIsPowerOfTwo(nBlockXSize))
    1148             :                 {
    1149           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1150             :                              "Overview building only works if "
    1151             :                              "BLOCKXSIZE=BLOCKYSIZE is a power of 2 "
    1152             :                              "between 64 and 4096.");
    1153           0 :                     return nullptr;
    1154             :                 }
    1155             :             }
    1156             : 
    1157             :             const auto writeTiles =
    1158          40 :                 [nChunkXSize, nChunkYSize, nBlockXSize, nBlockYSize, nBands,
    1159             :                  pixelType, pixelTypeSize, sliceBuffer, eDT, nDTSize,
    1160             :                  bRescaleDiv255, &channelNames, &op, &bufferFloat, &bufferHalf,
    1161             :                  &bufferUInt](GDALDataset *l_poDS, int iLevel,
    1162             :                               GDALProgressFunc l_pfnProgress,
    1163     8644620 :                               void *l_pProgressData)
    1164             :             {
    1165          40 :                 const int l_nXSize = l_poDS->GetRasterXSize();
    1166          40 :                 const int l_nYSize = l_poDS->GetRasterYSize();
    1167          40 :                 const int nXBlocks = DIV_ROUND_UP(l_nXSize, nBlockXSize);
    1168          40 :                 const int nYBlocks = DIV_ROUND_UP(l_nYSize, nBlockYSize);
    1169          87 :                 for (int y = 0; y < l_nYSize; y += nChunkYSize)
    1170             :                 {
    1171             :                     const int nLinesToRead =
    1172          47 :                         std::min(nChunkYSize, l_nYSize - y);
    1173          94 :                     for (int x = 0; x < l_nXSize; x += nChunkXSize)
    1174             :                     {
    1175             :                         const int nColsToRead =
    1176          47 :                             std::min(nChunkXSize, l_nXSize - x);
    1177          47 :                         FrameBuffer fb;
    1178         144 :                         for (int iBand = 0; iBand < nBands; iBand++)
    1179             :                         {
    1180             :                             const auto slice = Slice(
    1181             :                                 pixelType,
    1182             :                                 sliceBuffer +
    1183          97 :                                     iBand * pixelTypeSize * nChunkXSize *
    1184          97 :                                         nChunkYSize -
    1185          97 :                                     (x * pixelTypeSize +
    1186          97 :                                      y * pixelTypeSize * nChunkXSize),
    1187          97 :                                 pixelTypeSize, pixelTypeSize * nChunkXSize);
    1188          97 :                             fb.insert(channelNames[iBand], slice);
    1189             :                         }
    1190          47 :                         if (l_poDS->RasterIO(
    1191             :                                 GF_Read, x, y, nColsToRead, nLinesToRead,
    1192          47 :                                 !bufferFloat.empty()
    1193          41 :                                     ? reinterpret_cast<GByte *>(&bufferFloat[0])
    1194           6 :                                     : reinterpret_cast<GByte *>(&bufferUInt[0]),
    1195             :                                 nColsToRead, nLinesToRead, eDT, nBands, nullptr,
    1196          47 :                                 nDTSize, nDTSize * nChunkXSize,
    1197          47 :                                 nDTSize * nChunkXSize * nChunkYSize,
    1198          47 :                                 nullptr) != CE_None)
    1199             :                         {
    1200           0 :                             return false;
    1201             :                         }
    1202          47 :                         if (pixelType == HALF)
    1203             :                         {
    1204          31 :                             const size_t nPixelsInBuffer =
    1205          31 :                                 static_cast<size_t>(nChunkXSize) * nChunkYSize *
    1206          31 :                                 nBands;
    1207          31 :                             if (bRescaleDiv255)
    1208             :                             {
    1209     3955220 :                                 for (size_t i = 0; i < nPixelsInBuffer; i++)
    1210             :                                 {
    1211     3955200 :                                     bufferHalf[i] = bufferFloat[i] / 255.0f;
    1212             :                                 }
    1213             :                             }
    1214             :                             else
    1215             :                             {
    1216      366692 :                                 for (size_t i = 0; i < nPixelsInBuffer; i++)
    1217             :                                 {
    1218      366680 :                                     bufferHalf[i] = bufferFloat[i];
    1219             :                                 }
    1220             :                             }
    1221             :                         }
    1222          47 :                         op.setFrameBuffer(fb);
    1223          47 :                         const int blockXMax =
    1224          47 :                             (x + nColsToRead - 1) / nBlockXSize;
    1225          47 :                         const int blockYMax =
    1226          47 :                             (y + nLinesToRead - 1) / nBlockYSize;
    1227          47 :                         op.writeTiles(x / nBlockXSize, blockXMax,
    1228             :                                       y / nBlockYSize, blockYMax, iLevel);
    1229          94 :                         if (l_pfnProgress &&
    1230          47 :                             !l_pfnProgress(
    1231          47 :                                 (static_cast<double>(blockYMax) * nXBlocks +
    1232          47 :                                  blockXMax + 1) /
    1233             :                                     nXBlocks / nYBlocks,
    1234             :                                 "", l_pProgressData))
    1235             :                         {
    1236           0 :                             return false;
    1237             :                         }
    1238             :                     }
    1239             :                 }
    1240          40 :                 return true;
    1241          31 :             };
    1242             : 
    1243             :             struct ScaledProgressReleaser
    1244             :             {
    1245           0 :                 void operator()(void *progress) const
    1246             :                 {
    1247           0 :                     GDALDestroyScaledProgress(progress);
    1248           0 :                 }
    1249             :             };
    1250             : 
    1251             :             using ScaledProgressUniquePtr =
    1252             :                 std::unique_ptr<void, ScaledProgressReleaser>;
    1253           0 :             ScaledProgressUniquePtr progress;
    1254             : 
    1255             :             // Write full resolution imagery
    1256          31 :             if (bBuildOvr)
    1257           1 :                 progress.reset(GDALCreateScaledProgress(0, 0.5, pfnProgress,
    1258             :                                                         pProgressData));
    1259             :             else
    1260          30 :                 progress.reset(
    1261             :                     GDALCreateScaledProgress(0, 1, pfnProgress, pProgressData));
    1262          31 :             if (!writeTiles(poSrcDS, 0, GDALScaledProgress, progress.get()))
    1263             :             {
    1264           0 :                 if (!osTmpOvrFile.empty())
    1265           0 :                     VSIUnlink(osTmpOvrFile);
    1266           0 :                 return nullptr;
    1267             :             }
    1268             : 
    1269          31 :             if (bBuildOvr)
    1270             :             {
    1271             :                 // First build overviews in a temporary GTiff file
    1272           1 :                 GDALDefaultOverviews oOvr;
    1273           1 :                 oOvr.Initialize(poSrcDS);
    1274           1 :                 std::vector<int> anOvrFactors;
    1275          10 :                 for (int i = 1; i < op.numLevels(); i++)
    1276           9 :                     anOvrFactors.push_back(1 << i);
    1277           1 :                 std::vector<int> anBands;
    1278           4 :                 for (int iBand = 0; iBand < nBands; iBand++)
    1279           3 :                     anBands.push_back(iBand + 1);
    1280             :                 CPLConfigOptionSetter oSetter("GDAL_TIFF_OVR_BLOCKSIZE",
    1281             :                                               CPLSPrintf("%d", nBlockXSize),
    1282           1 :                                               false);
    1283             :                 const CPLString osTmpOvrFileRadix(
    1284           1 :                     CPLSPrintf("%s_tmp", pszFilename));
    1285           1 :                 osTmpOvrFile = osTmpOvrFileRadix + ".ovr";
    1286           1 :                 progress.reset(GDALCreateScaledProgress(0.5, 0.8, pfnProgress,
    1287             :                                                         pProgressData));
    1288           2 :                 if (oOvr.BuildOverviews(
    1289             :                         osTmpOvrFileRadix,
    1290             :                         CSLFetchNameValueDef(papszOptions,
    1291             :                                              "OVERVIEW_RESAMPLING", "CUBIC"),
    1292           1 :                         static_cast<int>(anOvrFactors.size()), &anOvrFactors[0],
    1293           1 :                         nBands, &anBands[0], GDALScaledProgress, progress.get(),
    1294           1 :                         nullptr) != CE_None)
    1295             :                 {
    1296           0 :                     VSIUnlink(osTmpOvrFile);
    1297           0 :                     return nullptr;
    1298             :                 }
    1299             : 
    1300             :                 // Transfer overviews from temporary file to main image
    1301             :                 std::unique_ptr<GDALDataset> poOvrDS(
    1302           1 :                     GDALDataset::Open(osTmpOvrFile));
    1303           1 :                 if (!poOvrDS)
    1304           0 :                     return nullptr;
    1305             :                 const int nOvrs =
    1306           1 :                     1 + poOvrDS->GetRasterBand(1)->GetOverviewCount();
    1307          10 :                 for (int i = 0; i < nOvrs; i++)
    1308             :                 {
    1309           9 :                     auto poThisOvrDS = (i == 0) ? poOvrDS.get()
    1310           8 :                                                 : poOvrDS->GetRasterBand(1)
    1311           8 :                                                       ->GetOverview(i - 1)
    1312           8 :                                                       ->GetDataset();
    1313           9 :                     CPLAssert(poThisOvrDS);
    1314           9 :                     if (i == 0)
    1315           1 :                         progress.reset(GDALCreateScaledProgress(
    1316             :                             0.8, nOvrs == 1 ? 1.0 : 0.9, pfnProgress,
    1317             :                             pProgressData));
    1318           8 :                     else if (i == 1)
    1319           1 :                         progress.reset(GDALCreateScaledProgress(
    1320             :                             0.9, nOvrs == 2 ? 1.0 : 0.95, pfnProgress,
    1321             :                             pProgressData));
    1322             :                     else
    1323           7 :                         progress.reset(GDALCreateScaledProgress(
    1324           7 :                             0.95 + 0.05 * (i - 2) / (nOvrs - 2),
    1325           7 :                             0.95 + 0.05 * (i - 2 + 1) / (nOvrs - 2),
    1326             :                             pfnProgress, pProgressData));
    1327           9 :                     if (!writeTiles(poThisOvrDS, i + 1, GDALScaledProgress,
    1328             :                                     progress.get()))
    1329             :                     {
    1330           0 :                         poOvrDS.reset();
    1331           0 :                         VSIUnlink(osTmpOvrFile);
    1332           0 :                         return nullptr;
    1333             :                     }
    1334             :                 }
    1335             : 
    1336           1 :                 poOvrDS.reset();
    1337           1 :                 VSIUnlink(osTmpOvrFile);
    1338             :             }
    1339             :         }
    1340             :         else
    1341             :         {
    1342           0 :             OutputPart op(mpof, 0);
    1343             : 
    1344           0 :             for (int y = 0; y < nYSize; y += nChunkYSize)
    1345             :             {
    1346           0 :                 FrameBuffer fb;
    1347           0 :                 const int nLinesToRead = std::min(nChunkYSize, nYSize - y);
    1348           0 :                 for (int iBand = 0; iBand < nBands; iBand++)
    1349             :                 {
    1350             :                     const auto slice = Slice(
    1351             :                         pixelType,
    1352             :                         sliceBuffer +
    1353           0 :                             iBand * pixelTypeSize * nXSize * nLinesToRead -
    1354           0 :                             y * pixelTypeSize * nXSize,
    1355           0 :                         pixelTypeSize, pixelTypeSize * nXSize);
    1356           0 :                     fb.insert(channelNames[iBand], slice);
    1357             :                 }
    1358           0 :                 if (poSrcDS->RasterIO(
    1359             :                         GF_Read, 0, y, nXSize, nLinesToRead,
    1360           0 :                         !bufferFloat.empty()
    1361           0 :                             ? reinterpret_cast<GByte *>(&bufferFloat[0])
    1362           0 :                             : reinterpret_cast<GByte *>(&bufferUInt[0]),
    1363             :                         nXSize, nLinesToRead, eDT, nBands, nullptr, nDTSize,
    1364           0 :                         nDTSize * nXSize, nDTSize * nXSize * nLinesToRead,
    1365           0 :                         nullptr) != CE_None)
    1366             :                 {
    1367           0 :                     return nullptr;
    1368             :                 }
    1369           0 :                 if (pixelType == HALF)
    1370             :                 {
    1371           0 :                     for (size_t i = 0; i < static_cast<size_t>(nXSize) *
    1372           0 :                                                nLinesToRead * nBands;
    1373             :                          i++)
    1374             :                     {
    1375             :                         // cppcheck-suppress unreadVariable
    1376           0 :                         bufferHalf[i] = bufferFloat[i];
    1377             :                     }
    1378             :                 }
    1379           0 :                 op.setFrameBuffer(fb);
    1380           0 :                 op.writePixels(nLinesToRead);
    1381           0 :                 if (pfnProgress &&
    1382           0 :                     !pfnProgress(static_cast<double>(y + nLinesToRead) / nYSize,
    1383             :                                  "", pProgressData))
    1384             :                 {
    1385           0 :                     return nullptr;
    1386             :                 }
    1387             :             }
    1388             :         }
    1389             :     }
    1390          10 :     catch (const std::exception &e)
    1391             :     {
    1392          10 :         if (!osTmpOvrFile.empty())
    1393           0 :             VSIUnlink(osTmpOvrFile);
    1394          10 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
    1395          10 :         return nullptr;
    1396             :     }
    1397          62 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    1398          31 :     return GDALEXRDataset::Open(&oOpenInfo);
    1399             : }
    1400             : 
    1401             : /************************************************************************/
    1402             : /*                         GDALEXRWritableDataset                       */
    1403             : /************************************************************************/
    1404             : 
    1405             : class GDALEXRWritableDataset final : public GDALPamDataset
    1406             : {
    1407             :     friend class GDALEXRDataset;
    1408             :     friend class GDALEXRWritableRasterBand;
    1409             : 
    1410             :     PixelType m_pixelType = HALF;
    1411             :     int m_nBlockXSize = 0;
    1412             :     int m_nBlockYSize = 0;
    1413             : 
    1414             :     // Keep stream before others, so that it is destroyed last
    1415             :     std::unique_ptr<OStream> m_pOStream{};
    1416             : 
    1417             :     std::unique_ptr<TiledOutputPart> m_pTOP{};
    1418             :     std::unique_ptr<MultiPartOutputFile> m_pMPOF{};
    1419             : 
    1420             :     std::vector<std::string> m_channelNames{};
    1421             : 
    1422             :     bool m_bTriedWritingHeader = false;
    1423             :     std::vector<half> m_bufferHalf{};
    1424             :     std::vector<float> m_bufferFloat{};
    1425             :     std::vector<GUInt32> m_bufferUInt{};
    1426             :     size_t m_nBufferEltSize = 0;
    1427             :     char *m_pSliceBuffer = nullptr;
    1428             : 
    1429             :     OGRSpatialReference m_oSRS{};
    1430             :     double m_adfGT[6] = {0, 1, 0, 0, 0, 1};
    1431             :     bool m_bHasGT = false;
    1432             : 
    1433             :     CPLStringList m_aosMetadata{};
    1434             : 
    1435             :     std::vector<bool> m_abWrittenBlocks{};
    1436             :     size_t m_nXBlocks = 0;
    1437             : 
    1438             :     bool m_bRescaleDiv255 = false;
    1439             : 
    1440             :     Header m_header;
    1441             : 
    1442             :     void WriteHeader();
    1443             : 
    1444             :   public:
    1445          37 :     GDALEXRWritableDataset(int nXSize, int nYSize) : m_header(nXSize, nYSize)
    1446             :     {
    1447          37 :         nRasterXSize = nXSize;
    1448          37 :         nRasterYSize = nYSize;
    1449          37 :     }
    1450             : 
    1451             :     ~GDALEXRWritableDataset() override;
    1452             : 
    1453             :     CPLErr SetGeoTransform(double *adfGT) override;
    1454             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
    1455             : 
    1456             :     const OGRSpatialReference *GetSpatialRef() const override;
    1457             :     CPLErr GetGeoTransform(double *adfGT) override;
    1458             : 
    1459             :     CPLErr SetMetadata(char **, const char * = "") override;
    1460             :     CPLErr SetMetadataItem(const char *, const char *,
    1461             :                            const char * = "") override;
    1462             : 
    1463             :     char **GetMetadata(const char *pszDomain = "") override;
    1464             :     const char *GetMetadataItem(const char *pszName,
    1465             :                                 const char *pszDomain = "") override;
    1466             : };
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                       ~GDALEXRWritableDataset()                      */
    1470             : /************************************************************************/
    1471             : 
    1472          74 : GDALEXRWritableDataset::~GDALEXRWritableDataset()
    1473             : {
    1474          37 :     WriteHeader();
    1475          37 :     FlushCache(true);
    1476          74 : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                            SetGeoTransform()                         */
    1480             : /************************************************************************/
    1481             : 
    1482          31 : CPLErr GDALEXRWritableDataset::SetGeoTransform(double *adfGT)
    1483             : {
    1484          31 :     if (m_bTriedWritingHeader)
    1485             :     {
    1486           0 :         CPLError(
    1487             :             CE_Warning, CPLE_AppDefined,
    1488             :             "SetGeoTransform() called after writing pixels. Will go to PAM");
    1489           0 :         return GDALPamDataset::SetGeoTransform(adfGT);
    1490             :     }
    1491          31 :     m_bHasGT = true;
    1492          31 :     memcpy(m_adfGT, adfGT, 6 * sizeof(double));
    1493          31 :     return CE_None;
    1494             : }
    1495             : 
    1496             : /************************************************************************/
    1497             : /*                            SetSpatialRef()                           */
    1498             : /************************************************************************/
    1499             : 
    1500          31 : CPLErr GDALEXRWritableDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1501             : {
    1502          31 :     if (m_bTriedWritingHeader)
    1503             :     {
    1504           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1505             :                  "SetSpatialRef() called after writing pixels. Will go to PAM");
    1506           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1507             :     }
    1508          31 :     if (poSRS)
    1509          31 :         m_oSRS = *poSRS;
    1510             :     else
    1511           0 :         m_oSRS.Clear();
    1512          31 :     return CE_None;
    1513             : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                             SetMetadata()                            */
    1517             : /************************************************************************/
    1518             : 
    1519           0 : CPLErr GDALEXRWritableDataset::SetMetadata(char **papszMD,
    1520             :                                            const char *pszDomain)
    1521             : {
    1522           0 :     if (pszDomain == nullptr || pszDomain[0] == 0)
    1523             :     {
    1524           0 :         m_aosMetadata = CSLDuplicate(papszMD);
    1525           0 :         if (m_bTriedWritingHeader)
    1526             :         {
    1527           0 :             CPLError(
    1528             :                 CE_Warning, CPLE_AppDefined,
    1529             :                 "SetMetadata() called after writing pixels. Will go to PAM");
    1530             :         }
    1531             :         else
    1532             :         {
    1533           0 :             return CE_None;
    1534             :         }
    1535             :     }
    1536           0 :     return GDALPamDataset::SetMetadata(papszMD, pszDomain);
    1537             : }
    1538             : 
    1539             : /************************************************************************/
    1540             : /*                           SetMetadataItem()                          */
    1541             : /************************************************************************/
    1542             : 
    1543           0 : CPLErr GDALEXRWritableDataset::SetMetadataItem(const char *pszName,
    1544             :                                                const char *pszValue,
    1545             :                                                const char *pszDomain)
    1546             : {
    1547           0 :     if (pszDomain == nullptr || pszDomain[0] == 0)
    1548             :     {
    1549           0 :         m_aosMetadata.SetNameValue(pszName, pszValue);
    1550           0 :         if (m_bTriedWritingHeader)
    1551             :         {
    1552           0 :             CPLError(
    1553             :                 CE_Warning, CPLE_AppDefined,
    1554             :                 "SetMetadata() called after writing pixels. Will go to PAM");
    1555             :         }
    1556             :         else
    1557             :         {
    1558           0 :             return CE_None;
    1559             :         }
    1560             :     }
    1561           0 :     return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1562             : }
    1563             : 
    1564             : /************************************************************************/
    1565             : /*                             GetMetadata()                            */
    1566             : /************************************************************************/
    1567             : 
    1568          37 : char **GDALEXRWritableDataset::GetMetadata(const char *pszDomain)
    1569             : {
    1570          37 :     if (pszDomain == nullptr || pszDomain[0] == 0)
    1571             :     {
    1572          37 :         return m_aosMetadata.List();
    1573             :     }
    1574           0 :     return GDALPamDataset::GetMetadata(pszDomain);
    1575             : }
    1576             : 
    1577             : /************************************************************************/
    1578             : /*                           GetMetadataItem()                          */
    1579             : /************************************************************************/
    1580             : 
    1581           5 : const char *GDALEXRWritableDataset::GetMetadataItem(const char *pszName,
    1582             :                                                     const char *pszDomain)
    1583             : {
    1584           5 :     if (pszDomain == nullptr || pszDomain[0] == 0)
    1585             :     {
    1586           5 :         return m_aosMetadata.FetchNameValue(pszName);
    1587             :     }
    1588           0 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    1589             : }
    1590             : 
    1591             : /************************************************************************/
    1592             : /*                          GetSpatialRef()                             */
    1593             : /************************************************************************/
    1594             : 
    1595          37 : const OGRSpatialReference *GDALEXRWritableDataset::GetSpatialRef() const
    1596             : {
    1597          37 :     const auto *poPamSRS = GDALPamDataset::GetSpatialRef();
    1598          37 :     if (poPamSRS)
    1599           0 :         return poPamSRS;
    1600          37 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1601             : }
    1602             : 
    1603             : /************************************************************************/
    1604             : /*                         GetGeoTransform()                            */
    1605             : /************************************************************************/
    1606             : 
    1607          68 : CPLErr GDALEXRWritableDataset::GetGeoTransform(double *adfGT)
    1608             : {
    1609          68 :     if (GDALPamDataset::GetGeoTransform(adfGT) == CE_None)
    1610             :     {
    1611           0 :         return CE_None;
    1612             :     }
    1613          68 :     memcpy(adfGT, m_adfGT, 6 * sizeof(double));
    1614          68 :     return m_bHasGT ? CE_None : CE_Failure;
    1615             : }
    1616             : 
    1617             : /************************************************************************/
    1618             : /*                             WriteHeader()                            */
    1619             : /************************************************************************/
    1620             : 
    1621          43 : void GDALEXRWritableDataset::WriteHeader()
    1622             : {
    1623          43 :     if (m_bTriedWritingHeader)
    1624           6 :         return;
    1625          37 :     m_bTriedWritingHeader = true;
    1626             : 
    1627             :     try
    1628             :     {
    1629          37 :         FillHeaderFromDataset(m_header, this);
    1630             : 
    1631          37 :         bool bRGB_or_RGBA = false;
    1632          37 :         if (nBands == 3 || nBands == 4)
    1633             :         {
    1634          15 :             bRGB_or_RGBA = true;
    1635          61 :             for (int i = 0; i < nBands; i++)
    1636             :             {
    1637          46 :                 bRGB_or_RGBA &=
    1638          46 :                     GetRasterBand(i + 1)->GetColorInterpretation() ==
    1639          46 :                     GCI_RedBand + i;
    1640             :             }
    1641             :         }
    1642          37 :         m_bRescaleDiv255 &= m_pixelType == HALF && bRGB_or_RGBA &&
    1643           0 :                             GetRasterBand(1)->GetRasterDataType() == GDT_Byte;
    1644             : 
    1645          37 :         if (bRGB_or_RGBA)
    1646             :         {
    1647           0 :             m_channelNames.push_back("R");
    1648           0 :             m_channelNames.push_back("G");
    1649           0 :             m_channelNames.push_back("B");
    1650           0 :             if (nBands == 4)
    1651             :             {
    1652           0 :                 m_channelNames.push_back("A");
    1653             :             }
    1654             :         }
    1655             :         else
    1656             :         {
    1657         110 :             for (int iBand = 0; iBand < nBands; iBand++)
    1658             :             {
    1659          73 :                 m_channelNames.push_back(CPLSPrintf("Band%d", iBand + 1));
    1660             :             }
    1661             :         }
    1662             : 
    1663         110 :         for (int i = 0; i < nBands; i++)
    1664             :         {
    1665          73 :             m_header.channels().insert(m_channelNames[i], Channel(m_pixelType));
    1666             :         }
    1667             : 
    1668          37 :         m_pMPOF.reset(new MultiPartOutputFile(*m_pOStream, &m_header, 1));
    1669          37 :         m_pTOP.reset(new TiledOutputPart(*m_pMPOF, 0));
    1670             : 
    1671          37 :         const size_t nElts =
    1672          37 :             static_cast<size_t>(nBands) * m_nBlockXSize * m_nBlockYSize;
    1673          37 :         if (m_pixelType == HALF)
    1674             :         {
    1675           7 :             m_bufferHalf.resize(nElts);
    1676           7 :             m_bufferFloat.resize(nElts / nBands);
    1677           7 :             m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferHalf[0]);
    1678           7 :             m_nBufferEltSize = sizeof(half);
    1679             :         }
    1680          30 :         else if (m_pixelType == FLOAT)
    1681             :         {
    1682          22 :             m_bufferFloat.resize(nElts);
    1683          22 :             m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferFloat[0]);
    1684          22 :             m_nBufferEltSize = sizeof(float);
    1685             :         }
    1686             :         else
    1687             :         {
    1688           8 :             m_bufferUInt.resize(nElts);
    1689           8 :             m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferUInt[0]);
    1690           8 :             m_nBufferEltSize = sizeof(unsigned int);
    1691             :         }
    1692             :     }
    1693           0 :     catch (const std::exception &e)
    1694             :     {
    1695           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
    1696           0 :         m_pTOP.reset();
    1697           0 :         m_pMPOF.reset();
    1698             :     }
    1699             : }
    1700             : 
    1701             : /************************************************************************/
    1702             : /*                       GDALEXRWritableRasterBand                      */
    1703             : /************************************************************************/
    1704             : 
    1705             : class GDALEXRWritableRasterBand final : public GDALPamRasterBand
    1706             : {
    1707             :     GDALColorInterp m_eInterp = GCI_Undefined;
    1708             : 
    1709             :   protected:
    1710             :     CPLErr IReadBlock(int, int, void *) override;
    1711             :     CPLErr IWriteBlock(int, int, void *) override;
    1712             : 
    1713             :   public:
    1714             :     GDALEXRWritableRasterBand(GDALEXRWritableDataset *poDSIn, int nBandIn,
    1715             :                               GDALDataType eTypeIn);
    1716             : 
    1717           0 :     CPLErr SetColorInterpretation(GDALColorInterp eInterp) override
    1718             :     {
    1719           0 :         m_eInterp = eInterp;
    1720           0 :         return CE_None;
    1721             :     }
    1722             : 
    1723          46 :     GDALColorInterp GetColorInterpretation() override
    1724             :     {
    1725          46 :         return m_eInterp;
    1726             :     }
    1727             : };
    1728             : 
    1729             : /************************************************************************/
    1730             : /*                       GDALEXRWritableRasterBand()                    */
    1731             : /************************************************************************/
    1732             : 
    1733          73 : GDALEXRWritableRasterBand::GDALEXRWritableRasterBand(
    1734          73 :     GDALEXRWritableDataset *poDSIn, int nBandIn, GDALDataType eTypeIn)
    1735             : {
    1736          73 :     poDS = poDSIn;
    1737          73 :     nBand = nBandIn;
    1738          73 :     nRasterXSize = poDSIn->GetRasterXSize();
    1739          73 :     nRasterYSize = poDSIn->GetRasterYSize();
    1740          73 :     nBlockXSize = poDSIn->m_nBlockXSize;
    1741          73 :     nBlockYSize = poDSIn->m_nBlockYSize;
    1742          73 :     eDataType = eTypeIn;
    1743          73 : }
    1744             : 
    1745             : /************************************************************************/
    1746             : /*                           IReadBlock()                               */
    1747             : /************************************************************************/
    1748             : 
    1749           0 : CPLErr GDALEXRWritableRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1750             :                                              void *pImage)
    1751             : {
    1752           0 :     auto poGDS = cpl::down_cast<GDALEXRWritableDataset *>(poDS);
    1753           0 :     if (!poGDS->m_abWrittenBlocks[nBlockYOff * poGDS->m_nXBlocks + nBlockXOff])
    1754             :     {
    1755           0 :         const size_t nPixelsInBlock =
    1756           0 :             static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1757           0 :         memset(pImage, 0, nPixelsInBlock * GDALGetDataTypeSizeBytes(eDataType));
    1758           0 :         return CE_None;
    1759             :     }
    1760           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    1761             :              "Reading blocks in a EXR dataset created by Create() is not "
    1762             :              "supported");
    1763           0 :     return CE_Failure;
    1764             : }
    1765             : 
    1766             : /************************************************************************/
    1767             : /*                           IWriteBlock()                              */
    1768             : /************************************************************************/
    1769             : 
    1770           6 : CPLErr GDALEXRWritableRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
    1771             :                                               void *pImage)
    1772             : {
    1773           6 :     auto poGDS = cpl::down_cast<GDALEXRWritableDataset *>(poDS);
    1774           6 :     poGDS->WriteHeader();
    1775           6 :     if (!poGDS->m_pTOP)
    1776           0 :         return CE_Failure;
    1777             : 
    1778           6 :     poGDS->m_abWrittenBlocks[nBlockYOff * poGDS->m_nXBlocks + nBlockXOff] =
    1779           6 :         true;
    1780             : 
    1781           6 :     bool bAllBlocksDirty = true;
    1782           6 :     std::vector<GDALRasterBlock *> apoBlocks;
    1783           6 :     apoBlocks.resize(poGDS->nBands);
    1784          12 :     for (int iBand = 0; iBand < poGDS->nBands; ++iBand)
    1785             :     {
    1786           6 :         if (iBand + 1 != nBand)
    1787             :         {
    1788           0 :             apoBlocks[iBand] =
    1789           0 :                 cpl::down_cast<GDALEXRWritableRasterBand *>(
    1790             :                     poGDS->GetRasterBand(iBand + 1))
    1791           0 :                     ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1792             : 
    1793           0 :             if (apoBlocks[iBand] == nullptr)
    1794             :             {
    1795           0 :                 bAllBlocksDirty = false;
    1796           0 :                 break;
    1797             :             }
    1798           0 :             else if (!apoBlocks[iBand]->GetDirty())
    1799             :             {
    1800           0 :                 apoBlocks[iBand]->DropLock();
    1801           0 :                 apoBlocks[iBand] = nullptr;
    1802           0 :                 bAllBlocksDirty = false;
    1803           0 :                 break;
    1804             :             }
    1805             :         }
    1806             :         else
    1807             :         {
    1808           6 :             apoBlocks[iBand] = nullptr;
    1809             :         }
    1810             :     }
    1811           6 :     if (!bAllBlocksDirty)
    1812             :     {
    1813           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1814             :                  "For block (%d, %d), blocks for some bands are not available "
    1815             :                  "in the cache. Corresponding data will be assumed to be zero.",
    1816             :                  nBlockXOff, nBlockYOff);
    1817             :     }
    1818             : 
    1819           6 :     CPLErr eErr = CE_None;
    1820             :     try
    1821             :     {
    1822          12 :         FrameBuffer fb;
    1823           6 :         const int x = nBlockXOff * nBlockXSize;
    1824           6 :         const int y = nBlockYOff * nBlockYSize;
    1825           6 :         const size_t nPixelsInBlock =
    1826           6 :             static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1827           6 :         const GDALDataType eDstDT =
    1828           6 :             poGDS->m_pixelType == UINT ? GDT_UInt32 : GDT_Float32;
    1829          12 :         for (int iBand = 0; iBand < poGDS->nBands; iBand++)
    1830             :         {
    1831           6 :             char *const dstPtr =
    1832           6 :                 poGDS->m_pSliceBuffer +
    1833           6 :                 iBand * poGDS->m_nBufferEltSize * nPixelsInBlock;
    1834             :             const auto slice = Slice(
    1835             :                 poGDS->m_pixelType,
    1836           6 :                 dstPtr - (x * poGDS->m_nBufferEltSize +
    1837           6 :                           y * poGDS->m_nBufferEltSize * nBlockXSize),
    1838           6 :                 poGDS->m_nBufferEltSize, poGDS->m_nBufferEltSize * nBlockXSize);
    1839           6 :             fb.insert(poGDS->m_channelNames[iBand], slice);
    1840             : 
    1841           6 :             const void *srcPtr = nullptr;
    1842           6 :             if (iBand + 1 == nBand)
    1843           6 :                 srcPtr = pImage;
    1844           0 :             else if (apoBlocks[iBand])
    1845           0 :                 srcPtr = apoBlocks[iBand]->GetDataRef();
    1846             :             else
    1847             :             {
    1848           0 :                 memset(poGDS->m_pSliceBuffer +
    1849           0 :                            iBand * poGDS->m_nBufferEltSize * nPixelsInBlock,
    1850           0 :                        0, nPixelsInBlock * poGDS->m_nBufferEltSize);
    1851           0 :                 continue;
    1852             :             }
    1853             : 
    1854          12 :             GDALCopyWords64(srcPtr, eDataType,
    1855             :                             GDALGetDataTypeSizeBytes(eDataType),
    1856           6 :                             poGDS->m_pixelType == HALF
    1857           2 :                                 ? static_cast<void *>(&poGDS->m_bufferFloat[0])
    1858             :                                 : static_cast<void *>(dstPtr),
    1859             :                             eDstDT, GDALGetDataTypeSizeBytes(eDstDT),
    1860             :                             static_cast<GPtrDiff_t>(nPixelsInBlock));
    1861           6 :             if (poGDS->m_pixelType == HALF)
    1862             :             {
    1863           2 :                 if (poGDS->m_bRescaleDiv255)
    1864             :                 {
    1865           0 :                     for (size_t i = 0; i < nPixelsInBlock; i++)
    1866             :                     {
    1867           0 :                         poGDS->m_bufferHalf[iBand * nPixelsInBlock + i] =
    1868           0 :                             poGDS->m_bufferFloat[i] / 255.0f;
    1869             :                     }
    1870             :                 }
    1871             :                 else
    1872             :                 {
    1873      131074 :                     for (size_t i = 0; i < nPixelsInBlock; i++)
    1874             :                     {
    1875      131072 :                         poGDS->m_bufferHalf[iBand * nPixelsInBlock + i] =
    1876      131072 :                             poGDS->m_bufferFloat[i];
    1877             :                     }
    1878             :                 }
    1879             :             }
    1880             :         }
    1881             : 
    1882           6 :         poGDS->m_pTOP->setFrameBuffer(fb);
    1883           6 :         poGDS->m_pTOP->writeTile(nBlockXOff, nBlockYOff);
    1884             :     }
    1885           0 :     catch (const std::exception &e)
    1886             :     {
    1887           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
    1888           0 :         eErr = CE_Failure;
    1889             :     }
    1890             : 
    1891          12 :     for (int iBand = 0; iBand < poGDS->nBands; ++iBand)
    1892             :     {
    1893           6 :         if (apoBlocks[iBand])
    1894             :         {
    1895           0 :             apoBlocks[iBand]->MarkClean();
    1896           0 :             apoBlocks[iBand]->DropLock();
    1897             :         }
    1898             :     }
    1899             : 
    1900           6 :     return eErr;
    1901             : }
    1902             : 
    1903             : /************************************************************************/
    1904             : /*                            Create()                                  */
    1905             : /************************************************************************/
    1906             : 
    1907          38 : GDALDataset *GDALEXRDataset::Create(const char *pszFilename, int nXSize,
    1908             :                                     int nYSize, int nBandsIn,
    1909             :                                     GDALDataType eType, char **papszOptions)
    1910             : {
    1911          38 :     if (nBandsIn == 0)
    1912           1 :         return nullptr;
    1913          37 :     const PixelType pixelType = getPixelType(eType, papszOptions);
    1914             : 
    1915          37 :     if (!CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "YES")))
    1916             :     {
    1917           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1918             :                  "Create() only supports tiled mode");
    1919           0 :         return nullptr;
    1920             :     }
    1921             : 
    1922          37 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "OVERVIEWS", "NO")))
    1923             :     {
    1924           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1925             :                  "Create() does not support overview creation.");
    1926           0 :         return nullptr;
    1927             :     }
    1928             : 
    1929          37 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PREVIEW", "NO")))
    1930             :     {
    1931           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1932             :                  "Create() does not support preview creation.");
    1933           0 :         return nullptr;
    1934             :     }
    1935             : 
    1936          37 :     Compression compression = ZIP_COMPRESSION;
    1937             :     const char *pszCompress =
    1938          37 :         CSLFetchNameValueDef(papszOptions, "COMPRESS", "");
    1939          37 :     if (pszCompress[0] != '\0')
    1940             :     {
    1941           1 :         bool bFound = false;
    1942           2 :         for (size_t i = 0; i < CPL_ARRAYSIZE(apszCompressions); i++)
    1943             :         {
    1944           2 :             if (EQUAL(pszCompress, apszCompressions[i]))
    1945             :             {
    1946           1 :                 bFound = true;
    1947           1 :                 compression = static_cast<Compression>(i);
    1948           1 :                 break;
    1949             :             }
    1950             :         }
    1951           1 :         if (!bFound)
    1952             :         {
    1953           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unknown compression %s",
    1954             :                      pszCompress);
    1955           0 :             return nullptr;
    1956             :         }
    1957             :     }
    1958             : 
    1959             :     const int nBlockXSize =
    1960          37 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256"));
    1961             :     const int nBlockYSize =
    1962          37 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256"));
    1963          37 :     if (nBlockXSize <= 8 || nBlockYSize <= 8 || nBlockXSize >= 8192 ||
    1964             :         nBlockYSize >= 8192)
    1965             :     {
    1966           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
    1967           0 :         return nullptr;
    1968             :     }
    1969             : 
    1970          37 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
    1971          37 :     if (fp == nullptr)
    1972             :     {
    1973           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
    1974           0 :         return nullptr;
    1975             :     }
    1976             :     auto poDS = std::unique_ptr<GDALEXRWritableDataset>(
    1977          74 :         new GDALEXRWritableDataset(nXSize, nYSize));
    1978          37 :     poDS->m_pOStream.reset(new GDALEXRIOStream(fp, pszFilename));
    1979          37 :     poDS->eAccess = GA_Update;
    1980          37 :     poDS->m_pixelType = pixelType;
    1981          37 :     poDS->m_header.compression() = compression;
    1982          37 :     poDS->m_header.setType(TILEDIMAGE);
    1983          74 :     poDS->m_header.setTileDescription(
    1984          37 :         TileDescription(nBlockXSize, nBlockYSize));
    1985          37 :     FillHeaderFromOptions(poDS->m_header, papszOptions);
    1986          37 :     poDS->m_nBlockXSize = nBlockXSize;
    1987          37 :     poDS->m_nBlockYSize = nBlockYSize;
    1988          37 :     poDS->m_nXBlocks = static_cast<size_t>(DIV_ROUND_UP(nXSize, nBlockXSize));
    1989          37 :     const size_t nYBlocks =
    1990          37 :         static_cast<size_t>(DIV_ROUND_UP(nYSize, nBlockYSize));
    1991          37 :     if (poDS->m_nXBlocks > std::numeric_limits<size_t>::max() / nYBlocks)
    1992             :     {
    1993           0 :         return nullptr;
    1994             :     }
    1995             :     try
    1996             :     {
    1997          37 :         poDS->m_abWrittenBlocks.resize(poDS->m_nXBlocks * nYBlocks);
    1998             :     }
    1999           0 :     catch (const std::exception &e)
    2000             :     {
    2001           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    2002           0 :         return nullptr;
    2003             :     }
    2004          74 :     poDS->m_bRescaleDiv255 =
    2005          37 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "AUTO_RESCALE", "YES"));
    2006             : 
    2007          37 :     if (nBandsIn > 1)
    2008             :     {
    2009          17 :         poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
    2010             :                                            "IMAGE_STRUCTURE");
    2011             :     }
    2012         110 :     for (int i = 0; i < nBandsIn; i++)
    2013             :     {
    2014         146 :         poDS->SetBand(i + 1,
    2015          73 :                       new GDALEXRWritableRasterBand(poDS.get(), i + 1, eType));
    2016             :     }
    2017          37 :     poDS->SetDescription(pszFilename);
    2018          37 :     poDS->TryLoadXML();
    2019          37 :     return poDS.release();
    2020             : }
    2021             : 
    2022             : /************************************************************************/
    2023             : /*                          GDALRegister_EXR()                          */
    2024             : /************************************************************************/
    2025             : 
    2026           9 : void GDALRegister_EXR()
    2027             : 
    2028             : {
    2029           9 :     if (!GDAL_CHECK_VERSION("EXR driver"))
    2030           0 :         return;
    2031             : 
    2032           9 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    2033           0 :         return;
    2034             : 
    2035           9 :     GDALDriver *poDriver = new GDALDriver();
    2036           9 :     EXRDriverSetCommonMetadata(poDriver);
    2037             : 
    2038           9 :     poDriver->pfnOpen = GDALEXRDataset::Open;
    2039           9 :     poDriver->pfnCreateCopy = GDALEXRDataset::CreateCopy;
    2040           9 :     poDriver->pfnCreate = GDALEXRDataset::Create;
    2041             : 
    2042           9 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2043             : }

Generated by: LCOV version 1.14