From 0ff945c6ff2b74d473d81b408d9a03a21259219c Mon Sep 17 00:00:00 2001 From: Nathan Gill Date: Thu, 22 Jan 2026 20:09:59 +0000 Subject: [PATCH] feat(image): use cairo conversion for png images, reduce pixbuf overhead - cairo natively supports png images, so create cairo surfaces from them directly, instead of going through pixbuf, which is wasteful. - add `detect_png` utility function to determine if a file is png based on file signature closes #44 --- src/state.rs | 38 +++++++++++++++++++++++++++++--------- src/util.rs | 24 +++++++++++++++++++++++- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/state.rs b/src/state.rs index d214afd..0d82bf8 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,9 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2026, Nathan Gill -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, +use std::{ + fs::File, + io::Seek, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, }; use anyhow::{Result, anyhow, bail}; @@ -27,7 +31,6 @@ use wayland_protocols::ext::session_lock::v1::client::{ }; use zeroize::Zeroizing; -use crate::auth::{AtomicAuthState, AuthState}; use crate::cairo_ext::{ImageSurfaceExt, SubpixelOrderExt}; use crate::config::NLockConfig; use crate::util::BackgroundType; @@ -36,6 +39,10 @@ use crate::{ seat::{NLockSeat, NLockXkb}, surface::NLockSurface, }; +use crate::{ + auth::{AtomicAuthState, AuthState}, + util::detect_png, +}; pub struct NLockState { pub config: NLockConfig, @@ -191,11 +198,24 @@ impl NLockState { return Ok(()); } - let pixbuf = Pixbuf::from_file(self.config.image.path.clone())?; - let pixbuf = pixbuf - .apply_embedded_orientation() - .ok_or(anyhow!("Failed to apply embedded image orientation"))?; - self.background_image = Some(ImageSurface::create_from_pixbuf(&pixbuf)?); + let mut image_file = File::open(&self.config.image.path)?; + let is_png = detect_png(&mut image_file)?; + image_file.rewind()?; + + // if it's a PNG, Cairo can handle it directly, pixbuf conversion is expensive + if is_png { + let image_surface = ImageSurface::create_from_png(&mut image_file)?; + self.background_image = Some(image_surface); + } else { + let pixbuf = Pixbuf::from_read(image_file)?; + let pixbuf = pixbuf + .apply_embedded_orientation() + .ok_or(anyhow!("Failed to apply embedded image orientation"))?; + + let image_surface = ImageSurface::create_from_pixbuf(&pixbuf)?; + self.background_image = Some(image_surface); + } + self.config.general.bg_type = BackgroundType::Image; Ok(()) diff --git a/src/util.rs b/src/util.rs index 40c8dea..659e9ab 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2026, Nathan Gill -use std::{os::fd::OwnedFd, str::FromStr}; +use std::{ + io::{self, Read}, + os::fd::OwnedFd, + str::FromStr, +}; use clap::ValueEnum; use nix::{ @@ -168,3 +172,21 @@ pub fn is_eintr(err: &std::io::Error) -> bool { None => false, } } + +const PNG_SIG: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; + +// Detect if a source stream starts with a PNG signature. +// Intended use case is for detecting whether a file is PNG or not. +pub fn detect_png(r: &mut R) -> io::Result +where + R: Read, +{ + let mut buf = [0; 8]; + let n = r.read(&mut buf)?; + + if n < 8 || !buf.eq(&PNG_SIG) { + return Ok(false); + } + + Ok(true) +}