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