1 /* This file is an image processing operation for GEGL
3 * GEGL is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
8 * GEGL is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
16 * Copyright 2016 Red Hat, Inc.
21 #include <glib/gi18n-lib.h>
23 #ifdef GEGL_PROPERTIES
25 enum_start (gegl_saturation_type
)
26 enum_value (GEGL_SATURATION_TYPE_NATIVE
, "Native", N_("Native"))
27 enum_value (GEGL_SATURATION_TYPE_CIE_LAB
, "CIE-Lab", N_("CIE Lab/Lch"))
28 enum_value (GEGL_SATURATION_TYPE_CIE_YUV
, "CIE-Yuv", N_("CIE Yuv"))
29 enum_end (GeglSaturationType
)
31 property_double (scale
, _("Scale"), 1.0)
32 description(_("Scale, strength of effect"))
33 value_range (0.0, 10.0)
36 property_enum (colorspace
, _("Interpolation Color Space"),
37 description(_("Set at Native if uncertain, the CIE based spaces might introduce hue shifts."))
38 GeglSaturationType
, gegl_saturation_type
,
39 GEGL_SATURATION_TYPE_NATIVE
)
43 #define GEGL_OP_POINT_FILTER
44 #define GEGL_OP_NAME saturation
45 #define GEGL_OP_C_SOURCE saturation.c
49 typedef void (*ProcessFunc
) (GeglOperation
*operation
,
53 const GeglRectangle
*roi
,
57 process_lab (GeglOperation
*operation
,
61 const GeglRectangle
*roi
,
64 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
66 gfloat
*out
= out_buf
;
69 for (i
= 0; i
< n_pixels
; i
++)
72 out
[1] = in
[1] * o
->scale
;
73 out
[2] = in
[2] * o
->scale
;
81 process_lab_alpha (GeglOperation
*operation
,
85 const GeglRectangle
*roi
,
88 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
90 gfloat
*out
= out_buf
;
93 for (i
= 0; i
< n_pixels
; i
++)
96 out
[1] = in
[1] * o
->scale
;
97 out
[2] = in
[2] * o
->scale
;
106 process_lch (GeglOperation
*operation
,
110 const GeglRectangle
*roi
,
113 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
115 gfloat
*out
= out_buf
;
118 for (i
= 0; i
< n_pixels
; i
++)
121 out
[1] = in
[1] * o
->scale
;
130 process_lch_alpha (GeglOperation
*operation
,
134 const GeglRectangle
*roi
,
137 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
139 gfloat
*out
= out_buf
;
142 for (i
= 0; i
< n_pixels
; i
++)
145 out
[1] = in
[1] * o
->scale
;
156 process_cie_yuv_alpha (GeglOperation
*operation
,
160 const GeglRectangle
*roi
,
163 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
165 gfloat
*out
= out_buf
;
167 float scale
= o
->scale
;
169 #define CIE_u_origin (4/19.0f)
170 #define CIE_v_origin (9/19.0f)
172 for (i
= 0; i
< n_pixels
; i
++)
175 out
[1] = (in
[1] - CIE_u_origin
) * scale
+ CIE_u_origin
;
176 out
[2] = (in
[2] - CIE_v_origin
) * scale
+ CIE_v_origin
;
185 process_rgb_alpha (GeglOperation
*operation
,
189 const GeglRectangle
*roi
,
192 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
193 const Babl
*space
= gegl_operation_get_source_space (operation
, "input");
195 gfloat
*out
= out_buf
;
197 float scale
= o
->scale
;
198 float rscale
= 1.0f
- o
->scale
;
200 float luminance_f
[3];
202 babl_space_get_rgb_luminance (space
,
203 &luminance
[0], &luminance
[1], &luminance
[2]);
204 for (int c
= 0; c
< 3; c
++)
205 luminance_f
[c
] = luminance
[c
];
207 for (i
= 0; i
< n_pixels
; i
++)
209 gfloat desaturated
= (in
[0] * luminance_f
[0] +
210 in
[1] * luminance_f
[1] +
211 in
[2] * luminance_f
[2]) * rscale
;
212 for (int c
= 0; c
< 3; c
++)
213 out
[c
] = desaturated
+ in
[c
] * scale
;
224 process_cmyk_alpha (GeglOperation
*operation
,
228 const GeglRectangle
*roi
,
231 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
232 const Babl
*space
= gegl_operation_get_source_space (operation
, "input");
233 const Babl
*in_format
= gegl_operation_get_format (operation
, "input");
235 gfloat
*out
= out_buf
;
237 float scale
= o
->scale
;
238 float rscale
= 1.0f
- o
->scale
;
239 const Babl
*fish1
= babl_fish (in_format
, babl_format_with_space ("YA float", space
));
240 const Babl
*fish2
= babl_fish (babl_format_with_space ("YA float", space
),
241 babl_format_with_space ("CMYKA float", space
));
242 float *grayA
= gegl_malloc (n_pixels
* 2 * sizeof (float));
243 float *cmykA
= gegl_malloc (n_pixels
* 5 * sizeof (float));
244 gfloat
*desaturated
= cmykA
;
246 babl_process (fish1
, in
, grayA
, n_pixels
);
247 babl_process (fish2
, grayA
, cmykA
, n_pixels
);
250 for (i
= 0; i
< n_pixels
; i
++)
252 for (int c
= 0; c
< 4; c
++)
253 out
[c
] = desaturated
[c
] * rscale
+ in
[c
] * scale
;
264 static void prepare (GeglOperation
*operation
)
266 const Babl
*input_format
= NULL
;
267 const Babl
*space
= gegl_operation_get_source_space (operation
, "input");
268 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
270 BablModelFlag model_flags
;
272 input_format
= gegl_operation_get_source_format (operation
, "input");
274 switch (o
->colorspace
)
277 case GEGL_SATURATION_TYPE_NATIVE
:
278 format
= babl_format_with_space ("RGBA float", space
);
279 o
->user_data
= process_rgb_alpha
;
282 model_flags
= babl_get_model_flags (input_format
);
283 if (model_flags
& BABL_MODEL_FLAG_CMYK
&& o
->scale
< 1.0)
285 /* we only use the CMYK code path when desaturating, it provides
286 the expected result, and retains the separation - wheras for
287 increasing saturation - to achieve expected behavior we need
288 to fall back to RGBA to achieve the desired result. */
289 format
= babl_format_with_space ("CMYKA float", space
);
290 o
->user_data
= process_cmyk_alpha
;
292 else if (model_flags
& BABL_MODEL_FLAG_CIE
)
294 format
= babl_format_with_space ("CIE Lab alpha float", space
);
295 o
->user_data
= process_lab_alpha
;
297 /* otherwise we use the RGB default */
300 case GEGL_SATURATION_TYPE_CIE_YUV
:
301 format
= babl_format_with_space ("CIE Yuv alpha float", space
);
302 o
->user_data
= process_cie_yuv_alpha
;
304 case GEGL_SATURATION_TYPE_CIE_LAB
:
306 const Babl
*lch_model
;
307 const Babl
*input_model
;
309 if (input_format
== NULL
)
311 format
= babl_format_with_space ("CIE Lab alpha float", space
);
312 o
->user_data
= process_lab_alpha
;
316 input_model
= babl_format_get_model (input_format
);
318 if (babl_format_has_alpha (input_format
))
320 lch_model
= babl_model_with_space ("CIE LCH(ab) alpha", space
);
321 if (input_model
== lch_model
)
323 format
= babl_format_with_space ("CIE LCH(ab) alpha float", space
);
324 o
->user_data
= process_lch_alpha
;
328 format
= babl_format_with_space ("CIE Lab alpha float", space
);
329 o
->user_data
= process_lab_alpha
;
334 lch_model
= babl_model_with_space ("CIE LCH(ab)", space
);
335 if (input_model
== lch_model
)
337 format
= babl_format_with_space ("CIE LCH(ab) float", space
);
338 o
->user_data
= process_lch
;
342 format
= babl_format_with_space ("CIE Lab float", space
);
343 o
->user_data
= process_lab
;
350 gegl_operation_set_format (operation
, "input", format
);
351 gegl_operation_set_format (operation
, "output", format
);
355 process (GeglOperation
*operation
,
359 const GeglRectangle
*roi
,
362 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
363 ProcessFunc real_process
= (ProcessFunc
) o
->user_data
;
365 real_process (operation
, in_buf
, out_buf
, n_pixels
, roi
, level
);
370 gegl_op_class_init (GeglOpClass
*klass
)
372 GeglOperationClass
*operation_class
;
373 GeglOperationPointFilterClass
*point_filter_class
;
375 "<?xml version='1.0' encoding='UTF-8'?>"
377 " <node operation='gegl:crop' width='200' height='200'/>"
378 " <node operation='gegl:over'>"
379 " <node operation='gegl:saturation'>"
381 " <param name='scale'>2.0</param>"
384 " <node operation='gegl:load' path='standard-input.png'/>"
386 " <node operation='gegl:checkerboard'>"
388 " <param name='color1'>rgb(0.25,0.25,0.25)</param>"
389 " <param name='color2'>rgb(0.75,0.75,0.75)</param>"
394 operation_class
= GEGL_OPERATION_CLASS (klass
);
395 point_filter_class
= GEGL_OPERATION_POINT_FILTER_CLASS (klass
);
397 operation_class
->prepare
= prepare
;
398 operation_class
->opencl_support
= FALSE
;
400 point_filter_class
->process
= process
;
402 gegl_operation_class_set_keys (operation_class
,
403 "name" , "gegl:saturation",
404 "title", _("Saturation"),
405 "categories" , "color",
407 "reference-hash", "c93c29f810f7743c454e3d8171878eee",
408 "reference-composition", composition
,
409 "description", _("Changes the saturation"),