Blender  V3.3
node_composite_output_file.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2006 Blender Foundation. All rights reserved. */
3 
8 #include <cstring>
9 
10 #include "BLI_string_utf8.h"
11 #include "BLI_string_utils.h"
12 #include "BLI_utildefines.h"
13 
14 #include "BKE_context.h"
15 #include "BKE_image_format.h"
16 
17 #include "RNA_access.h"
18 #include "RNA_prototypes.h"
19 
20 #include "UI_interface.h"
21 #include "UI_resources.h"
22 
23 #include "WM_api.h"
24 
25 #include "IMB_openexr.h"
26 
27 #include "node_composite_util.hh"
28 
29 /* **************** OUTPUT FILE ******************** */
30 
31 /* find unique path */
32 static bool unique_path_unique_check(void *arg, const char *name)
33 {
34  struct Args {
35  ListBase *lb;
36  bNodeSocket *sock;
37  };
38  Args *data = (Args *)arg;
39 
40  LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) {
41  if (sock != data->sock) {
42  NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
43  if (STREQ(sockdata->path, name)) {
44  return true;
45  }
46  }
47  }
48  return false;
49 }
51  bNodeSocket *sock,
52  const char defname[],
53  char delim)
54 {
55  NodeImageMultiFileSocket *sockdata;
56  struct {
57  ListBase *lb;
58  bNodeSocket *sock;
59  } data;
60  data.lb = list;
61  data.sock = sock;
62 
63  /* See if we are given an empty string */
64  if (ELEM(nullptr, sock, defname)) {
65  return;
66  }
67 
68  sockdata = (NodeImageMultiFileSocket *)sock->storage;
70  unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path));
71 }
72 
73 /* find unique EXR layer */
74 static bool unique_layer_unique_check(void *arg, const char *name)
75 {
76  struct Args {
77  ListBase *lb;
78  bNodeSocket *sock;
79  };
80  Args *data = (Args *)arg;
81 
82  LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) {
83  if (sock != data->sock) {
84  NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
85  if (STREQ(sockdata->layer, name)) {
86  return true;
87  }
88  }
89  }
90  return false;
91 }
93  bNodeSocket *sock,
94  const char defname[],
95  char delim)
96 {
97  struct {
98  ListBase *lb;
99  bNodeSocket *sock;
100  } data;
101  data.lb = list;
102  data.sock = sock;
103 
104  /* See if we are given an empty string */
105  if (ELEM(nullptr, sock, defname)) {
106  return;
107  }
108 
111  unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer));
112 }
113 
115  bNode *node,
116  const char *name,
117  const ImageFormatData *im_format)
118 {
119  NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
121  ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name);
122 
123  /* create format data for the input socket */
124  NodeImageMultiFileSocket *sockdata = MEM_cnew<NodeImageMultiFileSocket>(__func__);
125  sock->storage = sockdata;
126 
127  BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
128  ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
129  BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
130  ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
131 
132  if (im_format) {
133  BKE_image_format_copy(&sockdata->format, im_format);
135  if (BKE_imtype_is_movie(sockdata->format.imtype)) {
136  sockdata->format.imtype = R_IMF_IMTYPE_OPENEXR;
137  }
138  }
139  else {
140  BKE_image_format_init(&sockdata->format, false);
141  }
142  /* use node data format by default */
143  sockdata->use_node_format = true;
144  sockdata->save_as_render = true;
145 
146  nimf->active_input = BLI_findindex(&node->inputs, sock);
147 
148  return sock;
149 }
150 
152 {
153  NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
154  bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input);
155  int totinputs = BLI_listbase_count(&node->inputs);
156 
157  if (!sock) {
158  return 0;
159  }
160 
161  if (nimf->active_input == totinputs - 1) {
162  --nimf->active_input;
163  }
164 
165  /* free format data */
166  MEM_freeN(sock->storage);
167 
168  nodeRemoveSocket(ntree, node, sock);
169  return 1;
170 }
171 
172 void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name)
173 {
175  BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
176  ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
177 }
178 
179 void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name)
180 {
182  BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
183  ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
184 }
185 
187 
188 /* XXX uses initfunc_api callback, regular initfunc does not support context yet */
189 static void init_output_file(const bContext *C, PointerRNA *ptr)
190 {
193  bNode *node = (bNode *)ptr->data;
194  NodeImageMultiFile *nimf = MEM_cnew<NodeImageMultiFile>(__func__);
195  ImageFormatData *format = nullptr;
196  node->storage = nimf;
197 
198  if (scene) {
199  RenderData *rd = &scene->r;
200 
201  BLI_strncpy(nimf->base_path, rd->pic, sizeof(nimf->base_path));
202  BKE_image_format_copy(&nimf->format, &rd->im_format);
204  if (BKE_imtype_is_movie(nimf->format.imtype)) {
206  }
207 
208  format = &nimf->format;
209  }
210  else {
211  BKE_image_format_init(&nimf->format, false);
212  }
213 
214  /* add one socket by default */
216 }
217 
219 {
220  /* free storage data in sockets */
221  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
222  NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage;
223  BKE_image_format_free(&sockdata->format);
224  MEM_freeN(sock->storage);
225  }
226 
227  NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
229  MEM_freeN(node->storage);
230 }
231 
232 static void copy_output_file(bNodeTree *UNUSED(dest_ntree),
233  bNode *dest_node,
234  const bNode *src_node)
235 {
236  bNodeSocket *src_sock, *dest_sock;
237 
238  dest_node->storage = MEM_dupallocN(src_node->storage);
239  NodeImageMultiFile *dest_nimf = (NodeImageMultiFile *)dest_node->storage;
240  NodeImageMultiFile *src_nimf = (NodeImageMultiFile *)src_node->storage;
241  BKE_image_format_copy(&dest_nimf->format, &src_nimf->format);
242 
243  /* duplicate storage data in sockets */
244  for (src_sock = (bNodeSocket *)src_node->inputs.first,
245  dest_sock = (bNodeSocket *)dest_node->inputs.first;
246  src_sock && dest_sock;
247  src_sock = src_sock->next, dest_sock = (bNodeSocket *)dest_sock->next) {
248  dest_sock->storage = MEM_dupallocN(src_sock->storage);
249  NodeImageMultiFileSocket *dest_sockdata = (NodeImageMultiFileSocket *)dest_sock->storage;
250  NodeImageMultiFileSocket *src_sockdata = (NodeImageMultiFileSocket *)src_sock->storage;
251  BKE_image_format_copy(&dest_sockdata->format, &src_sockdata->format);
252  }
253 }
254 
256 {
257  PointerRNA ptr;
258 
259  /* XXX fix for T36706: remove invalid sockets added with bpy API.
260  * This is not ideal, but prevents crashes from missing storage.
261  * FileOutput node needs a redesign to support this properly.
262  */
263  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
264  if (sock->storage == nullptr) {
265  nodeRemoveSocket(ntree, node, sock);
266  }
267  }
268  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
269  nodeRemoveSocket(ntree, node, sock);
270  }
271 
273 
274  /* automatically update the socket type based on linked input */
275  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
276  if (sock->link) {
277  RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
278  RNA_enum_set(&ptr, "type", sock->link->fromsock->type);
279  }
280  }
281 }
282 
284 {
285  PointerRNA imfptr = RNA_pointer_get(ptr, "format");
286  const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER;
287 
288  if (multilayer) {
289  uiItemL(layout, IFACE_("Path:"), ICON_NONE);
290  }
291  else {
292  uiItemL(layout, IFACE_("Base Path:"), ICON_NONE);
293  }
294  uiItemR(layout, ptr, "base_path", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
295 }
296 
298 {
300  PointerRNA imfptr = RNA_pointer_get(ptr, "format");
301  PointerRNA active_input_ptr, op_ptr;
302  uiLayout *row, *col;
303  const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER;
304  const bool is_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR;
305  const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
306 
308  uiTemplateImageSettings(layout, &imfptr, true);
309 
310  /* disable stereo output for multilayer, too much work for something that no one will use */
311  /* if someone asks for that we can implement it */
312  if (is_multiview) {
313  uiTemplateImageFormatViews(layout, &imfptr, nullptr);
314  }
315 
316  uiItemS(layout);
317 
318  uiItemO(layout, IFACE_("Add Input"), ICON_ADD, "NODE_OT_output_file_add_socket");
319 
320  row = uiLayoutRow(layout, false);
321  col = uiLayoutColumn(row, true);
322 
323  const int active_index = RNA_int_get(ptr, "active_input_index");
324  /* using different collection properties if multilayer format is enabled */
325  if (multilayer) {
327  C,
328  "UI_UL_list",
329  "file_output_node",
330  ptr,
331  "layer_slots",
332  ptr,
333  "active_input_index",
334  nullptr,
335  0,
336  0,
337  0,
338  0,
341  ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr);
342  }
343  else {
345  C,
346  "UI_UL_list",
347  "file_output_node",
348  ptr,
349  "file_slots",
350  ptr,
351  "active_input_index",
352  nullptr,
353  0,
354  0,
355  0,
356  0,
359  ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr);
360  }
361  /* XXX collection lookup does not return the ID part of the pointer,
362  * setting this manually here */
363  active_input_ptr.owner_id = ptr->owner_id;
364 
365  col = uiLayoutColumn(row, true);
366  wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false);
367  uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
368  RNA_enum_set(&op_ptr, "direction", 1);
369  uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
370  RNA_enum_set(&op_ptr, "direction", 2);
371 
372  if (active_input_ptr.data) {
373  if (multilayer) {
374  col = uiLayoutColumn(layout, true);
375 
376  uiItemL(col, IFACE_("Layer:"), ICON_NONE);
377  row = uiLayoutRow(col, false);
378  uiItemR(row, &active_input_ptr, "name", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
379  uiItemFullO(row,
380  "NODE_OT_output_file_remove_active_socket",
381  "",
382  ICON_X,
383  nullptr,
386  nullptr);
387  }
388  else {
389  col = uiLayoutColumn(layout, true);
390 
391  uiItemL(col, IFACE_("File Subpath:"), ICON_NONE);
392  row = uiLayoutRow(col, false);
393  uiItemR(row, &active_input_ptr, "path", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
394  uiItemFullO(row,
395  "NODE_OT_output_file_remove_active_socket",
396  "",
397  ICON_X,
398  nullptr,
401  nullptr);
402 
403  /* format details for individual files */
404  imfptr = RNA_pointer_get(&active_input_ptr, "format");
405 
406  col = uiLayoutColumn(layout, true);
407  uiItemL(col, IFACE_("Format:"), ICON_NONE);
408  uiItemR(col,
409  &active_input_ptr,
410  "use_node_format",
412  nullptr,
413  ICON_NONE);
414 
415  const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR;
416  const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format");
417 
418  if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) {
419  uiItemR(col,
420  &active_input_ptr,
421  "save_as_render",
423  nullptr,
424  ICON_NONE);
425  }
426 
427  if (!use_node_format) {
428  const bool use_color_management = RNA_boolean_get(&active_input_ptr, "save_as_render");
429 
430  col = uiLayoutColumn(layout, false);
431  uiTemplateImageSettings(col, &imfptr, use_color_management);
432 
433  if (is_multiview) {
434  col = uiLayoutColumn(layout, false);
435  uiTemplateImageFormatViews(col, &imfptr, nullptr);
436  }
437  }
438  }
439  }
440 }
441 
442 } // namespace blender::nodes::node_composite_output_file_cc
443 
445 {
447 
448  static bNodeType ntype;
449 
454  ntype.flag |= NODE_PREVIEW;
456  &ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
458 
459  nodeRegisterType(&ntype);
460 }
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
void BKE_image_format_free(struct ImageFormatData *imf)
Definition: image_format.cc:52
void BKE_image_format_copy(struct ImageFormatData *imf_dst, const struct ImageFormatData *imf_src)
void BKE_image_format_init(struct ImageFormatData *imf, const bool render)
Definition: image_format.cc:26
bool BKE_imtype_is_movie(char imtype)
#define NODE_CLASS_OUTPUT
Definition: BKE_node.h:346
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
struct bNodeSocket * nodeAddStaticSocket(struct bNodeTree *ntree, struct bNode *node, eNodeSocketInOut in_out, int type, int subtype, const char *identifier, const char *name)
Definition: node.cc:1897
#define CMP_NODE_OUTPUT_FILE
Definition: BKE_node.h:1218
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
void nodeRemoveSocket(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock)
Definition: node.cc:1933
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(1
bool BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_len)
Definition: string_utils.c:233
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
#define IFACE_(msgid)
@ SOCK_IN
#define NODE_PREVIEW
@ SOCK_RGBA
#define R_MULTIVIEW
#define R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE
#define R_IMF_IMTYPE_MULTILAYER
#define R_IMF_IMTYPE_OPENEXR
@ PROP_NONE
Definition: RNA_types.h:126
#define C
Definition: RandGen.cpp:25
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemL(uiLayout *layout, const char *name, int icon)
void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, bool color_management)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiItemFullO_ptr(uiLayout *layout, struct wmOperatorType *ot, const char *name, int icon, struct IDProperty *properties, wmOperatorCallContext context, int flag, struct PointerRNA *r_opptr)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
@ UI_ITEM_R_ICON_ONLY
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
void uiItemFullO(uiLayout *layout, const char *opname, const char *name, int icon, struct IDProperty *properties, wmOperatorCallContext context, int flag, struct PointerRNA *r_opptr)
void uiTemplateList(uiLayout *layout, struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, const char *propname, struct PointerRNA *active_dataptr, const char *active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags)
@ UI_TEMPLATE_LIST_FLAG_NONE
void uiTemplateImageFormatViews(uiLayout *layout, struct PointerRNA *imfptr, struct PointerRNA *ptr)
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:201
@ WM_OP_EXEC_DEFAULT
Definition: WM_types.h:208
OperationNode * node
Scene scene
bNodeTree * ntree
uint col
format
Definition: logImageCore.h:38
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
static void init_output_file(const bContext *C, PointerRNA *ptr)
static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void copy_output_file(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
static void update_output_file(bNodeTree *ntree, bNode *node)
static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
void ntreeCompositOutputFileUniqueLayer(ListBase *list, bNodeSocket *sock, const char defname[], char delim)
void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name)
void register_node_type_cmp_output_file()
int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
bNodeSocket * ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, const ImageFormatData *im_format)
static bool unique_path_unique_check(void *arg, const char *name)
void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name)
static bool unique_layer_unique_check(void *arg, const char *name)
void ntreeCompositOutputFileUniquePath(ListBase *list, bNodeSocket *sock, const char defname[], char delim)
void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:136
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5167
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
int RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
Definition: rna_access.c:4097
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:5015
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
Definition: DNA_ID.h:368
void * first
Definition: DNA_listBase.h:31
ImageFormatData format
struct StructRNA * type
Definition: RNA_types.h:37
void * data
Definition: RNA_types.h:38
struct ID * owner_id
Definition: RNA_types.h:36
struct ImageFormatData im_format
char pic[1024]
struct RenderData r
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:226
short flag
Definition: BKE_node.h:236
void(* initfunc_api)(const struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:279
void(* draw_buttons_ex)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:246
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
ListBase inputs
void * storage
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)