~ubuntu-branches/ubuntu/saucy/darktable/saucy

« back to all changes in this revision

Viewing changes to src/iop/lowlight.c

  • Committer: Bazaar Package Importer
  • Author(s): David Bremner
  • Date: 2011-07-12 09:36:46 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20110712093646-yp9dbxan44dmw15h
Tags: 0.9-1
* New upstream release.
* Remove all patches now upstream; only patch for
  -Wno-error=unused-but-set-variable remains.
* Bump Standards-Version to 3.9.2 (no changes)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    This file is part of darktable,
 
3
    copyright (c) 2011 Rostyslav Pidgornyi
 
4
 
 
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.
 
9
 
 
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.
 
14
 
 
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/>.
 
17
*/
 
18
#ifdef HAVE_CONFIG_H
 
19
#include "config.h"
 
20
#endif
 
21
#include <stdlib.h>
 
22
#include <math.h>
 
23
#include <string.h>
 
24
#include <inttypes.h>
 
25
#include "common/colorspaces.h"
 
26
#include "common/darktable.h"
 
27
#include "common/debug.h"
 
28
#include "gui/histogram.h"
 
29
#include "develop/develop.h"
 
30
#include "control/control.h"
 
31
#include "control/conf.h"
 
32
#include "gui/gtk.h"
 
33
#include "gui/draw.h"
 
34
#include "gui/presets.h"
 
35
#include "dtgtk/slider.h"
 
36
 
 
37
DT_MODULE(1)
 
38
 
 
39
#define DT_IOP_LOWLIGHT_INSET 5
 
40
#define DT_IOP_LOWLIGHT_RES 64
 
41
#define DT_IOP_LOWLIGHT_BANDS 6
 
42
#define DT_IOP_LOWLIGHT_LUT_RES 0x10000
 
43
 
 
44
typedef struct dt_iop_lowlight_params_t
 
45
{
 
46
  float blueness;
 
47
  float transition_x[DT_IOP_LOWLIGHT_BANDS], transition_y[DT_IOP_LOWLIGHT_BANDS];
 
48
}
 
49
dt_iop_lowlight_params_t;
 
50
 
 
51
typedef struct dt_iop_lowlight_gui_data_t
 
52
{
 
53
  dt_draw_curve_t *transition_curve;        // curve for gui to draw
 
54
 
 
55
  GtkDarktableSlider *scale_blueness;
 
56
  GtkDrawingArea *area;
 
57
  double mouse_x, mouse_y, mouse_pick;
 
58
  float mouse_radius;
 
59
  dt_iop_lowlight_params_t drag_params;
 
60
  int dragging;
 
61
  int x_move;
 
62
  float draw_xs[DT_IOP_LOWLIGHT_RES], draw_ys[DT_IOP_LOWLIGHT_RES];
 
63
  float draw_min_xs[DT_IOP_LOWLIGHT_RES], draw_min_ys[DT_IOP_LOWLIGHT_RES];
 
64
  float draw_max_xs[DT_IOP_LOWLIGHT_RES], draw_max_ys[DT_IOP_LOWLIGHT_RES];
 
65
}
 
66
dt_iop_lowlight_gui_data_t;
 
67
 
 
68
typedef struct dt_iop_lowlight_data_t
 
69
{
 
70
  float blueness;
 
71
  dt_draw_curve_t *curve;
 
72
  float lut[DT_IOP_LOWLIGHT_LUT_RES];
 
73
}
 
74
dt_iop_lowlight_data_t;
 
75
 
 
76
const char
 
77
*name()
 
78
{
 
79
  return _("lowlight vision");
 
80
}
 
81
 
 
82
int flags()
 
83
{
 
84
  return IOP_FLAGS_INCLUDE_IN_STYLES;
 
85
}
 
86
 
 
87
int
 
88
groups ()
 
89
{
 
90
  return IOP_GROUP_EFFECT;
 
91
}
 
92
 
 
93
 
 
94
static float
 
95
lookup(const float *lut, const float i)
 
96
{
 
97
  const int bin0 = MIN(0xffff, MAX(0, DT_IOP_LOWLIGHT_LUT_RES *  i));
 
98
  const int bin1 = MIN(0xffff, MAX(0, DT_IOP_LOWLIGHT_LUT_RES *  i + 1));
 
99
  const float f = DT_IOP_LOWLIGHT_LUT_RES * i - bin0;
 
100
  return lut[bin1]*f + lut[bin0]*(1.-f);
 
101
}
 
