Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
270 lines
11 KiB
C++
270 lines
11 KiB
C++
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "COM_ImageNode.h"
|
|
#include "COM_ConvertOperation.h"
|
|
#include "COM_MultilayerImageOperation.h"
|
|
|
|
#include "COM_SetColorOperation.h"
|
|
#include "COM_SetValueOperation.h"
|
|
#include "COM_SetVectorOperation.h"
|
|
|
|
namespace blender::compositor {
|
|
|
|
ImageNode::ImageNode(bNode *editor_node) : Node(editor_node)
|
|
{
|
|
/* pass */
|
|
}
|
|
NodeOperation *ImageNode::do_multilayer_check(NodeConverter &converter,
|
|
RenderLayer *render_layer,
|
|
RenderPass *render_pass,
|
|
Image *image,
|
|
ImageUser *user,
|
|
int framenumber,
|
|
int outputsocket_index,
|
|
int view,
|
|
DataType datatype) const
|
|
{
|
|
NodeOutput *output_socket = this->get_output_socket(outputsocket_index);
|
|
MultilayerBaseOperation *operation = nullptr;
|
|
switch (datatype) {
|
|
case DataType::Value:
|
|
operation = new MultilayerValueOperation(render_layer, render_pass, view);
|
|
break;
|
|
case DataType::Vector:
|
|
operation = new MultilayerVectorOperation(render_layer, render_pass, view);
|
|
break;
|
|
case DataType::Color:
|
|
operation = new MultilayerColorOperation(render_layer, render_pass, view);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
operation->set_image(image);
|
|
operation->set_image_user(user);
|
|
operation->set_framenumber(framenumber);
|
|
|
|
converter.add_operation(operation);
|
|
converter.map_output_socket(output_socket, operation->get_output_socket());
|
|
|
|
return operation;
|
|
}
|
|
|
|
void ImageNode::convert_to_operations(NodeConverter &converter,
|
|
const CompositorContext &context) const
|
|
{
|
|
/** Image output */
|
|
NodeOutput *output_image = this->get_output_socket(0);
|
|
const bNode *editor_node = this->get_bnode();
|
|
Image *image = (Image *)editor_node->id;
|
|
ImageUser *imageuser = (ImageUser *)editor_node->storage;
|
|
int framenumber = context.get_framenumber();
|
|
bool output_straight_alpha = (editor_node->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0;
|
|
BKE_image_user_frame_calc(image, imageuser, context.get_framenumber());
|
|
/* Force a load, we assume #ImageUser index will be set OK anyway. */
|
|
if (image && image->type == IMA_TYPE_MULTILAYER) {
|
|
bool is_multilayer_ok = false;
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(image, imageuser, nullptr);
|
|
if (image->rr) {
|
|
RenderLayer *rl = (RenderLayer *)BLI_findlink(&image->rr->layers, imageuser->layer);
|
|
if (rl) {
|
|
is_multilayer_ok = true;
|
|
|
|
for (int64_t index = 0; index < outputs_.size(); index++) {
|
|
NodeOutput *socket = outputs_[index];
|
|
NodeOperation *operation = nullptr;
|
|
bNodeSocket *bnode_socket = socket->get_bnode_socket();
|
|
NodeImageLayer *storage = (NodeImageLayer *)bnode_socket->storage;
|
|
RenderPass *rpass = (RenderPass *)BLI_findstring(
|
|
&rl->passes, storage->pass_name, offsetof(RenderPass, name));
|
|
int view = 0;
|
|
|
|
if (STREQ(storage->pass_name, RE_PASSNAME_COMBINED) &&
|
|
STREQ(bnode_socket->name, "Alpha")) {
|
|
/* Alpha output is already handled with the associated combined output. */
|
|
continue;
|
|
}
|
|
|
|
/* returns the image view to use for the current active view */
|
|
if (BLI_listbase_count_at_most(&image->rr->views, 2) > 1) {
|
|
const int view_image = imageuser->view;
|
|
const bool is_allview = (view_image == 0); /* if view selected == All (0) */
|
|
|
|
if (is_allview) {
|
|
/* heuristic to match image name with scene names
|
|
* check if the view name exists in the image */
|
|
view = BLI_findstringindex(
|
|
&image->rr->views, context.get_view_name(), offsetof(RenderView, name));
|
|
if (view == -1) {
|
|
view = 0;
|
|
}
|
|
}
|
|
else {
|
|
view = view_image - 1;
|
|
}
|
|
}
|
|
|
|
if (rpass) {
|
|
switch (rpass->channels) {
|
|
case 1:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Value);
|
|
break;
|
|
/* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */
|
|
/* XXX any way to detect actual vector images? */
|
|
case 3:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Vector);
|
|
break;
|
|
case 4:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Color);
|
|
break;
|
|
default:
|
|
/* dummy operation is added below */
|
|
break;
|
|
}
|
|
if (index == 0 && operation) {
|
|
converter.add_preview(operation->get_output_socket());
|
|
}
|
|
if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnode_socket->flag & SOCK_UNAVAIL)) {
|
|
for (NodeOutput *alpha_socket : get_output_sockets()) {
|
|
bNodeSocket *bnode_alpha_socket = alpha_socket->get_bnode_socket();
|
|
if (!STREQ(bnode_alpha_socket->name, "Alpha")) {
|
|
continue;
|
|
}
|
|
NodeImageLayer *alpha_storage = (NodeImageLayer *)bnode_socket->storage;
|
|
if (!STREQ(alpha_storage->pass_name, RE_PASSNAME_COMBINED)) {
|
|
continue;
|
|
}
|
|
SeparateChannelOperation *separate_operation;
|
|
separate_operation = new SeparateChannelOperation();
|
|
separate_operation->set_channel(3);
|
|
converter.add_operation(separate_operation);
|
|
converter.add_link(operation->get_output_socket(),
|
|
separate_operation->get_input_socket(0));
|
|
converter.map_output_socket(alpha_socket, separate_operation->get_output_socket());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* In case we can't load the layer. */
|
|
if (operation == nullptr) {
|
|
converter.set_invalid_output(get_output_socket(index));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BKE_image_release_ibuf(image, ibuf, nullptr);
|
|
|
|
/* without this, multilayer that fail to load will crash blender #32490. */
|
|
if (is_multilayer_ok == false) {
|
|
for (NodeOutput *output : get_output_sockets()) {
|
|
converter.set_invalid_output(output);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
const int64_t number_of_outputs = get_output_sockets().size();
|
|
if (number_of_outputs > 0) {
|
|
ImageOperation *operation = new ImageOperation();
|
|
operation->set_image(image);
|
|
operation->set_image_user(imageuser);
|
|
operation->set_framenumber(framenumber);
|
|
operation->set_render_data(context.get_render_data());
|
|
operation->set_view_name(context.get_view_name());
|
|
converter.add_operation(operation);
|
|
|
|
if (output_straight_alpha) {
|
|
NodeOperation *alpha_convert_operation = new ConvertPremulToStraightOperation();
|
|
|
|
converter.add_operation(alpha_convert_operation);
|
|
converter.map_output_socket(output_image, alpha_convert_operation->get_output_socket());
|
|
converter.add_link(operation->get_output_socket(0),
|
|
alpha_convert_operation->get_input_socket(0));
|
|
}
|
|
else {
|
|
converter.map_output_socket(output_image, operation->get_output_socket());
|
|
}
|
|
|
|
converter.add_preview(operation->get_output_socket());
|
|
}
|
|
|
|
if (number_of_outputs > 1) {
|
|
NodeOutput *alpha_image = this->get_output_socket(1);
|
|
ImageAlphaOperation *alpha_operation = new ImageAlphaOperation();
|
|
alpha_operation->set_image(image);
|
|
alpha_operation->set_image_user(imageuser);
|
|
alpha_operation->set_framenumber(framenumber);
|
|
alpha_operation->set_render_data(context.get_render_data());
|
|
alpha_operation->set_view_name(context.get_view_name());
|
|
converter.add_operation(alpha_operation);
|
|
|
|
converter.map_output_socket(alpha_image, alpha_operation->get_output_socket());
|
|
}
|
|
else {
|
|
/* happens when unlinking image datablock from multilayer node */
|
|
for (int i = 2; i < number_of_outputs; i++) {
|
|
NodeOutput *output = this->get_output_socket(i);
|
|
NodeOperation *operation = nullptr;
|
|
switch (output->get_data_type()) {
|
|
case DataType::Value: {
|
|
SetValueOperation *valueoperation = new SetValueOperation();
|
|
valueoperation->set_value(0.0f);
|
|
operation = valueoperation;
|
|
break;
|
|
}
|
|
case DataType::Vector: {
|
|
SetVectorOperation *vectoroperation = new SetVectorOperation();
|
|
vectoroperation->setX(0.0f);
|
|
vectoroperation->setY(0.0f);
|
|
vectoroperation->setW(0.0f);
|
|
operation = vectoroperation;
|
|
break;
|
|
}
|
|
case DataType::Color: {
|
|
SetColorOperation *coloroperation = new SetColorOperation();
|
|
coloroperation->set_channel1(0.0f);
|
|
coloroperation->set_channel2(0.0f);
|
|
coloroperation->set_channel3(0.0f);
|
|
coloroperation->set_channel4(0.0f);
|
|
operation = coloroperation;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (operation) {
|
|
/* not supporting multiview for this generic case */
|
|
converter.add_operation(operation);
|
|
converter.map_output_socket(output, operation->get_output_socket());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace blender::compositor
|
|
|
|
} // namespace blender::compositor
|