/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2016 Alberts Muktupāvels
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (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
* along with this program. If not, see .
*/
#include "config.h"
#include
#include "meta-color-spec-private.h"
#include "meta-gradient-private.h"
#include "meta-gradient-spec-private.h"
#include "meta-theme.h"
struct _MetaGradientSpec
{
MetaGradientType type;
GSList *color_specs;
};
struct _MetaAlphaGradientSpec
{
MetaGradientType type;
guchar *alphas;
gint n_alphas;
};
static cairo_pattern_t *
create_cairo_pattern_from_gradient_spec (const MetaGradientSpec *spec,
const MetaAlphaGradientSpec *alpha_spec,
GtkStyleContext *context)
{
gint n_colors;
cairo_pattern_t *pattern;
GSList *tmp;
gint i;
n_colors = g_slist_length (spec->color_specs);
if (n_colors == 0)
return NULL;
if (alpha_spec != NULL)
g_assert (n_colors == alpha_spec->n_alphas);
if (spec->type == META_GRADIENT_HORIZONTAL)
pattern = cairo_pattern_create_linear (0, 0, 1, 0);
else if (spec->type == META_GRADIENT_VERTICAL)
pattern = cairo_pattern_create_linear (0, 0, 0, 1);
else if (spec->type == META_GRADIENT_DIAGONAL)
pattern = cairo_pattern_create_linear (0, 0, 1, 1);
else
g_assert_not_reached ();
i = 0;
tmp = spec->color_specs;
while (tmp != NULL)
{
GdkRGBA color;
meta_color_spec_render (tmp->data, context, &color);
if (alpha_spec != NULL)
cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) n_colors,
color.red, color.green, color.blue,
alpha_spec->alphas[i]);
else
cairo_pattern_add_color_stop_rgb (pattern, i / (gfloat) n_colors,
color.red, color.green, color.blue);
tmp = tmp->next;
++i;
}
return pattern;
}
static void
free_color_spec (gpointer spec,
gpointer user_data)
{
meta_color_spec_free (spec);
}
MetaGradientSpec *
meta_gradient_spec_new (MetaGradientType type)
{
MetaGradientSpec *spec;
spec = g_new (MetaGradientSpec, 1);
spec->type = type;
spec->color_specs = NULL;
return spec;
}
void
meta_gradient_spec_free (MetaGradientSpec *spec)
{
g_return_if_fail (spec != NULL);
g_slist_foreach (spec->color_specs, free_color_spec, NULL);
g_slist_free (spec->color_specs);
g_free (spec);
}
void
meta_gradient_spec_add_color_spec (MetaGradientSpec *spec,
MetaColorSpec *color_spec)
{
spec->color_specs = g_slist_append (spec->color_specs, color_spec);
}
void
meta_gradient_spec_render (const MetaGradientSpec *spec,
const MetaAlphaGradientSpec *alpha_spec,
cairo_t *cr,
GtkStyleContext *context,
gint x,
gint y,
gint width,
gint height)
{
cairo_pattern_t *pattern;
pattern = create_cairo_pattern_from_gradient_spec (spec, alpha_spec, context);
if (pattern == NULL)
return;
cairo_save (cr);
cairo_rectangle (cr, x, y, width, height);
cairo_translate (cr, x, y);
cairo_scale (cr, width, height);
cairo_set_source (cr, pattern);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
cairo_restore (cr);
}
gboolean
meta_gradient_spec_validate (MetaGradientSpec *spec,
GError **error)
{
g_return_val_if_fail (spec != NULL, FALSE);
if (g_slist_length (spec->color_specs) < 2)
{
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
_("Gradients should have at least two colors"));
return FALSE;
}
return TRUE;
}
MetaAlphaGradientSpec *
meta_alpha_gradient_spec_new (MetaGradientType type,
gint n_alphas)
{
MetaAlphaGradientSpec *spec;
g_return_val_if_fail (n_alphas > 0, NULL);
spec = g_new0 (MetaAlphaGradientSpec, 1);
spec->type = type;
spec->alphas = g_new0 (guchar, n_alphas);
spec->n_alphas = n_alphas;
return spec;
}
void
meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
{
g_return_if_fail (spec != NULL);
g_free (spec->alphas);
g_free (spec);
}
void
meta_alpha_gradient_spec_add_alpha (MetaAlphaGradientSpec *spec,
gint n_alpha,
gdouble alpha)
{
spec->alphas[n_alpha] = (guchar) (alpha * 255);
}
guchar
meta_alpha_gradient_spec_get_alpha (MetaAlphaGradientSpec *spec,
gint n_alpha)
{
return spec->alphas[n_alpha];
}
gboolean
meta_alpha_gradient_spec_needs_alpha (MetaAlphaGradientSpec *spec)
{
return spec && (spec->n_alphas > 1 || spec->alphas[0] != 0xff);
}
GdkPixbuf *
meta_alpha_gradient_spec_apply_alpha (MetaAlphaGradientSpec *spec,
GdkPixbuf *pixbuf,
gboolean force_copy)
{
GdkPixbuf *new_pixbuf;
gboolean needs_alpha;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
needs_alpha = meta_alpha_gradient_spec_needs_alpha (spec);
if (!needs_alpha)
return pixbuf;
if (!gdk_pixbuf_get_has_alpha (pixbuf))
{
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
else if (force_copy)
{
new_pixbuf = gdk_pixbuf_copy (pixbuf);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
return pixbuf;
}
GdkPixbuf *
meta_alpha_gradient_spec_render (MetaAlphaGradientSpec *spec,
gint width,
gint height,
GdkRGBA color)
{
gboolean has_alpha;
GdkPixbuf *pixbuf;
guint32 rgba;
has_alpha = spec && (spec->n_alphas > 1 || spec->alphas[0] != 0xff);
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
rgba = 0xff;
rgba |= (gint) (color.red * 255) << 24;
rgba |= (gint) (color.green * 255) << 16;
rgba |= (gint) (color.blue * 255) << 8;
if (!has_alpha)
{
gdk_pixbuf_fill (pixbuf, rgba);
}
else if (spec->n_alphas == 1)
{
rgba &= ~0xff;
rgba |= spec->alphas[0];
gdk_pixbuf_fill (pixbuf, rgba);
}
else
{
gdk_pixbuf_fill (pixbuf, rgba);
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas,
spec->type);
}
return pixbuf;
}