mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-23 07:41:03 +00:00
98 lines
2.3 KiB
Rust
98 lines
2.3 KiB
Rust
use std::{io::{stdout, BufWriter, Write}, path::Path};
|
|
|
|
use anyhow::{bail, Result};
|
|
use color_quant::NeuQuant;
|
|
use image::DynamicImage;
|
|
use ratatui::prelude::Rect;
|
|
use shared::Term;
|
|
|
|
use crate::{Image, CLOSE, ESCAPE, START};
|
|
|
|
pub(super) struct Sixel;
|
|
|
|
impl Sixel {
|
|
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<()> {
|
|
let img = Image::crop(path, (rect.width, rect.height)).await?;
|
|
let b = Self::encode(img).await?;
|
|
|
|
Self::image_hide(rect)?;
|
|
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
|
|
}
|
|
|
|
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
|
let stdout = BufWriter::new(stdout().lock());
|
|
Term::move_lock(stdout, (0, 0), |stdout| {
|
|
for y in rect.top()..rect.bottom() {
|
|
Term::move_to(stdout, rect.x, y)?;
|
|
Term::kill_to_end(stdout)?;
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
|
let alpha = img.color().has_alpha();
|
|
if img.width() == 0 || img.height() == 0 {
|
|
bail!("image is empty");
|
|
}
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
let img = img.into_rgba8();
|
|
let nq = NeuQuant::new(10, 256 - alpha as usize, &img);
|
|
|
|
let mut buf: Vec<u8> = Vec::with_capacity(1 << 16);
|
|
write!(buf, "{}P0;1;8q\"1;1;{};{}", START, img.width(), img.height())?;
|
|
|
|
// Palette
|
|
for (i, c) in nq.color_map_rgba().chunks(4).enumerate() {
|
|
write!(
|
|
buf,
|
|
"#{};2;{};{};{}",
|
|
i + alpha as usize,
|
|
c[0] as u16 * 100 / 255,
|
|
c[1] as u16 * 100 / 255,
|
|
c[2] as u16 * 100 / 255
|
|
)?;
|
|
}
|
|
|
|
for y in 0..img.height() {
|
|
let c = (b'?' + (1 << (y % 6))) as char;
|
|
|
|
let mut last = 0;
|
|
let mut repeat = 0usize;
|
|
for x in 0..img.width() {
|
|
let pixel = img.get_pixel(x, y).0;
|
|
let idx = if pixel[3] == 0 { 0 } else { nq.index_of(&pixel) as u8 + alpha as u8 };
|
|
|
|
if idx == last || repeat == 0 {
|
|
(last, repeat) = (idx, repeat + 1);
|
|
continue;
|
|
}
|
|
|
|
if repeat > 1 {
|
|
write!(buf, "#{last}!{repeat}{c}")?;
|
|
} else {
|
|
write!(buf, "#{last}{c}")?;
|
|
}
|
|
|
|
(last, repeat) = (idx, 1);
|
|
}
|
|
|
|
if repeat > 1 {
|
|
write!(buf, "#{last}!{repeat}{c}")?;
|
|
} else {
|
|
write!(buf, "#{last}{c}")?;
|
|
}
|
|
|
|
write!(buf, "$")?;
|
|
if y % 6 == 5 {
|
|
write!(buf, "-")?;
|
|
}
|
|
}
|
|
|
|
write!(buf, "{}\\{}", ESCAPE, CLOSE)?;
|
|
Ok(buf)
|
|
})
|
|
.await?
|
|
}
|
|
}
|