Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2022-03-18 23:28:04 +0300
committerBrecht Van Lommel <brecht@blender.org>2022-03-21 18:38:13 +0300
commit8530e48f8692f4b92c43489029f2d2ef67c7cb62 (patch)
treed335479baf037b897ddf14f42c238184f65d0472 /source/blender/blenkernel/intern/image_save.cc
parentb96462519f748fcf04028084354fbbf97eb8ce92 (diff)
Cleanup: move render image and multilayer EXR write code to image_save.cc
These share a lot of logic with regular image saving and should be unified more in the future.
Diffstat (limited to 'source/blender/blenkernel/intern/image_save.cc')
-rw-r--r--source/blender/blenkernel/intern/image_save.cc336
1 files changed, 332 insertions, 4 deletions
diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc
index 180008e0f64..fdeb5eedcde 100644
--- a/source/blender/blenkernel/intern/image_save.cc
+++ b/source/blender/blenkernel/intern/image_save.cc
@@ -20,6 +20,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "intern/openexr/openexr_multi.h"
+
#include "BKE_colortools.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
@@ -219,14 +221,14 @@ static bool image_save_single(ReportList *reports,
/* fancy multiview OpenEXR */
if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) {
/* save render result */
- ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, nullptr, layer);
+ ok = BKE_image_render_write_exr(reports, rr, opts->filepath, imf, NULL, layer);
image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed);
BKE_image_release_ibuf(ima, ibuf, lock);
}
/* regular mono pipeline */
else if (is_mono) {
if (is_exr_rr) {
- ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, nullptr, layer);
+ ok = BKE_image_render_write_exr(reports, rr, opts->filepath, imf, NULL, layer);
}
else {
colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(
@@ -261,7 +263,7 @@ static bool image_save_single(ReportList *reports,
if (is_exr_rr) {
BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath);
- ok_view = RE_WriteRenderResult(reports, rr, filepath, imf, view, layer);
+ ok_view = BKE_image_render_write_exr(reports, rr, filepath, imf, view, layer);
image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed);
}
else {
@@ -308,7 +310,7 @@ static bool image_save_single(ReportList *reports,
/* stereo (multiview) images */
else if (opts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) {
if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
- ok = RE_WriteRenderResult(reports, rr, opts->filepath, imf, nullptr, layer);
+ ok = BKE_image_render_write_exr(reports, rr, opts->filepath, imf, NULL, layer);
image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed);
BKE_image_release_ibuf(ima, ibuf, lock);
}
@@ -449,3 +451,329 @@ bool BKE_image_save(
return ok;
}
+
+/* OpenEXR saving, single and multilayer. */
+
+bool BKE_image_render_write_exr(ReportList *reports,
+ const RenderResult *rr,
+ const char *filename,
+ const ImageFormatData *imf,
+ const char *view,
+ int layer)
+{
+ void *exrhandle = IMB_exr_get_handle();
+ 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));
+
+ /* Write first layer if not multilayer and no layer was specified. */
+ if (!multi_layer && layer == -1) {
+ layer = 0;
+ }
+
+ /* First add views since IMB_exr_add_channel checks number of views. */
+ const RenderView *first_rview = (const RenderView *)rr->views.first;
+ if (first_rview && (first_rview->next || first_rview->name[0])) {
+ LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
+ if (!view || STREQ(view, rview->name)) {
+ IMB_exr_add_view(exrhandle, rview->name);
+ }
+ }
+ }
+
+ /* Compositing result. */
+ if (rr->have_combined) {
+ LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
+ if (!rview->rectf) {
+ continue;
+ }
+
+ const char *viewname = rview->name;
+ if (view) {
+ if (!STREQ(view, viewname)) {
+ continue;
+ }
+
+ viewname = "";
+ }
+
+ /* Skip compositing if only a single other layer is requested. */
+ if (!multi_layer && layer != 0) {
+ continue;
+ }
+
+ float *output_rect = rview->rectf;
+
+ for (int a = 0; a < 4; a++) {
+ char passname[EXR_PASS_MAXNAME];
+ char layname[EXR_PASS_MAXNAME];
+ const char *chan_id = "RGBA";
+
+ if (multi_layer) {
+ IMB_exr_channel_name(passname, NULL, "Combined", NULL, chan_id, a);
+ BLI_strncpy(layname, "Composite", sizeof(layname));
+ }
+ else {
+ passname[0] = chan_id[a];
+ passname[1] = '\0';
+ layname[0] = '\0';
+ }
+
+ IMB_exr_add_channel(
+ exrhandle, layname, passname, viewname, 4, 4 * rr->rectx, output_rect + 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;
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
+ /* Skip other render layers if requested. */
+ if (!multi_layer && nr != layer) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) {
+ /* 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;
+ }
+
+ viewname = "";
+ }
+
+ /* We only store RGBA passes as half float, for
+ * others precision loss can be problematic. */
+ const bool pass_RGBA = (STR_ELEM(rp->chan_id, "RGB", "RGBA", "R", "G", "B", "A"));
+ const bool pass_half_float = half_float && pass_RGBA;
+
+ float *output_rect = rp->rect;
+
+ 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) {
+ IMB_exr_channel_name(passname, NULL, rp->name, NULL, rp->chan_id, a);
+ BLI_strncpy(layname, rl->name, sizeof(layname));
+ }
+ else {
+ passname[0] = rp->chan_id[a];
+ passname[1] = '\0';
+ layname[0] = '\0';
+ }
+
+ IMB_exr_add_channel(exrhandle,
+ layname,
+ passname,
+ viewname,
+ rp->channels,
+ rp->channels * rr->rectx,
+ output_rect + a,
+ pass_half_float);
+ }
+ }
+ }
+
+ errno = 0;
+
+ BLI_make_existing_file(filename);
+
+ 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);
+ }
+ else {
+ /* TODO: get the error from openexr's exception. */
+ BKE_reportf(
+ reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno));
+ }
+
+ IMB_exr_close(exrhandle);
+ return success;
+}
+
+/* Render output. */
+
+static void image_render_print_save_message(ReportList *reports, const char *name, int ok, int err)
+{
+ if (ok) {
+ /* no need to report, just some helpful console info */
+ printf("Saved: '%s'\n", name);
+ }
+ else {
+ /* report on error since users will want to know what failed */
+ BKE_reportf(reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), name);
+ }
+}
+
+static int image_render_write_stamp_test(ReportList *reports,
+ const Scene *scene,
+ const RenderResult *rr,
+ ImBuf *ibuf,
+ const char *name,
+ const ImageFormatData *imf,
+ const bool stamp)
+{
+ int ok;
+
+ if (stamp) {
+ /* writes the name of the individual cameras */
+ ok = BKE_imbuf_write_stamp(scene, rr, ibuf, name, imf);
+ }
+ else {
+ ok = BKE_imbuf_write(ibuf, name, imf);
+ }
+
+ image_render_print_save_message(reports, name, ok, errno);
+
+ return ok;
+}
+
+bool BKE_image_render_write(ReportList *reports,
+ RenderResult *rr,
+ const Scene *scene,
+ const bool stamp,
+ const char *filename)
+{
+ bool ok = true;
+ const RenderData *rd = &scene->r;
+
+ if (!rr) {
+ return false;
+ }
+
+ const bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2;
+ const bool is_exr_rr = ELEM(rd->im_format.imtype,
+ R_IMF_IMTYPE_OPENEXR,
+ R_IMF_IMTYPE_MULTILAYER) &&
+ RE_HasFloatPixels(rr);
+ const float dither = scene->r.dither_intensity;
+
+ if (rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) {
+ ok = BKE_image_render_write_exr(reports, rr, filename, &rd->im_format, NULL, -1);
+ image_render_print_save_message(reports, filename, ok, errno);
+ }
+
+ /* mono, legacy code */
+ else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ int view_id = 0;
+ for (const RenderView *rv = (const RenderView *)rr->views.first; rv;
+ rv = rv->next, view_id++) {
+ char filepath[FILE_MAX];
+ if (is_mono) {
+ STRNCPY(filepath, filename);
+ }
+ else {
+ BKE_scene_multiview_view_filepath_get(&scene->r, filename, rv->name, filepath);
+ }
+
+ if (is_exr_rr) {
+ ok = BKE_image_render_write_exr(reports, rr, filepath, &rd->im_format, rv->name, -1);
+ image_render_print_save_message(reports, filepath, 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_path_extension_check(filepath, ".exr")) {
+ filepath[strlen(filepath) - 4] = 0;
+ }
+ BKE_image_path_ensure_ext_from_imformat(filepath, &imf);
+
+ ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &imf, dither, view_id);
+ ibuf->planes = 24;
+ IMB_colormanagement_imbuf_for_write(
+ ibuf, true, false, &scene->view_settings, &scene->display_settings, &imf);
+
+ ok = image_render_write_stamp_test(reports, scene, rr, ibuf, filepath, &imf, stamp);
+
+ IMB_freeImBuf(ibuf);
+ }
+ }
+ else {
+ ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id);
+
+ IMB_colormanagement_imbuf_for_write(
+ ibuf, true, false, &scene->view_settings, &scene->display_settings, &rd->im_format);
+
+ ok = image_render_write_stamp_test(
+ reports, scene, rr, ibuf, filepath, &rd->im_format, stamp);
+
+ /* imbuf knows which rects are not part of ibuf */
+ IMB_freeImBuf(ibuf);
+ }
+ }
+ }
+ else { /* R_IMF_VIEWS_STEREO_3D */
+ BLI_assert(rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D);
+
+ char filepath[FILE_MAX];
+ STRNCPY(filepath, filename);
+
+ if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
+ printf("Stereo 3D not supported for MultiLayer image: %s\n", filepath);
+ }
+ else {
+ ImBuf *ibuf_arr[3] = {NULL};
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
+ ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id);
+ IMB_colormanagement_imbuf_for_write(ibuf_arr[i],
+ true,
+ false,
+ &scene->view_settings,
+ &scene->display_settings,
+ &rd->im_format);
+ IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]);
+ }
+
+ ibuf_arr[2] = IMB_stereo3d_ImBuf(&rd->im_format, ibuf_arr[0], ibuf_arr[1]);
+
+ ok = image_render_write_stamp_test(
+ reports, scene, rr, ibuf_arr[2], filepath, &rd->im_format, stamp);
+
+ /* optional preview images for exr */
+ if (ok && is_exr_rr && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
+ ImageFormatData imf = rd->im_format;
+ imf.imtype = R_IMF_IMTYPE_JPEG90;
+
+ if (BLI_path_extension_check(filepath, ".exr")) {
+ filepath[strlen(filepath) - 4] = 0;
+ }
+
+ BKE_image_path_ensure_ext_from_imformat(filepath, &imf);
+ ibuf_arr[2]->planes = 24;
+
+ ok = image_render_write_stamp_test(reports, scene, rr, ibuf_arr[2], filepath, &imf, stamp);
+ }
+
+ /* imbuf knows which rects are not part of ibuf */
+ for (i = 0; i < 3; i++) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+ }
+
+ return ok;
+}