gtk2/gtk/gtksymboliccolor.c
Carlos Garnacho 142a72637d Add GtkGradient.
This type defines a gradient between 2 symbolic colors, with any number
of color stop between these. At the moment it has been put besides
GtkSymbolicColor, although should be completely private, it will likely
need extending in the future for radial gradients.

At some point, it maybe should also be used for images, so painting
both a gradient and an image is consistent.
2010-12-04 15:38:11 +01:00

375 lines
9.0 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.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 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
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "gtksymboliccolor.h"
#include "gtkstyleset.h"
#include "gtkintl.h"
G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
gtk_gradient_ref, gtk_gradient_unref)
/* Symbolic colors */
typedef enum {
COLOR_TYPE_LITERAL,
COLOR_TYPE_NAME,
COLOR_TYPE_SHADE,
COLOR_TYPE_MIX
} ColorType;
struct GtkSymbolicColor
{
ColorType type;
guint ref_count;
union
{
GdkColor color;
gchar *name;
struct
{
GtkSymbolicColor *color;
gdouble factor;
} shade;
struct
{
GtkSymbolicColor *color1;
GtkSymbolicColor *color2;
gdouble factor;
} mix;
};
};
typedef struct ColorStop ColorStop;
struct ColorStop
{
gdouble offset;
GtkSymbolicColor *color;
};
struct GtkGradient
{
gdouble x0;
gdouble y0;
gdouble x1;
gdouble y1;
GArray *stops;
guint ref_count;
};
GtkSymbolicColor *
gtk_symbolic_color_new_literal (GdkColor *color)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color != NULL, NULL);
symbolic_color = g_slice_new0 (GtkSymbolicColor);
symbolic_color->type = COLOR_TYPE_LITERAL;
symbolic_color->color = *color;
symbolic_color->ref_count = 1;
return symbolic_color;
}
GtkSymbolicColor *
gtk_symbolic_color_new_name (const gchar *name)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (name != NULL, NULL);
symbolic_color = g_slice_new0 (GtkSymbolicColor);
symbolic_color->type = COLOR_TYPE_NAME;
symbolic_color->name = g_strdup (name);
symbolic_color->ref_count = 1;
return symbolic_color;
}
GtkSymbolicColor *
gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
gdouble factor)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color != NULL, NULL);
symbolic_color = g_slice_new0 (GtkSymbolicColor);
symbolic_color->type = COLOR_TYPE_SHADE;
symbolic_color->shade.color = gtk_symbolic_color_ref (color);
symbolic_color->shade.factor = CLAMP (factor, 0, 1);
symbolic_color->ref_count = 1;
return symbolic_color;
}
GtkSymbolicColor *
gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
GtkSymbolicColor *color2,
gdouble factor)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color1 != NULL, NULL);
g_return_val_if_fail (color1 != NULL, NULL);
symbolic_color = g_slice_new0 (GtkSymbolicColor);
symbolic_color->type = COLOR_TYPE_MIX;
symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
symbolic_color->mix.factor = CLAMP (factor, 0, 1);
symbolic_color->ref_count = 1;
return symbolic_color;
}
GtkSymbolicColor *
gtk_symbolic_color_ref (GtkSymbolicColor *color)
{
g_return_val_if_fail (color != NULL, NULL);
color->ref_count++;
return color;
}
void
gtk_symbolic_color_unref (GtkSymbolicColor *color)
{
g_return_if_fail (color != NULL);
color->ref_count--;
if (color->ref_count == 0)
{
switch (color->type)
{
case COLOR_TYPE_NAME:
g_free (color->name);
break;
case COLOR_TYPE_SHADE:
gtk_symbolic_color_unref (color->shade.color);
break;
case COLOR_TYPE_MIX:
gtk_symbolic_color_unref (color->mix.color1);
gtk_symbolic_color_unref (color->mix.color2);
break;
default:
break;
}
g_slice_free (GtkSymbolicColor, color);
}
}
gboolean
gtk_symbolic_color_resolve (GtkSymbolicColor *color,
GtkStyleSet *style_set,
GdkColor *resolved_color)
{
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
g_return_val_if_fail (resolved_color != NULL, FALSE);
switch (color->type)
{
case COLOR_TYPE_LITERAL:
*resolved_color = color->color;
return TRUE;
case COLOR_TYPE_NAME:
{
GtkSymbolicColor *named_color;
named_color = gtk_style_set_lookup_color (style_set, color->name);
if (!named_color)
return FALSE;
return gtk_symbolic_color_resolve (named_color, style_set, resolved_color);
}
break;
case COLOR_TYPE_SHADE:
{
GdkColor shade;
if (!gtk_symbolic_color_resolve (color->shade.color, style_set, &shade))
return FALSE;
resolved_color->red = CLAMP (shade.red * color->shade.factor, 0, 65535);
resolved_color->green = CLAMP (shade.green * color->shade.factor, 0, 65535);
resolved_color->blue = CLAMP (shade.blue * color->shade.factor, 0, 65535);
return TRUE;
}
break;
case COLOR_TYPE_MIX:
{
GdkColor color1, color2;
if (!gtk_symbolic_color_resolve (color->mix.color1, style_set, &color1))
return FALSE;
if (!gtk_symbolic_color_resolve (color->mix.color2, style_set, &color2))
return FALSE;
resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 65535);
resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 65535);
resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 65535);
return TRUE;
}
break;
default:
g_assert_not_reached ();
}
return FALSE;
}
GType
gtk_symbolic_color_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (!type))
type = g_boxed_type_register_static (I_("GtkSymbolicColor"),
(GBoxedCopyFunc) gtk_symbolic_color_ref,
(GBoxedFreeFunc) gtk_symbolic_color_unref);
return type;
}
/* GtkGradient */
GtkGradient *
gtk_gradient_new_linear (gdouble x0,
gdouble y0,
gdouble x1,
gdouble y1)
{
GtkGradient *gradient;
gradient = g_slice_new (GtkGradient);
gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
gradient->x0 = x0;
gradient->y0 = y0;
gradient->x1 = x1;
gradient->y1 = y1;
gradient->ref_count = 1;
return gradient;
}
void
gtk_gradient_add_color_stop (GtkGradient *gradient,
gdouble offset,
GtkSymbolicColor *color)
{
ColorStop stop;
g_return_if_fail (gradient != NULL);
stop.offset = offset;
stop.color = gtk_symbolic_color_ref (color);
g_array_append_val (gradient->stops, stop);
}
GtkGradient *
gtk_gradient_ref (GtkGradient *gradient)
{
g_return_val_if_fail (gradient != NULL, NULL);
gradient->ref_count++;
return gradient;
}
void
gtk_gradient_unref (GtkGradient *gradient)
{
g_return_if_fail (gradient != NULL);
gradient->ref_count--;
if (gradient->ref_count == 0)
{
guint i;
for (i = 0; i < gradient->stops->len; i++)
{
ColorStop *stop;
stop = &g_array_index (gradient->stops, ColorStop, i);
gtk_symbolic_color_unref (stop->color);
}
g_array_free (gradient->stops, TRUE);
g_slice_free (GtkGradient, gradient);
}
}
gboolean
gtk_gradient_resolve (GtkGradient *gradient,
GtkStyleSet *style_set,
cairo_pattern_t **resolved_gradient)
{
cairo_pattern_t *pattern;
guint i;
g_return_val_if_fail (gradient != NULL, FALSE);
g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
g_return_val_if_fail (resolved_gradient != NULL, FALSE);
pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
gradient->x1, gradient->y1);
for (i = 0; i < gradient->stops->len; i++)
{
ColorStop *stop;
GdkColor color;
stop = &g_array_index (gradient->stops, ColorStop, i);
if (!gtk_symbolic_color_resolve (stop->color, style_set, &color))
{
cairo_pattern_destroy (pattern);
return FALSE;
}
cairo_pattern_add_color_stop_rgb (pattern, stop->offset,
color.red / 65535.,
color.green / 65535.,
color.blue / 65535.);
}
*resolved_gradient = pattern;
return TRUE;
}