1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <string.h>
21
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 #include <gegl.h>
24
25 #include "libgimpmath/gimpmath.h"
26
27 #include "core-types.h"
28
29 #include "gimpcurve.h"
30 #include "gimpcurve-map.h"
31
32
33 #if defined (HAVE_ISFINITE)
34 #define FINITE(x) isfinite(x)
35 #elif defined (HAVE_FINITE)
36 #define FINITE(x) finite(x)
37 #elif defined (G_OS_WIN32)
38 #define FINITE(x) _finite(x)
39 #else
40 #error "no FINITE() implementation available?!"
41 #endif
42
43
44 enum
45 {
46 CURVE_NONE = 0,
47 CURVE_COLORS = 1 << 0,
48 CURVE_RED = 1 << 1,
49 CURVE_GREEN = 1 << 2,
50 CURVE_BLUE = 1 << 3,
51 CURVE_ALPHA = 1 << 4
52 };
53
54 static guint gimp_curve_get_apply_mask (GimpCurve *curve_colors,
55 GimpCurve *curve_red,
56 GimpCurve *curve_green,
57 GimpCurve *curve_blue,
58 GimpCurve *curve_alpha);
59 static inline gdouble gimp_curve_map_value_inline (GimpCurve *curve,
60 gdouble value);
61
62
63 gdouble
gimp_curve_map_value(GimpCurve * curve,gdouble value)64 gimp_curve_map_value (GimpCurve *curve,
65 gdouble value)
66 {
67 g_return_val_if_fail (GIMP_IS_CURVE (curve), 0.0);
68
69 return gimp_curve_map_value_inline (curve, value);
70 }
71
72 void
gimp_curve_map_pixels(GimpCurve * curve_colors,GimpCurve * curve_red,GimpCurve * curve_green,GimpCurve * curve_blue,GimpCurve * curve_alpha,gfloat * src,gfloat * dest,glong samples)73 gimp_curve_map_pixels (GimpCurve *curve_colors,
74 GimpCurve *curve_red,
75 GimpCurve *curve_green,
76 GimpCurve *curve_blue,
77 GimpCurve *curve_alpha,
78 gfloat *src,
79 gfloat *dest,
80 glong samples)
81 {
82 g_return_if_fail (GIMP_IS_CURVE (curve_colors));
83 g_return_if_fail (GIMP_IS_CURVE (curve_red));
84 g_return_if_fail (GIMP_IS_CURVE (curve_green));
85 g_return_if_fail (GIMP_IS_CURVE (curve_blue));
86 g_return_if_fail (GIMP_IS_CURVE (curve_alpha));
87
88 switch (gimp_curve_get_apply_mask (curve_colors,
89 curve_red,
90 curve_green,
91 curve_blue,
92 curve_alpha))
93 {
94 case CURVE_NONE:
95 memcpy (dest, src, samples * 4 * sizeof (gfloat));
96 break;
97
98 case CURVE_COLORS:
99 while (samples--)
100 {
101 dest[0] = gimp_curve_map_value_inline (curve_colors, src[0]);
102 dest[1] = gimp_curve_map_value_inline (curve_colors, src[1]);
103 dest[2] = gimp_curve_map_value_inline (curve_colors, src[2]);
104 /* don't apply the colors curve to the alpha channel */
105 dest[3] = src[3];
106
107 src += 4;
108 dest += 4;
109 }
110 break;
111
112 case CURVE_RED:
113 while (samples--)
114 {
115 dest[0] = gimp_curve_map_value_inline (curve_red, src[0]);
116 dest[1] = src[1];
117 dest[2] = src[2];
118 dest[3] = src[3];
119
120 src += 4;
121 dest += 4;
122 }
123 break;
124
125 case CURVE_GREEN:
126 while (samples--)
127 {
128 dest[0] = src[0];
129 dest[1] = gimp_curve_map_value_inline (curve_green, src[1]);
130 dest[2] = src[2];
131 dest[3] = src[3];
132
133 src += 4;
134 dest += 4;
135 }
136 break;
137
138 case CURVE_BLUE:
139 while (samples--)
140 {
141 dest[0] = src[0];
142 dest[1] = src[1];
143 dest[2] = gimp_curve_map_value_inline (curve_blue, src[2]);
144 dest[3] = src[3];
145
146 src += 4;
147 dest += 4;
148 }
149 break;
150
151 case CURVE_ALPHA:
152 while (samples--)
153 {
154 dest[0] = src[0];
155 dest[1] = src[1];
156 dest[2] = src[2];
157 dest[3] = gimp_curve_map_value_inline (curve_alpha, src[3]);
158
159 src += 4;
160 dest += 4;
161 }
162 break;
163
164 case (CURVE_RED | CURVE_GREEN | CURVE_BLUE):
165 while (samples--)
166 {
167 dest[0] = gimp_curve_map_value_inline (curve_red, src[0]);
168 dest[1] = gimp_curve_map_value_inline (curve_green, src[1]);
169 dest[2] = gimp_curve_map_value_inline (curve_blue, src[2]);
170 dest[3] = src[3];
171
172 src += 4;
173 dest += 4;
174 }
175 break;
176
177 default:
178 while (samples--)
179 {
180 dest[0] = gimp_curve_map_value_inline (curve_colors,
181 gimp_curve_map_value_inline (curve_red,
182 src[0]));
183 dest[1] = gimp_curve_map_value_inline (curve_colors,
184 gimp_curve_map_value_inline (curve_green,
185 src[1]));
186 dest[2] = gimp_curve_map_value_inline (curve_colors,
187 gimp_curve_map_value_inline (curve_blue,
188 src[2]));
189 /* don't apply the colors curve to the alpha channel */
190 dest[3] = gimp_curve_map_value_inline (curve_alpha, src[3]);
191
192 src += 4;
193 dest += 4;
194 }
195 break;
196 }
197 }
198
199 static guint
gimp_curve_get_apply_mask(GimpCurve * curve_colors,GimpCurve * curve_red,GimpCurve * curve_green,GimpCurve * curve_blue,GimpCurve * curve_alpha)200 gimp_curve_get_apply_mask (GimpCurve *curve_colors,
201 GimpCurve *curve_red,
202 GimpCurve *curve_green,
203 GimpCurve *curve_blue,
204 GimpCurve *curve_alpha)
205 {
206 return ((gimp_curve_is_identity (curve_colors) ? 0 : CURVE_COLORS) |
207 (gimp_curve_is_identity (curve_red) ? 0 : CURVE_RED) |
208 (gimp_curve_is_identity (curve_green) ? 0 : CURVE_GREEN) |
209 (gimp_curve_is_identity (curve_blue) ? 0 : CURVE_BLUE) |
210 (gimp_curve_is_identity (curve_alpha) ? 0 : CURVE_ALPHA));
211 }
212
213 static inline gdouble
gimp_curve_map_value_inline(GimpCurve * curve,gdouble value)214 gimp_curve_map_value_inline (GimpCurve *curve,
215 gdouble value)
216 {
217 if (curve->identity)
218 {
219 if (FINITE (value))
220 return CLAMP (value, 0.0, 1.0);
221
222 return 0.0;
223 }
224
225 /* check for known values first, so broken values like NaN
226 * delivered by broken drivers don't run into the interpolation
227 * code
228 */
229 if (value > 0.0 && value < 1.0) /* interpolate the curve */
230 {
231 gdouble f;
232 gint index;
233
234 /* map value to the sample space */
235 value = value * (curve->n_samples - 1);
236
237 /* determine the indices of the closest sample points */
238 index = (gint) value;
239
240 /* calculate the position between the sample points */
241 f = value - index;
242
243 return (1.0 - f) * curve->samples[index] + f * curve->samples[index + 1];
244 }
245 else if (value >= 1.0)
246 {
247 return curve->samples[curve->n_samples - 1];
248 }
249 else
250 {
251 return curve->samples[0];
252 }
253 }
254