Blender  V3.3
openexr_api.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright by Gernot Ziegler <gz@lysator.liu.se>. All rights reserved. */
3 
8 #include <algorithm>
9 #include <cctype>
10 #include <cerrno>
11 #include <cstddef>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <fcntl.h>
15 #include <fstream>
16 #include <iostream>
17 #include <set>
18 #include <stdexcept>
19 #include <string>
20 
21 /* The OpenEXR version can reliably be found in this header file from OpenEXR,
22  * for both 2.x and 3.x:
23  */
24 #include <OpenEXR/OpenEXRConfig.h>
25 #define COMBINED_OPENEXR_VERSION \
26  ((10000 * OPENEXR_VERSION_MAJOR) + (100 * OPENEXR_VERSION_MINOR) + OPENEXR_VERSION_PATCH)
27 
28 #if COMBINED_OPENEXR_VERSION >= 20599
29 /* >=2.5.99 -> OpenEXR >=3.0 */
30 # include <Imath/half.h>
31 # include <OpenEXR/ImfFrameBuffer.h>
32 # define exr_file_offset_t uint64_t
33 #else
34 /* OpenEXR 2.x, use the old locations. */
35 # include <OpenEXR/half.h>
36 # define exr_file_offset_t Int64
37 #endif
38 
39 #include <OpenEXR/Iex.h>
40 #include <OpenEXR/ImfArray.h>
41 #include <OpenEXR/ImfChannelList.h>
42 #include <OpenEXR/ImfCompression.h>
43 #include <OpenEXR/ImfCompressionAttribute.h>
44 #include <OpenEXR/ImfIO.h>
45 #include <OpenEXR/ImfInputFile.h>
46 #include <OpenEXR/ImfOutputFile.h>
47 #include <OpenEXR/ImfPixelType.h>
48 #include <OpenEXR/ImfPreviewImage.h>
49 #include <OpenEXR/ImfRgbaFile.h>
50 #include <OpenEXR/ImfStandardAttributes.h>
51 #include <OpenEXR/ImfStringAttribute.h>
52 #include <OpenEXR/ImfVersion.h>
53 
54 /* multiview/multipart */
55 #include <OpenEXR/ImfInputPart.h>
56 #include <OpenEXR/ImfMultiPartInputFile.h>
57 #include <OpenEXR/ImfMultiPartOutputFile.h>
58 #include <OpenEXR/ImfMultiView.h>
59 #include <OpenEXR/ImfOutputPart.h>
60 #include <OpenEXR/ImfPartHelper.h>
61 #include <OpenEXR/ImfPartType.h>
62 #include <OpenEXR/ImfTiledOutputPart.h>
63 
64 #include "DNA_scene_types.h" /* For OpenEXR compression constants */
65 
66 #include <openexr_api.h>
67 
68 #if defined(WIN32)
69 # include "utfconv.h"
70 # include <io.h>
71 #else
72 # include <unistd.h>
73 #endif
74 
75 #include "MEM_guardedalloc.h"
76 
77 extern "C" {
78 
79 /* The following prevents a linking error in debug mode for MSVC using the libs in SVN. */
80 #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(DEBUG) && _MSC_VER < 1900
81 _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
82 {
83 }
84 #endif
85 }
86 #include "BLI_blenlib.h"
87 #include "BLI_fileops.h"
88 #include "BLI_math_color.h"
89 #include "BLI_mmap.h"
90 #include "BLI_string_utils.h"
91 #include "BLI_threads.h"
92 
93 #include "BKE_idprop.h"
94 #include "BKE_image.h"
95 
96 #include "IMB_allocimbuf.h"
97 #include "IMB_colormanagement.h"
99 #include "IMB_imbuf.h"
100 #include "IMB_imbuf_types.h"
101 #include "IMB_metadata.h"
102 #include "IMB_openexr.h"
103 
104 using namespace Imf;
105 using namespace Imath;
106 
107 extern "C" {
108 /* prototype */
109 static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname);
110 static bool exr_has_multiview(MultiPartInputFile &file);
111 static bool exr_has_multipart_file(MultiPartInputFile &file);
112 static bool exr_has_alpha(MultiPartInputFile &file);
113 static bool exr_has_zbuffer(MultiPartInputFile &file);
114 static void exr_printf(const char *__restrict fmt, ...);
115 static void imb_exr_type_by_channels(ChannelList &channels,
116  StringVector &views,
117  bool *r_singlelayer,
118  bool *r_multilayer,
119  bool *r_multiview);
120 }
121 
122 /* Memory Input Stream */
123 
124 class IMemStream : public Imf::IStream {
125  public:
126  IMemStream(unsigned char *exrbuf, size_t exrsize)
127  : IStream("<memory>"), _exrpos(0), _exrsize(exrsize)
128  {
129  _exrbuf = exrbuf;
130  }
131 
132  bool read(char c[], int n) override
133  {
134  if (n + _exrpos <= _exrsize) {
135  memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
136  _exrpos += n;
137  return true;
138  }
139 
140  return false;
141  }
142 
144  {
145  return _exrpos;
146  }
147 
148  void seekg(exr_file_offset_t pos) override
149  {
150  _exrpos = pos;
151  }
152 
153  void clear() override
154  {
155  }
156 
157  private:
158  exr_file_offset_t _exrpos;
159  exr_file_offset_t _exrsize;
160  unsigned char *_exrbuf;
161 };
162 
163 /* Memory-Mapped Input Stream */
164 
165 class IMMapStream : public Imf::IStream {
166  public:
167  IMMapStream(const char *filepath) : IStream(filepath)
168  {
169  int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
170  if (file < 0) {
171  throw IEX_NAMESPACE::InputExc("file not found");
172  }
173  _exrpos = 0;
174  _exrsize = BLI_file_descriptor_size(file);
175  imb_mmap_lock();
176  _mmap_file = BLI_mmap_open(file);
177  imb_mmap_unlock();
178  if (_mmap_file == nullptr) {
179  throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed");
180  }
181  close(file);
182  _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file);
183  }
184 
185  ~IMMapStream() override
186  {
187  imb_mmap_lock();
188  BLI_mmap_free(_mmap_file);
189  imb_mmap_unlock();
190  }
191 
192  /* This is implementing regular `read`, not `readMemoryMapped`, because DWAA and DWAB
193  * decompressors load on unaligned offsets. Therefore we can't avoid the memory copy. */
194 
195  bool read(char c[], int n) override
196  {
197  if (_exrpos + n > _exrsize) {
198  throw Iex::InputExc("Unexpected end of file.");
199  }
200  memcpy(c, _exrbuf + _exrpos, n);
201  _exrpos += n;
202 
203  return _exrpos < _exrsize;
204  }
205 
207  {
208  return _exrpos;
209  }
210 
211  void seekg(exr_file_offset_t pos) override
212  {
213  _exrpos = pos;
214  }
215 
216  private:
217  BLI_mmap_file *_mmap_file;
218  exr_file_offset_t _exrpos;
219  exr_file_offset_t _exrsize;
220  unsigned char *_exrbuf;
221 };
222 
223 /* File Input Stream */
224 
225 class IFileStream : public Imf::IStream {
226  public:
227  IFileStream(const char *filepath) : IStream(filepath)
228  {
229  /* utf-8 file path support on windows */
230 #if defined(WIN32)
231  wchar_t *wfilepath = alloc_utf16_from_8(filepath, 0);
232  ifs.open(wfilepath, std::ios_base::binary);
233  free(wfilepath);
234 #else
235  ifs.open(filepath, std::ios_base::binary);
236 #endif
237 
238  if (!ifs) {
239  Iex::throwErrnoExc();
240  }
241  }
242 
243  bool read(char c[], int n) override
244  {
245  if (!ifs) {
246  throw Iex::InputExc("Unexpected end of file.");
247  }
248 
249  errno = 0;
250  ifs.read(c, n);
251  return check_error();
252  }
253 
255  {
256  return std::streamoff(ifs.tellg());
257  }
258 
259  void seekg(exr_file_offset_t pos) override
260  {
261  ifs.seekg(pos);
262  check_error();
263  }
264 
265  void clear() override
266  {
267  ifs.clear();
268  }
269 
270  private:
271  bool check_error()
272  {
273  if (!ifs) {
274  if (errno) {
275  Iex::throwErrnoExc();
276  }
277 
278  return false;
279  }
280 
281  return true;
282  }
283 
284  std::ifstream ifs;
285 };
286 
287 /* Memory Output Stream */
288 
289 class OMemStream : public OStream {
290  public:
291  OMemStream(ImBuf *ibuf_) : OStream("<memory>"), ibuf(ibuf_), offset(0)
292  {
293  }
294 
295  void write(const char c[], int n) override
296  {
297  ensure_size(offset + n);
298  memcpy(ibuf->encodedbuffer + offset, c, n);
299  offset += n;
300  ibuf->encodedsize += n;
301  }
302 
304  {
305  return offset;
306  }
307 
308  void seekp(exr_file_offset_t pos) override
309  {
310  offset = pos;
311  ensure_size(offset);
312  }
313 
314  private:
315  void ensure_size(exr_file_offset_t size)
316  {
317  /* if buffer is too small increase it. */
318  while (size > ibuf->encodedbuffersize) {
319  if (!imb_enlargeencodedbufferImBuf(ibuf)) {
320  throw Iex::ErrnoExc("Out of memory.");
321  }
322  }
323  }
324 
325  ImBuf *ibuf;
327 };
328 
329 /* File Output Stream */
330 
331 class OFileStream : public OStream {
332  public:
333  OFileStream(const char *filepath) : OStream(filepath)
334  {
335  /* utf-8 file path support on windows */
336 #if defined(WIN32)
337  wchar_t *wfilepath = alloc_utf16_from_8(filepath, 0);
338  ofs.open(wfilepath, std::ios_base::binary);
339  free(wfilepath);
340 #else
341  ofs.open(filepath, std::ios_base::binary);
342 #endif
343 
344  if (!ofs) {
345  Iex::throwErrnoExc();
346  }
347  }
348 
349  void write(const char c[], int n) override
350  {
351  errno = 0;
352  ofs.write(c, n);
353  check_error();
354  }
355 
357  {
358  return std::streamoff(ofs.tellp());
359  }
360 
361  void seekp(exr_file_offset_t pos) override
362  {
363  ofs.seekp(pos);
364  check_error();
365  }
366 
367  private:
368  void check_error()
369  {
370  if (!ofs) {
371  if (errno) {
372  Iex::throwErrnoExc();
373  }
374 
375  throw Iex::ErrnoExc("File output failed.");
376  }
377  }
378 
379  std::ofstream ofs;
380 };
381 
382 struct _RGBAZ {
388 };
389 
390 using RGBAZ = _RGBAZ;
391 
392 static half float_to_half_safe(const float value)
393 {
394  return half(clamp_f(value, -HALF_MAX, HALF_MAX));
395 }
396 
397 extern "C" {
398 
399 bool imb_is_a_openexr(const unsigned char *mem, const size_t size)
400 {
401  /* No define is exposed for this size. */
402  if (size < 4) {
403  return false;
404  }
405  return Imf::isImfMagic((const char *)mem);
406 }
407 
408 static void openexr_header_compression(Header *header, int compression)
409 {
410  switch (compression) {
412  header->compression() = NO_COMPRESSION;
413  break;
415  header->compression() = PXR24_COMPRESSION;
416  break;
417  case R_IMF_EXR_CODEC_ZIP:
418  header->compression() = ZIP_COMPRESSION;
419  break;
420  case R_IMF_EXR_CODEC_PIZ:
421  header->compression() = PIZ_COMPRESSION;
422  break;
423  case R_IMF_EXR_CODEC_RLE:
424  header->compression() = RLE_COMPRESSION;
425  break;
427  header->compression() = ZIPS_COMPRESSION;
428  break;
429  case R_IMF_EXR_CODEC_B44:
430  header->compression() = B44_COMPRESSION;
431  break;
433  header->compression() = B44A_COMPRESSION;
434  break;
435 #if OPENEXR_VERSION_MAJOR > 2 || (OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2)
437  header->compression() = DWAA_COMPRESSION;
438  break;
440  header->compression() = DWAB_COMPRESSION;
441  break;
442 #endif
443  default:
444  header->compression() = ZIP_COMPRESSION;
445  break;
446  }
447 }
448 
449 static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
450 {
451  if (ibuf->metadata) {
452  IDProperty *prop;
453 
454  for (prop = (IDProperty *)ibuf->metadata->data.group.first; prop; prop = prop->next) {
455  if (prop->type == IDP_STRING) {
456  header->insert(prop->name, StringAttribute(IDP_String(prop)));
457  }
458  }
459  }
460 
461  if (ibuf->ppm[0] > 0.0) {
462  /* Convert meters to inches. */
463  addXDensity(*header, ibuf->ppm[0] * 0.0254);
464  }
465 }
466 
468  const char *propname,
469  char *prop,
470  int UNUSED(len))
471 {
472  Header *header = (Header *)data;
473  header->insert(propname, StringAttribute(prop));
474 }
475 
476 static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags)
477 {
478  const int channels = ibuf->channels;
479  const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
480  const bool is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != nullptr; /* summarize */
481  const int width = ibuf->x;
482  const int height = ibuf->y;
483  OStream *file_stream = nullptr;
484 
485  try {
486  Header header(width, height);
487 
489  openexr_header_metadata(&header, ibuf);
490 
491  /* create channels */
492  header.channels().insert("R", Channel(HALF));
493  header.channels().insert("G", Channel(HALF));
494  header.channels().insert("B", Channel(HALF));
495  if (is_alpha) {
496  header.channels().insert("A", Channel(HALF));
497  }
498  if (is_zbuf) {
499  /* z we do as float always */
500  header.channels().insert("Z", Channel(Imf::FLOAT));
501  }
502 
503  FrameBuffer frameBuffer;
504 
505  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
506  if (flags & IB_mem) {
507  file_stream = new OMemStream(ibuf);
508  }
509  else {
510  file_stream = new OFileStream(name);
511  }
512  OutputFile file(*file_stream, header);
513 
514  /* we store first everything in half array */
515  std::vector<RGBAZ> pixels(height * width);
516  RGBAZ *to = pixels.data();
517  int xstride = sizeof(RGBAZ);
518  int ystride = xstride * width;
519 
520  /* indicate used buffers */
521  frameBuffer.insert("R", Slice(HALF, (char *)&to->r, xstride, ystride));
522  frameBuffer.insert("G", Slice(HALF, (char *)&to->g, xstride, ystride));
523  frameBuffer.insert("B", Slice(HALF, (char *)&to->b, xstride, ystride));
524  if (is_alpha) {
525  frameBuffer.insert("A", Slice(HALF, (char *)&to->a, xstride, ystride));
526  }
527  if (is_zbuf) {
528  frameBuffer.insert("Z",
529  Slice(Imf::FLOAT,
530  (char *)(ibuf->zbuf_float + (height - 1) * width),
531  sizeof(float),
532  sizeof(float) * -width));
533  }
534  if (ibuf->rect_float) {
535  float *from;
536 
537  for (int i = ibuf->y - 1; i >= 0; i--) {
538  from = ibuf->rect_float + channels * i * width;
539 
540  for (int j = ibuf->x; j > 0; j--) {
541  to->r = float_to_half_safe(from[0]);
542  to->g = float_to_half_safe((channels >= 2) ? from[1] : from[0]);
543  to->b = float_to_half_safe((channels >= 3) ? from[2] : from[0]);
544  to->a = float_to_half_safe((channels >= 4) ? from[3] : 1.0f);
545  to++;
546  from += channels;
547  }
548  }
549  }
550  else {
551  unsigned char *from;
552 
553  for (int i = ibuf->y - 1; i >= 0; i--) {
554  from = (unsigned char *)ibuf->rect + 4 * i * width;
555 
556  for (int j = ibuf->x; j > 0; j--) {
557  to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
558  to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
559  to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
560  to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
561  to++;
562  from += 4;
563  }
564  }
565  }
566 
567  exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
568 
569  file.setFrameBuffer(frameBuffer);
570  file.writePixels(height);
571  }
572  catch (const std::exception &exc) {
573  delete file_stream;
574  printf("OpenEXR-save: ERROR: %s\n", exc.what());
575 
576  return false;
577  }
578 
579  delete file_stream;
580  return true;
581 }
582 
583 static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags)
584 {
585  const int channels = ibuf->channels;
586  const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
587  const bool is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != nullptr; /* summarize */
588  const int width = ibuf->x;
589  const int height = ibuf->y;
590  OStream *file_stream = nullptr;
591 
592  try {
593  Header header(width, height);
594 
596  openexr_header_metadata(&header, ibuf);
597 
598  /* create channels */
599  header.channels().insert("R", Channel(Imf::FLOAT));
600  header.channels().insert("G", Channel(Imf::FLOAT));
601  header.channels().insert("B", Channel(Imf::FLOAT));
602  if (is_alpha) {
603  header.channels().insert("A", Channel(Imf::FLOAT));
604  }
605  if (is_zbuf) {
606  header.channels().insert("Z", Channel(Imf::FLOAT));
607  }
608 
609  FrameBuffer frameBuffer;
610 
611  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
612  if (flags & IB_mem) {
613  file_stream = new OMemStream(ibuf);
614  }
615  else {
616  file_stream = new OFileStream(name);
617  }
618  OutputFile file(*file_stream, header);
619 
620  int xstride = sizeof(float) * channels;
621  int ystride = -xstride * width;
622 
623  /* Last scan-line, stride negative. */
624  float *rect[4] = {nullptr, nullptr, nullptr, nullptr};
625  rect[0] = ibuf->rect_float + channels * (height - 1) * width;
626  rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
627  rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
628  rect[3] = (channels >= 4) ?
629  rect[0] + 3 :
630  rect[0]; /* red as alpha, is this needed since alpha isn't written? */
631 
632  frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride));
633  frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride));
634  frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride));
635  if (is_alpha) {
636  frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride));
637  }
638  if (is_zbuf) {
639  frameBuffer.insert("Z",
640  Slice(Imf::FLOAT,
641  (char *)(ibuf->zbuf_float + (height - 1) * width),
642  sizeof(float),
643  sizeof(float) * -width));
644  }
645 
646  file.setFrameBuffer(frameBuffer);
647  file.writePixels(height);
648  }
649  catch (const std::exception &exc) {
650  printf("OpenEXR-save: ERROR: %s\n", exc.what());
651  delete file_stream;
652  return false;
653  }
654 
655  delete file_stream;
656  return true;
657 }
658 
659 bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
660 {
661  if (flags & IB_mem) {
663  ibuf->encodedsize = 0;
664  }
665 
666  if (ibuf->foptions.flag & OPENEXR_HALF) {
667  return imb_save_openexr_half(ibuf, name, flags);
668  }
669 
670  /* when no float rect, we save as half (16 bits is sufficient) */
671  if (ibuf->rect_float == nullptr) {
672  return imb_save_openexr_half(ibuf, name, flags);
673  }
674 
675  return imb_save_openexr_float(ibuf, name, flags);
676 }
677 
678 /* ******* Nicer API, MultiLayer and with Tile file support ************************************ */
679 
680 /* naming rules:
681  * - parse name from right to left
682  * - last character is channel ID, 1 char like 'A' 'R' 'G' 'B' 'X' 'Y' 'Z' 'W' 'U' 'V'
683  * - separated with a dot; the Pass name (like "Depth", "Color", "Diffuse" or "Combined")
684  * - separated with a dot: the Layer name (like "Light1" or "Walls" or "Characters")
685  */
686 
687 static ListBase exrhandles = {nullptr, nullptr};
688 
689 struct ExrHandle {
690  struct ExrHandle *next, *prev;
691  char name[FILE_MAX];
692 
693  IStream *ifile_stream;
694  MultiPartInputFile *ifile;
695 
697  MultiPartOutputFile *mpofile;
698  OutputFile *ofile;
699 
700  int tilex, tiley;
701  int width, height;
702  int mipmap;
703 
706  StringVector *multiView;
707 
708  int parts;
709 
710  ListBase channels; /* flattened out, ExrChannel */
711  ListBase layers; /* hierarchical, pointing in end to ExrChannel */
712 
713  int num_half_channels; /* used during filr save, allows faster temporary buffers allocation */
714 };
715 
716 /* flattened out channel */
717 struct ExrChannel {
718  struct ExrChannel *next, *prev;
719 
720  char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */
721  struct MultiViewChannelName *m; /* struct to store all multipart channel info */
722  int xstride, ystride; /* step to next pixel, to next scan-line. */
723  float *rect; /* first pointer to write in */
724  char chan_id; /* quick lookup of channel char */
725  int view_id; /* quick lookup of channel view */
726  bool use_half_float; /* when saving use half float for file storage */
727 };
728 
729 /* hierarchical; layers -> passes -> channels[] */
730 struct ExrPass {
731  struct ExrPass *next, *prev;
733  int totchan;
734  float *rect;
737 
738  char internal_name[EXR_PASS_MAXNAME]; /* name with no view */
740  int view_id;
741 };
742 
743 struct ExrLayer {
744  struct ExrLayer *next, *prev;
747 };
748 
750 
751 /* ********************** */
752 
754 {
755  ExrHandle *data = MEM_cnew<ExrHandle>("exr handle");
756  data->multiView = new StringVector();
757 
759  return data;
760 }
761 
762 void *IMB_exr_get_handle_name(const char *name)
763 {
765 
766  if (data == nullptr) {
768  BLI_strncpy(data->name, name, strlen(name) + 1);
769  }
770  return data;
771 }
772 
773 /* multiview functions */
774 } /* extern "C" */
775 
776 extern "C" {
777 
778 void IMB_exr_add_view(void *handle, const char *name)
779 {
780  ExrHandle *data = (ExrHandle *)handle;
781  data->multiView->push_back(name);
782 }
783 
784 static int imb_exr_get_multiView_id(StringVector &views, const std::string &name)
785 {
786  int count = 0;
787  for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) {
788  if (name == *i) {
789  return count;
790  }
791 
792  count++;
793  }
794 
795  /* no views or wrong name */
796  return -1;
797 }
798 
799 static void imb_exr_get_views(MultiPartInputFile &file, StringVector &views)
800 {
801  if (exr_has_multipart_file(file) == false) {
802  if (exr_has_multiview(file)) {
803  StringVector sv = multiView(file.header(0));
804  for (const std::string &view_name : sv) {
805  views.push_back(view_name);
806  }
807  }
808  }
809 
810  else {
811  for (int p = 0; p < file.parts(); p++) {
812  std::string view;
813  if (file.header(p).hasView()) {
814  view = file.header(p).view();
815  }
816 
817  if (imb_exr_get_multiView_id(views, view) == -1) {
818  views.push_back(view);
819  }
820  }
821  }
822 }
823 
824 /* Multilayer Blender files have the view name in all the passes (even the default view one) */
825 static void imb_exr_insert_view_name(char *name_full, const char *passname, const char *viewname)
826 {
827  BLI_assert(!ELEM(name_full, passname, viewname));
828 
829  if (viewname == nullptr || viewname[0] == '\0') {
830  BLI_strncpy(name_full, passname, sizeof(((ExrChannel *)nullptr)->name));
831  return;
832  }
833 
834  const char delims[] = {'.', '\0'};
835  const char *sep;
836  const char *token;
837  size_t len;
838 
839  len = BLI_str_rpartition(passname, delims, &sep, &token);
840 
841  if (sep) {
842  BLI_snprintf(name_full, EXR_PASS_MAXNAME, "%.*s.%s.%s", (int)len, passname, viewname, token);
843  }
844  else {
845  BLI_snprintf(name_full, EXR_PASS_MAXNAME, "%s.%s", passname, viewname);
846  }
847 }
848 
849 void IMB_exr_add_channel(void *handle,
850  const char *layname,
851  const char *passname,
852  const char *viewname,
853  int xstride,
854  int ystride,
855  float *rect,
856  bool use_half_float)
857 {
858  ExrHandle *data = (ExrHandle *)handle;
859  ExrChannel *echan;
860 
861  echan = MEM_cnew<ExrChannel>("exr channel");
862  echan->m = new MultiViewChannelName();
863 
864  if (layname && layname[0] != '\0') {
865  echan->m->name = layname;
866  echan->m->name.append(".");
867  echan->m->name.append(passname);
868  }
869  else {
870  echan->m->name.assign(passname);
871  }
872 
873  echan->m->internal_name = echan->m->name;
874 
875  echan->m->view.assign(viewname ? viewname : "");
876 
877  /* quick look up */
878  echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view));
879 
880  /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
881  if (layname && layname[0] != '\0') {
882  imb_exr_insert_view_name(echan->name, echan->m->name.c_str(), echan->m->view.c_str());
883  }
884  else if (!data->multiView->empty()) {
885  std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id);
886  BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name));
887  }
888  else {
889  BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name));
890  }
891 
892  echan->xstride = xstride;
893  echan->ystride = ystride;
894  echan->rect = rect;
895  echan->use_half_float = use_half_float;
896 
897  if (echan->use_half_float) {
898  data->num_half_channels++;
899  }
900 
901  exr_printf("added channel %s\n", echan->name);
902  BLI_addtail(&data->channels, echan);
903 }
904 
905 bool IMB_exr_begin_write(void *handle,
906  const char *filepath,
907  int width,
908  int height,
909  int compress,
910  const StampData *stamp)
911 {
912  ExrHandle *data = (ExrHandle *)handle;
913  Header header(width, height);
914  ExrChannel *echan;
915 
916  data->width = width;
917  data->height = height;
918 
919  bool is_singlelayer, is_multilayer, is_multiview;
920 
921  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
922  header.channels().insert(echan->name, Channel(echan->use_half_float ? Imf::HALF : Imf::FLOAT));
923  }
924 
925  openexr_header_compression(&header, compress);
927  &header, const_cast<StampData *>(stamp), openexr_header_metadata_callback, false);
928  /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
929 
931  header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview);
932 
933  if (is_multilayer) {
934  header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
935  }
936 
937  if (is_multiview) {
938  addMultiView(header, *data->multiView);
939  }
940 
941  /* avoid crash/abort when we don't have permission to write here */
942  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
943  try {
944  data->ofile_stream = new OFileStream(filepath);
945  data->ofile = new OutputFile(*(data->ofile_stream), header);
946  }
947  catch (const std::exception &exc) {
948  std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
949 
950  delete data->ofile;
951  delete data->ofile_stream;
952 
953  data->ofile = nullptr;
954  data->ofile_stream = nullptr;
955  }
956 
957  return (data->ofile != nullptr);
958 }
959 
961  void *handle, const char *filepath, int mipmap, int width, int height, int tilex, int tiley)
962 {
963  ExrHandle *data = (ExrHandle *)handle;
964  Header header(width, height);
965  std::vector<Header> headers;
966  ExrChannel *echan;
967 
968  data->tilex = tilex;
969  data->tiley = tiley;
970  data->width = width;
971  data->height = height;
972  data->mipmap = mipmap;
973 
974  header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL));
975  header.compression() = RLE_COMPRESSION;
976  header.setType(TILEDIMAGE);
977 
978  header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43"));
979 
980  int numparts = data->multiView->size();
981 
982  /* copy header from all parts of input to our header array
983  * those temporary files have one part per view */
984  for (int i = 0; i < numparts; i++) {
985  headers.push_back(header);
986  headers[headers.size() - 1].setView((*(data->multiView))[i]);
987  headers[headers.size() - 1].setName((*(data->multiView))[i]);
988  }
989 
990  exr_printf("\nIMB_exrtile_begin_write\n");
991  exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
992  exr_printf("---------------------------------------------------------------\n");
993 
994  /* Assign channels. */
995  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
996  /* Tiles are expected to be saved with full float currently. */
997  BLI_assert(echan->use_half_float == 0);
998 
999  echan->m->internal_name = echan->m->name;
1000  echan->m->part_number = echan->view_id;
1001 
1002  headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT));
1003  exr_printf("%d %-6s %-22s \"%s\"\n",
1004  echan->m->part_number,
1005  echan->m->view.c_str(),
1006  echan->m->name.c_str(),
1007  echan->m->internal_name.c_str());
1008  }
1009 
1010  /* avoid crash/abort when we don't have permission to write here */
1011  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
1012  try {
1013  data->ofile_stream = new OFileStream(filepath);
1014  data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), headers.data(), headers.size());
1015  }
1016  catch (const std::exception &) {
1017  delete data->mpofile;
1018  delete data->ofile_stream;
1019 
1020  data->mpofile = nullptr;
1021  data->ofile_stream = nullptr;
1022  }
1023 }
1024 
1026  void *handle, const char *filepath, int *width, int *height, const bool parse_channels)
1027 {
1028  ExrHandle *data = (ExrHandle *)handle;
1029  ExrChannel *echan;
1030 
1031  /* 32 is arbitrary, but zero length files crashes exr. */
1032  if (!(BLI_exists(filepath) && BLI_file_size(filepath) > 32)) {
1033  return false;
1034  }
1035 
1036  /* avoid crash/abort when we don't have permission to write here */
1037  try {
1038  data->ifile_stream = new IFileStream(filepath);
1039  data->ifile = new MultiPartInputFile(*(data->ifile_stream));
1040  }
1041  catch (const std::exception &) {
1042  delete data->ifile;
1043  delete data->ifile_stream;
1044 
1045  data->ifile = nullptr;
1046  data->ifile_stream = nullptr;
1047  }
1048 
1049  if (!data->ifile) {
1050  return false;
1051  }
1052 
1053  Box2i dw = data->ifile->header(0).dataWindow();
1054  data->width = *width = dw.max.x - dw.min.x + 1;
1055  data->height = *height = dw.max.y - dw.min.y + 1;
1056 
1057  if (parse_channels) {
1058  /* Parse channels into view/layer/pass. */
1060  return false;
1061  }
1062  }
1063  else {
1064  /* Read view and channels without parsing. */
1065  imb_exr_get_views(*data->ifile, *data->multiView);
1066 
1067  std::vector<MultiViewChannelName> channels;
1068  GetChannelsInMultiPartFile(*data->ifile, channels);
1069 
1070  for (const MultiViewChannelName &channel : channels) {
1072  data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
1073 
1074  echan = (ExrChannel *)data->channels.last;
1075  echan->m->name = channel.name;
1076  echan->m->view = channel.view;
1077  echan->m->part_number = channel.part_number;
1078  echan->m->internal_name = channel.internal_name;
1079  }
1080  }
1081 
1082  return true;
1083 }
1084 
1086  void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
1087 {
1088  ExrHandle *data = (ExrHandle *)handle;
1089  ExrChannel *echan;
1090  char name[EXR_TOT_MAXNAME + 1];
1091 
1092  if (layname && layname[0] != '\0') {
1093  char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
1094  BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
1095  BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
1096 
1097  BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
1098  }
1099  else {
1100  BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
1101  }
1102 
1103  echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
1104 
1105  if (echan) {
1106  echan->xstride = xstride;
1107  echan->ystride = ystride;
1108  echan->rect = rect;
1109  }
1110  else {
1111  printf("IMB_exr_set_channel error %s\n", name);
1112  }
1113 }
1114 
1115 float *IMB_exr_channel_rect(void *handle,
1116  const char *layname,
1117  const char *passname,
1118  const char *viewname)
1119 {
1120  ExrHandle *data = (ExrHandle *)handle;
1121  ExrChannel *echan;
1122  char name[EXR_TOT_MAXNAME + 1];
1123 
1124  if (layname) {
1125  char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
1126  BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
1127  BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
1128 
1129  BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
1130  }
1131  else {
1132  BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
1133  }
1134 
1135  /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
1136  if (layname && layname[0] != '\0') {
1137  char temp_buf[EXR_PASS_MAXNAME];
1138  imb_exr_insert_view_name(temp_buf, name, viewname);
1139  BLI_strncpy(name, temp_buf, sizeof(name));
1140  }
1141  else if (!data->multiView->empty()) {
1142  const int view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname));
1143  std::string raw_name = insertViewName(name, *data->multiView, view_id);
1144  BLI_strncpy(name, raw_name.c_str(), sizeof(name));
1145  }
1146 
1147  echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
1148 
1149  if (echan) {
1150  return echan->rect;
1151  }
1152 
1153  return nullptr;
1154 }
1155 
1156 void IMB_exr_clear_channels(void *handle)
1157 {
1158  ExrHandle *data = (ExrHandle *)handle;
1159  ExrChannel *chan;
1160 
1161  for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) {
1162  delete chan->m;
1163  }
1164 
1165  BLI_freelistN(&data->channels);
1166 }
1167 
1168 void IMB_exr_write_channels(void *handle)
1169 {
1170  ExrHandle *data = (ExrHandle *)handle;
1171  FrameBuffer frameBuffer;
1172  ExrChannel *echan;
1173 
1174  if (data->channels.first) {
1175  const size_t num_pixels = ((size_t)data->width) * data->height;
1176  half *rect_half = nullptr, *current_rect_half = nullptr;
1177 
1178  /* We allocate temporary storage for half pixels for all the channels at once. */
1179  if (data->num_half_channels != 0) {
1180  rect_half = (half *)MEM_mallocN(sizeof(half) * data->num_half_channels * num_pixels,
1181  __func__);
1182  current_rect_half = rect_half;
1183  }
1184 
1185  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1186  /* Writing starts from last scan-line, stride negative. */
1187  if (echan->use_half_float) {
1188  float *rect = echan->rect;
1189  half *cur = current_rect_half;
1190  for (size_t i = 0; i < num_pixels; i++, cur++) {
1191  *cur = float_to_half_safe(rect[i * echan->xstride]);
1192  }
1193  half *rect_to_write = current_rect_half + (data->height - 1L) * data->width;
1194  frameBuffer.insert(
1195  echan->name,
1196  Slice(Imf::HALF, (char *)rect_to_write, sizeof(half), -data->width * sizeof(half)));
1197  current_rect_half += num_pixels;
1198  }
1199  else {
1200  float *rect = echan->rect + echan->xstride * (data->height - 1L) * data->width;
1201  frameBuffer.insert(echan->name,
1202  Slice(Imf::FLOAT,
1203  (char *)rect,
1204  echan->xstride * sizeof(float),
1205  -echan->ystride * sizeof(float)));
1206  }
1207  }
1208 
1209  data->ofile->setFrameBuffer(frameBuffer);
1210  try {
1211  data->ofile->writePixels(data->height);
1212  }
1213  catch (const std::exception &exc) {
1214  std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
1215  }
1216  /* Free temporary buffers. */
1217  if (rect_half != nullptr) {
1218  MEM_freeN(rect_half);
1219  }
1220  }
1221  else {
1222  printf("Error: attempt to save MultiLayer without layers.\n");
1223  }
1224 }
1225 
1227  void *handle, int partx, int party, int level, const char *viewname, bool empty)
1228 {
1229  /* Can write empty channels for incomplete renders. */
1230  ExrHandle *data = (ExrHandle *)handle;
1231  FrameBuffer frameBuffer;
1232  std::string view(viewname);
1233  const int view_id = imb_exr_get_multiView_id(*data->multiView, view);
1234 
1235  exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname);
1236  exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
1237  exr_printf("---------------------------------------------------------------------\n");
1238 
1239  if (!empty) {
1240  ExrChannel *echan;
1241 
1242  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1243 
1244  /* eventually we can make the parts' channels to include
1245  * only the current view TODO */
1246  if (!STREQ(viewname, echan->m->view.c_str())) {
1247  continue;
1248  }
1249 
1250  exr_printf("%d %-6s %-22s \"%s\"\n",
1251  echan->m->part_number,
1252  echan->m->view.c_str(),
1253  echan->m->name.c_str(),
1254  echan->m->internal_name.c_str());
1255 
1256  float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
1257  frameBuffer.insert(echan->m->internal_name,
1258  Slice(Imf::FLOAT,
1259  (char *)rect,
1260  echan->xstride * sizeof(float),
1261  echan->ystride * sizeof(float)));
1262  }
1263  }
1264 
1265  TiledOutputPart out(*data->mpofile, view_id);
1266  out.setFrameBuffer(frameBuffer);
1267 
1268  try {
1269  // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
1270  out.writeTile(partx / data->tilex, party / data->tiley, level);
1271  }
1272  catch (const std::exception &exc) {
1273  std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
1274  }
1275 }
1276 
1277 void IMB_exr_read_channels(void *handle)
1278 {
1279  ExrHandle *data = (ExrHandle *)handle;
1280  int numparts = data->ifile->parts();
1281 
1282  /* Check if EXR was saved with previous versions of blender which flipped images. */
1283  const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>(
1284  "BlenderMultiChannel");
1285 
1286  /* 'previous multilayer attribute, flipped. */
1287  short flip = (ta && STRPREFIX(ta->value().c_str(), "Blender V2.43"));
1288 
1289  exr_printf(
1290  "\nIMB_exr_read_channels\n%s %-6s %-22s "
1291  "\"%s\"\n---------------------------------------------------------------------\n",
1292  "p",
1293  "view",
1294  "name",
1295  "internal_name");
1296 
1297  for (int i = 0; i < numparts; i++) {
1298  /* Read part header. */
1299  InputPart in(*data->ifile, i);
1300  Header header = in.header();
1301  Box2i dw = header.dataWindow();
1302 
1303  /* Insert all matching channel into frame-buffer. */
1304  FrameBuffer frameBuffer;
1305  ExrChannel *echan;
1306 
1307  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1308  if (echan->m->part_number != i) {
1309  continue;
1310  }
1311 
1312  exr_printf("%d %-6s %-22s \"%s\"\n",
1313  echan->m->part_number,
1314  echan->m->view.c_str(),
1315  echan->m->name.c_str(),
1316  echan->m->internal_name.c_str());
1317 
1318  if (echan->rect) {
1319  float *rect = echan->rect;
1320  size_t xstride = echan->xstride * sizeof(float);
1321  size_t ystride = echan->ystride * sizeof(float);
1322 
1323  if (!flip) {
1324  /* Inverse correct first pixel for data-window coordinates. */
1325  rect -= echan->xstride * (dw.min.x - dw.min.y * data->width);
1326  /* Move to last scan-line to flip to Blender convention. */
1327  rect += echan->xstride * (data->height - 1) * data->width;
1328  ystride = -ystride;
1329  }
1330  else {
1331  /* Inverse correct first pixel for data-window coordinates. */
1332  rect -= echan->xstride * (dw.min.x + dw.min.y * data->width);
1333  }
1334 
1335  frameBuffer.insert(echan->m->internal_name,
1336  Slice(Imf::FLOAT, (char *)rect, xstride, ystride));
1337  }
1338  else {
1339  printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str());
1340  }
1341  }
1342 
1343  /* Read pixels. */
1344  try {
1345  in.setFrameBuffer(frameBuffer);
1346  exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, dw.min.y, dw.max.y);
1347  in.readPixels(dw.min.y, dw.max.y);
1348  }
1349  catch (const std::exception &exc) {
1350  std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
1351  break;
1352  }
1353  }
1354 }
1355 
1356 void IMB_exr_multilayer_convert(void *handle,
1357  void *base,
1358  void *(*addview)(void *base, const char *str),
1359  void *(*addlayer)(void *base, const char *str),
1360  void (*addpass)(void *base,
1361  void *lay,
1362  const char *str,
1363  float *rect,
1364  int totchan,
1365  const char *chan_id,
1366  const char *view))
1367 {
1368  ExrHandle *data = (ExrHandle *)handle;
1369  ExrLayer *lay;
1370  ExrPass *pass;
1371 
1372  /* RenderResult needs at least one RenderView */
1373  if (data->multiView->empty()) {
1374  addview(base, "");
1375  }
1376  else {
1377  /* add views to RenderResult */
1378  for (const std::string &view_name : *data->multiView) {
1379  addview(base, view_name.c_str());
1380  }
1381  }
1382 
1383  if (BLI_listbase_is_empty(&data->layers)) {
1384  printf("cannot convert multilayer, no layers in handle\n");
1385  return;
1386  }
1387 
1388  for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1389  void *laybase = addlayer(base, lay->name);
1390  if (laybase) {
1391  for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1392  addpass(base,
1393  laybase,
1394  pass->internal_name,
1395  pass->rect,
1396  pass->totchan,
1397  pass->chan_id,
1398  pass->view);
1399  pass->rect = nullptr;
1400  }
1401  }
1402  }
1403 }
1404 
1405 void IMB_exr_close(void *handle)
1406 {
1407  ExrHandle *data = (ExrHandle *)handle;
1408  ExrLayer *lay;
1409  ExrPass *pass;
1410  ExrChannel *chan;
1411 
1412  delete data->ifile;
1413  delete data->ifile_stream;
1414  delete data->ofile;
1415  delete data->mpofile;
1416  delete data->ofile_stream;
1417  delete data->multiView;
1418 
1419  data->ifile = nullptr;
1420  data->ifile_stream = nullptr;
1421  data->ofile = nullptr;
1422  data->mpofile = nullptr;
1423  data->ofile_stream = nullptr;
1424 
1425  for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) {
1426  delete chan->m;
1427  }
1428  BLI_freelistN(&data->channels);
1429 
1430  for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1431  for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1432  if (pass->rect) {
1433  MEM_freeN(pass->rect);
1434  }
1435  }
1436  BLI_freelistN(&lay->passes);
1437  }
1438  BLI_freelistN(&data->layers);
1439 
1441  MEM_freeN(data);
1442 }
1443 
1444 /* ********* */
1445 
1446 /* get a substring from the end of the name, separated by '.' */
1447 static int imb_exr_split_token(const char *str, const char *end, const char **token)
1448 {
1449  const char delims[] = {'.', '\0'};
1450  const char *sep;
1451 
1452  BLI_str_partition_ex(str, end, delims, &sep, token, true);
1453 
1454  if (!sep) {
1455  *token = str;
1456  }
1457 
1458  return (int)(end - *token);
1459 }
1460 
1461 static void imb_exr_pass_name_from_channel(char *passname,
1462  const ExrChannel *echan,
1463  const char *channelname,
1464  const bool has_xyz_channels)
1465 {
1466  const int passname_maxncpy = EXR_TOT_MAXNAME;
1467 
1468  if (echan->chan_id == 'Z' && (!has_xyz_channels || BLI_strcaseeq(channelname, "depth"))) {
1469  BLI_strncpy(passname, "Depth", passname_maxncpy);
1470  }
1471  else if (echan->chan_id == 'Y' && !has_xyz_channels) {
1472  BLI_strncpy(passname, channelname, passname_maxncpy);
1473  }
1474  else if (ELEM(echan->chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
1475  BLI_strncpy(passname, "Combined", passname_maxncpy);
1476  }
1477  else {
1478  BLI_strncpy(passname, channelname, passname_maxncpy);
1479  }
1480 }
1481 
1483  char *layname,
1484  char *passname,
1485  bool has_xyz_channels)
1486 {
1487  const int layname_maxncpy = EXR_TOT_MAXNAME;
1488  const char *name = echan->m->name.c_str();
1489  const char *end = name + strlen(name);
1490  const char *token;
1491 
1492  /* Some multi-layers have the combined buffer with names V, RGBA, or XYZ saved. Additionally, the
1493  * Z channel can be interpreted as a Depth channel, but we only detect it as such if no X and Y
1494  * channels exists, since the Z in this case is part of XYZ. The same goes for the Y channel,
1495  * which can be detected as a luminance channel with the same name. */
1496  if (name[1] == 0) {
1497  /* Notice that we will be comparing with this upper-case version of the channel name, so the
1498  * below comparisons are effectively not case sensitive, and would also consider lowercase
1499  * versions of the listed channels. */
1500  echan->chan_id = BLI_toupper_ascii(name[0]);
1501  layname[0] = '\0';
1502  imb_exr_pass_name_from_channel(passname, echan, name, has_xyz_channels);
1503  return 1;
1504  }
1505 
1506  /* last token is channel identifier */
1507  size_t len = imb_exr_split_token(name, end, &token);
1508  if (len == 0) {
1509  printf("multilayer read: bad channel name: %s\n", name);
1510  return 0;
1511  }
1512 
1513  char channelname[EXR_TOT_MAXNAME];
1514  BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname)));
1515 
1516  if (len == 1) {
1517  echan->chan_id = BLI_toupper_ascii(channelname[0]);
1518  }
1519  else if (len > 1) {
1520  if (len == 2) {
1521  /* some multilayers are using two-letter channels name,
1522  * like, MX or NZ, which is basically has structure of
1523  * <pass_prefix><component>
1524  *
1525  * This is a bit silly, but see file from T35658.
1526  *
1527  * Here we do some magic to distinguish such cases.
1528  */
1529  const char chan_id = BLI_toupper_ascii(channelname[1]);
1530  if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) {
1531  echan->chan_id = chan_id;
1532  }
1533  else {
1534  echan->chan_id = 'X'; /* Default to X if unknown. */
1535  }
1536  }
1537  else if (BLI_strcaseeq(channelname, "red")) {
1538  echan->chan_id = 'R';
1539  }
1540  else if (BLI_strcaseeq(channelname, "green")) {
1541  echan->chan_id = 'G';
1542  }
1543  else if (BLI_strcaseeq(channelname, "blue")) {
1544  echan->chan_id = 'B';
1545  }
1546  else if (BLI_strcaseeq(channelname, "alpha")) {
1547  echan->chan_id = 'A';
1548  }
1549  else if (BLI_strcaseeq(channelname, "depth")) {
1550  echan->chan_id = 'Z';
1551  }
1552  else {
1553  echan->chan_id = 'X'; /* Default to X if unknown. */
1554  }
1555  }
1556  end -= len + 1; /* +1 to skip '.' separator */
1557 
1558  if (end > name) {
1559  /* second token is pass name */
1560  len = imb_exr_split_token(name, end, &token);
1561  if (len == 0) {
1562  printf("multilayer read: bad channel name: %s\n", name);
1563  return 0;
1564  }
1565  BLI_strncpy(passname, token, len + 1);
1566  end -= len + 1; /* +1 to skip '.' separator */
1567  }
1568  else {
1569  /* Single token, determine pass name from channel name. */
1570  imb_exr_pass_name_from_channel(passname, echan, channelname, has_xyz_channels);
1571  }
1572 
1573  /* all preceding tokens combined as layer name */
1574  if (end > name) {
1575  BLI_strncpy(layname, name, std::min(layname_maxncpy, int(end - name) + 1));
1576  }
1577  else {
1578  layname[0] = '\0';
1579  }
1580 
1581  return 1;
1582 }
1583 
1584 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
1585 {
1586  ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
1587 
1588  if (lay == nullptr) {
1589  lay = MEM_cnew<ExrLayer>("exr layer");
1590  BLI_addtail(lb, lay);
1591  BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
1592  }
1593 
1594  return lay;
1595 }
1596 
1597 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
1598 {
1599  ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
1600 
1601  if (pass == nullptr) {
1602  pass = MEM_cnew<ExrPass>("exr pass");
1603 
1604  if (STREQ(passname, "Combined")) {
1605  BLI_addhead(lb, pass);
1606  }
1607  else {
1608  BLI_addtail(lb, pass);
1609  }
1610  }
1611 
1612  BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
1613 
1614  return pass;
1615 }
1616 
1617 static bool exr_has_xyz_channels(ExrHandle *exr_handle)
1618 {
1619  bool x_found = false;
1620  bool y_found = false;
1621  bool z_found = false;
1622  LISTBASE_FOREACH (ExrChannel *, channel, &exr_handle->channels) {
1623  if (channel->m->name == "X" || channel->m->name == "x") {
1624  x_found = true;
1625  }
1626  if (channel->m->name == "Y" || channel->m->name == "y") {
1627  y_found = true;
1628  }
1629  if (channel->m->name == "Z" || channel->m->name == "z") {
1630  z_found = true;
1631  }
1632  }
1633 
1634  return x_found && y_found && z_found;
1635 }
1636 
1637 /* Replacement for OpenEXR GetChannelsInMultiPartFile, that also handles the
1638  * case where parts are used for passes instead of multiview. */
1639 static std::vector<MultiViewChannelName> exr_channels_in_multi_part_file(
1640  const MultiPartInputFile &file)
1641 {
1642  std::vector<MultiViewChannelName> channels;
1643 
1644  /* Detect if file has multiview. */
1645  StringVector multiview;
1646  bool has_multiview = false;
1647  if (file.parts() == 1) {
1648  if (hasMultiView(file.header(0))) {
1649  multiview = multiView(file.header(0));
1650  has_multiview = true;
1651  }
1652  }
1653 
1654  /* Get channels from each part. */
1655  for (int p = 0; p < file.parts(); p++) {
1656  const ChannelList &c = file.header(p).channels();
1657 
1658  std::string part_view = "";
1659  if (file.header(p).hasView()) {
1660  part_view = file.header(p).view();
1661  }
1662  std::string part_name = "";
1663  if (file.header(p).hasName()) {
1664  part_name = file.header(p).name();
1665  }
1666 
1667  for (ChannelList::ConstIterator i = c.begin(); i != c.end(); i++) {
1668  MultiViewChannelName m;
1669  m.name = std::string(i.name());
1670  m.internal_name = m.name;
1671 
1672  if (has_multiview) {
1673  m.view = viewFromChannelName(m.name, multiview);
1674  m.name = removeViewName(m.internal_name, m.view);
1675  }
1676  else {
1677  m.view = part_view;
1678  }
1679 
1680  /* Prepend part name as potential layer or pass name. */
1681  if (!part_name.empty()) {
1682  m.name = part_name + "." + m.name;
1683  }
1684 
1685  m.part_number = p;
1686  channels.push_back(m);
1687  }
1688  }
1689 
1690  return channels;
1691 }
1692 
1694 {
1695  std::vector<MultiViewChannelName> channels = exr_channels_in_multi_part_file(*data->ifile);
1696 
1697  imb_exr_get_views(*data->ifile, *data->multiView);
1698 
1699  for (const MultiViewChannelName &channel : channels) {
1701  data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
1702 
1703  ExrChannel *echan = (ExrChannel *)data->channels.last;
1704  echan->m->name = channel.name;
1705  echan->m->view = channel.view;
1706  echan->m->part_number = channel.part_number;
1707  echan->m->internal_name = channel.internal_name;
1708  }
1709 
1710  const bool has_xyz_channels = exr_has_xyz_channels(data);
1711 
1712  /* now try to sort out how to assign memory to the channels */
1713  /* first build hierarchical layer list */
1714  ExrChannel *echan = (ExrChannel *)data->channels.first;
1715  for (; echan; echan = echan->next) {
1716  char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
1717  if (imb_exr_split_channel_name(echan, layname, passname, has_xyz_channels)) {
1718  const char *view = echan->m->view.c_str();
1719  char internal_name[EXR_PASS_MAXNAME];
1720 
1721  BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME);
1722 
1723  if (view[0] != '\0') {
1724  char tmp_pass[EXR_PASS_MAXNAME];
1725  BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view);
1726  BLI_strncpy(passname, tmp_pass, sizeof(passname));
1727  }
1728 
1729  ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
1730  ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
1731 
1732  pass->chan[pass->totchan] = echan;
1733  pass->totchan++;
1734  pass->view_id = echan->view_id;
1735  BLI_strncpy(pass->view, view, sizeof(pass->view));
1736  BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME);
1737 
1738  if (pass->totchan >= EXR_PASS_MAXCHAN) {
1739  break;
1740  }
1741  }
1742  }
1743  if (echan) {
1744  printf("error, too many channels in one pass: %s\n", echan->m->name.c_str());
1745  return false;
1746  }
1747 
1748  /* with some heuristics, try to merge the channels in buffers */
1749  for (ExrLayer *lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1750  for (ExrPass *pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1751  if (pass->totchan) {
1752  pass->rect = (float *)MEM_callocN(
1753  data->width * data->height * pass->totchan * sizeof(float), "pass rect");
1754  if (pass->totchan == 1) {
1755  ExrChannel *echan = pass->chan[0];
1756  echan->rect = pass->rect;
1757  echan->xstride = 1;
1758  echan->ystride = data->width;
1759  pass->chan_id[0] = echan->chan_id;
1760  }
1761  else {
1762  char lookup[256];
1763 
1764  memset(lookup, 0, sizeof(lookup));
1765 
1766  /* we can have RGB(A), XYZ(W), UVA */
1767  if (ELEM(pass->totchan, 3, 4)) {
1768  if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||
1769  pass->chan[2]->chan_id == 'B') {
1770  lookup[(unsigned int)'R'] = 0;
1771  lookup[(unsigned int)'G'] = 1;
1772  lookup[(unsigned int)'B'] = 2;
1773  lookup[(unsigned int)'A'] = 3;
1774  }
1775  else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||
1776  pass->chan[2]->chan_id == 'Y') {
1777  lookup[(unsigned int)'X'] = 0;
1778  lookup[(unsigned int)'Y'] = 1;
1779  lookup[(unsigned int)'Z'] = 2;
1780  lookup[(unsigned int)'W'] = 3;
1781  }
1782  else {
1783  lookup[(unsigned int)'U'] = 0;
1784  lookup[(unsigned int)'V'] = 1;
1785  lookup[(unsigned int)'A'] = 2;
1786  }
1787  for (int a = 0; a < pass->totchan; a++) {
1788  echan = pass->chan[a];
1789  echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
1790  echan->xstride = pass->totchan;
1791  echan->ystride = data->width * pass->totchan;
1792  pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
1793  }
1794  }
1795  else { /* unknown */
1796  for (int a = 0; a < pass->totchan; a++) {
1797  ExrChannel *echan = pass->chan[a];
1798  echan->rect = pass->rect + a;
1799  echan->xstride = pass->totchan;
1800  echan->ystride = data->width * pass->totchan;
1801  pass->chan_id[a] = echan->chan_id;
1802  }
1803  }
1804  }
1805  }
1806  }
1807  }
1808 
1809  return true;
1810 }
1811 
1812 /* creates channels, makes a hierarchy and assigns memory to channels */
1813 static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
1814  MultiPartInputFile &file,
1815  int width,
1816  int height)
1817 {
1819 
1820  data->ifile_stream = &file_stream;
1821  data->ifile = &file;
1822 
1823  data->width = width;
1824  data->height = height;
1825 
1828  return nullptr;
1829  }
1830 
1831  return data;
1832 }
1833 
1834 /* ********************************************************* */
1835 
1836 /* debug only */
1837 static void exr_printf(const char *fmt, ...)
1838 {
1839 #if 0
1840  char output[1024];
1841  va_list args;
1842  va_start(args, fmt);
1843  std::vsprintf(output, fmt, args);
1844  va_end(args);
1845  printf("%s", output);
1846 #else
1847  (void)fmt;
1848 #endif
1849 }
1850 
1851 static void exr_print_filecontents(MultiPartInputFile &file)
1852 {
1853  int numparts = file.parts();
1854  if (numparts == 1 && hasMultiView(file.header(0))) {
1855  const StringVector views = multiView(file.header(0));
1856  printf("OpenEXR-load: MultiView file\n");
1857  printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str());
1858  for (const std::string &view : views) {
1859  printf("OpenEXR-load: Found view %s\n", view.c_str());
1860  }
1861  }
1862  else if (numparts > 1) {
1863  printf("OpenEXR-load: MultiPart file\n");
1864  for (int i = 0; i < numparts; i++) {
1865  if (file.header(i).hasView()) {
1866  printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str());
1867  }
1868  }
1869  }
1870 
1871  for (int j = 0; j < numparts; j++) {
1872  const ChannelList &channels = file.header(j).channels();
1873  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1874  const Channel &channel = i.channel();
1875  printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
1876  }
1877  }
1878 }
1879 
1880 /* For non-multi-layer, map R G B A channel names to something that's in this file. */
1881 static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
1882 {
1883  const ChannelList &channels = file.header(0).channels();
1884 
1885  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1886  // const Channel &channel = i.channel(); /* Not used yet. */
1887  const char *str = i.name();
1888  int len = strlen(str);
1889  if (len) {
1890  if (BLI_strcasecmp(chan, str + len - 1) == 0) {
1891  return str;
1892  }
1893  }
1894  }
1895  return chan;
1896 }
1897 
1898 static int exr_has_rgb(MultiPartInputFile &file, const char *rgb_channels[3])
1899 {
1900  /* Common names for RGB-like channels in order. The V channel name is used by convention for BW
1901  * images, which will be broadcast to RGB channel at the end. */
1902  static const char *channel_names[] = {
1903  "V", "R", "Red", "G", "Green", "B", "Blue", "AR", "RA", "AG", "GA", "AB", "BA", nullptr};
1904 
1905  const Header &header = file.header(0);
1906  int num_channels = 0;
1907 
1908  for (int i = 0; channel_names[i]; i++) {
1909  /* Also try to match lower case variant of of the channel names. */
1910  std::string lower_case_name = std::string(channel_names[i]);
1911  std::transform(lower_case_name.begin(),
1912  lower_case_name.end(),
1913  lower_case_name.begin(),
1914  [](unsigned char c) { return std::tolower(c); });
1915 
1916  if (header.channels().findChannel(channel_names[i]) ||
1917  header.channels().findChannel(lower_case_name)) {
1918  rgb_channels[num_channels++] = channel_names[i];
1919  if (num_channels == 3) {
1920  break;
1921  }
1922  }
1923  }
1924 
1925  return num_channels;
1926 }
1927 
1928 static bool exr_has_luma(MultiPartInputFile &file)
1929 {
1930  /* Y channel is the luma and should always present fir luma space images,
1931  * optionally it could be also channels for chromas called BY and RY.
1932  */
1933  const Header &header = file.header(0);
1934  return header.channels().findChannel("Y") != nullptr;
1935 }
1936 
1937 static bool exr_has_chroma(MultiPartInputFile &file)
1938 {
1939  const Header &header = file.header(0);
1940  return header.channels().findChannel("BY") != nullptr &&
1941  header.channels().findChannel("RY") != nullptr;
1942 }
1943 
1944 static bool exr_has_zbuffer(MultiPartInputFile &file)
1945 {
1946  const Header &header = file.header(0);
1947  return !(header.channels().findChannel("Z") == nullptr);
1948 }
1949 
1950 static bool exr_has_alpha(MultiPartInputFile &file)
1951 {
1952  const Header &header = file.header(0);
1953  return !(header.channels().findChannel("A") == nullptr);
1954 }
1955 
1956 static bool exr_has_xyz(MultiPartInputFile &file)
1957 {
1958  const Header &header = file.header(0);
1959  return (header.channels().findChannel("X") != nullptr ||
1960  header.channels().findChannel("x") != nullptr) &&
1961  (header.channels().findChannel("Y") != nullptr ||
1962  header.channels().findChannel("y") != nullptr) &&
1963  (header.channels().findChannel("Z") != nullptr ||
1964  header.channels().findChannel("z") != nullptr);
1965 }
1966 
1967 static bool exr_is_half_float(MultiPartInputFile &file)
1968 {
1969  const ChannelList &channels = file.header(0).channels();
1970  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1971  const Channel &channel = i.channel();
1972  if (channel.type != HALF) {
1973  return false;
1974  }
1975  }
1976  return true;
1977 }
1978 
1979 static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
1980 {
1981  const ChannelList &channels = file.header(0).channels();
1982  std::set<std::string> layerNames;
1983 
1984  /* This will not include empty layer names, so files with just R/G/B/A
1985  * channels without a layer name will be single layer. */
1986  channels.layers(layerNames);
1987 
1988  return (!layerNames.empty());
1989 }
1990 
1991 static void imb_exr_type_by_channels(ChannelList &channels,
1992  StringVector &views,
1993  bool *r_singlelayer,
1994  bool *r_multilayer,
1995  bool *r_multiview)
1996 {
1997  std::set<std::string> layerNames;
1998 
1999  *r_singlelayer = true;
2000  *r_multilayer = *r_multiview = false;
2001 
2002  /* will not include empty layer names */
2003  channels.layers(layerNames);
2004 
2005  if (!views.empty() && !views[0].empty()) {
2006  *r_multiview = true;
2007  }
2008  else {
2009  *r_singlelayer = false;
2010  *r_multilayer = (layerNames.size() > 1);
2011  *r_multiview = false;
2012  return;
2013  }
2014 
2015  if (!layerNames.empty()) {
2016  /* If `layerNames` is not empty, it means at least one layer is non-empty,
2017  * but it also could be layers without names in the file and such case
2018  * shall be considered a multi-layer EXR.
2019  *
2020  * That's what we do here: test whether there are empty layer names together
2021  * with non-empty ones in the file.
2022  */
2023  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) {
2024  for (const std::string &layer_name : layerNames) {
2025  /* see if any layername differs from a viewname */
2026  if (imb_exr_get_multiView_id(views, layer_name) == -1) {
2027  std::string layerName = layer_name;
2028  size_t pos = layerName.rfind('.');
2029 
2030  if (pos == std::string::npos) {
2031  *r_multilayer = true;
2032  *r_singlelayer = false;
2033  return;
2034  }
2035  }
2036  }
2037  }
2038  }
2039  else {
2040  *r_singlelayer = true;
2041  *r_multilayer = false;
2042  }
2043 
2044  BLI_assert(r_singlelayer != r_multilayer);
2045 }
2046 
2047 static bool exr_has_multiview(MultiPartInputFile &file)
2048 {
2049  for (int p = 0; p < file.parts(); p++) {
2050  if (hasMultiView(file.header(p))) {
2051  return true;
2052  }
2053  }
2054 
2055  return false;
2056 }
2057 
2058 static bool exr_has_multipart_file(MultiPartInputFile &file)
2059 {
2060  return file.parts() > 1;
2061 }
2062 
2063 /* it returns true if the file is multilayer or multiview */
2064 static bool imb_exr_is_multi(MultiPartInputFile &file)
2065 {
2066  /* Multipart files are treated as multilayer in blender -
2067  * even if they are single layer openexr with multiview. */
2069  return true;
2070  }
2071 
2072  if (exr_has_multiview(file)) {
2073  return true;
2074  }
2075 
2077  return true;
2078  }
2079 
2080  return false;
2081 }
2082 
2083 bool IMB_exr_has_multilayer(void *handle)
2084 {
2085  ExrHandle *data = (ExrHandle *)handle;
2086  return imb_exr_is_multi(*data->ifile);
2087 }
2088 
2089 struct ImBuf *imb_load_openexr(const unsigned char *mem,
2090  size_t size,
2091  int flags,
2092  char colorspace[IM_MAX_SPACE])
2093 {
2094  struct ImBuf *ibuf = nullptr;
2095  IMemStream *membuf = nullptr;
2096  MultiPartInputFile *file = nullptr;
2097 
2098  if (imb_is_a_openexr(mem, size) == 0) {
2099  return nullptr;
2100  }
2101 
2103 
2104  try {
2105  bool is_multi;
2106 
2107  membuf = new IMemStream((unsigned char *)mem, size);
2108  file = new MultiPartInputFile(*membuf);
2109 
2110  Box2i dw = file->header(0).dataWindow();
2111  const size_t width = dw.max.x - dw.min.x + 1;
2112  const size_t height = dw.max.y - dw.min.y + 1;
2113 
2114  // printf("OpenEXR-load: image data window %d %d %d %d\n",
2115  // dw.min.x, dw.min.y, dw.max.x, dw.max.y);
2116 
2117  if (false) { /* debug */
2119  }
2120 
2121  is_multi = imb_exr_is_multi(*file);
2122 
2123  /* do not make an ibuf when */
2124  if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
2125  printf("Error: can't process EXR multilayer file\n");
2126  }
2127  else {
2128  const int is_alpha = exr_has_alpha(*file);
2129 
2130  ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
2131  ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
2132 
2133  if (hasXDensity(file->header(0))) {
2134  /* Convert inches to meters. */
2135  ibuf->ppm[0] = (double)xDensity(file->header(0)) / 0.0254;
2136  ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio();
2137  }
2138 
2139  ibuf->ftype = IMB_FTYPE_OPENEXR;
2140 
2141  if (!(flags & IB_test)) {
2142 
2143  if (flags & IB_metadata) {
2144  const Header &header = file->header(0);
2145  Header::ConstIterator iter;
2146 
2147  IMB_metadata_ensure(&ibuf->metadata);
2148  for (iter = header.begin(); iter != header.end(); iter++) {
2149  const StringAttribute *attr = file->header(0).findTypedAttribute<StringAttribute>(
2150  iter.name());
2151 
2152  /* not all attributes are string attributes so we might get some NULLs here */
2153  if (attr) {
2154  IMB_metadata_set_field(ibuf->metadata, iter.name(), attr->value().c_str());
2155  ibuf->flags |= IB_metadata;
2156  }
2157  }
2158  }
2159 
2160  /* Only enters with IB_multilayer flag set. */
2161  if (is_multi && ((flags & IB_thumbnail) == 0)) {
2162  /* constructs channels for reading, allocates memory in channels */
2163  ExrHandle *handle = imb_exr_begin_read_mem(*membuf, *file, width, height);
2164  if (handle) {
2165  IMB_exr_read_channels(handle);
2166  ibuf->userdata = handle; /* potential danger, the caller has to check for this! */
2167  }
2168  }
2169  else {
2170  const char *rgb_channels[3];
2171  const int num_rgb_channels = exr_has_rgb(*file, rgb_channels);
2172  const bool has_luma = exr_has_luma(*file);
2173  const bool has_xyz = exr_has_xyz(*file);
2174  FrameBuffer frameBuffer;
2175  float *first;
2176  size_t xstride = sizeof(float[4]);
2177  size_t ystride = -xstride * width;
2178 
2179  imb_addrectfloatImBuf(ibuf, 4);
2180 
2181  /* Inverse correct first pixel for data-window
2182  * coordinates (- dw.min.y because of y flip). */
2183  first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
2184  /* But, since we read y-flipped (negative y stride) we move to last scan-line. */
2185  first += 4 * (height - 1) * width;
2186 
2187  if (num_rgb_channels > 0) {
2188  for (int i = 0; i < num_rgb_channels; i++) {
2189  frameBuffer.insert(exr_rgba_channelname(*file, rgb_channels[i]),
2190  Slice(Imf::FLOAT, (char *)(first + i), xstride, ystride));
2191  }
2192  }
2193  else if (has_xyz) {
2194  frameBuffer.insert(exr_rgba_channelname(*file, "X"),
2195  Slice(Imf::FLOAT, (char *)first, xstride, ystride));
2196  frameBuffer.insert(exr_rgba_channelname(*file, "Y"),
2197  Slice(Imf::FLOAT, (char *)(first + 1), xstride, ystride));
2198  frameBuffer.insert(exr_rgba_channelname(*file, "Z"),
2199  Slice(Imf::FLOAT, (char *)(first + 2), xstride, ystride));
2200  }
2201  else if (has_luma) {
2202  frameBuffer.insert(exr_rgba_channelname(*file, "Y"),
2203  Slice(Imf::FLOAT, (char *)first, xstride, ystride));
2204  frameBuffer.insert(
2205  exr_rgba_channelname(*file, "BY"),
2206  Slice(Imf::FLOAT, (char *)(first + 1), xstride, ystride, 1, 1, 0.5f));
2207  frameBuffer.insert(
2208  exr_rgba_channelname(*file, "RY"),
2209  Slice(Imf::FLOAT, (char *)(first + 2), xstride, ystride, 1, 1, 0.5f));
2210  }
2211 
2212  /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
2213  frameBuffer.insert(exr_rgba_channelname(*file, "A"),
2214  Slice(Imf::FLOAT, (char *)(first + 3), xstride, ystride, 1, 1, 1.0f));
2215 
2216  if (exr_has_zbuffer(*file)) {
2217  float *firstz;
2218 
2219  addzbuffloatImBuf(ibuf);
2220  firstz = ibuf->zbuf_float - (dw.min.x - dw.min.y * width);
2221  firstz += (height - 1) * width;
2222  frameBuffer.insert(
2223  "Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float)));
2224  }
2225 
2226  InputPart in(*file, 0);
2227  in.setFrameBuffer(frameBuffer);
2228  in.readPixels(dw.min.y, dw.max.y);
2229 
2230  /* XXX, ImBuf has no nice way to deal with this.
2231  * ideally IM_rect would be used when the caller wants a rect BUT
2232  * at the moment all functions use IM_rect.
2233  * Disabling this is ok because all functions should check
2234  * if a rect exists and create one on demand.
2235  *
2236  * Disabling this because the sequencer frees immediate. */
2237 #if 0
2238  if (flag & IM_rect) {
2239  IMB_rect_from_float(ibuf);
2240  }
2241 #endif
2242 
2243  if (num_rgb_channels == 0 && has_luma && exr_has_chroma(*file)) {
2244  for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
2245  float *color = ibuf->rect_float + a * 4;
2246  ycc_to_rgb(color[0] * 255.0f,
2247  color[1] * 255.0f,
2248  color[2] * 255.0f,
2249  &color[0],
2250  &color[1],
2251  &color[2],
2253  }
2254  }
2255  else if (!has_xyz && num_rgb_channels <= 1) {
2256  /* Convert 1 to 3 channels. */
2257  for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
2258  float *color = ibuf->rect_float + a * 4;
2259  color[1] = color[0];
2260  color[2] = color[0];
2261  }
2262  }
2263 
2264  /* file is no longer needed */
2265  delete membuf;
2266  delete file;
2267  }
2268  }
2269  else {
2270  delete membuf;
2271  delete file;
2272  }
2273 
2274  if (flags & IB_alphamode_detect) {
2275  ibuf->flags |= IB_alphamode_premul;
2276  }
2277  }
2278  return ibuf;
2279  }
2280  catch (const std::exception &exc) {
2281  std::cerr << exc.what() << std::endl;
2282  if (ibuf) {
2283  IMB_freeImBuf(ibuf);
2284  }
2285  delete file;
2286  delete membuf;
2287 
2288  return nullptr;
2289  }
2290 }
2291 
2292 struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath,
2293  const int UNUSED(flags),
2294  const size_t max_thumb_size,
2295  char colorspace[],
2296  size_t *r_width,
2297  size_t *r_height)
2298 {
2299  IStream *stream = nullptr;
2300  Imf::RgbaInputFile *file = nullptr;
2301 
2302  /* OpenExr uses exceptions for error-handling. */
2303  try {
2304 
2305  /* The memory-mapped stream is faster, but don't use for huge files as it requires contiguous
2306  * address space and we are processing multiple files at once (typically one per processor
2307  * core). The 100 MB limit here is arbitrary, but seems reasonable and conservative. */
2308  if (BLI_file_size(filepath) < 100 * 1024 * 1024) {
2309  stream = new IMMapStream(filepath);
2310  }
2311  else {
2312  stream = new IFileStream(filepath);
2313  }
2314 
2315  /* imb_initopenexr() creates a global pool of worker threads. But we thumbnail multiple images
2316  * at once, and by default each file will attempt to use the entire pool for itself, stalling
2317  * the others. So each thumbnail should use a single thread of the pool. */
2318  file = new RgbaInputFile(*stream, 1);
2319 
2320  if (!file->isComplete()) {
2321  return nullptr;
2322  }
2323 
2324  Imath::Box2i dw = file->dataWindow();
2325  int source_w = dw.max.x - dw.min.x + 1;
2326  int source_h = dw.max.y - dw.min.y + 1;
2327  *r_width = source_w;
2328  *r_height = source_h;
2329 
2330  /* If there is an embedded thumbnail, return that instead of making a new one. */
2331  if (file->header().hasPreviewImage()) {
2332  const Imf::PreviewImage &preview = file->header().previewImage();
2333  ImBuf *ibuf = IMB_allocFromBuffer(
2334  (unsigned int *)preview.pixels(), nullptr, preview.width(), preview.height(), 4);
2335  delete file;
2336  delete stream;
2337  IMB_flipy(ibuf);
2338  return ibuf;
2339  }
2340 
2341  /* Create a new thumbnail. */
2342 
2343  if (colorspace && colorspace[0]) {
2345  }
2346 
2347  float scale_factor = MIN2((float)max_thumb_size / (float)source_w,
2348  (float)max_thumb_size / (float)source_h);
2349  int dest_w = (int)(source_w * scale_factor);
2350  int dest_h = (int)(source_h * scale_factor);
2351 
2352  struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rectfloat);
2353 
2354  /* A single row of source pixels. */
2355  Imf::Array<Imf::Rgba> pixels(source_w);
2356 
2357  /* Loop through destination thumbnail rows. */
2358  for (int h = 0; h < dest_h; h++) {
2359 
2360  /* Load the single source row that corresponds with destination row. */
2361  int source_y = (int)((float)h / scale_factor) + dw.min.y;
2362  file->setFrameBuffer(&pixels[0] - dw.min.x - source_y * source_w, 1, source_w);
2363  file->readPixels(source_y);
2364 
2365  for (int w = 0; w < dest_w; w++) {
2366  /* For each destination pixel find single corresponding source pixel. */
2367  int source_x = (int)(MIN2((w / scale_factor), dw.max.x - 1));
2368  float *dest_px = &ibuf->rect_float[(h * dest_w + w) * 4];
2369  dest_px[0] = pixels[source_x].r;
2370  dest_px[1] = pixels[source_x].g;
2371  dest_px[2] = pixels[source_x].b;
2372  dest_px[3] = pixels[source_x].a;
2373  }
2374  }
2375 
2376  if (file->lineOrder() == INCREASING_Y) {
2377  IMB_flipy(ibuf);
2378  }
2379 
2380  delete file;
2381  delete stream;
2382 
2383  return ibuf;
2384  }
2385 
2386  catch (const std::exception &exc) {
2387  std::cerr << exc.what() << std::endl;
2388  delete file;
2389  delete stream;
2390  return nullptr;
2391  }
2392 
2393  return nullptr;
2394 }
2395 
2397 {
2398  /* In a multithreaded program, staticInitialize() must be called once during startup, before the
2399  * program accesses any other functions or classes in the IlmImf library. */
2400  Imf::staticInitialize();
2401  Imf::setGlobalThreadCount(BLI_system_thread_count());
2402 }
2403 
2405 {
2406  /* Tells OpenEXR to free thread pool, also ensures there is no running tasks. */
2407  Imf::setGlobalThreadCount(0);
2408 }
2409 
2410 } /* export "C" */
typedef float(TangentPoint)[2]
#define IDP_String(prop)
Definition: BKE_idprop.h:271
void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip)
#define BLI_assert(a)
Definition: BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
#define O_BINARY
Definition: BLI_fileops.h:319
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT
Definition: storage.c:178
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:187
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:920
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_rfindstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float clamp_f(float value, float min, float max)
void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace)
Definition: math_color.c:142
float srgb_to_linearrgb(float c)
Definition: math_color.c:403
#define BLI_YCC_ITU_BT709
BLI_mmap_file * BLI_mmap_open(int fd) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_mmap.c:131
void BLI_mmap_free(BLI_mmap_file *file) ATTR_NONNULL(1)
Definition: BLI_mmap.c:210
void * BLI_mmap_get_pointer(BLI_mmap_file *file) ATTR_WARN_UNUSED_RESULT
Definition: BLI_mmap.c:205
#define FILE_MAX
int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:623
char BLI_toupper_ascii(const char c)
Definition: string.c:922
int BLI_strcaseeq(const char *a, const char *b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:533
size_t BLI_str_partition_ex(const char *str, const char *end, const char delim[], const char **sep, const char **suf, bool from_right) ATTR_NONNULL(1
size_t BLI_str_rpartition(const char *str, const char delim[], const char **sep, const char **suf) ATTR_NONNULL()
Definition: string.c:994
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
int BLI_system_thread_count(void)
Definition: threads.cc:281
#define STRPREFIX(a, b)
#define UNUSED(x)
#define ELEM(...)
#define MIN2(a, b)
#define STREQ(a, b)
typedef double(DMatrix)[4][4]
@ IDP_STRING
Definition: DNA_ID.h:136
struct PreviewImage PreviewImage
#define R_IMF_EXR_CODEC_PXR24
#define R_IMF_EXR_CODEC_NONE
#define R_IMF_EXR_CODEC_RLE
#define R_IMF_EXR_CODEC_ZIP
#define R_IMF_EXR_CODEC_DWAA
#define R_IMF_EXR_CODEC_ZIPS
#define R_IMF_EXR_CODEC_DWAB
#define R_IMF_EXR_CODEC_B44A
#define R_IMF_EXR_CODEC_B44
#define R_IMF_EXR_CODEC_PIZ
static AppView * view
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
Header file for allocimbuf.c.
@ COLOR_ROLE_DEFAULT_FLOAT
struct ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
Definition: allocimbuf.c:500
bool imb_addrectfloatImBuf(struct ImBuf *ibuf, const unsigned int channels)
Definition: allocimbuf.c:367
void IMB_rect_from_float(struct ImBuf *ibuf)
Definition: divers.c:696
bool addzbuffloatImBuf(struct ImBuf *ibuf)
Definition: allocimbuf.c:270
#define IM_MAX_SPACE
Definition: IMB_imbuf.h:49
struct ImBuf * IMB_allocFromBuffer(const unsigned int *rect, const float *rectf, unsigned int w, unsigned int h, unsigned int channels)
Definition: allocimbuf.c:447
void IMB_flipy(struct ImBuf *ibuf)
Definition: rotate.c:16
Contains defines and structs used throughout the imbuf module.
#define OPENEXR_HALF
@ IB_halffloat
@ IB_alphamode_premul
@ IB_rectfloat
@ IB_metadata
@ IB_multilayer
@ IB_alphamode_detect
@ IB_zbuffloat
@ IB_thumbnail
@ IB_mem
@ IB_test
#define OPENEXR_COMPRESS
void IMB_metadata_set_field(struct IDProperty *metadata, const char *key, const char *value)
Definition: metadata.c:73
void IMB_metadata_ensure(struct IDProperty **metadata)
Definition: metadata.c:25
#define EXR_LAY_MAXNAME
Definition: IMB_openexr.h:14
#define EXR_VIEW_MAXNAME
Definition: IMB_openexr.h:16
#define EXR_PASS_MAXNAME
Definition: IMB_openexr.h:15
#define EXR_TOT_MAXNAME
Definition: IMB_openexr.h:17
#define EXR_PASS_MAXCHAN
Definition: IMB_openexr.h:18
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
void imb_mmap_lock(void)
Definition: allocimbuf.c:53
void imb_mmap_unlock(void)
Definition: allocimbuf.c:58
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
Definition: allocimbuf.c:310
bool imb_addencodedbufferImBuf(ImBuf *ibuf)
Definition: allocimbuf.c:287
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
void clear() override
void seekg(exr_file_offset_t pos) override
bool read(char c[], int n) override
exr_file_offset_t tellg() override
IFileStream(const char *filepath)
~IMMapStream() override
void seekg(exr_file_offset_t pos) override
IMMapStream(const char *filepath)
exr_file_offset_t tellg() override
bool read(char c[], int n) override
void seekg(exr_file_offset_t pos) override
IMemStream(unsigned char *exrbuf, size_t exrsize)
bool read(char c[], int n) override
void clear() override
exr_file_offset_t tellg() override
OFileStream(const char *filepath)
exr_file_offset_t tellp() override
void seekp(exr_file_offset_t pos) override
void write(const char c[], int n) override
void write(const char c[], int n) override
void seekp(exr_file_offset_t pos) override
OMemStream(ImBuf *ibuf_)
exr_file_offset_t tellp() override
void colorspace_set_default_role(char *colorspace, int size, int role)
unsigned short half
Definition: cuda/compat.h:110
@ HALF
Definition: curve_bevel.c:29
StackEntry * from
FILE * file
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
#define str(s)
uint pos
void IMB_freeImBuf(ImBuf *UNUSED(ibuf))
@ IMB_FTYPE_OPENEXR
int count
ccl_gpu_kernel_postfix ccl_global float int num_pixels
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static bool parse_channels(const ImageSpec &in_spec, vector< MergeImageLayer > &layers, string &error)
Definition: merge.cpp:134
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static const pxr::TfToken preview("preview", pxr::TfToken::Immortal)
bool IMB_exr_begin_write(void *handle, const char *filepath, int width, int height, int compress, const StampData *stamp)
static const char * exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
static bool exr_has_multiview(MultiPartInputFile &file)
static void exr_printf(const char *__restrict fmt,...)
void IMB_exr_add_view(void *handle, const char *name)
static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname, bool has_xyz_channels)
static bool exr_has_xyz(MultiPartInputFile &file)
static void openexr_header_compression(Header *header, int compression)
static int imb_exr_get_multiView_id(StringVector &views, const std::string &name)
bool IMB_exr_has_multilayer(void *handle)
static ExrLayer * imb_exr_get_layer(ListBase *lb, char *layname)
struct ImBuf * imb_load_openexr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
void IMB_exr_close(void *handle)
void IMB_exrtile_begin_write(void *handle, const char *filepath, int mipmap, int width, int height, int tilex, int tiley)
void IMB_exr_clear_channels(void *handle)
static bool exr_is_half_float(MultiPartInputFile &file)
static void imb_exr_pass_name_from_channel(char *passname, const ExrChannel *echan, const char *channelname, const bool has_xyz_channels)
static bool imb_exr_is_multi(MultiPartInputFile &file)
static void openexr_header_metadata_callback(void *data, const char *propname, char *prop, int UNUSED(len))
static bool exr_has_luma(MultiPartInputFile &file)
_RGBAZ RGBAZ
static int exr_has_rgb(MultiPartInputFile &file, const char *rgb_channels[3])
void imb_initopenexr(void)
static struct ExrPass * imb_exr_get_pass(ListBase *lb, char *passname)
bool IMB_exr_begin_read(void *handle, const char *filepath, int *width, int *height, const bool parse_channels)
static int imb_exr_split_token(const char *str, const char *end, const char **token)
bool imb_is_a_openexr(const unsigned char *mem, const size_t size)
void * IMB_exr_get_handle_name(const char *name)
struct ImBuf * imb_load_filepath_thumbnail_openexr(const char *filepath, const int UNUSED(flags), const size_t max_thumb_size, char colorspace[], size_t *r_width, size_t *r_height)
#define exr_file_offset_t
Definition: openexr_api.cpp:36
void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect, bool use_half_float)
void IMB_exr_write_channels(void *handle)
static bool exr_has_chroma(MultiPartInputFile &file)
static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
void IMB_exr_read_channels(void *handle)
static half float_to_half_safe(const float value)
static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname, bool empty)
static void imb_exr_insert_view_name(char *name_full, const char *passname, const char *viewname)
static ListBase exrhandles
void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
void * IMB_exr_get_handle(void)
static bool exr_has_multipart_file(MultiPartInputFile &file)
static void exr_print_filecontents(MultiPartInputFile &file)
static void imb_exr_type_by_channels(ChannelList &channels, StringVector &views, bool *r_singlelayer, bool *r_multilayer, bool *r_multiview)
static bool exr_has_xyz_channels(ExrHandle *exr_handle)
static void imb_exr_get_views(MultiPartInputFile &file, StringVector &views)
static std::vector< MultiViewChannelName > exr_channels_in_multi_part_file(const MultiPartInputFile &file)
void IMB_exr_multilayer_convert(void *handle, void *base, void *(*addview)(void *base, const char *str), void *(*addlayer)(void *base, const char *str), void(*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view))
static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags)
float * IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *viewname)
static bool exr_has_zbuffer(MultiPartInputFile &file)
static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags)
static ExrHandle * imb_exr_begin_read_mem(IStream &file_stream, MultiPartInputFile &file, int width, int height)
bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
static bool exr_has_alpha(MultiPartInputFile &file)
void imb_exitopenexr(void)
@ FLOAT
#define min(a, b)
Definition: sort.c:35
bool use_half_float
struct ExrChannel * next
char name[EXR_TOT_MAXNAME+1]
struct MultiViewChannelName * m
float * rect
OutputFile * ofile
int num_half_channels
IStream * ifile_stream
OFileStream * ofile_stream
struct ExrHandle * next
StringVector * multiView
ListBase channels
MultiPartInputFile * ifile
ListBase layers
MultiPartOutputFile * mpofile
char name[FILE_MAX]
struct ExrLayer * next
ListBase passes
char name[EXR_LAY_MAXNAME+1]
struct ExrPass * next
char chan_id[EXR_PASS_MAXCHAN]
float * rect
char view[EXR_VIEW_MAXNAME]
char name[EXR_PASS_MAXNAME]
struct ExrChannel * chan[EXR_PASS_MAXCHAN]
char internal_name[EXR_PASS_MAXNAME]
ListBase group
Definition: DNA_ID.h:101
struct IDProperty * next
Definition: DNA_ID.h:107
char name[64]
Definition: DNA_ID.h:111
IDPropertyData data
Definition: DNA_ID.h:117
char type
Definition: DNA_ID.h:108
struct IDProperty * metadata
void * userdata
float * zbuf_float
int channels
ImbFormatOptions foptions
unsigned char planes
enum eImbFileType ftype
unsigned int * rect
unsigned int encodedsize
float * rect_float
double ppm[2]
void * first
Definition: DNA_listBase.h:31
float max
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition: utfconv.c:291