Skip to content

Commit

Permalink
feat: support transparent image preview (#1556)
Browse files Browse the repository at this point in the history
Co-authored-by: sxyazi <[email protected]>
  • Loading branch information
diegodorado and sxyazi authored Sep 14, 2024
1 parent 276f691 commit 0acf5c2
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 41 deletions.
14 changes: 7 additions & 7 deletions yazi-adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use ratatui::layout::Rect;
use tracing::warn;
use yazi_shared::env_exists;

use super::{Iterm2, Kitty, KittyOld};
use super::{Iip, Kitty, KittyOld};
use crate::{Chafa, Emulator, Sixel, Ueberzug, SHOWN, TMUX, WSL};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Adapter {
Kitty,
KittyOld,
Iterm2,
Iip,
Sixel,

// Supported by Überzug++
Expand All @@ -26,7 +26,7 @@ impl Display for Adapter {
match self {
Self::Kitty => write!(f, "kitty"),
Self::KittyOld => write!(f, "kitty"),
Self::Iterm2 => write!(f, "iterm2"),
Self::Iip => write!(f, "iip"),
Self::Sixel => write!(f, "sixel"),
Self::X11 => write!(f, "x11"),
Self::Wayland => write!(f, "wayland"),
Expand All @@ -44,7 +44,7 @@ impl Adapter {
match self {
Self::Kitty => Kitty::image_show(path, max).await,
Self::KittyOld => KittyOld::image_show(path, max).await,
Self::Iterm2 => Iterm2::image_show(path, max).await,
Self::Iip => Iip::image_show(path, max).await,
Self::Sixel => Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await,
Self::Chafa => Chafa::image_show(path, max).await,
Expand All @@ -59,7 +59,7 @@ impl Adapter {
match self {
Self::Kitty => Kitty::image_erase(area),
Self::KittyOld => KittyOld::image_erase(area),
Self::Iterm2 => Iterm2::image_erase(area),
Self::Iip => Iip::image_erase(area),
Self::Sixel => Sixel::image_erase(area),
Self::X11 | Self::Wayland => Ueberzug::image_erase(area),
Self::Chafa => Chafa::image_erase(area),
Expand All @@ -76,7 +76,7 @@ impl Adapter {

#[inline]
pub(super) fn needs_ueberzug(self) -> bool {
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel)
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iip | Self::Sixel)
}
}

Expand All @@ -90,7 +90,7 @@ impl Adapter {

let mut protocols = emulator.adapters();
#[cfg(windows)]
protocols.retain(|p| *p == Self::Iterm2);
protocols.retain(|p| *p == Self::Iip);
if env_exists("ZELLIJ_SESSION_NAME") {
protocols.retain(|p| *p == Self::Sixel);
} else if *TMUX {
Expand Down
12 changes: 6 additions & 6 deletions yazi-adapter/src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ impl Emulator {
Self::Unknown(adapters) => adapters,
Self::Kitty => vec![Adapter::Kitty],
Self::Konsole => vec![Adapter::KittyOld],
Self::Iterm2 => vec![Adapter::Iterm2, Adapter::Sixel],
Self::WezTerm => vec![Adapter::Iterm2, Adapter::Sixel],
Self::Iterm2 => vec![Adapter::Iip, Adapter::Sixel],
Self::WezTerm => vec![Adapter::Iip, Adapter::Sixel],
Self::Foot => vec![Adapter::Sixel],
Self::Ghostty => vec![Adapter::Kitty],
Self::Microsoft => vec![Adapter::Sixel],
Self::BlackBox => vec![Adapter::Sixel],
Self::VSCode => vec![Adapter::Iterm2, Adapter::Sixel],
Self::Tabby => vec![Adapter::Iterm2, Adapter::Sixel],
Self::Hyper => vec![Adapter::Iterm2, Adapter::Sixel],
Self::Mintty => vec![Adapter::Iterm2],
Self::VSCode => vec![Adapter::Iip, Adapter::Sixel],
Self::Tabby => vec![Adapter::Iip, Adapter::Sixel],
Self::Hyper => vec![Adapter::Iip, Adapter::Sixel],
Self::Mintty => vec![Adapter::Iip],
Self::Neovim => vec![],
Self::Apple => vec![],
Self::Urxvt => vec![],
Expand Down
29 changes: 18 additions & 11 deletions yazi-adapter/src/iterm2.rs → yazi-adapter/src/iip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ use std::{io::Write, path::Path};
use anyhow::Result;
use base64::{engine::{general_purpose::STANDARD, Config}, Engine};
use crossterm::{cursor::MoveTo, queue};
use image::{codecs::jpeg::JpegEncoder, DynamicImage};
use image::{codecs::{jpeg::JpegEncoder, png::PngEncoder}, DynamicImage, ExtendedColorType, ImageEncoder};
use ratatui::layout::Rect;
use yazi_config::PREVIEW;

use super::image::Image;
use crate::{adapter::Adapter, Emulator, CLOSE, START};

pub(super) struct Iterm2;
pub(super) struct Iip;

impl Iterm2 {
impl Iip {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;

Adapter::Iterm2.image_hide()?;
Adapter::Iip.image_hide()?;
Adapter::shown_store(area);
Emulator::move_lock((max.x, max.y), |stderr| {
stderr.write_all(&b)?;
Expand All @@ -38,20 +39,26 @@ impl Iterm2 {

async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
tokio::task::spawn_blocking(move || {
let mut jpg = vec![];
JpegEncoder::new_with_quality(&mut jpg, 75).encode_image(&img)?;
let (w, h) = (img.width(), img.height());

let len = base64::encoded_len(jpg.len(), STANDARD.config().encode_padding());
let mut b = vec![];
if img.color().has_alpha() {
PngEncoder::new(&mut b).write_image(&img.into_rgba8(), w, h, ExtendedColorType::Rgba8)?;
} else {
JpegEncoder::new_with_quality(&mut b, PREVIEW.image_quality).encode_image(&img)?;
};

let len = base64::encoded_len(b.len(), STANDARD.config().encode_padding());
let mut buf = Vec::with_capacity(200 + len.unwrap_or(1 << 16));

write!(
buf,
"{}]1337;File=inline=1;size={};width={}px;height={}px;doNotMoveCursor=1:{}\x07{}",
START,
jpg.len(),
img.width(),
img.height(),
STANDARD.encode(&jpg),
b.len(),
w,
h,
STANDARD.encode(b),
CLOSE
)?;
Ok(buf)
Expand Down
30 changes: 17 additions & 13 deletions yazi-adapter/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{fs::File, io::BufReader, path::{Path, PathBuf}};

use anyhow::Result;
use exif::{In, Tag};
use image::{codecs::jpeg::JpegEncoder, imageops::{self, FilterType}, DynamicImage, Limits};
use image::{codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::{self, FilterType}, DynamicImage, ExtendedColorType, ImageEncoder, ImageError, Limits};
use ratatui::layout::Rect;
use yazi_config::{PREVIEW, TASKS};

Expand Down Expand Up @@ -30,19 +30,23 @@ impl Image {
img = img.resize(w, h, Self::filter());
}

let mut buf = Vec::new();
img = Self::rotate(img, orientation);
if !matches!(img, DynamicImage::ImageRgb8(_)) {
img = DynamicImage::ImageRgb8(img.into_rgb8());

if img.color().has_alpha() {
let rgba = img.into_rgba8();
PngEncoder::new(&mut buf).write_image(
&rgba,
rgba.width(),
rgba.height(),
ExtendedColorType::Rgba8,
)?;
} else {
JpegEncoder::new_with_quality(&mut buf, PREVIEW.image_quality)
.encode_image(&img.into_rgb8())?;
}

let mut buf = Vec::new();
JpegEncoder::new_with_quality(&mut buf, PREVIEW.image_quality).encode(
img.as_bytes(),
img.width(),
img.height(),
img.color().into(),
)?;
Ok::<_, anyhow::Error>(buf)
Ok::<_, ImageError>(buf)
})
.await??;

Expand Down Expand Up @@ -138,7 +142,7 @@ impl Image {

// https://magnushoff.com/articles/jpeg-orientation/
fn rotate(mut img: DynamicImage, orientation: u8) -> DynamicImage {
let rgba = img.color().has_alpha();
let alpha = img.color().has_alpha();
img = match orientation {
2 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&img)),
3 => DynamicImage::ImageRgba8(imageops::rotate180(&img)),
Expand All @@ -149,7 +153,7 @@ impl Image {
8 => DynamicImage::ImageRgba8(imageops::rotate270(&img)),
_ => img,
};
if !rgba {
if !alpha {
img = DynamicImage::ImageRgb8(img.into_rgb8());
}
img
Expand Down
2 changes: 1 addition & 1 deletion yazi-adapter/src/kitty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ impl Kitty {
tokio::task::spawn_blocking(move || match img {
DynamicImage::ImageRgb8(v) => output(v.as_raw(), 24, size),
DynamicImage::ImageRgba8(v) => output(v.as_raw(), 32, size),
v => output(v.to_rgb8().as_raw(), 24, size),
v => output(v.into_rgb8().as_raw(), 24, size),
})
.await?
}
Expand Down
2 changes: 1 addition & 1 deletion yazi-adapter/src/kitty_old.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl KittyOld {
tokio::task::spawn_blocking(move || match img {
DynamicImage::ImageRgb8(v) => output(v.as_raw(), 24, size),
DynamicImage::ImageRgba8(v) => output(v.as_raw(), 32, size),
v => output(v.to_rgb8().as_raw(), 24, size),
v => output(v.into_rgb8().as_raw(), 24, size),
})
.await?
}
Expand Down
4 changes: 2 additions & 2 deletions yazi-adapter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ mod adapter;
mod chafa;
mod dimension;
mod emulator;
mod iip;
mod image;
mod iterm2;
mod kitty;
mod kitty_old;
mod sixel;
Expand All @@ -15,7 +15,7 @@ pub use adapter::*;
use chafa::*;
pub use dimension::*;
pub use emulator::*;
use iterm2::*;
use iip::*;
use kitty::*;
use kitty_old::*;
use sixel::*;
Expand Down

0 comments on commit 0acf5c2

Please sign in to comment.