2008-01-04 15:16:32 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* gimpoperationcolorize.c
|
|
|
|
* Copyright (C) 2007 Michael Natterer <mitch@gimp.org>
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
2008-01-04 15:16:32 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-17 22:28:01 +00:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2008-01-04 15:16:32 +00:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2018-07-11 23:27:07 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2008-01-04 15:16:32 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2011-04-28 15:50:39 +02:00
|
|
|
#include <cairo.h>
|
2012-05-03 03:36:22 +02:00
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
2017-09-05 21:26:59 +02:00
|
|
|
#include <gegl.h>
|
2008-01-04 15:16:32 +00:00
|
|
|
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2017-09-05 21:26:59 +02:00
|
|
|
#include "libgimpconfig/gimpconfig.h"
|
2008-01-04 15:16:32 +00:00
|
|
|
|
2012-05-10 21:22:44 +02:00
|
|
|
#include "operations-types.h"
|
2008-01-04 15:16:32 +00:00
|
|
|
|
|
|
|
#include "gimpoperationcolorize.h"
|
|
|
|
|
2017-06-04 21:16:58 +02:00
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
2008-01-04 15:16:32 +00:00
|
|
|
|
2017-09-05 21:26:59 +02:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_HUE,
|
|
|
|
PROP_SATURATION,
|
|
|
|
PROP_LIGHTNESS,
|
|
|
|
PROP_COLOR
|
|
|
|
};
|
|
|
|
|
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
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_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);
|
2008-01-04 15:16:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (GimpOperationColorize, gimp_operation_colorize,
|
2008-01-25 20:50:32 +00:00
|
|
|
GIMP_TYPE_OPERATION_POINT_FILTER)
|
2008-01-04 15:16:32 +00:00
|
|
|
|
|
|
|
#define parent_class gimp_operation_colorize_parent_class
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2008-01-25 20:50:32 +00:00
|
|
|
gimp_operation_colorize_class_init (GimpOperationColorizeClass *klass)
|
2008-01-04 15:16:32 +00:00
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
|
|
|
|
GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
GeglColor *color;
|
|
|
|
gfloat hsl[3] = { 0.5f, 0.5f, 0.5f };
|
2008-01-04 15:16:32 +00:00
|
|
|
|
2017-09-05 21:26:59 +02:00
|
|
|
object_class->set_property = gimp_operation_colorize_set_property;
|
|
|
|
object_class->get_property = gimp_operation_colorize_get_property;
|
2008-01-04 15:16:32 +00:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
operation_class->prepare = gimp_operation_colorize_prepare;
|
|
|
|
|
2012-03-29 19:22:22 +01:00
|
|
|
gegl_operation_class_set_keys (operation_class,
|
2012-04-21 00:46:48 +02:00
|
|
|
"name", "gimp:colorize",
|
|
|
|
"categories", "color",
|
2017-06-04 21:16:58 +02:00
|
|
|
"description", _("Colorize the image"),
|
2012-04-21 00:46:48 +02:00
|
|
|
NULL);
|
2008-01-04 15:16:32 +00:00
|
|
|
|
2012-04-21 00:46:48 +02:00
|
|
|
point_class->process = gimp_operation_colorize_process;
|
2008-01-30 17:44:06 +00:00
|
|
|
|
2017-09-05 21:26:59 +02:00
|
|
|
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HUE,
|
|
|
|
"hue",
|
|
|
|
_("Hue"),
|
|
|
|
_("Hue"),
|
|
|
|
0.0, 1.0, 0.5, 0);
|
|
|
|
|
|
|
|
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SATURATION,
|
|
|
|
"saturation",
|
|
|
|
_("Saturation"),
|
|
|
|
_("Saturation"),
|
|
|
|
0.0, 1.0, 0.5, 0);
|
|
|
|
|
|
|
|
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LIGHTNESS,
|
|
|
|
"lightness",
|
|
|
|
_("Lightness"),
|
|
|
|
_("Lightness"),
|
|
|
|
-1.0, 1.0, 0.0, 0);
|
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
color = gegl_color_new (NULL);
|
|
|
|
gegl_color_set_pixel (color, babl_format ("HSL float"), hsl);
|
2017-09-05 21:26:59 +02:00
|
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_COLOR,
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
gegl_param_spec_color ("color",
|
|
|
|
_("Color"),
|
|
|
|
_("Color"),
|
|
|
|
/*FALSE,*/ color,
|
|
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_unref (color);
|
2008-01-04 15:16:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_operation_colorize_init (GimpOperationColorize *self)
|
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
self->hue = 0.5;
|
|
|
|
self->saturation = 0.5;
|
|
|
|
self->lightness = 0.5;
|
|
|
|
self->hsl_format = NULL;
|
2008-01-04 15:16:32 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 21:26:59 +02:00
|
|
|
static void
|
|
|
|
gimp_operation_colorize_get_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object);
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
{
|
|
|
|
case PROP_HUE:
|
|
|
|
g_value_set_double (value, self->hue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SATURATION:
|
|
|
|
g_value_set_double (value, self->saturation);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_LIGHTNESS:
|
|
|
|
g_value_set_double (value, self->lightness);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COLOR:
|
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
GeglColor *color;
|
|
|
|
gfloat hsl[3];
|
|
|
|
|
|
|
|
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);
|
2017-09-05 21:26:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_operation_colorize_set_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object);
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
{
|
|
|
|
case PROP_HUE:
|
|
|
|
self->hue = g_value_get_double (value);
|
|
|
|
g_object_notify (object, "color");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SATURATION:
|
|
|
|
self->saturation = g_value_get_double (value);
|
|
|
|
g_object_notify (object, "color");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_LIGHTNESS:
|
|
|
|
self->lightness = g_value_get_double (value);
|
|
|
|
g_object_notify (object, "color");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COLOR:
|
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
GeglColor *color;
|
|
|
|
float hsl[3];
|
2017-09-05 21:26:59 +02:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
color = g_value_get_object (value);
|
|
|
|
gegl_color_get_pixel (color, self->hsl_format, hsl);
|
2017-09-05 21:26:59 +02:00
|
|
|
|
|
|
|
g_object_set (self,
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
"hue", hsl[0],
|
|
|
|
"saturation", hsl[1],
|
|
|
|
"lightness", hsl[2] * 2.0 - 1.0,
|
2017-09-05 21:26:59 +02:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-01-04 15:16:32 +00:00
|
|
|
static gboolean
|
2008-06-11 09:42:22 +00:00
|
|
|
gimp_operation_colorize_process (GeglOperation *operation,
|
|
|
|
void *in_buf,
|
|
|
|
void *out_buf,
|
|
|
|
glong samples,
|
2012-03-26 01:13:37 +02:00
|
|
|
const GeglRectangle *roi,
|
|
|
|
gint level)
|
2008-01-04 15:16:32 +00:00
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation);
|
|
|
|
gfloat *src = in_buf;
|
|
|
|
gfloat *dest = out_buf;
|
|
|
|
gfloat hsl[3];
|
2008-01-04 15:16:32 +00:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
hsl[0] = colorize->hue;
|
|
|
|
hsl[1] = colorize->saturation;
|
2008-01-17 14:38:45 +00:00
|
|
|
|
2008-01-28 20:42:48 +00:00
|
|
|
while (samples--)
|
2008-01-04 15:16:32 +00:00
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
gfloat lum = GIMP_RGB_LUMINANCE (src[RED],
|
|
|
|
src[GREEN],
|
|
|
|
src[BLUE]);
|
2008-01-04 15:16:32 +00:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
/* 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)
|
2008-01-04 15:16:32 +00:00
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
lum = lum * (1.0f - colorize->lightness);
|
2008-01-04 15:16:32 +00:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
lum += 1.0f - (1.0f - colorize->lightness);
|
2008-01-04 15:16:32 +00:00
|
|
|
}
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
else if (colorize->lightness < 0.f)
|
2008-01-04 15:16:32 +00:00
|
|
|
{
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
lum = lum * (colorize->lightness + 1.0f);
|
2008-01-04 15:16:32 +00:00
|
|
|
}
|
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
hsl[2] = lum;
|
2008-01-04 15:16:32 +00:00
|
|
|
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
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.
|
2008-01-07 20:35:30 +00:00
|
|
|
*/
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
/*babl_process (colorize->fish_from_hsl, hsl, dest, 1);*/
|
|
|
|
|
2014-06-15 23:39:09 +02:00
|
|
|
dest[ALPHA] = src[ALPHA];
|
2008-01-04 15:16:32 +00:00
|
|
|
|
|
|
|
src += 4;
|
|
|
|
dest += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
app: port Colorize to GeglColor property.
While doing this, I could find a lot of problems in the algorithm:
1. It looks like the original intent (from GUI and code) is that you set hue and
saturation but not really the intended lightness, rather its increase or
decrease, relatively to every pixel current lightness. I.e. that every pixel
will be set to the selected hue and saturation, and only the lightness will
change. The "lightness" field is therefore a relative value (pixel per
pixel). The first problem is that instead of lightness, we compute the
luminance, which is related but different and set this in the lightness
field.
2. The second issue is that we were using gimp_hsl_to_rgb() which after testing
(because its documentation doesn't give any TRC/space info at all) looks like
it computes a color from HSL to non-linear RGB of the same space. Yet we were
outputting to a linear RGB space. So we compute the wrong values. On the
other hand, because of the first problem, I realize (after testing) that it
makes our render closer to the intended render by chance (at least when the
TRC is sRGB's). It's still wrong, but if we were to change the output to
"R'G'B'A float" instead, the render would be much darker. In both cases, it's
wrong anyway.
I would not say that the 2 problems are canceling each others, but they are
making the final result somewhat OK.
3. Ideally we should be using babl to convert colors, and this is the best way
to actually implement the original filter intent. Unfortunately so far, doing
this is much slower (though I save a lot of time by moving out of the samples
loop and processing data in chunks, it's still slower than the current,
nearly instant, implementation).
4. Because of all previous implementation irregularities, the render is
different depending on the actual image space. I.e. that the exact same image
filtered through Colorize with the exact same operation parameters will
render differently. I would need to test further, and maybe it's normal since
HSL is also space-dependant (and that's where we work on in this operation),
but I'm wondering if the result should not be independant of the working
space.
5. I implemented our own prepare() method because the one in
GimpOperationPointFilter parent seems to allow other input or output models.
Even though in all my tests, it was always linear RGB (which is what we want
here), let's make sure by having a custom prepare() method explicitly setting
these. It's also the opportunity to create some babl fishes.
In any case, I'm leaving the code as-is for now, because it's how this operation
has been since forever (at least for as long as I was around) and I don't think
it's the right idea to change it on a whim.
This raises even more the concern of versionning GEGL operation, which we have
been discussing with pippin on IRC lately, because if ever we want to change
this one, now that operations are not just applied, but possibly
non-destructively recreated at load, we need to make sure that we recreate the
render expected at the time of creation of a XCF while allowing us to have the
filters improving when needed.
2024-02-15 23:09:01 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|