Render: support pixel density in the render pipeline
Add a "Pixel Density" sub-panel to render output settings which can be used to set the density (as pixels per inch for example). This is then written to images that support pixel density. Details: - The scene has two values a PPM factor and a and base unit. - The base unit defaults to pixels per inch as this is the most common unit used. - Unit presets for pixels per inch/centimeter/meter are included. - The pixel density is stored in the render result & EXR cache. - For non 1:1 aspect renders, the density increases on the axis which looks "stretched", so the PPM will print the correct aspect with non-square pixels. Ref !127831
This commit is contained in:
1
scripts/presets/pixel_density/Custom.py
Normal file
1
scripts/presets/pixel_density/Custom.py
Normal file
@@ -0,0 +1 @@
|
||||
import bpy
|
||||
2
scripts/presets/pixel_density/Pixels_slash_Centimeter.py
Normal file
2
scripts/presets/pixel_density/Pixels_slash_Centimeter.py
Normal file
@@ -0,0 +1,2 @@
|
||||
import bpy
|
||||
bpy.context.scene.render.ppm_base = 0.01
|
||||
2
scripts/presets/pixel_density/Pixels_slash_Inch.py
Normal file
2
scripts/presets/pixel_density/Pixels_slash_Inch.py
Normal file
@@ -0,0 +1,2 @@
|
||||
import bpy
|
||||
bpy.context.scene.render.ppm_base = 0.0254
|
||||
2
scripts/presets/pixel_density/Pixels_slash_Meter.py
Normal file
2
scripts/presets/pixel_density/Pixels_slash_Meter.py
Normal file
@@ -0,0 +1,2 @@
|
||||
import bpy
|
||||
bpy.context.scene.render.ppm_base = 1.0
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_);
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
.ysch = 1080, \
|
||||
.xasp = 1, \
|
||||
.yasp = 1, \
|
||||
.ppm_factor = 72.0f, \
|
||||
.ppm_base = 0.0254f, \
|
||||
.tilex = 256, \
|
||||
.tiley = 256, \
|
||||
.size = 100, \
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<RenderLayer>("bake render layer");
|
||||
STRNCPY(rl->name, layername);
|
||||
|
||||
@@ -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<RenderResult>("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, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user