From 3051b75bff034e2e4d4ab140c0db60cdb249094f Mon Sep 17 00:00:00 2001 From: ogios Date: Fri, 13 Dec 2024 21:34:45 +0800 Subject: [PATCH 1/3] force scale image --- src/modules/tray/icon.rs | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/modules/tray/icon.rs b/src/modules/tray/icon.rs index a1877028..1a275372 100644 --- a/src/modules/tray/icon.rs +++ b/src/modules/tray/icon.rs @@ -1,11 +1,12 @@ use crate::image::ImageProvider; use crate::modules::tray::interface::TrayMenu; +use cairo::ImageSurface; use color_eyre::{Report, Result}; use glib::ffi::g_strfreev; use glib::translate::ToGlibPtr; use gtk::ffi::gtk_icon_theme_get_search_path; use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf}; -use gtk::prelude::IconThemeExt; +use gtk::prelude::{GdkContextExt, IconThemeExt}; use gtk::{IconLookupFlags, IconTheme, Image}; use std::collections::HashSet; use std::ffi::CStr; @@ -44,17 +45,42 @@ pub fn get_image( size: u32, prefer_icons: bool, ) -> Result { - if !prefer_icons && item.icon_pixmap.is_some() { + let pixbuf = if !prefer_icons && item.icon_pixmap.is_some() { get_image_from_pixmap(item, size) } else { get_image_from_icon_name(item, icon_theme, size) .or_else(|_| get_image_from_pixmap(item, size)) - } + }?; + + let image = if pixbuf.height() == size as i32 { + let image = Image::new(); + ImageProvider::create_and_load_surface(&pixbuf, &image)?; + image + } else { + Image::from_surface(Some(&scale_image_to_height(pixbuf, size as i32))) + }; + + Ok(image) +} + +fn scale_image_to_height(pixbuf: Pixbuf, size: i32) -> ImageSurface { + let scale = size as f64 / pixbuf.height() as f64; + let width = (pixbuf.width() as f64 * scale).ceil() as i32; + let height = (pixbuf.height() as f64 * scale).ceil() as i32; + + let surf = ImageSurface::create(cairo::Format::ARgb32, width, height).unwrap(); + let context = cairo::Context::new(&surf).unwrap(); + + context.scale(scale, scale); + context.set_source_pixbuf(&pixbuf, 0., 0.); + context.paint().unwrap(); + + surf } /// Attempts to get a GTK `Image` component /// for the status notifier item's icon. -fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) -> Result { +fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) -> Result { if let Some(path) = item.icon_theme_path.as_ref() { if !path.is_empty() && !get_icon_theme_search_paths(icon_theme).contains(path) { icon_theme.append_search_path(path); @@ -67,9 +93,7 @@ fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) if let Some(icon_info) = icon_info { let pixbuf = icon_info.load_icon()?; - let image = Image::new(); - ImageProvider::create_and_load_surface(&pixbuf, &image)?; - Ok(image) + Ok(pixbuf) } else { Err(Report::msg("could not find icon")) } @@ -81,7 +105,7 @@ fn get_image_from_icon_name(item: &TrayMenu, icon_theme: &IconTheme, size: u32) /// which has 8 bits per sample and a bit stride of `4*width`. /// The Pixbuf expects RGBA32 format, so some channel shuffling /// is required. -fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { +fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { const BITS_PER_SAMPLE: i32 = 8; let pixmap = item @@ -120,8 +144,5 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { let pixbuf = pixbuf .scale_simple(size as i32, size as i32, InterpType::Bilinear) .unwrap_or(pixbuf); - - let image = Image::new(); - ImageProvider::create_and_load_surface(&pixbuf, &image)?; - Ok(image) + Ok(pixbuf) } From 0cd9ee51866394607ffb0a3b8dc3c0af594518d3 Mon Sep 17 00:00:00 2001 From: ogios Date: Sun, 15 Dec 2024 20:51:54 +0800 Subject: [PATCH 2/3] find the appropriate icon pixmap in vec --- src/modules/tray/icon.rs | 84 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/src/modules/tray/icon.rs b/src/modules/tray/icon.rs index 1a275372..9f6d40fc 100644 --- a/src/modules/tray/icon.rs +++ b/src/modules/tray/icon.rs @@ -5,13 +5,14 @@ use color_eyre::{Report, Result}; use glib::ffi::g_strfreev; use glib::translate::ToGlibPtr; use gtk::ffi::gtk_icon_theme_get_search_path; -use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf}; +use gtk::gdk_pixbuf::{Colorspace, Pixbuf}; use gtk::prelude::{GdkContextExt, IconThemeExt}; use gtk::{IconLookupFlags, IconTheme, Image}; use std::collections::HashSet; use std::ffi::CStr; use std::os::raw::{c_char, c_int}; use std::ptr; +use system_tray::item::IconPixmap; /// Gets the GTK icon theme search paths by calling the FFI function. /// Conveniently returns the result as a `HashSet`. @@ -111,7 +112,9 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { let pixmap = item .icon_pixmap .as_ref() - .and_then(|pixmap| pixmap.first()) + // The vec is sorted(ASC) with size(width==height) most of the time, + // but we can not be sure that it'll always sorted by `height` + .and_then(|pixmap| find_approx_height(pixmap, size as i32)) .ok_or_else(|| Report::msg("Failed to get pixmap from tray icon"))?; if pixmap.width == 0 || pixmap.height == 0 { @@ -141,8 +144,79 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result { row_stride, ); - let pixbuf = pixbuf - .scale_simple(size as i32, size as i32, InterpType::Bilinear) - .unwrap_or(pixbuf); Ok(pixbuf) } + +/// finds the pixmap +/// which is the smallest but bigger than wanted +/// or +/// the biggest of all if no bigger than wanted +/// +/// O(n) +fn find_approx_height(v: &[IconPixmap], size: i32) -> Option<&IconPixmap> { + if v.is_empty() { + return None; + } + if v.len() == 1 { + return v.first(); + } + + let mut approx = &v[0]; + + for p in &v[1..] { + // p bigger than wanted size + // and then we check for + // `approx` is smaller than wanted || p smaller than `approx` + if (p.height >= size && (approx.height < size || p.height < approx.height)) + // or p smaller than wanted + // but bigger than `approx` + || (p.height < size && p.height > approx.height) + { + approx = p; + } + } + + Some(approx) +} + +mod tests { + + #[test] + fn test_find_approx_size() { + use super::{find_approx_height, IconPixmap}; + + macro_rules! make_list { + ($heights:expr) => { + $heights + .iter() + .map(|height| IconPixmap { + width: 0, + height: *height, + pixels: vec![], + }) + .collect::>() + }; + } + macro_rules! assert_found { + ($list:expr, $height:expr, $index:expr) => { + assert_eq!( + find_approx_height(&$list, $height).unwrap().height, + $list[$index].height + ); + }; + } + + let list = make_list!([10, 20, 50, 40, 30]); + assert_found!(list, 1, 0); + assert_found!(list, 10, 0); + assert_found!(list, 11, 1); + assert_found!(list, 20, 1); + assert_found!(list, 21, 4); + assert_found!(list, 30, 4); + assert_found!(list, 31, 3); + assert_found!(list, 40, 3); + assert_found!(list, 41, 2); + assert_found!(list, 50, 2); + assert_found!(list, 51, 2); + } +} From 0203d7f499e22f1812e838acd927416da9fa50c9 Mon Sep 17 00:00:00 2001 From: ogios Date: Sun, 15 Dec 2024 20:59:40 +0800 Subject: [PATCH 3/3] modify name --- src/modules/tray/icon.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/modules/tray/icon.rs b/src/modules/tray/icon.rs index 9f6d40fc..a20765de 100644 --- a/src/modules/tray/icon.rs +++ b/src/modules/tray/icon.rs @@ -182,7 +182,7 @@ fn find_approx_height(v: &[IconPixmap], size: i32) -> Option<&IconPixmap> { mod tests { #[test] - fn test_find_approx_size() { + fn test_find_approx_height() { use super::{find_approx_height, IconPixmap}; macro_rules! make_list { @@ -197,7 +197,7 @@ mod tests { .collect::>() }; } - macro_rules! assert_found { + macro_rules! assert_correct { ($list:expr, $height:expr, $index:expr) => { assert_eq!( find_approx_height(&$list, $height).unwrap().height, @@ -207,16 +207,16 @@ mod tests { } let list = make_list!([10, 20, 50, 40, 30]); - assert_found!(list, 1, 0); - assert_found!(list, 10, 0); - assert_found!(list, 11, 1); - assert_found!(list, 20, 1); - assert_found!(list, 21, 4); - assert_found!(list, 30, 4); - assert_found!(list, 31, 3); - assert_found!(list, 40, 3); - assert_found!(list, 41, 2); - assert_found!(list, 50, 2); - assert_found!(list, 51, 2); + assert_correct!(list, 1, 0); + assert_correct!(list, 10, 0); + assert_correct!(list, 11, 1); + assert_correct!(list, 20, 1); + assert_correct!(list, 21, 4); + assert_correct!(list, 30, 4); + assert_correct!(list, 31, 3); + assert_correct!(list, 40, 3); + assert_correct!(list, 41, 2); + assert_correct!(list, 50, 2); + assert_correct!(list, 51, 2); } }