diff options
Diffstat (limited to 'source/blender/imbuf/intern/openexr/openexr_api.cpp')
-rw-r--r-- | source/blender/imbuf/intern/openexr/openexr_api.cpp | 1030 |
1 files changed, 828 insertions, 202 deletions
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 45eae89ad9d..1262f0af95d 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -92,9 +92,32 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include <ImfStringAttribute.h> #include <ImfStandardAttributes.h> +/* multiview/multipart */ +#include <ImfMultiView.h> +#include <ImfMultiPartInputFile.h> +#include <ImfInputPart.h> +#include <ImfOutputPart.h> +#include <ImfMultiPartOutputFile.h> +#include <ImfTiledOutputPart.h> +#include <ImfPartType.h> +#include <ImfPartHelper.h> + using namespace Imf; using namespace Imath; +extern "C" +{ +/* prototype */ +static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname); +static bool exr_has_multiview(MultiPartInputFile& file); +static bool exr_has_multipart_file(MultiPartInputFile& file); +static int exr_has_alpha(MultiPartInputFile& file); +static int exr_has_zbuffer(MultiPartInputFile& file); +static void exr_printf(const char *__restrict format, ...); +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview); +} + /* Memory Input Stream */ class Mem_IStream : public Imf::IStream @@ -340,13 +363,21 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf) addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */ } -static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -355,13 +386,22 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(HALF)); - header.channels().insert("G", Channel(HALF)); - header.channels().insert("B", Channel(HALF)); - if (is_alpha) - header.channels().insert("A", Channel(HALF)); - if (is_zbuf) // z we do as float always - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("G", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("B", views, view_id), Channel(HALF)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(HALF)); + if (is_zbuf) // z we do as float always + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -370,75 +410,91 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags OutputFile file(file_stream, header); /* we store first everything in half array */ - RGBAZ *pixels = new RGBAZ[height * width]; - RGBAZ *to = pixels; + RGBAZ *pixels = new RGBAZ[height * width * totviews]; 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 (is_alpha) - frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (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 + channels * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = from[0]; - to->g = (channels >= 2) ? from[1] : from[0]; - to->b = (channels >= 3) ? from[2] : from[0]; - to->a = (channels >= 4) ? from[3] : 1.0f; - to++; from += channels; + for (view_id = 0; view_id < totviews; view_id ++) { + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + const size_t offset = view_id * width * height; + RGBAZ *to = pixels + offset; + + /* indicate used buffers */ + frameBuffer.insert(insertViewName("R", views, view_id), Slice(HALF, (char *) &pixels[offset].r, xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(HALF, (char *) &pixels[offset].g, xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(HALF, (char *) &pixels[offset].b, xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(HALF, (char *) &pixels[offset].a, xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *)(view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + if (view_ibuf->rect_float) { + float *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = view_ibuf->rect_float + channels * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = from[0]; + to->g = (channels >= 2) ? from[1] : from[0]; + to->b = (channels >= 3) ? from[2] : from[0]; + to->a = (channels >= 4) ? from[3] : 1.0f; + to++; from += channels; + } } } - } - else { - unsigned char *from; - - for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + 4 * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = srgb_to_linearrgb((float)from[0] / 255.0f); - to->g = srgb_to_linearrgb((float)from[1] / 255.0f); - to->b = srgb_to_linearrgb((float)from[2] / 255.0f); - to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; - to++; from += 4; + else { + unsigned char *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = (unsigned char *)view_ibuf->rect + 4 * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = srgb_to_linearrgb((float)from[0] / 255.0f); + to->g = srgb_to_linearrgb((float)from[1] / 255.0f); + to->b = srgb_to_linearrgb((float)from[2] / 255.0f); + to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; + to++; from += 4; + } } } + + if (is_multiview) + IMB_freeImBuf(view_ibuf); } -// printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); + exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); file.setFrameBuffer(frameBuffer); file.writePixels(height); delete[] pixels; } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - return (0); + return false; } - return (1); + return true; } -static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -447,13 +503,22 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(Imf::FLOAT)); - header.channels().insert("G", Channel(Imf::FLOAT)); - header.channels().insert("B", Channel(Imf::FLOAT)); - if (is_alpha) - header.channels().insert("A", Channel(Imf::FLOAT)); - if (is_zbuf) - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("G", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("B", views, view_id), Channel(Imf::FLOAT)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(Imf::FLOAT)); + if (is_zbuf) + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -463,37 +528,41 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag int xstride = sizeof(float) * channels; int ystride = -xstride * width; - float *rect[4] = {NULL, NULL, NULL, NULL}; - /* last scanline, stride negative */ - rect[0] = ibuf->rect_float + channels * (height - 1) * width; - rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; - rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; - rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ - - frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); - frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); - frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); - if (is_alpha) - frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width), - sizeof(float), sizeof(float) * -width)); + for (view_id = 0; view_id < totviews; view_id ++) { + float *rect[4] = {NULL, NULL, NULL, NULL}; + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + + /* last scanline, stride negative */ + rect[0] = view_ibuf->rect_float + channels * (height - 1) * width; + rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; + rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; + rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ + + frameBuffer.insert(insertViewName("R", views, view_id), Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *) (view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + + if (is_multiview) + IMB_freeImBuf(view_ibuf); + } file.setFrameBuffer(frameBuffer); file.writePixels(height); } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - - return (0); + return false; } - return (1); - // printf("OpenEXR-save: Done.\n"); + return true; } - int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) { if (flags & IB_mem) { @@ -504,16 +573,48 @@ int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) } if (ibuf->ftype & OPENEXR_HALF) - return imb_save_openexr_half(ibuf, name, flags); + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else { + /* when no float rect, we save as half (16 bits is sufficient) */ + if (ibuf->rect_float == NULL) + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else + return (int) imb_save_openexr_float(ibuf, name, flags, 1, NULL, NULL); + } +} + +static bool imb_save_openexr_multiview(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + if (flags & IB_mem) { + printf("OpenEXR-save: Create multiview EXR in memory CURRENTLY NOT SUPPORTED !\n"); + imb_addencodedbufferImBuf(ibuf); + ibuf->encodedsize = 0; + return false; + } + + if (ibuf->ftype & OPENEXR_HALF) + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); 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); + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); else - return imb_save_openexr_float(ibuf, name, flags); + return imb_save_openexr_float(ibuf, name, flags, totviews, getview, getbuffer); } } +/* Save single-layer multiview OpenEXR + * If we have more multiview formats in the future, the function below could be incorporated + * in our ImBuf write functions, meanwhile this is an OpenEXR special case only */ +bool IMB_exr_multiview_save(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + return imb_save_openexr_multiview(ibuf, name, flags, totviews, getview, getbuffer); +} + /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */ /* naming rules: @@ -527,18 +628,22 @@ static ListBase exrhandles = {NULL, NULL}; typedef struct ExrHandle { struct ExrHandle *next, *prev; + char name[FILE_MAX]; IFileStream *ifile_stream; - InputFile *ifile; + MultiPartInputFile *ifile; OFileStream *ofile_stream; - TiledOutputFile *tofile; + MultiPartOutputFile *mpofile; OutputFile *ofile; int tilex, tiley; int width, height; int mipmap; + StringVector *multiView; /* it needs to be a pointer due to Windows release builds of EXR2.0 segfault when opening EXR bug */ + int parts; + ListBase channels; /* flattened out, ExrChannel */ ListBase layers; /* hierarchical, pointing in end to ExrChannel */ } ExrHandle; @@ -547,10 +652,12 @@ typedef struct ExrHandle { typedef struct ExrChannel { struct ExrChannel *next, *prev; - char name[EXR_TOT_MAXNAME + 1]; /* full name of layer+pass */ + char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */ + struct MultiViewChannelName *m; /* struct to store all multipart channel info */ int xstride, ystride; /* step to next pixel, to next scanline */ float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ + int view_id; /* quick lookup of channel view */ } ExrChannel; @@ -562,6 +669,10 @@ typedef struct ExrPass { float *rect; struct ExrChannel *chan[EXR_PASS_MAXCHAN]; char chan_id[EXR_PASS_MAXCHAN]; + + char internal_name[EXR_PASS_MAXNAME]; /* name with no view */ + char view[EXR_VIEW_MAXNAME]; + int view_id; } ExrPass; typedef struct ExrLayer { @@ -575,39 +686,141 @@ typedef struct ExrLayer { void *IMB_exr_get_handle(void) { ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle"); + data->multiView = new StringVector(); + BLI_addtail(&exrhandles, data); return data; } +void *IMB_exr_get_handle_name(const char *name) +{ + ExrHandle *data = (ExrHandle *) BLI_rfindstring(&exrhandles, name, offsetof(ExrHandle, name)); + + if (data == NULL) { + data = (ExrHandle *)IMB_exr_get_handle(); + BLI_strncpy(data->name, name, strlen(name) + 1); + } + return data; +} + +/* multiview functions */ +} // extern "C" + +extern "C" +{ + +void IMB_exr_add_view(void *handle, const char *name) +{ + ExrHandle *data = (ExrHandle *)handle; + data->multiView->push_back(name); +} + +static int imb_exr_get_multiView_id(StringVector& views, const std::string& name) +{ + int count = 0; + for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) { + if (name == *i) + return count; + else + count ++; + } + + /* no views or wrong name */ + return -1; +} + +static void imb_exr_get_views(MultiPartInputFile& file, StringVector& views) +{ + if (exr_has_multipart_file(file) == false) { + if (exr_has_multiview(file)) { + StringVector sv = multiView(file.header(0)); + for (StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i) + views.push_back(*i); + } + } + + else { + for (int p = 0; p < file.parts(); p++) { + std::string view = ""; + if (file.header(p).hasView()) + view = file.header(p).view(); + + if (imb_exr_get_multiView_id(views, view) == -1) + views.push_back(view); + } + } +} + +/* Multilayer Blender files have the view name in all the passes (even the default view one) */ +static const char *imb_exr_insert_view_name(const char *passname, const char *viewname) +{ + if (viewname == NULL || viewname[0] == '\0') + return passname; + + static char retstr[EXR_PASS_MAXNAME]; + const char *end = passname + strlen(passname); + const char *token; + + int len = IMB_exr_split_token(passname, end, &token); + + if (len == 0) + BLI_snprintf(retstr, sizeof(retstr), "%s.%s", passname, viewname); + else + BLI_snprintf(retstr, sizeof(retstr), "%.*s%s.%s", + (int)(end - passname) - len, passname, viewname, token); + + return retstr; +} + /* adds flattened ExrChannels */ /* xstride, ystride and rect can be done in set_channel too, for tile writing */ -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) +/* passname does not include view */ +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; - echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel"); + echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel"); + echan->m = new MultiViewChannelName (); - if (layname) { - char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; - BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); - BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); + if (layname && layname[0] != '\0') { + echan->m->name = layname; + echan->m->name.append("."); + echan->m->name.append(passname); + } + else { + echan->m->name.assign(passname); + } - BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass); + echan->m->internal_name = echan->m->name; + + echan->m->view.assign(viewname ? viewname : ""); + + /* quick look up */ + echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view)); + + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(echan->m->name.c_str(), echan->m->view.c_str()); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); + } + else if (data->multiView->size() > 1) { + std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); } else { - BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1); + BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name)); } echan->xstride = xstride; echan->ystride = ystride; echan->rect = rect; - // printf("added channel %s\n", echan->name); + exr_printf("added channel %s\n", echan->name); BLI_addtail(&data->channels, echan); } -/* only used for writing temp. render results (not image files) */ +/* used for output files (from RenderResult) (single and multilayer, single and multiview) */ int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress) { ExrHandle *data = (ExrHandle *)handle; @@ -617,6 +830,8 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->width = width; data->height = height; + bool is_singlelayer, is_multilayer, is_multiview; + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) header.channels().insert(echan->name, Channel(Imf::FLOAT)); @@ -624,7 +839,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh // openexr_header_metadata(&header, ibuf); // no imbuf. cant write /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */ - header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + imb_exr_type_by_channels(header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview); + + if (is_multilayer) + header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + + if (is_multiview) + addMultiView(header, *data->multiView); /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ @@ -632,7 +853,7 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->ofile_stream = new OFileStream(filename); data->ofile = new OutputFile(*(data->ofile_stream), header); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl; delete data->ofile; @@ -645,10 +866,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh return (data->ofile != NULL); } +/* only used for writing temp. render results (not image files) + * (FSA and Save Buffers) */ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); + std::vector<Header> headers; ExrChannel *echan; data->tilex = tilex; @@ -657,26 +881,47 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int data->height = height; data->mipmap = mipmap; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) - header.channels().insert(echan->name, Channel(Imf::FLOAT)); - header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL)); - header.lineOrder() = RANDOM_Y; header.compression() = RLE_COMPRESSION; + header.setType(TILEDIMAGE); header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43")); + int numparts = data->multiView->size(); + + /* copy header from all parts of input to our header array + * those temporary files have one part per view */ + for (int i = 0; i < numparts; i++) { + headers.push_back (header); + headers[headers.size() - 1].setView((*(data->multiView))[i]); + headers[headers.size() - 1].setName((*(data->multiView))[i]); + } + + exr_printf("\nIMB_exrtile_begin_write\n"); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------\n"); + + /* assign channels */ + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + echan->m->internal_name = echan->m->name; + echan->m->part_number = echan->view_id; + + headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT)); + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); + } + /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { data->ofile_stream = new OFileStream(filename); - data->tofile = new TiledOutputFile(*(data->ofile_stream), header); + data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); } catch (const std::exception &) { - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; } } @@ -685,12 +930,13 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) { ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; if (BLI_exists(filename) && BLI_file_size(filename) > 32) { /* 32 is arbitrary, but zero length files crashes exr */ /* avoid crash/abort when we don't have permission to write here */ try { data->ifile_stream = new IFileStream(filename); - data->ifile = new InputFile(*(data->ifile_stream)); + data->ifile = new MultiPartInputFile(*(data->ifile_stream)); } catch (const std::exception &) { delete data->ifile; @@ -701,14 +947,24 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } if (data->ifile) { - Box2i dw = data->ifile->header().dataWindow(); + Box2i dw = data->ifile->header(0).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(); + imb_exr_get_views(*data->ifile, *data->multiView); + + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } return 1; } @@ -717,6 +973,7 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } /* still clumsy name handling, layers/channels can be ordered as list in list later */ +/* passname here is the raw channel name without the layer */ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; @@ -741,37 +998,53 @@ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname echan->rect = rect; } else - printf("IMB_exrtile_set_channel error %s\n", name); -} - -void IMB_exrtile_clear_channels(void *handle) -{ - ExrHandle *data = (ExrHandle *)handle; - BLI_freelistN(&data->channels); + printf("IMB_exr_set_channel error %s\n", name); } -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level) +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; - FrameBuffer frameBuffer; ExrChannel *echan; + char name[EXR_TOT_MAXNAME + 1]; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + if (layname) { + char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; + BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); + BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect, - echan->xstride * sizeof(float), echan->ystride * sizeof(float))); + BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass); } + else + BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1); - data->tofile->setFrameBuffer(frameBuffer); - - try { - // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); - data->tofile->writeTile(partx / data->tilex, party / data->tiley, level); + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(name, viewname); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } - catch (const std::exception &exc) { - std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + else if (data->multiView->size() > 1) { + size_t view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname)); + std::string raw_name = insertViewName(name, *data->multiView, view_id); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } + + echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name)); + + if (echan) + return echan->rect; + + return NULL; +} + +void IMB_exr_clear_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *chan; + + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) + delete chan->m; + + BLI_freelistN(&data->channels); } void IMB_exr_write_channels(void *handle) @@ -793,7 +1066,7 @@ void IMB_exr_write_channels(void *handle) try { data->ofile->writePixels(data->height); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl; } } @@ -802,49 +1075,171 @@ void IMB_exr_write_channels(void *handle) } } -void IMB_exr_read_channels(void *handle) +/* temporary function, used for FSA and Save Buffers */ +/* called once per tile * view */ +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; FrameBuffer frameBuffer; ExrChannel *echan; + std::string view(viewname); + const size_t view_id = imb_exr_get_multiView_id(*data->multiView, view); + + exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------------\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + /* eventually we can make the parts' channels to include + only the current view TODO */ + if (strcmp(viewname, echan->m->view.c_str()) != 0) + continue; + + exr_printf("%d %-6s %-22s \"%s\"\n", + echan->m->part_number, + echan->m->view.c_str(), + echan->m->name.c_str(), + echan->m->internal_name.c_str() + ); + + float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + frameBuffer.insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + echan->ystride * sizeof(float) + ) + ); + } + + TiledOutputPart out (*data->mpofile, view_id); + out.setFrameBuffer(frameBuffer); + + try { + // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); + out.writeTile(partx / data->tilex, party / data->tiley, level); + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + } +} + +/* called only when handle has all views */ +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) +{ + ExrHandle *data = (ExrHandle *)handle; + const size_t view_id = viewname ? imb_exr_get_multiView_id(*data->multiView, viewname) : -1; + int numparts = (view_id == -1 ? data->parts : view_id + 1); + std::vector <FrameBuffer> frameBuffers(numparts); + std::vector <OutputPart> outputParts; + ExrChannel *echan; + int i, part; + + if (data->channels.first == NULL) + return; + + exr_printf("\nIMB_exrmultiview_write_channels()\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + if (view_id != -1 && echan->view_id != view_id) + continue; + + part = (view_id == -1 ? echan->m->part_number : echan->view_id); + + /* last scanline, stride negative */ + float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width; + frameBuffers[part].insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + -echan->ystride * sizeof(float)) + ); + } + + for (i = 0; i < numparts; i++) { + OutputPart out(*data->mpofile, i); + out.setFrameBuffer(frameBuffers[i]); + outputParts.push_back(out); + } + + try { + for (i = 0; i < numparts; i++) { + if (view_id != -1 && i != view_id) + continue; + + outputParts[i].writePixels(data->height); + } + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-write Multi Part: ERROR: " << exc.what() << std::endl; + } +} + +void IMB_exr_read_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; + int numparts = data->ifile->parts(); + std::vector<FrameBuffer> frameBuffers(numparts); + std::vector<InputPart> inputParts; /* check if exr was saved with previous versions of blender which flipped images */ - const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); + const StringAttribute *ta = data->ifile->header(0).findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); /* 'previous multilayer attribute, flipped */ + exr_printf("\nIMB_exr_read_channels\n%s %-6s %-22s \"%s\"\n---------------------------------------------------------------------\n", "p", "view", "name", "internal_name"); + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); if (echan->rect) { if (flip) - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)echan->rect, + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)echan->rect, echan->xstride * sizeof(float), echan->ystride * sizeof(float))); else - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), echan->xstride * sizeof(float), -echan->ystride * sizeof(float))); } else - printf("warning, channel with no rect set %s\n", echan->name); + printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str()); } - data->ifile->setFrameBuffer(frameBuffer); + for (int i = 0; i < numparts; i++) { + InputPart in (*data->ifile, i); + in.setFrameBuffer(frameBuffers[i]); + inputParts.push_back(in); + } try { - data->ifile->readPixels(0, data->height - 1); + for (int i = 0; i < numparts; i++) { + Header header = inputParts[i].header(); + exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(0, data->height - 1); + } } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl; } } void IMB_exr_multilayer_convert(void *handle, void *base, + void * (*addview)(void *base, const char *str), void * (*addlayer)(void *base, const char *str), void (*addpass)(void *base, void *lay, const char *str, - float *rect, int totchan, const char *chan_id)) + float *rect, int totchan, const char *chan_id, + const char *view)) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + if (BLI_listbase_is_empty(&data->layers)) { printf("cannot convert multilayer, no layers in handle\n"); return; @@ -854,32 +1249,98 @@ void IMB_exr_multilayer_convert(void *handle, void *base, void *laybase = addlayer(base, lay->name); if (laybase) { for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { - addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id); + addpass(base, laybase, pass->internal_name, pass->rect, pass->totchan, pass->chan_id, pass->view); pass->rect = NULL; } } } } +void IMB_exr_multiview_convert(void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, ImBuf *ibuf, const int frame), + const int frame) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + ExrLayer *lay; + ExrPass *pass; + ImBuf *ibuf = NULL; + const int is_alpha = exr_has_alpha(*file); + Box2i dw = file->header(0).dataWindow(); + const size_t width = dw.max.x - dw.min.x + 1; + const size_t height = dw.max.y - dw.min.y + 1; + const bool is_depth = exr_has_zbuffer(*file); + + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + + if (BLI_listbase_is_empty(&data->layers)) { + printf("cannot convert multiviews, no views in handle\n"); + return; + } + + /* there is one float/pass per layer (layer here is a view) */ + BLI_assert(BLI_listbase_count_ex(&data->layers, 2) == 1); + lay = (ExrLayer *)data->layers.first; + for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { + if (STREQ(pass->chan_id, "RGB") || STREQ(pass->chan_id, "RGBA")) { + ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, IB_rectfloat); + + if (!ibuf) { + printf("error creating multiview buffer\n"); + return; + } + + IMB_buffer_float_from_float( + ibuf->rect_float, pass->rect, pass->totchan, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + ibuf->x, ibuf->y, ibuf->x, ibuf->x); + + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); + } + + if (is_depth) { + ExrPass *zpass; + for (zpass = (ExrPass *)lay->passes.first; zpass; zpass = zpass->next) { + if (STREQ(zpass->chan_id, "Z") && STREQ(zpass->view, pass->view)) { + addzbuffloatImBuf(ibuf); + memcpy(ibuf->zbuf_float, zpass->rect, sizeof(float) * ibuf->x * ibuf->y); + } + } + } + + addbuffer(base, pass->view, ibuf, frame); + } + } +} void IMB_exr_close(void *handle) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + ExrChannel *chan; delete data->ifile; delete data->ifile_stream; delete data->ofile; - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; data->ifile = NULL; data->ifile_stream = NULL; data->ofile = NULL; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) { + delete chan->m; + } BLI_freelistN(&data->channels); for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { @@ -897,7 +1358,7 @@ void IMB_exr_close(void *handle) /* ********* */ /* get a substring from the end of the name, separated by '.' */ -static int imb_exr_split_token(const char *str, const char *end, const char **token) +int IMB_exr_split_token(const char *str, const char *end, const char **token) { ptrdiff_t maxlen = end - str; int len = 0; @@ -911,7 +1372,7 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname) { - const char *name = echan->name; + const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; char tokenbuf[EXR_TOT_MAXNAME]; @@ -933,7 +1394,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is single character channel identifier */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -971,7 +1432,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa end -= len + 1; /* +1 to skip '.' separator */ /* second token is pass name */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -1020,7 +1481,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) } /* creates channels, makes a hierarchy and assigns memory to channels */ -static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) +static ExrHandle *imb_exr_begin_read_mem(MultiPartInputFile& file, int width, int height) { ExrLayer *lay; ExrPass *pass; @@ -1029,30 +1490,57 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) int a; char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; - data->ifile = file; + data->ifile = &file; data->width = width; data->height = height; - const ChannelList &channels = data->ifile->header().channels(); + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + data->multiView = new StringVector(); + imb_exr_get_views(*data->ifile, *data->multiView); + + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - if (imb_exr_split_channel_name(echan, layname, passname) ) { + if (imb_exr_split_channel_name(echan, layname, passname)) { + + const char *view = echan->m->view.c_str(); + char internal_name[EXR_PASS_MAXNAME]; + + BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME); + + if (view[0] != '\0') { + char tmp_pass[EXR_PASS_MAXNAME]; + BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view); + BLI_strncpy(passname, tmp_pass, sizeof(passname)); + } + ExrLayer *lay = imb_exr_get_layer(&data->layers, layname); ExrPass *pass = imb_exr_get_pass(&lay->passes, passname); pass->chan[pass->totchan] = echan; pass->totchan++; + pass->view_id = echan->view_id; + BLI_strncpy(pass->view, view, sizeof(pass->view)); + BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME); + if (pass->totchan >= EXR_PASS_MAXCHAN) break; } } if (echan) { - printf("error, too many channels in one pass: %s\n", echan->name); + printf("error, too many channels in one pass: %s\n", echan->m->name.c_str()); IMB_exr_close(data); return NULL; } @@ -1122,20 +1610,52 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) /* ********************************************************* */ /* debug only */ -static void exr_print_filecontents(InputFile *file) +static void exr_printf(const char *fmt, ...) { - const ChannelList &channels = file->header().channels(); +#if 0 + char output[1024]; + va_list args; + va_start(args, fmt); + std::vsprintf(output, fmt, args); + va_end(args); + printf("%s", output); +#else + (void)fmt; +#endif +} - 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 void exr_print_filecontents(MultiPartInputFile& file) +{ + int numparts = file.parts(); + if (numparts == 1 && hasMultiView(file.header(0))) { + const StringVector views = multiView(file.header(0)); + printf("OpenEXR-load: MultiView file\n"); + printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str()); + for (StringVector::const_iterator i = views.begin(); i != views.end(); ++i) { + printf("OpenEXR-load: Found view %s\n", (*i).c_str()); + } + } + else if (numparts > 1) { + printf("OpenEXR-load: MultiPart file\n"); + for (int i = 0; i < numparts; i++) { + if (file.header(i).hasView()) + printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str()); + } + } + + for (int j = 0; j < numparts; j++) { + const ChannelList& channels = file.header(j).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); + } } } /* for non-multilayer, map R G B A channel names to something that's in this file */ -static const char *exr_rgba_channelname(InputFile *file, const char *chan) +static const char *exr_rgba_channelname(MultiPartInputFile& file, const char *chan) { - const ChannelList &channels = file->header().channels(); + const ChannelList& channels = file.header(0).channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { /* const Channel &channel = i.channel(); */ /* Not used yet */ @@ -1150,48 +1670,48 @@ static const char *exr_rgba_channelname(InputFile *file, const char *chan) return chan; } -static bool exr_has_rgb(InputFile *file) +static bool exr_has_rgb(MultiPartInputFile& file) { - return file->header().channels().findChannel("R") != NULL && - file->header().channels().findChannel("G") != NULL && - file->header().channels().findChannel("B") != NULL; + return file.header(0).channels().findChannel("R") != NULL && + file.header(0).channels().findChannel("G") != NULL && + file.header(0).channels().findChannel("B") != NULL; } -static bool exr_has_luma(InputFile *file) +static bool exr_has_luma(MultiPartInputFile& file) { /* Y channel is the luma and should always present fir luma space images, * optionally it could be also channels for chromas called BY and RY. */ - return file->header().channels().findChannel("Y") != NULL; + return file.header(0).channels().findChannel("Y") != NULL; } -static bool exr_has_chroma(InputFile *file) +static bool exr_has_chroma(MultiPartInputFile& file) { - return file->header().channels().findChannel("BY") != NULL && - file->header().channels().findChannel("RY") != NULL; + return file.header(0).channels().findChannel("BY") != NULL && + file.header(0).channels().findChannel("RY") != NULL; } -static int exr_has_zbuffer(InputFile *file) +static int exr_has_zbuffer(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("Z") == NULL); + return !(file.header(0).channels().findChannel("Z") == NULL); } -static int exr_has_alpha(InputFile *file) +static int exr_has_alpha(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("A") == NULL); + return !(file.header(0).channels().findChannel("A") == NULL); } -static bool exr_is_multilayer(InputFile *file) +static bool imb_exr_is_multilayer_file(MultiPartInputFile& file) { - const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel"); - const ChannelList &channels = file->header().channels(); + const StringAttribute *comments = file.header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + const ChannelList& channels = file.header(0).channels(); std::set <std::string> layerNames; /* will not include empty layer names */ channels.layers(layerNames); if (comments || layerNames.size() > 1) - return 1; + return true; if (layerNames.size()) { /* if layerNames is not empty, it means at least one layer is non-empty, @@ -1206,17 +1726,122 @@ static bool exr_is_multilayer(InputFile *file) size_t pos = layerName.rfind ('.'); if (pos == std::string::npos) - return 1; + return true; } } - return 0; + return false; +} + +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview) +{ + std::set <std::string> layerNames; + + *r_singlelayer = true; + *r_multilayer = *r_multiview = false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + if (views.size() && views[0] != "") + *r_multiview = true; + + if (layerNames.size()) { + /* if layerNames is not empty, it means at least one layer is non-empty, + * but it also could be layers without names in the file and such case + * shall be considered a multilayer exr + * + * that's what we do here: test whether there're empty layer names together + * with non-empty ones in the file + */ + for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + /* see if any layername differs from a viewname */ + if (imb_exr_get_multiView_id(views, *i) == -1) { + std::string layerName = *i; + size_t pos = layerName.rfind ('.'); + + if (pos != std::string::npos) { + *r_multilayer = true; + *r_singlelayer = false; + return; + } + } + } + else { + *r_singlelayer = true; + *r_multilayer = false; + *r_multiview = false; + } + + BLI_assert(r_singlelayer != r_multilayer); +} + +bool IMB_exr_has_singlelayer_multiview(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + std::set <std::string> layerNames; + const ChannelList& channels = file->header(0).channels(); + const StringAttribute *comments; + + if (exr_has_multiview(*file) == false) + return false; + + comments = file->header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + + if (comments) + return false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + /* returns false if any layer differs from views list */ + if (layerNames.size()) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + if (imb_exr_get_multiView_id(*data->multiView, *i) == -1) + return false; + + return true; +} + +bool IMB_exr_has_multilayer(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + return imb_exr_is_multilayer_file(*data->ifile); +} + +static bool exr_has_multiview(MultiPartInputFile& file) +{ + return hasMultiView(file.header(0)); +} + +static bool exr_has_multipart_file(MultiPartInputFile& file) +{ + return file.parts() > 1; +} + +/* it returns true if the file is multilayer or multiview */ +static bool imb_exr_is_multi(MultiPartInputFile& file) +{ + /* multipart files are treated as multilayer in blender - even if they are single layer openexr with multiview */ + if (exr_has_multipart_file(file)) + return true; + + if (exr_has_multiview(file)) + return true; + + if (imb_exr_is_multilayer_file(file)) + return true; + + return false; } struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; - InputFile *file = NULL; + MultiPartInputFile *file = NULL; if (imb_is_a_openexr(mem) == 0) return(NULL); @@ -1226,9 +1851,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char { Mem_IStream *membuf = new Mem_IStream(mem, size); bool is_multi; - file = new InputFile(*membuf); + file = new MultiPartInputFile(*membuf); - Box2i dw = file->header().dataWindow(); + Box2i dw = file->header(0).dataWindow(); const int width = dw.max.x - dw.min.x + 1; const int height = dw.max.y - dw.min.y + 1; @@ -1236,38 +1861,38 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char // dw.min.x, dw.min.y, dw.max.x, dw.max.y); if (0) // debug - exr_print_filecontents(file); + exr_print_filecontents(*file); - is_multi = exr_is_multilayer(file); + is_multi = imb_exr_is_multi(*file); /* do not make an ibuf when */ if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) { printf("Error: can't process EXR multilayer file\n"); } else { - const int is_alpha = exr_has_alpha(file); + const int is_alpha = exr_has_alpha(*file); ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0); - if (hasXDensity(file->header())) { - ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f; - ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio(); + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); } ibuf->ftype = OPENEXR; if (!(flags & IB_test)) { - if (is_multi) { /* only enters with IB_multilayer flag set */ + if (is_multi && ((flags & IB_thumbnail) == 0)) { /* only enters with IB_multilayer flag set */ /* constructs channels for reading, allocates memory in channels */ - ExrHandle *handle = imb_exr_begin_read_mem(file, width, height); + ExrHandle *handle = imb_exr_begin_read_mem(*file, width, height); if (handle) { IMB_exr_read_channels(handle); ibuf->userdata = handle; /* potential danger, the caller has to check for this! */ } } else { - const bool has_rgb = exr_has_rgb(file); - const bool has_luma = exr_has_luma(file); + const bool has_rgb = exr_has_rgb(*file); + const bool has_luma = exr_has_luma(*file); FrameBuffer frameBuffer; float *first; int xstride = sizeof(float) * 4; @@ -1281,27 +1906,27 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char first += 4 * (height - 1) * width; if (has_rgb) { - frameBuffer.insert(exr_rgba_channelname(file, "R"), + frameBuffer.insert(exr_rgba_channelname(*file, "R"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "G"), + frameBuffer.insert(exr_rgba_channelname(*file, "G"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "B"), + frameBuffer.insert(exr_rgba_channelname(*file, "B"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride)); } else if (has_luma) { - frameBuffer.insert(exr_rgba_channelname(file, "Y"), + frameBuffer.insert(exr_rgba_channelname(*file, "Y"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "BY"), + frameBuffer.insert(exr_rgba_channelname(*file, "BY"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride, 1, 1, 0.5f)); - frameBuffer.insert(exr_rgba_channelname(file, "RY"), + frameBuffer.insert(exr_rgba_channelname(*file, "RY"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride, 1, 1, 0.5f)); } /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */ - frameBuffer.insert(exr_rgba_channelname(file, "A"), + frameBuffer.insert(exr_rgba_channelname(*file, "A"), Slice(Imf::FLOAT, (char *) (first + 3), xstride, ystride, 1, 1, 1.0f)); - if (exr_has_zbuffer(file)) { + if (exr_has_zbuffer(*file)) { float *firstz; addzbuffloatImBuf(ibuf); @@ -1310,8 +1935,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float))); } - file->setFrameBuffer(frameBuffer); - file->readPixels(dw.min.y, dw.max.y); + InputPart in (*file, 0); + in.setFrameBuffer(frameBuffer); + in.readPixels(dw.min.y, dw.max.y); // XXX, ImBuf has no nice way to deal with this. // ideally IM_rect would be used when the caller wants a rect BUT @@ -1325,7 +1951,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char if (!has_rgb && has_luma) { size_t a; - if (exr_has_chroma(file)) { + if (exr_has_chroma(*file)) { for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) { float *color = ibuf->rect_float + a * 4; ycc_to_rgb(color[0] * 255.0f, color[1] * 255.0f, color[2] * 255.0f, @@ -1351,7 +1977,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char } return(ibuf); } - catch (const std::exception &exc) + catch (const std::exception& exc) { std::cerr << exc.what() << std::endl; if (ibuf) IMB_freeImBuf(ibuf); |