[gnome-software: 3/12] lib: Split key colour generation out into a separate file
- From: Phaedrus Leeds <mwleeds src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software: 3/12] lib: Split key colour generation out into a separate file
- Date: Sat, 6 Mar 2021 05:32:15 +0000 (UTC)
commit f352fbd4b9e4aeeec90eedde9148482e1d4d9e78
Author: Philip Withnall <pwithnall endlessos org>
Date: Mon Feb 22 15:20:47 2021 +0000
lib: Split key colour generation out into a separate file
This will make it a little easier to maintain and, crucially, test
separately.
The code has not been modified apart from changing it to return results
as a `GArray` rather than calling `gs_app_add_key_color()` directly.
Signed-off-by: Philip Withnall <pwithnall endlessos org>
Helps: #1148
lib/gs-app.c | 133 ++----------------------------------
lib/gs-key-colors.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/gs-key-colors.h | 27 ++++++++
lib/meson.build | 2 +
4 files changed, 226 insertions(+), 126 deletions(-)
---
diff --git a/lib/gs-app.c b/lib/gs-app.c
index b45b0728a..a9ca7d441 100644
--- a/lib/gs-app.c
+++ b/lib/gs-app.c
@@ -44,6 +44,7 @@
#include "gs-app-private.h"
#include "gs-desktop-data.h"
#include "gs-enums.h"
+#include "gs-key-colors.h"
#include "gs-os-release.h"
#include "gs-plugin.h"
#include "gs-utils.h"
@@ -4075,137 +4076,13 @@ gs_app_get_is_update_downloaded (GsApp *app)
return priv->is_update_downloaded;
}
-typedef struct {
- guint8 R;
- guint8 G;
- guint8 B;
-} CdColorRGB8;
-
-static guint32
-cd_color_rgb8_to_uint32 (CdColorRGB8 *rgb)
-{
- return (guint32) rgb->R |
- (guint32) rgb->G << 8 |
- (guint32) rgb->B << 16;
-}
-
-typedef struct {
- GdkRGBA color;
- guint cnt;
-} GsColorBin;
-
-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;
-}
-
-/* convert range of 0..255 to 0..1 */
-static inline gdouble
-_convert_from_rgb8 (guchar val)
-{
- return (gdouble) val / 255.f;
-}
-
-static void
-key_colors_set_for_pixbuf (GsApp *app, GdkPixbuf *pb, guint number)
-{
- gint rowstride, n_channels;
- gint x, y, width, height;
- guchar *pixels, *p;
- guint bin_size = 200;
- guint i;
- guint number_of_bins;
-
- /* go through each pixel */
- n_channels = gdk_pixbuf_get_n_channels (pb);
- rowstride = gdk_pixbuf_get_rowstride (pb);
- pixels = gdk_pixbuf_get_pixels (pb);
- width = gdk_pixbuf_get_width (pb);
- height = gdk_pixbuf_get_height (pb);
-
- 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 < height; y++) {
- for (x = 0; x < width; 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 = (guint8) (p[0] / bin_size);
- tmp.G = (guint8) (p[1] / bin_size);
- tmp.B = (guint8) (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.red += _convert_from_rgb8 (p[0]);
- s->color.green += _convert_from_rgb8 (p[1]);
- s->color.blue += _convert_from_rgb8 (p[2]);
- s->cnt++;
- continue;
- }
-
- /* add to hash table */
- s = g_new0 (GsColorBin, 1);
- s->color.red = _convert_from_rgb8 (p[0]);
- s->color.green = _convert_from_rgb8 (p[1]);
- s->color.blue = _convert_from_rgb8 (p[2]);
- s->color.alpha = 1.0;
- s->cnt = 1;
- g_hash_table_insert (hash, key, s);
- }
- }
-
- number_of_bins = g_hash_table_size (hash);
- 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 (GList *l = values; l != NULL; l = l->next) {
- GsColorBin *s = l->data;
- g_autofree GdkRGBA *color = g_new0 (GdkRGBA, 1);
- color->red = s->color.red / s->cnt;
- color->green = s->color.green / s->cnt;
- color->blue = s->color.blue / s->cnt;
- gs_app_add_key_color (app, color);
- }
- return;
- }
- }
-
- /* the algorithm failed, so just return a monochrome ramp */
- for (i = 0; i < 3; i++) {
- g_autofree GdkRGBA *color = g_new0 (GdkRGBA, 1);
- color->red = (gdouble) i / 3.f;
- color->green = color->red;
- color->blue = color->red;
- color->alpha = 1.0f;
- gs_app_add_key_color (app, color);
- }
-}
-
static void
calculate_key_colors (GsApp *app)
{
GsAppPrivate *priv = gs_app_get_instance_private (app);
g_autoptr(GdkPixbuf) pb_small = NULL;
const gchar *overrides_str;
+ g_autoptr(GArray) colors = NULL;
/* Lazily create the array */
if (priv->key_colors == NULL)
@@ -4262,7 +4139,11 @@ calculate_key_colors (GsApp *app)
}
/* get a list of key colors */
- key_colors_set_for_pixbuf (app, pb_small, 10);
+ colors = gs_calculate_key_colors (pb_small);
+ for (guint i = 0; i < colors->len; i++) {
+ GdkRGBA *rgba = &g_array_index (colors, GdkRGBA, i);
+ g_ptr_array_add (priv->key_colors, gdk_rgba_copy (rgba));
+ }
}
/**
diff --git a/lib/gs-key-colors.c b/lib/gs-key-colors.c
new file mode 100644
index 000000000..42fd25beb
--- /dev/null
+++ b/lib/gs-key-colors.c
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ * Copyright (C) 2014-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2021 Endless OS Foundation, Inc
+ *
+ * Authors:
+ * - Richard Hughes <richard hughsie com>
+ * - Kalev Lember <klember redhat com>
+ * - Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/**
+ * SECTION:gs-key-colors
+ * @short_description: Helper functions for calculating key colors
+ *
+ * Key colors are RGB colors which represent an app, and they are derived from
+ * the app’s icon, or manually specified as an override.
+ *
+ * Use gs_calculate_key_colors() to calculate the key colors from an app’s icon.
+ *
+ * Since: 40
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "gs-key-colors.h"
+
+typedef struct {
+ guint8 R;
+ guint8 G;
+ guint8 B;
+} CdColorRGB8;
+
+static guint32
+cd_color_rgb8_to_uint32 (CdColorRGB8 *rgb)
+{
+ return (guint32) rgb->R |
+ (guint32) rgb->G << 8 |
+ (guint32) rgb->B << 16;
+}
+
+typedef struct {
+ GdkRGBA color;
+ guint cnt;
+} GsColorBin;
+
+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;
+}
+
+/* convert range of 0..255 to 0..1 */
+static inline gdouble
+_convert_from_rgb8 (guchar val)
+{
+ return (gdouble) val / 255.f;
+}
+
+static void
+key_colors_set_for_pixbuf (GArray *colors, GdkPixbuf *pb, guint number)
+{
+ gint rowstride, n_channels;
+ gint x, y, width, height;
+ guchar *pixels, *p;
+ guint bin_size = 200;
+ guint i;
+ guint number_of_bins;
+
+ /* go through each pixel */
+ n_channels = gdk_pixbuf_get_n_channels (pb);
+ rowstride = gdk_pixbuf_get_rowstride (pb);
+ pixels = gdk_pixbuf_get_pixels (pb);
+ width = gdk_pixbuf_get_width (pb);
+ height = gdk_pixbuf_get_height (pb);
+
+ 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 < height; y++) {
+ for (x = 0; x < width; 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 = (guint8) (p[0] / bin_size);
+ tmp.G = (guint8) (p[1] / bin_size);
+ tmp.B = (guint8) (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.red += _convert_from_rgb8 (p[0]);
+ s->color.green += _convert_from_rgb8 (p[1]);
+ s->color.blue += _convert_from_rgb8 (p[2]);
+ s->cnt++;
+ continue;
+ }
+
+ /* add to hash table */
+ s = g_new0 (GsColorBin, 1);
+ s->color.red = _convert_from_rgb8 (p[0]);
+ s->color.green = _convert_from_rgb8 (p[1]);
+ s->color.blue = _convert_from_rgb8 (p[2]);
+ s->color.alpha = 1.0;
+ s->cnt = 1;
+ g_hash_table_insert (hash, key, s);
+ }
+ }
+
+ number_of_bins = g_hash_table_size (hash);
+ 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 (GList *l = values; l != NULL; l = l->next) {
+ GsColorBin *s = l->data;
+ GdkRGBA color;
+ color.red = s->color.red / s->cnt;
+ color.green = s->color.green / s->cnt;
+ color.blue = s->color.blue / s->cnt;
+ g_array_append_val (colors, color);
+ }
+ return;
+ }
+ }
+
+ /* the algorithm failed, so just return a monochrome ramp */
+ for (i = 0; i < 3; i++) {
+ GdkRGBA color;
+ color.red = (gdouble) i / 3.f;
+ color.green = color.red;
+ color.blue = color.red;
+ color.alpha = 1.0f;
+ g_array_append_val (colors, color);
+ }
+}
+
+/**
+ * gs_calculate_key_colors:
+ * @pixbuf: an app icon to calculate key colors from
+ *
+ * Calculate the set of key colors present in @pixbuf. These are the colors
+ * which stand out the most, and they are subjective. This function does not
+ * guarantee to return perfect results, but should return workable results for
+ * most icons.
+ *
+ * @pixbuf will be scaled down to 32×32 pixels, so if it can be provided at
+ * that resolution by the caller, this function will return faster.
+ *
+ * Returns: (transfer full) (element-type GdkRGBA): key colors for @pixbuf
+ * Since: 40
+ */
+GArray *
+gs_calculate_key_colors (GdkPixbuf *pixbuf)
+{
+ g_autoptr(GdkPixbuf) pb_small = NULL;
+ g_autoptr(GArray) colors = g_array_new (FALSE, FALSE, sizeof (GdkRGBA));
+
+ /* scale the icon down to simplify calculations */
+ pb_small = gdk_pixbuf_scale_simple (pixbuf, 32, 32, GDK_INTERP_BILINEAR);
+
+ /* get a list of key colors */
+ key_colors_set_for_pixbuf (colors, pb_small, 10);
+
+ return g_steal_pointer (&colors);
+}
diff --git a/lib/gs-key-colors.h b/lib/gs-key-colors.h
new file mode 100644
index 000000000..be8bef808
--- /dev/null
+++ b/lib/gs-key-colors.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ * Copyright (C) 2014-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2021 Endless OS Foundation, Inc
+ *
+ * Authors:
+ * - Richard Hughes <richard hughsie com>
+ * - Kalev Lember <klember redhat com>
+ * - Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+GArray *gs_calculate_key_colors (GdkPixbuf *pixbuf);
+
+G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index 2c318dfbf..0c53797d3 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -11,6 +11,7 @@ libgnomesoftware_public_headers = [
'gs-category-manager.h',
'gs-desktop-data.h',
'gs-ioprio.h',
+ 'gs-key-colors.h',
'gs-metered.h',
'gs-os-release.h',
'gs-plugin.h',
@@ -70,6 +71,7 @@ libgnomesoftware = static_library(
'gs-desktop-data.c',
'gs-ioprio.c',
'gs-ioprio.h',
+ 'gs-key-colors.c',
'gs-metered.c',
'gs-os-release.c',
'gs-plugin.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]