Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support transparent image preview #1556

Merged
merged 2 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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