Blender  V2.93
openexr_api.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright by Gernot Ziegler <gz@lysator.liu.se>.
17  * All rights reserved.
18  */
19 
24 #include <algorithm>
25 #include <cerrno>
26 #include <cstddef>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <fstream>
30 #include <iostream>
31 #include <set>
32 #include <stdexcept>
33 #include <string>
34 
35 #include <Iex.h>
36 #include <ImathBox.h>
37 #include <ImfArray.h>
38 #include <ImfChannelList.h>
39 #include <ImfCompression.h>
40 #include <ImfCompressionAttribute.h>
41 #include <ImfIO.h>
42 #include <ImfInputFile.h>
43 #include <ImfOutputFile.h>
44 #include <ImfPixelType.h>
45 #include <ImfStandardAttributes.h>
46 #include <ImfStringAttribute.h>
47 #include <ImfVersion.h>
48 #include <half.h>
49 
50 /* multiview/multipart */
51 #include <ImfInputPart.h>
52 #include <ImfMultiPartInputFile.h>
53 #include <ImfMultiPartOutputFile.h>
54 #include <ImfMultiView.h>
55 #include <ImfOutputPart.h>
56 #include <ImfPartHelper.h>
57 #include <ImfPartType.h>
58 #include <ImfTiledOutputPart.h>
59 
60 #include "DNA_scene_types.h" /* For OpenEXR compression constants */
61 
62 #include <openexr_api.h>
63 
64 #if defined(WIN32)
65 # include "utfconv.h"
66 #endif
67 
68 #include "MEM_guardedalloc.h"
69 
70 extern "C" {
71 
72 // The following prevents a linking error in debug mode for MSVC using the libs in CVS
73 #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(DEBUG) && _MSC_VER < 1900
74 _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
75 {
76 }
77 #endif
78 }
79 #include "BLI_blenlib.h"
80 #include "BLI_math_color.h"
81 #include "BLI_threads.h"
82 
83 #include "BKE_idprop.h"
84 #include "BKE_image.h"
85 
86 #include "IMB_allocimbuf.h"
87 #include "IMB_colormanagement.h"
89 #include "IMB_imbuf.h"
90 #include "IMB_imbuf_types.h"
91 #include "IMB_metadata.h"
92 
93 #include "openexr_multi.h"
94 
95 using namespace Imf;
96 using namespace Imath;
97 
98 extern "C" {
99 /* prototype */
100 static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname);
101 static bool exr_has_multiview(MultiPartInputFile &file);
102 static bool exr_has_multipart_file(MultiPartInputFile &file);
103 static bool exr_has_alpha(MultiPartInputFile &file);
104 static bool exr_has_zbuffer(MultiPartInputFile &file);
105 static void exr_printf(const char *__restrict fmt, ...);
106 static void imb_exr_type_by_channels(ChannelList &channels,
107  StringVector &views,
108  bool *r_singlelayer,
109  bool *r_multilayer,
110  bool *r_multiview);
111 }
112 
113 /* Memory Input Stream */
114 
115 class IMemStream : public Imf::IStream {
116  public:
117  IMemStream(unsigned char *exrbuf, size_t exrsize)
118  : IStream("<memory>"), _exrpos(0), _exrsize(exrsize)
119  {
120  _exrbuf = exrbuf;
121  }
122 
123  bool read(char c[], int n) override
124  {
125  if (n + _exrpos <= _exrsize) {
126  memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
127  _exrpos += n;
128  return true;
129  }
130 
131  return false;
132  }
133 
134  Int64 tellg() override
135  {
136  return _exrpos;
137  }
138 
139  void seekg(Int64 pos) override
140  {
141  _exrpos = pos;
142  }
143 
144  void clear() override
145  {
146  }
147 
148  private:
149  Int64 _exrpos;
150  Int64 _exrsize;
151  unsigned char *_exrbuf;
152 };
153 
154 /* File Input Stream */
155 
156 class IFileStream : public Imf::IStream {
157  public:
158  IFileStream(const char *filename) : IStream(filename)
159  {
160  /* utf-8 file path support on windows */
161 #if defined(WIN32)
162  wchar_t *wfilename = alloc_utf16_from_8(filename, 0);
163  ifs.open(wfilename, std::ios_base::binary);
164  free(wfilename);
165 #else
166  ifs.open(filename, std::ios_base::binary);
167 #endif
168 
169  if (!ifs) {
170  Iex::throwErrnoExc();
171  }
172  }
173 
174  bool read(char c[], int n) override
175  {
176  if (!ifs) {
177  throw Iex::InputExc("Unexpected end of file.");
178  }
179 
180  errno = 0;
181  ifs.read(c, n);
182  return check_error();
183  }
184 
185  Int64 tellg() override
186  {
187  return std::streamoff(ifs.tellg());
188  }
189 
190  void seekg(Int64 pos) override
191  {
192  ifs.seekg(pos);
193  check_error();
194  }
195 
196  void clear() override
197  {
198  ifs.clear();
199  }
200 
201  private:
202  bool check_error()
203  {
204  if (!ifs) {
205  if (errno) {
206  Iex::throwErrnoExc();
207  }
208 
209  return false;
210  }
211 
212  return true;
213  }
214 
215  std::ifstream ifs;
216 };
217 
218 /* Memory Output Stream */
219 
220 class OMemStream : public OStream {
221  public:
222  OMemStream(ImBuf *ibuf_) : OStream("<memory>"), ibuf(ibuf_), offset(0)
223  {
224  }
225 
226  void write(const char c[], int n) override
227  {
228  ensure_size(offset + n);
229  memcpy(ibuf->encodedbuffer + offset, c, n);
230  offset += n;
231  ibuf->encodedsize += n;
232  }
233 
234  Int64 tellp() override
235  {
236  return offset;
237  }
238 
239  void seekp(Int64 pos) override
240  {
241  offset = pos;
242  ensure_size(offset);
243  }
244 
245  private:
246  void ensure_size(Int64 size)
247  {
248  /* if buffer is too small increase it. */
249  while (size > ibuf->encodedbuffersize) {
250  if (!imb_enlargeencodedbufferImBuf(ibuf)) {
251  throw Iex::ErrnoExc("Out of memory.");
252  }
253  }
254  }
255 
256  ImBuf *ibuf;
257  Int64 offset;
258 };
259 
260 /* File Output Stream */
261 
262 class OFileStream : public OStream {
263  public:
264  OFileStream(const char *filename) : OStream(filename)
265  {
266  /* utf-8 file path support on windows */
267 #if defined(WIN32)
268  wchar_t *wfilename = alloc_utf16_from_8(filename, 0);
269  ofs.open(wfilename, std::ios_base::binary);
270  free(wfilename);
271 #else
272  ofs.open(filename, std::ios_base::binary);
273 #endif
274 
275  if (!ofs) {
276  Iex::throwErrnoExc();
277  }
278  }
279 
280  void write(const char c[], int n) override
281  {
282  errno = 0;
283  ofs.write(c, n);
284  check_error();
285  }
286 
287  Int64 tellp() override
288  {
289  return std::streamoff(ofs.tellp());
290  }
291 
292  void seekp(Int64 pos) override
293  {
294  ofs.seekp(pos);
295  check_error();
296  }
297 
298  private:
299  void check_error()
300  {
301  if (!ofs) {
302  if (errno) {
303  Iex::throwErrnoExc();
304  }
305 
306  throw Iex::ErrnoExc("File output failed.");
307  }
308  }
309 
310  std::ofstream ofs;
311 };
312 
313 struct _RGBAZ {
319 };
320 
321 using RGBAZ = _RGBAZ;
322 
323 static half float_to_half_safe(const float value)
324 {
325  return half(clamp_f(value, -HALF_MAX, HALF_MAX));
326 }
327 
328 extern "C" {
329 
334 bool imb_is_a_openexr(const unsigned char *mem, const size_t size)
335 {
336  /* No define is exposed for this size. */
337  if (size < 4) {
338  return false;
339  }
340  return Imf::isImfMagic((const char *)mem);
341 }
342 
343 static void openexr_header_compression(Header *header, int compression)
344 {
345  switch (compression) {
347  header->compression() = NO_COMPRESSION;
348  break;
350  header->compression() = PXR24_COMPRESSION;
351  break;
352  case R_IMF_EXR_CODEC_ZIP:
353  header->compression() = ZIP_COMPRESSION;
354  break;
355  case R_IMF_EXR_CODEC_PIZ:
356  header->compression() = PIZ_COMPRESSION;
357  break;
358  case R_IMF_EXR_CODEC_RLE:
359  header->compression() = RLE_COMPRESSION;
360  break;
362  header->compression() = ZIPS_COMPRESSION;
363  break;
364  case R_IMF_EXR_CODEC_B44:
365  header->compression() = B44_COMPRESSION;
366  break;
368  header->compression() = B44A_COMPRESSION;
369  break;
370 #if OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2
372  header->compression() = DWAA_COMPRESSION;
373  break;
375  header->compression() = DWAB_COMPRESSION;
376  break;
377 #endif
378  default:
379  header->compression() = ZIP_COMPRESSION;
380  break;
381  }
382 }
383 
384 static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
385 {
386  if (ibuf->metadata) {
387  IDProperty *prop;
388 
389  for (prop = (IDProperty *)ibuf->metadata->data.group.first; prop; prop = prop->next) {
390  if (prop->type == IDP_STRING) {
391  header->insert(prop->name, StringAttribute(IDP_String(prop)));
392  }
393  }
394  }
395 
396  if (ibuf->ppm[0] > 0.0) {
397  /* Convert meters to inches. */
398  addXDensity(*header, ibuf->ppm[0] * 0.0254);
399  }
400 }
401 
403  const char *propname,
404  char *prop,
405  int UNUSED(len))
406 {
407  Header *header = (Header *)data;
408  header->insert(propname, StringAttribute(prop));
409 }
410 
411 static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags)
412 {
413  const int channels = ibuf->channels;
414  const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
415  const bool is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != nullptr; /* summarize */
416  const int width = ibuf->x;
417  const int height = ibuf->y;
418  OStream *file_stream = nullptr;
419 
420  try {
421  Header header(width, height);
422 
424  openexr_header_metadata(&header, ibuf);
425 
426  /* create channels */
427  header.channels().insert("R", Channel(HALF));
428  header.channels().insert("G", Channel(HALF));
429  header.channels().insert("B", Channel(HALF));
430  if (is_alpha) {
431  header.channels().insert("A", Channel(HALF));
432  }
433  if (is_zbuf) {
434  /* z we do as float always */
435  header.channels().insert("Z", Channel(Imf::FLOAT));
436  }
437 
438  FrameBuffer frameBuffer;
439 
440  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
441  if (flags & IB_mem) {
442  file_stream = new OMemStream(ibuf);
443  }
444  else {
445  file_stream = new OFileStream(name);
446  }
447  OutputFile file(*file_stream, header);
448 
449  /* we store first everything in half array */
450  std::vector<RGBAZ> pixels(height * width);
451  RGBAZ *to = &pixels[0];
452  int xstride = sizeof(RGBAZ);
453  int ystride = xstride * width;
454 
455  /* indicate used buffers */
456  frameBuffer.insert("R", Slice(HALF, (char *)&to->r, xstride, ystride));
457  frameBuffer.insert("G", Slice(HALF, (char *)&to->g, xstride, ystride));
458  frameBuffer.insert("B", Slice(HALF, (char *)&to->b, xstride, ystride));
459  if (is_alpha) {
460  frameBuffer.insert("A", Slice(HALF, (char *)&to->a, xstride, ystride));
461  }
462  if (is_zbuf) {
463  frameBuffer.insert("Z",
464  Slice(Imf::FLOAT,
465  (char *)(ibuf->zbuf_float + (height - 1) * width),
466  sizeof(float),
467  sizeof(float) * -width));
468  }
469  if (ibuf->rect_float) {
470  float *from;
471 
472  for (int i = ibuf->y - 1; i >= 0; i--) {
473  from = ibuf->rect_float + channels * i * width;
474 
475  for (int j = ibuf->x; j > 0; j--) {
476  to->r = float_to_half_safe(from[0]);
477  to->g = float_to_half_safe((channels >= 2) ? from[1] : from[0]);
478  to->b = float_to_half_safe((channels >= 3) ? from[2] : from[0]);
479  to->a = float_to_half_safe((channels >= 4) ? from[3] : 1.0f);
480  to++;
481  from += channels;
482  }
483  }
484  }
485  else {
486  unsigned char *from;
487 
488  for (int i = ibuf->y - 1; i >= 0; i--) {
489  from = (unsigned char *)ibuf->rect + 4 * i * width;
490 
491  for (int j = ibuf->x; j > 0; j--) {
492  to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
493  to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
494  to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
495  to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
496  to++;
497  from += 4;
498  }
499  }
500  }
501 
502  exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
503 
504  file.setFrameBuffer(frameBuffer);
505  file.writePixels(height);
506  }
507  catch (const std::exception &exc) {
508  delete file_stream;
509  printf("OpenEXR-save: ERROR: %s\n", exc.what());
510 
511  return false;
512  }
513 
514  delete file_stream;
515  return true;
516 }
517 
518 static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags)
519 {
520  const int channels = ibuf->channels;
521  const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
522  const bool is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != nullptr; /* summarize */
523  const int width = ibuf->x;
524  const int height = ibuf->y;
525  OStream *file_stream = nullptr;
526 
527  try {
528  Header header(width, height);
529 
531  openexr_header_metadata(&header, ibuf);
532 
533  /* create channels */
534  header.channels().insert("R", Channel(Imf::FLOAT));
535  header.channels().insert("G", Channel(Imf::FLOAT));
536  header.channels().insert("B", Channel(Imf::FLOAT));
537  if (is_alpha) {
538  header.channels().insert("A", Channel(Imf::FLOAT));
539  }
540  if (is_zbuf) {
541  header.channels().insert("Z", Channel(Imf::FLOAT));
542  }
543 
544  FrameBuffer frameBuffer;
545 
546  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
547  if (flags & IB_mem) {
548  file_stream = new OMemStream(ibuf);
549  }
550  else {
551  file_stream = new OFileStream(name);
552  }
553  OutputFile file(*file_stream, header);
554 
555  int xstride = sizeof(float) * channels;
556  int ystride = -xstride * width;
557 
558  /* last scanline, stride negative */
559  float *rect[4] = {nullptr, nullptr, nullptr, nullptr};
560  rect[0] = ibuf->rect_float + channels * (height - 1) * width;
561  rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
562  rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
563  rect[3] = (channels >= 4) ?
564  rect[0] + 3 :
565  rect[0]; /* red as alpha, is this needed since alpha isn't written? */
566 
567  frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride));
568  frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride));
569  frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride));
570  if (is_alpha) {
571  frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride));
572  }
573  if (is_zbuf) {
574  frameBuffer.insert("Z",
575  Slice(Imf::FLOAT,
576  (char *)(ibuf->zbuf_float + (height - 1) * width),
577  sizeof(float),
578  sizeof(float) * -width));
579  }
580 
581  file.setFrameBuffer(frameBuffer);
582  file.writePixels(height);
583  }
584  catch (const std::exception &exc) {
585  printf("OpenEXR-save: ERROR: %s\n", exc.what());
586  delete file_stream;
587  return false;
588  }
589 
590  delete file_stream;
591  return true;
592 }
593 
594 bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
595 {
596  if (flags & IB_mem) {
598  ibuf->encodedsize = 0;
599  }
600 
601  if (ibuf->foptions.flag & OPENEXR_HALF) {
602  return imb_save_openexr_half(ibuf, name, flags);
603  }
604 
605  /* when no float rect, we save as half (16 bits is sufficient) */
606  if (ibuf->rect_float == nullptr) {
607  return imb_save_openexr_half(ibuf, name, flags);
608  }
609 
610  return imb_save_openexr_float(ibuf, name, flags);
611 }
612 
613 /* ******* Nicer API, MultiLayer and with Tile file support ************************************ */
614 
615 /* naming rules:
616  * - parse name from right to left
617  * - last character is channel ID, 1 char like 'A' 'R' 'G' 'B' 'X' 'Y' 'Z' 'W' 'U' 'V'
618  * - separated with a dot; the Pass name (like "Depth", "Color", "Diffuse" or "Combined")
619  * - separated with a dot: the Layer name (like "Light1" or "Walls" or "Characters")
620  */
621 
622 static ListBase exrhandles = {nullptr, nullptr};
623 
624 struct ExrHandle {
625  struct ExrHandle *next, *prev;
626  char name[FILE_MAX];
627 
628  IStream *ifile_stream;
629  MultiPartInputFile *ifile;
630 
632  MultiPartOutputFile *mpofile;
633  OutputFile *ofile;
634 
635  int tilex, tiley;
636  int width, height;
637  int mipmap;
638 
641  StringVector *multiView;
642 
643  int parts;
644 
645  ListBase channels; /* flattened out, ExrChannel */
646  ListBase layers; /* hierarchical, pointing in end to ExrChannel */
647 
648  int num_half_channels; /* used during filr save, allows faster temporary buffers allocation */
649 };
650 
651 /* flattened out channel */
652 struct ExrChannel {
653  struct ExrChannel *next, *prev;
654 
655  char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */
656  struct MultiViewChannelName *m; /* struct to store all multipart channel info */
657  int xstride, ystride; /* step to next pixel, to next scanline */
658  float *rect; /* first pointer to write in */
659  char chan_id; /* quick lookup of channel char */
660  int view_id; /* quick lookup of channel view */
661  bool use_half_float; /* when saving use half float for file storage */
662 };
663 
664 /* hierarchical; layers -> passes -> channels[] */
665 struct ExrPass {
666  struct ExrPass *next, *prev;
668  int totchan;
669  float *rect;
672 
673  char internal_name[EXR_PASS_MAXNAME]; /* name with no view */
675  int view_id;
676 };
677 
678 struct ExrLayer {
679  struct ExrLayer *next, *prev;
682 };
683 
684 /* ********************** */
685 
687 {
688  ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle");
689  data->multiView = new StringVector();
690 
692  return data;
693 }
694 
695 void *IMB_exr_get_handle_name(const char *name)
696 {
698 
699  if (data == nullptr) {
701  BLI_strncpy(data->name, name, strlen(name) + 1);
702  }
703  return data;
704 }
705 
706 /* multiview functions */
707 } /* extern "C" */
708 
709 extern "C" {
710 
711 void IMB_exr_add_view(void *handle, const char *name)
712 {
713  ExrHandle *data = (ExrHandle *)handle;
714  data->multiView->push_back(name);
715 }
716 
717 static int imb_exr_get_multiView_id(StringVector &views, const std::string &name)
718 {
719  int count = 0;
720  for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) {
721  if (name == *i) {
722  return count;
723  }
724 
725  count++;
726  }
727 
728  /* no views or wrong name */
729  return -1;
730 }
731 
732 static void imb_exr_get_views(MultiPartInputFile &file, StringVector &views)
733 {
734  if (exr_has_multipart_file(file) == false) {
735  if (exr_has_multiview(file)) {
736  StringVector sv = multiView(file.header(0));
737  for (const std::string &view_name : sv) {
738  views.push_back(view_name);
739  }
740  }
741  }
742 
743  else {
744  for (int p = 0; p < file.parts(); p++) {
745  std::string view;
746  if (file.header(p).hasView()) {
747  view = file.header(p).view();
748  }
749 
750  if (imb_exr_get_multiView_id(views, view) == -1) {
751  views.push_back(view);
752  }
753  }
754  }
755 }
756 
757 /* Multilayer Blender files have the view name in all the passes (even the default view one) */
758 static void imb_exr_insert_view_name(char *name_full, const char *passname, const char *viewname)
759 {
760  BLI_assert(!ELEM(name_full, passname, viewname));
761 
762  if (viewname == nullptr || viewname[0] == '\0') {
763  BLI_strncpy(name_full, passname, sizeof(((ExrChannel *)nullptr)->name));
764  return;
765  }
766 
767  const char delims[] = {'.', '\0'};
768  const char *sep;
769  const char *token;
770  size_t len;
771 
772  len = BLI_str_rpartition(passname, delims, &sep, &token);
773 
774  if (sep) {
775  BLI_snprintf(name_full, EXR_PASS_MAXNAME, "%.*s.%s.%s", (int)len, passname, viewname, token);
776  }
777  else {
778  BLI_snprintf(name_full, EXR_PASS_MAXNAME, "%s.%s", passname, viewname);
779  }
780 }
781 
782 /* adds flattened ExrChannels */
783 /* xstride, ystride and rect can be done in set_channel too, for tile writing */
784 /* passname does not include view */
785 void IMB_exr_add_channel(void *handle,
786  const char *layname,
787  const char *passname,
788  const char *viewname,
789  int xstride,
790  int ystride,
791  float *rect,
792  bool use_half_float)
793 {
794  ExrHandle *data = (ExrHandle *)handle;
795  ExrChannel *echan;
796 
797  echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel");
798  echan->m = new MultiViewChannelName();
799 
800  if (layname && layname[0] != '\0') {
801  echan->m->name = layname;
802  echan->m->name.append(".");
803  echan->m->name.append(passname);
804  }
805  else {
806  echan->m->name.assign(passname);
807  }
808 
809  echan->m->internal_name = echan->m->name;
810 
811  echan->m->view.assign(viewname ? viewname : "");
812 
813  /* quick look up */
814  echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view));
815 
816  /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
817  if (layname && layname[0] != '\0') {
818  imb_exr_insert_view_name(echan->name, echan->m->name.c_str(), echan->m->view.c_str());
819  }
820  else if (!data->multiView->empty()) {
821  std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id);
822  BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name));
823  }
824  else {
825  BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name));
826  }
827 
828  echan->xstride = xstride;
829  echan->ystride = ystride;
830  echan->rect = rect;
831  echan->use_half_float = use_half_float;
832 
833  if (echan->use_half_float) {
834  data->num_half_channels++;
835  }
836 
837  exr_printf("added channel %s\n", echan->name);
838  BLI_addtail(&data->channels, echan);
839 }
840 
841 /* used for output files (from RenderResult) (single and multilayer, single and multiview) */
842 int IMB_exr_begin_write(void *handle,
843  const char *filename,
844  int width,
845  int height,
846  int compress,
847  const StampData *stamp)
848 {
849  ExrHandle *data = (ExrHandle *)handle;
850  Header header(width, height);
851  ExrChannel *echan;
852 
853  data->width = width;
854  data->height = height;
855 
856  bool is_singlelayer, is_multilayer, is_multiview;
857 
858  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
859  header.channels().insert(echan->name, Channel(echan->use_half_float ? Imf::HALF : Imf::FLOAT));
860  }
861 
862  openexr_header_compression(&header, compress);
864  &header, const_cast<StampData *>(stamp), openexr_header_metadata_callback, false);
865  /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
866 
868  header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview);
869 
870  if (is_multilayer) {
871  header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
872  }
873 
874  if (is_multiview) {
875  addMultiView(header, *data->multiView);
876  }
877 
878  /* avoid crash/abort when we don't have permission to write here */
879  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
880  try {
881  data->ofile_stream = new OFileStream(filename);
882  data->ofile = new OutputFile(*(data->ofile_stream), header);
883  }
884  catch (const std::exception &exc) {
885  std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
886 
887  delete data->ofile;
888  delete data->ofile_stream;
889 
890  data->ofile = nullptr;
891  data->ofile_stream = nullptr;
892  }
893 
894  return (data->ofile != nullptr);
895 }
896 
897 /* only used for writing temp. render results (not image files)
898  * (FSA and Save Buffers) */
900  void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
901 {
902  ExrHandle *data = (ExrHandle *)handle;
903  Header header(width, height);
904  std::vector<Header> headers;
905  ExrChannel *echan;
906 
907  data->tilex = tilex;
908  data->tiley = tiley;
909  data->width = width;
910  data->height = height;
911  data->mipmap = mipmap;
912 
913  header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL));
914  header.compression() = RLE_COMPRESSION;
915  header.setType(TILEDIMAGE);
916 
917  header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43"));
918 
919  int numparts = data->multiView->size();
920 
921  /* copy header from all parts of input to our header array
922  * those temporary files have one part per view */
923  for (int i = 0; i < numparts; i++) {
924  headers.push_back(header);
925  headers[headers.size() - 1].setView((*(data->multiView))[i]);
926  headers[headers.size() - 1].setName((*(data->multiView))[i]);
927  }
928 
929  exr_printf("\nIMB_exrtile_begin_write\n");
930  exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
931  exr_printf("---------------------------------------------------------------\n");
932 
933  /* assign channels */
934  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
935  /* Tiles are expected to be saved with full float currently. */
936  BLI_assert(echan->use_half_float == 0);
937 
938  echan->m->internal_name = echan->m->name;
939  echan->m->part_number = echan->view_id;
940 
941  headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT));
942  exr_printf("%d %-6s %-22s \"%s\"\n",
943  echan->m->part_number,
944  echan->m->view.c_str(),
945  echan->m->name.c_str(),
946  echan->m->internal_name.c_str());
947  }
948 
949  /* avoid crash/abort when we don't have permission to write here */
950  /* manually create ofstream, so we can handle utf-8 filepaths on windows */
951  try {
952  data->ofile_stream = new OFileStream(filename);
953  data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size());
954  }
955  catch (const std::exception &) {
956  delete data->mpofile;
957  delete data->ofile_stream;
958 
959  data->mpofile = nullptr;
960  data->ofile_stream = nullptr;
961  }
962 }
963 
964 /* read from file */
965 int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
966 {
967  ExrHandle *data = (ExrHandle *)handle;
968  ExrChannel *echan;
969 
970  /* 32 is arbitrary, but zero length files crashes exr. */
971  if (BLI_exists(filename) && BLI_file_size(filename) > 32) {
972  /* avoid crash/abort when we don't have permission to write here */
973  try {
974  data->ifile_stream = new IFileStream(filename);
975  data->ifile = new MultiPartInputFile(*(data->ifile_stream));
976  }
977  catch (const std::exception &) {
978  delete data->ifile;
979  delete data->ifile_stream;
980 
981  data->ifile = nullptr;
982  data->ifile_stream = nullptr;
983  }
984 
985  if (data->ifile) {
986  Box2i dw = data->ifile->header(0).dataWindow();
987  data->width = *width = dw.max.x - dw.min.x + 1;
988  data->height = *height = dw.max.y - dw.min.y + 1;
989 
990  imb_exr_get_views(*data->ifile, *data->multiView);
991 
992  std::vector<MultiViewChannelName> channels;
993  GetChannelsInMultiPartFile(*data->ifile, channels);
994 
995  for (const MultiViewChannelName &channel : channels) {
997  data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
998 
999  echan = (ExrChannel *)data->channels.last;
1000  echan->m->name = channel.name;
1001  echan->m->view = channel.view;
1002  echan->m->part_number = channel.part_number;
1003  echan->m->internal_name = channel.internal_name;
1004  }
1005 
1006  return 1;
1007  }
1008  }
1009  return 0;
1010 }
1011 
1012 /* still clumsy name handling, layers/channels can be ordered as list in list later */
1013 /* passname here is the raw channel name without the layer */
1015  void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
1016 {
1017  ExrHandle *data = (ExrHandle *)handle;
1018  ExrChannel *echan;
1019  char name[EXR_TOT_MAXNAME + 1];
1020 
1021  if (layname && layname[0] != '\0') {
1022  char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
1023  BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
1024  BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
1025 
1026  BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
1027  }
1028  else {
1029  BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
1030  }
1031 
1032  echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
1033 
1034  if (echan) {
1035  echan->xstride = xstride;
1036  echan->ystride = ystride;
1037  echan->rect = rect;
1038  }
1039  else {
1040  printf("IMB_exr_set_channel error %s\n", name);
1041  }
1042 }
1043 
1044 float *IMB_exr_channel_rect(void *handle,
1045  const char *layname,
1046  const char *passname,
1047  const char *viewname)
1048 {
1049  ExrHandle *data = (ExrHandle *)handle;
1050  ExrChannel *echan;
1051  char name[EXR_TOT_MAXNAME + 1];
1052 
1053  if (layname) {
1054  char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
1055  BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
1056  BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
1057 
1058  BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
1059  }
1060  else {
1061  BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
1062  }
1063 
1064  /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
1065  if (layname && layname[0] != '\0') {
1066  char temp_buf[EXR_PASS_MAXNAME];
1067  imb_exr_insert_view_name(temp_buf, name, viewname);
1068  BLI_strncpy(name, temp_buf, sizeof(name));
1069  }
1070  else if (!data->multiView->empty()) {
1071  const int view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname));
1072  std::string raw_name = insertViewName(name, *data->multiView, view_id);
1073  BLI_strncpy(name, raw_name.c_str(), sizeof(name));
1074  }
1075 
1076  echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
1077 
1078  if (echan) {
1079  return echan->rect;
1080  }
1081 
1082  return nullptr;
1083 }
1084 
1085 void IMB_exr_clear_channels(void *handle)
1086 {
1087  ExrHandle *data = (ExrHandle *)handle;
1088  ExrChannel *chan;
1089 
1090  for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) {
1091  delete chan->m;
1092  }
1093 
1094  BLI_freelistN(&data->channels);
1095 }
1096 
1097 void IMB_exr_write_channels(void *handle)
1098 {
1099  ExrHandle *data = (ExrHandle *)handle;
1100  FrameBuffer frameBuffer;
1101  ExrChannel *echan;
1102 
1103  if (data->channels.first) {
1104  const size_t num_pixels = ((size_t)data->width) * data->height;
1105  half *rect_half = nullptr, *current_rect_half = nullptr;
1106 
1107  /* We allocate temporary storage for half pixels for all the channels at once. */
1108  if (data->num_half_channels != 0) {
1109  rect_half = (half *)MEM_mallocN(sizeof(half) * data->num_half_channels * num_pixels,
1110  __func__);
1111  current_rect_half = rect_half;
1112  }
1113 
1114  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1115  /* Writing starts from last scanline, stride negative. */
1116  if (echan->use_half_float) {
1117  float *rect = echan->rect;
1118  half *cur = current_rect_half;
1119  for (size_t i = 0; i < num_pixels; i++, cur++) {
1120  *cur = float_to_half_safe(rect[i * echan->xstride]);
1121  }
1122  half *rect_to_write = current_rect_half + (data->height - 1L) * data->width;
1123  frameBuffer.insert(
1124  echan->name,
1125  Slice(Imf::HALF, (char *)rect_to_write, sizeof(half), -data->width * sizeof(half)));
1126  current_rect_half += num_pixels;
1127  }
1128  else {
1129  float *rect = echan->rect + echan->xstride * (data->height - 1L) * data->width;
1130  frameBuffer.insert(echan->name,
1131  Slice(Imf::FLOAT,
1132  (char *)rect,
1133  echan->xstride * sizeof(float),
1134  -echan->ystride * sizeof(float)));
1135  }
1136  }
1137 
1138  data->ofile->setFrameBuffer(frameBuffer);
1139  try {
1140  data->ofile->writePixels(data->height);
1141  }
1142  catch (const std::exception &exc) {
1143  std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
1144  }
1145  /* Free temporary buffers. */
1146  if (rect_half != nullptr) {
1147  MEM_freeN(rect_half);
1148  }
1149  }
1150  else {
1151  printf("Error: attempt to save MultiLayer without layers.\n");
1152  }
1153 }
1154 
1155 /* temporary function, used for FSA and Save Buffers */
1156 /* called once per tile * view */
1158  void *handle, int partx, int party, int level, const char *viewname, bool empty)
1159 {
1160  /* Can write empty channels for incomplete renders. */
1161  ExrHandle *data = (ExrHandle *)handle;
1162  FrameBuffer frameBuffer;
1163  std::string view(viewname);
1164  const int view_id = imb_exr_get_multiView_id(*data->multiView, view);
1165 
1166  exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname);
1167  exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
1168  exr_printf("---------------------------------------------------------------------\n");
1169 
1170  if (!empty) {
1171  ExrChannel *echan;
1172 
1173  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1174 
1175  /* eventually we can make the parts' channels to include
1176  * only the current view TODO */
1177  if (!STREQ(viewname, echan->m->view.c_str())) {
1178  continue;
1179  }
1180 
1181  exr_printf("%d %-6s %-22s \"%s\"\n",
1182  echan->m->part_number,
1183  echan->m->view.c_str(),
1184  echan->m->name.c_str(),
1185  echan->m->internal_name.c_str());
1186 
1187  float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
1188  frameBuffer.insert(echan->m->internal_name,
1189  Slice(Imf::FLOAT,
1190  (char *)rect,
1191  echan->xstride * sizeof(float),
1192  echan->ystride * sizeof(float)));
1193  }
1194  }
1195 
1196  TiledOutputPart out(*data->mpofile, view_id);
1197  out.setFrameBuffer(frameBuffer);
1198 
1199  try {
1200  // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
1201  out.writeTile(partx / data->tilex, party / data->tiley, level);
1202  }
1203  catch (const std::exception &exc) {
1204  std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
1205  }
1206 }
1207 
1208 void IMB_exr_read_channels(void *handle)
1209 {
1210  ExrHandle *data = (ExrHandle *)handle;
1211  int numparts = data->ifile->parts();
1212 
1213  /* Check if EXR was saved with previous versions of blender which flipped images. */
1214  const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>(
1215  "BlenderMultiChannel");
1216 
1217  /* 'previous multilayer attribute, flipped. */
1218  short flip = (ta && STRPREFIX(ta->value().c_str(), "Blender V2.43"));
1219 
1220  exr_printf(
1221  "\nIMB_exr_read_channels\n%s %-6s %-22s "
1222  "\"%s\"\n---------------------------------------------------------------------\n",
1223  "p",
1224  "view",
1225  "name",
1226  "internal_name");
1227 
1228  for (int i = 0; i < numparts; i++) {
1229  /* Read part header. */
1230  InputPart in(*data->ifile, i);
1231  Header header = in.header();
1232  Box2i dw = header.dataWindow();
1233 
1234  /* Insert all matching channel into frame-buffer. */
1235  FrameBuffer frameBuffer;
1236  ExrChannel *echan;
1237 
1238  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1239  if (echan->m->part_number != i) {
1240  continue;
1241  }
1242 
1243  exr_printf("%d %-6s %-22s \"%s\"\n",
1244  echan->m->part_number,
1245  echan->m->view.c_str(),
1246  echan->m->name.c_str(),
1247  echan->m->internal_name.c_str());
1248 
1249  if (echan->rect) {
1250  float *rect = echan->rect;
1251  size_t xstride = echan->xstride * sizeof(float);
1252  size_t ystride = echan->ystride * sizeof(float);
1253 
1254  if (!flip) {
1255  /* Inverse correct first pixel for data-window coordinates. */
1256  rect -= echan->xstride * (dw.min.x - dw.min.y * data->width);
1257  /* move to last scanline to flip to Blender convention */
1258  rect += echan->xstride * (data->height - 1) * data->width;
1259  ystride = -ystride;
1260  }
1261  else {
1262  /* Inverse correct first pixel for data-window coordinates. */
1263  rect -= echan->xstride * (dw.min.x + dw.min.y * data->width);
1264  }
1265 
1266  frameBuffer.insert(echan->m->internal_name,
1267  Slice(Imf::FLOAT, (char *)rect, xstride, ystride));
1268  }
1269  else {
1270  printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str());
1271  }
1272  }
1273 
1274  /* Read pixels. */
1275  try {
1276  in.setFrameBuffer(frameBuffer);
1277  exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, dw.min.y, dw.max.y);
1278  in.readPixels(dw.min.y, dw.max.y);
1279  }
1280  catch (const std::exception &exc) {
1281  std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
1282  break;
1283  }
1284  }
1285 }
1286 
1287 void IMB_exr_multilayer_convert(void *handle,
1288  void *base,
1289  void *(*addview)(void *base, const char *str),
1290  void *(*addlayer)(void *base, const char *str),
1291  void (*addpass)(void *base,
1292  void *lay,
1293  const char *str,
1294  float *rect,
1295  int totchan,
1296  const char *chan_id,
1297  const char *view))
1298 {
1299  ExrHandle *data = (ExrHandle *)handle;
1300  ExrLayer *lay;
1301  ExrPass *pass;
1302 
1303  /* RenderResult needs at least one RenderView */
1304  if (data->multiView->empty()) {
1305  addview(base, "");
1306  }
1307  else {
1308  /* add views to RenderResult */
1309  for (const std::string &view_name : *data->multiView) {
1310  addview(base, view_name.c_str());
1311  }
1312  }
1313 
1314  if (BLI_listbase_is_empty(&data->layers)) {
1315  printf("cannot convert multilayer, no layers in handle\n");
1316  return;
1317  }
1318 
1319  for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1320  void *laybase = addlayer(base, lay->name);
1321  if (laybase) {
1322  for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1323  addpass(base,
1324  laybase,
1325  pass->internal_name,
1326  pass->rect,
1327  pass->totchan,
1328  pass->chan_id,
1329  pass->view);
1330  pass->rect = nullptr;
1331  }
1332  }
1333  }
1334 }
1335 
1336 void IMB_exr_close(void *handle)
1337 {
1338  ExrHandle *data = (ExrHandle *)handle;
1339  ExrLayer *lay;
1340  ExrPass *pass;
1341  ExrChannel *chan;
1342 
1343  delete data->ifile;
1344  delete data->ifile_stream;
1345  delete data->ofile;
1346  delete data->mpofile;
1347  delete data->ofile_stream;
1348  delete data->multiView;
1349 
1350  data->ifile = nullptr;
1351  data->ifile_stream = nullptr;
1352  data->ofile = nullptr;
1353  data->mpofile = nullptr;
1354  data->ofile_stream = nullptr;
1355 
1356  for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) {
1357  delete chan->m;
1358  }
1359  BLI_freelistN(&data->channels);
1360 
1361  for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1362  for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1363  if (pass->rect) {
1364  MEM_freeN(pass->rect);
1365  }
1366  }
1367  BLI_freelistN(&lay->passes);
1368  }
1369  BLI_freelistN(&data->layers);
1370 
1372  MEM_freeN(data);
1373 }
1374 
1375 /* ********* */
1376 
1377 /* get a substring from the end of the name, separated by '.' */
1378 static int imb_exr_split_token(const char *str, const char *end, const char **token)
1379 {
1380  const char delims[] = {'.', '\0'};
1381  const char *sep;
1382 
1383  BLI_str_partition_ex(str, end, delims, &sep, token, true);
1384 
1385  if (!sep) {
1386  *token = str;
1387  }
1388 
1389  return (int)(end - *token);
1390 }
1391 
1392 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
1393 {
1394  const char *name = echan->m->name.c_str();
1395  const char *end = name + strlen(name);
1396  const char *token;
1397  char tokenbuf[EXR_TOT_MAXNAME];
1398  int len;
1399 
1400  /* some multilayers have the combined buffer with names A B G R saved */
1401  if (name[1] == 0) {
1402  echan->chan_id = name[0];
1403  layname[0] = '\0';
1404 
1405  if (ELEM(name[0], 'R', 'G', 'B', 'A')) {
1406  strcpy(passname, "Combined");
1407  }
1408  else if (name[0] == 'Z') {
1409  strcpy(passname, "Depth");
1410  }
1411  else {
1412  strcpy(passname, name);
1413  }
1414 
1415  return 1;
1416  }
1417 
1418  /* last token is channel identifier */
1419  len = imb_exr_split_token(name, end, &token);
1420  if (len == 0) {
1421  printf("multilayer read: bad channel name: %s\n", name);
1422  return 0;
1423  }
1424  if (len == 1) {
1425  echan->chan_id = token[0];
1426  }
1427  else if (len > 1) {
1428  bool ok = false;
1429 
1430  if (len == 2) {
1431  /* some multilayers are using two-letter channels name,
1432  * like, MX or NZ, which is basically has structure of
1433  * <pass_prefix><component>
1434  *
1435  * This is a bit silly, but see file from T35658.
1436  *
1437  * Here we do some magic to distinguish such cases.
1438  */
1439  if (ELEM(token[1], 'X', 'Y', 'Z') || ELEM(token[1], 'R', 'G', 'B') ||
1440  ELEM(token[1], 'U', 'V', 'A')) {
1441  echan->chan_id = token[1];
1442  ok = true;
1443  }
1444  }
1445  else if (BLI_strcaseeq(token, "red")) {
1446  echan->chan_id = 'R';
1447  ok = true;
1448  }
1449  else if (BLI_strcaseeq(token, "green")) {
1450  echan->chan_id = 'G';
1451  ok = true;
1452  }
1453  else if (BLI_strcaseeq(token, "blue")) {
1454  echan->chan_id = 'B';
1455  ok = true;
1456  }
1457  else if (BLI_strcaseeq(token, "alpha")) {
1458  echan->chan_id = 'A';
1459  ok = true;
1460  }
1461  else if (BLI_strcaseeq(token, "depth")) {
1462  echan->chan_id = 'Z';
1463  ok = true;
1464  }
1465 
1466  if (ok == false) {
1467  BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME));
1468  printf("multilayer read: unknown channel token: %s\n", tokenbuf);
1469  return 0;
1470  }
1471  }
1472  end -= len + 1; /* +1 to skip '.' separator */
1473 
1474  /* second token is pass name */
1475  len = imb_exr_split_token(name, end, &token);
1476  if (len == 0) {
1477  printf("multilayer read: bad channel name: %s\n", name);
1478  return 0;
1479  }
1480  BLI_strncpy(passname, token, len + 1);
1481  end -= len + 1; /* +1 to skip '.' separator */
1482 
1483  /* all preceding tokens combined as layer name */
1484  if (end > name) {
1485  BLI_strncpy(layname, name, (int)(end - name) + 1);
1486  }
1487  else {
1488  layname[0] = '\0';
1489  }
1490 
1491  return 1;
1492 }
1493 
1494 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
1495 {
1496  ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
1497 
1498  if (lay == nullptr) {
1499  lay = (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
1500  BLI_addtail(lb, lay);
1501  BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
1502  }
1503 
1504  return lay;
1505 }
1506 
1507 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
1508 {
1509  ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
1510 
1511  if (pass == nullptr) {
1512  pass = (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
1513 
1514  if (STREQ(passname, "Combined")) {
1515  BLI_addhead(lb, pass);
1516  }
1517  else {
1518  BLI_addtail(lb, pass);
1519  }
1520  }
1521 
1522  BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
1523 
1524  return pass;
1525 }
1526 
1527 /* creates channels, makes a hierarchy and assigns memory to channels */
1528 static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
1529  MultiPartInputFile &file,
1530  int width,
1531  int height)
1532 {
1533  ExrLayer *lay;
1534  ExrPass *pass;
1535  ExrChannel *echan;
1537  int a;
1538  char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
1539 
1540  data->ifile_stream = &file_stream;
1541  data->ifile = &file;
1542 
1543  data->width = width;
1544  data->height = height;
1545 
1546  std::vector<MultiViewChannelName> channels;
1547  GetChannelsInMultiPartFile(*data->ifile, channels);
1548 
1549  imb_exr_get_views(*data->ifile, *data->multiView);
1550 
1551  for (const MultiViewChannelName &channel : channels) {
1553  data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
1554 
1555  echan = (ExrChannel *)data->channels.last;
1556  echan->m->name = channel.name;
1557  echan->m->view = channel.view;
1558  echan->m->part_number = channel.part_number;
1559  echan->m->internal_name = channel.internal_name;
1560  }
1561 
1562  /* now try to sort out how to assign memory to the channels */
1563  /* first build hierarchical layer list */
1564  for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1565  if (imb_exr_split_channel_name(echan, layname, passname)) {
1566 
1567  const char *view = echan->m->view.c_str();
1568  char internal_name[EXR_PASS_MAXNAME];
1569 
1570  BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME);
1571 
1572  if (view[0] != '\0') {
1573  char tmp_pass[EXR_PASS_MAXNAME];
1574  BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view);
1575  BLI_strncpy(passname, tmp_pass, sizeof(passname));
1576  }
1577 
1578  ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
1579  ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
1580 
1581  pass->chan[pass->totchan] = echan;
1582  pass->totchan++;
1583  pass->view_id = echan->view_id;
1584  BLI_strncpy(pass->view, view, sizeof(pass->view));
1585  BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME);
1586 
1587  if (pass->totchan >= EXR_PASS_MAXCHAN) {
1588  break;
1589  }
1590  }
1591  }
1592  if (echan) {
1593  printf("error, too many channels in one pass: %s\n", echan->m->name.c_str());
1595  return nullptr;
1596  }
1597 
1598  /* with some heuristics, try to merge the channels in buffers */
1599  for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1600  for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1601  if (pass->totchan) {
1602  pass->rect = (float *)MEM_callocN(width * height * pass->totchan * sizeof(float),
1603  "pass rect");
1604  if (pass->totchan == 1) {
1605  echan = pass->chan[0];
1606  echan->rect = pass->rect;
1607  echan->xstride = 1;
1608  echan->ystride = width;
1609  pass->chan_id[0] = echan->chan_id;
1610  }
1611  else {
1612  char lookup[256];
1613 
1614  memset(lookup, 0, sizeof(lookup));
1615 
1616  /* we can have RGB(A), XYZ(W), UVA */
1617  if (ELEM(pass->totchan, 3, 4)) {
1618  if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||
1619  pass->chan[2]->chan_id == 'B') {
1620  lookup[(unsigned int)'R'] = 0;
1621  lookup[(unsigned int)'G'] = 1;
1622  lookup[(unsigned int)'B'] = 2;
1623  lookup[(unsigned int)'A'] = 3;
1624  }
1625  else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||
1626  pass->chan[2]->chan_id == 'Y') {
1627  lookup[(unsigned int)'X'] = 0;
1628  lookup[(unsigned int)'Y'] = 1;
1629  lookup[(unsigned int)'Z'] = 2;
1630  lookup[(unsigned int)'W'] = 3;
1631  }
1632  else {
1633  lookup[(unsigned int)'U'] = 0;
1634  lookup[(unsigned int)'V'] = 1;
1635  lookup[(unsigned int)'A'] = 2;
1636  }
1637  for (a = 0; a < pass->totchan; a++) {
1638  echan = pass->chan[a];
1639  echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
1640  echan->xstride = pass->totchan;
1641  echan->ystride = width * pass->totchan;
1642  pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
1643  }
1644  }
1645  else { /* unknown */
1646  for (a = 0; a < pass->totchan; a++) {
1647  echan = pass->chan[a];
1648  echan->rect = pass->rect + a;
1649  echan->xstride = pass->totchan;
1650  echan->ystride = width * pass->totchan;
1651  pass->chan_id[a] = echan->chan_id;
1652  }
1653  }
1654  }
1655  }
1656  }
1657  }
1658 
1659  return data;
1660 }
1661 
1662 /* ********************************************************* */
1663 
1664 /* debug only */
1665 static void exr_printf(const char *fmt, ...)
1666 {
1667 #if 0
1668  char output[1024];
1669  va_list args;
1670  va_start(args, fmt);
1671  std::vsprintf(output, fmt, args);
1672  va_end(args);
1673  printf("%s", output);
1674 #else
1675  (void)fmt;
1676 #endif
1677 }
1678 
1679 static void exr_print_filecontents(MultiPartInputFile &file)
1680 {
1681  int numparts = file.parts();
1682  if (numparts == 1 && hasMultiView(file.header(0))) {
1683  const StringVector views = multiView(file.header(0));
1684  printf("OpenEXR-load: MultiView file\n");
1685  printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str());
1686  for (const std::string &view : views) {
1687  printf("OpenEXR-load: Found view %s\n", view.c_str());
1688  }
1689  }
1690  else if (numparts > 1) {
1691  printf("OpenEXR-load: MultiPart file\n");
1692  for (int i = 0; i < numparts; i++) {
1693  if (file.header(i).hasView()) {
1694  printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str());
1695  }
1696  }
1697  }
1698 
1699  for (int j = 0; j < numparts; j++) {
1700  const ChannelList &channels = file.header(j).channels();
1701  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1702  const Channel &channel = i.channel();
1703  printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
1704  }
1705  }
1706 }
1707 
1708 /* for non-multilayer, map R G B A channel names to something that's in this file */
1709 static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
1710 {
1711  const ChannelList &channels = file.header(0).channels();
1712 
1713  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1714  /* const Channel &channel = i.channel(); */ /* Not used yet */
1715  const char *str = i.name();
1716  int len = strlen(str);
1717  if (len) {
1718  if (BLI_strcasecmp(chan, str + len - 1) == 0) {
1719  return str;
1720  }
1721  }
1722  }
1723  return chan;
1724 }
1725 
1726 static int exr_has_rgb(MultiPartInputFile &file, const char *rgb_channels[3])
1727 {
1728  /* Common names for RGB-like channels in order. */
1729  static const char *channel_names[] = {
1730  "R", "Red", "G", "Green", "B", "Blue", "AR", "RA", "AG", "GA", "AB", "BA", nullptr};
1731 
1732  const Header &header = file.header(0);
1733  int num_channels = 0;
1734 
1735  for (int i = 0; channel_names[i]; i++) {
1736  if (header.channels().findChannel(channel_names[i])) {
1737  rgb_channels[num_channels++] = channel_names[i];
1738  if (num_channels == 3) {
1739  break;
1740  }
1741  }
1742  }
1743 
1744  return num_channels;
1745 }
1746 
1747 static bool exr_has_luma(MultiPartInputFile &file)
1748 {
1749  /* Y channel is the luma and should always present fir luma space images,
1750  * optionally it could be also channels for chromas called BY and RY.
1751  */
1752  const Header &header = file.header(0);
1753  return header.channels().findChannel("Y") != nullptr;
1754 }
1755 
1756 static bool exr_has_chroma(MultiPartInputFile &file)
1757 {
1758  const Header &header = file.header(0);
1759  return header.channels().findChannel("BY") != nullptr &&
1760  header.channels().findChannel("RY") != nullptr;
1761 }
1762 
1763 static bool exr_has_zbuffer(MultiPartInputFile &file)
1764 {
1765  const Header &header = file.header(0);
1766  return !(header.channels().findChannel("Z") == nullptr);
1767 }
1768 
1769 static bool exr_has_alpha(MultiPartInputFile &file)
1770 {
1771  const Header &header = file.header(0);
1772  return !(header.channels().findChannel("A") == nullptr);
1773 }
1774 
1775 static bool exr_is_half_float(MultiPartInputFile &file)
1776 {
1777  const ChannelList &channels = file.header(0).channels();
1778  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1779  const Channel &channel = i.channel();
1780  if (channel.type != HALF) {
1781  return false;
1782  }
1783  }
1784  return true;
1785 }
1786 
1787 static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
1788 {
1789  const ChannelList &channels = file.header(0).channels();
1790  std::set<std::string> layerNames;
1791 
1792  /* This will not include empty layer names, so files with just R/G/B/A
1793  * channels without a layer name will be single layer. */
1794  channels.layers(layerNames);
1795 
1796  return (!layerNames.empty());
1797 }
1798 
1799 static void imb_exr_type_by_channels(ChannelList &channels,
1800  StringVector &views,
1801  bool *r_singlelayer,
1802  bool *r_multilayer,
1803  bool *r_multiview)
1804 {
1805  std::set<std::string> layerNames;
1806 
1807  *r_singlelayer = true;
1808  *r_multilayer = *r_multiview = false;
1809 
1810  /* will not include empty layer names */
1811  channels.layers(layerNames);
1812 
1813  if (!views.empty() && !views[0].empty()) {
1814  *r_multiview = true;
1815  }
1816  else {
1817  *r_singlelayer = false;
1818  *r_multilayer = (layerNames.size() > 1);
1819  *r_multiview = false;
1820  return;
1821  }
1822 
1823  if (!layerNames.empty()) {
1824  /* If `layerNames` is not empty, it means at least one layer is non-empty,
1825  * but it also could be layers without names in the file and such case
1826  * shall be considered a multi-layer EXR.
1827  *
1828  * That's what we do here: test whether there are empty layer names together
1829  * with non-empty ones in the file.
1830  */
1831  for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) {
1832  for (const std::string &layer_name : layerNames) {
1833  /* see if any layername differs from a viewname */
1834  if (imb_exr_get_multiView_id(views, layer_name) == -1) {
1835  std::string layerName = layer_name;
1836  size_t pos = layerName.rfind('.');
1837 
1838  if (pos == std::string::npos) {
1839  *r_multilayer = true;
1840  *r_singlelayer = false;
1841  return;
1842  }
1843  }
1844  }
1845  }
1846  }
1847  else {
1848  *r_singlelayer = true;
1849  *r_multilayer = false;
1850  }
1851 
1852  BLI_assert(r_singlelayer != r_multilayer);
1853 }
1854 
1855 static bool exr_has_multiview(MultiPartInputFile &file)
1856 {
1857  for (int p = 0; p < file.parts(); p++) {
1858  if (hasMultiView(file.header(p))) {
1859  return true;
1860  }
1861  }
1862 
1863  return false;
1864 }
1865 
1866 static bool exr_has_multipart_file(MultiPartInputFile &file)
1867 {
1868  return file.parts() > 1;
1869 }
1870 
1871 /* it returns true if the file is multilayer or multiview */
1872 static bool imb_exr_is_multi(MultiPartInputFile &file)
1873 {
1874  /* Multipart files are treated as multilayer in blender -
1875  * even if they are single layer openexr with multiview. */
1877  return true;
1878  }
1879 
1880  if (exr_has_multiview(file)) {
1881  return true;
1882  }
1883 
1885  return true;
1886  }
1887 
1888  return false;
1889 }
1890 
1891 bool IMB_exr_has_multilayer(void *handle)
1892 {
1893  ExrHandle *data = (ExrHandle *)handle;
1894  return imb_exr_is_multi(*data->ifile);
1895 }
1896 
1897 struct ImBuf *imb_load_openexr(const unsigned char *mem,
1898  size_t size,
1899  int flags,
1900  char colorspace[IM_MAX_SPACE])
1901 {
1902  struct ImBuf *ibuf = nullptr;
1903  IMemStream *membuf = nullptr;
1904  MultiPartInputFile *file = nullptr;
1905 
1906  if (imb_is_a_openexr(mem, size) == 0) {
1907  return nullptr;
1908  }
1909 
1911 
1912  try {
1913  bool is_multi;
1914 
1915  membuf = new IMemStream((unsigned char *)mem, size);
1916  file = new MultiPartInputFile(*membuf);
1917 
1918  Box2i dw = file->header(0).dataWindow();
1919  const int width = dw.max.x - dw.min.x + 1;
1920  const int height = dw.max.y - dw.min.y + 1;
1921 
1922  // printf("OpenEXR-load: image data window %d %d %d %d\n",
1923  // dw.min.x, dw.min.y, dw.max.x, dw.max.y);
1924 
1925  if (false) { /* debug */
1927  }
1928 
1929  is_multi = imb_exr_is_multi(*file);
1930 
1931  /* do not make an ibuf when */
1932  if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
1933  printf("Error: can't process EXR multilayer file\n");
1934  }
1935  else {
1936  const int is_alpha = exr_has_alpha(*file);
1937 
1938  ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
1939  ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
1940 
1941  if (hasXDensity(file->header(0))) {
1942  /* Convert inches to meters. */
1943  ibuf->ppm[0] = (double)xDensity(file->header(0)) / 0.0254;
1944  ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio();
1945  }
1946 
1947  ibuf->ftype = IMB_FTYPE_OPENEXR;
1948 
1949  if (!(flags & IB_test)) {
1950 
1951  if (flags & IB_metadata) {
1952  const Header &header = file->header(0);
1953  Header::ConstIterator iter;
1954 
1955  IMB_metadata_ensure(&ibuf->metadata);
1956  for (iter = header.begin(); iter != header.end(); iter++) {
1957  const StringAttribute *attr = file->header(0).findTypedAttribute<StringAttribute>(
1958  iter.name());
1959 
1960  /* not all attributes are string attributes so we might get some NULLs here */
1961  if (attr) {
1962  IMB_metadata_set_field(ibuf->metadata, iter.name(), attr->value().c_str());
1963  ibuf->flags |= IB_metadata;
1964  }
1965  }
1966  }
1967 
1968  /* Only enters with IB_multilayer flag set. */
1969  if (is_multi && ((flags & IB_thumbnail) == 0)) {
1970  /* constructs channels for reading, allocates memory in channels */
1971  ExrHandle *handle = imb_exr_begin_read_mem(*membuf, *file, width, height);
1972  if (handle) {
1973  IMB_exr_read_channels(handle);
1974  ibuf->userdata = handle; /* potential danger, the caller has to check for this! */
1975  }
1976  }
1977  else {
1978  const char *rgb_channels[3];
1979  const int num_rgb_channels = exr_has_rgb(*file, rgb_channels);
1980  const bool has_luma = exr_has_luma(*file);
1981  FrameBuffer frameBuffer;
1982  float *first;
1983  int xstride = sizeof(float[4]);
1984  int ystride = -xstride * width;
1985 
1986  imb_addrectfloatImBuf(ibuf);
1987 
1988  /* Inverse correct first pixel for data-window
1989  * coordinates (- dw.min.y because of y flip). */
1990  first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
1991  /* but, since we read y-flipped (negative y stride) we move to last scanline */
1992  first += 4 * (height - 1) * width;
1993 
1994  if (num_rgb_channels > 0) {
1995  for (int i = 0; i < num_rgb_channels; i++) {
1996  frameBuffer.insert(exr_rgba_channelname(*file, rgb_channels[i]),
1997  Slice(Imf::FLOAT, (char *)(first + i), xstride, ystride));
1998  }
1999  }
2000  else if (has_luma) {
2001  frameBuffer.insert(exr_rgba_channelname(*file, "Y"),
2002  Slice(Imf::FLOAT, (char *)first, xstride, ystride));
2003  frameBuffer.insert(
2004  exr_rgba_channelname(*file, "BY"),
2005  Slice(Imf::FLOAT, (char *)(first + 1), xstride, ystride, 1, 1, 0.5f));
2006  frameBuffer.insert(
2007  exr_rgba_channelname(*file, "RY"),
2008  Slice(Imf::FLOAT, (char *)(first + 2), xstride, ystride, 1, 1, 0.5f));
2009  }
2010 
2011  /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
2012  frameBuffer.insert(exr_rgba_channelname(*file, "A"),
2013  Slice(Imf::FLOAT, (char *)(first + 3), xstride, ystride, 1, 1, 1.0f));
2014 
2015  if (exr_has_zbuffer(*file)) {
2016  float *firstz;
2017 
2018  addzbuffloatImBuf(ibuf);
2019  firstz = ibuf->zbuf_float - (dw.min.x - dw.min.y * width);
2020  firstz += (height - 1) * width;
2021  frameBuffer.insert(
2022  "Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float)));
2023  }
2024 
2025  InputPart in(*file, 0);
2026  in.setFrameBuffer(frameBuffer);
2027  in.readPixels(dw.min.y, dw.max.y);
2028 
2029  /* XXX, ImBuf has no nice way to deal with this.
2030  * ideally IM_rect would be used when the caller wants a rect BUT
2031  * at the moment all functions use IM_rect.
2032  * Disabling this is ok because all functions should check
2033  * if a rect exists and create one on demand.
2034  *
2035  * Disabling this because the sequencer frees immediate. */
2036 #if 0
2037  if (flag & IM_rect) {
2038  IMB_rect_from_float(ibuf);
2039  }
2040 #endif
2041 
2042  if (num_rgb_channels == 0 && has_luma && exr_has_chroma(*file)) {
2043  for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
2044  float *color = ibuf->rect_float + a * 4;
2045  ycc_to_rgb(color[0] * 255.0f,
2046  color[1] * 255.0f,
2047  color[2] * 255.0f,
2048  &color[0],
2049  &color[1],
2050  &color[2],
2052  }
2053  }
2054  else if (num_rgb_channels <= 1) {
2055  /* Convert 1 to 3 channels. */
2056  for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
2057  float *color = ibuf->rect_float + a * 4;
2058  if (num_rgb_channels <= 1) {
2059  color[1] = color[0];
2060  }
2061  if (num_rgb_channels <= 2) {
2062  color[2] = color[0];
2063  }
2064  }
2065  }
2066 
2067  /* file is no longer needed */
2068  delete membuf;
2069  delete file;
2070  }
2071  }
2072  else {
2073  delete membuf;
2074  delete file;
2075  }
2076 
2077  if (flags & IB_alphamode_detect) {
2078  ibuf->flags |= IB_alphamode_premul;
2079  }
2080  }
2081  return ibuf;
2082  }
2083  catch (const std::exception &exc) {
2084  std::cerr << exc.what() << std::endl;
2085  if (ibuf) {
2086  IMB_freeImBuf(ibuf);
2087  }
2088  delete file;
2089  delete membuf;
2090 
2091  return nullptr;
2092  }
2093 }
2094 
2096 {
2097  int num_threads = BLI_system_thread_count();
2098 
2099  setGlobalThreadCount(num_threads);
2100 }
2101 
2103 {
2104  /* Tells OpenEXR to free thread pool, also ensures there is no running
2105  * tasks.
2106  */
2107  setGlobalThreadCount(0);
2108 }
2109 
2110 } /* export "C" */
typedef float(TangentPoint)[2]
#define IDP_String(prop)
Definition: BKE_idprop.h:181
void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip)
Definition: image.c:2686
#define BLI_assert(a)
Definition: BLI_assert.h:58
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:219
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:124
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:87
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:547
void * BLI_findstring(const struct ListBase *listbase, const char *id, const int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
void * BLI_rfindstring(const struct ListBase *listbase, const char *id, const 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:169
float srgb_to_linearrgb(float c)
Definition: math_color.c:434
#define BLI_YCC_ITU_BT709
#define FILE_MAX
size_t BLI_str_partition_ex(const char *str, const char *end, const char delim[], const char **sep, const char **suf, const bool from_right) ATTR_NONNULL(1
int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:666
int BLI_strcaseeq(const char *a, const char *b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:570
size_t BLI_str_rpartition(const char *str, const char delim[], const char **sep, const char **suf) ATTR_NONNULL()
Definition: string.c:1071
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
int BLI_system_thread_count(void)
Definition: threads.cc:309
#define STRPREFIX(a, b)
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
typedef double(DMatrix)[4][4]
@ IDP_STRING
Definition: DNA_ID.h:97
#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 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
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
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:478
void IMB_rect_from_float(struct ImBuf *ibuf)
Definition: divers.c:720
void IMB_freeImBuf(struct ImBuf *ibuf)
Definition: allocimbuf.c:211
bool addzbuffloatImBuf(struct ImBuf *ibuf)
Definition: allocimbuf.c:289
bool imb_addrectfloatImBuf(struct ImBuf *ibuf)
Definition: allocimbuf.c:386
#define IM_MAX_SPACE
Definition: IMB_imbuf.h:65
Contains defines and structs used throughout the imbuf module.
#define OPENEXR_HALF
@ IB_halffloat
@ IB_alphamode_premul
@ 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:89
void IMB_metadata_ensure(struct IDProperty **metadata)
Definition: metadata.c:41
Read Guarded memory(de)allocation.
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
Definition: allocimbuf.c:329
bool imb_addencodedbufferImBuf(ImBuf *ibuf)
Definition: allocimbuf.c:306
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
#define output
void clear() override
Int64 tellg() override
bool read(char c[], int n) override
IFileStream(const char *filename)
void seekg(Int64 pos) override
void seekg(Int64 pos) override
Int64 tellg() override
IMemStream(unsigned char *exrbuf, size_t exrsize)
bool read(char c[], int n) override
void clear() override
void seekp(Int64 pos) override
Int64 tellp() override
OFileStream(const char *filename)
void write(const char c[], int n) override
void write(const char c[], int n) override
OMemStream(ImBuf *ibuf_)
void seekp(Int64 pos) override
Int64 tellp() override
Definition: util_half.h:41
void colorspace_set_default_role(char *colorspace, int size, int role)
@ HALF
Definition: curve_bevel.c:43
StackEntry * from
FILE * file
#define str(s)
uint pos
@ IMB_FTYPE_OPENEXR
int count
unsigned short half
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
static const char * exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
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 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_exr_clear_channels(void *handle)
static bool exr_is_half_float(MultiPartInputFile &file)
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)
static int imb_exr_split_token(const char *str, const char *end, const char **token)
void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
bool imb_is_a_openexr(const unsigned char *mem, const size_t size)
void * IMB_exr_get_handle_name(const char *name)
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)
int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress, const StampData *stamp)
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 void imb_exr_get_views(MultiPartInputFile &file, StringVector &views)
int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
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)
#define EXR_LAY_MAXNAME
Definition: openexr_multi.h:30
#define EXR_VIEW_MAXNAME
Definition: openexr_multi.h:32
#define EXR_PASS_MAXNAME
Definition: openexr_multi.h:31
#define EXR_TOT_MAXNAME
Definition: openexr_multi.h:33
#define EXR_PASS_MAXCHAN
Definition: openexr_multi.h:34
#define min(a, b)
Definition: sort.c:51
bool use_half_float
struct ExrChannel * next
char name[EXR_TOT_MAXNAME+1]
struct MultiViewChannelName * m
float * rect
struct ExrChannel * prev
OutputFile * ofile
int num_half_channels
IStream * ifile_stream
OFileStream * ofile_stream
struct ExrHandle * next
StringVector * multiView
ListBase channels
MultiPartInputFile * ifile
struct ExrHandle * prev
ListBase layers
MultiPartOutputFile * mpofile
char name[FILE_MAX]
struct ExrLayer * next
ListBase passes
struct ExrLayer * prev
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 ExrPass * prev
struct ExrChannel * chan[EXR_PASS_MAXCHAN]
char internal_name[EXR_PASS_MAXNAME]
ListBase group
Definition: DNA_ID.h:64
struct IDProperty * next
Definition: DNA_ID.h:70
char name[64]
Definition: DNA_ID.h:74
IDPropertyData data
Definition: DNA_ID.h:80
char type
Definition: DNA_ID.h:71
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:47
float max
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition: utfconv.c:296
uint len