1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpoperationdesaturate.c
5  * Copyright (C) 2007 Michael Natterer <mitch@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <cairo.h>
24 #include <gegl.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 
27 #include "libgimpcolor/gimpcolor.h"
28 #include "libgimpconfig/gimpconfig.h"
29 
30 #include "operations-types.h"
31 
32 #include "gimpoperationdesaturate.h"
33 
34 #include "gimp-intl.h"
35 
36 
37 enum
38 {
39   PROP_0,
40   PROP_MODE
41 };
42 
43 
44 static void     gimp_operation_desaturate_get_property (GObject             *object,
45                                                         guint                property_id,
46                                                         GValue              *value,
47                                                         GParamSpec          *pspec);
48 static void     gimp_operation_desaturate_set_property (GObject             *object,
49                                                         guint                property_id,
50                                                         const GValue        *value,
51                                                         GParamSpec          *pspec);
52 
53 static void     gimp_operation_desaturate_prepare      (GeglOperation       *operation);
54 static gboolean gimp_operation_desaturate_process      (GeglOperation       *operation,
55                                                         void                *in_buf,
56                                                         void                *out_buf,
57                                                         glong                samples,
58                                                         const GeglRectangle *roi,
59                                                         gint                 level);
60 
61 
G_DEFINE_TYPE(GimpOperationDesaturate,gimp_operation_desaturate,GIMP_TYPE_OPERATION_POINT_FILTER)62 G_DEFINE_TYPE (GimpOperationDesaturate, gimp_operation_desaturate,
63                GIMP_TYPE_OPERATION_POINT_FILTER)
64 
65 #define parent_class gimp_operation_desaturate_parent_class
66 
67 
68 static void
69 gimp_operation_desaturate_class_init (GimpOperationDesaturateClass *klass)
70 {
71   GObjectClass                  *object_class    = G_OBJECT_CLASS (klass);
72   GeglOperationClass            *operation_class = GEGL_OPERATION_CLASS (klass);
73   GeglOperationPointFilterClass *point_class     = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
74 
75   object_class->set_property = gimp_operation_desaturate_set_property;
76   object_class->get_property = gimp_operation_desaturate_get_property;
77 
78   operation_class->prepare   = gimp_operation_desaturate_prepare;
79 
80   point_class->process       = gimp_operation_desaturate_process;
81 
82   gegl_operation_class_set_keys (operation_class,
83                                  "name",        "gimp:desaturate",
84                                  "categories",  "color",
85                                  "description", _("Turn colors into shades of gray"),
86                                  NULL);
87 
88   GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE,
89                          "mode",
90                          _("Mode"),
91                          _("Choose shade of gray based on"),
92                          GIMP_TYPE_DESATURATE_MODE,
93                          GIMP_DESATURATE_LUMINANCE,
94                          GIMP_PARAM_STATIC_STRINGS);
95 }
96 
97 static void
gimp_operation_desaturate_init(GimpOperationDesaturate * self)98 gimp_operation_desaturate_init (GimpOperationDesaturate *self)
99 {
100 }
101 
102 static void
gimp_operation_desaturate_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)103 gimp_operation_desaturate_get_property (GObject    *object,
104                                         guint       property_id,
105                                         GValue     *value,
106                                         GParamSpec *pspec)
107 {
108   GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object);
109 
110   switch (property_id)
111     {
112     case PROP_MODE:
113       g_value_set_enum (value, desaturate->mode);
114       break;
115 
116     default:
117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
118       break;
119     }
120 }
121 
122 static void
gimp_operation_desaturate_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)123 gimp_operation_desaturate_set_property (GObject      *object,
124                                         guint         property_id,
125                                         const GValue *value,
126                                         GParamSpec   *pspec)
127 {
128   GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object);
129 
130   switch (property_id)
131     {
132     case PROP_MODE:
133       desaturate->mode = g_value_get_enum (value);
134       break;
135 
136     default:
137       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
138       break;
139     }
140 }
141 
142 static void
gimp_operation_desaturate_prepare(GeglOperation * operation)143 gimp_operation_desaturate_prepare (GeglOperation *operation)
144 {
145   GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation);
146   const Babl              *format = gegl_operation_get_source_format (operation, "input");
147 
148   if (desaturate->mode == GIMP_DESATURATE_LUMINANCE)
149     {
150       format = babl_format_with_space ("RGBA float", format);
151     }
152   else
153     {
154       format = babl_format_with_space ("R'G'B'A float", format);
155     }
156 
157   gegl_operation_set_format (operation, "input",  format);
158   gegl_operation_set_format (operation, "output", format);
159 }
160 
161 static gboolean
gimp_operation_desaturate_process(GeglOperation * operation,void * in_buf,void * out_buf,glong samples,const GeglRectangle * roi,gint level)162 gimp_operation_desaturate_process (GeglOperation       *operation,
163                                    void                *in_buf,
164                                    void                *out_buf,
165                                    glong                samples,
166                                    const GeglRectangle *roi,
167                                    gint                 level)
168 {
169   GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation);
170   gfloat                  *src        = in_buf;
171   gfloat                  *dest       = out_buf;
172 
173   switch (desaturate->mode)
174     {
175     case GIMP_DESATURATE_LIGHTNESS:
176       /* This is the formula for Lightness in the HSL "bi-hexcone"
177        * model: https://en.wikipedia.org/wiki/HSL_and_HSV
178        */
179       while (samples--)
180         {
181           gfloat min, max, value;
182 
183           max = MAX (src[0], src[1]);
184           max = MAX (max, src[2]);
185           min = MIN (src[0], src[1]);
186           min = MIN (min, src[2]);
187 
188           value = (max + min) / 2;
189 
190           dest[0] = value;
191           dest[1] = value;
192           dest[2] = value;
193           dest[3] = src[3];
194 
195           src  += 4;
196           dest += 4;
197         }
198       break;
199 
200     case GIMP_DESATURATE_LUMA:
201     case GIMP_DESATURATE_LUMINANCE:
202       while (samples--)
203         {
204           gfloat value = GIMP_RGB_LUMINANCE (src[0], src[1], src[2]);
205 
206           dest[0] = value;
207           dest[1] = value;
208           dest[2] = value;
209           dest[3] = src[3];
210 
211           src  += 4;
212           dest += 4;
213         }
214       break;
215 
216     case GIMP_DESATURATE_AVERAGE:
217       /* This is the formula for Intensity in the HSI model:
218        * https://en.wikipedia.org/wiki/HSL_and_HSV
219        */
220       while (samples--)
221         {
222           gfloat value = (src[0] + src[1] + src[2]) / 3;
223 
224           dest[0] = value;
225           dest[1] = value;
226           dest[2] = value;
227           dest[3] = src[3];
228 
229           src  += 4;
230           dest += 4;
231         }
232       break;
233 
234     case GIMP_DESATURATE_VALUE:
235       /* This is the formula for Value in the HSV model:
236        * https://en.wikipedia.org/wiki/HSL_and_HSV
237        */
238       while (samples--)
239         {
240           gfloat value;
241 
242           value = MAX (src[0], src[1]);
243           value = MAX (value, src[2]);
244 
245           dest[0] = value;
246           dest[1] = value;
247           dest[2] = value;
248           dest[3] = src[3];
249 
250           src  += 4;
251           dest += 4;
252         }
253       break;
254     }
255 
256   return TRUE;
257 }
258