mcraw_tui/
thumbnail_worker.rs1use 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 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}