From 2e4b1e72f4a272c426a12135f3487aa2017c746a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 29 Jan 2016 20:23:27 -0500 Subject: [PATCH] css: Support the image() notation This lets us do fallback in case an image format is not supported, and also lets us provide solid-color images. We don't support image fragment notations. See ttps://www.w3.org/TR/css3-images/#image-notation https://bugzilla.gnome.org/show_bug.cgi?id=761318 --- docs/reference/gtk/css-overview.xml | 20 ++- gtk/Makefile.am | 2 + gtk/gtkcssimage.c | 4 +- gtk/gtkcssimagefallback.c | 255 ++++++++++++++++++++++++++++ gtk/gtkcssimagefallbackprivate.h | 59 +++++++ 5 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 gtk/gtkcssimagefallback.c create mode 100644 gtk/gtkcssimagefallbackprivate.h diff --git a/docs/reference/gtk/css-overview.xml b/docs/reference/gtk/css-overview.xml index 77562133c7..40e26878c6 100644 --- a/docs/reference/gtk/css-overview.xml +++ b/docs/reference/gtk/css-overview.xml @@ -608,8 +608,9 @@ background-color: @bg_color; and borders. -〈image〉 = 〈url〉 | 〈crossfade〉 | 〈gradient〉 | 〈gtk image〉 +〈image〉 = 〈url〉 | 〈crossfade〉 | 〈alternatives〉 | 〈gradient〉 | 〈gtk image〉 〈crossfade〉 = cross-fade( 〈percentage〉, 〈image〉, 〈image〉) +〈alternatives〉 = image([ 〈image〉, ]* [ 〈image〉 | 〈color〉 ]) 〈gradient〉 = 〈linear gradient〉 | 〈radial gradient〉 〈linear gradient〉 = [ linear-gradient | repeating-linear-gradient ] ( [ [ 〈angle〉 | to 〈side or corner〉 ] , ]? @@ -661,6 +662,23 @@ button { ]]> + + The image() syntax provides a way to specify fallbacks in case an image + format may not be supported. Multiple fallback images can be specified, + and will be tried in turn until one can be loaded successfully. The + last fallback may be a color, which will be rendered as a solid color + image. + + + + Image fallback + + + Gradients are images that smoothly fades from one color to another. CSS provides ways to specify repeating and non-repeating linear and radial diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 7b130e3cf3..2f1099182c 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -398,6 +398,7 @@ gtk_private_h_sources = \ gtkcssiconthemevalueprivate.h \ gtkcssimagebuiltinprivate.h \ gtkcssimagecrossfadeprivate.h \ + gtkcssimagefallbackprivate.h \ gtkcssimagegradientprivate.h \ gtkcssimageiconthemeprivate.h \ gtkcssimagelinearprivate.h \ @@ -661,6 +662,7 @@ gtk_base_c_sources = \ gtkcssimage.c \ gtkcssimagebuiltin.c \ gtkcssimagecrossfade.c \ + gtkcssimagefallback.c \ gtkcssimagegradient.c \ gtkcssimageicontheme.c \ gtkcssimagelinear.c \ diff --git a/gtk/gtkcssimage.c b/gtk/gtkcssimage.c index 6f141767a8..3b9ff41a41 100644 --- a/gtk/gtkcssimage.c +++ b/gtk/gtkcssimage.c @@ -32,6 +32,7 @@ #include "gtk/gtkcssimageurlprivate.h" #include "gtk/gtkcssimagescaledprivate.h" #include "gtk/gtkcssimagerecolorprivate.h" +#include "gtk/gtkcssimagefallbackprivate.h" #include "gtk/gtkcssimagewin32private.h" G_DEFINE_ABSTRACT_TYPE (GtkCssImage, _gtk_css_image, G_TYPE_OBJECT) @@ -426,7 +427,8 @@ gtk_css_image_get_parser_type (GtkCssParser *parser) { "repeating-linear-gradient", _gtk_css_image_linear_get_type }, { "radial-gradient", _gtk_css_image_radial_get_type }, { "repeating-radial-gradient", _gtk_css_image_radial_get_type }, - { "cross-fade", _gtk_css_image_cross_fade_get_type } + { "cross-fade", _gtk_css_image_cross_fade_get_type }, + { "image", _gtk_css_image_fallback_get_type } }; guint i; diff --git a/gtk/gtkcssimagefallback.c b/gtk/gtkcssimagefallback.c new file mode 100644 index 0000000000..b7c3a9d224 --- /dev/null +++ b/gtk/gtkcssimagefallback.c @@ -0,0 +1,255 @@ +/* + * Copyright © 2016 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#include "config.h" + +#include "gtkcssimagefallbackprivate.h" +#include "gtkcssimagesurfaceprivate.h" +#include "gtkcsscolorvalueprivate.h" +#include "gtkcssrgbavalueprivate.h" + +#include "gtkstyleproviderprivate.h" + +G_DEFINE_TYPE (GtkCssImageFallback, _gtk_css_image_fallback, GTK_TYPE_CSS_IMAGE) + +static int +gtk_css_image_fallback_get_width (GtkCssImage *image) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + + if (fallback->used < 0) + return 0; + + return _gtk_css_image_get_width (fallback->images[fallback->used]); +} + +static int +gtk_css_image_fallback_get_height (GtkCssImage *image) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + + if (fallback->used < 0) + return 0; + + return _gtk_css_image_get_height (fallback->images[fallback->used]); +} + +static double +gtk_css_image_fallback_get_aspect_ratio (GtkCssImage *image) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + + if (fallback->used < 0) + return 1; + + return _gtk_css_image_get_aspect_ratio (fallback->images[fallback->used]); +} + +static void +gtk_css_image_fallback_draw (GtkCssImage *image, + cairo_t *cr, + double width, + double height) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + + if (fallback->used < 0) + { + if (fallback->color) + gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (fallback->color)); + else + cairo_set_source_rgb (cr, 1, 0, 9); + + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); + } + else + _gtk_css_image_draw (fallback->images[fallback->used], cr, width, height); +} + +static void +gtk_css_image_fallback_print (GtkCssImage *image, + GString *string) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + int i; + + g_string_append (string, "image("); + for (i = 0; i < fallback->n_images; i++) + { + if (i > 0) + g_string_append (string, ","); + _gtk_css_image_print (fallback->images[i], string); + } + if (fallback->color) + { + g_string_append (string, ","); + _gtk_css_value_print (fallback->color, string); + } + + g_string_append (string, ")"); +} + +static void +gtk_css_image_fallback_dispose (GObject *object) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (object); + int i; + + for (i = 0; i < fallback->n_images; i++) + g_object_unref (fallback->images[i]); + g_free (fallback->images); + fallback->images = NULL; + + if (fallback->color) + { + _gtk_css_value_unref (fallback->color); + fallback->color = NULL; + } + + G_OBJECT_CLASS (_gtk_css_image_fallback_parent_class)->dispose (object); +} + + +static GtkCssImage * +gtk_css_image_fallback_compute (GtkCssImage *image, + guint property_id, + GtkStyleProviderPrivate *provider, + GtkCssStyle *style, + GtkCssStyle *parent_style) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + GtkCssImageFallback *copy; + int i; + + if (fallback->used < 0) + { + copy = g_object_new (_gtk_css_image_fallback_get_type (), NULL); + copy->n_images = fallback->n_images; + copy->images = g_new (GtkCssImage *, fallback->n_images); + for (i = 0; i < fallback->n_images; i++) + { + copy->images[i] = _gtk_css_image_compute (fallback->images[i], + property_id, + provider, + style, + parent_style); + + /* Assume that failing to load an image leaves a 0x0 surface image */ + if (GTK_IS_CSS_IMAGE_SURFACE (copy->images[i]) && + _gtk_css_image_get_width (copy->images[i]) == 0 && + _gtk_css_image_get_height (copy->images[i]) == 0) + continue; + + if (copy->used < 0) + copy->used = i; + } + + if (fallback->color) + copy->color = _gtk_css_value_compute (fallback->color, + property_id, + provider, + style, + parent_style); + else + copy->color = NULL; + + return GTK_CSS_IMAGE (copy); + } + else + return g_object_ref (fallback); +} + +static gboolean +gtk_css_image_fallback_parse (GtkCssImage *image, + GtkCssParser *parser) +{ + GtkCssImageFallback *fallback = GTK_CSS_IMAGE_FALLBACK (image); + GPtrArray *images; + GtkCssImage *child; + + if (!_gtk_css_parser_try (parser, "image", TRUE)) + { + _gtk_css_parser_error (parser, "'image'"); + return FALSE; + } + + if (!_gtk_css_parser_try (parser, "(", TRUE)) + { + _gtk_css_parser_error (parser, + "Expected '(' after 'image'"); + return FALSE; + } + + images = g_ptr_array_new_with_free_func (g_object_unref); + + do + { + child = NULL; + if (_gtk_css_image_can_parse (parser)) + child = _gtk_css_image_new_parse (parser); + if (child == NULL) + { + fallback->color = _gtk_css_color_value_parse (parser); + if (fallback->color) + break; + + g_ptr_array_free (images, TRUE); + return FALSE; + } + g_ptr_array_add (images, child); + } + while ( _gtk_css_parser_try (parser, ",", TRUE)); + + if (!_gtk_css_parser_try (parser, ")", TRUE)) + { + g_ptr_array_free (images, TRUE); + _gtk_css_parser_error (parser, + "Expected ')' at end of 'image'"); + return FALSE; + } + + fallback->n_images = images->len; + fallback->images = (GtkCssImage **) g_ptr_array_free (images, FALSE); + + return TRUE; +} + +static void +_gtk_css_image_fallback_class_init (GtkCssImageFallbackClass *klass) +{ + GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + image_class->get_width = gtk_css_image_fallback_get_width; + image_class->get_height = gtk_css_image_fallback_get_height; + image_class->get_aspect_ratio = gtk_css_image_fallback_get_aspect_ratio; + image_class->draw = gtk_css_image_fallback_draw; + image_class->parse = gtk_css_image_fallback_parse; + image_class->compute = gtk_css_image_fallback_compute; + image_class->print = gtk_css_image_fallback_print; + + object_class->dispose = gtk_css_image_fallback_dispose; +} + +static void +_gtk_css_image_fallback_init (GtkCssImageFallback *image_fallback) +{ + image_fallback->used = -1; +} diff --git a/gtk/gtkcssimagefallbackprivate.h b/gtk/gtkcssimagefallbackprivate.h new file mode 100644 index 0000000000..7de1ba96b1 --- /dev/null +++ b/gtk/gtkcssimagefallbackprivate.h @@ -0,0 +1,59 @@ +/* + * Copyright © 2016 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GTK_CSS_IMAGE_FALLBACK_PRIVATE_H__ +#define __GTK_CSS_IMAGE_FALLBACK_PRIVATE_H__ + +#include "gtk/gtkcssimageprivate.h" +#include "gtk/gtkcssvalueprivate.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_CSS_IMAGE_FALLBACK (_gtk_css_image_fallback_get_type ()) +#define GTK_CSS_IMAGE_FALLBACK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_IMAGE_FALLBACK, GtkCssImageFallback)) +#define GTK_CSS_IMAGE_FALLBACK_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_IMAGE_FALLBACK, GtkCssImageFallbackClass)) +#define GTK_IS_CSS_IMAGE_FALLBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_IMAGE_FALLBACK)) +#define GTK_IS_CSS_IMAGE_FALLBACK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_IMAGE_FALLBACK)) +#define GTK_CSS_IMAGE_FALLBACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_IMAGE_FALLBACK, GtkCssImageFallbackClass)) + +typedef struct _GtkCssImageFallback GtkCssImageFallback; +typedef struct _GtkCssImageFallbackClass GtkCssImageFallbackClass; + +struct _GtkCssImageFallback +{ + GtkCssImage parent; + + GtkCssImage **images; + int n_images; + + int used; + + GtkCssValue *color; +}; + +struct _GtkCssImageFallbackClass +{ + GtkCssImageClass parent_class; +}; + +GType _gtk_css_image_fallback_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_CSS_IMAGE_FALLBACK_PRIVATE_H__ */