detection of color count

This commit is contained in:
bendn 2024-03-13 11:18:39 +07:00
parent 3bc1dd6c07
commit ae82460a8f
5 changed files with 104 additions and 55 deletions

View file

@ -26,6 +26,7 @@ minifb = { version = "0.25.0", default-features = false, features = [
wgpu = { version = "0.19.1", default-features = false, optional = true } wgpu = { version = "0.19.1", default-features = false, optional = true }
atools = "0.1.0" atools = "0.1.0"
qwant = { version = "1.0.0", optional = true } qwant = { version = "1.0.0", optional = true }
libc = "0.2.153"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = { version = "0.53.0", features = [ windows = { version = "0.53.0", features = [
@ -67,7 +68,7 @@ text = ["fontdue"]
blur = ["slur"] blur = ["slur"]
term = ["qwant", "save", "scale", "windows"] term = ["qwant", "save", "scale", "windows"]
real-show = ["minifb", "text"] real-show = ["minifb", "text"]
default = ["save", "scale"] default = ["save", "scale", "term"]
wgpu-convert = ["dep:wgpu"] wgpu-convert = ["dep:wgpu"]
[profile.release] [profile.release]

View file

@ -68,6 +68,7 @@
const_option, const_option,
array_chunks, array_chunks,
let_chains, let_chains,
try_blocks,
test test
)] )]
#![warn( #![warn(

View file

@ -114,9 +114,27 @@ where
#[cfg(unix)] #[cfg(unix)]
// https://github.com/benjajaja/ratatui-image/blob/master/src/picker.rs#L226 // https://github.com/benjajaja/ratatui-image/blob/master/src/picker.rs#L226
fn guess_harder(&self, to: &mut impl Write) -> Option<Result> { fn guess_harder(&self, to: &mut impl Write) -> Option<Result> {
extern crate libc; // contains a kitty gfx and sixel query, the `\x1b[c` is for sixels
use std::{io::Read, mem::MaybeUninit}; let buf = query(r"_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\")?;
if buf.contains("_Gi=31;OK") {
Some(Kitty(self.as_ref()).write(to))
} else if buf.contains(";4;")
|| buf.contains("?4;")
|| buf.contains(";4c")
|| buf.contains("?4c")
{
Some(Sixel(self.as_ref()).write(to))
} else {
None
}
}
}
#[cfg(unix)]
// https://github.com/benjajaja/ratatui-image/blob/master/src/picker.rs#L226
fn query(device_query_code: &'static str) -> Option<String> {
extern crate libc;
use std::mem::MaybeUninit;
fn r(result: i32) -> Option<()> { fn r(result: i32) -> Option<()> {
(result != -1).then_some(()) (result != -1).then_some(())
} }
@ -138,41 +156,41 @@ where
}, },
) )
}; };
let buf = {
// contains a kitty gfx and sixel query, the `\x1b[c` is for sixels
println!(r"_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\");
let mut stdin = std::io::stdin();
let mut buf = String::new();
let mut b = [0; 16]; let buf = try {
'l: loop { // SAFETY: linux time out'd reading
let n = stdin.read(&mut b).ok()?; unsafe {
if n == 0 { println!("{device_query_code}");
continue; let mut buf = Vec::new();
let mut tmp = [0; 1 << 5];
loop {
let mut x = std::mem::zeroed::<libc::fd_set>();
libc::FD_SET(0, &mut x);
match libc::select(
1,
&mut x,
0 as _,
0 as _,
&mut libc::timeval {
tv_sec: 0,
tv_usec: 5e5 as _,
},
) {
0 => break,
-1 => return None,
_ => {}
} }
for b in b { match libc::read(libc::STDIN_FILENO, tmp.as_mut_ptr().cast(), tmp.len()) {
buf.push(b as char); 0 => continue,
if b == b'c' { -1 => return None,
break 'l; n => buf.extend_from_slice(&tmp[..n as _]),
} }
} }
String::from_utf8(buf).ok()?
} }
buf
}; };
// SAFETY: reset attrs to what they were before we became nosy // SAFETY: reset attrs to what they were before we became nosy
unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) }; unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) };
buf
if buf.contains("_Gi=31;OK") {
Some(Kitty(self.as_ref()).write(to))
} else if buf.contains(";4;")
|| buf.contains("?4;")
|| buf.contains(";4c")
|| buf.contains("?4c")
{
Some(Sixel(self.as_ref()).write(to))
} else {
None
}
}
} }

View file

@ -39,7 +39,7 @@ where
LaneCount<N>: SupportedLaneCount, LaneCount<N>: SupportedLaneCount,
{ {
fn cas<U>(self) -> U { fn cas<U>(self) -> U {
assert!(std::mem::size_of::<U>() == std::mem::size_of::<Simd<T, N>>()); assert!(std::mem::size_of::<U>() == std::mem::size_of::<Self>());
unsafe { transmute_unchecked(self) } unsafe { transmute_unchecked(self) }
} }
} }
@ -49,7 +49,7 @@ where
LaneCount<N>: SupportedLaneCount, LaneCount<N>: SupportedLaneCount,
{ {
fn cas<U>(self) -> U { fn cas<U>(self) -> U {
assert!(std::mem::size_of::<U>() == std::mem::size_of::<Mask<T, N>>()); assert!(std::mem::size_of::<U>() == std::mem::size_of::<Self>());
unsafe { transmute_unchecked(self) } unsafe { transmute_unchecked(self) }
} }
} }

View file

@ -40,6 +40,34 @@ impl<T: AsRef<[u8]>, const N: usize> Sixel<T, N> {
where where
[(); N]: Basic, [(); N]: Basic,
{ {
#[cfg(unix)]
let q = {
extern crate libc;
// SAFETY: is stdout a tty
(unsafe { libc::isatty(0) } == 1)
};
#[cfg(not(unix))]
let q = true;
let colors = q
.then_some(super::query("[?1;1;0S").and_then(|x| {
// [?1;0;65536S
if let [b'?', b'1', b';', b'0', b';', n @ ..] = x.as_bytes() {
Some(
n.iter()
.copied()
.take_while(u8::is_ascii_digit)
.fold(0u16, |acc, x| {
acc.saturating_mul(10).saturating_add((x - b'0') as u16)
})
.max(64)
.min(0xfff),
)
} else {
None
}
}))
.flatten()
.unwrap_or(255);
to.write_str("Pq")?; to.write_str("Pq")?;
write!(to, r#""1;1;{};{}"#, self.width(), self.height())?; write!(to, r#""1;1;{};{}"#, self.width(), self.height())?;
let buf; let buf;
@ -63,14 +91,15 @@ impl<T: AsRef<[u8]>, const N: usize> Sixel<T, N> {
&*buf &*buf
}; };
let q = qwant::NeuQuant::new(15, 255, rgba); let q = qwant::NeuQuant::new(15, colors as _, rgba);
// TODO: don't colllect // TODO: don't colllect
let pixels: Vec<u8> = rgba.iter().map(|&pix| q.index_of(pix) as u8).collect(); let pixels: Vec<u16> = rgba.iter().map(|&pix| q.index_of(pix) as _).collect();
for ([r, g, b], i) in q for ([r, g, b], i) in q
.color_map_rgb() .color_map_rgb()
.map(|x| x.map(|x| (x as f32 * (100. / 255.)) as u32)) .map(|x| x.map(|x| (x as f32 * (100. / 255.)) as u32))
.zip(0u8..) .zip(0u64..)
{ {
write!(to, "#{i};2;{r};{g};{b}")?; write!(to, "#{i};2;{r};{g};{b}")?;
} }