]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssimage.c
gtk: Make functions static that don't need to be non-static
[~andy/gtk] / gtk / gtkcssimage.c
1 /*
2  * Copyright © 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.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: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcssimageprivate.h"
23
24 #include "gtkcsscomputedvaluesprivate.h"
25
26 /* for the types only */
27 #include "gtk/gtkcssimagecrossfadeprivate.h"
28 #include "gtk/gtkcssimagegradientprivate.h"
29 #include "gtk/gtkcssimagelinearprivate.h"
30 #include "gtk/gtkcssimageurlprivate.h"
31 #include "gtk/gtkcssimagewin32private.h"
32
33 G_DEFINE_ABSTRACT_TYPE (GtkCssImage, _gtk_css_image, G_TYPE_OBJECT)
34
35 static int
36 gtk_css_image_real_get_width (GtkCssImage *image)
37 {
38   return 0;
39 }
40
41 static int
42 gtk_css_image_real_get_height (GtkCssImage *image)
43 {
44   return 0;
45 }
46
47 static double
48 gtk_css_image_real_get_aspect_ratio (GtkCssImage *image)
49 {
50   int width, height;
51
52   width = _gtk_css_image_get_width (image);
53   height = _gtk_css_image_get_height (image);
54
55   if (width && height)
56     return (double) width / height;
57   else
58     return 0;
59 }
60
61 static GtkCssImage *
62 gtk_css_image_real_compute (GtkCssImage             *image,
63                             guint                    property_id,
64                             GtkStyleProviderPrivate *provider,
65                             GtkCssComputedValues    *values,
66                             GtkCssComputedValues    *parent_values,
67                             GtkCssDependencies      *dependencies)
68 {
69   return g_object_ref (image);
70 }
71
72 static GtkCssImage *
73 gtk_css_image_real_transition (GtkCssImage *start,
74                                GtkCssImage *end,
75                                guint        property_id,
76                                double       progress)
77 {
78   if (progress <= 0.0)
79     return g_object_ref (start);
80   else if (progress >= 1.0)
81     return end ? g_object_ref (end) : NULL;
82   else
83     return _gtk_css_image_cross_fade_new (start, end, progress);
84 }
85
86 static void
87 _gtk_css_image_class_init (GtkCssImageClass *klass)
88 {
89   klass->get_width = gtk_css_image_real_get_width;
90   klass->get_height = gtk_css_image_real_get_height;
91   klass->get_aspect_ratio = gtk_css_image_real_get_aspect_ratio;
92   klass->compute = gtk_css_image_real_compute;
93   klass->transition = gtk_css_image_real_transition;
94 }
95
96 static void
97 _gtk_css_image_init (GtkCssImage *image)
98 {
99 }
100
101 int
102 _gtk_css_image_get_width (GtkCssImage *image)
103 {
104   GtkCssImageClass *klass;
105
106   g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), 0);
107
108   klass = GTK_CSS_IMAGE_GET_CLASS (image);
109
110   return klass->get_width (image);
111 }
112
113 int
114 _gtk_css_image_get_height (GtkCssImage *image)
115 {
116   GtkCssImageClass *klass;
117
118   g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), 0);
119
120   klass = GTK_CSS_IMAGE_GET_CLASS (image);
121
122   return klass->get_height (image);
123 }
124
125 double
126 _gtk_css_image_get_aspect_ratio (GtkCssImage *image)
127 {
128   GtkCssImageClass *klass;
129
130   g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), 0);
131
132   klass = GTK_CSS_IMAGE_GET_CLASS (image);
133
134   return klass->get_aspect_ratio (image);
135 }
136
137 GtkCssImage *
138 _gtk_css_image_compute (GtkCssImage             *image,
139                         guint                    property_id,
140                         GtkStyleProviderPrivate *provider,
141                         GtkCssComputedValues    *values,
142                         GtkCssComputedValues    *parent_values,
143                         GtkCssDependencies      *dependencies)
144 {
145   GtkCssDependencies unused;
146   GtkCssImageClass *klass;
147
148   g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL);
149   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
150   g_return_val_if_fail (parent_values == NULL || GTK_IS_CSS_COMPUTED_VALUES (parent_values), NULL);
151
152   if (dependencies == NULL)
153     dependencies = &unused;
154   *dependencies = 0;
155
156   klass = GTK_CSS_IMAGE_GET_CLASS (image);
157
158   return klass->compute (image, property_id, provider, values, parent_values, dependencies);
159 }
160
161 GtkCssImage *
162 _gtk_css_image_transition (GtkCssImage *start,
163                            GtkCssImage *end,
164                            guint        property_id,
165                            double       progress)
166 {
167   GtkCssImageClass *klass;
168
169   g_return_val_if_fail (start == NULL || GTK_IS_CSS_IMAGE (start), NULL);
170   g_return_val_if_fail (end == NULL || GTK_IS_CSS_IMAGE (end), NULL);
171
172   progress = CLAMP (progress, 0.0, 1.0);
173
174   if (start == NULL)
175     {
176       if (end == NULL)
177         return NULL;
178       else
179         {
180           start = end;
181           end = NULL;
182           progress = 1.0 - progress;
183         }
184     }
185
186   klass = GTK_CSS_IMAGE_GET_CLASS (start);
187
188   return klass->transition (start, end, property_id, progress);
189 }
190
191 void
192 _gtk_css_image_draw (GtkCssImage        *image,
193                      cairo_t            *cr,
194                      double              width,
195                      double              height)
196 {
197   GtkCssImageClass *klass;
198
199   g_return_if_fail (GTK_IS_CSS_IMAGE (image));
200   g_return_if_fail (cr != NULL);
201
202   cairo_save (cr);
203
204   klass = GTK_CSS_IMAGE_GET_CLASS (image);
205
206   klass->draw (image, cr, width, height);
207
208   cairo_restore (cr);
209 }
210
211 void
212 _gtk_css_image_print (GtkCssImage *image,
213                       GString     *string)
214 {
215   GtkCssImageClass *klass;
216
217   g_return_if_fail (GTK_IS_CSS_IMAGE (image));
218   g_return_if_fail (string != NULL);
219
220   klass = GTK_CSS_IMAGE_GET_CLASS (image);
221
222   klass->print (image, string);
223 }
224
225 /* Applies the algorithm outlined in
226  * http://dev.w3.org/csswg/css3-images/#default-sizing
227  */
228 void
229 _gtk_css_image_get_concrete_size (GtkCssImage *image,
230                                   double       specified_width,
231                                   double       specified_height,
232                                   double       default_width,
233                                   double       default_height,
234                                   double      *concrete_width,
235                                   double      *concrete_height)
236 {
237   double image_width, image_height, image_aspect;
238
239   g_return_if_fail (GTK_IS_CSS_IMAGE (image));
240   g_return_if_fail (specified_width >= 0);
241   g_return_if_fail (specified_height >= 0);
242   g_return_if_fail (default_width > 0);
243   g_return_if_fail (default_height > 0);
244   g_return_if_fail (concrete_width != NULL);
245   g_return_if_fail (concrete_height != NULL);
246
247   /* If the specified size is a definite width and height,
248    * the concrete object size is given that width and height.
249    */
250   if (specified_width && specified_height)
251     {
252       *concrete_width = specified_width;
253       *concrete_height = specified_height;
254       return;
255     }
256
257   image_width  = _gtk_css_image_get_width (image);
258   image_height = _gtk_css_image_get_height (image);
259   image_aspect = _gtk_css_image_get_aspect_ratio (image);
260
261   /* If the specified size has neither a definite width nor height,
262    * and has no additional contraints, the dimensions of the concrete
263    * object size are calculated as follows:
264    */
265   if (specified_width == 0.0 && specified_height == 0.0)
266     {
267       /* If the object has only an intrinsic aspect ratio,
268        * the concrete object size must have that aspect ratio,
269        * and additionally be as large as possible without either
270        * its height or width exceeding the height or width of the
271        * default object size.
272        */
273       if (image_aspect > 0 && image_width == 0 && image_height == 0)
274         {
275           if (image_aspect * default_height > default_width)
276             {
277               *concrete_width = default_height * image_aspect;
278               *concrete_height = default_height;
279             }
280           else
281             {
282               *concrete_width = default_width;
283               *concrete_height = default_width / image_aspect;
284             }
285         }
286
287       /* Otherwise, the width and height of the concrete object
288        * size is the same as the object's intrinsic width and
289        * intrinsic height, if they exist.
290        * If the concrete object size is still missing a width or
291        * height, and the object has an intrinsic aspect ratio,
292        * the missing dimension is calculated from the present
293        * dimension and the intrinsic aspect ratio.
294        * Otherwise, the missing dimension is taken from the default
295        * object size. 
296        */
297       if (image_width)
298         *concrete_width = image_width;
299       else if (image_aspect)
300         *concrete_width = image_height * image_aspect;
301       else
302         *concrete_width = default_width;
303
304       if (image_height)
305         *concrete_height = image_height;
306       else if (image_aspect)
307         *concrete_height = image_width / image_aspect;
308       else
309         *concrete_height = default_height;
310
311       return;
312     }
313
314   /* If the specified size has only a width or height, but not both,
315    * then the concrete object size is given that specified width or height.
316    * The other dimension is calculated as follows:
317    * If the object has an intrinsic aspect ratio, the missing dimension of
318    * the concrete object size is calculated using the intrinsic aspect-ratio
319    * and the present dimension.
320    * Otherwise, if the missing dimension is present in the object's intrinsic
321    * dimensions, the missing dimension is taken from the object's intrinsic
322    * dimensions.
323    * Otherwise, the missing dimension of the concrete object size is taken
324    * from the default object size. 
325    */
326   if (specified_width)
327     {
328       *concrete_width = specified_width;
329       if (image_aspect)
330         *concrete_height = specified_width / image_aspect;
331       else if (image_height)
332         *concrete_height = image_height;
333       else
334         *concrete_height = default_height;
335     }
336   else
337     {
338       *concrete_height = specified_height;
339       if (image_aspect)
340         *concrete_width = specified_height * image_aspect;
341       else if (image_width)
342         *concrete_width = image_width;
343       else
344         *concrete_width = default_width;
345     }
346 }
347
348 cairo_surface_t *
349 _gtk_css_image_get_surface (GtkCssImage     *image,
350                             cairo_surface_t *target,
351                             int              surface_width,
352                             int              surface_height)
353 {
354   cairo_surface_t *result;
355   cairo_t *cr;
356
357   g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL);
358   g_return_val_if_fail (surface_width > 0, NULL);
359   g_return_val_if_fail (surface_height > 0, NULL);
360
361   if (target)
362     result = cairo_surface_create_similar (target,
363                                            CAIRO_CONTENT_COLOR_ALPHA,
364                                            surface_width,
365                                            surface_height);
366   else
367     result = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
368                                          surface_width,
369                                          surface_height);
370
371   cr = cairo_create (result);
372   _gtk_css_image_draw (image, cr, surface_width, surface_height);
373   cairo_destroy (cr);
374
375   return result;
376 }
377
378 static GType
379 gtk_css_image_get_parser_type (GtkCssParser *parser)
380 {
381   static const struct {
382     const char *prefix;
383     GType (* type_func) (void);
384   } image_types[] = {
385     { "url", _gtk_css_image_url_get_type },
386     { "-gtk-gradient", _gtk_css_image_gradient_get_type },
387     { "-gtk-win32-theme-part", _gtk_css_image_win32_get_type },
388     { "linear-gradient", _gtk_css_image_linear_get_type },
389     { "repeating-linear-gradient", _gtk_css_image_linear_get_type },
390     { "cross-fade", _gtk_css_image_cross_fade_get_type }
391   };
392   guint i;
393
394   for (i = 0; i < G_N_ELEMENTS (image_types); i++)
395     {
396       if (_gtk_css_parser_has_prefix (parser, image_types[i].prefix))
397         return image_types[i].type_func ();
398     }
399
400   return G_TYPE_INVALID;
401 }
402
403 /**
404  * _gtk_css_image_can_parse:
405  * @parser: a css parser
406  *
407  * Checks if the parser can potentially parse the given stream as an
408  * image from looking at the first token of @parser. This is useful for
409  * implementing shorthand properties. A successful parse of an image
410  * can not be guaranteed.
411  *
412  * Returns: %TURE if it looks like an image.
413  **/
414 gboolean
415 _gtk_css_image_can_parse (GtkCssParser *parser)
416 {
417   return gtk_css_image_get_parser_type (parser) != G_TYPE_INVALID;
418 }
419
420 GtkCssImage *
421 _gtk_css_image_new_parse (GtkCssParser *parser)
422 {
423   GtkCssImageClass *klass;
424   GtkCssImage *image;
425   GType image_type;
426
427   g_return_val_if_fail (parser != NULL, NULL);
428
429   image_type = gtk_css_image_get_parser_type (parser);
430   if (image_type == G_TYPE_INVALID)
431     {
432       _gtk_css_parser_error (parser, "Not a valid image");
433       return NULL;
434     }
435
436   image = g_object_new (image_type, NULL);
437
438   klass = GTK_CSS_IMAGE_GET_CLASS (image);
439   if (!klass->parse (image, parser))
440     {
441       g_object_unref (image);
442       return NULL;
443     }
444
445   return image;
446 }
447