102
 
 
103
void
 
104
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)
 
105
{
 
106
  dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)(piece->data);
 
107
  const int ch = piece->colors;
 
108
 
 
109
  // empiric coefficient
 
110
  const float c = 0.5f;
 
111
  const float threshold = 0.01f;
 
112
 
 
113
  // scotopic white, blue saturated
 
114
  float Lab_sw[3] = { 100.0f , 0 , -d->blueness };
 
115
  float XYZ_sw[3];
 
116
 
 
117
  dt_Lab_to_XYZ(Lab_sw, XYZ_sw);
 
118
 
 
119
#ifdef _OPENMP
 
120
  #pragma omp parallel for default(none) schedule(static) shared(roi_in, roi_out, d, i, o, XYZ_sw)
 
121
#endif
 
122
  for(int k=0; k<roi_out->width*roi_out->height; k++)
 
123
  {
 
124
    float *in = (float *)i + ch*k;
 
125
    float *out = (float *)o + ch*k;
 
126
    float XYZ[3], XYZ_s[3];
 
127
    float V;
 
128
    float w;
 
129
 
 
130
    dt_Lab_to_XYZ(in, XYZ);
 
131
 
 
132
    // calculate scotopic luminanse
 
133
    if (XYZ[0] > threshold)
 
134
    {
 
135
      // normal flow
 
136
      V = XYZ[1] * ( 1.33f * ( 1.0f + (XYZ[1]+XYZ[2])/XYZ[0]) - 1.68f );
 
137
    }
 
138
    else
 
139
    {
 
140
      // low red flow, avoids "snow" on dark noisy areas
 
141
      V = XYZ[1] * ( 1.33f * ( 1.0f + (XYZ[1]+XYZ[2])/threshold) - 1.68f );
 
142
    }
 
143
 
 
144
    // scale using empiric coefficient and fit inside limits
 
145
    V = fminf(1.0f,fmaxf(0.0f,c*V));
 
146
 
 
147
    // blending coefficient from curve
 
148
    w = lookup(d->lut,in[0]/100.f);
 
149
 
 
150
    XYZ_s[0] = V * XYZ_sw[0];
 
151
    XYZ_s[1] = V * XYZ_sw[1];
 
152
    XYZ_s[2] = V * XYZ_sw[2];
 
153
 
 
154
    XYZ[0] = w * XYZ[0] + (1.0f - w) * XYZ_s[0];
 
155
    XYZ[1] = w * XYZ[1] + (1.0f - w) * XYZ_s[1];
 
156
    XYZ[2] = w * XYZ[2] + (1.0f - w) * XYZ_s[2];
 
157
 
 
158
    dt_XYZ_to_Lab(XYZ,out);
 
159
  }
 
160
}
 
161
 
 
162
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)
 
163
{
 
164
  dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)(piece->data);
 
165
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)p1;
 
166
  dt_draw_curve_set_point(d->curve, 0, p->transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p->transition_y[0]);
 
167
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
168
    dt_draw_curve_set_point(d->curve, k+1, p->transition_x[k], p->transition_y[k]);
 
169
  dt_draw_curve_set_point(d->curve, DT_IOP_LOWLIGHT_BANDS+1, p->transition_x[1]+1.0, p->transition_y[DT_IOP_LOWLIGHT_BANDS-1]);
 
170
  dt_draw_curve_calc_values(d->curve, 0.0, 1.0, DT_IOP_LOWLIGHT_LUT_RES, NULL, d->lut);
 
171
  d->blueness = p->blueness;
 
172
}
 
173
 
 
174
void init_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
 
175
{
 
176
  dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)malloc(sizeof(dt_iop_lowlight_data_t));
 
177
  dt_iop_lowlight_params_t *default_params = (dt_iop_lowlight_params_t *)self->default_params;
 
178
  piece->data = (void *)d;
 
179
  d->curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
 
180
  (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, default_params->transition_y[DT_IOP_LOWLIGHT_BANDS-2]);
 
181
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
182
    (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[k], default_params->transition_y[k]);
 
183
  (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[1]+1.0, default_params->transition_y[1]);
 
184
}
 
185
 
 
186
void cleanup_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
 
