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,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)44 gtk_css_value_bg_size_compute (GtkCssValue *value,
45 guint property_id,
46 GtkStyleProviderPrivate *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 gtk_css_value_bg_size_free,
157 gtk_css_value_bg_size_compute,
158 gtk_css_value_bg_size_equal,
159 gtk_css_value_bg_size_transition,
160 gtk_css_value_bg_size_print
161 };
162
163 static GtkCssValue auto_singleton = { >K_CSS_VALUE_BG_SIZE, 1, FALSE, FALSE, NULL, NULL };
164 static GtkCssValue cover_singleton = { >K_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, NULL, NULL };
165 static GtkCssValue contain_singleton = { >K_CSS_VALUE_BG_SIZE, 1, FALSE, TRUE, NULL, NULL };
166
167 GtkCssValue *
_gtk_css_bg_size_value_new(GtkCssValue * x,GtkCssValue * y)168 _gtk_css_bg_size_value_new (GtkCssValue *x,
169 GtkCssValue *y)
170 {
171 GtkCssValue *result;
172
173 if (x == NULL && y == NULL)
174 return _gtk_css_value_ref (&auto_singleton);
175
176 result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_BG_SIZE);
177 result->x = x;
178 result->y = y;
179
180 return result;
181 }
182
183 GtkCssValue *
_gtk_css_bg_size_value_parse(GtkCssParser * parser)184 _gtk_css_bg_size_value_parse (GtkCssParser *parser)
185 {
186 GtkCssValue *x, *y;
187
188 if (_gtk_css_parser_try (parser, "cover", TRUE))
189 return _gtk_css_value_ref (&cover_singleton);
190 else if (_gtk_css_parser_try (parser, "contain", TRUE))
191 return _gtk_css_value_ref (&contain_singleton);
192
193 if (_gtk_css_parser_try (parser, "auto", TRUE))
194 x = NULL;
195 else
196 {
197 x = _gtk_css_number_value_parse (parser,
198 GTK_CSS_POSITIVE_ONLY
199 | GTK_CSS_PARSE_PERCENT
200 | GTK_CSS_PARSE_LENGTH);
201 if (x == NULL)
202 return NULL;
203 }
204
205 if (_gtk_css_parser_try (parser, "auto", TRUE))
206 y = NULL;
207 else if (!gtk_css_number_value_can_parse (parser))
208 y = NULL;
209 else
210 {
211 y = _gtk_css_number_value_parse (parser,
212 GTK_CSS_POSITIVE_ONLY
213 | GTK_CSS_PARSE_PERCENT
214 | GTK_CSS_PARSE_LENGTH);
215 if (y == NULL)
216 {
217 _gtk_css_value_unref (x);
218 return NULL;
219 }
220 }
221
222 return _gtk_css_bg_size_value_new (x, y);
223 }
224
225 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)226 gtk_css_bg_size_compute_size_for_cover_contain (gboolean cover,
227 GtkCssImage *image,
228 double width,
229 double height,
230 double *concrete_width,
231 double *concrete_height)
232 {
233 double aspect, image_aspect;
234
235 image_aspect = _gtk_css_image_get_aspect_ratio (image);
236 if (image_aspect == 0.0)
237 {
238 *concrete_width = width;
239 *concrete_height = height;
240 return;
241 }
242
243 aspect = width / height;
244
245 if ((aspect >= image_aspect && cover) ||
246 (aspect < image_aspect && !cover))
247 {
248 *concrete_width = width;
249 *concrete_height = width / image_aspect;
250 }
251 else
252 {
253 *concrete_height = height;
254 *concrete_width = height * image_aspect;
255 }
256 }
257
258 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)259 _gtk_css_bg_size_value_compute_size (const GtkCssValue *value,
260 GtkCssImage *image,
261 double area_width,
262 double area_height,
263 double *out_width,
264 double *out_height)
265 {
266 g_return_if_fail (value->class == >K_CSS_VALUE_BG_SIZE);
267
268 if (value->contain || value->cover)
269 {
270 gtk_css_bg_size_compute_size_for_cover_contain (value->cover,
271 image,
272 area_width, area_height,
273 out_width, out_height);
274 }
275 else
276 {
277 double x, y;
278
279 /* note: 0 does the right thing later for 'auto' */
280 x = value->x ? _gtk_css_number_value_get (value->x, area_width) : 0;
281 y = value->y ? _gtk_css_number_value_get (value->y, area_height) : 0;
282
283 if ((x <= 0 && value->x) ||
284 (y <= 0 && value->y))
285 {
286 *out_width = 0;
287 *out_height = 0;
288 }
289 else
290 {
291 _gtk_css_image_get_concrete_size (image,
292 x, y,
293 area_width, area_height,
294 out_width, out_height);
295 }
296 }
297 }
298
299