Blender V2.61 - r43446
|
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"