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