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