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.
This commit is contained in:
Matthias Clasen 2024-04-28 16:55:55 -04:00
parent d9582c123e
commit 9ce4471527
2 changed files with 130 additions and 34 deletions

View File

@ -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, "<svg", 4) == 0)
{
for (j = i + strlen ("<svg"); j < len - 9; j++)
{
if (strncmp (data + j, "height=\"", strlen ("height=\"")) == 0)
{
k = l = j + strlen ("height=\"");
while (l < len && data[l] != '\"')
l++;
*height = g_strndup (data + k, l - k);
if (*width && *height)
return;
j = l;
}
else if (strncmp (data + j, "width=\"", strlen ("width=\"")) == 0)
{
k = l = j + strlen ("width=\"");
while (l < len && data[l] != '\"')
l++;
*width = g_strndup (data + k, l - k);
if (*width && *height)
return;
j = l;
}
else if (data[j] == '>')
{
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);
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 (!has_symbolic_classes)
{
GInputStream *stream;
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 (!reference)
return NULL;
if (pixbuf)
keep_alpha (pixbuf);
icon_width = gdk_pixbuf_get_width (reference);
icon_height = gdk_pixbuf_get_height (reference);
g_object_unref (reference);
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);
}
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);

View File

@ -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 <stdlib.h>\n' +
'#include <string.h>\n' +
'#include <unistd.h>\n' +
'#include <sys/mman.h>\n' +
'#include <fcntl.h>\n' +