From 9ce447152715292b6acda477c43ad70e7719ea62 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 28 Apr 2024 16:55:55 -0400 Subject: [PATCH] Speed up symbolic svg loading If the svg doesn't use the symbolic style classes, we can avoid loading it multiple times. This brought the time for loading system-run-symbolic at 256@2 from 6.8ms down to 2ms. --- gtk/gdktextureutils.c | 162 +++++++++++++++++++++++++++++++++--------- meson.build | 2 + 2 files changed, 130 insertions(+), 34 deletions(-) diff --git a/gtk/gdktextureutils.c b/gtk/gdktextureutils.c index 534a0e0e71..158573650e 100644 --- a/gtk/gdktextureutils.c +++ b/gtk/gdktextureutils.c @@ -296,6 +296,99 @@ extract_plane (GdkPixbuf *src, return all_clear; } +static void +keep_alpha (GdkPixbuf *src) +{ + guchar *data; + int width, height; + gsize stride; + + data = gdk_pixbuf_get_pixels (src); + width = gdk_pixbuf_get_width (src); + height = gdk_pixbuf_get_height (src); + stride = gdk_pixbuf_get_rowstride (src); + + for (int y = 0; y < height; y++) + { + guchar *row = data + stride * y; + for (int x = 0; x < width; x++) + { + row[0] = row[1] = row[2] = 0; + row += 4; + } + } +} + +static void +svg_find_size_strings (const char *data, + gsize len, + char **width, + char **height) +{ + gsize i, j, k, l; + + *width = NULL; + *height = NULL; + + for (i = 0; i < len - 4; i++) + { + if (strncmp (data + i, "') + { + break; + } + } + + break; + } + } + + *width = g_strdup ("16px"); + *height = g_strdup ("16px"); +} + +static gboolean +svg_has_symbolic_classes (const char *data, + gsize len) +{ +#ifdef HAVE_MEMMEM + return memmem (data, len, "class=\"error\"", strlen ("class=\"error\"")) != NULL || + memmem (data, len, "class=\"warning\"", strlen ("class=\"warning\"")) != NULL || + memmem (data, len, "class=\"success\"", strlen ("class=\"success\"")) != NULL; +#else + return TRUE; +#endif +} + GdkPixbuf * gtk_make_symbolic_pixbuf_from_data (const char *file_data, gsize file_len, @@ -308,43 +401,46 @@ gtk_make_symbolic_pixbuf_from_data (const char *file_data, { const char *r_string = "rgb(255,0,0)"; const char *g_string = "rgb(0,255,0)"; - char *icon_width_str; - char *icon_height_str; - GdkPixbuf *loaded; - GdkPixbuf *pixbuf = NULL; - int plane; - int icon_width, icon_height; - char *escaped_file_data; + char *icon_width_str = NULL; + char *icon_height_str = NULL; + char *escaped_file_data = NULL; gsize len; - gboolean only_fg; + GdkPixbuf *pixbuf = NULL; + gboolean has_symbolic_classes; + gboolean only_fg = TRUE; + + has_symbolic_classes = svg_has_symbolic_classes (file_data, file_len); /* Fetch size from the original icon */ - GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL); - GdkPixbuf *reference = gdk_pixbuf_new_from_stream (stream, NULL, error); + if (has_symbolic_classes || width == 0 || height == 0) + svg_find_size_strings (file_data, file_len, &icon_width_str, &icon_height_str); - g_object_unref (stream); + if (width == 0) + width = (int) (g_ascii_strtoull (icon_width_str, NULL, 0) * scale); + if (height == 0) + height = (int) (g_ascii_strtoull (icon_height_str, NULL, 0) * scale); - if (!reference) - return NULL; + if (!has_symbolic_classes) + { + GInputStream *stream; - icon_width = gdk_pixbuf_get_width (reference); - icon_height = gdk_pixbuf_get_height (reference); - g_object_unref (reference); + stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL); + pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error); + g_object_unref (stream); + + if (pixbuf) + keep_alpha (pixbuf); + + goto out; + } escaped_file_data = g_base64_encode ((guchar *) file_data, file_len); len = strlen (escaped_file_data); - icon_width_str = g_strdup_printf ("%d", icon_width); - icon_height_str = g_strdup_printf ("%d", icon_height); - - if (width == 0) - width = icon_width * scale; - if (height == 0) - height = icon_height * scale; - - only_fg = TRUE; - for (plane = 0; plane < 3; plane++) + for (int plane = 0; plane < 3; plane++) { + GdkPixbuf *loaded; + /* 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 @@ -378,28 +474,26 @@ gtk_make_symbolic_pixbuf_from_data (const char *file_data, g_free (filename); } - if (pixbuf == NULL) + if (plane == 0) { pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, gdk_pixbuf_get_width (loaded), gdk_pixbuf_get_height (loaded)); - gdk_pixbuf_fill (pixbuf, 0); - } + memset (gdk_pixbuf_get_pixels (pixbuf), 0, gdk_pixbuf_get_byte_length (pixbuf)); - if (plane == 0) - extract_plane (loaded, pixbuf, 3, 3); + extract_plane (loaded, pixbuf, 3, 3); + } only_fg &= extract_plane (loaded, pixbuf, 0, plane); g_object_unref (loaded); } - if (only_fg) +out: + if (only_fg && pixbuf) gdk_pixbuf_set_option (pixbuf, "tEXt::only-foreground", "true"); g_free (escaped_file_data); - -out: g_free (icon_width_str); g_free (icon_height_str); diff --git a/meson.build b/meson.build index a00878961c..cd76d4ba33 100644 --- a/meson.build +++ b/meson.build @@ -201,6 +201,7 @@ check_functions = [ 'posix_fallocate', 'sincos', 'sincosf', + 'memmem', ] foreach func : check_functions @@ -208,6 +209,7 @@ foreach func : check_functions args: '-D_GNU_SOURCE', prefix: '#include \n' + + '#include \n' + '#include \n' + '#include \n' + '#include \n' +