647d90f792785d2bdbd5579b402c79380c780d7e
[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() = ZIP_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, int compress)
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         openexr_header_compression(&header, compress);
446         header.lineOrder() = DECREASING_Y;
447         
448         header.insert ("BlenderMultiChannel", StringAttribute ("Blender V2.43"));
449         
450         data->ofile = new OutputFile(filename, header);
451 }
452
453 void IMB_exrtile_begin_write(void *handle, char *filename, int width, int height, int tilex, int tiley)
454 {
455         ExrHandle *data= (ExrHandle *)handle;
456         Header header (width, height);
457         ExrChannel *echan;
458         
459         data->tilex= tilex;
460         data->tiley= tiley;
461         data->width= width;
462         data->height= height;
463         
464         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
465                 header.channels().insert (echan->name, Channel (FLOAT));
466         
467         header.setTileDescription (TileDescription (tilex, tiley, ONE_LEVEL));
468         header.lineOrder() = RANDOM_Y;
469         header.compression() = RLE_COMPRESSION;
470         
471         header.insert ("BlenderMultiChannel", StringAttribute ("Blender V2.43"));
472         
473         data->tofile = new TiledOutputFile(filename, header);
474 }
475
476 /* read from file */
477 int IMB_exr_begin_read(void *handle, char *filename, int *width, int *height)
478 {
479         ExrHandle *data= (ExrHandle *)handle;
480         
481         if(BLI_exists(filename)) {
482                 data->ifile = new InputFile(filename);
483                 if(data->ifile) {
484                         Box2i dw = data->ifile->header().dataWindow();
485                         data->width= *width  = dw.max.x - dw.min.x + 1;
486                         data->height= *height = dw.max.y - dw.min.y + 1;
487                         
488                         const ChannelList &channels = data->ifile->header().channels();
489                         
490                         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
491                                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
492                         
493                         return 1;
494                 }
495         }
496         return 0;
497 }
498
499 /* still clumsy name handling, layers/channels can be ordered as list in list later */
500 void IMB_exr_set_channel(void *handle, char *layname, char *passname, int xstride, int ystride, float *rect)
501 {
502         ExrHandle *data= (ExrHandle *)handle;
503         ExrChannel *echan;
504         char name[EXR_TOT_MAXNAME + 1];
505         
506         if(layname) {
507                 char lay[EXR_LAY_MAXNAME+1], pass[EXR_PASS_MAXNAME+1];
508                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
509                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
510                 
511                 sprintf(name, "%s.%s", lay, pass);
512         }
513         else
514                 BLI_strncpy(name, passname, EXR_TOT_MAXNAME-1);
515         
516         
517         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
518                 if(strcmp(echan->name, name)==0)
519                         break;
520         
521         if(echan) {
522                 echan->xstride= xstride;
523                 echan->ystride= ystride;
524                 echan->rect= rect;
525         }
526         else
527                 printf("IMB_exrtile_set_channel error %s\n", name);
528 }
529
530 void IMB_exrtile_clear_channels(void *handle)
531 {
532         ExrHandle *data= (ExrHandle *)handle;
533         BLI_freelistN(&data->channels);
534 }
535
536 void IMB_exrtile_write_channels(void *handle, int partx, int party)
537 {
538         ExrHandle *data= (ExrHandle *)handle;
539         FrameBuffer frameBuffer;
540         ExrChannel *echan;
541         
542         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
543                 float *rect= echan->rect - echan->xstride*partx - echan->ystride*party;
544
545                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)rect, 
546                                                         echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
547         }
548         
549         data->tofile->setFrameBuffer (frameBuffer);
550         // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
551         data->tofile->writeTile (partx/data->tilex, party/data->tiley); 
552         
553 }
554
555 void IMB_exr_write_channels(void *handle)
556 {
557         ExrHandle *data= (ExrHandle *)handle;
558         FrameBuffer frameBuffer;
559         ExrChannel *echan;
560         
561         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
562                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)echan->rect, 
563                                                                                                 echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
564         
565         data->ofile->setFrameBuffer (frameBuffer);
566         data->ofile->writePixels (data->height);        
567         
568 }
569
570 void IMB_exr_read_channels(void *handle)
571 {
572         ExrHandle *data= (ExrHandle *)handle;
573         FrameBuffer frameBuffer;
574         ExrChannel *echan;
575         
576         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
577                 /* no datawindow correction needed */
578                 if(echan->rect)
579                         frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)echan->rect, 
580                                                                                                 echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
581                 else 
582                         printf("warning, channel with no rect set %s\n", echan->name);
583         }
584         
585         data->ifile->setFrameBuffer (frameBuffer);
586         data->ifile->readPixels (0, data->height-1);    
587 }
588
589 void IMB_exr_multilayer_convert(void *handle, void *base,  
590                                                                 void * (*addlayer)(void *base, char *str), 
591                                                                 void (*addpass)(void *base, void *lay, char *str, 
592                                                                                                 float *rect, int totchan, char *chan_id))
593 {
594         ExrHandle *data= (ExrHandle *)handle;
595         ExrLayer *lay;
596         ExrPass *pass;
597
598         if(data->layers.first==NULL) {
599                 printf("cannot convert multilayer, no layers in handle\n");
600                 return;
601         }
602
603         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
604                 void *laybase= addlayer(base, lay->name);
605                 if(laybase) {
606                         for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
607                                 addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
608                                 pass->rect= NULL;
609                         }
610                 }
611         }
612 }
613
614
615 void IMB_exr_close(void *handle)
616 {
617         ExrHandle *data= (ExrHandle *)handle;
618         ExrChannel *echan;
619         ExrLayer *lay;
620         ExrPass *pass;
621         
622         if(data->ifile)
623                 delete data->ifile;
624         else if(data->ofile)
625                 delete data->ofile;
626         else if(data->tofile)
627                 delete data->tofile;
628         
629         data->ifile= NULL;
630         data->ofile= NULL;
631         data->tofile= NULL;
632         
633         BLI_freelistN(&data->channels);
634         
635         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
636                 for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next)
637                         if(pass->rect)
638                                 MEM_freeN(pass->rect);
639                 BLI_freelistN(&lay->passes);
640         }
641         BLI_freelistN(&data->layers);
642         
643         BLI_remlink(&exrhandles, data);
644         MEM_freeN(data);
645 }
646
647 /* ********* */
648
649 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
650 {
651         int plen, len= strlen(echan->name);
652         
653         if(len < 4) {
654                 printf("multilayer read: name too short: %s\n", echan->name);
655                 return 0;
656         }
657         if(echan->name[len-2]!='.') {
658                 printf("multilayer read: name has no Channel: %s\n", echan->name);
659                 return 0;
660         }
661         echan->chan_id= echan->name[len-1];
662         
663         len-= 3;
664         while(len>=0) {
665                 if(echan->name[len]=='.')
666                         break;
667                 len--;
668         }
669         BLI_strncpy(passname, echan->name+len+1, EXR_PASS_MAXNAME);
670         plen= strlen(passname);
671         if(plen < 3) {
672                 printf("multilayer read: should not happen: %s\n", echan->name);
673                 return 0;
674         }
675         passname[plen-2]= 0;
676         
677         if(len<1)
678                 layname[0]= 0;
679         else {
680                 BLI_strncpy(layname, echan->name, EXR_LAY_MAXNAME);
681                 layname[len]= 0;
682         }
683         // printf("found lay %s pass %s chan %c\n", layname, passname, echan->chan_id);
684         return 1;
685 }
686
687 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
688 {
689         ExrLayer *lay;
690         
691         for(lay= (ExrLayer *)lb->first; lay; lay= lay->next) {
692                 if( strcmp(lay->name, layname)==0 )
693                         return lay;
694         }
695         lay= (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
696         BLI_addtail(lb, lay);
697         BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
698         
699         return lay;
700 }
701
702 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
703 {
704         ExrPass *pass;
705         
706         for(pass= (ExrPass *)lb->first; pass; pass= pass->next) {
707                 if( strcmp(pass->name, passname)==0 )
708                         return pass;
709         }
710         
711         pass= (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
712
713         if(strcmp(passname, "Combined")==0)
714                 BLI_addhead(lb, pass);
715         else
716                 BLI_addtail(lb, pass);
717         
718         BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
719         
720         return pass;
721 }
722
723 /* creates channels, makes a hierarchy and assigns memory to channels */
724 static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
725 {
726         ExrLayer *lay;
727         ExrPass *pass;
728         ExrChannel *echan;
729         ExrHandle *data= (ExrHandle *)IMB_exr_get_handle();
730         int a;
731         char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
732         
733         data->ifile= file;
734         data->width= width;
735         data->height= height;
736         
737         const ChannelList &channels = data->ifile->header().channels();
738
739         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
740                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
741         
742         /* now try to sort out how to assign memory to the channels */
743         /* first build hierarchical layer list */
744         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
745                 if( imb_exr_split_channel_name(echan, layname, passname) ) {
746                         ExrLayer *lay= imb_exr_get_layer(&data->layers, layname);
747                         ExrPass *pass= imb_exr_get_pass(&lay->passes, passname);
748                         
749                         pass->chan[pass->totchan]= echan;
750                         pass->totchan++;
751                         if(pass->totchan>=EXR_PASS_MAXCHAN)
752                                 break;
753                 }
754         }
755         if(echan) {
756                 printf("error, too many channels in one pass: %s\n", echan->name);
757                 IMB_exr_close(data);
758                 return NULL;
759         }
760         
761         /* with some heuristics, try to merge the channels in buffers */
762         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
763                 for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
764                         if(pass->totchan) {
765                                 pass->rect= (float *)MEM_mapallocN(width*height*pass->totchan*sizeof(float), "pass rect");
766                                 if(pass->totchan==1) {
767                                         echan= pass->chan[0];
768                                         echan->rect= pass->rect;
769                                         echan->xstride= 1;
770                                         echan->ystride= width;
771                                         pass->chan_id[0]= echan->chan_id;
772                                 }
773                                 else {
774                                         char lookup[256];
775                                         
776                                         memset(lookup, 0, sizeof(lookup));
777                                                    
778                                         /* we can have RGB(A), XYZ(W), UVA */
779                                         if(pass->totchan==3 || pass->totchan==4) {
780                                                 if(pass->chan[0]->chan_id=='B' || pass->chan[1]->chan_id=='B' ||  pass->chan[2]->chan_id=='B') {
781                                                         lookup['R']= 0;
782                                                         lookup['G']= 1;
783                                                         lookup['B']= 2;
784                                                         lookup['A']= 3;
785                                                 }
786                                                 else if(pass->chan[0]->chan_id=='Y' || pass->chan[1]->chan_id=='Y' ||  pass->chan[2]->chan_id=='Y') {
787                                                         lookup['X']= 0;
788                                                         lookup['Y']= 1;
789                                                         lookup['Z']= 2;
790                                                         lookup['W']= 3;
791                                                 }
792                                                 else {
793                                                         lookup['U']= 0;
794                                                         lookup['V']= 1;
795                                                         lookup['A']= 2;
796                                                 }
797                                                 for(a=0; a<pass->totchan; a++) {
798                                                         echan= pass->chan[a];
799                                                         echan->rect= pass->rect + lookup[echan->chan_id];
800                                                         echan->xstride= pass->totchan;
801                                                         echan->ystride= width*pass->totchan;
802                                                         pass->chan_id[ lookup[echan->chan_id] ]= echan->chan_id;
803                                                 }
804                                         }
805                                         else { /* unknown */
806                                                 for(a=0; a<pass->totchan; a++) {
807                                                         echan= pass->chan[a];
808                                                         echan->rect= pass->rect + a;
809                                                         echan->xstride= pass->totchan;
810                                                         echan->ystride= width*pass->totchan;
811                                                         pass->chan_id[a]= echan->chan_id;
812                                                 }
813                                         }
814                                 }
815                         }
816                 }
817         }
818         
819         return data;
820 }
821
822
823 /* ********************************************************* */
824
825 typedef struct RGBA
826 {
827         float r;
828         float g;
829         float b;
830         float a;
831 } RGBA;
832
833
834 static void exr_print_filecontents(InputFile *file)
835 {
836         const ChannelList &channels = file->header().channels();
837         
838         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
839         {
840                 const Channel &channel = i.channel();
841                 printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
842         }
843 }
844
845 static int exr_has_zbuffer(InputFile *file)
846 {
847         const ChannelList &channels = file->header().channels();
848         
849         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
850         {
851                 const Channel &channel = i.channel();
852                 if(strcmp("Z", i.name())==0)
853                         return 1;
854         }
855         return 0;
856 }
857
858 static int exr_is_renderresult(InputFile *file)
859 {
860         const StringAttribute *comments= file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
861         if(comments)
862 //              if(comments->value() == "Blender MultiChannel")
863                         return 1;
864         return 0;
865 }
866
867 struct ImBuf *imb_load_openexr(unsigned char *mem, int size, int flags)
868 {
869         struct ImBuf *ibuf = NULL;
870         InputFile *file = NULL;
871         
872         if (imb_is_a_openexr(mem) == 0) return(NULL);
873         
874         try
875         {
876                 Mem_IStream membuf(mem, size); 
877                 int is_multi;
878                 file = new InputFile(membuf);
879                 
880                 Box2i dw = file->header().dataWindow();
881                 int width  = dw.max.x - dw.min.x + 1;
882                 int height = dw.max.y - dw.min.y + 1;
883                 
884                 //printf("OpenEXR-load: image data window %d %d %d %d\n", 
885                 //         dw.min.x, dw.min.y, dw.max.x, dw.max.y);
886
887                 // exr_print_filecontents(file);
888                 
889                 is_multi= exr_is_renderresult(file);
890                 
891                 /* do not make an ibuf when */
892                 if(is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) 
893                 {
894                         printf("Error: can't process EXR multilayer file\n");
895                 }
896                 else {
897                 
898                         ibuf = IMB_allocImBuf(width, height, 32, 0, 0);
899                         ibuf->ftype = OPENEXR;
900                         
901                         if (!(flags & IB_test))
902                         {
903                                 if(is_multi) /* only enters with IB_multilayer flag set */
904                                 {
905                                         /* constructs channels for reading, allocates memory in channels */
906                                         ExrHandle *handle= imb_exr_begin_read_mem(file, width, height);
907                                         if(handle) {
908                                                 IMB_exr_read_channels(handle);
909                                                 ibuf->userdata= handle;                 /* potential danger, the caller has to check for this! */
910                                                 return ibuf;
911                                         }
912                                 }
913                                 else {
914                                         FrameBuffer frameBuffer;
915                                         float *first;
916                                         int xstride = sizeof(float) * 4;
917                                         int ystride = - xstride*width;
918                                         
919                                         imb_addrectfloatImBuf(ibuf);
920                                         
921                                         /* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
922                                         first= ibuf->rect_float - 4*(dw.min.x - dw.min.y*width);
923                                         /* but, since we read y-flipped (negative y stride) we move to last scanline */
924                                         first+= 4*(height-1)*width;
925                                         
926                                         frameBuffer.insert ("R", Slice (FLOAT,  (char *) first, xstride, ystride));
927                                         frameBuffer.insert ("G", Slice (FLOAT,  (char *) (first+1), xstride, ystride));
928                                         frameBuffer.insert ("B", Slice (FLOAT,  (char *) (first+2), xstride, ystride));
929                                                                                                                                                         /* 1.0 is fill value */
930                                         frameBuffer.insert ("A", Slice (FLOAT,  (char *) (first+3), xstride, ystride, 1, 1, 1.0f));
931
932                                         if(exr_has_zbuffer(file)) 
933                                         {
934                                                 float *firstz;
935                                                 
936                                                 addzbuffloatImBuf(ibuf);
937                                                 firstz= ibuf->zbuf_float - (dw.min.x - dw.min.y*width);
938                                                 firstz+= (height-1)*width;
939                                                 frameBuffer.insert ("Z", Slice (FLOAT,  (char *)firstz , sizeof(float), -width*sizeof(float)));
940                                         }
941                                         
942                                         file->setFrameBuffer (frameBuffer);
943                                         file->readPixels (dw.min.y, dw.max.y);
944                                         
945                                         IMB_rect_from_float(ibuf);
946                                 }
947                         }
948                         
949                         delete file;
950                 }
951                 
952                 return(ibuf);
953                                 
954         }
955         catch (const std::exception &exc)
956         {
957                 std::cerr << exc.what() << std::endl;
958                 if (ibuf) IMB_freeImBuf(ibuf);
959                 
960                 return (0);
961         }
962         
963 }
964
965
966 } // export "C"