LCOV - code coverage report
Current view: top level - frmts/exr - exrdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 696 956 72.8 %
Date: 2025-07-01 22:47:05 Functions: 46 58 79.3 %

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

Generated by: LCOV version 1.14