Color Management: Reduce reliance on untonemapped view
No longer use it for display of images without View as Render, color swatches and color picker, and 3D viewport in solid mode. Instead go directly to extended sRGB matching the system graphics buffer. We still use it for setting the image colorspace after Save as Render, and for color value inspection in the image editor and tooltips. Ref #144911 Pull Request: https://projects.blender.org/blender/blender/pulls/146487
This commit is contained in:
@@ -780,15 +780,6 @@ void IMB_colormanagement_display_settings_from_ctx(
|
||||
}
|
||||
}
|
||||
|
||||
static const ColorSpace *get_untonemapped_display_colorspace(
|
||||
const ColorManagedDisplaySettings *display_settings)
|
||||
{
|
||||
ColorManagedViewSettings view_settings = {};
|
||||
IMB_colormanagement_init_untonemapped_view_settings(&view_settings, display_settings);
|
||||
return g_config->get_display_view_color_space(display_settings->display_device,
|
||||
view_settings.view_transform);
|
||||
}
|
||||
|
||||
static std::shared_ptr<const ocio::CPUProcessor> get_display_buffer_processor(
|
||||
const ColorManagedDisplaySettings &display_settings,
|
||||
const char *look,
|
||||
@@ -822,22 +813,11 @@ static std::shared_ptr<const ocio::CPUProcessor> get_display_buffer_processor(
|
||||
}
|
||||
|
||||
void IMB_colormanagement_init_untonemapped_view_settings(
|
||||
ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
|
||||
ColorManagedViewSettings *view_settings,
|
||||
const ColorManagedDisplaySettings * /*display_settings*/)
|
||||
{
|
||||
/* Get untonemapped view from the display. */
|
||||
const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
|
||||
const ocio::View *default_view = (display) ? display->get_untonemapped_view() : nullptr;
|
||||
/* If that fails, we fall back to the default view transform of the display
|
||||
* as per OCIO configuration. */
|
||||
if (default_view == nullptr) {
|
||||
default_view = (display) ? display->get_default_view() : nullptr;
|
||||
}
|
||||
if (default_view != nullptr) {
|
||||
STRNCPY_UTF8(view_settings->view_transform, default_view->name().c_str());
|
||||
}
|
||||
else {
|
||||
view_settings->view_transform[0] = '\0';
|
||||
}
|
||||
/* Empty view transform name means skip tone mapping. */
|
||||
view_settings->view_transform[0] = '\0';
|
||||
/* TODO(sergey): Find a way to safely/reliable un-hardcode this. */
|
||||
STRNCPY_UTF8(view_settings->look, "None");
|
||||
/* Initialize rest of the settings. */
|
||||
@@ -3036,16 +3016,22 @@ const ColorSpace *IMB_colormangement_display_get_color_space(
|
||||
const ColorManagedDisplaySettings *display_settings)
|
||||
{
|
||||
/* Get the colorspace that the image is in after applying this view and display
|
||||
* transform. If we are going to a display referred colorspace we can use that.
|
||||
* However this is not always available, especially in v1 configs. We then rely
|
||||
* on guessing what the untonemapped view transform is. */
|
||||
const ColorSpace *colorspace = g_config->get_display_view_color_space(
|
||||
display_settings->display_device, view_settings->view_transform);
|
||||
* transform. If we are going to a display referred colorspace we can use that. */
|
||||
const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device);
|
||||
const ocio::View *view = (display) ? display->get_view_by_name(view_settings->view_transform) :
|
||||
nullptr;
|
||||
const ColorSpace *colorspace = (view) ? view->display_colorspace() : nullptr;
|
||||
if (colorspace && colorspace->is_display_referred()) {
|
||||
return colorspace;
|
||||
}
|
||||
const ColorSpace *untonemapped_colorspace = get_untonemapped_display_colorspace(
|
||||
display_settings);
|
||||
/* If not available, try to guess what the untonemapped view is and use its colorspace.
|
||||
* This is especially needed for v1 configs. */
|
||||
const ocio::View *untonemapped_view = (display) ? display->get_untonemapped_view() : nullptr;
|
||||
const ocio::ColorSpace *untonemapped_colorspace = (untonemapped_view) ?
|
||||
g_config->get_display_view_color_space(
|
||||
display_settings->display_device,
|
||||
untonemapped_view->name()) :
|
||||
nullptr;
|
||||
return (untonemapped_colorspace) ? untonemapped_colorspace : colorspace;
|
||||
}
|
||||
|
||||
@@ -4146,7 +4132,8 @@ ColormanageProcessor *IMB_colormanagement_display_processor_new(
|
||||
applied_view_settings = &untonemapped_view_settings;
|
||||
}
|
||||
|
||||
display_colorspace = IMB_colormangement_display_get_color_space(view_settings, display_settings);
|
||||
display_colorspace = IMB_colormangement_display_get_color_space(applied_view_settings,
|
||||
display_settings);
|
||||
if (display_colorspace) {
|
||||
cm_processor->is_data_result = display_colorspace->is_data();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# include <optional>
|
||||
# include <sstream>
|
||||
|
||||
# include "BLI_colorspace.hh"
|
||||
# include "BLI_math_matrix.hh"
|
||||
|
||||
# include "OCIO_config.hh"
|
||||
@@ -27,6 +28,74 @@ static CLG_LogRef LOG = {"color_management"};
|
||||
|
||||
namespace blender::ocio {
|
||||
|
||||
static TransferFunction system_extended_srgb_transfer_function(const LibOCIOView *view,
|
||||
const bool use_hdr_buffer)
|
||||
{
|
||||
# ifdef __APPLE__
|
||||
/* The Metal backend always uses sRGB or extended sRGB buffer.
|
||||
*
|
||||
* How this will be decoded depends on the macOS display preset, but from testing
|
||||
* on a MacBook P3 M3 it appears:
|
||||
* - Apple XDR Display (P3 - 1600 nits): Decode with gamma 2.2
|
||||
* - HDR Video (P3-ST 2084): Decode with sRGB. As we encode with the sRGB transfer
|
||||
* function, this will be cancelled out, and linear values will be passed on
|
||||
* effectively unmodified.
|
||||
*/
|
||||
UNUSED_VARS(use_hdr_buffer, view);
|
||||
return TransferFunction::sRGB;
|
||||
# elif defined(_WIN32)
|
||||
/* The Vulkan backend uses either sRGB for SDR, or linear extended sRGB for HDR.
|
||||
*
|
||||
* - Windows HDR mode off: use_hdr_buffer will be false, and we encode with sRGB.
|
||||
* By default Windows will decode with gamma 2.2.
|
||||
* - Windows HDR mode on: use_hdr_buffer will be true, and we encode with sRGB.
|
||||
* The Vulkan HDR swapchain blitting will decode with sRGB to cancel this out
|
||||
* exactly, meaning we effectively pass on linear values unmodified.
|
||||
*
|
||||
* Note this means that both the user interface and SDR content will not be
|
||||
* displayed the same in HDR mode off and on. However it is consistent with other
|
||||
* software. To match, gamma 2.2 would have to be used.
|
||||
*/
|
||||
UNUSED_VARS(use_hdr_buffer, view);
|
||||
return TransferFunction::sRGB;
|
||||
# else
|
||||
/* The Vulkan backend uses either sRGB for SDR, or linear extended sRGB for HDR.
|
||||
*
|
||||
* - When using a HDR swapchain and the display + view is HDR, ensure we pass on
|
||||
* values linearly by doing gamma 2.2 encode here + gamma 2.2 decode in the
|
||||
* Vulkan HDR swapchain blitting.
|
||||
* - When using HDR swapain and the display + view is SDR, use sRGB encode to
|
||||
* emulate what happens on a typical SDR monitor.
|
||||
* - When using an SDR swapchain, the buffer is always sRGB.
|
||||
*/
|
||||
return (use_hdr_buffer && view && view->is_hdr()) ? TransferFunction::Gamma22 :
|
||||
TransferFunction::sRGB;
|
||||
# endif
|
||||
}
|
||||
|
||||
static OCIO_NAMESPACE::TransformRcPtr create_extended_srgb_transform(
|
||||
const TransferFunction transfer_function)
|
||||
{
|
||||
if (transfer_function == TransferFunction::sRGB) {
|
||||
/* Piecewise sRGB transfer function. */
|
||||
auto to_ui = OCIO_NAMESPACE::ExponentWithLinearTransform::Create();
|
||||
to_ui->setGamma({2.4, 2.4, 2.4, 1.0});
|
||||
to_ui->setOffset({0.055, 0.055, 0.055, 0.0});
|
||||
/* Mirrored for negative as specified by scRGB and extended sRGB. */
|
||||
to_ui->setNegativeStyle(OCIO_NAMESPACE::NEGATIVE_MIRROR);
|
||||
to_ui->setDirection(OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
return to_ui;
|
||||
}
|
||||
|
||||
/* Pure gamma 2.2 function. */
|
||||
auto to_ui = OCIO_NAMESPACE::ExponentTransform::Create();
|
||||
to_ui->setValue({2.2, 2.2, 2.2, 1.0});
|
||||
/* Mirrored for negative as specified by scRGB and extended sRGB. */
|
||||
to_ui->setNegativeStyle(OCIO_NAMESPACE::NEGATIVE_MIRROR);
|
||||
to_ui->setDirection(OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
return to_ui;
|
||||
}
|
||||
|
||||
static void display_as_extended_srgb(const LibOCIOConfig &config,
|
||||
OCIO_NAMESPACE::GroupTransformRcPtr &group,
|
||||
StringRefNull display_name,
|
||||
@@ -58,47 +127,8 @@ static void display_as_extended_srgb(const LibOCIOConfig &config,
|
||||
return;
|
||||
}
|
||||
|
||||
# ifdef __APPLE__
|
||||
/* The Metal backend always uses sRGB or extended sRGB buffer.
|
||||
*
|
||||
* How this will be decoded depends on the macOS display preset, but from testing
|
||||
* on a MacBook P3 M3 it appears:
|
||||
* - Apple XDR Display (P3 - 1600 nits): Decode with gamma 2.2
|
||||
* - HDR Video (P3-ST 2084): Decode with sRGB. As we encode with the sRGB transfer
|
||||
* function, this will be cancelled out, and linear values will be passed on
|
||||
* effectively unmodified.
|
||||
*/
|
||||
const TransferFunction target_transfer_function = TransferFunction::sRGB;
|
||||
UNUSED_VARS(use_hdr_buffer);
|
||||
# elif defined(_WIN32)
|
||||
/* The Vulkan backend uses either sRGB for SDR, or linear extended sRGB for HDR.
|
||||
*
|
||||
* - Windows HDR mode off: use_hdr_buffer will be false, and we encode with sRGB.
|
||||
* By default Windows will decode with gamma 2.2.
|
||||
* - Windows HDR mode on: use_hdr_buffer will be true, and we encode with sRGB.
|
||||
* The Vulkan HDR swapchain blitting will decode with sRGB to cancel this out
|
||||
* exactly, meaning we effectively pass on linear values unmodified.
|
||||
*
|
||||
* Note this means that both the user interface and SDR content will not be
|
||||
* displayed the same in HDR mode off and on. However it is consistent with other
|
||||
* software. To match, gamma 2.2 would have to be used.
|
||||
*/
|
||||
const TransferFunction target_transfer_function = TransferFunction::sRGB;
|
||||
UNUSED_VARS(use_hdr_buffer);
|
||||
# else
|
||||
/* The Vulkan backend uses either sRGB for SDR, or linear extended sRGB for HDR.
|
||||
*
|
||||
* - When using a HDR swapchain and the display + view is HDR, ensure we pass on
|
||||
* values linearly by doing gamma 2.2 encode here + gamma 2.2 decode in the
|
||||
* Vulkan HDR swapchain blitting.
|
||||
* - When using HDR swapain and the display + view is SDR, use sRGB encode to
|
||||
* emulate what happens on a typical SDR monitor.
|
||||
* - When using an SDR swapchain, the buffer is always sRGB.
|
||||
*/
|
||||
const TransferFunction target_transfer_function = (use_hdr_buffer && view->is_hdr()) ?
|
||||
TransferFunction::Gamma22 :
|
||||
TransferFunction::sRGB;
|
||||
# endif
|
||||
const TransferFunction target_transfer_function = system_extended_srgb_transfer_function(
|
||||
view, use_hdr_buffer);
|
||||
|
||||
/* If we are already in the desired display colorspace, all we have to do is clamp. */
|
||||
if ((view->transfer_function() == target_transfer_function ||
|
||||
@@ -252,25 +282,7 @@ static void display_as_extended_srgb(const LibOCIOConfig &config,
|
||||
group->appendTransform(to_rec709);
|
||||
}
|
||||
|
||||
if (target_transfer_function == TransferFunction::sRGB) {
|
||||
/* Piecewise sRGB transfer function. */
|
||||
auto to_ui = OCIO_NAMESPACE::ExponentWithLinearTransform::Create();
|
||||
to_ui->setGamma({2.4, 2.4, 2.4, 1.0});
|
||||
to_ui->setOffset({0.055, 0.055, 0.055, 0.0});
|
||||
/* Mirrored for negative as specified by scRGB and extended sRGB. */
|
||||
to_ui->setNegativeStyle(OCIO_NAMESPACE::NEGATIVE_MIRROR);
|
||||
to_ui->setDirection(OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
group->appendTransform(to_ui);
|
||||
}
|
||||
else {
|
||||
/* Pure gamma 2.2 function. */
|
||||
auto to_ui = OCIO_NAMESPACE::ExponentTransform::Create();
|
||||
to_ui->setValue({2.2, 2.2, 2.2, 1.0});
|
||||
/* Mirrored for negative as specified by scRGB and extended sRGB. */
|
||||
to_ui->setNegativeStyle(OCIO_NAMESPACE::NEGATIVE_MIRROR);
|
||||
to_ui->setDirection(OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
group->appendTransform(to_ui);
|
||||
}
|
||||
group->appendTransform(create_extended_srgb_transform(target_transfer_function));
|
||||
}
|
||||
|
||||
OCIO_NAMESPACE::TransformRcPtr create_ocio_display_transform(
|
||||
@@ -323,6 +335,34 @@ OCIO_NAMESPACE::TransformRcPtr create_ocio_display_transform(
|
||||
return group;
|
||||
}
|
||||
|
||||
static OCIO_NAMESPACE::TransformRcPtr create_untonemapped_ocio_display_transform(
|
||||
const LibOCIOConfig &config,
|
||||
StringRefNull display_name,
|
||||
StringRefNull from_colorspace,
|
||||
bool use_hdr_buffer)
|
||||
{
|
||||
/* Convert to extended sRGB without any tone mapping. */
|
||||
const auto group = OCIO_NAMESPACE::GroupTransform::Create();
|
||||
|
||||
const auto to_scene_linear = OCIO_NAMESPACE::ColorSpaceTransform::Create();
|
||||
to_scene_linear->setSrc(from_colorspace.c_str());
|
||||
to_scene_linear->setDst(OCIO_NAMESPACE::ROLE_SCENE_LINEAR);
|
||||
group->appendTransform(to_scene_linear);
|
||||
|
||||
const auto to_rec709 = OCIO_NAMESPACE::MatrixTransform::Create();
|
||||
to_rec709->setMatrix(math::transpose(double4x4(colorspace::scene_linear_to_rec709)).base_ptr());
|
||||
group->appendTransform(to_rec709);
|
||||
|
||||
const LibOCIODisplay *display = static_cast<const LibOCIODisplay *>(
|
||||
config.get_display_by_name(display_name));
|
||||
const LibOCIOView *view = (display) ? static_cast<const LibOCIOView *>(
|
||||
display->get_untonemapped_view()) :
|
||||
nullptr;
|
||||
group->appendTransform(create_extended_srgb_transform(
|
||||
system_extended_srgb_transfer_function(view, use_hdr_buffer)));
|
||||
return group;
|
||||
}
|
||||
|
||||
OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_display_processor(
|
||||
const LibOCIOConfig &config, const DisplayParameters &display_parameters)
|
||||
{
|
||||
@@ -359,30 +399,37 @@ OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_display_processor(
|
||||
group->appendTransform(mt);
|
||||
}
|
||||
|
||||
/* Core display processor. */
|
||||
group->appendTransform(create_ocio_display_transform(ocio_config,
|
||||
display_parameters.display,
|
||||
display_parameters.view,
|
||||
display_parameters.look,
|
||||
from_colorspace));
|
||||
if (!display_parameters.view.is_empty()) {
|
||||
/* Core display processor. */
|
||||
group->appendTransform(create_ocio_display_transform(ocio_config,
|
||||
display_parameters.display,
|
||||
display_parameters.view,
|
||||
display_parameters.look,
|
||||
from_colorspace));
|
||||
/* Gamma. */
|
||||
if (display_parameters.exponent != 1.0f) {
|
||||
ExponentTransformRcPtr et = ExponentTransform::Create();
|
||||
const double value[4] = {display_parameters.exponent,
|
||||
display_parameters.exponent,
|
||||
display_parameters.exponent,
|
||||
1.0};
|
||||
et->setValue(value);
|
||||
group->appendTransform(et);
|
||||
}
|
||||
|
||||
/* Gamma. */
|
||||
if (display_parameters.exponent != 1.0f) {
|
||||
ExponentTransformRcPtr et = ExponentTransform::Create();
|
||||
const double value[4] = {display_parameters.exponent,
|
||||
display_parameters.exponent,
|
||||
display_parameters.exponent,
|
||||
1.0};
|
||||
et->setValue(value);
|
||||
group->appendTransform(et);
|
||||
/* Convert to extended sRGB to match the system graphics buffer. */
|
||||
if (display_parameters.use_display_emulation) {
|
||||
display_as_extended_srgb(config,
|
||||
group,
|
||||
display_parameters.display,
|
||||
display_parameters.view,
|
||||
display_parameters.use_hdr_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (display_parameters.use_display_emulation) {
|
||||
display_as_extended_srgb(config,
|
||||
group,
|
||||
display_parameters.display,
|
||||
display_parameters.view,
|
||||
display_parameters.use_hdr_buffer);
|
||||
else {
|
||||
/* Untonemapped case, directly to extended sRGB. */
|
||||
group->appendTransform(create_untonemapped_ocio_display_transform(
|
||||
config, display_parameters.display, from_colorspace, display_parameters.use_hdr_buffer));
|
||||
}
|
||||
|
||||
if (display_parameters.inverse) {
|
||||
|
||||
Reference in New Issue
Block a user