Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dev_viewport.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel.
3 Copyright (C) 2026 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
21#include "develop/develop.h"
22
23#include <string.h>
24
25/* The publication protocol is the geometry record's, for the same reason: the GUI thread is
26 * the only writer, while the darkroom worker and drawlayer's paint thread read. A mutator
27 * builds the whole next state in a local, compares it, and publishes it in one bracketed
28 * store -- so "half the old pan and half the new" is not a state a reader can observe.
29 */
30
31static inline void _publish(dt_dev_viewport_t *viewport, const dt_dev_viewport_state_t *next)
32{
34 viewport->state = *next;
36}
37
39{
40 // Not a zeroed struct: these are the values dt_dev_reset_roi() left on every dev, including
41 // headless ones, before the viewport was an object. A dev with no viewport must keep reading
42 // them, or every consumer of the product (scaling * natural_scale) changes answer.
44 memset(&state, 0, sizeof(state));
45 state.scaling = 1.f;
46 state.center_x = 0.5f;
47 state.center_y = 0.5f;
48 return state;
49}
50
52{
54 if(IS_NULL_PTR(viewport)) return NULL;
55
56 viewport->state = dt_dev_viewport_neutral();
57 dt_atomic_set_uint64(&viewport->generation, 0);
58 return viewport;
59}
60
62{
63 dt_free(viewport);
64}
65
67{
68 return !IS_NULL_PTR(dev) && !IS_NULL_PTR(dev->viewport);
69}
70
72{
74
75 const dt_dev_viewport_t *viewport = dev->viewport;
77
78 // Seqlock read: copy, then check nothing was published while we copied.
79 for(;;)
80 {
81 const uint64_t before = dt_atomic_get_uint64(&viewport->generation);
82 if(before & 1) continue;
83
84 state = viewport->state;
85
86 const uint64_t after = dt_atomic_get_uint64(&viewport->generation);
87 if(before == after) break;
88 }
89
90 return state;
91}
92
102
103gboolean dt_dev_viewport_set_widget_size(dt_develop_t *dev, const int32_t widget_width,
104 const int32_t widget_height)
105{
106 if(!dt_dev_viewport_exists(dev)) return FALSE;
107
109 if(next.widget_width == widget_width && next.widget_height == widget_height) return FALSE;
110
111 next.widget_width = widget_width;
112 next.widget_height = widget_height;
113 _publish(dev->viewport, &next);
114 // The ROI request is derived from this state; republish it so the two cannot disagree.
116 return TRUE;
117}
118
119gboolean dt_dev_viewport_set_border(dt_develop_t *dev, const int32_t border_size)
120{
121 if(!dt_dev_viewport_exists(dev)) return FALSE;
122
124 if(next.border_size == border_size) return FALSE;
125
126 next.border_size = border_size;
127 _publish(dev->viewport, &next);
128 // The ROI request is derived from this state; republish it so the two cannot disagree.
130 return TRUE;
131}
132
133gboolean dt_dev_viewport_set_box(dt_develop_t *dev, const int32_t box_width, const int32_t box_height)
134{
135 if(!dt_dev_viewport_exists(dev)) return FALSE;
136
138 if(next.box_width == box_width && next.box_height == box_height && next.configured) return FALSE;
139
140 next.box_width = box_width;
141 next.box_height = box_height;
142 next.configured = TRUE;
143 _publish(dev->viewport, &next);
144 // The ROI request is derived from this state; republish it so the two cannot disagree.
146 return TRUE;
147}
148
149gboolean dt_dev_viewport_set_center(dt_develop_t *dev, const float center_x, const float center_y)
150{
151 if(!dt_dev_viewport_exists(dev)) return FALSE;
152
154 if(next.center_x == center_x && next.center_y == center_y) return FALSE;
155
156 // The pair is published together, which is the whole point: the two coordinates were eight
157 // independent stores before, and the worker read them as two independent loads.
158 next.center_x = center_x;
159 next.center_y = center_y;
160 _publish(dev->viewport, &next);
161 // The ROI request is derived from this state; republish it so the two cannot disagree.
163 return TRUE;
164}
165
167{
168 if(!dt_dev_viewport_exists(dev)) return FALSE;
169
171 if(next.scaling == scaling) return FALSE;
172
173 next.scaling = scaling;
174 _publish(dev->viewport, &next);
175 // The ROI request is derived from this state; republish it so the two cannot disagree.
177 return TRUE;
178}
179
181{
182 if(!dt_dev_viewport_exists(dev)) return;
183
186 next.scaling = neutral.scaling;
187 next.center_x = neutral.center_x;
188 next.center_y = neutral.center_y;
189 _publish(dev->viewport, &next);
191}
192
193// clang-format off
194// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
195// vim: shiftwidth=2 expandtab tabstop=2 cindent
196// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
197// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
uint64_t dt_atomic_get_uint64(const dt_atomic_uint64 *var)
void dt_atomic_set_uint64(dt_atomic_uint64 *var, uint64_t value)
static const char neutral[]
Definition basecurve.c:252
static const float scaling
uint64_t dt_dev_roi_request_publish(dt_develop_t *dev)
Recompute the derived members from the viewport and the geometry record, and publish if anything chan...
gboolean dt_dev_viewport_set_border(dt_develop_t *dev, const int32_t border_size)
gboolean dt_dev_viewport_exists(const dt_develop_t *dev)
void dt_dev_viewport_free(dt_dev_viewport_t *viewport)
gboolean dt_dev_viewport_set_box(dt_develop_t *dev, const int32_t box_width, const int32_t box_height)
gboolean dt_dev_viewport_set_center(dt_develop_t *dev, const float center_x, const float center_y)
void dt_dev_viewport_reset(dt_develop_t *dev)
gboolean dt_dev_viewport_set_widget_size(dt_develop_t *dev, const int32_t widget_width, const int32_t widget_height)
float dt_dev_viewport_center_y(const dt_develop_t *dev)
gboolean dt_dev_viewport_set_scaling(dt_develop_t *dev, const float scaling)
static void _publish(dt_dev_viewport_t *viewport, const dt_dev_viewport_state_t *next)
dt_dev_viewport_state_t dt_dev_viewport_neutral(void)
float dt_dev_viewport_center_x(const dt_develop_t *dev)
int32_t dt_dev_viewport_border_size(const dt_develop_t *dev)
gboolean dt_dev_viewport_configured(const dt_develop_t *dev)
int32_t dt_dev_viewport_box_height(const dt_develop_t *dev)
dt_dev_viewport_t * dt_dev_viewport_new(void)
float dt_dev_viewport_scaling(const dt_develop_t *dev)
dt_dev_viewport_state_t dt_dev_viewport_get(const dt_develop_t *dev)
int32_t dt_dev_viewport_widget_height(const dt_develop_t *dev)
int32_t dt_dev_viewport_widget_width(const dt_develop_t *dev)
int32_t dt_dev_viewport_box_width(const dt_develop_t *dev)
#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:65
#define dt_free(ptr)
Definition mem_alloc.h:97
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
What the darkroom view asks the pipeline to show: the window onto the image.
dt_atomic_uint64 generation
dt_dev_viewport_state_t state
dt_dev_viewport_t * viewport
The darkroom view's window onto the image: widget allocation, borders, zoom, pan.
Definition develop.h:201