1 /*
2 * UFRaw - Unidentified Flying Raw converter for digital camera images
3 *
4 * ufraw-gimp.c - The GIMP plug-in.
5 * Copyright 2004-2016 by Udi Fuchs
6 *
7 * based on the GIMP plug-in by Pawel T. Jochym jochym at ifj edu pl,
8 *
9 * based on the GIMP plug-in by Dave Coffin
10 * http://www.cybercom.net/~dcoffin/
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 */
17
18 #include "ufraw.h"
19 #include "uf_gtk.h"
20 #if HAVE_GIMP_2_9
21 #include <gegl.h>
22 #endif
23 #include <libgimpbase/gimpbase.h>
24 #include <libgimp/gimp.h>
25 #include <libgimp/gimpui.h>
26 #include <glib/gi18n.h>
27 #include <string.h>
28
29 void query();
30 void run(const gchar *name,
31 gint nparams,
32 const GimpParam *param,
33 gint *nreturn_vals,
34 GimpParam **return_vals);
35
36 long ufraw_save_gimp_image(ufraw_data *uf, GtkWidget *widget);
37
38 GimpPlugInInfo PLUG_IN_INFO = {
39 NULL, /* init_procedure */
40 NULL, /* quit_procedure */
41 query, /* query_procedure */
42 run, /* run_procedure */
43 };
44
MAIN()45 MAIN()
46
47 void query()
48 {
49 static const GimpParamDef load_args[] = {
50 { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
51 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
52 { GIMP_PDB_STRING, "raw_filename", "The name of the file to load" },
53 };
54 static const GimpParamDef load_return_vals[] = {
55 { GIMP_PDB_IMAGE, "image", "Output image" },
56 };
57 static const GimpParamDef thumb_args[] = {
58 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
59 { GIMP_PDB_INT32, "thumb_size", "Preferred thumbnail size" }
60 };
61 static const GimpParamDef thumb_return_vals[] = {
62 { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
63 { GIMP_PDB_INT32, "image_width", "Width of full-sized image" },
64 { GIMP_PDB_INT32, "image_height", "Height of full-sized image" }
65 };
66 gimp_install_procedure("file_ufraw_load",
67 "Loads digital camera raw files",
68 "Loads digital camera raw files.",
69 "Udi Fuchs",
70 "Copyright 2003 by Dave Coffin\n"
71 "Copyright 2004 by Pawel Jochym\n"
72 "Copyright 2004-2016 by Udi Fuchs",
73 "ufraw-" VERSION,
74 "raw image",
75 NULL,
76 GIMP_PLUGIN,
77 G_N_ELEMENTS(load_args),
78 G_N_ELEMENTS(load_return_vals),
79 load_args,
80 load_return_vals);
81
82 #if HAVE_GIMP_2_9
83 gimp_register_magic_load_handler("file_ufraw_load",
84 (char *)raw_ext,
85 "",
86 "0,string,II*\\0,"
87 "0,string,MM\\0*,"
88 "0,string,<?xml");
89 #else
90 gimp_register_load_handler("file_ufraw_load", (char *)raw_ext, "");
91 #endif
92
93 gimp_install_procedure("file_ufraw_load_thumb",
94 "Loads thumbnails from digital camera raw files.",
95 "Loads thumbnails from digital camera raw files.",
96 "Udi Fuchs",
97 "Copyright 2004-2016 by Udi Fuchs",
98 "ufraw-" VERSION,
99 NULL,
100 NULL,
101 GIMP_PLUGIN,
102 G_N_ELEMENTS(thumb_args),
103 G_N_ELEMENTS(thumb_return_vals),
104 thumb_args, thumb_return_vals);
105
106 gimp_register_thumbnail_loader("file_ufraw_load",
107 "file_ufraw_load_thumb");
108 }
109
110 char *ufraw_binary;
111 gboolean sendToGimpMode;
112
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)113 void run(const gchar *name,
114 gint nparams,
115 const GimpParam *param,
116 gint *nreturn_vals,
117 GimpParam **return_vals)
118 {
119 // TODO: Check if the static variable here is really needed.
120 // In any case this should cause no issues with threads.
121 static GimpParam values[4];
122 GimpRunMode run_mode;
123 char *filename;
124 int size;
125 ufraw_data *uf;
126 conf_data rc;
127 int status;
128
129 #if !GLIB_CHECK_VERSION(2,31,0)
130 g_thread_init(NULL);
131 #endif
132 #ifndef _WIN32
133 gdk_threads_init();
134 gdk_threads_enter();
135 #endif
136 ufraw_binary = g_path_get_basename(gimp_get_progname());
137 uf_init_locale(gimp_get_progname());
138 #if HAVE_GIMP_2_9
139 gegl_init(NULL, NULL);
140 #endif
141
142 *nreturn_vals = 1;
143 *return_vals = values;
144
145 if (!strcmp(name, "file_ufraw_load_thumb")) {
146 run_mode = 0;
147 filename = param[0].data.d_string;
148 size = param[1].data.d_int32;
149 } else if (!strcmp(name, "file_ufraw_load")) {
150 run_mode = (GimpRunMode)param[0].data.d_int32;
151 filename = param[1].data.d_string;
152 size = 0;
153 } else {
154 values[0].type = GIMP_PDB_STATUS;
155 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
156 #ifndef _WIN32
157 gdk_threads_leave();
158 #endif
159 return;
160 }
161 gboolean loadThumbnail = size > 0;
162 char *gtkrcfile = g_build_filename(uf_get_home_dir(),
163 ".ufraw-gtkrc", NULL);
164 gtk_rc_add_default_file(gtkrcfile);
165 g_free(gtkrcfile);
166 gimp_ui_init("ufraw-gimp", TRUE);
167
168 uf = ufraw_open(filename);
169 /* if UFRaw fails on jpg/jpeg or tif/tiff then open with GIMP */
170 if (uf == NULL) {
171 if (!strcasecmp(filename + strlen(filename) - 4, ".jpg") ||
172 !strcasecmp(filename + strlen(filename) - 5, ".jpeg")) {
173 if (loadThumbnail)
174 *return_vals = gimp_run_procedure2("file_jpeg_load_thumb",
175 nreturn_vals, nparams, param);
176 else
177 *return_vals = gimp_run_procedure2("file_jpeg_load",
178 nreturn_vals, nparams, param);
179 #ifndef _WIN32
180 gdk_threads_leave();
181 #endif
182 return;
183 } else if (!strcasecmp(filename + strlen(filename) - 4, ".tif") ||
184 !strcasecmp(filename + strlen(filename) - 5, ".tiff")) {
185 if (!loadThumbnail)
186 *return_vals = gimp_run_procedure2("file_tiff_load",
187 nreturn_vals, nparams, param);
188 else {
189 /* There is no "file_tiff_load_thumb".
190 * The call to "file_ufraw_load" will handle the thumbnail */
191 /* Following is another solution for tiff thumbnails
192 GimpParam tiffParam[3];
193 tiffParam[0].type = GIMP_PDB_INT32;
194 tiffParam[0].data.d_int32 = GIMP_RUN_NONINTERACTIVE;
195 tiffParam[1].type = GIMP_PDB_STRING;
196 tiffParam[1].data.d_string = filename;
197 tiffParam[2].type = GIMP_PDB_STRING;
198 tiffParam[2].data.d_string = filename;
199 *return_vals = gimp_run_procedure2 ("file_tiff_load",
200 nreturn_vals, 3, tiffParam);
201 */
202 }
203 #ifndef _WIN32
204 gdk_threads_leave();
205 #endif
206 return;
207 } else {
208 /* Don't issue a message on thumbnail failure, since ufraw-gimp
209 * will be called again with "file_ufraw_load" */
210 if (loadThumbnail) {
211 #ifndef _WIN32
212 gdk_threads_leave();
213 #endif
214 return;
215 }
216 ufraw_icons_init();
217 GtkWidget *dummyWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
218 gtk_window_set_icon_name(GTK_WINDOW(dummyWindow), "ufraw");
219 ufraw_message(UFRAW_SET_PARENT, (char *)dummyWindow);
220
221 ufraw_message(UFRAW_REPORT, NULL);
222 values[0].type = GIMP_PDB_STATUS;
223 /* With GIMP_PDB_CANCEL, Gimp won't issue a warning */
224 values[0].data.d_status = GIMP_PDB_CANCEL;
225
226 gtk_widget_destroy(dummyWindow);
227 #ifndef _WIN32
228 gdk_threads_leave();
229 #endif
230 return;
231 }
232 }
233 /* Load $HOME/.ufrawrc */
234 conf_load(&rc, NULL);
235
236 ufraw_config(uf, &rc, NULL, NULL);
237 sendToGimpMode = (uf->conf->createID == send_id);
238 #if !HAVE_GIMP_2_9
239 if (loadThumbnail) {
240 uf->conf->size = size;
241 uf->conf->embeddedImage = TRUE;
242 }
243 #else
244 if (run_mode == GIMP_RUN_NONINTERACTIVE) uf->conf->shrink = 8;
245 #endif
246 /* UFRaw already issues warnings.
247 * With GIMP_PDB_CANCEL, Gimp won't issue another one. */
248 values[0].type = GIMP_PDB_STATUS;
249 values[0].data.d_status = GIMP_PDB_CANCEL;
250 /* BUG - what should be done with GIMP_RUN_WITH_LAST_VALS */
251 if (run_mode == GIMP_RUN_INTERACTIVE &&
252 !loadThumbnail && !sendToGimpMode) {
253 /* Show the preview in interactive mode, unless if we are
254 * in thumbnail mode or 'send to gimp' mode. */
255 status = ufraw_preview(uf, &rc, ufraw_gimp_plugin, ufraw_save_gimp_image);
256 } else {
257 if (sendToGimpMode) {
258 char *text = g_strdup_printf(_("Loading raw file '%s'"),
259 uf->filename);
260 gimp_progress_init(text);
261 g_free(text);
262 }
263 if (sendToGimpMode) gimp_progress_update(0.1);
264 status = ufraw_load_raw(uf);
265 if (status != UFRAW_SUCCESS) {
266 values[0].type = GIMP_PDB_STATUS;
267 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
268 #ifndef _WIN32
269 gdk_threads_leave();
270 #endif
271 return;
272 }
273 if (sendToGimpMode) gimp_progress_update(0.3);
274 ufraw_save_gimp_image(uf, NULL);
275 if (sendToGimpMode) gimp_progress_update(1.0);
276 ufraw_close_darkframe(uf->conf);
277 ufraw_close(uf);
278 /* To make sure we don't delete the raw file by mistake we check
279 * that the file is really an ID file. */
280 if (sendToGimpMode &&
281 strcasecmp(filename + strlen(filename) - 6, ".ufraw") == 0)
282 g_unlink(filename);
283 }
284 if (status != UFRAW_SUCCESS || uf->gimpImage == -1) {
285 values[0].type = GIMP_PDB_STATUS;
286 if (status == UFRAW_CANCEL)
287 values[0].data.d_status = GIMP_PDB_CANCEL;
288 else
289 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
290 #ifndef _WIN32
291 gdk_threads_leave();
292 #endif
293 return;
294 }
295 *nreturn_vals = 2;
296 values[0].type = GIMP_PDB_STATUS;
297 values[0].data.d_status = GIMP_PDB_SUCCESS;
298 values[1].type = GIMP_PDB_IMAGE;
299 values[1].data.d_image = uf->gimpImage;
300 if (loadThumbnail) {
301 *nreturn_vals = 4;
302 values[2].type = GIMP_PDB_INT32;
303 values[2].data.d_int32 = uf->initialWidth;
304 values[3].type = GIMP_PDB_INT32;
305 values[3].data.d_int32 = uf->initialHeight;
306 }
307 #ifndef _WIN32
308 gdk_threads_leave();
309 #endif
310 return;
311 }
312
gimp_row_writer(ufraw_data * uf,void * volatile out,void * pixbuf,int row,int width,int height,int grayscale,int bitDepth)313 int gimp_row_writer(ufraw_data *uf, void *volatile out, void *pixbuf,
314 int row, int width, int height, int grayscale, int bitDepth)
315 {
316 (void)uf;
317 (void)grayscale;
318 (void)bitDepth;
319
320 #if HAVE_GIMP_2_9
321 gegl_buffer_set(out, GEGL_RECTANGLE(0, row, width, height),
322 0, NULL, pixbuf,
323 GEGL_AUTO_ROWSTRIDE);
324 #else
325 gimp_pixel_rgn_set_rect(out, pixbuf, 0, row, width, height);
326 #endif
327
328 return UFRAW_SUCCESS;
329 }
330
ufraw_save_gimp_image(ufraw_data * uf,GtkWidget * widget)331 long ufraw_save_gimp_image(ufraw_data *uf, GtkWidget *widget)
332 {
333 #if HAVE_GIMP_2_9
334 GeglBuffer *buffer;
335 #else
336 GimpDrawable *drawable;
337 GimpPixelRgn pixel_region;
338 int tile_height, row, nrows;
339 #endif
340 gint32 layer;
341 UFRectangle Crop;
342 int depth;
343 (void)widget;
344
345 uf->gimpImage = -1;
346
347 if (uf->conf->embeddedImage) {
348 if (ufraw_convert_embedded(uf) != UFRAW_SUCCESS)
349 return UFRAW_ERROR;
350 Crop.height = uf->thumb.height;
351 Crop.width = uf->thumb.width;
352 Crop.y = 0;
353 Crop.x = 0;
354 depth = 3;
355 } else {
356 if (ufraw_convert_image(uf) != UFRAW_SUCCESS)
357 return UFRAW_ERROR;
358 ufraw_get_scaled_crop(uf, &Crop);
359 #if HAVE_GIMP_2_9
360 if (uf->conf->profile[out_profile]
361 [uf->conf->profileIndex[out_profile]].BitDepth == 16)
362 depth = 6;
363 else
364 depth = 3;
365 #else
366 depth = 3;
367 #endif
368 }
369 #if HAVE_GIMP_2_9
370 uf->gimpImage =
371 gimp_image_new_with_precision(Crop.width, Crop.height, GIMP_RGB,
372 depth == 3 ? GIMP_PRECISION_U8_GAMMA :
373 GIMP_PRECISION_U16_GAMMA);
374 #else
375 uf->gimpImage = gimp_image_new(Crop.width, Crop.height, GIMP_RGB);
376 #endif
377 if (uf->gimpImage == -1) {
378 ufraw_message(UFRAW_ERROR, _("Can't allocate new image."));
379 return UFRAW_ERROR;
380 }
381 gimp_image_set_filename(uf->gimpImage, uf->filename);
382
383 /* Create the "background" layer to hold the image... */
384 layer = gimp_layer_new(uf->gimpImage, _("Background"), Crop.width,
385 Crop.height, GIMP_RGB_IMAGE, 100.0,
386 GIMP_NORMAL_MODE);
387 #if defined(GIMP_CHECK_VERSION) && GIMP_CHECK_VERSION(2,7,3)
388 gimp_image_insert_layer(uf->gimpImage, layer, 0, 0);
389 #else
390 gimp_image_add_layer(uf->gimpImage, layer, 0);
391 #endif
392
393 /* Get the drawable and set the pixel region for our load... */
394 #if HAVE_GIMP_2_9
395 buffer = gimp_drawable_get_buffer(layer);
396 #else
397 drawable = gimp_drawable_get(layer);
398 gimp_pixel_rgn_init(&pixel_region, drawable, 0, 0, drawable->width,
399 drawable->height, TRUE, FALSE);
400 tile_height = gimp_tile_height();
401 #endif
402
403 if (uf->conf->embeddedImage) {
404 #if HAVE_GIMP_2_9
405 gegl_buffer_set(buffer,
406 GEGL_RECTANGLE(0, 0, Crop.width, Crop.height),
407 0, NULL, uf->thumb.buffer,
408 GEGL_AUTO_ROWSTRIDE);
409 #else
410 for (row = 0; row < Crop.height; row += tile_height) {
411 nrows = MIN(Crop.height - row, tile_height);
412 gimp_pixel_rgn_set_rect(&pixel_region,
413 uf->thumb.buffer + 3 * row * Crop.width, 0, row, Crop.width, nrows);
414 }
415 #endif
416 } else {
417 #if HAVE_GIMP_2_9
418 ufraw_write_image_data(uf, buffer, &Crop, depth == 3 ? 8 : 16, 0,
419 gimp_row_writer);
420 #else
421 ufraw_write_image_data(uf, &pixel_region, &Crop, depth == 3 ? 8 : 16, 0,
422 gimp_row_writer);
423 #endif
424 }
425 #if HAVE_GIMP_2_9
426 gegl_buffer_flush(buffer);
427 #else
428 gimp_drawable_flush(drawable);
429 gimp_drawable_detach(drawable);
430 #endif
431
432 if (uf->conf->embeddedImage) return UFRAW_SUCCESS;
433
434 ufraw_exif_prepare_output(uf);
435 if (uf->outputExifBuf != NULL) {
436 if (uf->outputExifBufLen > 65533) {
437 ufraw_message(UFRAW_SET_WARNING,
438 _("EXIF buffer length %d, too long, ignored."),
439 uf->outputExifBufLen);
440 } else {
441 #if HAVE_GIMP_2_9
442 GimpMetadata *metadata;
443 metadata = gimp_metadata_new();
444 if (gimp_metadata_set_from_exif(metadata,
445 uf->outputExifBuf,
446 uf->outputExifBufLen,
447 NULL)) {
448 gimp_image_set_metadata(uf->gimpImage, metadata);
449 }
450 g_object_unref(metadata);
451 #else // !HAVE_GIMP_2_9
452 GimpParasite *exif_parasite;
453
454 exif_parasite = gimp_parasite_new("exif-data",
455 GIMP_PARASITE_PERSISTENT, uf->outputExifBufLen, uf->outputExifBuf);
456 #if defined(GIMP_CHECK_VERSION) && GIMP_CHECK_VERSION(2,8,0)
457 gimp_image_attach_parasite(uf->gimpImage, exif_parasite);
458 #else
459 gimp_image_parasite_attach(uf->gimpImage, exif_parasite);
460 #endif
461 gimp_parasite_free(exif_parasite);
462
463 #if defined(GIMP_CHECK_VERSION) && GIMP_CHECK_VERSION(2,8,0)
464 {
465 GimpParam *return_vals;
466 gint nreturn_vals;
467 return_vals = gimp_run_procedure("plug-in-metadata-decode-exif",
468 &nreturn_vals,
469 GIMP_PDB_IMAGE, uf->gimpImage,
470 GIMP_PDB_INT32, 7,
471 GIMP_PDB_INT8ARRAY, "unused",
472 GIMP_PDB_END);
473 if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) {
474 g_warning("UFRaw Exif -> XMP Merge failed");
475 }
476 }
477 #endif
478 #endif // HAVE_GIMP_2_9
479 }
480 }
481 /* Create "icc-profile" parasite from output profile
482 * if it is not the internal sRGB.*/
483 if (strcmp(uf->developer->profileFile[out_profile], "")) {
484 char *buf;
485 gsize len;
486 if (g_file_get_contents(uf->developer->profileFile[out_profile],
487 &buf, &len, NULL)) {
488 GimpParasite *icc_parasite;
489 icc_parasite = gimp_parasite_new("icc-profile",
490 GIMP_PARASITE_PERSISTENT, len, buf);
491 #if defined(GIMP_CHECK_VERSION) && GIMP_CHECK_VERSION(2,8,0)
492 gimp_image_attach_parasite(uf->gimpImage, icc_parasite);
493 #else
494 gimp_image_parasite_attach(uf->gimpImage, icc_parasite);
495 #endif
496 gimp_parasite_free(icc_parasite);
497 g_free(buf);
498 } else {
499 ufraw_message(UFRAW_WARNING,
500 _("Failed to embed output profile '%s' in image."),
501 uf->developer->profileFile[out_profile]);
502 }
503 }
504 return UFRAW_SUCCESS;
505 }
506