Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
color_picker_proxy.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2018-2021 Pascal Obry.
4 Copyright (C) 2019-2021 Diederik Ter Rahe.
5 Copyright (C) 2019 Edgardo Hoszowski.
6 Copyright (C) 2019 Ulrich Pegelow.
7 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
8 Copyright (C) 2020 Hubert Kowalski.
9 Copyright (C) 2020 Marco.
10 Copyright (C) 2021 Dan Torop.
11 Copyright (C) 2021 Paolo DePetrillo.
12 Copyright (C) 2021 Ralf Brown.
13 Copyright (C) 2022 Aldric Renaudin.
14 Copyright (C) 2022 Martin Bařinka.
15 Copyright (C) 2022 Philipp Lutz.
16 Copyright (C) 2023 Alynx Zhou.
17 Copyright (C) 2023, 2025-2026 Aurélien PIERRE.
18
19 darktable is free software: you can redistribute it and/or modify
20 it under the terms of the GNU Lesser General Public License as published by
21 the Free Software Foundation, either version 3 of the License, or
22 (at your option) any later version.
23
24 darktable is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU Lesser General Public License for more details.
28
29 You should have received a copy of the GNU Lesser General Public License
30 along with darktable. If not, see <http://www.gnu.org/licenses/>.
31*/
33#include "develop/iop_profile.h"
34#include "widgets/bauhaus.h"
36#include "common/color_picker.h"
37#include "control/signal.h"
38#include "control/redraw.h"
41#include "libs/colorpicker.h"
42#include "libs/lib.h"
43
44#include <inttypes.h>
45#include <math.h>
46#include <string.h>
48
49/*
50 The color_picker_proxy code is an interface which links the UI
51 colorpicker buttons in iops (and the colorpicker lib) with the rest
52 of the implementation (selecting/drawing colorpicker area in center
53 view, reading color value from preview pipe, and displaying results
54 in the colorpicker lib).
55
56 From the iop (or lib) POV, all that is necessary is to instantiate
57 color picker(s) via dt_color_picker_new() or
58 dt_color_picker_new_with_cst(), then subscribe to
59 DT_SIGNAL_CONTROL_PICKERDATA_READY and resolve the current ready sample
60 through dt_iop_color_picker_get_ready_data().
61
62 This code will initialize new pickers with a default area, then
63 remember the last area of the picker and use that when the picker is
64 reactivated.
65
66 The actual work of cache lookup and sampling happens here on the GUI
67 thread. The drawing & mouse-sensitivity of the picker overlay in the
68 center view happens in darkroom.c. The display of current sample
69 values occurs via libs/colorpicker.c, which uses this code to
70 activate its own picker.
71
72 The sample position is potentially stored in two places:
73
74 1. For each sampler widget, in dt_iop_color_picker_t.
75 2. For the active iop, the primary, and the live samples in
76 dt_colorpicker_sample_t.
77
78 There will be at most one editable sample, with one active picker, at
79 one time in the center view.
80*/
81
82
83// FIXME: should this be here or perhaps lib.c?
85{
86 const gboolean module_picker = dev->gui_module
87 && dev->gui_module->enabled
88 && dev->color_picker.enabled
89 && dev->color_picker.module == dev->gui_module;
90
91 const gboolean primary_picker = dev && dev->color_picker.enabled && !dev->color_picker.module;
92
93 return module_picker || primary_picker;
94}
95
97{
98 return module && module->dev
99 && module->dev->color_picker.enabled
100 && module->dev->color_picker.module == module;
101}
102
116{
117 dt_develop_t *const dev = dt_dev_get_global();
118 const dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
119 gboolean changed = FALSE;
120 if(self && sample)
121 {
123 {
124 for(int k = 0; k < 2; k++)
125 {
126 if(self->pick_pos[k] != sample->point[k])
127 {
128 self->pick_pos[k] = sample->point[k];
129 changed = TRUE;
130 }
131 }
132 self->geometry_is_raw = TRUE;
133 }
134 else if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
135 {
136 for(int k = 0; k < 4; k++)
137 {
138 if(self->pick_box[k] != sample->box[k])
139 {
140 self->pick_box[k] = sample->box[k];
141 changed = TRUE;
142 }
143 }
144 self->geometry_is_raw = TRUE;
145 }
146 }
147 return changed;
148}
149
155
162
163static void _refresh_active_picker(dt_develop_t *dev);
165static void _restart_picker_cache_wait(gpointer user_data);
166
167static inline void _picker_raw_point_to_image_norm(const dt_develop_t *dev, const float raw_point[2],
168 float image_point[2])
169{
170 image_point[0] = raw_point[0];
171 image_point[1] = raw_point[1];
173}
174
175static inline void _picker_raw_box_to_image_norm(const dt_develop_t *dev, const float raw_box[4],
176 float image_box[4])
177{
178 memcpy(image_box, raw_box, sizeof(float) * 4);
180}
181
183 const dt_iop_module_t *active_module,
184 float bounds[4])
185{
186 bounds[0] = 0.0f;
187 bounds[1] = 0.0f;
188 bounds[2] = 1.0f;
189 bounds[3] = 1.0f;
190
191 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->preview_pipe) || IS_NULL_PTR(active_module)) return;
193 const float processed_width = geometry.processed_width;
194 const float processed_height = geometry.processed_height;
195 if(processed_width <= 0.0f || processed_height <= 0.0f) return;
196
198 (dt_iop_module_t *)active_module);
199 if(IS_NULL_PTR(piece)) return;
200
201 float quad[8] = {
202 0.0f, 0.0f,
203 (float)piece->buf_out.width, 0.0f,
204 (float)piece->buf_out.width, (float)piece->buf_out.height,
205 0.0f, (float)piece->buf_out.height
206 };
209
210 float min_x = fminf(fminf(quad[0], quad[2]), fminf(quad[4], quad[6]));
211 float min_y = fminf(fminf(quad[1], quad[3]), fminf(quad[5], quad[7]));
212 float max_x = fmaxf(fmaxf(quad[0], quad[2]), fmaxf(quad[4], quad[6]));
213 float max_y = fmaxf(fmaxf(quad[1], quad[3]), fmaxf(quad[5], quad[7]));
214
215 min_x = CLAMP(min_x / processed_width, 0.0f, 1.0f);
216 min_y = CLAMP(min_y / processed_height, 0.0f, 1.0f);
217 max_x = CLAMP(max_x / processed_width, 0.0f, 1.0f);
218 max_y = CLAMP(max_y / processed_height, 0.0f, 1.0f);
219 bounds[0] = fminf(min_x, max_x);
220 bounds[1] = fminf(min_y, max_y);
221 bounds[2] = fmaxf(min_x, max_x);
222 bounds[3] = fmaxf(min_y, max_y);
223}
224
226{
227 if(IS_NULL_PTR(picker) || IS_NULL_PTR(dev) || picker->geometry_is_raw) return;
228
229 float bounds[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
230 _picker_get_module_bounds_image_norm(dev, picker->module, bounds);
231
233 const float processed_width = geometry.processed_width;
234 const float processed_height = geometry.processed_height;
235 if(processed_width <= 0.0f || processed_height <= 0.0f) return;
236 // Fixed border inset in scale-1 image pixels, then converted to image-norm.
237 // Keep this explicit here so the caller directly controls default picker coverage.
238 const float inset_pixels = 64.0f;
239 const float inset_x = inset_pixels / processed_width;
240 const float inset_y = inset_pixels / processed_height;
241 const float width = fmaxf(bounds[2] - bounds[0], 0.0f);
242 const float height = fmaxf(bounds[3] - bounds[1], 0.0f);
243 picker->pick_pos[0] = 0.5f * (bounds[0] + bounds[2]);
244 picker->pick_pos[1] = 0.5f * (bounds[1] + bounds[3]);
245 picker->pick_box[0] = bounds[0] + fminf(inset_x, 0.5f * width);
246 picker->pick_box[1] = bounds[1] + fminf(inset_y, 0.5f * height);
247 picker->pick_box[2] = bounds[2] - fminf(inset_x, 0.5f * width);
248 picker->pick_box[3] = bounds[3] - fminf(inset_y, 0.5f * height);
249
250 picker->pick_pos[0] = CLAMP(picker->pick_pos[0], bounds[0], bounds[2]);
251 picker->pick_pos[1] = CLAMP(picker->pick_pos[1], bounds[1], bounds[3]);
252 picker->pick_box[0] = CLAMP(picker->pick_box[0], bounds[0], bounds[2]);
253 picker->pick_box[1] = CLAMP(picker->pick_box[1], bounds[1], bounds[3]);
254 picker->pick_box[2] = CLAMP(picker->pick_box[2], bounds[0], bounds[2]);
255 picker->pick_box[3] = CLAMP(picker->pick_box[3], bounds[1], bounds[3]);
256 if(picker->pick_box[0] > picker->pick_box[2])
257 {
258 const float center = 0.5f * (picker->pick_box[0] + picker->pick_box[2]);
259 picker->pick_box[0] = center;
260 picker->pick_box[2] = center;
261 }
262 if(picker->pick_box[1] > picker->pick_box[3])
263 {
264 const float center = 0.5f * (picker->pick_box[1] + picker->pick_box[3]);
265 picker->pick_box[1] = center;
266 picker->pick_box[3] = center;
267 }
268
271 picker->geometry_is_raw = TRUE;
272}
273
274static int _picker_sample_box(const dt_iop_module_t *module, const dt_iop_roi_t *roi,
275 const dt_pixelpipe_picker_source_t picker_source, int *box)
276{
277 dt_develop_t *const dev = dt_dev_get_global();
278 const dt_colorpicker_sample_t *const sample = dev ? dev->color_picker.primary_sample : NULL;
279 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module) || IS_NULL_PTR(roi) || IS_NULL_PTR(sample)) return 1;
280
281 dt_boundingbox_t fbox = { 0.0f };
282
283 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
284 {
285 _picker_raw_box_to_image_norm(dev, sample->box, fbox);
287 }
288 else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
289 {
290 _picker_raw_point_to_image_norm(dev, sample->point, fbox);
292 fbox[2] = fbox[0];
293 fbox[3] = fbox[1];
294 }
295
297 picker_source == PIXELPIPE_PICKER_INPUT
300 fbox, 2);
301
302 const float roi_scale = roi->scale;
303 if(roi_scale != 1.0f)
304 {
305 fbox[0] *= roi_scale;
306 fbox[1] *= roi_scale;
307 fbox[2] *= roi_scale;
308 fbox[3] *= roi_scale;
309 }
310
311 fbox[0] -= roi->x;
312 fbox[1] -= roi->y;
313 fbox[2] -= roi->x;
314 fbox[3] -= roi->y;
315
316 box[0] = fminf(fbox[0], fbox[2]);
317 box[1] = fminf(fbox[1], fbox[3]);
318 box[2] = fmaxf(fbox[0], fbox[2]);
319 box[3] = fmaxf(fbox[1], fbox[3]);
320
322 {
323 box[2] += 1;
324 box[3] += 1;
325 }
326
327 if(box[0] >= roi->width || box[1] >= roi->height || box[2] < 0 || box[3] < 0) return 1;
328
329 box[0] = MIN(roi->width - 1, MAX(0, box[0]));
330 box[1] = MIN(roi->height - 1, MAX(0, box[1]));
331 box[2] = MIN(roi->width - 1, MAX(0, box[2]));
332 box[3] = MIN(roi->height - 1, MAX(0, box[3]));
333
334 if(box[2] <= box[0] || box[3] <= box[1]) return 1;
335
336 return 0;
337}
338
340 const dt_iop_buffer_dsc_t *dsc, const dt_iop_roi_t *roi,
341 const float *pixel, dt_aligned_pixel_t avg_out,
342 dt_aligned_pixel_t min_out, dt_aligned_pixel_t max_out,
343 const dt_pixelpipe_picker_source_t picker_source)
344{
345 int box[4];
346 if(_picker_sample_box(module, roi, picker_source, box)) return FALSE;
347
348 dt_aligned_pixel_t avg = { 0.0f };
349 dt_aligned_pixel_t min = { INFINITY, INFINITY, INFINITY, INFINITY };
350 dt_aligned_pixel_t max = { -INFINITY, -INFINITY, -INFINITY, -INFINITY };
351
354 dt_color_picker_helper(dsc, pixel, roi, box, avg, min, max, dsc->cst, picker_cst, profile);
355
356 for(int k = 0; k < 4; k++)
357 {
358 avg_out[k] = avg[k];
359 min_out[k] = min[k];
360 max_out[k] = max[k];
361 }
362
363 return TRUE;
364}
365
367 dt_dev_pixelpipe_t **pipe,
368 const dt_dev_pixelpipe_iop_t **piece)
369{
370 dt_develop_t *const dev = dt_dev_get_global();
371 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module) || dev->color_picker.pending_module != module || IS_NULL_PTR(dev->color_picker.pending_pipe))
372 return 1;
373
374 dt_dev_pixelpipe_t *const current_pipe = dev->color_picker.pending_pipe;
375 const dt_dev_pixelpipe_iop_t *current_piece = NULL;
376
377 /* We walk the current preview graph and look for the current piece matching the sampled hash.
378 Piece pointers cannot cross the signal boundary because the pipe may have been rebuilt. */
379 for(GList *pieces = g_list_first(current_pipe->nodes); pieces; pieces = g_list_next(pieces))
380 {
381 const dt_dev_pixelpipe_iop_t *const current = pieces->data;
382 if(current->module == module && current->global_hash == dev->color_picker.piece_hash)
383 {
384 current_piece = current;
385 break;
386 }
387 }
388
389 if(IS_NULL_PTR(current_piece))
390 {
392 "[picker] ready-data miss module=%s pending_hash=%" PRIu64 " pipe=%p\n",
393 module->op, dev->color_picker.piece_hash, (void *)current_pipe);
394 return 1;
395 }
396
397 if(picker) *picker = dev->color_picker.widget;
398 if(pipe) *pipe = current_pipe;
399 if(piece) *piece = current_piece;
401 "[picker] ready-data module=%s hash=%" PRIu64 " pipe=%p picker=%p\n",
402 module->op, current_piece->global_hash, (void *)current_pipe, (void *)dev->color_picker.widget);
403 return 0;
404}
405
407{
409 || !dev->color_picker.module || !dev->gui_module || dev->color_picker.module != dev->gui_module
410 || !dev->gui_module->enabled)
411 {
413 "[picker] guard bailout picker=%p enabled=%d picker_module=%s gui_module=%s "
414 "same_module=%d gui_module_enabled=%d\n",
415 (void *)(dev ? dev->color_picker.picker : NULL),
416 dev ? dev->color_picker.enabled : -1,
417 (dev && dev->color_picker.module) ? dev->color_picker.module->op : "-",
418 (dev && dev->gui_module) ? dev->gui_module->op : "-",
419 (dev && dev->color_picker.module && dev->gui_module)
420 ? (dev->color_picker.module == dev->gui_module)
421 : -1,
422 (dev && dev->gui_module) ? dev->gui_module->enabled : -1);
424 }
425
426 dt_dev_pixelpipe_t *const pipe = dev->preview_pipe;
428 const dt_dev_pixelpipe_iop_t *const previous_piece
430 if(IS_NULL_PTR(piece) || IS_NULL_PTR(previous_piece))
431 {
432 dt_print(DT_DEBUG_DEV, "[picker] sample retry module=%s piece=%p prev=%p\n",
433 dev->color_picker.module ? dev->color_picker.module->op : "-", (void *)piece, (void *)previous_piece);
435 }
436
438 || previous_piece->global_hash == DT_PIXELPIPE_CACHE_HASH_INVALID)
439 {
441 "[picker] invalid hash module=%s piece=%" PRIu64 " prev=%" PRIu64 "\n",
442 piece->module->op, piece->global_hash, previous_piece->global_hash);
444 }
445
446 /* Check the module's own OUTPUT before its input. Computing a module's output requires computing
447 * its input first (process_rec always recurses upstream before producing the requesting node), so
448 * requesting the output as the MODULE cache target makes a single recompute satisfy both this and
449 * the input wait below. Checking input first (as before) would bail out on an input miss without
450 * ever touching output, costing a full separate recompute round-trip for output afterwards even
451 * though computing it would have produced the input as a side effect anyway -- the common case
452 * right after focusing a module whose input/output were never sampled on this image (e.g. right
453 * after loading, or a module that stayed collapsed since darkroom opened). Output is skipped here
454 * only when it can structurally never resolve (bypass_cache/no_cache/realtime), since then nothing
455 * would ever recurse through for us and we must fall back to requesting the input on its own. */
456 void *output = NULL;
457 dt_pixel_cache_entry_t *output_entry = NULL;
458 const gboolean output_cache_blocked_by_policy
459 = piece->bypass_cache || pipe->bypass_cache || pipe->no_cache || dt_dev_pixelpipe_get_realtime(pipe);
460 dt_dev_pixelpipe_cache_wait_set_owner(&dev->color_picker.output_wait, "color-picker-output", dev->color_picker.module);
461 /* Retained: module input/output cachelines are intermediates sitting at refcount 0 between
462 * renders, so the reference must be taken under the same cache lock as the lookup -- taking it
463 * afterwards leaves a window in which the worker evicts the entry and frees the payload. From
464 * here to the releases below, every exit path owes one release per retained entry. */
465 const gboolean have_output = dt_dev_pixelpipe_cache_peek_gui_retained(pipe, piece, &output, &output_entry,
468 if(!have_output && !output_cache_blocked_by_policy)
469 {
472 "[picker] output cache miss module=%s hash=%" PRIu64 " (recompute will also satisfy input)\n",
473 piece->module->op, piece->global_hash);
475 }
476
477 void *input = NULL;
478 dt_pixel_cache_entry_t *input_entry = NULL;
479 dt_dev_pixelpipe_cache_wait_set_owner(&dev->color_picker.input_wait, "color-picker-input", dev->color_picker.module);
480 if(!dt_dev_pixelpipe_cache_peek_gui_retained(pipe, previous_piece, &input, &input_entry,
482 {
483 dev->color_picker.wait_input_hash = previous_piece->global_hash;
484 dt_print(DT_DEBUG_DEV, "[picker] input cache miss module=%s prev_hash=%" PRIu64 "\n",
485 piece->module->op, previous_piece->global_hash);
486 if(have_output) dt_dev_pixelpipe_cache_ref_count_entry(FALSE, output_entry);
488 }
489
490 if(!have_output)
491 {
492 /* Only reachable when output_cache_blocked_by_policy: module GUIs such as color equalizer only
493 consume the module-input sample, and some pipe modes (bypass_cache/no_cache/realtime) can never
494 publish a module output cacheline at all. Keep output statistics explicitly invalid so
495 output-dependent consumers can detect the missing sample, but still publish the ready input
496 sample instead of blocking picker feedback forever. */
497 dt_print(DT_DEBUG_DEV, "[picker] output cache blocked by policy module=%s hash=%" PRIu64 "\n",
498 piece->module->op, piece->global_hash);
500
501 for(int k = 0; k < 4; k++)
502 {
503 piece->module->picked_output_color[k] = 0.0f;
504 piece->module->picked_output_color_min[k] = 666.0f;
505 piece->module->picked_output_color_max[k] = -666.0f;
506 }
507 }
508
509 if(previous_piece->dsc_out.datatype != TYPE_FLOAT || (have_output && piece->dsc_out.datatype != TYPE_FLOAT))
510 {
512 "[picker] non-float buffers module=%s input_type=%d output_type=%d\n",
513 piece->module->op, previous_piece->dsc_out.datatype, piece->dsc_out.datatype);
514 if(have_output) dt_dev_pixelpipe_cache_ref_count_entry(FALSE, output_entry);
517 }
518
519 /* Unlike histogram/global backbuffers, module color-pickers do not publish a dedicated long-lived buffer.
520 * They reopen the current module input/output cachelines by immutable `global_hash` and hold the reference
521 * the retained lookup gave them, plus a read lock for the duration of the sampling pass, so concurrent
522 * cache recycling cannot free the payload mid-read. */
524 if(have_output) dt_dev_pixelpipe_cache_rdlock_entry(TRUE, output_entry);
525
526 const gboolean sampled_input
527 = _sample_picker_buffer(pipe, piece->module, &previous_piece->dsc_out, &previous_piece->roi_out,
528 input, piece->module->picked_color, piece->module->picked_color_min,
529 piece->module->picked_color_max, PIXELPIPE_PICKER_INPUT);
530 const gboolean sampled_output
531 = have_output
532 ? _sample_picker_buffer(pipe, piece->module, &piece->dsc_out, &piece->roi_out, output,
533 piece->module->picked_output_color, piece->module->picked_output_color_min,
534 piece->module->picked_output_color_max, PIXELPIPE_PICKER_OUTPUT)
535 : FALSE;
536
537 if(!have_output && sampled_input)
538 {
539 // Keep GUI picker feedback alive whenever module output is temporarily
540 // unavailable: mirror input sample statistics to output slots. If output
541 // cache becomes available later, subsequent refreshes overwrite these with
542 // true output samples.
543 for(int k = 0; k < 4; k++)
544 {
545 piece->module->picked_output_color[k] = piece->module->picked_color[k];
546 piece->module->picked_output_color_min[k] = piece->module->picked_color_min[k];
547 piece->module->picked_output_color_max[k] = piece->module->picked_color_max[k];
548 }
549 }
550
551 if(have_output)
552 {
555 }
558
559 if(!sampled_input)
560 {
561 dt_print(DT_DEBUG_DEV, "[picker] sample failed module=%s input=%d output=%d\n",
562 piece->module->op, sampled_input, sampled_output);
563 dev->color_picker.picker->update_pending = FALSE;
566 }
567
569 "[picker] sampled module=%s hash=%" PRIu64 " avg=(%g,%g,%g) min=(%g,%g,%g) max=(%g,%g,%g)\n",
570 piece->module->op, piece->global_hash,
571 piece->module->picked_color[0], piece->module->picked_color[1], piece->module->picked_color[2],
572 piece->module->picked_color_min[0], piece->module->picked_color_min[1], piece->module->picked_color_min[2],
573 piece->module->picked_color_max[0], piece->module->picked_color_max[1], piece->module->picked_color_max[2]);
574
575 dev->color_picker.piece_hash = piece->global_hash;
576 dev->color_picker.pending_module = piece->module;
577 dev->color_picker.pending_pipe = pipe;
580 dev->color_picker.picker->update_pending = FALSE;
582
584 dev->color_picker.pending_module = NULL;
585 dev->color_picker.pending_pipe = NULL;
588}
589
590static gboolean _refresh_active_picker_idle(gpointer user_data)
591{
592 dt_develop_t *const dev = (dt_develop_t *)user_data;
593 if(dev) dev->color_picker.refresh_idle_source = 0;
595 return G_SOURCE_REMOVE;
596}
597
599{
600 if(IS_NULL_PTR(dev)) return;
601 if(dev->color_picker.refresh_idle_source) return;
603}
604
605static void _restart_picker_cache_wait(gpointer user_data)
606{
607 dt_develop_t *const dev = (dt_develop_t *)user_data;
609}
610
612{
613 if(IS_NULL_PTR(dev) || IS_NULL_PTR(dev->color_picker.picker) || !dev->color_picker.enabled) return;
614 if(!dev->color_picker.picker->update_pending && !dev->color_picker.update_pending) return;
615
617 "[picker] refresh module=%s update_pending=%d widget_pending=%d processing=%d\n",
618 dev->color_picker.module ? dev->color_picker.module->op : "-",
619 dev->color_picker.update_pending, dev->color_picker.picker->update_pending,
620 dev->preview_pipe ? dev->preview_pipe->processing : -1);
621
623
624 /* A picker update is satisfied either from the current preview cache or from the next completed
625 preview run. If preview is already processing, re-dirtying it here only feeds TOP_CHANGED
626 loops and prevents the current run from ever publishing the cacheline we are waiting for. */
628 {
629 // Make sure CACHELINE_READY can wake us as soon as this in-flight run
630 // publishes input/output cachelines for the active picker.
632 return;
633 }
634
635 if(IS_NULL_PTR(dev->color_picker.module))
636 {
637 /* The global picker already samples from histogram backbuffers published by preview updates.
638 Refresh it directly from that GUI-owned cache when possible so dragging the picker updates
639 the histogram labels immediately without waiting for another preview completion. */
643 {
644 dev->color_picker.picker->update_pending = FALSE;
647 return;
648 }
649 }
650
651 if(dev->preview_pipe)
652 {
654 if(sampled != DT_COLOR_PICKER_RESAMPLE_RETRY)
655 return;
656 }
657}
658
660{
661 if(picker)
662 {
663 if(picker->module) dt_iop_set_cache_bypass(picker->module, FALSE);
664
666
668 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(picker->colorpick), FALSE);
669 else
671
673 }
674}
675
677{
678 (void)widget;
679 dt_develop_t *const dev = dt_dev_get_global();
680 if(IS_NULL_PTR(dev) || dev->color_picker.picker != picker) return;
681
682 // Mirror dt_iop_color_picker_reset()'s cleanup: the picker's own button widget is the one being
683 // destroyed here (e.g. a module GUI panel rebuild), so there is no widget left to toggle off, but
684 // every other piece of dev-wide picker state must still be cleared the same way. Leaving
685 // dev->color_picker.enabled/.module stale keeps dt_iop_color_picker_is_visible() reporting a
686 // picker "active" for a module whose picker widget no longer exists -- observed as the custom
687 // cursor in iop/toneequal.c getting stuck, fixed only by fully reloading the image (issue #1028).
688 dt_iop_module_t *const module = dev->color_picker.module;
689
690 dt_dev_pixelpipe_cache_wait_cleanup(&dev->color_picker.input_wait, "picker-widget-destroyed-input");
691 dt_dev_pixelpipe_cache_wait_cleanup(&dev->color_picker.output_wait, "picker-widget-destroyed-output");
692
693 dev->color_picker.picker = NULL;
694 dev->color_picker.widget = NULL;
695 dev->color_picker.module = NULL;
700 if(module) module->request_color_pick = DT_REQUEST_COLORPICK_OFF;
701}
702
703void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
704{
705 dt_develop_t *const dev = dt_dev_get_global();
706 dt_iop_color_picker_t *picker = dev ? dev->color_picker.picker : NULL;
707 if(picker && picker->module == module)
708 {
709 if(!keep)
710 {
713 _color_picker_reset(picker);
714 dev->color_picker.picker = NULL;
715 dev->color_picker.widget = NULL;
716 dev->color_picker.module = NULL;
721 if(module) module->request_color_pick = DT_REQUEST_COLORPICK_OFF;
722 }
723 }
724}
725
728{
729 // module is NULL if primary colorpicker
730 picker->module = module;
731 picker->kind = kind;
732 picker->picker_cst = module ? module->default_colorspace(module, NULL, NULL) : IOP_CS_NONE;
733 picker->colorpick = button;
734 picker->update_pending = FALSE;
735 picker->geometry_is_raw = FALSE;
736
737 // default values
738 const float middle = 0.5f;
739 const float area = 0.975f;
740 picker->pick_pos[0] = picker->pick_pos[1] = middle;
741 picker->pick_box[0] = (1.0f - area);
742 picker->pick_box[1] = (1.0f - area);
743 picker->pick_box[2] = area;
744 picker->pick_box[3] = area;
745
746 _color_picker_reset(picker);
747}
748
749static gboolean _color_picker_callback_button_press(GtkWidget *button, GdkEventButton *e, dt_iop_color_picker_t *self)
750{
751 // module is NULL if primary colorpicker
752 dt_iop_module_t *module = self->module;
753 dt_develop_t *const dev = dt_dev_get_global();
754
756 {
757 dt_print(DT_DEBUG_DEV, "[picker] click ignored: widgets suppressed module=%s\n",
758 module ? module->op : "global");
759 return FALSE;
760 }
761
762 dt_iop_color_picker_t *prior_picker = dev ? dev->color_picker.picker : NULL;
763 if(prior_picker && prior_picker != self)
764 {
765 _color_picker_reset(prior_picker);
766 if(prior_picker->module) prior_picker->module->request_color_pick = DT_REQUEST_COLORPICK_OFF;
767 }
768
769 GtkWidget *off = dt_iop_gui_get_off(module);
770 if(off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(off), TRUE);
771
772 const GdkModifierType state = !IS_NULL_PTR(e) ? e->state : dt_key_modifier_state();
773 const gboolean ctrl_key_pressed = dt_modifier_is(state, DT_PRIMARY_MASK) || (!IS_NULL_PTR(e) && e->button == 3);
775
776 if(prior_picker != self || (kind == DT_COLOR_PICKER_POINT_AREA &&
777 (ctrl_key_pressed ^ (dev->color_picker.primary_sample->size == DT_LIB_COLORPICKER_SIZE_BOX))))
778 {
779 dev->color_picker.picker = self;
780 dev->color_picker.widget = self->colorpick;
781 dev->color_picker.module = module;
782 dev->color_picker.kind = kind;
783 dev->color_picker.picker_cst = self->picker_cst;
784 dev->color_picker.enabled = TRUE;
785
786 if(module) module->request_color_pick = DT_REQUEST_COLORPICK_MODULE;
787
789 {
790 kind = ctrl_key_pressed ? DT_COLOR_PICKER_AREA : DT_COLOR_PICKER_POINT;
791 }
793
795 {
796 dt_boundingbox_t image_box = { 0.0f };
797 _picker_raw_box_to_image_norm(dev, self->pick_box, image_box);
799 }
800 else if(kind == DT_COLOR_PICKER_POINT)
801 {
802 float image_point[2] = { 0.0f };
803 _picker_raw_point_to_image_norm(dev, self->pick_pos, image_point);
805 }
806 else
808
809 // important to have set up state before toggling button and
810 // triggering more callbacks
813 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->colorpick), TRUE);
814 else
817
818 dt_iop_module_t *const gui_module_before_focus = dev->gui_module;
819 if(module)
820 dt_iop_request_focus(module);
821
823 "[picker] activate module=%s picker=%p widget=%p kind=%d cst=%d gui_module_before=%s "
824 "gui_module_after=%s widgets_suppressed=%d\n",
825 module ? module->op : "global", (void *)self, (void *)self->colorpick, kind, self->picker_cst,
826 gui_module_before_focus ? gui_module_before_focus->op : "-",
827 dev->gui_module ? dev->gui_module->op : "-", dt_gui_widgets_suppressed());
828
829 }
830 else
831 {
832 dt_dev_pixelpipe_cache_wait_cleanup(&dev->color_picker.input_wait, "picker-deactivate-input");
833 dt_dev_pixelpipe_cache_wait_cleanup(&dev->color_picker.output_wait, "picker-deactivate-output");
834 dev->color_picker.picker = NULL;
835 dev->color_picker.widget = NULL;
836 dev->color_picker.module = NULL;
837 dev->color_picker.kind = DT_COLOR_PICKER_POINT;
838 dev->color_picker.picker_cst = IOP_CS_NONE;
839 dev->color_picker.enabled = FALSE;
840 dev->color_picker.update_pending = FALSE;
842 if(module)
843 {
844 module->request_color_pick = DT_REQUEST_COLORPICK_OFF;
845 }
846 dt_print(DT_DEBUG_DEV, "[picker] deactivate module=%s picker=%p widget=%p\n",
847 module ? module->op : "global", (void *)self, (void *)self->colorpick);
848 }
849
850 // Draw picker geometry immediately; data sampling update can complete later.
852 if(dev->color_picker.enabled)
854
855 return TRUE;
856}
857
859{
860 _color_picker_callback_button_press(button, NULL, self);
861}
862
864{
865 dt_develop_t *const dev = dt_dev_get_global();
866 dt_iop_color_picker_t *const picker = dev ? dev->color_picker.picker : NULL;
867 if(picker && picker->module == module && picker->picker_cst != picker_cst)
868 {
869 picker->picker_cst = picker_cst;
871 }
872}
873
875{
876 dt_develop_t *const dev = dt_dev_get_global();
877 dt_iop_color_picker_t *picker = dev ? dev->color_picker.picker : NULL;
878 if(picker && picker->module == module)
879 return picker->picker_cst;
880 else
881 return IOP_CS_NONE;
882}
883
885{
886 dt_develop_t *const dev = dt_dev_get_global();
887 dt_iop_color_picker_t *picker = dev ? dev->color_picker.picker : NULL;
888 if(picker) picker->update_pending = TRUE;
889 if(dev)
890 {
892 }
893 if(dev)
894 dt_print(DT_DEBUG_DEV, "[picker] request update module=%s picker=%p widget=%p\n",
895 dev->color_picker.module ? dev->color_picker.module->op : "-",
896 (void *)dev->color_picker.picker, (void *)dev->color_picker.widget);
898}
899
901{
902 if(IS_NULL_PTR(dev))
903 return;
904
907
909 || !dev->color_picker.module || !dev->gui_module || dev->color_picker.module != dev->gui_module
910 || !dev->gui_module->enabled)
911 return;
912
914 dev->color_picker.module);
915 const dt_dev_pixelpipe_iop_t *const previous_piece
917 if(piece) dev->color_picker.wait_output_hash = piece->global_hash;
918 if(previous_piece) dev->color_picker.wait_input_hash = previous_piece->global_hash;
919}
920
921static void _iop_color_picker_history_resync_callback(gpointer instance, gpointer user_data)
922{
923 (void)instance;
924 (void)user_data;
925 dt_develop_t *const dev = dt_dev_get_global();
928}
929
930static void _iop_color_picker_cacheline_ready_callback(gpointer instance, const guint64 hash,
931 const guint64 producer_node_key, gpointer user_data)
932{
933 (void)instance;
934 (void)producer_node_key; // the shared cache-wait manager serves the picker's input/output
935 // waits by producer node; this direct path stays exact-hash only.
936 (void)user_data;
937
938 dt_develop_t *const dev = dt_dev_get_global();
939 if(IS_NULL_PTR(dev)) return;
940
941 gboolean matched = FALSE;
942 if(dev->color_picker.wait_input_hash == hash)
943 {
945 matched = TRUE;
946 }
947 if(dev->color_picker.wait_output_hash == hash)
948 {
950 matched = TRUE;
951 }
952
953 if(matched) _queue_refresh_active_picker(dev);
954}
955
975static void _iop_color_picker_pipe_finished_callback(gpointer instance, gpointer user_data)
976{
977 (void)instance;
978 (void)user_data;
979 dt_develop_t *const dev = dt_dev_get_global();
980 if(IS_NULL_PTR(dev)) return;
982}
983
992static void _iop_color_picker_notebook_tab_changed_callback(gpointer instance, gpointer owner, gpointer user_data)
993{
994 (void)instance;
995 (void)user_data;
996 if(IS_NULL_PTR(owner)) return;
998}
999
1011
1023
1025 const gboolean init_cst, const dt_iop_colorspace_type_t cst)
1026{
1027 dt_iop_color_picker_t *color_picker = (dt_iop_color_picker_t *)g_malloc(sizeof(dt_iop_color_picker_t));
1028
1029 if(IS_NULL_PTR(w) || GTK_IS_BOX(w))
1030 {
1032 _init_picker(color_picker, module, kind, button);
1033 if(init_cst)
1034 color_picker->picker_cst = cst;
1035 g_signal_connect_data(G_OBJECT(button), "button-press-event",
1036 G_CALLBACK(_color_picker_callback_button_press), color_picker, (GClosureNotify)g_free, 0);
1037 g_signal_connect(G_OBJECT(button), "destroy", G_CALLBACK(_color_picker_widget_destroy), color_picker);
1038 if(w) gtk_box_pack_start(GTK_BOX(w), button, FALSE, FALSE, 0);
1039
1040 dt_develop_t *const dev = dt_dev_get_global();
1041 if(dev && dev->color_picker.enabled && dev->color_picker.module == module
1043 && dev->color_picker.kind == kind
1044 && dev->color_picker.picker_cst == color_picker->picker_cst)
1045 {
1046 dev->color_picker.picker = color_picker;
1047 dev->color_picker.widget = button;
1048
1050 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
1052 }
1053
1054 return button;
1055 }
1056 else
1057 {
1060 _init_picker(color_picker, module, kind, w);
1061 if(init_cst)
1062 color_picker->picker_cst = cst;
1063 g_signal_connect_data(G_OBJECT(w), "quad-pressed",
1064 G_CALLBACK(_color_picker_callback), color_picker, (GClosureNotify)g_free, 0);
1065 g_signal_connect(G_OBJECT(w), "destroy", G_CALLBACK(_color_picker_widget_destroy), color_picker);
1066
1067 dt_develop_t *const dev = dt_dev_get_global();
1068 if(dev && dev->color_picker.enabled && dev->color_picker.module == module
1070 && dev->color_picker.kind == kind
1071 && dev->color_picker.picker_cst == color_picker->picker_cst)
1072 {
1073 dev->color_picker.picker = color_picker;
1074 dev->color_picker.widget = w;
1075
1079 }
1080
1081 return w;
1082 }
1083}
1084
1089
1095
1096// clang-format off
1097// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1098// vim: shiftwidth=2 expandtab tabstop=2 cindent
1099// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1100// clang-format on
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1572
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1578
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1554
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const dt_adaptation_t kind
dt_iop_colorspace_type_t
@ IOP_CS_NONE
void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t image_cst, const dt_iop_colorspace_type_t picker_cst, const dt_iop_order_iccprofile_info_t *const profile)
static void _restart_picker_cache_wait(gpointer user_data)
static void _iop_color_picker_cacheline_ready_callback(gpointer instance, const guint64 hash, const guint64 producer_node_key, gpointer user_data)
static void _picker_raw_box_to_image_norm(const dt_develop_t *dev, const float raw_box[4], float image_box[4])
static void _color_picker_widget_destroy(GtkWidget *widget, dt_iop_color_picker_t *picker)
static void _queue_refresh_active_picker(dt_develop_t *dev)
void dt_iop_color_picker_init(void)
static void _refresh_active_picker(dt_develop_t *dev)
static int _picker_sample_box(const dt_iop_module_t *module, const dt_iop_roi_t *roi, const dt_pixelpipe_picker_source_t picker_source, int *box)
void dt_iop_color_picker_cleanup(void)
static void _iop_color_picker_notebook_tab_changed_callback(gpointer instance, gpointer owner, gpointer user_data)
static gboolean _record_point_area(dt_iop_color_picker_t *self)
Synchronize one picker cached geometry with the primary sample.
static gboolean _sample_picker_buffer(dt_dev_pixelpipe_t *pipe, dt_iop_module_t *module, const dt_iop_buffer_dsc_t *dsc, const dt_iop_roi_t *roi, const float *pixel, dt_aligned_pixel_t avg_out, dt_aligned_pixel_t min_out, dt_aligned_pixel_t max_out, const dt_pixelpipe_picker_source_t picker_source)
static void _iop_color_picker_pipe_finished_callback(gpointer instance, gpointer user_data)
void dt_iop_color_picker_request_update(void)
static void _picker_get_module_bounds_image_norm(const dt_develop_t *dev, const dt_iop_module_t *active_module, float bounds[4])
static void _track_active_picker_hashes(dt_develop_t *dev)
static gboolean _refresh_active_picker_idle(gpointer user_data)
static void _iop_color_picker_history_resync_callback(gpointer instance, gpointer user_data)
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
int dt_iop_color_picker_get_ready_data(const dt_iop_module_t *module, GtkWidget **picker, dt_dev_pixelpipe_t **pipe, const dt_dev_pixelpipe_iop_t **piece)
dt_pixelpipe_picker_source_t
@ PIXELPIPE_PICKER_INPUT
@ PIXELPIPE_PICKER_OUTPUT
static gboolean _color_picker_callback_button_press(GtkWidget *button, GdkEventButton *e, dt_iop_color_picker_t *self)
void dt_iop_color_picker_set_cst(dt_iop_module_t *module, const dt_iop_colorspace_type_t picker_cst)
GtkWidget * dt_color_picker_new_with_cst(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w, const dt_iop_colorspace_type_t cst)
static void _color_picker_reset(dt_iop_color_picker_t *picker)
static dt_color_picker_resample_status_t _sample_picker_from_cache(dt_develop_t *dev)
gboolean dt_iop_color_picker_is_active_module(const dt_iop_module_t *module)
Tell whether one module currently owns the active darkroom picker.
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
static void _picker_raw_point_to_image_norm(const dt_develop_t *dev, const float raw_point[2], float image_point[2])
gboolean dt_iop_color_picker_is_visible(const dt_develop_t *dev)
static void _color_picker_callback(GtkWidget *button, dt_iop_color_picker_t *self)
dt_color_picker_resample_status_t
@ DT_COLOR_PICKER_RESAMPLE_RETRY
@ DT_COLOR_PICKER_RESAMPLE_CONSUMED
@ DT_COLOR_PICKER_RESAMPLE_EMITTED
static void _init_picker(dt_iop_color_picker_t *picker, dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *button)
static GtkWidget * _color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w, const gboolean init_cst, const dt_iop_colorspace_type_t cst)
dt_iop_colorspace_type_t dt_iop_color_picker_get_active_cst(dt_iop_module_t *module)
static void _picker_initialize_geometry_raw(dt_iop_color_picker_t *picker, dt_develop_t *dev)
enum _iop_color_picker_kind_t dt_iop_color_picker_kind_t
@ DT_COLOR_PICKER_AREA
@ DT_COLOR_PICKER_POINT
@ DT_COLOR_PICKER_POINT_AREA
@ DT_LIB_COLORPICKER_SIZE_POINT
Definition colorpicker.h:38
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:39
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static const float const float const float min
const float max
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_lib_t * dt_lib_get_global(void)
Definition darktable.c:621
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
const dt_dev_pixelpipe_iop_t * dt_dev_pixelpipe_get_module_piece(const dt_dev_pixelpipe_t *pipe, const dt_iop_module_t *module)
void dt_dev_pixelpipe_cache_wait_cleanup(dt_dev_pixelpipe_cache_wait_t *wait, const char *reason)
Cancel one pending GUI cache wait request and clear its runtime state.
const dt_dev_pixelpipe_iop_t * dt_dev_pixelpipe_get_prev_enabled_piece(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
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_retained(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)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1797
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1278
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1271
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1203
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1826
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:108
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
Definition develop.h:109
GtkWidget * geometry
its size, under the preview
@ TYPE_FLOAT
Definition format.h:56
float dt_boundingbox_t[4]
Definition image.h:83
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:1669
@ DT_REQUEST_COLORPICK_OFF
Definition imageop.h:215
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
GtkWidget * dt_iop_gui_get_off(dt_iop_module_t *module)
void dt_lib_colorpicker_set_box_area(dt_lib_t *lib, const dt_boundingbox_t box)
Definition lib.c:1229
void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
Definition lib.c:1251
@ DT_DEBUG_DEV
Definition logging.h:53
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.
float *const restrict const size_t k
#define dt_unreachable_codepath()
Mark a branch as impossible.
Definition macros.h:141
#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_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
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.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
Asking the interface to repaint itself.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:177
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
Definition signal.h:212
@ DT_SIGNAL_CACHELINE_READY
This signal is raised when one cacheline write lock is released. 1 : uint64_t cacheline hash no retur...
Definition signal.h:188
@ DT_SIGNAL_CONTROL_NOTEBOOK_TAB_CHANGED
This signal is raised when a GtkNotebook created/registered via dt_ui_notebook_set_picker_owner() swi...
Definition signal.h:305
@ DT_SIGNAL_CONTROL_PICKERDATA_READY
This signal is raised when new color picker data are available in darkroom. no param,...
Definition signal.h:296
#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
const float uint32_t state[4]
dt_lib_colorpicker_size_t size
Definition colorpicker.h:64
dt_boundingbox_t box
Definition colorpicker.h:63
Objective facts about the image a dev is working on.
dt_iop_buffer_dsc_t dsc_out
struct dt_iop_module_t *void * data
int picker_cst
Definition develop.h:369
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:374
gboolean update_pending
Definition develop.h:371
uint64_t wait_output_hash
Definition develop.h:386
GtkWidget * widget
Definition develop.h:367
gboolean(* refresh_global_picker)(struct dt_lib_module_t *self)
Definition develop.h:382
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
struct dt_iop_module_t * pending_module
Definition develop.h:390
struct dt_iop_module_t * gui_module
Definition develop.h:170
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:214
dt_dev_pixelpipe_cache_wait_t output_wait
Definition develop.h:388
struct dt_iop_module_t *struct dt_iop_color_picker_t * picker
Definition develop.h:366
uint64_t wait_input_hash
Definition develop.h:385
guint refresh_idle_source
Definition develop.h:372
struct dt_dev_pixelpipe_t * pending_pipe
Definition develop.h:391
struct dt_lib_module_t * histogram_module
Definition develop.h:381
gboolean enabled
Definition develop.h:370
uint64_t piece_hash
Definition develop.h:384
dt_dev_pixelpipe_cache_wait_t input_wait
Definition develop.h:387
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_iop_module_t *dt_iop_color_picker_kind_t kind
dt_boundingbox_t pick_box
dt_iop_colorspace_type_t picker_cst
gboolean enabled
Definition imageop.h:313
dt_dev_operation_t op
Definition imageop.h:259
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_IS_TOGGLEBUTTON(obj)
GdkModifierType dt_key_modifier_state()
gboolean dt_gui_widgets_suppressed(void)
static gboolean dt_modifier_is(GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
void dtgtk_cairo_paint_colorpicker(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)