Also color labels

Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
Vasiliy Doylov 2025-07-10 18:41:01 +03:00
parent a00b668f28
commit b0d9a44f36
4 changed files with 132 additions and 63 deletions

View file

@ -0,0 +1,70 @@
namespace Color {
public Gee.HashMap<Gdk.RGBA?, int> quanitize_pixbuff (Gdk.Pixbuf pixbuf) {
var histogram = new Gee.HashMap<Gdk.RGBA?, int>
(
(a) =>
((uint8) a.red * 255 + ((uint8) a.green * 255 << 8) + ((uint8) a.blue * 255 << 16)),
(a, b) => (a.red == b.red && a.blue == b.blue && a.green == b.green)
);
unowned uint8[] pixels = pixbuf.get_pixels ();
var width = pixbuf.get_width ();
var height = pixbuf.get_height ();
var rowstride = pixbuf.get_rowstride ();
var n_channels = pixbuf.get_n_channels ();
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var offset = y * rowstride + x * n_channels;
var r = pixels[offset];
var g = pixels[offset + 1];
var b = pixels[offset + 2];
var a = (n_channels == 4) ? pixels[offset + 3] : 255;
var rgba = Gdk.RGBA () {
red = r / 255.0,
green = g / 255.0,
blue = b / 255.0,
alpha = a / 255.0
};
histogram[rgba] = histogram[rgba] + 1;
}
}
return histogram;
}
public Gdk.RGBA get_most_common_color (ref Gee.HashMap<Gdk.RGBA?, int> histogram) {
int max = 0;
Gdk.RGBA color = {};
foreach (var kv in histogram) {
if (kv.value > max) {
max = kv.value;
color = kv.key;
}
}
return color;
}
public double get_pixel_luminance (Gdk.RGBA pix) {
return pix.red * 0.299 + pix.green * 0.587 + pix.blue * 0.114;
}
public Gee.ArrayList<Gdk.RGBA?> sort_colors (Gee.HashMap<Gdk.RGBA?, int> histogram) {
var new_keys = new Gee.ArrayList<Gdk.RGBA?> ();
var tr = 2;
foreach (var key in histogram.keys) {
if (key == null)
continue;
if (histogram[key] < tr)
continue;
new_keys.add (key);
}
new_keys.sort ((a, b) => {
var a_ = histogram[a];
var b_ = histogram[b];
return a_ > b_ ? -1 : a_ == b_ ? 0 : 1;
});
return new_keys;
}
}

View file

