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