1 /*
2 * Copyright (C) 2001 Havoc Pennington
3 * Copyright (C) 2016 Alberts Muktupāvels
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include <glib/gi18n-lib.h>
22
23 #include "meta-color-spec-private.h"
24 #include "meta-gradient-spec-private.h"
25 #include "meta-theme.h"
26
27 struct _MetaGradientSpec
28 {
29 MetaGradientType type;
30 GSList *color_specs;
31 };
32
33 struct _MetaAlphaGradientSpec
34 {
35 MetaGradientType type;
36 guchar *alphas;
37 gint n_alphas;
38 };
39
40
41 static cairo_pattern_t *
create_cairo_pattern_from_gradient_spec(const MetaGradientSpec * spec,const MetaAlphaGradientSpec * alpha_spec,GtkStyleContext * context)42 create_cairo_pattern_from_gradient_spec (const MetaGradientSpec *spec,
43 const MetaAlphaGradientSpec *alpha_spec,
44 GtkStyleContext *context)
45 {
46 gint n_colors;
47 cairo_pattern_t *pattern;
48 GSList *tmp;
49 gint i;
50
51 n_colors = g_slist_length (spec->color_specs);
52 if (n_colors == 0)
53 return NULL;
54
55 if (alpha_spec != NULL && alpha_spec->n_alphas != 1)
56 g_assert (n_colors == alpha_spec->n_alphas);
57
58 if (spec->type == META_GRADIENT_HORIZONTAL)
59 pattern = cairo_pattern_create_linear (0, 0, 1, 0);
60 else if (spec->type == META_GRADIENT_VERTICAL)
61 pattern = cairo_pattern_create_linear (0, 0, 0, 1);
62 else if (spec->type == META_GRADIENT_DIAGONAL)
63 pattern = cairo_pattern_create_linear (0, 0, 1, 1);
64 else
65 g_assert_not_reached ();
66
67 i = 0;
68 tmp = spec->color_specs;
69 while (tmp != NULL)
70 {
71 GdkRGBA color;
72
73 meta_color_spec_render (tmp->data, context, &color);
74
75 if (alpha_spec != NULL)
76 {
77 gdouble alpha;
78
79 if (alpha_spec->n_alphas == 1)
80 alpha = alpha_spec->alphas[0] / 255.0;
81 else
82 alpha = alpha_spec->alphas[i] / 255.0;
83
84 cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_colors - 1),
85 color.red, color.green, color.blue,
86 alpha);
87 }
88 else
89 cairo_pattern_add_color_stop_rgb (pattern, i / (gfloat) (n_colors - 1),
90 color.red, color.green, color.blue);
91
92 tmp = tmp->next;
93 ++i;
94 }
95
96 if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
97 {
98 cairo_pattern_destroy (pattern);
99 return NULL;
100 }
101
102 return pattern;
103 }
104
105 static void
free_color_spec(gpointer spec,gpointer user_data)106 free_color_spec (gpointer spec,
107 gpointer user_data)
108 {
109 meta_color_spec_free (spec);
110 }
111
112 MetaGradientSpec *
meta_gradient_spec_new(MetaGradientType type)113 meta_gradient_spec_new (MetaGradientType type)
114 {
115 MetaGradientSpec *spec;
116
117 spec = g_new (MetaGradientSpec, 1);
118
119 spec->type = type;
120 spec->color_specs = NULL;
121
122 return spec;
123 }
124
125 void
meta_gradient_spec_free(MetaGradientSpec * spec)126 meta_gradient_spec_free (MetaGradientSpec *spec)
127 {
128 g_return_if_fail (spec != NULL);
129
130 g_slist_foreach (spec->color_specs, free_color_spec, NULL);
131 g_slist_free (spec->color_specs);
132
133 g_free (spec);
134 }
135
136 void
meta_gradient_spec_add_color_spec(MetaGradientSpec * spec,MetaColorSpec * color_spec)137 meta_gradient_spec_add_color_spec (MetaGradientSpec *spec,
138 MetaColorSpec *color_spec)
139 {
140 spec->color_specs = g_slist_append (spec->color_specs, color_spec);
141 }
142
143 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)144 meta_gradient_spec_render (const MetaGradientSpec *spec,
145 const MetaAlphaGradientSpec *alpha_spec,
146 cairo_t *cr,
147 GtkStyleContext *context,
148 gdouble x,
149 gdouble y,
150 gdouble width,
151 gdouble height)
152 {
153 cairo_pattern_t *pattern;
154
155 pattern = create_cairo_pattern_from_gradient_spec (spec, alpha_spec, context);
156 if (pattern == NULL)
157 return;
158
159 cairo_save (cr);
160
161 cairo_rectangle (cr, x, y, width, height);
162
163 cairo_translate (cr, x, y);
164 cairo_scale (cr, width, height);
165
166 cairo_set_source (cr, pattern);
167 cairo_fill (cr);
168 cairo_pattern_destroy (pattern);
169
170 cairo_restore (cr);
171 }
172
173 gboolean
meta_gradient_spec_validate(MetaGradientSpec * spec,GError ** error)174 meta_gradient_spec_validate (MetaGradientSpec *spec,
175 GError **error)
176 {
177 g_return_val_if_fail (spec != NULL, FALSE);
178
179 if (g_slist_length (spec->color_specs) < 2)
180 {
181 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
182 _("Gradients should have at least two colors"));
183
184 return FALSE;
185 }
186
187 return TRUE;
188 }
189
190 MetaAlphaGradientSpec *
meta_alpha_gradient_spec_new(MetaGradientType type,gint n_alphas)191 meta_alpha_gradient_spec_new (MetaGradientType type,
192 gint n_alphas)
193 {
194 MetaAlphaGradientSpec *spec;
195
196 g_return_val_if_fail (n_alphas > 0, NULL);
197
198 spec = g_new0 (MetaAlphaGradientSpec, 1);
199
200 spec->type = type;
201 spec->alphas = g_new0 (guchar, n_alphas);
202 spec->n_alphas = n_alphas;
203
204 return spec;
205 }
206
207 void
meta_alpha_gradient_spec_free(MetaAlphaGradientSpec * spec)208 meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
209 {
210 g_return_if_fail (spec != NULL);
211
212 g_free (spec->alphas);
213 g_free (spec);
214 }
215
216 void
meta_alpha_gradient_spec_add_alpha(MetaAlphaGradientSpec * spec,gint n_alpha,gdouble alpha)217 meta_alpha_gradient_spec_add_alpha (MetaAlphaGradientSpec *spec,
218 gint n_alpha,
219 gdouble alpha)
220 {
221 spec->alphas[n_alpha] = (guchar) (alpha * 255);
222 }
223
224 guchar
meta_alpha_gradient_spec_get_alpha(MetaAlphaGradientSpec * spec,gint n_alpha)225 meta_alpha_gradient_spec_get_alpha (MetaAlphaGradientSpec *spec,
226 gint n_alpha)
227 {
228 return spec->alphas[n_alpha];
229 }
230
231 void
meta_alpha_gradient_spec_render(MetaAlphaGradientSpec * spec,GdkRGBA color,cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height)232 meta_alpha_gradient_spec_render (MetaAlphaGradientSpec *spec,
233 GdkRGBA color,
234 cairo_t *cr,
235 gdouble x,
236 gdouble y,
237 gdouble width,
238 gdouble height)
239 {
240 if (!spec || spec->n_alphas == 1)
241 {
242 if (spec)
243 color.alpha = spec->alphas[0] / 255.0;
244
245 gdk_cairo_set_source_rgba (cr, &color);
246 cairo_rectangle (cr, x, y, width, height);
247 cairo_fill (cr);
248 }
249 else
250 {
251 cairo_pattern_t *pattern;
252 gint n_alphas;
253 gint i;
254
255 /* Hardcoded in meta-theme-metacity.c */
256 g_assert (spec->type == META_GRADIENT_HORIZONTAL);
257
258 pattern = cairo_pattern_create_linear (0, 0, 1, 0);
259 n_alphas = spec->n_alphas;
260
261 for (i = 0; i < n_alphas; i++)
262 cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_alphas - 1),
263 color.red, color.green, color.blue,
264 spec->alphas[i] / 255.0);
265
266 if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
267 {
268 cairo_pattern_destroy (pattern);
269 return;
270 }
271
272 cairo_save (cr);
273 cairo_rectangle (cr, x, y, width, height);
274
275 cairo_translate (cr, x, y);
276 cairo_scale (cr, width, height);
277
278 cairo_set_source (cr, pattern);
279 cairo_fill (cr);
280
281 cairo_pattern_destroy (pattern);
282 cairo_restore (cr);
283 }
284 }
285
286 cairo_pattern_t *
meta_alpha_gradient_spec_get_mask(const MetaAlphaGradientSpec * spec)287 meta_alpha_gradient_spec_get_mask (const MetaAlphaGradientSpec *spec)
288 {
289 gint n_alphas;
290 cairo_pattern_t *pattern;
291 gint i;
292
293 /* Hardcoded in meta-theme-metacity.c */
294 g_assert (spec->type == META_GRADIENT_HORIZONTAL);
295
296 n_alphas = spec->n_alphas;
297 if (n_alphas == 0)
298 return NULL;
299
300 if (n_alphas == 1)
301 return cairo_pattern_create_rgba (0, 0, 0, spec->alphas[0] / 255.0);
302
303 pattern = cairo_pattern_create_linear (0, 0, 1, 0);
304
305 for (i = 0; i < n_alphas; i++)
306 cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_alphas - 1),
307 0, 0, 0, spec->alphas[i] / 255.0);
308
309 if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
310 {
311 cairo_pattern_destroy (pattern);
312 return NULL;
313 }
314
315 return pattern;
316 }
317