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