[gtk+] Fix symbolic.svg rendering



commit 284fd36e5d3f1297a6e443971802f9191b11f42a
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Nov 7 18:55:01 2017 -0500

    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 3b24ca7..d1cc606 100644
--- a/gtk/encodesymbolic.c
+++ b/gtk/encodesymbolic.c
@@ -18,7 +18,6 @@
 #include "config.h"
 
 #include <glib.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
 #include <gdk/gdk.h>
 #include <glib/gi18n.h>
 
@@ -32,6 +31,8 @@
 #include <stdlib.h>
 #include <locale.h>
 
+#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 ("<?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;
-        }
-    }
-}
-
-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 3da0323..ac9863a 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;
+}
diff --git a/gtk/gdkpixbufutilsprivate.h b/gtk/gdkpixbufutilsprivate.h
index f01f177..1ea23dc 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 f738579..94e0323 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 166e81e..8dedfdd 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']],
 ]
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]