[gnome-software/wip/hughsie/recolor-app-tile] Recolor the GsAppTile with a symbolic icon



commit b3f9b14388dd0f8ae75c38a06b1d00a387347d6a
Author: Richard Hughes <richard hughsie com>
Date:   Tue Apr 5 19:27:57 2016 +0100

    Recolor the GsAppTile with a symbolic icon

 data/Makefile.am                   |    1 +
 data/tile-symbolic.svg             |  259 ++++++++++++++++++++++++++++++++++++
 src/gs-cmd.c                       |    2 +
 src/gs-plugin.h                    |    1 +
 src/gs-popular-tile.c              |   25 ++++
 src/gs-shell-overview.c            |    9 +-
 src/plugins/Makefile.am            |    6 +
 src/plugins/gs-plugin-key-colors.c |  232 ++++++++++++++++++++++++++++++++
 8 files changed, 532 insertions(+), 3 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index e5a45f1..8fbcaf5 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -14,6 +14,7 @@ org.gnome.Software.xml.gz: $(appstream_files)
 
 resourcesdir = $(datadir)/gnome-software
 dist_resources_DATA =                                  \
+       tile-symbolic.svg                               \
        featured-ardour.png                             \
        featured-ardour-bg.png                          \
        featured-blender.png                            \
diff --git a/data/tile-symbolic.svg b/data/tile-symbolic.svg
new file mode 100644
index 0000000..66b218c
--- /dev/null
+++ b/data/tile-symbolic.svg
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="60.959999mm"
+   height="60.959999mm"
+   viewBox="0 0 216 216"
+   id="svg6040"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="app-tile-pattern-bg-symbolic.svg">
+  <defs
+     id="defs6042" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4142136"
+     inkscape:cx="134.08264"
+     inkscape:cy="157.87158"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1016"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid6048"
+       originx="-79.969604"
+       originy="-688.97086" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata6045">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-79.969604,-147.39134)">
+    <g
+       id="g4251"
+       style="opacity:0.25030525">
+      <path
+         inkscape:connector-curvature="0"
+         id="path6617"
+         d="m 79.969762,219.39121 35.999938,-35.99995 35.99995,35.99995 z"
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6623"
+         d="M 223.96955,147.39131 187.9696,183.39126 151.96965,147.39131 Z"
+         class="error"
+         
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,219.39126 35.99995,-35.99995 35.99995,35.99995 z"
+         id="path6625"
+         inkscape:connector-curvature="0" />
+      <path
+         class="error"
+         
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,147.39131 35.99995,35.99995 -35.99995,36 z"
+         id="path6629"
+         inkscape:connector-curvature="0" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6643"
+         d="m 223.96955,219.39131 35.99995,-35.99995 35.99995,35.99995 z"
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6647"
+         d="m 223.96955,147.39136 35.99995,35.99995 -35.99995,36 z"
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6675"
+         d="M 151.96965,219.39131 115.9697,255.39126 79.969762,219.39131 Z"
+         
style="fill:#29ff29;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6679"
+         d="m 151.96965,291.39126 -35.9999,-35.99995 35.9999,-35.99995 z"
+         class="error"
+         
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         
style="fill:#29ff29;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 79.969762,219.39131 35.999938,35.99995 -35.999938,36 z"
+         id="path6681"
+         inkscape:connector-curvature="0" />
+      <path
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 223.96955,219.39136 187.9696,255.39131 151.96965,219.39136 Z"
+         id="path6683"
+         inkscape:connector-curvature="0" />
+      <path
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 223.96955,291.39131 -35.9999,-35.99995 35.9999,-35.99995 z"
+         id="path6687"
+         inkscape:connector-curvature="0" />
+      <path
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 223.96955,219.39141 35.99995,35.99995 -35.99995,36 z"
+         id="path6697"
+         inkscape:connector-curvature="0" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6703"
+         d="m 79.969762,363.39131 35.999938,-35.99995 35.99995,35.99995 z"
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,363.39131 -35.9999,-35.99995 35.9999,-35.99995 z"
+         id="path6705"
+         inkscape:connector-curvature="0" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6713"
+         d="m 223.96955,363.39136 -35.9999,-35.99995 35.9999,-35.99995 z"
+         
style="fill:#2929ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,291.39141 35.99995,35.99995 -35.99995,36 z"
+         id="path6715"
+         inkscape:connector-curvature="0" />
+      <path
+         class="error"
+         
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 295.96945,291.39146 259.9695,327.39141 223.96955,291.39146 Z"
+         id="path6717"
+         inkscape:connector-curvature="0" />
+      <path
+         class="error"
+         
style="fill:#00ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 295.96945,363.39141 -35.9999,-35.99995 35.9999,-35.99995 z"
+         id="path6721"
+         inkscape:connector-curvature="0" />
+      <path
+         class="error"
+         
style="fill:#343434;fill-opacity:0.20165746;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 295.96945,363.39141 -35.9999,-35.99995 35.9999,-35.99995 z"
+         id="path6761"
+         inkscape:connector-curvature="0" />
+      <path
+         class="warning"
+         inkscape:connector-curvature="0"
+         id="path6763"
+         d="m 151.96967,363.39141 35.9999,-35.99995 -35.9999,-35.99995 z"
+         
style="fill:#000000;fill-opacity:0.52173911;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="warning"
+         
style="fill:#000000;fill-opacity:0.52173911;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96966,363.39129 -35.99995,-35.9999 -35.999954,35.9999 z"
+         id="path6765"
+         inkscape:connector-curvature="0" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6767"
+         d="m 223.96955,219.39123 -35.99995,-35.9999 -35.99995,35.9999 z"
+         
style="fill:#000000;fill-opacity:0.52173911;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="error"
+         inkscape:connector-curvature="0"
+         id="path6773"
+         d="M 79.969782,291.39123 115.96968,255.39128 79.969782,219.39133 Z"
+         
style="fill:#000000;fill-opacity:0.31521744;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="error"
+         inkscape:connector-curvature="0"
+         id="path6775"
+         d="m 151.96966,219.39133 -35.99995,35.9999 -35.999956,-35.9999 z"
+         
style="fill:#000000;fill-opacity:0.52173911;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="error"
+         
style="fill:#000000;fill-opacity:0.31521744;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,147.39133 35.99995,35.9999 35.99995,-35.9999 z"
+         id="path6777"
+         inkscape:connector-curvature="0" />
+      <path
+         class="warning"
+         inkscape:connector-curvature="0"
+         id="path6779"
+         d="m 223.96955,219.39129 35.99995,-35.9999 35.99995,35.9999 z"
+         
style="fill:#000000;fill-opacity:0.31521744;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 151.96965,219.39121 115.9697,183.39127 151.96965,147.39132 Z"
+         id="path6781"
+         inkscape:connector-curvature="0" />
+      <path
+         class="success"
+         inkscape:connector-curvature="0"
+         id="path6783"
+         d="m 223.96955,219.39141 -35.9999,35.99995 35.9999,35.99995 z"
+         
style="fill:#000000;fill-opacity:0.31521744;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         inkscape:connector-curvature="0"
+         id="path6785"
+         d="M 295.96945,291.39146 259.9695,255.39151 223.96955,291.39146 Z"
+         class="success"
+         
style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+      <path
+         class="warning"
+         
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,291.39131 35.99995,35.99995 35.99995,-35.99995 z"
+         id="path6787"
+         inkscape:connector-curvature="0" />
+      <path
+         class="warning"
+         
style="fill:#000000;fill-opacity:0.31521744;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 151.96965,291.39133 35.99995,35.99991 35.99995,-35.99991 z"
+         id="path6789"
+         inkscape:connector-curvature="0" />
+      <path
+         class="success"
+         
style="fill:#000000;fill-opacity:0.60326089;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 223.96955,363.39141 -35.9999,-35.99995 35.9999,-35.99995 z"
+         id="path6795"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index 116315c..927c7c3 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -144,6 +144,8 @@ gs_cmd_refine_flag_from_string (const gchar *flag, GError **error)
                return GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS;
        if (g_strcmp0 (flag, "review-ratings") == 0)
                return GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS;
