summaryrefslogtreecommitdiff
path: root/gtk/gdkpixbufutils.c
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2017-11-07 18:55:01 -0500
committerMatthias Clasen <mclasen@redhat.com>2017-11-07 18:56:32 -0500
commit284fd36e5d3f1297a6e443971802f9191b11f42a (patch)
tree910ce994b2899fcdb1aeaf3e95f26d0efd6bd946 /gtk/gdkpixbufutils.c
parent73a3d4b785205319013a79d8d509296bb2b1fd24 (diff)
downloadgtk+-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.c212
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;
+}