187
{
 
188
  // clean up everything again.
 
189
  dt_iop_lowlight_data_t *d = (dt_iop_lowlight_data_t *)(piece->data);
 
190
  dt_draw_curve_destroy(d->curve);
 
191
  free(piece->data);
 
192
}
 
193
 
 
194
void gui_update(struct dt_iop_module_t *self)
 
195
{
 
196
  dt_iop_lowlight_gui_data_t *g = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
197
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)self->params;
 
198
  dtgtk_slider_set_value(g->scale_blueness, p->blueness);
 
199
  gtk_widget_queue_draw(self->widget);
 
200
}
 
201
 
 
202
void init(dt_iop_module_t *module)
 
203
{
 
204
  module->params = malloc(sizeof(dt_iop_lowlight_params_t));
 
205
  module->default_params = malloc(sizeof(dt_iop_lowlight_params_t));
 
206
  module->default_enabled = 0; // we're a rather slow and rare op.
 
207
  module->priority = 511; // module order created by iop_dependencies.py, do not edit!
 
208
  module->params_size = sizeof(dt_iop_lowlight_params_t);
 
209
  module->gui_data = NULL;
 
210
  dt_iop_lowlight_params_t tmp;
 
211
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) tmp.transition_x[k] = k/(DT_IOP_LOWLIGHT_BANDS-1.0);
 
212
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) tmp.transition_y[k] = 0.5f;
 
213
  tmp.blueness = 0.0f;
 
214
  memcpy(module->params, &tmp, sizeof(dt_iop_lowlight_params_t));
 
215
  memcpy(module->default_params, &tmp, sizeof(dt_iop_lowlight_params_t));
 
216
}
 
217
 
 
218
void cleanup(dt_iop_module_t *module)
 
219
{
 
220
  free(module->gui_data);
 
221
  module->gui_data = NULL;
 
222
  free(module->params);
 
223
  module->params = NULL;
 
224
}
 
225
 
 
226
void init_presets (dt_iop_module_t *self)
 