@ -23,6 +23,7 @@ vala_quick_setting_resources = gnome.compile_resources(
)
vala_quick_setting_plugin_sources = files(
'color.vala',
'extension.vala',
'player.vala',
'qs.vala',

View file

@ -41,13 +41,11 @@ public class Extension.Player : Gtk.Box {
return Math.PI / 180.0 * deg;
}
void set_colors (Gdk.RGBA[] colors) {
var gradient_points = new string[0];
for (uint i = 0; i < colors.length; i++) {
gradient_points += "%s %d%%".printf (colors[i].to_string (), (int) (100.0 / colors.length * i));
}
var data = ".%s { background: radial-gradient(circle at left, %s); }"
.printf (id, string.joinv (",", gradient_points));
void set_colors (Gdk.RGBA start, Gdk.RGBA end, Gdk.RGBA fg) {
var data = ".%s {
color: %s;
background: radial-gradient(circle at left, %s 0%%, %s 100%%);
}".printf (id, fg.to_string (), start.to_string (), end.to_string ());
try {
css_provider.load_from_data (data);
} catch (Error err) {
@ -55,64 +53,63 @@ public class Extension.Player : Gtk.Box {
}
}
private async Gee.HashMap<Gdk.RGBA?, int> quanitize_pixbuff (Gdk.Pixbuf pixbuf) {
var histogram = new Gee.HashMap<Gdk.RGBA?, int>
(
(a) =>
((uint8) a.red * 255 + ((uint8) a.green * 255 << 8) + ((uint8) a.blue * 255 << 16)),
(a, b) => (a.red == b.red && a.blue == b.blue && a.green == b.green)
);
unowned uint8[] pixels = pixbuf.get_pixels ();
var width = pixbuf.get_width ();
var height = pixbuf.get_height ();
var rowstride = pixbuf.get_rowstride ();
var n_channels = pixbuf.get_n_channels ();
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var offset = y * rowstride + x * n_channels;
var r = pixels[offset];
var g = pixels[offset + 1];
var b = pixels[offset + 2];
var a = (n_channels == 4) ? pixels[offset + 3] : 255;
var rgba = Gdk.RGBA () {
red = r / 255.0,
green = g / 255.0,
blue = b / 255.0,
alpha = a / 255.0
private void generate_style (Gdk.Pixbuf pixbuff) {
message ("Start");
var histogram = Color.quanitize_pixbuff (pixbuff);
message ("Histogram OK");
var colors = Color.sort_colors (histogram);
message ("Sort OK");
Gdk.RGBA? fg_color = null, bg_start_color = null, bg_end_color = null;
double fg_lum = 0, bg_start_lum = 0, bg_end_lum = 0;
foreach (var color in colors) {
if (fg_color != null && bg_start_color != null && bg_end_color != null)
break;
var lum = Color.get_pixel_luminance (color);
if (bg_start_color == null) {
bg_start_color = color;
bg_start_lum = lum;
continue;
}
if (Math.fabs (bg_start_lum - lum) > 0.4) {
if (fg_color != null)
continue;
fg_color = color;
continue;
} else if (bg_end_color == null) {
bg_end_color = color;
continue;
}
}
if (bg_end_color == null) {
message ("Failed to chose end background color color. Fallbacking to start color");
bg_end_color = bg_start_color;
}
if (fg_color == null) {
message ("Failed to chose forground color. Fallbacking to contrast");
if (bg_start_lum > 0.5)
fg_color = Gdk.RGBA () {
red = 0,
green = 0,
blue = 0,
alpha = 1,
};
else
fg_color = Gdk.RGBA () {
red = 1,
green = 1,
blue = 1,
alpha = 1,
};
histogram[rgba] += 1;
}
}
return histogram;
Idle.add (() => {
set_colors (bg_start_color, bg_end_color, fg_color);
return false;
});
}
private Gdk.RGBA get_most_common_color (ref Gee.HashMap<Gdk.RGBA?, int> histogram) {
int max = 0;
Gdk.RGBA bebra = {};
foreach (var kv in histogram) {
if (kv.value > max) {
max = kv.value;
bebra = kv.key;
}
}
return bebra;
}
private async void generate_style (Gdk.Pixbuf pixbuff) {
var histogram = yield quanitize_pixbuff (pixbuff);
var bg_colors = new Gdk.RGBA[0];
for (var i = 0; i < 2; i++) {
var c = get_most_common_color (ref histogram);
bg_colors += c;
histogram[c] = -1;
}
set_colors (bg_colors);
private void run_color_thred (Gdk.Pixbuf pb) {
var thread = new Thread<void> ("Picture", () => generate_style (pb));
}
private async Icon ? process_icon (Icon? icon) {
@ -136,7 +133,7 @@ public class Extension.Player : Gtk.Box {
// Downscale to 110x110
pixbuff = pixbuff.scale_simple (110, 110, Gdk.InterpType.BILINEAR);
// Start background generator
generate_style.begin (pixbuff);
run_color_thred (pixbuff);
var width = pixbuff.width;
var height = pixbuff.height;
@ -172,7 +169,7 @@ public class Extension.Player : Gtk.Box {
default :
foreach (var child2 in ((Gtk.Box) child).get_children ()) {
switch (child2.get_name ()) {
case "lbl_artist":
case "lbl_artist" :
child2.bind_property ("label", artist, "label");
break;
case "lbl_title":

View file

@ -1,6 +1,7 @@
.nekoplayer {
/* background: linear-gradient(to bottom, #ff7e5f 0%, #feb47b 100%); */
border-radius: 10px;
transition: all 0.1s ease-out;
}
.nekoplayer-controls button {