1/*
2 * Copyright 2019 Michael Gratton <mike@vee.net>
3 *
4 * This software is licensed under the GNU Lesser General Public License
5 * (version 2.1 or later). See the COPYING file in this distribution.
6 */
7
8namespace Util.Avatar {
9
10    // The following was based on code written by Felipe Borges for
11    // gnome-control-enter in panels/user-accounts/user-utils.c commit
12    // 02c288ab6f069a0c106323a93400f192a63cb67e. The copyright in that
13    // file is: "Copyright 2009-2010  Red Hat, Inc,"
14
15    public Gdk.Pixbuf generate_user_picture(string name, int size) {
16        Cairo.Surface surface = new Cairo.ImageSurface(
17            Cairo.Format.ARGB32, size, size
18        );
19        Cairo.Context cr = new Cairo.Context(surface);
20        cr.rectangle(0, 0, size, size);
21
22        /* Fill the background with a colour for the name */
23        Gdk.RGBA color = get_color_for_name(name);
24        cr.set_source_rgb(
25            color.red / 255.0, color.green / 255.0, color.blue / 255.0
26        );
27        cr.fill();
28
29        /* Draw the initials on top */
30        string? initials = extract_initials_from_name(name);
31        if (initials != null) {
32            string font = "Sans %d".printf((int) GLib.Math.ceil(size / 2.5));
33
34            cr.set_source_rgb(1.0, 1.0, 1.0);
35            Pango.Layout layout = Pango.cairo_create_layout(cr);
36            layout.set_text(initials, -1);
37            layout.set_font_description(Pango.FontDescription.from_string(font));
38
39            int width, height;
40            layout.get_size(out width, out height);
41            cr.translate(size / 2, size / 2);
42            cr.move_to(
43                -((double) width / Pango.SCALE) / 2,
44                -((double) height / Pango.SCALE) / 2
45            );
46            Pango.cairo_show_layout(cr, layout);
47        }
48
49        return Gdk.pixbuf_get_from_surface(
50            surface, 0, 0, size, size
51        );
52    }
53
54    public Gdk.Pixbuf round_image(Gdk.Pixbuf source) {
55        int size = source.width;
56        Cairo.Surface surface = new Cairo.ImageSurface(
57            Cairo.Format.ARGB32, size, size
58        );
59        Cairo.Context cr = new Cairo.Context(surface);
60
61        /* Clip a circle */
62        cr.arc(size / 2, size / 2, size / 2, 0, 2 * GLib.Math.PI);
63        cr.clip();
64        cr.new_path();
65
66        Gdk.cairo_set_source_pixbuf(cr, source, 0, 0);
67        cr.paint();
68
69        return Gdk.pixbuf_get_from_surface(
70            surface, 0, 0, size, size
71        );
72    }
73
74    public string? extract_initials_from_name(string name) {
75        string normalized = name.strip().normalize(-1, DEFAULT_COMPOSE);
76        string? initials = null;
77        if (normalized != "") {
78            GLib.StringBuilder buf = new GLib.StringBuilder();
79            unichar c = 0;
80            int index  = 0;
81
82            // Get the first alphanumeric char of the string
83            for (int i = 0; normalized.get_next_char(ref index, out c); i++) {
84                if (c.isalnum()) {
85                    buf.append_unichar(c.toupper());
86                    break;
87                }
88            }
89
90            // Get the first alphanumeric char of the last word of the string
91            index = normalized.last_index_of_char(' ');
92            if (index >= 0) {
93                for (int i = 0; normalized.get_next_char(ref index, out c); i++) {
94                    if (c.isalnum()) {
95                        buf.append_unichar(c.toupper());
96                        break;
97                    }
98                }
99            }
100
101            if (buf.data.length > 0) {
102                initials = (string) buf.data;
103            }
104        }
105        return initials;
106    }
107
108
109    public Gdk.RGBA get_color_for_name(string name) {
110        // https://gitlab.gnome.org/Community/Design/HIG-app-icons/blob/master/GNOME%20HIG.gpl
111        const double[,] GNOME_COLOR_PALETTE = {
112            {  98, 160, 234 },
113            {  53, 132, 228 },
114            {  28, 113, 216 },
115            {  26,  95, 180 },
116            {  87, 227, 137 },
117            {  51, 209, 122 },
118            {  46, 194, 126 },
119            {  38, 162, 105 },
120            { 248, 228,  92 },
121            { 246, 211,  45 },
122            { 245, 194,  17 },
123            { 229, 165,  10 },
124            { 255, 163,  72 },
125            { 255, 120,   0 },
126            { 230,  97,   0 },
127            { 198,  70,   0 },
128            { 237,  51,  59 },
129            { 224,  27,  36 },
130            { 192,  28,  40 },
131            { 165,  29,  45 },
132            { 192,  97, 203 },
133            { 163,  71, 186 },
134            { 129,  61, 156 },
135            {  97,  53, 131 },
136            { 181, 131,  90 },
137            { 152, 106,  68 },
138            { 134,  94,  60 },
139            {  99,  69,  44 }
140        };
141
142        Gdk.RGBA color = { 255, 255, 255, 1.0 };
143        uint hash;
144        uint number_of_colors;
145        uint idx;
146
147        if (name == "") {
148            return color;
149        }
150
151        hash = name.hash();
152        number_of_colors = GNOME_COLOR_PALETTE.length[0];
153        idx = hash % number_of_colors;
154
155        color.red   = GNOME_COLOR_PALETTE[idx,0];
156        color.green = GNOME_COLOR_PALETTE[idx,1];
157        color.blue  = GNOME_COLOR_PALETTE[idx,2];
158
159        return color;
160    }
161
162}
163