Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
color_picker.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016 Roman Lebedev.
4 Copyright (C) 2019 Andreas Schneider.
5 Copyright (C) 2019 Edgardo Hoszowski.
6 Copyright (C) 2019-2021 Pascal Obry.
7 Copyright (C) 2020-2021 Harold le Clément de Saint-Marcq.
8 Copyright (C) 2020 Heiko Bauke.
9 Copyright (C) 2020 Hubert Kowalski.
10 Copyright (C) 2020-2021 Ralf Brown.
11 Copyright (C) 2021 Hanno Schwalm.
12 Copyright (C) 2022, 2025-2026 Aurélien PIERRE.
13 Copyright (C) 2022 Martin Bařinka.
14 Copyright (C) 2022 Philipp Lutz.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#include "common/color_picker.h"
32#include "pixel/bspline.h"
33#include "system/macros.h"
34#include "system/openmp.h"
35#include "system/mem_alloc.h"
36#include "system/simd.h"
37#include "common/logging.h"
38#include "common/times.h"
41#include "pixel/format.h"
43
44static inline size_t _box_size(const int *const box)
45{
46 return (size_t)((box[3] - box[1]) * (box[2] - box[0]));
47}
48
49static inline void rgb_to_JzCzhz(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t JzCzhz,
50 const dt_iop_order_iccprofile_info_t *const profile);
51
53static inline gboolean _picker_cst_wants_lab(const dt_iop_colorspace_type_t picker_cst)
54{
55 return picker_cst == IOP_CS_LAB || picker_cst == IOP_CS_LCH;
56}
57
68static void _color_picker_convert_buffer(const float *const restrict input, float *const restrict output,
69 const size_t pixels, const dt_iop_colorspace_type_t image_cst,
70 const dt_iop_colorspace_type_t picker_cst,
71 const dt_iop_order_iccprofile_info_t *const profile)
72{
73 /* Two steps, so every pairing of the blending tabs is served: first bring the pixel into the
74 * family the picker space derives from (Lab for Lab/LCh, RGB for RGB/HSL/JzCzhz) -- the only
75 * step that needs the profile -- then derive the picker space from it. A Lab module blended in
76 * RGB (scene) asks Lab -> JzCzhz, an RGB module blended in Lab asks RGB -> LCh. */
77 const gboolean image_lab = (image_cst == IOP_CS_LAB);
78 const gboolean want_lab = _picker_cst_wants_lab(picker_cst);
79
81 for(size_t k = 0; k < pixels; k++)
82 {
83 const size_t offset = 4 * k;
84 dt_aligned_pixel_t base = { input[offset], input[offset + 1], input[offset + 2], 0.0f };
85
86 if(image_lab && !want_lab)
87 dt_ioppr_lab_to_rgb_matrix(input + offset, base, profile->matrix_out_transposed, profile->lut_out,
88 profile->unbounded_coeffs_out, profile->lutsize, profile->nonlinearlut);
89 else if(!image_lab && want_lab)
90 dt_ioppr_rgb_matrix_to_lab(input + offset, base, profile->matrix_in_transposed, profile->lut_in,
91 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
92
93 switch(picker_cst)
94 {
95 case IOP_CS_LCH:
96 dt_Lab_2_LCH(base, output + offset);
97 break;
98 case IOP_CS_HSL:
99 dt_RGB_2_HSL(base, output + offset);
100 break;
101 case IOP_CS_JZCZHZ:
102 rgb_to_JzCzhz(base, output + offset, profile);
103 break;
104 default:
105 for(int c = 0; c < 3; c++) output[offset + c] = base[c];
106 break;
107 }
108 output[offset + 3] = input[offset + 3];
109 }
110}
111
112__OMP_DECLARE_SIMD__(aligned(rgb, JzCzhz: 16) uniform(profile))
114 const dt_iop_order_iccprofile_info_t *const profile)
115{
116 dt_aligned_pixel_t XYZ_D65 = { 0.0f, 0.0f, 0.0f };
117 dt_aligned_pixel_t JzAzBz = { 0.0f, 0.0f, 0.0f };
118
119 if(profile)
120 {
121 dt_aligned_pixel_t XYZ_D50 = { 0.0f, 0.0f, 0.0f };
122 dt_ioppr_rgb_matrix_to_xyz(rgb, XYZ_D50, profile->matrix_in_transposed, profile->lut_in, profile->unbounded_coeffs_in,
123 profile->lutsize, profile->nonlinearlut);
124 dt_XYZ_D50_2_XYZ_D65(XYZ_D50, XYZ_D65);
125 }
126 else
127 {
128 // This should not happen (we don't know what RGB is), but use this when profile is not defined
129 dt_XYZ_D50_2_XYZ_D65(rgb, XYZ_D65);
130 }
131
132 dt_XYZ_2_JzAzBz(XYZ_D65, JzAzBz);
133 dt_JzAzBz_2_JzCzhz(JzAzBz, JzCzhz);
134}
136 const float *const pixels, const float w, const size_t width)
137{
138 for(size_t i = 0; i < width; i += 4)
139 {
140 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
141 for(size_t k = 0; k < 4; k++)
142 {
143 avg[k] += w * pick[k];
144 min[k] = fminf(min[k], pick[k]);
145 max[k] = fmaxf(max[k], pick[k]);
146 }
147 }
148}
150 const float *const pixels, const float w, const size_t width)
151{
152 for(size_t i = 0; i < width; i += 4)
153 {
154 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
155 pick[3] = pick[0] < 0.5f ? pick[0] + 0.5f : pick[0] - 0.5f;
156 for(size_t k = 0; k < 4; k++)
157 {
158 avg[k] += w * pick[k];
159 min[k] = fminf(min[k], pick[k]);
160 max[k] = fmaxf(max[k], pick[k]);
161 }
162 }
163}
165 dt_aligned_pixel_t max, const float *const pixels,
166 const float w, const size_t width)
167{
168 for(size_t i = 0; i < width; i += 4)
169 {
170 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
171 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
172 for(size_t k = 0; k < 4; k++)
173 {
174 avg[k] += w * pick[k];
175 min[k] = fminf(min[k], pick[k]);
176 max[k] = fmaxf(max[k], pick[k]);
177 }
178 }
179}
181 const float *const pixels, const float w, const size_t width)
182{
183 for(size_t i = 0; i < width; i += 4)
184 {
186 dt_Lab_2_LCH(pixels + i, pick);
187 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
188 for(size_t k = 0; k < 4; k++)
189 {
190 avg[k] += w * pick[k];
191 min[k] = fminf(min[k], pick[k]);
192 max[k] = fmaxf(max[k], pick[k]);
193 }
194 }
195}
197 const float *const pixels, const float w, const size_t width)
198{
199 for(size_t i = 0; i < width; i += 4)
200 {
202 dt_RGB_2_HSL(pixels + i, pick);
203 pick[3] = pick[0] < 0.5f ? pick[0] + 0.5f : pick[0] - 0.5f;
204 for(size_t k = 0; k < 4; k++)
205 {
206 avg[k] += w * pick[k];
207 min[k] = fminf(min[k], pick[k]);
208 max[k] = fmaxf(max[k], pick[k]);
209 }
210 }
211}
213 const float *const pixels, const float w, const size_t width,
214 const dt_iop_order_iccprofile_info_t *const profile)
215{
216 for(size_t i = 0; i < width; i += 4)
217 {
219 rgb_to_JzCzhz(pixels + i, pick, profile);
220 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
221 for(size_t k = 0; k < 4; k++)
222 {
223 avg[k] += w * pick[k];
224 min[k] = fminf(min[k], pick[k]);
225 max[k] = fmaxf(max[k], pick[k]);
226 }
227 }
228}
229
230static void color_picker_helper_4ch_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
231 const dt_iop_roi_t *const roi, const int *const box,
232 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
233 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to,
234 const dt_iop_order_iccprofile_info_t *const profile)
235{
236 const int width = roi->width;
237
238 const size_t size = _box_size(box);
239 const size_t stride = 4 * (size_t)(box[2] - box[0]);
240 const size_t off_mul = 4 * width;
241 const size_t off_add = 4 * box[0];
242
243 const float w = 1.0f / (float)size;
244
245 // code path for small region, especially for color picker point mode
246 if(cst_to == IOP_CS_LCH)
247 {
248 for(size_t j = box[1]; j < box[3]; j++)
249 {
250 const size_t offset = j * off_mul + off_add;
251 _color_picker_lch(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
252 }
253 }
254 else if(cst_to == IOP_CS_HSL)
255 {
256 for(size_t j = box[1]; j < box[3]; j++)
257 {
258 const size_t offset = j * off_mul + off_add;
259 _color_picker_hsl(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
260 }
261 }
262 else if(cst_to == IOP_CS_JZCZHZ)
263 {
264 for(size_t j = box[1]; j < box[3]; j++)
265 {
266 const size_t offset = j * off_mul + off_add;
267 _color_picker_jzczhz(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride, profile);
268 }
269 }
270 else
271 {
272 for(size_t j = box[1]; j < box[3]; j++)
273 {
274 const size_t offset = j * off_mul + off_add;
275 _color_picker_rgb_or_lab(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
276 }
277 }
278}
279
280static void color_picker_helper_4ch_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
281 const dt_iop_roi_t *const roi, const int *const box,
282 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
283 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to,
284 const dt_iop_order_iccprofile_info_t *const profile)
285{
286 const int width = roi->width;
287
288 const size_t size = _box_size(box);
289 const size_t stride = 4 * (size_t)(box[2] - box[0]);
290 const size_t off_mul = 4 * width;
291 const size_t off_add = 4 * box[0];
292
293 const float w = 1.0f / (float)size;
294
295 const size_t numthreads = MAX(1, (size_t)omp_get_max_threads());
296
297 size_t allocsize;
298 float *const restrict mean = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
299 float *const restrict mmin = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
300 float *const restrict mmax = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
301
302 if(IS_NULL_PTR(mean) || IS_NULL_PTR(mmax) || IS_NULL_PTR(mmin))
303 goto error;
304
305 for(int n = 0; n < allocsize * numthreads; n++)
306 {
307 mean[n] = 0.0f;
308 mmin[n] = INFINITY;
309 mmax[n] = -INFINITY;
310 }
311
312 if(cst_to == IOP_CS_LCH)
313 {
315 {
316 float *const restrict tmean = dt_get_perthread(mean,allocsize);
317 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
318 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
320 for(size_t j = box[1]; j < box[3]; j++)
321 {
322 const size_t offset = j * off_mul + off_add;
323 _color_picker_lch(tmean, tmmin, tmmax, pixel + offset, w, stride);
324 }
325 }
326 }
327 else if(cst_to == IOP_CS_HSL)
328 {
330 {
331 float *const restrict tmean = dt_get_perthread(mean,allocsize);
332 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
333 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
335 for(size_t j = box[1]; j < box[3]; j++)
336 {
337 const size_t offset = j * off_mul + off_add;
338 _color_picker_hsl(tmean, tmmin, tmmax, pixel + offset, w, stride);
339 }
340 }
341 }
342 else if(cst_to == IOP_CS_JZCZHZ)
343 {
345 {
346 float *const restrict tmean = dt_get_perthread(mean,allocsize);
347 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
348 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
350 for(size_t j = box[1]; j < box[3]; j++)
351 {
352 const size_t offset = j * off_mul + off_add;
353 _color_picker_jzczhz(tmean, tmmin, tmmax, pixel + offset, w, stride, profile);
354 }
355 }
356 }
357 else
358 {
360 {
361 float *const restrict tmean = dt_get_perthread(mean,allocsize);
362 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
363 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
365 for(size_t j = box[1]; j < box[3]; j++)
366 {
367 const size_t offset = j * off_mul + off_add;
368 _color_picker_rgb_or_lab(tmean, tmmin, tmmax, pixel + offset, w, stride);
369 }
370 }
371 }
372
373 for(int n = 0; n < numthreads; n++)
374 {
375 for(int k = 0; k < 4; k++)
376 {
377 picked_color[k] += mean[allocsize * n + k];
378 picked_color_min[k] = fminf(picked_color_min[k], mmin[allocsize * n + k]);
379 picked_color_max[k] = fmaxf(picked_color_max[k], mmax[allocsize * n + k]);
380 }
381 }
382
383error:;
387}
388
389static void color_picker_helper_4ch(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
390 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
391 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max,
392 const dt_iop_colorspace_type_t cst_to,
393 const dt_iop_order_iccprofile_info_t *const profile)
394{
395 const size_t size = _box_size(box);
396
397 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
398 return color_picker_helper_4ch_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
399 picked_color_max, cst_to, profile);
400 else
401 return color_picker_helper_4ch_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max,
402 cst_to, profile);
403}
404
405static void color_picker_helper_4ch_converted_seq(const float *const pixel, const dt_iop_roi_t *const roi,
406 const int *const box, dt_aligned_pixel_t picked_color,
407 dt_aligned_pixel_t picked_color_min,
408 dt_aligned_pixel_t picked_color_max,
409 const dt_iop_colorspace_type_t picker_cst)
410{
411 const int width = roi->width;
412 const size_t size = _box_size(box);
413 const size_t stride = 4 * (size_t)(box[2] - box[0]);
414 const size_t off_mul = 4 * width;
415 const size_t off_add = 4 * box[0];
416 const float w = 1.0f / (float)size;
417
418 for(size_t j = box[1]; j < box[3]; j++)
419 {
420 const size_t offset = j * off_mul + off_add;
421 if(picker_cst == IOP_CS_HSL)
422 _color_picker_direct_hsl(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
423 else if(picker_cst == IOP_CS_LCH || picker_cst == IOP_CS_JZCZHZ)
424 _color_picker_direct_lch_or_jzczhz(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
425 else
426 _color_picker_rgb_or_lab(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
427 }
428}
429
430static void color_picker_helper_4ch_converted_parallel(const float *const pixel, const dt_iop_roi_t *const roi,
431 const int *const box, dt_aligned_pixel_t picked_color,
432 dt_aligned_pixel_t picked_color_min,
433 dt_aligned_pixel_t picked_color_max,
434 const dt_iop_colorspace_type_t picker_cst)
435{
436 const int width = roi->width;
437 const size_t size = _box_size(box);
438 const size_t stride = 4 * (size_t)(box[2] - box[0]);
439 const size_t off_mul = 4 * width;
440 const size_t off_add = 4 * box[0];
441 const float w = 1.0f / (float)size;
442 const size_t numthreads = dt_get_num_openmp_threads();
443
444 size_t allocsize;
445 float *const restrict mean = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
446 float *const restrict mmin = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
447 float *const restrict mmax = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
448
449 if(IS_NULL_PTR(mean) || IS_NULL_PTR(mmax) || IS_NULL_PTR(mmin))
450 goto error;
451
452 for(int n = 0; n < allocsize * numthreads; n++)
453 {
454 mean[n] = 0.0f;
455 mmin[n] = INFINITY;
456 mmax[n] = -INFINITY;
457 }
459 {
460 float *const restrict tmean = dt_get_perthread(mean,allocsize);
461 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
462 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
464 for(size_t j = box[1]; j < box[3]; j++)
465 {
466 const size_t offset = j * off_mul + off_add;
467 if(picker_cst == IOP_CS_HSL)
468 _color_picker_direct_hsl(tmean, tmmin, tmmax, pixel + offset, w, stride);
469 else if(picker_cst == IOP_CS_LCH || picker_cst == IOP_CS_JZCZHZ)
470 _color_picker_direct_lch_or_jzczhz(tmean, tmmin, tmmax, pixel + offset, w, stride);
471 else
472 _color_picker_rgb_or_lab(tmean, tmmin, tmmax, pixel + offset, w, stride);
473 }
474 }
475
476 for(int n = 0; n < numthreads; n++)
477 {
478 float *const restrict tmean = dt_get_bythread(mean,allocsize,n);
479 float *const restrict tmmin = dt_get_bythread(mmin,allocsize,n);
480 float *const restrict tmmax = dt_get_bythread(mmax,allocsize,n);
481
483 {
484 picked_color[k] += tmean[k];
485 picked_color_min[k] = fminf(picked_color_min[k], tmmin[k]);
486 picked_color_max[k] = fmaxf(picked_color_max[k], tmmax[k]);
487 }
488 }
489
490error:
494}
495
496static void color_picker_helper_4ch_converted(const float *const pixel, const dt_iop_roi_t *const roi,
497 const int *const box, dt_aligned_pixel_t picked_color,
498 dt_aligned_pixel_t picked_color_min,
499 dt_aligned_pixel_t picked_color_max,
500 const dt_iop_colorspace_type_t picker_cst)
501{
502 if(_box_size(box) > 10000)
503 color_picker_helper_4ch_converted_parallel(pixel, roi, box, picked_color, picked_color_min,
504 picked_color_max, picker_cst);
505 else
506 color_picker_helper_4ch_converted_seq(pixel, roi, box, picked_color, picked_color_min,
507 picked_color_max, picker_cst);
508}
509
510static void color_picker_helper_bayer_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
511 const dt_iop_roi_t *const roi, const int *const box,
512 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
513 dt_aligned_pixel_t picked_color_max)
514{
515 const int width = roi->width;
516 const uint32_t filters = dsc->filters;
517
518 uint32_t weights[4] = { 0u, 0u, 0u, 0u };
519
520 // code path for small region, especially for color picker point mode
521 for(size_t j = box[1]; j < box[3]; j++)
522 {
523 for(size_t i = box[0]; i < box[2]; i++)
524 {
525 const int c = FC(j + roi->y, i + roi->x, filters);
526 const size_t k = width * j + i;
527
528 const float v = pixel[k];
529
530 picked_color[c] += v;
531 picked_color_min[c] = fminf(picked_color_min[c], v);
532 picked_color_max[c] = fmaxf(picked_color_max[c], v);
533 weights[c]++;
534 }
535 }
536
537 // and finally normalize data. For bayer, there is twice as much green.
538 for(int c = 0; c < 4; c++)
539 {
540 picked_color[c] = weights[c] ? (picked_color[c] / (float)weights[c]) : 0.0f;
541 }
542}
543
544static void color_picker_helper_bayer_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
545 const dt_iop_roi_t *const roi, const int *const box,
546 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
547 dt_aligned_pixel_t picked_color_max)
548{
549 const int width = roi->width;
550 const uint32_t filters = dsc->filters;
551
552 uint32_t weights[4] = { 0u, 0u, 0u, 0u };
553
554 const size_t numthreads = dt_get_num_openmp_threads();
555
556 //TODO: convert to use dt_pixelpipe_cache_alloc_perthread
557 float *const msum = malloc(sizeof(float) * numthreads * 4);
558 float *const mmin = malloc(sizeof(float) * numthreads * 4);
559 float *const mmax = malloc(sizeof(float) * numthreads * 4);
560 uint32_t *const cnt = malloc(sizeof(uint32_t) * numthreads * 4);
561
562 if(IS_NULL_PTR(msum) || IS_NULL_PTR(mmin) || IS_NULL_PTR(mmax) || IS_NULL_PTR(cnt))
563 goto error;
564
565 for(int n = 0; n < 4 * numthreads; n++)
566 {
567 msum[n] = 0.0f;
568 mmin[n] = INFINITY;
569 mmax[n] = -INFINITY;
570 cnt[n] = 0u;
571 }
572 __OMP_PARALLEL__(num_threads(numthreads))
573 {
574 const int tnum = dt_get_thread_num();
575
576 float *const tsum = msum + 4 * tnum;
577 float *const tmmin = mmin + 4 * tnum;
578 float *const tmmax = mmax + 4 * tnum;
579 uint32_t *const tcnt = cnt + 4 * tnum;
580 __OMP_FOR__(collapse(2))
581 for(size_t j = box[1]; j < box[3]; j++)
582 {
583 for(size_t i = box[0]; i < box[2]; i++)
584 {
585 const int c = FC(j + roi->y, i + roi->x, filters);
586 const size_t k = width * j + i;
587
588 const float v = pixel[k];
589
590 tsum[c] += v;
591 tmmin[c] = fminf(tmmin[c], v);
592 tmmax[c] = fmaxf(tmmax[c], v);
593 tcnt[c]++;
594 }
595 }
596 }
597
598 for(int n = 0; n < numthreads; n++)
599 {
600 for(int c = 0; c < 4; c++)
601 {
602 picked_color[c] += msum[4 * n + c];
603 picked_color_min[c] = fminf(picked_color_min[c], mmin[4 * n + c]);
604 picked_color_max[c] = fmaxf(picked_color_max[c], mmax[4 * n + c]);
605 weights[c] += cnt[4 * n + c];
606 }
607 }
608
609 // and finally normalize data. For bayer, there is twice as much green.
610 for(int c = 0; c < 4; c++)
611 {
612 picked_color[c] = weights[c] ? (picked_color[c] / (float)weights[c]) : 0.0f;
613 }
614
615error:;
616 dt_free(cnt);
617 dt_free(mmax);
618 dt_free(mmin);
619 dt_free(msum);
620}
621
622static void color_picker_helper_bayer(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
623 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
624 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
625{
626 const size_t size = _box_size(box);
627
628 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
629 return color_picker_helper_bayer_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
630 picked_color_max);
631 else
632 return color_picker_helper_bayer_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
633}
634
635static void color_picker_helper_xtrans_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
636 const dt_iop_roi_t *const roi, const int *const box,
637 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
638 dt_aligned_pixel_t picked_color_max)
639{
640 const int width = roi->width;
641 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])dsc->xtrans;
642
643 uint32_t weights[3] = { 0u, 0u, 0u };
644
645 // code path for small region, especially for color picker point mode
646 for(size_t j = box[1]; j < box[3]; j++)
647 {
648 for(size_t i = box[0]; i < box[2]; i++)
649 {
650 const int c = FCxtrans(j, i, roi, xtrans);
651 const size_t k = width * j + i;
652
653 const float v = pixel[k];
654
655 picked_color[c] += v;
656 picked_color_min[c] = fminf(picked_color_min[c], v);
657 picked_color_max[c] = fmaxf(picked_color_max[c], v);
658 weights[c]++;
659 }
660 }
661
662 // and finally normalize data.
663 // X-Trans RGB weighting averages to 2:5:2 for each 3x3 cell
664 for(int c = 0; c < 3; c++)
665 {
666 picked_color[c] /= (float)weights[c];
667 }
668}
669
670static void color_picker_helper_xtrans_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
671 const dt_iop_roi_t *const roi, const int *const box,
672 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
673 dt_aligned_pixel_t picked_color_max)
674{
675 const int width = roi->width;
676 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])dsc->xtrans;
677
678 uint32_t weights[3] = { 0u, 0u, 0u };
679
680 const size_t numthreads = MAX(1, (size_t)omp_get_max_threads());
681
682 //TODO: convert to use dt_pixelpipe_cache_alloc_perthread
683 float *const mmin = malloc(sizeof(float) * numthreads * 3);
684 float *const msum = malloc(sizeof(float) * numthreads * 3);
685 float *const mmax = malloc(sizeof(float) * numthreads * 3);
686 uint32_t *const cnt = malloc(sizeof(uint32_t) * numthreads * 3);
687
688 if(IS_NULL_PTR(mmin) || IS_NULL_PTR(msum) || IS_NULL_PTR(mmax) || IS_NULL_PTR(cnt))
689 goto error;
690
691
692 for(int n = 0; n < 3 * numthreads; n++)
693 {
694 msum[n] = 0.0f;
695 mmin[n] = INFINITY;
696 mmax[n] = -INFINITY;
697 cnt[n] = 0u;
698 }
699 __OMP_PARALLEL__(num_threads(numthreads))
700 {
701 const int tnum = dt_get_thread_num();
702
703 float *const tsum = msum + 3 * tnum;
704 float *const tmmin = mmin + 3 * tnum;
705 float *const tmmax = mmax + 3 * tnum;
706 uint32_t *const tcnt = cnt + 3 * tnum;
707 __OMP_FOR__(collapse(2))
708 for(size_t j = box[1]; j < box[3]; j++)
709 {
710 for(size_t i = box[0]; i < box[2]; i++)
711 {
712 const int c = FCxtrans(j, i, roi, xtrans);
713 const size_t k = width * j + i;
714
715 const float v = pixel[k];
716
717 tsum[c] += v;
718 tmmin[c] = fminf(tmmin[c], v);
719 tmmax[c] = fmaxf(tmmax[c], v);
720 tcnt[c]++;
721 }
722 }
723 }
724
725 for(int n = 0; n < numthreads; n++)
726 {
727 for(int c = 0; c < 3; c++)
728 {
729 picked_color[c] += msum[3 * n + c];
730 picked_color_min[c] = fminf(picked_color_min[c], mmin[3 * n + c]);
731 picked_color_max[c] = fmaxf(picked_color_max[c], mmax[3 * n + c]);
732 weights[c] += cnt[3 * n + c];
733 }
734 }
735
736 // and finally normalize data.
737 // X-Trans RGB weighting averages to 2:5:2 for each 3x3 cell
738 for(int c = 0; c < 3; c++)
739 {
740 picked_color[c] /= (float)weights[c];
741 }
742
743error:;
744 dt_free(cnt);
745 dt_free(mmax);
746 dt_free(mmin);
747 dt_free(msum);
748}
749
750static void color_picker_helper_xtrans(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
751 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
752 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
753{
754 const size_t size = _box_size(box);
755
756 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
757 return color_picker_helper_xtrans_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
758 picked_color_max);
759 else
760 return color_picker_helper_xtrans_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
761}
762
763// picked_color, picked_color_min and picked_color_max should be aligned
764void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi,
765 const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
766 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t image_cst,
767 const dt_iop_colorspace_type_t picker_cst,
768 const dt_iop_order_iccprofile_info_t *const profile)
769{
770 dt_times_t start_time = { 0 }, end_time = { 0 };
771 if(dt_get_debug_flags() & DT_DEBUG_PERF) dt_get_times(&start_time);
772
773 if(dsc->channels == 4u)
774 {
775 // Denoise the image
776 size_t padded_size;
777 float *const restrict denoised = dt_pixelpipe_cache_alloc_align_float_cache(4 * roi->width * roi->height, 0);
778 float *converted = NULL;
779 float *const tempbuf = dt_pixelpipe_cache_alloc_perthread_float(4 * roi->width, &padded_size); // TODO: alloc in caller
780 if(IS_NULL_PTR(tempbuf) || IS_NULL_PTR(denoised))
781 goto error;
782
783 // blur without clipping negatives because Lab a and b channels can be legitimately negative
784 blur_2D_Bspline(pixel, denoised, tempbuf, roi->width, roi->height, 1, FALSE);
785
786 // Lab and RGB buffers convert to every picker space derived from either family; only crossing
787 // from one family to the other needs the profile.
788 const gboolean image_convertible = (image_cst == IOP_CS_LAB) || dt_iop_colorspace_is_rgb(image_cst);
789 const gboolean picker_convertible = _picker_cst_wants_lab(picker_cst) || dt_iop_colorspace_is_rgb(picker_cst)
790 || (picker_cst == IOP_CS_HSL) || (picker_cst == IOP_CS_JZCZHZ);
791 const gboolean crosses_family = (image_cst == IOP_CS_LAB) != _picker_cst_wants_lab(picker_cst);
792 const gboolean convertible
793 = image_convertible && picker_convertible && (!crosses_family || !IS_NULL_PTR(profile));
794
795 if(((image_cst == picker_cst) || (picker_cst == IOP_CS_NONE)))
796 color_picker_helper_4ch(dsc, denoised, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst, profile);
797 else if(convertible)
798 {
799 /* The picker samples module input/output buffers after the previous piece has written them.
800 When the requested picker colorspace differs from that buffer colorspace, we need a real
801 conversion before averaging. Falling back to raw channel statistics would silently report
802 Lab values as RGB (or the other way around), which makes module-side picker feedback wrong. */
803 converted = dt_pixelpipe_cache_alloc_align_float_cache(4 * roi->width * roi->height, 0);
804 if(IS_NULL_PTR(converted))
805 goto error;
806
807 _color_picker_convert_buffer(denoised, converted, (size_t)roi->width * roi->height, image_cst, picker_cst, profile);
808 color_picker_helper_4ch_converted(converted, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst);
809 }
810 else // This is a fallback, better than crashing as happens with monochromes
811 color_picker_helper_4ch(dsc, denoised, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst, profile);
812
813 error:;
817 }
818 else if(dsc->channels == 1u && dsc->filters != 0u && dsc->filters != 9u)
819 color_picker_helper_bayer(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
820 else if(dsc->channels == 1u && dsc->filters == 9u)
821 color_picker_helper_xtrans(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
822 else
824
826 {
827 dt_get_times(&end_time);
828 fprintf(stderr, "colorpicker stats reading took %.3f secs (%.3f CPU)\n",
829 end_time.clock - start_time.clock, end_time.user - start_time.user);
830 }
831}
832
833// clang-format off
834// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
835// vim: shiftwidth=2 expandtab tabstop=2 cindent
836// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
837// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define FALSE
Definition ashift_lsd.c:158
static void blur_2D_Bspline(const float *const restrict in, float *const restrict out, const size_t width, const size_t height)
Definition blurs.c:137
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_iop_colorspace_type_t
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
static void _color_picker_convert_buffer(const float *const restrict input, float *const restrict output, const size_t pixels, 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)
Convert a 4-channel sampling buffer into the picker colorspace.
static size_t _box_size(const int *const box)
static void color_picker_helper_4ch_converted_seq(const float *const pixel, const dt_iop_roi_t *const 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 picker_cst)
static void color_picker_helper_xtrans_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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)
static void color_picker_helper_4ch_converted(const float *const pixel, const dt_iop_roi_t *const 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 picker_cst)
static void rgb_to_JzCzhz(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t JzCzhz, const dt_iop_order_iccprofile_info_t *const profile)
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 _color_picker_hsl(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_4ch_converted_parallel(const float *const pixel, const dt_iop_roi_t *const 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 picker_cst)
static void color_picker_helper_bayer(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)
static void color_picker_helper_xtrans(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)
static void _color_picker_jzczhz(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width, const dt_iop_order_iccprofile_info_t *const profile)
static void _color_picker_direct_lch_or_jzczhz(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_bayer_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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)
static void _color_picker_direct_hsl(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void _color_picker_lch(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_4ch(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 cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void color_picker_helper_4ch_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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 cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void _color_picker_rgb_or_lab(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_bayer_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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)
static gboolean _picker_cst_wants_lab(const dt_iop_colorspace_type_t picker_cst)
Whether a picker colorspace derives from Lab (Lab, LCh) rather than from RGB (RGB,...
static void color_picker_helper_4ch_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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 cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void color_picker_helper_xtrans_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const 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)
The colour-profile struct and the maths over it: the derived matrix/LUT engine.
const float v
static dt_aligned_pixel_t rgb
static const float const float const float min
const float max
static dt_aligned_pixel_t XYZ_D65
static dt_aligned_pixel_t XYZ_D50
static dt_aligned_pixel_t JzAzBz
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:518
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition format.h:75
@ DT_DEBUG_PERF
Definition logging.h:55
int32_t dt_get_debug_flags(void)
Definition darktable.c:2085
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
#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
size_t size
Definition mipmap_cache.c:3
#define __OMP_FOR__(...)
Definition openmp.h:98
#define omp_get_max_threads()
Definition openmp.h:90
static int dt_get_thread_num()
Index of the calling thread within its parallel region, 0 outside one.
Definition openmp.h:129
#define __OMP_DECLARE_SIMD__(...)
Definition openmp.h:100
#define __OMP_PARALLEL__(...)
Definition openmp.h:94
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_get_bythread(buf, padsize, tnum)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_get_perthread(buf, padsize)
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_four_channels(_var,...)
Definition simd.h:89
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
uint8_t xtrans[6][6]
Definition format.h:99
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
float * lut_out[3]
Per-channel linear -> encoded tone curve, same convention as lut_in.
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
double clock
Definition times.h:39
double user
Definition times.h:40
#define MAX(a, b)
Definition thinplate.c:29
static void dt_get_times(dt_times_t *t)
Definition times.h:50