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