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 "bauhaus/bauhaus.h"
35#include "common/darktable.h"
36#include "develop/imageop_gui.h"
37#include "dtgtk/button.h"
38#include "gui/guides.h"
39#include "gui/draw.h"
40#include "control/control.h"
41
42#define DEFAULT_GUIDE_NAME "rules of thirds"
43
44static const char *_guide_names[]
45 = { N_("grid"),
46 N_("rules of thirds"),
47 N_("metering"),
48 N_("perspective"),
49 N_("diagonal method"),
50 N_("harmonious triangles"),
51 N_("golden sections"),
52 N_("golden spiral"),
53 N_("golden spiral sections"),
54 N_("golden mean (all guides)"),
55 NULL };
56
64
65typedef struct dt_QRect_t
66{
69
70static void dt_guides_q_rect(dt_QRect_t *R1, float left, float top, float width, float height)
71{
72 R1->left = left;
73 R1->top = top;
74 R1->right = left + width;
75 R1->bottom = top + height;
76 R1->width = width;
77 R1->height = height;
78}
79
84
85
86// return the index of the guide in the list or -1 if not found
87static int _guides_get_value(gchar *name)
88{
89 int i = 0;
90 for(GList *iter = darktable.guides; iter; iter = g_list_next(iter), i++)
91 {
92 dt_guides_t *guide = (dt_guides_t *)iter->data;
93 if(!g_strcmp0(name, guide->name)) return i;
94 }
95 return -1;
96}
97
98static gchar *_conf_get_path(gchar *module_name, gchar *property_1, gchar *property_2)
99{
100 if(IS_NULL_PTR(darktable.view_manager)) return NULL;
102 // in lighttable, we store panels states per layout
103 char lay[32] = "";
104 if(g_strcmp0(cv->module_name, "lighttable") == 0)
105 {
106 g_snprintf(lay, sizeof(lay), "%d/", 0);
107 }
108 else if(g_strcmp0(cv->module_name, "darkroom") == 0)
109 {
110 g_snprintf(lay, sizeof(lay), "%d/", dt_view_darkroom_get_layout(darktable.view_manager));
111 }
112
113 if(property_2)
114 return dt_util_dstrcat(NULL, "guides/%s/%s%s/%s/%s", cv->module_name, lay, module_name, property_1, property_2);
115 else
116 return dt_util_dstrcat(NULL, "guides/%s/%s%s/%s", cv->module_name, lay, module_name, property_1);
117}
118
119static dt_guides_t *_conf_get_guide(gchar *module_name)
120{
121 dt_guides_t *guide = NULL;
122 gchar *key = _conf_get_path(module_name, "guide", NULL);
124 gchar *val = dt_conf_get_string(key);
125 guide = (dt_guides_t *)g_list_nth_data(darktable.guides, _guides_get_value(val));
126 dt_free(val);
127 dt_free(key);
128
129 // if we don't have any valid guide, let's fallback to default one
130 if(!guide)
131 {
132 // global case : we fallback to "rule of third"
133 guide = (dt_guides_t *)g_list_nth_data(darktable.guides, 1);
134 }
135
136 return guide;
137}
138
139static gchar *_conf_get_guide_name(gchar *module_name)
140{
141 dt_guides_t *guide = _conf_get_guide(module_name);
142 if(guide) return guide->name;
143 return NULL;
144}
145
146static void dt_guides_draw_grid(cairo_t *cr, const float x, const float y, const float w, const float h,
147 float zoom_scale, void *data)
148{
149 // retrieve the grid values in settings
150 int nbh = 3, nbv = 3, subdiv = 3;
151 gboolean loaded = FALSE;
152
153 // if we want the global setting
154 gchar *val = _conf_get_guide_name("global");
155 if(val && !g_strcmp0(val, "grid"))
156 {
157 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
159 dt_free(key);
160 key = _conf_get_path("global", "grid_nbv", NULL);
162 dt_free(key);
163 key = _conf_get_path("global", "grid_subdiv", NULL);
165 dt_free(key);
166 loaded = TRUE;
167 }
168 // if stille not loaded that mean we don't want to be here !
169 if(!loaded) return;
170
171 float right = x + w;
172 float bottom = y + h;
173 double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
174
175 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
176
177 cairo_set_dash(cr, &dashes, 1, 0);
179 dt_draw_horizontal_lines(cr, (1 + nbh) * (1 + subdiv), x, y, right, bottom);
180 dt_draw_vertical_lines(cr, (1 + nbv) * (1 + subdiv), x, y, right, bottom);
181 cairo_set_dash(cr, &dashes, 1, dashes);
183 dt_draw_horizontal_lines(cr, (1 + nbh) * (1 + subdiv), x, y, right, bottom);
184 dt_draw_vertical_lines(cr, (1 + nbv) * (1 + subdiv), x, y, right, bottom);
185
186 cairo_set_dash(cr, &dashes, 1, 0);
188 dt_draw_horizontal_lines(cr, 1 + nbh, x, y, right, bottom);
189 dt_draw_vertical_lines(cr, 1 + nbv, x, y, right, bottom);
190
191 cairo_set_dash(cr, &dashes, 1, dashes);
193 dt_draw_horizontal_lines(cr, 1 + nbh, x, y, right, bottom);
194 dt_draw_vertical_lines(cr, 1 + nbv, x, y, right, bottom);
195}
196
197static void _grid_horizontal_changed(GtkWidget *w, void *data)
198{
199 int horizontal = dt_bauhaus_slider_get(w);
200 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
201 dt_conf_set_int(key, horizontal);
202 dt_free(key);
204}
205
206static void _grid_vertical_changed(GtkWidget *w, void *data)
207{
208 int vertical = dt_bauhaus_slider_get(w);
209 gchar *key = _conf_get_path("global", "grid_nbv", NULL);
210 dt_conf_set_int(key, vertical);
211 dt_free(key);
213}
214
215static void _grid_subdiv_changed(GtkWidget *w, void *data)
216{
217 int subdiv = dt_bauhaus_slider_get(w);
218 gchar *key = _conf_get_path("global", "grid_subdiv", NULL);
219 dt_conf_set_int(key, subdiv);
220 dt_free(key);
222}
223
224static GtkWidget *_guides_gui_grid(dt_iop_module_t *self, void *user_data)
225{
226 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
227
228 GtkWidget *grid_horizontal = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0, 12, 1, 3, 0);
229 dt_bauhaus_slider_set_hard_max(grid_horizontal, 36);
230 dt_bauhaus_widget_set_label(grid_horizontal, N_("horizontal lines"));
231 gtk_widget_set_tooltip_text(grid_horizontal, _("number of horizontal guide lines"));
232 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_horizontal), TRUE, TRUE, 0);
233 gchar *key = _conf_get_path("global", "grid_nbh", NULL);
235 dt_free(key);
236 g_signal_connect(G_OBJECT(grid_horizontal), "value-changed", G_CALLBACK(_grid_horizontal_changed), user_data);
237
238 GtkWidget *grid_vertical = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0, 12, 1, 3, 0);
239 dt_bauhaus_slider_set_hard_max(grid_vertical, 36);
240 dt_bauhaus_widget_set_label(grid_vertical, N_("vertical lines"));
241 gtk_widget_set_tooltip_text(grid_vertical, _("number of vertical guide lines"));
242 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_vertical), TRUE, TRUE, 0);
243 key = _conf_get_path("global", "grid_nbv", NULL);
245 dt_free(key);
246 g_signal_connect(G_OBJECT(grid_vertical), "value-changed", G_CALLBACK(_grid_vertical_changed), user_data);
247
248 GtkWidget *grid_subdiv = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(NULL), 0, 10, 1, 3, 0);
249 dt_bauhaus_slider_set_hard_max(grid_subdiv, 30);
250 dt_bauhaus_widget_set_label(grid_subdiv, N_("subdivisions"));
251 gtk_widget_set_tooltip_text(grid_subdiv, _("number of subdivisions per grid rectangle"));
252 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(grid_subdiv), TRUE, TRUE, 0);
253 key = _conf_get_path("global", "grid_subdiv", NULL);
255 dt_free(key);
256 g_signal_connect(G_OBJECT(grid_subdiv), "value-changed", G_CALLBACK(_grid_subdiv_changed), user_data);
257
258 return box;
259}
260
261static void dt_guides_draw_diagonal_method(cairo_t *cr, const float x, const float y, const float w, const float h)
262{
263 if(w > h)
264 {
265 dt_draw_line(cr, x, y, x + h, y + h);
266 dt_draw_line(cr, x, y + h, x + h, y);
267 dt_draw_line(cr, x + w - h, y, x + w, y + h);
268 dt_draw_line(cr, x + w - h, y + h, x + w, y);
269 }
270 else
271 {
272 dt_draw_line(cr, x, y, x + w, y + w);
273 dt_draw_line(cr, x, y + w, x + w, y);
274 dt_draw_line(cr, x, y + h - w, x + w, y + h);
275 dt_draw_line(cr, x, y + h, x + w, y + h - w);
276 }
277}
278
279
280static void dt_guides_draw_rules_of_thirds(cairo_t *cr, const float left, const float top,
281 const float width, const float height)
282{
283 const float right = left + width, bottom = top + height;
284 const float x_3 = width / 3.0, y_3 = height / 3.0;
285
286 dt_draw_line(cr, left + x_3, top, left + x_3, bottom);
287 dt_draw_line(cr, left + 2 * x_3, top, left + 2 * x_3, bottom);
288
289 dt_draw_line(cr, left, top + y_3, right, top + y_3);
290 dt_draw_line(cr, left, top + 2 * y_3, right, top + 2 * y_3);
291}
292
293
294static void dt_guides_draw_harmonious_triangles(cairo_t *cr, const float left, const float top, const float width,
295 const float height/*, const float dst*/)
296{
297 int dst = (int)((height * cosf(atanf(width / height)) / (cosf(atanf(height / width)))));
298
299 dt_draw_line(cr, -width / 2, -height / 2, width / 2, height / 2);
300 dt_draw_line(cr, -width / 2 + dst, -height / 2, -width / 2, height / 2);
301 dt_draw_line(cr, width / 2, -height / 2, width / 2 - dst, height / 2);
302}
303
304
305#define PERSPECTIVE_LINES 16
306static void dt_guides_draw_perspective(cairo_t *cr, const float x, const float y, const float w, const float h)
307{
308 const float rotation_step = 2.0 / PERSPECTIVE_LINES,
309 line_length = w * w + h * h; // no need for sqrt or *0.25, this is inside a cairo_clip anyway
310
311 cairo_save(cr);
312 for(int i = 0; i < PERSPECTIVE_LINES; i++)
313 {
314 cairo_save(cr);
315 cairo_rotate(cr, -M_PI * rotation_step * i);
316 dt_draw_line(cr, 0, 0, line_length, 0);
317 cairo_restore(cr);
318 }
319 cairo_restore(cr);
320}
321#undef PERSPECTIVE_LINES
322
323
324#define X_LINES 49
325#define Y_LINES 33
326#define CROSSES 6
327static void dt_guides_draw_metering(cairo_t *cr, const float x, const float y, const float w, const float h)
328{
329 const float x_step = w / (X_LINES - 1), y_step = h / (Y_LINES - 1), length_short = MIN(w, h) * 0.02,
330 length_middle = length_short * 1.5,
331 length_long = length_middle * 1.5; // these are effectively * 2!
332
333 cairo_save(cr);
334 cairo_translate(cr, x, y);
335
336 // along x axis
337 cairo_save(cr);
338 cairo_translate(cr, 0, h * 0.5);
339 for(int i = 0; i < X_LINES; i++)
340 if(i % 4 != 0)
341 dt_draw_line(cr, i * x_step, -length_short, i * x_step, length_short); // short lines
342 else if(i % 12 != 0)
343 dt_draw_line(cr, i * x_step, -length_middle, i * x_step, length_middle); // medium lines
344 else if(i != X_LINES / 2)
345 dt_draw_line(cr, i * x_step, -length_long, i * x_step, length_long); // long lines
346 else
347 dt_draw_line(cr, i * x_step, -h * 0.5, i * x_step, h * 0.5); // middle line
348 cairo_restore(cr);
349
350 // along y axis
351 cairo_save(cr);
352 cairo_translate(cr, w * 0.5, 0);
353 for(int i = 0; i < Y_LINES; i++)
354 if((i - 4) % 4 != 0)
355 dt_draw_line(cr, -length_short, i * y_step, length_short, i * y_step); // short lines
356 else if(i == Y_LINES / 2)
357 dt_draw_line(cr, -w * 0.5, i * y_step, w * 0.5, i * y_step); // middle line
358 else if((i - 4) % 12 != 0)
359 dt_draw_line(cr, -length_middle, i * y_step, length_middle, i * y_step); // medium lines
360 else
361 dt_draw_line(cr, -length_long, i * y_step, length_long, i * y_step); // long lines
362 cairo_restore(cr);
363
364 // small crosses
365 const float length_cross = length_short * .5, cross_x_step = w / CROSSES, cross_y_step = h / CROSSES;
366 for(int cx = 1; cx < CROSSES; cx++)
367 for(int cy = 1; cy < CROSSES; cy++)
368 if(cx != CROSSES / 2 && cy != CROSSES / 2)
369 {
370 float _x = cx * cross_x_step, _y = cy * cross_y_step;
371 dt_draw_line(cr, _x - length_cross, _y, _x + length_cross, _y);
372 dt_draw_line(cr, _x, _y - length_cross, _x, _y + length_cross);
373 }
374 cairo_restore(cr);
375}
376#undef X_LINES
377#undef y_LINES
378#undef CROSSES
379
380#define RADIANS(degrees) ((degrees) * (M_PI / 180.))
381static 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,
382 dt_QRect_t *R5, dt_QRect_t *R6, dt_QRect_t *R7, gboolean goldenSection,
383 gboolean goldenTriangle, gboolean goldenSpiralSection, gboolean goldenSpiral)
384{
385 // Drawing Golden sections.
386 if(goldenSection)
387 {
388 // horizontal lines:
389 dt_draw_line(cr, R1->left, R2->top, R2->right, R2->top);
390 dt_draw_line(cr, R1->left, R1->top + R2->height, R2->right, R1->top + R2->height);
391
392 // vertical lines:
393 dt_draw_line(cr, R1->right, R1->top, R1->right, R1->bottom);
394 dt_draw_line(cr, R1->left + R2->width, R1->top, R1->left + R2->width, R1->bottom);
395 }
396
397 // Drawing Golden triangle guides.
398 if(goldenTriangle)
399 {
400 dt_draw_line(cr, R1->left, R1->bottom, R2->right, R1->top);
401 dt_draw_line(cr, R1->left, R1->top, R2->right - R1->width, R1->bottom);
402 dt_draw_line(cr, R1->left + R1->width, R1->top, R2->right, R1->bottom);
403 }
404
405 // Drawing Golden spiral sections.
406 if(goldenSpiralSection)
407 {
408 dt_draw_line(cr, R1->right, R1->top, R1->right, R1->bottom);
409 dt_draw_line(cr, R2->left, R2->top, R2->right, R2->top);
410 dt_draw_line(cr, R3->left, R3->top, R3->left, R3->bottom);
411 dt_draw_line(cr, R4->left, R4->bottom, R4->right, R4->bottom);
412 dt_draw_line(cr, R5->right, R5->top, R5->right, R5->bottom);
413 dt_draw_line(cr, R6->left, R6->top, R6->right, R6->top);
414 dt_draw_line(cr, R7->left, R7->top, R7->left, R7->bottom);
415 }
416
417 // Drawing Golden Spiral.
418 if(goldenSpiral)
419 {
420 cairo_save(cr);
421 cairo_new_sub_path(cr);
422 cairo_scale(cr, R1->width / R1->height, 1);
423 cairo_arc(cr, R1->right / R1->width * R1->height, R1->top, R1->height, RADIANS(90), RADIANS(180));
424 cairo_restore(cr);
425
426 cairo_save(cr);
427 cairo_new_sub_path(cr);
428 cairo_scale(cr, R2->width / R2->height, 1);
429 cairo_arc(cr, R2->left / R2->width * R2->height, R2->top, R2->height, RADIANS(0), RADIANS(90));
430 cairo_restore(cr);
431
432 cairo_save(cr);
433 cairo_new_sub_path(cr);
434 cairo_scale(cr, R3->width / R3->height, 1);
435 cairo_arc(cr, R3->left / R3->width * R3->height, R3->bottom, R3->height, RADIANS(270), RADIANS(360));
436 cairo_restore(cr);
437
438 cairo_save(cr);
439 cairo_new_sub_path(cr);
440 cairo_scale(cr, 1, R4->height / R4->width);
441 cairo_arc(cr, R4->right, R4->bottom / R4->height * R4->width, R4->width, RADIANS(180), RADIANS(270));
442 cairo_restore(cr);
443
444 cairo_save(cr);
445 cairo_new_sub_path(cr);
446 cairo_scale(cr, 1, R5->height / R5->width);
447 cairo_arc(cr, R5->right, R5->top / R5->height * R5->width, R5->width, RADIANS(90), RADIANS(180));
448 cairo_restore(cr);
449
450 cairo_save(cr);
451 cairo_new_sub_path(cr);
452 cairo_scale(cr, 1, R6->height / R6->width);
453 cairo_arc(cr, R6->left, R6->top / R6->height * R6->width, R6->width, RADIANS(0), RADIANS(90));
454 cairo_restore(cr);
455
456 cairo_save(cr);
457 cairo_new_sub_path(cr);
458 cairo_scale(cr, R7->width / R7->height, 1);
459 cairo_arc(cr, R7->left / R7->width * R7->height, R7->bottom, R7->height, RADIANS(270), RADIANS(360));
460 cairo_restore(cr);
461
462 cairo_save(cr);
463 cairo_new_sub_path(cr);
464 cairo_scale(cr, (R6->width - R7->width) / R7->height, 1);
465 cairo_arc(cr, R7->left / (R6->width - R7->width) * R7->height, R7->bottom, R7->height, RADIANS(210),
466 RADIANS(270));
467 cairo_restore(cr);
468 }
469}
470#undef RADIANS
471
472
474
475static void _guides_draw_grid(cairo_t *cr, const float x, const float y,
476 const float w, const float h,
477 const float zoom_scale, void *user_data)
478{
479 dt_guides_draw_grid(cr, x, y, w, h, zoom_scale, user_data);
480}
481static void _guides_draw_rules_of_thirds(cairo_t *cr, const float x, const float y,
482 const float w, const float h,
483 const float zoom_scale, void *user_data)
484{
485 dt_guides_draw_rules_of_thirds(cr, x, y, w, h);
486}
487static void _guides_draw_metering(cairo_t *cr, const float x, const float y,
488 const float w, const float h,
489 const float zoom_scale, void *user_data)
490{
491 dt_guides_draw_metering(cr, x, y, w, h);
492}
493static void _guides_draw_perspective(cairo_t *cr, const float x, const float y,
494 const float w, const float h,
495 const float zoom_scale, void *user_data)
496{
497 dt_guides_draw_perspective(cr, x, y, w, h);
498}
499static void _guides_draw_diagonal_method(cairo_t *cr, const float x, const float y,
500 const float w, const float h,
501 const float zoom_scale, void *user_data)
502{
503 dt_guides_draw_diagonal_method(cr, x, y, w, h);
504}
505static void _guides_draw_harmonious_triangles(cairo_t *cr, const float x, const float y,
506 const float w, const float h,
507 const float zoom_scale, void *user_data)
508{
510}
511static void _guides_draw_golden_mean(cairo_t *cr, const float x, const float y,
512 const float w, const float h,
513 const float zoom_scale, void *user_data)
514{
515 // retrieve the golden extra in settings
517 if(user_data) extra = GPOINTER_TO_INT(user_data);
518
519 // lengths for the golden mean and half the sizes of the region:
520 float w_g = w * INVPHI;
521 float h_g = h * INVPHI;
522 float w_2 = w / 2;
523 float h_2 = h / 2;
524
525 dt_QRect_t R1, R2, R3, R4, R5, R6, R7;
526 dt_guides_q_rect(&R1, -w_2, -h_2, w_g, h);
527
528 // w - 2*w_2 corrects for one-pixel difference
529 // so that R2.right() is really at the right end of the region
530 dt_guides_q_rect(&R2, w_g - w_2, h_2 - h_g, w - w_g + 1 - (w - 2 * w_2), h_g);
531 dt_guides_q_rect(&R3, w_2 - R2.width * INVPHI, -h_2, R2.width * INVPHI, h - R2.height);
532 dt_guides_q_rect(&R4, R2.left, R1.top, R3.left - R2.left, R3.height * INVPHI);
533 dt_guides_q_rect(&R5, R4.left, R4.bottom, R4.width * INVPHI, R3.height - R4.height);
534 dt_guides_q_rect(&R6, R5.left + R5.width, R5.bottom - R5.height * INVPHI, R3.left - R5.right,
535 R5.height * INVPHI);
536 dt_guides_q_rect(&R7, R6.right - R6.width * INVPHI, R4.bottom, R6.width * INVPHI, R5.height - R6.height);
537
539 cr, &R1, &R2, &R3, &R4, &R5, &R6, &R7, (extra == GOLDEN_SECTION || extra == GOLDEN_ALL), FALSE,
540 (extra == GOLDEN_SPIRAL_SECTION || extra == GOLDEN_ALL), (extra == GOLDEN_SPIRAL || extra == GOLDEN_ALL));
541}
542
543static void _guides_add_guide(GList **list, const char *name,
546 void *user_data, GDestroyNotify free,
547 gboolean support_flip)
548{
549 dt_guides_t *guide = (dt_guides_t *)malloc(sizeof(dt_guides_t));
550 g_strlcpy(guide->name, name, sizeof(guide->name));
551 guide->draw = draw;
552 guide->widget = widget;
553 guide->user_data = user_data;
554 guide->free = free;
555 guide->support_flip = support_flip;
556 *list = g_list_append(*list, guide);
557
558 gchar *key, *val;
559 key = _conf_get_path("global", "guide", NULL);
560 if(key)
561 {
562 val = dt_conf_get_string(key);
563 if(val)
564 {
565 const int i = _guides_get_value(val);
566 dt_free(val);
568 }
569 dt_free(key);
570 }
571}
572
573void dt_guides_add_guide(const char *name, dt_guides_draw_callback draw, dt_guides_widget_callback widget, void *user_data, GDestroyNotify free)
574{
575 _guides_add_guide(&darktable.guides, name, draw, widget, user_data, free, TRUE);
576
578}
579
581{
582 GList *guides = NULL;
583
584 const char **names = _guide_names;
585 _guides_add_guide(&guides, *names++, _guides_draw_grid, _guides_gui_grid, NULL, NULL, FALSE);
586 _guides_add_guide(&guides, *names++, _guides_draw_rules_of_thirds, NULL, NULL, NULL, FALSE);
587 _guides_add_guide(&guides, *names++, _guides_draw_metering, NULL, NULL, NULL, FALSE);
588 _guides_add_guide(&guides, *names++, _guides_draw_perspective, NULL, NULL, NULL, FALSE); // TODO: make the number of lines configurable with a slider?
589 _guides_add_guide(&guides, *names++, _guides_draw_diagonal_method, NULL, NULL, NULL, FALSE);
590 _guides_add_guide(&guides, *names++, _guides_draw_harmonious_triangles, NULL, NULL, NULL, TRUE);
591 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SECTION), NULL, TRUE);
592 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SPIRAL), NULL, TRUE);
593 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_SPIRAL_SECTION), NULL, TRUE);
594 _guides_add_guide(&guides, *names++, _guides_draw_golden_mean, NULL, GINT_TO_POINTER(GOLDEN_ALL), NULL, TRUE);
595
596 return guides;
597}
598
600{
601 // show or hide the flip and extra widgets for global case
603 gtk_widget_set_visible(gw->g_flip, (guide && guide->support_flip));
604 gtk_widget_set_visible(gw->g_widgets, (guide && guide->widget));
605 if((guide && guide->widget))
606 {
607 GtkWidget *w = gtk_bin_get_child(GTK_BIN(gw->g_widgets));
608 if(w) gtk_widget_destroy(w);
609
610 GtkWidget *extra = guide->widget(NULL, guide->user_data);
611 gtk_container_add(GTK_CONTAINER(gw->g_widgets), extra);
612 gtk_widget_show_all(extra);
613 }
614}
615
617{
619
620 // we retrieve the global settings
622 if(guide && guide->support_flip)
623 {
624 gchar *key = _conf_get_path("global", guide->name, "flip");
626 dt_free(key);
627 }
628
630}
631
633{
634 // we save the new setting
636 gchar *key = _conf_get_path("global", "guide", NULL);
637 dt_conf_set_string(key, guide ? guide->name : "rule of thirds");
638 dt_free(key);
639
640 // we update the flip combo
642 // we update the gui
644 // we update global button state
646
647 // we update the drawing
649}
650
652{
653 // we save the new setting
655 if(guide)
656 {
657 gchar *key = _conf_get_path("global", guide->name, "flip");
659 dt_free(key);
660 }
661
662 // we update the drawing
664}
665
667{
668 const int overlay_color = dt_conf_get_int("darkroom/ui/overlay_color");
669
670 darktable.gui->overlay_contrast = dt_conf_get_float("darkroom/ui/overlay_contrast");
671
673
674 if(overlay_color == DT_DEV_OVERLAY_GRAY)
676 else if(overlay_color == DT_DEV_OVERLAY_RED)
677 darktable.gui->overlay_red = 1.0f;
678 else if(overlay_color == DT_DEV_OVERLAY_GREEN)
680 else if(overlay_color == DT_DEV_OVERLAY_YELLOW)
682 else if(overlay_color == DT_DEV_OVERLAY_CYAN)
684 else if(overlay_color == DT_DEV_OVERLAY_MAGENTA)
686}
687
694
696{
697 dt_conf_set_float("darkroom/ui/overlay_contrast", dt_bauhaus_slider_get(slider));
700}
701
702// return the box to be included in the settings popup
704{
705 GtkWidget *pop = gtk_popover_new(button);
706
707 // create a new struct for all the widgets
708 _guides_settings_t *gw = (_guides_settings_t *)g_malloc0(sizeof(_guides_settings_t));
709 g_object_set_data_full(G_OBJECT(pop), "guides-settings", gw, g_free);
710 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
711
712 // title
713 GtkWidget *lb = gtk_label_new(_("global guide overlay settings"));
714 gtk_label_set_justify(GTK_LABEL(lb), GTK_JUSTIFY_CENTER);
715 dt_gui_add_class(lb, "dt_section_label");
716 gtk_box_pack_start(GTK_BOX(vbox), lb, TRUE, TRUE, 0);
717
718 // global guides section
719 gw->g_widgets = gtk_event_box_new();
720 gtk_box_pack_start(GTK_BOX(vbox), gw->g_widgets, TRUE, TRUE, 0);
721 gtk_widget_set_no_show_all(gw->g_widgets, TRUE);
722
723 DT_BAUHAUS_COMBOBOX_NEW_FULL(darktable.bauhaus, gw->g_flip, NULL, N_("flip"), _("flip guides"),
724 0, (GtkCallback)_settings_flip_changed, gw,
725 N_("none"),
726 N_("horizontally"),
727 N_("vertically"),
728 N_("both"));
729 gtk_box_pack_start(GTK_BOX(vbox), gw->g_flip, TRUE, TRUE, 0);
730 gtk_widget_set_no_show_all(gw->g_flip, TRUE);
731
733 darktable.bauhaus, NULL, N_("type"), _("setup guide lines"), 0,
734 (GtkCallback)_settings_guides_changed, gw, _guide_names);
735 gtk_box_pack_start(GTK_BOX(vbox), darktable.view_manager->guides, TRUE, TRUE, 0);
736
737 // color section
738 gtk_box_pack_start(GTK_BOX(vbox), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), TRUE, TRUE, 0);
739
740 DT_BAUHAUS_COMBOBOX_NEW_FULL(darktable.bauhaus, darktable.view_manager->guides_colors, NULL, N_("overlay color"), _("set overlay color"),
741 dt_conf_get_int("darkroom/ui/overlay_color"), (GtkCallback)_settings_colors_changed, gw,
742 N_("gray"),
743 N_("red"),
744 N_("green"),
745 N_("yellow"),
746 N_("cyan"),
747 N_("magenta"));
748 // NOTE: any change in the number of entries above will require a corresponding change in _overlay_cycle_callback
749 // in src/views/darkroom.c
750 gtk_box_pack_start(GTK_BOX(vbox), darktable.view_manager->guides_colors, TRUE, TRUE, 0);
751
753 dt_bauhaus_widget_set_label(contrast, N_("contrast"));
754 gtk_widget_set_tooltip_text(contrast, N_("set the contrast between the lightest and darkest part of the guide overlays"));
755 dt_bauhaus_slider_set(contrast, dt_conf_get_float("darkroom/ui/overlay_contrast"));
756 gtk_box_pack_start(GTK_BOX(vbox), contrast, TRUE, TRUE, 0);
757 g_signal_connect(G_OBJECT(contrast), "value-changed", G_CALLBACK(_settings_contrast_changed), NULL);
758
759 gtk_container_add(GTK_CONTAINER(pop), vbox);
760
761 gtk_widget_show_all(vbox);
762
763 return pop;
764}
765
767{
770
771 gchar *key = _conf_get_path("global", "show", NULL);
772 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), dt_conf_get_bool(key));
773 dt_free(key);
774}
775
776void dt_guides_button_toggled(gboolean active)
777{
778 gchar *key = _conf_get_path("global", "show", NULL);
779 dt_conf_set_bool(key, active);
780 dt_free(key);
781}
782
783void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height,
784 const float zoom_scale)
785{
786 const double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
787
788 // first, we check if we need to show the guides or not
789 gchar *key = _conf_get_path("global", "show", NULL);
790 gboolean show = dt_conf_get_bool(key);
791 dt_free(key);
792 if(!show) return;
793
794 // let's get the guide to show
795 dt_guides_t *guide = _conf_get_guide("global");
796 if(IS_NULL_PTR(guide)) return;
797
798 int flip = 0;
799 // retrieve guide flip
800 if(guide->support_flip)
801 {
802 key = _conf_get_path("global", guide->name, "flip");
804 dt_free(key);
805 }
806
807 // save context
808 cairo_save(cr);
809 cairo_rectangle(cr, left, top, width, height);
810 cairo_clip(cr);
811 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
813 cairo_set_dash(cr, &dashes, 0, 0);
814
815 // Move coordinates to local center selection.
816 cairo_translate(cr, (width / 2 + left), (height / 2 + top));
817
818 // Flip horizontal.
819 if(flip == 1 || flip == 3) cairo_scale(cr, -1, 1);
820 // Flip vertical.
821 if(flip == 2 || flip == 3) cairo_scale(cr, 1, -1);
822
823 // we do the drawing itself
824 guide->draw(cr, -width / 2.0, -height / 2.0, width, height, zoom_scale, guide->user_data);
825
826 cairo_stroke_preserve(cr);
827 cairo_set_dash(cr, &dashes, 1, 0);
829 cairo_stroke(cr);
830
831 cairo_restore(cr);
832}
833
834static void free_guide(void *data)
835{
836 dt_guides_t *guide = (dt_guides_t *)data;
837 if(guide->free) guide->free(guide->user_data);
838 dt_free(guide);
839}
840
841void dt_guides_cleanup(GList *guides)
842{
843 g_list_free_full(guides, free_guide);
844 guides = NULL;
845}
846
848{
849 // configure the values that may have changed from the last show
850 gchar *key = _conf_get_path("global", "guide", NULL);
852 gchar *val = dt_conf_get_string(key);
853 dt_free(key);
854 const int i = _guides_get_value(val);
855 dt_free(val);
857 // colors
860}
861// clang-format off
862// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
863// vim: shiftwidth=2 expandtab tabstop=2 cindent
864// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
865// 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:1849
void dt_bauhaus_slider_set_hard_max(GtkWidget *widget, float val)
Definition bauhaus.c:1583
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3483
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
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:1780
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:392
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
const float top
char * key
char * name
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)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
darktable_t darktable
Definition darktable.c:181
#define dt_free(ptr)
Definition darktable.h:456
#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 darktable.h:281
@ DT_DEV_OVERLAY_CYAN
Definition develop.h:83
@ DT_DEV_OVERLAY_YELLOW
Definition develop.h:82
@ DT_DEV_OVERLAY_RED
Definition develop.h:80
@ DT_DEV_OVERLAY_GREEN
Definition develop.h:81
@ DT_DEV_OVERLAY_GRAY
Definition develop.h:79
@ DT_DEV_OVERLAY_MAGENTA
Definition develop.h:84
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:253
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
Definition draw.h:106
static void dt_draw_line(cairo_t *cr, float left, float top, float right, float bottom)
Definition draw.h:137
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:240
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
#define DT_GUI_MODULE(x)
static void _grid_subdiv_changed(GtkWidget *w, void *data)
Definition guides.c:215
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:493
static dt_guides_t * _conf_get_guide(gchar *module_name)
Definition guides.c:119
dt_golden_type_t
Definition guides.c:58
@ GOLDEN_SECTION
Definition guides.c:59
@ GOLDEN_SPIRAL_SECTION
Definition guides.c:61
@ GOLDEN_ALL
Definition guides.c:62
@ GOLDEN_SPIRAL
Definition guides.c:60
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:783
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:505
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:573
static void _grid_horizontal_changed(GtkWidget *w, void *data)
Definition guides.c:197
static gchar * _conf_get_path(gchar *module_name, gchar *property_1, gchar *property_2)
Definition guides.c:98
static gchar * _conf_get_guide_name(gchar *module_name)
Definition guides.c:139
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:487
#define CROSSES
Definition guides.c:326
static void free_guide(void *data)
Definition guides.c:834
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:294
void dt_guides_update_popover_values()
Definition guides.c:847
static GtkWidget * _guides_gui_grid(dt_iop_module_t *self, void *user_data)
Definition guides.c:224
#define RADIANS(degrees)
Definition guides.c:380
static void _settings_flip_changed(GtkWidget *w, _guides_settings_t *gw)
Definition guides.c:651
static void _grid_vertical_changed(GtkWidget *w, void *data)
Definition guides.c:206
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:280
void dt_guides_set_overlay_colors()
Definition guides.c:666
static void _settings_colors_changed(GtkWidget *combo, _guides_settings_t *gw)
Definition guides.c:688
static void _settings_update_visibility(_guides_settings_t *gw)
Definition guides.c:599
GList * dt_guides_init()
Definition guides.c:580
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:475
#define PERSPECTIVE_LINES
Definition guides.c:305
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:511
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:543
#define Y_LINES
Definition guides.c:325
static void dt_guides_q_rect(dt_QRect_t *R1, float left, float top, float width, float height)
Definition guides.c:70
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:146
void dt_guides_cleanup(GList *guides)
Definition guides.c:841
static void dt_guides_draw_metering(cairo_t *cr, const float x, const float y, const float w, const float h)
Definition guides.c:327
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:261
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:381
static const char * _guide_names[]
Definition guides.c:45
GtkWidget * dt_guides_popover(dt_view_t *self, GtkWidget *button)
Definition guides.c:703
void dt_guides_button_toggled(gboolean active)
Definition guides.c:776
static void dt_guides_draw_perspective(cairo_t *cr, const float x, const float y, const float w, const float h)
Definition guides.c:306
static void _settings_contrast_changed(GtkWidget *slider, _guides_settings_t *gw)
Definition guides.c:695
void dt_guides_update_button_state()
Definition guides.c:766
#define X_LINES
Definition guides.c:324
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:481
static void _settings_guides_changed(GtkWidget *w, _guides_settings_t *gw)
Definition guides.c:632
static int _guides_get_value(gchar *name)
Definition guides.c:87
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:499
#define DEFAULT_GUIDE_NAME
Definition guides.c:42
static void _settings_flip_update(_guides_settings_t *gw)
Definition guides.c:616
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:31
GtkWidget *(* dt_guides_widget_callback)(dt_iop_module_t *self, void *user_data)
Definition guides.h:35
static const float x
#define INVPHI
Definition math.h:72
#define M_PI
Definition math.h:45
struct _GtkWidget GtkWidget
Definition splash.h:29
const float const int flip
GtkWidget * g_flip
Definition guides.c:82
GtkWidget * g_widgets
Definition guides.c:82
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_view_manager_t * view_manager
Definition darktable.h:772
GList * guides
Definition darktable.h:827
float left
Definition guides.c:67
float right
Definition guides.c:67
float bottom
Definition guides.c:67
float height
Definition guides.c:67
float top
Definition guides.c:67
float width
Definition guides.c:67
double overlay_contrast
Definition gtk.h:198
int32_t reset
Definition gtk.h:172
double overlay_red
Definition gtk.h:198
double overlay_green
Definition gtk.h:198
double overlay_blue
Definition gtk.h:198
gboolean support_flip
Definition guides.h:44
void * user_data
Definition guides.h:42
dt_guides_draw_callback draw
Definition guides.h:40
GDestroyNotify free
Definition guides.h:43
char name[64]
Definition guides.h:39
dt_guides_widget_callback widget
Definition guides.h:41
GtkWidget * guides_colors
Definition view.h:220
GtkWidget * guides
Definition view.h:220
GtkWidget * guides_toggle
Definition view.h:220
GtkWidget * guides_contrast
Definition view.h:220
char module_name[64]
Definition view.h:153
#define MIN(a, b)
Definition thinplate.c:32
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:95
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm)
Definition view.c:1332
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:140