diff options
Diffstat (limited to 'source/blender/imbuf')
-rw-r--r-- | source/blender/imbuf/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/imbuf/IMB_imbuf.h | 40 | ||||
-rw-r--r-- | source/blender/imbuf/IMB_imbuf_types.h | 4 | ||||
-rw-r--r-- | source/blender/imbuf/intern/IMB_anim.h | 1 | ||||
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 5 | ||||
-rw-r--r-- | source/blender/imbuf/intern/indexer.c | 12 | ||||
-rw-r--r-- | source/blender/imbuf/intern/openexr/openexr_api.cpp | 1030 | ||||
-rw-r--r-- | source/blender/imbuf/intern/openexr/openexr_multi.h | 39 | ||||
-rw-r--r-- | source/blender/imbuf/intern/openexr/openexr_stub.cpp | 46 | ||||
-rw-r--r-- | source/blender/imbuf/intern/stereoimbuf.c | 1292 | ||||
-rw-r--r-- | source/blender/imbuf/intern/thumbs.c | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/util.c | 12 | ||||
-rw-r--r-- | source/blender/imbuf/intern/writeimage.c | 30 |
13 files changed, 2273 insertions, 241 deletions
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index e8977913948..eadedbd5e00 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/rectop.c intern/rotate.c intern/scaling.c + intern/stereoimbuf.c intern/targa.c intern/thumbs.c intern/thumbs_blend.c diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 18682302b3a..a39832dd1cb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -89,6 +89,13 @@ struct ColorManagedDisplay; struct GSet; /** * + * \attention defined in DNA_scene_types.h + */ +struct ImageFormatData; +struct Stereo3dFormat; + +/** + * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -279,10 +286,10 @@ bool IMB_anim_get_fps(struct anim *anim, * \attention Defined in anim_movie.c */ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]); +void IMB_suffix_anim(struct anim *anim, const char *suffix); void IMB_close_anim(struct anim *anim); void IMB_close_anim_proxies(struct anim *anim); - /** * * \attention Defined in anim_movie.c @@ -377,6 +384,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ short IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); +struct ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf); /** * @@ -399,6 +407,12 @@ int imb_get_anim_type(const char *name); /** * + * \attention Defined in util.c + */ +bool IMB_isfloat(struct ImBuf *ibuf); + +/** + * * \attention Defined in divers.c */ void IMB_de_interlace(struct ImBuf *ibuf); @@ -557,5 +571,27 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); -#endif +/** + * + * \attention defined in stereoimbuf.c + */ +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +int *IMB_stereo3d_from_rect( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right); +float *IMB_stereo3d_from_rectf( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right); +struct ImBuf *IMB_stereo3d_ImBuf( + struct ImageFormatData *im_format, + struct ImBuf *ibuf_left, struct ImBuf *ibuf_right); +void IMB_ImBufFromStereo3d( + struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo, + struct ImBuf **r_ibuf_left, struct ImBuf **r_ibuf_right); +#endif diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 9e7201f53b4..862c587f75b 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -110,7 +110,7 @@ typedef struct ImBuf { int index; /* reference index for ImBuf lists */ int userflags; /* used to set imbuf to dirty and other stuff */ struct IDProperty *metadata; /* image metadata */ - void *userdata; /* temporary storage, only used by baking at the moment */ + void *userdata; /* temporary storage */ /* file information */ int ftype; /* file type we are going to save as */ @@ -172,6 +172,8 @@ typedef struct ImBuf { #define IB_alphamode_premul (1 << 12) /* indicates whether image on disk have premul alpha */ #define IB_alphamode_detect (1 << 13) /* if this flag is set, alpha mode would be guessed from file */ #define IB_ignore_alpha (1 << 14) /* ignore alpha on load and substitude it with 1.0f */ +#define IB_thumbnail (1 << 15) +#define IB_multiview (1 << 16) /* * The bit flag is stored in the ImBuf.ftype variable. diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index ed349e8f7eb..1fc43e22f68 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -194,6 +194,7 @@ struct anim { struct anim_index *curr_idx[IMB_TC_MAX_SLOT]; char colorspace[64]; + char suffix[64]; /* MAX_NAME - multiview */ }; #endif diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index ffdecb793aa..0bb9f0fce51 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -288,6 +288,11 @@ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char return(anim); } +void IMB_suffix_anim(struct anim *anim, const char *suffix) +{ + BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix)); +} + #ifdef WITH_AVI static int startavi(struct anim *anim) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 183159f7d6c..150ea0995ac 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -398,7 +398,7 @@ static void get_proxy_filename(struct anim *anim, IMB_Proxy_Size preview_size, } BLI_snprintf(proxy_name, sizeof(proxy_name), name, - (int) (proxy_fac[i] * 100), stream_suffix); + (int) (proxy_fac[i] * 100), stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); @@ -411,10 +411,10 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); const char *index_names[] = { - "record_run%s.blen_tc", - "free_run%s.blen_tc", - "interp_free_run%s.blen_tc", - "record_run_no_gaps%s.blen_tc" + "record_run%s%s.blen_tc", + "free_run%s%s.blen_tc", + "interp_free_run%s%s.blen_tc", + "record_run_no_gaps%s%s.blen_tc" }; char stream_suffix[20]; @@ -426,7 +426,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex); } - BLI_snprintf(index_name, 256, index_names[i], stream_suffix); + BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); 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); diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 376d2401b1c..45ab0ed37c7 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -39,8 +39,9 @@ /* This api also supports max 8 channels per pass now. easy to fix! */ #define EXR_LAY_MAXNAME 64 #define EXR_PASS_MAXNAME 64 +#define EXR_VIEW_MAXNAME 64 #define EXR_TOT_MAXNAME 64 -#define EXR_PASS_MAXCHAN 8 +#define EXR_PASS_MAXCHAN 24 #ifdef __cplusplus @@ -48,25 +49,49 @@ extern "C" { #endif void *IMB_exr_get_handle(void); -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +void *IMB_exr_get_handle_name(const char *name); +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect); int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress); void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *view); void IMB_exr_read_channels(void *handle); void IMB_exr_write_channels(void *handle); -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level); -void IMB_exrtile_clear_channels(void *handle); +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname); +void IMB_exrmultiview_write_channels(void *handle, const char *viewname); +void IMB_exr_clear_channels(void *handle); -void IMB_exr_multilayer_convert(void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)); +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, const char *view)); + +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), + const int frame); + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)); void IMB_exr_close(void *handle); +void IMB_exr_add_view(void *handle, const char *name); + +int IMB_exr_split_token(const char *str, const char *end, const char **token); + +bool IMB_exr_has_multilayer(void *handle); +bool IMB_exr_has_singlelayer_multiview(void *handle); + #ifdef __cplusplus } // extern "C" #endif diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 21fa878c08a..c326efd2427 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -31,27 +31,55 @@ #include "openexr_api.h" #include "openexr_multi.h" - +#include "BLI_utildefines.h" /* UNUSED_VARS */ void *IMB_exr_get_handle (void) {return NULL;} -void IMB_exr_add_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void *IMB_exr_get_handle_name (const char *name) {(void)name; return NULL;} +void IMB_exr_add_channel (void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } int IMB_exr_begin_read (void *handle, const char *filename, int *width, int *height) { (void)handle; (void)filename; (void)width; (void)height; return 0;} int IMB_exr_begin_write (void *handle, const char *filename, int width, int height, int compress) { (void)handle; (void)filename; (void)width; (void)height; (void)compress; return 0;} void IMB_exrtile_begin_write (void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { (void)handle; (void)filename; (void)mipmap; (void)width; (void)height; (void)tilex; (void)tiley; } -void IMB_exr_set_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void IMB_exr_set_channel (void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } +float *IMB_exr_channel_rect (void *handle, const char *layname, const char *passname, const char *view) { (void)handle; (void)layname; (void)passname; (void)view; return NULL; } void IMB_exr_read_channels (void *handle) { (void)handle; } void IMB_exr_write_channels (void *handle) { (void)handle; } -void IMB_exrtile_write_channels (void *handle, int partx, int party, int level) { (void)handle; (void)partx; (void)party; (void)level; } -void IMB_exrtile_clear_channels (void *handle) { (void)handle; } +void IMB_exrtile_write_channels (void *handle, int partx, int party, int level, const char *viewname) { UNUSED_VARS(handle, partx, party, level, viewname); } +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) { UNUSED_VARS(handle, viewname); } +void IMB_exr_clear_channels (void *handle) { (void)handle; } + +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, const char *view)) +{ + UNUSED_VARS(handle, base, addview, addlayer, addpass); +} -void IMB_exr_multilayer_convert (void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)) +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), const int frame) { - (void)handle; (void)base; (void)addlayer; (void)addpass; + UNUSED_VARS(handle, base, addview, addbuffer, frame); +} + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + UNUSED_VARS(ibuf, name, flags, totviews, getview, getbuffer); + return false; } void IMB_exr_close (void *handle) { (void)handle; } + +void IMB_exr_add_view(void *handle, const char *name) { UNUSED_VARS(handle, name); } +int IMB_exr_split_token(const char *str, const char *end, const char **token) { UNUSED_VARS(str, end, token); return 1; } +bool IMB_exr_has_multilayer(void *handle) { UNUSED_VARS(handle); return false; } +bool IMB_exr_has_singlelayer_multiview(void *handle) { UNUSED_VARS(handle); return false; } diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c new file mode 100644 index 00000000000..cf9c6b388ff --- /dev/null +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -0,0 +1,1292 @@ +/* + * ***** BEGIN GPL LICENSE 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/imbuf/intern/stereoimbuf.c + * \ingroup imbuf + */ + +#include <stddef.h> + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "IMB_allocimbuf.h" +#include "IMB_filetype.h" +#include "IMB_metadata.h" +#include "IMB_colormanagement_intern.h" + +#include "imbuf.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "DNA_userdef_types.h" +#include "DNA_scene_types.h" + +/* prototypes */ +struct Stereo3DData; +static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); +static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); + +typedef struct Stereo3DData { + struct { float *left, *right, *stereo; } rectf; + struct { uchar *left, *right, *stereo; } rect; + size_t x, y, channels; + bool is_float; +} Stereo3DData; + +static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } +} + +static void imb_stereo3d_write_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(float) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + const float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(uchar) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width * 2; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(float) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(uchar) * channels * stride_from); + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(float) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(uchar) * channels * stride_from); + } + } +} + +/**************************** dimension utils ****************************************/ + +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width : width * 2; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height : height * 2; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width / 2 : width; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height / 2 : height; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +/**************************** un/squeeze frame ****************************************/ + +static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_squeeze_rectf(float *rectf, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat); + + IMB_buffer_float_from_float( + ibuf->rect_float, rectf, channels, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rectf = MEM_dupallocN(ibuf->rect_float); + IMB_freeImBuf(ibuf); +} + +static void imb_stereo3d_squeeze_rect(int *rect, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rect); + + IMB_buffer_byte_from_byte( + (unsigned char *)ibuf->rect, (unsigned char *)rect, + IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rect = MEM_dupallocN(ibuf->rect); + IMB_freeImBuf(ibuf); +} + + +/*************************** preparing to call the write functions **************************/ + +static void imb_stereo3d_data_initialize( + Stereo3DData *s3d_data, const bool is_float, + const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right, int *rect_stereo, + float *rectf_left, float *rectf_right, float *rectf_stereo) +{ + s3d_data->is_float = is_float; + s3d_data->x = x; + s3d_data->y = y; + s3d_data->channels = channels; + s3d_data->rect.left = (uchar *)rect_left; + s3d_data->rect.right = (uchar *)rect_right; + s3d_data->rect.stereo = (uchar *)rect_stereo; + s3d_data->rectf.left = rectf_left; + s3d_data->rectf.right = rectf_right; + s3d_data->rectf.stereo = rectf_stereo; +} + +int *IMB_stereo3d_from_rect( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right) +{ + int *r_rect; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); + + return r_rect; +} + +float *IMB_stereo3d_from_rectf( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right) +{ + float *r_rectf; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); + + return r_rectf; +} + +/* left/right are always float */ +ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) +{ + ImBuf *ibuf_stereo = NULL; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height); + ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_stereo->flags = ibuf_left->flags; + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo->rect_float); + + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); + + return ibuf_stereo; +} + +static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_write_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_write_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_write_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_write_topbottom(s3d_data); + break; + default: + break; + } +} + +/******************************** reading stereo imbufs **********************/ + +static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_from = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_from = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } +} + +static void imb_stereo3d_read_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(float) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[i], from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[i], from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[j], from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[j], from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + uchar *rect_left = s3d->rect.right; + uchar *rect_right = s3d->rect.left; + const uchar *rect_from = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(uchar) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[i], (char *)from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[i], (char *)from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width * 2; + const int stride_to = width; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(float) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + /* always RGBA input/output */ + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(uchar) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(float) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(uchar) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + + +/*************************** preparing to call the read functions **************************/ + +/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */ +void IMB_ImBufFromStereo3d( + Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, + ImBuf **r_ibuf_left, ImBuf **r_ibuf_right) +{ + Stereo3DData s3d_data = {{NULL}}; + ImBuf *ibuf_left, *ibuf_right; + size_t width, height; + const bool is_float = (ibuf_stereo3d->rect_float != NULL); + + IMB_stereo3d_read_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + + ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_left->flags = ibuf_stereo3d->flags; + ibuf_right->flags = ibuf_stereo3d->flags; + + /* we always work with unsqueezed formats */ + IMB_stereo3d_write_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo3d->rect_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + + if (ibuf_stereo3d->flags & (IB_zbuf | IB_zbuffloat)) { + if (is_float) { + addzbuffloatImBuf(ibuf_left); + addzbuffloatImBuf(ibuf_right); + } + else { + addzbufImBuf(ibuf_left); + addzbufImBuf(ibuf_right); + } + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 1, + (int *)ibuf_left->zbuf, (int *)ibuf_right->zbuf, (int *)ibuf_stereo3d->zbuf, + ibuf_left->zbuf_float, ibuf_right->zbuf_float, ibuf_stereo3d->zbuf_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + } + + IMB_freeImBuf(ibuf_stereo3d); + + *r_ibuf_left = ibuf_left; + *r_ibuf_right = ibuf_right; +} + +static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_read_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_read_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_read_topbottom(s3d_data); + break; + default: + break; + } +} diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 118f0405303..21f440a8232 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -338,7 +338,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im img = IMB_loadblend_thumb(path); } else { - img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL); + img = IMB_loadiffname(path, IB_rect | IB_metadata | IB_thumbnail, NULL); } } diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 32100aa2288..3fee6688014 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -439,3 +439,15 @@ bool IMB_isanim(const char *filename) return (type && type != ANIM_SEQUENCE); } + +bool IMB_isfloat(ImBuf *ibuf) +{ + ImFileType *type; + + for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { + if (type->ftype(type, ibuf)) { + return (type->flag & IM_FTYPE_FLOAT) != 0; + } + } + return false; +} diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 087330d10d2..5e9ccf4437e 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -43,19 +43,7 @@ static ImBuf *prepare_write_imbuf(ImFileType *type, ImBuf *ibuf) { - ImBuf *write_ibuf = ibuf; - - if (type->flag & IM_FTYPE_FLOAT) { - /* pass */ - } - else { - if (ibuf->rect == NULL && ibuf->rect_float) { - ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); - IMB_rect_from_float(ibuf); - } - } - - return write_ibuf; + return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); } short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) @@ -86,3 +74,19 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) return false; } +ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) +{ + ImBuf *write_ibuf = ibuf; + + if (isfloat) { + /* pass */ + } + else { + if (ibuf->rect == NULL && ibuf->rect_float) { + ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); + IMB_rect_from_float(ibuf); + } + } + + return write_ibuf; +} |