diff --git a/gtk/gdktextureutils.c b/gtk/gdktextureutils.c index 721566ff1b..534a0e0e71 100644 --- a/gtk/gdktextureutils.c +++ b/gtk/gdktextureutils.c @@ -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; } diff --git a/gtk/gdktextureutilsprivate.h b/gtk/gdktextureutilsprivate.h index 77e21a4c75..a71c357cf0 100644 --- a/gtk/gdktextureutilsprivate.h +++ b/gtk/gdktextureutilsprivate.h @@ -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); diff --git a/gtk/gtkcssimagerecolor.c b/gtk/gtkcssimagerecolor.c index ab566b3e24..df37d09a3a 100644 --- a/gtk/gtkcssimagerecolor.c +++ b/gtk/gtkcssimagerecolor.c @@ -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); diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 3505b41d20..8a322915b1 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -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 diff --git a/tools/encodesymbolic.c b/tools/encodesymbolic.c index d471394544..c36b313f53 100644 --- a/tools/encodesymbolic.c +++ b/tools/encodesymbolic.c @@ -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 (_("Can’t 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);