Port the security fixes to Windows. Thanks to PXEGeek for much patience and testing...
[synfig.git] / synfig-core / trunk / src / modules / mod_imagemagick / trgt_imagemagick.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file trgt_imagemagick.cpp
3 **      \brief ppm Target Module
4 **
5 **      \legal
6 ** $Id$
7 **
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 **
21 ** === N O T E S ===========================================================
22 **
23 ** ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #define SYNFIG_TARGET
28
29 #ifdef USING_PCH
30 #       include "pch.h"
31 #else
32 #ifdef HAVE_CONFIG_H
33 #       include <config.h>
34 #endif
35
36 #include <ETL/stringf>
37 #include "trgt_imagemagick.h"
38 #include <stdio.h>
39 #include <sys/types.h>
40 #if HAVE_SYS_WAIT_H
41  #include <sys/wait.h>
42 #endif
43 #if HAVE_IO_H
44  #include <io.h>
45 #endif
46 #if HAVE_PROCESS_H
47  #include <process.h>
48 #endif
49 #if HAVE_FCNTL_H
50  #include <fcntl.h>
51 #endif
52 #include <unistd.h>
53 #include <algorithm>
54 #include <functional>
55 #include <ETL/clock>
56 #include <ETL/misc>
57
58 #endif
59
60 /* === M A C R O S ========================================================= */
61
62 using namespace synfig;
63 using namespace std;
64 using namespace etl;
65
66 #if defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID)
67  #define UNIX_PIPE_TO_PROCESSES
68 #elif defined(HAVE__SPAWNLP) && defined(HAVE__PIPE) && defined(HAVE_CWAIT)
69  #define WIN32_PIPE_TO_PROCESSES
70 #endif
71
72 /* === G L O B A L S ======================================================= */
73
74 SYNFIG_TARGET_INIT(imagemagick_trgt);
75 SYNFIG_TARGET_SET_NAME(imagemagick_trgt,"imagemagick");
76 SYNFIG_TARGET_SET_EXT(imagemagick_trgt,"miff");
77 SYNFIG_TARGET_SET_VERSION(imagemagick_trgt,"0.1");
78 SYNFIG_TARGET_SET_CVS_ID(imagemagick_trgt,"$Id$");
79
80 /* === M E T H O D S ======================================================= */
81
82 imagemagick_trgt::imagemagick_trgt(const char *Filename)
83 {
84         pid=-1;
85         file=NULL;
86         filename=Filename;
87         multi_image=false;
88         buffer=NULL;
89         color_buffer=0;
90 }
91
92 imagemagick_trgt::~imagemagick_trgt()
93 {
94         if(file){
95                 fclose(file);
96                 int status;
97 #if defined(WIN32_PIPE_TO_PROCESSES)
98                 cwait(&status,pid,0);
99 #elif defined(UNIX_PIPE_TO_PROCESSES)
100                 waitpid(pid,&status,0);
101 #endif
102         }
103         file=NULL;
104         delete [] buffer;
105         delete [] color_buffer;
106 }
107
108 bool
109 imagemagick_trgt::set_rend_desc(RendDesc *given_desc)
110 {
111         if(filename_extension(filename) == ".xpm")
112                 pf=PF_RGB;
113         else
114                 pf=PF_RGB|PF_A;
115
116         desc=*given_desc;
117         return true;
118 }
119
120 bool
121 imagemagick_trgt::init()
122 {
123         imagecount=desc.get_frame_start();
124         if(desc.get_frame_end()-desc.get_frame_start()>0)
125                 multi_image=true;
126
127         delete [] buffer;
128         buffer=new unsigned char[channels(pf)*desc.get_w()];
129         delete [] color_buffer;
130         color_buffer=new Color[desc.get_w()];
131         return true;
132 }
133
134 void
135 imagemagick_trgt::end_frame()
136 {
137         if(file)
138         {
139                 fputc(0,file);
140                 fflush(file);
141                 fclose(file);
142                 int status;
143 #if defined(WIN32_PIPE_TO_PROCESSES)
144                 cwait(&status,pid,0);
145 #elif defined(UNIX_PIPE_TO_PROCESSES)
146                 waitpid(pid,&status,0);
147 #endif
148         }
149         file=NULL;
150         imagecount++;
151 }
152
153 bool
154 imagemagick_trgt::start_frame(synfig::ProgressCallback *cb)
155 {
156         const char *msg=_("Unable to open pipe to imagemagick's convert utility");
157
158         string newfilename;
159
160         if (multi_image)
161                 newfilename = (filename_sans_extension(filename) +
162                                            etl::strprintf(".%04d",imagecount) +
163                                            filename_extension(filename));
164         else
165                 newfilename = filename;
166
167 #if defined(WIN32_PIPE_TO_PROCESSES)
168
169         int p[2];
170         int stdin_fileno, stdout_fileno;
171
172         if(_pipe(p, 512, O_BINARY | O_NOINHERIT) < 0) {
173                 if(cb) cb->error(N_(msg));
174                 else synfig::error(N_(msg));
175                 return false;
176         }
177
178         // Save stdin/stdout so we can restore them later
179         stdin_fileno  = _dup(_fileno(stdin));
180         stdout_fileno = _dup(_fileno(stdout));
181
182         // convert should read from the pipe
183         if(_dup2(p[0], _fileno(stdin)) != 0) {
184                 if(cb) cb->error(N_(msg));
185                 else synfig::error(N_(msg));
186                 return false;
187         }
188
189         /*
190         convert accepts the output filename on the command-line
191         if(_dup2(_fileno(output), _fileno(stdout)) != 0) {
192                 if(cb) cb->error(N_(msg));
193                 else synfig::error(N_(msg));
194                 return false;
195         }
196         */
197
198         pid = _spawnlp(_P_NOWAIT, "convert", "convert",
199                 "-depth", "8",
200                 "-size", strprintf("%dx%d", desc.get_w(), desc.get_h()).c_str(),
201                 ((channels(pf) == 4) ? "rgba:-[0]" : "rgb:-[0]"),
202                 "-density", strprintf("%dx%d", round_to_int(desc.get_x_res()/39.3700787402), round_to_int(desc.get_y_res()/39.3700787402)).c_str(),
203                 newfilename.c_str(),
204                 (const char *)NULL);
205
206         if( pid < 0) {
207                 if(cb) cb->error(N_(msg));
208                 else synfig::error(N_(msg));
209                 return false;
210         }
211
212         // Restore stdin/stdout
213         if(_dup2(stdin_fileno, _fileno(stdin)) != 0) {
214                 if(cb) cb->error(N_(msg));
215                 else synfig::error(N_(msg));
216                 return false;
217         }
218         if(_dup2(stdout_fileno, _fileno(stdout)) != 0) {
219                 if(cb) cb->error(N_(msg));
220                 else synfig::error(N_(msg));
221                 return false;
222         }
223         close(stdin_fileno);
224         close(stdout_fileno);
225
226         // Close the pipe read end - convert uses it
227         close(p[0]);
228         
229         // We write data to the write end of the pipe
230         file = fdopen(p[1], "wb");
231
232 #elif defined(UNIX_PIPE_TO_PROCESSES)
233
234         int p[2];
235   
236         if (pipe(p)) {
237                 if(cb) cb->error(N_(msg));
238                 else synfig::error(N_(msg));
239                 return false;
240         };
241   
242         pid = fork();
243   
244         if (pid == -1) {
245                 if(cb) cb->error(N_(msg));
246                 else synfig::error(N_(msg));
247                 return false;
248         }
249         
250         if (pid == 0){
251                 // Child process
252                 // Close pipeout, not needed
253                 close(p[1]);
254                 // Dup pipeout to stdin
255                 if( dup2( p[0], STDIN_FILENO ) == -1 ){
256                         if(cb) cb->error(N_(msg));
257                         else synfig::error(N_(msg));
258                         return false;
259                 }
260                 // Close the unneeded pipeout
261                 close(p[0]);
262                 execlp("convert", "convert",
263                         "-depth", "8",
264                         "-size", strprintf("%dx%d", desc.get_w(), desc.get_h()).c_str(),
265                         ((channels(pf) == 4) ? "rgba:-[0]" : "rgb:-[0]"),
266                         "-density", strprintf("%dx%d", round_to_int(desc.get_x_res()/39.3700787402), round_to_int(desc.get_y_res()/39.3700787402)).c_str(),
267                         newfilename.c_str(),
268                         (const char *)NULL);
269                 // We should never reach here unless the exec failed
270                 if(cb) cb->error(N_(msg));
271                 else synfig::error(N_(msg));
272                 return false;
273         } else {
274                 // Parent process
275                 // Close pipein, not needed
276                 close(p[0]);
277                 // Save pipeout to file handle, will write to it later
278                 file = fdopen(p[1], "wb");
279         }
280
281 #else
282         #error There are no known APIs for creating child processes
283 #endif
284
285         if(!file)
286         {
287                 if(cb)cb->error(N_(msg));
288                 else synfig::error(N_(msg));
289                 return false;
290         }
291
292         //etl::yield();
293
294         return true;
295 }
296
297 Color *
298 imagemagick_trgt::start_scanline(int /*scanline*/)
299 {
300         return color_buffer;
301 }
302
303 bool
304 imagemagick_trgt::end_scanline(void)
305 {
306         if(!file)
307                 return false;
308
309         convert_color_format(buffer, color_buffer, desc.get_w(), pf, gamma());
310
311         if(!fwrite(buffer,channels(pf),desc.get_w(),file))
312                 return false;
313
314         return true;
315 }