mirror of https://github.com/GNOME/gimp.git
libgimpcolor: new functions gimp_color_set_alpha() and…
… gimp_color_is_perceptually_identical(). gimp_color_is_perceptually_identical() is meant to replace gimp_rgb_distance() which is anyway always used to decide whether 2 colors can be considered equal. So rather than having a distance algorithm which we won't be able to change later on (if people start relying on specific values), let's just give the answer directly on what's a same color (perceptually) or not. Also now the distance is computed through the intermediate color space LCh which seems to be one of the most perceptually uniform space, therefore a good choice for such an algorithm (comparing distances on a non-perceptual uniform space doesn't make very much sense, since a same distance may be perceived differently in different subspaces).
This commit is contained in:
parent
39544f96b4
commit
9602926012
|
@ -0,0 +1,125 @@
|
|||
/* LIBGIMP - The GIMP Library
|
||||
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
||||
*
|
||||
* gimpcolor.c
|
||||
* Copyright (C) 2023 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include <babl/babl.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "gimpcolor.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION: gimpcolor
|
||||
* @title: GimpColor
|
||||
* @short_description: API to manipulate [class@Gegl.Color] objects.
|
||||
*
|
||||
* #GimpColor contains a few helper functions to manipulate [class@Gegl.Color]
|
||||
* objects more easily.
|
||||
**/
|
||||
|
||||
/**
|
||||
* gimp_color_set_alpha:
|
||||
* @color: a [class@Gegl.Color]
|
||||
* @alpha: new value for the alpha channel.
|
||||
*
|
||||
* Update the @alpha channel, and any other component if necessary (e.g. in case
|
||||
* of premultiplied channels), without changing the format of @color.
|
||||
*
|
||||
* If @color has no alpha component, this function is a no-op.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gimp_color_set_alpha (GeglColor *color,
|
||||
gdouble alpha)
|
||||
{
|
||||
const Babl *format;
|
||||
gdouble red;
|
||||
gdouble green;
|
||||
gdouble blue;
|
||||
guint8 pixel[40];
|
||||
|
||||
format = gegl_color_get_format (color);
|
||||
|
||||
gegl_color_get_rgba (color, &red, &green, &blue, NULL);
|
||||
gegl_color_set_rgba (color, red, green, blue, alpha);
|
||||
|
||||
/* I could stop at this point, but we want to keep the initial format as much
|
||||
* as possible. Since we made a round-trip through linear RGBA float, we need
|
||||
* to reset the right format.
|
||||
*
|
||||
* Also why we do this round trip is because we know we can just change the
|
||||
* alpha channel and babl fishes will do the appropriate conversion. I first
|
||||
* thought of updating the alpha channel directly by editing the raw data
|
||||
* depending on the format, but doing so would break e.g. with premultiplied
|
||||
* channels. Babl already has all the internal knowledge so let it do its
|
||||
* thing. The only risk is the possible precision loss during conversion.
|
||||
* Let's assume that since we use an unbounded 32-bit intermediate value
|
||||
* (float), the loss would be acceptable.
|
||||
*/
|
||||
gegl_color_get_pixel (color, format, pixel);
|
||||
gegl_color_set_pixel (color, format, pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_color_is_perceptually_identical:
|
||||
* @color1: a [class@Gegl.Color]
|
||||
* @color2: a [class@Gegl.Color]
|
||||
*
|
||||
* Determine whether @color1 and @color2 can be considered identical to the
|
||||
* human eyes, by computing the distance in a color space as perceptually
|
||||
* uniform as possible.
|
||||
*
|
||||
* Returns: whether the 2 colors can be considered the same for the human eyes.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
gboolean
|
||||
gimp_color_is_perceptually_identical (GeglColor *color1,
|
||||
GeglColor *color2)
|
||||
{
|
||||
gfloat pixel1[3];
|
||||
gfloat pixel2[3];
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_COLOR (color1), FALSE);
|
||||
g_return_val_if_fail (GEGL_IS_COLOR (color2), FALSE);
|
||||
|
||||
/* CIE LCh space is considered quite perceptually uniform, a bit better than
|
||||
* Lab on this aspect, AFAIU.
|
||||
*/
|
||||
gegl_color_get_pixel (color1, babl_format ("CIE LCH(ab) float"), pixel1);
|
||||
gegl_color_get_pixel (color2, babl_format ("CIE LCH(ab) float"), pixel2);
|
||||
|
||||
/* This is not a proper distance computation, but is acceptable for our use
|
||||
* case while being simpler. While we used to use 1e-6 as threshold with float
|
||||
* RGB, LCh is not in [0, 1] range, and some channels can reach over 300 with
|
||||
* wide gamut spaces. This is why use the threshold is 1e-4 right now.
|
||||
*/
|
||||
#define SQR(x) ((x) * (x))
|
||||
return (SQR (pixel1[0] - pixel2[0]) +
|
||||
SQR (pixel1[1] - pixel2[1]) +
|
||||
SQR (pixel1[2] - pixel2[2]) <= 1e-4);
|
||||
#undef SQR
|
||||
}
|
|
@ -19,6 +19,7 @@ EXPORTS
|
|||
gimp_cmyka_get_uchar
|
||||
gimp_cmyka_set
|
||||
gimp_cmyka_set_uchar
|
||||
gimp_color_is_perceptually_identical
|
||||
gimp_color_managed_get_color_profile
|
||||
gimp_color_managed_get_icc_profile
|
||||
gimp_color_managed_get_simulation_bpc
|
||||
|
@ -58,6 +59,7 @@ EXPORTS
|
|||
gimp_color_profile_new_rgb_srgb_linear
|
||||
gimp_color_profile_new_srgb_trc_from_color_profile
|
||||
gimp_color_profile_save_to_file
|
||||
gimp_color_set_alpha
|
||||
gimp_color_transform_can_gegl_copy
|
||||
gimp_color_transform_get_type
|
||||
gimp_color_transform_new
|
||||
|
|
|
@ -38,4 +38,22 @@
|
|||
|
||||
#undef __GIMP_COLOR_H_INSIDE__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* GEGL_TYPE_COLOR
|
||||
*/
|
||||
|
||||
#define GIMP_VALUE_HOLDS_COLOR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GEGL_TYPE_COLOR))
|
||||
|
||||
|
||||
void gimp_color_set_alpha (GeglColor *color,
|
||||
gdouble alpha);
|
||||
|
||||
gboolean gimp_color_is_perceptually_identical (GeglColor *color1,
|
||||
GeglColor *color2);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GIMP_COLOR_H__ */
|
||||
|
|
|
@ -28,12 +28,6 @@ G_BEGIN_DECLS
|
|||
/* For information look into the C source or the html documentation */
|
||||
|
||||
|
||||
/*
|
||||
* GIMP_TYPE_COLOR
|
||||
*/
|
||||
|
||||
#define GIMP_VALUE_HOLDS_COLOR(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GEGL_TYPE_COLOR))
|
||||
|
||||
/*
|
||||
* GIMP_TYPE_RGB
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,7 @@ libgimpcolor_sources = files(
|
|||
'gimpbilinear.c',
|
||||
'gimpcairo.c',
|
||||
'gimpcmyk.c',
|
||||
'gimpcolor.c',
|
||||
'gimpcolormanaged.c',
|
||||
'gimpcolorprofile.c',
|
||||
'gimpcolorspace.c',
|
||||
|
|
Loading…
Reference in New Issue