From 284fd36e5d3f1297a6e443971802f9191b11f42a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 7 Nov 2017 18:55:01 -0500 Subject: [PATCH] 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. --- gtk/encodesymbolic.c | 197 +++------------------------------ gtk/gdkpixbufutils.c | 212 ++++++++++++++++++++++++++++++++++++ gtk/gdkpixbufutilsprivate.h | 13 +++ gtk/gtkicontheme.c | 13 ++- gtk/meson.build | 2 +- 5 files changed, 252 insertions(+), 185 deletions(-) diff --git a/gtk/encodesymbolic.c b/gtk/encodesymbolic.c index 3b24ca7dba..d1cc6069ec 100644 --- a/gtk/encodesymbolic.c +++ b/gtk/encodesymbolic.c @@ -18,7 +18,6 @@ #include "config.h" #include -#include #include #include @@ -32,6 +31,8 @@ #include #include +#include "gtkpixbufutilsprivate.h" + static gchar *output_dir = NULL; static GOptionEntry args[] = { @@ -39,184 +40,6 @@ static GOptionEntry args[] = { { NULL } }; -static GdkPixbuf * -load_symbolic_svg (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 ("\n" - "\n" - " \n" - " \n" - "", - 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; - } - } -} - -static GdkPixbuf * -make_symbolic_pixbuf (char *file, - int width, - int height, - GError **error) - -{ - GdkRGBA r = { 1,0,0,1}, g = {0,1,0,1}; - GdkPixbuf *loaded; - GdkPixbuf *pixbuf; - int plane; - gchar *file_data; - gsize file_len; - - if (!g_file_get_contents (file, &file_data, &file_len, error)) - return NULL; - - 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); - } - - g_free (file_data); - - return pixbuf; -} - int main (int argc, char **argv) { @@ -228,6 +51,8 @@ main (int argc, char **argv) gchar **sizev; GFileOutputStream *out; GFile *dest; + char *data; + gsize len; setlocale (LC_ALL, ""); @@ -254,12 +79,12 @@ main (int argc, char **argv) width = 0; height = 0; sizev = g_strsplit (argv[2], "x", 0); - if (g_strv_length (sizev) == 2) + if (g_strv_length (sizev) == 2) { width = atoi(sizev[0]); height = atoi(sizev[1]); } - g_strfreev (sizev); + g_strfreev (sizev); if (width == 0 || height == 0) { @@ -273,13 +98,21 @@ main (int argc, char **argv) #endif error = NULL; - symbolic = make_symbolic_pixbuf (path, width, height, &error); + if (!g_file_get_contents (path, &data, &len, &error)) + { + g_printerr (_("Can’t load file: %s\n"), error->message); + return 1; + } + + symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, &error); if (symbolic == NULL) { g_printerr (_("Can’t load file: %s\n"), error->message); return 1; } + g_free (data); + basename = g_path_get_basename (path); dot = strrchr (basename, '.'); 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 #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 ("\n" + "\n" + " \n" + " \n" + "", + 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; +} diff --git a/gtk/gdkpixbufutilsprivate.h b/gtk/gdkpixbufutilsprivate.h index f01f1774b6..1ea23dc2a2 100644 --- a/gtk/gdkpixbufutilsprivate.h +++ b/gtk/gdkpixbufutilsprivate.h @@ -30,6 +30,19 @@ GdkPixbuf *_gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path, gdouble scale, GError **error); +GdkPixbuf *gtk_make_symbolic_pixbuf_from_data (const char *data, + gsize len, + int width, + int height, + GError **error); +GdkPixbuf *gtk_make_symbolic_pixbuf_from_file (GFile *file, + int width, + int height, + GError **error); +GdkPixbuf *gtk_make_symbolic_pixbuf_from_resource (const char *path, + int width, + int height, + GError **error); G_END_DECLS #endif /* __GDK_PIXBUF_UTILS_PRIVATE_H__ */ diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index f738579644..94e032388a 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -3793,7 +3793,11 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info) else size = icon_info->dir_size * dir_scale * icon_info->scale; - if (size == 0) + if (gtk_icon_info_is_symbolic (icon_info)) + source_pixbuf = gtk_make_symbolic_pixbuf_from_resource (icon_info->filename, + size, size, + &icon_info->load_error); + else if (size == 0) source_pixbuf = _gdk_pixbuf_new_from_resource_scaled (icon_info->filename, icon_info->desired_scale, &icon_info->load_error); @@ -3828,7 +3832,12 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info) size = scaled_desired_size; else size = icon_info->dir_size * dir_scale * icon_info->scale; - if (size == 0) + + if (gtk_icon_info_is_symbolic (icon_info) && icon_info->icon_file) + source_pixbuf = gtk_make_symbolic_pixbuf_from_file (icon_info->icon_file, + size, size, + &icon_info->load_error); + else if (size == 0) source_pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, icon_info->desired_scale, NULL, diff --git a/gtk/meson.build b/gtk/meson.build index 166e81e1a1..8dedfdd4c0 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -1021,7 +1021,7 @@ gtk_tools = [ ['gtk4-query-settings', ['gtk-query-settings.c']], ['gtk4-builder-tool', ['gtk-builder-tool.c']], ['gtk4-update-icon-cache', ['updateiconcache.c']], - ['gtk4-encode-symbolic-svg', ['encodesymbolic.c']], + ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']], ['gtk4-query-immodules', ['queryimmodules.c', 'gtkutils.c']], ]