diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 86f7e7408f..4f74c1507e 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -1629,7 +1629,8 @@ endif # bin_PROGRAMS = \ gtk-query-immodules-3.0 \ - gtk-launch + gtk-launch \ + gtk-encode-symbolic-svg if BUILD_ICON_CACHE bin_PROGRAMS += gtk-update-icon-cache @@ -1676,6 +1677,10 @@ gtk_update_icon_cache_LDADD = $(GDK_PIXBUF_LIBS) gtk_update_icon_cache_SOURCES = updateiconcache.c endif +gtk_encode_symbolic_svg_LDADD = $(GDK_PIXBUF_LIBS) $(top_builddir)/gdk/libgdk-3.la +gtk_encode_symbolic_svg_SOURCES = encodesymbolic.c + + gtk_launch_LDADD = $(LDADDS) gtk_launch_SOURCES = gtk-launch.c diff --git a/gtk/encodesymbolic.c b/gtk/encodesymbolic.c new file mode 100644 index 0000000000..6f4ba57a41 --- /dev/null +++ b/gtk/encodesymbolic.c @@ -0,0 +1,326 @@ +/* encodesymbolic.c + * Copyright (C) 2014 Alexander Larsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef G_OS_WIN32 +#include +#endif +#include +#include + +static gchar *output_dir = NULL; + +static GOptionEntry args[] = { + { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL }, + { 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 ("\n" + "\n" + " \n" + " \n" + "", + 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) +{ + gchar *path, *basename, *pngpath, *pngfile, *dot; + GOptionContext *context; + GdkPixbuf *symbolic; + GError *error; + int width, height; + gchar **sizev; + GFileOutputStream *out; + GFile *dest; + + setlocale (LC_ALL, ""); + +#ifdef ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif +#endif + + g_set_prgname ("gtk-encode-symbolic-svg"); + + context = g_option_context_new ("PATH WIDTHxHEIGHT"); + g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE); + + g_option_context_parse (context, &argc, &argv, NULL); + + if (argc < 3) + { + g_printerr (g_option_context_get_help (context, FALSE, NULL)); + return 1; + } + + width = 0; + height = 0; + sizev = g_strsplit (argv[2], "x", 0); + if (g_strv_length (sizev) == 2) + { + width = atoi(sizev[0]); + height = atoi(sizev[1]); + } + g_strfreev (sizev); + + if (width == 0 || height == 0) + { + g_printerr (_("Invalid size %s\n"), argv[2]); + return 1; + } + + path = argv[1]; +#ifdef G_OS_WIN32 + path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL); +#endif + + error = NULL; + symbolic = make_symbolic_pixbuf (path, width, height, &error); + if (symbolic == NULL) + { + g_printerr ("Can't load file: %s\n", error->message); + return 1; + } + + basename = g_path_get_basename (path); + + dot = strchr(basename, '.'); + if (dot != NULL) + *dot = 0; + pngfile = g_strconcat (basename, ".symbolic.png", NULL); + g_free (basename); + + if (output_dir != NULL) + pngpath = g_build_filename (output_dir, pngfile, NULL); + else + pngpath = g_strdup (pngfile); + + g_free (pngfile); + + dest = g_file_new_for_path (pngpath); + + + out = g_file_replace (dest, + NULL, FALSE, + G_FILE_CREATE_REPLACE_DESTINATION, + NULL, &error); + if (out == NULL) + { + g_printerr ("Can't save file %s: %s\n", pngpath, error->message); + return 1; + } + + if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL)) + { + g_printerr ("Can't save file %s: %s\n", pngpath, error->message); + return 1; + } + + if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error)) + { + g_printerr ("Can't close stream"); + return 1; + } + + g_object_unref (out); + g_free (pngpath); + + return 0; +}