1/*
2 * Copyright © 2016 Red Hat Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Matthias Clasen <mclasen@redhat.com>
18 */
19
20#include "config.h"
21
22#include "gtkcssimagerecolorprivate.h"
23#include "gtkcssimageprivate.h"
24#include "gtkcsspalettevalueprivate.h"
25#include "gtkcsscolorvalueprivate.h"
26#include "gtkiconthemeprivate.h"
27#include "gdkpixbufutilsprivate.h"
28
29#include "gtkstyleproviderprivate.h"
30
31G_DEFINE_TYPE (GtkCssImageRecolor, _gtk_css_image_recolor, GTK_TYPE_CSS_IMAGE)
32
33static void
34gtk_css_image_recolor_print (GtkCssImage *image,
35 GString *string)
36{
37 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
38 char *uri;
39
40 g_string_append (string, val: "-gtk-recolor(url(");
41 uri = g_file_get_uri (file: recolor->file);
42 g_string_append (string, val: uri);
43 g_free (mem: uri);
44 g_string_append (string, val: ")");
45 if (recolor->palette)
46 {
47 g_string_append (string, val: ",");
48 _gtk_css_value_print (value: recolor->palette, string);
49 }
50 g_string_append (string, val: ")");
51}
52
53static void
54gtk_css_image_recolor_dispose (GObject *object)
55{
56 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (object);
57
58 g_clear_pointer (&recolor->palette, _gtk_css_value_unref);
59 g_clear_object (&recolor->file);
60 g_clear_object (&recolor->texture);
61
62 G_OBJECT_CLASS (_gtk_css_image_recolor_parent_class)->dispose (object);
63}
64
65static void
66lookup_symbolic_colors (GtkCssStyle *style,
67 GtkCssValue *palette,
68 GdkRGBA *color_out,
69 GdkRGBA *success_out,
70 GdkRGBA *warning_out,
71 GdkRGBA *error_out)
72{
73 const GdkRGBA *lookup;
74
75 *color_out = *gtk_css_color_value_get_rgba (color: style->core->color);
76
77 lookup = gtk_css_palette_value_get_color (value: palette, color_name: "success");
78 if (lookup)
79 *success_out = *lookup;
80 else
81 *success_out = *color_out;
82
83 lookup = gtk_css_palette_value_get_color (value: palette, color_name: "warning");
84 if (lookup)
85 *warning_out = *lookup;
86 else
87 *warning_out = *color_out;
88
89 lookup = gtk_css_palette_value_get_color (value: palette, color_name: "error");
90 if (lookup)
91 *error_out = *lookup;
92 else
93 *error_out = *color_out;
94}
95
96static void
97gtk_css_image_recolor_load_texture (GtkCssImageRecolor *recolor,
98 GError **error)
99{
100 char *uri;
101
102 if (recolor->texture)
103 return;
104
105 uri = g_file_get_uri (file: recolor->file);
106
107 if (g_file_has_uri_scheme (file: recolor->file, uri_scheme: "resource"))
108 {
109 char *resource_path = g_uri_unescape_string (escaped_string: uri + strlen (s: "resource://"), NULL);
110
111 if (g_str_has_suffix (str: uri, suffix: ".symbolic.png"))
112 recolor->texture = gtk_load_symbolic_texture_from_resource (data: resource_path);
113 else
114 recolor->texture = gtk_make_symbolic_texture_from_resource (path: resource_path, width: 0, height: 0, scale: 1.0, NULL);
115
116 g_free (mem: resource_path);
117 }
118 else
119 {
120 if (g_str_has_suffix (str: uri, suffix: ".symbolic.png"))
121 recolor->texture = gtk_load_symbolic_texture_from_file (file: recolor->file);
122 else
123 recolor->texture = gtk_make_symbolic_texture_from_file (file: recolor->file, width: 0, height: 0, scale: 1.0, NULL);
124 }
125
126 g_free (mem: uri);
127}
128
129static GtkCssImage *
130gtk_css_image_recolor_load (GtkCssImageRecolor *recolor,
131 GtkCssStyle *style,
132 GtkCssValue *palette,
133 int scale,
134 GError **gerror)
135{
136 GError *local_error = NULL;
137 GtkCssImageRecolor *image;
138
139 image = g_object_new (GTK_TYPE_CSS_IMAGE_RECOLOR, NULL);
140
141 lookup_symbolic_colors (style, palette, color_out: &image->color, success_out: &image->success, warning_out: &image->warning, error_out: &image->error);
142 gtk_css_image_recolor_load_texture (recolor, error: &local_error);
143
144 image->file = g_object_ref (recolor->file);
145
146 if (recolor->texture)
147 image->texture = g_object_ref (recolor->texture);
148 else
149 {
150 if (gerror)
151 {
152 char *uri;
153
154 uri = g_file_get_uri (file: recolor->file);
155 g_set_error (err: gerror,
156 GTK_CSS_PARSER_ERROR,
157 code: GTK_CSS_PARSER_ERROR_FAILED,
158 format: "Error loading image '%s': %s", uri, local_error ? local_error->message : "");
159 g_free (mem: uri);
160 }
161 }
162
163 g_clear_error (err: &local_error);
164
165 return GTK_CSS_IMAGE (image);
166}
167
168static void
169gtk_css_image_recolor_snapshot (GtkCssImage *image,
170 GtkSnapshot *snapshot,
171 double width,
172 double height)
173{
174 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
175 const GdkRGBA *fg = &recolor->color;
176 const GdkRGBA *sc = &recolor->success;
177 const GdkRGBA *wc = &recolor->warning;
178 const GdkRGBA *ec = &recolor->error;
179 graphene_matrix_t matrix;
180 graphene_vec4_t offset;
181
182 if (recolor->texture == NULL)
183 return;
184
185 graphene_matrix_init_from_float (m: &matrix,
186 v: (float[16]) {
187 sc->red - fg->red, sc->green - fg->green, sc->blue - fg->blue, 0,
188 wc->red - fg->red, wc->green - fg->green, wc->blue - fg->blue, 0,
189 ec->red - fg->red, ec->green - fg->green, ec->blue - fg->blue, 0,
190 0, 0, 0, fg->alpha
191 });
192
193 graphene_vec4_init (v: &offset, x: fg->red, y: fg->green, z: fg->blue, w: 0);
194 gtk_snapshot_push_color_matrix (snapshot, color_matrix: &matrix, color_offset: &offset);
195
196 gtk_snapshot_append_texture (snapshot,
197 texture: recolor->texture,
198 bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
199
200 gtk_snapshot_pop (snapshot);
201}
202
203static GtkCssImage *
204gtk_css_image_recolor_compute (GtkCssImage *image,
205 guint property_id,
206 GtkStyleProvider *provider,
207 GtkCssStyle *style,
208 GtkCssStyle *parent_style)
209{
210 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
211 GtkCssValue *palette;
212 GtkCssImage *img;
213 int scale;
214 GError *error = NULL;
215
216 scale = gtk_style_provider_get_scale (provider);
217
218 if (recolor->palette)
219 palette = _gtk_css_value_compute (value: recolor->palette, property_id, provider, style, parent_style);
220 else
221 palette = _gtk_css_value_ref (value: style->core->icon_palette);
222
223 img = gtk_css_image_recolor_load (recolor, style, palette, scale, gerror: &error);
224
225 if (error)
226 {
227 GtkCssSection *section = gtk_css_style_get_section (style, id: property_id);
228 gtk_style_provider_emit_error (provider, section, error);
229 g_error_free (error);
230 }
231
232 _gtk_css_value_unref (value: palette);
233
234 return img;
235}
236
237static guint
238gtk_css_image_recolor_parse_arg (GtkCssParser *parser,
239 guint arg,
240 gpointer data)
241{
242 GtkCssImageRecolor *self = data;
243
244 switch (arg)
245 {
246 case 0:
247 {
248 char *url = gtk_css_parser_consume_url (self: parser);
249 if (url == NULL)
250 return 0;
251 self->file = gtk_css_parser_resolve_url (self: parser, url);
252 g_free (mem: url);
253 if (self->file == NULL)
254 return 0;
255 return 1;
256 }
257
258 case 1:
259 self->palette = gtk_css_palette_value_parse (parser);
260 if (self->palette == NULL)
261 return 0;
262 return 1;
263
264 default:
265 g_assert_not_reached ();
266 return 0;
267 }
268}
269
270static gboolean
271gtk_css_image_recolor_parse (GtkCssImage *image,
272 GtkCssParser *parser)
273{
274 if (!gtk_css_parser_has_function (self: parser, name: "-gtk-recolor"))
275 {
276 gtk_css_parser_error_syntax (self: parser, format: "Expected '-gtk-recolor('");
277 return FALSE;
278 }
279
280 return gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 2, parse_func: gtk_css_image_recolor_parse_arg, data: image);
281}
282
283static int
284gtk_css_image_recolor_get_width (GtkCssImage *image)
285{
286 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
287
288 gtk_css_image_recolor_load_texture (recolor, NULL);
289
290 if (recolor->texture == NULL)
291 return 0;
292
293 return gdk_texture_get_width (texture: recolor->texture);
294}
295
296static int
297gtk_css_image_recolor_get_height (GtkCssImage *image)
298{
299 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
300
301 gtk_css_image_recolor_load_texture (recolor, NULL);
302
303 if (recolor->texture == NULL)
304 return 0;
305
306 return gdk_texture_get_height (texture: recolor->texture);
307}
308
309static gboolean
310gtk_css_image_recolor_is_computed (GtkCssImage *image)
311{
312 GtkCssImageRecolor *recolor = GTK_CSS_IMAGE_RECOLOR (image);
313
314 return recolor->texture &&
315 (!recolor->palette || gtk_css_value_is_computed (value: recolor->palette));
316}
317
318static void
319_gtk_css_image_recolor_class_init (GtkCssImageRecolorClass *klass)
320{
321 GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
322 GObjectClass *object_class = G_OBJECT_CLASS (klass);
323
324 image_class->get_width = gtk_css_image_recolor_get_width;
325 image_class->get_height = gtk_css_image_recolor_get_height;
326 image_class->compute = gtk_css_image_recolor_compute;
327 image_class->snapshot = gtk_css_image_recolor_snapshot;
328 image_class->parse = gtk_css_image_recolor_parse;
329 image_class->print = gtk_css_image_recolor_print;
330 image_class->is_computed = gtk_css_image_recolor_is_computed;
331
332 object_class->dispose = gtk_css_image_recolor_dispose;
333}
334
335static void
336_gtk_css_image_recolor_init (GtkCssImageRecolor *image_recolor)
337{
338}
339

source code of gtk/gtk/gtkcssimagerecolor.c