baa5efed7b362981092abe7780e849d9efbdd805
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / svg / svg.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "clip.h"
22 #include "filexml.h"
23 #include "language.h"
24 #include "mainerror.h"
25 #include "svg.h"
26 #include "svgwin.h"
27 #include "overlayframe.inc"
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/mman.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38
39
40 REGISTER_PLUGIN(SvgMain)
41
42 SvgConfig::SvgConfig()
43 {
44         out_x = 0;   out_y = 0;
45         out_w = 640; out_h = 480;
46         dpi = 96;
47         strcpy(svg_file, "");
48         ms_time = 0;
49 }
50
51 int SvgConfig::equivalent(SvgConfig &that)
52 {
53         return EQUIV(dpi, that.dpi) &&
54                 EQUIV(out_x, that.out_x) &&
55                 EQUIV(out_y, that.out_y) &&
56                 EQUIV(out_w, that.out_w) &&
57                 EQUIV(out_h, that.out_h) &&
58                 !strcmp(svg_file, that.svg_file) &&
59                 ms_time != 0 && that.ms_time != 0 &&
60                 ms_time == that.ms_time;
61 }
62
63 void SvgConfig::copy_from(SvgConfig &that)
64 {
65         out_x = that.out_x;
66         out_y = that.out_y;
67         out_w = that.out_w;
68         out_h = that.out_h;
69         dpi = that.dpi;
70         strcpy(svg_file, that.svg_file);
71         ms_time = that.ms_time;
72 }
73
74 void SvgConfig::interpolate(SvgConfig &prev, SvgConfig &next,
75         long prev_frame, long next_frame, long current_frame)
76 {
77         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
78         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
79
80         this->out_x = prev.out_x * prev_scale + next.out_x * next_scale;
81         this->out_y = prev.out_y * prev_scale + next.out_y * next_scale;
82         this->out_w = prev.out_w * prev_scale + next.out_w * next_scale;
83         this->out_h = prev.out_h * prev_scale + next.out_h * next_scale;
84         this->dpi = prev.dpi;
85         strcpy(this->svg_file, prev.svg_file);
86         this->ms_time = prev.ms_time;
87 }
88
89
90 SvgMain::SvgMain(PluginServer *server)
91  : PluginVClient(server)
92 {
93         ofrm = 0;
94         overlayer = 0;
95         need_reconfigure = 1;
96 }
97
98 SvgMain::~SvgMain()
99 {
100         delete ofrm;
101         delete overlayer;
102 }
103
104 const char* SvgMain::plugin_title() { return N_("SVG via Inkscape"); }
105 int SvgMain::is_realtime() { return 1; }
106 int SvgMain::is_synthesis() { return 1; }
107
108
109 LOAD_CONFIGURATION_MACRO(SvgMain, SvgConfig)
110
111 void SvgMain::save_data(KeyFrame *keyframe)
112 {
113         FileXML output;
114
115 // cause data to be stored directly in text
116         output.set_shared_output(keyframe->xbuf);
117
118         output.tag.set_title("SVG");
119         output.tag.set_property("OUT_X", config.out_x);
120         output.tag.set_property("OUT_Y", config.out_y);
121         output.tag.set_property("OUT_W", config.out_w);
122         output.tag.set_property("OUT_H", config.out_h);
123         output.tag.set_property("DPI", config.dpi);
124         output.tag.set_property("SVG_FILE", config.svg_file);
125         output.tag.set_property("MS_TIME", config.ms_time);
126         output.append_tag();
127         output.tag.set_title("/SVG");
128         output.append_tag();
129
130         output.terminate_string();
131 }
132
133 void SvgMain::read_data(KeyFrame *keyframe)
134 {
135         FileXML input;
136
137         input.set_shared_input(keyframe->xbuf);
138         int result = 0;
139
140         while( !(result = input.read_tag()) ) {
141                 if(input.tag.title_is("SVG")) {
142                         config.out_x = input.tag.get_property("OUT_X", config.out_x);
143                         config.out_y = input.tag.get_property("OUT_Y", config.out_y);
144                         config.out_w = input.tag.get_property("OUT_W", config.out_w);
145                         config.out_h = input.tag.get_property("OUT_H", config.out_h);
146                         config.dpi = input.tag.get_property("DPI", config.dpi);
147                         input.tag.get_property("SVG_FILE", config.svg_file);
148                         config.ms_time = input.tag.get_property("MS_TIME", config.ms_time);
149                 }
150         }
151 }
152
153 static int exec_command(char* const*argv)
154 {
155         pid_t pid = vfork();
156         if( pid < 0 ) return -1;
157         if( pid > 0 ) {
158                 int stat = 0;
159                 waitpid(pid, &stat, 0);
160                 if( stat ) {
161                         char msg[BCTEXTLEN];
162                         sprintf(msg, "%s: error exit status %d", argv[0], stat);
163                         MainError::show_error(msg);
164                 }
165                 return 0;
166         }
167         execvp(argv[0], &argv[0]);
168         return -1;
169 }
170
171
172 int SvgMain::process_realtime(VFrame *input, VFrame *output)
173 {
174         if( input != output )
175                 output->copy_from(input);
176
177         int need_export = 0;
178         float last_dpi = config.dpi;
179         char last_svg_file[BCTEXTLEN];
180         strcpy(last_svg_file, config.svg_file);
181         int64_t last_ms_time = config.ms_time;
182         need_reconfigure = load_configuration();
183         if( !ofrm || last_dpi != config.dpi )
184                 need_export = 1;
185         if( strcmp(last_svg_file, config.svg_file) ||
186             last_ms_time != config.ms_time )
187                 need_reconfigure = 1;
188
189         if( need_reconfigure || need_export ) {
190                 need_reconfigure = 0;
191                 if( config.svg_file[0] == 0 ) return 0;
192                 delete ofrm;  ofrm = 0;
193                 char filename_png[BCTEXTLEN];
194                 strcpy(filename_png, config.svg_file);
195                 strncat(filename_png, ".png", sizeof(filename_png));
196                 struct stat st_png;
197                 int64_t ms_time = need_export || stat(filename_png, &st_png) ? 0 :
198                         st_png.st_mtim.tv_sec*1000 + st_png.st_mtim.tv_nsec/1000000;
199                 int fd = ms_time < config.ms_time ? -1 : open(filename_png, O_RDWR);
200                 if( fd < 0 ) { // file does not exist, export it
201                         char command[BCTEXTLEN], dpi[BCSTRLEN];
202                         snprintf(command, sizeof(command),
203                                 "inkscape --without-gui --export-background=0x000000 "
204                                 "--export-background-opacity=0 -d %f %s --export-png=%s",
205                                 config.dpi, config.svg_file, filename_png);
206                         printf(_("Running command %s\n"), command);
207                         snprintf(dpi, sizeof(dpi), "%f", config.dpi);
208                         snprintf(command, sizeof(command), "--export-png=%s",filename_png);
209                         char *const argv[] = {
210                                 (char*)"inkscape",
211                                 (char*)"--without-gui",
212                                 (char*)"--export-background=0x000000",
213                                 (char*)"--export-background-opacity=0",
214                                 (char*)"-d", dpi, config.svg_file, command,
215                                 0, };
216                         exec_command(argv);
217                         // in order for lockf to work it has to be open for writing
218                         fd = open(filename_png, O_RDWR);
219                         if( fd < 0 )
220                                 printf(_("Export of %s to %s failed\n"), config.svg_file, filename_png);
221                 }
222                 if( fd >= 0 ) {
223                         ofrm = VFramePng::vframe_png(fd);
224                         close(fd);
225                         if( ofrm && ofrm->get_color_model() != output->get_color_model() ) {
226                                 VFrame *vfrm = new VFrame(ofrm->get_w(), ofrm->get_h(),
227                                         output->get_color_model(), 0);
228                                 vfrm->transfer_from(ofrm);
229                                 delete ofrm;  ofrm = vfrm;
230                         }
231                         if( !ofrm )
232                                 printf (_("The file %s that was generated from %s is not in PNG format."
233                                           " Try to delete all *.png files.\n"), filename_png, config.svg_file);
234                 }
235         }
236         if( ofrm ) {
237                 if(!overlayer) overlayer = new OverlayFrame(smp + 1);
238                 overlayer->overlay(output, ofrm,
239                          0, 0, ofrm->get_w(), ofrm->get_h(),
240                         config.out_x, config.out_y,
241                         config.out_x + config.out_w,
242                         config.out_y + config.out_h,
243                         1, TRANSFER_NORMAL, LINEAR_LINEAR);
244         }
245         return 0;
246 }
247
248
249 NEW_WINDOW_MACRO(SvgMain, SvgWin)
250
251 void SvgMain::update_gui()
252 {
253         if(thread) {
254                 load_configuration();
255                 SvgWin *window = (SvgWin*)thread->window;
256                 window->update_gui(config);
257         }
258 }
259