diff --git a/app/operations/gimpoperationcolorize.c b/app/operations/gimpoperationcolorize.c index fd6484092b..fd01127233 100644 --- a/app/operations/gimpoperationcolorize.c +++ b/app/operations/gimpoperationcolorize.c @@ -44,21 +44,26 @@ enum }; -static void gimp_operation_colorize_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); -static void gimp_operation_colorize_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); +static void gimp_operation_colorize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_colorize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); -static gboolean gimp_operation_colorize_process (GeglOperation *operation, - void *in_buf, - void *out_buf, - glong samples, - const GeglRectangle *roi, - gint level); +static void gimp_operation_colorize_prepare (GeglOperation *operation); + +static gboolean gimp_operation_colorize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + +static void gimp_hsl_to_non_linear_rgb (const gfloat *hsl, + gfloat *rgb); G_DEFINE_TYPE (GimpOperationColorize, gimp_operation_colorize, @@ -73,12 +78,14 @@ gimp_operation_colorize_class_init (GimpOperationColorizeClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); - GimpHSL hsl; - GimpRGB rgb; + GeglColor *color; + gfloat hsl[3] = { 0.5f, 0.5f, 0.5f }; object_class->set_property = gimp_operation_colorize_set_property; object_class->get_property = gimp_operation_colorize_get_property; + operation_class->prepare = gimp_operation_colorize_prepare; + gegl_operation_class_set_keys (operation_class, "name", "gimp:colorize", "categories", "color", @@ -105,21 +112,25 @@ gimp_operation_colorize_class_init (GimpOperationColorizeClass *klass) _("Lightness"), -1.0, 1.0, 0.0, 0); - gimp_hsl_set (&hsl, 0.5, 0.5, 0.5); - gimp_hsl_set_alpha (&hsl, 1.0); - gimp_hsl_to_rgb (&hsl, &rgb); + color = gegl_color_new (NULL); + gegl_color_set_pixel (color, babl_format ("HSL float"), hsl); g_object_class_install_property (object_class, PROP_COLOR, - gimp_param_spec_rgb ("color", - _("Color"), - _("Color"), - FALSE, &rgb, - G_PARAM_READWRITE)); + gegl_param_spec_color ("color", + _("Color"), + _("Color"), + /*FALSE,*/ color, + G_PARAM_READWRITE)); + g_object_unref (color); } static void gimp_operation_colorize_init (GimpOperationColorize *self) { + self->hue = 0.5; + self->saturation = 0.5; + self->lightness = 0.5; + self->hsl_format = NULL; } static void @@ -146,16 +157,16 @@ gimp_operation_colorize_get_property (GObject *object, case PROP_COLOR: { - GimpHSL hsl; - GimpRGB rgb; + GeglColor *color; + gfloat hsl[3]; - gimp_hsl_set (&hsl, - self->hue, - self->saturation, - (self->lightness + 1.0) / 2.0); - gimp_hsl_set_alpha (&hsl, 1.0); - gimp_hsl_to_rgb (&hsl, &rgb); - gimp_value_set_rgb (value, &rgb); + hsl[0] = self->hue; + hsl[1] = self->saturation; + hsl[2] = (self->lightness + 1.0) / 2.0; + + color = gegl_color_new (NULL); + gegl_color_set_pixel (color, babl_format ("HSL float"), hsl); + g_value_take_object (value, color); } break; @@ -192,22 +203,16 @@ gimp_operation_colorize_set_property (GObject *object, case PROP_COLOR: { - GimpRGB rgb; - GimpHSL hsl; + GeglColor *color; + float hsl[3]; - gimp_value_get_rgb (value, &rgb); - gimp_rgb_to_hsl (&rgb, &hsl); - - if (hsl.h == -1) - hsl.h = self->hue; - - if (hsl.l == 0.0 || hsl.l == 1.0) - hsl.s = self->saturation; + color = g_value_get_object (value); + gegl_color_get_pixel (color, self->hsl_format, hsl); g_object_set (self, - "hue", hsl.h, - "saturation", hsl.s, - "lightness", hsl.l * 2.0 - 1.0, + "hue", hsl[0], + "saturation", hsl[1], + "lightness", hsl[2] * 2.0 - 1.0, NULL); } break; @@ -218,6 +223,31 @@ gimp_operation_colorize_set_property (GObject *object, } } +static void +gimp_operation_colorize_prepare (GeglOperation *operation) +{ + GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation); + const Babl *space = gegl_operation_get_source_space (operation, + "input"); + const Babl *in_format; + const Babl *out_format; + + /* GIMP_RGB_LUMINANCE() requires the input to be linear RGB for correctness. */ + in_format = babl_format_with_space ("RGBA float", space); + /* Technically it looks like our code is returning non-linear RGB so we should + * set the output format to "R'G'B'A float". I leave this like this for now as + * it's the algorithm we used for years. + */ + out_format = babl_format_with_space ("RGBA float", space); + + gegl_operation_set_format (operation, "input", in_format); + gegl_operation_set_format (operation, "output", out_format); + + colorize->hsl_format = babl_format_with_space ("HSL float", in_format); + colorize->fish_to_lum = babl_fish (in_format, "Y float"); + colorize->fish_from_hsl = babl_fish (colorize->hsl_format, out_format); +} + static gboolean gimp_operation_colorize_process (GeglOperation *operation, void *in_buf, @@ -226,44 +256,46 @@ gimp_operation_colorize_process (GeglOperation *operation, const GeglRectangle *roi, gint level) { - GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation); - gfloat *src = in_buf; - gfloat *dest = out_buf; - GimpHSL hsl; + GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + gfloat hsl[3]; - hsl.h = colorize->hue; - hsl.s = colorize->saturation; + hsl[0] = colorize->hue; + hsl[1] = colorize->saturation; while (samples--) { - GimpRGB rgb; - gfloat lum = GIMP_RGB_LUMINANCE (src[RED], - src[GREEN], - src[BLUE]); + gfloat lum = GIMP_RGB_LUMINANCE (src[RED], + src[GREEN], + src[BLUE]); - if (colorize->lightness > 0) + /* TODO: the following would compute luminance correctly whatever the + * input format but is slower: + */ + /*babl_process (colorize->fish_to_lum, src, &lum, 1);*/ + + if (colorize->lightness > 0.f) { - lum = lum * (1.0 - colorize->lightness); + lum = lum * (1.0f - colorize->lightness); - lum += 1.0 - (1.0 - colorize->lightness); + lum += 1.0f - (1.0f - colorize->lightness); } - else if (colorize->lightness < 0) + else if (colorize->lightness < 0.f) { - lum = lum * (colorize->lightness + 1.0); + lum = lum * (colorize->lightness + 1.0f); } - hsl.l = lum; + hsl[2] = lum; - gimp_hsl_to_rgb (&hsl, &rgb); - - /* the code in base/colorize.c would multiply r,b,g with lum, - * but this is a bug since it should multiply with 255. We - * don't repeat this bug here (this is the reason why the gegl - * colorize is brighter than the legacy one). + gimp_hsl_to_non_linear_rgb (hsl, dest); + /* TODO: the following would convert correctly from HSL to RGB but it's a + * lot slower. Also the result is different as of now because + * gimp_hsl_to_rgb() computes values in non-linear whereas we set our + * output format to be linear. */ - dest[RED] = rgb.r; /* * lum */ - dest[GREEN] = rgb.g; /* * lum */ - dest[BLUE] = rgb.b; /* * lum */ + /*babl_process (colorize->fish_from_hsl, hsl, dest, 1);*/ + dest[ALPHA] = src[ALPHA]; src += 4; @@ -272,3 +304,63 @@ gimp_operation_colorize_process (GeglOperation *operation, return TRUE; } + +/* This is a copy of gimp_hsl_value() from libgimpcolor/gimpcolorspace.c */ +static inline gdouble +gimp_hsl_value (gdouble n1, + gdouble n2, + gdouble hue) +{ + gdouble val; + + if (hue > 6.0) + hue -= 6.0; + else if (hue < 0.0) + hue += 6.0; + + if (hue < 1.0) + val = n1 + (n2 - n1) * hue; + else if (hue < 3.0) + val = n2; + else if (hue < 4.0) + val = n1 + (n2 - n1) * (4.0 - hue); + else + val = n1; + + return val; +} + +/* This is a copy of gimp_hsl_to_rgb() from libgimpcolor/gimpcolorspace.c + * except that instead of working on former GimpHSL and GimpRGB types, we work + * on float arrays of size 3 directly. + */ +static void +gimp_hsl_to_non_linear_rgb (const gfloat *hsl, + gfloat *rgb) +{ + g_return_if_fail (hsl != NULL); + g_return_if_fail (rgb != NULL); + + if (hsl[1] == 0) + { + /* achromatic case */ + rgb[0] = hsl[2]; + rgb[1] = hsl[2]; + rgb[2] = hsl[2]; + } + else + { + gdouble m1, m2; + + if (hsl[2] <= 0.5) + m2 = hsl[2] * (1.0 + hsl[1]); + else + m2 = hsl[2] + hsl[1] - hsl[2] * hsl[1]; + + m1 = 2.0 * hsl[2] - m2; + + rgb[0] = gimp_hsl_value (m1, m2, hsl[0] * 6.0 + 2.0); + rgb[1] = gimp_hsl_value (m1, m2, hsl[0] * 6.0); + rgb[2] = gimp_hsl_value (m1, m2, hsl[0] * 6.0 - 2.0); + } +} diff --git a/app/operations/gimpoperationcolorize.h b/app/operations/gimpoperationcolorize.h index cfb5545fe5..b6aaf3f39f 100644 --- a/app/operations/gimpoperationcolorize.h +++ b/app/operations/gimpoperationcolorize.h @@ -40,9 +40,12 @@ struct _GimpOperationColorize { GimpOperationPointFilter parent_instance; - gdouble hue; - gdouble saturation; - gdouble lightness; + gfloat hue; + gfloat saturation; + gfloat lightness; + const Babl *hsl_format; + const Babl *fish_to_lum; + const Babl *fish_from_hsl; }; struct _GimpOperationColorizeClass