Skip to content

Commit

Permalink
commit crimes
Browse files Browse the repository at this point in the history
  • Loading branch information
bend-n committed Mar 12, 2024
1 parent 418c13b commit c0ed179
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 131 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fimg"
version = "0.4.39"
version = "0.4.40"
authors = ["bend-n <bend.n@outlook.com>"]
license = "MIT"
edition = "2021"
Expand Down Expand Up @@ -67,7 +67,7 @@ text = ["fontdue"]
blur = ["slur"]
term = ["qwant", "save", "scale", "windows"]
real-show = ["minifb", "text"]
default = ["save", "scale", "term"]
default = ["save", "scale"]
wgpu-convert = ["dep:wgpu"]

[profile.release]
Expand Down
16 changes: 14 additions & 2 deletions src/dyn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,21 @@ macro_rules! e {
}
use e;

impl<'a> std::fmt::Display for DynImage<&'a [u8]> {
impl<T: AsRef<[u8]>> std::fmt::Display for crate::term::Display<DynImage<T>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
e!(self, |x| crate::term::Display(*x).write(f))
e!(&self.0, |x| crate::term::Display(x.as_ref()).write(f))
}
}

impl<T: AsRef<[u8]>> std::fmt::Debug for crate::term::Display<DynImage<T>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
e!(&self.0, |x| crate::term::Display(x.as_ref()).write(f))
}
}

impl<T: AsRef<[u8]>> std::fmt::Display for DynImage<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
e!(&self, |x| crate::term::Display(x.as_ref()).write(f))
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
generic_const_exprs,
iter_array_chunks,
split_at_checked,
core_intrinsics,
slice_as_chunks,
unchecked_math,
slice_flatten,
Expand All @@ -82,7 +83,8 @@
clippy::zero_prefixed_literal,
mixed_script_confusables,
incomplete_features,
confusable_idents
confusable_idents,
internal_features
)]
use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range};

Expand Down Expand Up @@ -208,6 +210,7 @@ impl Image<&[u8], 3> {

/// A image with a variable number of channels, and a nonzero size.
#[derive(Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Image<T, const CHANNELS: usize> {
/// column order 2d slice/vec
buffer: T,
Expand Down
83 changes: 38 additions & 45 deletions src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,34 @@ mod bloc;
mod kitty;
mod sixel;
mod size;
use crate::Image;
pub use bloc::Bloc;
pub use iterm2::Iterm2;
pub use kitty::Kitty;
pub use sixel::Sixel;
use std::fmt::{Result, Write};

use crate::{pixels::convert::PFrom, Image, WritePng};
mod seal {
pub trait Sealed {}
}
use seal::Sealed;
#[doc(hidden)]
pub trait Basic: Sealed {}
impl Sealed for [(); 1] {}
impl Basic for [(); 1] {}
impl Sealed for [(); 2] {}
impl Basic for [(); 2] {}
impl Sealed for [(); 3] {}
impl Basic for [(); 3] {}
impl Sealed for [(); 4] {}
impl Basic for [(); 4] {}

mod b64;
mod iterm2;

impl<'a, const N: usize> std::fmt::Display for Image<&'a [u8], N>
where
[u8; 3]: PFrom<N>,
[u8; 4]: PFrom<N>,
Image<&'a [u8], N>: kitty::Data + WritePng,
Image<&'a [u8], N>: bloc::Scaled<N>,
[(); N]: Basic,
{
/// Display an image in the terminal.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
Expand All @@ -39,88 +50,70 @@ where
/// Print an image in the terminal.
///
/// This is a wrapper for `print!("{}", term::Display(image))`
pub fn print<'a, const N: usize>(i: Image<&'a [u8], N>)
pub fn print<T: AsRef<[u8]>, const N: usize>(i: Image<T, N>)
where
[u8; 3]: PFrom<N>,
[u8; 4]: PFrom<N>,
Image<&'a [u8], N>: bloc::Scaled<N>,
Image<&'a [u8], N>: kitty::Data + WritePng,
[(); N]: Basic,
Display<Image<T, N>>: std::fmt::Display,
{
print!("{}", Display(i))
}

#[derive(Copy, Clone)]
/// Display an image in the terminal.
/// This type implements [`Display`](std::fmt::Display) and [`Debug`](std::fmt::Debug).
pub struct Display<'a, const N: usize>(pub Image<&'a [u8], N>);
pub struct Display<T>(pub T);

impl<'a, const N: usize> std::ops::Deref for Display<'a, N> {
type Target = Image<&'a [u8], N>;
impl<T> std::ops::Deref for Display<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<'a, const N: usize> std::fmt::Debug for Display<'a, N>
impl<T: AsRef<[u8]>, const N: usize> std::fmt::Debug for Display<Image<T, N>>
where
[u8; 3]: PFrom<N>,
[u8; 4]: PFrom<N>,
Image<&'a [u8], N>: bloc::Scaled<N>,
Image<&'a [u8], N>: kitty::Data + WritePng,
[(); N]: Basic,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
self.write(f)
}
}

impl<'a, const N: usize> std::fmt::Display for Display<'a, N>
where
Image<&'a [u8], N>: bloc::Scaled<N>,
[u8; 4]: PFrom<N>,
[u8; 3]: PFrom<N>,
Image<&'a [u8], N>: kitty::Data + WritePng,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.write(f)
Display(self.as_ref()).write(f)
}
}

