[gnome-control-center/wip/feborges/round-and-generate-user-images: 53/53] user-accounts: Generate a default avatar for new users



commit bb245850afb69863de5d93b5bb9705f0cfbe6d4c
Author: Felipe Borges <felipeborges gnome org>
Date:   Mon Feb 4 15:46:16 2019 +0000

    user-accounts: Generate a default avatar for new users
    
    When a user is created, we generate an avatar with their initials.
    
    See https://gitlab.gnome.org/GNOME/Initiatives/issues/6

 panels/user-accounts/cc-avatar-chooser.c |  40 -------
 panels/user-accounts/cc-user-panel.c     |   2 +
 panels/user-accounts/user-utils.c        | 180 +++++++++++++++++++++++++++++++
 panels/user-accounts/user-utils.h        |   5 +
 4 files changed, 187 insertions(+), 40 deletions(-)
---
diff --git a/panels/user-accounts/cc-avatar-chooser.c b/panels/user-accounts/cc-avatar-chooser.c
index ca3c8d967..0db3adb8c 100644
--- a/panels/user-accounts/cc-avatar-chooser.c
+++ b/panels/user-accounts/cc-avatar-chooser.c
@@ -66,46 +66,6 @@ struct _CcAvatarChooser {
 
 G_DEFINE_TYPE (CcAvatarChooser, cc_avatar_chooser, GTK_TYPE_POPOVER)
 
-static void
-set_user_icon_data (ActUser   *user,
-                    GdkPixbuf *pixbuf)
-{
-        gchar *path;
-        gint fd;
-        GOutputStream *stream;
-        GError *error;
-
-        path = g_build_filename (g_get_tmp_dir (), "gnome-control-center-user-icon-XXXXXX", NULL);
-        fd = g_mkstemp (path);
-
-        if (fd == -1) {
-                g_warning ("failed to create temporary file for image data");
-                g_free (path);
-                return;
-        }
-
-        stream = g_unix_output_stream_new (fd, TRUE);
-
-        error = NULL;
-        if (!gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error, NULL)) {
-                g_warning ("failed to save image: %s", error->message);
-                g_error_free (error);
-                g_object_unref (stream);
-                return;
-        }
-
-        g_object_unref (stream);
-
-        act_user_set_icon_file (user, path);
-
-        /* if we ever make the dbus call async, the g_remove call needs
-         * to wait for its completion
-         */
-        g_remove (path);
-
-        g_free (path);
-}
-
 static void
 crop_dialog_response (GtkWidget       *dialog,
                       gint             response_id,
diff --git a/panels/user-accounts/cc-user-panel.c b/panels/user-accounts/cc-user-panel.c
index ab933e47f..f71e83b1a 100644
--- a/panels/user-accounts/cc-user-panel.c
+++ b/panels/user-accounts/cc-user-panel.c
@@ -354,6 +354,7 @@ static void
 add_user (CcUserPanel *self)
 {
         CcAddUserDialog *dialog;
+        g_autoptr(GdkPixbuf) pixbuf = NULL;
         GtkWindow *toplevel;
         ActUser *user;
 
@@ -364,6 +365,7 @@ add_user (CcUserPanel *self)
         gtk_dialog_run (GTK_DIALOG (dialog));
 
         user = cc_add_user_dialog_get_user (dialog);
+        generate_user_avatar (user);
         if (user != NULL)
                 reload_users (self, user);
 
diff --git a/panels/user-accounts/user-utils.c b/panels/user-accounts/user-utils.c
index 70e578887..c5dfa9c05 100644
--- a/panels/user-accounts/user-utils.c
+++ b/panels/user-accounts/user-utils.c
@@ -29,10 +29,14 @@
 #include <pwd.h>
 
 #include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
 #include <glib/gi18n.h>
+#include <glib/gstdio.h>
 
 #include "user-utils.h"
 
+#define IMAGE_SIZE 512
+
 typedef struct {
         gchar *text;
         gchar *placeholder_str;
@@ -469,3 +473,179 @@ round_image (GdkPixbuf *pixbuf,
 
         return dest;
 }
+
+static gchar *
+extract_initials_from_name (const gchar *name)
+{
+        GString *initials;
+        g_autofree gchar *p = NULL;
+        g_autofree gchar *normalized = NULL;
+        gunichar unichar;
+        gpointer q = NULL;
+
+        p = g_utf8_strup (name, -1);
+        normalized = g_utf8_normalize (g_strstrip (p), -1, G_NORMALIZE_DEFAULT_COMPOSE);
+        if (normalized == NULL) {
+                return NULL;
+        }
+
+        initials = g_string_new ("");
+
+        unichar = g_utf8_get_char (normalized);
+        g_string_append_unichar (initials, unichar);
+
+        q = g_utf8_strrchr (normalized, -1, ' ');
+        if (q != NULL && g_utf8_next_char (q) != NULL) {
+                q = g_utf8_next_char (q);
+
+                unichar = g_utf8_get_char (q);
+                g_string_append_unichar (initials, unichar);
+        }
+
+        return g_string_free (initials, FALSE);
+}
+
+static GdkRGBA
+get_color_for_name (const gchar *name)
+{
+        // https://gitlab.gnome.org/Community/Design/HIG-app-icons/blob/master/GNOME%20HIG.gpl
+        static gdouble gnome_color_palette[][3] = {
+                {  98, 160, 234 },
+                {  53, 132, 228 },
+                {  28, 113, 216 },
+                {  26,  95, 180 },
+                {  87, 227, 137 },
+                {  51, 209, 122 },
+                {  46, 194, 126 },
+                {  38, 162, 105 },
+                { 248, 228,  92 },
+                { 246, 211,  45 },
+                { 245, 194,  17 },
+                { 229, 165,  10 },
+                { 255, 163,  72 },
+                { 255, 120,   0 },
+                { 230,  97,   0 },
+                { 198,  70,   0 },
+                { 237,  51,  59 },
+                { 224,  27,  36 },
+                { 192,  28,  40 },
+                { 165,  29,  45 },
+                { 192,  97, 203 },
+                { 163,  71, 186 },
+                { 129,  61, 156 },
+                {  97,  53, 131 },
+                { 181, 131,  90 },
+                { 152, 106,  68 },
+                { 134,  94,  60 },
+                {  99,  69,  44 }
+        };
+
+        GdkRGBA color = { 255, 255, 255, 1.0 };
+        guint hash;
+        gint number_of_colors;
+        gint idx;
+
+        if (name == NULL || strlen (name) == 0)
+                return color;
+
+        hash = g_str_hash (name);
+        number_of_colors = G_N_ELEMENTS (gnome_color_palette);
+        idx = hash % number_of_colors;
+
+        color.red   = gnome_color_palette[idx][0];
+        color.green = gnome_color_palette[idx][1];
+        color.blue  = gnome_color_palette[idx][2];
+
+        return color;
+}
+
+static cairo_surface_t *
+generate_user_picture (const gchar *name)
+{
+        PangoFontDescription *font_desc;
+        g_autofree gchar *initials = extract_initials_from_name (name);
+        g_autofree gchar *font = g_strdup_printf ("Sans %d", (int)ceil (IMAGE_SIZE / 2.5));
+        PangoLayout *layout;
+        GdkRGBA color = get_color_for_name (name);
+        cairo_surface_t *surface;
+        gint width, height;
+        cairo_t *cr;
+
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                              IMAGE_SIZE,
+                                              IMAGE_SIZE);
+        cr = cairo_create (surface);
+        cairo_rectangle (cr, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
+        cairo_set_source_rgb (cr, color.red/255.0, color.green/255.0, color.blue/255.0);
+        cairo_fill (cr);
+
+        /* Draw the initials on top */
+        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+        layout = pango_cairo_create_layout (cr);
+        pango_layout_set_text (layout, initials, -1);
+        font_desc = pango_font_description_from_string (font);
+        pango_layout_set_font_description (layout, font_desc);
+        pango_font_description_free (font_desc);
+
+        pango_layout_get_size (layout, &width, &height);
+        cairo_translate (cr, IMAGE_SIZE/2, IMAGE_SIZE/2);
+        cairo_move_to (cr, - ((double)width / PANGO_SCALE)/2, - ((double)height/PANGO_SCALE)/2);
+        pango_cairo_show_layout (cr, layout);
+        cairo_destroy (cr);
+
+        return surface;
+}
+
+void
+set_user_icon_data (ActUser   *user,
+                    GdkPixbuf *pixbuf)
+{
+        gchar *path;
+        gint fd;
+        GOutputStream *stream;
+        GError *error;
+
+        path = g_build_filename (g_get_tmp_dir (), "gnome-control-center-user-icon-XXXXXX", NULL);
+        fd = g_mkstemp (path);
+
+        if (fd == -1) {
+                g_warning ("failed to create temporary file for image data");
+                g_free (path);
+                return;
+        }
+
+        stream = g_unix_output_stream_new (fd, TRUE);
+
+        error = NULL;
+        if (!gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error, NULL)) {
+                g_warning ("failed to save image: %s", error->message);
+                g_error_free (error);
+                g_object_unref (stream);
+                return;
+        }
+
+        g_object_unref (stream);
+
+        act_user_set_icon_file (user, path);
+
+        /* if we ever make the dbus call async, the g_remove call needs
+         * to wait for its completion
+         */
+        g_remove (path);
+
+        g_free (path);
+}
+
+void
+generate_user_avatar (ActUser *user)
+{
+        g_autoptr(GdkPixbuf) pixbuf = NULL;
+        cairo_surface_t *surface;
+
+        surface = generate_user_picture (act_user_get_real_name (user));
+
+        pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
+        cairo_surface_destroy (surface);
+
+        set_user_icon_data (user, pixbuf);
+}
diff --git a/panels/user-accounts/user-utils.h b/panels/user-accounts/user-utils.h
index 2c49524c3..a40f0d5d1 100644
--- a/panels/user-accounts/user-utils.h
+++ b/panels/user-accounts/user-utils.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <gtk/gtk.h>
+#include <act/act.h>
 
 G_BEGIN_DECLS
 
@@ -44,4 +45,8 @@ gboolean is_valid_username                (const gchar *name,
                                            gchar      **tip);
 GdkPixbuf *round_image                    (GdkPixbuf  *pixbuf,
                                            gint        icon_size);
+void       generate_user_avatar           (ActUser *user);
+void       set_user_icon_data             (ActUser     *user,
+                                           GdkPixbuf   *pixbuf);
+
 G_END_DECLS


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