Code cleanup: use bool for static methods
[blender.git] / source / blender / imbuf / intern / openexr / openexr_api.cpp
1 /*
2  * ***** BEGIN GPLLICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright by Gernot Ziegler <gz@lysator.liu.se>.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Austin Benesh, Ton Roosendaal (float, half, speedup, cleanup...).
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/imbuf/intern/openexr/openexr_api.cpp
29  *  \ingroup openexr
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <fstream>
36 #include <string>
37 #include <set>
38 #include <errno.h>
39 #include <algorithm>
40
41 #include <openexr_api.h>
42
43 #if defined (WIN32) && !defined(FREE_WINDOWS)
44 #include "utfconv.h"
45 #endif
46
47 extern "C"
48 {
49
50 // The following prevents a linking error in debug mode for MSVC using the libs in CVS
51 #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(_DEBUG) && !defined(__MINGW32__) && !defined(__CYGWIN__)
52 _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
53 {
54 }
55 #endif
56
57 #include "MEM_guardedalloc.h"
58
59 #include "BLI_blenlib.h"
60 #include "BLI_math_color.h"
61 #include "BLI_threads.h"
62
63 #include "IMB_imbuf_types.h"
64 #include "IMB_imbuf.h"
65 #include "IMB_allocimbuf.h"
66 #include "IMB_metadata.h"
67
68 #include "IMB_colormanagement.h"
69 #include "IMB_colormanagement_intern.h"
70
71 #include "openexr_multi.h"
72 }
73
74 #include <iostream>
75
76 #include <half.h>
77 #include <Iex.h>
78 #include <ImfVersion.h>
79 #include <ImathBox.h>
80 #include <ImfArray.h>
81 #include <ImfIO.h>
82 #include <ImfChannelList.h>
83 #include <ImfPixelType.h>
84 #include <ImfInputFile.h>
85 #include <ImfOutputFile.h>
86 #include <ImfCompression.h>
87 #include <ImfCompressionAttribute.h>
88 #include <ImfStringAttribute.h>
89 #include <ImfStandardAttributes.h>
90
91 using namespace Imf;
92 using namespace Imath;
93
94 /* Memory Input Stream */
95
96 class Mem_IStream : public Imf::IStream
97 {
98 public:
99
100         Mem_IStream (unsigned char *exrbuf, size_t exrsize) :
101                 IStream("dummy"), _exrpos(0), _exrsize(exrsize)
102         {
103                 _exrbuf = exrbuf;
104         }
105
106         virtual bool    read(char c[], int n);
107         virtual Int64   tellg();
108         virtual void    seekg(Int64 pos);
109         virtual void    clear();
110         //virtual ~Mem_IStream() {}; // unused
111
112 private:
113
114         Int64 _exrpos;
115         Int64 _exrsize;
116         unsigned char *_exrbuf;
117 };
118
119 bool Mem_IStream::read(char c[], int n)
120 {
121         if (n + _exrpos <= _exrsize) {
122                 memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
123                 _exrpos += n;
124                 return true;
125         }
126         else
127                 return false;
128 }
129
130 Int64 Mem_IStream::tellg()
131 {
132         return _exrpos;
133 }
134
135 void Mem_IStream::seekg(Int64 pos)
136 {
137         _exrpos = pos;
138 }
139
140 void Mem_IStream::clear()
141 {
142 }
143
144 /* File Input Stream */
145
146 class IFileStream : public Imf::IStream
147 {
148 public:
149         IFileStream(const char *filename)
150         : IStream(filename)
151         {
152                 /* utf-8 file path support on windows */
153 #if defined (WIN32) && !defined(FREE_WINDOWS)
154                 wchar_t *wfilename = alloc_utf16_from_8(filename, 0);
155                 ifs.open(wfilename, std::ios_base::binary);
156                 free(wfilename);
157 #else
158                 ifs.open(filename, std::ios_base::binary);
159 #endif
160
161                 if (!ifs)
162                         Iex::throwErrnoExc();
163         }
164
165         virtual bool read(char c[], int n)
166         {
167                 if (!ifs)
168                         throw Iex::InputExc("Unexpected end of file.");
169
170                 errno = 0;
171                 ifs.read(c, n);
172                 return check_error();
173         }
174
175         virtual Int64 tellg()
176         {
177                 return std::streamoff(ifs.tellg());
178         }
179
180         virtual void seekg(Int64 pos)
181         {
182                 ifs.seekg(pos);
183                 check_error();
184         }
185
186         virtual void clear()
187         {
188                 ifs.clear();
189         }
190
191 private:
192         bool check_error()
193         {
194                 if (!ifs) {
195                         if (errno)
196                                 Iex::throwErrnoExc();
197
198                         return false;
199                 }
200
201                 return true;
202         }
203
204         std::ifstream ifs;
205 };
206
207 /* File Output Stream */
208
209 class OFileStream : public OStream
210 {
211 public:
212         OFileStream(const char *filename)
213         : OStream(filename)
214         {
215                 /* utf-8 file path support on windows */
216 #if defined (WIN32) && !defined(FREE_WINDOWS)
217                 wchar_t *wfilename = alloc_utf16_from_8(filename, 0);
218                 ofs.open(wfilename, std::ios_base::binary);
219                 free(wfilename);
220 #else
221                 ofs.open(filename, std::ios_base::binary);
222 #endif
223
224                 if (!ofs)
225                         Iex::throwErrnoExc();
226         }
227
228         virtual void write(const char c[], int n)
229         {
230                 errno = 0;
231                 ofs.write(c, n);
232                 check_error();
233         }
234
235         virtual Int64 tellp()
236         {
237                 return std::streamoff(ofs.tellp());
238         }
239
240         virtual void seekp(Int64 pos)
241         {
242                 ofs.seekp(pos);
243                 check_error();
244         }
245
246 private:
247         void check_error()
248         {
249                 if (!ofs) {
250                         if (errno)
251                                 Iex::throwErrnoExc();
252
253                         throw Iex::ErrnoExc("File output failed.");
254                 }
255         }
256
257         std::ofstream ofs;
258 };
259
260 struct _RGBAZ {
261         half r;
262         half g;
263         half b;
264         half a;
265         half z;
266 };
267
268 typedef struct _RGBAZ RGBAZ;
269
270 extern "C"
271 {
272
273 int imb_is_a_openexr(unsigned char *mem)
274 {
275         return Imf::isImfMagic((const char *)mem);
276 }
277
278 static void openexr_header_compression(Header *header, int compression)
279 {
280         switch (compression) {
281                 case 0:
282                         header->compression() = NO_COMPRESSION;
283                         break;
284                 case 1:
285                         header->compression() = PXR24_COMPRESSION;
286                         break;
287                 case 2:
288                         header->compression() = ZIP_COMPRESSION;
289                         break;
290                 case 3:
291                         header->compression() = PIZ_COMPRESSION;
292                         break;
293                 case 4:
294                         header->compression() = RLE_COMPRESSION;
295                         break;
296                 default:
297                         header->compression() = ZIP_COMPRESSION;
298                         break;
299         }
300 }
301
302 static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
303 {
304         ImMetaData *info;
305
306         for (info = ibuf->metadata; info; info = info->next)
307                 header->insert(info->key, StringAttribute(info->value));
308
309         if (ibuf->ppm[0] > 0.0)
310                 addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */
311 }
312
313 static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags)
314 {
315         const int channels = ibuf->channels;
316         const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
317         const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
318         const int width = ibuf->x;
319         const int height = ibuf->y;
320
321         try
322         {
323                 Header header(width, height);
324
325                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
326                 openexr_header_metadata(&header, ibuf);
327
328                 header.channels().insert("R", Channel(HALF));
329                 header.channels().insert("G", Channel(HALF));
330                 header.channels().insert("B", Channel(HALF));
331                 if (is_alpha)
332                         header.channels().insert("A", Channel(HALF));
333                 if (is_zbuf)     // z we do as float always
334                         header.channels().insert("Z", Channel(Imf::FLOAT));
335
336                 FrameBuffer frameBuffer;
337
338                 /* manually create ofstream, so we can handle utf-8 filepaths on windows */
339                 OFileStream file_stream(name);
340                 OutputFile file(file_stream, header);
341
342                 /* we store first everything in half array */
343                 RGBAZ *pixels = new RGBAZ[height * width];
344                 RGBAZ *to = pixels;
345                 int xstride = sizeof(RGBAZ);
346                 int ystride = xstride * width;
347
348                 /* indicate used buffers */
349                 frameBuffer.insert("R", Slice(HALF,  (char *) &pixels[0].r, xstride, ystride));
350                 frameBuffer.insert("G", Slice(HALF,  (char *) &pixels[0].g, xstride, ystride));
351                 frameBuffer.insert("B", Slice(HALF,  (char *) &pixels[0].b, xstride, ystride));
352                 if (is_alpha)
353                         frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride));
354                 if (is_zbuf)
355                         frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (height - 1) * width),
356                                                       sizeof(float), sizeof(float) * -width));
357                 if (ibuf->rect_float) {
358                         float *from;
359
360                         for (int i = ibuf->y - 1; i >= 0; i--) {
361                                 from = ibuf->rect_float + channels * i * width;
362
363                                 for (int j = ibuf->x; j > 0; j--) {
364                                         to->r = from[0];
365                                         to->g = (channels >= 2) ? from[1] : from[0];
366                                         to->b = (channels >= 3) ? from[2] : from[0];
367                                         to->a = (channels >= 4) ? from[3] : 1.0f;
368                                         to++; from += channels;
369                                 }
370                         }
371                 }
372                 else {
373                         unsigned char *from;
374
375                         for (int i = ibuf->y - 1; i >= 0; i--) {
376                                 from = (unsigned char *)ibuf->rect + 4 * i * width;
377
378                                 for (int j = ibuf->x; j > 0; j--) {
379                                         to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
380                                         to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
381                                         to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
382                                         to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
383                                         to++; from += 4;
384                                 }
385                         }
386                 }
387
388 //              printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
389
390                 file.setFrameBuffer(frameBuffer);
391                 file.writePixels(height);
392
393                 delete[] pixels;
394         }
395         catch (const std::exception &exc)
396         {
397                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
398
399                 return (0);
400         }
401
402         return (1);
403 }
404
405 static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags)
406 {
407         const int channels = ibuf->channels;
408         const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
409         const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
410         const int width = ibuf->x;
411         const int height = ibuf->y;
412
413         try
414         {
415                 Header header(width, height);
416
417                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
418                 openexr_header_metadata(&header, ibuf);
419
420                 header.channels().insert("R", Channel(Imf::FLOAT));
421                 header.channels().insert("G", Channel(Imf::FLOAT));
422                 header.channels().insert("B", Channel(Imf::FLOAT));
423                 if (is_alpha)
424                         header.channels().insert("A", Channel(Imf::FLOAT));
425                 if (is_zbuf)
426                         header.channels().insert("Z", Channel(Imf::FLOAT));
427
428                 FrameBuffer frameBuffer;
429
430                 /* manually create ofstream, so we can handle utf-8 filepaths on windows */
431                 OFileStream file_stream(name);
432                 OutputFile file(file_stream, header);
433
434                 int xstride = sizeof(float) * channels;
435                 int ystride = -xstride * width;
436                 float *rect[4] = {NULL, NULL, NULL, NULL};
437
438                 /* last scanline, stride negative */
439                 rect[0] = ibuf->rect_float + channels * (height - 1) * width;
440                 rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
441                 rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
442                 rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */
443
444                 frameBuffer.insert("R", Slice(Imf::FLOAT,  (char *)rect[0], xstride, ystride));
445                 frameBuffer.insert("G", Slice(Imf::FLOAT,  (char *)rect[1], xstride, ystride));
446                 frameBuffer.insert("B", Slice(Imf::FLOAT,  (char *)rect[2], xstride, ystride));
447                 if (is_alpha)
448                         frameBuffer.insert("A", Slice(Imf::FLOAT,  (char *)rect[3], xstride, ystride));
449                 if (is_zbuf)
450                         frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width),
451                                                       sizeof(float), sizeof(float) * -width));
452                 file.setFrameBuffer(frameBuffer);
453                 file.writePixels(height);
454         }
455         catch (const std::exception &exc)
456         {
457                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
458
459                 return (0);
460         }
461
462         return (1);
463         //      printf("OpenEXR-save: Done.\n");
464 }
465
466
467 int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
468 {
469         if (flags & IB_mem) {
470                 printf("OpenEXR-save: Create EXR in memory CURRENTLY NOT SUPPORTED !\n");
471                 imb_addencodedbufferImBuf(ibuf);
472                 ibuf->encodedsize = 0;
473                 return(0);
474         }
475
476         if (ibuf->ftype & OPENEXR_HALF)
477                 return imb_save_openexr_half(ibuf, name, flags);
478         else {
479                 /* when no float rect, we save as half (16 bits is sufficient) */
480                 if (ibuf->rect_float == NULL)
481                         return imb_save_openexr_half(ibuf, name, flags);
482                 else
483                         return imb_save_openexr_float(ibuf, name, flags);
484         }
485 }
486
487 /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */
488
489 /* naming rules:
490  * - parse name from right to left
491  * - last character is channel ID, 1 char like 'A' 'R' 'G' 'B' 'X' 'Y' 'Z' 'W' 'U' 'V'
492  * - separated with a dot; the Pass name (like "Depth", "Color", "Diffuse" or "Combined")
493  * - separated with a dot: the Layer name (like "Lamp1" or "Walls" or "Characters")
494  */
495
496 static ListBase exrhandles = {NULL, NULL};
497
498 typedef struct ExrHandle {
499         struct ExrHandle *next, *prev;
500
501         IFileStream *ifile_stream;
502         InputFile *ifile;
503
504         OFileStream *ofile_stream;
505         TiledOutputFile *tofile;
506         OutputFile *ofile;
507
508         int tilex, tiley;
509         int width, height;
510         int mipmap;
511
512         ListBase channels;  /* flattened out, ExrChannel */
513         ListBase layers;    /* hierarchical, pointing in end to ExrChannel */
514 } ExrHandle;
515
516 /* flattened out channel */
517 typedef struct ExrChannel {
518         struct ExrChannel *next, *prev;
519
520         char name[EXR_TOT_MAXNAME + 1];  /* full name of layer+pass */
521         int xstride, ystride;            /* step to next pixel, to next scanline */
522         float *rect;                     /* first pointer to write in */
523         char chan_id;                    /* quick lookup of channel char */
524 } ExrChannel;
525
526
527 /* hierarchical; layers -> passes -> channels[] */
528 typedef struct ExrPass {
529         struct ExrPass *next, *prev;
530         char name[EXR_PASS_MAXNAME];
531         int totchan;
532         float *rect;
533         struct ExrChannel *chan[EXR_PASS_MAXCHAN];
534         char chan_id[EXR_PASS_MAXCHAN];
535 } ExrPass;
536
537 typedef struct ExrLayer {
538         struct ExrLayer *next, *prev;
539         char name[EXR_LAY_MAXNAME + 1];
540         ListBase passes;
541 } ExrLayer;
542
543 /* ********************** */
544
545 void *IMB_exr_get_handle(void)
546 {
547         ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle");
548         BLI_addtail(&exrhandles, data);
549         return data;
550 }
551
552 /* adds flattened ExrChannels */
553 /* xstride, ystride and rect can be done in set_channel too, for tile writing */
554 void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
555 {
556         ExrHandle *data = (ExrHandle *)handle;
557         ExrChannel *echan;
558
559         echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel");
560
561         if (layname) {
562                 char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
563                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
564                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
565
566                 BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass);
567         }
568         else {
569                 BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1);
570         }
571
572         echan->xstride = xstride;
573         echan->ystride = ystride;
574         echan->rect = rect;
575
576         // printf("added channel %s\n", echan->name);
577         BLI_addtail(&data->channels, echan);
578 }
579
580 /* only used for writing temp. render results (not image files) */
581 int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress)
582 {
583         ExrHandle *data = (ExrHandle *)handle;
584         Header header(width, height);
585         ExrChannel *echan;
586
587         data->width = width;
588         data->height = height;
589
590         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
591                 header.channels().insert(echan->name, Channel(Imf::FLOAT));
592
593         openexr_header_compression(&header, compress);
594         // openexr_header_metadata(&header, ibuf); // no imbuf. cant write
595         /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
596
597         header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
598
599         /* avoid crash/abort when we don't have permission to write here */
600         /* manually create ofstream, so we can handle utf-8 filepaths on windows */
601         try {
602                 data->ofile_stream = new OFileStream(filename);
603                 data->ofile = new OutputFile(*(data->ofile_stream), header);
604         }
605         catch (const std::exception &exc) {
606                 std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
607
608                 delete data->ofile;
609                 delete data->ofile_stream;
610
611                 data->ofile = NULL;
612                 data->ofile_stream = NULL;
613         }
614
615         return (data->ofile != NULL);
616 }
617
618 void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
619 {
620         ExrHandle *data = (ExrHandle *)handle;
621         Header header(width, height);
622         ExrChannel *echan;
623
624         data->tilex = tilex;
625         data->tiley = tiley;
626         data->width = width;
627         data->height = height;
628         data->mipmap = mipmap;
629
630         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
631                 header.channels().insert(echan->name, Channel(Imf::FLOAT));
632
633         header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL));
634         header.lineOrder() = RANDOM_Y;
635         header.compression() = RLE_COMPRESSION;
636
637         header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43"));
638
639         /* avoid crash/abort when we don't have permission to write here */
640         /* manually create ofstream, so we can handle utf-8 filepaths on windows */
641         try {
642                 data->ofile_stream = new OFileStream(filename);
643                 data->tofile = new TiledOutputFile(*(data->ofile_stream), header);
644         }
645         catch (const std::exception &) {
646                 delete data->tofile;
647                 delete data->ofile_stream;
648
649                 data->tofile = NULL;
650                 data->ofile_stream = NULL;
651         }
652 }
653
654 /* read from file */
655 int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
656 {
657         ExrHandle *data = (ExrHandle *)handle;
658
659         if (BLI_exists(filename) && BLI_file_size(filename) > 32) {   /* 32 is arbitrary, but zero length files crashes exr */
660                 /* avoid crash/abort when we don't have permission to write here */
661                 try {
662                         data->ifile_stream = new IFileStream(filename);
663                         data->ifile = new InputFile(*(data->ifile_stream));
664                 }
665                 catch (const std::exception &) {
666                         delete data->ifile;
667                         delete data->ifile_stream;
668
669                         data->ifile = NULL;
670                         data->ifile_stream = NULL;
671                 }
672
673                 if (data->ifile) {
674                         Box2i dw = data->ifile->header().dataWindow();
675                         data->width = *width  = dw.max.x - dw.min.x + 1;
676                         data->height = *height = dw.max.y - dw.min.y + 1;
677
678                         const ChannelList &channels = data->ifile->header().channels();
679
680                         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
681                                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
682
683                         return 1;
684                 }
685         }
686         return 0;
687 }
688
689 /* still clumsy name handling, layers/channels can be ordered as list in list later */
690 void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
691 {
692         ExrHandle *data = (ExrHandle *)handle;
693         ExrChannel *echan;
694         char name[EXR_TOT_MAXNAME + 1];
695
696         if (layname) {
697                 char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
698                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
699                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
700
701                 BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
702         }
703         else
704                 BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
705
706         echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
707
708         if (echan) {
709                 echan->xstride = xstride;
710                 echan->ystride = ystride;
711                 echan->rect = rect;
712         }
713         else
714                 printf("IMB_exrtile_set_channel error %s\n", name);
715 }
716
717 void IMB_exrtile_clear_channels(void *handle)
718 {
719         ExrHandle *data = (ExrHandle *)handle;
720         BLI_freelistN(&data->channels);
721 }
722
723 void IMB_exrtile_write_channels(void *handle, int partx, int party, int level)
724 {
725         ExrHandle *data = (ExrHandle *)handle;
726         FrameBuffer frameBuffer;
727         ExrChannel *echan;
728
729         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
730                 float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
731
732                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)rect,
733                                                       echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
734         }
735
736         data->tofile->setFrameBuffer(frameBuffer);
737
738         try {
739                 // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
740                 data->tofile->writeTile(partx / data->tilex, party / data->tiley, level);
741         }
742         catch (const std::exception &exc) {
743                 std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
744         }
745 }
746
747 void IMB_exr_write_channels(void *handle)
748 {
749         ExrHandle *data = (ExrHandle *)handle;
750         FrameBuffer frameBuffer;
751         ExrChannel *echan;
752
753         if (data->channels.first) {
754                 for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
755                         /* last scanline, stride negative */
756                         float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width;
757
758                         frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)rect,
759                                                               echan->xstride * sizeof(float), -echan->ystride * sizeof(float)));
760                 }
761
762                 data->ofile->setFrameBuffer(frameBuffer);
763                 try {
764                         data->ofile->writePixels(data->height);
765                 }
766                 catch (const std::exception &exc) {
767                         std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
768                 }
769         }
770         else {
771                 printf("Error: attempt to save MultiLayer without layers.\n");
772         }
773 }
774
775 void IMB_exr_read_channels(void *handle)
776 {
777         ExrHandle *data = (ExrHandle *)handle;
778         FrameBuffer frameBuffer;
779         ExrChannel *echan;
780
781         /* check if exr was saved with previous versions of blender which flipped images */
782         const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
783         short flip = (ta && strncmp(ta->value().c_str(), "Blender V2.43", 13) == 0); /* 'previous multilayer attribute, flipped */
784
785         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
786
787                 if (echan->rect) {
788                         if (flip)
789                                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)echan->rect,
790                                                                       echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
791                         else
792                                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width),
793                                                                       echan->xstride * sizeof(float), -echan->ystride * sizeof(float)));
794                 }
795                 else
796                         printf("warning, channel with no rect set %s\n", echan->name);
797         }
798
799         data->ifile->setFrameBuffer(frameBuffer);
800
801         try {
802                 data->ifile->readPixels(0, data->height - 1);
803         }
804         catch (const std::exception &exc) {
805                 std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
806         }
807 }
808
809 void IMB_exr_multilayer_convert(void *handle, void *base,
810                                 void * (*addlayer)(void *base, const char *str),
811                                 void (*addpass)(void *base, void *lay, const char *str,
812                                                 float *rect, int totchan, const char *chan_id))
813 {
814         ExrHandle *data = (ExrHandle *)handle;
815         ExrLayer *lay;
816         ExrPass *pass;
817
818         if (data->layers.first == NULL) {
819                 printf("cannot convert multilayer, no layers in handle\n");
820                 return;
821         }
822
823         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
824                 void *laybase = addlayer(base, lay->name);
825                 if (laybase) {
826                         for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
827                                 addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
828                                 pass->rect = NULL;
829                         }
830                 }
831         }
832 }
833
834
835 void IMB_exr_close(void *handle)
836 {
837         ExrHandle *data = (ExrHandle *)handle;
838         ExrLayer *lay;
839         ExrPass *pass;
840
841         delete data->ifile;
842         delete data->ifile_stream;
843         delete data->ofile;
844         delete data->tofile;
845         delete data->ofile_stream;
846
847         data->ifile = NULL;
848         data->ifile_stream = NULL;
849         data->ofile = NULL;
850         data->tofile = NULL;
851         data->ofile_stream = NULL;
852
853         BLI_freelistN(&data->channels);
854
855         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
856                 for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next)
857                         if (pass->rect)
858                                 MEM_freeN(pass->rect);
859                 BLI_freelistN(&lay->passes);
860         }
861         BLI_freelistN(&data->layers);
862
863         BLI_remlink(&exrhandles, data);
864         MEM_freeN(data);
865 }
866
867 /* ********* */
868
869 /* get a substring from the end of the name, separated by '.' */
870 static int imb_exr_split_token(const char *str, const char *end, const char **token)
871 {
872         ptrdiff_t maxlen = end - str;
873         int len = 0;
874         while (len < maxlen && *(end - len - 1) != '.') {
875                 len++;
876         }
877
878         *token = end - len;
879         return len;
880 }
881
882 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
883 {
884         const char *name = echan->name;
885         const char *end = name + strlen(name);
886         const char *token;
887         char tokenbuf[EXR_TOT_MAXNAME];
888         int len;
889         
890         /* some multilayers have the combined buffer with names A B G R saved */
891         if (name[1] == 0) {
892                 echan->chan_id = name[0];
893                 layname[0] = '\0';
894
895                 if (ELEM4(name[0], 'R', 'G', 'B', 'A'))
896                         strcpy(passname, "Combined");
897                 else if (name[0] == 'Z')
898                         strcpy(passname, "Depth");
899                 else
900                         strcpy(passname, name);
901
902                 return 1;
903         }
904
905         /* last token is single character channel identifier */
906         len = imb_exr_split_token(name, end, &token);
907         if (len == 0) {
908                 printf("multilayer read: bad channel name: %s\n", name);
909                 return 0;
910         }
911         else if (len == 1) {
912                 echan->chan_id = token[0];
913         }
914         else if (len > 1) {
915                 bool ok = false;
916
917                 if (len == 2) {
918                         /* some multilayers are using two-letter channels name,
919                          * like, MX or NZ, which is basically has structure of
920                          *   <pass_prefix><component>
921                          *
922                          * This is a bit silly, but see file from [#35658].
923                          *
924                          * Here we do some magic to distinguish such cases.
925                          */
926                         if (ELEM3(token[1], 'X', 'Y', 'Z') ||
927                             ELEM3(token[1], 'R', 'G', 'B') ||
928                             ELEM3(token[1], 'U', 'V', 'A'))
929                         {
930                                 echan->chan_id = token[1];
931                                 ok = true;
932                         }
933                 }
934
935                 if (ok == false) {
936                         BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME));
937                         printf("multilayer read: channel token too long: %s\n", tokenbuf);
938                         return 0;
939                 }
940         }
941         end -= len + 1; /* +1 to skip '.' separator */
942
943         /* second token is pass name */
944         len = imb_exr_split_token(name, end, &token);
945         if (len == 0) {
946                 printf("multilayer read: bad channel name: %s\n", name);
947                 return 0;
948         }
949         BLI_strncpy(passname, token, len + 1);
950         end -= len + 1; /* +1 to skip '.' separator */
951
952         /* all preceding tokens combined as layer name */
953         if (end > name)
954                 BLI_strncpy(layname, name, (int)(end - name) + 1);
955         else
956                 layname[0] = '\0';
957
958         return 1;
959 }
960
961 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
962 {
963         ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
964
965         if (lay == NULL) {
966                 lay = (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
967                 BLI_addtail(lb, lay);
968                 BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
969         }
970
971         return lay;
972 }
973
974 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
975 {
976         ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
977
978         if (pass == NULL) {
979                 pass = (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
980
981                 if (strcmp(passname, "Combined") == 0)
982                         BLI_addhead(lb, pass);
983                 else
984                         BLI_addtail(lb, pass);
985         }
986
987         BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
988
989         return pass;
990 }
991
992 /* creates channels, makes a hierarchy and assigns memory to channels */
993 static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
994 {
995         ExrLayer *lay;
996         ExrPass *pass;
997         ExrChannel *echan;
998         ExrHandle *data = (ExrHandle *)IMB_exr_get_handle();
999         int a;
1000         char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
1001
1002         data->ifile = file;
1003         data->width = width;
1004         data->height = height;
1005
1006         const ChannelList &channels = data->ifile->header().channels();
1007
1008         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
1009                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
1010
1011         /* now try to sort out how to assign memory to the channels */
1012         /* first build hierarchical layer list */
1013         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
1014                 if (imb_exr_split_channel_name(echan, layname, passname) ) {
1015                         ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
1016                         ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
1017
1018                         pass->chan[pass->totchan] = echan;
1019                         pass->totchan++;
1020                         if (pass->totchan >= EXR_PASS_MAXCHAN)
1021                                 break;
1022                 }
1023         }
1024         if (echan) {
1025                 printf("error, too many channels in one pass: %s\n", echan->name);
1026                 IMB_exr_close(data);
1027                 return NULL;
1028         }
1029
1030         /* with some heuristics, try to merge the channels in buffers */
1031         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
1032                 for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
1033                         if (pass->totchan) {
1034                                 pass->rect = (float *)MEM_mapallocN(width * height * pass->totchan * sizeof(float), "pass rect");
1035                                 if (pass->totchan == 1) {
1036                                         echan = pass->chan[0];
1037                                         echan->rect = pass->rect;
1038                                         echan->xstride = 1;
1039                                         echan->ystride = width;
1040                                         pass->chan_id[0] = echan->chan_id;
1041                                 }
1042                                 else {
1043                                         char lookup[256];
1044
1045                                         memset(lookup, 0, sizeof(lookup));
1046
1047                                         /* we can have RGB(A), XYZ(W), UVA */
1048                                         if (pass->totchan == 3 || pass->totchan == 4) {
1049                                                 if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||  pass->chan[2]->chan_id == 'B') {
1050                                                         lookup[(unsigned int)'R'] = 0;
1051                                                         lookup[(unsigned int)'G'] = 1;
1052                                                         lookup[(unsigned int)'B'] = 2;
1053                                                         lookup[(unsigned int)'A'] = 3;
1054                                                 }
1055                                                 else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||  pass->chan[2]->chan_id == 'Y') {
1056                                                         lookup[(unsigned int)'X'] = 0;
1057                                                         lookup[(unsigned int)'Y'] = 1;
1058                                                         lookup[(unsigned int)'Z'] = 2;
1059                                                         lookup[(unsigned int)'W'] = 3;
1060                                                 }
1061                                                 else {
1062                                                         lookup[(unsigned int)'U'] = 0;
1063                                                         lookup[(unsigned int)'V'] = 1;
1064                                                         lookup[(unsigned int)'A'] = 2;
1065                                                 }
1066                                                 for (a = 0; a < pass->totchan; a++) {
1067                                                         echan = pass->chan[a];
1068                                                         echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
1069                                                         echan->xstride = pass->totchan;
1070                                                         echan->ystride = width * pass->totchan;
1071                                                         pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
1072                                                 }
1073                                         }
1074                                         else { /* unknown */
1075                                                 for (a = 0; a < pass->totchan; a++) {
1076                                                         echan = pass->chan[a];
1077                                                         echan->rect = pass->rect + a;
1078                                                         echan->xstride = pass->totchan;
1079                                                         echan->ystride = width * pass->totchan;
1080                                                         pass->chan_id[a] = echan->chan_id;
1081                                                 }
1082                                         }
1083                                 }
1084                         }
1085                 }
1086         }
1087
1088         return data;
1089 }
1090
1091
1092 /* ********************************************************* */
1093
1094 /* debug only */
1095 static void exr_print_filecontents(InputFile *file)
1096 {
1097         const ChannelList &channels = file->header().channels();
1098
1099         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1100                 const Channel &channel = i.channel();
1101                 printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
1102         }
1103 }
1104
1105 /* for non-multilayer, map  R G B A channel names to something that's in this file */
1106 static const char *exr_rgba_channelname(InputFile *file, const char *chan)
1107 {
1108         const ChannelList &channels = file->header().channels();
1109
1110         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1111                 /* const Channel &channel = i.channel(); */ /* Not used yet */
1112                 const char *str = i.name();
1113                 int len = strlen(str);
1114                 if (len) {
1115                         if (BLI_strcasecmp(chan, str + len - 1) == 0) {
1116                                 return str;
1117                         }
1118                 }
1119         }
1120         return chan;
1121 }
1122
1123 static int exr_has_zbuffer(InputFile *file)
1124 {
1125         return !(file->header().channels().findChannel("Z") == NULL);
1126 }
1127
1128 static int exr_has_alpha(InputFile *file)
1129 {
1130         return !(file->header().channels().findChannel("A") == NULL);
1131 }
1132
1133 static bool exr_is_multilayer(InputFile *file)
1134 {
1135         const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
1136         const ChannelList &channels = file->header().channels();
1137         std::set <std::string> layerNames;
1138
1139         /* will not include empty layer names */
1140         channels.layers(layerNames);
1141
1142         if (comments || layerNames.size() > 1)
1143                 return 1;
1144
1145         if (layerNames.size()) {
1146                 /* if layerNames is not empty, it means at least one layer is non-empty,
1147                  * but it also could be layers without names in the file and such case
1148                  * shall be considered a multilayer exr
1149                  *
1150                  * that's what we do here: test whether there're empty layer names together
1151                  * with non-empty ones in the file
1152                  */
1153                 for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) {
1154                         std::string layerName = i.name();
1155                         size_t pos = layerName.rfind ('.');
1156
1157                         if (pos == std::string::npos)
1158                                 return 1;
1159                 }
1160         }
1161
1162         return 0;
1163 }
1164
1165 struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
1166 {
1167         struct ImBuf *ibuf = NULL;
1168         InputFile *file = NULL;
1169
1170         if (imb_is_a_openexr(mem) == 0) return(NULL);
1171
1172         colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
1173
1174         try
1175         {
1176                 Mem_IStream *membuf = new Mem_IStream(mem, size);
1177                 bool is_multi;
1178                 file = new InputFile(*membuf);
1179
1180                 Box2i dw = file->header().dataWindow();
1181                 const int width  = dw.max.x - dw.min.x + 1;
1182                 const int height = dw.max.y - dw.min.y + 1;
1183
1184                 //printf("OpenEXR-load: image data window %d %d %d %d\n",
1185                 //         dw.min.x, dw.min.y, dw.max.x, dw.max.y);
1186
1187                 if (0) // debug
1188                         exr_print_filecontents(file);
1189
1190                 is_multi = exr_is_multilayer(file);
1191
1192                 /* do not make an ibuf when */
1193                 if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
1194                         printf("Error: can't process EXR multilayer file\n");
1195                 }
1196                 else {
1197                         const int is_alpha = exr_has_alpha(file);
1198
1199                         ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
1200
1201                         if (hasXDensity(file->header())) {
1202                                 ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f;
1203                                 ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio();
1204                         }
1205
1206                         ibuf->ftype = OPENEXR;
1207
1208                         if (!(flags & IB_test)) {
1209                                 if (is_multi) { /* only enters with IB_multilayer flag set */
1210                                         /* constructs channels for reading, allocates memory in channels */
1211                                         ExrHandle *handle = imb_exr_begin_read_mem(file, width, height);
1212                                         if (handle) {
1213                                                 IMB_exr_read_channels(handle);
1214                                                 ibuf->userdata = handle;         /* potential danger, the caller has to check for this! */
1215                                         }
1216                                 }
1217                                 else {
1218                                         FrameBuffer frameBuffer;
1219                                         float *first;
1220                                         int xstride = sizeof(float) * 4;
1221                                         int ystride = -xstride * width;
1222
1223                                         imb_addrectfloatImBuf(ibuf);
1224
1225                                         /* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
1226                                         first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
1227                                         /* but, since we read y-flipped (negative y stride) we move to last scanline */
1228                                         first += 4 * (height - 1) * width;
1229
1230                                         frameBuffer.insert(exr_rgba_channelname(file, "R"),
1231                                                            Slice(Imf::FLOAT,  (char *) first, xstride, ystride));
1232                                         frameBuffer.insert(exr_rgba_channelname(file, "G"),
1233                                                            Slice(Imf::FLOAT,  (char *) (first + 1), xstride, ystride));
1234                                         frameBuffer.insert(exr_rgba_channelname(file, "B"),
1235                                                            Slice(Imf::FLOAT,  (char *) (first + 2), xstride, ystride));
1236
1237                                         /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
1238                                         frameBuffer.insert(exr_rgba_channelname(file, "A"),
1239                                                            Slice(Imf::FLOAT,  (char *) (first + 3), xstride, ystride, 1, 1, 1.0f));
1240
1241                                         if (exr_has_zbuffer(file)) {
1242                                                 float *firstz;
1243
1244                                                 addzbuffloatImBuf(ibuf);
1245                                                 firstz = ibuf->zbuf_float - (dw.min.x - dw.min.y * width);
1246                                                 firstz += (height - 1) * width;
1247                                                 frameBuffer.insert("Z", Slice(Imf::FLOAT,  (char *)firstz, sizeof(float), -width * sizeof(float)));
1248                                         }
1249
1250                                         file->setFrameBuffer(frameBuffer);
1251                                         file->readPixels(dw.min.y, dw.max.y);
1252
1253                                         // XXX, ImBuf has no nice way to deal with this.
1254                                         // ideally IM_rect would be used when the caller wants a rect BUT
1255                                         // at the moment all functions use IM_rect.
1256                                         // Disabling this is ok because all functions should check if a rect exists and create one on demand.
1257                                         //
1258                                         // Disabling this because the sequencer frees immediate.
1259                                         //
1260                                         // if (flag & IM_rect)
1261                                         //     IMB_rect_from_float(ibuf);
1262
1263                                         /* file is no longer needed */
1264                                         delete file;
1265                                 }
1266                         }
1267
1268                         if (flags & IB_alphamode_detect)
1269                                 ibuf->flags |= IB_alphamode_premul;
1270                 }
1271                 return(ibuf);
1272         }
1273         catch (const std::exception &exc)
1274         {
1275                 std::cerr << exc.what() << std::endl;
1276                 if (ibuf) IMB_freeImBuf(ibuf);
1277                 delete file;
1278
1279                 return (0);
1280         }
1281
1282 }
1283
1284 void imb_initopenexr(void)
1285 {
1286         int num_threads = BLI_system_thread_count();
1287
1288         setGlobalThreadCount(num_threads);
1289 }
1290
1291 } // export "C"