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