mirror of
https://github.com/bend-n/fimg.git
synced 2024-12-22 02:28:19 -06:00
commit crimes
This commit is contained in:
parent
418c13be5d
commit
b464d4b0fc
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fimg"
|
name = "fimg"
|
||||||
version = "0.4.39"
|
version = "0.4.41"
|
||||||
authors = ["bend-n <bend.n@outlook.com>"]
|
authors = ["bend-n <bend.n@outlook.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -67,7 +67,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", "term"]
|
default = ["save", "scale"]
|
||||||
wgpu-convert = ["dep:wgpu"]
|
wgpu-convert = ["dep:wgpu"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
|
@ -40,9 +40,24 @@ macro_rules! e {
|
||||||
}
|
}
|
||||||
use e;
|
use e;
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for DynImage<&'a [u8]> {
|
#[cfg(feature = "term")]
|
||||||
|
impl<T: AsRef<[u8]>> std::fmt::Display for crate::term::Display<DynImage<T>> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "term")]
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
generic_const_exprs,
|
generic_const_exprs,
|
||||||
iter_array_chunks,
|
iter_array_chunks,
|
||||||
split_at_checked,
|
split_at_checked,
|
||||||
|
core_intrinsics,
|
||||||
slice_as_chunks,
|
slice_as_chunks,
|
||||||
unchecked_math,
|
unchecked_math,
|
||||||
slice_flatten,
|
slice_flatten,
|
||||||
|
@ -82,7 +83,8 @@
|
||||||
clippy::zero_prefixed_literal,
|
clippy::zero_prefixed_literal,
|
||||||
mixed_script_confusables,
|
mixed_script_confusables,
|
||||||
incomplete_features,
|
incomplete_features,
|
||||||
confusable_idents
|
confusable_idents,
|
||||||
|
internal_features
|
||||||
)]
|
)]
|
||||||
use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range};
|
use std::{hint::assert_unchecked, num::NonZeroU32, ops::Range};
|
||||||
|
|
||||||
|
@ -208,6 +210,7 @@ impl Image<&[u8], 3> {
|
||||||
|
|
||||||
/// A image with a variable number of channels, and a nonzero size.
|
/// A image with a variable number of channels, and a nonzero size.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Image<T, const CHANNELS: usize> {
|
pub struct Image<T, const CHANNELS: usize> {
|
||||||
/// column order 2d slice/vec
|
/// column order 2d slice/vec
|
||||||
buffer: T,
|
buffer: T,
|
||||||
|
|
83
src/term.rs
83
src/term.rs
|
@ -12,23 +12,34 @@ mod bloc;
|
||||||
mod kitty;
|
mod kitty;
|
||||||
mod sixel;
|
mod sixel;
|
||||||
mod size;
|
mod size;
|
||||||
|
use crate::Image;
|
||||||
pub use bloc::Bloc;
|
pub use bloc::Bloc;
|
||||||
pub use iterm2::Iterm2;
|
pub use iterm2::Iterm2;
|
||||||
pub use kitty::Kitty;
|
pub use kitty::Kitty;
|
||||||
pub use sixel::Sixel;
|
pub use sixel::Sixel;
|
||||||
use std::fmt::{Result, Write};
|
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 b64;
|
||||||
mod iterm2;
|
mod iterm2;
|
||||||
|
|
||||||
impl<'a, const N: usize> std::fmt::Display for Image<&'a [u8], N>
|
impl<'a, const N: usize> std::fmt::Display for Image<&'a [u8], N>
|
||||||
where
|
where
|
||||||
[u8; 3]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
[u8; 4]: PFrom<N>,
|
|
||||||
Image<&'a [u8], N>: kitty::Data + WritePng,
|
|
||||||
Image<&'a [u8], N>: bloc::Scaled<N>,
|
|
||||||
{
|
{
|
||||||
/// Display an image in the terminal.
|
/// Display an image in the terminal.
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
|
||||||
|
@ -39,12 +50,10 @@ where
|
||||||
/// Print an image in the terminal.
|
/// Print an image in the terminal.
|
||||||
///
|
///
|
||||||
/// This is a wrapper for `print!("{}", term::Display(image))`
|
/// 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
|
where
|
||||||
[u8; 3]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
[u8; 4]: PFrom<N>,
|
Display<Image<T, N>>: std::fmt::Display,
|
||||||
Image<&'a [u8], N>: bloc::Scaled<N>,
|
|
||||||
Image<&'a [u8], N>: kitty::Data + WritePng,
|
|
||||||
{
|
{
|
||||||
print!("{}", Display(i))
|
print!("{}", Display(i))
|
||||||
}
|
}
|
||||||
|
@ -52,75 +61,59 @@ where
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
/// Display an image in the terminal.
|
/// Display an image in the terminal.
|
||||||
/// This type implements [`Display`](std::fmt::Display) and [`Debug`](std::fmt::Debug).
|
/// 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> {
|
impl<T> std::ops::Deref for Display<T> {
|
||||||
type Target = Image<&'a [u8], N>;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&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
|
where
|
||||||
[u8; 3]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
[u8; 4]: PFrom<N>,
|
|
||||||
Image<&'a [u8], N>: bloc::Scaled<N>,
|
|
||||||
Image<&'a [u8], N>: kitty::Data + WritePng,
|
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
Display(self.as_ref()).write(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> std::fmt::Display for Display<'a, N>
|
impl<const N: usize> Display<Image<&[u8], N>>
|
||||||
where
|
where
|
||||||
Image<&'a [u8], N>: bloc::Scaled<N>,
|
[(); N]: Basic,
|
||||||
[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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, const N: usize> Display<'a, N>
|
|
||||||
where
|
|
||||||
[u8; 4]: PFrom<N>,
|
|
||||||
[u8; 3]: PFrom<N>,
|
|
||||||
Image<&'a [u8], N>: bloc::Scaled<N>,
|
|
||||||
Image<&'a [u8], N>: kitty::Data + WritePng,
|
|
||||||
{
|
{
|
||||||
/// Write $TERM protocol encoded image data.
|
/// Write $TERM protocol encoded image data.
|
||||||
pub fn write(self, f: &mut impl Write) -> Result {
|
pub fn write(self, f: &mut impl Write) -> Result {
|
||||||
if let Ok(term) = std::env::var("TERM") {
|
if let Ok(term) = std::env::var("TERM") {
|
||||||
match &*term {
|
match &*term {
|
||||||
"mlterm" | "yaft-256color" => return Sixel(*self).write(f),
|
"mlterm" | "yaft-256color" => return Sixel(self.0).write(f),
|
||||||
x if x.contains("kitty") => return Kitty(*self).write(f),
|
x if x.contains("kitty") => return Kitty(self.0).write(f),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(term_program) = std::env::var("TERM_PROGRAM") {
|
if let Ok(term_program) = std::env::var("TERM_PROGRAM") {
|
||||||
match &*term_program {
|
match &*term_program {
|
||||||
"MacTerm" => return Sixel(*self).write(f),
|
"MacTerm" => return Sixel(self.0).write(f),
|
||||||
"iTerm" | "WezTerm" => return Iterm2(*self).write(f),
|
"iTerm" | "WezTerm" => return Iterm2(self.0).write(f),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok("iTerm") = std::env::var("LC_TERMINAL").as_deref() {
|
if let Ok("iTerm") = std::env::var("LC_TERMINAL").as_deref() {
|
||||||
return Iterm2(*self).write(f);
|
return Iterm2(self.0).write(f);
|
||||||
}
|
}
|
||||||
#[cfg(unix)]
|
#[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))]
|
#[cfg(not(unix))]
|
||||||
return Bloc(*self).write(f);
|
return Bloc(*self).write(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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;
|
extern crate libc;
|
||||||
use std::{io::Read, mem::MaybeUninit};
|
use std::{io::Read, mem::MaybeUninit};
|
||||||
|
|
||||||
|
@ -171,13 +164,13 @@ where
|
||||||
unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) };
|
unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) };
|
||||||
|
|
||||||
if buf.contains("_Gi=31;OK") {
|
if buf.contains("_Gi=31;OK") {
|
||||||
Some(Kitty(*self).write(to))
|
Some(Kitty(self.as_ref()).write(to))
|
||||||
} else if buf.contains(";4;")
|
} else if buf.contains(";4;")
|
||||||
|| buf.contains("?4;")
|
|| buf.contains("?4;")
|
||||||
|| buf.contains(";4c")
|
|| buf.contains(";4c")
|
||||||
|| buf.contains("?4c")
|
|| buf.contains("?4c")
|
||||||
{
|
{
|
||||||
Some(Sixel(*self).write(to))
|
Some(Sixel(self.as_ref()).write(to))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use super::Basic;
|
||||||
use crate::{pixels::convert::PFrom, scale, term::size::fit, Image};
|
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};
|
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
||||||
|
|
||||||
/// Colored `▀`s. The simple, stupid solution.
|
/// Colored `▀`s. The simple, stupid solution.
|
||||||
|
@ -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>
|
impl<T: AsRef<[u8]>, const N: usize> Display for Bloc<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: Scaled<N>,
|
[(); N]: Basic,
|
||||||
[u8; 3]: PFrom<N>,
|
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -24,44 +25,16 @@ where
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Debug for Bloc<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Debug for Bloc<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: Scaled<N>,
|
[(); N]: Basic,
|
||||||
[u8; 3]: PFrom<N>,
|
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
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>
|
impl<T: AsRef<[u8]>, const N: usize> Bloc<T, N>
|
||||||
where
|
where
|
||||||
[u8; 3]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
Image<T, N>: Scaled<N>,
|
|
||||||
{
|
{
|
||||||
/// Write out halfblocks.
|
/// Write out halfblocks.
|
||||||
pub fn write(&self, to: &mut impl Write) -> Result {
|
pub fn write(&self, to: &mut impl Write) -> Result {
|
||||||
|
@ -74,7 +47,31 @@ where
|
||||||
}
|
}
|
||||||
let buf;
|
let buf;
|
||||||
let i = if !cfg!(test) {
|
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()
|
buf.as_ref()
|
||||||
} else {
|
} else {
|
||||||
self.as_ref()
|
self.as_ref()
|
||||||
|
@ -83,7 +80,18 @@ where
|
||||||
for [a, b] in i
|
for [a, b] in i
|
||||||
.flatten()
|
.flatten()
|
||||||
.chunks_exact(i.width() as _)
|
.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>()
|
.array_chunks::<2>()
|
||||||
{
|
{
|
||||||
for (a, b) in a.zip(b) {
|
for (a, b) in a.zip(b) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::b64;
|
use super::{b64, Basic};
|
||||||
use crate::{Image, WritePng};
|
use crate::{Image, WritePng};
|
||||||
|
use core::intrinsics::transmute_unchecked as transmute;
|
||||||
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
||||||
|
|
||||||
/// Outputs [Iterm2 Inline image protocol](https://iterm2.com/documentation-images.html) encoded data.
|
/// Outputs [Iterm2 Inline image protocol](https://iterm2.com/documentation-images.html) encoded data.
|
||||||
|
@ -14,7 +15,7 @@ impl<T: AsRef<[u8]>, const N: usize> std::ops::Deref for Iterm2<T, N> {
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Display for Iterm2<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Display for Iterm2<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: WritePng,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -23,7 +24,7 @@ where
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Debug for Iterm2<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Debug for Iterm2<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: WritePng,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -32,12 +33,28 @@ where
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Iterm2<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Iterm2<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: WritePng,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
/// Write out kitty gfx data.
|
/// Write out kitty gfx data.
|
||||||
pub fn write(&self, to: &mut impl Write) -> Result {
|
pub fn write(&self, to: &mut impl Write) -> Result {
|
||||||
let mut d = Vec::with_capacity(1024);
|
let mut d = Vec::with_capacity(1024);
|
||||||
WritePng::write(&**self, &mut d).unwrap();
|
macro_rules! n {
|
||||||
|
($n:literal) => {
|
||||||
|
WritePng::write(
|
||||||
|
// SAFETY: ... i renounce traits
|
||||||
|
&unsafe { transmute::<Image<&[u8], N>, Image<&[u8], $n>>(self.as_ref()) },
|
||||||
|
&mut d,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
match N {
|
||||||
|
1 => n![1],
|
||||||
|
2 => n![2],
|
||||||
|
3 => n![3],
|
||||||
|
4 => n![4],
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
let mut e = Vec::with_capacity(b64::size(&d));
|
let mut e = Vec::with_capacity(b64::size(&d));
|
||||||
b64::encode(&d, &mut e).unwrap();
|
b64::encode(&d, &mut e).unwrap();
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::b64;
|
use super::{b64, Basic};
|
||||||
use crate::Image;
|
use crate::Image;
|
||||||
|
use core::intrinsics::transmute_unchecked as transmute;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ impl<T: AsRef<[u8]>, const N: usize> std::ops::Deref for Kitty<T, N> {
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Display for Kitty<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Display for Kitty<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: Data,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -25,52 +26,43 @@ where
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Debug for Kitty<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Debug for Kitty<T, N>
|
||||||
where
|
where
|
||||||
Image<T, N>: Data,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod seal {
|
|
||||||
pub trait Sealed {}
|
|
||||||
}
|
|
||||||
use seal::Sealed;
|
|
||||||
|
|
||||||
pub trait Data: Sealed {
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn get(&self) -> (Cow<[u8]>, &'static str);
|
|
||||||
}
|
|
||||||
macro_rules! imp {
|
|
||||||
($n:literal, $f:expr) => {
|
|
||||||
impl<T: AsRef<[u8]>> Sealed for Image<T, $n> {}
|
|
||||||
impl<T: AsRef<[u8]>> Data for Image<T, $n> {
|
|
||||||
fn get(&self) -> (Cow<[u8]>, &'static str) {
|
|
||||||
const fn castor<
|
|
||||||
T: AsRef<[u8]>,
|
|
||||||
F: FnMut(&Image<T, $n>) -> (Cow<[u8]>, &'static str),
|
|
||||||
>(
|
|
||||||
f: F,
|
|
||||||
) -> F {
|
|
||||||
f
|
|
||||||
}
|
|
||||||
castor($f)(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
imp! { 4, |x| (Cow::from(x.bytes()), "32") }
|
|
||||||
imp! { 3, |x| (Cow::from(x.bytes()), "24") }
|
|
||||||
imp! { 2, |x| (Cow::Owned(<Image<Box<[u8]>, 3>>::from(x.as_ref()).take_buffer().to_vec()), "24") }
|
|
||||||
imp! { 1, |x| (Cow::Owned(<Image<Box<[u8]>, 3>>::from(x.as_ref()).take_buffer().to_vec()), "24") }
|
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Kitty<T, N> {
|
impl<T: AsRef<[u8]>, const N: usize> Kitty<T, N> {
|
||||||
/// Write out kitty gfx data.
|
/// Write out kitty gfx data.
|
||||||
pub fn write(&self, to: &mut impl Write) -> Result
|
pub fn write(&self, to: &mut impl Write) -> Result
|
||||||
where
|
where
|
||||||
Image<T, N>: Data,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
let (bytes, dtype) = self.get();
|
macro_rules! cast {
|
||||||
|
($n:literal) => {
|
||||||
|
(
|
||||||
|
Cow::Owned(
|
||||||
|
<Image<Box<[u8]>, 3>>::from({
|
||||||
|
// SAFETY: ...
|
||||||
|
unsafe { transmute::<Image<&[u8], N>, Image<&[u8], $n>>(self.as_ref()) }
|
||||||
|
})
|
||||||
|
.take_buffer()
|
||||||
|
.to_vec(),
|
||||||
|
),
|
||||||
|
"24",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let (bytes, dtype) = {
|
||||||
|
match N {
|
||||||
|
1 => cast!(1),
|
||||||
|
2 => cast!(2),
|
||||||
|
3 => (Cow::from(self.bytes()), "24"),
|
||||||
|
4 => (Cow::from(self.bytes()), "32"),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
let (w, h) = (self.width(), self.height());
|
let (w, h) = (self.width(), self.height());
|
||||||
|
|
||||||
let mut enc = Vec::with_capacity(b64::size(&bytes));
|
let mut enc = Vec::with_capacity(b64::size(&bytes));
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
use core::intrinsics::transmute_unchecked as transmute;
|
||||||
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
use std::fmt::{Debug, Display, Formatter, Result, Write};
|
||||||
|
|
||||||
use crate::{pixels::convert::PFrom, Image};
|
use crate::{pixels::convert::PFrom, Image};
|
||||||
|
|
||||||
|
use super::Basic;
|
||||||
|
|
||||||
/// Outputs [sixel](https://en.wikipedia.org/wiki/Sixel) encoded data in its [`Display`] and [`Debug`] implementations, for easy visual debugging.
|
/// Outputs [sixel](https://en.wikipedia.org/wiki/Sixel) encoded data in its [`Display`] and [`Debug`] implementations, for easy visual debugging.
|
||||||
pub struct Sixel<T: AsRef<[u8]>, const N: usize>(pub Image<T, N>);
|
pub struct Sixel<T: AsRef<[u8]>, const N: usize>(pub Image<T, N>);
|
||||||
|
|
||||||
|
@ -15,7 +18,7 @@ impl<T: AsRef<[u8]>, const N: usize> std::ops::Deref for Sixel<T, N> {
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Display for Sixel<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Display for Sixel<T, N>
|
||||||
where
|
where
|
||||||
[u8; 4]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -24,7 +27,7 @@ where
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>, const N: usize> Debug for Sixel<T, N>
|
impl<T: AsRef<[u8]>, const N: usize> Debug for Sixel<T, N>
|
||||||
where
|
where
|
||||||
[u8; 4]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
self.write(f)
|
self.write(f)
|
||||||
|
@ -35,7 +38,7 @@ impl<T: AsRef<[u8]>, const N: usize> Sixel<T, N> {
|
||||||
/// Write out sixel data.
|
/// Write out sixel data.
|
||||||
pub fn write(&self, to: &mut impl Write) -> Result
|
pub fn write(&self, to: &mut impl Write) -> Result
|
||||||
where
|
where
|
||||||
[u8; 4]: PFrom<N>,
|
[(); N]: Basic,
|
||||||
{
|
{
|
||||||
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())?;
|
||||||
|
@ -47,7 +50,15 @@ impl<T: AsRef<[u8]>, const N: usize> Sixel<T, N> {
|
||||||
buf = self
|
buf = self
|
||||||
.chunked()
|
.chunked()
|
||||||
.copied()
|
.copied()
|
||||||
.map(<[u8; 4] as PFrom<N>>::pfrom)
|
// SAFETY: #[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
|
.map(|x| unsafe {
|
||||||
|
match N {
|
||||||
|
1 => <[u8; 4] as PFrom<1>>::pfrom(transmute(x)),
|
||||||
|
2 => <[u8; 4] as PFrom<2>>::pfrom(transmute(x)),
|
||||||
|
3 => <[u8; 4] as PFrom<3>>::pfrom(transmute(x)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
&*buf
|
&*buf
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ struct Fd(i32, bool);
|
||||||
impl Drop for Fd {
|
impl Drop for Fd {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.1 {
|
if self.1 {
|
||||||
|
// SAFETY: #[allow(clippy::undocumented_unsafe_blocks)]
|
||||||
unsafe { close(self.0) };
|
unsafe { close(self.0) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +44,7 @@ pub fn size() -> Option<(u16, u16)> {
|
||||||
*File::open("/dev/tty")
|
*File::open("/dev/tty")
|
||||||
.map(Fd::new)
|
.map(Fd::new)
|
||||||
.unwrap_or(Fd::from(STDIN_FILENO)),
|
.unwrap_or(Fd::from(STDIN_FILENO)),
|
||||||
TIOCGWINSZ.into(),
|
TIOCGWINSZ,
|
||||||
size.as_mut_ptr(),
|
size.as_mut_ptr(),
|
||||||
) != -1
|
) != -1
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue