Fix various issues with (multiview) OpenEXR file save/load.
* Fix saving a multiview render from the image editor giving invalid files. * Fix failure to load multiview images with a single view per part. * Fix loss of multiview metadata when saving/loading a single view. * Fix Z-Buffer writing option for single layer EXR not being respected. Multiview EXRs are now always handled as multilayer internally, significantly reducing the amount of code. Reviewed By: dfelinto Differential Revision: https://developer.blender.org/D2887
This commit is contained in:
parent
ed7260071b
commit
2a097527f2
Notes:
blender-bot
2023-02-14 06:25:25 +01:00
Referenced by issue #54311, `--render-format EXR` saves as multi-layer EXR Referenced by issue #54269, EXRs produced by latest master non-readable in most editors
|
@ -2924,7 +2924,8 @@ bool BKE_image_is_multilayer(Image *ima)
|
|||
|
||||
bool BKE_image_is_multiview(Image *ima)
|
||||
{
|
||||
return (BLI_listbase_count_ex(&ima->views, 2) > 1);
|
||||
ImageView *view = ima->views.first;
|
||||
return (view && (view->next || view->name[0]));
|
||||
}
|
||||
|
||||
bool BKE_image_is_stereo(Image *ima)
|
||||
|
@ -3030,51 +3031,6 @@ void BKE_image_backup_render(Scene *scene, Image *ima, bool free_current_slot)
|
|||
ima->last_render_slot = slot;
|
||||
}
|
||||
|
||||
/**************************** multiview save openexr *********************************/
|
||||
#ifdef WITH_OPENEXR
|
||||
static const char *image_get_view_cb(void *base, const int view_id)
|
||||
{
|
||||
Image *ima = base;
|
||||
ImageView *iv = BLI_findlink(&ima->views, view_id);
|
||||
return iv ? iv->name : "";
|
||||
}
|
||||
#endif /* WITH_OPENEXR */
|
||||
|
||||
#ifdef WITH_OPENEXR
|
||||
static ImBuf *image_get_buffer_cb(void *base, const int view_id)
|
||||
{
|
||||
Image *ima = base;
|
||||
ImageUser iuser = {0};
|
||||
|
||||
iuser.view = view_id;
|
||||
iuser.ok = 1;
|
||||
|
||||
BKE_image_multiview_index(ima, &iuser);
|
||||
|
||||
return image_acquire_ibuf(ima, &iuser, NULL);
|
||||
}
|
||||
#endif /* WITH_OPENEXR */
|
||||
|
||||
bool BKE_image_save_openexr_multiview(Image *ima, ImBuf *ibuf, const char *filepath, const int flags)
|
||||
{
|
||||
#ifdef WITH_OPENEXR
|
||||
char name[FILE_MAX];
|
||||
bool ok;
|
||||
|
||||
BLI_strncpy(name, filepath, sizeof(name));
|
||||
BLI_path_abs(name, G.main->name);
|
||||
|
||||
ibuf->userdata = ima;
|
||||
ok = IMB_exr_multiview_save(ibuf, name, flags, BLI_listbase_count(&ima->views), image_get_view_cb, image_get_buffer_cb);
|
||||
ibuf->userdata = NULL;
|
||||
|
||||
return ok;
|
||||
#else
|
||||
UNUSED_VARS(ima, ibuf, filepath, flags);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************** multiview load openexr *********************************/
|
||||
|
||||
static void image_add_view(Image *ima, const char *viewname, const char *filepath)
|
||||
|
@ -3107,51 +3063,6 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENEXR
|
||||
static void image_add_view_cb(void *base, const char *str)
|
||||
{
|
||||
Image *ima = base;
|
||||
image_add_view(ima, str, ima->name);
|
||||
}
|
||||
|
||||
static void image_add_buffer_cb(void *base, const char *str, ImBuf *ibuf, const int frame)
|
||||
{
|
||||
Image *ima = base;
|
||||
int id;
|
||||
bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL);
|
||||
const char *colorspace = ima->colorspace_settings.name;
|
||||
const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
|
||||
|
||||
if (ibuf == NULL)
|
||||
return;
|
||||
|
||||
id = BLI_findstringindex(&ima->views, str, offsetof(ImageView, name));
|
||||
|
||||
if (id == -1)
|
||||
return;
|
||||
|
||||
if (ibuf->channels >= 3)
|
||||
IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels,
|
||||
colorspace, to_colorspace, predivide);
|
||||
|
||||
image_assign_ibuf(ima, ibuf, id, frame);
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
#endif /* WITH_OPENEXR */
|
||||
|
||||
/* after imbuf load, openexr type can return with a exrhandle open */
|
||||
/* in that case we have to build a render-result */
|
||||
#ifdef WITH_OPENEXR
|
||||
static void image_create_multiview(Image *ima, ImBuf *ibuf, const int frame)
|
||||
{
|
||||
BKE_image_free_views(ima);
|
||||
|
||||
IMB_exr_multiview_convert(ibuf->userdata, ima, image_add_view_cb, image_add_buffer_cb, frame);
|
||||
|
||||
IMB_exr_close(ibuf->userdata);
|
||||
}
|
||||
#endif /* WITH_OPENEXR */
|
||||
|
||||
/* after imbuf load, openexr type can return with a exrhandle open */
|
||||
/* in that case we have to build a render-result */
|
||||
#ifdef WITH_OPENEXR
|
||||
|
@ -3263,16 +3174,10 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons
|
|||
|
||||
if (ibuf) {
|
||||
#ifdef WITH_OPENEXR
|
||||
/* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) {
|
||||
/* handle singlelayer multiview case assign ibuf based on available views */
|
||||
if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) {
|
||||
image_create_multiview(ima, ibuf, frame);
|
||||
IMB_freeImBuf(ibuf);
|
||||
ibuf = NULL;
|
||||
}
|
||||
else if (IMB_exr_has_multilayer(ibuf->userdata)) {
|
||||
/* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
|
||||
/* Handle multilayer and multiview cases, don't assign ibuf here.
|
||||
* will be set layer in BKE_image_acquire_ibuf from ima->rr. */
|
||||
if (IMB_exr_has_multilayer(ibuf->userdata)) {
|
||||
image_create_multilayer(ima, ibuf, frame);
|
||||
ima->type = IMA_TYPE_MULTILAYER;
|
||||
IMB_freeImBuf(ibuf);
|
||||
|
@ -3561,14 +3466,9 @@ static ImBuf *load_image_single(
|
|||
if (ibuf) {
|
||||
#ifdef WITH_OPENEXR
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) {
|
||||
if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) {
|
||||
/* handle singlelayer multiview case assign ibuf based on available views */
|
||||
image_create_multiview(ima, ibuf, cfra);
|
||||
IMB_freeImBuf(ibuf);
|
||||
ibuf = NULL;
|
||||
}
|
||||
else if (IMB_exr_has_multilayer(ibuf->userdata)) {
|
||||
/* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
|
||||
/* Handle multilayer and multiview cases, don't assign ibuf here.
|
||||
* will be set layer in BKE_image_acquire_ibuf from ima->rr. */
|
||||
if (IMB_exr_has_multilayer(ibuf->userdata)) {
|
||||
image_create_multilayer(ima, ibuf, cfra);
|
||||
ima->type = IMA_TYPE_MULTILAYER;
|
||||
IMB_freeImBuf(ibuf);
|
||||
|
@ -4408,7 +4308,7 @@ void BKE_image_update_frame(const Main *bmain, int cfra)
|
|||
|
||||
void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
|
||||
{
|
||||
if (BKE_image_is_multiview(ima) && (ima->rr == NULL)) {
|
||||
if (BKE_image_is_multiview(ima)) {
|
||||
ImageView *iv = BLI_findlink(&ima->views, iuser->view);
|
||||
if (iv->filepath[0])
|
||||
BLI_strncpy(filepath, iv->filepath, FILE_MAX);
|
||||
|
|
|
@ -1112,7 +1112,7 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man
|
|||
uiItemR(row, imfptr, "use_zbuffer", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
if (is_render_out && (imf->imtype == R_IMF_IMTYPE_OPENEXR)) {
|
||||
if (is_render_out && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
|
||||
show_preview = true;
|
||||
uiItemR(row, imfptr, "use_preview", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
|
|
@ -1824,9 +1824,6 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
const bool save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render"));
|
||||
ImageFormatData *imf = &simopts->im_format;
|
||||
|
||||
const bool is_multilayer = imf->imtype == R_IMF_IMTYPE_MULTILAYER;
|
||||
bool is_mono;
|
||||
|
||||
/* old global to ensure a 2nd save goes to same dir */
|
||||
BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima));
|
||||
|
||||
|
@ -1853,7 +1850,8 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
/* we need renderresult for exr and rendered multiview */
|
||||
scene = CTX_data_scene(C);
|
||||
rr = BKE_image_acquire_renderresult(scene, ima);
|
||||
is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : BLI_listbase_count_ex(&ima->views, 2) < 2;
|
||||
bool is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : BLI_listbase_count_ex(&ima->views, 2) < 2;
|
||||
bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER);
|
||||
|
||||
/* error handling */
|
||||
if (!rr) {
|
||||
|
@ -1883,28 +1881,23 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
}
|
||||
|
||||
/* fancy multiview OpenEXR */
|
||||
if ((imf->imtype == R_IMF_IMTYPE_MULTILAYER) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) {
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, true, NULL);
|
||||
if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) {
|
||||
/* save render result */
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, NULL, sima->iuser.layer);
|
||||
save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath);
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
else if ((imf->imtype == R_IMF_IMTYPE_OPENEXR) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) {
|
||||
/* treat special Openexr case separetely (this is the singlelayer multiview OpenEXR */
|
||||
BKE_imbuf_write_prepare(ibuf, imf);
|
||||
ok = BKE_image_save_openexr_multiview(ima, ibuf, simopts->filepath, (IB_rect | IB_zbuf | IB_zbuffloat | IB_multiview));
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
/* regular mono pipeline */
|
||||
else if (is_mono) {
|
||||
if (is_multilayer) {
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL);
|
||||
if (is_exr_rr) {
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, NULL, -1);
|
||||
}
|
||||
else {
|
||||
colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf);
|
||||
ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, imf, save_copy);
|
||||
save_imbuf_post(ibuf, colormanaged_ibuf);
|
||||
}
|
||||
save_image_post(op, ibuf, ima, ok, (is_multilayer ? true : save_copy), relbase, relative, do_newpath, simopts->filepath);
|
||||
save_image_post(op, ibuf, ima, ok, (is_exr_rr ? true : save_copy), relbase, relative, do_newpath, simopts->filepath);
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
/* individual multiview images */
|
||||
|
@ -1913,7 +1906,7 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
unsigned char planes = ibuf->planes;
|
||||
const int totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views));
|
||||
|
||||
if (!is_multilayer) {
|
||||
if (!is_exr_rr) {
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
|
||||
|
@ -1923,9 +1916,9 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
const char *view = rr ? ((RenderView *) BLI_findlink(&rr->views, i))->name :
|
||||
((ImageView *) BLI_findlink(&ima->views, i))->name;
|
||||
|
||||
if (is_multilayer) {
|
||||
if (is_exr_rr) {
|
||||
BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath);
|
||||
ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, false, view);
|
||||
ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, view, -1);
|
||||
save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath);
|
||||
}
|
||||
else {
|
||||
|
@ -1953,14 +1946,14 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
|
|||
ok &= ok_view;
|
||||
}
|
||||
|
||||
if (is_multilayer) {
|
||||
if (is_exr_rr) {
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
}
|
||||
/* stereo (multiview) images */
|
||||
else if (simopts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) {
|
||||
if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL);
|
||||
ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, NULL, -1);
|
||||
save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath);
|
||||
ED_space_image_release_buffer(sima, ibuf, lock);
|
||||
}
|
||||
|
|
|
@ -374,21 +374,13 @@ static void openexr_header_metadata_callback(void *data, const char *propname, c
|
|||
|
||||
|
||||
static bool imb_save_openexr_half(
|
||||
ImBuf *ibuf, const char *name, const int flags, const int totviews,
|
||||
const char * (*getview)(void *base, int view_id),
|
||||
ImBuf *(*getbuffer)(void *base, const int view_id))
|
||||
ImBuf *ibuf, const char *name, const int flags)
|
||||
{
|
||||
const int channels = ibuf->channels;
|
||||
const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
|
||||
const bool 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;
|
||||
int view_id;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -397,22 +389,14 @@ static bool imb_save_openexr_half(
|
|||
openexr_header_compression(&header, ibuf->foptions.flag & OPENEXR_COMPRESS);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
|
||||
/* 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));
|
||||
}
|
||||
/* create channels */
|
||||
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));
|
||||
|
||||
FrameBuffer frameBuffer;
|
||||
|
||||
|
@ -421,65 +405,49 @@ static bool imb_save_openexr_half(
|
|||
OutputFile file(file_stream, header);
|
||||
|
||||
/* we store first everything in half array */
|
||||
std::vector<RGBAZ> pixels(height * width * totviews);
|
||||
std::vector<RGBAZ> pixels(height * width);
|
||||
RGBAZ *to = &pixels[0];
|
||||
int xstride = sizeof(RGBAZ);
|
||||
int ystride = xstride * width;
|
||||
|
||||
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("R", Slice(HALF, (char *) &to->r, xstride, ystride));
|
||||
frameBuffer.insert("G", Slice(HALF, (char *) &to->g, xstride, ystride));
|
||||
frameBuffer.insert("B", Slice(HALF, (char *) &to->b, xstride, ystride));
|
||||
if (is_alpha)
|
||||
frameBuffer.insert("A", Slice(HALF, (char *) &to->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;
|
||||
|
||||
/* TODO (dfelinto)
|
||||
* In some cases we get NULL ibufs, it needs investigation, meanwhile prevent crash
|
||||
* Multiview Render + Image Editor + OpenEXR + Multi-View
|
||||
*/
|
||||
if (view_ibuf == NULL) {
|
||||
throw std::runtime_error(std::string("Missing data to write to ") + name);
|
||||
}
|
||||
for (int i = ibuf->y - 1; i >= 0; i--) {
|
||||
from = ibuf->rect_float + channels * i * width;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned char *from;
|
||||
}
|
||||
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 i = ibuf->y - 1; i >= 0; i--) {
|
||||
from = (unsigned char *)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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_multiview)
|
||||
IMB_freeImBuf(view_ibuf);
|
||||
}
|
||||
|
||||
exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
|
||||
|
@ -498,21 +466,13 @@ static bool imb_save_openexr_half(
|
|||
}
|
||||
|
||||
static bool imb_save_openexr_float(
|
||||
ImBuf *ibuf, const char *name, const int flags, const int totviews,
|
||||
const char * (*getview)(void *base, const int view_id),
|
||||
ImBuf *(*getbuffer)(void *base, const int view_id))
|
||||
ImBuf *ibuf, const char *name, const int flags)
|
||||
{
|
||||
const int channels = ibuf->channels;
|
||||
const bool is_alpha = (channels >= 4) && (ibuf->planes == 32);
|
||||
const bool 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;
|
||||
int view_id;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -521,22 +481,14 @@ static bool imb_save_openexr_float(
|
|||
openexr_header_compression(&header, ibuf->foptions.flag & OPENEXR_COMPRESS);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
|
||||
/* 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));
|
||||
}
|
||||
/* create channels */
|
||||
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));
|
||||
|
||||
FrameBuffer frameBuffer;
|
||||
|
||||
|
@ -547,36 +499,22 @@ static bool imb_save_openexr_float(
|
|||
int xstride = sizeof(float) * channels;
|
||||
int ystride = -xstride * 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 */
|
||||
float *rect[4] = {NULL, NULL, NULL, NULL};
|
||||
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? */
|
||||
|
||||
/* TODO (dfelinto)
|
||||
* In some cases we get NULL ibufs, it needs investigation, meanwhile prevent crash
|
||||
* Multiview Render + Image Editor + OpenEXR + Multi-View
|
||||
*/
|
||||
if (view_ibuf == NULL) {
|
||||
throw std::runtime_error(std::string("Missing data to write to ") + name);
|
||||
}
|
||||
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));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
@ -599,50 +537,16 @@ int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
|
|||
}
|
||||
|
||||
if (ibuf->foptions.flag & OPENEXR_HALF)
|
||||
return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL);
|
||||
return (int) imb_save_openexr_half(ibuf, name, flags);
|
||||
else {
|
||||
/* when no float rect, we save as half (16 bits is sufficient) */
|
||||
if (ibuf->rect_float == NULL)
|
||||
return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL);
|
||||
return (int) imb_save_openexr_half(ibuf, name, flags);
|
||||
else
|
||||
return (int) imb_save_openexr_float(ibuf, name, flags, 1, NULL, NULL);
|
||||
return (int) imb_save_openexr_float(ibuf, name, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static bool imb_save_openexr_multiview(
|
||||
ImBuf *ibuf, const char *name, const int flags, const int totviews,
|
||||
const char *(*getview)(void *base, const int view_id),
|
||||
ImBuf *(*getbuffer)(void *base, const int 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->foptions.flag & 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, totviews, getview, getbuffer);
|
||||
else
|
||||
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 int totviews,
|
||||
const char *(*getview)(void *base, const int view_id),
|
||||
ImBuf *(*getbuffer)(void *base, const int view_id))
|
||||
{
|
||||
return imb_save_openexr_multiview(ibuf, name, flags, totviews, getview, getbuffer);
|
||||
}
|
||||
|
||||
/* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */
|
||||
|
||||
/* naming rules:
|
||||
|
@ -841,7 +745,7 @@ void IMB_exr_add_channel(void *handle,
|
|||
if (layname && layname[0] != '\0') {
|
||||
imb_exr_insert_view_name(echan->name, echan->m->name.c_str(), echan->m->view.c_str());
|
||||
}
|
||||
else if (data->multiView->size() > 1) {
|
||||
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));
|
||||
}
|
||||
|
@ -1071,7 +975,7 @@ float *IMB_exr_channel_rect(void *handle, const char *layname, const char *pass
|
|||
imb_exr_insert_view_name(temp_buf, name, viewname);
|
||||
BLI_strncpy(name, temp_buf, sizeof(name));
|
||||
}
|
||||
else if (data->multiView->size() > 1) {
|
||||
else if (data->multiView->size() >= 1) {
|
||||
const int 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));
|
||||
|
@ -1199,57 +1103,6 @@ void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, c
|
|||
}
|
||||
}
|
||||
|
||||
/* called only when handle has all views */
|
||||
void IMB_exrmultiview_write_channels(void *handle, const char *viewname)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
const int 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;
|
||||
|
@ -1336,69 +1189,6 @@ void IMB_exr_multilayer_convert(void *handle, void *base,
|
|||
}
|
||||
}
|
||||
|
||||
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 bool 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;
|
||||
|
@ -1863,49 +1653,20 @@ static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views,
|
|||
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));
|
||||
for (int p = 0; p < file.parts(); p++) {
|
||||
if (hasMultiView(file.header(p))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool exr_has_multipart_file(MultiPartInputFile& file)
|
||||
|
@ -1929,6 +1690,12 @@ static bool imb_exr_is_multi(MultiPartInputFile& file)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool IMB_exr_has_multilayer(void *handle)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
return imb_exr_is_multi(*data->ifile);
|
||||
}
|
||||
|
||||
struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
|
||||
{
|
||||
struct ImBuf *ibuf = NULL;
|
||||
|
|
|
@ -68,7 +68,6 @@ float *IMB_exr_channel_rect(void *handle, const char *layname, const char *pass
|
|||
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, 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(
|
||||
|
@ -78,23 +77,11 @@ void IMB_exr_multilayer_convert(
|
|||
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 int totviews,
|
||||
const char *(*getview)(void *base, int view_id),
|
||||
struct ImBuf *(*getbuffer)(void *base, const int view_id));
|
||||
|
||||
void IMB_exr_close(void *handle);
|
||||
|
||||
void IMB_exr_add_view(void *handle, const char *name);
|
||||
|
||||
bool IMB_exr_has_multilayer(void *handle);
|
||||
bool IMB_exr_has_singlelayer_multiview(void *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
@ -48,7 +48,6 @@ float *IMB_exr_channel_rect (void * /*handle*/, const char * /*layname*/
|
|||
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*/, 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(
|
||||
|
@ -60,24 +59,7 @@ void IMB_exr_multilayer_convert(
|
|||
{
|
||||
}
|
||||
|
||||
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 int /*totviews*/,
|
||||
const char *(* /*getview*/)(void *base, const int view_id),
|
||||
struct ImBuf *(* /*getbuffer*/)(void *base, const int view_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void IMB_exr_close (void * /*handle*/) { }
|
||||
|
||||
void IMB_exr_add_view(void * /*handle*/, const char * /*name*/) { }
|
||||
bool IMB_exr_has_multilayer(void * /*handle*/) { return false; }
|
||||
bool IMB_exr_has_singlelayer_multiview(void * /*handle*/) { return false; }
|
||||
|
|
|
@ -310,7 +310,7 @@ void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene
|
|||
bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode);
|
||||
bool RE_WriteRenderResult(
|
||||
struct ReportList *reports, RenderResult *rr, const char *filename,
|
||||
struct ImageFormatData *imf, const bool multiview, const char *view);
|
||||
struct ImageFormatData *imf, const char *view, const int layer);
|
||||
struct RenderResult *RE_MultilayerConvert(
|
||||
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ void render_result_rect_get_pixels(struct RenderResult *rr,
|
|||
|
||||
void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src);
|
||||
void render_result_views_shallowdelete(struct RenderResult *rr);
|
||||
bool render_result_has_views(struct RenderResult *rr);
|
||||
|
||||
#endif /* __RENDER_RESULT_H__ */
|
||||
|
||||
|
|
|
@ -437,6 +437,8 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
|
|||
rr->rectz = rv->rectz;
|
||||
rr->rect32 = rv->rect32;
|
||||
|
||||
rr->have_combined = (rv->rectf != NULL);
|
||||
|
||||
/* active layer */
|
||||
rl = render_get_active_layer(re, re->result);
|
||||
|
||||
|
@ -3307,19 +3309,18 @@ void RE_RenderFreestyleExternal(Render *re)
|
|||
|
||||
bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, const bool stamp, char *name)
|
||||
{
|
||||
bool is_mono;
|
||||
bool ok = true;
|
||||
RenderData *rd = &scene->r;
|
||||
|
||||
if (!rr)
|
||||
return false;
|
||||
|
||||
is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
|
||||
bool is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
|
||||
bool is_exr_rr = ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER);
|
||||
|
||||
if (ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) &&
|
||||
rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW)
|
||||
if (rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr)
|
||||
{
|
||||
ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, true, NULL);
|
||||
ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, NULL, -1);
|
||||
render_print_save_message(reports, name, ok, errno);
|
||||
}
|
||||
|
||||
|
@ -3337,9 +3338,26 @@ bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scen
|
|||
BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name);
|
||||
}
|
||||
|
||||
if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
|
||||
ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, false, rv->name);
|
||||
if (is_exr_rr) {
|
||||
ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, rv->name, -1);
|
||||
render_print_save_message(reports, name, ok, errno);
|
||||
|
||||
/* optional preview images for exr */
|
||||
if (ok && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
|
||||
ImageFormatData imf = rd->im_format;
|
||||
imf.imtype = R_IMF_IMTYPE_JPEG90;
|
||||
|
||||
if (BLI_testextensie(name, ".exr"))
|
||||
name[strlen(name) - 4] = 0;
|
||||
BKE_image_path_ensure_ext_from_imformat(name, &imf);
|
||||
|
||||
ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id);
|
||||
ibuf->planes = 24;
|
||||
|
||||
ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &imf, stamp);
|
||||
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id);
|
||||
|
@ -3349,19 +3367,6 @@ bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scen
|
|||
|
||||
ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &rd->im_format, stamp);
|
||||
|
||||
/* optional preview images for exr */
|
||||
if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
|
||||
ImageFormatData imf = rd->im_format;
|
||||
imf.imtype = R_IMF_IMTYPE_JPEG90;
|
||||
|
||||
if (BLI_testextensie(name, ".exr"))
|
||||
name[strlen(name) - 4] = 0;
|
||||
BKE_image_path_ensure_ext_from_imformat(name, &imf);
|
||||
ibuf->planes = 24;
|
||||
|
||||
ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &imf, stamp);
|
||||
}
|
||||
|
||||
/* imbuf knows which rects are not part of ibuf */
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
@ -3391,7 +3396,7 @@ bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scen
|
|||
ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp);
|
||||
|
||||
/* optional preview images for exr */
|
||||
if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR &&
|
||||
if (ok && is_exr_rr &&
|
||||
(rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG))
|
||||
{
|
||||
ImageFormatData imf = rd->im_format;
|
||||
|
|
|
@ -585,7 +585,7 @@ static void *ml_addlayer_cb(void *base, const char *str)
|
|||
|
||||
rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
|
||||
BLI_addtail(&rr->layers, rl);
|
||||
|
||||
|
||||
BLI_strncpy(rl->name, str, EXR_LAY_MAXNAME);
|
||||
return rl;
|
||||
}
|
||||
|
@ -756,6 +756,12 @@ void render_result_views_new(RenderResult *rr, RenderData *rd)
|
|||
}
|
||||
}
|
||||
|
||||
bool render_result_has_views(RenderResult *rr)
|
||||
{
|
||||
RenderView *rv = rr->views.first;
|
||||
return (rv && (rv->next || rv->name[0]));
|
||||
}
|
||||
|
||||
/*********************************** Merge ***********************************/
|
||||
|
||||
static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize)
|
||||
|
@ -820,101 +826,124 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
|
|||
}
|
||||
}
|
||||
|
||||
/* called from within UI and render pipeline, saves both rendered result as a file-read result
|
||||
* if multiview is true saves all views in a multiview exr
|
||||
* else if view is not NULL saves single view
|
||||
* else saves stereo3d
|
||||
*/
|
||||
bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const bool multiview, const char *view)
|
||||
/* Called from the UI and render pipeline, to save multilayer and multiview
|
||||
* images, optionally isolating a specific, view, layer or RGBA/Z pass. */
|
||||
bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const char *view, int layer)
|
||||
{
|
||||
RenderLayer *rl;
|
||||
RenderPass *rpass;
|
||||
RenderView *rview;
|
||||
void *exrhandle = IMB_exr_get_handle();
|
||||
bool success;
|
||||
int a, nr;
|
||||
const char *chan_view = NULL;
|
||||
int compress = (imf ? imf->exr_codec : 0);
|
||||
size_t width, height;
|
||||
const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16);
|
||||
const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR);
|
||||
const bool write_z = !multi_layer && (imf && (imf->flag & R_IMF_FLAG_ZBUF));
|
||||
|
||||
const bool is_mono = view && !multiview;
|
||||
const bool use_half_float = (imf != NULL) ? (imf->depth == R_IMF_CHAN_DEPTH_16) : false;
|
||||
/* Write first layer if not multilayer and no layer was specified. */
|
||||
if (!multi_layer && layer == -1) {
|
||||
layer = 0;
|
||||
}
|
||||
|
||||
width = rr->rectx;
|
||||
height = rr->recty;
|
||||
|
||||
if (imf && imf->imtype == R_IMF_IMTYPE_OPENEXR && multiview) {
|
||||
/* single layer OpenEXR */
|
||||
const char *RGBAZ[] = {"R", "G", "B", "A", "Z"};
|
||||
for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) {
|
||||
IMB_exr_add_view(exrhandle, rview->name);
|
||||
|
||||
if (rview->rectf) {
|
||||
for (a = 0; a < 4; a++) {
|
||||
IMB_exr_add_channel(exrhandle, "", RGBAZ[a],
|
||||
rview->name, 4, 4 * width, rview->rectf + a,
|
||||
use_half_float);
|
||||
}
|
||||
if (rview->rectz) {
|
||||
/* Z pass is always stored as float. */
|
||||
IMB_exr_add_channel(exrhandle, "", RGBAZ[4],
|
||||
rview->name, 1, width, rview->rectz,
|
||||
false);
|
||||
}
|
||||
/* First add views since IMB_exr_add_channel checks number of views. */
|
||||
if (render_result_has_views(rr)) {
|
||||
for (RenderView *rview = rr->views.first; rview; rview = rview->next) {
|
||||
if (!view || STREQ(view, rview->name)) {
|
||||
IMB_exr_add_view(exrhandle, rview->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) {
|
||||
if (is_mono) {
|
||||
if (!STREQ(view, rview->name)) {
|
||||
|
||||
/* Compositing result. */
|
||||
if (rr->have_combined) {
|
||||
for (RenderView *rview = rr->views.first; rview; rview = rview->next) {
|
||||
if (!rview->rectf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *viewname = rview->name;
|
||||
if (view) {
|
||||
if (!STREQ(view, viewname)) {
|
||||
continue;
|
||||
}
|
||||
chan_view = "";
|
||||
}
|
||||
else {
|
||||
/* if rendered only one view, we treat as a a non-view render */
|
||||
chan_view = rview->name;
|
||||
}
|
||||
|
||||
IMB_exr_add_view(exrhandle, rview->name);
|
||||
|
||||
if (rview->rectf) {
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
for (a = 0; a < 4; a++) {
|
||||
set_pass_name(passname, RE_PASSNAME_COMBINED, a, "RGBA");
|
||||
IMB_exr_add_channel(exrhandle, "Composite", passname,
|
||||
chan_view, 4, 4 * width, rview->rectf + a,
|
||||
use_half_float);
|
||||
else {
|
||||
viewname = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add layers/passes and assign channels */
|
||||
for (rl = rr->layers.first; rl; rl = rl->next) {
|
||||
/* Skip compositing if only a single other layer is requested. */
|
||||
if (!multi_layer && layer != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* passes are allocated in sync */
|
||||
for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
|
||||
const int xstride = rpass->channels;
|
||||
for (int a = 0; a < 4; a++) {
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
char layname[EXR_PASS_MAXNAME];
|
||||
const char *chan_id = "RGBA";
|
||||
|
||||
if (is_mono) {
|
||||
if (!STREQ(view, rpass->view)) {
|
||||
continue;
|
||||
}
|
||||
chan_view = "";
|
||||
if (multi_layer) {
|
||||
set_pass_name(passname, "Combined", a, chan_id);
|
||||
BLI_strncpy(layname, "Composite", sizeof(layname));
|
||||
}
|
||||
else {
|
||||
/* if rendered only one view, we treat as a a non-view render */
|
||||
chan_view = (nr > 1 ? rpass->view :"");
|
||||
passname[0] = chan_id[a];
|
||||
passname[1] = '\0';
|
||||
layname[0] = '\0';
|
||||
}
|
||||
|
||||
for (a = 0; a < xstride; a++) {
|
||||
set_pass_name(passname, rpass->name, a, rpass->chan_id);
|
||||
IMB_exr_add_channel(exrhandle, rl->name, passname, chan_view,
|
||||
xstride, xstride * width, rpass->rect + a,
|
||||
STREQ(rpass->name, RE_PASSNAME_Z) ? false : use_half_float);
|
||||
IMB_exr_add_channel(exrhandle, layname, passname, viewname,
|
||||
4, 4 * rr->rectx, rview->rectf + a, half_float);
|
||||
}
|
||||
|
||||
if (write_z && rview->rectz) {
|
||||
const char *layname = (multi_layer)? "Composite": "";
|
||||
IMB_exr_add_channel(exrhandle, layname, "Z", viewname,
|
||||
1, rr->rectx, rview->rectz, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Other render layers. */
|
||||
int nr = (rr->have_combined)? 1: 0;
|
||||
for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next, nr++) {
|
||||
/* Skip other render layers if requested. */
|
||||
if (!multi_layer && nr != layer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (RenderPass *rp = rl->passes.first; rp; rp = rp->next) {
|
||||
/* Skip non-RGBA and Z passes if not using multi layer. */
|
||||
if (!multi_layer && !(STREQ(rp->name, RE_PASSNAME_COMBINED) ||
|
||||
STREQ(rp->name, "") ||
|
||||
(STREQ(rp->name, RE_PASSNAME_Z) && write_z))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip pass if it does not match the requested view(s). */
|
||||
const char *viewname = rp->view;
|
||||
if (view) {
|
||||
if (!STREQ(view, viewname)) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
viewname = "";
|
||||
}
|
||||
}
|
||||
|
||||
for (int a = 0; a < rp->channels; a++) {
|
||||
/* Save Combined as RGBA if single layer save. */
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
char layname[EXR_PASS_MAXNAME];
|
||||
|
||||
if (multi_layer) {
|
||||
set_pass_name(passname, rp->name, a, rp->chan_id);
|
||||
BLI_strncpy(layname, rl->name, sizeof(layname));
|
||||
}
|
||||
else {
|
||||
passname[0] = rp->chan_id[a];
|
||||
passname[1] = '\0';
|
||||
layname[0] = '\0';
|
||||
}
|
||||
|
||||
/* Add channel. */
|
||||
IMB_exr_add_channel(exrhandle, layname, passname, viewname,
|
||||
rp->channels, rp->channels * rr->rectx, rp->rect + a,
|
||||
STREQ(rp->name, RE_PASSNAME_Z) ? false : half_float);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -923,14 +952,14 @@ bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *fil
|
|||
|
||||
BLI_make_existing_file(filename);
|
||||
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, compress, rr->stamp_data)) {
|
||||
int compress = (imf ? imf->exr_codec : 0);
|
||||
bool success = IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress, rr->stamp_data);
|
||||
if (success) {
|
||||
IMB_exr_write_channels(exrhandle);
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
/* TODO, get the error from openexr's exception */
|
||||
BKE_reportf(reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno));
|
||||
success = false;
|
||||
}
|
||||
|
||||
IMB_exr_close(exrhandle);
|
||||
|
@ -1245,7 +1274,7 @@ void render_result_exr_file_cache_write(Render *re)
|
|||
render_result_exr_file_cache_path(re->scene, root, str);
|
||||
printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str);
|
||||
|
||||
RE_WriteRenderResult(NULL, rr, str, NULL, true, NULL);
|
||||
RE_WriteRenderResult(NULL, rr, str, NULL, NULL, -1);
|
||||
}
|
||||
|
||||
/* For cache, makes exact copy of render result */
|
||||
|
|
Loading…
Reference in New Issue