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