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 | |
31 | G_DEFINE_TYPE (GtkCssImageRecolor, _gtk_css_image_recolor, GTK_TYPE_CSS_IMAGE) |
32 | |
33 | static void |
34 | gtk_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 | |
53 | static void |
54 | gtk_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 | |
65 | static void |
66 | lookup_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 | |
96 | static void |
97 | gtk_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 | |
129 | static GtkCssImage * |
130 | gtk_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 | |
168 | static void |
169 | gtk_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 | |
203 | static GtkCssImage * |
204 | gtk_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 | |
237 | static guint |
238 | gtk_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 | |
270 | static gboolean |
271 | gtk_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 | |
283 | static int |
284 | gtk_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 | |
296 | static int |
297 | gtk_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 | |
309 | static gboolean |
310 | gtk_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 | |
318 | static 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 | |
335 | static void |
336 | _gtk_css_image_recolor_init (GtkCssImageRecolor *image_recolor) |
337 | { |
338 | } |
339 | |