1 |
/* |
2 |
Raw photo loader plugin for The GIMP |
3 |
by Dave Coffin at cybercom dot net, user dcoffin |
4 |
http://www.cybercom.net/~dcoffin/ |
5 |
|
6 |
$Revision: 1.32 $ |
7 |
$Date: 2008/09/16 05:41:39 $ |
8 |
|
9 |
This code is licensed under the same terms as The GIMP. |
10 |
To simplify maintenance, it calls my command-line "dcraw" |
11 |
program to do the actual decoding. |
12 |
|
13 |
To install locally: |
14 |
gimptool --install rawphoto.c |
15 |
|
16 |
To install globally: |
17 |
gimptool --install-admin rawphoto.c |
18 |
|
19 |
To build without installing: |
20 |
gcc -o rawphoto rawphoto.c `gtk-config --cflags --libs` -lgimp -lgimpui |
21 |
*/ |
22 |
|
23 |
#include <stdio.h> |
24 |
#include <stdlib.h> |
25 |
#include <string.h> |
26 |
|
27 |
#include <gtk/gtk.h> |
28 |
|
29 |
#include <libgimp/gimp.h> |
30 |
#include <libgimp/gimpui.h> |
31 |
|
32 |
#if GIMP_CHECK_VERSION(1,3,2) |
33 |
#define GimpRunModeType GimpRunMode |
34 |
#endif |
35 |
|
36 |
#if GIMP_CHECK_VERSION(1,3,17) |
37 |
#define RAWPHOTO_CONST const |
38 |
#else |
39 |
#define RAWPHOTO_CONST |
40 |
#endif |
41 |
|
42 |
#include <locale.h> |
43 |
#include <libintl.h> |
44 |
#define _(String) gettext(String) |
45 |
|
46 |
#define PLUG_IN_VERSION "1.1.20 - 16 September 2008" |
47 |
|
48 |
static void query(void); |
49 |
static void run(RAWPHOTO_CONST gchar *name, |
50 |
gint nparams, |
51 |
RAWPHOTO_CONST GimpParam *param, |
52 |
gint *nreturn_vals, |
53 |
GimpParam **return_vals); |
54 |
|
55 |
static gint load_dialog (gchar *name); |
56 |
static gint32 load_image (gchar *filename); |
57 |
|
58 |
GimpPlugInInfo PLUG_IN_INFO = |
59 |
{ |
60 |
NULL, /* init_procedure */ |
61 |
NULL, /* quit_procedure */ |
62 |
query, /* query_procedure */ |
63 |
run, /* run_procedure */ |
64 |
}; |
65 |
|
66 |
static struct { |
67 |
gboolean check_val[6]; |
68 |
gfloat spin_val[2]; |
69 |
} cfg = { |
70 |
{ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, |
71 |
{ 1, 0 } |
72 |
}; |
73 |
|
74 |
MAIN () |
75 |
|
76 |
static void query (void) |
77 |
{ |
78 |
static GimpParamDef load_args[] = |
79 |
{ |
80 |
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, |
81 |
{ GIMP_PDB_STRING, "filename", "The name of the file to load" }, |
82 |
{ GIMP_PDB_STRING, "raw_filename", "The name of the file to load" }, |
83 |
}; |
84 |
static GimpParamDef load_return_vals[] = |
85 |
{ |
86 |
{ GIMP_PDB_IMAGE, "image", "Output image" }, |
87 |
}; |
88 |
|
89 |
static gint num_load_args = |
90 |
sizeof load_args / sizeof load_args[0]; |
91 |
static gint num_load_return_vals = |
92 |
sizeof load_return_vals / sizeof load_return_vals[0]; |
93 |
|
94 |
gimp_install_procedure ("file_rawphoto_load", |
95 |
"Loads raw digital camera files", |
96 |
"This plug-in loads raw digital camera files.", |
97 |
"Dave Coffin at cybercom dot net, user dcoffin", |
98 |
"Copyright 2003-2008 by Dave Coffin", |
99 |
PLUG_IN_VERSION, |
100 |
"<Load>/rawphoto", |
101 |
NULL, |
102 |
GIMP_PLUGIN, |
103 |
num_load_args, |
104 |
num_load_return_vals, |
105 |
load_args, |
106 |
load_return_vals); |
107 |
|
108 |
gimp_register_load_handler ("file_rawphoto_load", |
109 |
"3fr,arw,bay,bmq,cine,cr2,crw,cs1,dc2,dcr,dng,erf,fff,hdr,ia,jpg,k25,kc2,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,pxn,qtk,raf,raw,rdc,rw2,sr2,srf,sti,tif,x3f", ""); |
110 |
} |
111 |
|
112 |
static void run (RAWPHOTO_CONST gchar *name, |
113 |
gint nparams, |
114 |
RAWPHOTO_CONST GimpParam *param, |
115 |
gint *nreturn_vals, |
116 |
GimpParam **return_vals) |
117 |
{ |
118 |
static GimpParam values[2]; |
119 |
GimpRunModeType run_mode; |
120 |
GimpPDBStatusType status; |
121 |
gint32 image_id = -1; |
122 |
gchar *command, *fname; |
123 |
int stat; |
124 |
|
125 |
*nreturn_vals = 1; |
126 |
*return_vals = values; |
127 |
|
128 |
status = GIMP_PDB_CALLING_ERROR; |
129 |
if (strcmp (name, "file_rawphoto_load")) goto done; |
130 |
|
131 |
status = GIMP_PDB_EXECUTION_ERROR; |
132 |
fname = param[1].data.d_string; |
133 |
command = g_malloc (strlen(fname)+20); |
134 |
if (!command) goto done; |
135 |
/* |
136 |
Is the file really a raw photo? If not, try loading it |
137 |
as a regular JPEG or TIFF. |
138 |
*/ |
139 |
sprintf (command, "dcraw -i '%s'\n",fname); |
140 |
fputs (command, stderr); |
141 |
stat = system (command); |
142 |
g_free (command); |
143 |
if (stat) { |
144 |
if (stat > 0x200) |
145 |
g_message (_("The \"rawphoto\" plugin won't work because " |
146 |
"there is no \"dcraw\" executable in your path.")); |
147 |
if (!strcasecmp (fname + strlen(fname) - 4, ".jpg")) |
148 |
*return_vals = gimp_run_procedure2 |
149 |
("file_jpeg_load", nreturn_vals, nparams, param); |
150 |
else |
151 |
*return_vals = gimp_run_procedure2 |
152 |
("file_tiff_load", nreturn_vals, nparams, param); |
153 |
return; |
154 |
} |
155 |
gimp_get_data ("plug_in_rawphoto", &cfg); |
156 |
status = GIMP_PDB_CANCEL; |
157 |
run_mode = param[0].data.d_int32; |
158 |
if (run_mode == GIMP_RUN_INTERACTIVE) |
159 |
if (!load_dialog (param[1].data.d_string)) goto done; |
160 |
|
161 |
status = GIMP_PDB_EXECUTION_ERROR; |
162 |
image_id = load_image (param[1].data.d_string); |
163 |
if (image_id == -1) goto done; |
164 |
|
165 |
*nreturn_vals = 2; |
166 |
values[1].type = GIMP_PDB_IMAGE; |
167 |
values[1].data.d_image = image_id; |
168 |
status = GIMP_PDB_SUCCESS; |
169 |
gimp_set_data ("plug_in_rawphoto", &cfg, sizeof cfg); |
170 |
|
171 |
done: |
172 |
values[0].type = GIMP_PDB_STATUS; |
173 |
values[0].data.d_status = status; |
174 |
} |
175 |
|
176 |
static gint32 load_image (gchar *filename) |
177 |
{ |
178 |
int tile_height, depth, width, height, row, nrows; |
179 |
FILE *pfp; |
180 |
gint32 image, layer; |
181 |
GimpDrawable *drawable; |
182 |
GimpPixelRgn pixel_region; |
183 |
guchar *pixel; |
184 |
char *command, nl; |
185 |
|
186 |
setlocale (LC_NUMERIC, "C"); |
187 |
command = g_malloc (strlen(filename)+100); |
188 |
if (!command) return -1; |
189 |
sprintf (command, |
190 |
"dcraw -c%s%s%s%s%s%s -b %0.2f -H %d '%s'\n", |
191 |
cfg.check_val[0] ? " -q 0":"", |
192 |
cfg.check_val[1] ? " -h":"", |
193 |
cfg.check_val[2] ? " -f":"", |
194 |
cfg.check_val[3] ? " -d":"", |
195 |
cfg.check_val[4] ? " -a":"", |
196 |
cfg.check_val[5] ? " -w":"", |
197 |
cfg.spin_val[0], (int) cfg.spin_val[1], |
198 |
filename ); |
199 |
fputs (command, stderr); |
200 |
pfp = popen (command, "r"); |
201 |
g_free (command); |
202 |
if (!pfp) { |
203 |
perror ("dcraw"); |
204 |
return -1; |
205 |
} |
206 |
|
207 |
if (fscanf (pfp, "P%d %d %d 255%c", &depth, &width, &height, &nl) != 4 |
208 |
|| (depth-5)/2 ) { |
209 |
pclose (pfp); |
210 |
g_message ("Not a raw digital camera image.\n"); |
211 |
return -1; |
212 |
} |
213 |
|
214 |
depth = depth*2 - 9; |
215 |
image = gimp_image_new (width, height, depth == 3 ? GIMP_RGB : GIMP_GRAY); |
216 |
if (image == -1) { |
217 |
pclose (pfp); |
218 |
g_message ("Can't allocate new image.\n"); |
219 |
return -1; |
220 |
} |
221 |
|
222 |
gimp_image_set_filename (image, filename); |
223 |
|
224 |
/* Create the "background" layer to hold the image... */ |
225 |
layer = gimp_layer_new (image, "Background", width, height, |
226 |
depth == 3 ? GIMP_RGB_IMAGE : GIMP_GRAY_IMAGE, |
227 |
100, GIMP_NORMAL_MODE); |
228 |
gimp_image_add_layer (image, layer, 0); |
229 |
|
230 |
/* Get the drawable and set the pixel region for our load... */ |
231 |
drawable = gimp_drawable_get (layer); |
232 |
gimp_pixel_rgn_init (&pixel_region, drawable, 0, 0, drawable->width, |
233 |
drawable->height, TRUE, FALSE); |
234 |
|
235 |
/* Temporary buffers... */ |
236 |
tile_height = gimp_tile_height(); |
237 |
pixel = g_new (guchar, tile_height * width * depth); |
238 |
|
239 |
/* Load the image... */ |
240 |
for (row = 0; row < height; row += tile_height) { |
241 |
nrows = height - row; |
242 |
if (nrows > tile_height) |
243 |
nrows = tile_height; |
244 |
fread (pixel, width * depth, nrows, pfp); |
245 |
gimp_pixel_rgn_set_rect (&pixel_region, pixel, 0, row, width, nrows); |
246 |
} |
247 |
|
248 |
pclose (pfp); |
249 |
g_free (pixel); |
250 |
|
251 |
gimp_drawable_flush (drawable); |
252 |
gimp_drawable_detach (drawable); |
253 |
|
254 |
return image; |
255 |
} |
256 |
|
257 |
#if !GIMP_CHECK_VERSION(1,3,23) |
258 |
/* this is set to true after OK click in any dialog */ |
259 |
gboolean result = FALSE; |
260 |
|
261 |
static void callback_ok (GtkWidget * widget, gpointer data) |
262 |
{ |
263 |
result = TRUE; |
264 |
gtk_widget_destroy (GTK_WIDGET (data)); |
265 |
} |
266 |
#endif |
267 |
|
268 |
#define NCHECK (sizeof cfg.check_val / sizeof (gboolean)) |
269 |
|
270 |
gint load_dialog (gchar * name) |
271 |
{ |
272 |
GtkWidget *dialog; |
273 |
GtkWidget *table; |
274 |
GtkObject *adj; |
275 |
GtkWidget *widget; |
276 |
int i; |
277 |
static const char *label[9] = |
278 |
{ "Quick interpolation", "Half-size interpolation", |
279 |
"Four color interpolation", "Grayscale document", |
280 |
"Automatic white balance", "Camera white balance", |
281 |
"Brightness", "Highlight mode" }; |
282 |
|
283 |
gimp_ui_init ("rawphoto", TRUE); |
284 |
|
285 |
dialog = gimp_dialog_new (_("Raw Photo Loader " PLUG_IN_VERSION), "rawphoto", |
286 |
#if !GIMP_CHECK_VERSION(1,3,23) |
287 |
gimp_standard_help_func, "rawphoto", |
288 |
GTK_WIN_POS_MOUSE, |
289 |
FALSE, TRUE, FALSE, |
290 |
_("OK"), callback_ok, NULL, NULL, NULL, TRUE, |
291 |
FALSE, _("Cancel"), gtk_widget_destroy, NULL, |
292 |
1, NULL, FALSE, TRUE, NULL); |
293 |
gtk_signal_connect |
294 |
(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); |
295 |
#else |
296 |
NULL, 0, |
297 |
gimp_standard_help_func, "rawphoto", |
298 |
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
299 |
GTK_STOCK_OK, GTK_RESPONSE_OK, |
300 |
NULL); |
301 |
#endif |
302 |
|
303 |
table = gtk_table_new (9, 2, FALSE); |
304 |
gtk_container_set_border_width (GTK_CONTAINER(table), 6); |
305 |
gtk_box_pack_start |
306 |
(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, FALSE, FALSE, 0); |
307 |
gtk_widget_show (table); |
308 |
|
309 |
for (i=0; i < NCHECK; i++) { |
310 |
widget = gtk_check_button_new_with_label |
311 |
(_(label[i])); |
312 |
gtk_toggle_button_set_active |
313 |
(GTK_TOGGLE_BUTTON (widget), cfg.check_val[i]); |
314 |
gtk_table_attach |
315 |
(GTK_TABLE(table), widget, 0, 2, i, i+1, GTK_FILL, GTK_FILL, 0, 0); |
316 |
gtk_signal_connect (GTK_OBJECT (widget), "toggled", |
317 |
GTK_SIGNAL_FUNC (gimp_toggle_button_update), |
318 |
&cfg.check_val[i]); |
319 |
gtk_widget_show (widget); |
320 |
} |
321 |
|
322 |
for (i=NCHECK; i < NCHECK+2; i++) { |
323 |
widget = gtk_label_new (_(label[i])); |
324 |
gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); |
325 |
gtk_misc_set_padding (GTK_MISC (widget), 10, 0); |
326 |
gtk_table_attach |
327 |
(GTK_TABLE(table), widget, 0, 1, i, i+1, GTK_FILL, GTK_FILL, 0, 0); |
328 |
gtk_widget_show (widget); |
329 |
if (i == NCHECK+1) |
330 |
widget = gimp_spin_button_new |
331 |
(&adj, cfg.spin_val[i-NCHECK], 0, 9, 1, 9, 1, 1, 0); |
332 |
else |
333 |
widget = gimp_spin_button_new |
334 |
(&adj, cfg.spin_val[i-NCHECK], 0.01, 4.0, 0.01, 0.1, 0.1, 0.1, 2); |
335 |
gtk_table_attach |
336 |
(GTK_TABLE(table), widget, 1, 2, i, i+1, GTK_FILL, GTK_FILL, 0, 0); |
337 |
gtk_signal_connect (GTK_OBJECT (adj), "value_changed", |
338 |
GTK_SIGNAL_FUNC (gimp_float_adjustment_update), |
339 |
&cfg.spin_val[i-NCHECK]); |
340 |
gtk_widget_show (widget); |
341 |
} |
342 |
|
343 |
gtk_widget_show (dialog); |
344 |
|
345 |
#if !GIMP_CHECK_VERSION(1,3,23) |
346 |
gtk_main(); |
347 |
gdk_flush(); |
348 |
|
349 |
return result; |
350 |
#else |
351 |
i = gimp_dialog_run (GIMP_DIALOG (dialog)); |
352 |
gtk_widget_destroy (dialog); |
353 |
return i == GTK_RESPONSE_OK; |
354 |
#endif |
355 |
} |