impl<'a, const N: usize> Display<'a, N>
impl<const N: usize> Display<Image<&[u8], N>>
where
[u8; 4]: PFrom<N>,
[u8; 3]: PFrom<N>,
Image<&'a [u8], N>: bloc::Scaled<N>,
Image<&'a [u8], N>: kitty::Data + WritePng,
[(); N]: Basic,
{
/// Write $TERM protocol encoded image data.
pub fn write(self, f: &mut impl Write) -> Result {
if let Ok(term) = std::env::var("TERM") {
match &*term {
"mlterm" | "yaft-256color" => return Sixel(*self).write(f),
x if x.contains("kitty") => return Kitty(*self).write(f),
"mlterm" | "yaft-256color" => return Sixel(self.0).write(f),
x if x.contains("kitty") => return Kitty(self.0).write(f),
_ => (),
}
}
if let Ok(term_program) = std::env::var("TERM_PROGRAM") {
match &*term_program {
"MacTerm" => return Sixel(*self).write(f),
"iTerm" | "WezTerm" => return Iterm2(*self).write(f),
"MacTerm" => return Sixel(self.0).write(f),
"iTerm" | "WezTerm" => return Iterm2(self.0).write(f),
_ => (),
}
}
if let Ok("iTerm") = std::env::var("LC_TERMINAL").as_deref() {
return Iterm2(*self).write(f);
return Iterm2(self.0).write(f);
}
#[cfg(unix)]
return self.guess_harder(f).unwrap_or_else(|| Bloc(*self).write(f));
return self
.guess_harder(f)
.unwrap_or_else(|| Bloc(self.0).write(f));
#[cfg(not(unix))]
return Bloc(*self).write(f);
}

#[cfg(unix)]
// 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;
use std::{io::Read, mem::MaybeUninit};

Expand Down Expand Up @@ -171,13 +164,13 @@ where
unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) };

if buf.contains("_Gi=31;OK") {
Some(Kitty(*self).write(to))
Some(Kitty(self.as_ref()).write(to))
} else if buf.contains(";4;")
|| buf.contains("?4;")
|| buf.contains(";4c")
|| buf.contains("?4c")
{
Some(Sixel(*self).write(to))
Some(Sixel(self.as_ref()).write(to))
} else {
None
}
Expand Down
76 changes: 42 additions & 34 deletions src/term/bloc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
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.
Expand All @@ -14,8 +16,7 @@ impl<T: AsRef<[u8]>, const N: usize> std::ops::Deref for Bloc<T, N> {

impl<T: AsRef<[u8]>, const N: usize> Display for Bloc<T, N>
where
Image<T, N>: Scaled<N>,
[u8; 3]: PFrom<N>,
[(); N]: Basic,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
self.write(f)
Expand All @@ -24,44 +25,16 @@ where

impl<T: AsRef<[u8]>, const N: usize> Debug for Bloc<T, N>
where
Image<T, N>: Scaled<N>,
[u8; 3]: PFrom<N>,
[(); N]: Basic,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
self.write(f)
}
}

#[doc(hidden)]
pub trait Scaled<const N: usize> {
fn scaled(&self, to: (u32, u32)) -> Image<Box<[u8]>, N>;
}

macro_rules! n {
($n:literal) => {
impl<T: AsRef<[u8]>> Scaled<$n> for Image<T, $n> {
fn scaled(&self, (w, h): (u32, u32)) -> Image<Box<[u8]>, $n> {
self.scale::<scale::Nearest>(w, h)
}
}
};
(o $n:literal) => {
impl<T: AsRef<[u8]>> Scaled<$n> for Image<T, $n> {
fn scaled(&self, (w, h): (u32, u32)) -> Image<Box<[u8]>, $n> {
self.as_ref().to_owned().scale::<scale::Nearest>(w, h)
}
}
};
}
n!(1);
n!(o 2);
n!(3);
n!(o 4);

impl<T: AsRef<[u8]>, const N: usize> Bloc<T, N>
where
[u8; 3]: PFrom<N>,
Image<T, N>: Scaled<N>,
[(); N]: Basic,
{
/// Write out halfblocks.
pub fn write(&self, to: &mut impl Write) -> Result {
Expand All @@ -74,7 +47,31 @@ where
}
let buf;
let i = if !cfg!(test) {
buf = self.scaled(fit((self.width(), self.height())));
let (w, h) = fit((self.width(), self.height()));
macro_rules! n {
($n:literal) => {
transmute::<Image<Box<[u8]>, $n>, Image<Box<[u8]>, N>>(
transmute::<Image<&[u8], N>, Image<&[u8], $n>>(self.as_ref())
.scale::<scale::Nearest>(w, h),
)
};
(o $n:literal) => {
transmute::<Image<Box<[u8]>, 1>, Image<Box<[u8]>, N>>(
transmute::<Image<Vec<u8>, N>, Image<&[u8], 1>>(self.as_ref().to_owned())
.scale::<scale::Nearest>(w, h),
)
};
}
// 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()
Expand All @@ -83,7 +80,18 @@ where
for [a, b] in i
.flatten()
.chunks_exact(i.width() as _)
.map(|x| x.iter().copied().map(<[u8; 3] as PFrom<N>>::pfrom))
.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) {
Expand Down
Loading

0 comments on commit c0ed179

Please sign in to comment.