1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 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 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
18 #include "config.h"
19
20 #include "gtkcssbgsizevalueprivate.h"
21
22 #include "gtkcssnumbervalueprivate.h"
23
24 struct _GtkCssValue {
25 GTK_CSS_VALUE_BASE
26 guint cover :1;
27 guint contain :1;
28 GtkCssValue *x;
29 GtkCssValue *y;
30 };
31
32 static void
gtk_css_value_bg_size_free(GtkCssValue * value)33 gtk_css_value_bg_size_free (GtkCssValue *value)
34 {
35 if (value->x)
36 _gtk_css_value_unref (value->x);
37 if (value->y)
38 _gtk_css_value_unref (value->y);
39
40 g_slice_free (GtkCssValue, value);
41 }
42
43 static GtkCssValue *
gtk_css_value_bg_size_compute(GtkCssValue * value,guint property_id,GtkStyleProvider * provider,GtkCssStyle * style,GtkCssStyle * parent_style)44 gtk_css_value_bg_size_compute (GtkCssValue *value,
45 guint property_id,
46 GtkStyleProvider *provider,
47 GtkCssStyle *style,
48 GtkCssStyle *parent_style)
49 {
50 GtkCssValue *x, *y;
51
52 if (value->x == NULL && value->y == NULL)
53 return _gtk_css_value_ref (value);
54
55 x = y = NULL;
56
57 if (value->x)
58 x = _gtk_css_value_compute (value->x, property_id, provider, style, parent_style);
59
60 if (value->y)
61 y = _gtk_css_value_compute (value->y, property_id, provider, style, parent_style);
62
63 if (x == value->x && y == value->y)
64 {
65 if (x)
66 _gtk_css_value_unref (x);
67 if (y)
68 _gtk_css_value_unref (y);
69
70 return _gtk_css_value_ref (value);
71 }
72
73 return _gtk_css_bg_size_value_new (value->x ? x : NULL,
74 value->y ? y : NULL);
75 }
76
77 static gboolean
gtk_css_value_bg_size_equal(const GtkCssValue * value1,const GtkCssValue * value2)78 gtk_css_value_bg_size_equal (const GtkCssValue *value1,
79 const GtkCssValue *value2)
80 {
81 return value1->cover == value2->cover &&
82 value1->contain == value2->contain &&
83 (value1->x == value2->x ||
84 (value1->x != NULL && value2->x != NULL &&
85 _gtk_css_value_equal (value1->x, value2->x))) &&
86 (value1->y == value2->y ||
87 (value1->y != NULL && value2->y != NULL &&
88 _gtk_css_value_equal (value1->y, value2->y)));
89 }
90
91 static GtkCssValue *
gtk_css_value_bg_size_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)92 gtk_css_value_bg_size_transition (GtkCssValue *start,
93 GtkCssValue *end,
94 guint property_id,
95 double progress)
96 {
97 GtkCssValue *x, *y;
98
99 if (start->cover)
100 return end->cover ? _gtk_css_value_ref (end) : NULL;
101 if (start->contain)
102 return end->contain ? _gtk_css_value_ref (end) : NULL;
103
104 if ((start->x != NULL) ^ (end->x != NULL) ||
105 (start->y != NULL) ^ (end->y != NULL))
106 return NULL;
107
108 if (start->x)
109 {
110 x = _gtk_css_value_transition (start->x, end->x, property_id, progress);
111 if (x == NULL)
112 return NULL;
113 }
114 else
115 x = NULL;
116
117 if (start->y)
118 {
119 y = _gtk_css_value_transition (start->y, end->y, property_id, progress);
120 if (y == NULL)
121 {
122 _gtk_css_value_unref (x);
123 return NULL;
124 }
125 }
126 else
127 y = NULL;
128
129 return _gtk_css_bg_size_value_new (x, y);
130 }
131
132 static void
gtk_css_value_bg_size_print(const GtkCssValue * value,GString * string)133 gtk_css_value_bg_size_print (const GtkCssValue *value,
134 GString *string)
135 {
136 if (value->cover)
137 g_string_append (string, "cover");
138 else if (value->contain)
139 g_string_append (string, "contain");
140 else
141 {
142 if (value->x == NULL)
143 g_string_append (string, "auto");
144 else
145 _gtk_css_value_print (value->x, string);
146
147 if (value->y)
148 {
149 g_string_append_c (string, ' ');
150 _gtk_css_value_print (value->y, string);
151 }
152 }
153 }
154
155 static const GtkCssValueClass GTK_CSS_VALUE_BG_SIZE = {
156 "GtkCssBgSizeValue",
157 gtk_css_value_bg_size_free,
158 gtk_css_value_bg_size_compute,
159 gtk_css_value_bg_size_equal,
160 gtk_css_value_bg_size_transition,
161 NULL,
162 NULL,
163 gtk_css_value_bg_size_print
164 };
165
166 static GtkCssValue auto_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, FALSE, NULL, NULL };
167 static GtkCssValue cover_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, TRUE, FALSE, NULL, NULL };
168 static GtkCssValue contain_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, TRUE, NULL, NULL };
169
170 GtkCssValue *
_gtk_css_bg_size_value_new(GtkCssValue * x,GtkCssValue * y)171 _gtk_css_bg_size_value_new (GtkCssValue *x,
172 GtkCssValue *y)
173 {
174 GtkCssValue *result;
175
176 if (x == NULL && y == NULL)
177 return _gtk_css_value_ref (&auto_singleton);
178
179 result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_BG_SIZE);
180 result->x = x;
181 result->y = y;
182 result->is_computed = (!x || gtk_css_value_is_computed (x)) &&
183 (!y || gtk_css_value_is_computed (y));
184
185 return result;
186 }
187
188 GtkCssValue *
_gtk_css_bg_size_value_parse(GtkCssParser * parser)189 _gtk_css_bg_size_value_parse (GtkCssParser *parser)
190 {
191 GtkCssValue *x, *y;
192
193 if (gtk_css_parser_try_ident (parser, "cover"))
194 return _gtk_css_value_ref (&cover_singleton);
195 else if (gtk_css_parser_try_ident (parser, "contain"))
196 return _gtk_css_value_ref (&contain_singleton);
197
198 if (gtk_css_parser_try_ident (parser, "auto"))
199 x = NULL;
200 else
201 {
202 x = _gtk_css_number_value_parse (parser,
203 GTK_CSS_POSITIVE_ONLY
204 | GTK_CSS_PARSE_PERCENT
205 | GTK_CSS_PARSE_LENGTH);
206 if (x == NULL)
207 return NULL;
208 }
209
210 if (gtk_css_parser_try_ident (parser, "auto"))
211 y = NULL;
212 else if (!gtk_css_number_value_can_parse (parser))
213 y = NULL;
214 else
215 {
216 y = _gtk_css_number_value_parse (parser,
217 GTK_CSS_POSITIVE_ONLY
218 | GTK_CSS_PARSE_PERCENT
219 | GTK_CSS_PARSE_LENGTH);
220 if (y == NULL)
221 {
222 _gtk_css_value_unref (x);
223 return NULL;
224 }
225 }
226
227 return _gtk_css_bg_size_value_new (x, y);
228 }
229
230 static void
gtk_css_bg_size_compute_size_for_cover_contain(gboolean cover,GtkCssImage * image,double width,double height,double * concrete_width,double * concrete_height)231 gtk_css_bg_size_compute_size_for_cover_contain (gboolean cover,
232 GtkCssImage *image,
233 double width,
234 double height,
235 double *concrete_width,
236 double *concrete_height)
237 {
238 double aspect, image_aspect;
239
240 image_aspect = _gtk_css_image_get_aspect_ratio (image);
241 if (image_aspect == 0.0)
242 {
243 *concrete_width = width;
244 *concrete_height = height;
245 return;
246 }
247
248 aspect = width / height;
249
250 if ((aspect >= image_aspect && cover) ||
251 (aspect < image_aspect && !cover))
252 {
253 *concrete_width = width;
254 *concrete_height = width / image_aspect;
255 }
256 else
257 {
258 *concrete_height = height;
259 *concrete_width = height * image_aspect;
260 }
261 }
262
263 void
_gtk_css_bg_size_value_compute_size(const GtkCssValue * value,GtkCssImage * image,double area_width,double area_height,double * out_width,double * out_height)264 _gtk_css_bg_size_value_compute_size (const GtkCssValue *value,
265 GtkCssImage *image,
266 double area_width,
267 double area_height,
268 double *out_width,
269 double *out_height)
270 {
271 g_return_if_fail (value->class == >K_CSS_VALUE_BG_SIZE);
272
273 if (value->contain || value->cover)
274 {
275 gtk_css_bg_size_compute_size_for_cover_contain (value->cover,
276 image,
277 area_width, area_height,
278 out_width, out_height);
279 }
280 else
281 {
282 double x, y;
283
284 /* note: 0 does the right thing later for 'auto' */
285 x = value->x ? _gtk_css_number_value_get (value->x, area_width) : 0;
286 y = value->y ? _gtk_css_number_value_get (value->y, area_height) : 0;
287
288 if ((x <= 0 && value->x) ||
289 (y <= 0 && value->y))
290 {
291 *out_width = 0;
292 *out_height = 0;
293 }
294 else
295 {
296 _gtk_css_image_get_concrete_size (image,
297 x, y,
298 area_width, area_height,
299 out_width, out_height);
300 }
301 }
302 }
303
304