use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread::JoinHandle;
use image::RgbaImage;
use crate::capture::encode_webp;
use crate::progress::Progress;
pub enum Pixels {
Rgba(RgbaImage),
#[cfg(target_os = "linux")]
Portal(crate::screencast::Frame),
}
pub struct Job {
pub pixels: Pixels,
pub path: PathBuf,
pub quality: f32,
}
pub struct Writer {
tx: Option<Sender<Job>>,
thread: Option<JoinHandle<()>>,
}
impl Writer {
pub fn new(progress: Progress, lang: crate::i18n::Lang) -> Self {
let (tx, rx) = channel::<Job>();
let thread = std::thread::Builder::new()
.name("ssr-writer".into())
.spawn(move || run(rx, progress, lang))
.ok();
Self {
tx: Some(tx),
thread,
}
}
pub fn sender(&self) -> Sender<Job> {
self.tx.clone().expect("writer actif")
}
}
impl Drop for Writer {
fn drop(&mut self) {
self.tx = None;
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
}
fn run(rx: Receiver<Job>, progress: Progress, lang: crate::i18n::Lang) {
while let Ok(job) = rx.recv() {
progress.set(lang.msg_saving_image());
if let Err(err) = write_job(job) {
eprintln!("ssr: écriture d'une capture échouée : {err}");
}
progress.clear();
}
}
fn write_job(job: Job) -> std::io::Result<()> {
let image = match job.pixels {
Pixels::Rgba(img) => img,
#[cfg(target_os = "linux")]
Pixels::Portal(frame) => {
let (rgba, w, h) = frame
.to_rgba_cropped()
.ok_or_else(|| std::io::Error::other("format de frame non géré"))?;
RgbaImage::from_raw(w, h, rgba)
.ok_or_else(|| std::io::Error::other("dimensions de frame incohérentes"))?
}
};
let bytes = encode_webp(&image, job.quality);
std::fs::write(&job.path, bytes)
}