227
{
 
228
  dt_iop_lowlight_params_t p;
 
229
 
 
230
  DT_DEBUG_SQLITE3_EXEC(darktable.db, "begin", NULL, NULL, NULL);
 
231
 
 
232
  p.transition_x[0] = 0.000000;
 
233
  p.transition_x[1] = 0.200000;
 
234
  p.transition_x[2] = 0.400000;
 
235
  p.transition_x[3] = 0.600000;
 
236
  p.transition_x[4] = 0.800000;
 
237
  p.transition_x[5] = 1.000000;
 
238
 
 
239
  p.transition_y[0] = 1.000000;
 
240
  p.transition_y[1] = 1.000000;
 
241
  p.transition_y[2] = 1.000000;
 
242
  p.transition_y[3] = 1.000000;
 
243
  p.transition_y[4] = 1.000000;
 
244
  p.transition_y[5] = 1.000000;
 
245
 
 
246
  p.blueness = 0.0f;
 
247
  dt_gui_presets_add_generic(_("daylight"), self->op, &p, sizeof(p), 1);
 
248
 
 
249
  p.transition_x[0] = 0.000000;
 
250
  p.transition_x[1] = 0.200000;
 
251
  p.transition_x[2] = 0.400000;
 
252
  p.transition_x[3] = 0.600000;
 
253
  p.transition_x[4] = 0.800000;
 
254
  p.transition_x[5] = 1.000000;
 
255
 
 
256
  p.transition_y[0] = 0.600000;
 
257
  p.transition_y[1] = 0.800000;
 
258
  p.transition_y[2] = 0.950000;
 
259
  p.transition_y[3] = 0.980000;
 
260
  p.transition_y[4] = 1.000000;
 
261
  p.transition_y[5] = 1.000000;
 
262
 
 
263
  p.blueness = 30.0f;
 
264
  dt_gui_presets_add_generic(_("indoor bright"), self->op, &p, sizeof(p), 1);
 
265
 
 
266
  p.transition_x[0] = 0.000000;
 
267
  p.transition_x[1] = 0.200000;
 
268
  p.transition_x[2] = 0.400000;
 
269
  p.transition_x[3] = 0.600000;
 
270
  p.transition_x[4] = 0.800000;
 
271
  p.transition_x[5] = 1.000000;
 
272
 
 
273
  p.transition_y[0] = 0.300000;
 
274
  p.transition_y[1] = 0.500000;
 
275
  p.transition_y[2] = 0.700000;
 
276
  p.transition_y[3] = 0.850000;
 
277
  p.transition_y[4] = 0.970000;
 
278
  p.transition_y[5] = 1.000000;
 
279
 
 
280
  p.blueness = 30.0f;
 
281
  dt_gui_presets_add_generic(_("indoor dim"), self->op, &p, sizeof(p), 1);
 
282
 
 
283
  p.transition_x[0] = 0.000000;
 
284
  p.transition_x[1] = 0.200000;
 
285
  p.transition_x[2] = 0.400000;
 
286
  p.transition_x[3] = 0.600000;
 
287
  p.transition_x[4] = 0.800000;
 
288
  p.transition_x[5] = 1.000000;
 
289
 
 
290
  p.transition_y[0] = 0.050000;
 
291
  p.transition_y[1] = 0.200000;
 
292
  p.transition_y[2] = 0.400000;
 
293
  p.transition_y[3] = 0.700000;
 
294
  p.transition_y[4] = 0.920000;
 
295
  p.transition_y[5] = 1.000000;
 
296
 
 
297
  p.blueness = 40.0f;
 
298
  dt_gui_presets_add_generic(_("indoor dark"), self->op, &p, sizeof(p), 1);
 
299
 
 
300
  p.transition_x[0] = 0.000000;
 
301
  p.transition_x[1] = 0.200000;
 
302
  p.transition_x[2] = 0.400000;
 
303
  p.transition_x[3] = 0.600000;
 
304
  p.transition_x[4] = 0.800000;
 
305
  p.transition_x[5] = 1.000000;
 
306
 
 
307
  p.transition_y[0] = 0.070000;
 
308
  p.transition_y[1] = 0.100000;
 
309
  p.transition_y[2] = 0.180000;
 
310
  p.transition_y[3] = 0.350000;
 
311
  p.transition_y[4] = 0.750000;
 
312
  p.transition_y[5] = 1.000000;
 
313
 
 
314
  p.blueness = 50.0f;
 
315
  dt_gui_presets_add_generic(_("twilight"), self->op, &p, sizeof(p), 1);
 
316
 
 
317
  p.transition_x[0] = 0.000000;
 
318
  p.transition_x[1] = 0.200000;
 
319
  p.transition_x[2] = 0.400000;
 
320
  p.transition_x[3] = 0.600000;
 
321
  p.transition_x[4] = 0.800000;
 
322
  p.transition_x[5] = 1.000000;
 
323
 
 
324
  p.transition_y[0] = 0.000000;
 
325
  p.transition_y[1] = 0.450000;
 
326
  p.transition_y[2] = 0.750000;
 
327
  p.transition_y[3] = 0.930000;
 
328
  p.transition_y[4] = 0.990000;
 
329
  p.transition_y[5] = 1.000000;
 
330
 
 
331
  p.blueness = 30.0f;
 
332
  dt_gui_presets_add_generic(_("night street lit"), self->op, &p, sizeof(p), 1);
 
333
 
 
334
  p.transition_x[0] = 0.000000;
 
335
  p.transition_x[1] = 0.200000;
 
336
  p.transition_x[2] = 0.400000;
 
337
  p.transition_x[3] = 0.600000;
 
338
  p.transition_x[4] = 0.800000;
 
339
  p.transition_x[5] = 1.000000;
 
340
 
 
341
  p.transition_y[0] = 0.000000;
 
342
  p.transition_y[1] = 0.150000;
 
343
  p.transition_y[2] = 0.350000;
 
344
  p.transition_y[3] = 0.800000;
 
345
  p.transition_y[4] = 0.970000;
 
346
  p.transition_y[5] = 1.000000;
 
347
 
 
348
  p.blueness = 30.0f;
 
349
  dt_gui_presets_add_generic(_("night street"), self->op, &p, sizeof(p), 1);
 
350
 
 
351
  p.transition_x[0] = 0.000000;
 
352
  p.transition_x[1] = 0.150000;
 
353
  p.transition_x[2] = 0.400000;
 
354
  p.transition_x[3] = 0.600000;
 
355
  p.transition_x[4] = 0.800000;
 
356
  p.transition_x[5] = 1.000000;
 
357
 
 
358
  p.transition_y[0] = 0.000000;
 
359
  p.transition_y[1] = 0.020000;
 
360
  p.transition_y[2] = 0.050000;
 
361
  p.transition_y[3] = 0.200000;
 
362
  p.transition_y[4] = 0.550000;
 
363
  p.transition_y[5] = 1.000000;
 
364
 
 
365
  p.blueness = 40.0f;
 
366
  dt_gui_presets_add_generic(_("night street dark"), self->op, &p, sizeof(p), 1);
 
367
 
 
368
  p.transition_x[0] = 0.000000;
 
369
  p.transition_x[1] = 0.200000;
 
370
  p.transition_x[2] = 0.400000;
 
371
  p.transition_x[3] = 0.600000;
 
372
  p.transition_x[4] = 0.800000;
 
373
  p.transition_x[5] = 1.000000;
 
374
 
 
375
  p.transition_y[0] = 0.000000;
 
376
  p.transition_y[1] = 0.000000;
 
377
  p.transition_y[2] = 0.000000;
 
378
  p.transition_y[3] = 0.000000;
 
379
  p.transition_y[4] = 0.000000;
 
380
  p.transition_y[5] = 0.000000;
 
381
 
 
382
 
 
383
  p.blueness = 50.0f;
 
384
  dt_gui_presets_add_generic(_("night"), self->op, &p, sizeof(p), 1);
 
385
 
 
386
  DT_DEBUG_SQLITE3_EXEC(darktable.db, "commit", NULL, NULL, NULL);
 
387
}
 
