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