Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorcontrast.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Bruce Guenter.
4 Copyright (C) 2010-2013 johannes hanika.
5 Copyright (C) 2011-2012 Henrik Andersson.
6 Copyright (C) 2011 Jérémy Rosen.
7 Copyright (C) 2011 Robert Bieber.
8 Copyright (C) 2011 Rostyslav Pidgornyi.
9 Copyright (C) 2011 Sergey Astanin.
10 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
11 Copyright (C) 2012 Richard Wonka.
12 Copyright (C) 2012, 2014 Ulrich Pegelow.
13 Copyright (C) 2013 Simon Spannagel.
14 Copyright (C) 2014-2016 Roman Lebedev.
15 Copyright (C) 2015 Pedro Côrte-Real.
16 Copyright (C) 2017 Heiko Bauke.
17 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
18 Copyright (C) 2018 Edgardo Hoszowski.
19 Copyright (C) 2018 Maurizio Paglia.
20 Copyright (C) 2018-2020, 2022 Pascal Obry.
21 Copyright (C) 2018 rawfiner.
22 Copyright (C) 2019 Andreas Schneider.
23 Copyright (C) 2020 Aldric Renaudin.
24 Copyright (C) 2020 Diederik Ter Rahe.
25 Copyright (C) 2020-2021 Hubert Kowalski.
26 Copyright (C) 2020-2021 Ralf Brown.
27 Copyright (C) 2022 Hanno Schwalm.
28 Copyright (C) 2022 Martin Bařinka.
29 Copyright (C) 2022 Philipp Lutz.
30
31 darktable is free software: you can redistribute it and/or modify
32 it under the terms of the GNU General Public License as published by
33 the Free Software Foundation, either version 3 of the License, or
34 (at your option) any later version.
35
36 darktable is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 GNU General Public License for more details.
40
41 You should have received a copy of the GNU General Public License
42 along with darktable. If not, see <http://www.gnu.org/licenses/>.
43*/
44/* -*- Mode: c; c-basic-offset: 2; -*- */
45
46#ifdef HAVE_CONFIG_H
47#include "config.h"
48#endif
49#include "widgets/bauhaus.h"
52#include "develop/imageop.h"
53#include "develop/imageop_gui.h"
54
55#include "iop/iop_api.h"
56
57#include <assert.h>
58#include <gtk/gtk.h>
59#include <stdlib.h>
60
62
70
72{
73 float a_steepness; // $MIN: 0.0 $MAX: 5.0 $DEFAULT: 1.0 $DESCRIPTION: "green-magenta contrast"
74 float a_offset;
75 float b_steepness; // $MIN: 0.0 $MAX: 5.0 $DEFAULT: 1.0 $DESCRIPTION: "blue-yellow contrast"
76 float b_offset;
77 int unbound; // $DEFAULT: 1
79
81{
82 // whatever you need to make your gui happy.
83 // stored in self->gui_data
84 GtkBox *vbox;
85 GtkWidget *a_scale; // this is needed by gui_update
88
90{
91 // this is stored in the pixelpipeline after a commit (not the db),
92 // you can do some precomputation and get this data in process().
93 // stored in piece->data
95 float a_offset;
97 float b_offset;
100
101const char *name()
102{
103 return _("color contrast");
104}
105
106const char *aliases()
107{
108 return _("saturation");
109}
110
111const char **description(struct dt_iop_module_t *self)
112{
113 return dt_iop_set_description(self, _("increase saturation and separation between\n"
114 "opposite colors"),
115 _("creative"),
116 _("non-linear, Lab, display-referred"),
117 _("non-linear, Lab"),
118 _("non-linear, Lab, display-referred"));
119}
120
125
127{
128 return IOP_GROUP_COLOR;
129}
130
132{
133 return IOP_CS_LAB;
134}
135
136int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
137 void *new_params, const int new_version)
138{
139 if(old_version == 1 && new_version == 2)
140 {
141 const dt_iop_colorcontrast_params1_t *old = old_params;
142 dt_iop_colorcontrast_params_t *new = new_params;
143
144 new->a_steepness = old->a_steepness;
145 new->a_offset = old->a_offset;
146 new->b_steepness = old->b_steepness;
147 new->b_offset = old->b_offset;
148 new->unbound = 0;
149 return 0;
150 }
151 return 1;
152}
153
154__OMP_DECLARE_SIMD__(aligned(in,out:64) aligned(slope,offset,low,high))
155static inline void clamped_scaling(float *const restrict out, const float *const restrict in,
156 const dt_aligned_pixel_t slope, const dt_aligned_pixel_t offset,
157 const dt_aligned_pixel_t low, const dt_aligned_pixel_t high)
158{
159 for_each_channel(c,dt_omp_nontemporal(out))
160 out[c] = CLAMPS(in[c] * slope[c] + offset[c], low[c], high[c]);
161}
162
164int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
165 void *const ovoid)
166{
167 const dt_iop_roi_t *const roi_out = &piece->roi_out;
168 // this is called for preview and full pipe separately, each with its own pixelpipe piece.
169
170 // get our data struct:
172
173 // how many colors in our buffer?
174
175 const float *const restrict in = DT_IS_ALIGNED((const float *const)ivoid);
176 float *const restrict out = DT_IS_ALIGNED((float *const)ovoid);
177 const size_t npixels = (size_t)roi_out->width * roi_out->height;
178
179 const dt_aligned_pixel_t slope = { 1.0f, d->a_steepness, d->b_steepness, 1.0f };
180 const dt_aligned_pixel_t offset = { 0.0f, d->a_offset, d->b_offset, 0.0f };
181 const dt_aligned_pixel_t lowlimit = { -INFINITY, -128.0f, -128.0f, -INFINITY };
182 const dt_aligned_pixel_t highlimit = { INFINITY, 128.0f, 128.0f, INFINITY };
183
184 if(d->unbound)
185 {
187 for(size_t k = 0; k < (size_t)4 * npixels; k += 4)
188 {
189 for_each_channel(c,dt_omp_nontemporal(out))
190 {
191 out[k + c] = (in[k + c] * slope[c]) + offset[c];
192 }
193 }
194 }
195 else
196 {
198 for(size_t k = 0; k < npixels; k ++)
199 {
200 // the inner per-pixel loop needs to be declared in a separate vectorizable function to convince the
201 // compiler that it doesn't need to check for overlap or misalignment of the buffers for *every* pixel,
202 // which actually makes the code slower than not vectorizing....
203 clamped_scaling(out + 4*k, in + 4*k, slope, offset, lowlimit, highlimit);
204 }
205 }
206
207 return 0;
208}
209
213{
216 d->a_steepness = p->a_steepness;
217 d->a_offset = p->a_offset;
218 d->b_steepness = p->b_steepness;
219 d->b_offset = p->b_offset;
220 d->unbound = p->unbound;
221}
222
228
230{
231 dt_free_align(piece->data);
232 piece->data = NULL;
233}
234
242
244{
246
247 g->a_scale = dt_bauhaus_slider_from_params(self, "a_steepness");
248 gtk_widget_set_tooltip_text(g->a_scale, _("steepness of the a* curve in Lab\nlower values desaturate greens and magenta while higher saturate them"));
249
250 g->b_scale = dt_bauhaus_slider_from_params(self, "b_steepness");
251 gtk_widget_set_tooltip_text(g->b_scale, _("steepness of the b* curve in Lab\nlower values desaturate blues and yellows while higher saturate them"));
252}
253
254// clang-format off
255// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
256// vim: shiftwidth=2 expandtab tabstop=2 cindent
257// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
258// clang-format on
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_LAB
const char ** description(struct dt_iop_module_t *self)
int default_group()
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void gui_update(dt_iop_module_t *self)
const char * aliases()
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
const char * name()
void gui_init(dt_iop_module_t *self)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_iop_params_t
Definition dev_history.h:43
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_GROUP_COLOR
Definition imageop.h:158
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
float *const restrict const size_t k
#define CLAMPS(A, L, H)
Definition math.h:78
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define DT_IS_ALIGNED(x)
Promise the compiler that x is already cacheline-aligned, and return it.
Definition mem_alloc.h:51
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_DECLARE_SIMD__(...)
Definition openmp.h:100
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
struct dt_iop_module_t *void * data
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__