388
 
 
389
// fills in new parameters based on mouse position (in 0,1)
 
390
static void
 
391
dt_iop_lowlight_get_params(dt_iop_lowlight_params_t *p, const double mouse_x, const double mouse_y, const float rad)
 
392
{
 
393
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
394
  {
 
395
    const float f = expf(-(mouse_x - p->transition_x[k])*(mouse_x - p->transition_x[k])/(rad*rad));
 
396
    p->transition_y[k] = (1-f)*p->transition_y[k] + f*mouse_y;
 
397
  }
 
398
}
 
399
 
 
400
static gboolean
 
401
lowlight_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
 
402
{
 
403
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
404
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
405
  dt_iop_lowlight_params_t p = *(dt_iop_lowlight_params_t *)self->params;
 
406
 
 
407
  dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]);
 
408
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]);
 
409
  dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]);
 
410
 
 
411
  const int inset = DT_IOP_LOWLIGHT_INSET;
 
412
  int width = widget->allocation.width, height = widget->allocation.height;
 
413
  cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
 
414
  cairo_t *cr = cairo_create(cst);
 
415
 
 
416
  cairo_set_source_rgb (cr, .2, .2, .2);
 
417
  cairo_paint(cr);
 
418
 
 
419
  cairo_translate(cr, inset, inset);
 
420
  width -= 2*inset;
 
421
  height -= 2*inset;
 
422
 
 
423
  cairo_set_line_width(cr, 1.0);
 
424
  cairo_set_source_rgb (cr, .1, .1, .1);
 
425
  cairo_rectangle(cr, 0, 0, width, height);
 
426
  cairo_stroke(cr);
 
427
 
 
428
  cairo_set_source_rgb (cr, .3, .3, .3);
 
429
  cairo_rectangle(cr, 0, 0, width, height);
 
430
  cairo_fill(cr);
 
431
 
 
432
  // draw grid
 
433
  cairo_set_line_width(cr, .4);
 
434
  cairo_set_source_rgb (cr, .1, .1, .1);
 
435
  dt_draw_grid(cr, 8, 0, 0, width, height);
 
436
 
 
437
 
 
438
  if(c->mouse_y > 0 || c->dragging)
 
439
  {
 
440
    // draw min/max curves:
 
441
    dt_iop_lowlight_get_params(&p, c->mouse_x, 1., c->mouse_radius);
 
442
    dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]);
 
443
    for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
444
      dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]);
 
445
    dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]);
 
446
    dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_min_xs, c->draw_min_ys);
 
447
 
 
448
    p = *(dt_iop_lowlight_params_t *)self->params;
 
449
    dt_iop_lowlight_get_params(&p, c->mouse_x, .0, c->mouse_radius);
 
450
    dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]);
 
451
    for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
452
      dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]);
 
453
    dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]);
 
454
    dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_max_xs, c->draw_max_ys);
 
455
  }
 
