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