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 = { &GTK_CSS_VALUE_BG_SIZE, 1, FALSE, FALSE, NULL, NULL };
164 static GtkCssValue cover_singleton = { &GTK_CSS_VALUE_BG_SIZE, 1, TRUE, FALSE, NULL, NULL };
165 static GtkCssValue contain_singleton = { &GTK_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, &GTK_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 == &GTK_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