1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2006 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup cmpnodes
22 */
23
24 #include "BLI_string_utils.h"
25 #include "BLI_utildefines.h"
26 #include <string.h>
27
28 #include "BKE_context.h"
29
30 #include "RNA_access.h"
31
32 #include "node_composite_util.h"
33
34 #include "intern/openexr/openexr_multi.h"
35
36 /* **************** OUTPUT FILE ******************** */
37
38 /* find unique path */
unique_path_unique_check(void * arg,const char * name)39 static bool unique_path_unique_check(void *arg, const char *name)
40 {
41 struct {
42 ListBase *lb;
43 bNodeSocket *sock;
44 } *data = arg;
45 bNodeSocket *sock;
46 for (sock = data->lb->first; sock; sock = sock->next) {
47 if (sock != data->sock) {
48 NodeImageMultiFileSocket *sockdata = sock->storage;
49 if (STREQ(sockdata->path, name)) {
50 return true;
51 }
52 }
53 }
54 return false;
55 }
ntreeCompositOutputFileUniquePath(ListBase * list,bNodeSocket * sock,const char defname[],char delim)56 void ntreeCompositOutputFileUniquePath(ListBase *list,
57 bNodeSocket *sock,
58 const char defname[],
59 char delim)
60 {
61 NodeImageMultiFileSocket *sockdata;
62 struct {
63 ListBase *lb;
64 bNodeSocket *sock;
65 } data;
66 data.lb = list;
67 data.sock = sock;
68
69 /* See if we are given an empty string */
70 if (ELEM(NULL, sock, defname)) {
71 return;
72 }
73
74 sockdata = sock->storage;
75 BLI_uniquename_cb(
76 unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path));
77 }
78
79 /* find unique EXR layer */
unique_layer_unique_check(void * arg,const char * name)80 static bool unique_layer_unique_check(void *arg, const char *name)
81 {
82 struct {
83 ListBase *lb;
84 bNodeSocket *sock;
85 } *data = arg;
86 bNodeSocket *sock;
87 for (sock = data->lb->first; sock; sock = sock->next) {
88 if (sock != data->sock) {
89 NodeImageMultiFileSocket *sockdata = sock->storage;
90 if (STREQ(sockdata->layer, name)) {
91 return true;
92 }
93 }
94 }
95 return false;
96 }
ntreeCompositOutputFileUniqueLayer(ListBase * list,bNodeSocket * sock,const char defname[],char delim)97 void ntreeCompositOutputFileUniqueLayer(ListBase *list,
98 bNodeSocket *sock,
99 const char defname[],
100 char delim)
101 {
102 NodeImageMultiFileSocket *sockdata;
103 struct {
104 ListBase *lb;
105 bNodeSocket *sock;
106 } data;
107 data.lb = list;
108 data.sock = sock;
109
110 /* See if we are given an empty string */
111 if (ELEM(NULL, sock, defname)) {
112 return;
113 }
114
115 sockdata = sock->storage;
116 BLI_uniquename_cb(
117 unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer));
118 }
119
ntreeCompositOutputFileAddSocket(bNodeTree * ntree,bNode * node,const char * name,ImageFormatData * im_format)120 bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
121 bNode *node,
122 const char *name,
123 ImageFormatData *im_format)
124 {
125 NodeImageMultiFile *nimf = node->storage;
126 bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name);
127
128 /* create format data for the input socket */
129 NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket),
130 "socket image format");
131 sock->storage = sockdata;
132
133 BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
134 ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
135 BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
136 ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
137
138 if (im_format) {
139 sockdata->format = *im_format;
140 if (BKE_imtype_is_movie(sockdata->format.imtype)) {
141 sockdata->format.imtype = R_IMF_IMTYPE_OPENEXR;
142 }
143 }
144 else {
145 BKE_imformat_defaults(&sockdata->format);
146 }
147 /* use node data format by default */
148 sockdata->use_node_format = true;
149
150 nimf->active_input = BLI_findindex(&node->inputs, sock);
151
152 return sock;
153 }
154
ntreeCompositOutputFileRemoveActiveSocket(bNodeTree * ntree,bNode * node)155 int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
156 {
157 NodeImageMultiFile *nimf = node->storage;
158 bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
159 int totinputs = BLI_listbase_count(&node->inputs);
160
161 if (!sock) {
162 return 0;
163 }
164
165 if (nimf->active_input == totinputs - 1) {
166 --nimf->active_input;
167 }
168
169 /* free format data */
170 MEM_freeN(sock->storage);
171
172 nodeRemoveSocket(ntree, node, sock);
173 return 1;
174 }
175
ntreeCompositOutputFileSetPath(bNode * node,bNodeSocket * sock,const char * name)176 void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name)
177 {
178 NodeImageMultiFileSocket *sockdata = sock->storage;
179 BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
180 ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
181 }
182
ntreeCompositOutputFileSetLayer(bNode * node,bNodeSocket * sock,const char * name)183 void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name)
184 {
185 NodeImageMultiFileSocket *sockdata = sock->storage;
186 BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
187 ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
188 }
189
190 /* XXX uses initfunc_api callback, regular initfunc does not support context yet */
init_output_file(const bContext * C,PointerRNA * ptr)191 static void init_output_file(const bContext *C, PointerRNA *ptr)
192 {
193 Scene *scene = CTX_data_scene(C);
194 bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
195 bNode *node = ptr->data;
196 NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
197 ImageFormatData *format = NULL;
198 node->storage = nimf;
199
200 if (scene) {
201 RenderData *rd = &scene->r;
202
203 BLI_strncpy(nimf->base_path, rd->pic, sizeof(nimf->base_path));
204 nimf->format = rd->im_format;
205 if (BKE_imtype_is_movie(nimf->format.imtype)) {
206 nimf->format.imtype = R_IMF_IMTYPE_OPENEXR;
207 }
208
209 format = &nimf->format;
210 }
211 else {
212 BKE_imformat_defaults(&nimf->format);
213 }
214
215 /* add one socket by default */
216 ntreeCompositOutputFileAddSocket(ntree, node, "Image", format);
217 }
218
free_output_file(bNode * node)219 static void free_output_file(bNode *node)
220 {
221 bNodeSocket *sock;
222
223 /* free storage data in sockets */
224 for (sock = node->inputs.first; sock; sock = sock->next) {
225 MEM_freeN(sock->storage);
226 }
227
228 MEM_freeN(node->storage);
229 }
230
copy_output_file(bNodeTree * UNUSED (dest_ntree),bNode * dest_node,const bNode * src_node)231 static void copy_output_file(bNodeTree *UNUSED(dest_ntree),
232 bNode *dest_node,
233 const bNode *src_node)
234 {
235 bNodeSocket *src_sock, *dest_sock;
236
237 dest_node->storage = MEM_dupallocN(src_node->storage);
238
239 /* duplicate storage data in sockets */
240 for (src_sock = src_node->inputs.first, dest_sock = dest_node->inputs.first;
241 src_sock && dest_sock;
242 src_sock = src_sock->next, dest_sock = dest_sock->next) {
243 dest_sock->storage = MEM_dupallocN(src_sock->storage);
244 }
245 }
246
update_output_file(bNodeTree * ntree,bNode * node)247 static void update_output_file(bNodeTree *ntree, bNode *node)
248 {
249 bNodeSocket *sock, *sock_next;
250 PointerRNA ptr;
251
252 /* XXX fix for T36706: remove invalid sockets added with bpy API.
253 * This is not ideal, but prevents crashes from missing storage.
254 * FileOutput node needs a redesign to support this properly.
255 */
256 for (sock = node->inputs.first; sock; sock = sock_next) {
257 sock_next = sock->next;
258 if (sock->storage == NULL) {
259 nodeRemoveSocket(ntree, node, sock);
260 }
261 }
262 for (sock = node->outputs.first; sock; sock = sock_next) {
263 sock_next = sock->next;
264 nodeRemoveSocket(ntree, node, sock);
265 }
266
267 cmp_node_update_default(ntree, node);
268
269 /* automatically update the socket type based on linked input */
270 for (sock = node->inputs.first; sock; sock = sock->next) {
271 if (sock->link) {
272 RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
273 RNA_enum_set(&ptr, "type", sock->link->fromsock->type);
274 }
275 }
276 }
277
register_node_type_cmp_output_file(void)278 void register_node_type_cmp_output_file(void)
279 {
280 static bNodeType ntype;
281
282 cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW);
283 node_type_socket_templates(&ntype, NULL, NULL);
284 ntype.initfunc_api = init_output_file;
285 node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file);
286 node_type_update(&ntype, update_output_file);
287
288 nodeRegisterType(&ntype);
289 }
290