1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * file-rawtherapee.c -- raw file format plug-in that uses RawTherapee
5  * Copyright (C) 2012 Simon Budig <simon@gimp.org>
6  * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
7  * Copyright (C) 2017 Alberto Griggio <alberto.griggio@gmail.com>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <glib/gstdio.h>
26 
27 #include <libgimp/gimp.h>
28 
29 #include "libgimp/stdplugins-intl.h"
30 
31 #include "file-raw-formats.h"
32 #include "file-raw-utils.h"
33 
34 
35 #define LOAD_THUMB_PROC "file-rawtherapee-load-thumb"
36 #define REGISTRY_KEY_BASE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\rawtherapee"
37 
38 
39 static void     init                 (void);
40 static void     query                (void);
41 static void     run                  (const gchar      *name,
42                                       gint              nparams,
43                                       const GimpParam  *param,
44                                       gint             *nreturn_vals,
45                                       GimpParam       **return_vals);
46 static gint32   load_image           (const gchar      *filename,
47                                       GimpRunMode       run_mode,
48                                       GError          **error);
49 
50 static gint32   load_thumbnail_image (const gchar      *filename,
51                                       gint             thumb_size,
52                                       GError          **error);
53 
54 const GimpPlugInInfo PLUG_IN_INFO =
55 {
56   init,  /* init_proc */
57   NULL,  /* quit_proc */
58   query, /* query proc */
59   run,   /* run_proc */
60 };
61 
MAIN()62 MAIN ()
63 
64 
65 static void
66 init (void)
67 {
68   static const GimpParamDef load_args[] =
69   {
70     { GIMP_PDB_INT32,  "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
71     { GIMP_PDB_STRING, "filename",     "The name of the file to load." },
72     { GIMP_PDB_STRING, "raw-filename", "The name entered" },
73   };
74 
75   static const GimpParamDef load_return_vals[] =
76   {
77     { GIMP_PDB_IMAGE,  "image",        "Output image" }
78   };
79 
80   static const GimpParamDef thumb_args[] =
81   {
82     { GIMP_PDB_STRING, "filename",     "The name of the file to load"  },
83     { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"      }
84   };
85 
86   static const GimpParamDef thumb_return_vals[] =
87   {
88     { GIMP_PDB_IMAGE,  "image",        "Thumbnail image"               }
89   };
90 
91   /* check if rawtherapee is installed
92    * TODO: allow setting the location of the executable in preferences
93    */
94   gboolean  search_path      = FALSE;
95   gchar    *exec_path        = file_raw_get_executable_path ("rawtherapee", NULL,
96                                                              "RAWTHERAPEE_EXECUTABLE",
97                                                              "com.rawtherapee.rawtherapee",
98                                                              REGISTRY_KEY_BASE,
99                                                              &search_path);
100 #ifdef G_OS_WIN32
101   /* Issue #2716 - Prevent RT from opening a console window */
102   gchar    *argv[]             = { exec_path, "-v", "-w", NULL };
103 #else
104   gchar    *argv[]             = { exec_path, "-v", NULL };
105 #endif
106   gchar    *rawtherapee_stdout = NULL;
107   gboolean  have_rawtherapee   = FALSE;
108   gint      i;
109 
110   if (g_spawn_sync (NULL,
111                     argv,
112                     NULL,
113                     (search_path ? G_SPAWN_SEARCH_PATH : 0) |
114                     G_SPAWN_STDERR_TO_DEV_NULL,
115                     NULL,
116                     NULL,
117                     &rawtherapee_stdout,
118                     NULL,
119                     NULL,
120                     NULL))
121     {
122       gint rtmajor = 0;
123       gint rtminor = 0;
124 
125       if (sscanf (rawtherapee_stdout,
126                   "RawTherapee, version %d.%d",
127                   &rtmajor, &rtminor) == 2 &&
128           ((rtmajor == 5 && rtminor >= 2) || rtmajor >= 6))
129         {
130           have_rawtherapee = TRUE;
131         }
132 
133       g_free (rawtherapee_stdout);
134     }
135 
136   g_free (exec_path);
137 
138   if (! have_rawtherapee)
139     return;
140 
141   gimp_install_procedure (LOAD_THUMB_PROC,
142                           "Load thumbnail from a raw image via rawtherapee",
143                           "This plug-in loads a thumbnail from a raw image by calling rawtherapee-cli.",
144                           "Alberto Griggio",
145                           "Alberto Griggio",
146                           "2017",
147                           NULL,
148                           NULL,
149                           GIMP_PLUGIN,
150                           G_N_ELEMENTS (thumb_args),
151                           G_N_ELEMENTS (thumb_return_vals),
152                           thumb_args, thumb_return_vals);
153 
154   for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
155     {
156       const FileFormat *format = &file_formats[i];
157       gchar            *load_proc;
158       gchar            *load_blurb;
159       gchar            *load_help;
160 
161       load_proc  = g_strdup_printf (format->load_proc_format,  "rawtherapee");
162       load_blurb = g_strdup_printf (format->load_blurb_format, "rawtherapee");
163       load_help  = g_strdup_printf (format->load_help_format,  "rawtherapee");
164 
165       gimp_install_procedure (load_proc,
166                               load_blurb,
167                               load_help,
168                               "Alberto Griggio",
169                               "Alberto Griggio",
170                               "2017",
171                               format->file_type,
172                               NULL,
173                               GIMP_PLUGIN,
174                               G_N_ELEMENTS (load_args),
175                               G_N_ELEMENTS (load_return_vals),
176                               load_args, load_return_vals);
177 
178       gimp_register_file_handler_mime (load_proc,
179                                        format->mime_type);
180       gimp_register_file_handler_raw (load_proc);
181       gimp_register_magic_load_handler (load_proc,
182                                         format->extensions,
183                                         "",
184                                         format->magic);
185 
186       gimp_register_thumbnail_loader (load_proc, LOAD_THUMB_PROC);
187 
188       g_free (load_proc);
189       g_free (load_blurb);
190       g_free (load_help);
191     }
192 }
193 
194 static void
query(void)195 query (void)
196 {
197   /* query() is run only the first time for efficiency. Yet this plugin
198    * is dependent on the presence of rawtherapee which may be installed
199    * or uninstalled between GIMP startups. Therefore we should move the
200    * usual gimp_install_procedure() to init() so that the check is done
201    * at every startup instead.
202    */
203 }
204 
205 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)206 run (const gchar      *name,
207      gint              nparams,
208      const GimpParam  *param,
209      gint             *nreturn_vals,
210      GimpParam       **return_vals)
211 {
212   static GimpParam   values[6];
213   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
214   GimpRunMode        run_mode;
215   gint               image_ID;
216   GError            *error = NULL;
217   gint               i;
218 
219   INIT_I18N ();
220 
221   run_mode = param[0].data.d_int32;
222 
223   *nreturn_vals = 1;
224   *return_vals  = values;
225 
226   values[0].type          = GIMP_PDB_STATUS;
227   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
228 
229   /* check if the format passed is actually supported & load */
230   for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
231     {
232       const FileFormat *format    = &file_formats[i];
233       gchar            *load_proc = NULL;
234 
235       if (format->load_proc_format)
236         load_proc = g_strdup_printf (format->load_proc_format, "rawtherapee");
237 
238       if (load_proc && ! strcmp (name, load_proc))
239         {
240           image_ID = load_image (param[1].data.d_string, run_mode, &error);
241 
242           if (image_ID != -1)
243             {
244               *nreturn_vals = 2;
245               values[1].type         = GIMP_PDB_IMAGE;
246               values[1].data.d_image = image_ID;
247             }
248           else
249             {
250               status = GIMP_PDB_EXECUTION_ERROR;
251             }
252 
253           break;
254         }
255       else if (! strcmp (name, LOAD_THUMB_PROC))
256         {
257           image_ID = load_thumbnail_image (param[0].data.d_string,
258                                            param[1].data.d_int32,
259                                            &error);
260 
261           if (image_ID != -1)
262             {
263               *nreturn_vals = 4;
264               values[1].type         = GIMP_PDB_IMAGE;
265               values[1].data.d_image = image_ID;
266               values[4].type         = GIMP_PDB_INT32;
267               values[4].data.d_int32 = GIMP_RGB_IMAGE;
268               values[5].type         = GIMP_PDB_INT32;
269               values[5].data.d_int32 = 1; /* num_layers */
270             }
271           else
272             {
273               status = GIMP_PDB_EXECUTION_ERROR;
274             }
275 
276           break;
277         }
278     }
279 
280   if (i == G_N_ELEMENTS (file_formats))
281     status = GIMP_PDB_CALLING_ERROR;
282 
283   if (status != GIMP_PDB_SUCCESS && error)
284     {
285       *nreturn_vals = 2;
286       values[1].type           = GIMP_PDB_STRING;
287       values[1].data.d_string  = error->message;
288     }
289 
290   values[0].data.d_status = status;
291 }
292 
293 static gint32
load_image(const gchar * filename,GimpRunMode run_mode,GError ** error)294 load_image (const gchar  *filename,
295             GimpRunMode   run_mode,
296             GError      **error)
297 {
298   gint32    image_ID           = -1;
299   gchar    *filename_out       = gimp_temp_name ("tif");
300   gchar    *rawtherapee_stdout = NULL;
301 
302   gboolean  search_path        = FALSE;
303   gchar    *exec_path          = file_raw_get_executable_path ("rawtherapee", NULL,
304                                                                "RAWTHERAPEE_EXECUTABLE",
305                                                                "com.rawtherapee.rawtherapee",
306                                                                REGISTRY_KEY_BASE,
307                                                                &search_path);
308 
309   /* linear sRGB for now as GIMP uses that internally in many places anyway */
310   gchar *argv[] =
311     {
312       exec_path,
313       "-gimp",
314       (gchar *) filename,
315       filename_out,
316       NULL
317     };
318 
319   gimp_progress_init_printf (_("Opening '%s'"),
320                              gimp_filename_to_utf8 (filename));
321 
322   if (g_spawn_sync (NULL,
323                     argv,
324                     NULL,
325                     /*G_SPAWN_STDOUT_TO_DEV_NULL |*/
326                     G_SPAWN_STDERR_TO_DEV_NULL |
327                     (search_path ? G_SPAWN_SEARCH_PATH : 0),
328                     NULL,
329                     NULL,
330                     &rawtherapee_stdout,
331                     NULL,
332                     NULL,
333                     error))
334     {
335       image_ID = gimp_file_load (run_mode, filename_out, filename_out);
336       if (image_ID != -1)
337         gimp_image_set_filename (image_ID, filename);
338     }
339 
340   /*if (rawtherapee_stdout) printf ("%s\n", rawtherapee_stdout);*/
341   g_free (rawtherapee_stdout);
342   g_free (exec_path);
343 
344   g_unlink (filename_out);
345   g_free (filename_out);
346 
347   gimp_progress_update (1.0);
348 
349   return image_ID;
350 }
351 
352 static gint32
load_thumbnail_image(const gchar * filename,gint thumb_size,GError ** error)353 load_thumbnail_image (const gchar   *filename,
354                       gint           thumb_size,
355                       GError       **error)
356 {
357   gint32  image_ID         = -1;
358   gchar  *filename_out     = gimp_temp_name ("jpg");
359   gchar  *thumb_pp3        = gimp_temp_name ("pp3");
360   FILE   *thumb_pp3_f      = fopen (thumb_pp3, "w");
361   gchar  *rawtherapee_stdout = NULL;
362   const char *pp3_content =
363     "[Version]\n"
364     "AppVersion=5.0\n"
365     "Version=326\n"
366     "\n"
367     "[Resize]\n"
368     "Enabled=true\n"
369     "AppliesTo=Cropped area\n"
370     "Method=Lanczos\n"
371     "Width=%d\n"
372     "Height=%d\n"
373     "\n"
374     "[Sharpening]\n"
375     "Enabled=false\n"
376     "\n"
377     "[SharpenEdge]\n"
378     "Enabled=false\n"
379     "\n"
380     "[SharpenMicro]\n"
381     "Enabled=false\n"
382     "\n"
383     "[Defringing]\n"
384     "Enabled=false\n"
385     "\n"
386     "[Directional Pyramid Equalizer]\n"
387     "Enabled=false\n"
388     "\n"
389     "[PostResizeSharpening]\n"
390     "Enabled=false\n"
391     "\n"
392     "[Directional Pyramid Denoising]\n"
393     "Enabled=false\n"
394     "\n"
395     "[Impulse Denoising]\n"
396     "Enabled=false\n"
397     "\n"
398     "[Wavelet]\n"
399     "Enabled=false\n"
400     "\n"
401     "[RAW Bayer]\n"
402     "Method=fast\n"
403     "\n"
404     "[RAW X-Trans]\n"
405     "Method=fast\n";
406 
407 
408   gboolean  search_path = FALSE;
409   gchar    *exec_path   = file_raw_get_executable_path ("rawtherapee", "-cli",
410                                                         "RAWTHERAPEE_EXECUTABLE",
411                                                         "com.rawtherapee.rawtherapee",
412                                                         REGISTRY_KEY_BASE,
413                                                         &search_path);
414   gchar *argv[] =
415     {
416       exec_path,
417       "-o", filename_out,
418       "-d",
419       "-s",
420       "-j",
421       "-p", thumb_pp3,
422       "-f",
423       "-c", (char *) filename,
424       NULL
425     };
426 
427   if (thumb_pp3_f)
428     {
429       if (fprintf (thumb_pp3_f, pp3_content, thumb_size, thumb_size) < 0)
430         {
431           fclose (thumb_pp3_f);
432           thumb_pp3_f = NULL;
433         }
434     }
435 
436   gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
437                              gimp_filename_to_utf8 (filename));
438 
439   if (thumb_pp3_f &&
440       g_spawn_sync (NULL,
441                     argv,
442                     NULL,
443                     G_SPAWN_STDERR_TO_DEV_NULL |
444                     (search_path ? G_SPAWN_SEARCH_PATH : 0),
445                     NULL,
446                     NULL,
447                     &rawtherapee_stdout,
448                     NULL,
449                     NULL,
450                     error))
451     {
452       gimp_progress_update (0.5);
453 
454       image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
455                                  filename_out,
456                                  filename_out);
457       if (image_ID != -1)
458         {
459           /* is this needed for thumbnails? */
460           gimp_image_set_filename (image_ID, filename);
461         }
462     }
463 
464   gimp_progress_update (1.0);
465 
466   if (thumb_pp3_f)
467     fclose (thumb_pp3_f);
468 
469   g_unlink (thumb_pp3);
470   g_free (filename_out);
471   g_free (thumb_pp3);
472   g_free (rawtherapee_stdout);
473   g_free (exec_path);
474 
475   return image_ID;
476 }
477