34 : data(data), data_type(data_type), original_data_type(original_data_type)
55 return static_cast<float *
>(
56 MEM_malloc_arrayN(
size_t(width) * height,
sizeof(
float) * size,
"File Output Buffer."));
62 : context_(context), node_data_(node_data), file_output_inputs_(inputs)
76 for (
int i = 0; i < file_output_inputs_.size(); i++) {
79 if (!input.image_input) {
83 input.image_input->get_width(), input.image_input->get_height(), input.data_type);
91 for (
int i = 0; i < file_output_inputs_.size(); i++) {
93 if (!input.output_buffer) {
99 input.output_buffer, channels_count, inputs[i]->get_width(), inputs[i]->get_height());
100 output_buf.
copy_from(inputs[i], inputs[i]->get_rect(), 0, inputs[i]->get_num_channels(), 0);
107 std::unique_ptr<MetaData> meta_data = input.image_input->get_meta_data();
114 BLI_strnlen(input.data->layer,
sizeof(input.data->layer))));
115 meta_data->replace_hash_neutral_cryptomatte_keys(layer_name);
116 meta_data->for_each_entry([&](
const std::string &key,
const std::string &value) {
127 if (size ==
int2(0)) {
131 if (input.output_buffer) {
138 if (is_multi_layer()) {
139 execute_multi_layer();
142 execute_single_layer();
150void FileOutputOperation::execute_single_layer()
154 if (!input.image_input || input.image_input->get_flags().is_constant_operation) {
157 if (input.output_buffer) {
164 get_single_layer_image_base_path(input.data->path, base_path);
171 const auto &
format = input.data->use_node_format ? node_data_->
format : input.data->format;
172 const bool save_as_render = input.data->use_node_format ? node_data_->
save_as_render :
173 input.data->save_as_render;
177 execute_single_layer_multi_view_exr(input,
format, base_path);
182 get_single_layer_image_path(base_path,
format, image_path);
184 const int2 size =
int2(input.image_input->get_width(), input.image_input->get_height());
186 image_path,
format, size, save_as_render);
188 add_view_for_input(file_output, input, context_->
get_view_name());
198void FileOutputOperation::execute_single_layer_multi_view_exr(
const FileOutputInput &input,
200 const char *base_path)
207 const char *path_view = has_views ?
"" : context_->
get_view_name();
208 get_multi_layer_exr_image_path(base_path, path_view, image_path);
210 const int2 size =
int2(input.image_input->get_width(), input.image_input->get_height());
212 image_path,
format, size,
true);
216 const char *view_name = has_views ? context_->
get_view_name() :
"";
217 file_output.add_view(view_name);
218 add_pass_for_input(file_output, input,
"", view_name);
227void FileOutputOperation::execute_multi_layer()
229 const bool store_views_in_single_file = is_multi_view_exr();
235 const char *write_view = store_views_in_single_file ?
"" :
view;
236 get_multi_layer_exr_image_path(get_base_path(), write_view, image_path);
241 image_path,
format, size,
true);
245 const char *pass_view = store_views_in_single_file ?
view :
"";
248 for (
const FileOutputInput &input : file_output_inputs_) {
249 if (!input.image_input) {
252 if (input.output_buffer) {
258 const char *pass_name = input.data->layer;
259 add_pass_for_input(file_output, input, pass_name, pass_view);
269 float *float3_image =
static_cast<float *
>(
270 MEM_malloc_arrayN(
size_t(size.x) * size.y,
sizeof(
float[3]),
"File Output Vector Buffer."));
273 for (const int64_t y : sub_y_range) {
274 for (const int64_t x : IndexRange(size.x)) {
275 for (int i = 0; i < 3; i++) {
276 const int pixel_index = y * size.x + x;
277 float3_image[pixel_index * 3 + i] = float4_image[pixel_index * 4 + i];
291 BLI_assert(input.image_input->get_flags().is_constant_operation);
293 switch (input.data_type) {
294 case DataType::Value: {
296 size_t(size.x) * size.y,
sizeof(
float),
"File Output Inflated Buffer."));
298 const float value = input.image_input->get_constant_value_default(0.0f);
300 for (const int64_t y : sub_y_range) {
301 for (const int64_t x : IndexRange(size.x)) {
302 buffer[y * size.x + x] = value;
308 case DataType::Color: {
310 size_t(size.x) * size.y,
sizeof(
float[4]),
"File Output Inflated Buffer."));
312 const float *value = input.image_input->get_constant_elem_default(
nullptr);
314 for (const int64_t y : sub_y_range) {
315 for (const int64_t x : IndexRange(size.x)) {
316 copy_v4_v4(buffer + ((y * size.x + x) * 4), value);
333void FileOutputOperation::add_pass_for_input(realtime_compositor::FileOutput &file_output,
334 const FileOutputInput &input,
335 const char *pass_name,
336 const char *view_name)
340 const int2 size = input.image_input->get_flags().is_constant_operation ?
341 int2(this->get_width(), this->get_height()) :
342 int2(input.image_input->get_width(), input.image_input->get_height());
346 float *buffer =
nullptr;
347 if (input.image_input->get_flags().is_constant_operation) {
352 buffer = input.output_buffer;
355 switch (input.original_data_type) {
356 case DataType::Color:
360 if (input.image_input->get_meta_data() &&
361 input.image_input->get_meta_data()->is_cryptomatte_layer())
363 file_output.add_pass(pass_name, view_name,
"rgba", buffer);
366 file_output.add_pass(pass_name, view_name,
"RGBA", buffer);
369 case DataType::Vector:
370 if (input.image_input->get_meta_data() && input.image_input->get_meta_data()->is_4d_vector) {
371 file_output.add_pass(pass_name, view_name,
"XYZW", buffer);
377 case DataType::Value:
378 file_output.add_pass(pass_name, view_name,
"V", buffer);
380 case DataType::Float2:
387void FileOutputOperation::add_view_for_input(realtime_compositor::FileOutput &file_output,
388 const FileOutputInput &input,
389 const char *view_name)
391 const int2 size =
int2(input.image_input->get_width(), input.image_input->get_height());
392 switch (input.original_data_type) {
393 case DataType::Color:
394 file_output.add_view(view_name, 4, input.output_buffer);
396 case DataType::Vector:
399 case DataType::Value:
400 file_output.add_view(view_name, 1, input.output_buffer);
402 case DataType::Float2:
409void FileOutputOperation::get_single_layer_image_base_path(
const char *base_name,
char *base_path)
420void FileOutputOperation::get_single_layer_image_path(
const char *base_path,
427 context_->get_framenumber(),
429 use_file_extension(),
434void FileOutputOperation::get_multi_layer_exr_image_path(
const char *base_path,
442 context_->get_framenumber(),
444 use_file_extension(),
449bool FileOutputOperation::is_multi_layer()
454const char *FileOutputOperation::get_base_path()
456 return node_data_->base_path;
459bool FileOutputOperation::use_file_extension()
461 return context_->get_render_data()->scemode &
R_EXTENSION;
464bool FileOutputOperation::is_multi_view_exr()
466 if (!is_multi_view_scene()) {
473bool FileOutputOperation::is_multi_view_scene()
475 return context_->get_render_data()->scemode &
R_MULTIVIEW;
const char * BKE_main_blendfile_path_from_global()
int BKE_scene_multiview_num_views_get(const RenderData *rd)
const char * BKE_scene_multiview_view_suffix_get(const RenderData *rd, const char *viewname)
#define BLI_assert_unreachable()
File and directory operations.
#define BLI_path_join(...)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
@ R_IMF_IMTYPE_MULTILAYER
Overall context of the compositor.
const RenderData * get_render_data() const
get the scene of the context
const char * get_view_name() const
get the active rendering view
realtime_compositor::RenderContext * get_render_context() const
get the render context
void init_execution() override
void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void deinit_execution() override
FileOutputOperation(const CompositorContext *context, const NodeImageMultiFile *node_data, Vector< FileOutputInput > inputs)
a MemoryBuffer contains access to the data
void copy_from(const MemoryBuffer *src, const rcti &area)
unsigned int get_height() const
SocketReader * get_input_socket_reader(unsigned int index)
unsigned int get_width() const
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void set_canvas_input_index(unsigned int index)
set the index of the input socket that will determine the canvas of this operation
void add_view(const char *view_name)
void add_meta_data(std::string key, std::string value)
FileOutput & get_file_output(std::string path, ImageFormatData format, int2 size, bool save_as_render)
DataType
possible data types for sockets
ResizeMode
Resize modes of inputsockets How are the input and working resolutions matched.
@ Vector
Vector data type.
@ Center
Center the input image to the center of the working area of the node, no resizing occurs.
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
void MEM_freeN(void *vmemh)
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name)
static constexpr unsigned int RESOLUTION_INPUT_ANY
static float * float4_to_float3_image(int2 size, float *float4_image)
static float * inflate_input(const FileOutputInput &input, const int2 size)
static float * initialize_buffer(uint width, uint height, DataType datatype)
static int get_channels_count(DataType datatype)
static void add_meta_data_for_input(realtime_compositor::FileOutput &file_output, const FileOutputInput &input)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< int32_t, 2 > int2
static blender::bke::bNodeSocketTemplate inputs[]