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