456
 
 
457
  cairo_save(cr);
 
458
 
 
459
  // draw x positions
 
460
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
 
461
  cairo_set_line_width(cr, 1.);
 
462
  const float arrw = 7.0f;
 
463
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
464
  {
 
465
    cairo_move_to(cr, width*p.transition_x[k], height+inset-1);
 
466
    cairo_rel_line_to(cr, -arrw*.5f, 0);
 
467
    cairo_rel_line_to(cr, arrw*.5f, -arrw);
 
468
    cairo_rel_line_to(cr, arrw*.5f, arrw);
 
469
    cairo_close_path(cr);
 
470
    if(c->x_move == k) cairo_fill(cr);
 
471
    else               cairo_stroke(cr);
 
472
  }
 
473
 
 
474
  // draw selected cursor
 
475
  cairo_translate(cr, 0, height);
 
476
 
 
477
  // cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
 
478
  //cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
 
479
  cairo_set_line_width(cr, 2.);
 
480
  cairo_set_source_rgba(cr, .7, .7, .7, 1.0);
 
481
 
 
482
  p = *(dt_iop_lowlight_params_t *)self->params;
 
483
  dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]);
 
484
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
485
    dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]);
 
486
  dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]);
 
487
  dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_xs, c->draw_ys);
 
488
  cairo_move_to(cr, 0*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_ys[0]);
 
489
  for(int k=1; k<DT_IOP_LOWLIGHT_RES; k++) cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_ys[k]);
 
490
  cairo_stroke(cr);
 
491
 
 
492
  // draw dots on knots
 
493
  cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
 
494
  cairo_set_line_width(cr, 1.);
 
495
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
496
  {
 
497
    cairo_arc(cr, width*p.transition_x[k], - height*p.transition_y[k], 3.0, 0.0, 2.0*M_PI);
 
498
    if(c->x_move == k) cairo_fill(cr);
 
499
    else               cairo_stroke(cr);
 
500
  }
 
501
 
 
502
  if(c->mouse_y > 0 || c->dragging)
 
503
  {
 
504
    // draw min/max, if selected
 
505
    cairo_set_source_rgba(cr, .7, .7, .7, .6);
 
506
    cairo_move_to(cr, 0, - height*c->draw_min_ys[0]);
 
507
    for(int k=1; k<DT_IOP_LOWLIGHT_RES; k++)    cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_min_ys[k]);
 
508
    for(int k=DT_IOP_LOWLIGHT_RES-1; k>=0; k--) cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_max_ys[k]);
 
509
    cairo_close_path(cr);
 
510
    cairo_fill(cr);
 
511
    // draw mouse focus circle
 
512
    cairo_set_source_rgba(cr, .9, .9, .9, .5);
 
513
    const float pos = DT_IOP_LOWLIGHT_RES * c->mouse_x;
 
514
    int k = (int)pos;
 
515
    const float f = k - pos;
 
516
    if(k >= DT_IOP_LOWLIGHT_RES-1) k = DT_IOP_LOWLIGHT_RES - 2;
 
517
    float ht = -height*(f*c->draw_ys[k] + (1-f)*c->draw_ys[k+1]);
 
518
    cairo_arc(cr, c->mouse_x*width, ht, c->mouse_radius*width, 0, 2.*M_PI);
 
519
    cairo_stroke(cr);
 
520
  }
 
521
 
 
522
  cairo_restore (cr);
 
523
 
 
524
  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
 
525
 
 
526
  // draw labels:
 
527
  cairo_text_extents_t ext;
 
528
  cairo_set_source_rgb(cr, .1, .1, .1);
 
529
  cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
 
530
  cairo_set_font_size (cr, .06*height);
 
531
 
 
532
  cairo_text_extents (cr, _("dark"), &ext);
 
533
  cairo_move_to (cr, .02*width+ext.height, .5*(height+ext.width));
 
534
  cairo_save (cr);
 
535
  cairo_rotate (cr, -M_PI*.5f);
 
536
  cairo_show_text(cr, _("dark"));
 
537
  cairo_restore (cr);
 
538
 
 
539
  cairo_text_extents (cr, _("bright"), &ext);
 
540
  cairo_move_to (cr, .98*width, .5*(height+ext.width));
 
541
  cairo_save (cr);
 
542
  cairo_rotate (cr, -M_PI*.5f);
 
