Compositing Convert color space node

Compositor node to convert between color spaces.

Conversion is skipped when converting between the same color spaces or to or from data spaces.
Implementation done for tiled and full frame compositor.

Reviewed By: Blendify, jbakker

Differential Revision: https://developer.blender.org/D12481
This commit is contained in:
Jeroen Bakker 2022-01-10 08:57:53 +01:00
parent eb7333e772
commit 6beaa29791
Notes: blender-bot 2023-10-25 18:04:50 +02:00
Referenced by issue #68926, Color Management Improvements
18 changed files with 507 additions and 0 deletions

View File

@ -542,6 +542,7 @@ compositor_node_categories = [
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
NodeItem("CompositorNodeSwitchView"),
NodeItem("CompositorNodeConvertColorSpace"),
]),
CompositorNodeCategory("CMP_OP_FILTER", "Filter", items=[
NodeItem("CompositorNodeBlur"),

View File

@ -1318,6 +1318,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_NODE_EXPOSURE 325
#define CMP_NODE_CRYPTOMATTE 326
#define CMP_NODE_POSTERIZE 327
#define CMP_NODE_CONVERT_COLOR_SPACE 328
/* channel toggles */
#define CMP_CHAN_RGB 1

View File

@ -4582,6 +4582,7 @@ static void registerCompositNodes()
register_node_type_cmp_denoise();
register_node_type_cmp_antialiasing();
register_node_type_cmp_convert_color_space();
register_node_type_cmp_valtorgb();
register_node_type_cmp_rgbtobw();
register_node_type_cmp_setalpha();

View File

@ -39,6 +39,7 @@ set(INC
../../../extern/clew/include
../../../intern/atomic
../../../intern/guardedalloc
../../../intern/clog
)
set(INC_SYS
@ -242,6 +243,8 @@ set(SRC
nodes/COM_ColorToBWNode.h
nodes/COM_ConvertAlphaNode.cc
nodes/COM_ConvertAlphaNode.h
nodes/COM_ConvertColorSpaceNode.cc
nodes/COM_ConvertColorSpaceNode.h
nodes/COM_GammaNode.cc
nodes/COM_GammaNode.h
nodes/COM_HueSaturationValueCorrectNode.cc
@ -567,6 +570,8 @@ set(SRC
operations/COM_DotproductOperation.cc
operations/COM_DotproductOperation.h
operations/COM_ConvertColorSpaceOperation.cc
operations/COM_ConvertColorSpaceOperation.h
# Matte operation
operations/COM_BoxMaskOperation.cc

View File

@ -46,6 +46,7 @@
#include "COM_CombineColorNode.h"
#include "COM_CompositorNode.h"
#include "COM_ConvertAlphaNode.h"
#include "COM_ConvertColorSpaceNode.h"
#include "COM_ConvertOperation.h"
#include "COM_Converter.h"
#include "COM_CornerPinNode.h"
@ -426,6 +427,9 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_POSTERIZE:
node = new PosterizeNode(b_node);
break;
case CMP_NODE_CONVERT_COLOR_SPACE:
node = new ConvertColorSpaceNode(b_node);
break;
}
return node;
}

View File

@ -289,6 +289,23 @@ void MemoryBuffer::copy_from(const uchar *src,
}
}
void MemoryBuffer::apply_processor(ColormanageProcessor &processor, const rcti area)
{
const int width = BLI_rcti_size_x(&area);
const int height = BLI_rcti_size_y(&area);
float *out = get_elem(area.xmin, area.ymin);
/* If area allows continuous memory do conversion in one step. Otherwise per row. */
if (get_width() == width) {
IMB_colormanagement_processor_apply(&processor, out, width, height, get_num_channels(), false);
}
else {
for (int y = 0; y < height; y++) {
IMB_colormanagement_processor_apply(&processor, out, width, 1, get_num_channels(), false);
out += row_stride;
}
}
}
static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, ColorSpace *colorspace)
{
const int width = BLI_rcti_size_x(&area);

View File

@ -26,6 +26,8 @@
#include "BLI_math_interp.h"
#include "BLI_rect.h"
#include "IMB_colormanagement.h"
struct ImBuf;
namespace blender::compositor {
@ -578,6 +580,11 @@ class MemoryBuffer {
return state_ == MemoryBufferState::Temporary;
}
/**
* \brief Apply a color processor on the given area.
*/
void apply_processor(ColormanageProcessor &processor, const rcti area);
void copy_from(const MemoryBuffer *src, const rcti &area);
void copy_from(const MemoryBuffer *src, const rcti &area, int to_x, int to_y);
void copy_from(const MemoryBuffer *src,

View File

@ -0,0 +1,100 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "COM_ConvertColorSpaceNode.h"
#include "BKE_node.h"
#include "BLI_utildefines.h"
#include "COM_ConvertColorSpaceOperation.h"
#include "COM_ConvertOperation.h"
#include "COM_ExecutionSystem.h"
#include "COM_ImageOperation.h"
#include "COM_MultilayerImageOperation.h"
#include "CLG_log.h"
static CLG_LogRef LOG = {"compositor"};
namespace blender::compositor {
ConvertColorSpaceNode::ConvertColorSpaceNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
}
void ConvertColorSpaceNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &UNUSED(context)) const
{
bNode *b_node = get_bnode();
NodeInput *inputSocketImage = this->get_input_socket(0);
NodeOutput *outputSocketImage = this->get_output_socket(0);
NodeConvertColorSpace *settings = static_cast<NodeConvertColorSpace *>(b_node->storage);
if (!performs_conversion(*settings)) {
converter.map_output_socket(get_output_socket(0),
converter.add_input_proxy(get_input_socket(0), false));
return;
}
ConvertColorSpaceOperation *operation = new ConvertColorSpaceOperation();
operation->set_settings((NodeConvertColorSpace *)b_node->storage);
converter.add_operation(operation);
converter.map_input_socket(inputSocketImage, operation->get_input_socket(0));
converter.map_output_socket(outputSocketImage, operation->get_output_socket());
}
bool ConvertColorSpaceNode::performs_conversion(NodeConvertColorSpace &settings) const
{
bNode *b_node = get_bnode();
if (IMB_colormanagement_space_name_is_data(settings.from_color_space)) {
CLOG_INFO(&LOG,
2,
"Color space conversion bypassed for node: %s. From color space is data: %s.",
b_node->name,
settings.from_color_space);
return false;
}
if (IMB_colormanagement_space_name_is_data(settings.to_color_space)) {
CLOG_INFO(&LOG,
2,
"Color space conversion bypassed for node: %s. To color space is data: %s.",
b_node->name,
settings.to_color_space);
return false;
}
if (STREQLEN(
settings.from_color_space, settings.to_color_space, sizeof(settings.from_color_space))) {
CLOG_INFO(&LOG,
2,
"Color space conversion bypassed for node: %s. To and from are the same: %s.",
b_node->name,
settings.from_color_space);
return false;
}
return true;
}
} // namespace blender::compositor

View File

@ -0,0 +1,46 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#pragma once
#include "COM_Node.h"
#include "COM_defines.h"
#include "DNA_image_types.h"
#include "DNA_node_types.h"
#include "RE_engine.h"
#include "RE_pipeline.h"
namespace blender::compositor {
/**
* \brief ImageNode
* \ingroup Node
*/
class ConvertColorSpaceNode : public Node {
public:
ConvertColorSpaceNode(bNode *editorNode);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
private:
/** \brief check if the given settings changes color space. */
bool performs_conversion(NodeConvertColorSpace &settings) const;
};
} // namespace blender::compositor

View File

@ -0,0 +1,91 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "COM_ConvertColorSpaceOperation.h"
namespace blender::compositor {
ConvertColorSpaceOperation::ConvertColorSpaceOperation()
{
this->add_input_socket(DataType::Color);
this->add_output_socket(DataType::Color);
this->input_program_ = nullptr;
color_processor_ = nullptr;
}
void ConvertColorSpaceOperation::set_settings(NodeConvertColorSpace *node_color_space)
{
this->settings_ = node_color_space;
}
void ConvertColorSpaceOperation::init_execution()
{
if (BLI_strnlen(settings_->from_color_space, sizeof(settings_->from_color_space)) == 0 ||
BLI_strnlen(settings_->to_color_space, sizeof(settings_->to_color_space)) == 0) {
return;
}
int in_colorspace_index = IMB_colormanagement_colorspace_get_named_index(
settings_->from_color_space);
int out_colorspace_index = IMB_colormanagement_colorspace_get_named_index(
settings_->to_color_space);
if (in_colorspace_index == 0 || out_colorspace_index == 0) {
return;
}
this->input_program_ = this->get_input_socket_reader(0);
color_processor_ = IMB_colormanagement_colorspace_processor_new(settings_->from_color_space,
settings_->to_color_space);
}
void ConvertColorSpaceOperation::execute_pixel_sampled(float output[4],
float x,
float y,
PixelSampler sampler)
{
this->input_program_->read_sampled(output, x, y, sampler);
if (color_processor_ != nullptr) {
IMB_colormanagement_processor_apply_pixel(color_processor_, output, 3);
}
}
void ConvertColorSpaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
copy_v4_v4(it.out, it.in(0));
}
if (color_processor_ != nullptr) {
output->apply_processor(*color_processor_, area);
}
}
void ConvertColorSpaceOperation::deinit_execution()
{
if (color_processor_ != nullptr) {
IMB_colormanagement_processor_free(color_processor_);
}
this->input_program_ = nullptr;
this->color_processor_ = nullptr;
}
} // namespace blender::compositor