+       if (g_strcmp0 (flag, "background") == 0)
+               return GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND;
        g_set_error (error,
                     GS_PLUGIN_ERROR,
                     GS_PLUGIN_ERROR_NOT_SUPPORTED,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 6242375..a800e84 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -112,6 +112,7 @@ typedef enum {
        GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE       = 1 << 17,
        GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS          = 1 << 18,
        GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS   = 1 << 19,
+       GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND       = 1 << 20,
        GS_PLUGIN_REFINE_FLAGS_LAST
 } GsPluginRefineFlags;
 
diff --git a/src/gs-popular-tile.c b/src/gs-popular-tile.c
index d083104..e8f5b48 100644
--- a/src/gs-popular-tile.c
+++ b/src/gs-popular-tile.c
@@ -103,6 +103,8 @@ app_state_changed (GsApp *app, GParamSpec *pspec, GsPopularTile *tile)
 void
 gs_popular_tile_set_app (GsPopularTile *tile, GsApp *app)
 {
+       const gchar *background;
+
        g_return_if_fail (GS_IS_POPULAR_TILE (tile));
        g_return_if_fail (GS_IS_APP (app) || app == NULL);
 
@@ -129,6 +131,29 @@ gs_popular_tile_set_app (GsPopularTile *tile, GsApp *app)
        gs_image_set_from_pixbuf (GTK_IMAGE (tile->image), gs_app_get_pixbuf (tile->app));
 
        gtk_label_set_label (GTK_LABEL (tile->label), gs_app_get_name (app));
+
+       /* do we have new CSS to use? */
+       background = gs_app_get_metadata_item (app, "GnomeSoftware::popular-background");
+       if (background != NULL) {
+               GtkStyleContext *context;
+               g_autofree gchar *css = NULL;
+               g_autoptr(GtkCssProvider) css_provider = NULL;
+               context = gtk_widget_get_style_context (GTK_WIDGET (tile));
+               css_provider = gtk_css_provider_new ();
+               gtk_style_context_add_provider (context,
+                                               GTK_STYLE_PROVIDER (css_provider),
+                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+               css = g_strdup_printf (
+                               ".view.tile {\n"
+                               "       padding: 0;\n"
+                               "       box-shadow: inset 0 1px @theme_base_color, 0 1px 1px 
alpha(black,0.4);\n"
+                               "       border: 1px solid mix(@theme_base_color,@theme_fg_color,0.3);\n"
+                               "       background: %s;\n"
+                               "       background-size: 100%% 100%%;\n"
+                               "       background-repeat: no-repeat;\n"
+                               "}", background);
+               gtk_css_provider_load_from_data (css_provider, css, -1, NULL);
+       }
 }
 
 static void
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index ec35c82..71320bf 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -391,7 +391,8 @@ gs_shell_overview_load (GsShellOverview *self)
        if (!priv->loading_featured) {
                priv->loading_featured = TRUE;
                gs_plugin_loader_get_featured_async (priv->plugin_loader,
-                                                    GS_PLUGIN_REFINE_FLAGS_DEFAULT,
+                                                    GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+                                                    GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND,
                                                     priv->cancellable,
                                                     gs_shell_overview_get_featured_cb,
                                                     self);
@@ -401,7 +402,8 @@ gs_shell_overview_load (GsShellOverview *self)
        if (!priv->loading_popular) {
                priv->loading_popular = TRUE;
                gs_plugin_loader_get_popular_async (priv->plugin_loader,
-                                                   GS_PLUGIN_REFINE_FLAGS_DEFAULT,
+                                                   GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+                                                   GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND,
                                                    priv->cancellable,
                                                    gs_shell_overview_get_popular_cb,
                                                    self);
@@ -423,7 +425,8 @@ gs_shell_overview_load (GsShellOverview *self)
                priv->loading_popular_rotating = TRUE;
                gs_plugin_loader_get_category_apps_async (priv->plugin_loader,
                                                          featured_category,
-                                                         GS_PLUGIN_REFINE_FLAGS_DEFAULT,
+                                                         GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+                                                         GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND,
                                                          priv->cancellable,
                                                          gs_shell_overview_get_popular_rotating_cb,
                                                          load_data);
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 32d2605..da7ce58 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -31,6 +31,7 @@ noinst_LTLIBRARIES =                                  \
 plugindir = $(libdir)/gs-plugins-${GS_PLUGIN_API_VERSION}
 plugin_LTLIBRARIES =                                   \
        libgs_plugin_appstream.la                       \
+       libgs_plugin_key_colors.la                      \
        libgs_plugin_dummy.la                           \
        libgs_plugin_hardcoded-blacklist.la             \
        libgs_plugin_menu-spec-categories.la            \
@@ -94,6 +95,11 @@ libgs_plugin_dummy_la_LIBADD = $(GS_PLUGIN_LIBS)
 libgs_plugin_dummy_la_LDFLAGS = -module -avoid-version
 libgs_plugin_dummy_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
 
+libgs_plugin_key_colors_la_SOURCES = gs-plugin-key-colors.c
+libgs_plugin_key_colors_la_LIBADD = $(GS_PLUGIN_LIBS)
+libgs_plugin_key_colors_la_LDFLAGS = -module -avoid-version
+libgs_plugin_key_colors_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+
 if HAVE_SHELL_EXTENSIONS
 libgs_plugin_shell_extensions_la_SOURCES = gs-plugin-shell-extensions.c
 libgs_plugin_shell_extensions_la_LIBADD = $(GS_PLUGIN_LIBS) $(JSON_GLIB_LIBS)
diff --git a/src/plugins/gs-plugin-key-colors.c b/src/plugins/gs-plugin-key-colors.c
new file mode 100644
index 0000000..05d8b91
--- /dev/null
+++ b/src/plugins/gs-plugin-key-colors.c
@@ -0,0 +1,232 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011-2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <gs-plugin.h>
+
+/**
+ * gs_plugin_get_name:
+ */
+const gchar *
+gs_plugin_get_name (void)
+{
+       return "key-colors";
+}
+
+/**
+ * gs_plugin_get_deps:
+ */
+const gchar **
+gs_plugin_get_deps (GsPlugin *plugin)
+{
+       static const gchar *deps[] = {
+               "icons",        /* need icon */
+               NULL };
+       return deps;
+}
+
+typedef struct {
+       gdouble          R;
+       gdouble          G;
+       gdouble          B;
+} CdColorRGB;
+
+typedef struct {
+       guint8           R;
+       guint8           G;
+       guint8           B;
+} CdColorRGB8;
+
+/**
+ * cd_color_rgb8_to_uint32:
+ */
+static guint32
+cd_color_rgb8_to_uint32 (CdColorRGB8 *rgb)
+{
+       return (guint32) rgb->R |
+               (guint32) rgb->G << 8 |
+               (guint32) rgb->B << 16;
+}
+
+typedef struct {
+       CdColorRGB      color;
+       guint           cnt;
+} GsColorBin;
+
+/**
+ * gs_color_bin_sort_cb:
+ */
+static gint
+gs_color_bin_sort_cb (gconstpointer a, gconstpointer b)
+{
+       GsColorBin *s1 = (GsColorBin *) a;
+       GsColorBin *s2 = (GsColorBin *) b;
+       if (s1->cnt < s2->cnt)
+               return 1;
+       if (s1->cnt > s2->cnt)
+               return -1;
+       return 0;
+}
+
+/**
+ * gs_plugin_key_colors_calculate:
+ */
+static GPtrArray *
+gs_plugin_key_colors_calculate (GdkPixbuf *pb, guint number)
+{
+       GList *l;
+       GPtrArray *key_colors = NULL;
+       gint rowstride, n_channels;
+       gint x, y;
+       guchar *pixels, *p;
+       guint bin_size = 200;
+       guint i;
+       guint number_of_bins;
+       g_autoptr(AsImage) im = NULL;
+
+       /* go through each pixel */
+       n_channels = gdk_pixbuf_get_n_channels (pb);
+       rowstride = gdk_pixbuf_get_rowstride (pb);
+       pixels = gdk_pixbuf_get_pixels (pb);
+       key_colors = g_ptr_array_new_with_free_func (g_free);
+       for (bin_size = 250; bin_size > 0; bin_size -= 2) {
+               g_autoptr(GHashTable) hash = NULL;
+               hash = g_hash_table_new_full (g_direct_hash,  g_direct_equal,
+                                             NULL, g_free);
+               for (y = 0; y < gdk_pixbuf_get_height (pb); y++) {
+                       for (x = 0; x < gdk_pixbuf_get_width (pb); x++) {
+                               CdColorRGB8 tmp;
+                               GsColorBin *s;
+                               gpointer key;
+
+                               /* disregard any with alpha */
+                               p = pixels + y * rowstride + x * n_channels;
+                               if (p[3] != 255)
+                                       continue;
+
+                               /* find in cache */
+                               tmp.R = p[0] / bin_size;
+                               tmp.G = p[1] / bin_size;
+                               tmp.B = p[2] / bin_size;
+                               key = GUINT_TO_POINTER (cd_color_rgb8_to_uint32 (&tmp));
+                               s = g_hash_table_lookup (hash, key);
+                               if (s != NULL) {
+                                       s->color.R += p[0];
+                                       s->color.G += p[1];
+                                       s->color.B += p[2];
+                                       s->cnt++;
+                                       continue;
+                               }
+
+                               /* add to hash table */
+                               s = g_new0 (GsColorBin, 1);
+                               s->color.R = p[0];
+                               s->color.G = p[1];
+                               s->color.B = p[2];
+                               s->cnt = 1;
+                               g_hash_table_insert (hash, key, s);
+                       }
+               }
+
+               number_of_bins = g_hash_table_size (hash);
+//             g_debug ("number of colors: %i", number_of_bins);
+               if (number_of_bins >= number) {
+                       g_autoptr(GList) values = NULL;
+
+                       /* order by most popular */
+                       values = g_hash_table_get_values (hash);
+                       values = g_list_sort (values, gs_color_bin_sort_cb);
+                       for (l = values; l != NULL; l = l->next) {
+                               GsColorBin *s = l->data;
+                               CdColorRGB *color = g_new0 (CdColorRGB, 1);
+                               color->R = s->color.R / s->cnt;
+                               color->G = s->color.G / s->cnt;
+                               color->B = s->color.B / s->cnt;
+                               g_ptr_array_add (key_colors, color);
+                       }
+                       break;
+               }
+       }
+
+       /* the algorithm failed, so just return a monochrome ramp */
+       if (key_colors->len == 0) {
+               for (i = 0; i < 3; i++) {
+                       CdColorRGB *color = g_new0 (CdColorRGB, 1);
+                       color->R = 255 * i / 3;
+                       color->G = color->R;
+                       color->B = color->R;
+                       g_ptr_array_add (key_colors, color);
+               }
+       }
+
+       return key_colors;
+}
+
+/**
+ * gs_plugin_refine_app:
+ */
+gboolean
+gs_plugin_refine_app (GsPlugin *plugin,
+                     GsApp *app,
+                     GsPluginRefineFlags flags,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       GdkPixbuf *pb;
+       CdColorRGB *color1;
+       CdColorRGB *color2;
+       CdColorRGB *color3;
+       g_autofree gchar *css = NULL;
+       g_autoptr(GdkPixbuf) pb_small = NULL;
+       g_autoptr(GPtrArray) key_colors = NULL;
+
+       /* add a rating */
+       if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_BACKGROUND) == 0)
+               return TRUE;
+
+       /* already set */
+       if (gs_app_get_metadata_item (app, "GnomeSoftware::popular-background") != NULL)
+               return TRUE;
+
+       /* no pixbuf */
+       pb = gs_app_get_pixbuf (app);
+       if (pb == NULL)
+               return TRUE;
+
+       /* get a list of key colors */
+       pb_small = gdk_pixbuf_scale_simple (pb, 32, 32, GDK_INTERP_BILINEAR);
+       key_colors = gs_plugin_key_colors_calculate (pb_small, 10);
+       color1 = g_ptr_array_index (key_colors, 0);
+       color2 = g_ptr_array_index (key_colors, 1);
+       color3 = g_ptr_array_index (key_colors, 2);
+       css = g_strdup_printf ("-gtk-recolor(url(\"file://%s/gnome-software/tile-symbolic.svg\"), "
+                                            "error rgb(%.0f,%.0f,%.0f), "
+                                            "warning rgb(%.0f,%.0f,%.0f), "
+                                            "success rgb(%.0f,%.0f,%.0f))",
+                              DATADIR,
+                              color1->R, color1->G, color1->B,
+                              color2->R, color2->G, color2->B,
+                              color3->R, color3->G, color3->B);
+       gs_app_set_metadata (app, "GnomeSoftware::popular-background", css);
+
+       return TRUE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]