Add 'only foreground' to texture utility api

Add an 'only_fg' argument to all our internal texture utility
api, so GtkIconTheme can find out if a symbolic png or svg uses
colors beyond the foreground or not.

This information is used in gtk_symbolic_paintable_snapshot_symbolic
to optimize rendering of such symbolic icons.
This commit is contained in:
Matthias Clasen 2024-04-28 15:05:29 -04:00
parent 08d1353cde
commit 4862c3f779
5 changed files with 179 additions and 37 deletions

View File

@ -21,9 +21,16 @@
#include "gtkscalerprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/loaders/gdkpngprivate.h"
/* {{{ Pixbuf helpers */
static inline gboolean
pixbuf_is_only_fg (GdkPixbuf *pixbuf)
{
return gdk_pixbuf_get_option (pixbuf, "tEXt::only-foreground") != NULL;
}
static GdkPixbuf *
load_from_stream (GdkPixbufLoader *loader,
GInputStream *stream,
@ -246,7 +253,7 @@ load_symbolic_svg (const char *escaped_file_data,
return pixbuf;
}
static void
static gboolean
extract_plane (GdkPixbuf *src,
GdkPixbuf *dst,
int from_plane,
@ -257,6 +264,7 @@ extract_plane (GdkPixbuf *src,
gsize src_stride, dst_stride;
guchar *src_row, *dst_row;
int x, y;
gboolean all_clear = TRUE;
width = gdk_pixbuf_get_width (src);
height = gdk_pixbuf_get_height (src);
@ -276,11 +284,16 @@ extract_plane (GdkPixbuf *src,
dst_row = dst_data + dst_stride * y;
for (x = 0; x < width; x++)
{
if (src_row[from_plane] != 0)
all_clear = FALSE;
dst_row[to_plane] = src_row[from_plane];
src_row += 4;
dst_row += 4;
}
}
return all_clear;
}
GdkPixbuf *
@ -303,6 +316,7 @@ gtk_make_symbolic_pixbuf_from_data (const char *file_data,
int icon_width, icon_height;
char *escaped_file_data;
gsize len;
gboolean only_fg;
/* Fetch size from the original icon */
GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
@ -328,6 +342,7 @@ gtk_make_symbolic_pixbuf_from_data (const char *file_data,
if (height == 0)
height = icon_height * scale;
only_fg = TRUE;
for (plane = 0; plane < 3; plane++)
{
/* Here we render the svg with all colors solid, this should
@ -374,11 +389,14 @@ gtk_make_symbolic_pixbuf_from_data (const char *file_data,
if (plane == 0)
extract_plane (loaded, pixbuf, 3, 3);
extract_plane (loaded, pixbuf, 0, plane);
only_fg &= extract_plane (loaded, pixbuf, 0, plane);
g_object_unref (loaded);
}
if (only_fg)
gdk_pixbuf_set_option (pixbuf, "tEXt::only-foreground", "true");
g_free (escaped_file_data);
out:
@ -458,11 +476,65 @@ make_symbolic_pixbuf_from_file (GFile *file,
/* }}} */
/* {{{ Texture API */
static GdkTexture *
texture_new_from_bytes (GBytes *bytes,
gboolean *only_fg,
GError **error)
{
GHashTable *options;
GdkTexture *texture;
if (!gdk_is_png (bytes))
return gdk_texture_new_from_bytes (bytes, error);
options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
texture = gdk_load_png (bytes, options, error);
*only_fg = g_hash_table_contains (options, "foreground-only");
g_hash_table_unref (options);
return texture;
}
GdkTexture *
gdk_texture_new_from_filename_with_fg (const char *filename,
gboolean *only_fg,
GError **error)
{
GFile *file;
GBytes *bytes;
GdkTexture *texture = NULL;
file = g_file_new_for_path (filename);
bytes = g_file_load_bytes (file, NULL, NULL, error);
if (bytes)
texture = texture_new_from_bytes (bytes, only_fg, error);
g_bytes_unref (bytes);
g_object_unref (file);
return texture;
}
GdkTexture *
gdk_texture_new_from_resource_with_fg (const char *path,
gboolean *only_fg)
{
GBytes *bytes;
GdkTexture *texture = NULL;
bytes = g_resources_lookup_data (path, 0, NULL);
if (bytes)
texture = texture_new_from_bytes (bytes, only_fg, NULL);
g_bytes_unref (bytes);
return texture;
}
GdkTexture *
gdk_texture_new_from_stream_at_scale (GInputStream *stream,
int width,
int height,
gboolean aspect,
gboolean *only_fg,
GCancellable *cancellable,
GError **error)
{
@ -472,6 +544,7 @@ gdk_texture_new_from_stream_at_scale (GInputStream *stream,
pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, width, height, aspect, cancellable, error);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
@ -481,6 +554,7 @@ gdk_texture_new_from_stream_at_scale (GInputStream *stream,
GdkTexture *
gdk_texture_new_from_stream (GInputStream *stream,
gboolean *only_fg,
GCancellable *cancellable,
GError **error)
{
@ -490,6 +564,7 @@ gdk_texture_new_from_stream (GInputStream *stream,
pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, 0, cancellable, error);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
@ -502,6 +577,7 @@ gdk_texture_new_from_resource_at_scale (const char *path,
int width,
int height,
gboolean preserve_aspect,
gboolean *only_fg,
GError **error)
{
GdkPixbuf *pixbuf;
@ -510,6 +586,7 @@ gdk_texture_new_from_resource_at_scale (const char *path,
pixbuf = _gdk_pixbuf_new_from_resource_at_scale (path, width, height, preserve_aspect, error);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
@ -525,6 +602,7 @@ gdk_texture_new_from_path_symbolic (const char *path,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error)
{
GdkPixbuf *pixbuf;
@ -533,6 +611,7 @@ gdk_texture_new_from_path_symbolic (const char *path,
pixbuf = make_symbolic_pixbuf_from_path (path, width, height, scale, error);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
@ -551,6 +630,7 @@ gdk_texture_new_from_resource_symbolic (const char *path,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error)
{
GdkPixbuf *pixbuf;
@ -559,6 +639,7 @@ gdk_texture_new_from_resource_symbolic (const char *path,
pixbuf = make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
@ -593,14 +674,19 @@ gdk_texture_new_from_file_symbolic (GFile *file,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error)
{
GdkPixbuf *pixbuf;
GdkTexture *texture;
GdkTexture *texture = NULL;
pixbuf = make_symbolic_pixbuf_from_file (file, width, height, scale, error);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
if (pixbuf)
{
*only_fg = pixbuf_is_only_fg (pixbuf);
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
return texture;
}

View File

@ -29,35 +29,46 @@ GdkPixbuf *gtk_make_symbolic_pixbuf_from_data (const char *data,
const char *debug_output_to,
GError **error);
GdkTexture *gdk_texture_new_from_filename_with_fg (const char *filename,
gboolean *only_fg,
GError **error);
GdkTexture *gdk_texture_new_from_resource_with_fg (const char *path,
gboolean *only_fg);
GdkTexture *gdk_texture_new_from_stream (GInputStream *stream,
gboolean *only_fg,
GCancellable *cancellable,
GError **error);
GdkTexture *gdk_texture_new_from_stream_at_scale (GInputStream *stream,
int width,
int height,
gboolean aspect,
gboolean *only_fg,
GCancellable *cancellable,
GError **error);
GdkTexture *gdk_texture_new_from_resource_at_scale (const char *path,
int width,
int height,
gboolean aspect,
gboolean *only_fg,
GError **error);
GdkTexture *gdk_texture_new_from_path_symbolic (const char *path,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error);
GdkTexture *gdk_texture_new_from_file_symbolic (GFile *file,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error);
GdkTexture *gdk_texture_new_from_resource_symbolic (const char *path,
int width,
int height,
double scale,
gboolean *only_fg,
GError **error);
GdkTexture *gtk_load_symbolic_texture_from_file (GFile *file);

View File

@ -97,6 +97,7 @@ gtk_css_image_recolor_load_texture (GtkCssImageRecolor *recolor,
GError **error)
{
char *uri;
gboolean only_fg;
if (recolor->texture)
return;
@ -110,7 +111,7 @@ gtk_css_image_recolor_load_texture (GtkCssImageRecolor *recolor,
if (g_str_has_suffix (uri, ".symbolic.png"))
recolor->texture = gtk_load_symbolic_texture_from_resource (resource_path);
else
recolor->texture = gdk_texture_new_from_resource_symbolic (resource_path, 0, 0, 1.0, NULL);
recolor->texture = gdk_texture_new_from_resource_symbolic (resource_path, 0, 0, 1.0, &only_fg, NULL);
g_free (resource_path);
}
@ -119,7 +120,7 @@ gtk_css_image_recolor_load_texture (GtkCssImageRecolor *recolor,
if (g_str_has_suffix (uri, ".symbolic.png"))
recolor->texture = gtk_load_symbolic_texture_from_file (recolor->file);
else
recolor->texture = gdk_texture_new_from_file_symbolic (recolor->file, 0, 0, 1.0, NULL);
recolor->texture = gdk_texture_new_from_file_symbolic (recolor->file, 0, 0, 1.0, &only_fg, NULL);
}
g_free (uri);

View File

@ -414,6 +414,7 @@ struct _GtkIconPaintable
guint is_svg : 1;
guint is_resource : 1;
guint is_symbolic : 1;
guint only_fg : 1;
/* Cached information if we go ahead and try to load the icon.
*
@ -3712,6 +3713,7 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
gint64 before;
int pixel_size;
GError *load_error = NULL;
gboolean only_fg = FALSE;
icon_cache_mark_used_if_cached (icon);
@ -3739,27 +3741,31 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
{
if (icon->is_svg)
{
if (gtk_icon_paintable_is_symbolic (icon))
if (icon->is_symbolic)
icon->texture = gdk_texture_new_from_resource_symbolic (icon->filename,
pixel_size, pixel_size,
icon->desired_scale,
&only_fg,
&load_error);
else
icon->texture = gdk_texture_new_from_resource_at_scale (icon->filename,
pixel_size, pixel_size,
TRUE, &load_error);
TRUE,
&only_fg,
&load_error);
}
else
icon->texture = gdk_texture_new_from_resource (icon->filename);
icon->texture = gdk_texture_new_from_resource_with_fg (icon->filename, &only_fg);
}
else if (icon->filename)
{
if (icon->is_svg)
{
if (gtk_icon_paintable_is_symbolic (icon))
if (icon->is_symbolic)
icon->texture = gdk_texture_new_from_path_symbolic (icon->filename,
pixel_size, pixel_size,
icon->desired_scale,
&only_fg,
&load_error);
else
{
@ -3770,7 +3776,9 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
{
icon->texture = gdk_texture_new_from_stream_at_scale (stream,
pixel_size, pixel_size,
TRUE, NULL,
TRUE,
&only_fg,
NULL,
&load_error);
g_object_unref (stream);
}
@ -3780,7 +3788,7 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
}
else
{
icon->texture = gdk_texture_new_from_filename (icon->filename, &load_error);
icon->texture = gdk_texture_new_from_filename_with_fg (icon->filename, &only_fg, &load_error);
}
}
else
@ -3798,15 +3806,19 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
if (icon->is_svg)
icon->texture = gdk_texture_new_from_stream_at_scale (stream,
pixel_size, pixel_size,
TRUE, NULL,
TRUE,
&only_fg,
NULL,
&load_error);
else
icon->texture = gdk_texture_new_from_stream (stream, NULL, &load_error);
icon->texture = gdk_texture_new_from_stream (stream, &only_fg, NULL, &load_error);
g_object_unref (stream);
}
}
icon->only_fg = only_fg;
if (!icon->texture)
{
g_warning ("Failed to load icon %s: %s", icon->filename, load_error ? load_error->message : "");
@ -3814,6 +3826,7 @@ icon_ensure_texture__locked (GtkIconPaintable *icon,
icon->texture = gdk_texture_new_from_resource (IMAGE_MISSING_RESOURCE_PATH);
icon->icon_name = g_strdup ("image-missing");
icon->is_symbolic = FALSE;
icon->only_fg = FALSE;
}
if (GDK_PROFILER_IS_RUNNING)
@ -3896,22 +3909,9 @@ gtk_icon_paintable_snapshot_symbolic (GtkSymbolicPaintable *paintable,
int texture_width, texture_height;
double render_width;
double render_height;
gboolean symbolic;
graphene_rect_t render_rect;
texture = gtk_icon_paintable_ensure_texture (icon);
symbolic = gtk_icon_paintable_is_symbolic (icon);
if (symbolic)
{
graphene_matrix_t matrix;
graphene_vec4_t offset;
init_color_matrix (&matrix, &offset,
&colors[0], &colors[3],
&colors[2], &colors[1]);
gtk_snapshot_push_color_matrix (snapshot, &matrix, &offset);
}
texture_width = gdk_texture_get_width (texture);
texture_height = gdk_texture_get_height (texture);
@ -3928,14 +3928,39 @@ gtk_icon_paintable_snapshot_symbolic (GtkSymbolicPaintable *paintable,
render_height = height;
}
gtk_snapshot_append_texture (snapshot, texture,
&GRAPHENE_RECT_INIT ((width - render_width) / 2,
(height - render_height) / 2,
render_width,
render_height));
graphene_rect_init (&render_rect,
(width - render_width) / 2,
(height - render_height) / 2,
render_width,
render_height);
if (symbolic)
gtk_snapshot_pop (snapshot);
if (icon->is_symbolic && icon->only_fg)
{
g_debug ("snapshot symbolic icon using mask");
gtk_snapshot_push_mask (snapshot, GSK_MASK_MODE_ALPHA);
gtk_snapshot_append_texture (snapshot, texture, &render_rect);
gtk_snapshot_pop (snapshot);
gtk_snapshot_append_color (snapshot, &colors[0], &render_rect);
gtk_snapshot_pop (snapshot);
}
else if (icon->is_symbolic)
{
graphene_matrix_t matrix;
graphene_vec4_t offset;
g_debug ("snapshot symbolic icon using color-matrix");
init_color_matrix (&matrix, &offset,
&colors[0], &colors[3],
&colors[2], &colors[1]);
gtk_snapshot_push_color_matrix (snapshot, &matrix, &offset);
gtk_snapshot_append_texture (snapshot, texture, &render_rect);
gtk_snapshot_pop (snapshot);
}
else
{
gtk_snapshot_append_texture (snapshot, texture, &render_rect);
}
}
static GdkPaintableFlags

View File

@ -56,6 +56,9 @@ main (int argc, char **argv)
GFile *dest;
char *data;
gsize len;
GHashTable *options;
GPtrArray *keys;
GPtrArray *values;
setlocale (LC_ALL, "");
@ -142,7 +145,19 @@ main (int argc, char **argv)
return 1;
}
if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
options = gdk_pixbuf_get_options (symbolic);
keys = g_hash_table_get_keys_as_ptr_array (options);
values = g_hash_table_get_values_as_ptr_array (options);
g_ptr_array_add (keys, NULL);
g_ptr_array_add (values, NULL);
if (!gdk_pixbuf_save_to_streamv (symbolic,
G_OUTPUT_STREAM (out),
"png",
(char **) keys->pdata,
(char **) values->pdata,
NULL,
&error))
{
g_printerr (_("Cant save file %s: %s\n"), pngpath, error->message);
return 1;
@ -154,6 +169,10 @@ main (int argc, char **argv)
return 1;
}
g_ptr_array_unref (keys);
g_ptr_array_unref (values);
g_hash_table_unref (options);
g_object_unref (out);
g_free (pngpath);