Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
preview_window.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2025 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "common/darktable.h"
19#include "control/control.h"
20#include "common/image_cache.h"
21#include "views/view.h"
22
23#include <gtk/gtk.h>
24
25#ifdef GDK_WINDOWING_QUARTZ
26#include "osx/osx.h"
27#endif
28
38
39void _preview_redraw(gpointer instance, dt_preview_window_t *preview);
40
41static void _preview_window_destroy(GtkWidget *dialog, gpointer user_data)
42{
43 dt_preview_window_t *preview = (dt_preview_window_t *)user_data;
44 // Disconnect from DARKROOM_UI_CHANGED before freeing: otherwise the handler keeps
45 // firing with a dangling `preview` pointer and crashes on the next UI change
46 // (e.g. moving the border-size slider).
49 dt_free(preview);
50}
51
52static void _close_preview_popup(GtkWidget *dialog, gint response_id, gpointer data)
53{
54 gtk_widget_destroy(dialog);
55}
56
57static void _preview_window_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
58{
59 dt_preview_window_t *preview = (dt_preview_window_t *)user_data;
60 if(IS_NULL_PTR(preview) || IS_NULL_PTR(allocation)) return;
61 if(allocation->width < 2 || allocation->height < 2) return;
62 if(preview->width == allocation->width && preview->height == allocation->height) return;
63
64 preview->width = allocation->width;
65 preview->height = allocation->height;
66
67 /* The async fetcher can stop its current pixelpipe through the request-owned
68 * shutdown flag. Trigger that as soon as the popup size changes so we don't
69 * keep rendering a surface for an obsolete allocation. */
71 gtk_widget_queue_draw(widget);
72}
73
74// TODO: we color-manage the preview window assuming it sits on the same screen as the main window
75// Will need to grab the color profile of the actual monitor where it sits.
76void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
77{
78 dt_aligned_pixel_t Lab = { L, a, b, 1.f };
79 dt_aligned_pixel_t XYZ = { 0.f, 0.f, 0.f, 1.f };
82}
83
84static gboolean
85_thumb_draw_image(GtkWidget *widget, cairo_t *cr, gpointer user_data)
86{
87 dt_preview_window_t *preview = (dt_preview_window_t *)user_data;
88 if(IS_NULL_PTR(preview)) return TRUE;
89
90 const double start = dt_get_wtime();
91
92 int w = gtk_widget_get_allocated_width(widget);
93 int h = gtk_widget_get_allocated_height(widget);
94
95 // Paint background
96 dt_aligned_pixel_t bg_color;
97 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color);
98 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
99 cairo_paint(cr);
100
101 int borders = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size"));
102 w -= 2 * borders;
103 h -= 2 * borders;
104
105 // FIXME: this gets the color-managed image surface using the color profile of the monitor
106 // where the main window sits. Need to detect the monitor of the preview window instead.
107 const dt_view_surface_value_t res =
108 dt_view_image_get_surface_async(&preview->fetcher, preview->imgid, w, h, &preview->surface, widget, 0);
109
110 if(preview->surface && res == DT_VIEW_SURFACE_OK)
111 {
112 // The image is immediately available
113 int width = cairo_image_surface_get_width(preview->surface);
114 int height = cairo_image_surface_get_height(preview->surface);
115 double sx = 1.0, sy = 1.0;
116 cairo_surface_get_device_scale(preview->surface, &sx, &sy);
117 const double logical_width = width / sx;
118 const double logical_height = height / sy;
119
120 // we draw the image
121 cairo_save(cr);
122 double x_offset = (w - logical_width) / 2. + borders;
123 double y_offset = (h - logical_height) / 2. + borders;
124 cairo_set_source_surface(cr, preview->surface, x_offset, y_offset);
125
126 // get the transparency value
127 GdkRGBA im_color;
128 GtkStyleContext *context = gtk_widget_get_style_context(widget);
129 gtk_style_context_get_color(context, gtk_widget_get_state_flags(widget), &im_color);
130 cairo_paint_with_alpha(cr, im_color.alpha);
131
132 // and eventually the image border
133 gtk_render_frame(context, cr, borders, borders, w, h);
134 cairo_restore(cr);
135 }
136 else
137 {
138 dt_control_draw_busy_msg(cr, w, h);
139 }
140
141 dt_print(DT_DEBUG_LIGHTTABLE, "Redrawing the preview window for %i in %0.04f sec\n", preview->imgid,
142 dt_get_wtime() - start);
143
144 return TRUE;
145}
146
147
148void _preview_redraw(gpointer instance, dt_preview_window_t *preview)
149{
150 gtk_widget_queue_draw(preview->area);
151}
152
153
154void dt_preview_window_spawn(const int32_t imgid)
155{
156 dt_preview_window_t *preview = calloc(1, sizeof(dt_preview_window_t));
157 preview->imgid = imgid;
159
160 GtkWidget *dialog = gtk_dialog_new();
161
162 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
163 gchar *name = g_strdup_printf(_("Ansel - Preview : %s"), img->filename);
165 gtk_window_set_title(GTK_WINDOW(dialog), name);
166 dt_free(name);
167
168#ifdef GDK_WINDOWING_QUARTZ
170 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
171#endif
172
173 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
174 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
175 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));
176 gtk_window_set_default_size(GTK_WINDOW(dialog), 350, 350);
177 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_close_preview_popup), NULL);
178 g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(_preview_window_destroy), preview);
179
180 preview->area = gtk_drawing_area_new();
181 gtk_widget_set_hexpand(preview->area, TRUE);
182 gtk_widget_set_vexpand(preview->area, TRUE);
183 gtk_widget_set_halign(preview->area, GTK_ALIGN_FILL);
184 gtk_widget_set_valign(preview->area, GTK_ALIGN_FILL);
185 gtk_widget_set_size_request(preview->area, 350, 350);
186 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), preview->area, TRUE, TRUE, 0);
187 g_signal_connect(G_OBJECT(preview->area), "draw", G_CALLBACK(_thumb_draw_image), preview);
188 g_signal_connect(G_OBJECT(preview->area), "size-allocate", G_CALLBACK(_preview_window_size_allocate), preview);
190
191 gtk_widget_set_visible(preview->area, TRUE);
192 gtk_widget_show_all(dialog);
193}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
dt_Lab_to_XYZ(Lab, XYZ)
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
static dt_aligned_pixel_t RGB
char * name
int dt_conf_get_int(const char *name)
void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
Definition control.c:505
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_LIGHTTABLE
Definition darktable.h:725
#define dt_free(ptr)
Definition darktable.h:456
static double dt_get_wtime(void)
Definition darktable.h:914
#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
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
float dt_aligned_pixel_t[4]
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
static void _close_preview_popup(GtkWidget *dialog, gint response_id, gpointer data)
static void _preview_window_destroy(GtkWidget *dialog, gpointer user_data)
void _preview_redraw(gpointer instance, dt_preview_window_t *preview)
static gboolean _thumb_draw_image(GtkWidget *widget, cairo_t *cr, gpointer user_data)
static void _preview_window_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
void dt_preview_window_spawn(const int32_t imgid)
void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:368
@ DT_SIGNAL_DARKROOM_UI_CHANGED
Signal that the darkroom GUI color changed.
Definition signal.h:224
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_colorspaces_t * color_profiles
Definition darktable.h:788
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_image_cache_t * image_cache
Definition darktable.h:777
cmsHTRANSFORM transform_xyz_to_display
dt_ui_t * ui
Definition gtk.h:164
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
cairo_surface_t * surface
dt_view_image_surface_fetcher_t fetcher
Track one asynchronous Cairo surface fetch request for a GUI widget.
Definition view.h:117
void dt_view_image_surface_fetcher_invalidate(dt_view_image_surface_fetcher_t *fetcher, cairo_surface_t **target)
Definition view.c:866
void dt_view_image_surface_fetcher_cleanup(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:842
void dt_view_image_surface_fetcher_init(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:831
dt_view_surface_value_t dt_view_image_get_surface_async(dt_view_image_surface_fetcher_t *fetcher, int32_t imgid, int width, int height, cairo_surface_t **target, GtkWidget *widget, int zoom)
Definition view.c:881
dt_view_surface_value_t
Definition view.h:102
@ DT_VIEW_SURFACE_OK
Definition view.h:103