Merge lp:~mterry/unity-greeter/average-color into lp:unity-greeter

Proposed by Michael Terry
Status: Merged
Approved by: Robert Ancell
Approved revision: 546
Merged at revision: 547
Proposed branch: lp:~mterry/unity-greeter/average-color
Merge into: lp:unity-greeter
Diff against target: 205 lines (+162/-0)
1 file modified
src/background.vala (+162/-0)
To merge this branch: bzr merge lp:~mterry/unity-greeter/average-color
Reviewer Review Type Date Requested Status
Robert Ancell Approve
Review via email: mp+121977@code.launchpad.net

Description of the change

Canonical owns the copyright to the algorithm. It was originally written for Unity. Then it also got used in a patch for gnome-desktop3 by didrocks, to replace upstream's algorithm. Then I ported it here to Vala.

To post a comment you must log in.
Revision history for this message
Robert Ancell (robert-ancell) wrote :

Looks good to me. Don't know if there's any performance hit but it works in Unity/GNOME desktop so guessing not. Either way we can cross that bridge later.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/background.vala'
2--- src/background.vala 2012-08-27 22:30:33 +0000
3+++ src/background.vala 2012-08-30 02:57:17 +0000
4@@ -26,6 +26,7 @@
5 public int[] widths;
6 public int[] heights;
7 public Cairo.Pattern[] patterns;
8+ public Gdk.RGBA average_color;
9
10 private Cairo.Surface target_surface;
11 private bool draw_grid;
12@@ -122,6 +123,8 @@
13 for (var i = 0; i < widths.length; i++)
14 {
15 patterns[i] = create_pattern (images[i]);
16+ if (i == 0)
17+ pixbuf_average_value (images[i], out average_color);
18 images[i] = null;
19 }
20
21@@ -201,6 +204,150 @@
22
23 return pattern;
24 }
25+
26+ /* The following color averaging algorithm was originally written for
27+ Unity in C++, then patched into gnome-desktop3 in C. I've taken it
28+ and put it here in Vala. It would be nice if we could get
29+ gnome-desktop3 to expose this for our use instead of copying the
30+ code... */
31+
32+ static const int QUAD_MAX_LEVEL_OF_RECURSION = 16;
33+ static const int QUAD_MIN_LEVEL_OF_RECURSION = 2;
34+ static const int QUAD_CORNER_WEIGHT_NW = 3;
35+ static const int QUAD_CORNER_WEIGHT_NE = 1;
36+ static const int QUAD_CORNER_WEIGHT_SE = 1;
37+ static const int QUAD_CORNER_WEIGHT_SW = 3;
38+ static const int QUAD_CORNER_WEIGHT_CENTER = 2;
39+ static const int QUAD_CORNER_WEIGHT_TOTAL = (QUAD_CORNER_WEIGHT_NW + QUAD_CORNER_WEIGHT_NE + QUAD_CORNER_WEIGHT_SE + QUAD_CORNER_WEIGHT_SW + QUAD_CORNER_WEIGHT_CENTER);
40+
41+ /* Pixbuf utilities */
42+ private Gdk.RGBA get_pixbuf_sample (uint8[] pixels,
43+ int rowstride,
44+ int channels,
45+ int x,
46+ int y)
47+ {
48+ var sample = Gdk.RGBA ();
49+ double dd = 0xFF;
50+ int offset = ((y * rowstride) + (x * channels));
51+
52+ sample.red = pixels[offset++] / dd;
53+ sample.green = pixels[offset++] / dd;
54+ sample.blue = pixels[offset++] / dd;
55+ sample.alpha = 1.0f;
56+
57+ return sample;
58+ }
59+
60+ private bool is_color_different (Gdk.RGBA color_a,
61+ Gdk.RGBA color_b)
62+ {
63+ var diff = Gdk.RGBA ();
64+
65+ diff.red = color_a.red - color_b.red;
66+ diff.green = color_a.green - color_b.green;
67+ diff.blue = color_a.blue - color_b.blue;
68+ diff.alpha = 1.0f;
69+
70+ if (GLib.Math.fabs (diff.red) > 0.15 ||
71+ GLib.Math.fabs (diff.green) > 0.15 ||
72+ GLib.Math.fabs (diff.blue) > 0.15)
73+ return true;
74+
75+ return false;
76+ }
77+
78+ private Gdk.RGBA get_quad_average (int x,
79+ int y,
80+ int width,
81+ int height,
82+ int level_of_recursion,
83+ uint8[] pixels,
84+ int rowstride,
85+ int channels)
86+ {
87+ // samples four corners
88+ // c1-----c2
89+ // | |
90+ // c3-----c4
91+
92+ var average = Gdk.RGBA ();
93+ var corner1 = get_pixbuf_sample (pixels, rowstride, channels, x , y );
94+ var corner2 = get_pixbuf_sample (pixels, rowstride, channels, x + width, y );
95+ var corner3 = get_pixbuf_sample (pixels, rowstride, channels, x , y + height);
96+ var corner4 = get_pixbuf_sample (pixels, rowstride, channels, x + width, y + height);
97+ var centre = get_pixbuf_sample (pixels, rowstride, channels, x + (width / 2), y + (height / 2));
98+
99+ /* If we're over the max we want to just take the average and be happy
100+ with that value */
101+ if (level_of_recursion < QUAD_MAX_LEVEL_OF_RECURSION) {
102+ /* Otherwise we want to look at each value and check it's distance
103+ from the center color and take the average if they're far apart. */
104+
105+ /* corner 1 */
106+ if (level_of_recursion < QUAD_MIN_LEVEL_OF_RECURSION ||
107+ is_color_different(corner1, centre)) {
108+ corner1 = get_quad_average (x, y, width/2, height/2, level_of_recursion + 1, pixels, rowstride, channels);
109+ }
110+
111+ /* corner 2 */
112+ if (level_of_recursion < QUAD_MIN_LEVEL_OF_RECURSION ||
113+ is_color_different(corner2, centre)) {
114+ corner2 = get_quad_average (x + width/2, y, width/2, height/2, level_of_recursion + 1, pixels, rowstride, channels);
115+ }
116+
117+ /* corner 3 */
118+ if (level_of_recursion < QUAD_MIN_LEVEL_OF_RECURSION ||
119+ is_color_different(corner3, centre)) {
120+ corner3 = get_quad_average (x, y + height/2, width/2, height/2, level_of_recursion + 1, pixels, rowstride, channels);
121+ }
122+
123+ /* corner 4 */
124+ if (level_of_recursion < QUAD_MIN_LEVEL_OF_RECURSION ||
125+ is_color_different(corner4, centre)) {
126+ corner4 = get_quad_average (x + width/2, y + height/2, width/2, height/2, level_of_recursion + 1, pixels, rowstride, channels);
127+ }
128+ }
129+
130+ average.red = ((corner1.red * QUAD_CORNER_WEIGHT_NW) +
131+ (corner3.red * QUAD_CORNER_WEIGHT_SW) +
132+ (centre.red * QUAD_CORNER_WEIGHT_CENTER) +
133+ (corner2.red * QUAD_CORNER_WEIGHT_NE) +
134+ (corner4.red * QUAD_CORNER_WEIGHT_SE))
135+ / QUAD_CORNER_WEIGHT_TOTAL;
136+ average.green = ((corner1.green * QUAD_CORNER_WEIGHT_NW) +
137+ (corner3.green * QUAD_CORNER_WEIGHT_SW) +
138+ (centre.green * QUAD_CORNER_WEIGHT_CENTER) +
139+ (corner2.green * QUAD_CORNER_WEIGHT_NE) +
140+ (corner4.green * QUAD_CORNER_WEIGHT_SE))
141+ / QUAD_CORNER_WEIGHT_TOTAL;
142+ average.blue = ((corner1.blue * QUAD_CORNER_WEIGHT_NW) +
143+ (corner3.blue * QUAD_CORNER_WEIGHT_SW) +
144+ (centre.blue * QUAD_CORNER_WEIGHT_CENTER) +
145+ (corner2.blue * QUAD_CORNER_WEIGHT_NE) +
146+ (corner4.blue * QUAD_CORNER_WEIGHT_SE))
147+ / QUAD_CORNER_WEIGHT_TOTAL;
148+ average.alpha = 1.0f;
149+
150+ return average;
151+ }
152+
153+ private void pixbuf_average_value (Gdk.Pixbuf pixbuf,
154+ out Gdk.RGBA result)
155+ {
156+ var average = get_quad_average (0, 0,
157+ pixbuf.get_width () - 1, pixbuf.get_height () - 1,
158+ 1,
159+ pixbuf.get_pixels (),
160+ pixbuf.get_rowstride (),
161+ pixbuf.get_n_channels ());
162+
163+ result = Gdk.RGBA ();
164+ result.red = average.red;
165+ result.green = average.green;
166+ result.blue = average.blue;
167+ result.alpha = average.alpha;
168+ }
169 }
170
171 public class Monitor
172@@ -343,6 +490,7 @@
173 if (old == current)
174 old = new_background;
175 current = new_background;
176+ publish_average_color ();
177 }
178
179 /* Fade to this background when loaded */
180@@ -438,6 +586,7 @@
181 }
182
183 queue_draw ();
184+ publish_average_color ();
185 }
186 }
187
188@@ -518,4 +667,17 @@
189 c.fill ();
190 c.restore ();
191 }
192+
193+ void publish_average_color ()
194+ {
195+ var rgba = current.average_color.to_string ();
196+ var root = get_screen ().get_root_window ();
197+
198+ Gdk.property_change (root,
199+ Gdk.Atom.intern_static_string ("_GNOME_BACKGROUND_REPRESENTATIVE_COLORS"),
200+ Gdk.Atom.intern_static_string ("STRING"),
201+ 8,
202+ Gdk.PropMode.REPLACE,
203+ rgba.data);
204+ }
205 }

Subscribers

People subscribed via source and target branches