libgimpwidgets, libgimpcolor: RGB and HSV should also show out-of-gamut…

… in soft-proof space when soft-proofing is enabled.
This commit is contained in:
Jehan 2023-12-20 22:56:12 +09:00
parent de83bca814
commit 4b3702bfc1
2 changed files with 425 additions and 226 deletions

View File

@ -275,23 +275,19 @@ gimp_color_is_out_of_gamut (GeglColor *color,
} }
else if (babl_space_is_cmyk (space)) else if (babl_space_is_cmyk (space))
{ {
GeglColor *c = gegl_color_new (NULL);
gdouble cmyk[4]; gdouble cmyk[4];
/* CMYK conversion always produces colors in [0; 1] range. What we want
* to check is whether the source and converted colors are the same in
* Lab space.
*/
gegl_color_get_pixel (color, gegl_color_get_pixel (color,
babl_format_with_space ("CMYK double", space), babl_format_with_space ("CMYK double", space),
cmyk); cmyk);
/* We make sure that each component is within [0; 1], but accept a small gegl_color_set_pixel (c, babl_format_with_space ("CMYK double", space), cmyk);
* error of margin (we don't want to show small precision errors as is_out_of_gamut = (! gimp_color_is_perceptually_identical (color, c));
* out-of-gamut colors). g_object_unref (c);
*/
is_out_of_gamut = ((cmyk[0] < 0.0 && -cmyk[0] > CHANNEL_EPSILON) ||
(cmyk[0] > 1.0 && cmyk[0] - 1.0 > CHANNEL_EPSILON) ||
(cmyk[1] < 0.0 && -cmyk[1] > CHANNEL_EPSILON) ||
(cmyk[1] > 1.0 && cmyk[1] - 1.0 > CHANNEL_EPSILON) ||
(cmyk[2] < 0.0 && -cmyk[2] > CHANNEL_EPSILON) ||
(cmyk[2] > 1.0 && cmyk[2] - 1.0 > CHANNEL_EPSILON) ||
(cmyk[3] < 0.0 && -cmyk[3] > CHANNEL_EPSILON) ||
(cmyk[3] > 1.0 && cmyk[3] - 1.0 > CHANNEL_EPSILON));
} }
else else
{ {

View File

@ -315,6 +315,8 @@ static const ColorSelectRenderFunc render_funcs[] =
static const Babl *fish_lch_to_rgb = NULL; static const Babl *fish_lch_to_rgb = NULL;
static const Babl *fish_lch_to_rgb_u8 = NULL; static const Babl *fish_lch_to_rgb_u8 = NULL;
static const Babl *fish_lch_to_softproof = NULL; static const Babl *fish_lch_to_softproof = NULL;
static const Babl *rgbf_format = NULL;
static const Babl *hsvf_format = NULL;
static const Babl *softproof_format = NULL; static const Babl *softproof_format = NULL;
@ -655,6 +657,8 @@ gimp_color_select_set_format (GimpColorSelector *selector,
{ {
select->format = format; select->format = format;
rgbf_format = babl_format_with_space ("R'G'B' float", format);
hsvf_format = babl_format_with_space ("HSV float", format);
fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"), fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"),
babl_format_with_space ("R'G'B' double", format)); babl_format_with_space ("R'G'B' double", format));
fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) double"), fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) double"),
@ -749,7 +753,7 @@ gimp_color_select_simulation (GimpColorSelector *selector,
const Babl *format = NULL; const Babl *format = NULL;
GimpColorProfile *profile = NULL; GimpColorProfile *profile = NULL;
if (enabled && gimp_color_selector_get_simulation (selector, &profile, NULL, NULL)) if (enabled && gimp_color_selector_get_simulation (selector, &profile, NULL, NULL) && profile)
{ {
GError *error = NULL; GError *error = NULL;
@ -1624,128 +1628,189 @@ color_select_render_lch_hue (ColorSelectFill *csf)
static void static void
color_select_render_red_green (ColorSelectFill *csf) color_select_render_red_green (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat r = 0; gfloat dr;
gfloat g = 0; gfloat rgb[3];
gfloat b = 0;
gfloat dr = 0;
gint i; gint i;
b = csf->rgb[2] * 255.0; if (softproof_format)
c = gegl_color_new (NULL);
if (b < 0.0 || b > 255.0) rgb[0] = 0.f;
{ rgb[1] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;
r = csf->oog_color[0]; rgb[2] = csf->rgb[2];
g = csf->oog_color[1];
b = csf->oog_color[2];
}
else
{
g = (csf->height - csf->y + 1) * 255.0 / csf->height;
dr = 255.0 / csf->width; dr = 1.f / csf->width;
}
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{ {
*p++ = r; gboolean in_gamut = TRUE;
*p++ = g;
*p++ = b;
r += dr; if (softproof_format)
{
gegl_color_set_pixel (c, rgbf_format, rgb);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
} }
}
if (in_gamut)
{
*p++ = rgb[0] * 255.0;
*p++ = rgb[1] * 255.0;
*p++ = rgb[2] * 255.0;
}
rgb[0] += dr;
}
g_clear_object (&c);
} }
static void static void
color_select_render_red_blue (ColorSelectFill *csf) color_select_render_red_blue (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat r = 0; gfloat rgb[3];
gfloat g = 0; gfloat dr;
gfloat b = 0;
gfloat dr = 0;
gint i; gint i;
g = csf->rgb[1] * 255.0; if (softproof_format)
c = gegl_color_new (NULL);
if (g < 0.0 || g > 255.0) rgb[0] = 0.f;
{ rgb[1] = csf->rgb[1];
r = csf->oog_color[0]; rgb[2] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;
g = csf->oog_color[1];
b = csf->oog_color[2];
}
else
{
b = (csf->height - csf->y + 1) * 255.0 / csf->height;
dr = 255.0 / csf->width; dr = 1.f / csf->width;
}
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{ {
*p++ = r; gboolean in_gamut = TRUE;
*p++ = g;
*p++ = b;
r += dr; if (softproof_format)
{
gegl_color_set_pixel (c, rgbf_format, rgb);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
} }
}
if (in_gamut)
{
*p++ = rgb[0] * 255.0;
*p++ = rgb[1] * 255.0;
*p++ = rgb[2] * 255.0;
}
rgb[0] += dr;
}
g_clear_object (&c);
} }
static void static void
color_select_render_green_blue (ColorSelectFill *csf) color_select_render_green_blue (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat r = 0; gfloat rgb[3];
gfloat g = 0; gfloat dg;
gfloat b = 0;
gfloat dg = 0;
gint i; gint i;
r = csf->rgb[0] * 255.0; if (softproof_format)
c = gegl_color_new (NULL);
if (r < 0.0 || r > 255.0) rgb[0] = csf->rgb[0];
{ rgb[1] = 0.f;
r = csf->oog_color[0]; rgb[2] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;
g = csf->oog_color[1];
b = csf->oog_color[2];
}
else
{
b = (csf->height - csf->y + 1) * 255.0 / csf->height;
dg = 255.0 / csf->width; dg = 1.f / csf->width;
}
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{ {
*p++ = r; gboolean in_gamut = TRUE;
*p++ = g;
*p++ = b;
g += dg; if (softproof_format)
{
gegl_color_set_pixel (c, rgbf_format, rgb);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
} }
}
if (in_gamut)
{
*p++ = rgb[0] * 255.0;
*p++ = rgb[1] * 255.0;
*p++ = rgb[2] * 255.0;
}
rgb[1] += dg;
}
g_clear_object (&c);
} }
static void static void
color_select_render_hue_saturation (ColorSelectFill *csf) color_select_render_hue_saturation (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat h, dh, s, v; gfloat hsv[3];
gfloat dh;
gint f; gint f;
gint i; gint i;
v = csf->hsv[2]; if (softproof_format)
c = gegl_color_new (NULL);
s = (gfloat) csf->y / csf->height; hsv[0] = 0.f;
s = CLAMP (s, 0.0, 1.0); hsv[1] = (gfloat) csf->y / csf->height;
s = 1.0 - s; hsv[1] = CLAMP (hsv[1], 0.f, 1.f);
hsv[1] = 1.f - hsv[1];
hsv[2] = csf->hsv[2];
h = 0;
dh = 360.0 / csf->width; dh = 360.0 / csf->width;
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
gfloat r, g, b; gfloat r, g, b;
gfloat h, s, v;
h = hsv[0];
s = hsv[1];
v = hsv[2];
f = ((h / 60) - (int) (h / 60)) * 255; f = ((h / 60) - (int) (h / 60)) * 255;
@ -1784,45 +1849,62 @@ color_select_render_hue_saturation (ColorSelectFill *csf)
break; break;
} }
if (r < 0.0 || r > 255.0 ||
g < 0.0 || g > 255.0 ||
b < 0.0 || b > 255.0)
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
}
else
{
*p++ = r; *p++ = r;
*p++ = g; *p++ = g;
*p++ = b; *p++ = b;
} }
h += dh; hsv[0] += dh;
} }
g_clear_object (&c);
} }
static void static void
color_select_render_hue_value (ColorSelectFill *csf) color_select_render_hue_value (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat h, dh, s, v; gfloat hsv[3];
gfloat dh;
gint f; gint f;
gint i; gint i;
s = csf->hsv[1]; if (softproof_format)
c = gegl_color_new (NULL);
v = (gfloat) csf->y / csf->height; hsv[0] = 0.f;
v = CLAMP (v, 0.0, 1.0); hsv[1] = csf->hsv[1];
v = 1.0 - v; hsv[2] = (gfloat) csf->y / csf->height;
hsv[2] = CLAMP (hsv[2], 0.f, 1.f);
hsv[2] = 1.f - hsv[2];
h = 0;
dh = 360.0 / csf->width; dh = 360.0 / csf->width;
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
gfloat r, g, b; gfloat r, g, b;
gfloat h, s, v;
h = hsv[0];
s = hsv[1];
v = hsv[2];
f = ((h / 60) - (int) (h / 60)) * 255; f = ((h / 60) - (int) (h / 60)) * 255;
@ -1875,95 +1957,216 @@ color_select_render_hue_value (ColorSelectFill *csf)
*p++ = g; *p++ = g;
*p++ = b; *p++ = b;
} }
h += dh;
} }
hsv[0] += dh;
}
g_clear_object (&c);
} }
static void static void
color_select_render_saturation_value (ColorSelectFill *csf) color_select_render_saturation_value (ColorSelectFill *csf)
{ {
GeglColor *c = NULL;
guchar *p = csf->buffer; guchar *p = csf->buffer;
gfloat hsv[3];
gfloat h, s, ds, v; gfloat h, s, ds, v;
gint f; gint f;
gint i; gint i;
h = (gfloat) csf->hsv[0] * 360.0; if (softproof_format)
if (h >= 360) c = gegl_color_new (NULL);
h -= 360;
h /= 60; hsv[0] = (gfloat) csf->hsv[0] * 360.0;
if (hsv[0] >= 360)
hsv[0] -= 360;
h = hsv[0] / 60;
f = (h - (gint) h) * 255; f = (h - (gint) h) * 255;
v = (gfloat) csf->y / csf->height; v = (gfloat) csf->y / csf->height;
v = CLAMP (v, 0.0, 1.0); v = CLAMP (v, 0.f, 1.f);
v = 1.0 - v; v = 1.f - v;
s = 0; s = 0.f;
hsv[1] = s;
hsv[2] = v;
ds = 1.0 / csf->width; ds = 1.0 / csf->width;
switch ((gint) h) switch ((gint) h)
{ {
case 0: case 0:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * 255; *p++ = v * 255;
*p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 - (s * (255 - f)));
*p++ = v * 255 * (1 - s); *p++ = v * 255 * (1 - s);
}
s += ds; hsv[1] = s += ds;
} }
break; break;
case 1: case 1:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * (255 - s * f); *p++ = v * (255 - s * f);
*p++ = v * 255; *p++ = v * 255;
*p++ = v * 255 * (1 - s); *p++ = v * 255 * (1 - s);
}
s += ds; hsv[1] = s += ds;
} }
break; break;
case 2: case 2:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * 255 * (1 - s); *p++ = v * 255 * (1 - s);
*p++ = v *255; *p++ = v *255;
*p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 - (s * (255 - f)));
}
s += ds; hsv[1] = s += ds;
} }
break; break;
case 3: case 3:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * 255 * (1 - s); *p++ = v * 255 * (1 - s);
*p++ = v * (255 - s * f); *p++ = v * (255 - s * f);
*p++ = v * 255; *p++ = v * 255;
}
s += ds; hsv[1] = s += ds;
} }
break; break;
case 4: case 4:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 - (s * (255 - f)));
*p++ = v * (255 * (1 - s)); *p++ = v * (255 * (1 - s));
*p++ = v * 255; *p++ = v * 255;
}
s += ds; hsv[1] = s += ds;
} }
break; break;
case 5: case 5:
for (i = 0; i < csf->width; i++) for (i = 0; i < csf->width; i++)
{
gboolean in_gamut = TRUE;
if (softproof_format)
{
gegl_color_set_pixel (c, hsvf_format, hsv);
if (gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
{
*p++ = csf->oog_color[0];
*p++ = csf->oog_color[1];
*p++ = csf->oog_color[2];
in_gamut = FALSE;
}
}
if (in_gamut)
{ {
*p++ = v * 255; *p++ = v * 255;
*p++ = v * 255 * (1 - s); *p++ = v * 255 * (1 - s);
*p++ = v * (255 - s * f); *p++ = v * (255 - s * f);
}
s += ds; hsv[1] = s += ds;
} }
break; break;
} }
g_clear_object (&c);
} }
static void static void