Blender  V2.59
openexr_api.cpp
Go to the documentation of this file.
00001 
00004 /*
00005 *
00006  * ***** BEGIN GPLLICENSE BLOCK *****
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License
00010  * as published by the Free Software Foundation; either version 2
00011  * of the License, or (at your option) any later version. 
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software Foundation,
00020  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  *
00022  * Copyright by Gernot Ziegler <gz@lysator.liu.se>.
00023  * All rights reserved.
00024  *
00025  * The Original Code is: all of this file.
00026  *
00027  * Contributor(s): Austin Benesh, Ton Roosendaal (float, half, speedup, cleanup...).
00028  *
00029  * ***** END GPL LICENSE BLOCK *****
00030  */
00031 
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <stddef.h>
00035 #include <string>
00036 
00037 
00038 #include <openexr_api.h>
00039 
00040 extern "C"
00041 {
00042 
00043 // The following prevents a linking error in debug mode for MSVC using the libs in CVS
00044 #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(_DEBUG) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00045 _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
00046 {
00047 }
00048 #endif
00049 
00050 #include "MEM_guardedalloc.h"
00051 
00052 #include "BLI_blenlib.h"
00053 #include "BLI_math_color.h"
00054 
00055 #include "IMB_imbuf_types.h"
00056 #include "IMB_imbuf.h"
00057 #include "IMB_allocimbuf.h"
00058 #include "IMB_metadata.h"
00059 
00060 #include "openexr_multi.h"
00061 }
00062 
00063 #include <iostream>
00064 
00065 #if defined (_WIN32) && !defined(FREE_WINDOWS)
00066 #include <half.h>
00067 #include <IlmImf/ImfVersion.h>
00068 #include <IlmImf/ImfArray.h>
00069 #include <IlmImf/ImfIO.h>
00070 #include <IlmImf/ImfChannelList.h>
00071 #include <IlmImf/ImfPixelType.h>
00072 #include <IlmImf/ImfInputFile.h>
00073 #include <IlmImf/ImfOutputFile.h>
00074 #include <IlmImf/ImfCompression.h>
00075 #include <IlmImf/ImfCompressionAttribute.h>
00076 #include <IlmImf/ImfStringAttribute.h>
00077 #include <Imath/ImathBox.h>
00078 #else
00079 #include <half.h>
00080 #include <ImfVersion.h>
00081 #include <ImathBox.h>
00082 #include <ImfArray.h>
00083 #include <ImfIO.h>
00084 #include <ImfChannelList.h>
00085 #include <ImfPixelType.h>
00086 #include <ImfInputFile.h>
00087 #include <ImfOutputFile.h>
00088 #include <ImfCompression.h>
00089 #include <ImfCompressionAttribute.h>
00090 #include <ImfStringAttribute.h>
00091 #endif
00092 
00093 using namespace Imf;
00094 using namespace Imath;
00095 
00096 class Mem_IStream: public IStream
00097 {
00098 public:
00099         
00100         Mem_IStream (unsigned char *exrbuf, size_t exrsize):
00101     IStream("dummy"), _exrpos (0), _exrsize(exrsize)  { _exrbuf = exrbuf; }
00102         
00103         virtual bool    read (char c[], int n);
00104         virtual Int64   tellg ();
00105         virtual void    seekg (Int64 pos);
00106         virtual void    clear ();
00107         //virtual ~Mem_IStream() {}; // unused
00108         
00109 private:
00110                 
00111                 Int64 _exrpos;
00112         Int64 _exrsize;
00113         unsigned char *_exrbuf;
00114 };
00115 
00116 bool Mem_IStream::read (char c[], int n)
00117 {
00118         if (n + _exrpos <= _exrsize)
00119     {
00120                 memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
00121                 _exrpos += n;
00122                 return true;
00123     }
00124         else
00125                 return false;
00126 }
00127 
00128 Int64 Mem_IStream::tellg ()
00129 {
00130         return _exrpos;
00131 }
00132 
00133 void Mem_IStream::seekg (Int64 pos)
00134 {
00135         _exrpos = pos;
00136 }
00137 
00138 void Mem_IStream::clear () 
00139 { 
00140 }
00141 
00142 struct _RGBAZ
00143 {
00144         half r;
00145         half g;
00146         half b;
00147         half a;
00148         half z;
00149 };
00150 
00151 typedef struct _RGBAZ RGBAZ;
00152 
00153 extern "C"
00154 {
00155         
00156 int imb_is_a_openexr(unsigned char *mem)
00157 {
00158         return Imf::isImfMagic ((const char *)mem);
00159 }
00160 
00161 static void openexr_header_compression(Header *header, int compression)
00162 {
00163         switch(compression)
00164         {
00165                 case 0:
00166                         header->compression() = NO_COMPRESSION;
00167                         break;
00168                 case 1:
00169                         header->compression() = PXR24_COMPRESSION;
00170                         break;
00171                 case 2:
00172                         header->compression() = ZIP_COMPRESSION;
00173                         break;
00174                 case 3:
00175                         header->compression() = PIZ_COMPRESSION;
00176                         break;
00177                 case 4:
00178                         header->compression() = RLE_COMPRESSION;
00179                         break;
00180                 default:
00181                         header->compression() = ZIP_COMPRESSION;
00182                         break; 
00183         }
00184 }
00185 
00186 static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
00187 {
00188         ImMetaData* info;
00189 
00190         for(info= ibuf->metadata; info; info= info->next)
00191                 header->insert(info->key, StringAttribute(info->value));
00192 }
00193 
00194 static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags)
00195 {
00196         int channels = ibuf->channels;
00197         int width = ibuf->x;
00198         int height = ibuf->y;
00199         int write_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL;   // summarize
00200         
00201         try
00202         {
00203                 Header header (width, height);
00204                 
00205                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
00206                 openexr_header_metadata(&header, ibuf);
00207                 
00208                 header.channels().insert ("R", Channel (HALF));
00209                 header.channels().insert ("G", Channel (HALF));
00210                 header.channels().insert ("B", Channel (HALF));
00211                 if (ibuf->depth==32 && channels >= 4)
00212                         header.channels().insert ("A", Channel (HALF));
00213                 if (write_zbuf)         // z we do as float always
00214                         header.channels().insert ("Z", Channel (FLOAT));
00215                 
00216                 FrameBuffer frameBuffer;                        
00217                 OutputFile *file = new OutputFile(name, header);                        
00218                 
00219                 /* we store first everything in half array */
00220                 RGBAZ *pixels = new RGBAZ[height * width];
00221                 RGBAZ *to = pixels;
00222                 int xstride= sizeof (RGBAZ);
00223                 int ystride= xstride*width;
00224 
00225                 /* indicate used buffers */
00226                 frameBuffer.insert ("R", Slice (HALF,  (char *) &pixels[0].r, xstride, ystride));       
00227                 frameBuffer.insert ("G", Slice (HALF,  (char *) &pixels[0].g, xstride, ystride));
00228                 frameBuffer.insert ("B", Slice (HALF,  (char *) &pixels[0].b, xstride, ystride));
00229                 if (ibuf->depth==32 && channels >= 4)
00230                         frameBuffer.insert ("A", Slice (HALF, (char *) &pixels[0].a, xstride, ystride));
00231                 if (write_zbuf)
00232                         frameBuffer.insert ("Z", Slice (FLOAT, (char *)(ibuf->zbuf_float + (height-1)*width),
00233                                                                                         sizeof(float), sizeof(float) * -width));
00234                 if(ibuf->rect_float) {
00235                         float *from;
00236 
00237                         if(ibuf->profile == IB_PROFILE_LINEAR_RGB) {
00238                                 for (int i = ibuf->y-1; i >= 0; i--)
00239                                 {
00240                                         from= ibuf->rect_float + channels*i*width;
00241 
00242                                         for (int j = ibuf->x; j > 0; j--)
00243                                         {
00244                                                 to->r = from[0];
00245                                                 to->g = from[1];
00246                                                 to->b = from[2];
00247                                                 to->a = (channels >= 4)? from[3]: 1.0f;
00248                                                 to++; from += 4;
00249                                         }
00250                                 }
00251                         }
00252                         else {
00253                                 for (int i = ibuf->y-1; i >= 0; i--)
00254                                 {
00255                                         from= ibuf->rect_float + channels*i*width;
00256 
00257                                         for (int j = ibuf->x; j > 0; j--)
00258                                         {
00259                                                 to->r = srgb_to_linearrgb(from[0]);
00260                                                 to->g = srgb_to_linearrgb(from[1]);
00261                                                 to->b = srgb_to_linearrgb(from[2]);
00262                                                 to->a = (channels >= 4)? from[3]: 1.0f;
00263                                                 to++; from += 4;
00264                                         }
00265                                 }
00266                         }
00267                 }
00268                 else {
00269                         unsigned char *from;
00270 
00271                         if(ibuf->profile == IB_PROFILE_LINEAR_RGB) {
00272                                 for (int i = ibuf->y-1; i >= 0; i--)
00273                                 {
00274                                         from= (unsigned char *)ibuf->rect + channels*i*width;
00275 
00276                                         for (int j = ibuf->x; j > 0; j--)
00277                                         {
00278                                                 to->r = (float)(from[0])/255.0;
00279                                                 to->g = (float)(from[1])/255.0;
00280                                                 to->b = (float)(from[2])/255.0;
00281                                                 to->a = (float)(channels >= 4) ? from[3]/255.0 : 1.0f;
00282                                                 to++; from += 4;
00283                                         }
00284                                 }
00285                         }
00286                         else {
00287                                 for (int i = ibuf->y-1; i >= 0; i--)
00288                                 {
00289                                         from= (unsigned char *)ibuf->rect + channels*i*width;
00290 
00291                                         for (int j = ibuf->x; j > 0; j--)
00292                                         {
00293                                                 to->r = srgb_to_linearrgb((float)from[0] / 255.0);
00294                                                 to->g = srgb_to_linearrgb((float)from[1] / 255.0);
00295                                                 to->b = srgb_to_linearrgb((float)from[2] / 255.0);
00296                                                 to->a = channels >= 4 ? (float)from[3]/255.0 : 1.0f;
00297                                                 to++; from += 4;
00298                                         }
00299                                 }
00300                         }
00301                 }
00302                 
00303 //              printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
00304                 
00305                 file->setFrameBuffer (frameBuffer);                               
00306                 file->writePixels (height);                                       
00307                 delete file;
00308                 delete [] pixels;
00309         }
00310         catch (const std::exception &exc)
00311         {      
00312                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
00313                 if (ibuf) IMB_freeImBuf(ibuf);
00314                 
00315                 return (0);
00316         }
00317         
00318         return (1);
00319 }
00320 
00321 static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags)
00322 {
00323         int channels = ibuf->channels;
00324         int width = ibuf->x;
00325         int height = ibuf->y;
00326         int write_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL;   // summarize
00327 
00328         try
00329         {
00330                 Header header (width, height);
00331                 
00332                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
00333                 openexr_header_metadata(&header, ibuf);
00334                 
00335                 header.channels().insert ("R", Channel (FLOAT));
00336                 header.channels().insert ("G", Channel (FLOAT));
00337                 header.channels().insert ("B", Channel (FLOAT));
00338                 if (ibuf->depth==32 && channels >= 4)
00339                         header.channels().insert ("A", Channel (FLOAT));
00340                 if (write_zbuf)
00341                         header.channels().insert ("Z", Channel (FLOAT));
00342                 
00343                 FrameBuffer frameBuffer;                        
00344                 OutputFile *file = new OutputFile(name, header);                        
00345                 int xstride = sizeof(float) * channels;
00346                 int ystride = - xstride*width;
00347                 float *rect[4] = {NULL, NULL, NULL, NULL};
00348 
00349                 /* last scanline, stride negative */
00350                 rect[0]= ibuf->rect_float + channels*(height-1)*width;
00351                 rect[1]= rect[0]+1;
00352                 rect[2]= rect[0]+2;
00353                 rect[3]= (channels >= 4)? rect[0]+3:rect[0]; /* red as alpha, is this needed since alpha isnt written? */
00354 
00355                 frameBuffer.insert ("R", Slice (FLOAT,  (char *)rect[0], xstride, ystride));
00356                 frameBuffer.insert ("G", Slice (FLOAT,  (char *)rect[1], xstride, ystride));
00357                 frameBuffer.insert ("B", Slice (FLOAT,  (char *)rect[2], xstride, ystride));
00358                 if (ibuf->depth==32 && channels >= 4)
00359                         frameBuffer.insert ("A", Slice (FLOAT,  (char *)rect[3], xstride, ystride));
00360                 if (write_zbuf)
00361                         frameBuffer.insert ("Z", Slice (FLOAT, (char *) (ibuf->zbuf_float + (height-1)*width),
00362                                                                                         sizeof(float), sizeof(float) * -width));
00363                 file->setFrameBuffer (frameBuffer);                               
00364                 file->writePixels (height);                                       
00365                 delete file;
00366         }
00367         catch (const std::exception &exc)
00368         {      
00369                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
00370                 if (ibuf) IMB_freeImBuf(ibuf);
00371                 
00372                 return (0);
00373         }
00374         
00375         return (1);
00376         //      printf("OpenEXR-save: Done.\n");
00377 }
00378 
00379 
00380 int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
00381 {
00382         if (flags & IB_mem) 
00383         {
00384                 printf("OpenEXR-save: Create EXR in memory CURRENTLY NOT SUPPORTED !\n");
00385                 imb_addencodedbufferImBuf(ibuf);
00386                 ibuf->encodedsize = 0;    
00387                 return(0);
00388         } 
00389         
00390         if (ibuf->ftype & OPENEXR_HALF) 
00391                 return imb_save_openexr_half(ibuf, name, flags);
00392         else {
00393                 /* when no float rect, we save as half (16 bits is sufficient) */
00394                 if (ibuf->rect_float==NULL)
00395                         return imb_save_openexr_half(ibuf, name, flags);
00396                 else
00397                         return imb_save_openexr_float(ibuf, name, flags);
00398         }
00399 }
00400 
00401 /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */
00402 
00403 /* naming rules:
00404    - parse name from right to left
00405    - last character is channel ID, 1 char like 'A' 'R' 'G' 'B' 'X' 'Y' 'Z' 'W' 'U' 'V'
00406    - separated with a dot; the Pass name (like "Depth", "Color", "Diffuse" or "Combined")
00407    - separated with a dot: the Layer name (like "Lamp1" or "Walls" or "Characters")
00408 */
00409 
00410 static ListBase exrhandles= {NULL, NULL};
00411 
00412 typedef struct ExrHandle {
00413         struct ExrHandle *next, *prev;
00414         
00415         InputFile *ifile;
00416         TiledOutputFile *tofile;
00417         OutputFile *ofile;
00418         int tilex, tiley;
00419         int width, height;
00420         int mipmap;
00421         
00422         ListBase channels;      /* flattened out, ExrChannel */
00423         ListBase layers;        /* hierarchical, pointing in end to ExrChannel */
00424 } ExrHandle;
00425 
00426 /* flattened out channel */
00427 typedef struct ExrChannel {
00428         struct ExrChannel *next, *prev;
00429         
00430         char name[EXR_TOT_MAXNAME+1];   /* full name of layer+pass */
00431         int xstride, ystride;           /* step to next pixel, to next scanline */
00432         float *rect;                            /* first pointer to write in */
00433         char chan_id;                           /* quick lookup of channel char */
00434 } ExrChannel;
00435 
00436 
00437 /* hierarchical; layers -> passes -> channels[] */
00438 typedef struct ExrPass {
00439         struct ExrPass *next, *prev;
00440         char name[EXR_PASS_MAXNAME];
00441         int totchan;
00442         float *rect;
00443         struct ExrChannel *chan[EXR_PASS_MAXCHAN];
00444         char chan_id[EXR_PASS_MAXCHAN];
00445 } ExrPass;
00446 
00447 typedef struct ExrLayer {
00448         struct ExrLayer *next, *prev;
00449         char name[EXR_LAY_MAXNAME+1];
00450         ListBase passes;
00451 } ExrLayer;
00452 
00453 /* ********************** */
00454 
00455 void *IMB_exr_get_handle(void)
00456 {
00457         ExrHandle *data= (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle");
00458         BLI_addtail(&exrhandles, data);
00459         return data;
00460 }
00461 
00462 /* adds flattened ExrChannels */
00463 /* xstride, ystride and rect can be done in set_channel too, for tile writing */
00464 void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
00465 {
00466         ExrHandle *data= (ExrHandle *)handle;
00467         ExrChannel *echan;
00468         
00469         echan= (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel");
00470         
00471         if(layname) {
00472                 char lay[EXR_LAY_MAXNAME+1], pass[EXR_PASS_MAXNAME+1];
00473                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
00474                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
00475 
00476                 sprintf(echan->name, "%s.%s", lay, pass);
00477         }
00478         else
00479                 BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME-1);
00480         
00481         echan->xstride= xstride;
00482         echan->ystride= ystride;
00483         echan->rect= rect;
00484         
00485         // printf("added channel %s\n", echan->name);
00486         BLI_addtail(&data->channels, echan);
00487 }
00488 
00489 /* only used for writing temp. render results (not image files) */
00490 void IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress)
00491 {
00492         ExrHandle *data= (ExrHandle *)handle;
00493         Header header (width, height);
00494         ExrChannel *echan;
00495         
00496         data->width= width;
00497         data->height= height;
00498         
00499         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
00500                 header.channels().insert (echan->name, Channel (FLOAT));
00501         
00502         openexr_header_compression(&header, compress);
00503         // openexr_header_metadata(&header, ibuf); // no imbuf. cant write
00504         /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
00505         
00506         header.insert ("BlenderMultiChannel", StringAttribute ("Blender V2.55.1 and newer"));
00507         
00508         data->ofile = new OutputFile(filename, header);
00509 }
00510 
00511 void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
00512 {
00513         ExrHandle *data= (ExrHandle *)handle;
00514         Header header (width, height);
00515         ExrChannel *echan;
00516         
00517         data->tilex= tilex;
00518         data->tiley= tiley;
00519         data->width= width;
00520         data->height= height;
00521         data->mipmap= mipmap;
00522         
00523         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
00524                 header.channels().insert (echan->name, Channel (FLOAT));
00525         
00526         header.setTileDescription (TileDescription (tilex, tiley, (mipmap)? MIPMAP_LEVELS: ONE_LEVEL));
00527         header.lineOrder() = RANDOM_Y;
00528         header.compression() = RLE_COMPRESSION;
00529         
00530         header.insert ("BlenderMultiChannel", StringAttribute ("Blender V2.43"));
00531         
00532         data->tofile = new TiledOutputFile(filename, header);
00533 }
00534 
00535 /* read from file */
00536 int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
00537 {
00538         ExrHandle *data= (ExrHandle *)handle;
00539         
00540         if(BLI_exists(filename) && BLI_filepathsize(filename)>32) {     /* 32 is arbitrary, but zero length files crashes exr */
00541                 data->ifile = new InputFile(filename);
00542                 if(data->ifile) {
00543                         Box2i dw = data->ifile->header().dataWindow();
00544                         data->width= *width  = dw.max.x - dw.min.x + 1;
00545                         data->height= *height = dw.max.y - dw.min.y + 1;
00546                         
00547                         const ChannelList &channels = data->ifile->header().channels();
00548                         
00549                         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00550                                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
00551                         
00552                         return 1;
00553                 }
00554         }
00555         return 0;
00556 }
00557 
00558 /* still clumsy name handling, layers/channels can be ordered as list in list later */
00559 void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
00560 {
00561         ExrHandle *data= (ExrHandle *)handle;
00562         ExrChannel *echan;
00563         char name[EXR_TOT_MAXNAME + 1];
00564         
00565         if(layname) {
00566                 char lay[EXR_LAY_MAXNAME+1], pass[EXR_PASS_MAXNAME+1];
00567                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
00568                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
00569                 
00570                 sprintf(name, "%s.%s", lay, pass);
00571         }
00572         else
00573                 BLI_strncpy(name, passname, EXR_TOT_MAXNAME-1);
00574 
00575         echan= (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
00576 
00577         if(echan) {
00578                 echan->xstride= xstride;
00579                 echan->ystride= ystride;
00580                 echan->rect= rect;
00581         }
00582         else
00583                 printf("IMB_exrtile_set_channel error %s\n", name);
00584 }
00585 
00586 void IMB_exrtile_clear_channels(void *handle)
00587 {
00588         ExrHandle *data= (ExrHandle *)handle;
00589         BLI_freelistN(&data->channels);
00590 }
00591 
00592 void IMB_exrtile_write_channels(void *handle, int partx, int party, int level)
00593 {
00594         ExrHandle *data= (ExrHandle *)handle;
00595         FrameBuffer frameBuffer;
00596         ExrChannel *echan;
00597         
00598         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00599                 float *rect= echan->rect - echan->xstride*partx - echan->ystride*party;
00600 
00601                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)rect, 
00602                                                         echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
00603         }
00604         
00605         data->tofile->setFrameBuffer (frameBuffer);
00606 
00607         try {
00608                 // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
00609                 data->tofile->writeTile (partx/data->tilex, party/data->tiley, level);
00610         }
00611         catch (const std::exception &exc) {
00612                 std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
00613         }
00614 }
00615 
00616 void IMB_exr_write_channels(void *handle)
00617 {
00618         ExrHandle *data= (ExrHandle *)handle;
00619         FrameBuffer frameBuffer;
00620         ExrChannel *echan;
00621         
00622         if(data->channels.first) {
00623                 for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00624                         /* last scanline, stride negative */
00625                         float *rect = echan->rect + echan->xstride*(data->height-1)*data->width;
00626                         
00627                         frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)rect, 
00628                                                                                                         echan->xstride*sizeof(float), -echan->ystride*sizeof(float)));
00629                 }
00630                 
00631                 data->ofile->setFrameBuffer (frameBuffer);
00632                 try {
00633                         data->ofile->writePixels (data->height);        
00634                 }
00635                 catch (const std::exception &exc) {
00636                         std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
00637                 }
00638         }
00639         else {
00640                 printf("Error: attempt to save MultiLayer without layers.\n");
00641         }
00642 }
00643 
00644 void IMB_exr_read_channels(void *handle)
00645 {
00646         ExrHandle *data= (ExrHandle *)handle;
00647         FrameBuffer frameBuffer;
00648         ExrChannel *echan;
00649         
00650         /* check if exr was saved with previous versions of blender which flipped images */
00651         const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
00652         short flip = (ta && strncmp(ta->value().c_str(), "Blender V2.43", 13)==0); /* 'previous multilayer attribute, flipped */
00653         
00654         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00655                 
00656                 if(echan->rect) {
00657                         if(flip)
00658                                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)echan->rect, 
00659                                                                                         echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
00660                         else
00661                                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)(echan->rect + echan->xstride*(data->height-1)*data->width), 
00662                                                                                         echan->xstride*sizeof(float), -echan->ystride*sizeof(float)));
00663                 }
00664                 else 
00665                         printf("warning, channel with no rect set %s\n", echan->name);
00666         }
00667         
00668         data->ifile->setFrameBuffer (frameBuffer);
00669 
00670         try {
00671                 data->ifile->readPixels (0, data->height-1);    
00672         }
00673         catch (const std::exception &exc) {
00674                 std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
00675         }
00676 }
00677 
00678 void IMB_exr_multilayer_convert(void *handle, void *base,  
00679                                                                 void * (*addlayer)(void *base, char *str), 
00680                                                                 void (*addpass)(void *base, void *lay, char *str, 
00681                                                                                                 float *rect, int totchan, char *chan_id))
00682 {
00683         ExrHandle *data= (ExrHandle *)handle;
00684         ExrLayer *lay;
00685         ExrPass *pass;
00686 
00687         if(data->layers.first==NULL) {
00688                 printf("cannot convert multilayer, no layers in handle\n");
00689                 return;
00690         }
00691 
00692         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00693                 void *laybase= addlayer(base, lay->name);
00694                 if(laybase) {
00695                         for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
00696                                 addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
00697                                 pass->rect= NULL;
00698                         }
00699                 }
00700         }
00701 }
00702 
00703 
00704 void IMB_exr_close(void *handle)
00705 {
00706         ExrHandle *data= (ExrHandle *)handle;
00707         ExrLayer *lay;
00708         ExrPass *pass;
00709         
00710         if(data->ifile)
00711                 delete data->ifile;
00712         else if(data->ofile)
00713                 delete data->ofile;
00714         else if(data->tofile)
00715                 delete data->tofile;
00716         
00717         data->ifile= NULL;
00718         data->ofile= NULL;
00719         data->tofile= NULL;
00720         
00721         BLI_freelistN(&data->channels);
00722         
00723         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00724                 for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next)
00725                         if(pass->rect)
00726                                 MEM_freeN(pass->rect);
00727                 BLI_freelistN(&lay->passes);
00728         }
00729         BLI_freelistN(&data->layers);
00730         
00731         BLI_remlink(&exrhandles, data);
00732         MEM_freeN(data);
00733 }
00734 
00735 /* ********* */
00736 
00737 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
00738 {
00739         int plen, len= strlen(echan->name);
00740         
00741         if(len < 4) {
00742                 printf("multilayer read: name too short: %s\n", echan->name);
00743                 return 0;
00744         }
00745         if(echan->name[len-2]!='.') {
00746                 printf("multilayer read: name has no Channel: %s\n", echan->name);
00747                 return 0;
00748         }
00749         echan->chan_id= echan->name[len-1];
00750         
00751         len-= 3;
00752         while(len>=0) {
00753                 if(echan->name[len]=='.')
00754                         break;
00755                 len--;
00756         }
00757         BLI_strncpy(passname, echan->name+len+1, EXR_PASS_MAXNAME);
00758         plen= strlen(passname);
00759         if(plen < 3) {
00760                 printf("multilayer read: should not happen: %s\n", echan->name);
00761                 return 0;
00762         }
00763         passname[plen-2]= 0;
00764         
00765         if(len<1)
00766                 layname[0]= 0;
00767         else {
00768                 BLI_strncpy(layname, echan->name, EXR_LAY_MAXNAME);
00769                 layname[len]= 0;
00770         }
00771         // printf("found lay %s pass %s chan %c\n", layname, passname, echan->chan_id);
00772         return 1;
00773 }
00774 
00775 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
00776 {
00777         ExrLayer *lay= (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
00778 
00779         if(lay==NULL) {
00780                 lay= (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
00781                 BLI_addtail(lb, lay);
00782                 BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
00783         }
00784 
00785         return lay;
00786 }
00787 
00788 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
00789 {
00790         ExrPass *pass= (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
00791         
00792         if(pass==NULL) {
00793                 pass= (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
00794 
00795                 if(strcmp(passname, "Combined")==0)
00796                         BLI_addhead(lb, pass);
00797                 else
00798                         BLI_addtail(lb, pass);
00799         }
00800 
00801         BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
00802         
00803         return pass;
00804 }
00805 
00806 /* creates channels, makes a hierarchy and assigns memory to channels */
00807 static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
00808 {
00809         ExrLayer *lay;
00810         ExrPass *pass;
00811         ExrChannel *echan;
00812         ExrHandle *data= (ExrHandle *)IMB_exr_get_handle();
00813         int a;
00814         char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
00815         
00816         data->ifile= file;
00817         data->width= width;
00818         data->height= height;
00819         
00820         const ChannelList &channels = data->ifile->header().channels();
00821 
00822         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00823                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
00824         
00825         /* now try to sort out how to assign memory to the channels */
00826         /* first build hierarchical layer list */
00827         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00828                 if( imb_exr_split_channel_name(echan, layname, passname) ) {
00829                         ExrLayer *lay= imb_exr_get_layer(&data->layers, layname);
00830                         ExrPass *pass= imb_exr_get_pass(&lay->passes, passname);
00831                         
00832                         pass->chan[pass->totchan]= echan;
00833                         pass->totchan++;
00834                         if(pass->totchan>=EXR_PASS_MAXCHAN)
00835                                 break;
00836                 }
00837         }
00838         if(echan) {
00839                 printf("error, too many channels in one pass: %s\n", echan->name);
00840                 IMB_exr_close(data);
00841                 return NULL;
00842         }
00843         
00844         /* with some heuristics, try to merge the channels in buffers */
00845         for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00846                 for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
00847                         if(pass->totchan) {
00848                                 pass->rect= (float *)MEM_mapallocN(width*height*pass->totchan*sizeof(float), "pass rect");
00849                                 if(pass->totchan==1) {
00850                                         echan= pass->chan[0];
00851                                         echan->rect= pass->rect;
00852                                         echan->xstride= 1;
00853                                         echan->ystride= width;
00854                                         pass->chan_id[0]= echan->chan_id;
00855                                 }
00856                                 else {
00857                                         char lookup[256];
00858                                         
00859                                         memset(lookup, 0, sizeof(lookup));
00860                                                    
00861                                         /* we can have RGB(A), XYZ(W), UVA */
00862                                         if(pass->totchan==3 || pass->totchan==4) {
00863                                                 if(pass->chan[0]->chan_id=='B' || pass->chan[1]->chan_id=='B' ||  pass->chan[2]->chan_id=='B') {
00864                                                         lookup[(unsigned int)'R']= 0;
00865                                                         lookup[(unsigned int)'G']= 1;
00866                                                         lookup[(unsigned int)'B']= 2;
00867                                                         lookup[(unsigned int)'A']= 3;
00868                                                 }
00869                                                 else if(pass->chan[0]->chan_id=='Y' || pass->chan[1]->chan_id=='Y' ||  pass->chan[2]->chan_id=='Y') {
00870                                                         lookup[(unsigned int)'X']= 0;
00871                                                         lookup[(unsigned int)'Y']= 1;
00872                                                         lookup[(unsigned int)'Z']= 2;
00873                                                         lookup[(unsigned int)'W']= 3;
00874                                                 }
00875                                                 else {
00876                                                         lookup[(unsigned int)'U']= 0;
00877                                                         lookup[(unsigned int)'V']= 1;
00878                                                         lookup[(unsigned int)'A']= 2;
00879                                                 }
00880                                                 for(a=0; a<pass->totchan; a++) {
00881                                                         echan= pass->chan[a];
00882                                                         echan->rect= pass->rect + lookup[(unsigned int)echan->chan_id];
00883                                                         echan->xstride= pass->totchan;
00884                                                         echan->ystride= width*pass->totchan;
00885                                                         pass->chan_id[ (unsigned int)lookup[(unsigned int)echan->chan_id] ]= echan->chan_id;
00886                                                 }
00887                                         }
00888                                         else { /* unknown */
00889                                                 for(a=0; a<pass->totchan; a++) {
00890                                                         echan= pass->chan[a];
00891                                                         echan->rect= pass->rect + a;
00892                                                         echan->xstride= pass->totchan;
00893                                                         echan->ystride= width*pass->totchan;
00894                                                         pass->chan_id[a]= echan->chan_id;
00895                                                 }
00896                                         }
00897                                 }
00898                         }
00899                 }
00900         }
00901         
00902         return data;
00903 }
00904 
00905 
00906 /* ********************************************************* */
00907 
00908 typedef struct RGBA
00909 {
00910         float r;
00911         float g;
00912         float b;
00913         float a;
00914 } RGBA;
00915 
00916 
00917 /* debug only */
00918 static void exr_print_filecontents(InputFile *file)
00919 {
00920         const ChannelList &channels = file->header().channels();
00921         
00922         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00923         {
00924                 const Channel &channel = i.channel();
00925                 printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
00926         }
00927 }
00928 
00929 /* for non-multilayer, map  R G B A channel names to something that's in this file */
00930 static const char *exr_rgba_channelname(InputFile *file, const char *chan)
00931 {
00932         const ChannelList &channels = file->header().channels();
00933         
00934         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00935         {
00936                 /* const Channel &channel = i.channel(); */ /* Not used yet */
00937                 const char *str= i.name();
00938                 int len= strlen(str);
00939                 if(len) {
00940                         if(BLI_strcasecmp(chan, str+len-1)==0) {
00941                                 return str;
00942                         }
00943                 }
00944         }
00945         return chan;
00946 }
00947 
00948 
00949 
00950 static int exr_has_zbuffer(InputFile *file)
00951 {
00952         return !(file->header().channels().findChannel("Z") == NULL);
00953 }
00954 
00955 static int exr_is_renderresult(InputFile *file)
00956 {
00957         const StringAttribute *comments= file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
00958         if(comments)
00959 //              if(comments->value() == "Blender MultiChannel")
00960                         return 1;
00961         return 0;
00962 }
00963 
00964 struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags)
00965 {
00966         struct ImBuf *ibuf = NULL;
00967         InputFile *file = NULL;
00968         
00969         if (imb_is_a_openexr(mem) == 0) return(NULL);
00970         
00971         try
00972         {
00973                 Mem_IStream *membuf = new Mem_IStream(mem, size); 
00974                 int is_multi;
00975                 file = new InputFile(*membuf);
00976                 
00977                 Box2i dw = file->header().dataWindow();
00978                 int width  = dw.max.x - dw.min.x + 1;
00979                 int height = dw.max.y - dw.min.y + 1;
00980                 
00981                 //printf("OpenEXR-load: image data window %d %d %d %d\n", 
00982                 //         dw.min.x, dw.min.y, dw.max.x, dw.max.y);
00983 
00984                 if(0) // debug
00985                         exr_print_filecontents(file);
00986                 
00987                 is_multi= exr_is_renderresult(file);
00988                 
00989                 /* do not make an ibuf when */
00990                 if(is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) 
00991                 {
00992                         printf("Error: can't process EXR multilayer file\n");
00993                 }
00994                 else {
00995                 
00996                         ibuf = IMB_allocImBuf(width, height, 32, 0);
00997                         ibuf->ftype = OPENEXR;
00998 
00999                         /* openEXR is linear as per EXR spec */
01000                         ibuf->profile = IB_PROFILE_LINEAR_RGB;
01001                         
01002                         if (!(flags & IB_test))
01003                         {
01004                                 if(is_multi) /* only enters with IB_multilayer flag set */
01005                                 {
01006                                         /* constructs channels for reading, allocates memory in channels */
01007                                         ExrHandle *handle= imb_exr_begin_read_mem(file, width, height);
01008                                         if(handle) {
01009                                                 IMB_exr_read_channels(handle);
01010                                                 ibuf->userdata= handle;                 /* potential danger, the caller has to check for this! */
01011                                                 return ibuf;
01012                                         }
01013                                 }
01014                                 else {
01015                                         FrameBuffer frameBuffer;
01016                                         float *first;
01017                                         int xstride = sizeof(float) * 4;
01018                                         int ystride = - xstride*width;
01019                                         
01020                                         imb_addrectfloatImBuf(ibuf);
01021                                         
01022                                         /* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
01023                                         first= ibuf->rect_float - 4*(dw.min.x - dw.min.y*width);
01024                                         /* but, since we read y-flipped (negative y stride) we move to last scanline */
01025                                         first+= 4*(height-1)*width;
01026                                         
01027                                         frameBuffer.insert ( exr_rgba_channelname(file, "R"), 
01028                                                                                 Slice (FLOAT,  (char *) first, xstride, ystride));
01029                                         frameBuffer.insert ( exr_rgba_channelname(file, "G"), 
01030                                                                                 Slice (FLOAT,  (char *) (first+1), xstride, ystride));
01031                                         frameBuffer.insert ( exr_rgba_channelname(file, "B"), 
01032                                                                                 Slice (FLOAT,  (char *) (first+2), xstride, ystride));
01033                                                                                                                                                         
01034                                         frameBuffer.insert ( exr_rgba_channelname(file, "A"), 
01035                                                                                 Slice (FLOAT,  (char *) (first+3), xstride, ystride, 1, 1, 1.0f)); /* 1.0 is fill value */
01036 
01037                                         if(exr_has_zbuffer(file)) 
01038                                         {
01039                                                 float *firstz;
01040                                                 
01041                                                 addzbuffloatImBuf(ibuf);
01042                                                 firstz= ibuf->zbuf_float - (dw.min.x - dw.min.y*width);
01043                                                 firstz+= (height-1)*width;
01044                                                 frameBuffer.insert ("Z", Slice (FLOAT,  (char *)firstz , sizeof(float), -width*sizeof(float)));
01045                                         }
01046                                         
01047                                         file->setFrameBuffer (frameBuffer);
01048                                         file->readPixels (dw.min.y, dw.max.y);
01049 
01050                                         // XXX, ImBuf has no nice way to deal with this.
01051                                         // ideally IM_rect would be used when the caller wants a rect BUT
01052                                         // at the moment all functions use IM_rect.
01053                                         // Disabling this is ok because all functions should check if a rect exists and create one on demand.
01054                                         //
01055                                         // Disabling this because the sequencer frees immediate.
01056                                         //
01057                                         // if(flag & IM_rect)
01058                                         //     IMB_rect_from_float(ibuf);
01059                                 }
01060                         }
01061                         
01062                 }
01063                 delete file;
01064                 return(ibuf);
01065         }
01066         catch (const std::exception &exc)
01067         {
01068                 std::cerr << exc.what() << std::endl;
01069                 if (ibuf) IMB_freeImBuf(ibuf);
01070                 delete file;
01071                 
01072                 return (0);
01073         }
01074         
01075 }
01076 
01077 
01078 } // export "C"