Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
guides.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2012 Richard Wonka.
4 * Copyright (C) 2012-2016 Tobias Ellinghaus.
5 * Copyright (C) 2015 Edouard Gomez.
6 * Copyright (C) 2016 Roman Lebedev.
7 * Copyright (C) 2019 Jakub Filipowicz.
8 * Copyright (C) 2020, 2022 Chris Elston.
9 * Copyright (C) 2020-2022 Diederik Ter Rahe.
10 * Copyright (C) 2020 Pascal Obry.
11 * Copyright (C) 2020, 2022 Ralf Brown.
12 * Copyright (C) 2021-2022 Aldric Renaudin.
13 * Copyright (C) 2022 Martin Bařinka.
14 * Copyright (C) 2022 Nicolas Auffray.
15 * Copyright (C) 2023-2025 Aurélien PIERRE.
16 * Copyright (C) 2025 Guillaume Stutin.
17 *
18 * darktable is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * darktable is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
30 */
31
32#include <glib.h>
33
34#include "widgets/bauhaus.h"
35#include "common/conf.h"
36#include "common/utility.h"
37#include "gui/guides.h"
38#include "widgets/draw.h"
39#include "control/redraw.h"
40#include "develop/develop.h"
42
43/* The registered guide overlays. Module-private: every reader and the registration path
44 * live in this file, so the list no longer sits on darktable_t behind a pair of accessors
45 * whose only consumers were... this file. Populated by dt_guides_init() from the GUI
46 * bootstrap; a GUI-less process never calls it and never carries the list. */
47static GList *_guides = NULL;
48
49static GList *dt_guides_get_list(void)
50{
51 return _guides;
52}
53
54
55#define DEFAULT_GUIDE_NAME "rules of thirds"
56
57static const char *_guide_names[]
58 = { N_("grid"),
59 N_("rules of thirds"),
60 N_("metering"),
61 N_("perspective"),
62 N_("diagonal method"),
63 N_("harmonious triangles"),
64 N_("golden sections"),
65 N_("golden spiral"),
66 N_("golden spiral sections"),
67 N_("golden mean (all guides)"),
68 NULL };
69
77
78typedef struct dt_QRect_t
79{
82
83static void dt_guides_q_rect(dt_QRect_t *R1, float left, float top, float width, float height)
84{
85 R1->left = left;
86 R1->top = top;
87 R1->right = left + width;
88 R1->bottom = top + height;
89 R1->width = width;
90 R1->height = height;
91}
92
97
98
99// return the index of the guide in the list or -1 if not found
100static int _guides_get_value(gchar *name)
101{
102 int i = 0;
103 for(GList *iter = dt_guides_get_list(); iter; iter = g_list_next(iter), i++)
104 {
105 dt_guides_t *guide = (dt_guides_t *)iter->data;
106 if(!g_strcmp0(name, guide->name)) return i;
107 }
108 return -1;
109}
110
111static gchar *_conf_get_path(gchar *module_name, gchar *property_1, gchar *property_2)
112{
114 if(IS_NULL_PTR(vm)) return NULL;
116 // in lighttable, we store panels states per layout
117 char lay[32] = "";
118 if(g_strcmp0(cv->module_name, "lighttable") == 0)
119 {
120 g_snprintf(lay, sizeof(lay), "%d/", 0);
121 }
122 else if(g_strcmp0(cv->module_name, "darkroom") == 0)
123 {
124 g_snprintf(lay, sizeof(lay), "%d/", dt_view_darkroom_get_layout(vm));
125 }
126
127 if(property_2)
128 return dt_util_dstrcat(NULL, "guides/%s/%s%s/%s/%s", cv->module_name, lay, module_name, property_1, property_2);
129 else
130 return dt_util_dstrcat(NULL, "guides/%s/%s%s/%s", cv->module_name, lay, module_name, property_1);
131}
132
133static dt_guides_t *_conf_get_guide(gchar *module_name)
134{
135 dt_guides_t *guide = NULL;
136 gchar *key = _conf_get_path(module_name, "guide", NULL);
138 gchar *val = dt_conf_get_string(key);
139 guide = (dt_guides_t *)g_list_nth_data(dt_guides_get_list(), _guides_get_value(val));
140 dt_free(val);
141 dt_free(key);
142
143 // if we don't have any valid guide, let's fallback to default one
144 if(!guide)
145 {
146 // global case : we fallback to "rule of third"
147 guide = (dt_guides_t *)g_list_nth_data(dt_guides_get_list(), 1);
148 }
149
150 return guide;
151}
152
153static gchar *_conf_get_guide_name(gchar *module_name)
154{
155 dt_guides_t *guide = _conf_get_guide(module_name);
156 if(guide) return guide->name;
157 return NULL;
158}
159
160static void dt_guides_draw_grid(cairo_t *cr, const float x, const float y, const float w, const float h,
161 float zoom_scale, void *data)
162{
163 // retrieve the grid values in settings
164 int nbh = 3, nbv = 3, subdiv = 3;
165 gboolean loaded = FALSE;
166
167 // if we want the global setting
168 gchar *val = _conf_get_guide_name("global");
169 if(val && !g_strcmp0(val, "grid"))
170 {
171 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
173 dt_free(key);
174 key = _conf_get_path("global", "grid_nbv", NULL);
176 dt_free(key);
177 key = _conf_get_path("global", "grid_subdiv", NULL);
179 dt_free(key);
180 loaded = TRUE;
181 }
182 // if stille not loaded that mean we don't want to be here !
183 if(!loaded) return;
184
185 float right = x + w;
186 float bottom = y + h;
187 double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
188
189 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
190
191 cairo_set_dash(cr, &dashes, 1, 0);
193 dt_draw_horizontal_lines(cr, (1 + nbh) * (1 + subdiv), x, y, right, bottom);
194 dt_draw_vertical_lines(cr, (1 + nbv) * (1 + subdiv), x, y, right, bottom);
195 cairo_set_dash(cr, &dashes, 1, dashes);
197 dt_draw_horizontal_lines(cr, (1 + nbh) * (1 + subdiv), x, y, right, bottom);
198 dt_draw_vertical_lines(cr, (1 + nbv) * (1 + subdiv), x, y, right, bottom);
199
200 cairo_set_dash(cr, &dashes, 1, 0);
202 dt_draw_horizontal_lines(cr, 1 + nbh, x, y, right, bottom);
203 dt_draw_vertical_lines(cr, 1 + nbv, x, y, right, bottom);
204
205 cairo_set_dash(cr, &dashes, 1, dashes);
207 dt_draw_horizontal_lines(cr, 1 + nbh, x, y, right, bottom);
208 dt_draw_vertical_lines(cr, 1 + nbv, x, y, right, bottom);
209}
210
211static void _grid_horizontal_changed(GtkWidget *w, void *data)
212{
213 int horizontal = dt_bauhaus_slider_get(w);
214 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
215 dt_conf_set_int(key, horizontal);
216 dt_free(key);
218}
219
220static void _grid_vertical_changed(GtkWidget *w, void *data)
221{
222 int vertical = dt_bauhaus_slider_get(w);
223 gchar *key = _conf_get_path("global", "grid_nbv", NULL);
224 dt_conf_set_int(key, vertical);
225 dt_free(key);
227}
228
229static void _grid_subdiv_changed(GtkWidget *w, void *data)
230{
231 int subdiv = dt_bauhaus_slider_get(w);
232 gchar *key = _conf_get_path("global", "grid_subdiv", NULL);
233 dt_conf_set_int(key, subdiv);
234 dt_free(key);
236}
237
238static GtkWidget *_guides_gui_grid(dt_iop_module_t *self, void *user_data)
239{
240 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
241
242 GtkWidget *grid_horizontal = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(NULL), 0, 12, 1, 3, 0);
243 dt_bauhaus_slider_set_hard_max(grid_horizontal, 36);
244 dt_bauhaus_widget_set_label(grid_horizontal, N_("horizontal lines"));
245 gtk_widget_set_tooltip_text(grid_horizontal, _("number of horizontal guide lines"));
246 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_horizontal), TRUE, TRUE, 0);
247 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
249 dt_free(key);
250 g_signal_connect(G_OBJECT(grid_horizontal), "value-changed", G_CALLBACK(_grid_horizontal_changed), user_data);
251
252 GtkWidget *grid_vertical = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(NULL), 0, 12, 1, 3, 0);
253 dt_bauhaus_slider_set_hard_max(grid_vertical, 36);
254 dt_bauhaus_widget_set_label(grid_vertical, N_("vertical lines"));
255 gtk_widget_set_tooltip_text(grid_vertical, _("number of vertical guide lines"));
256 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_vertical), TRUE, TRUE, 0);
257 key = _conf_get_path("global", "grid_nbv", NULL);
259 dt_free(key);
260 g_signal_connect(G_OBJECT(grid_vertical), "value-changed", G_CALLBACK(_grid_vertical_changed), user_data);
261
262 GtkWidget *grid_subdiv = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(NULL), 0, 10, 1, 3, 0);
263 dt_bauhaus_slider_set_hard_max(grid_subdiv, 30);
264 dt_bauhaus_widget_set_label(grid_subdiv, N_("subdivisions"));
265 gtk_widget_set_tooltip_text(grid_subdiv, _("number of subdivisions per grid rectangle"));
266 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_subdiv), TRUE, TRUE, 0);
267 key = _conf_get_path("global", "grid_subdiv", NULL);
269 dt_free(key);
270 g_signal_connect(G_OBJECT(grid_subdiv), "value-changed", G_CALLBACK(_grid_subdiv_changed), user_data);
271
272 return box;
273}
274
275static void dt_guides_draw_diagonal_method(cairo_t *cr, const float x, const float y, const float w, const float h)
276{
277 if(w > h)
278 {
279 dt_draw_line(cr, x, y, x + h, y + h);
280 dt_draw_line(cr, x, y + h, x + h, y);
281 dt_draw_line(cr, x + w - h, y, x + w, y + h);
282 dt_draw_line(cr, x + w - h, y + h, x + w, y);
283 }
284 else
285 {
286 dt_draw_line(cr, x, y, x + w, y + w);
287 dt_draw_line(cr, x, y + w, x + w, y);
288 dt_draw_line(cr, x, y + h - w, x + w, y + h);
289 dt_draw_line(cr, x, y + h, x + w, y + h - w);
290 }
291}
292
293
294static void dt_guides_draw_rules_of_thirds(cairo_t *cr, const float left, const float top,
295 const float width, const float height)
296{
297 const float right = left + width, bottom = top + height;
298 const float x_3 = width / 3.0, y_3 = height / 3.0;
299
300 dt_draw_line(cr, left + x_3, top, left + x_3, bottom);
301 dt_draw_line(cr, left + 2 * x_3, top, left + 2 * x_3, bottom);
302
303 dt_draw_line(cr, left, top + y_3, right, top + y_3);
304 dt_draw_line(cr, left, top + 2 * y_3, right, top + 2 * y_3);
305}
306
307
308static void dt_guides_draw_harmonious_triangles(cairo_t *cr, const float left, const float top, const float width,
309 const float height/*, const float dst*/)
310{
311 int dst = (int)((height * cosf(atanf(width / height)) / (cosf(atanf(height / width)))));
312
313 dt_draw_line(cr, -width / 2, -height / 2, width / 2, height / 2);
314 dt_draw_line(cr, -width / 2 + dst, -height / 2, -width / 2, height / 2);
315 dt_draw_line(cr, width / 2, -height / 2, width / 2 - dst, height / 2);
316}
317
318
319#define PERSPECTIVE_LINES 16
320static void dt_guides_draw_perspective(cairo_t *cr, const float x, const float y, const float w, const float h)
321{
322 const float rotation_step = 2.0 / PERSPECTIVE_LINES,
323 line_length = w * w + h * h; // no need for sqrt or *0.25, this is inside a cairo_clip anyway
324
325 cairo_save(cr);
326 for(int i = 0; i < PERSPECTIVE_LINES; i++)
327 {
328 cairo_save(cr);
329 cairo_rotate(cr, -M_PI * rotation_step * i);
330 dt_draw_line(cr, 0, 0, line_length, 0);
331 cairo_restore(cr);
332 }
333 cairo_restore(cr);
334}
335#undef PERSPECTIVE_LINES
336
337
338#define X_LINES 49
339#define Y_LINES 33
340#define CROSSES 6
341static void dt_guides_draw_metering(cairo_t *cr, const float x, const float y, const float w, const float h)
342{
343 const float x_step = w / (X_LINES - 1), y_step = h / (Y_LINES - 1), length_short = MIN(w, h) * 0.02,
344 length_middle = length_short * 1.5,
345 length_long = length_middle * 1.5; // these are effectively * 2!
346
347 cairo_save(cr);
348 cairo_translate(cr, x, y);
349
350 // along x axis
351 cairo_save(cr);
352 cairo_translate(cr, 0, h * 0.5);
353 for(int i = 0; i < X_LINES; i++)
354 if(i % 4 != 0)
355 dt_draw_line(cr, i * x_step, -length_short, i * x_step, length_short); // short lines
356 else if(i % 12 != 0)
357 dt_draw_line(cr, i * x_step, -length_middle, i * x_step, length_middle); // medium lines
358 else if(i != X_LINES / 2)
359 dt_draw_line(cr, i * x_step, -length_long, i * x_step, length_long); // long lines
360 else
361 dt_draw_line(cr, i * x_step, -h * 0.5, i * x_step, h * 0.5); // middle line
362 cairo_restore(cr);
363
364 // along y axis
365 cairo_save(cr);
366 cairo_translate(cr, w * 0.5, 0);
367 for(int i = 0; i < Y_LINES; i++)
368 if((i - 4) % 4 != 0)
369 dt_draw_line(cr, -length_short, i * y_step, length_short, i * y_step); // short lines
370 else if(i == Y_LINES / 2)
371 dt_draw_line(cr, -w * 0.5, i * y_step, w * 0.5, i * y_step); // middle line
372 else if((i - 4) % 12 != 0)
373 dt_draw_line(cr, -length_middle, i * y_step, length_middle, i * y_step); // medium lines
374 else
375 dt_draw_line(cr, -length_long, i * y_step, length_long, i * y_step); // long lines
376 cairo_restore(cr);
377
378 // small crosses
379 const float length_cross = length_short * .5, cross_x_step = w / CROSSES, cross_y_step = h / CROSSES;
380 for(int cx = 1; cx < CROSSES; cx++)
381 for(int cy = 1; cy < CROSSES; cy++)
382 if(cx != CROSSES / 2 && cy != CROSSES / 2)
383 {
384 float _x = cx * cross_x_step, _y = cy * cross_y_step;
385 dt_draw_line(cr, _x - length_cross, _y, _x + length_cross, _y);
386 dt_draw_line(cr, _x, _y - length_cross, _x, _y + length_cross);
387 }
388 cairo_restore(cr);
389}
390#undef X_LINES
391#undef y_LINES
392#undef CROSSES
393
394#define RADIANS(degrees) ((degrees) * (M_PI / 180.))
395static void dt_guides_draw_golden_mean(cairo_t *cr, dt_QRect_t *R1, dt_QRect_t *R2, dt_QRect_t *R3, dt_QRect_t *R4,
396 dt_QRect_t *R5, dt_QRect_t *R6, dt_QRect_t *R7, gboolean goldenSection,
397 gboolean goldenTriangle, gboolean goldenSpiralSection, gboolean goldenSpiral)
398{
399 // Drawing Golden sections.
400 if(goldenSection)
401 {
402 // horizontal lines:
403 dt_draw_line(cr, R1->left, R2->top, R2->right, R2->top);
404 dt_draw_line(cr, R1->left, R1->top + R2->height, R2->right, R1->top + R2->height);
405
406 // vertical lines:
407 dt_draw_line(cr, R1->right, R1->top, R1->right, R1->bottom);
408 dt_draw_line(cr, R1->left + R2->width, R1->top, R1->left + R2->width, R1->bottom);
409 }
410
411 // Drawing Golden triangle guides.
412 if(goldenTriangle)
413 {
414 dt_draw_line(cr, R1->left, R1->bottom, R2->right, R1->top);
415 dt_draw_line(cr, R1->left, R1->top, R2->right - R1->width, R1->bottom);
416 dt_draw_line(cr, R1->left + R1->width, R1->top, R2->right, R1->bottom);
417 }
418
419 // Drawing Golden spiral sections.
420 if(goldenSpiralSection)
421 {
422 dt_draw_line(cr, R1->right, R1->top, R1->right, R1->bottom);
423 dt_draw_line(cr, R2->left, R2->top, R2->right, R2->top);
424 dt_draw_line(cr, R3->left, R3->top, R3->left, R3->bottom);
425 dt_draw_line(cr, R4->left, R4->bottom, R4->right, R4->bottom);
426 dt_draw_line(cr, R5->right, R5->top, R5->right, R5->bottom);
427 dt_draw_line(cr, R6->left, R6->top, R6->right, R6->top);
428 dt_draw_line(cr, R7->left, R7->top, R7->left, R7->bottom);
429 }
430
431 // Drawing Golden Spiral.
432 if(goldenSpiral)
433 {
434 cairo_save(cr);
435 cairo_new_sub_path(cr);
436 cairo_scale(cr, R1->width / R1->height, 1);
437 cairo_arc(cr, R1->right / R1->width * R1->height, R1->top, R1->height, RADIANS(90), RADIANS(180));
438 cairo_restore(cr);
439
440 cairo_save(cr);
441 cairo_new_sub_path(cr);
442 cairo_scale(cr, R2->width / R2->height, 1);
443 cairo_arc(cr, R2->left / R2->width * R2->height, R2->top, R2->height, RADIANS(0), RADIANS(90));
444 cairo_restore(cr);
445
446 cairo_save(cr);
447 cairo_new_sub_path(cr);
448 cairo_scale(cr, R3->width / R3->height, 1);
449 cairo_arc(cr, R3->left / R3->width * R3->height, R3->bottom, R3->height, RADIANS(270), RADIANS(360));
450 cairo_restore(cr);
451
452 cairo_save(cr);
453 cairo_new_sub_path(cr);
454 cairo_scale(cr, 1, R4->height / R4->width);
455 cairo_arc(cr, R4->right, R4->bottom / R4->height * R4->width, R4->width, RADIANS(180), RADIANS(270));
456 cairo_restore(cr);
457
458 cairo_save(cr);
459 cairo_new_sub_path(cr);
460 cairo_scale(cr, 1, R5->height / R5->width);
461 cairo_arc(cr, R5->right, R5->top / R5->height * R5->width, R5->width, RADIANS(90), RADIANS(180));
462 cairo_restore(cr);
463
464 cairo_save(cr);
465 cairo_new_sub_path(cr);
466 cairo_scale(cr, 1, R6->height / R6->width);
467 cairo_arc(cr, R6->left, R6->top / R6->height * R6->width, R6->width, RADIANS(0), RADIANS(90));
468 cairo_restore(cr);
469
470 cairo_save(cr);
471 cairo_new_sub_path(cr);
472 cairo_scale(cr, R7->width / R7->height, 1);
473 cairo_arc(cr, R7->left / R7->width * R7->height, R7->bottom, R7->height, RADIANS(270), RADIANS(360));
474 cairo_restore(cr);
475
476 cairo_save(cr);
477 cairo_new_sub_path(cr);
478 cairo_scale(cr, (R6->width - R7->width) / R7->height, 1);
479 cairo_arc(cr, R7->left / (R6->width - R7->width) * R7->height, R7->bottom, R7->height, RADIANS(210),
480 RADIANS(270));
481 cairo_restore(cr);
482 }
483}
484#undef RADIANS
485
486
488
489static void _guides_draw_grid(cairo_t *cr, const float x, const float y,
490 const float w, const float h,
491 const float zoom_scale, void *user_data)
492{
493 dt_guides_draw_grid(cr, x, y, w, h, zoom_scale, user_data);
494}
495static void _guides_draw_rules_of_thirds(cairo_t *cr, const float x, const float y,
496 const float w, const float h,
497 const float zoom_scale, void *user_data)
498{
499 dt_guides_draw_rules_of_thirds(cr, x, y, w, h);
500}
501static void _guides_draw_metering(cairo_t *cr, const float x, const float y,
502 const float w, const float h,
503 const float zoom_scale, void *user_data)
504{
505 dt_guides_draw_metering(cr, x, y, w, h);
506}
507static void _guides_draw_perspective(cairo_t *cr, const float x, const float y,
508 const float w, const float h,
509 const float zoom_scale, void *user_data)
510{
511 dt_guides_draw_perspective(cr, x, y, w, h);
512}
513static void _guides_draw_diagonal_method(cairo_t *cr, const float x, const float y,
514 const float w, const float h,
515 const float zoom_scale, void *user_data)
516{
517 dt_guides_draw_diagonal_method(cr, x, y, w, h);
518}
519static void _guides_draw_harmonious_triangles(cairo_t *cr, const float x, const float y,
520 const float w, const float h,
521 const float zoom_scale, void *user_data)
522{
524}
525static void _guides_draw_golden_mean(cairo_t *cr, const float x, const float y,
526 const float w, const float h,
527 const float zoom_scale, void *user_data)
528{
529 // retrieve the golden extra in settings
531 if(user_data) extra = GPOINTER_TO_INT(user_data);
532
533 // lengths for the golden mean and half the sizes of the region:
534 float w_g = w * INVPHI;
535 float h_g = h * INVPHI;
536 float w_2 = w / 2;
537 float h_2 = h / 2;
538
539 dt_QRect_t R1, R2, R3, R4, R5, R6, R7;
540 dt_guides_q_rect(&R1, -w_2, -h_2, w_g, h);
541
542 // w - 2*w_2 corrects for one-pixel difference
543 // so that R2.right() is really at the right end of the region
544 dt_guides_q_rect(&R2, w_g - w_2, h_2 - h_g, w - w_g + 1 - (w - 2 * w_2), h_g);
545 dt_guides_q_rect(&R3, w_2 - R2.width * INVPHI, -h_2, R2.width * INVPHI, h - R2.height);
546 dt_guides_q_rect(&R4, R2.left, R1.top, R3.left - R2.left, R3.height * INVPHI);
547 dt_guides_q_rect(&R5, R4.left, R4.bottom, R4.width * INVPHI, R3.height - R4.height);
548 dt_guides_q_rect(&R6, R5.left + R5.width, R5.bottom - R5.height * INVPHI, R3.left - R5.right,
549 R5.height * INVPHI);
550 dt_guides_q_rect(&R7, R6.right - R6.width * INVPHI, R4.bottom, R6.width * INVPHI, R5.height - R6.height);
551
553 cr, &R1, &R2, &R3, &R4, &R5, &R6, &R7, (extra == GOLDEN_SECTION || extra == GOLDEN_ALL), FALSE,
554 (extra == GOLDEN_SPIRAL_SECTION || extra == GOLDEN_ALL), (extra == GOLDEN_SPIRAL || extra == GOLDEN_ALL));
555}
556
557static void _guides_add_guide(GList **list, const char *name,
560 void *user_data, GDestroyNotify free,
561 gboolean support_flip)
562{
563 dt_guides_t *guide = (dt_guides_t *)malloc(sizeof(dt_guides_t));
564 g_strlcpy(guide->name, name, sizeof(guide->name));
565 guide->draw = draw;
566 guide->widget = widget;
567 guide->user_data = user_data;
568 guide->free = free;
569 guide->support_flip = support_flip;
570 *list = g_list_append(*list, guide);
571
572 gchar *key, *val;
573 key = _conf_get_path("global", "guide", NULL);
574 if(key)
575 {
576 val = dt_conf_get_string(key);
577 if(val)
578 {
579 const int i = _guides_get_value(val);
580 dt_free(val);
582 }
583 dt_free(key);
584 }
585}
586
587void dt_guides_add_guide(const char *name, dt_guides_draw_callback draw, dt_guides_widget_callback widget, void *user_data, GDestroyNotify free)
588{
589 _guides_add_guide(&_guides, name, draw, widget, user_data, free, TRUE);
590
592}
593
595{
596 GList *guides = NULL;
597
598 const char **names = _guide_names;
600 _guides_add_guide(&guides, *names++, _guides_draw_rules_of_thirds, NULL, NULL, NULL, FALSE);
601 _guides_add_guide(&guides, *names++, _guides_draw_metering, NULL, NULL, NULL, FALSE);
602 _guides_add_guide(&guides, *names++, _guides_draw_perspective, NULL, NULL, NULL, FALSE); // TODO: make the number of lines configurable with a slider?
603 _guides_add_guide(&guides, *names++, _guides_draw_diagonal_method, NULL, NULL, NULL, FALSE);
604 _guides_add_guide(&guides, *names++, _guides_draw_harmonious_triangles, NULL, NULL, NULL, TRUE);
605 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SECTION), NULL, TRUE);
606 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SPIRAL), NULL, TRUE);
607 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SPIRAL_SECTION), NULL, TRUE);
608 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_ALL), NULL, TRUE);
609
610 _guides = guides;
611}
612
614{
615 // show or hide the flip and extra widgets for global case
617 gtk_widget_set_visible(gw->g_flip, (guide && guide->support_flip));
618 gtk_widget_set_visible(gw->g_widgets, (guide && guide->widget));
619 if((guide && guide->widget))
620 {
621 GtkWidget *w = gtk_bin_get_child(GTK_BIN(gw->g_widgets));
622 if(w) gtk_widget_destroy(w);
623
624 GtkWidget *extra = guide->widget(NULL, guide->user_data);
625 gtk_container_add(GTK_CONTAINER(gw->g_widgets), extra);
626 gtk_widget_show_all(extra);
627 }
628}
629
631{
633
634 // we retrieve the global settings
636 if(guide && guide->support_flip)
637 {
638 gchar *key = _conf_get_path("global", guide->name, "flip");
640 dt_free(key);
641 }
642
644}
645
647{
648 // we save the new setting
650 gchar *key = _conf_get_path("global", "guide", NULL);
651 dt_conf_set_string(key, guide ? guide->name : "rule of thirds");
652 dt_free(key);
653
654 // we update the flip combo
656 // we update the gui
658 // we update global button state
660
661 // we update the drawing
663}
664
666{
667 // we save the new setting
669 if(guide)
670 {
671 gchar *key = _conf_get_path("global", guide->name, "flip");
673 dt_free(key);
674 }
675
676 // we update the drawing
678}
679
681{
682 const int overlay_color = dt_conf_get_int("darkroom/ui/overlay_color");
683 const double contrast = dt_conf_get_float("darkroom/ui/overlay_contrast");
684
685 double red = 0.0, green = 0.0, blue = 0.0;
686
687 if(overlay_color == DT_DEV_OVERLAY_GRAY)
688 red = green = blue = 1.0;
689 else if(overlay_color == DT_DEV_OVERLAY_RED)
690 red = 1.0;
691 else if(overlay_color == DT_DEV_OVERLAY_GREEN)
692 green = 1.0;
693 else if(overlay_color == DT_DEV_OVERLAY_YELLOW)
694 red = green = 1.0;
695 else if(overlay_color == DT_DEV_OVERLAY_CYAN)
696 green = blue = 1.0;
697 else if(overlay_color == DT_DEV_OVERLAY_MAGENTA)
698 red = blue = 1.0;
699
700 dt_widget_set_overlay_color(red, green, blue, contrast);
701}
702
709
711{
712 dt_conf_set_float("darkroom/ui/overlay_contrast", dt_bauhaus_slider_get(slider));
715}
716
717// return the box to be included in the settings popup
719{
720 GtkWidget *pop = gtk_popover_new(button);
721
722 // create a new struct for all the widgets
723 _guides_settings_t *gw = (_guides_settings_t *)g_malloc0(sizeof(_guides_settings_t));
724 g_object_set_data_full(G_OBJECT(pop), "guides-settings", gw, g_free);
725 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
726
727 // title
728 GtkWidget *lb = gtk_label_new(_("global guide overlay settings"));
729 gtk_label_set_justify(GTK_LABEL(lb), GTK_JUSTIFY_CENTER);
730 dt_gui_add_class(lb, "dt_section_label");
731 gtk_box_pack_start(GTK_BOX(vbox), lb, TRUE, TRUE, 0);
732
733 // global guides section
734 gw->g_widgets = gtk_event_box_new();
735 gtk_box_pack_start(GTK_BOX(vbox), gw->g_widgets, TRUE, TRUE, 0);
736 gtk_widget_set_no_show_all(gw->g_widgets, TRUE);
737
738 DT_BAUHAUS_COMBOBOX_NEW_FULL(dt_bauhaus_get_global(), gw->g_flip, NULL, N_("flip"), _("flip guides"),
739 0, (GtkCallback)_settings_flip_changed, gw,
740 N_("none"),
741 N_("horizontally"),
742 N_("vertically"),
743 N_("both"));
744 gtk_box_pack_start(GTK_BOX(vbox), gw->g_flip, TRUE, TRUE, 0);
745 gtk_widget_set_no_show_all(gw->g_flip, TRUE);
746
749 dt_bauhaus_get_global(), NULL, N_("type"), _("setup guide lines"), 0,
750 (GtkCallback)_settings_guides_changed, gw, _guide_names);
751 gtk_box_pack_start(GTK_BOX(vbox), vm->guides, TRUE, TRUE, 0);
752
753 // color section
754 gtk_box_pack_start(GTK_BOX(vbox), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), TRUE, TRUE, 0);
755
756 DT_BAUHAUS_COMBOBOX_NEW_FULL(dt_bauhaus_get_global(), vm->guides_colors, NULL, N_("overlay color"), _("set overlay color"),
757 dt_conf_get_int("darkroom/ui/overlay_color"), (GtkCallback)_settings_colors_changed, gw,
758 N_("gray"),
759 N_("red"),
760 N_("green"),
761 N_("yellow"),
762 N_("cyan"),
763 N_("magenta"));
764 // NOTE: any change in the number of entries above will require a corresponding change in _overlay_cycle_callback
765 // in src/views/darkroom.c
766 gtk_box_pack_start(GTK_BOX(vbox), vm->guides_colors, TRUE, TRUE, 0);
767
769 dt_bauhaus_widget_set_label(contrast, N_("contrast"));
770 gtk_widget_set_tooltip_text(contrast, N_("set the contrast between the lightest and darkest part of the guide overlays"));
771 dt_bauhaus_slider_set(contrast, dt_conf_get_float("darkroom/ui/overlay_contrast"));
772 gtk_box_pack_start(GTK_BOX(vbox), contrast, TRUE, TRUE, 0);
773 g_signal_connect(G_OBJECT(contrast), "value-changed", G_CALLBACK(_settings_contrast_changed), NULL);
774
775 gtk_container_add(GTK_CONTAINER(pop), vbox);
776
777 gtk_widget_show_all(vbox);
778
779 return pop;
780}
781
783{
785 if(IS_NULL_PTR(vm)) return;
786 GtkWidget *bt = vm->guides_toggle;
787
788 gchar *key = _conf_get_path("global", "show", NULL);
789 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), dt_conf_get_bool(key));
790 dt_free(key);
791}
792
793void dt_guides_button_toggled(gboolean active)
794{
795 gchar *key = _conf_get_path("global", "show", NULL);
796 dt_conf_set_bool(key, active);
797 dt_free(key);
798}
799
800void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height,
801 const float zoom_scale)
802{
803 const double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
804
805 // first, we check if we need to show the guides or not
806 gchar *key = _conf_get_path("global", "show", NULL);
807 gboolean show = dt_conf_get_bool(key);
808 dt_free(key);
809 if(!show) return;
810
811 // let's get the guide to show
812 dt_guides_t *guide = _conf_get_guide("global");
813 if(IS_NULL_PTR(guide)) return;
814
815 int flip = 0;
816 // retrieve guide flip
817 if(guide->support_flip)
818 {
819 key = _conf_get_path("global", guide->name, "flip");
821 dt_free(key);
822 }
823
824 // save context
825 cairo_save(cr);
826 cairo_rectangle(cr, left, top, width, height);
827 cairo_clip(cr);
828 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
830 cairo_set_dash(cr, &dashes, 0, 0);
831
832 // Move coordinates to local center selection.
833 cairo_translate(cr, (width / 2 + left), (height / 2 + top));
834
835 // Flip horizontal.
836 if(flip == 1 || flip == 3) cairo_scale(cr, -1, 1);
837 // Flip vertical.
838 if(flip == 2 || flip == 3) cairo_scale(cr, 1, -1);
839
840 // we do the drawing itself
841 guide->draw(cr, -width / 2.0, -height / 2.0, width, height, zoom_scale, guide->user_data);
842
843 cairo_stroke_preserve(cr);
844 cairo_set_dash(cr, &dashes, 1, 0);
846 cairo_stroke(cr);
847
848 cairo_restore(cr);
849}
850
851static void free_guide(void *data)
852{
853 dt_guides_t *guide = (dt_guides_t *)data;
854 if(guide->free) guide->free(guide->user_data);
855 dt_free(guide);
856}
857
859{
860 g_list_free_full(_guides, free_guide);
861 _guides = NULL;
862}
863
865{
866 // configure the values that may have changed from the last show
867 gchar *key = _conf_get_path("global", "guide", NULL);
869 gchar *val = dt_conf_get_string(key);
870 dt_free(key);
871 const int i = _guides_get_value(val);
872 dt_free(val);
875 // colors
876 dt_bauhaus_combobox_set(vm->guides_colors, dt_conf_get_int("darkroom/ui/overlay_color"));
877 dt_bauhaus_slider_set(vm->guides_contrast, dt_conf_get_float("darkroom/ui/overlay_contrast"));
878}
879// clang-format off
880// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
881// vim: shiftwidth=2 expandtab tabstop=2 cindent
882// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
883// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
GtkWidget * dt_bauhaus_combobox_new_full(dt_bauhaus_t *bh, dt_gui_module_t *self, const char *label, const char *tip, int pos, GtkCallback callback, gpointer data, const char **texts)
Definition bauhaus.c:1705
void dt_bauhaus_slider_set_hard_max(GtkWidget *widget, float val)
Definition bauhaus.c:1432
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3280
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1632
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:379
static const float x
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const float top
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
Does key have a value?
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
@ DT_DEV_OVERLAY_CYAN
Definition develop.h:88
@ DT_DEV_OVERLAY_YELLOW
Definition develop.h:87
@ DT_DEV_OVERLAY_RED
Definition develop.h:85
@ DT_DEV_OVERLAY_GREEN
Definition develop.h:86
@ DT_DEV_OVERLAY_GRAY
Definition develop.h:84
@ DT_DEV_OVERLAY_MAGENTA
Definition develop.h:89
GHashTable * names
GtkWidget* -> the name the application knows it by.
static void dt_draw_horizontal_lines(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:268
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
Definition draw.h:147
static void dt_draw_line(cairo_t *cr, float left, float top, float right, float bottom)
Definition draw.h:118
static void dt_draw_vertical_lines(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:255
#define DT_GUI_MODULE(x)
static void _grid_subdiv_changed(GtkWidget *w, void *data)
Definition guides.c:229
static void _guides_draw_perspective(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:507
static dt_guides_t * _conf_get_guide(gchar *module_name)
Definition guides.c:133
dt_golden_type_t
Definition guides.c:71
@ GOLDEN_SECTION
Definition guides.c:72
@ GOLDEN_SPIRAL_SECTION
Definition guides.c:74
@ GOLDEN_ALL
Definition guides.c:75
@ GOLDEN_SPIRAL
Definition guides.c:73
void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height, const float zoom_scale)
Definition guides.c:800
static void _guides_draw_harmonious_triangles(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:519
void dt_guides_add_guide(const char *name, dt_guides_draw_callback draw, dt_guides_widget_callback widget, void *user_data, GDestroyNotify free)
Definition guides.c:587
void dt_guides_init(void)
Definition guides.c:594
static void _grid_horizontal_changed(GtkWidget *w, void *data)
Definition guides.c:211
static gchar * _conf_get_path(gchar *module_name, gchar *property_1, gchar *property_2)
Definition guides.c:111
static gchar * _conf_get_guide_name(gchar *module_name)
Definition guides.c:153
static void _guides_draw_metering(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:501
#define CROSSES
Definition guides.c:340
static void free_guide(void *data)
Definition guides.c:851
static void dt_guides_draw_harmonious_triangles(cairo_t *cr, const float left, const float top, const float width, const float height)
Definition guides.c:308
void dt_guides_update_popover_values()
Definition guides.c:864
static GtkWidget * _guides_gui_grid(dt_iop_module_t *self, void *user_data)
Definition guides.c:238
#define RADIANS(degrees)
Definition guides.c:394
static void _settings_flip_changed(GtkWidget *w, _guides_settings_t *gw)
Definition guides.c:665
static void _grid_vertical_changed(GtkWidget *w, void *data)
Definition guides.c:220
static void dt_guides_draw_rules_of_thirds(cairo_t *cr, const float left, const float top, const float width, const float height)
Definition guides.c:294
void dt_guides_set_overlay_colors()
Definition guides.c:680
static void _settings_colors_changed(GtkWidget *combo, _guides_settings_t *gw)
Definition guides.c:703
static void _settings_update_visibility(_guides_settings_t *gw)
Definition guides.c:613
static void _guides_draw_grid(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:489
#define PERSPECTIVE_LINES
Definition guides.c:319
static void _guides_draw_golden_mean(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:525
static void _guides_add_guide(GList **list, const char *name, dt_guides_draw_callback draw, dt_guides_widget_callback widget, void *user_data, GDestroyNotify free, gboolean support_flip)
Definition guides.c:557
#define Y_LINES
Definition guides.c:339
static void dt_guides_q_rect(dt_QRect_t *R1, float left, float top, float width, float height)
Definition guides.c:83
static GList * dt_guides_get_list(void)
Definition guides.c:49
static void dt_guides_draw_grid(cairo_t *cr, const float x, const float y, const float w, const float h, float zoom_scale, void *data)
Definition guides.c:160
static void dt_guides_draw_metering(cairo_t *cr, const float x, const float y, const float w, const float h)
Definition guides.c:341
static void dt_guides_draw_diagonal_method(cairo_t *cr, const float x, const float y, const float w, const float h)
Definition guides.c:275
static void dt_guides_draw_golden_mean(cairo_t *cr, dt_QRect_t *R1, dt_QRect_t *R2, dt_QRect_t *R3, dt_QRect_t *R4, dt_QRect_t *R5, dt_QRect_t *R6, dt_QRect_t *R7, gboolean goldenSection, gboolean goldenTriangle, gboolean goldenSpiralSection, gboolean goldenSpiral)
Definition guides.c:395
static const char * _guide_names[]
Definition guides.c:58
GtkWidget * dt_guides_popover(dt_view_t *self, GtkWidget *button)
Definition guides.c:718
void dt_guides_cleanup(void)
Definition guides.c:858
void dt_guides_button_toggled(gboolean active)
Definition guides.c:793
static void dt_guides_draw_perspective(cairo_t *cr, const float x, const float y, const float w, const float h)
Definition guides.c:320
static void _settings_contrast_changed(GtkWidget *slider, _guides_settings_t *gw)
Definition guides.c:710
void dt_guides_update_button_state()
Definition guides.c:782
#define X_LINES
Definition guides.c:338
static void _guides_draw_rules_of_thirds(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:495
static void _settings_guides_changed(GtkWidget *w, _guides_settings_t *gw)
Definition guides.c:646
static int _guides_get_value(gchar *name)
Definition guides.c:100
static void _guides_draw_diagonal_method(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.c:513
#define DEFAULT_GUIDE_NAME
Definition guides.c:55
static void _settings_flip_update(_guides_settings_t *gw)
Definition guides.c:630
static GList * _guides
Definition guides.c:47
void(* dt_guides_draw_callback)(cairo_t *cr, const float x, const float y, const float w, const float h, const float zoom_scale, void *user_data)
Definition guides.h:32
GtkWidget *(* dt_guides_widget_callback)(dt_iop_module_t *self, void *user_data)
Definition guides.h:36
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define INVPHI
Definition math.h:74
#define M_PI
Definition math.h:47
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
const char * name
Definition pdf.h:90
Asking the interface to repaint itself.
const float const int flip
GtkWidget * g_flip
Definition guides.c:95
GtkWidget * g_widgets
Definition guides.c:95
float left
Definition guides.c:80
float right
Definition guides.c:80
float bottom
Definition guides.c:80
float height
Definition guides.c:80
float top
Definition guides.c:80
float width
Definition guides.c:80
gboolean support_flip
Definition guides.h:45
void * user_data
Definition guides.h:43
dt_guides_draw_callback draw
Definition guides.h:41
GDestroyNotify free
Definition guides.h:44
char name[64]
Definition guides.h:40
dt_guides_widget_callback widget
Definition guides.h:42
GtkWidget * guides_colors
Definition view.h:213
GtkWidget * guides
Definition view.h:213
GtkWidget * guides_toggle
Definition view.h:213
GtkWidget * guides_contrast
Definition view.h:213
char module_name[64]
Definition view.h:155
#define MIN(a, b)
Definition thinplate.c:32
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:99
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm)
Definition view.c:1335
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139
void dt_widget_set_overlay_color(double red, double green, double blue, double contrast)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)