2
* Copyright (C) 2006-2009 Anders Brander <anders@brander.dk> and
3
* Anders Kvist <akv@lnxbx.dk>
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
#include <glib/gstdio.h>
22
#include <math.h> /* pow() */
23
#include <string.h> /* memset() */
26
#include "rawstudio.h"
27
#include "gtk-interface.h"
28
#include "gtk-helper.h"
32
#include "tiff-meta.h"
33
#include "ciff-meta.h"
38
#include "conf_interface.h"
46
#include "rs-color-transform.h"
47
#include "rs-preview-widget.h"
48
#include "rs-histogram.h"
53
#include "rs-metadata.h"
54
#include "rs-filetypes.h"
57
static void photo_settings_changed(RS_PHOTO *photo, RSSettingsMask mask, RS_BLOB *rs);
58
static void photo_spatial_changed(RS_PHOTO *photo, RS_BLOB *rs);
59
static void rs_gdk_load_meta(const gchar *src, RSMetadata *metadata);
61
RS_FILETYPE *filetypes;
64
rs_add_filetype(gchar *id, gint filetype, const gchar *ext, gchar *description,
65
RS_IMAGE16 *(*load)(const gchar *, gboolean),
66
void (*load_meta)(const gchar *, RSMetadata *),
67
gboolean (*save)(RS_PHOTO *photo, const gchar *filename, gint filetype, gint width, gint height, gboolean keep_aspect, gdouble scale, gint snapshot, RS_CMS *cms))
69
RS_FILETYPE *cur = filetypes;
71
cur = filetypes = g_malloc(sizeof(RS_FILETYPE));
74
while (cur->next) cur = cur->next;
75
cur->next = g_malloc(sizeof(RS_FILETYPE));
79
cur->filetype = filetype;
81
cur->description = description;
88
rs_init_filetypes(void)
93
#define REGISTER_FILETYPE(extension, description, load, meta) do { \
94
rs_filetype_register_loader(extension, description, load, 10); \
95
rs_filetype_register_meta_loader(extension, description, meta, 10); \
98
/* Raw file formats */
99
REGISTER_FILETYPE(".cr2", _("Canon CR2"), rs_image16_open_dcraw, rs_tiff_load_meta);
100
REGISTER_FILETYPE(".crw", _("Canon CIFF"), rs_image16_open_dcraw, rs_ciff_load_meta);
101
REGISTER_FILETYPE(".nef", _("Nikon NEF"), rs_image16_open_dcraw, rs_tiff_load_meta);
102
REGISTER_FILETYPE(".mrw", _("Minolta raw"), rs_image16_open_dcraw, rs_mrw_load_meta);
103
REGISTER_FILETYPE(".tif", _("Canon TIFF"), rs_image16_open_dcraw, rs_tiff_load_meta);
104
REGISTER_FILETYPE(".arw", _("Sony"), rs_image16_open_dcraw, rs_sony_load_meta);
105
REGISTER_FILETYPE(".sr2", _("Sony"), rs_image16_open_dcraw, rs_sony_load_meta);
106
REGISTER_FILETYPE(".srf", _("Sony"), rs_image16_open_dcraw, rs_sony_load_meta);
107
REGISTER_FILETYPE(".kdc", _("Kodak"), rs_image16_open_dcraw, rs_tiff_load_meta);
108
REGISTER_FILETYPE(".dcr", _("Kodak"), rs_image16_open_dcraw, rs_tiff_load_meta);
109
REGISTER_FILETYPE(".x3f", _("Sigma"), rs_image16_open_dcraw, rs_x3f_load_meta);
110
REGISTER_FILETYPE(".orf", _("Olympus"), rs_image16_open_dcraw, rs_tiff_load_meta);
111
REGISTER_FILETYPE(".raw", _("Panasonic raw"), rs_image16_open_dcraw, rs_tiff_load_meta);
112
REGISTER_FILETYPE(".rw2", _("Panasonic raw 2"), rs_image16_open_dcraw, rs_tiff_load_meta);
113
REGISTER_FILETYPE(".pef", _("Pentax raw"), rs_image16_open_dcraw, rs_tiff_load_meta);
114
REGISTER_FILETYPE(".dng", _("Adobe Digital negative"), rs_image16_open_dcraw, rs_tiff_load_meta);
115
REGISTER_FILETYPE(".mef", _("Mamiya"), rs_image16_open_dcraw, rs_tiff_load_meta);
116
REGISTER_FILETYPE(".3fr", _("Hasselblad"), rs_image16_open_dcraw, rs_tiff_load_meta);
117
REGISTER_FILETYPE(".erf", _("Epson"), rs_image16_open_dcraw, rs_tiff_load_meta);
120
REGISTER_FILETYPE(".jpg", _("JPEG (Joint Photographic Experts Group)"), rs_image16_open_gdk, rs_gdk_load_meta);
121
REGISTER_FILETYPE(".png", _("PNG (Portable Network Graphics)"), rs_image16_open_gdk, rs_gdk_load_meta);
123
#undef REGISTER_FILETYPE
125
/* TIFF is special - we need higher priority to try raw first */
126
rs_filetype_register_loader(".tif", _("8-bit TIFF (Tagged Image File Format)"), rs_image16_open_gdk, 20);
127
rs_filetype_register_meta_loader(".tif", _("8-bit TIFF (Tagged Image File Format)"), rs_gdk_load_meta, 20);
129
/* Old-style savers - FIXME: Port to RSFiletype */
131
rs_add_filetype("jpeg", FILETYPE_JPEG, ".jpg", _("JPEG (Joint Photographic Experts Group)"),
132
rs_image16_open_gdk, rs_gdk_load_meta, rs_photo_save);
133
rs_add_filetype("png", FILETYPE_PNG, ".png", _("PNG (Portable Network Graphics)"),
134
rs_image16_open_gdk, rs_gdk_load_meta, rs_photo_save);
135
rs_add_filetype("tiff8", FILETYPE_TIFF8, ".tif", _("8-bit TIFF (Tagged Image File Format)"),
136
rs_image16_open_gdk, rs_gdk_load_meta, rs_photo_save);
137
rs_add_filetype("tiff16", FILETYPE_TIFF16, ".tif", _("16-bit TIFF (Tagged Image File Format)"),
138
rs_image16_open_gdk, rs_gdk_load_meta, rs_photo_save);
146
g_object_unref(rs->photo);
150
photo_settings_changed(RS_PHOTO *photo, RSSettingsMask mask, RS_BLOB *rs)
152
const gint snapshot = mask>>24;
155
/* Is this really safe? */
156
if (snapshot != rs->current_setting)
159
if (photo == rs->photo)
161
/* Update histogram */
162
rs_histogram_set_settings(RS_HISTOGRAM_WIDGET(rs->histogram), rs->photo->settings[rs->current_setting]);
164
/* Update histogram in curve */
165
rs_curve_draw_histogram(RS_CURVE_WIDGET(rs->curve[rs->current_setting]),
166
rs->histogram_dataset,
167
rs->photo->settings[rs->current_setting]);
172
photo_spatial_changed(RS_PHOTO *photo, RS_BLOB *rs)
174
if (photo == rs->photo)
176
/* Update histogram dataset */
177
if (rs->histogram_dataset)
178
rs_image16_free(rs->histogram_dataset);
180
rs->histogram_dataset = rs_image16_transform(photo->input, NULL,
181
NULL, NULL, photo->crop, 250, 250,
182
TRUE, -1.0f, photo->angle, photo->orientation, NULL);
184
rs_histogram_set_image(RS_HISTOGRAM_WIDGET(rs->histogram), rs->histogram_dataset);
186
/* Force update of histograms */
187
photo_settings_changed(photo, MASK_ALL, rs);
192
rs_set_photo(RS_BLOB *rs, RS_PHOTO *photo)
194
g_assert(rs != NULL);
196
/* Unref old photo if any */
198
g_object_unref(rs->photo);
201
/* Apply settings from photo */
202
rs_settings_copy(photo->settings[0], MASK_ALL, rs->settings[0]);
203
rs_settings_copy(photo->settings[1], MASK_ALL, rs->settings[1]);
204
rs_settings_copy(photo->settings[2], MASK_ALL, rs->settings[2]);
206
/* make sure we're synchronized */
207
rs_settings_link(rs->settings[0], photo->settings[0]);
208
rs_settings_link(rs->settings[1], photo->settings[1]);
209
rs_settings_link(rs->settings[2], photo->settings[2]);
212
rs_settings_link(photo->settings[0], rs->settings[0]);
213
rs_settings_link(photo->settings[1], rs->settings[1]);
214
rs_settings_link(photo->settings[2], rs->settings[2]);
216
/* Set photo in preview-widget */
217
rs_preview_widget_set_photo(RS_PREVIEW_WIDGET(rs->preview), photo);
219
/* Save photo in blob */
224
g_signal_connect(G_OBJECT(rs->photo), "settings-changed", G_CALLBACK(photo_settings_changed), rs);
225
g_signal_connect(G_OBJECT(rs->photo), "spatial-changed", G_CALLBACK(photo_spatial_changed), rs);
227
/* Force an update! */
228
photo_spatial_changed(rs->photo, rs);
233
rs_set_snapshot(RS_BLOB *rs, gint snapshot)
235
g_assert (rs != NULL);
236
g_assert ((snapshot>=0) && (snapshot<=2));
238
rs->current_setting = snapshot;
240
/* Switch preview widget to the correct snapshot */
241
rs_preview_widget_set_snapshot(RS_PREVIEW_WIDGET(rs->preview), 0, snapshot);
243
/* Force an update */
245
photo_settings_changed(rs->photo, MASK_ALL|(snapshot<<24), rs);
249
rs_photo_save(RS_PHOTO *photo, const gchar *filename, gint filetype, gint width, gint height, gboolean keep_aspect, gdouble scale, gint snapshot, RS_CMS *cms)
253
RS_IMAGE16 *sharpened;
256
gboolean uncompressed_tiff = FALSE;
257
RSColorTransform *rct;
258
void *transform = NULL;
261
g_assert(RS_IS_PHOTO(photo));
262
g_assert(filename != NULL);
264
rs_image16_demosaic(photo->input, RS_DEMOSAIC_PPG);
266
sharpened = rs_image16_sharpen(photo->input, NULL, photo->settings[snapshot]->sharpen, NULL);
268
/* transform and crop */
269
rsi = rs_image16_transform(sharpened, NULL,
270
NULL, NULL, photo->crop, width, height, keep_aspect, scale,
271
photo->angle, photo->orientation, NULL);
273
rs_image16_unref(sharpened);
276
transform = rs_cms_get_transform(cms, TRANSFORM_EXPORT);
278
/* Initialize color transform */
279
rct = rs_color_transform_new();
280
rs_color_transform_set_cms_transform(rct, transform);
281
rs_color_transform_set_adobe_matrix(rct, &photo->metadata->adobe_coeff);
282
rs_color_transform_set_from_settings(rct, photo->settings[snapshot], MASK_ALL);
283
rs_color_transform_set_output_format(rct, 8);
289
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rsi->w, rsi->h);
290
rs_color_transform_transform(rct, rsi->w, rsi->h, rsi->pixels,
291
rsi->rowstride, gdk_pixbuf_get_pixels(pixbuf), gdk_pixbuf_get_rowstride(pixbuf));
293
rs_conf_get_integer(CONF_EXPORT_JPEG_QUALITY, &quality);
296
else if (quality < 0)
299
rs_jpeg_save(pixbuf, filename, quality, rs_cms_get_profile_filename(cms, CMS_PROFILE_EXPORT));
300
g_object_unref(pixbuf);
303
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rsi->w, rsi->h);
304
rs_color_transform_transform(rct, rsi->w, rsi->h, rsi->pixels, rsi->rowstride,
305
gdk_pixbuf_get_pixels(pixbuf), gdk_pixbuf_get_rowstride(pixbuf));
306
gdk_pixbuf_save(pixbuf, filename, "png", NULL, NULL);
307
g_object_unref(pixbuf);
310
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rsi->w, rsi->h);
311
rs_conf_get_boolean(CONF_EXPORT_TIFF_UNCOMPRESSED, &uncompressed_tiff);
312
rs_color_transform_transform(rct, rsi->w, rsi->h, rsi->pixels,
313
rsi->rowstride, gdk_pixbuf_get_pixels(pixbuf), gdk_pixbuf_get_rowstride(pixbuf));
314
rs_tiff8_save(pixbuf, filename, rs_cms_get_profile_filename(cms, CMS_PROFILE_EXPORT), uncompressed_tiff);
315
g_object_unref(pixbuf);
317
case FILETYPE_TIFF16:
318
rs_conf_get_boolean(CONF_EXPORT_TIFF_UNCOMPRESSED, &uncompressed_tiff);
319
image16 = rs_image16_new(rsi->w, rsi->h, 3, 3);
320
rs_color_transform_set_output_format(rct, 16);
321
rs_color_transform_transform(rct, rsi->w, rsi->h,
322
rsi->pixels, rsi->rowstride,
323
image16->pixels, image16->rowstride*2);
324
rs_tiff16_save(image16, filename, rs_cms_get_profile_filename(cms, CMS_PROFILE_EXPORT), uncompressed_tiff);
325
rs_image16_free(image16);
329
rs_image16_free(rsi);
332
photo->exported = TRUE;
333
rs_cache_save(photo, MASK_ALL);
335
/* Set the exported flag */
336
rs_store_set_flags(NULL, photo->filename, NULL, NULL, &photo->exported);
338
/* Save exif for JPEG files */
339
if (filetype == FILETYPE_JPEG)
341
exif = rs_exif_load_from_file(photo->filename);
344
rs_exif_add_to_file(exif, filename);
357
rs = g_malloc(sizeof(RS_BLOB));
358
rs->histogram_dataset = NULL;
359
rs->settings_buffer = NULL;
361
rs->queue = rs_batch_new_queue();
362
rs->current_setting = 0;
364
rs->settings[c] = rs_settings_new();
369
rs_gdk_load_meta(const gchar *src, RSMetadata *metadata)
371
metadata->thumbnail = gdk_pixbuf_new_from_file_at_size(src, 128, 128, NULL);
375
rs_white_black_point(RS_BLOB *rs)
381
gdouble black_threshold = 0.003; // Percent underexposed pixels
382
gdouble white_threshold = 0.01; // Percent overexposed pixels
386
RSColorTransform *rct;
388
rct = rs_color_transform_new();
389
rs_color_transform_set_from_settings(rct, rs->photo->settings[rs->current_setting], MASK_ALL ^ MASK_CURVE);
390
rs_color_transform_make_histogram(rct, rs->histogram_dataset, hist);
393
// calculate black point
395
total += hist[R][i]+hist[G][i]+hist[B][i];
396
if ((total/3) > ((250*250*3)/100*black_threshold))
400
blackpoint = (gdouble) i / (gdouble) 255;
402
// calculate white point
405
total += hist[R][i]+hist[G][i]+hist[B][i];
406
if ((total/3) > ((250*250*3)/100*white_threshold))
410
whitepoint = (gdouble) i / (gdouble) 255;
412
rs_curve_widget_move_knot(RS_CURVE_WIDGET(rs->curve[rs->current_setting]),0,blackpoint,0.0);
413
rs_curve_widget_move_knot(RS_CURVE_WIDGET(rs->curve[rs->current_setting]),-1,whitepoint,1.0);
418
* This is a very simple regression test for Rawstudio. Filenames will be read
419
* from "testimages" in the current directory, one filename per line, and a
420
* small series of tests will be carried out for each filename. Output can be
421
* piped to a file for further processing.
426
gchar *filename, *basename;
428
GIOChannel *io = g_io_channel_new_file("testimages", "r", NULL);
429
gint sum, good = 0, bad = 0;
431
printf("basename, load, filetype, thumb, meta, make, a-make, a-model, aperture, iso, s-speed, wb, f-length\n");
432
while (G_IO_STATUS_EOF != g_io_channel_read_line(io, &filename, NULL, NULL, NULL))
434
gboolean filetype_ok = FALSE;
435
gboolean load_ok = FALSE;
436
gboolean thumbnail_ok = FALSE;
437
gboolean load_meta_ok = FALSE;
438
gboolean make_ok = FALSE;
439
gboolean make_ascii_ok = FALSE;
440
gboolean model_ascii_ok = FALSE;
441
gboolean aperture_ok = FALSE;
442
gboolean iso_ok = FALSE;
443
gboolean shutterspeed_ok = FALSE;
444
gboolean wb_ok = FALSE;
445
gboolean focallength_ok = FALSE;
447
g_strstrip(filename);
449
if (rs_filetype_can_load(filename))
451
RS_PHOTO *photo = NULL;
453
photo = rs_photo_load_from_file(filename, TRUE);
457
g_object_unref(photo);
460
RSMetadata *metadata = rs_metadata_new_from_file(filename);
464
if (metadata->make != MAKE_UNKNOWN)
466
if (metadata->make_ascii != NULL)
467
make_ascii_ok = TRUE;
468
if (metadata->model_ascii != NULL)
469
model_ascii_ok = TRUE;
470
if (metadata->aperture > 0.0)
472
if (metadata->iso > 0)
474
if (metadata->shutterspeed > 1.0)
475
shutterspeed_ok = TRUE;
476
if (metadata->cam_mul[0] > 0.1 && metadata->cam_mul[0] != 1.0)
478
if (metadata->focallength > 0.0)
479
focallength_ok = TRUE;
481
/* FIXME: Port to RSFiletype */
482
pixbuf = rs_metadata_get_thumbnail(metadata);
486
g_object_unref(pixbuf);
488
g_object_unref(metadata);
492
basename = g_path_get_basename(filename);
493
printf("%s, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
528
printf("Passed: %d Failed: %d (%d%%)\n", good, bad, (good*100)/(good+bad));
529
g_io_channel_shutdown(io, TRUE, NULL);
533
/* We use out own reentrant locking for GDK/GTK */
535
static GStaticRecMutex gdk_lock = G_STATIC_REC_MUTEX_INIT;
540
g_static_rec_mutex_lock (&gdk_lock);
546
g_static_rec_mutex_unlock (&gdk_lock);
551
main(int argc, char **argv)
555
gboolean do_test = FALSE;
557
gboolean use_system_theme = DEFAULT_CONF_USE_SYSTEM_THEME;
559
while ((opt = getopt(argc, argv, "nt")) != -1) {
570
gdk_threads_set_lock_functions(rs_gdk_lock, rs_gdk_unlock);
574
/* Bind default C functions */
575
rs_bind_default_functions();
577
/* Bind optimized functions if any */
578
if (likely(optimized)) {
579
rs_bind_optimized_functions();
583
bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
584
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
585
textdomain(GETTEXT_PACKAGE);
588
/* Make sure the GType system is initialized */
591
/* Switch to rawstudio theme before any drawing if needed */
592
rs_conf_get_boolean_with_default(CONF_USE_SYSTEM_THEME, &use_system_theme, DEFAULT_CONF_USE_SYSTEM_THEME);
593
if (!use_system_theme)
594
gui_select_theme(RAWSTUDIO_THEME);
597
gtk_init(&argc, &argv);
601
rs->queue->cms = rs->cms = rs_cms_init();
606
gui_init(argc, argv, rs);
608
/* This is so fucking evil, but Rawstudio will deadlock in some GTK atexit() function from time to time :-/ */