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