fast image operations
Diffstat (limited to 'src/term/bloc.rs')
-rw-r--r--src/term/bloc.rs115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/term/bloc.rs b/src/term/bloc.rs
new file mode 100644
index 0000000..58c8655
--- /dev/null
+++ b/src/term/bloc.rs
@@ -0,0 +1,115 @@
+use super::Basic;
+use crate::{pixels::convert::PFrom, scale, term::size::fit, Image};
+use core::intrinsics::transmute_unchecked as transmute;
+use std::fmt::{Debug, Display, Formatter, Result, Write};
+
+/// Colored `â–€`s. The simple, stupid solution.
+/// May be too big for your terminal.
+pub struct Bloc<T: AsRef<[u8]>, const N: usize>(pub Image<T, N>);
+impl<T: AsRef<[u8]>, const N: usize> std::ops::Deref for Bloc<T, N> {
+ type Target = Image<T, N>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<T: AsRef<[u8]>, const N: usize> Display for Bloc<T, N>
+where
+ [(); N]: Basic,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ self.write(f)
+ }
+}
+
+impl<T: AsRef<[u8]>, const N: usize> Debug for Bloc<T, N>
+where
+ [(); N]: Basic,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ self.write(f)
+ }
+}
+
+impl<T: AsRef<[u8]>, const N: usize> Bloc<T, N>
+where
+ [(); N]: Basic,
+{
+ /// Write out halfblocks.
+ pub fn write(&self, to: &mut impl Write) -> Result {
+ macro_rules! c {
+ (fg $fg:expr, bg $bg:expr) => {{
+ let [fr, fg, fb] = $fg;
+ let [br, bg, bb] = $bg;
+ write!(to, "\x1b[38;2;{fr};{fg};{fb};48;2;{br};{bg};{bb}mâ–€")?;
+ }};
+ }
+ let buf;
+ let i = if !cfg!(test) {
+ let (w, h) = fit((self.width(), self.height()));
+ macro_rules! n {
+ ($n:literal) => {
+ self.as_ref()
+ .trans::<$n>()
+ .scale::<scale::Nearest>(w, h)
+ .trans::<N>()
+ };
+ (o $n:literal) => {
+ self.as_ref()
+ .to_owned()
+ .trans::<1>()
+ .scale::<scale::Nearest>(w, h)
+ .trans::<N>()
+ };
+ }
+ // SAFETY: #[allow(clippy::undocumented_unsafe_blocks)]
+ buf = unsafe {
+ match N {
+ 1 => n![1],
+ 2 => n![o 2],
+ 3 => n![3],
+ 4 => n![o 4],
+ _ => unreachable!(),
+ }
+ };
+ buf.as_ref()
+ } else {
+ self.as_ref()
+ };
+
+ for [a, b] in i
+ .flatten()
+ .chunks_exact(i.width() as _)
+ .map(|x| {
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ x.iter().copied().map(|x| unsafe {
+ match N {
+ 1 => <[u8; 3] as PFrom<1>>::pfrom(transmute(x)),
+ 2 => <[u8; 3] as PFrom<2>>::pfrom(transmute(x)),
+ 3 => <[u8; 3] as PFrom<3>>::pfrom(transmute(x)),
+ 4 => <[u8; 3] as PFrom<4>>::pfrom(transmute(x)),
+ _ => unreachable!(),
+ }
+ })
+ })
+ .array_chunks::<2>()
+ {
+ for (a, b) in a.zip(b) {
+ c! { fg a, bg b };
+ }
+ writeln!(to)?;
+ }
+ write!(to, "\x1b[0m")?;
+ Ok(())
+ }
+}
+
+#[test]
+fn test() {
+ let x = Image::<_, 3>::open("tdata/small_cat.png");
+ use std::hash::Hasher;
+ let mut h = std::hash::DefaultHasher::new();
+ h.write(Bloc(x).to_string().as_bytes());
+ assert_eq!(h.finish(), 0x6546104ffee16f77);
+}