Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
channelmixerrgb_shared.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
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
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "bauhaus/bauhaus.h"
26#include "common/illuminants.h"
27#include "common/matrices.h"
31
32#include <float.h>
33#include <math.h>
34#include <string.h>
35
36#define DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 0.5773502691896258f
37
39{
40 while(angle <= -M_PI) angle += 2.f * (float)M_PI;
41 while(angle > M_PI) angle -= 2.f * (float)M_PI;
42 return angle;
43}
44
46{
47 while(angle <= -(float)M_PI_2) angle += (float)M_PI;
48 while(angle > (float)M_PI_2) angle -= (float)M_PI;
49 return angle;
50}
51
53{
54 if(stretch >= 1.f) return fminf(0.5f * (stretch + 1.f), 1.5f);
55 if(stretch >= -1.f) return stretch;
56 return fmaxf(0.5f * (stretch - 1.f), -1.5f);
57}
58
60{
61 if(slider >= 1.f) return 2.f * slider - 1.f;
62 if(slider >= -1.f) return slider;
63 return 2.f * slider + 1.f;
64}
65
67{
68 return atanf(fmaxf(amount, 0.f)) / DT_IOP_CHANNELMIXER_SHARED_SIMPLE_TAN_SCALE;
69}
70
72{
73 const float bounded = CLAMP(slider, 0.f, 0.999f);
75}
76
87
98
101{
102 primaries->achromatic_hue = dt_bauhaus_slider_get(widgets[0]) * (float)M_PI_2;
103 primaries->achromatic_purity = dt_bauhaus_slider_get(widgets[1]);
104 primaries->red_hue = dt_bauhaus_slider_get(widgets[2]) * (float)M_PI_2;
105 primaries->red_purity = dt_bauhaus_slider_get(widgets[3]);
106 primaries->green_hue = dt_bauhaus_slider_get(widgets[4]) * (float)M_PI_2;
107 primaries->green_purity = dt_bauhaus_slider_get(widgets[5]);
108 primaries->blue_hue = dt_bauhaus_slider_get(widgets[6]) * (float)M_PI_2;
109 primaries->blue_purity = dt_bauhaus_slider_get(widgets[7]);
110 primaries->gain = dt_bauhaus_slider_get(widgets[8]);
111}
112
114 GtkWidget *const widgets[9])
115{
116 dt_bauhaus_slider_set(widgets[0], CLAMP(primaries->achromatic_hue / (float)M_PI_2, -1.f, 1.f));
117 dt_bauhaus_slider_set(widgets[1], primaries->achromatic_purity);
118 dt_bauhaus_slider_set(widgets[2], CLAMP(primaries->red_hue / (float)M_PI_2, -1.f, 1.f));
119 dt_bauhaus_slider_set(widgets[3], primaries->red_purity);
120 dt_bauhaus_slider_set(widgets[4], CLAMP(primaries->green_hue / (float)M_PI_2, -1.f, 1.f));
121 dt_bauhaus_slider_set(widgets[5], primaries->green_purity);
122 dt_bauhaus_slider_set(widgets[6], CLAMP(primaries->blue_hue / (float)M_PI_2, -1.f, 1.f));
123 dt_bauhaus_slider_set(widgets[7], primaries->blue_purity);
124 dt_bauhaus_slider_set(widgets[8], primaries->gain);
125}
126
128{
129 return normalize[0] && normalize[1] && normalize[2];
130}
131
132gboolean dt_iop_channelmixer_shared_get_matrix(const float rows[3][3], const gboolean normalize[3],
133 const gboolean force_normalize, float M[3][3])
134{
135 for(int row = 0; row < 3; row++)
136 {
137 float sum = 1.f;
138 if(normalize[row] || force_normalize)
139 {
140 sum = rows[row][0] + rows[row][1] + rows[row][2];
141 if(sum == 0.f) return FALSE;
142 }
143
144 for(int col = 0; col < 3; col++) M[row][col] = rows[row][col] / sum;
145 }
146
147 return TRUE;
148}
149
150void dt_iop_channelmixer_shared_set_matrix(float rows[3][3], const float M[3][3])
151{
152 for(int row = 0; row < 3; row++)
153 for(int col = 0; col < 3; col++)
154 rows[row][col] = M[row][col];
155}
156
157void dt_iop_channelmixer_shared_mul3x3(const float A[3][3], const float B[3][3], float C[3][3])
158{
159 for(int row = 0; row < 3; row++)
160 for(int col = 0; col < 3; col++)
161 {
162 C[row][col] = 0.f;
163 for(int k = 0; k < 3; k++) C[row][col] += A[row][k] * B[k][col];
164 }
165}
166
167static void _mixer_to_chroma_basis(const float M[3][3], float B[3][3])
168{
169 static const float P[3][3]
170 = { { 0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
171 { -0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
172 { 0.f, -0.8164965809277261f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 } };
173 float PTM[3][3] = { { 0.f } };
174 float PT[3][3] = { { 0.f } };
175
176 for(int row = 0; row < 3; row++)
177 for(int col = 0; col < 3; col++)
178 PT[row][col] = P[col][row];
179
182}
183
184static void _mixer_from_chroma_basis(const float B[3][3], float M[3][3])
185{
186 static const float P[3][3]
187 = { { 0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
188 { -0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
189 { 0.f, -0.8164965809277261f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 } };
190 float temp[3][3] = { { 0.f } };
191 float PT[3][3] = { { 0.f } };
192
193 for(int row = 0; row < 3; row++)
194 for(int col = 0; col < 3; col++)
195 PT[row][col] = P[col][row];
196
199}
200
203{
204 float B[3][3] = { { 0.f } };
206
207 const float a = B[0][0];
208 const float b = B[0][1];
209 const float c = B[1][0];
210 const float d = B[1][1];
211 const float theta = dt_iop_channelmixer_shared_wrap_pi(atan2f(c - b, a + d));
212 const float ctheta = cosf(theta);
213 const float stheta = sinf(theta);
214
215 const float s00 = ctheta * a + stheta * c;
216 const float s01 = ctheta * b + stheta * d;
217 const float s11 = -stheta * b + ctheta * d;
218 const float diff = s00 - s11;
219 const float psi = dt_iop_channelmixer_shared_wrap_half_pi(0.5f * atan2f(2.f * s01, diff));
220 const float radius = hypotf(0.5f * diff, s01);
221 const float trace = s00 + s11;
222
223 simple->theta = theta;
224 simple->psi = psi;
225 simple->stretch_1 = 0.5f * trace + radius;
226 simple->stretch_2 = 0.5f * trace - radius;
227 simple->coupling_amount = hypotf(B[2][0], B[2][1]);
228 simple->coupling_hue = simple->coupling_amount > 0.f
229 ? dt_iop_channelmixer_shared_wrap_pi(atan2f(B[2][1], B[2][0]))
230 : 0.f;
231}
232
234 float M[3][3])
235{
236 const float ctheta = cosf(simple->theta);
237 const float stheta = sinf(simple->theta);
238 const float cpsi = cosf(simple->psi);
239 const float spsi = sinf(simple->psi);
240 const float coupling_0 = simple->coupling_amount * cosf(simple->coupling_hue);
241 const float coupling_1 = simple->coupling_amount * sinf(simple->coupling_hue);
242
243 const float s00 = simple->stretch_1 * cpsi * cpsi + simple->stretch_2 * spsi * spsi;
244 const float s01 = (simple->stretch_1 - simple->stretch_2) * cpsi * spsi;
245 const float s11 = simple->stretch_1 * spsi * spsi + simple->stretch_2 * cpsi * cpsi;
246 const float B[3][3]
247 = { { ctheta * s00 - stheta * s01, ctheta * s01 - stheta * s11, 0.f },
248 { stheta * s00 + ctheta * s01, stheta * s01 + ctheta * s11, 0.f },
249 { coupling_0, coupling_1, 1.f } };
250
252}
253
254float dt_iop_channelmixer_shared_roundtrip_error(const float M[3][3], const float roundtrip[3][3])
255{
256 float max_error = 0.f;
257 for(int row = 0; row < 3; row++)
258 for(int col = 0; col < 3; col++)
259 max_error = fmaxf(max_error, fabsf(M[row][col] - roundtrip[row][col]));
260
261 return max_error;
262}
263
282
284{
285 dt_aligned_pixel_t D50 = { 0.f };
286
287 switch(basis)
288 {
291 break;
294 break;
297 break;
299 default:
301 break;
302 }
303
304 for(int c = 0; c < 3; c++) white[c] = D50[c];
305}
306
307static inline float _affine_sum3(const float vector[3])
308{
309 return vector[0] + vector[1] + vector[2];
310}
311
312static gboolean _affine_normalize(const float vector[3], float normalized[3])
313{
314 const float sum = _affine_sum3(vector);
315 if(fabsf(sum) < DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS) return FALSE;
316
317 for(int c = 0; c < 3; c++) normalized[c] = vector[c] / sum;
318 return TRUE;
319}
320
321static void _affine_project_difference(const float difference[3], float uv[2])
322{
323 uv[0] = 0.7071067811865475f * (difference[0] - difference[1]);
324 uv[1] = 0.4082482904638631f * (difference[0] + difference[1]) - 0.8164965809277261f * difference[2];
325}
326
327static void _affine_unproject_difference(const float uv[2], float difference[3])
328{
329 difference[0] = 0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
330 difference[1] = -0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
331 difference[2] = -0.8164965809277261f * uv[1];
332}
333
334static void _rotate_2d(const float vector[2], const float angle, float rotated[2])
335{
336 const float cosine = cosf(angle);
337 const float sine = sinf(angle);
338 rotated[0] = cosine * vector[0] - sine * vector[1];
339 rotated[1] = sine * vector[0] + cosine * vector[1];
340}
341
342static float _intersect_affine_ray_segment(const float direction[2], const float first[2], const float second[2])
343{
344 const float edge_x = second[0] - first[0];
345 const float edge_y = second[1] - first[1];
346 const float denominator = direction[0] * edge_y - direction[1] * edge_x;
347 if(fabsf(denominator) < DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS) return FLT_MAX;
348
349 const float t = (first[0] * edge_y - first[1] * edge_x) / denominator;
350 const float u = (first[0] * direction[1] - first[1] * direction[0]) / denominator;
351 if(t >= 0.f && u >= 0.f && u <= 1.f) return t;
352 return FLT_MAX;
353}
354
355static float _affine_distance_to_edge(const float direction[2], const float reference_primaries[3][2])
356{
357 float distance_to_edge = FLT_MAX;
358
359 for(int i = 0; i < 3; i++)
360 {
361 const int next = i == 2 ? 0 : i + 1;
362 const float distance = _intersect_affine_ray_segment(direction, reference_primaries[i],
363 reference_primaries[next]);
364 if(distance < distance_to_edge) distance_to_edge = distance;
365 }
366
367 return distance_to_edge;
368}
369
371 float white_normalized[3], float reference_primaries[3][2])
372{
373 const float identity[3][3] = { { 1.f, 0.f, 0.f },
374 { 0.f, 1.f, 0.f },
375 { 0.f, 0.f, 1.f } };
376 float white[3] = { 0.f };
377
378 _primaries_reference_white(basis, white);
379 if(!_affine_normalize(white, white_normalized)) return FALSE;
380
381 for(int i = 0; i < 3; i++)
382 {
383 float difference[3] = { identity[i][0] - white_normalized[0],
384 identity[i][1] - white_normalized[1],
385 identity[i][2] - white_normalized[2] };
386 _affine_project_difference(difference, reference_primaries[i]);
387 }
388
389 return TRUE;
390}
391
392static gboolean _affine_point_from_polar(const float white_normalized[3], const float reference_primaries[3][2],
393 const int reference_index, const float hue, const float purity,
394 float point_normalized[3])
395{
396 const float *const reference = reference_primaries[reference_index];
397 const float radius = hypotf(reference[0], reference[1]);
399
400 const float direction_reference[2] = { reference[0] / radius, reference[1] / radius };
401 float direction[2] = { 0.f };
402 float difference[3] = { 0.f };
403
404 _rotate_2d(direction_reference, hue, direction);
405
406 const float distance_to_edge = _affine_distance_to_edge(direction, reference_primaries);
407 if(distance_to_edge == FLT_MAX) return FALSE;
408
409 const float uv[2] = { purity * distance_to_edge * direction[0],
410 purity * distance_to_edge * direction[1] };
411 _affine_unproject_difference(uv, difference);
412
413 for(int c = 0; c < 3; c++) point_normalized[c] = white_normalized[c] + difference[c];
414 return TRUE;
415}
416
417static gboolean _affine_polar_from_point(const float white_normalized[3], const float reference_primaries[3][2],
418 const int reference_index, const float point_normalized[3], float *hue,
419 float *purity)
420{
421 const float *const reference = reference_primaries[reference_index];
422 const float reference_angle = atan2f(reference[1], reference[0]);
423 float difference[3] = { point_normalized[0] - white_normalized[0],
424 point_normalized[1] - white_normalized[1],
425 point_normalized[2] - white_normalized[2] };
426 float uv[2] = { 0.f };
427
428 _affine_project_difference(difference, uv);
429
430 const float radius = hypotf(uv[0], uv[1]);
432 {
433 *hue = 0.f;
434 *purity = 0.f;
435 return TRUE;
436 }
437
438 const float direction[2] = { uv[0] / radius, uv[1] / radius };
439 const float distance_to_edge = _affine_distance_to_edge(direction, reference_primaries);
440 if(distance_to_edge == FLT_MAX || distance_to_edge < DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS) return FALSE;
441
442 *hue = dt_iop_channelmixer_shared_wrap_pi(atan2f(direction[1], direction[0]) - reference_angle);
443 *purity = radius / distance_to_edge;
444 return TRUE;
445}
446
449 float M[3][3])
450{
451 float white_reference[3] = { 0.f };
452 float reference_primaries[3][2] = { { 0.f } };
453 float white_reference_normalized[3] = { 0.f };
454 float custom_white_normalized[3] = { 0.f };
455 float custom_primaries[3][3] = { { 0.f } };
456 dt_colormatrix_t normalized_inverse = { { 0.f } };
457 dt_colormatrix_t normalized_primaries = { { 0.f } };
458
459 _primaries_reference_white(basis, white_reference);
460 const float white_reference_sum = _affine_sum3(white_reference);
461 if(fabsf(white_reference_sum) < DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS) return FALSE;
462
463 if(!_build_affine_simplex(basis, white_reference_normalized, reference_primaries)) return FALSE;
464
465 if(!_affine_point_from_polar(white_reference_normalized, reference_primaries, 0, primaries->achromatic_hue,
466 primaries->achromatic_purity, custom_white_normalized))
467 return FALSE;
468
469 if(!_affine_point_from_polar(white_reference_normalized, reference_primaries, 0, primaries->red_hue,
470 primaries->red_purity, custom_primaries[0]))
471 return FALSE;
472 if(!_affine_point_from_polar(white_reference_normalized, reference_primaries, 1, primaries->green_hue,
473 primaries->green_purity, custom_primaries[1]))
474 return FALSE;
475 if(!_affine_point_from_polar(white_reference_normalized, reference_primaries, 2, primaries->blue_hue,
476 primaries->blue_purity, custom_primaries[2]))
477 return FALSE;
478
479 for(int row = 0; row < 3; row++)
480 for(int col = 0; col < 3; col++)
481 normalized_primaries[row][col] = custom_primaries[col][row];
482
483 if(mat3SSEinv(normalized_inverse, normalized_primaries)) return FALSE;
484
485 const float white_gain = primaries->gain * white_reference_sum;
486 const dt_aligned_pixel_t custom_white = { white_gain * custom_white_normalized[0],
487 white_gain * custom_white_normalized[1],
488 white_gain * custom_white_normalized[2],
489 0.f };
490 dt_aligned_pixel_t column_scales = { 0.f };
491 dt_apply_transposed_color_matrix(custom_white, normalized_inverse, column_scales);
492
493 for(int col = 0; col < 3; col++)
494 for(int row = 0; row < 3; row++)
495 M[row][col] = column_scales[col] * custom_primaries[col][row];
496
497 return TRUE;
498}
499
501 const float M[3][3],
503{
504 dt_colormatrix_t padded = { { 0.f } };
505 dt_colormatrix_t inverse = { { 0.f } };
506 float white_reference[3] = { 0.f };
507 float white_reference_normalized[3] = { 0.f };
508 float reference_primaries[3][2] = { { 0.f } };
509 float custom_white[3] = { 0.f };
510 float custom_white_normalized[3] = { 0.f };
511 float custom_primary_normalized[3] = { 0.f };
512
513 for(int row = 0; row < 3; row++)
514 for(int col = 0; col < 3; col++)
515 padded[row][col] = M[row][col];
516 if(mat3SSEinv(inverse, padded)) return FALSE;
517
518 _primaries_reference_white(basis, white_reference);
519 const float white_reference_sum = _affine_sum3(white_reference);
520 if(fabsf(white_reference_sum) < DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS) return FALSE;
521
522 if(!_build_affine_simplex(basis, white_reference_normalized, reference_primaries)) return FALSE;
523
524 for(int row = 0; row < 3; row++) custom_white[row] = M[row][0] + M[row][1] + M[row][2];
525 if(!_affine_normalize(custom_white, custom_white_normalized)) return FALSE;
526
527 primaries->gain = _affine_sum3(custom_white) / white_reference_sum;
528 if(!_affine_polar_from_point(white_reference_normalized, reference_primaries, 0, custom_white_normalized,
529 &primaries->achromatic_hue, &primaries->achromatic_purity))
530 return FALSE;
531
532 for(int primary = 0; primary < 3; primary++)
533 {
534 const float column[3] = { M[0][primary], M[1][primary], M[2][primary] };
535 if(!_affine_normalize(column, custom_primary_normalized)) return FALSE;
536
537 float *hue = primary == 0 ? &primaries->red_hue : primary == 1 ? &primaries->green_hue : &primaries->blue_hue;
538 float *purity = primary == 0 ? &primaries->red_purity
539 : primary == 1 ? &primaries->green_purity
540 : &primaries->blue_purity;
541
542 if(!_affine_polar_from_point(white_reference_normalized, reference_primaries, primary,
543 custom_primary_normalized, hue, purity))
544 return FALSE;
545 }
546
547 return TRUE;
548}
549
551 float source[3])
552{
553 static const float P[3][3]
554 = { { 0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
555 { -0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
556 { 0.f, -0.8164965809277261f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 } };
557 const float basis[3]
561 : 0.f,
565 : 0.f,
566 1.f };
567
568 for(int row = 0; row < 3; row++)
569 {
570 source[row] = 0.f;
571 for(int col = 0; col < 3; col++) source[row] += P[row][col] * basis[col];
572 }
573}
574
576{
577 const float max_RGB = fmaxf(fmaxf(linear_display_rgb[0], linear_display_rgb[1]), linear_display_rgb[2]);
578 if(max_RGB > 1.f)
579 for(int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c] / max_RGB, 0.f);
580 else
581 for(int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c], 0.f);
582}
583
585 const dt_iop_order_iccprofile_info_t *const work_profile,
586 const dt_iop_order_iccprofile_info_t *const display_profile,
587 dt_aligned_pixel_t display_rgb)
588{
589 if(!IS_NULL_PTR(work_profile) && !IS_NULL_PTR(display_profile))
590 {
591 dt_aligned_pixel_t XYZ = { 0.f };
592 dt_aligned_pixel_t linear_display_rgb = { 0.f };
593
594 dt_ioppr_rgb_matrix_to_xyz(work_rgb, XYZ, work_profile->matrix_in_transposed, work_profile->lut_in,
595 work_profile->unbounded_coeffs_in, work_profile->lutsize,
596 work_profile->nonlinearlut);
597 dt_apply_transposed_color_matrix(XYZ, display_profile->matrix_out_transposed, linear_display_rgb);
598 _normalize_linear_display_rgb(linear_display_rgb);
599
600 if(display_profile->nonlinearlut)
601 _apply_trc(linear_display_rgb, display_rgb, display_profile->lut_out, display_profile->unbounded_coeffs_out,
602 display_profile->lutsize);
603 else
604 for(int c = 0; c < 4; c++) display_rgb[c] = linear_display_rgb[c];
605 }
606 else
607 {
608 for(int c = 0; c < 4; c++) display_rgb[c] = work_rgb[c];
610 }
611
612 for(int c = 0; c < 3; c++) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
613}
614
616 const dt_iop_order_iccprofile_info_t *const work_profile,
617 const dt_iop_order_iccprofile_info_t *const display_profile,
618 float display_rgb[3])
619{
620 dt_aligned_pixel_t work_rgb = { module_color[0], module_color[1], module_color[2], 0.f };
621 dt_aligned_pixel_t display = { 0.f };
622
624 {
625 dt_aligned_pixel_t LMS = { module_color[0], module_color[1], module_color[2], 0.f };
626 convert_any_LMS_to_RGB(LMS, work_rgb, adaptation);
627 }
628
629 dt_iop_channelmixer_shared_work_rgb_to_display(work_rgb, work_profile, display_profile, display);
630 for(int c = 0; c < 3; c++) display_rgb[c] = display[c];
631}
632
633void dt_iop_channelmixer_shared_paint_temperature_slider(GtkWidget *const widget, const float temperature_min,
634 const float temperature_max)
635{
637
638 const float temp_range = temperature_max - temperature_min;
639 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
640 {
641 const float stop = (float)i / (float)(DT_BAUHAUS_SLIDER_MAX_STOPS - 1);
642 const float temperature = temperature_min + stop * temp_range;
643 dt_aligned_pixel_t RGB = { 0.f };
644
645 illuminant_CCT_to_RGB(temperature, RGB);
646 dt_bauhaus_slider_set_stop(widget, stop, RGB[0], RGB[1], RGB[2]);
647 }
648
649 gtk_widget_queue_draw(widget);
650}
651
653 const dt_iop_order_iccprofile_info_t *const work_profile,
654 const dt_iop_order_iccprofile_info_t *const display_profile,
655 GtkWidget *const widget, const float stop, const float c,
656 const float r, const float g, const float b)
657{
658 const float module_color[3]
659 = { 0.5f * (c * r + 1.f - r), 0.5f * (c * g + 1.f - g), 0.5f * (c * b + 1.f - b) };
660 float display_rgb[3] = { 0.f };
661
662 dt_iop_channelmixer_shared_module_color_to_display(module_color, adaptation, work_profile, display_profile,
663 display_rgb);
664 dt_bauhaus_slider_set_stop(widget, stop, display_rgb[0], display_rgb[1], display_rgb[2]);
665}
666
668 const dt_iop_order_iccprofile_info_t *const work_profile,
669 const dt_iop_order_iccprofile_info_t *const display_profile,
670 const float r, const float g, const float b,
671 const gboolean normalize, const float row[3],
672 GtkWidget *const widgets[3])
673{
674 dt_aligned_pixel_t RGB = { row[0], row[1], row[2], 0.f };
675
676 if(normalize)
677 {
678 const float sum = RGB[0] + RGB[1] + RGB[2];
679 if(sum != 0.f)
680 for(int c = 0; c < 3; c++) RGB[c] /= sum;
681 }
682
683 for(int widget = 0; widget < 3; widget++) dt_bauhaus_slider_clear_stops(widgets[widget]);
684
685 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
686 {
687 const float stop = (float)i / (float)(DT_BAUHAUS_SLIDER_MAX_STOPS - 1);
688 const float x_r = dt_bauhaus_slider_get_hard_min(widgets[0])
689 + stop * (dt_bauhaus_slider_get_hard_max(widgets[0]) - dt_bauhaus_slider_get_hard_min(widgets[0]));
690 const float x_g = dt_bauhaus_slider_get_hard_min(widgets[1])
691 + stop * (dt_bauhaus_slider_get_hard_max(widgets[1]) - dt_bauhaus_slider_get_hard_min(widgets[1]));
692 const float x_b = dt_bauhaus_slider_get_hard_min(widgets[2])
693 + stop * (dt_bauhaus_slider_get_hard_max(widgets[2]) - dt_bauhaus_slider_get_hard_min(widgets[2]));
694
695 _paint_RGB_slider_stop(adaptation, work_profile, display_profile, widgets[0], stop, x_r + RGB[1] + RGB[2],
696 r, g, b);
697 _paint_RGB_slider_stop(adaptation, work_profile, display_profile, widgets[1], stop, RGB[0] + x_g + RGB[2],
698 r, g, b);
699 _paint_RGB_slider_stop(adaptation, work_profile, display_profile, widgets[2], stop, RGB[0] + RGB[1] + x_b,
700 r, g, b);
701 }
702
703 for(int widget = 0; widget < 3; widget++) gtk_widget_queue_draw(widgets[widget]);
704}
705
706static void _shared_paint_probe_matrix(const float source[3], const float M[3][3], float module_color[3])
707{
708 dt_colormatrix_t mix = { { 0.f } };
709 dt_aligned_pixel_t padded_source = { source[0], source[1], source[2], 0.f };
710 dt_aligned_pixel_t padded_color = { 0.f };
711
712 for(int row = 0; row < 3; row++)
713 for(int col = 0; col < 3; col++)
714 mix[row][col] = M[row][col];
715
716 dot_product(padded_source, mix, padded_color);
717 for(int c = 0; c < 3; c++) module_color[c] = padded_color[c];
718}
719
733static void _shared_simple_hue_probe(const float hue, float module_color[3])
734{
735 static const float P[3][3]
736 = { { 0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
737 { -0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
738 { 0.f, -0.8164965809277261f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 } };
739 const float basis[3]
742
743 for(int row = 0; row < 3; row++)
744 {
745 module_color[row] = 0.f;
746 for(int col = 0; col < 3; col++) module_color[row] += P[row][col] * basis[col];
747 }
748}
749
751 const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile,
752 const dt_iop_order_iccprofile_info_t *const display_profile,
753 const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
754{
755 static const dt_iop_channelmixer_shared_simple_probe_t probes[6]
762
763 for(int widget = 0; widget < 6; widget++) dt_bauhaus_slider_clear_stops(widgets[widget]);
764
765 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
766 {
767 const float stop = (float)i / (float)(DT_BAUHAUS_SLIDER_MAX_STOPS - 1);
768 const float slider = 2.f * stop - 1.f;
769 const float stretch_slider = 3.f * stop - 1.5f;
770
771 for(int widget = 0; widget < 6; widget++)
772 {
773 dt_iop_channelmixer_shared_simple_params_t probe_simple = *simple;
774 float source[3] = { 0.f };
775 float M[3][3] = { { 0.f } };
776 float module_color[3] = { 0.f };
777 float display_rgb[3] = { 0.f };
778 float coupling_hue = simple->coupling_hue;
779
780 switch(widget)
781 {
782 case 0:
783 probe_simple.theta = slider * (float)M_PI;
784 break;
785 case 1:
786 probe_simple.psi = slider * (float)M_PI_2;
787 break;
788 case 2:
789 probe_simple.stretch_1 = dt_iop_channelmixer_shared_decode_simple_stretch(stretch_slider);
790 break;
791 case 3:
792 probe_simple.stretch_2 = dt_iop_channelmixer_shared_decode_simple_stretch(stretch_slider);
793 break;
794 case 4:
796 break;
797 case 5:
798 probe_simple.coupling_hue = slider * (float)M_PI;
799 coupling_hue = probe_simple.coupling_hue;
800 break;
801 default:
802 break;
803 }
804
806 if(widget < 4)
808 else
809 {
810 static const float P[3][3]
811 = { { 0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
812 { -0.7071067811865475f, 0.4082482904638631f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 },
813 { 0.f, -0.8164965809277261f, DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 } };
814 const float basis[3]
815 = { DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE * cosf(coupling_hue),
816 DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE * sinf(coupling_hue), 1.f };
817
818 for(int row = 0; row < 3; row++)
819 {
820 source[row] = 0.f;
821 for(int col = 0; col < 3; col++) source[row] += P[row][col] * basis[col];
822 }
823 }
824
825 _shared_paint_probe_matrix(source, M, module_color);
826 dt_iop_channelmixer_shared_module_color_to_display(module_color, adaptation, work_profile, display_profile,
827 display_rgb);
828
829 if((widget == 0 || widget == 5)
830 && fabsf(fmaxf(fmaxf(display_rgb[0], display_rgb[1]), display_rgb[2])
831 - fminf(fminf(display_rgb[0], display_rgb[1]), display_rgb[2])) < 0.05f)
832 {
833 const float hue = widget == 0 ? probe_simple.theta : coupling_hue;
834 _shared_simple_hue_probe(hue, module_color);
835 dt_iop_channelmixer_shared_module_color_to_display(module_color, adaptation, work_profile, display_profile,
836 display_rgb);
837 }
838
839 dt_bauhaus_slider_set_stop(widgets[widget], stop, display_rgb[0], display_rgb[1], display_rgb[2]);
840 }
841 }
842
843 for(int widget = 0; widget < 6; widget++) gtk_widget_queue_draw(widgets[widget]);
844}
845
846static void _shared_primaries_probe_color(const float M[3][3], const int widget_index, float module_color[3])
847{
848 if(widget_index <= 1 || widget_index == 8)
849 {
850 for(int row = 0; row < 3; row++)
851 module_color[row] = M[row][0] + M[row][1] + M[row][2];
852 return;
853 }
854
855 const int column = widget_index <= 3 ? 0 : widget_index <= 5 ? 1 : 2;
856 for(int row = 0; row < 3; row++) module_color[row] = M[row][column];
857}
858
860 const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile,
861 const dt_iop_order_iccprofile_info_t *const display_profile,
863 const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
864{
865 for(int widget = 0; widget < 9; widget++) dt_bauhaus_slider_clear_stops(widgets[widget]);
866
867 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
868 {
869 const float stop = (float)i / (float)(DT_BAUHAUS_SLIDER_MAX_STOPS - 1);
870
871 for(int widget = 0; widget < 9; widget++)
872 {
874 float M[3][3] = { { 0.f } };
875 float module_color[3] = { 0.f };
876 float display_rgb[3] = { 0.f };
877 const float hard_min = dt_bauhaus_slider_get_hard_min(widgets[widget]);
878 const float hard_max = dt_bauhaus_slider_get_hard_max(widgets[widget]);
879 const float value = hard_min + stop * (hard_max - hard_min);
880
881 switch(widget)
882 {
883 case 0:
884 probe.achromatic_hue = value * (float)M_PI_2;
885 break;
886 case 1:
887 probe.achromatic_purity = value;
888 break;
889 case 2:
890 probe.red_hue = value * (float)M_PI_2;
891 break;
892 case 3:
893 probe.red_purity = value;
894 break;
895 case 4:
896 probe.green_hue = value * (float)M_PI_2;
897 break;
898 case 5:
899 probe.green_purity = value;
900 break;
901 case 6:
902 probe.blue_hue = value * (float)M_PI_2;
903 break;
904 case 7:
905 probe.blue_purity = value;
906 break;
907 case 8:
908 probe.gain = value;
909 break;
910 default:
911 break;
912 }
913
914 if(!dt_iop_channelmixer_shared_primaries_to_matrix(basis, &probe, M)) continue;
915
916 _shared_primaries_probe_color(M, widget, module_color);
917 dt_iop_channelmixer_shared_module_color_to_display(module_color, adaptation, work_profile, display_profile,
918 display_rgb);
919 dt_bauhaus_slider_set_stop(widgets[widget], stop, display_rgb[0], display_rgb[1], display_rgb[2]);
920 }
921 }
922
923 for(int widget = 0; widget < 9; widget++) gtk_widget_queue_draw(widgets[widget]);
924}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
Definition bauhaus.c:1838
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:1846
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:2859
float dt_bauhaus_slider_get_hard_min(GtkWidget *widget)
Definition bauhaus.c:1226
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:2882
float dt_bauhaus_slider_get_hard_max(GtkWidget *widget)
Definition bauhaus.c:1251
#define DT_BAUHAUS_SLIDER_MAX_STOPS
Definition bauhaus.h:71
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
Definition blurs.c:347
void dt_iop_channelmixer_shared_module_color_to_display(const float module_color[3], const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, float display_rgb[3])
Definition channelmixerrgb_shared.c:615
void dt_iop_channelmixer_shared_paint_row_sliders(dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const float r, const float g, const float b, const gboolean normalize, const float row[3], GtkWidget *const widgets[3])
Definition channelmixerrgb_shared.c:667
void dt_iop_channelmixer_shared_paint_primaries_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
Definition channelmixerrgb_shared.c:859
static void _shared_paint_probe_matrix(const float source[3], const float M[3][3], float module_color[3])
Definition channelmixerrgb_shared.c:706
static void _shared_primaries_probe_color(const float M[3][3], const int widget_index, float module_color[3])
Definition channelmixerrgb_shared.c:846
static void _affine_project_difference(const float difference[3], float uv[2])
Definition channelmixerrgb_shared.c:321
void dt_iop_channelmixer_shared_simple_from_sliders(GtkWidget *const widgets[6], dt_iop_channelmixer_shared_simple_params_t *simple)
Definition channelmixerrgb_shared.c:77
void dt_iop_channelmixer_shared_paint_temperature_slider(GtkWidget *const widget, const float temperature_min, const float temperature_max)
Definition channelmixerrgb_shared.c:633
static float _affine_distance_to_edge(const float direction[2], const float reference_primaries[3][2])
Definition channelmixerrgb_shared.c:355
void dt_iop_channelmixer_shared_primaries_to_sliders(const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
Definition channelmixerrgb_shared.c:113
void dt_iop_channelmixer_shared_simple_probe_source(const dt_iop_channelmixer_shared_simple_probe_t probe, float source[3])
Definition channelmixerrgb_shared.c:550
float dt_iop_channelmixer_shared_decode_simple_stretch(const float slider)
Definition channelmixerrgb_shared.c:59
void dt_iop_channelmixer_shared_simple_to_matrix(const dt_iop_channelmixer_shared_simple_params_t *const simple, float M[3][3])
Definition channelmixerrgb_shared.c:233
static gboolean _affine_point_from_polar(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float hue, const float purity, float point_normalized[3])
Definition channelmixerrgb_shared.c:392
static void _mixer_from_chroma_basis(const float B[3][3], float M[3][3])
Definition channelmixerrgb_shared.c:184
static void _mixer_to_chroma_basis(const float M[3][3], float B[3][3])
Definition channelmixerrgb_shared.c:167
static void _paint_RGB_slider_stop(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, GtkWidget *const widget, const float stop, const float c, const float r, const float g, const float b)
Definition channelmixerrgb_shared.c:652
float dt_iop_channelmixer_shared_encode_simple_coupling_amount(const float amount)
Definition channelmixerrgb_shared.c:66
void dt_iop_channelmixer_shared_simple_to_sliders(const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
Definition channelmixerrgb_shared.c:88
float dt_iop_channelmixer_shared_decode_simple_coupling_amount(const float slider)
Definition channelmixerrgb_shared.c:71
dt_iop_channelmixer_shared_primaries_basis_t dt_iop_channelmixer_shared_primaries_basis_from_adaptation(const dt_adaptation_t adaptation)
Definition channelmixerrgb_shared.c:265
float dt_iop_channelmixer_shared_encode_simple_stretch(const float stretch)
Definition channelmixerrgb_shared.c:52
void dt_iop_channelmixer_shared_work_rgb_to_display(const dt_aligned_pixel_t work_rgb, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, dt_aligned_pixel_t display_rgb)
Definition channelmixerrgb_shared.c:584
static void _shared_simple_hue_probe(const float hue, float module_color[3])
Build a stable hue cue in the fixed chroma basis for hue-like sliders.
Definition channelmixerrgb_shared.c:733
void dt_iop_channelmixer_shared_set_matrix(float rows[3][3], const float M[3][3])
Definition channelmixerrgb_shared.c:150
static void _normalize_linear_display_rgb(dt_aligned_pixel_t linear_display_rgb)
Definition channelmixerrgb_shared.c:575
static float _intersect_affine_ray_segment(const float direction[2], const float first[2], const float second[2])
Definition channelmixerrgb_shared.c:342
void dt_iop_channelmixer_shared_paint_simple_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
Definition channelmixerrgb_shared.c:750
gboolean dt_iop_channelmixer_shared_primaries_from_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const float M[3][3], dt_iop_channelmixer_shared_primaries_params_t *primaries)
Definition channelmixerrgb_shared.c:500
static void _primaries_reference_white(const dt_iop_channelmixer_shared_primaries_basis_t basis, float white[3])
Definition channelmixerrgb_shared.c:283
static void _rotate_2d(const float vector[2], const float angle, float rotated[2])
Definition channelmixerrgb_shared.c:334
static void _affine_unproject_difference(const float uv[2], float difference[3])
Definition channelmixerrgb_shared.c:327
#define DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3
Definition channelmixerrgb_shared.c:36
void dt_iop_channelmixer_shared_simple_from_matrix(const float M[3][3], dt_iop_channelmixer_shared_simple_params_t *simple)
Definition channelmixerrgb_shared.c:201
void dt_iop_channelmixer_shared_mul3x3(const float A[3][3], const float B[3][3], float C[3][3])
Definition channelmixerrgb_shared.c:157
gboolean dt_iop_channelmixer_shared_rows_are_normalized(const gboolean normalize[3])
Definition channelmixerrgb_shared.c:127
float dt_iop_channelmixer_shared_wrap_pi(float angle)
Definition channelmixerrgb_shared.c:38
static gboolean _affine_normalize(const float vector[3], float normalized[3])
Definition channelmixerrgb_shared.c:312
float dt_iop_channelmixer_shared_roundtrip_error(const float M[3][3], const float roundtrip[3][3])
Definition channelmixerrgb_shared.c:254
gboolean dt_iop_channelmixer_shared_get_matrix(const float rows[3][3], const gboolean normalize[3], const gboolean force_normalize, float M[3][3])
Definition channelmixerrgb_shared.c:132
gboolean dt_iop_channelmixer_shared_primaries_to_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_primaries_params_t *primaries, float M[3][3])
Definition channelmixerrgb_shared.c:447
void dt_iop_channelmixer_shared_primaries_from_sliders(GtkWidget *const widgets[9], dt_iop_channelmixer_shared_primaries_params_t *primaries)
Definition channelmixerrgb_shared.c:99
static float _affine_sum3(const float vector[3])
Definition channelmixerrgb_shared.c:307
static gboolean _affine_polar_from_point(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float point_normalized[3], float *hue, float *purity)
Definition channelmixerrgb_shared.c:417
float dt_iop_channelmixer_shared_wrap_half_pi(float angle)
Definition channelmixerrgb_shared.c:45
static gboolean _build_affine_simplex(const dt_iop_channelmixer_shared_primaries_basis_t basis, float white_normalized[3], float reference_primaries[3][2])
Definition channelmixerrgb_shared.c:370
dt_iop_channelmixer_shared_simple_probe_t
Definition channelmixerrgb_shared.h:27
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_2
Definition channelmixerrgb_shared.h:30
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_1
Definition channelmixerrgb_shared.h:29
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_ROTATION
Definition channelmixerrgb_shared.h:28
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE
Definition channelmixerrgb_shared.h:16
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_TAN_SCALE
Definition channelmixerrgb_shared.h:14
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS
Definition channelmixerrgb_shared.h:15
dt_iop_channelmixer_shared_primaries_basis_t
Definition channelmixerrgb_shared.h:19
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_CAT16
Definition channelmixerrgb_shared.h:23
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_RGB
Definition channelmixerrgb_shared.h:20
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_BRADFORD
Definition channelmixerrgb_shared.h:22
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_XYZ
Definition channelmixerrgb_shared.h:21
static const dt_aligned_pixel_simd_t const dt_adaptation_t adaptation
Definition chromatic_adaptation.h:308
static void convert_D50_to_LMS(const dt_adaptation_t adaptation, dt_aligned_pixel_t D50)
Definition chromatic_adaptation.h:344
dt_adaptation_t
Definition chromatic_adaptation.h:30
@ DT_ADAPTATION_LAST
Definition chromatic_adaptation.h:36
@ DT_ADAPTATION_FULL_BRADFORD
Definition chromatic_adaptation.h:33
@ DT_ADAPTATION_XYZ
Definition chromatic_adaptation.h:34
@ DT_ADAPTATION_CAT16
Definition chromatic_adaptation.h:32
@ DT_ADAPTATION_RGB
Definition chromatic_adaptation.h:35
@ DT_ADAPTATION_LINEAR_BRADFORD
Definition chromatic_adaptation.h:31
#define B(y, x)
Definition colorspaces.c:187
#define A(y, x)
Definition colorspaces.c:186
dt_aligned_pixel_t LMS
Definition colorspaces_inline_conversions.h:701
const float i
Definition colorspaces_inline_conversions.h:440
const float g
Definition colorspaces_inline_conversions.h:674
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
const float d
Definition colorspaces_inline_conversions.h:680
static dt_aligned_pixel_t XYZ
Definition colorspaces_inline_conversions.h:98
static const float const float C
Definition colorspaces_inline_conversions.h:437
static const int row
Definition colorspaces_inline_conversions.h:35
static dt_aligned_pixel_t RGB
Definition colorspaces_inline_conversions.h:327
static const dt_colormatrix_t M
Definition colorspaces_inline_conversions.h:682
#define P(V, params)
Definition common/histogram.c:37
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
static void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
Definition illuminants.h:208
const int t
Definition iop_profile.h:225
static float mix(const float a, const float b, const float t)
Definition liquify.c:704
float *const restrict const size_t k
Definition luminance_mask.h:78
#define M_PI
Definition math.h:45
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
Definition matrices.h:36
float dt_aligned_pixel_t[4]
Definition noiseprofile.c:28
struct _GtkWidget GtkWidget
Definition splash.h:29
const float r
Definition src/develop/noise_generator.h:101
Definition channelmixerrgb_shared.h:44
float green_purity
Definition channelmixerrgb_shared.h:50
float gain
Definition channelmixerrgb_shared.h:53
float red_hue
Definition channelmixerrgb_shared.h:47
float blue_hue
Definition channelmixerrgb_shared.h:51
float achromatic_hue
Definition channelmixerrgb_shared.h:45
float red_purity
Definition channelmixerrgb_shared.h:48
float achromatic_purity
Definition channelmixerrgb_shared.h:46
float green_hue
Definition channelmixerrgb_shared.h:49
float blue_purity
Definition channelmixerrgb_shared.h:52
Definition channelmixerrgb_shared.h:34
float coupling_hue
Definition channelmixerrgb_shared.h:40
float coupling_amount
Definition channelmixerrgb_shared.h:39
float stretch_1
Definition channelmixerrgb_shared.h:37
float stretch_2
Definition channelmixerrgb_shared.h:38
float theta
Definition channelmixerrgb_shared.h:35
float psi
Definition channelmixerrgb_shared.h:36
Definition iop_profile.h:52
int nonlinearlut
Definition iop_profile.h:63
int lutsize
Definition iop_profile.h:58
dt_colormatrix_t matrix_out_transposed
Definition iop_profile.h:66
float * lut_out[3]
Definition iop_profile.h:60
float * lut_in[3]
Definition iop_profile.h:59
dt_colormatrix_t matrix_in_transposed
Definition iop_profile.h:65