diff --git a/scripts/presets/pixel_density/Custom.py b/scripts/presets/pixel_density/Custom.py new file mode 100644 index 00000000000..001d19a2280 --- /dev/null +++ b/scripts/presets/pixel_density/Custom.py @@ -0,0 +1 @@ +import bpy diff --git a/scripts/presets/pixel_density/Pixels_slash_Centimeter.py b/scripts/presets/pixel_density/Pixels_slash_Centimeter.py new file mode 100644 index 00000000000..88ad93ff8af --- /dev/null +++ b/scripts/presets/pixel_density/Pixels_slash_Centimeter.py @@ -0,0 +1,2 @@ +import bpy +bpy.context.scene.render.ppm_base = 0.01 diff --git a/scripts/presets/pixel_density/Pixels_slash_Inch.py b/scripts/presets/pixel_density/Pixels_slash_Inch.py new file mode 100644 index 00000000000..86bc75b9a7a --- /dev/null +++ b/scripts/presets/pixel_density/Pixels_slash_Inch.py @@ -0,0 +1,2 @@ +import bpy +bpy.context.scene.render.ppm_base = 0.0254 diff --git a/scripts/presets/pixel_density/Pixels_slash_Meter.py b/scripts/presets/pixel_density/Pixels_slash_Meter.py new file mode 100644 index 00000000000..1521184f676 --- /dev/null +++ b/scripts/presets/pixel_density/Pixels_slash_Meter.py @@ -0,0 +1,2 @@ +import bpy +bpy.context.scene.render.ppm_base = 1.0 diff --git a/scripts/startup/bl_ui/properties_output.py b/scripts/startup/bl_ui/properties_output.py index 2b5781bca8d..de93d23c028 100644 --- a/scripts/startup/bl_ui/properties_output.py +++ b/scripts/startup/bl_ui/properties_output.py @@ -32,6 +32,13 @@ class RENDER_MT_framerate_presets(Menu): draw = Menu.draw_preset +class RENDER_MT_pixeldensity_presets(Menu): + bl_label = "Pixel Density Presets" + preset_subdir = "pixel_density" + preset_operator = "script.execute_preset" + draw = Menu.draw_preset + + class RenderOutputButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -375,6 +382,83 @@ class RENDER_PT_output_color_management(RenderOutputButtonsPanel, Panel): col.template_colormanaged_view_settings(owner, "view_settings") +class RENDER_PT_output_pixel_density(RenderOutputButtonsPanel, Panel): + bl_label = "Pixel Density" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "RENDER_PT_output" + + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_EEVEE', + 'BLENDER_EEVEE_NEXT', + 'BLENDER_WORKBENCH', + } + + _pixel_density_args_prev = None + _preset_class = None + + @staticmethod + def _draw_pixeldensity_label(*args): + # Avoids re-creating text string each draw. + if RENDER_PT_output_pixel_density._pixel_density_args_prev == args: + return RENDER_PT_output_pixel_density._pixel_density_ret + + ppm_base, preset_label = args + + # NOTE: `as_float_32` is needed because Blender stores this value as a 32bit float. + # Which won't match Python's 64bit float. + def as_float_32(f): + from struct import pack, unpack + return unpack("f", pack("f", f))[0] + + # NOTE: Values here are duplicated from presets, ideally this could be avoided. + unit_name = { + as_float_32(0.0254): iface_("Inch"), + as_float_32(0.01): iface_("Centimeter"), + as_float_32(1.0): iface_("Meter"), + }.get(ppm_base) + + if unit_name is None: + pixeldensity_label_text = iface_("Custom") + show_pixeldensity = True + else: + pixeldensity_label_text = iface_("Pixels/{:s}").format(unit_name) + show_pixeldensity = (preset_label == "Custom") + + RENDER_PT_output_pixel_density._pixel_density_args_prev = args + RENDER_PT_output_pixel_density._pixel_density_ret = args = (pixeldensity_label_text, show_pixeldensity) + return args + + @staticmethod + def draw_pixeldensity(layout, rd): + if RENDER_PT_output_pixel_density._preset_class is None: + RENDER_PT_output_pixel_density._preset_class = bpy.types.RENDER_MT_pixeldensity_presets + + args = rd.ppm_base, RENDER_PT_output_pixel_density._preset_class.bl_label + pixeldensity_label_text, show_pixeldensity = RENDER_PT_output_pixel_density._draw_pixeldensity_label(*args) + + layout.prop(rd, "ppm_factor", text="Pixels") + + row = layout.split(factor=0.4) + row.alignment = 'RIGHT' + row.label(text="Unit") + row.menu("RENDER_MT_pixeldensity_presets", text=pixeldensity_label_text) + + if show_pixeldensity: + col = layout.column(align=True) + col.prop(rd, "ppm_base", text="Base") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + rd = scene.render + + self.draw_pixeldensity(layout, rd) + + class RENDER_PT_encoding(RenderOutputButtonsPanel, Panel): bl_label = "Encoding" bl_parent_id = "RENDER_PT_output" @@ -603,6 +687,7 @@ classes = ( RENDER_PT_format_presets, RENDER_PT_ffmpeg_presets, RENDER_MT_framerate_presets, + RENDER_MT_pixeldensity_presets, RENDER_PT_format, RENDER_PT_frame_range, RENDER_PT_time_stretching, @@ -610,6 +695,7 @@ classes = ( RENDER_PT_output, RENDER_PT_output_views, RENDER_PT_output_color_management, + RENDER_PT_output_pixel_density, RENDER_PT_encoding, RENDER_PT_encoding_video, RENDER_PT_encoding_audio, diff --git a/source/blender/blenkernel/BKE_scene.hh b/source/blender/blenkernel/BKE_scene.hh index 42e2e1daa66..44986ae4198 100644 --- a/source/blender/blenkernel/BKE_scene.hh +++ b/source/blender/blenkernel/BKE_scene.hh @@ -282,6 +282,10 @@ void BKE_scene_multiview_view_prefix_get(Scene *scene, void BKE_scene_multiview_videos_dimensions_get( const RenderData *rd, size_t width, size_t height, size_t *r_width, size_t *r_height); int BKE_scene_multiview_num_videos_get(const RenderData *rd); +/** + * Calculate the final pixels-per-meter, from the scenes PPM & aspect data. + */ +void BKE_scene_ppm_get(const RenderData *rd, double r_ppm[2]); /* depsgraph */ void BKE_scene_allocate_depsgraph_hash(Scene *scene); diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 2fc6ccf7548..f87bb3c8d0d 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -4158,6 +4158,7 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e IMB_refImBuf(ibuf); BKE_imbuf_stamp_info(ima->rr, ibuf); + copy_v2_v2_db(ibuf->ppm, ima->rr->ppm); image_init_after_load(ima, iuser, ibuf); image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); @@ -4458,6 +4459,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) image_init_after_load(ima, iuser, ibuf); BKE_imbuf_stamp_info(ima->rr, ibuf); + copy_v2_v2_db(ibuf->ppm, ima->rr->ppm); image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); } diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index f3cf9ae9a5a..c8295f5fb5b 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -377,11 +377,35 @@ static bool image_save_single(ReportList *reports, BKE_imbuf_stamp_info(rr, ibuf); } + /* Don't write permanently into the render-result. */ + double rr_ppm_prev[2] = {0, 0}; + + if (save_as_render && rr) { + /* These could be used in the case of a null `rr`, currently they're not though. + * Note that setting zero when there is no `rr` is intentional, + * this signifies no valid PPM is set. */ + double ppm[2] = {0, 0}; + if (opts->scene) { + BKE_scene_ppm_get(&opts->scene->r, ppm); + } + copy_v2_v2_db(rr_ppm_prev, rr->ppm); + copy_v2_v2_db(rr->ppm, ppm); + } + + /* From now on, calls to #BKE_image_release_renderresult must restore the PPM beforehand. */ + auto render_result_restore_ppm = [rr, save_as_render, rr_ppm_prev]() { + if (save_as_render && rr) { + copy_v2_v2_db(rr->ppm, rr_ppm_prev); + } + }; + /* fancy multiview OpenEXR */ if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { /* save render result */ ok = BKE_image_render_write_exr( reports, rr, opts->filepath, imf, save_as_render, nullptr, layer); + + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); BKE_image_release_ibuf(ima, ibuf, lock); @@ -397,6 +421,8 @@ static bool image_save_single(ReportList *reports, ok = BKE_imbuf_write_as(colormanaged_ibuf, opts->filepath, imf, save_copy); imbuf_save_post(ibuf, colormanaged_ibuf); } + + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); image_save_post(reports, ima, @@ -465,6 +491,7 @@ static bool image_save_single(ReportList *reports, ok &= ok_view; } + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); if (is_exr_rr) { @@ -476,6 +503,8 @@ static bool image_save_single(ReportList *reports, if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { ok = BKE_image_render_write_exr( reports, rr, opts->filepath, imf, save_as_render, nullptr, layer); + + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed); BKE_image_release_ibuf(ima, ibuf, lock); @@ -552,10 +581,12 @@ static bool image_save_single(ReportList *reports, IMB_freeImBuf(ibuf_stereo[i]); } + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); } } else { + render_result_restore_ppm(); BKE_image_release_renderresult(opts->scene, ima, rr); BKE_image_release_ibuf(ima, ibuf, lock); } @@ -967,7 +998,7 @@ bool BKE_image_render_write_exr(ReportList *reports, const int compress = (imf ? imf->exr_codec : 0); const int quality = (imf ? imf->quality : 90); bool success = IMB_exr_begin_write( - exrhandle, filepath, rr->rectx, rr->recty, compress, quality, rr->stamp_data); + exrhandle, filepath, rr->rectx, rr->recty, rr->ppm, compress, quality, rr->stamp_data); if (success) { IMB_exr_write_channels(exrhandle); } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index bffac40c1cf..d1f5463c529 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -43,6 +43,7 @@ #include "DNA_world_types.h" #include "BLI_listbase.h" +#include "BLI_math_base.h" #include "BLI_math_rotation.h" #include "BLI_path_utils.hh" #include "BLI_string.h" @@ -3188,6 +3189,29 @@ int BKE_scene_multiview_num_videos_get(const RenderData *rd) return BKE_scene_multiview_num_views_get(rd); } +void BKE_scene_ppm_get(const RenderData *rd, double r_ppm[2]) +{ + /* Should not be zero, prevent divide by zero if it is. */ + if (UNLIKELY(rd->ppm_base == 0.0f)) { + /* Zero PPM should be ignored. */ + r_ppm[0] = 0.0; + r_ppm[1] = 0.0; + } + double xasp = 1.0, yasp = 1.0; + if (rd->xasp < rd->yasp) { + yasp = double(rd->yasp) / double(rd->xasp); + } + else if (rd->xasp > rd->yasp) { + xasp = double(rd->xasp) / double(rd->yasp); + } + + const double ppm_base = rd->ppm_base; + const double ppm_factor = rd->ppm_factor; + + r_ppm[0] = (ppm_factor / ppm_base) * xasp; + r_ppm[1] = (ppm_factor / ppm_base) * yasp; +} + /* Manipulation of depsgraph storage. */ /* This is a key which identifies depsgraph. */ diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index aedc2ab9e4b..4d0b4883c5a 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -6693,6 +6693,16 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) rename_mesh_uv_seam_attribute(*mesh); } + /* TODO: define version bump. */ + { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->r.ppm_factor == 0.0f && scene->r.ppm_base == 0.0f) { + scene->r.ppm_factor = 72.0f; + scene->r.ppm_base = 0.0254f; + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/compositor/intern/render_context.cc b/source/blender/compositor/intern/render_context.cc index dbc3d9e36b1..28565ab5a9e 100644 --- a/source/blender/compositor/intern/render_context.cc +++ b/source/blender/compositor/intern/render_context.cc @@ -8,6 +8,7 @@ #include "BLI_assert.h" #include "BLI_listbase.h" #include "BLI_map.hh" +#include "BLI_math_vector.h" #include "BLI_math_vector_types.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -23,6 +24,7 @@ #include "BKE_image.hh" #include "BKE_image_save.hh" #include "BKE_report.hh" +#include "BKE_scene.hh" #include "RE_pipeline.h" @@ -45,6 +47,12 @@ FileOutput::FileOutput(const std::string &path, render_result_->rectx = size.x; render_result_->recty = size.y; + /* NOTE: set dummy values which will won't be used unless overwritten. + * When `save_as_render` is set, this is overwritten by the scenes PPM setting. + * We *could* support setting the DPI in the file output node too. */ + render_result_->ppm[0] = 0.0; + render_result_->ppm[1] = 0.0; + /* File outputs are always single layer, as images are actually stored in passes on that single * layer. Create a single unnamed layer to add the passes to. A single unnamed layer is treated * by the EXR writer as a special case where the channel names take the form: @@ -108,6 +116,7 @@ void FileOutput::add_pass(const char *pass_name, render_pass->ibuf = IMB_allocImBuf( render_result_->rectx, render_result_->recty, channels_count * 8, 0); render_pass->ibuf->channels = channels_count; + copy_v2_v2_db(render_pass->ibuf->ppm, render_result_->ppm); IMB_assign_float_buffer(render_pass->ibuf, buffer, IB_TAKE_OWNERSHIP); } @@ -127,6 +136,12 @@ void FileOutput::save(Scene *scene) BKE_render_result_stamp_data(render_result_, field.key.c_str(), field.value.c_str()); } + /* NOTE: without this the file will be written without any density information. + * So always write this. */ + if (save_as_render_ || true) { + BKE_scene_ppm_get(&scene->r, render_result_->ppm); + } + BKE_image_render_write( &reports, render_result_, scene, true, path_.c_str(), &format_, save_as_render_); diff --git a/source/blender/imbuf/IMB_openexr.hh b/source/blender/imbuf/IMB_openexr.hh index e2d0c3ec66c..e1cd26163c8 100644 --- a/source/blender/imbuf/IMB_openexr.hh +++ b/source/blender/imbuf/IMB_openexr.hh @@ -49,6 +49,7 @@ bool IMB_exr_begin_write(void *handle, const char *filepath, int width, int height, + const double ppm[2], int compress, int quality, const StampData *stamp); @@ -85,3 +86,5 @@ 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_get_ppm(void *handle, double ppm[2]); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 1d2e61ea424..5e201148d1c 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -961,6 +961,7 @@ bool IMB_exr_begin_write(void *handle, const char *filepath, int width, int height, + const double ppm[2], int compress, int quality, const StampData *stamp) @@ -993,6 +994,11 @@ bool IMB_exr_begin_write(void *handle, addMultiView(header, *data->multiView); } + if (ppm[0] != 0.0 && ppm[1] != 0.0) { + addXDensity(header, ppm[0] * 0.0254); + header.pixelAspectRatio() = blender::math::safe_divide(ppm[1], ppm[0]); + } + /* 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 { @@ -2036,6 +2042,23 @@ static void imb_exr_set_known_colorspace(const Header &header, ImFileColorSpace } } +static bool exr_get_ppm(MultiPartInputFile &file, double ppm[2]) +{ + const Header &header = file.header(0); + if (!hasXDensity(header)) { + return false; + } + ppm[0] = double(xDensity(header)) / 0.0254; + ppm[1] = ppm[0] * double(header.pixelAspectRatio()); + return true; +} + +bool IMB_exr_get_ppm(void *handle, double ppm[2]) +{ + ExrHandle *data = (ExrHandle *)handle; + return exr_get_ppm(*data->ifile, ppm); +} + ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImBuf *ibuf = nullptr; @@ -2077,11 +2100,7 @@ ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpa ibuf->foptions.flag |= exr_is_half_float(*file) ? OPENEXR_HALF : 0; ibuf->foptions.flag |= openexr_header_get_compression(file_header); - if (hasXDensity(file_header)) { - /* Convert inches to meters. */ - ibuf->ppm[0] = double(xDensity(file_header)) / 0.0254; - ibuf->ppm[1] = ibuf->ppm[0] * double(file_header.pixelAspectRatio()); - } + exr_get_ppm(*file, ibuf->ppm); imb_exr_set_known_colorspace(file_header, r_colorspace); diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 6048ba44b14..fc07e0c741b 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -39,6 +39,7 @@ bool IMB_exr_begin_write(void * /*handle*/, const char * /*filepath*/, int /*width*/, int /*height*/, + const double /*ppm*/[2], int /*compress*/, int /*quality*/, const StampData * /*stamp*/) @@ -80,3 +81,8 @@ bool IMB_exr_has_multilayer(void * /*handle*/) { return false; } + +bool IMB_exr_get_ppm(void * /*handle*/, double /*ppm*/[2]) +{ + return false; +} diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index e50730c7a1a..9f9e2340c32 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -66,6 +66,8 @@ .ysch = 1080, \ .xasp = 1, \ .yasp = 1, \ + .ppm_factor = 72.0f, \ + .ppm_base = 0.0254f, \ .tilex = 256, \ .tiley = 256, \ .size = 100, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 182427e0a76..3d3a618b003 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -736,6 +736,29 @@ typedef struct RenderData { */ float xasp, yasp; + /** + * Pixels per meter (factor of PPM base). + * The final calculated PPM is stored as a pair of doubles, + * taking the render aspect into support separate X/Y density. + * Editing the final PPM directly isn't practical as common DPI + * values often result the fractional part having many decimal places. + * So expose the factor & base, where the base is used to set the "preset" in the GUI, + * (Inch CM, MM... etc). + * + * Once calculated the final PPM is stored in the #ImBuf & #RenderResult + * which are saved/loaded through #ImBuf API's or multi-layer EXR images + * in the case of the render-result. + * + * Note that storing the X/Y density means it's possible know the aspect + * used to render the image which may be useful. + */ + float ppm_factor; + /** + * Pixels per meter base (0.0254 for DPI), a multiplier for `ppm_factor`. + * Used to implement "presets". + */ + float ppm_base; + float frs_sec_base; /** diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 44b3b2094e2..fd5299ce4ba 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -6922,6 +6922,22 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop, "Pixel Aspect Y", "Vertical aspect ratio - for anamorphic or non-square pixel output"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneCamera_update"); + /* Pixels per meters (also DPI). */ + prop = RNA_def_property(srna, "ppm_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "ppm_factor"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, 1e-5f, 1e6f); + RNA_def_property_ui_range(prop, 0.0001f, 10000.0f, 2, 2); + RNA_def_property_ui_text(prop, "PPM Factor", "The unit multiplier for pixels per meter"); + + prop = RNA_def_property(srna, "ppm_base", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "ppm_base"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, 1e-5f, 1e6f); + /* Important to show at least 3 decimal points because multiple presets set this to 1.001. */ + RNA_def_property_ui_range(prop, 0.0001f, 10000.0f, 2, 4); + RNA_def_property_ui_text(prop, "PPM Base", "The unit multiplier for pixels per meter"); + prop = RNA_def_property(srna, "ffmpeg", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "FFmpegSettings"); RNA_def_property_pointer_sdna(prop, nullptr, "ffcodecdata"); diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 6ed52992d80..a37de51f54a 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -128,6 +128,14 @@ struct RenderResult { /* for render results in Image, verify validity for sequences */ int framenr; + /** + * Pixels per meter (for image output). + * - Typically initialized via #BKE_scene_ppm_get. + * - May be zero which indicates the PPM being "unset". + * Although in most cases a scene is available. + */ + double ppm[2]; + /* for acquire image, to indicate if it there is a combined layer */ bool have_combined; diff --git a/source/blender/render/intern/engine.cc b/source/blender/render/intern/engine.cc index 87c9ccb542d..18fc66e57d7 100644 --- a/source/blender/render/intern/engine.cc +++ b/source/blender/render/intern/engine.cc @@ -200,6 +200,8 @@ static RenderResult *render_result_from_bake( rr->tilerect.xmax = x + w; rr->tilerect.ymax = y + h; + BKE_scene_ppm_get(&engine->re->r, rr->ppm); + /* Add single baking render layer. */ RenderLayer *rl = MEM_callocN("bake render layer"); STRNCPY(rl->name, layername); diff --git a/source/blender/render/intern/pipeline.cc b/source/blender/render/intern/pipeline.cc index 259f3e6fe64..85e95b35d0d 100644 --- a/source/blender/render/intern/pipeline.cc +++ b/source/blender/render/intern/pipeline.cc @@ -398,6 +398,8 @@ void RE_AcquireResultImageViews(Render *re, RenderResult *rr) rr->rectx = re->result->rectx; rr->recty = re->result->recty; + copy_v2_v2_db(rr->ppm, re->result->ppm); + /* creates a temporary duplication of views */ render_result_views_shallowcopy(rr, re->result); @@ -450,6 +452,8 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id) rr->rectx = re->result->rectx; rr->recty = re->result->recty; + copy_v2_v2_db(rr->ppm, re->result->ppm); + /* `scene.rd.actview` view. */ rv = RE_RenderViewGetById(re->result, view_id); rr->have_combined = (rv->ibuf != nullptr); @@ -921,6 +925,7 @@ void RE_InitState(Render *re, re->result = MEM_callocN("new render result"); re->result->rectx = re->rectx; re->result->recty = re->recty; + BKE_scene_ppm_get(&re->r, re->result->ppm); render_result_view_new(re->result, ""); } diff --git a/source/blender/render/intern/render_result.cc b/source/blender/render/intern/render_result.cc index 7b36369538c..31cfe3af1d2 100644 --- a/source/blender/render/intern/render_result.cc +++ b/source/blender/render/intern/render_result.cc @@ -210,6 +210,7 @@ static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) rp->ibuf = IMB_allocImBuf(rr->rectx, rr->recty, get_num_planes_for_pass_ibuf(*rp), 0); rp->ibuf->channels = rp->channels; + copy_v2_v2_db(rp->ibuf->ppm, rr->ppm); IMB_assign_float_buffer(rp->ibuf, buffer_data, IB_TAKE_OWNERSHIP); assign_render_pass_ibuf_colorspace(*rp); @@ -291,6 +292,8 @@ RenderResult *render_result_new(Render *re, rr->rectx = rectx; rr->recty = recty; + BKE_scene_ppm_get(&re->r, rr->ppm); + /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ rr->tilerect.xmin = partrct->xmin - re->disprect.xmin; rr->tilerect.xmax = partrct->xmax - re->disprect.xmin; @@ -715,6 +718,8 @@ RenderResult *render_result_new_from_exr( rr->rectx = rectx; rr->recty = recty; + IMB_exr_get_ppm(exrhandle, rr->ppm); + IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { @@ -727,6 +732,8 @@ RenderResult *render_result_new_from_exr( rpass->rectx = rectx; rpass->recty = recty; + copy_v2_v2_db(rpass->ibuf->ppm, rr->ppm); + if (RE_RenderPassIsColor(rpass)) { IMB_colormanagement_transform_float(rpass->ibuf->float_buffer.data, rpass->rectx, @@ -1104,6 +1111,8 @@ ImBuf *RE_render_result_rect_to_ibuf(RenderResult *rr, /* float factor for random dither, imbuf takes care of it */ ibuf->dither = dither; + copy_v2_v2_db(ibuf->ppm, rr->ppm); + /* prepare to gamma correct to sRGB color space * note that sequence editor can generate 8bpc render buffers */ @@ -1343,6 +1352,9 @@ RenderResult *RE_DuplicateRenderResult(RenderResult *rr) new_rr->ibuf = IMB_dupImBuf(rr->ibuf); new_rr->stamp_data = BKE_stamp_data_copy(new_rr->stamp_data); + + copy_v2_v2_db(new_rr->ppm, rr->ppm); + return new_rr; }