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/logging.h"
19#include "colorprofiles/colorspaces.h" // dt_colorprofiles_xyz_to_display()
20#include "common/colorspaces_inline_conversions.h" // dt_Lab_to_XYZ
21#include "common/conf.h"
22#include "system/macros.h"
23#include "system/mem_alloc.h"
24#include "system/simd.h"
25#include "common/times.h"
26#include "control/signal.h"
27#include "gui/application.h"
28#include "control/control.h"
29#include "caches/image_cache.h"
30#include "views/view.h"
31
32#include <gtk/gtk.h>
34
35#ifdef GDK_WINDOWING_QUARTZ
36#include "osx/osx.h"
37#endif
38
48
49void _preview_redraw(gpointer instance, dt_preview_window_t *preview);
50
51static void _preview_window_destroy(GtkWidget *dialog, gpointer user_data)
52{
54 // Disconnect from DARKROOM_UI_CHANGED before freeing: otherwise the handler keeps
55 // firing with a dangling `preview` pointer and crashes on the next UI change
56 // (e.g. moving the border-size slider).
60}
61
62static void _close_preview_popup(GtkWidget *dialog, gint response_id, gpointer data)
63{
64 gtk_widget_destroy(dialog);
65}
66
67static void _preview_window_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
68{
70 if(IS_NULL_PTR(preview) || IS_NULL_PTR(allocation)) return;
71 if(allocation->width < 2 || allocation->height < 2) return;
72 if(preview->width == allocation->width && preview->height == allocation->height) return;
73
74 preview->width = allocation->width;
75 preview->height = allocation->height;
76
77 /* The async fetcher can stop its current pixelpipe through the request-owned
78 * shutdown flag. Trigger that as soon as the popup size changes so we don't
79 * keep rendering a surface for an obsolete allocation. */
81 gtk_widget_queue_draw(widget);
82}
83
84// TODO: we color-manage the preview window assuming it sits on the same screen as the main window
85// Will need to grab the color profile of the actual monitor where it sits.
86void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
87{
88 dt_aligned_pixel_t Lab = { L, a, b, 1.f };
89 dt_aligned_pixel_t XYZ = { 0.f, 0.f, 0.f, 1.f };
92}
93
94static gboolean
95_thumb_draw_image(GtkWidget *widget, cairo_t *cr, gpointer user_data)
96{
98 if(IS_NULL_PTR(preview)) return TRUE;
99
100 const double start = dt_get_wtime();
101
102 int w = gtk_widget_get_allocated_width(widget);
103 int h = gtk_widget_get_allocated_height(widget);
104
105 // Paint background
106 dt_aligned_pixel_t bg_color;
107 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color);
108 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
109 cairo_paint(cr);
110
111 int borders = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size"));
112 w -= 2 * borders;
113 h -= 2 * borders;
114
115 // FIXME: this gets the color-managed image surface using the color profile of the monitor
116 // where the main window sits. Need to detect the monitor of the preview window instead.
118 dt_view_image_get_surface_async(&preview->fetcher, preview->imgid, w, h, &preview->surface, widget, 0);
119
120 if(preview->surface && res == DT_VIEW_SURFACE_OK)
121 {
122 // The image is immediately available
123 int width = cairo_image_surface_get_width(preview->surface);
124 int height = cairo_image_surface_get_height(preview->surface);
125 double sx = 1.0, sy = 1.0;
126 cairo_surface_get_device_scale(preview->surface, &sx, &sy);
127 const double logical_width = width / sx;
128 const double logical_height = height / sy;
129
130 // we draw the image
131 cairo_save(cr);
132 double x_offset = (w - logical_width) / 2. + borders;
133 double y_offset = (h - logical_height) / 2. + borders;
134 cairo_set_source_surface(cr, preview->surface, x_offset, y_offset);
135
136 // get the transparency value
137 GdkRGBA im_color;
138 GtkStyleContext *context = gtk_widget_get_style_context(widget);
139 gtk_style_context_get_color(context, gtk_widget_get_state_flags(widget), &im_color);
140 cairo_paint_with_alpha(cr, im_color.alpha);
141
142 // and eventually the image border
143 gtk_render_frame(context, cr, borders, borders, w, h);
144 cairo_restore(cr);
145 }
146 else
147 {
148 dt_control_draw_busy_msg(cr, w, h);
149 }
150
151 dt_print(DT_DEBUG_LIGHTTABLE, "Redrawing the preview window for %i in %0.04f sec\n", preview->imgid,
152 dt_get_wtime() - start);
153
154 return TRUE;
155}
156
157
159{
160 gtk_widget_queue_draw(preview->area);
161}
162
163
164void dt_preview_window_spawn(const int32_t imgid)
165{
166 dt_preview_window_t *preview = calloc(1, sizeof(dt_preview_window_t));
167 preview->imgid = imgid;
169
170 GtkWidget *dialog = gtk_dialog_new();
171
172 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
173 gchar *name = g_strdup_printf(_("Ansel - Preview : %s"), img->filename);
175 gtk_window_set_title(GTK_WINDOW(dialog), name);
176 dt_free(name);
177
178#ifdef GDK_WINDOWING_QUARTZ
180 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
181#endif
182
183 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
184 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
185 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_gui_main_window()));
186 gtk_window_set_default_size(GTK_WINDOW(dialog), 350, 350);
187 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_close_preview_popup), NULL);
188 g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(_preview_window_destroy), preview);
189
190 preview->area = gtk_drawing_area_new();
191 gtk_widget_set_hexpand(preview->area, TRUE);
192 gtk_widget_set_vexpand(preview->area, TRUE);
193 gtk_widget_set_halign(preview->area, GTK_ALIGN_FILL);
194 gtk_widget_set_valign(preview->area, GTK_ALIGN_FILL);
195 gtk_widget_set_size_request(preview->area, 350, 350);
196 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), preview->area, TRUE, TRUE, 0);
197 g_signal_connect(G_OBJECT(preview->area), "draw", G_CALLBACK(_thumb_draw_image), preview);
198 g_signal_connect(G_OBJECT(preview->area), "size-allocate", G_CALLBACK(_preview_window_size_allocate), preview);
200
201 gtk_widget_set_visible(preview->area, TRUE);
202 gtk_widget_show_all(dialog);
203}
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_colorprofiles_xyz_to_display(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t RGB)
Convert one D50 XYZ pixel to display RGB.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
dt_Lab_to_XYZ(Lab, XYZ)
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
static dt_aligned_pixel_t RGB
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_control_draw_busy_msg(cairo_t *cr, int width, int height)
Definition control.c:530
GtkWidget * preview
what the selected row actually captures
const int res
Definition dtpthread.h:351
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_DEBUG_LIGHTTABLE
Definition logging.h:60
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#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
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
const char * name
Definition pdf.h:90
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:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DARKROOM_UI_CHANGED
Signal that the darkroom GUI color changed.
Definition signal.h:227
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
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:119
static double dt_get_wtime(void)
Definition times.h:43
void dt_view_image_surface_fetcher_invalidate(dt_view_image_surface_fetcher_t *fetcher, cairo_surface_t **target)
Definition view.c:871
void dt_view_image_surface_fetcher_cleanup(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:847
void dt_view_image_surface_fetcher_init(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:836
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:892
dt_view_surface_value_t
Definition view.h:104
@ DT_VIEW_SURFACE_OK
Definition view.h:105
#define DT_PIXEL_APPLY_DPI(value)