2
This file is part of darktable,
3
copyright (c) 2009--2010 johannes hanika.
5
darktable is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
10
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
28
#include "common/colorspaces.h"
29
#include "iop/colorcorrection.h"
30
#include "develop/develop.h"
31
#include "control/control.h"
33
#include "develop/imageop.h"
34
#include "dtgtk/resetlabel.h"
38
#define DT_COLORCORRECTION_INSET 5
39
#define DT_COLORCORRECTION_MAX 40.
43
return _("color correction");
48
return IOP_FLAGS_INCLUDE_IN_STYLES;
54
return IOP_GROUP_COLOR;
58
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *i, void *o, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out)
60
dt_iop_colorcorrection_data_t *d = (dt_iop_colorcorrection_data_t *)piece->data;
61
float *in = (float *)i;
62
float *out = (float *)o;
63
const int ch = piece->colors;
64
for(int k=0; k<roi_out->width*roi_out->height; k++)
67
out[1] = d->saturation*(in[1] + in[0] * d->a_scale + d->a_base);
68
out[2] = d->saturation*(in[2] + in[0] * d->b_scale + d->b_base);
74
void commit_params (struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
76
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)p1;
78
// pull in new params to gegl
79
gegl_node_set(piece->input, "high_a_delta", p->hia, "high_b_delta", p->hib, "low_a_delta", p->loa, "low_b_delta", p->lob, "saturation", p->saturation, NULL);
81
dt_iop_colorcorrection_data_t *d = (dt_iop_colorcorrection_data_t *)piece->data;
82
d->a_scale = (p->hia - p->loa)/100.0;
84
d->b_scale = (p->hib - p->lob)/100.0;
86
d->saturation = p->saturation;
90
void init_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
93
// create part of the gegl pipeline
95
dt_iop_colorcorrection_params_t *default_params = (dt_iop_colorcorrection_params_t *)self->default_params;
96
piece->input = piece->output = gegl_node_new_child(pipe->gegl, "operation", "gegl:whitebalance", "high_a_delta", default_params->hia, "high_b_delta", default_params->hib, "low_a_delta", default_params->loa, "low_b_delta", default_params->lob, "saturation", default_params->saturation, NULL);
98
piece->data = malloc(sizeof(dt_iop_colorcorrection_data_t));
99
self->commit_params(self, self->default_params, pipe, piece);
103
void cleanup_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
106
// clean up everything again.
107
(void)gegl_node_remove_child(pipe->gegl, piece->input);
108
// no free necessary, no data is alloc'ed
114
void gui_update(struct dt_iop_module_t *self)
116
dt_iop_module_t *module = (dt_iop_module_t *)self;
117
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
118
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)module->params;
119
dtgtk_slider_set_value(g->scale5, p->saturation);
120
gtk_widget_queue_draw(self->widget);
123
void init(dt_iop_module_t *module)
125
// module->data = malloc(sizeof(dt_iop_colorcorrection_data_t));
126
module->params = malloc(sizeof(dt_iop_colorcorrection_params_t));
127
module->default_params = malloc(sizeof(dt_iop_colorcorrection_params_t));
128
module->default_enabled = 0;
129
module->priority = 800;
130
module->params_size = sizeof(dt_iop_colorcorrection_params_t);
131
module->gui_data = NULL;
132
dt_iop_colorcorrection_params_t tmp = (dt_iop_colorcorrection_params_t)
136
memcpy(module->params, &tmp, sizeof(dt_iop_colorcorrection_params_t));
137
memcpy(module->default_params, &tmp, sizeof(dt_iop_colorcorrection_params_t));
140
void cleanup(dt_iop_module_t *module)
142
free(module->gui_data);
143
module->gui_data = NULL;
144
free(module->params);
145
module->params = NULL;
148
static void sat_callback (GtkDarktableSlider *slider, gpointer user_data);
149
static gboolean dt_iop_colorcorrection_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
150
static gboolean dt_iop_colorcorrection_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data);
151
static gboolean dt_iop_colorcorrection_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
152
static gboolean dt_iop_colorcorrection_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
153
static gboolean dt_iop_colorcorrection_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data);
154
static gboolean dt_iop_colorcorrection_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data);
156
void gui_init(struct dt_iop_module_t *self)
158
self->gui_data = malloc(sizeof(dt_iop_colorcorrection_gui_data_t));
159
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
160
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)self->params;
162
g->selected = g->dragging = 0;
163
g->press_x = g->press_y = -1;
165
self->widget = GTK_WIDGET(gtk_vbox_new(FALSE, DT_GUI_IOP_MODULE_CONTROL_SPACING));
166
g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
167
GtkWidget *asp = gtk_aspect_frame_new(NULL, 0.5, 0.5, 1.0, TRUE);
168
gtk_box_pack_start(GTK_BOX(self->widget), asp, TRUE, TRUE, 0);
169
gtk_container_add(GTK_CONTAINER(asp), GTK_WIDGET(g->area));
170
gtk_drawing_area_size(g->area, 258, 258);
172
gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK);
173
g_signal_connect (G_OBJECT (g->area), "expose-event",
174
G_CALLBACK (dt_iop_colorcorrection_expose), self);
175
g_signal_connect (G_OBJECT (g->area), "button-press-event",
176
G_CALLBACK (dt_iop_colorcorrection_button_press), self);
177
g_signal_connect (G_OBJECT (g->area), "button-release-event",
178
G_CALLBACK (dt_iop_colorcorrection_button_release), self);
179
g_signal_connect (G_OBJECT (g->area), "motion-notify-event",
180
G_CALLBACK (dt_iop_colorcorrection_motion_notify), self);
181
g_signal_connect (G_OBJECT (g->area), "leave-notify-event",
182
G_CALLBACK (dt_iop_colorcorrection_leave_notify), self);
183
g_signal_connect (G_OBJECT (g->area), "scroll-event",
184
G_CALLBACK (dt_iop_colorcorrection_scrolled), self);
186
g->hbox = GTK_HBOX(gtk_hbox_new(FALSE, 0));
187
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->hbox), TRUE, TRUE, 0);
188
g->vbox1 = GTK_VBOX(gtk_vbox_new(FALSE, 0));
189
g->vbox2 = GTK_VBOX(gtk_vbox_new(FALSE, 0));
190
gtk_box_pack_start(GTK_BOX(g->hbox), GTK_WIDGET(g->vbox1), FALSE, FALSE, 5);
191
gtk_box_pack_start(GTK_BOX(g->hbox), GTK_WIDGET(g->vbox2), TRUE, TRUE, 5);
192
GtkWidget *label = dtgtk_reset_label_new(_("saturation"), self, &p->saturation, sizeof(float));
193
gtk_box_pack_start(GTK_BOX(g->vbox1), GTK_WIDGET(label), TRUE, TRUE, 0);
194
g->scale5 = DTGTK_SLIDER(dtgtk_slider_new_with_range(DARKTABLE_SLIDER_BAR,-3.0, 3.0, 0.01, p->saturation,2));
196
gtk_box_pack_start(GTK_BOX(g->vbox2), GTK_WIDGET(g->scale5), TRUE, TRUE, 0);
199
g_signal_connect (G_OBJECT (g->scale5), "value-changed",
200
G_CALLBACK (sat_callback), self);
201
g->hsRGB = dt_colorspaces_create_srgb_profile();
202
g->hLab = dt_colorspaces_create_lab_profile();
203
g->xform = cmsCreateTransform(g->hLab, TYPE_Lab_DBL, g->hsRGB, TYPE_RGB_DBL,
204
INTENT_PERCEPTUAL, 0);//cmsFLAGS_NOTPRECALC);
207
void gui_cleanup(struct dt_iop_module_t *self)
209
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
210
dt_colorspaces_cleanup_profile(g->hsRGB);
211
dt_colorspaces_cleanup_profile(g->hLab);
212
cmsDeleteTransform(g->xform);
213
free(self->gui_data);
214
self->gui_data = NULL;
217
static void sat_callback (GtkDarktableSlider *slider, gpointer user_data)
219
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
220
if(self->dt->gui->reset) return;
221
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)self->params;
222
p->saturation = dtgtk_slider_get_value(slider);
223
dt_dev_add_history_item(darktable.develop, self, TRUE);
224
gtk_widget_queue_draw(self->widget);
227
static gboolean dt_iop_colorcorrection_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
229
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
230
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
231
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)self->params;
232
dt_iop_colorcorrection_params_t *p1 = &g->press_params;
234
const int inset = DT_COLORCORRECTION_INSET;
235
int width = widget->allocation.width, height = widget->allocation.height;
236
cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
237
cairo_t *cr = cairo_create(cst);
239
cairo_set_source_rgb (cr, .2, .2, .2);
242
cairo_translate(cr, inset, inset);
246
cairo_translate(cr, 0, height);
247
cairo_scale(cr, 1., -1.);
249
for(int j=0; j<cells; j++) for(int i=0; i<cells; i++)
251
double rgb[3] = {0.5, 0.5, 0.5}; // Lab: rgb grey converted to Lab
254
Lab.a = Lab.b = 0; // grey
255
// dt_iop_sRGB_to_Lab(rgb, Lab, 0, 0, 1.0, 1, 1); // get grey in Lab
256
// printf("lab = %f %f %f\n", Lab[0], Lab[1], Lab[2]);
257
Lab.a = p->saturation*(Lab.a + Lab.L * .05*DT_COLORCORRECTION_MAX*(i/(cells-1.0) - .5));
258
Lab.b = p->saturation*(Lab.b + Lab.L * .05*DT_COLORCORRECTION_MAX*(j/(cells-1.0) - .5));
259
cmsDoTransform(g->xform, &Lab, rgb, 1);
260
// dt_iop_Lab_to_sRGB(Lab, rgb, 0, 0, 1.0, 1, 1);
261
cairo_set_source_rgb (cr, rgb[0], rgb[1], rgb[2]);
262
cairo_rectangle(cr, width*i/(float)cells, height*j/(float)cells, width/(float)cells-1, height/(float)cells-1);
265
float loa, hia, lob, hib;
266
if(!g->dragging) p1 = p;
267
loa = .5f*(width + width*p1->loa/(float)DT_COLORCORRECTION_MAX);
268
hia = .5f*(width + width*p1->hia/(float)DT_COLORCORRECTION_MAX);
269
lob = .5f*(height + height*p1->lob/(float)DT_COLORCORRECTION_MAX);
270
hib = .5f*(height + height*p1->hib/(float)DT_COLORCORRECTION_MAX);
271
cairo_set_line_width(cr, 2.);
274
cairo_rectangle(cr, loa, lob, hia-loa, hib-lob);
275
if(g->selected & 1) loa = /*MIN(g->selected < 0xf ? hia : INFINITY,*/ loa + g->mouse_x-g->press_x;//);
276
if(g->selected & 2) lob = /*MIN(g->selected < 0xf ? hib : INFINITY,*/ lob + g->mouse_y-g->press_y;//);
277
if(g->selected & 4) hia = /*MAX(g->selected < 0xf ? loa : -INFINITY,*/ hia + g->mouse_x-g->press_x;//);
278
if(g->selected & 8) hib = /*MAX(g->selected < 0xf ? lob : -INFINITY,*/ hib + g->mouse_y-g->press_y;//);
279
p->loa = (2.0*loa - width) *DT_COLORCORRECTION_MAX/(float)width;
280
p->hia = (2.0*hia - width) *DT_COLORCORRECTION_MAX/(float)width;
281
p->lob = (2.0*lob - height)*DT_COLORCORRECTION_MAX/(float)height;
282
p->hib = (2.0*hib - height)*DT_COLORCORRECTION_MAX/(float)height;
286
cairo_set_source_rgb(cr, .1, .1, .1);
287
cairo_move_to(cr, loa, hib);
288
cairo_line_to(cr, loa, lob);
289
cairo_line_to(cr, hia, lob);
291
cairo_set_source_rgb(cr, .9, .9, .9);
292
cairo_move_to(cr, hia, lob);
293
cairo_line_to(cr, hia, hib);
294
cairo_line_to(cr, loa, hib);
296
cairo_rectangle(cr, loa, lob, hia-loa, hib-lob);
297
if(g->selected & 1) loa = loa < hia ? loa-7 : loa+7;
298
if(g->selected & 2) lob = lob < hib ? lob-7 : lob+7;
299
if(g->selected & 4) hia = loa < hia ? hia+7 : hia-7;
300
if(g->selected & 8) hib = lob < hib ? hib+7 : hib-7;
302
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
303
cairo_set_source_rgba(cr, .9, .9, .9, .5);
304
cairo_rectangle(cr, loa, lob, hia-loa, hib-lob);
305
cairo_fill_preserve(cr);
308
dt_dev_add_history_item(darktable.develop, self, TRUE);
311
cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget));
312
cairo_set_source_surface (cr_pixmap, cst, 0, 0);
313
cairo_paint(cr_pixmap);
314
cairo_destroy(cr_pixmap);
315
cairo_surface_destroy(cst);
319
static gboolean dt_iop_colorcorrection_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
321
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
322
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
323
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)self->params;
324
const int inset = DT_COLORCORRECTION_INSET;
325
int width = widget->allocation.width - 2*inset, height = widget->allocation.height - 2*inset;
326
g->mouse_x = CLAMP(event->x - inset, 0, width);
327
g->mouse_y = CLAMP(height - 1 - event->y + inset, 0, height);
330
g->press_x = g->mouse_x;
331
g->press_y = g->mouse_y;
332
const float loa = .5f*(width + width*p->loa/(float)DT_COLORCORRECTION_MAX),
333
hia = .5f*(width + width*p->hia/(float)DT_COLORCORRECTION_MAX),
334
lob = .5f*(height + height*p->lob/(float)DT_COLORCORRECTION_MAX),
335
hib = .5f*(height + height*p->hib/(float)DT_COLORCORRECTION_MAX);
339
if(g->press_x <= loa) g->selected |= 1;
340
if(g->press_x >= hia) g->selected |= 4;
344
if(g->press_x <= hia) g->selected |= 4;
345
if(g->press_x >= loa) g->selected |= 1;
349
if(g->press_y <= lob) g->selected |= 2;
350
if(g->press_y >= hib) g->selected |= 8;
354
if(g->press_y <= hib) g->selected |= 8;
355
if(g->press_y >= lob) g->selected |= 2;
357
if(g->press_x > MIN(loa, hia) && g->press_x < MAX(hia,loa) && g->press_y > MIN(lob,hib) && g->press_y < MAX(hib,lob)) g->selected = 0xf;
358
g->press_params = *p;
360
gtk_widget_queue_draw(self->widget);
362
gdk_window_get_pointer(event->window, &x, &y, NULL);
366
static gboolean dt_iop_colorcorrection_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
368
if(event->button == 1)
370
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
371
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
378
static gboolean dt_iop_colorcorrection_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
380
if(event->button == 1)
382
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
383
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
390
static gboolean dt_iop_colorcorrection_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
392
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
393
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
394
g->selected = g->dragging = 0;
395
gtk_widget_queue_draw(self->widget);
399
static gboolean dt_iop_colorcorrection_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
401
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
402
dt_iop_colorcorrection_gui_data_t *g = (dt_iop_colorcorrection_gui_data_t *)self->gui_data;
403
dt_iop_colorcorrection_params_t *p = (dt_iop_colorcorrection_params_t *)self->params;
404
if(event->direction == GDK_SCROLL_UP && p->saturation > -3.0) p->saturation -= 0.1;
405
if(event->direction == GDK_SCROLL_DOWN && p->saturation < 3.0) p->saturation += 0.1;
406
dtgtk_slider_set_value(g->scale5, p->saturation);
407
gtk_widget_queue_draw(widget);