2010-10-21 13:23:48 +00:00
|
|
|
|
/* GDK - The GIMP Drawing Kit
|
|
|
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
|
|
|
*
|
|
|
|
|
* 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 2 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
|
2012-02-27 13:01:10 +00:00
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
|
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
|
|
|
* files for a list of changes. These files are distributed with
|
|
|
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
2019-04-06 00:40:29 +00:00
|
|
|
|
|
|
|
|
|
#include "gdkrgbaprivate.h"
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
#include <string.h>
|
2012-01-09 03:16:36 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <math.h>
|
2010-10-21 13:23:48 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* SECTION:rgba_colors
|
2020-10-29 17:36:26 +00:00
|
|
|
|
* @Title: GdkRGBA
|
2010-10-21 13:23:48 +00:00
|
|
|
|
* @Short_description: RGBA colors
|
2010-12-06 17:56:11 +00:00
|
|
|
|
*
|
2020-10-29 17:36:26 +00:00
|
|
|
|
* #GdkRGBA is a convenient way to pass colors around.
|
2014-02-07 18:01:26 +00:00
|
|
|
|
* It’s based on cairo’s way to deal with colors and mirrors its behavior.
|
2010-12-06 17:56:11 +00:00
|
|
|
|
* All values are in the range from 0.0 to 1.0 inclusive. So the color
|
|
|
|
|
* (0.0, 0.0, 0.0, 0.0) represents transparent black and
|
|
|
|
|
* (1.0, 1.0, 1.0, 1.0) is opaque white. Other values will be clamped
|
|
|
|
|
* to this range when drawing.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
|
|
|
|
|
gdk_rgba_copy, gdk_rgba_free)
|
|
|
|
|
|
2010-12-06 17:56:11 +00:00
|
|
|
|
/**
|
|
|
|
|
* GdkRGBA:
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* @red: The intensity of the red channel from 0.0 to 1.0 inclusive
|
|
|
|
|
* @green: The intensity of the green channel from 0.0 to 1.0 inclusive
|
|
|
|
|
* @blue: The intensity of the blue channel from 0.0 to 1.0 inclusive
|
2010-12-06 17:56:11 +00:00
|
|
|
|
* @alpha: The opacity of the color from 0.0 for completely translucent to
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* 1.0 for opaque
|
2010-12-06 17:56:11 +00:00
|
|
|
|
*
|
2014-01-27 22:04:46 +00:00
|
|
|
|
* A #GdkRGBA is used to represent a (possibly translucent)
|
2018-03-19 15:13:04 +00:00
|
|
|
|
* color, in a way that is compatible with cairo’s notion of color.
|
2010-12-06 17:56:11 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_copy:
|
|
|
|
|
* @rgba: a #GdkRGBA
|
|
|
|
|
*
|
2014-01-27 22:04:46 +00:00
|
|
|
|
* Makes a copy of a #GdkRGBA.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* The result must be freed through gdk_rgba_free().
|
|
|
|
|
*
|
|
|
|
|
* Returns: A newly allocated #GdkRGBA, with the same contents as @rgba
|
|
|
|
|
*/
|
2010-10-21 13:23:48 +00:00
|
|
|
|
GdkRGBA *
|
2010-12-05 13:54:43 +00:00
|
|
|
|
gdk_rgba_copy (const GdkRGBA *rgba)
|
2010-10-21 13:23:48 +00:00
|
|
|
|
{
|
2012-01-08 00:00:47 +00:00
|
|
|
|
return g_slice_dup (GdkRGBA, rgba);
|
2010-10-21 13:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_free:
|
|
|
|
|
* @rgba: a #GdkRGBA
|
|
|
|
|
*
|
2014-01-27 22:04:46 +00:00
|
|
|
|
* Frees a #GdkRGBA created with gdk_rgba_copy()
|
2011-02-09 07:43:07 +00:00
|
|
|
|
*/
|
2010-10-21 13:23:48 +00:00
|
|
|
|
void
|
|
|
|
|
gdk_rgba_free (GdkRGBA *rgba)
|
|
|
|
|
{
|
|
|
|
|
g_slice_free (GdkRGBA, rgba);
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-18 21:31:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_is_clear:
|
|
|
|
|
* @rgba: a #GdkRGBA
|
|
|
|
|
*
|
|
|
|
|
* Checks if an @rgba value is transparent. That is, drawing with the value
|
|
|
|
|
* would not produce any change.
|
|
|
|
|
*
|
2017-07-06 13:03:08 +00:00
|
|
|
|
* Returns: %TRUE if the @rgba is clear
|
2016-12-18 21:31:18 +00:00
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gdk_rgba_is_clear (const GdkRGBA *rgba)
|
|
|
|
|
{
|
2019-12-10 13:22:52 +00:00
|
|
|
|
return rgba->alpha < ((float) 0x00ff / (float) 0xffff);
|
2016-12-18 21:31:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_is_opaque:
|
|
|
|
|
* @rgba: a #GdkRGBA
|
|
|
|
|
*
|
|
|
|
|
* Checks if an @rgba value is opaque. That is, drawing with the value
|
|
|
|
|
* will not retain any results from previous contents.
|
|
|
|
|
*
|
2017-07-06 13:03:08 +00:00
|
|
|
|
* Returns: %TRUE if the @rgba is opaque
|
2016-12-18 21:31:18 +00:00
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gdk_rgba_is_opaque (const GdkRGBA *rgba)
|
|
|
|
|
{
|
2019-12-10 13:22:52 +00:00
|
|
|
|
return rgba->alpha > ((float)0xff00 / (float)0xffff);
|
2016-12-18 21:31:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-02 19:30:44 +00:00
|
|
|
|
#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
|
|
|
|
|
|
|
|
|
|
/* Parses a single color component from a rgb() or rgba() specification
|
|
|
|
|
* according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
|
|
|
|
|
* in what we accept as follows:
|
|
|
|
|
*
|
|
|
|
|
* - For non-percentage values, we accept floats in the range 0-255
|
|
|
|
|
* not just [0-9]+ integers
|
|
|
|
|
* - For percentage values we accept any float, not just
|
2014-02-07 19:03:49 +00:00
|
|
|
|
* [ 0-9]+ | [0-9]* “.” [0-9]+
|
2010-11-02 19:30:44 +00:00
|
|
|
|
* - We accept mixed percentages and non-percentages in a single
|
|
|
|
|
* rgb() or rgba() specification.
|
|
|
|
|
*/
|
2012-01-09 03:16:36 +00:00
|
|
|
|
static gboolean
|
2020-07-24 18:40:36 +00:00
|
|
|
|
parse_rgb_value (const char *str,
|
|
|
|
|
char **endp,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double *number)
|
2010-11-02 19:30:44 +00:00
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
|
2012-01-09 03:16:36 +00:00
|
|
|
|
*number = g_ascii_strtod (str, endp);
|
|
|
|
|
if (errno == ERANGE || *endp == str ||
|
|
|
|
|
isinf (*number) || isnan (*number))
|
|
|
|
|
return FALSE;
|
2010-11-02 19:30:44 +00:00
|
|
|
|
|
|
|
|
|
p = *endp;
|
|
|
|
|
|
|
|
|
|
SKIP_WHITESPACES (p);
|
|
|
|
|
|
|
|
|
|
if (*p == '%')
|
|
|
|
|
{
|
|
|
|
|
*endp = (char *)(p + 1);
|
2012-01-09 03:16:36 +00:00
|
|
|
|
*number = CLAMP(*number / 100., 0., 1.);
|
2010-11-02 19:30:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-01-09 03:16:36 +00:00
|
|
|
|
*number = CLAMP(*number / 255., 0., 1.);
|
2010-11-02 19:30:44 +00:00
|
|
|
|
}
|
2012-01-09 03:16:36 +00:00
|
|
|
|
|
|
|
|
|
return TRUE;
|
2010-11-02 19:30:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_parse:
|
2016-02-02 15:08:10 +00:00
|
|
|
|
* @rgba: the #GdkRGBA to fill in
|
2010-11-28 18:49:47 +00:00
|
|
|
|
* @spec: the string specifying the color
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* Parses a textual representation of a color, filling in
|
2014-01-27 22:13:43 +00:00
|
|
|
|
* the @red, @green, @blue and @alpha fields of the @rgba #GdkRGBA.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* The string can be either one of:
|
2014-02-02 06:07:39 +00:00
|
|
|
|
* - A standard name (Taken from the X11 rgb.txt file).
|
2014-02-09 22:24:06 +00:00
|
|
|
|
* - A hexadecimal value in the form “\#rgb”, “\#rrggbb”,
|
|
|
|
|
* “\#rrrgggbbb” or ”\#rrrrggggbbbb”
|
2020-08-03 03:23:49 +00:00
|
|
|
|
* - A hexadecimal value in the form “\#rgba”, “\#rrggbbaa”,
|
|
|
|
|
* or ”\#rrrrggggbbbbaaaa”
|
2014-02-07 19:03:49 +00:00
|
|
|
|
* - A RGB color in the form “rgb(r,g,b)” (In this case the color will
|
2014-02-02 06:07:39 +00:00
|
|
|
|
* have full opacity)
|
2014-02-07 19:03:49 +00:00
|
|
|
|
* - A RGBA color in the form “rgba(r,g,b,a)”
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
2014-02-07 19:03:49 +00:00
|
|
|
|
* Where “r”, “g”, “b” and “a” are respectively the red, green, blue and
|
2020-08-03 03:23:49 +00:00
|
|
|
|
* alpha color values. In the last two cases, “r”, “g”, and “b” are either
|
|
|
|
|
* integers in the range 0 to 255 or percentage values in the range 0% to
|
|
|
|
|
* 100%, and a is a floating point value in the range 0 to 1.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the parsing succeeded
|
2011-02-09 07:43:07 +00:00
|
|
|
|
*/
|
2010-10-21 13:23:48 +00:00
|
|
|
|
gboolean
|
2020-08-03 03:23:49 +00:00
|
|
|
|
gdk_rgba_parse (GdkRGBA *rgba,
|
2020-07-24 18:40:36 +00:00
|
|
|
|
const char *spec)
|
2010-10-21 13:23:48 +00:00
|
|
|
|
{
|
|
|
|
|
gboolean has_alpha;
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double r, g, b, a;
|
2020-07-24 18:40:36 +00:00
|
|
|
|
char *str = (char *) spec;
|
|
|
|
|
char *p;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
|
2014-08-27 21:14:15 +00:00
|
|
|
|
g_return_val_if_fail (spec != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
if (strncmp (str, "rgba", 4) == 0)
|
|
|
|
|
{
|
|
|
|
|
has_alpha = TRUE;
|
|
|
|
|
str += 4;
|
|
|
|
|
}
|
|
|
|
|
else if (strncmp (str, "rgb", 3) == 0)
|
|
|
|
|
{
|
|
|
|
|
has_alpha = FALSE;
|
|
|
|
|
a = 1;
|
|
|
|
|
str += 3;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PangoColor pango_color;
|
2020-08-03 03:23:49 +00:00
|
|
|
|
guint16 alpha;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
|
|
|
|
|
/* Resort on PangoColor for rgb.txt color
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* map and '#' prefixed colors
|
|
|
|
|
*/
|
2020-08-03 03:23:49 +00:00
|
|
|
|
if (pango_color_parse_with_alpha (&pango_color, &alpha, str))
|
2010-10-21 13:23:48 +00:00
|
|
|
|
{
|
|
|
|
|
if (rgba)
|
|
|
|
|
{
|
|
|
|
|
rgba->red = pango_color.red / 65535.;
|
|
|
|
|
rgba->green = pango_color.green / 65535.;
|
|
|
|
|
rgba->blue = pango_color.blue / 65535.;
|
2020-08-03 03:23:49 +00:00
|
|
|
|
rgba->alpha = alpha / 65535.;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
|
|
|
|
|
if (*str != '(')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
/* Parse red */
|
|
|
|
|
SKIP_WHITESPACES (str);
|
2012-01-09 03:16:36 +00:00
|
|
|
|
if (!parse_rgb_value (str, &str, &r))
|
|
|
|
|
return FALSE;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
|
|
|
|
|
if (*str != ',')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
/* Parse green */
|
|
|
|
|
SKIP_WHITESPACES (str);
|
2012-01-09 03:16:36 +00:00
|
|
|
|
if (!parse_rgb_value (str, &str, &g))
|
|
|
|
|
return FALSE;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
|
|
|
|
|
if (*str != ',')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
/* Parse blue */
|
|
|
|
|
SKIP_WHITESPACES (str);
|
2012-01-09 03:16:36 +00:00
|
|
|
|
if (!parse_rgb_value (str, &str, &b))
|
|
|
|
|
return FALSE;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
|
|
|
|
|
if (has_alpha)
|
|
|
|
|
{
|
|
|
|
|
if (*str != ',')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
SKIP_WHITESPACES (str);
|
2012-01-09 03:16:36 +00:00
|
|
|
|
a = g_ascii_strtod (str, &p);
|
|
|
|
|
if (errno == ERANGE || p == str ||
|
|
|
|
|
isinf (a) || isnan (a))
|
|
|
|
|
return FALSE;
|
|
|
|
|
str = p;
|
2010-10-21 13:23:48 +00:00
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*str != ')')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2012-01-09 03:16:36 +00:00
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
SKIP_WHITESPACES (str);
|
|
|
|
|
|
|
|
|
|
if (*str != '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
if (rgba)
|
|
|
|
|
{
|
|
|
|
|
rgba->red = CLAMP (r, 0, 1);
|
|
|
|
|
rgba->green = CLAMP (g, 0, 1);
|
|
|
|
|
rgba->blue = CLAMP (b, 0, 1);
|
|
|
|
|
rgba->alpha = CLAMP (a, 0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-02 19:30:44 +00:00
|
|
|
|
#undef SKIP_WHITESPACES
|
|
|
|
|
|
2010-10-21 13:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_hash:
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* @p: (type GdkRGBA): a #GdkRGBA pointer
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* A hash function suitable for using for a hash
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* table that stores #GdkRGBAs.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
2014-02-19 23:49:43 +00:00
|
|
|
|
* Returns: The hash value for @p
|
2011-02-09 07:43:07 +00:00
|
|
|
|
*/
|
2010-10-21 13:23:48 +00:00
|
|
|
|
guint
|
|
|
|
|
gdk_rgba_hash (gconstpointer p)
|
|
|
|
|
{
|
|
|
|
|
const GdkRGBA *rgba = p;
|
|
|
|
|
|
|
|
|
|
return ((guint) (rgba->red * 65535) +
|
2011-02-09 07:43:07 +00:00
|
|
|
|
((guint) (rgba->green * 65535) << 11) +
|
|
|
|
|
((guint) (rgba->blue * 65535) << 22) +
|
|
|
|
|
((guint) (rgba->alpha * 65535) >> 6));
|
2010-10-21 13:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_equal:
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* @p1: (type GdkRGBA): a #GdkRGBA pointer
|
|
|
|
|
* @p2: (type GdkRGBA): another #GdkRGBA pointer
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* Compares two RGBA colors.
|
|
|
|
|
*
|
2014-02-19 23:49:43 +00:00
|
|
|
|
* Returns: %TRUE if the two colors compare equal
|
2011-02-09 07:43:07 +00:00
|
|
|
|
*/
|
2010-10-21 13:23:48 +00:00
|
|
|
|
gboolean
|
|
|
|
|
gdk_rgba_equal (gconstpointer p1,
|
|
|
|
|
gconstpointer p2)
|
|
|
|
|
{
|
|
|
|
|
const GdkRGBA *rgba1, *rgba2;
|
|
|
|
|
|
|
|
|
|
rgba1 = p1;
|
|
|
|
|
rgba2 = p2;
|
|
|
|
|
|
|
|
|
|
if (rgba1->red == rgba2->red &&
|
|
|
|
|
rgba1->green == rgba2->green &&
|
|
|
|
|
rgba1->blue == rgba2->blue &&
|
|
|
|
|
rgba1->alpha == rgba2->alpha)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gdk_rgba_to_string:
|
|
|
|
|
* @rgba: a #GdkRGBA
|
|
|
|
|
*
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* Returns a textual specification of @rgba in the form
|
2018-12-17 19:57:09 +00:00
|
|
|
|
* `rgb(r,g,b)` or
|
2020-07-12 08:55:31 +00:00
|
|
|
|
* `rgba(r,g,b,a)`,
|
2014-02-07 19:03:49 +00:00
|
|
|
|
* where “r”, “g”, “b” and “a” represent the red, green,
|
2018-12-17 19:59:28 +00:00
|
|
|
|
* blue and alpha values respectively. “r”, “g”, and “b” are
|
|
|
|
|
* represented as integers in the range 0 to 255, and “a”
|
2018-12-17 20:13:51 +00:00
|
|
|
|
* is represented as a floating point value in the range 0 to 1.
|
2010-11-02 19:30:44 +00:00
|
|
|
|
*
|
2018-12-17 20:13:51 +00:00
|
|
|
|
* These string forms are string forms that are supported by
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* the CSS3 colors module, and can be parsed by gdk_rgba_parse().
|
|
|
|
|
*
|
2013-05-22 15:37:51 +00:00
|
|
|
|
* Note that this string representation may lose some
|
2018-12-17 19:59:28 +00:00
|
|
|
|
* precision, since “r”, “g” and “b” are represented as 8-bit
|
2011-02-09 07:43:07 +00:00
|
|
|
|
* integers. If this is a concern, you should use a
|
|
|
|
|
* different representation.
|
2010-10-21 13:23:48 +00:00
|
|
|
|
*
|
|
|
|
|
* Returns: A newly allocated text string
|
2011-02-09 07:43:07 +00:00
|
|
|
|
*/
|
2020-07-24 18:40:36 +00:00
|
|
|
|
char *
|
2010-10-30 01:16:52 +00:00
|
|
|
|
gdk_rgba_to_string (const GdkRGBA *rgba)
|
2010-10-21 13:23:48 +00:00
|
|
|
|
{
|
2010-11-02 19:30:44 +00:00
|
|
|
|
if (rgba->alpha > 0.999)
|
|
|
|
|
{
|
|
|
|
|
return g_strdup_printf ("rgb(%d,%d,%d)",
|
2011-02-09 07:43:07 +00:00
|
|
|
|
(int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
|
|
|
|
|
(int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
|
|
|
|
|
(int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
|
2010-11-02 19:30:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-24 18:40:36 +00:00
|
|
|
|
char alpha[G_ASCII_DTOSTR_BUF_SIZE];
|
2010-10-25 16:10:48 +00:00
|
|
|
|
|
2019-05-29 12:29:25 +00:00
|
|
|
|
g_ascii_formatd (alpha, G_ASCII_DTOSTR_BUF_SIZE, "%g", CLAMP (rgba->alpha, 0, 1));
|
2010-10-25 16:10:48 +00:00
|
|
|
|
|
2010-11-02 19:30:44 +00:00
|
|
|
|
return g_strdup_printf ("rgba(%d,%d,%d,%s)",
|
2011-02-09 07:43:07 +00:00
|
|
|
|
(int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
|
|
|
|
|
(int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
|
|
|
|
|
(int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
|
|
|
|
|
alpha);
|
2010-11-02 19:30:44 +00:00
|
|
|
|
}
|
2010-10-21 13:23:48 +00:00
|
|
|
|
}
|
2019-04-06 00:40:29 +00:00
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
parse_color_channel_value (GtkCssParser *parser,
|
2019-12-10 13:22:52 +00:00
|
|
|
|
float *value,
|
2019-04-06 00:40:29 +00:00
|
|
|
|
gboolean is_percentage)
|
|
|
|
|
{
|
2019-12-10 13:22:52 +00:00
|
|
|
|
double dvalue;
|
|
|
|
|
|
2019-04-06 00:40:29 +00:00
|
|
|
|
if (is_percentage)
|
|
|
|
|
{
|
2019-12-10 13:22:52 +00:00
|
|
|
|
if (!gtk_css_parser_consume_percentage (parser, &dvalue))
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
2019-12-10 13:22:52 +00:00
|
|
|
|
*value = CLAMP (dvalue, 0.0, 100.0) / 100.0;
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-10 13:22:52 +00:00
|
|
|
|
if (!gtk_css_parser_consume_number (parser, &dvalue))
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
2019-12-10 13:22:52 +00:00
|
|
|
|
*value = CLAMP (dvalue, 0.0, 255.0) / 255.0;
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
|
|
|
|
parse_color_channel (GtkCssParser *parser,
|
|
|
|
|
guint arg,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA *rgba = data;
|
2019-12-10 13:22:52 +00:00
|
|
|
|
double dvalue;
|
2019-04-06 00:40:29 +00:00
|
|
|
|
|
|
|
|
|
switch (arg)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
/* We abuse rgba->alpha to store if we use percentages or numbers */
|
|
|
|
|
if (gtk_css_token_is (gtk_css_parser_get_token (parser), GTK_CSS_TOKEN_PERCENTAGE))
|
|
|
|
|
rgba->alpha = 1.0;
|
|
|
|
|
else
|
|
|
|
|
rgba->alpha = 0.0;
|
|
|
|
|
|
|
|
|
|
if (!parse_color_channel_value (parser, &rgba->red, rgba->alpha != 0.0))
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
if (!parse_color_channel_value (parser, &rgba->green, rgba->alpha != 0.0))
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
if (!parse_color_channel_value (parser, &rgba->blue, rgba->alpha != 0.0))
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case 3:
|
2019-12-10 13:22:52 +00:00
|
|
|
|
if (!gtk_css_parser_consume_number (parser, &dvalue))
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2019-12-10 13:22:52 +00:00
|
|
|
|
rgba->alpha = CLAMP (dvalue, 0.0, 1.0);
|
2019-04-06 00:40:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
rgba_init_chars (GdkRGBA *rgba,
|
|
|
|
|
const char s[8])
|
|
|
|
|
{
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!g_ascii_isxdigit (s[i]))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rgba->red = (g_ascii_xdigit_value (s[0]) * 16 + g_ascii_xdigit_value (s[1])) / 255.0;
|
|
|
|
|
rgba->green = (g_ascii_xdigit_value (s[2]) * 16 + g_ascii_xdigit_value (s[3])) / 255.0;
|
|
|
|
|
rgba->blue = (g_ascii_xdigit_value (s[4]) * 16 + g_ascii_xdigit_value (s[5])) / 255.0;
|
|
|
|
|
rgba->alpha = (g_ascii_xdigit_value (s[6]) * 16 + g_ascii_xdigit_value (s[7])) / 255.0;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
gdk_rgba_parser_parse (GtkCssParser *parser,
|
|
|
|
|
GdkRGBA *rgba)
|
|
|
|
|
{
|
|
|
|
|
const GtkCssToken *token;
|
|
|
|
|
|
|
|
|
|
token = gtk_css_parser_get_token (parser);
|
|
|
|
|
if (gtk_css_token_is_function (token, "rgb"))
|
|
|
|
|
{
|
|
|
|
|
if (!gtk_css_parser_consume_function (parser, 3, 3, parse_color_channel, rgba))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
rgba->alpha = 1.0;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (gtk_css_token_is_function (token, "rgba"))
|
|
|
|
|
{
|
|
|
|
|
return gtk_css_parser_consume_function (parser, 4, 4, parse_color_channel, rgba);
|
|
|
|
|
}
|
|
|
|
|
else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID) ||
|
|
|
|
|
gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_UNRESTRICTED))
|
|
|
|
|
{
|
|
|
|
|
const char *s = token->string.string;
|
|
|
|
|
|
|
|
|
|
switch (strlen (s))
|
|
|
|
|
{
|
|
|
|
|
case 3:
|
|
|
|
|
if (!rgba_init_chars (rgba, (char[8]) {s[0], s[0], s[1], s[1], s[2], s[2], 'F', 'F' }))
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_value (parser, "Hash code is not a valid hex color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
if (!rgba_init_chars (rgba, (char[8]) {s[0], s[0], s[1], s[1], s[2], s[2], s[3], s[3] }))
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_value (parser, "Hash code is not a valid hex color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
if (!rgba_init_chars (rgba, (char[8]) {s[0], s[1], s[2], s[3], s[4], s[5], 'F', 'F' }))
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_value (parser, "Hash code is not a valid hex color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
|
if (!rgba_init_chars (rgba, s))
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_value (parser, "Hash code is not a valid hex color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
gtk_css_parser_error_value (parser, "Hash code is not a valid hex color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_css_parser_consume_token (parser);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
|
|
|
|
|
{
|
|
|
|
|
if (gtk_css_token_is_ident (token, "transparent"))
|
|
|
|
|
{
|
|
|
|
|
*rgba = (GdkRGBA) { 0, 0, 0, 0 };
|
|
|
|
|
}
|
|
|
|
|
else if (gdk_rgba_parse (rgba, token->string.string))
|
|
|
|
|
{
|
|
|
|
|
/* everything's fine */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_syntax (parser, "\"%s\" is not a valid color name.", token->string.string);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_css_parser_consume_token (parser);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_css_parser_error_syntax (parser, "Expected a valid color.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|