1 /* This file is part of GEGL editor -- a gtk frontend for GEGL
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2016 Øyvind Kolås
17 */
18
19 #include "config.h"
20
21 #include <glib.h>
22 #include <glib/gprintf.h>
23 #include <glib/gi18n-lib.h>
24 #include <gegl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include "gegl-options.h"
33 #ifdef HAVE_SPIRO
34 #include "gegl-path-spiro.h"
35 #endif
36 #include "gegl-path-smooth.h"
37 #include "operation/gegl-extension-handler.h"
38
39 #ifdef G_OS_WIN32
40 #include <direct.h>
41 #define getcwd(b,n) _getcwd(b,n)
42 #define realpath(a,b) _fullpath(b,a,_MAX_PATH)
43 #endif
44
45 #define DEFAULT_COMPOSITION \
46 "<?xml version='1.0' encoding='UTF-8'?> <gegl> <node operation='gegl:crop'> <params> <param name='x'>0</param> <param name='y'>0</param> <param name='width'>395</param> <param name='height'>200</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>80</param> <param name='y'>162</param> </params> </node> <node operation='gegl:opacity'> <params> <param name='value'>0.5</param> </params> </node> <node name='text' operation='gegl:text'> <params> <param name='string'>2000-2011 © Various contributors</param> <param name='font'>Sans</param> <param name='size'>12</param> <param name='color'>rgb(0.0000, 0.0000, 0.0000)</param> <param name='wrap'>628</param> <param name='alignment'>0</param> <param name='width'>622</param> <param name='height'>40</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>20</param> <param name='y'>50</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>0</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <gegl:fill-path d='M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z' color='white'/> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>88</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:fill-path'> <params> <param name='d'>M50,0 C23,0 0,22 0,50 C0,77 22,100 50,100 C68,100 85,90 93,75 L40,75 C35,75 35,65 40,65 L98,65 C100,55 100,45 98,35 L40,35 C35,35 35,25 40,25 L93,25 C84,10 68,0 50,0 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>176</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:fill-path'> <params> <param name='d'>M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:translate'> <params> <param name='x'>264</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:fill-path'> <params> <param name='d'>M30,4 C12,13 0,30 0,50 C0,78 23,100 50,100 C71,100 88,88 96,71 L56,71 C42,71 30,59 30,45 L30,4 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:rotate'> <params> <param name='origin-x'>0</param> <param name='origin-y'>0</param> <param name='sampler'>linear</param> <param name='degrees'>42</param> </params> </node> <node operation='gegl:checkerboard'> <params> <param name='x'>43</param> <param name='y'>44</param> <param name='x-offset'>0</param> <param name='y-offset'>0</param> <param name='color1'>rgb(0.7097, 0.7097, 0.7097)</param> <param name='color2'>rgb(0.7661, 0.7661, 0.7661)</param> </params> </node> </gegl>"
47
48 #define STDIN_BUF_SIZE 128
49
50 static void
gegl_enable_fatal_warnings(void)51 gegl_enable_fatal_warnings (void)
52 {
53 GLogLevelFlags fatal_mask;
54
55 fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
56 fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
57
58 g_log_set_always_fatal (fatal_mask);
59 }
60
61 int gegl_str_has_image_suffix (char *path);
62 int gegl_str_has_video_suffix (char *path);
63
file_is_gegl_composition(const gchar * path)64 static gboolean file_is_gegl_composition (const gchar *path)
65 {
66 gchar *extension;
67
68 extension = strrchr (path, '.');
69 if (!extension)
70 return FALSE;
71 extension++;
72 if (extension[0]=='\0')
73 return FALSE;
74 if (!strcmp (extension, "xml")||
75 !strcmp (extension, "gegl")||
76 !strcmp (extension, "XML")||
77 !strcmp (extension, "svg")
78 )
79 return TRUE;
80 return FALSE;
81 }
82
is_xml_fragment(const char * data)83 static gboolean is_xml_fragment (const char *data)
84 {
85 int i;
86 for (i = 0; data && data[i]; i++)
87 switch (data[i])
88 {
89 case ' ':case '\t':case '\n':case '\r': break;
90 case '<': return TRUE;
91 default: return FALSE;
92 }
93 return FALSE;
94 }
95
96 int mrg_ui_main (int argc, char **argv, char **ops);
97
98 gint
main(gint argc,gchar ** argv)99 main (gint argc,
100 gchar **argv)
101 {
102 GeglOptions *o = NULL;
103 GeglNode *gegl = NULL;
104 gchar *script = NULL;
105 GError *err = NULL;
106 gchar *path_root = NULL;
107
108 #ifdef HAVE_MRG
109 const gchar *renderer = g_getenv ("GEGL_RENDERER");
110 if (renderer && ( !strcmp (renderer, "blit-mipmap") ||
111 !strcmp (renderer, "mipmap")))
112 g_setenv ("GEGL_MIPMAP_RENDERING", "1", TRUE);
113
114
115 #endif
116
117 g_object_set (gegl_config (),
118 "application-license", "GPL3",
119 #ifdef HAVE_MRG
120 "use-opencl", FALSE,
121 #endif
122 NULL);
123
124 o = gegl_options_parse (argc, argv);
125 gegl_init (NULL, NULL);
126 #ifdef HAVE_SPIRO
127 gegl_path_spiro_init ();
128 #endif
129 gegl_path_smooth_init ();
130
131
132 if (o->fatal_warnings)
133 {
134 gegl_enable_fatal_warnings ();
135 }
136
137 if (o->xml)
138 {
139 path_root = g_get_current_dir ();
140 }
141 else if (o->file)
142 {
143 if (!strcmp (o->file, "-")) /* read XML from stdin */
144 {
145 path_root = g_get_current_dir ();
146 }
147 else
148 {
149 gchar *tmp = g_path_get_dirname (o->file);
150 gchar *tmp2 = realpath (tmp, NULL);
151 path_root = g_strdup (tmp2);
152 g_free (tmp);
153 free (tmp2); /* don't use g_free - realpath isn't glib */
154 }
155 }
156
157 if (o->xml)
158 {
159 script = g_strdup (o->xml);
160 }
161 else if (o->file)
162 {
163 if (!strcmp (o->file, "-")) /* read XML from stdin */
164 {
165 gchar buf[STDIN_BUF_SIZE];
166 GString *acc = g_string_new ("");
167
168 while (fgets (buf, STDIN_BUF_SIZE, stdin))
169 {
170 g_string_append (acc, buf);
171 }
172 script = g_string_free (acc, FALSE);
173 }
174 else if (file_is_gegl_composition (o->file))
175 {
176 g_file_get_contents (o->file, &script, NULL, &err);
177 if (err != NULL)
178 {
179 g_warning (_("Unable to read file: %s"), err->message);
180 }
181 }
182 else
183 {
184 gchar *file_basename = g_path_get_basename (o->file);
185
186 if (gegl_str_has_video_suffix (file_basename))
187 script = g_strconcat ("<gegl><gegl:ff-load path='",
188 file_basename,
189 "'/></gegl>",
190 NULL);
191 else
192 script = g_strconcat ("<gegl><gegl:load path='",
193 file_basename,
194 "'/></gegl>",
195 NULL);
196
197 g_free (file_basename);
198 }
199 }
200 else
201 {
202 if (o->rest)
203 {
204 script = g_strdup ("<gegl></gegl>");
205 }
206 else
207 {
208 script = g_strdup (DEFAULT_COMPOSITION);
209 }
210 }
211
212 if (o->mode == GEGL_RUN_MODE_DISPLAY)
213 {
214 #ifdef HAVE_MRG
215 mrg_ui_main (argc, argv, o->rest);
216 return 0;
217 #endif
218 }
219
220 if (is_xml_fragment (script))
221 gegl = gegl_node_new_from_xml (script, path_root);
222 else
223 gegl = gegl_node_new_from_serialized (script, path_root);
224
225 if (!gegl)
226 {
227 g_print (_("Invalid graph, abort.\n"));
228 return 1;
229 }
230
231 {
232 GeglNode *proxy = gegl_node_get_output_proxy (gegl, "output");
233 GeglNode *iter = gegl_node_get_producer (proxy, "input", NULL);
234 if (o->rest)
235 {
236 GeglNode *ret_sink = NULL;
237
238 GError *error = (void*)(&ret_sink);
239 gegl_create_chain_argv (o->rest, iter, proxy, 0, gegl_node_get_bounding_box (gegl).height, path_root, &error);
240 if (error)
241 {
242 fprintf (stderr, "Error: %s\n", error->message);
243 }
244 if (ret_sink)
245 {
246 gegl_node_process (ret_sink);
247 exit(0);
248 }
249 if (o->serialize)
250 {
251 fprintf (stderr, "%s\n", gegl_serialize (iter,
252 gegl_node_get_producer (proxy, "input", NULL), "/",
253 GEGL_SERIALIZE_VERSION|GEGL_SERIALIZE_INDENT));
254 }
255 }
256 }
257
258 switch (o->mode)
259 {
260 case GEGL_RUN_MODE_DISPLAY:
261 {
262 GeglNode *output = gegl_node_new_child (gegl,
263 "operation", "gegl:display",
264 o->file ? "window-title" : NULL, o->file,
265 NULL);
266 gegl_node_connect_from (output, "input", gegl_node_get_output_proxy (gegl, "output"), "output");
267 gegl_node_process (output);
268 g_main_loop_run (g_main_loop_new (NULL, TRUE));
269 g_object_unref (output);
270 }
271 break;
272 case GEGL_RUN_MODE_XML:
273 g_printf ("%s\n", gegl_node_to_xml (gegl, path_root));
274 return 0;
275 break;
276
277 case GEGL_RUN_MODE_OUTPUT:
278 if (gegl_str_has_video_suffix ((void*)o->output))
279 {
280 GeglNode *output = gegl_node_new_child (gegl,
281 "operation", "gegl:ff-save",
282 "path", o->output,
283 "video-bit-rate", 4000,
284 NULL);
285 {
286 GeglRectangle bounds = gegl_node_get_bounding_box (gegl);
287 GeglBuffer *tempb;
288 GeglNode *n0;
289 GeglNode *iter;
290 GeglAudioFragment *audio = NULL;
291 int frame_no = 0;
292 guchar *temp;
293
294 bounds.x *= o->scale;
295 bounds.y *= o->scale;
296 bounds.width *= o->scale;
297 bounds.height *= o->scale;
298 temp = gegl_malloc (bounds.width * bounds.height * 4);
299 tempb = gegl_buffer_new (&bounds, babl_format("R'G'B'A u8"));
300
301 n0 = gegl_node_new_child (gegl, "operation", "gegl:buffer-source",
302 "buffer", tempb,
303 NULL);
304 gegl_node_connect_from (output, "input", n0, "output");
305
306 iter = gegl_node_get_output_proxy (gegl, "output");
307
308 while (gegl_node_get_producer (iter, "input", NULL))
309 iter = (gegl_node_get_producer (iter, "input", NULL));
310 {
311 int duration = 0;
312 gegl_node_get (iter, "frames", &duration, NULL);
313
314 while (frame_no < duration)
315 {
316 gegl_node_blit (gegl, o->scale, &bounds,
317 babl_format("R'G'B'A u8"), temp,
318 GEGL_AUTO_ROWSTRIDE,
319 GEGL_BLIT_DEFAULT);
320
321 gegl_buffer_set (tempb, &bounds, 0.0, babl_format ("R'G'B'A u8"),
322 temp, GEGL_AUTO_ROWSTRIDE);
323
324 gegl_node_get (iter, "audio", &audio, NULL);
325 if (audio)
326 gegl_node_set (output, "audio", audio, NULL);
327 fprintf (stderr, "\r%i/%i %p", frame_no, duration-1, audio);
328
329 gegl_node_process (output);
330
331 frame_no ++;
332 gegl_node_set (iter, "frame", frame_no, NULL);
333 }
334 fprintf (stderr, "\n");
335 }
336 gegl_free (temp);
337 g_object_unref (tempb);
338 g_object_unref (output);
339 }
340
341 }
342 else
343 {
344 GeglNode *output = gegl_node_new_child (gegl,
345 "operation", "gegl:save",
346 "path", o->output,
347 NULL);
348
349 if (o->scale != 1.0){
350 GeglRectangle bounds = gegl_node_get_bounding_box (gegl);
351 GeglBuffer *tempb;
352 GeglNode *n0;
353
354 guchar *temp;
355
356 bounds.x *= o->scale;
357 bounds.y *= o->scale;
358 bounds.width *= o->scale;
359 bounds.height *= o->scale;
360 temp = gegl_malloc (bounds.width * bounds.height * 4);
361 tempb = gegl_buffer_new (&bounds, babl_format("R'G'B'A u8"));
362 gegl_node_blit (gegl, o->scale, &bounds, babl_format("R'G'B'A u8"), temp, GEGL_AUTO_ROWSTRIDE,
363 GEGL_BLIT_DEFAULT);
364
365 gegl_buffer_set (tempb, &bounds, 0.0, babl_format ("R'G'B'A u8"),
366 temp, GEGL_AUTO_ROWSTRIDE);
367
368 n0 = gegl_node_new_child (gegl, "operation", "gegl:buffer-source",
369 "buffer", tempb,
370 NULL);
371 gegl_node_connect_from (output, "input", n0, "output");
372 gegl_node_process (output);
373 gegl_free (temp);
374 g_object_unref (tempb);
375 }
376 else
377 {
378 gegl_node_connect_from (output, "input", gegl, "output");
379 gegl_node_process (output);
380 }
381
382 g_object_unref (output);
383 }
384 break;
385
386 case GEGL_RUN_MODE_HELP:
387 break;
388
389 default:
390 g_warning (_("Unknown GeglOption mode: %d"), o->mode);
391 break;
392 }
393
394 g_list_free_full (o->files, g_free);
395 g_free (o);
396 g_object_unref (gegl);
397 g_free (script);
398 g_clear_error (&err);
399 g_free (path_root);
400 gegl_exit ();
401 return 0;
402 }
403
gegl_str_has_image_suffix(char * path)404 int gegl_str_has_image_suffix (char *path)
405 {
406 return g_str_has_suffix (path, ".jpg") ||
407 g_str_has_suffix (path, ".png") ||
408 g_str_has_suffix (path, ".xml") ||
409 g_str_has_suffix (path, ".svg") ||
410 g_str_has_suffix (path, ".pdf") ||
411 g_str_has_suffix (path, ".PDF") ||
412 g_str_has_suffix (path, ".SVG") ||
413 g_str_has_suffix (path, ".JPG") ||
414 g_str_has_suffix (path, ".PNG") ||
415 g_str_has_suffix (path, ".gif") ||
416 g_str_has_suffix (path, ".GIF") ||
417 g_str_has_suffix (path, ".tif") ||
418 g_str_has_suffix (path, ".tiff") ||
419 g_str_has_suffix (path, ".TIF") ||
420 g_str_has_suffix (path, ".TIFF") ||
421 g_str_has_suffix (path, ".jpeg") ||
422 g_str_has_suffix (path, ".JPEG") ||
423 g_str_has_suffix (path, ".CR2") ||
424 g_str_has_suffix (path, ".cr2") ||
425 g_str_has_suffix (path, ".lui") ||
426 g_str_has_suffix (path, ".exr");
427 }
428
gegl_str_has_video_suffix(char * path)429 int gegl_str_has_video_suffix (char *path)
430 {
431 return g_str_has_suffix (path, ".avi") ||
432 g_str_has_suffix (path, ".AVI") ||
433 g_str_has_suffix (path, ".mp4") ||
434 g_str_has_suffix (path, ".mov") ||
435 g_str_has_suffix (path, ".MOV") ||
436 g_str_has_suffix (path, ".dv") ||
437 g_str_has_suffix (path, ".DV") ||
438 g_str_has_suffix (path, ".mp3") ||
439 g_str_has_suffix (path, ".MP3") ||
440 g_str_has_suffix (path, ".mpg") ||
441 g_str_has_suffix (path, ".ogv") ||
442 g_str_has_suffix (path, ".MPG") ||
443 g_str_has_suffix (path, ".webm") ||
444 g_str_has_suffix (path, ".MP4") ||
445 g_str_has_suffix (path, ".mkv") ||
446 //g_str_has_suffix (path, ".gif") ||
447 //g_str_has_suffix (path, ".GIF") ||
448 g_str_has_suffix (path, ".MKV") ||
449 g_str_has_suffix (path, ".mov") ||
450 g_str_has_suffix (path, ".ogg");
451 }
452
453