-
-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathBackgroundUtils.vala
More file actions
204 lines (157 loc) · 7.54 KB
/
BackgroundUtils.vala
File metadata and controls
204 lines (157 loc) · 7.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* The method for calculating the background information and the classes that are
* related to it are copied from Gala.DBus.
*/
namespace GreeterCompositor.BackgroundUtils {
private const double SATURATION_WEIGHT = 1.5;
private const double WEIGHT_THRESHOLD = 1.0;
private class DummyOffscreenEffect : Clutter.OffscreenEffect {
public signal void done_painting ();
public override void post_paint (Clutter.PaintNode node, Clutter.PaintContext context) {
base.post_paint (node, context);
Idle.add (() => {
done_painting ();
return false;
});
}
}
public struct ColorInformation {
double average_red;
double average_green;
double average_blue;
double mean_luminance;
double luminance_variance;
double mean_acutance;
}
public async ColorInformation get_background_color_information (GreeterCompositor.WindowManager wm, int panel_height) throws DBusError {
unowned var display = wm.get_display ();
var primary = display.get_primary_monitor ();
var geometry = display.get_monitor_geometry (primary);
var bg_manager = (BackgroundManager) wm.background_group.get_child_at_index (primary);
if (bg_manager == null) {
throw new DBusError.INVALID_ARGS ("Couldn't get BackgroundManagerInterface on monitor %i".printf (primary));
}
var effect = new DummyOffscreenEffect ();
unowned var newest_background_actor = bg_manager.newest_background_actor;
newest_background_actor.add_effect (effect);
var bg_actor_width = (int) newest_background_actor.width;
var bg_actor_height = (int) newest_background_actor.height;
// A commit in mutter added some padding to offscreen textures, so we
// need to avoid looking at the edges of the texture as it often has a
// black border. The commit specifies that up to 1.75px around each side
// could now be padding, so cut off 2px from left and top if necessary
// (https://gitlab.gnome.org/GNOME/mutter/commit/8655bc5d8de6a969e0ca83eff8e450f62d28fbee)
var x_start = 2;
var y_start = 2;
// For the same reason as above, we need to not use the bottom and right
// 2px of the texture. However, if the caller has specified an area of
// interest that already misses these parts, use that instead, otherwise
// chop 2px
int width = int.min (bg_actor_width - 2, geometry.width);
int height = int.min (bg_actor_height - 2, panel_height);
if (x_start > bg_actor_width || y_start > bg_actor_height || width <= 0 || height <= 0) {
throw new DBusError.INVALID_ARGS ("Got invalid rectangle: %i, %i, %i, %i".printf (x_start, y_start, width, height));
}
double mean_acutance = 0, variance = 0, mean = 0, r_total = 0, g_total = 0, b_total = 0;
ulong paint_signal_handler = 0;
paint_signal_handler = effect.done_painting.connect (() => {
SignalHandler.disconnect (effect, paint_signal_handler);
newest_background_actor.remove_effect (effect);
var texture = (Cogl.Texture)effect.get_texture ();
var texture_width = texture.get_width ();
var texture_height = texture.get_height ();
var pixels = new uint8[texture_width * texture_height * 4];
var pixel_lums = new double[texture_width * texture_height];
texture.get_data (Cogl.PixelFormat.BGRA_8888_PRE, 0, pixels);
int size = width * height;
double mean_squares = 0;
double pixel = 0;
double max, min, score, delta, score_total = 0, r_total2 = 0, g_total2 = 0, b_total2 = 0;
/*
* code to calculate weighted average color is copied from
* plank's lib/Drawing/DrawingService.vala average_color()
* http://bazaar.launchpad.net/~docky-core/plank/trunk/view/head:/lib/Drawing/DrawingService.vala
*/
for (int y = y_start; y < (y_start + height); y++) {
for (int x = x_start; x < (x_start + width); x++) {
int i = (y * (int)texture_width * 4) + (x * 4);
uint8 b = pixels[i];
uint8 g = pixels[i + 1];
uint8 r = pixels[i + 2];
pixel = (0.3 * r + 0.59 * g + 0.11 * b) ;
pixel_lums[y * width + x] = pixel;
min = uint8.min (r, uint8.min (g, b));
max = uint8.max (r, uint8.max (g, b));
delta = max - min;
/* prefer colored pixels over shades of grey */
score = SATURATION_WEIGHT * (delta == 0 ? 0.0 : delta / max);
r_total += score * r;
g_total += score * g;
b_total += score * b;
score_total += score;
r_total += r;
g_total += g;
b_total += b;
mean += pixel;
mean_squares += pixel * pixel;
}
}
for (int y = y_start + 1; y < (y_start + height) - 1; y++) {
for (int x = x_start + 1; x < (x_start + width) - 1; x++) {
var acutance =
(pixel_lums[y * width + x] * 4) -
(
pixel_lums[y * width + x - 1] +
pixel_lums[y * width + x + 1] +
pixel_lums[(y - 1) * width + x] +
pixel_lums[(y + 1) * width + x]
);
mean_acutance += acutance > 0 ? acutance : -acutance;
}
}
score_total /= size;
b_total /= size;
g_total /= size;
r_total /= size;
if (score_total > 0.0) {
b_total /= score_total;
g_total /= score_total;
r_total /= score_total;
}
b_total2 /= size * uint8.MAX;
g_total2 /= size * uint8.MAX;
r_total2 /= size * uint8.MAX;
/*
* combine weighted and not weighted sum depending on the average "saturation"
* if saturation isn't reasonable enough
* s = 0.0 -> f = 0.0 ; s = WEIGHT_THRESHOLD -> f = 1.0
*/
if (score_total <= WEIGHT_THRESHOLD) {
var f = 1.0 / WEIGHT_THRESHOLD * score_total;
var rf = 1.0 - f;
b_total = b_total * f + b_total2 * rf;
g_total = g_total * f + g_total2 * rf;
r_total = r_total * f + r_total2 * rf;
}
/* there shouldn't be values larger then 1.0 */
var max_val = double.max (r_total, double.max (g_total, b_total));
if (max_val > 1.0) {
b_total /= max_val;
g_total /= max_val;
r_total /= max_val;
}
mean /= size;
mean_squares = mean_squares / size;
variance = (mean_squares - (mean * mean));
mean_acutance /= (width - 2) * (height - 2);
get_background_color_information.callback ();
});
newest_background_actor.queue_redraw ();
yield;
return { r_total, g_total, b_total, mean, variance, mean_acutance };
}
}