Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
navigation.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2012, 2016, 2018 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2012 Richard Wonka.
6 Copyright (C) 2012-2014, 2016-2017 Tobias Ellinghaus.
7 Copyright (C) 2013, 2015, 2019-2020 Aldric Renaudin.
8 Copyright (C) 2013 Josef Wells.
9 Copyright (C) 2013 Pascal de Bruijn.
10 Copyright (C) 2013-2016 Roman Lebedev.
11 Copyright (C) 2014 parafin.
12 Copyright (C) 2014-2015 Ulrich Pegelow.
13 Copyright (C) 2016 Asma.
14 Copyright (C) 2018 Maurizio Paglia.
15 Copyright (C) 2018-2021 Pascal Obry.
16 Copyright (C) 2018 rawfiner.
17 Copyright (C) 2018 Sam Smith.
18 Copyright (C) 2019 Alexis Mousset.
19 Copyright (C) 2019, 2023, 2025-2026 Aurélien PIERRE.
20 Copyright (C) 2019 Edgardo Hoszowski.
21 Copyright (C) 2019 yuri1969.
22 Copyright (C) 2020 Chris Elston.
23 Copyright (C) 2020 Hubert Kowalski.
24 Copyright (C) 2021 Dan Torop.
25 Copyright (C) 2021-2022 Diederik Ter Rahe.
26 Copyright (C) 2021 Hanno Schwalm.
27 Copyright (C) 2021 Paolo DePetrillo.
28 Copyright (C) 2022 Martin Bařinka.
29 Copyright (C) 2023 Alynx Zhou.
30 Copyright (C) 2025-2026 Guillaume Stutin.
31
32 darktable is free software: you can redistribute it and/or modify
33 it under the terms of the GNU General Public License as published by
34 the Free Software Foundation, either version 3 of the License, or
35 (at your option) any later version.
36
37 darktable is distributed in the hope that it will be useful,
38 but WITHOUT ANY WARRANTY; without even the implied warranty of
39 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 GNU General Public License for more details.
41
42 You should have received a copy of the GNU General Public License
43 along with darktable. If not, see <http://www.gnu.org/licenses/>.
44*/
45
46#include "widgets/bauhaus.h"
47#include "system/macros.h"
49#include "common/times.h"
50#include "common/hash.h"
51#include "control/control.h"
54#include "develop/develop.h"
55
56#include "libs/lib.h"
57#include "libs/lib_api.h"
58#include "gui/window_manager.h"
59#include "widgets/popup.h"
60#include "widgets/scroll_wrap.h"
61#include "gui/screen_metrics.h"
62#include "control/signal.h"
63
64DT_MODULE(1)
65
66#define DT_NAVIGATION_INSET 5
67
68typedef struct dt_lib_navigation_t
69{
70 GtkWidget *area; // the drawing area (self->widget is the resizable wrapper around it)
72 int zoom_w, zoom_h; // size of the zoom button
73 cairo_surface_t *image_surface; // the preview frame, scaled to the widget
74 /* The whole picture this widget shows -- background, thumbnail, viewport box, zoom label,
75 * arrow -- composed once per state and blitted on every later expose. Keyed on everything it
76 * depends on (_lib_navigation_picture_key()): an expose that changes none of it costs one
77 * blit, where it used to cost a fresh full-size surface, a background render, a pango layout
78 * and the thumbnail copy. Such exposes are the rule, not the exception: the resize grip
79 * floating over this area is translucent, so GTK repaints what lies under it every time the
80 * grip is hovered, and a panel that moved redraws the whole window with it. */
81 cairo_surface_t *composed;
87
102
103
104/* expose function for navigation module */
105static gboolean _lib_navigation_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data);
106/* motion notify callback handler*/
107static gboolean _lib_navigation_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event,
108 gpointer user_data);
109/* button press callback */
110static gboolean _lib_navigation_button_press_callback(GtkWidget *widget, GdkEventButton *event,
111 gpointer user_data);
112/* button release callback */
113static gboolean _lib_navigation_button_release_callback(GtkWidget *widget, GdkEventButton *event,
114 gpointer user_data);
115/* leave notify callback */
116static gboolean _lib_navigation_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event,
117 gpointer user_data);
118
119/* helper function for position set */
120static void _lib_navigation_set_position(struct dt_lib_module_t *self, double x, double y, int alloc_wd, int alloc_ht);
121
122const char *name(struct dt_lib_module_t *self)
123{
124 return _("navigation");
125}
126
127const char **views(dt_lib_module_t *self)
128{
129 static const char *v[] = {"darkroom", NULL};
130 return v;
131}
132
134{
136}
137
139{
140 return 0;
141}
142
144{
145 return 1001;
146}
147
148
149static void _lib_navigation_control_redraw_callback(gpointer instance, gpointer user_data)
150{
151 (void)instance;
152 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
155}
156
157static void _lib_navigation_restart_cache_wait(gpointer user_data)
158{
159 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
160 if(IS_NULL_PTR(self)) return;
163}
164
165static void _lib_navigation_history_resync_callback(gpointer instance, gpointer user_data)
166{
167 (void)instance;
168
169 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
172 if(IS_NULL_PTR(d) || IS_NULL_PTR(dev) || IS_NULL_PTR(dev->preview_pipe)) return;
173
174 dt_dev_pixelpipe_cache_wait_set_owner(&d->preview_wait, "navigation-preview", self);
175 if(dt_dev_pixelpipe_cache_peek_gui(dev->preview_pipe, NULL, NULL, NULL, &d->preview_wait,
178}
179
180
182{
183 /* initialize ui widgets */
185 self->data = (void *)d;
186 d->image_surface = NULL;
187
188 /* create drawingarea */
189 d->area = gtk_drawing_area_new();
190 gtk_widget_set_events(d->area, GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK
191 | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
192 | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK);
193
194 /* connect callbacks */
195 gtk_widget_set_app_paintable(d->area, TRUE);
196 g_signal_connect(G_OBJECT(d->area), "draw", G_CALLBACK(_lib_navigation_draw_callback), self);
197 g_signal_connect(G_OBJECT(d->area), "button-press-event",
198 G_CALLBACK(_lib_navigation_button_press_callback), self);
199 g_signal_connect(G_OBJECT(d->area), "button-release-event",
201 g_signal_connect(G_OBJECT(d->area), "motion-notify-event",
203 g_signal_connect(G_OBJECT(d->area), "leave-notify-event",
204 G_CALLBACK(_lib_navigation_leave_notify_callback), self);
205
206 gtk_widget_set_name(GTK_WIDGET(d->area), "navigation-module");
207
208 /* make the navigation preview vertically resizable, persisting its height */
209 self->widget = dt_ui_resizable_drawing_area(d->area, "plugins/darkroom/navigation/height", 175, 100);
210
211 /* connect redraw callbacks to targeted preview cache publication and explicit redraw requests */
216
217 dt_lib_get_global()->proxy.navigation.module = self;
218}
219
221{
222 if(IS_NULL_PTR(self->data)) return;
224
225 /* disconnect from signal */
228 dt_dev_pixelpipe_cache_wait_cleanup(&d->preview_wait, "navigation-gui-cleanup");
229
230 if(!IS_NULL_PTR(d->image_surface)) cairo_surface_destroy(d->image_surface);
231 d->image_surface = NULL;
232 if(!IS_NULL_PTR(d->composed)) cairo_surface_destroy(d->composed);
233 d->composed = NULL;
234
235 dt_free(self->data);
236}
237
239{
241 if(!IS_NULL_PTR(d->image_surface)) cairo_surface_destroy(d->image_surface);
242 d->image_surface = NULL;
243}
244
245/* Everything the picture this widget shows depends on, folded into one key: the preview frame,
246 * the widget's size, the image, the viewport (zoom, pan, box, border) and the preview's plan.
247 * Equal keys are equal pictures. */
248static uint64_t _lib_navigation_picture_key(const dt_develop_t *dev, const int width, const int height,
249 const gboolean has_preview_image)
250{
251 uint64_t key = has_preview_image ? dt_dev_backbuf_get_hash(&dev->preview_pipe->backbuf) : 0;
252 key = dt_hash(key, (const char *)&width, sizeof(width));
253 key = dt_hash(key, (const char *)&height, sizeof(height));
254 key = dt_hash(key, (const char *)&has_preview_image, sizeof(has_preview_image));
255 const int32_t imgid = dev->image_storage.id;
256 key = dt_hash(key, (const char *)&imgid, sizeof(imgid));
257 // padding-free, hashed whole the way the darkroom keys its own composition
258 const dt_dev_viewport_state_t viewport = dt_dev_viewport_get(dev);
259 key = dt_hash(key, (const char *)&viewport, sizeof(viewport));
260 const float natural_scale = dt_dev_roi_request_natural_scale(dev);
261 key = dt_hash(key, (const char *)&natural_scale, sizeof(natural_scale));
262 const int preview_width = dt_dev_roi_request_preview_width(dev);
263 const int preview_height = dt_dev_roi_request_preview_height(dev);
264 key = dt_hash(key, (const char *)&preview_width, sizeof(preview_width));
265 key = dt_hash(key, (const char *)&preview_height, sizeof(preview_height));
266 return key;
267}
268
269/* Compose the picture for @p key into a fresh surface and make it the current one. Returns
270 * FALSE, leaving the previous picture in place, when the preview frame cannot be read yet. */
272 const int width, const int height, const gboolean has_preview_image,
273 const uint64_t key)
274{
276
277 static uint64_t image_hash = -1;
278 static int wd = 0;
279 static int ht = 0;
280 static float scale = 1.f;
281 static int32_t imgid = UNKNOWN_IMAGE;
282
283 const gboolean changed = (d->composed_width != width) || (d->composed_height != height);
284
285 cairo_surface_t *target = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
286 cairo_t *cr = cairo_create(target);
287 GtkStyleContext *context = gtk_widget_get_style_context(widget);
288 gtk_render_background(context, cr, 0, 0, width, height);
289 cairo_save(cr);
290
291 if(has_preview_image
292 && (!d->image_surface || image_hash != dt_dev_backbuf_get_hash(&dev->preview_pipe->backbuf) || changed))
293 {
294 if(d->image_surface) cairo_surface_destroy(d->image_surface);
295 d->image_surface = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
296
297 // Fetch the backbuffer. The preview pipe backbuffer already owns the cache keepalive ref:
298 // navigation only needs a short read-only critical section while it copies into its own cairo surface.
299 struct dt_pixel_cache_entry_t *cache_entry = NULL;
300 void *data = NULL;
301 dt_dev_pixelpipe_cache_wait_set_owner(&d->preview_wait, "navigation-preview", self);
302 if(!dt_dev_pixelpipe_cache_peek_gui(dev->preview_pipe, NULL, &data, &cache_entry, &d->preview_wait,
304 {
305 /* the frame is not published yet: the wait above redraws when it is, and until then the
306 * previous picture stands (this used to return with the surface and its context leaked) */
307 cairo_destroy(cr);
308 cairo_surface_destroy(target);
309 return FALSE;
310 }
311
313
314 /* One read of the publication: the cacheline just resolved and the shape its pixels are in
315 * have to come from the same frame, or the stride below is computed from the next frame's
316 * width and this paints diagonal striping. See dt_backbuf_t. */
318 wd = published.width;
319 ht = published.height;
321
322 /* Can this cacheline hold an image of those dimensions at all?
323 *
324 * The centre view asks the same question before it paints (dt_dev_lock_pipe_surface); this
325 * widget did not, and handed cairo a buffer to walk with a stride computed from a width the
326 * data need not have. When they disagree the thumbnail is striped diagonally, and the read
327 * runs past the allocation whenever the recorded dimensions are the larger pair.
328 *
329 * The way they came to disagree -- a hash resolved from one publication paired with
330 * dimensions from the next -- is closed by the snapshot above. This stays because it answers
331 * a different question: whether the cacheline this hash names is big enough at all, which
332 * neither the snapshot nor the cache can tell. Keep the previous thumbnail and come back on
333 * the next expose rather than draw garbage. */
334 const size_t required_size = (size_t)stride * (size_t)ht;
335 if(wd <= 0 || ht <= 0 || dt_pixel_cache_entry_get_size(cache_entry) < required_size
336 || dt_pixel_cache_entry_get_data(cache_entry) != data)
337 {
339 cairo_destroy(cr);
340 cairo_surface_destroy(target);
341 return FALSE;
342 }
343
344 scale = fminf(width / (float)wd, height / (float)ht);
346
347 image_hash = published.hash;
348
349 cairo_t *cri = cairo_create(d->image_surface);
351 cairo_translate(cri, width / 2., height / 2.);
352 cairo_scale(cri, scale, scale);
353 cairo_translate(cri, -wd / 2., -ht / 2.);
358
360
361 imgid = dev->image_storage.id;
362 }
363 else
364 {
367 }
368
369 if(d->image_surface && imgid == dev->image_storage.id)
370 {
371 cairo_rectangle(cr, 0, 0, width, height);
373 cairo_set_source_surface(cr, d->image_surface, 0, 0);
374 cairo_fill(cr);
375 }
376
377 cairo_translate(cr, width / 2., height / 2.);
378 cairo_scale(cr, scale, scale);
379 cairo_translate(cr, -wd / 2., -ht / 2.);
380
381 // Calculate the thickness in user space (not affected by scale)
382 double line_width = DT_PIXEL_APPLY_DPI(1);
383 {
384 double dx = line_width, dy = 0;
385 cairo_device_to_user_distance(cr, &dx, &dy);
386 line_width = dx;
387 }
388
389 // draw box where we are
390 if(dt_dev_viewport_scaling(dev) > 1.f)
391 {
392 // Add a dark overlay on the picture to make it fade
393 cairo_rectangle(cr, 0, 0, wd, ht);
394 cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
395
396 // clip dimensions to navigation area
397 float boxw = 1, boxh = 1;
398 // Reads the box; the centre clamp it also performed is kept, because removing a
399 // mutation from a paint handler is a behaviour change that belongs with the setter work,
400 // not with the relocation.
405 const float roi_w = MIN(boxw * wd, wd);
406 const float roi_h = MIN(boxh * ht, ht);
407 const float roi_x = dt_dev_viewport_center_x(dev) * wd - roi_w * 0.5f;
408 const float roi_y = dt_dev_viewport_center_y(dev) * ht - roi_h * 0.5f;
409 cairo_rectangle(cr, roi_x - 1, roi_y - 1, roi_w + 2, roi_h + 2);
410
411 /* Use even–odd rule to punch the hole */
413 cairo_fill(cr);
414
416
417 // Paint the external border in black
418 cairo_set_source_rgb(cr, 0., 0., 0.);
420 cairo_stroke(cr);
421
422 // Paint the internal border in white
423 cairo_set_source_rgb(cr, 1., 1., 1.);
424 cairo_rectangle(cr, roi_x, roi_y, roi_w, roi_h);
425 cairo_stroke(cr);
426 }
427 else
428 {
429 // draw a simple border
430 cairo_set_source_rgb(cr, 1., 1., 1.);
432 cairo_rectangle(cr, 0.5, 0.5, wd - 1, ht - 1);
433 cairo_stroke(cr);
434 }
435
436 cairo_restore(cr);
437
438 /* Zoom % */
439 PangoLayout *layout;
440 PangoRectangle logic;
443 layout = pango_cairo_create_layout(cr);
444 const float fontsize = DT_PIXEL_APPLY_DPI(14);
447
448 // translate to left bottom corner
449 cairo_translate(cr, 0, height);
450 cairo_set_source_rgba(cr, 1., 1., 1., 0.5);
452
453 gchar *zoomline;
454 {
455 gchar *fit = NULL;
456 if(dt_dev_viewport_scaling(dev) == 1.f)
457 fit = g_strdup(_("Fit"));
458
460 if(fit)
461 {
462 dt_free(fit);
463 }
464 }
465
466 pango_layout_set_text(layout, zoomline, -1);
469 d->zoom_h = logic.height;
470 d->zoom_w = logic.width;
471 const double h = d->zoom_h;
472 const double w = d->zoom_w;
473 const int xp = width - w - h - logic.x;
474 const int yp = - logic.height;
475
476 cairo_move_to(cr, xp, yp);
477 cairo_save(cr);
479
480 GdkRGBA *color;
481 gtk_style_context_get(context, gtk_widget_get_state_flags(widget), "background-color", &color, NULL);
482
484 pango_cairo_layout_path(cr, layout);
486 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
487 cairo_fill(cr);
488
489 cairo_restore(cr);
490
493 g_object_unref(layout);
494
495 const float arrow_h = fontsize;
496
497 /* draw the drop-down arrow for zoom menu */
498 cairo_move_to(cr, width - 0.95 * arrow_h, -0.9 * arrow_h - 2);
499 cairo_line_to(cr, width - 0.05 * arrow_h, -0.9 * arrow_h - 2);
500 cairo_line_to(cr, width - 0.5 * arrow_h, -0.1 * arrow_h - 2);
501 cairo_fill(cr);
502
503
504 cairo_destroy(cr);
505 if(!IS_NULL_PTR(d->composed)) cairo_surface_destroy(d->composed);
506 d->composed = target;
507 d->composed_key = key;
508 d->composed_width = width;
509 d->composed_height = height;
510 return TRUE;
511}
512
513static gboolean _lib_navigation_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data)
514{
515 dt_times_t start;
516 dt_get_times(&start);
517
519 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
521
522 GtkAllocation allocation;
523 gtk_widget_get_allocation(widget, &allocation);
524 const int width = allocation.width;
525 const int height = allocation.height;
526
529
530 gboolean composed = FALSE;
531 if(IS_NULL_PTR(d->composed) || d->composed_key != key || d->composed_width != width
532 || d->composed_height != height)
533 composed = _lib_navigation_compose(widget, self, dev, width, height, has_preview_image, key);
534
535 if(!IS_NULL_PTR(d->composed))
536 {
537 cairo_set_source_surface(crf, d->composed, 0, 0);
539 }
540
541 /* The clip says who asked: the whole widget is a request of this module's own, a strip along
542 * an edge is the grip floating over it, hovered. */
544 {
545 double x1 = 0.0;
546 double y1 = 0.0;
547 double x2 = 0.0;
548 double y2 = 0.0;
549 cairo_clip_extents(crf, &x1, &y1, &x2, &y2);
550 dt_show_times_f(&start, "[navigation]", "redraw: %s, clip %.0fx%.0f at %.0f,%.0f of %dx%d",
551 composed ? "composed" : "cached", x2 - x1, y2 - y1, x1, y1, width, height);
552 }
553
554 return TRUE;
555}
556
557static void _lib_navigation_set_position(dt_lib_module_t *self, double x, double y, int alloc_wd, int alloc_ht)
558{
560 const dt_lib_navigation_t *d = (const dt_lib_navigation_t *)self->data;
561 if(!(dev && d->dragging && dt_dev_viewport_scaling(dev) > 1.f)) return;
562
563 // Compute size of navigation ROI in widget coordinates
564 int proc_wd, proc_ht;
566 const float nav_img_scale = fminf(alloc_wd / (float)proc_wd, alloc_ht / (float)proc_ht);
567 const int nav_img_w = (int)((float)proc_wd * nav_img_scale);
568 const int nav_img_h = (int)((float)proc_ht * nav_img_scale);
569
570 // Correct widget coordinates for margins
571 float fx = x - (alloc_wd - nav_img_w) * 0.5f;
572 float fy = y - (alloc_ht - nav_img_h) * 0.5f;
573
574 // Convert widget coordinates to relative coordinates within navigation ROI
575 // and commit relative coordinates of the ROI center
576 fx /= (float)nav_img_w;
577 fy /= (float)nav_img_h;
579
581
582 /* redraw myself */
584
585 /* redraw pipe */
587}
588
590 gpointer user_data)
591{
592 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
593 GtkAllocation allocation;
594 gtk_widget_get_allocation(widget, &allocation);
595 _lib_navigation_set_position(self, event->x, event->y, allocation.width, allocation.height);
596 return TRUE;
597}
598
600{
602 if(IS_NULL_PTR(dev)) return;
603
604 switch(zoom)
605 {
606 default:
608 break;
609 case LIB_ZOOM_SMALL:
611 break;
612 case LIB_ZOOM_FIT:
614 break;
615 case LIB_ZOOM_25:
617 break;
618 case LIB_ZOOM_33:
620 break;
621 case LIB_ZOOM_50:
623 break;
624 case LIB_ZOOM_100:
626 break;
627 case LIB_ZOOM_200:
629 break;
630 case LIB_ZOOM_400:
632 break;
633 case LIB_ZOOM_800:
635 break;
636 case LIB_ZOOM_1600:
638 break;
639 }
640
641 // Actual pixelpipe scaling is dt_dev_viewport_scaling(dev) * dt_dev_roi_request_natural_scale(dev),
642 // where dt_dev_roi_request_natural_scale(dev) ensures the image fits within the viewport.
644
647}
648
649static void _zoom_preset_callback(GtkButton *button, gpointer user_data)
650{
652}
653
655 gpointer user_data)
656{
657 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
659
660 GtkAllocation allocation;
661 gtk_widget_get_allocation(widget, &allocation);
662 int w = allocation.width;
663 int h = allocation.height;
664 if(event->x >= w - DT_NAVIGATION_INSET - d->zoom_h - d->zoom_w
665 && event->y >= h - DT_NAVIGATION_INSET - d->zoom_h)
666 {
667 // we show the zoom menu
669 GtkWidget *item;
670
671 item = gtk_menu_item_new_with_label(_("Small"));
673 gtk_menu_shell_append(menu, item);
674
675 item = gtk_menu_item_new_with_label(_("Fit to screen"));
677 gtk_menu_shell_append(menu, item);
678
679 item = gtk_menu_item_new_with_label(_("25%"));
681 gtk_menu_shell_append(menu, item);
682
683 item = gtk_menu_item_new_with_label(_("33%"));
685 gtk_menu_shell_append(menu, item);
686
687 item = gtk_menu_item_new_with_label(_("50%"));
689 gtk_menu_shell_append(menu, item);
690
691 item = gtk_menu_item_new_with_label(_("100%"));
693 gtk_menu_shell_append(menu, item);
694
695 item = gtk_menu_item_new_with_label(_("200%"));
697 gtk_menu_shell_append(menu, item);
698
699 item = gtk_menu_item_new_with_label(_("400%"));
701 gtk_menu_shell_append(menu, item);
702
703 item = gtk_menu_item_new_with_label(_("800%"));
705 gtk_menu_shell_append(menu, item);
706
707 item = gtk_menu_item_new_with_label(_("1600%"));
709 gtk_menu_shell_append(menu, item);
710
711 dt_gui_menu_popup(GTK_MENU(menu), NULL, 0, 0);
712
713 return TRUE;
714 }
715
716 d->dragging = 1;
717 _lib_navigation_set_position(self, event->x, event->y, w, h);
718 return TRUE;
719}
720
722 gpointer user_data)
723{
724 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
726 d->dragging = 0;
727
728 return TRUE;
729}
730
732 gpointer user_data)
733{
734 return TRUE;
735}
736
737// clang-format off
738// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
739// vim: shiftwidth=2 expandtab tabstop=2 cindent
740// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
741// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const float x
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
Definition control.c:1015
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:531
struct dt_lib_t * dt_lib_get_global(void)
Definition darktable.c:624
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
void dt_dev_pixelpipe_cache_wait_cleanup(dt_dev_pixelpipe_cache_wait_t *wait, const char *reason)
Cancel one pending GUI cache wait request and clear its runtime state.
gboolean dt_dev_pixelpipe_is_backbufer_valid(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_change_zoom_main(dt_develop_t *dev)
void dt_dev_pixelpipe_cache_wait_set_owner(dt_dev_pixelpipe_cache_wait_t *wait, const char *owner_tag, gpointer owner_object)
Attach debug ownership metadata to one cache wait request.
gboolean dt_dev_pixelpipe_cache_peek_gui(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, void **data, dt_pixel_cache_entry_t **cache_entry, dt_dev_pixelpipe_cache_wait_t *wait, dt_dev_pixelpipe_cache_ready_callback_t restart, gpointer restart_data)
int32_t dt_dev_roi_request_preview_height(const dt_develop_t *dev)
float dt_dev_roi_request_natural_scale(const dt_develop_t *dev)
int32_t dt_dev_roi_request_preview_width(const dt_develop_t *dev)
gboolean dt_dev_viewport_set_center(dt_develop_t *dev, const float center_x, const float center_y)
float dt_dev_viewport_center_y(const dt_develop_t *dev)
gboolean dt_dev_viewport_set_scaling(dt_develop_t *dev, const float scaling)
float dt_dev_viewport_center_x(const dt_develop_t *dev)
float dt_dev_viewport_scaling(const dt_develop_t *dev)
dt_dev_viewport_state_t dt_dev_viewport_get(const dt_develop_t *dev)
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:1176
gboolean dt_dev_clamp_viewport_center(dt_develop_t *dev)
Clamp the viewport centre against the box currently visible at this zoom.
Definition develop.c:1132
void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
Ensure that the current ROI position is within allowed bounds .
Definition develop.c:1141
GdkRGBA color[]
Definition geotagging.c:541
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition hash.h:55
#define UNKNOWN_IMAGE
Definition image.h:78
@ DT_DEBUG_PERF
Definition logging.h:55
int32_t dt_get_debug_flags(void)
Definition darktable.c:2094
#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 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
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
void gui_reset(dt_lib_module_t *self)
Definition navigation.c:238
static void _zoom_preset_callback(GtkButton *button, gpointer user_data)
Definition navigation.c:649
static void _lib_navigation_history_resync_callback(gpointer instance, gpointer user_data)
Definition navigation.c:165
static uint64_t _lib_navigation_picture_key(const dt_develop_t *dev, const int width, const int height, const gboolean has_preview_image)
Definition navigation.c:248
static gboolean _lib_navigation_compose(GtkWidget *widget, dt_lib_module_t *self, dt_develop_t *dev, const int width, const int height, const gboolean has_preview_image, const uint64_t key)
Definition navigation.c:271
static void _lib_navigation_restart_cache_wait(gpointer user_data)
Definition navigation.c:157
static void _lib_navigation_control_redraw_callback(gpointer instance, gpointer user_data)
Definition navigation.c:149
dt_lib_zoom_t
Definition navigation.c:89
@ LIB_ZOOM_100
Definition navigation.c:95
@ LIB_ZOOM_SMALL
Definition navigation.c:90
@ LIB_ZOOM_200
Definition navigation.c:96
@ LIB_ZOOM_33
Definition navigation.c:93
@ LIB_ZOOM_25
Definition navigation.c:92
@ LIB_ZOOM_400
Definition navigation.c:97
@ LIB_ZOOM_FIT
Definition navigation.c:91
@ LIB_ZOOM_50
Definition navigation.c:94
@ LIB_ZOOM_LAST
Definition navigation.c:100
@ LIB_ZOOM_1600
Definition navigation.c:99
@ LIB_ZOOM_800
Definition navigation.c:98
void gui_cleanup(dt_lib_module_t *self)
Definition navigation.c:220
static gboolean _lib_navigation_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition navigation.c:513
static gboolean _lib_navigation_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition navigation.c:731
static gboolean _lib_navigation_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition navigation.c:589
uint32_t container(dt_lib_module_t *self)
Definition navigation.c:133
static void _lib_navigation_set_position(struct dt_lib_module_t *self, double x, double y, int alloc_wd, int alloc_ht)
Definition navigation.c:557
static void _zoom_preset_change(dt_lib_zoom_t zoom)
Definition navigation.c:599
void gui_init(dt_lib_module_t *self)
Definition navigation.c:181
int position()
Definition navigation.c:143
const char ** views(dt_lib_module_t *self)
Definition navigation.c:127
int expandable(dt_lib_module_t *self)
Definition navigation.c:138
static gboolean _lib_navigation_button_press_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition navigation.c:654
#define DT_NAVIGATION_INSET
Definition navigation.c:66
static gboolean _lib_navigation_button_release_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition navigation.c:721
const char * name
Definition pdf.h:90
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
Peek the host pixels a cache entry holds for its current hash, without allocating.
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
size_t dt_pixel_cache_entry_get_size(const dt_pixel_cache_entry_t *entry)
Pixelpipe cache for storing intermediate results in the pixelpipe.
static dt_backbuf_state_t dt_dev_backbuf_snapshot(const dt_backbuf_t *backbuf)
Read the whole record as one publication.
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition popup.c:53
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:411
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
Definition signal.h:213
@ DT_SIGNAL_CONTROL_NAVIGATION_REDRAW
This signal is raised when dt_control_navigation_redraw() is called. no param, no returned value.
Definition signal.h:282
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:400
unsigned __int64 uint64_t
Definition strptime.c:75
One coherent publication, by value.
dt_backbuf_t backbuf
What the darkroom view asks the pipeline to show: the window onto the image.
dt_image_t image_storage
Definition develop.h:225
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:214
int32_t id
Definition image.h:410
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
cairo_surface_t * image_surface
Definition navigation.c:73
dt_dev_pixelpipe_cache_wait_t preview_wait
Definition navigation.c:85
GtkWidget * area
Definition navigation.c:70
uint64_t composed_key
Definition navigation.c:82
cairo_surface_t * composed
Definition navigation.c:81
struct dt_lib_t::@49 proxy
struct dt_lib_t::@49::@50 navigation
void * data
dt_atomic_uint64 age
One consumer's outstanding request, owned by that consumer, not by the queue.
#define MIN(a, b)
Definition thinplate.c:32
static void dt_get_times(dt_times_t *t)
Definition times.h:50
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...) __attribute__((format(printf
#define DT_PIXEL_APPLY_DPI(value)
@ DT_UI_CONTAINER_PANEL_LEFT_TOP