/*
* 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-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 && alpha_spec->n_alphas != 1)
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)
{
gdouble alpha;
if (alpha_spec->n_alphas == 1)
alpha = alpha_spec->alphas[0] / 255.0;
else
alpha = alpha_spec->alphas[i] / 255.0;
cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_colors - 1),
color.red, color.green, color.blue,
alpha);
}
else
cairo_pattern_add_color_stop_rgb (pattern, i / (gfloat) (n_colors - 1),
color.red, color.green, color.blue);
tmp = tmp->next;
++i;
}
if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
{
cairo_pattern_destroy (pattern);
return NULL;
}
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,
gdouble x,
gdouble y,
gdouble width,
gdouble 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];
}
void
meta_alpha_gradient_spec_render (MetaAlphaGradientSpec *spec,
GdkRGBA color,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
if (!spec || spec->n_alphas == 1)
{
if (spec)
color.alpha = spec->alphas[0] / 255.0;
gdk_cairo_set_source_rgba (cr, &color);
cairo_rectangle (cr, x, y, width, height);
cairo_fill (cr);
}
else
{
cairo_pattern_t *pattern;
gint n_alphas;
gint i;
/* Hardcoded in meta-theme-metacity.c */
g_assert (spec->type == META_GRADIENT_HORIZONTAL);
pattern = cairo_pattern_create_linear (0, 0, 1, 0);
n_alphas = spec->n_alphas;
for (i = 0; i < n_alphas; i++)
cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_alphas - 1),
color.red, color.green, color.blue,
spec->alphas[i] / 255.0);
if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
{
cairo_pattern_destroy (pattern);
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);
}
}
cairo_pattern_t *
meta_alpha_gradient_spec_get_mask (const MetaAlphaGradientSpec *spec)
{
gint n_alphas;
cairo_pattern_t *pattern;
gint i;
/* Hardcoded in meta-theme-metacity.c */
g_assert (spec->type == META_GRADIENT_HORIZONTAL);
n_alphas = spec->n_alphas;
if (n_alphas == 0)
return NULL;
if (n_alphas == 1)
return cairo_pattern_create_rgba (0, 0, 0, spec->alphas[0] / 255.0);
pattern = cairo_pattern_create_linear (0, 0, 1, 0);
for (i = 0; i < n_alphas; i++)
cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_alphas - 1),
0, 0, 0, spec->alphas[i] / 255.0);
if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
{
cairo_pattern_destroy (pattern);
return NULL;
}
return pattern;
}