1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 18:51:04 +02:00

fix(image): still blurry on hidpi

Adds in proper HiDPI image support into the image provider, using calls to Cairo.

Resolves #96 at last! (I hope...)
This commit is contained in:
Jake Stanger 2023-05-08 15:57:14 +01:00
parent 3b54d527b2
commit 9012feee4f
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61

View file

@ -1,6 +1,8 @@
use crate::desktop_file::get_desktop_icon_name; use crate::desktop_file::get_desktop_icon_name;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use color_eyre::{Help, Report, Result}; use color_eyre::{Help, Report, Result};
use gtk::cairo::Surface;
use gtk::gdk::ffi::gdk_cairo_surface_create_from_pixbuf;
use gtk::gdk_pixbuf::Pixbuf; use gtk::gdk_pixbuf::Pixbuf;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{IconLookupFlags, IconTheme}; use gtk::{IconLookupFlags, IconTheme};
@ -115,17 +117,24 @@ impl<'a> ImageProvider<'a> {
let size = self.size; let size = self.size;
rx.attach(None, move |bytes| { rx.attach(None, move |bytes| {
let stream = MemoryInputStream::from_bytes(&bytes); let stream = MemoryInputStream::from_bytes(&bytes);
let scale = image.scale_factor();
let scaled_size = size * scale;
let pixbuf = Pixbuf::from_stream_at_scale( let pixbuf = Pixbuf::from_stream_at_scale(
&stream, &stream,
size, scaled_size,
size, scaled_size,
true, true,
Some(&Cancellable::new()), Some(&Cancellable::new()),
); );
match pixbuf { // Different error types makes this a bit awkward
Ok(pixbuf) => image.set_pixbuf(Some(&pixbuf)), match pixbuf.map(|pixbuf| Self::create_and_load_surface(&pixbuf, &image, scale))
{
Ok(Err(err)) => error!("{err:?}"),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
_ => {}
} }
Continue(false) Continue(false)
@ -141,18 +150,35 @@ impl<'a> ImageProvider<'a> {
Ok(()) Ok(())
} }
/// Attempts to synchronously fetch an image from location
/// and load into into the image.
fn load_into_image_sync(&self, image: &gtk::Image) -> Result<()> { fn load_into_image_sync(&self, image: &gtk::Image) -> Result<()> {
let scale = image.scale_factor();
let pixbuf = match &self.location { let pixbuf = match &self.location {
ImageLocation::Icon { name, theme } => { ImageLocation::Icon { name, theme } => self.get_from_icon(name, theme, scale),
self.get_from_icon(name, theme, image.scale_factor()) ImageLocation::Local(path) => self.get_from_file(path, scale),
} ImageLocation::Steam(steam_id) => self.get_from_steam_id(steam_id, scale),
ImageLocation::Local(path) => self.get_from_file(path),
ImageLocation::Steam(steam_id) => self.get_from_steam_id(steam_id),
#[cfg(feature = "http")] #[cfg(feature = "http")]
_ => unreachable!(), // handled above _ => unreachable!(), // handled above
}?; }?;
image.set_pixbuf(Some(&pixbuf)); Self::create_and_load_surface(&pixbuf, image, scale)
}
/// Attempts to create a Cairo surface from the provided `Pixbuf`,
/// using the provided scaling factor.
/// The surface is then loaded into the provided image.
///
/// This is necessary for HiDPI since `Pixbuf`s are always treated as scale factor 1.
fn create_and_load_surface(pixbuf: &Pixbuf, image: &gtk::Image, scale: i32) -> Result<()> {
let surface = unsafe {
let ptr =
gdk_cairo_surface_create_from_pixbuf(pixbuf.as_ptr(), scale, std::ptr::null_mut());
Surface::from_raw_full(ptr)
}?;
image.set_from_surface(Some(&surface));
Ok(()) Ok(())
} }
@ -161,7 +187,7 @@ impl<'a> ImageProvider<'a> {
fn get_from_icon(&self, name: &str, theme: &IconTheme, scale: i32) -> Result<Pixbuf> { fn get_from_icon(&self, name: &str, theme: &IconTheme, scale: i32) -> Result<Pixbuf> {
let pixbuf = let pixbuf =
match theme.lookup_icon_for_scale(name, self.size, scale, IconLookupFlags::empty()) { match theme.lookup_icon_for_scale(name, self.size, scale, IconLookupFlags::empty()) {
Some(_) => theme.load_icon(name, self.size, IconLookupFlags::FORCE_SIZE), Some(_) => theme.load_icon(name, self.size * scale, IconLookupFlags::FORCE_SIZE),
None => Ok(None), None => Ok(None),
}?; }?;
@ -172,14 +198,15 @@ impl<'a> ImageProvider<'a> {
} }
/// Attempts to get a `Pixbuf` from a local file. /// Attempts to get a `Pixbuf` from a local file.
fn get_from_file(&self, path: &Path) -> Result<Pixbuf> { fn get_from_file(&self, path: &Path, scale: i32) -> Result<Pixbuf> {
let pixbuf = Pixbuf::from_file_at_scale(path, self.size, self.size, true)?; let scaled_size = self.size * scale;
let pixbuf = Pixbuf::from_file_at_scale(path, scaled_size, scaled_size, true)?;
Ok(pixbuf) Ok(pixbuf)
} }
/// Attempts to get a `Pixbuf` from a local file, /// Attempts to get a `Pixbuf` from a local file,
/// using the Steam game ID to look it up. /// using the Steam game ID to look it up.
fn get_from_steam_id(&self, steam_id: &str) -> Result<Pixbuf> { fn get_from_steam_id(&self, steam_id: &str, scale: i32) -> Result<Pixbuf> {
// TODO: Can we load this from icon theme with app id `steam_icon_{}`? // TODO: Can we load this from icon theme with app id `steam_icon_{}`?
let path = dirs::data_dir().map_or_else( let path = dirs::data_dir().map_or_else(
|| Err(Report::msg("Missing XDG data dir")), || Err(Report::msg("Missing XDG data dir")),
@ -190,7 +217,7 @@ impl<'a> ImageProvider<'a> {
}, },
)?; )?;
self.get_from_file(&path) self.get_from_file(&path, scale)
} }
/// Attempts to get `Bytes` from an HTTP resource asynchronously. /// Attempts to get `Bytes` from an HTTP resource asynchronously.