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 "cpl_multiproc.h"
13 : #include "gdal_pam.h"
14 : #include "gdal_frmts.h"
15 : #include "ogr_spatialref.h"
16 :
17 : #include <algorithm>
18 : #include <limits>
19 : #include <mutex>
20 :
21 : #include "openexr_headers.h"
22 : #include "exrdrivercore.h"
23 :
24 : using namespace OPENEXR_IMF_NAMESPACE;
25 : using namespace IMATH_NAMESPACE;
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() override;
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 : CSLConstList papszOptions);
81 : static GDALDataset *CreateCopy(const char *pszFilename,
82 : GDALDataset *poSrcDS, int bStrict,
83 : CSLConstList 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_UInt8;
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_UInt8, 4, pImage, GDT_UInt8, 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() override
446 168 : {
447 168 : VSIFCloseL(m_fp);
448 295 : }
449 :
450 : bool read(char c[/*n*/], int n) override;
451 : void write(const char c[/*n*/], int n) override;
452 : IoInt64Type tellg() override;
453 :
454 310 : IoInt64Type tellp() override
455 : {
456 310 : return tellg();
457 : }
458 :
459 : void seekg(IoInt64Type pos) override;
460 :
461 136 : 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(GDALMD_INTERLEAVE, "PIXEL",
671 0 : GDAL_MDD_IMAGE_STRUCTURE);
672 0 : poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
673 0 : GDAL_MDD_IMAGE_STRUCTURE);
674 : }
675 89 : else if (BGR || ABGR)
676 : {
677 5 : const int nBands = i;
678 5 : i = 0;
679 20 : for (auto iter = channels.begin(); iter != channels.end();
680 15 : ++iter, ++i)
681 : {
682 : auto poBand = new GDALEXRRasterBand(
683 15 : poDS.get(), nBands - i, iter.name(), samePixelType,
684 15 : nBlockXSize, nBlockYSize);
685 15 : poBand->m_eInterp = static_cast<GDALColorInterp>(
686 15 : GCI_RedBand + nBands - 1 - i);
687 15 : poDS->SetBand(nBands - i, poBand);
688 5 : }
689 : }
690 : else
691 : {
692 84 : i = 0;
693 214 : for (auto iter = channels.begin(); iter != channels.end();
694 130 : ++iter, ++i)
695 : {
696 130 : const Channel &channel = iter.channel();
697 : auto poBand = new GDALEXRRasterBand(
698 130 : poDS.get(), i + 1, iter.name(), channel.type,
699 130 : nBlockXSize, nBlockYSize);
700 260 : const std::string name(iter.name());
701 130 : if (name != CPLSPrintf("Band%d", i + 1))
702 0 : poBand->SetDescription(name.c_str());
703 130 : if (name == "B")
704 0 : poBand->m_eInterp = GCI_BlueBand;
705 130 : else if (name == "G")
706 0 : poBand->m_eInterp = GCI_GreenBand;
707 130 : else if (name == "R")
708 0 : poBand->m_eInterp = GCI_RedBand;
709 130 : else if (name == "A")
710 0 : poBand->m_eInterp = GCI_AlphaBand;
711 130 : else if (name == "Y")
712 0 : poBand->m_eInterp = GCI_GrayIndex;
713 130 : poDS->SetBand(i + 1, poBand);
714 : }
715 : }
716 :
717 178 : if (poDS->m_pTiledIP && !BYRYY &&
718 : // Not completely clear on tiling & overviews would work
719 : // on dataWindow.min != 0, so exclude that for now
720 178 : dataWindow.min.x == 0 && dataWindow.min.y == 0)
721 : {
722 89 : int nLevels = std::min(poDS->m_pTiledIP->numXLevels(),
723 178 : poDS->m_pTiledIP->numYLevels());
724 90 : for (int iLevel = 1; iLevel < nLevels; iLevel++)
725 : {
726 2 : const int nOvrWidth = poDS->m_pTiledIP->levelWidth(iLevel);
727 : const int nOvrHeight =
728 2 : poDS->m_pTiledIP->levelHeight(iLevel);
729 2 : if (nOvrWidth < 128 && nOvrHeight < 128)
730 : {
731 1 : break;
732 : }
733 1 : auto poOvrDS = std::make_unique<GDALEXRDataset>();
734 1 : poOvrDS->m_iLevel = iLevel;
735 1 : poOvrDS->nRasterXSize = nOvrWidth;
736 1 : poOvrDS->nRasterYSize = nOvrHeight;
737 1 : i = 0;
738 4 : for (auto iter = channels.begin(); iter != channels.end();
739 3 : ++iter, ++i)
740 : {
741 3 : const Channel &channel = iter.channel();
742 : auto poBand = std::make_unique<GDALEXRRasterBand>(
743 3 : poOvrDS.get(), i + 1, iter.name(), channel.type,
744 3 : nBlockXSize, nBlockYSize);
745 3 : poOvrDS->SetBand(i + 1, std::move(poBand));
746 : }
747 1 : poDS->AddOverview(std::move(poOvrDS));
748 : }
749 : }
750 :
751 1169 : for (auto iter = header.begin(); iter != header.end(); ++iter)
752 : {
753 1080 : const Attribute *attr = &iter.attribute();
754 : const StringAttribute *stringAttr =
755 1080 : dynamic_cast<const StringAttribute *>(attr);
756 : const M33dAttribute *m33DAttr =
757 1080 : dynamic_cast<const M33dAttribute *>(attr);
758 1080 : if (stringAttr && strcmp(iter.name(), "gdal:crsWkt") == 0)
759 : {
760 78 : poDS->m_oSRS.SetAxisMappingStrategy(
761 : OAMS_TRADITIONAL_GIS_ORDER);
762 78 : poDS->m_oSRS.importFromWkt(stringAttr->value().c_str());
763 : }
764 1080 : else if (m33DAttr &&
765 78 : strcmp(iter.name(), "gdal:geoTransform") == 0)
766 : {
767 78 : poDS->m_bHasGT = true;
768 78 : poDS->m_gt.xorig = m33DAttr->value()[0][2];
769 78 : poDS->m_gt.xscale = m33DAttr->value()[0][0];
770 78 : poDS->m_gt.xrot = m33DAttr->value()[0][1];
771 78 : poDS->m_gt.yorig = m33DAttr->value()[1][2];
772 78 : poDS->m_gt.yrot = m33DAttr->value()[1][0];
773 78 : poDS->m_gt.yscale = m33DAttr->value()[1][1];
774 : }
775 924 : else if (stringAttr && STARTS_WITH(iter.name(), "gdal:"))
776 : {
777 64 : poDS->SetMetadataItem(iter.name() + strlen("gdal:"),
778 32 : stringAttr->value().c_str());
779 : }
780 892 : else if (stringAttr && strcmp(iter.name(), "type") != 0)
781 : {
782 0 : poDS->SetMetadataItem(iter.name(),
783 0 : stringAttr->value().c_str());
784 : }
785 : }
786 :
787 89 : const auto &compression = header.compression();
788 89 : if (compression == NO_COMPRESSION)
789 : {
790 : // nothing
791 : }
792 89 : else if (compression < CPL_ARRAYSIZE(apszCompressions))
793 : {
794 89 : poDS->SetMetadataItem(GDALMD_COMPRESSION,
795 89 : apszCompressions[compression],
796 89 : GDAL_MDD_IMAGE_STRUCTURE);
797 : }
798 : else
799 : {
800 0 : CPLDebug("EXR", "Unknown compression method: %d", compression);
801 : }
802 :
803 89 : if (header.hasPreviewImage())
804 : {
805 2 : CPLStringList aosSubDS;
806 : aosSubDS.SetNameValue("SUBDATASET_1_NAME",
807 : CPLSPrintf("EXR:PREVIEW:%d:%s", iPart + 1,
808 1 : osFilename.c_str()));
809 1 : aosSubDS.SetNameValue("SUBDATASET_1_DESC", "Preview image");
810 1 : poDS->SetMetadata(aosSubDS.List(), GDAL_MDD_SUBDATASETS);
811 : }
812 : }
813 : else
814 : {
815 0 : CPLStringList aosSubDS;
816 0 : for (int i = 0; i < poDS->m_pMPIF->parts(); i++)
817 : {
818 0 : const auto &header = poDS->m_pMPIF->header(i);
819 : aosSubDS.SetNameValue(
820 : CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
821 0 : CPLSPrintf("EXR:%d:%s", i + 1, poOpenInfo->pszFilename));
822 : aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
823 0 : header.name().c_str());
824 : }
825 0 : poDS->SetMetadata(aosSubDS.List(), GDAL_MDD_SUBDATASETS);
826 : }
827 :
828 89 : poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
829 :
830 : // Initialize any PAM information.
831 89 : poDS->SetDescription(poOpenInfo->pszFilename);
832 89 : poDS->TryLoadXML();
833 :
834 89 : return poDS.release();
835 : }
836 0 : catch (const std::exception &e)
837 : {
838 0 : CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
839 0 : return nullptr;
840 : }
841 : }
842 :
843 : /************************************************************************/
844 : /* getPixelType() */
845 : /************************************************************************/
846 :
847 81 : static PixelType getPixelType(GDALDataType eSrcDT, CSLConstList papszOptions)
848 : {
849 81 : PixelType pixelType =
850 125 : (eSrcDT == GDT_UInt8) ? HALF
851 41 : : (eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16 || eSrcDT == GDT_UInt32)
852 85 : ? UINT
853 : : FLOAT;
854 : const char *pszPixelType =
855 81 : CSLFetchNameValueDef(papszOptions, "PIXEL_TYPE", "");
856 81 : if (EQUAL(pszPixelType, "HALF"))
857 1 : pixelType = HALF;
858 80 : else if (EQUAL(pszPixelType, "FLOAT"))
859 1 : pixelType = FLOAT;
860 79 : else if (EQUAL(pszPixelType, "UINT"))
861 1 : pixelType = UINT;
862 81 : return pixelType;
863 : }
864 :
865 72 : static void WriteSRSInHeader(Header &header, const OGRSpatialReference *poSRS)
866 : {
867 72 : char *pszWKT = nullptr;
868 72 : const char *apszOptions[] = {"FORMAT=WKT2_2018", nullptr};
869 72 : poSRS->exportToWkt(&pszWKT, apszOptions);
870 72 : if (pszWKT)
871 : {
872 72 : header.insert("gdal:crsWkt", StringAttribute(pszWKT));
873 72 : CPLFree(pszWKT);
874 : }
875 72 : }
876 :
877 72 : static void WriteGeoTransformInHeader(Header &header,
878 : const GDALGeoTransform >In)
879 : {
880 72 : M33d gt_33;
881 72 : gt_33[0][0] = gtIn.xscale;
882 72 : gt_33[0][1] = gtIn.xrot;
883 72 : gt_33[0][2] = gtIn.xorig;
884 72 : gt_33[1][0] = gtIn.yrot;
885 72 : gt_33[1][1] = gtIn.yscale;
886 72 : gt_33[1][2] = gtIn.yorig;
887 72 : gt_33[2][0] = 0;
888 72 : gt_33[2][1] = 0;
889 72 : gt_33[2][2] = 1;
890 72 : header.insert("gdal:geoTransform", M33dAttribute(gt_33));
891 72 : }
892 :
893 78 : static void WriteMetadataInHeader(Header &header, CSLConstList papszMD)
894 : {
895 119 : for (CSLConstList papszIter = papszMD; papszIter && *papszIter; ++papszIter)
896 : {
897 41 : char *pszKey = nullptr;
898 41 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
899 41 : if (pszKey && pszValue)
900 : {
901 16 : header.insert((std::string("gdal:") + pszKey).c_str(),
902 32 : StringAttribute(pszValue));
903 : }
904 41 : CPLFree(pszKey);
905 : }
906 78 : }
907 :
908 78 : static void FillHeaderFromDataset(Header &header, GDALDataset *poDS)
909 : {
910 78 : const auto poSRS = poDS->GetSpatialRef();
911 78 : if (poSRS)
912 : {
913 72 : WriteSRSInHeader(header, poSRS);
914 : }
915 :
916 78 : GDALGeoTransform gt;
917 78 : if (poDS->GetGeoTransform(gt) == CE_None)
918 : {
919 72 : WriteGeoTransformInHeader(header, gt);
920 : }
921 :
922 78 : WriteMetadataInHeader(header, poDS->GetMetadata());
923 78 : }
924 :
925 78 : static void FillHeaderFromOptions(Header &header, CSLConstList papszOptions)
926 : {
927 : const char *pszDWACompressLevel =
928 78 : CSLFetchNameValue(papszOptions, "DWA_COMPRESSION_LEVEL");
929 78 : if (pszDWACompressLevel)
930 : {
931 1 : header.insert(
932 : "dwaCompressionLevel",
933 2 : FloatAttribute(static_cast<float>(CPLAtof(pszDWACompressLevel))));
934 : }
935 78 : }
936 :
937 : /************************************************************************/
938 : /* CreateCopy() */
939 : /************************************************************************/
940 :
941 45 : GDALDataset *GDALEXRDataset::CreateCopy(const char *pszFilename,
942 : GDALDataset *poSrcDS, int,
943 : CSLConstList papszOptions,
944 : GDALProgressFunc pfnProgress,
945 : void *pProgressData)
946 : {
947 45 : const int nBands = poSrcDS->GetRasterCount();
948 45 : const int nXSize = poSrcDS->GetRasterXSize();
949 45 : const int nYSize = poSrcDS->GetRasterYSize();
950 45 : if (nBands == 0)
951 1 : return nullptr;
952 :
953 44 : bool bRGB_or_RGBA = false;
954 44 : if ((nBands == 3 || nBands == 4))
955 : {
956 7 : bRGB_or_RGBA = true;
957 29 : for (int iBand = 0; iBand < nBands; iBand++)
958 : {
959 22 : bRGB_or_RGBA &=
960 22 : (poSrcDS->GetRasterBand(iBand + 1)->GetColorInterpretation() ==
961 22 : GCI_RedBand + iBand);
962 : }
963 : }
964 :
965 : const bool bPreview =
966 45 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "PREVIEW", "NO")) &&
967 1 : (nXSize > 100 || nYSize > 100);
968 44 : const GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
969 44 : if (bPreview && !(bRGB_or_RGBA && eSrcDT == GDT_UInt8))
970 : {
971 0 : CPLError(
972 : CE_Failure, CPLE_NotSupported,
973 : "Preview creation only supported on RGB/RGBA images of type Byte");
974 0 : return nullptr;
975 : }
976 44 : const PixelType pixelType = getPixelType(eSrcDT, papszOptions);
977 : const bool bRescaleDiv255 =
978 49 : pixelType == HALF && bRGB_or_RGBA && eSrcDT == GDT_UInt8 &&
979 5 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "AUTO_RESCALE", "YES"));
980 :
981 44 : setNumThreads();
982 :
983 88 : CPLString osTmpOvrFile;
984 : try
985 : {
986 44 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
987 44 : if (fp == nullptr)
988 : {
989 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
990 3 : return nullptr;
991 : }
992 51 : GDALEXRIOStream ostream(fp, pszFilename);
993 :
994 51 : std::vector<std::string> channelNames;
995 41 : if (bRGB_or_RGBA)
996 : {
997 5 : channelNames.push_back("R");
998 5 : channelNames.push_back("G");
999 5 : channelNames.push_back("B");
1000 5 : if (nBands == 4)
1001 : {
1002 0 : channelNames.push_back("A");
1003 : }
1004 : }
1005 : else
1006 : {
1007 82 : for (int iBand = 0; iBand < nBands; iBand++)
1008 : {
1009 46 : channelNames.push_back(CPLSPrintf("Band%d", iBand + 1));
1010 : }
1011 : }
1012 :
1013 51 : Header header(nXSize, nYSize);
1014 :
1015 41 : if (bPreview)
1016 : {
1017 1 : const int previewWidth = 100;
1018 : const int previewHeight = std::max(
1019 2 : 1, static_cast<int>(static_cast<GIntBig>(previewWidth) *
1020 1 : nYSize / nXSize));
1021 2 : std::vector<PreviewRgba> pixels(previewWidth * previewHeight);
1022 1 : if (poSrcDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, &pixels[0],
1023 : previewWidth, previewHeight, GDT_UInt8,
1024 : nBands, nullptr, 4, 4 * previewWidth, 1,
1025 1 : nullptr) == CE_None)
1026 : {
1027 1 : header.setPreviewImage(
1028 2 : PreviewImage(previewWidth, previewHeight, &pixels[0]));
1029 : }
1030 : }
1031 :
1032 41 : FillHeaderFromDataset(header, poSrcDS);
1033 :
1034 : const char *pszCompress =
1035 41 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "");
1036 41 : if (pszCompress[0] != '\0')
1037 : {
1038 2 : bool bFound = false;
1039 12 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszCompressions); i++)
1040 : {
1041 12 : if (EQUAL(pszCompress, apszCompressions[i]))
1042 : {
1043 2 : bFound = true;
1044 2 : header.compression() = static_cast<Compression>(i);
1045 2 : break;
1046 : }
1047 : }
1048 2 : if (!bFound)
1049 : {
1050 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown compression %s",
1051 : pszCompress);
1052 0 : return nullptr;
1053 : }
1054 : }
1055 :
1056 41 : FillHeaderFromOptions(header, papszOptions);
1057 :
1058 51 : std::vector<half> bufferHalf;
1059 51 : std::vector<float> bufferFloat;
1060 51 : std::vector<GUInt32> bufferUInt;
1061 41 : const size_t pixelTypeSize = (pixelType == HALF) ? 2 : 4;
1062 41 : const GDALDataType eDT = (pixelType == UINT) ? GDT_UInt32 : GDT_Float32;
1063 41 : const GSpacing nDTSize = GDALGetDataTypeSizeBytes(eDT);
1064 :
1065 : const bool bTiled =
1066 41 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "YES"));
1067 :
1068 : int nChunkXSize;
1069 : int nChunkYSize;
1070 : const int nBlockXSize =
1071 41 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256"));
1072 : const int nBlockYSize =
1073 41 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256"));
1074 41 : if (nBlockXSize <= 8 || nBlockYSize <= 8 || nBlockXSize >= 8192 ||
1075 : nBlockYSize >= 8192)
1076 : {
1077 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
1078 0 : return nullptr;
1079 : }
1080 41 : constexpr int MAX_BUFFER_SIZE = 10 * 1024 * 1024;
1081 :
1082 : const bool bBuildOvr =
1083 41 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "OVERVIEWS", "NO"));
1084 41 : if (bBuildOvr && !bTiled)
1085 : {
1086 0 : CPLError(CE_Failure, CPLE_NotSupported,
1087 : "Overviews only supported on tiled images");
1088 0 : return nullptr;
1089 : }
1090 :
1091 41 : if (bTiled)
1092 : {
1093 41 : header.setType(TILEDIMAGE);
1094 41 : header.setTileDescription(TileDescription(
1095 : nBlockXSize, nBlockYSize, bBuildOvr ? MIPMAP_LEVELS : ONE_LEVEL,
1096 : ROUND_UP));
1097 41 : nChunkYSize = nBlockYSize;
1098 41 : nChunkXSize =
1099 : std::min(std::max(nBlockXSize,
1100 41 : static_cast<int>(
1101 41 : MAX_BUFFER_SIZE /
1102 41 : (pixelTypeSize * nBands * nBlockYSize) /
1103 41 : nBlockXSize * nBlockXSize)),
1104 41 : nXSize);
1105 : }
1106 : else
1107 : {
1108 0 : header.setType(SCANLINEIMAGE);
1109 0 : nChunkXSize = nXSize;
1110 0 : nChunkYSize = std::min(
1111 0 : std::max(1,
1112 0 : static_cast<int>(MAX_BUFFER_SIZE /
1113 0 : (pixelTypeSize * nBands * nXSize))),
1114 0 : nYSize);
1115 : }
1116 : char *sliceBuffer;
1117 41 : if (pixelType == UINT)
1118 : {
1119 6 : bufferUInt.resize(static_cast<size_t>(nBands) * nChunkXSize *
1120 6 : nChunkYSize);
1121 6 : sliceBuffer = reinterpret_cast<char *>(bufferUInt.data());
1122 : }
1123 : else
1124 : {
1125 35 : bufferFloat.resize(static_cast<size_t>(nBands) * nChunkXSize *
1126 35 : nChunkYSize);
1127 35 : if (pixelType == HALF)
1128 : {
1129 25 : bufferHalf.resize(static_cast<size_t>(nBands) * nChunkXSize *
1130 25 : nChunkYSize);
1131 25 : sliceBuffer = reinterpret_cast<char *>(bufferHalf.data());
1132 : }
1133 : else
1134 : {
1135 10 : sliceBuffer = reinterpret_cast<char *>(bufferFloat.data());
1136 : }
1137 : }
1138 :
1139 102 : for (const auto &channelName : channelNames)
1140 : {
1141 61 : header.channels().insert(channelName, Channel(pixelType));
1142 : }
1143 :
1144 41 : MultiPartOutputFile mpof(ostream, &header, 1);
1145 31 : if (bTiled)
1146 : {
1147 31 : TiledOutputPart op(mpof, 0);
1148 :
1149 31 : if (bBuildOvr)
1150 : {
1151 1 : if (nBlockXSize != nBlockYSize)
1152 : {
1153 0 : CPLError(CE_Failure, CPLE_NotSupported,
1154 : "Overview building only works if "
1155 : "BLOCKXSIZE=BLOCKYSIZE");
1156 0 : return nullptr;
1157 : }
1158 2 : if (nBlockXSize < 64 || nBlockXSize > 4096 ||
1159 1 : !CPLIsPowerOfTwo(nBlockXSize))
1160 : {
1161 0 : CPLError(CE_Failure, CPLE_NotSupported,
1162 : "Overview building only works if "
1163 : "BLOCKXSIZE=BLOCKYSIZE is a power of 2 "
1164 : "between 64 and 4096.");
1165 0 : return nullptr;
1166 : }
1167 : }
1168 :
1169 : const auto writeTiles =
1170 40 : [nChunkXSize, nChunkYSize, nBlockXSize, nBlockYSize, nBands,
1171 : pixelType, pixelTypeSize, sliceBuffer, eDT, nDTSize,
1172 : bRescaleDiv255, &channelNames, &op, &bufferFloat, &bufferHalf,
1173 : &bufferUInt](GDALDataset *l_poDS, int iLevel,
1174 : GDALProgressFunc l_pfnProgress,
1175 8644620 : void *l_pProgressData)
1176 : {
1177 40 : const int l_nXSize = l_poDS->GetRasterXSize();
1178 40 : const int l_nYSize = l_poDS->GetRasterYSize();
1179 40 : const int nXBlocks = DIV_ROUND_UP(l_nXSize, nBlockXSize);
1180 40 : const int nYBlocks = DIV_ROUND_UP(l_nYSize, nBlockYSize);
1181 87 : for (int y = 0; y < l_nYSize; y += nChunkYSize)
1182 : {
1183 : const int nLinesToRead =
1184 47 : std::min(nChunkYSize, l_nYSize - y);
1185 94 : for (int x = 0; x < l_nXSize; x += nChunkXSize)
1186 : {
1187 : const int nColsToRead =
1188 47 : std::min(nChunkXSize, l_nXSize - x);
1189 47 : FrameBuffer fb;
1190 144 : for (int iBand = 0; iBand < nBands; iBand++)
1191 : {
1192 : const auto slice = Slice(
1193 : pixelType,
1194 : sliceBuffer +
1195 97 : iBand * pixelTypeSize * nChunkXSize *
1196 97 : nChunkYSize -
1197 97 : (x * pixelTypeSize +
1198 97 : y * pixelTypeSize * nChunkXSize),
1199 97 : pixelTypeSize, pixelTypeSize * nChunkXSize);
1200 97 : fb.insert(channelNames[iBand], slice);
1201 : }
1202 47 : if (l_poDS->RasterIO(
1203 : GF_Read, x, y, nColsToRead, nLinesToRead,
1204 47 : !bufferFloat.empty()
1205 41 : ? reinterpret_cast<GByte *>(&bufferFloat[0])
1206 6 : : reinterpret_cast<GByte *>(&bufferUInt[0]),
1207 : nColsToRead, nLinesToRead, eDT, nBands, nullptr,
1208 47 : nDTSize, nDTSize * nChunkXSize,
1209 47 : nDTSize * nChunkXSize * nChunkYSize,
1210 47 : nullptr) != CE_None)
1211 : {
1212 0 : return false;
1213 : }
1214 47 : if (pixelType == HALF)
1215 : {
1216 31 : const size_t nPixelsInBuffer =
1217 31 : static_cast<size_t>(nChunkXSize) * nChunkYSize *
1218 31 : nBands;
1219 31 : if (bRescaleDiv255)
1220 : {
1221 3955220 : for (size_t i = 0; i < nPixelsInBuffer; i++)
1222 : {
1223 3955200 : bufferHalf[i] = bufferFloat[i] / 255.0f;
1224 : }
1225 : }
1226 : else
1227 : {
1228 366692 : for (size_t i = 0; i < nPixelsInBuffer; i++)
1229 : {
1230 366680 : bufferHalf[i] = bufferFloat[i];
1231 : }
1232 : }
1233 : }
1234 47 : op.setFrameBuffer(fb);
1235 47 : const int blockXMax =
1236 47 : (x + nColsToRead - 1) / nBlockXSize;
1237 47 : const int blockYMax =
1238 47 : (y + nLinesToRead - 1) / nBlockYSize;
1239 47 : op.writeTiles(x / nBlockXSize, blockXMax,
1240 : y / nBlockYSize, blockYMax, iLevel);
1241 94 : if (l_pfnProgress &&
1242 47 : !l_pfnProgress(
1243 47 : (static_cast<double>(blockYMax) * nXBlocks +
1244 47 : blockXMax + 1) /
1245 : nXBlocks / nYBlocks,
1246 : "", l_pProgressData))
1247 : {
1248 0 : return false;
1249 : }
1250 : }
1251 : }
1252 40 : return true;
1253 31 : };
1254 :
1255 : struct ScaledProgressReleaser
1256 : {
1257 0 : void operator()(void *progress) const
1258 : {
1259 0 : GDALDestroyScaledProgress(progress);
1260 0 : }
1261 : };
1262 :
1263 : using ScaledProgressUniquePtr =
1264 : std::unique_ptr<void, ScaledProgressReleaser>;
1265 0 : ScaledProgressUniquePtr progress;
1266 :
1267 : // Write full resolution imagery
1268 31 : if (bBuildOvr)
1269 1 : progress.reset(GDALCreateScaledProgress(0, 0.5, pfnProgress,
1270 : pProgressData));
1271 : else
1272 30 : progress.reset(
1273 : GDALCreateScaledProgress(0, 1, pfnProgress, pProgressData));
1274 31 : if (!writeTiles(poSrcDS, 0, GDALScaledProgress, progress.get()))
1275 : {
1276 0 : if (!osTmpOvrFile.empty())
1277 0 : VSIUnlink(osTmpOvrFile);
1278 0 : return nullptr;
1279 : }
1280 :
1281 31 : if (bBuildOvr)
1282 : {
1283 : // First build overviews in a temporary GTiff file
1284 1 : GDALDefaultOverviews oOvr;
1285 1 : oOvr.Initialize(poSrcDS);
1286 1 : std::vector<int> anOvrFactors;
1287 10 : for (int i = 1; i < op.numLevels(); i++)
1288 9 : anOvrFactors.push_back(1 << i);
1289 1 : std::vector<int> anBands;
1290 4 : for (int iBand = 0; iBand < nBands; iBand++)
1291 3 : anBands.push_back(iBand + 1);
1292 : CPLConfigOptionSetter oSetter("GDAL_TIFF_OVR_BLOCKSIZE",
1293 : CPLSPrintf("%d", nBlockXSize),
1294 1 : false);
1295 : const CPLString osTmpOvrFileRadix(
1296 1 : CPLSPrintf("%s_tmp", pszFilename));
1297 1 : osTmpOvrFile = osTmpOvrFileRadix + ".ovr";
1298 1 : progress.reset(GDALCreateScaledProgress(0.5, 0.8, pfnProgress,
1299 : pProgressData));
1300 2 : if (oOvr.BuildOverviews(
1301 : osTmpOvrFileRadix,
1302 : CSLFetchNameValueDef(papszOptions,
1303 : "OVERVIEW_RESAMPLING", "CUBIC"),
1304 1 : static_cast<int>(anOvrFactors.size()), &anOvrFactors[0],
1305 1 : nBands, &anBands[0], GDALScaledProgress, progress.get(),
1306 1 : nullptr) != CE_None)
1307 : {
1308 0 : VSIUnlink(osTmpOvrFile);
1309 0 : return nullptr;
1310 : }
1311 :
1312 : // Transfer overviews from temporary file to main image
1313 : std::unique_ptr<GDALDataset> poOvrDS(
1314 1 : GDALDataset::Open(osTmpOvrFile));
1315 1 : if (!poOvrDS)
1316 0 : return nullptr;
1317 : const int nOvrs =
1318 1 : 1 + poOvrDS->GetRasterBand(1)->GetOverviewCount();
1319 10 : for (int i = 0; i < nOvrs; i++)
1320 : {
1321 9 : auto poThisOvrDS = (i == 0) ? poOvrDS.get()
1322 8 : : poOvrDS->GetRasterBand(1)
1323 8 : ->GetOverview(i - 1)
1324 8 : ->GetDataset();
1325 9 : CPLAssert(poThisOvrDS);
1326 9 : if (i == 0)
1327 1 : progress.reset(GDALCreateScaledProgress(
1328 : 0.8, nOvrs == 1 ? 1.0 : 0.9, pfnProgress,
1329 : pProgressData));
1330 8 : else if (i == 1)
1331 1 : progress.reset(GDALCreateScaledProgress(
1332 : 0.9, nOvrs == 2 ? 1.0 : 0.95, pfnProgress,
1333 : pProgressData));
1334 : else
1335 7 : progress.reset(GDALCreateScaledProgress(
1336 7 : 0.95 + 0.05 * (i - 2) / (nOvrs - 2),
1337 7 : 0.95 + 0.05 * (i - 2 + 1) / (nOvrs - 2),
1338 : pfnProgress, pProgressData));
1339 9 : if (!writeTiles(poThisOvrDS, i + 1, GDALScaledProgress,
1340 : progress.get()))
1341 : {
1342 0 : poOvrDS.reset();
1343 0 : VSIUnlink(osTmpOvrFile);
1344 0 : return nullptr;
1345 : }
1346 : }
1347 :
1348 1 : poOvrDS.reset();
1349 1 : VSIUnlink(osTmpOvrFile);
1350 : }
1351 : }
1352 : else
1353 : {
1354 0 : OutputPart op(mpof, 0);
1355 :
1356 0 : for (int y = 0; y < nYSize; y += nChunkYSize)
1357 : {
1358 0 : FrameBuffer fb;
1359 0 : const int nLinesToRead = std::min(nChunkYSize, nYSize - y);
1360 0 : for (int iBand = 0; iBand < nBands; iBand++)
1361 : {
1362 : const auto slice = Slice(
1363 : pixelType,
1364 : sliceBuffer +
1365 0 : iBand * pixelTypeSize * nXSize * nLinesToRead -
1366 0 : y * pixelTypeSize * nXSize,
1367 0 : pixelTypeSize, pixelTypeSize * nXSize);
1368 0 : fb.insert(channelNames[iBand], slice);
1369 : }
1370 0 : if (poSrcDS->RasterIO(
1371 : GF_Read, 0, y, nXSize, nLinesToRead,
1372 0 : !bufferFloat.empty()
1373 0 : ? reinterpret_cast<GByte *>(&bufferFloat[0])
1374 0 : : reinterpret_cast<GByte *>(&bufferUInt[0]),
1375 : nXSize, nLinesToRead, eDT, nBands, nullptr, nDTSize,
1376 0 : nDTSize * nXSize, nDTSize * nXSize * nLinesToRead,
1377 0 : nullptr) != CE_None)
1378 : {
1379 0 : return nullptr;
1380 : }
1381 0 : if (pixelType == HALF)
1382 : {
1383 0 : for (size_t i = 0; i < static_cast<size_t>(nXSize) *
1384 0 : nLinesToRead * nBands;
1385 : i++)
1386 : {
1387 : // cppcheck-suppress unreadVariable
1388 0 : bufferHalf[i] = bufferFloat[i];
1389 : }
1390 : }
1391 0 : op.setFrameBuffer(fb);
1392 0 : op.writePixels(nLinesToRead);
1393 0 : if (pfnProgress &&
1394 0 : !pfnProgress(static_cast<double>(y + nLinesToRead) / nYSize,
1395 : "", pProgressData))
1396 : {
1397 0 : return nullptr;
1398 : }
1399 : }
1400 : }
1401 : }
1402 10 : catch (const std::exception &e)
1403 : {
1404 10 : if (!osTmpOvrFile.empty())
1405 0 : VSIUnlink(osTmpOvrFile);
1406 10 : CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
1407 10 : return nullptr;
1408 : }
1409 62 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1410 31 : return GDALEXRDataset::Open(&oOpenInfo);
1411 : }
1412 :
1413 : /************************************************************************/
1414 : /* GDALEXRWritableDataset */
1415 : /************************************************************************/
1416 :
1417 : class GDALEXRWritableDataset final : public GDALPamDataset
1418 : {
1419 : friend class GDALEXRDataset;
1420 : friend class GDALEXRWritableRasterBand;
1421 :
1422 : PixelType m_pixelType = HALF;
1423 : int m_nBlockXSize = 0;
1424 : int m_nBlockYSize = 0;
1425 :
1426 : // Keep stream before others, so that it is destroyed last
1427 : std::unique_ptr<OStream> m_pOStream{};
1428 :
1429 : std::unique_ptr<TiledOutputPart> m_pTOP{};
1430 : std::unique_ptr<MultiPartOutputFile> m_pMPOF{};
1431 :
1432 : std::vector<std::string> m_channelNames{};
1433 :
1434 : bool m_bTriedWritingHeader = false;
1435 : std::vector<half> m_bufferHalf{};
1436 : std::vector<float> m_bufferFloat{};
1437 : std::vector<GUInt32> m_bufferUInt{};
1438 : size_t m_nBufferEltSize = 0;
1439 : char *m_pSliceBuffer = nullptr;
1440 :
1441 : OGRSpatialReference m_oSRS{};
1442 : GDALGeoTransform m_gt{};
1443 : bool m_bHasGT = false;
1444 :
1445 : CPLStringList m_aosMetadata{};
1446 :
1447 : std::vector<bool> m_abWrittenBlocks{};
1448 : size_t m_nXBlocks = 0;
1449 :
1450 : bool m_bRescaleDiv255 = false;
1451 :
1452 : Header m_header;
1453 :
1454 : void WriteHeader();
1455 :
1456 : CPL_DISALLOW_COPY_ASSIGN(GDALEXRWritableDataset)
1457 :
1458 : public:
1459 37 : GDALEXRWritableDataset(int nXSize, int nYSize) : m_header(nXSize, nYSize)
1460 : {
1461 37 : nRasterXSize = nXSize;
1462 37 : nRasterYSize = nYSize;
1463 37 : }
1464 :
1465 : ~GDALEXRWritableDataset() override;
1466 :
1467 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
1468 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1469 :
1470 : const OGRSpatialReference *GetSpatialRef() const override;
1471 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
1472 :
1473 : CPLErr SetMetadata(CSLConstList, const char * = "") override;
1474 : CPLErr SetMetadataItem(const char *, const char *,
1475 : const char * = "") override;
1476 :
1477 : CSLConstList GetMetadata(const char *pszDomain = "") override;
1478 : const char *GetMetadataItem(const char *pszName,
1479 : const char *pszDomain = "") override;
1480 : };
1481 :
1482 : /************************************************************************/
1483 : /* ~GDALEXRWritableDataset() */
1484 : /************************************************************************/
1485 :
1486 74 : GDALEXRWritableDataset::~GDALEXRWritableDataset()
1487 : {
1488 37 : WriteHeader();
1489 37 : FlushCache(true);
1490 74 : }
1491 :
1492 : /************************************************************************/
1493 : /* SetGeoTransform() */
1494 : /************************************************************************/
1495 :
1496 31 : CPLErr GDALEXRWritableDataset::SetGeoTransform(const GDALGeoTransform >)
1497 : {
1498 31 : if (m_bTriedWritingHeader)
1499 : {
1500 0 : CPLError(
1501 : CE_Warning, CPLE_AppDefined,
1502 : "SetGeoTransform() called after writing pixels. Will go to PAM");
1503 0 : return GDALPamDataset::SetGeoTransform(gt);
1504 : }
1505 31 : m_bHasGT = true;
1506 31 : m_gt = gt;
1507 31 : return CE_None;
1508 : }
1509 :
1510 : /************************************************************************/
1511 : /* SetSpatialRef() */
1512 : /************************************************************************/
1513 :
1514 31 : CPLErr GDALEXRWritableDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1515 : {
1516 31 : if (m_bTriedWritingHeader)
1517 : {
1518 0 : CPLError(CE_Warning, CPLE_AppDefined,
1519 : "SetSpatialRef() called after writing pixels. Will go to PAM");
1520 0 : return GDALPamDataset::SetSpatialRef(poSRS);
1521 : }
1522 31 : if (poSRS)
1523 31 : m_oSRS = *poSRS;
1524 : else
1525 0 : m_oSRS.Clear();
1526 31 : return CE_None;
1527 : }
1528 :
1529 : /************************************************************************/
1530 : /* SetMetadata() */
1531 : /************************************************************************/
1532 :
1533 0 : CPLErr GDALEXRWritableDataset::SetMetadata(CSLConstList papszMD,
1534 : const char *pszDomain)
1535 : {
1536 0 : if (pszDomain == nullptr || pszDomain[0] == 0)
1537 : {
1538 0 : m_aosMetadata = CSLDuplicate(papszMD);
1539 0 : if (m_bTriedWritingHeader)
1540 : {
1541 0 : CPLError(
1542 : CE_Warning, CPLE_AppDefined,
1543 : "SetMetadata() called after writing pixels. Will go to PAM");
1544 : }
1545 : else
1546 : {
1547 0 : return CE_None;
1548 : }
1549 : }
1550 0 : return GDALPamDataset::SetMetadata(papszMD, pszDomain);
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* SetMetadataItem() */
1555 : /************************************************************************/
1556 :
1557 0 : CPLErr GDALEXRWritableDataset::SetMetadataItem(const char *pszName,
1558 : const char *pszValue,
1559 : const char *pszDomain)
1560 : {
1561 0 : if (pszDomain == nullptr || pszDomain[0] == 0)
1562 : {
1563 0 : m_aosMetadata.SetNameValue(pszName, pszValue);
1564 0 : if (m_bTriedWritingHeader)
1565 : {
1566 0 : CPLError(
1567 : CE_Warning, CPLE_AppDefined,
1568 : "SetMetadata() called after writing pixels. Will go to PAM");
1569 : }
1570 : else
1571 : {
1572 0 : return CE_None;
1573 : }
1574 : }
1575 0 : return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1576 : }
1577 :
1578 : /************************************************************************/
1579 : /* GetMetadata() */
1580 : /************************************************************************/
1581 :
1582 37 : CSLConstList GDALEXRWritableDataset::GetMetadata(const char *pszDomain)
1583 : {
1584 37 : if (pszDomain == nullptr || pszDomain[0] == 0)
1585 : {
1586 37 : return m_aosMetadata.List();
1587 : }
1588 0 : return GDALPamDataset::GetMetadata(pszDomain);
1589 : }
1590 :
1591 : /************************************************************************/
1592 : /* GetMetadataItem() */
1593 : /************************************************************************/
1594 :
1595 5 : const char *GDALEXRWritableDataset::GetMetadataItem(const char *pszName,
1596 : const char *pszDomain)
1597 : {
1598 5 : if (pszDomain == nullptr || pszDomain[0] == 0)
1599 : {
1600 5 : return m_aosMetadata.FetchNameValue(pszName);
1601 : }
1602 0 : return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
1603 : }
1604 :
1605 : /************************************************************************/
1606 : /* GetSpatialRef() */
1607 : /************************************************************************/
1608 :
1609 37 : const OGRSpatialReference *GDALEXRWritableDataset::GetSpatialRef() const
1610 : {
1611 37 : const auto *poPamSRS = GDALPamDataset::GetSpatialRef();
1612 37 : if (poPamSRS)
1613 0 : return poPamSRS;
1614 37 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1615 : }
1616 :
1617 : /************************************************************************/
1618 : /* GetGeoTransform() */
1619 : /************************************************************************/
1620 :
1621 68 : CPLErr GDALEXRWritableDataset::GetGeoTransform(GDALGeoTransform >) const
1622 : {
1623 68 : if (GDALPamDataset::GetGeoTransform(gt) == CE_None)
1624 : {
1625 0 : return CE_None;
1626 : }
1627 68 : gt = m_gt;
1628 68 : return m_bHasGT ? CE_None : CE_Failure;
1629 : }
1630 :
1631 : /************************************************************************/
1632 : /* WriteHeader() */
1633 : /************************************************************************/
1634 :
1635 43 : void GDALEXRWritableDataset::WriteHeader()
1636 : {
1637 43 : if (m_bTriedWritingHeader)
1638 6 : return;
1639 37 : m_bTriedWritingHeader = true;
1640 :
1641 : try
1642 : {
1643 37 : FillHeaderFromDataset(m_header, this);
1644 :
1645 37 : bool bRGB_or_RGBA = false;
1646 37 : if (nBands == 3 || nBands == 4)
1647 : {
1648 15 : bRGB_or_RGBA = true;
1649 61 : for (int i = 0; i < nBands; i++)
1650 : {
1651 46 : bRGB_or_RGBA &=
1652 46 : GetRasterBand(i + 1)->GetColorInterpretation() ==
1653 46 : GCI_RedBand + i;
1654 : }
1655 : }
1656 37 : m_bRescaleDiv255 &= m_pixelType == HALF && bRGB_or_RGBA &&
1657 0 : GetRasterBand(1)->GetRasterDataType() == GDT_UInt8;
1658 :
1659 37 : if (bRGB_or_RGBA)
1660 : {
1661 0 : m_channelNames.push_back("R");
1662 0 : m_channelNames.push_back("G");
1663 0 : m_channelNames.push_back("B");
1664 0 : if (nBands == 4)
1665 : {
1666 0 : m_channelNames.push_back("A");
1667 : }
1668 : }
1669 : else
1670 : {
1671 110 : for (int iBand = 0; iBand < nBands; iBand++)
1672 : {
1673 73 : m_channelNames.push_back(CPLSPrintf("Band%d", iBand + 1));
1674 : }
1675 : }
1676 :
1677 110 : for (int i = 0; i < nBands; i++)
1678 : {
1679 73 : m_header.channels().insert(m_channelNames[i], Channel(m_pixelType));
1680 : }
1681 :
1682 37 : m_pMPOF.reset(new MultiPartOutputFile(*m_pOStream, &m_header, 1));
1683 37 : m_pTOP.reset(new TiledOutputPart(*m_pMPOF, 0));
1684 :
1685 37 : const size_t nElts =
1686 37 : static_cast<size_t>(nBands) * m_nBlockXSize * m_nBlockYSize;
1687 37 : if (m_pixelType == HALF)
1688 : {
1689 7 : m_bufferHalf.resize(nElts);
1690 7 : m_bufferFloat.resize(nElts / nBands);
1691 7 : m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferHalf[0]);
1692 7 : m_nBufferEltSize = sizeof(half);
1693 : }
1694 30 : else if (m_pixelType == FLOAT)
1695 : {
1696 22 : m_bufferFloat.resize(nElts);
1697 22 : m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferFloat[0]);
1698 22 : m_nBufferEltSize = sizeof(float);
1699 : }
1700 : else
1701 : {
1702 8 : m_bufferUInt.resize(nElts);
1703 8 : m_pSliceBuffer = reinterpret_cast<char *>(&m_bufferUInt[0]);
1704 8 : m_nBufferEltSize = sizeof(unsigned int);
1705 : }
1706 : }
1707 0 : catch (const std::exception &e)
1708 : {
1709 0 : CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
1710 0 : m_pTOP.reset();
1711 0 : m_pMPOF.reset();
1712 : }
1713 : }
1714 :
1715 : /************************************************************************/
1716 : /* GDALEXRWritableRasterBand */
1717 : /************************************************************************/
1718 :
1719 : class GDALEXRWritableRasterBand final : public GDALPamRasterBand
1720 : {
1721 : GDALColorInterp m_eInterp = GCI_Undefined;
1722 :
1723 : protected:
1724 : CPLErr IReadBlock(int, int, void *) override;
1725 : CPLErr IWriteBlock(int, int, void *) override;
1726 :
1727 : public:
1728 : GDALEXRWritableRasterBand(GDALEXRWritableDataset *poDSIn, int nBandIn,
1729 : GDALDataType eTypeIn);
1730 :
1731 0 : CPLErr SetColorInterpretation(GDALColorInterp eInterp) override
1732 : {
1733 0 : m_eInterp = eInterp;
1734 0 : return CE_None;
1735 : }
1736 :
1737 46 : GDALColorInterp GetColorInterpretation() override
1738 : {
1739 46 : return m_eInterp;
1740 : }
1741 : };
1742 :
1743 : /************************************************************************/
1744 : /* GDALEXRWritableRasterBand() */
1745 : /************************************************************************/
1746 :
1747 73 : GDALEXRWritableRasterBand::GDALEXRWritableRasterBand(
1748 73 : GDALEXRWritableDataset *poDSIn, int nBandIn, GDALDataType eTypeIn)
1749 : {
1750 73 : poDS = poDSIn;
1751 73 : nBand = nBandIn;
1752 73 : nRasterXSize = poDSIn->GetRasterXSize();
1753 73 : nRasterYSize = poDSIn->GetRasterYSize();
1754 73 : nBlockXSize = poDSIn->m_nBlockXSize;
1755 73 : nBlockYSize = poDSIn->m_nBlockYSize;
1756 73 : eDataType = eTypeIn;
1757 73 : }
1758 :
1759 : /************************************************************************/
1760 : /* IReadBlock() */
1761 : /************************************************************************/
1762 :
1763 0 : CPLErr GDALEXRWritableRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1764 : void *pImage)
1765 : {
1766 0 : auto poGDS = cpl::down_cast<GDALEXRWritableDataset *>(poDS);
1767 0 : if (!poGDS->m_abWrittenBlocks[nBlockYOff * poGDS->m_nXBlocks + nBlockXOff])
1768 : {
1769 0 : const size_t nPixelsInBlock =
1770 0 : static_cast<size_t>(nBlockXSize) * nBlockYSize;
1771 0 : memset(pImage, 0, nPixelsInBlock * GDALGetDataTypeSizeBytes(eDataType));
1772 0 : return CE_None;
1773 : }
1774 0 : CPLError(CE_Failure, CPLE_AppDefined,
1775 : "Reading blocks in a EXR dataset created by Create() is not "
1776 : "supported");
1777 0 : return CE_Failure;
1778 : }
1779 :
1780 : /************************************************************************/
1781 : /* IWriteBlock() */
1782 : /************************************************************************/
1783 :
1784 6 : CPLErr GDALEXRWritableRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
1785 : void *pImage)
1786 : {
1787 6 : auto poGDS = cpl::down_cast<GDALEXRWritableDataset *>(poDS);
1788 6 : poGDS->WriteHeader();
1789 6 : if (!poGDS->m_pTOP)
1790 0 : return CE_Failure;
1791 :
1792 6 : poGDS->m_abWrittenBlocks[nBlockYOff * poGDS->m_nXBlocks + nBlockXOff] =
1793 6 : true;
1794 :
1795 6 : bool bAllBlocksDirty = true;
1796 6 : std::vector<GDALRasterBlock *> apoBlocks;
1797 6 : apoBlocks.resize(poGDS->nBands);
1798 12 : for (int iBand = 0; iBand < poGDS->nBands; ++iBand)
1799 : {
1800 6 : if (iBand + 1 != nBand)
1801 : {
1802 0 : apoBlocks[iBand] =
1803 0 : cpl::down_cast<GDALEXRWritableRasterBand *>(
1804 : poGDS->GetRasterBand(iBand + 1))
1805 0 : ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
1806 :
1807 0 : if (apoBlocks[iBand] == nullptr)
1808 : {
1809 0 : bAllBlocksDirty = false;
1810 0 : break;
1811 : }
1812 0 : else if (!apoBlocks[iBand]->GetDirty())
1813 : {
1814 0 : apoBlocks[iBand]->DropLock();
1815 0 : apoBlocks[iBand] = nullptr;
1816 0 : bAllBlocksDirty = false;
1817 0 : break;
1818 : }
1819 : }
1820 : else
1821 : {
1822 6 : apoBlocks[iBand] = nullptr;
1823 : }
1824 : }
1825 6 : if (!bAllBlocksDirty)
1826 : {
1827 0 : CPLError(CE_Warning, CPLE_AppDefined,
1828 : "For block (%d, %d), blocks for some bands are not available "
1829 : "in the cache. Corresponding data will be assumed to be zero.",
1830 : nBlockXOff, nBlockYOff);
1831 : }
1832 :
1833 6 : CPLErr eErr = CE_None;
1834 : try
1835 : {
1836 12 : FrameBuffer fb;
1837 6 : const int x = nBlockXOff * nBlockXSize;
1838 6 : const int y = nBlockYOff * nBlockYSize;
1839 6 : const size_t nPixelsInBlock =
1840 6 : static_cast<size_t>(nBlockXSize) * nBlockYSize;
1841 6 : const GDALDataType eDstDT =
1842 6 : poGDS->m_pixelType == UINT ? GDT_UInt32 : GDT_Float32;
1843 12 : for (int iBand = 0; iBand < poGDS->nBands; iBand++)
1844 : {
1845 6 : char *const dstPtr =
1846 6 : poGDS->m_pSliceBuffer +
1847 6 : iBand * poGDS->m_nBufferEltSize * nPixelsInBlock;
1848 : const auto slice = Slice(
1849 : poGDS->m_pixelType,
1850 6 : dstPtr - (x * poGDS->m_nBufferEltSize +
1851 6 : y * poGDS->m_nBufferEltSize * nBlockXSize),
1852 6 : poGDS->m_nBufferEltSize, poGDS->m_nBufferEltSize * nBlockXSize);
1853 6 : fb.insert(poGDS->m_channelNames[iBand], slice);
1854 :
1855 6 : const void *srcPtr = nullptr;
1856 6 : if (iBand + 1 == nBand)
1857 6 : srcPtr = pImage;
1858 0 : else if (apoBlocks[iBand])
1859 0 : srcPtr = apoBlocks[iBand]->GetDataRef();
1860 : else
1861 : {
1862 0 : memset(poGDS->m_pSliceBuffer +
1863 0 : iBand * poGDS->m_nBufferEltSize * nPixelsInBlock,
1864 0 : 0, nPixelsInBlock * poGDS->m_nBufferEltSize);
1865 0 : continue;
1866 : }
1867 :
1868 12 : GDALCopyWords64(srcPtr, eDataType,
1869 : GDALGetDataTypeSizeBytes(eDataType),
1870 6 : poGDS->m_pixelType == HALF
1871 2 : ? static_cast<void *>(&poGDS->m_bufferFloat[0])
1872 : : static_cast<void *>(dstPtr),
1873 : eDstDT, GDALGetDataTypeSizeBytes(eDstDT),
1874 : static_cast<GPtrDiff_t>(nPixelsInBlock));
1875 6 : if (poGDS->m_pixelType == HALF)
1876 : {
1877 2 : if (poGDS->m_bRescaleDiv255)
1878 : {
1879 0 : for (size_t i = 0; i < nPixelsInBlock; i++)
1880 : {
1881 0 : poGDS->m_bufferHalf[iBand * nPixelsInBlock + i] =
1882 0 : poGDS->m_bufferFloat[i] / 255.0f;
1883 : }
1884 : }
1885 : else
1886 : {
1887 131074 : for (size_t i = 0; i < nPixelsInBlock; i++)
1888 : {
1889 131072 : poGDS->m_bufferHalf[iBand * nPixelsInBlock + i] =
1890 131072 : poGDS->m_bufferFloat[i];
1891 : }
1892 : }
1893 : }
1894 : }
1895 :
1896 6 : poGDS->m_pTOP->setFrameBuffer(fb);
1897 6 : poGDS->m_pTOP->writeTile(nBlockXOff, nBlockYOff);
1898 : }
1899 0 : catch (const std::exception &e)
1900 : {
1901 0 : CPLError(CE_Failure, CPLE_AppDefined, "OpenEXR: %s", e.what());
1902 0 : eErr = CE_Failure;
1903 : }
1904 :
1905 12 : for (int iBand = 0; iBand < poGDS->nBands; ++iBand)
1906 : {
1907 6 : if (apoBlocks[iBand])
1908 : {
1909 0 : apoBlocks[iBand]->MarkClean();
1910 0 : apoBlocks[iBand]->DropLock();
1911 : }
1912 : }
1913 :
1914 6 : return eErr;
1915 : }
1916 :
1917 : /************************************************************************/
1918 : /* Create() */
1919 : /************************************************************************/
1920 :
1921 38 : GDALDataset *GDALEXRDataset::Create(const char *pszFilename, int nXSize,
1922 : int nYSize, int nBandsIn,
1923 : GDALDataType eType,
1924 : CSLConstList papszOptions)
1925 : {
1926 38 : if (nBandsIn == 0)
1927 1 : return nullptr;
1928 37 : const PixelType pixelType = getPixelType(eType, papszOptions);
1929 :
1930 37 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "YES")))
1931 : {
1932 0 : CPLError(CE_Failure, CPLE_NotSupported,
1933 : "Create() only supports tiled mode");
1934 0 : return nullptr;
1935 : }
1936 :
1937 37 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "OVERVIEWS", "NO")))
1938 : {
1939 0 : CPLError(CE_Failure, CPLE_NotSupported,
1940 : "Create() does not support overview creation.");
1941 0 : return nullptr;
1942 : }
1943 :
1944 37 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PREVIEW", "NO")))
1945 : {
1946 0 : CPLError(CE_Failure, CPLE_NotSupported,
1947 : "Create() does not support preview creation.");
1948 0 : return nullptr;
1949 : }
1950 :
1951 37 : Compression compression = ZIP_COMPRESSION;
1952 : const char *pszCompress =
1953 37 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "");
1954 37 : if (pszCompress[0] != '\0')
1955 : {
1956 1 : bool bFound = false;
1957 2 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszCompressions); i++)
1958 : {
1959 2 : if (EQUAL(pszCompress, apszCompressions[i]))
1960 : {
1961 1 : bFound = true;
1962 1 : compression = static_cast<Compression>(i);
1963 1 : break;
1964 : }
1965 : }
1966 1 : if (!bFound)
1967 : {
1968 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown compression %s",
1969 : pszCompress);
1970 0 : return nullptr;
1971 : }
1972 : }
1973 :
1974 : const int nBlockXSize =
1975 37 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256"));
1976 : const int nBlockYSize =
1977 37 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256"));
1978 37 : if (nBlockXSize <= 8 || nBlockYSize <= 8 || nBlockXSize >= 8192 ||
1979 : nBlockYSize >= 8192)
1980 : {
1981 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
1982 0 : return nullptr;
1983 : }
1984 :
1985 37 : VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
1986 37 : if (fp == nullptr)
1987 : {
1988 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
1989 0 : return nullptr;
1990 : }
1991 : auto poDS = std::unique_ptr<GDALEXRWritableDataset>(
1992 74 : new GDALEXRWritableDataset(nXSize, nYSize));
1993 37 : poDS->m_pOStream.reset(new GDALEXRIOStream(fp, pszFilename));
1994 37 : poDS->eAccess = GA_Update;
1995 37 : poDS->m_pixelType = pixelType;
1996 37 : poDS->m_header.compression() = compression;
1997 37 : poDS->m_header.setType(TILEDIMAGE);
1998 74 : poDS->m_header.setTileDescription(
1999 37 : TileDescription(nBlockXSize, nBlockYSize));
2000 37 : FillHeaderFromOptions(poDS->m_header, papszOptions);
2001 37 : poDS->m_nBlockXSize = nBlockXSize;
2002 37 : poDS->m_nBlockYSize = nBlockYSize;
2003 37 : poDS->m_nXBlocks = static_cast<size_t>(DIV_ROUND_UP(nXSize, nBlockXSize));
2004 37 : const size_t nYBlocks =
2005 37 : static_cast<size_t>(DIV_ROUND_UP(nYSize, nBlockYSize));
2006 37 : if (poDS->m_nXBlocks > std::numeric_limits<size_t>::max() / nYBlocks)
2007 : {
2008 0 : return nullptr;
2009 : }
2010 : try
2011 : {
2012 37 : poDS->m_abWrittenBlocks.resize(poDS->m_nXBlocks * nYBlocks);
2013 : }
2014 0 : catch (const std::exception &e)
2015 : {
2016 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
2017 0 : return nullptr;
2018 : }
2019 74 : poDS->m_bRescaleDiv255 =
2020 37 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "AUTO_RESCALE", "YES"));
2021 :
2022 37 : if (nBandsIn > 1)
2023 : {
2024 17 : poDS->GDALDataset::SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
2025 : GDAL_MDD_IMAGE_STRUCTURE);
2026 : }
2027 110 : for (int i = 0; i < nBandsIn; i++)
2028 : {
2029 146 : poDS->SetBand(i + 1,
2030 73 : new GDALEXRWritableRasterBand(poDS.get(), i + 1, eType));
2031 : }
2032 37 : poDS->SetDescription(pszFilename);
2033 37 : poDS->TryLoadXML();
2034 37 : return poDS.release();
2035 : }
2036 :
2037 : /************************************************************************/
2038 : /* GDALRegister_EXR() */
2039 : /************************************************************************/
2040 :
2041 10 : void GDALRegister_EXR()
2042 :
2043 : {
2044 10 : if (!GDAL_CHECK_VERSION("EXR driver"))
2045 0 : return;
2046 :
2047 10 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
2048 0 : return;
2049 :
2050 10 : GDALDriver *poDriver = new GDALDriver();
2051 10 : EXRDriverSetCommonMetadata(poDriver);
2052 :
2053 10 : poDriver->pfnOpen = GDALEXRDataset::Open;
2054 10 : poDriver->pfnCreateCopy = GDALEXRDataset::CreateCopy;
2055 10 : poDriver->pfnCreate = GDALEXRDataset::Create;
2056 :
2057 10 : poDriver->SetMetadataItem("OPENEXR_VERSION", OPENEXR_VERSION_STRING, "EXR");
2058 :
2059 10 : GetGDALDriverManager()->RegisterDriver(poDriver);
2060 : }
|