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