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