passes in single file. Code is currently disabled, commit is mainly to have a nicer method of excluding OpenEXR dependency from render module. This should compile with disabled WITH_OPENEXR too. Reason why EXR is great to include by default in Blender is its feature to store unlimited layers and channels, and write this tile based. I need the feature for saving memory; while rendering tiles, all full-size buffers for all layers and passes are kept in memory now, which can go into 100s of MB easily. The code I commit now doesn't allocate these buffers while rendering, but saves the tiles to disk. In the end is it read back. Overhead for large renders (like 300 meg buffers) is 10-15 seconds, not bad. Two more interesting aspects: - Blender can save such multi-layer files in the temp directory, storing it with .blend file name and scene name. That way, on each restart of Blender, or on switching scenes, these buffers can be read. So you always see what was rendered last. Also great for compositing work. - This can also become an output image type for rendering. There's plenty of cases where you want specific layers or passes saved to disk for later use. Anyhoo, finishing it is another days of work, and I got more urgent stuff now!
674 lines
17 KiB
C++
674 lines
17 KiB
C++
/**
|
|
*
|
|
* ***** BEGIN GPLLICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Copyright by Gernot Ziegler <gz@lysator.liu.se>.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Austin Benesh, Ton Roosendaal (float, half, speedup, cleanup...).
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
|
|
|
|
#include <openexr_api.h>
|
|
|
|
extern "C"
|
|
{
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_allocimbuf.h"
|
|
|
|
#define WITH_OPENEXR
|
|
#include "openexr_multi.h"
|
|
}
|
|
|
|
#include <iostream>
|
|
|
|
#if defined (_WIN32) && !defined(FREE_WINDOWS)
|
|
#include <half.h>
|
|
#include <IlmImf/ImfVersion.h>
|
|
#include <IlmImf/ImfArray.h>
|
|
#include <IlmImf/ImfIO.h>
|
|
#include <IlmImf/ImfChannelList.h>
|
|
#include <IlmImf/ImfPixelType.h>
|
|
#include <IlmImf/ImfInputFile.h>
|
|
#include <IlmImf/ImfOutputFile.h>
|
|
#include <IlmImf/ImfCompression.h>
|
|
#include <IlmImf/ImfCompressionAttribute.h>
|
|
#include <IlmImf/ImfStringAttribute.h>
|
|
#include <Imath/ImathBox.h>
|
|
#else
|
|
#include <OpenEXR/half.h>
|
|
#include <OpenEXR/ImfVersion.h>
|
|
#include <OpenEXR/ImathBox.h>
|
|
#include <OpenEXR/ImfArray.h>
|
|
#include <OpenEXR/ImfIO.h>
|
|
#include <OpenEXR/ImfChannelList.h>
|
|
#include <OpenEXR/ImfPixelType.h>
|
|
#include <OpenEXR/ImfInputFile.h>
|
|
#include <OpenEXR/ImfOutputFile.h>
|
|
#include <OpenEXR/ImfCompression.h>
|
|
#include <OpenEXR/ImfCompressionAttribute.h>
|
|
#include <OpenEXR/ImfStringAttribute.h>
|
|
#endif
|
|
|
|
using namespace Imf;
|
|
using namespace Imath;
|
|
|
|
class Mem_IStream: public IStream
|
|
{
|
|
public:
|
|
|
|
Mem_IStream (unsigned char *exrbuf, int exrsize):
|
|
IStream("dummy"), _exrpos (0), _exrsize(exrsize) { _exrbuf = exrbuf; }
|
|
|
|
virtual bool read (char c[], int n);
|
|
virtual Int64 tellg ();
|
|
virtual void seekg (Int64 pos);
|
|
virtual void clear ();
|
|
//virtual ~Mem_IStream() {}; // unused
|
|
|
|
private:
|
|
|
|
Int64 _exrpos;
|
|
Int64 _exrsize;
|
|
unsigned char *_exrbuf;
|
|
};
|
|
|
|
bool Mem_IStream::read (char c[], int n)
|
|
{
|
|
if (n + _exrpos <= _exrsize)
|
|
{
|
|
memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
|
|
_exrpos += n;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
Int64 Mem_IStream::tellg ()
|
|
{
|
|
return _exrpos;
|
|
}
|
|
|
|
void Mem_IStream::seekg (Int64 pos)
|
|
{
|
|
_exrpos = pos;
|
|
}
|
|
|
|
void Mem_IStream::clear ()
|
|
{
|
|
}
|
|
|
|
struct _RGBAZ
|
|
{
|
|
half r;
|
|
half g;
|
|
half b;
|
|
half a;
|
|
half z;
|
|
};
|
|
|
|
typedef struct _RGBAZ RGBAZ;
|
|
|
|
extern "C"
|
|
{
|
|
|
|
int imb_is_a_openexr(unsigned char *mem)
|
|
{
|
|
return Imf::isImfMagic ((const char *)mem);
|
|
}
|
|
|
|
static void openexr_header_compression(Header *header, int compression)
|
|
{
|
|
switch(compression)
|
|
{
|
|
case 0:
|
|
header->compression() = NO_COMPRESSION;
|
|
break;
|
|
case 1:
|
|
header->compression() = PXR24_COMPRESSION;
|
|
break;
|
|
case 2:
|
|
header->compression() = ZIP_COMPRESSION;
|
|
break;
|
|
case 3:
|
|
header->compression() = PIZ_COMPRESSION;
|
|
break;
|
|
case 4:
|
|
header->compression() = RLE_COMPRESSION;
|
|
break;
|
|
default:
|
|
header->compression() = NO_COMPRESSION;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static short imb_save_openexr_half(struct ImBuf *ibuf, char *name, int flags)
|
|
{
|
|
|
|
int width = ibuf->x;
|
|
int height = ibuf->y;
|
|
int write_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; // summarize
|
|
|
|
try
|
|
{
|
|
Header header (width, height);
|
|
|
|
openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
|
|
|
|
header.channels().insert ("R", Channel (HALF));
|
|
header.channels().insert ("G", Channel (HALF));
|
|
header.channels().insert ("B", Channel (HALF));
|
|
if (ibuf->depth==32)
|
|
header.channels().insert ("A", Channel (HALF));
|
|
if (write_zbuf) // z we do as float always
|
|
header.channels().insert ("Z", Channel (FLOAT));
|
|
|
|
FrameBuffer frameBuffer;
|
|
OutputFile *file = new OutputFile(name, header);
|
|
|
|
/* we store first everything in half array */
|
|
RGBAZ *pixels = new RGBAZ[height * width];
|
|
RGBAZ *to = pixels;
|
|
int xstride= sizeof (RGBAZ);
|
|
int ystride= xstride*width;
|
|
|
|
/* indicate used buffers */
|
|
frameBuffer.insert ("R", Slice (HALF, (char *) &pixels[0].r, xstride, ystride));
|
|
frameBuffer.insert ("G", Slice (HALF, (char *) &pixels[0].g, xstride, ystride));
|
|
frameBuffer.insert ("B", Slice (HALF, (char *) &pixels[0].b, xstride, ystride));
|
|
if (ibuf->depth==32)
|
|
frameBuffer.insert ("A", Slice (HALF, (char *) &pixels[0].a, xstride, ystride));
|
|
if (write_zbuf)
|
|
frameBuffer.insert ("Z", Slice (FLOAT, (char *) ibuf->zbuf_float + 4*(height-1)*width,
|
|
sizeof(float), sizeof(float) * -width));
|
|
if(ibuf->rect_float) {
|
|
float *from;
|
|
|
|
for (int i = ibuf->y-1; i >= 0; i--)
|
|
{
|
|
from= ibuf->rect_float + 4*i*width;
|
|
|
|
for (int j = ibuf->x; j > 0; j--)
|
|
{
|
|
to->r = from[0];
|
|
to->g = from[1];
|
|
to->b = from[2];
|
|
to->a = from[3];
|
|
to++; from += 4;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
unsigned char *from;
|
|
|
|
for (int i = ibuf->y-1; i >= 0; i--)
|
|
{
|
|
from= (unsigned char *)(ibuf->rect + i*width);
|
|
|
|
for (int j = ibuf->x; j > 0; j--)
|
|
{
|
|
to->r = (float)(from[0])/255.0;
|
|
to->g = (float)(from[1])/255.0;
|
|
to->b = (float)(from[2])/255.0;
|
|
to->a = (float)(from[3])/255.0;
|
|
to++; from += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
// printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
|
|
|
|
file->setFrameBuffer (frameBuffer);
|
|
file->writePixels (height);
|
|
delete file;
|
|
}
|
|
catch (const std::exception &exc)
|
|
{
|
|
printf("OpenEXR-save: ERROR: %s\n", exc.what());
|
|
if (ibuf) IMB_freeImBuf(ibuf);
|
|
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static short imb_save_openexr_float(struct ImBuf *ibuf, char *name, int flags)
|
|
{
|
|
|
|
int width = ibuf->x;
|
|
int height = ibuf->y;
|
|
int write_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; // summarize
|
|
|
|
try
|
|
{
|
|
Header header (width, height);
|
|
|
|
openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
|
|
|
|
header.channels().insert ("R", Channel (FLOAT));
|
|
header.channels().insert ("G", Channel (FLOAT));
|
|
header.channels().insert ("B", Channel (FLOAT));
|
|
if (ibuf->depth==32)
|
|
header.channels().insert ("A", Channel (FLOAT));
|
|
if (write_zbuf)
|
|
header.channels().insert ("Z", Channel (FLOAT));
|
|
|
|
FrameBuffer frameBuffer;
|
|
OutputFile *file = new OutputFile(name, header);
|
|
float *first= ibuf->rect_float + 4*(height-1)*width;
|
|
int xstride = sizeof(float) * 4;
|
|
int ystride = - xstride*width;
|
|
|
|
frameBuffer.insert ("R", Slice (FLOAT, (char *) first, xstride, ystride));
|
|
frameBuffer.insert ("G", Slice (FLOAT, (char *) (first+1), xstride, ystride));
|
|
frameBuffer.insert ("B", Slice (FLOAT, (char *) (first+2), xstride, ystride));
|
|
if (ibuf->depth==32)
|
|
frameBuffer.insert ("A", Slice (FLOAT, (char *) (first+3), xstride, ystride));
|
|
if (write_zbuf)
|
|
frameBuffer.insert ("Z", Slice (FLOAT, (char *) ibuf->zbuf_float + 4*(height-1)*width,
|
|
sizeof(float), sizeof(float) * -width));
|
|
file->setFrameBuffer (frameBuffer);
|
|
file->writePixels (height);
|
|
delete file;
|
|
}
|
|
catch (const std::exception &exc)
|
|
{
|
|
printf("OpenEXR-save: ERROR: %s\n", exc.what());
|
|
if (ibuf) IMB_freeImBuf(ibuf);
|
|
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
// printf("OpenEXR-save: Done.\n");
|
|
}
|
|
|
|
|
|
short imb_save_openexr(struct ImBuf *ibuf, char *name, int flags)
|
|
{
|
|
if (flags & IB_mem)
|
|
{
|
|
printf("OpenEXR-save: Create EXR in memory CURRENTLY NOT SUPPORTED !\n");
|
|
imb_addencodedbufferImBuf(ibuf);
|
|
ibuf->encodedsize = 0;
|
|
return(0);
|
|
}
|
|
|
|
if (ibuf->ftype & OPENEXR_HALF)
|
|
return imb_save_openexr_half(ibuf, name, flags);
|
|
else {
|
|
/* when no float rect, we save as half (16 bits is sufficient) */
|
|
if (ibuf->rect_float==NULL)
|
|
return imb_save_openexr_half(ibuf, name, flags);
|
|
else
|
|
return imb_save_openexr_float(ibuf, name, flags);
|
|
}
|
|
}
|
|
|
|
/* ********************* Tile file support ************************************ */
|
|
|
|
typedef struct ExrHandle {
|
|
InputFile *ifile;
|
|
TiledOutputFile *tofile;
|
|
OutputFile *ofile;
|
|
int tilex, tiley;
|
|
int width, height;
|
|
ListBase channels;
|
|
} ExrHandle;
|
|
|
|
#define CHANMAXNAME 64
|
|
typedef struct ExrChannel {
|
|
struct ExrChannel *next, *prev;
|
|
char name[2*CHANMAXNAME + 1];
|
|
int xstride, ystride;
|
|
float *rect;
|
|
} ExrChannel;
|
|
|
|
/* not threaded! write one tiled file at a time */
|
|
void *IMB_exr_get_handle(void)
|
|
{
|
|
static ExrHandle data;
|
|
|
|
memset(&data, sizeof(ExrHandle), 0);
|
|
|
|
return &data;
|
|
}
|
|
|
|
/* still clumsy name handling, layers/channels can be ordered as list in list later */
|
|
void IMB_exr_add_channel(void *handle, const char *layname, const char *channame)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
ExrChannel *echan;
|
|
|
|
echan= (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel");
|
|
|
|
if(layname) {
|
|
char lay[CHANMAXNAME], chan[CHANMAXNAME];
|
|
strncpy(lay, layname, CHANMAXNAME-1);
|
|
strncpy(chan, channame, CHANMAXNAME-1);
|
|
|
|
sprintf(echan->name, "%s.%s", lay, chan);
|
|
}
|
|
else
|
|
strncpy(echan->name, channame, 2*CHANMAXNAME);
|
|
printf("added channel %s\n", echan->name);
|
|
BLI_addtail(&data->channels, echan);
|
|
}
|
|
|
|
void IMB_exr_begin_write(void *handle, char *filename, int width, int height)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
Header header (width, height);
|
|
ExrChannel *echan;
|
|
|
|
data->width= width;
|
|
data->height= height;
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
|
|
header.channels().insert (echan->name, Channel (FLOAT));
|
|
|
|
header.insert ("comments", StringAttribute ("Blender MultiChannel"));
|
|
|
|
data->ofile = new OutputFile(filename, header);
|
|
}
|
|
|
|
void IMB_exrtile_begin_write(void *handle, char *filename, int width, int height, int tilex, int tiley)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
Header header (width, height);
|
|
ExrChannel *echan;
|
|
|
|
data->tilex= tilex;
|
|
data->tiley= tiley;
|
|
data->width= width;
|
|
data->height= height;
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
|
|
header.channels().insert (echan->name, Channel (FLOAT));
|
|
|
|
header.setTileDescription (TileDescription (tilex, tiley, ONE_LEVEL));
|
|
header.lineOrder() = RANDOM_Y,
|
|
header.compression() = NO_COMPRESSION;
|
|
|
|
header.insert ("comments", StringAttribute ("Blender MultiChannel"));
|
|
|
|
data->tofile = new TiledOutputFile(filename, header);
|
|
}
|
|
|
|
int IMB_exr_begin_read(void *handle, char *filename, int *width, int *height)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
|
|
data->ifile = new InputFile(filename);
|
|
if(data->ifile) {
|
|
Box2i dw = data->ifile->header().dataWindow();
|
|
data->width= *width = dw.max.x - dw.min.x + 1;
|
|
data->height= *height = dw.max.y - dw.min.y + 1;
|
|
|
|
const ChannelList &channels = data->ifile->header().channels();
|
|
|
|
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
|
|
IMB_exr_add_channel(data, NULL, i.name());
|
|
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* still clumsy name handling, layers/channels can be ordered as list in list later */
|
|
void IMB_exr_set_channel(void *handle, char *layname, char *channame, int xstride, int ystride, float *rect)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
ExrChannel *echan;
|
|
char name[2*CHANMAXNAME + 1];
|
|
|
|
if(layname) {
|
|
char lay[CHANMAXNAME], chan[CHANMAXNAME];
|
|
strncpy(lay, layname, CHANMAXNAME-1);
|
|
strncpy(chan, channame, CHANMAXNAME-1);
|
|
|
|
sprintf(name, "%s.%s", lay, chan);
|
|
}
|
|
else
|
|
strncpy(name, channame, 2*CHANMAXNAME);
|
|
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
|
|
if(strcmp(echan->name, name)==0)
|
|
break;
|
|
|
|
if(echan) {
|
|
echan->xstride= xstride;
|
|
echan->ystride= ystride;
|
|
echan->rect= rect;
|
|
}
|
|
else
|
|
printf("IMB_exrtile_set_channel error %s\n", name);
|
|
}
|
|
|
|
|
|
void IMB_exrtile_write_channels(void *handle, int partx, int party)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
FrameBuffer frameBuffer;
|
|
ExrChannel *echan;
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
|
|
float *rect= echan->rect - echan->xstride*partx - echan->ystride*party;
|
|
|
|
frameBuffer.insert (echan->name, Slice (FLOAT, (char *)rect,
|
|
echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
|
|
}
|
|
|
|
data->tofile->setFrameBuffer (frameBuffer);
|
|
printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
|
|
data->tofile->writeTile (partx/data->tilex, party/data->tiley);
|
|
|
|
}
|
|
|
|
void IMB_exr_write_channels(void *handle)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
FrameBuffer frameBuffer;
|
|
ExrChannel *echan;
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next)
|
|
frameBuffer.insert (echan->name, Slice (FLOAT, (char *)echan->rect,
|
|
echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
|
|
|
|
data->ofile->setFrameBuffer (frameBuffer);
|
|
data->ofile->writePixels (data->height);
|
|
|
|
}
|
|
|
|
void IMB_exr_read_channels(void *handle)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
FrameBuffer frameBuffer;
|
|
ExrChannel *echan;
|
|
|
|
for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) {
|
|
/* no datawindow correction needed */
|
|
if(echan->rect)
|
|
frameBuffer.insert (echan->name, Slice (FLOAT, (char *)echan->rect,
|
|
echan->xstride*sizeof(float), echan->ystride*sizeof(float)));
|
|
}
|
|
|
|
data->ifile->setFrameBuffer (frameBuffer);
|
|
data->ifile->readPixels (0, data->height-1);
|
|
}
|
|
|
|
|
|
void IMB_exr_close(void *handle)
|
|
{
|
|
ExrHandle *data= (ExrHandle *)handle;
|
|
ExrChannel *echan;
|
|
|
|
if(data->ifile)
|
|
delete data->ifile;
|
|
else if(data->ofile)
|
|
delete data->ofile;
|
|
else if(data->tofile)
|
|
delete data->tofile;
|
|
|
|
data->ifile= NULL;
|
|
data->ofile= NULL;
|
|
data->tofile= NULL;
|
|
|
|
BLI_freelistN(&data->channels);
|
|
}
|
|
|
|
|
|
/* ********************************************************* */
|
|
|
|
typedef struct RGBA
|
|
{
|
|
float r;
|
|
float g;
|
|
float b;
|
|
float a;
|
|
} RGBA;
|
|
|
|
|
|
static void exr_print_filecontents(InputFile *file)
|
|
{
|
|
const ChannelList &channels = file->header().channels();
|
|
|
|
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
|
|
{
|
|
const Channel &channel = i.channel();
|
|
printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
|
|
}
|
|
}
|
|
|
|
static int exr_has_zbuffer(InputFile *file)
|
|
{
|
|
const ChannelList &channels = file->header().channels();
|
|
|
|
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
|
|
{
|
|
const Channel &channel = i.channel();
|
|
if(strcmp("Z", i.name())==0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int exr_is_renderresult(InputFile *file)
|
|
{
|
|
const StringAttribute *comments= file->header().findTypedAttribute<StringAttribute>("comments");
|
|
if(comments)
|
|
if(comments->value() == "Blender MultiChannel")
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
struct ImBuf *imb_load_openexr(unsigned char *mem, int size, int flags)
|
|
{
|
|
struct ImBuf *ibuf = NULL;
|
|
InputFile *file = NULL;
|
|
|
|
if (imb_is_a_openexr(mem) == 0) return(NULL);
|
|
|
|
try
|
|
{
|
|
Mem_IStream membuf(mem, size);
|
|
file = new InputFile(membuf);
|
|
|
|
Box2i dw = file->header().dataWindow();
|
|
int width = dw.max.x - dw.min.x + 1;
|
|
int height = dw.max.y - dw.min.y + 1;
|
|
|
|
//printf("OpenEXR-load: image data window %d %d %d %d\n",
|
|
// dw.min.x, dw.min.y, dw.max.x, dw.max.y);
|
|
|
|
//exr_print_filecontents(file);
|
|
int flipped= exr_is_renderresult(file);
|
|
|
|
ibuf = IMB_allocImBuf(width, height, 32, 0, 0);
|
|
|
|
if (ibuf)
|
|
{
|
|
ibuf->ftype = OPENEXR;
|
|
|
|
if (!(flags & IB_test))
|
|
{
|
|
FrameBuffer frameBuffer;
|
|
float *first;
|
|
int xstride = sizeof(float) * 4;
|
|
int ystride = flipped ? xstride*width : - xstride*width;
|
|
|
|
imb_addrectfloatImBuf(ibuf);
|
|
|
|
/* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
|
|
first= ibuf->rect_float - 4*(dw.min.x - dw.min.y*width);
|
|
/* but, since we read y-flipped (negative y stride) we move to last scanline */
|
|
if(!flipped) first+= 4*(height-1)*width;
|
|
|
|
frameBuffer.insert ("R", Slice (FLOAT, (char *) first, xstride, ystride));
|
|
frameBuffer.insert ("G", Slice (FLOAT, (char *) (first+1), xstride, ystride));
|
|
frameBuffer.insert ("B", Slice (FLOAT, (char *) (first+2), xstride, ystride));
|
|
/* 1.0 is fill value */
|
|
frameBuffer.insert ("A", Slice (FLOAT, (char *) (first+3), xstride, ystride, 1, 1, 1.0f));
|
|
|
|
if(exr_has_zbuffer(file))
|
|
{
|
|
float *firstz;
|
|
|
|
addzbuffloatImBuf(ibuf);
|
|
firstz= ibuf->zbuf_float - (dw.min.x - dw.min.y*width);
|
|
if(!flipped) firstz+= (height-1)*width;
|
|
frameBuffer.insert ("Z", Slice (FLOAT, (char *)firstz , sizeof(float), -width*sizeof(float)));
|
|
}
|
|
|
|
file->setFrameBuffer (frameBuffer);
|
|
file->readPixels (dw.min.y, dw.max.y);
|
|
|
|
IMB_rect_from_float(ibuf);
|
|
}
|
|
}
|
|
return(ibuf);
|
|
|
|
}
|
|
catch (const std::exception &exc)
|
|
{
|
|
std::cerr << exc.what() << std::endl;
|
|
if (ibuf) IMB_freeImBuf(ibuf);
|
|
|
|
return (0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} // export "C"
|