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
24#include "bauhaus/bauhaus.h"
25#include "common/colorspaces.h"
27#include "common/darktable.h"
28#include "control/control.h"
29#include "develop/develop.h"
32#include "develop/supervisor.h"
33
34#include <pango/pangocairo.h>
35
36static void _colormanage_ui_color(const float L, const float a, const float b, dt_aligned_pixel_t RGB)
37{
38 dt_aligned_pixel_t Lab = { L, a, b, 1.f };
39 dt_aligned_pixel_t XYZ = { 0.f, 0.f, 0.f, 1.f };
42}
43
45{
46 // user param is Lab lightness/brightness (which is the same for greys)
47 if(dev->iso_12646.enabled)
48 _colormanage_ui_color(50., 0., 0., bg_color);
49 else
50 _colormanage_ui_color((float)dt_conf_get_int("display/brightness"), 0., 0., bg_color);
51}
52
53void dt_dev_draw_iso12646_border(cairo_t *cr, double width, double height, int border)
54{
55 // draw the white frame around picture
56 cairo_rectangle(cr, -border * .5f, -border * .5f, width + border, height + border);
57 cairo_set_source_rgb(cr, 1., 1., 1.);
58 cairo_fill(cr);
59}
60
62{
64
65 gchar *label = darktable.color_profiles->mode == DT_PROFILE_GAMUTCHECK ? _("gamut check") : _("soft proof");
66 cairo_set_source_rgba(cri, 0.5, 0.5, 0.5, 0.5);
67 PangoLayout *layout;
68 PangoRectangle ink;
69 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
70 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
71 layout = pango_cairo_create_layout(cri);
72 pango_font_description_set_absolute_size(desc, DT_PIXEL_APPLY_DPI(20) * PANGO_SCALE);
73 pango_layout_set_font_description(layout, desc);
74 pango_layout_set_text(layout, label, -1);
75 pango_layout_get_pixel_extents(layout, &ink, NULL);
76 cairo_move_to(cri, ink.height * 2, height - (ink.height * 3));
77 pango_cairo_layout_path(cri, layout);
78 cairo_set_source_rgb(cri, 0.7, 0.7, 0.7);
79 cairo_fill_preserve(cri);
80 cairo_set_line_width(cri, 0.7);
81 cairo_set_source_rgb(cri, 0.3, 0.3, 0.3);
82 cairo_stroke(cri);
83 pango_font_description_free(desc);
84 g_object_unref(layout);
85}
86
88{
89 if(IS_NULL_PTR(locked)) return;
90
91 if(locked->surface)
92 {
93 cairo_surface_destroy(locked->surface);
94 locked->surface = NULL;
95 }
96
97 /* These cairo views only mirror whatever cacheline the pipeline currently exposes as backbuffer.
98 * They never own the backbuffer keepalive ref themselves: `pixelpipe_hb.c` swaps that ownership
99 * when publishing a new backbuffer. Releasing the surface must therefore only drop the GUI view. */
100 locked->entry = NULL;
101 locked->data = NULL;
103 locked->width = 0;
104 locked->height = 0;
105}
106
107static void _dev_backbuf_restart_cache_wait(gpointer user_data)
108{
109 dt_develop_t *dev = (dt_develop_t *)user_data;
110 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
112}
113
115 dt_dev_pixelpipe_cache_wait_t *wait, const char *wait_owner_tag,
116 gboolean keep_previous_on_fail)
117{
118 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || IS_NULL_PTR(locked)) return FALSE;
119 if(!IS_NULL_PTR(wait))
120 dt_dev_pixelpipe_cache_wait_set_owner(wait, wait_owner_tag, dev);
121
123 if(hash == DT_PIXELPIPE_CACHE_HASH_INVALID) return keep_previous_on_fail && (!IS_NULL_PTR(locked->surface));
124
125 /* Fast-path reuse is only valid if the cacheline identity/data pointer are
126 * still the same behind the hash key. This avoids reusing stale cairo views
127 * when an entry is recreated or replaced under the same key. */
128 dt_pixel_cache_entry_t *live_entry = NULL;
129 void *live_data = NULL;
130 if(!IS_NULL_PTR(locked->surface) && locked->hash == hash
131 && dt_dev_pixelpipe_cache_peek_gui(pipe, NULL, &live_data, &live_entry, wait,
133 && live_entry == locked->entry && live_data == locked->data)
134 {
135 locked->width = pipe->backbuf.width;
136 locked->height = pipe->backbuf.height;
137 return TRUE;
138 }
139
140 struct dt_pixel_cache_entry_t *entry = NULL;
141 /* GUI surfaces only borrow the currently published backbuffer. They rely on the backbuffer keepalive ref
142 * owned by `pixelpipe_hb.c`, so they must not take or drop their own cache refs here. */
143 void *data = NULL;
144 if(!dt_dev_pixelpipe_cache_peek_gui(pipe, NULL, &data, &entry, wait, _dev_backbuf_restart_cache_wait, dev))
145 data = NULL;
146 if(IS_NULL_PTR(data))
147 {
148 /* Keep previous frame only while waiting for a *different* target hash.
149 * If requested hash equals the currently locked one but cache lookup fails,
150 * the cached line was likely flushed/invalidated: drop stale lock so the
151 * line can be recreated and displayed again. */
152 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
154 return FALSE;
155 }
156
157 const int width = pipe->backbuf.width;
158 const int height = pipe->backbuf.height;
159 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
160 const size_t required_size = (size_t)stride * (size_t)height;
161 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
162 if(width <= 0 || height <= 0 || entry_size < required_size || dt_pixel_cache_entry_get_data(entry) != data)
163 {
164 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface) && locked->hash != hash) return TRUE;
166 return FALSE;
167 }
168
169 // The widget is about to (re)bind its surface to this backbuffer cacheline.
170 // Emitted off the fast-path, so it fires when the displayed buffer changes,
171 // not on every identical expose.
173 dt_supervisor_widget(DT_SV_READ, wait_owner_tag, hash, pipe->type, pipe->imgid);
174
175 if(!IS_NULL_PTR(locked->surface) && locked->data == data && locked->width == width && locked->height == height)
176 {
177 locked->hash = hash;
178 locked->entry = entry;
179 locked->data = data;
180 return TRUE;
181 }
182
183 cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_RGB24, width, height, stride);
184 if(IS_NULL_PTR(surface) || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
185 {
186 if(!IS_NULL_PTR(surface)) cairo_surface_destroy(surface);
187 if(keep_previous_on_fail && !IS_NULL_PTR(locked->surface)) return TRUE;
189 return FALSE;
190 }
191
193 locked->hash = hash;
194 locked->width = width;
195 locked->height = height;
196 locked->data = data;
197 locked->entry = entry;
198 locked->surface = surface;
199 return TRUE;
200}
201
202gboolean dt_dev_render_locked_surface(cairo_t *cr, const dt_develop_t *dev, dt_dev_locked_surface_t *locked,
203 const int width, const int height, const int border,
204 const dt_aligned_pixel_t bg_color)
205{
206 if(IS_NULL_PTR(cr) || IS_NULL_PTR(dev) || IS_NULL_PTR(locked) || IS_NULL_PTR(locked->surface)) return FALSE;
207 if(IS_NULL_PTR(locked->entry) || locked->hash == DT_PIXELPIPE_CACHE_HASH_INVALID) return FALSE;
208
209 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
210 cairo_paint(cr);
211
212 int wd = locked->width;
213 int ht = locked->height;
214 if(wd <= 0 || ht <= 0) return FALSE;
215
216 wd /= darktable.gui->ppd;
217 ht /= darktable.gui->ppd;
218 cairo_translate(cr, .5f * (width - wd), .5f * (height - ht));
219
220 if(dev->iso_12646.enabled) dt_dev_draw_iso12646_border(cr, wd, ht, border);
221
223 cairo_surface_set_device_scale(locked->surface, darktable.gui->ppd, darktable.gui->ppd);
224 cairo_rectangle(cr, 0, 0, wd, ht);
225 cairo_set_source_surface(cr, locked->surface, 0, 0);
226 cairo_fill(cr);
228
229 return TRUE;
230}
231
233 const char *wait_owner_tag, cairo_t *cr, dt_develop_t *dev, int width,
234 int height, int border, const dt_aligned_pixel_t bg_color,
235 gboolean keep_previous_on_fail)
236{
237 if(IS_NULL_PTR(dev)) return FALSE;
238 if(!dt_dev_lock_pipe_surface(dev, dev->pipe, locked, wait, wait_owner_tag, keep_previous_on_fail))
239 return FALSE;
240 if(IS_NULL_PTR(locked->surface)) return FALSE;
241 return dt_dev_render_locked_surface(cr, dev, locked, width, height, border, bg_color);
242}
243
244// clang-format off
245// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
246// vim: shiftwidth=2 expandtab tabstop=2 cindent
247// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
248// clang-format on
#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_PROFILE_GAMUTCHECK
@ DT_PROFILE_NORMAL
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)
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:877
darktable_t darktable
Definition darktable.c:183
#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:293
void dt_dev_draw_iso12646_border(cairo_t *cr, double width, double height, int border)
Definition dev_backbuf.c:53
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:36
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:44
static void _dev_backbuf_restart_cache_wait(gpointer user_data)
void dt_dev_release_locked_surface(dt_dev_locked_surface_t *locked)
Definition dev_backbuf.c:87
void dt_dev_draw_profile_mode_label(cairo_t *cri, int height)
Definition dev_backbuf.c:61
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)
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
float dt_aligned_pixel_t[4]
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache entry.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:808
struct dt_gui_gtk_t * gui
Definition darktable.h:793
struct dt_colorspaces_t * color_profiles
Definition darktable.h:806
struct dt_bauhaus_t * bauhaus
Definition darktable.h:796
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
cmsHTRANSFORM transform_xyz_to_display
dt_colorspaces_color_mode_t mode
struct dt_pixel_cache_entry_t * entry
Definition dev_backbuf.h:43
cairo_surface_t * surface
Definition dev_backbuf.h:44
dt_backbuf_t backbuf
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:162
struct dt_develop_t::@26 iso_12646
gboolean enabled
Definition develop.h:393
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
double ppd
Definition gtk.h:205
uint64_t hash
void * data
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:79
static gboolean dt_supervisor_active(void)
Definition supervisor.h:94