Skip to main content

mcraw_tui/
thumbnail_worker.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::thread;
4
5use crossbeam_channel::{unbounded, Receiver, Sender};
6
7use crate::decoder::Decoder;
8use crate::preview::pipeline::params::PreviewParams;
9use crate::thumbnail::{cpu_thumbnail, CachedThumbnail};
10
11use std::time::Instant;
12
13#[derive(Debug)]
14pub struct ThumbnailRequest {
15    pub path: PathBuf,
16    pub timestamp_ns: i64,
17    pub params: PreviewParams,
18}
19
20#[derive(Debug)]
21pub struct ThumbnailResult {
22    pub path: PathBuf,
23    pub sixel: Option<Vec<u8>>,
24    pub width: u32,
25    pub height: u32,
26    pub error: Option<String>,
27}
28
29impl ThumbnailResult {
30    pub fn to_cached(&self) -> Option<CachedThumbnail> {
31        self.sixel.as_ref().map(|s| CachedThumbnail {
32            sixel: s.clone(),
33            width: self.width,
34            height: self.height,
35            encode_time: Instant::now(),
36        })
37    }
38}
39
40pub struct ThumbnailWorkerPool {
41    request_tx: Sender<ThumbnailRequest>,
42    pub result_rx: Receiver<ThumbnailResult>,
43    _handles: Vec<thread::JoinHandle<()>>,
44}
45
46impl ThumbnailWorkerPool {
47    pub fn new(num_workers: usize) -> Self {
48        let (request_tx, request_rx) = unbounded::<ThumbnailRequest>();
49        let (result_tx, result_rx) = unbounded::<ThumbnailResult>();
50
51        let mut handles = Vec::new();
52        for _ in 0..num_workers {
53            let request_rx = request_rx.clone();
54            let result_tx = result_tx.clone();
55            handles.push(thread::spawn(move || {
56                worker_loop(request_rx, result_tx);
57            }));
58        }
59
60        Self {
61            request_tx,
62            result_rx,
63            _handles: handles,
64        }
65    }
66
67    pub fn submit(&self, req: ThumbnailRequest) {
68        let _ = self.request_tx.send(req);
69    }
70}
71
72fn worker_loop(request_rx: Receiver<ThumbnailRequest>, result_tx: Sender<ThumbnailResult>) {
73    let mut decoders: HashMap<PathBuf, Option<Decoder>> = HashMap::new();
74
75    while let Ok(req) = request_rx.recv() {
76        // Lazy-init the decoder for this file path
77        let entry = decoders.entry(req.path.clone()).or_insert_with(|| {
78            Decoder::new(&req.path).ok()
79        });
80
81        let decoder: &Decoder = match entry.as_ref() {
82            Some(d) => d,
83            None => {
84                let _ = result_tx.send(ThumbnailResult {
85                    path: req.path,
86                    sixel: None,
87                    width: 0,
88                    height: 0,
89                    error: Some("Failed to open decoder".into()),
90                });
91                continue;
92            }
93        };
94
95        let load_result: Result<(Vec<u16>, _), _> = decoder.load_frame(req.timestamp_ns);
96        match load_result {
97            Ok((bayer, _)) => {
98                match cpu_thumbnail(&bayer, &req.params) {
99                    Ok((sixel, w, h)) => {
100                        let _ = result_tx.send(ThumbnailResult {
101                            path: req.path,
102                            sixel: Some(sixel),
103                            width: w,
104                            height: h,
105                            error: None,
106                        });
107                    }
108                    Err(e) => {
109                        let _ = result_tx.send(ThumbnailResult {
110                            path: req.path,
111                            sixel: None,
112                            width: 0,
113                            height: 0,
114                            error: Some(format!("CPU thumbnail: {}", e)),
115                        });
116                    }
117                }
118            }
119            Err(e) => {
120                let _ = result_tx.send(ThumbnailResult {
121                    path: req.path,
122                    sixel: None,
123                    width: 0,
124                    height: 0,
125                    error: Some(format!("Decode: {}", e)),
126                });
127            }
128        }
129    }
130}