Blender V2.61 - r43446

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->planes==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->planes==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->planes==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->planes==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 int 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     /* avoid crash/abort when we dont have permission to write here */
00509     try {
00510         data->ofile = new OutputFile(filename, header);
00511     }
00512     catch (const std::exception &exc) {
00513         std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
00514         data->ofile = NULL;
00515     }
00516 
00517     return (data->ofile != NULL);
00518 }
00519 
00520 void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
00521 {
00522     ExrHandle *data= (ExrHandle *)handle;
00523     Header header (width, height);
00524     ExrChannel *echan;
00525     
00526     data->tilex= tilex;
00527     data->tiley= tiley;
00528     data->width= width;
00529     data->height= height;
00530     data->mipmap= mipmap;
00531     
00532     for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
00533         header.channels().insert (echan->name, Channel (FLOAT));
00534     
00535     header.setTileDescription (TileDescription (tilex, tiley, (mipmap)? MIPMAP_LEVELS: ONE_LEVEL));
00536     header.lineOrder() = RANDOM_Y;
00537     header.compression() = RLE_COMPRESSION;
00538     
00539     header.insert ("BlenderMultiChannel", StringAttribute ("Blender V2.43"));
00540     
00541     data->tofile = new TiledOutputFile(filename, header);
00542 }
00543 
00544 /* read from file */
00545 int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
00546 {
00547     ExrHandle *data= (ExrHandle *)handle;
00548     
00549     if(BLI_exists(filename) && BLI_file_size(filename)>32) {    /* 32 is arbitrary, but zero length files crashes exr */
00550         data->ifile = new InputFile(filename);
00551         if(data->ifile) {
00552             Box2i dw = data->ifile->header().dataWindow();
00553             data->width= *width  = dw.max.x - dw.min.x + 1;
00554             data->height= *height = dw.max.y - dw.min.y + 1;
00555             
00556             const ChannelList &channels = data->ifile->header().channels();
00557             
00558             for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00559                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
00560             
00561             return 1;
00562         }
00563     }
00564     return 0;
00565 }
00566 
00567 /* still clumsy name handling, layers/channels can be ordered as list in list later */
00568 void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
00569 {
00570     ExrHandle *data= (ExrHandle *)handle;
00571     ExrChannel *echan;
00572     char name[EXR_TOT_MAXNAME + 1];
00573     
00574     if(layname) {
00575         char lay[EXR_LAY_MAXNAME+1], pass[EXR_PASS_MAXNAME+1];
00576         BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
00577         BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
00578         
00579         sprintf(name, "%s.%s", lay, pass);
00580     }
00581     else
00582         BLI_strncpy(name, passname, EXR_TOT_MAXNAME-1);
00583 
00584     echan= (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
00585 
00586     if(echan) {
00587         echan->xstride= xstride;
00588         echan->ystride= ystride;
00589         echan->rect= rect;
00590     }
00591     else
00592         printf("IMB_exrtile_set_channel error %s\n", name);
00593 }
00594 
00595 void IMB_exrtile_clear_channels(void *handle)
00596 {
00597     ExrHandle *data= (ExrHandle *)handle;
00598     BLI_freelistN(&data->channels);
00599 }
00600 
00601 void IMB_exrtile_write_channels(void *handle, int partx, int party, int level)
00602 {
00603     ExrHandle *data= (ExrHandle *)handle;
00604     FrameBuffer frameBuffer;
00605     ExrChannel *echan;
00606     
00607     for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00608         float *rect= echan->rect - echan->xstride*partx - echan->ystride*party;
00609 
00610         frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)rect, 
00611                             echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
00612     }
00613     
00614     data->tofile->setFrameBuffer (frameBuffer);
00615 
00616     try {
00617         // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
00618         data->tofile->writeTile (partx/data->tilex, party/data->tiley, level);
00619     }
00620     catch (const std::exception &exc) {
00621         std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
00622     }
00623 }
00624 
00625 void IMB_exr_write_channels(void *handle)
00626 {
00627     ExrHandle *data= (ExrHandle *)handle;
00628     FrameBuffer frameBuffer;
00629     ExrChannel *echan;
00630     
00631     if(data->channels.first) {
00632         for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00633             /* last scanline, stride negative */
00634             float *rect = echan->rect + echan->xstride*(data->height-1)*data->width;
00635             
00636             frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)rect, 
00637                                                     echan->xstride*sizeof(float), -echan->ystride*sizeof(float)));
00638         }
00639         
00640         data->ofile->setFrameBuffer (frameBuffer);
00641         try {
00642             data->ofile->writePixels (data->height);    
00643         }
00644         catch (const std::exception &exc) {
00645             std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
00646         }
00647     }
00648     else {
00649         printf("Error: attempt to save MultiLayer without layers.\n");
00650     }
00651 }
00652 
00653 void IMB_exr_read_channels(void *handle)
00654 {
00655     ExrHandle *data= (ExrHandle *)handle;
00656     FrameBuffer frameBuffer;
00657     ExrChannel *echan;
00658     
00659     /* check if exr was saved with previous versions of blender which flipped images */
00660     const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
00661     short flip = (ta && strncmp(ta->value().c_str(), "Blender V2.43", 13)==0); /* 'previous multilayer attribute, flipped */
00662     
00663     for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00664         
00665         if(echan->rect) {
00666             if(flip)
00667                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)echan->rect, 
00668                                             echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
00669             else
00670                 frameBuffer.insert (echan->name, Slice (FLOAT,  (char *)(echan->rect + echan->xstride*(data->height-1)*data->width), 
00671                                             echan->xstride*sizeof(float), -echan->ystride*sizeof(float)));
00672         }
00673         else 
00674             printf("warning, channel with no rect set %s\n", echan->name);
00675     }
00676     
00677     data->ifile->setFrameBuffer (frameBuffer);
00678 
00679     try {
00680         data->ifile->readPixels (0, data->height-1);    
00681     }
00682     catch (const std::exception &exc) {
00683         std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
00684     }
00685 }
00686 
00687 void IMB_exr_multilayer_convert(void *handle, void *base,  
00688                                 void * (*addlayer)(void *base, char *str), 
00689                                 void (*addpass)(void *base, void *lay, char *str, 
00690                                                 float *rect, int totchan, char *chan_id))
00691 {
00692     ExrHandle *data= (ExrHandle *)handle;
00693     ExrLayer *lay;
00694     ExrPass *pass;
00695 
00696     if(data->layers.first==NULL) {
00697         printf("cannot convert multilayer, no layers in handle\n");
00698         return;
00699     }
00700 
00701     for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00702         void *laybase= addlayer(base, lay->name);
00703         if(laybase) {
00704             for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
00705                 addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
00706                 pass->rect= NULL;
00707             }
00708         }
00709     }
00710 }
00711 
00712 
00713 void IMB_exr_close(void *handle)
00714 {
00715     ExrHandle *data= (ExrHandle *)handle;
00716     ExrLayer *lay;
00717     ExrPass *pass;
00718     
00719     if(data->ifile)
00720         delete data->ifile;
00721     else if(data->ofile)
00722         delete data->ofile;
00723     else if(data->tofile)
00724         delete data->tofile;
00725     
00726     data->ifile= NULL;
00727     data->ofile= NULL;
00728     data->tofile= NULL;
00729     
00730     BLI_freelistN(&data->channels);
00731     
00732     for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00733         for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next)
00734             if(pass->rect)
00735                 MEM_freeN(pass->rect);
00736         BLI_freelistN(&lay->passes);
00737     }
00738     BLI_freelistN(&data->layers);
00739     
00740     BLI_remlink(&exrhandles, data);
00741     MEM_freeN(data);
00742 }
00743 
00744 /* ********* */
00745 
00746 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
00747 {
00748     int plen, len= strlen(echan->name);
00749     
00750     if(len < 4) {
00751         printf("multilayer read: name too short: %s\n", echan->name);
00752         return 0;
00753     }
00754     if(echan->name[len-2]!='.') {
00755         printf("multilayer read: name has no Channel: %s\n", echan->name);
00756         return 0;
00757     }
00758     echan->chan_id= echan->name[len-1];
00759     
00760     len-= 3;
00761     while(len>=0) {
00762         if(echan->name[len]=='.')
00763             break;
00764         len--;
00765     }
00766     BLI_strncpy(passname, echan->name+len+1, EXR_PASS_MAXNAME);
00767     plen= strlen(passname);
00768     if(plen < 3) {
00769         printf("multilayer read: should not happen: %s\n", echan->name);
00770         return 0;
00771     }
00772     passname[plen-2]= 0;
00773     
00774     if(len<1)
00775         layname[0]= 0;
00776     else {
00777         BLI_strncpy(layname, echan->name, EXR_LAY_MAXNAME);
00778         layname[len]= 0;
00779     }
00780     // printf("found lay %s pass %s chan %c\n", layname, passname, echan->chan_id);
00781     return 1;
00782 }
00783 
00784 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
00785 {
00786     ExrLayer *lay= (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
00787 
00788     if(lay==NULL) {
00789         lay= (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
00790         BLI_addtail(lb, lay);
00791         BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
00792     }
00793 
00794     return lay;
00795 }
00796 
00797 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
00798 {
00799     ExrPass *pass= (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
00800     
00801     if(pass==NULL) {
00802         pass= (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
00803 
00804         if(strcmp(passname, "Combined")==0)
00805             BLI_addhead(lb, pass);
00806         else
00807             BLI_addtail(lb, pass);
00808     }
00809 
00810     BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
00811     
00812     return pass;
00813 }
00814 
00815 /* creates channels, makes a hierarchy and assigns memory to channels */
00816 static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
00817 {
00818     ExrLayer *lay;
00819     ExrPass *pass;
00820     ExrChannel *echan;
00821     ExrHandle *data= (ExrHandle *)IMB_exr_get_handle();
00822     int a;
00823     char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
00824     
00825     data->ifile= file;
00826     data->width= width;
00827     data->height= height;
00828     
00829     const ChannelList &channels = data->ifile->header().channels();
00830 
00831     for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00832         IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
00833     
00834     /* now try to sort out how to assign memory to the channels */
00835     /* first build hierarchical layer list */
00836     for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
00837         if( imb_exr_split_channel_name(echan, layname, passname) ) {
00838             ExrLayer *lay= imb_exr_get_layer(&data->layers, layname);
00839             ExrPass *pass= imb_exr_get_pass(&lay->passes, passname);
00840             
00841             pass->chan[pass->totchan]= echan;
00842             pass->totchan++;
00843             if(pass->totchan>=EXR_PASS_MAXCHAN)
00844                 break;
00845         }
00846     }
00847     if(echan) {
00848         printf("error, too many channels in one pass: %s\n", echan->name);
00849         IMB_exr_close(data);
00850         return NULL;
00851     }
00852     
00853     /* with some heuristics, try to merge the channels in buffers */
00854     for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) {
00855         for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) {
00856             if(pass->totchan) {
00857                 pass->rect= (float *)MEM_mapallocN(width*height*pass->totchan*sizeof(float), "pass rect");
00858                 if(pass->totchan==1) {
00859                     echan= pass->chan[0];
00860                     echan->rect= pass->rect;
00861                     echan->xstride= 1;
00862                     echan->ystride= width;
00863                     pass->chan_id[0]= echan->chan_id;
00864                 }
00865                 else {
00866                     char lookup[256];
00867                     
00868                     memset(lookup, 0, sizeof(lookup));
00869                            
00870                     /* we can have RGB(A), XYZ(W), UVA */
00871                     if(pass->totchan==3 || pass->totchan==4) {
00872                         if(pass->chan[0]->chan_id=='B' || pass->chan[1]->chan_id=='B' ||  pass->chan[2]->chan_id=='B') {
00873                             lookup[(unsigned int)'R']= 0;
00874                             lookup[(unsigned int)'G']= 1;
00875                             lookup[(unsigned int)'B']= 2;
00876                             lookup[(unsigned int)'A']= 3;
00877                         }
00878                         else if(pass->chan[0]->chan_id=='Y' || pass->chan[1]->chan_id=='Y' ||  pass->chan[2]->chan_id=='Y') {
00879                             lookup[(unsigned int)'X']= 0;
00880                             lookup[(unsigned int)'Y']= 1;
00881                             lookup[(unsigned int)'Z']= 2;
00882                             lookup[(unsigned int)'W']= 3;
00883                         }
00884                         else {
00885                             lookup[(unsigned int)'U']= 0;
00886                             lookup[(unsigned int)'V']= 1;
00887                             lookup[(unsigned int)'A']= 2;
00888                         }
00889                         for(a=0; a<pass->totchan; a++) {
00890                             echan= pass->chan[a];
00891                             echan->rect= pass->rect + lookup[(unsigned int)echan->chan_id];
00892                             echan->xstride= pass->totchan;
00893                             echan->ystride= width*pass->totchan;
00894                             pass->chan_id[ (unsigned int)lookup[(unsigned int)echan->chan_id] ]= echan->chan_id;
00895                         }
00896                     }
00897                     else { /* unknown */
00898                         for(a=0; a<pass->totchan; a++) {
00899                             echan= pass->chan[a];
00900                             echan->rect= pass->rect + a;
00901                             echan->xstride= pass->totchan;
00902                             echan->ystride= width*pass->totchan;
00903                             pass->chan_id[a]= echan->chan_id;
00904                         }
00905                     }
00906                 }
00907             }
00908         }
00909     }
00910     
00911     return data;
00912 }
00913 
00914 
00915 /* ********************************************************* */
00916 
00917 typedef struct RGBA
00918 {
00919     float r;
00920     float g;
00921     float b;
00922     float a;
00923 } RGBA;
00924 
00925 
00926 /* debug only */
00927 static void exr_print_filecontents(InputFile *file)
00928 {
00929     const ChannelList &channels = file->header().channels();
00930     
00931     for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00932     {
00933         const Channel &channel = i.channel();
00934         printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
00935     }
00936 }
00937 
00938 /* for non-multilayer, map  R G B A channel names to something that's in this file */
00939 static const char *exr_rgba_channelname(InputFile *file, const char *chan)
00940 {
00941     const ChannelList &channels = file->header().channels();
00942     
00943     for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
00944     {
00945         /* const Channel &channel = i.channel(); */ /* Not used yet */
00946         const char *str= i.name();
00947         int len= strlen(str);
00948         if(len) {
00949             if(BLI_strcasecmp(chan, str+len-1)==0) {
00950                 return str;
00951             }
00952         }
00953     }
00954     return chan;
00955 }
00956 
00957 
00958 
00959 static int exr_has_zbuffer(InputFile *file)
00960 {
00961     return !(file->header().channels().findChannel("Z") == NULL);
00962 }
00963 
00964 static int exr_is_renderresult(InputFile *file)
00965 {
00966     const StringAttribute *comments= file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
00967     if(comments)
00968 //      if(comments->value() == "Blender MultiChannel")
00969             return 1;
00970     return 0;
00971 }
00972 
00973 struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags)
00974 {
00975     struct ImBuf *ibuf = NULL;
00976     InputFile *file = NULL;
00977     
00978     if (imb_is_a_openexr(mem) == 0) return(NULL);
00979     
00980     try
00981     {
00982         Mem_IStream *membuf = new Mem_IStream(mem, size); 
00983         int is_multi;
00984         file = new InputFile(*membuf);
00985         
00986         Box2i dw = file->header().dataWindow();
00987         int width  = dw.max.x - dw.min.x + 1;
00988         int height = dw.max.y - dw.min.y + 1;
00989         
00990         //printf("OpenEXR-load: image data window %d %d %d %d\n", 
00991         //     dw.min.x, dw.min.y, dw.max.x, dw.max.y);
00992 
00993         if(0) // debug
00994             exr_print_filecontents(file);
00995         
00996         is_multi= exr_is_renderresult(file);
00997         
00998         /* do not make an ibuf when */
00999         if(is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) 
01000         {
01001             printf("Error: can't process EXR multilayer file\n");
01002         }
01003         else {
01004         
01005             ibuf = IMB_allocImBuf(width, height, 32, 0);
01006             ibuf->ftype = OPENEXR;
01007 
01008             /* openEXR is linear as per EXR spec */
01009             ibuf->profile = IB_PROFILE_LINEAR_RGB;
01010             
01011             if (!(flags & IB_test))
01012             {
01013                 if(is_multi) /* only enters with IB_multilayer flag set */
01014                 {
01015                     /* constructs channels for reading, allocates memory in channels */
01016                     ExrHandle *handle= imb_exr_begin_read_mem(file, width, height);
01017                     if(handle) {
01018                         IMB_exr_read_channels(handle);
01019                         ibuf->userdata= handle;         /* potential danger, the caller has to check for this! */
01020                         return ibuf;
01021                     }
01022                 }
01023                 else {
01024                     FrameBuffer frameBuffer;
01025                     float *first;
01026                     int xstride = sizeof(float) * 4;
01027                     int ystride = - xstride*width;
01028                     
01029                     imb_addrectfloatImBuf(ibuf);
01030                     
01031                     /* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
01032                     first= ibuf->rect_float - 4*(dw.min.x - dw.min.y*width);
01033                     /* but, since we read y-flipped (negative y stride) we move to last scanline */
01034                     first+= 4*(height-1)*width;
01035                     
01036                     frameBuffer.insert ( exr_rgba_channelname(file, "R"), 
01037                                         Slice (FLOAT,  (char *) first, xstride, ystride));
01038                     frameBuffer.insert ( exr_rgba_channelname(file, "G"), 
01039                                         Slice (FLOAT,  (char *) (first+1), xstride, ystride));
01040                     frameBuffer.insert ( exr_rgba_channelname(file, "B"), 
01041                                         Slice (FLOAT,  (char *) (first+2), xstride, ystride));
01042                                                                             
01043                     frameBuffer.insert ( exr_rgba_channelname(file, "A"), 
01044                                         Slice (FLOAT,  (char *) (first+3), xstride, ystride, 1, 1, 1.0f)); /* 1.0 is fill value */
01045 
01046                     if(exr_has_zbuffer(file)) 
01047                     {
01048                         float *firstz;
01049                         
01050                         addzbuffloatImBuf(ibuf);
01051                         firstz= ibuf->zbuf_float - (dw.min.x - dw.min.y*width);
01052                         firstz+= (height-1)*width;
01053                         frameBuffer.insert ("Z", Slice (FLOAT,  (char *)firstz , sizeof(float), -width*sizeof(float)));
01054                     }
01055                     
01056                     file->setFrameBuffer (frameBuffer);
01057                     file->readPixels (dw.min.y, dw.max.y);
01058 
01059                     // XXX, ImBuf has no nice way to deal with this.
01060                     // ideally IM_rect would be used when the caller wants a rect BUT
01061                     // at the moment all functions use IM_rect.
01062                     // Disabling this is ok because all functions should check if a rect exists and create one on demand.
01063                     //
01064                     // Disabling this because the sequencer frees immediate.
01065                     //
01066                     // if(flag & IM_rect)
01067                     //     IMB_rect_from_float(ibuf);
01068                 }
01069             }
01070             
01071         }
01072         delete file;
01073         return(ibuf);
01074     }
01075     catch (const std::exception &exc)
01076     {
01077         std::cerr << exc.what() << std::endl;
01078         if (ibuf) IMB_freeImBuf(ibuf);
01079         delete file;
01080         
01081         return (0);
01082     }
01083     
01084 }
01085 
01086 
01087 } // export "C"