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
This commit is contained in:
Matthias Clasen 2016-01-29 20:23:27 -05:00
parent 0fe468c789
commit 2e4b1e72f4
5 changed files with 338 additions and 2 deletions

View File

@ -608,8 +608,9 @@ background-color: @bg_color;
and borders.
</para>
<literallayout><code>〈image〉 = 〈url〉 | 〈crossfade〉 | 〈gradient〉 | 〈gtk image〉</code>
<literallayout><code>〈image〉 = 〈url〉 | 〈crossfade〉 | 〈alternatives〉 | 〈gradient〉 | 〈gtk image〉</code>
<code>〈crossfade〉 = cross-fade( 〈percentage〉, 〈image〉, 〈image〉)</code>
<code>〈alternatives〉 = image([ 〈image〉, ]* [ 〈image〉 | 〈color〉 ])</code>
<code>〈gradient〉 = 〈linear gradient〉 | 〈radial gradient〉</code>
<code>〈linear gradient〉 = [ linear-gradient | repeating-linear-gradient ] (</code>
<code> [ [ 〈angle〉 | to 〈side or corner〉 ] , ]?</code>
@ -661,6 +662,23 @@ button {
]]></programlisting>
</example>
<para>
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.
</para>
<example>
<title>Image fallback</title>
<programlisting><![CDATA[
button {
background-image: image(url("fancy.svg"), url("plain.png"), green);
}
]]></programlisting>
</example>
<para>
Gradients are images that smoothly fades from one color to another. CSS
provides ways to specify repeating and non-repeating linear and radial

View File

@ -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 \

View File

@ -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;

255
gtk/gtkcssimagefallback.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#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__ */