1 /* This file is part of GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL 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 GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2003-2007 Calvin Williamson, Øyvind Kolås
17  *           2013      Daniel Sabo
18  */
19 
20 #include "config.h"
21 #define __GEGL_INIT_C
22 
23 #include <babl/babl.h>
24 
25 #include <glib-object.h>
26 #include <glib/gstdio.h>
27 #include <glib/gi18n-lib.h>
28 
29 #include <locale.h>
30 
31 #include <stdlib.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 
36 #ifdef G_OS_WIN32
37 
38 #include <windows.h>
39 
40 static HMODULE hLibGeglModule = NULL;
41 
42 /* DllMain prototype */
43 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
44                      DWORD     fdwReason,
45                      LPVOID    lpvReserved);
46 
47 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)48 DllMain (HINSTANCE hinstDLL,
49          DWORD     fdwReason,
50          LPVOID    lpvReserved)
51 {
52   hLibGeglModule = hinstDLL;
53   return TRUE;
54 }
55 
56 #endif
57 
58 
59 #include "gegl-debug.h"
60 
61 guint gegl_debug_flags = 0;
62 
63 #include "gegl-types.h"
64 #include "gegl-types-internal.h"
65 #include "gegl-instrument.h"
66 #include "gegl-init.h"
67 #include "gegl-init-private.h"
68 #include "module/geglmodule.h"
69 #include "module/geglmoduledb.h"
70 #include "buffer/gegl-buffer.h"
71 #include "operation/gegl-operation.h"
72 #include "operation/gegl-operations.h"
73 #include "operation/gegl-operation-handlers-private.h"
74 #include "buffer/gegl-buffer-private.h"
75 #include "buffer/gegl-buffer-iterator-private.h"
76 #include "buffer/gegl-buffer-swap-private.h"
77 #include "buffer/gegl-compression.h"
78 #include "buffer/gegl-tile-alloc.h"
79 #include "buffer/gegl-tile-backend-ram.h"
80 #include "buffer/gegl-tile-backend-file.h"
81 #include "gegl-config.h"
82 #include "gegl-stats.h"
83 #include "graph/gegl-node-private.h"
84 #include "gegl-random-private.h"
85 #include "gegl-parallel-private.h"
86 
87 static gboolean  gegl_post_parse_hook (GOptionContext *context,
88                                        GOptionGroup   *group,
89                                        gpointer        data,
90                                        GError        **error);
91 
92 
93 static GeglConfig   *config = NULL;
94 
95 static GeglStats    *stats = NULL;
96 
97 static GeglModuleDB *module_db   = NULL;
98 
99 static glong         global_time = 0;
100 
101 static void load_module_path(gchar *path, GeglModuleDB *db);
102 
103 static void
gegl_config_application_license_notify(GObject * gobject,GParamSpec * pspec,gpointer user_data)104 gegl_config_application_license_notify (GObject    *gobject,
105                                         GParamSpec *pspec,
106                                         gpointer    user_data)
107 {
108   GeglConfig *cfg = GEGL_CONFIG (gobject);
109   GSList *paths = gegl_get_default_module_paths ();
110 
111   gegl_operations_set_licenses_from_string (cfg->application_license);
112 
113   /* causes load of .so's that might have been skipped due to filename */
114   g_slist_foreach(paths, (GFunc)load_module_path, module_db);
115   g_slist_free_full (paths, g_free);
116 }
117 
118 
119 static void
gegl_config_use_opencl_notify(GObject * gobject,GParamSpec * pspec,gpointer user_data)120 gegl_config_use_opencl_notify (GObject    *gobject,
121                                GParamSpec *pspec,
122                                gpointer    user_data)
123 {
124   GeglConfig *cfg = GEGL_CONFIG (gobject);
125 
126   g_signal_handlers_block_by_func (gobject,
127                                    gegl_config_use_opencl_notify,
128                                    NULL);
129 
130   if (cfg->use_opencl)
131     {
132        gegl_cl_init (NULL);
133     }
134   else
135     {
136       gegl_cl_disable ();
137     }
138 
139   cfg->use_opencl = gegl_cl_is_accelerated();
140 
141   g_signal_handlers_unblock_by_func (gobject,
142                                      gegl_config_use_opencl_notify,
143                                      NULL);
144 }
145 
146 static void
gegl_init_i18n(void)147 gegl_init_i18n (void)
148 {
149   static gboolean i18n_initialized = FALSE;
150 
151   if (! i18n_initialized)
152     {
153       bindtextdomain (GETTEXT_PACKAGE, GEGL_LOCALEDIR);
154       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
155 
156       i18n_initialized = TRUE;
157     }
158 }
159 
160 static GThread *main_thread = NULL;
161 
gegl_is_main_thread(void)162 gboolean gegl_is_main_thread (void)
163 {
164   return g_thread_self () == main_thread;
165 }
166 
167 void _gegl_init_u8_lut (void);
168 
169 void
gegl_init(gint * argc,gchar *** argv)170 gegl_init (gint    *argc,
171            gchar ***argv)
172 {
173   GOptionContext *context;
174   GError         *error = NULL;
175   static gboolean initialized = FALSE;
176 
177   if (initialized)
178     return;
179 
180 
181 
182   initialized = TRUE;
183 
184   context = g_option_context_new (NULL);
185   g_option_context_set_ignore_unknown_options (context, TRUE);
186   g_option_context_set_help_enabled (context, FALSE);
187   g_option_context_set_main_group (context, gegl_get_option_group ());
188 
189   if (!g_option_context_parse (context, argc, argv, &error))
190     {
191       g_warning ("%s", error->message);
192       g_error_free (error);
193     }
194 
195   g_option_context_free (context);
196 }
197 
198 static gchar    *cmd_gegl_swap             = NULL;
199 static gchar    *cmd_gegl_swap_compression = NULL;
200 static gchar    *cmd_gegl_cache_size       = NULL;
201 static gchar    *cmd_gegl_chunk_size       = NULL;
202 static gchar    *cmd_gegl_quality          = NULL;
203 static gchar    *cmd_gegl_tile_size        = NULL;
204 static gchar    *cmd_gegl_threads          = NULL;
205 static gboolean *cmd_gegl_disable_opencl   = NULL;
206 
207 static const GOptionEntry cmd_entries[]=
208 {
209     {
210      "gegl-swap", 0, 0,
211      G_OPTION_ARG_STRING, &cmd_gegl_swap,
212      N_("Where GEGL stores its swap"), "<uri>"
213     },
214     {
215      "gegl-swap-compression", 0, 0,
216      G_OPTION_ARG_STRING, &cmd_gegl_swap_compression,
217      N_("Compression algorithm used for data stored in the swap"), "<algorithm>"
218     },
219     {
220      "gegl-cache-size", 0, 0,
221      G_OPTION_ARG_STRING, &cmd_gegl_cache_size,
222      N_("How much memory to (approximately) use for caching imagery"), "<megabytes>"
223     },
224     {
225      "gegl-tile-size", 0, 0,
226      G_OPTION_ARG_STRING, &cmd_gegl_tile_size,
227      N_("Default size of tiles in GeglBuffers"), "<widthxheight>"
228     },
229     {
230      "gegl-chunk-size", 0, 0,
231      G_OPTION_ARG_STRING, &cmd_gegl_chunk_size,
232      N_("The count of pixels to compute simultaneously"), "pixel count"
233     },
234     {
235      "gegl-quality", 0, 0,
236      G_OPTION_ARG_STRING, &cmd_gegl_quality,
237      N_("The quality of rendering, a value between 0.0 (fast) and 1.0 (reference)"), "<quality>"
238     },
239     {
240      "gegl-threads", 0, 0,
241      G_OPTION_ARG_STRING, &cmd_gegl_threads,
242      N_("The number of concurrent processing threads to use"), "<threads>"
243     },
244     {
245       "gegl-disable-opencl", 0, 0,
246       G_OPTION_ARG_NONE, &cmd_gegl_disable_opencl,
247       N_("Disable OpenCL"), NULL
248     },
249     { NULL }
250 };
251 
252 GOptionGroup *
gegl_get_option_group(void)253 gegl_get_option_group (void)
254 {
255   GOptionGroup *group;
256 
257   gegl_init_i18n ();
258 
259   group = g_option_group_new ("gegl", "GEGL Options", _("Show GEGL Options"),
260                               NULL, NULL);
261   g_option_group_add_entries (group, cmd_entries);
262 
263   g_option_group_set_parse_hooks (group, NULL, gegl_post_parse_hook);
264 
265   return group;
266 }
267 
268 static void
gegl_config_parse_env(GeglConfig * config)269 gegl_config_parse_env (GeglConfig *config)
270 {
271   if (g_getenv ("GEGL_MIPMAP_RENDERING"))
272     {
273       const gchar *value = g_getenv ("GEGL_MIPMAP_RENDERING");
274       if (!strcmp (value, "1")||
275           !strcmp (value, "true")||
276           !strcmp (value, "yes"))
277         g_object_set (config, "mipmap-rendering", TRUE, NULL);
278       else
279         g_object_set (config, "mipmap-rendering", FALSE, NULL);
280     }
281 
282 
283   if (g_getenv ("GEGL_QUALITY"))
284     {
285       const gchar *quality = g_getenv ("GEGL_QUALITY");
286 
287       if (g_str_equal (quality, "fast"))
288         g_object_set (config, "quality", 0.0, NULL);
289       else if (g_str_equal (quality, "good"))
290         g_object_set (config, "quality", 0.5, NULL);
291       else if (g_str_equal (quality, "best"))
292         g_object_set (config, "quality", 1.0, NULL);
293       else
294         g_object_set (config, "quality", atof (quality), NULL);
295     }
296 
297   if (g_getenv ("GEGL_CACHE_SIZE"))
298     {
299       g_object_set (config,
300                     "tile-cache-size",
301                     (guint64) atoll(g_getenv("GEGL_CACHE_SIZE")) * 1024 * 1024,
302                     NULL);
303     }
304 
305   if (g_getenv ("GEGL_CHUNK_SIZE"))
306     config->chunk_size = atoi(g_getenv("GEGL_CHUNK_SIZE"));
307 
308   if (g_getenv ("GEGL_TILE_SIZE"))
309     {
310       const gchar *str = g_getenv ("GEGL_TILE_SIZE");
311       gint         width;
312       gint         height;
313       width = height = atoi(str);
314       str = strchr (str, 'x');
315       if (str)
316         height = atoi(str+1);
317       g_object_set (config,
318                     "tile-width",  width,
319                     "tile-height", height,
320                     NULL);
321     }
322 
323   if (g_getenv ("GEGL_THREADS"))
324     {
325       _gegl_threads = atoi(g_getenv("GEGL_THREADS"));
326 
327       if (_gegl_threads > GEGL_MAX_THREADS)
328         {
329           g_warning ("Tried to use %i threads, max is %i",
330                      _gegl_threads, GEGL_MAX_THREADS);
331           _gegl_threads = GEGL_MAX_THREADS;
332         }
333     }
334 
335   if (g_getenv ("GEGL_USE_OPENCL"))
336     {
337       const char *opencl_env = g_getenv ("GEGL_USE_OPENCL");
338 
339       if (g_ascii_strcasecmp (opencl_env, "yes") == 0)
340         g_object_set (config, "use-opencl", TRUE, NULL);
341       else if (g_ascii_strcasecmp (opencl_env, "no") == 0)
342         gegl_cl_hard_disable ();
343       else if (g_ascii_strcasecmp (opencl_env, "cpu") == 0) {
344         gegl_cl_set_default_device_type (CL_DEVICE_TYPE_CPU);
345         g_object_set (config, "use-opencl", TRUE, NULL);
346       } else if (g_ascii_strcasecmp (opencl_env, "gpu") == 0) {
347         gegl_cl_set_default_device_type (CL_DEVICE_TYPE_GPU);
348         g_object_set (config, "use-opencl", TRUE, NULL);
349       } else if (g_ascii_strcasecmp (opencl_env, "accelerator") == 0) {
350         gegl_cl_set_default_device_type (CL_DEVICE_TYPE_ACCELERATOR);
351         g_object_set (config, "use-opencl", TRUE, NULL);
352       } else
353         g_warning ("Unknown value for GEGL_USE_OPENCL: %s", opencl_env);
354     }
355 
356   if (g_getenv ("GEGL_SWAP"))
357     g_object_set (config, "swap", g_getenv ("GEGL_SWAP"), NULL);
358 
359   if (g_getenv ("GEGL_SWAP_COMPRESSION"))
360     {
361       g_object_set (config,
362                     "swap-compression", g_getenv ("GEGL_SWAP_COMPRESSION"),
363                     NULL);
364     }
365 }
366 
367 GeglConfig *
gegl_config(void)368 gegl_config (void)
369 {
370   if (!config)
371     config = g_object_new (GEGL_TYPE_CONFIG, NULL);
372 
373   return config;
374 }
375 
376 GeglStats *
gegl_stats(void)377 gegl_stats (void)
378 {
379   if (! stats)
380     stats = g_object_new (GEGL_TYPE_STATS, NULL);
381 
382   return stats;
383 }
384 
385 void
gegl_reset_stats(void)386 gegl_reset_stats (void)
387 {
388   gegl_stats_reset (gegl_stats ());
389 }
390 
391 void
392 gegl_temp_buffer_free (void);
393 
394 void
gegl_exit(void)395 gegl_exit (void)
396 {
397   if (!config)
398     {
399       g_warning("gegl_exit() called without matching call to gegl_init()");
400       return;
401     }
402 
403   GEGL_INSTRUMENT_START()
404 
405   gegl_tile_backend_swap_cleanup ();
406   gegl_tile_cache_destroy ();
407   gegl_operation_gtype_cleanup ();
408   gegl_operation_handlers_cleanup ();
409   gegl_compression_cleanup ();
410   gegl_random_cleanup ();
411   gegl_parallel_cleanup ();
412   gegl_buffer_swap_cleanup ();
413   gegl_tile_alloc_cleanup ();
414   gegl_cl_cleanup ();
415 
416   gegl_temp_buffer_free ();
417 
418   g_clear_object (&module_db);
419 
420   babl_exit ();
421 
422   GEGL_INSTRUMENT_END ("gegl", "gegl_exit")
423 
424   /* used when tracking buffer and tile leaks */
425   if (g_getenv ("GEGL_DEBUG_BUFS") != NULL)
426     {
427       gegl_buffer_stats ();
428       gegl_tile_backend_ram_stats ();
429       gegl_tile_backend_file_stats ();
430     }
431   global_time = gegl_ticks () - global_time;
432   gegl_instrument ("gegl", "gegl", global_time);
433 
434   if (gegl_instrument_enabled)
435     {
436       g_printf ("\n%s", gegl_instrument_utf8 ());
437     }
438 
439   if (gegl_buffer_leaks ())
440     {
441       g_printf ("EEEEeEeek! %i GeglBuffers leaked\n", gegl_buffer_leaks ());
442 #ifdef GEGL_ENABLE_DEBUG
443       if (!(gegl_debug_flags & GEGL_DEBUG_BUFFER_ALLOC))
444         g_printerr ("To debug GeglBuffer leaks, set the environment "
445                     "variable GEGL_DEBUG to \"buffer-alloc\"\n");
446 #endif
447     }
448 
449   g_clear_object (&config);
450   global_time = 0;
451 }
452 
453 void
gegl_get_version(int * major,int * minor,int * micro)454 gegl_get_version (int *major,
455                   int *minor,
456                   int *micro)
457 {
458   if (major != NULL)
459     *major = GEGL_MAJOR_VERSION;
460 
461   if (minor != NULL)
462     *minor = GEGL_MINOR_VERSION;
463 
464   if (micro != NULL)
465     *micro = GEGL_MICRO_VERSION;
466 }
467 
468 void
gegl_load_module_directory(const gchar * path)469 gegl_load_module_directory (const gchar *path)
470 {
471   g_return_if_fail (g_file_test (path, G_FILE_TEST_IS_DIR));
472 
473   gegl_module_db_load (module_db, path);
474 }
475 
476 
477 GSList *
gegl_get_default_module_paths(void)478 gegl_get_default_module_paths(void)
479 {
480   GSList *list = NULL;
481   gchar *module_path = NULL;
482 
483   // GEGL_PATH
484   const gchar *gegl_path = g_getenv ("GEGL_PATH");
485   if (gegl_path)
486     {
487       list = g_slist_append (list, g_strdup (gegl_path));
488       return list;
489     }
490 
491   // System library dir
492 #ifdef G_OS_WIN32
493   {
494     gchar *prefix;
495     prefix = g_win32_get_package_installation_directory_of_module ( hLibGeglModule );
496     module_path = g_build_filename (prefix, "lib", GEGL_LIBRARY, NULL);
497     g_free(prefix);
498   }
499 #else
500   module_path = g_build_filename (LIBDIR, GEGL_LIBRARY, NULL);
501 #endif
502   list = g_slist_append (list, module_path);
503 
504   /* User data dir
505    * ~/.local/share/gegl-x.y/plug-ins */
506   module_path = g_build_filename (g_get_user_data_dir (),
507                                   GEGL_LIBRARY,
508                                   "plug-ins",
509                                   NULL);
510   g_mkdir_with_parents (module_path, S_IRUSR | S_IWUSR | S_IXUSR);
511   list = g_slist_append (list, module_path);
512 
513   return list;
514 }
515 
516 static void
load_module_path(gchar * path,GeglModuleDB * db)517 load_module_path(gchar *path, GeglModuleDB *db)
518 {
519   gegl_module_db_load (db, path);
520 }
521 
522 static gboolean
gegl_post_parse_hook(GOptionContext * context,GOptionGroup * group,gpointer data,GError ** error)523 gegl_post_parse_hook (GOptionContext *context,
524                       GOptionGroup   *group,
525                       gpointer        data,
526                       GError        **error)
527 {
528   GeglConfig *config;
529 
530   g_assert (global_time == 0);
531   global_time = gegl_ticks ();
532 
533   if (g_getenv ("GEGL_DEBUG_TIME") != NULL)
534     gegl_instrument_enable ();
535 
536   gegl_instrument ("gegl", "gegl_init", 0);
537 
538   config = gegl_config ();
539 
540   gegl_config_parse_env (config);
541 
542   babl_init ();
543   _gegl_init_u8_lut ();
544 
545 #ifdef GEGL_ENABLE_DEBUG
546   {
547     const char *env_string;
548     env_string = g_getenv ("GEGL_DEBUG");
549     if (env_string != NULL)
550       {
551         gegl_debug_flags =
552           g_parse_debug_string (env_string,
553                                 gegl_debug_keys,
554                                 G_N_ELEMENTS (gegl_debug_keys));
555         env_string = NULL;
556       }
557   }
558 #endif /* GEGL_ENABLE_DEBUG */
559 
560   if (cmd_gegl_swap)
561     g_object_set (config, "swap", cmd_gegl_swap, NULL);
562   if (cmd_gegl_swap_compression)
563     g_object_set (config, "swap-compression", cmd_gegl_swap_compression, NULL);
564   if (cmd_gegl_quality)
565     config->quality = atof (cmd_gegl_quality);
566   if (cmd_gegl_cache_size)
567     {
568       g_object_set (config,
569                     "tile-cache-size",
570                     (guint64) atoll (cmd_gegl_cache_size) * 1024 * 1024,
571                     NULL);
572     }
573   if (cmd_gegl_chunk_size)
574     config->chunk_size = atoi (cmd_gegl_chunk_size);
575   if (cmd_gegl_tile_size)
576     {
577       const gchar *str = cmd_gegl_tile_size;
578       gint         width;
579       gint         height;
580       width = height = atoi(str);
581       str = strchr (str, 'x');
582       if (str)
583         height = atoi(str+1);
584       g_object_set (config,
585                     "tile-width",  width,
586                     "tile-height", height,
587                     NULL);
588     }
589   if (cmd_gegl_threads)
590     {
591       _gegl_threads = atoi (cmd_gegl_threads);
592       if (_gegl_threads > GEGL_MAX_THREADS)
593         {
594           g_warning ("Tried to use %i threads, max is %i",
595                      _gegl_threads, GEGL_MAX_THREADS);
596           _gegl_threads = GEGL_MAX_THREADS;
597         }
598     }
599   if (cmd_gegl_disable_opencl)
600     gegl_cl_hard_disable ();
601 
602   GEGL_INSTRUMENT_START();
603 
604   gegl_tile_alloc_init ();
605   gegl_buffer_swap_init ();
606   gegl_parallel_init ();
607   gegl_compression_init ();
608   gegl_operation_gtype_init ();
609   gegl_tile_cache_init ();
610 
611   if (!module_db)
612     {
613       GSList *paths = gegl_get_default_module_paths ();
614       module_db = gegl_module_db_new (FALSE);
615       g_slist_foreach(paths, (GFunc)load_module_path, module_db);
616       g_slist_free_full (paths, g_free);
617     }
618 
619   GEGL_INSTRUMENT_END ("gegl_init", "load modules");
620 
621   gegl_instrument ("gegl", "gegl_init", gegl_ticks () - global_time);
622 
623   g_signal_connect (G_OBJECT (config),
624                    "notify::use-opencl",
625                    G_CALLBACK (gegl_config_use_opencl_notify),
626                    NULL);
627   g_object_set (config, "use-opencl", config->use_opencl, NULL);
628 
629   g_signal_connect (G_OBJECT (config),
630                    "notify::application-license",
631                    G_CALLBACK (gegl_config_application_license_notify),
632                    NULL);
633   gegl_operations_set_licenses_from_string (config->application_license);
634 
635   main_thread = g_thread_self ();
636 
637   return TRUE;
638 }
639 
640 gboolean
gegl_get_debug_enabled(void)641 gegl_get_debug_enabled (void)
642 {
643 #ifdef GEGL_ENABLE_DEBUG
644   return gegl_debug_flags != 0;
645 #else
646   return FALSE;
647 #endif
648 }
649