543
  cairo_show_text(cr, _("bright"));
 
544
  cairo_restore (cr);
 
545
 
 
546
  cairo_text_extents (cr, _("day vision"), &ext);
 
547
  cairo_move_to (cr, .5*(width-ext.width), .08*height);
 
548
  cairo_show_text(cr, _("day vision"));
 
549
 
 
550
  cairo_text_extents (cr, _("night vision"), &ext);
 
551
  cairo_move_to (cr, .5*(width-ext.width), .97*height);
 
552
  cairo_show_text(cr, _("night vision"));
 
553
 
 
554
 
 
555
  cairo_destroy(cr);
 
556
  cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget));
 
557
  cairo_set_source_surface (cr_pixmap, cst, 0, 0);
 
558
  cairo_paint(cr_pixmap);
 
559
  cairo_destroy(cr_pixmap);
 
560
  cairo_surface_destroy(cst);
 
561
  return TRUE;
 
562
}
 
563
 
 
564
static gboolean
 
565
lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
 
566
{
 
567
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
568
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
569
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)self->params;
 
570
  const int inset = DT_IOP_LOWLIGHT_INSET;
 
571
  int height = widget->allocation.height - 2*inset, width = widget->allocation.width - 2*inset;
 
572
  if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width)/(float)width;
 
573
  c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height)/(float)height;
 
574
  if(c->dragging)
 
575
  {
 
576
    *p = c->drag_params;
 
577
    if(c->x_move >= 0)
 
578
    {
 
579
      const float mx = CLAMP(event->x - inset, 0, width)/(float)width;
 
580
      if(c->x_move > 0 && c->x_move < DT_IOP_LOWLIGHT_BANDS-1)
 
581
      {
 
582
        const float minx = p->transition_x[c->x_move-1]+0.001f;
 
583
        const float maxx = p->transition_x[c->x_move+1]-0.001f;
 
584
        p->transition_x[c->x_move] = fminf(maxx, fmaxf(minx, mx));
 
585
      }
 
586
    }
 
587
    else
 
588
    {
 
589
      dt_iop_lowlight_get_params(p, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
 
590
    }
 
591
    dt_dev_add_history_item(darktable.develop, self, TRUE);
 
592
  }
 
593
  else if(event->y > height)
 
594
  {
 
595
    c->x_move = 0;
 
596
    float dist = fabsf(p->transition_x[0] - c->mouse_x);
 
597
    for(int k=1; k<DT_IOP_LOWLIGHT_BANDS; k++)
 
598
    {
 
599
      float d2 = fabsf(p->transition_x[k] - c->mouse_x);
 
600
      if(d2 < dist)
 
601
      {
 
602
        c->x_move = k;
 
603
        dist = d2;
 
604
      }
 
605
    }
 
606
  }
 
607
  else
 
608
  {
 
609
    c->x_move = -1;
 
610
  }
 
611
  gtk_widget_queue_draw(widget);
 
612
  gint x, y;
 
613
  gdk_window_get_pointer(event->window, &x, &y, NULL);
 
614
  return TRUE;
 
615
}
 
616
 
 
617
static gboolean
 
618
lowlight_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
 
619
{
 
620
  if(event->button == 1)
 
621
  {
 
622
    dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
623
    dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
624
    c->drag_params = *(dt_iop_lowlight_params_t *)self->params;
 
625
    const int inset = DT_IOP_LOWLIGHT_INSET;
 
626
    int height = widget->allocation.height - 2*inset, width = widget->allocation.width - 2*inset;
 
627
    c->mouse_pick = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width)/(float)width);
 
628
    c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height)/(float)height;
 
629
    c->dragging = 1;
 
630
    return TRUE;
 
631
  }
 
632
  return FALSE;
 
633
}
 
634
 
 
635
static gboolean
 
636
lowlight_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
 
637
{
 
638
  if(event->button == 1)
 
639
  {
 
640
    dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
641
    dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
642
    c->dragging = 0;
 
643
    return TRUE;
 
644
  }
 
645
  return FALSE;
 
646
}
 
647
 
 
648
static gboolean
 
649
lowlight_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
 
650
{
 
651
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
652
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
653
  if(!c->dragging) c->mouse_x = c->mouse_y = -1.0;
 
654
  gtk_widget_queue_draw(widget);
 
655
  return TRUE;
 
656
}
 
