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