diff options
author | Matthias Clasen <mclasen@redhat.com> | 2017-11-07 18:55:01 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2017-11-07 18:56:32 -0500 |
commit | 284fd36e5d3f1297a6e443971802f9191b11f42a (patch) | |
tree | 910ce994b2899fcdb1aeaf3e95f26d0efd6bd946 /gtk/gdkpixbufutils.c | |
parent | 73a3d4b785205319013a79d8d509296bb2b1fd24 (diff) | |
download | gtk+-284fd36e5d3f1297a6e443971802f9191b11f42a.tar.gz |
Fix symbolic.svg rendering
With the shader approach to symbolic recoloring, we must
not recolor the svgs anymore as we're loading them. Instead,
load them the same way that gtk-encode-symbolic-svg does.
This fixes the rendering of large symbolic icons e.g. the
'no search results found' page in the file chooser.
Diffstat (limited to 'gtk/gdkpixbufutils.c')
-rw-r--r-- | gtk/gdkpixbufutils.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/gtk/gdkpixbufutils.c b/gtk/gdkpixbufutils.c index 3da0323623..ac9863a64f 100644 --- a/gtk/gdkpixbufutils.c +++ b/gtk/gdkpixbufutils.c @@ -16,6 +16,7 @@ #include "config.h" +#include <gdk/gdk.h> #include "gdkpixbufutilsprivate.h" static GdkPixbuf * @@ -130,3 +131,214 @@ _gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path, return pixbuf; } +static GdkPixbuf * +load_symbolic_svg (const char *file_data, + gsize file_len, + int width, + int height, + const GdkRGBA *fg, + const GdkRGBA *success_color, + const GdkRGBA *warning_color, + const GdkRGBA *error_color, + GError **error) +{ + GInputStream *stream; + GdkPixbuf *pixbuf; + gchar *css_fg; + gchar *css_success; + gchar *css_warning; + gchar *css_error; + gchar *data; + gchar *svg_width, *svg_height; + gchar *escaped_file_data; + + css_fg = gdk_rgba_to_string (fg); + + css_success = css_warning = css_error = NULL; + + css_warning = gdk_rgba_to_string (warning_color); + css_error = gdk_rgba_to_string (error_color); + css_success = gdk_rgba_to_string (success_color); + + /* Fetch size from the original icon */ + stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL); + pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error); + g_object_unref (stream); + + if (!pixbuf) + return NULL; + + svg_width = g_strdup_printf ("%d", gdk_pixbuf_get_width (pixbuf)); + svg_height = g_strdup_printf ("%d",gdk_pixbuf_get_height (pixbuf)); + g_object_unref (pixbuf); + + escaped_file_data = g_markup_escape_text (file_data, file_len); + + data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + "<svg version=\"1.1\"\n" + " xmlns=\"http://www.w3.org/2000/svg\"\n" + " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n" + " width=\"", svg_width, "\"\n" + " height=\"", svg_height, "\">\n" + " <style type=\"text/css\">\n" + " rect,circle,path {\n" + " fill: ", css_fg," !important;\n" + " }\n" + " .warning {\n" + " fill: ", css_warning, " !important;\n" + " }\n" + " .error {\n" + " fill: ", css_error ," !important;\n" + " }\n" + " .success {\n" + " fill: ", css_success, " !important;\n" + " }\n" + " </style>\n" + " <xi:include href=\"data:text/xml,", escaped_file_data, "\"/>\n" + "</svg>", + NULL); + g_free (escaped_file_data); + g_free (css_fg); + g_free (css_warning); + g_free (css_error); + g_free (css_success); + g_free (svg_width); + g_free (svg_height); + + stream = g_memory_input_stream_new_from_data (data, -1, g_free); + pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error); + g_object_unref (stream); + + return pixbuf; +} + +static void +extract_plane (GdkPixbuf *src, + GdkPixbuf *dst, + int from_plane, + int to_plane) +{ + guchar *src_data, *dst_data; + int width, height, src_stride, dst_stride; + guchar *src_row, *dst_row; + int x, y; + + width = gdk_pixbuf_get_width (src); + height = gdk_pixbuf_get_height (src); + + g_assert (width <= gdk_pixbuf_get_width (dst)); + g_assert (height <= gdk_pixbuf_get_height (dst)); + + src_stride = gdk_pixbuf_get_rowstride (src); + src_data = gdk_pixbuf_get_pixels (src); + + dst_data = gdk_pixbuf_get_pixels (dst); + dst_stride = gdk_pixbuf_get_rowstride (dst); + + for (y = 0; y < height; y++) + { + src_row = src_data + src_stride * y; + dst_row = dst_data + dst_stride * y; + for (x = 0; x < width; x++) + { + dst_row[to_plane] = src_row[from_plane]; + src_row += 4; + dst_row += 4; + } + } +} + +GdkPixbuf * +gtk_make_symbolic_pixbuf_from_data (const char *file_data, + gsize file_len, + int width, + int height, + GError **error) + +{ + GdkRGBA r = { 1,0,0,1}, g = {0,1,0,1}; + GdkPixbuf *loaded; + GdkPixbuf *pixbuf; + int plane; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + + gdk_pixbuf_fill (pixbuf, 0); + + for (plane = 0; plane < 3; plane++) + { + /* Here we render the svg with all colors solid, this should + * always make the alpha channel the same and it should match + * the final alpha channel for all possible renderings. We + * Just use it as-is for final alpha. + * + * For the 3 non-fg colors, we render once each with that + * color as red, and every other color as green. The resulting + * red will describe the amount of that color is in the + * opaque part of the color. We store these as the rgb + * channels, with the color of the fg being implicitly + * the "rest", as all color fractions should add up to 1. + */ + loaded = load_symbolic_svg (file_data, file_len, width, height, + &g, + plane == 0 ? &r : &g, + plane == 1 ? &r : &g, + plane == 2 ? &r : &g, + error); + if (loaded == NULL) + return NULL; + + if (plane == 0) + extract_plane (loaded, pixbuf, 3, 3); + + extract_plane (loaded, pixbuf, 0, plane); + + g_object_unref (loaded); + } + + return pixbuf; +} + +GdkPixbuf * +gtk_make_symbolic_pixbuf_from_resource (const char *path, + int width, + int height, + GError **error) +{ + GBytes *bytes; + const char *data; + gsize size; + GdkPixbuf *pixbuf; + + bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error); + if (bytes == NULL) + return NULL; + + data = g_bytes_get_data (bytes, &size); + + pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, error); + + g_bytes_unref (bytes); + + return pixbuf; +} + +GdkPixbuf * +gtk_make_symbolic_pixbuf_from_file (GFile *file, + int width, + int height, + GError **error) +{ + char *data; + gsize size; + GdkPixbuf *pixbuf; + + if (!g_file_load_contents (file, NULL, &data, &size, NULL, error)) + return NULL; + + pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, error); + + g_free (data); + + return pixbuf; +} |