657
 
 
658
static gboolean
 
659
lowlight_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
 
660
{
 
661
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
662
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
663
  if(event->direction == GDK_SCROLL_UP   && c->mouse_radius > 0.2/DT_IOP_LOWLIGHT_BANDS) c->mouse_radius *= 0.9; //0.7;
 
664
  if(event->direction == GDK_SCROLL_DOWN && c->mouse_radius < 1.0) c->mouse_radius *= (1.0/0.9); //1.42;
 
665
  gtk_widget_queue_draw(widget);
 
666
  return TRUE;
 
667
}
 
668
 
 
669
static void
 
670
blueness_callback (GtkDarktableSlider *slider, gpointer user_data)
 
671
{
 
672
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
 
673
  if(self->dt->gui->reset) return;
 
674
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)self->params;
 
675
  p->blueness = dtgtk_slider_get_value(slider);
 
676
  dt_dev_add_history_item(darktable.develop, self, TRUE);
 
677
}
 
678
 
 
679
void gui_init(struct dt_iop_module_t *self)
 
680
{
 
681
  self->gui_data = malloc(sizeof(dt_iop_lowlight_gui_data_t));
 
682
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
683
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)self->params;
 
684
 
 
685
  c->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
 
686
  (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p->transition_y[DT_IOP_LOWLIGHT_BANDS-2]);
 
687
  for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[k], p->transition_y[k]);
 
688
  (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[1]+1.0, p->transition_y[1]);
 
689
 
 
690
  c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
 
691
  c->dragging = 0;
 
692
  c->x_move = -1;
 
693
  c->mouse_radius = 1.0/DT_IOP_LOWLIGHT_BANDS;
 
694
 
 
695
  self->widget = GTK_WIDGET(gtk_vbox_new(FALSE, DT_GUI_IOP_MODULE_CONTROL_SPACING));
 
696
 
 
697
  c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
 
698
  gtk_drawing_area_size(c->area, 195, 195);
 
699
 
 
700
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(c->area),FALSE, FALSE, 0);
 
701
 
 
702
  gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK);
 
703
  g_signal_connect (G_OBJECT (c->area), "expose-event",
 
704
                    G_CALLBACK (lowlight_expose), self);
 
705
  g_signal_connect (G_OBJECT (c->area), "button-press-event",
 
706
                    G_CALLBACK (lowlight_button_press), self);
 
707
  g_signal_connect (G_OBJECT (c->area), "button-release-event",
 
708
                    G_CALLBACK (lowlight_button_release), self);
 
709
  g_signal_connect (G_OBJECT (c->area), "motion-notify-event",
 
710
                    G_CALLBACK (lowlight_motion_notify), self);
 
711
  g_signal_connect (G_OBJECT (c->area), "leave-notify-event",
 
712
                    G_CALLBACK (lowlight_leave_notify), self);
 
713
  g_signal_connect (G_OBJECT (c->area), "scroll-event",
 
714
                    G_CALLBACK (lowlight_scrolled), self);
 
715
 
 
716
  c->scale_blueness = DTGTK_SLIDER(dtgtk_slider_new_with_range(DARKTABLE_SLIDER_BAR,0.0, 100.0, 5.0, p->blueness, 2));
 
717
  dtgtk_slider_set_default_value(c->scale_blueness, p->blueness);
 
718
  dtgtk_slider_set_label(c->scale_blueness,_("blue shift"));
 
719
  dtgtk_slider_set_unit(c->scale_blueness,"%");
 
720
  dtgtk_slider_set_format_type(c->scale_blueness,DARKTABLE_SLIDER_FORMAT_PERCENT);
 
721
  g_object_set(G_OBJECT(c->scale_blueness), "tooltip-text", _("blueness in shadows"), (char *)NULL);
 
722
 
 
723
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(c->scale_blueness), TRUE, TRUE, 5);
 
724
 
 
725
  g_signal_connect (G_OBJECT (c->scale_blueness), "value-changed",
 
726
                    G_CALLBACK (blueness_callback), self);
 
727
}
 
728
 
 
729
void gui_cleanup(struct dt_iop_module_t *self)
 
730
{
 
731
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
 
732
  dt_draw_curve_destroy(c->transition_curve);
 
733
  free(self->gui_data);
 
734
  self->gui_data = NULL;
 
735
}
 
736
 
 
737
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;