Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dev_backbuf.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Guillaume STUTIN.
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
22#include "views/dev_backbuf.h"
23#include "common/colorspaces_inline_conversions.h" // dt_Lab_to_XYZ(), dt_XYZ_to_sRGB()
24#include "common/conf.h"
25
26#include "widgets/bauhaus.h"
28#include "system/macros.h"
29#include "system/simd.h"
30#include "gui/application.h"
31#include "control/redraw.h"
32#include "develop/develop.h"
35#include "develop/supervisor.h"
36
37#include <pango/pangocairo.h>
38
39static void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
40{
41 dt_aligned_pixel_t Lab = { L, a, b, 1.f };
42 dt_aligned_pixel_t XYZ = { 0.f, 0.f, 0.f, 1.f };
45}
46
48{
49 // user param is Lab lightness/brightness (which is the same for greys)
50 if(dev->iso_12646.enabled)
51 _colormanage_ui_color(50., 0., 0., bg_color);
52 else
53 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color);
54}
55
56void dt_dev_draw_iso12646_border(cairo_t *cr, double width, double height, int border)
57{
58 /* the white frame around the picture: the ring only, the picture is painted over the middle
59 * anyway and the middle is most of the window */
60 cairo_save(cr);
61 cairo_rectangle(cr, -border * .5f, -border * .5f, width + border, height + border);
62 cairo_rectangle(cr, 1.0, 1.0, width - 2.0, height - 2.0);
64 cairo_set_source_rgb(cr, 1., 1., 1.);
65 cairo_fill(cr);
66 cairo_restore(cr);
67}
68
70{
71 /* One snapshot: reading mode twice could print a label for a mode that has already
72 * changed between the guard and the text. */
75 if(settings.mode == DT_PROFILE_NORMAL) return;
76
77 gchar *label = settings.mode == DT_PROFILE_GAMUTCHECK ? _("gamut check") : _("soft proof");
78 cairo_set_source_rgba(cri, 0.5, 0.5, 0.5, 0.5);
79 PangoLayout *layout;
80 PangoRectangle ink;
86 pango_layout_set_text(layout, label, -1);
88 cairo_move_to(cri, ink.height * 2, height - (ink.height * 3));
90 cairo_set_source_rgb(cri, 0.7, 0.7, 0.7);
93 cairo_set_source_rgb(cri, 0.3, 0.3, 0.3);
96 g_object_unref(layout);
97}
98
119{
120 if(locked->entry == entry) return;
122 locked->entry = entry;
124}
125
127{
128 if(IS_NULL_PTR(locked)) return;
129
130 if(locked->surface)
131 {
133 locked->surface = NULL;
134 }
135
136 _locked_bind_entry(locked, NULL);
137 locked->data = NULL;
139 locked->width = 0;
140 locked->height = 0;
141}
142
143static void _dev_backbuf_restart_cache_wait(gpointer user_data)
144{
145 dt_develop_t *dev = (dt_develop_t *)user_data;
146 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
148}
149
152 gboolean keep_previous_on_fail)
153{
154 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || IS_NULL_PTR(locked)) return FALSE;
155 if(!IS_NULL_PTR(wait))
157
158 /* ONE read of the publication. The hash names the cacheline this surface will be built on and
159 * the dimensions say what shape its pixels are; reading them separately lets a republication
160 * land in between and pairs a cacheline with the next frame's width -- which is a wrong cairo
161 * stride, i.e. diagonal striping. See dt_backbuf_t. */
163 const uint64_t hash = published.hash;
165
166 /* Fast-path reuse is only valid if the cacheline identity/data pointer are
167 * still the same behind the hash key. This avoids reusing stale cairo views
168 * when an entry is recreated or replaced under the same key. */
170 void *live_data = NULL;
171 if(!IS_NULL_PTR(locked->surface) && locked->hash == hash
174 && live_entry == locked->entry && live_data == locked->data
175 && locked->width == (int)published.width && locked->height == (int)published.height)
176 {
177 return TRUE;
178 }
179
180 /* Note what this fast path must NOT do: assign the published dimensions to `locked' and keep
181 * the existing surface. That surface was created with a stride baked in from the dimensions it
182 * was built at, so writing new ones over it describes the cairo surface falsely -- and it also
183 * defeats the slow path's own shape check below, which compares against exactly these fields.
184 * A publication that changes the shape must build a new surface, which is what falling through
185 * to the slow path does. */
186
187 struct dt_pixel_cache_entry_t *entry = NULL;
188 /* The claim on whatever this resolves to is taken by _locked_bind_entry() below, once the
189 * entry is known to be usable -- never here, where the lookup may still be refused. */
190 void *data = NULL;
192 data = NULL;
193 if(IS_NULL_PTR(data))
194 {
195 /* Keep previous frame only while waiting for a *different* target hash.
196 * If requested hash equals the currently locked one but cache lookup fails,
197 * the cached line was likely flushed/invalidated: drop stale lock so the
198 * line can be recreated and displayed again. */
199 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
201 return FALSE;
202 }
203
204 const int width = (int)published.width;
205 const int height = (int)published.height;
207 const size_t required_size = (size_t)stride * (size_t)height;
208 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
209 if(width <= 0 || height <= 0 || entry_size < required_size || dt_pixel_cache_entry_get_data(entry) != data)
210 {
211 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
213 return FALSE;
214 }
215
216 // The widget is about to (re)bind its surface to this backbuffer cacheline.
217 // Emitted off the fast-path, so it fires when the displayed buffer changes,
218 // not on every identical expose.
221
222 if(!IS_NULL_PTR(locked->surface) && locked->data == data && locked->width == width && locked->height == height)
223 {
224 /* Same pixels, so the cairo surface stands -- but the ENTRY behind them may be a different
225 * one (this path compares the data pointer, the fast path above compares both), and the
226 * claim belongs to whichever entry actually owns the memory now. */
227 locked->hash = hash;
228 _locked_bind_entry(locked, entry);
229 locked->data = data;
230 return TRUE;
231 }
232
234 if(IS_NULL_PTR(surface) || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
235 {
236 if(!IS_NULL_PTR(surface)) cairo_surface_destroy(surface);
237 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface)) return TRUE;
239 return FALSE;
240 }
241
243 locked->hash = hash;
244 locked->width = width;
245 locked->height = height;
246 locked->data = data;
247 _locked_bind_entry(locked, entry);
248 locked->surface = surface;
249 return TRUE;
250}
251
253 const int width, const int height, const int border,
254 const dt_aligned_pixel_t bg_color)
255{
256 if(IS_NULL_PTR(cr) || IS_NULL_PTR(dev) || IS_NULL_PTR(locked) || IS_NULL_PTR(locked->surface)) return FALSE;
257 if(IS_NULL_PTR(locked->entry) || locked->hash == DT_PIXELPIPE_CACHE_HASH_INVALID) return FALSE;
258
259 int wd = locked->width;
260 int ht = locked->height;
261 if(wd <= 0 || ht <= 0) return FALSE;
262
263 wd /= dt_gui_get_global()->ppd;
264 ht /= dt_gui_get_global()->ppd;
265 const double x0 = .5 * (width - wd);
266 const double y0 = .5 * (height - ht);
267
268 /* The background where the image will not cover: the four bands around it, as one even-odd
269 * fill. The hole is a pixel smaller than the image on every side so the blit overlaps it and
270 * no seam shows between the band and an antialiased image edge. A fill of the whole window
271 * under an image that covers most of it was a full pass for nothing: 5.9 ms of a frame at a
272 * 2560x1440 window on a 2x screen, against 1.3 ms for the bands. */
273 cairo_save(cr);
274 cairo_rectangle(cr, 0, 0, width, height);
275 cairo_rectangle(cr, x0 + 1.0, y0 + 1.0, wd - 2.0, ht - 2.0);
277 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
278 cairo_fill(cr);
279 cairo_restore(cr);
280
281 cairo_translate(cr, x0, y0);
282
283 if(dev->iso_12646.enabled) dt_dev_draw_iso12646_border(cr, wd, ht, border);
284
287 cairo_rectangle(cr, 0, 0, wd, ht);
288 cairo_set_source_surface(cr, locked->surface, 0, 0);
289 /* pixel for pixel on a matching target; say so, rather than leave the default filter to
290 * discover a mismatch the expensive way */
292 cairo_fill(cr);
294
295 return TRUE;
296}
297
299 const char *wait_owner_tag, cairo_t *cr, dt_develop_t *dev, int width,
300 int height, int border, const dt_aligned_pixel_t bg_color,
301 gboolean keep_previous_on_fail)
302{
303 if(IS_NULL_PTR(dev)) return FALSE;
305 return FALSE;
306 if(IS_NULL_PTR(locked->surface)) return FALSE;
307 return dt_dev_render_locked_surface(cr, dev, locked, width, height, border, bg_color);
308}
309
310// clang-format off
311// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
312// vim: shiftwidth=2 expandtab tabstop=2 cindent
313// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
314// clang-format on
#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.
void dt_colorprofiles_get_settings(dt_colorprofiles_settings_t *const out)
Copy the current settings into caller-provided storage, under one lock.
The colour-profile module's API: which profiles exist, and how to apply one.
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_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:970
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
static void _locked_bind_entry(dt_dev_locked_surface_t *locked, struct dt_pixel_cache_entry_t *entry)
Point this surface at a cacheline, holding the GUI's own claim on it.
void dt_dev_draw_iso12646_border(cairo_t *cr, double width, double height, int border)
Definition dev_backbuf.c:56
gboolean dt_dev_lock_pipe_surface(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_dev_locked_surface_t *locked, dt_dev_pixelpipe_cache_wait_t *wait, const char *wait_owner_tag, gboolean keep_previous_on_fail)
gboolean dt_dev_render_locked_surface(cairo_t *cr, const dt_develop_t *dev, dt_dev_locked_surface_t *locked, const int width, const int height, const int border, const dt_aligned_pixel_t bg_color)
static void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
Definition dev_backbuf.c:39
gboolean dt_dev_paint_main_backbuf(dt_dev_locked_surface_t *locked, dt_dev_pixelpipe_cache_wait_t *wait, const char *wait_owner_tag, cairo_t *cr, dt_develop_t *dev, int width, int height, int border, const dt_aligned_pixel_t bg_color, gboolean keep_previous_on_fail)
void dt_dev_get_background_color(const dt_develop_t *dev, dt_aligned_pixel_t bg_color)
Definition dev_backbuf.c:47
static void _dev_backbuf_restart_cache_wait(gpointer user_data)
void dt_dev_release_locked_surface(dt_dev_locked_surface_t *locked)
void dt_dev_draw_profile_mode_label(cairo_t *cri, int height)
Definition dev_backbuf.c:69
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)
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
hash
Definition layers.h:90
#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
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
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_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Take or release one reference on a cache line. A referenced line is never evicted,...
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.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
static dt_backbuf_state_t dt_dev_backbuf_snapshot(const dt_backbuf_t *backbuf)
Read the whole record as one publication.
@ DT_PROFILE_GAMUTCHECK
@ DT_PROFILE_NORMAL
Asking the interface to repaint itself.
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
unsigned __int64 uint64_t
Definition strptime.c:75
One coherent publication, by value.
Consistent snapshot of the display and soft-proofing settings.
dt_colorspaces_color_mode_t mode
NORMAL / SOFTPROOF / GAMUTCHECK.
struct dt_pixel_cache_entry_t * entry
Definition dev_backbuf.h:45
cairo_surface_t * surface
Definition dev_backbuf.h:46
dt_backbuf_t backbuf
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:167
gboolean enabled
Definition develop.h:370
struct dt_develop_t::@18 iso_12646
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:214
void * data
dt_atomic_uint64 age
One consumer's outstanding request, owned by that consumer, not by the queue.
void dt_supervisor_widget(const dt_sv_op_t op, const char *widget_tag, const uint64_t consumed_hash, const int pipe_type, const int32_t imgid)
@ DT_SV_READ
Definition supervisor.h:80
static gboolean dt_supervisor_active(void)
Definition supervisor.h:95
#define DT_PIXEL_APPLY_DPI(value)