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