View File

@ -0,0 +1,57 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#pragma once
#include "COM_ConvertColorSpaceNode.h"
#include "COM_MultiThreadedOperation.h"
#include "IMB_colormanagement.h"
namespace blender::compositor {
class ConvertColorSpaceOperation : public MultiThreadedOperation {
private:
SocketReader *input_program_;
NodeConvertColorSpace *settings_;
ColormanageProcessor *color_processor_;
public:
ConvertColorSpaceOperation();
void set_settings(NodeConvertColorSpace *node_color_space);
/**
* The inner loop of this operation.
*/
void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
/**
* Initialize the execution
*/
void init_execution() override;
/**
* Deinitialize the execution
*/
void deinit_execution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -923,6 +923,11 @@ typedef struct NodeColorspill {
float uspillr, uspillg, uspillb;
} NodeColorspill;
typedef struct NodeConvertColorSpace {
char from_color_space[64];
char to_color_space[64];
} NodeConvertColorSpace;
typedef struct NodeDilateErode {
char falloff;
} NodeDilateErode;

View File

@ -137,6 +137,7 @@ extern StructRNA RNA_CompositorNodeChannelMatte;
extern StructRNA RNA_CompositorNodeChromaMatte;
extern StructRNA RNA_CompositorNodeColorMatte;
extern StructRNA RNA_CompositorNodeColorSpill;
extern StructRNA RNA_CompositorNodeConvertColorSpace;
extern StructRNA RNA_CompositorNodeCombHSVA;
extern StructRNA RNA_CompositorNodeCombRGBA;
extern StructRNA RNA_CompositorNodeCombYCCA;

View File

@ -52,6 +52,7 @@
#include "rna_internal.h"
#include "rna_internal_types.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@ -4666,6 +4667,55 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
return ma->gp_style == NULL;
}
static int rna_NodeConvertColorSpace_from_color_space_get(struct PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeConvertColorSpace *node_storage = node->storage;
return IMB_colormanagement_colorspace_get_named_index(node_storage->from_color_space);
}
static void rna_NodeConvertColorSpace_from_color_space_set(struct PointerRNA *ptr, int value)
{
bNode *node = (bNode *)ptr->data;
NodeConvertColorSpace *node_storage = node->storage;
const char *name = IMB_colormanagement_colorspace_get_indexed_name(value);
if (name && name[0]) {
BLI_strncpy(node_storage->from_color_space, name, sizeof(node_storage->from_color_space));
}
}
static int rna_NodeConvertColorSpace_to_color_space_get(struct PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeConvertColorSpace *node_storage = node->storage;
return IMB_colormanagement_colorspace_get_named_index(node_storage->to_color_space);
}
static void rna_NodeConvertColorSpace_to_color_space_set(struct PointerRNA *ptr, int value)
{
bNode *node = (bNode *)ptr->data;
NodeConvertColorSpace *node_storage = node->storage;
const char *name = IMB_colormanagement_colorspace_get_indexed_name(value);
if (name && name[0]) {
BLI_strncpy(node_storage->to_color_space, name, sizeof(node_storage->to_color_space));
}
}
static const EnumPropertyItem *rna_NodeConvertColorSpace_color_space_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
EnumPropertyItem *items = NULL;
int totitem = 0;
IMB_colormanagement_colorspace_items_add(&items, &totitem);
RNA_enum_item_end(&items, &totitem);
*r_free = true;
return items;
}
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@ -7313,6 +7363,42 @@ static void def_cmp_distance_matte(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_convert_color_space(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeConvertColorSpace", "storage");
static const EnumPropertyItem color_space_items[] = {
{0,
"NONE",
0,
"None",
"Do not perform any color transform on load, treat colors as in scene linear space "
"already"},
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_property(srna, "from_color_space", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
RNA_def_property_enum_items(prop, color_space_items);
RNA_def_property_enum_funcs(prop,
"rna_NodeConvertColorSpace_from_color_space_get",
"rna_NodeConvertColorSpace_from_color_space_set",
"rna_NodeConvertColorSpace_color_space_itemf");
RNA_def_property_ui_text(prop, "From", "Color space of the input image");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "to_color_space", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
RNA_def_property_enum_items(prop, color_space_items);
RNA_def_property_enum_funcs(prop,
"rna_NodeConvertColorSpace_to_color_space_get",
"rna_NodeConvertColorSpace_to_color_space_set",
"rna_NodeConvertColorSpace_color_space_itemf");
RNA_def_property_ui_text(prop, "To", "Color space of the output image");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_color_spill(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -62,6 +62,7 @@ void register_node_type_cmp_alphaover(void);
void register_node_type_cmp_zcombine(void);
void register_node_type_cmp_colorbalance(void);
void register_node_type_cmp_huecorrect(void);
void register_node_type_cmp_convert_color_space(void);
void register_node_type_cmp_normal(void);
void register_node_type_cmp_curve_vec(void);

View File

@ -227,6 +227,7 @@ DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOIS
DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" )
DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" )
DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" )
DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )

View File

@ -51,6 +51,7 @@ set(SRC
nodes/node_composite_color_spill.cc
nodes/node_composite_colorbalance.cc
nodes/node_composite_colorcorrection.cc
nodes/node_composite_convert_color_space.cc
nodes/node_composite_common.cc
nodes/node_composite_composite.cc
nodes/node_composite_cornerpin.cc

View File

@ -0,0 +1,82 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2021 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup cmpnodes
*/
#include "node_composite_util.hh"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "IMB_colormanagement.h"
namespace blender::nodes {
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
}
} // namespace blender::nodes
static void node_composit_init_convert_colorspace(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeConvertColorSpace *ncs = static_cast<NodeConvertColorSpace *>(
MEM_callocN(sizeof(NodeConvertColorSpace), "node colorspace"));
const char *first_colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_SCENE_LINEAR);
if (first_colorspace && first_colorspace[0]) {
STRNCPY(ncs->from_color_space, first_colorspace);
STRNCPY(ncs->to_color_space, first_colorspace);
}
else {
ncs->from_color_space[0] = 0;
ncs->to_color_space[0] = 0;
}
node->storage = ncs;
}
static void node_composit_buts_convert_colorspace(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "from_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
void register_node_type_cmp_convert_color_space(void)
{
static bNodeType ntype;
cmp_node_type_base(
&ntype, CMP_NODE_CONVERT_COLOR_SPACE, "Convert Colorspace", NODE_CLASS_CONVERTER);
ntype.declare = blender::nodes::CMP_NODE_CONVERT_COLOR_SPACE_declare;
ntype.draw_buttons = node_composit_buts_convert_colorspace;
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, node_composit_init_convert_colorspace);
node_type_storage(
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
}