Skip to content

Commit 3bed2f7

Browse files
committed
Fix comments:
- use image, remove separate svg logic, - clean yazi-default.toml, but make it configurable with lua plugin.
1 parent 98cce71 commit 3bed2f7

File tree

19 files changed

+207
-349
lines changed

19 files changed

+207
-349
lines changed

yazi-adapter/src/adapter.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,6 @@ impl Adapter {
5050
}
5151
}
5252

53-
pub async fn svg_show(self, path: &Path, max: Rect) -> Result<Rect> {
54-
if max.is_empty() {
55-
return Ok(Rect::default());
56-
}
57-
58-
match self {
59-
Self::Kgp => drivers::Kgp::svg_show(path, max).await,
60-
Self::KgpOld => drivers::KgpOld::svg_show(path, max).await,
61-
Self::Iip => drivers::Iip::svg_show(path, max).await,
62-
Self::Sixel => drivers::Sixel::svg_show(path, max).await,
63-
Self::X11 | Self::Wayland => drivers::Ueberzug::svg_show(path, max).await,
64-
Self::Chafa => drivers::Chafa::svg_show(path, max).await,
65-
}
66-
}
67-
6853
pub fn image_hide(self) -> Result<()> {
6954
if let Some(area) = SHOWN.replace(None) { self.image_erase(area) } else { Ok(()) }
7055
}

yazi-adapter/src/drivers/chafa.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,6 @@ impl Chafa {
6464
})
6565
}
6666

67-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
68-
Self::image_show(path, max).await
69-
}
70-
7167
pub(crate) fn image_erase(area: Rect) -> Result<()> {
7268
let s = " ".repeat(area.width as usize);
7369
Emulator::move_lock((0, 0), |w| {

yazi-adapter/src/drivers/iip.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,13 @@ use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEn
77
use ratatui::layout::Rect;
88
use yazi_config::YAZI;
99

10-
use crate::{CLOSE, Emulator, Image, START, Svg, adapter::Adapter};
10+
use crate::{CLOSE, Emulator, Image, START, adapter::Adapter};
1111

1212
pub(crate) struct Iip;
1313

1414
impl Iip {
1515
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
1616
let img = Image::downscale(path, max).await?;
17-
Self::draw_image(img, max).await
18-
}
19-
20-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
21-
let img = Svg::downscale(path, max).await?;
22-
Self::draw_image(img, max).await
23-
}
24-
25-
async fn draw_image(img: DynamicImage, max: Rect) -> Result<Rect> {
2617
let area = Image::pixel_area((img.width(), img.height()), max);
2718
let b = Self::encode(img).await?;
2819

yazi-adapter/src/drivers/kgp.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crossterm::{cursor::MoveTo, queue};
77
use image::DynamicImage;
88
use ratatui::layout::Rect;
99

10-
use crate::{CLOSE, ESCAPE, Emulator, START, Svg, adapter::Adapter, image::Image};
10+
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter, image::Image};
1111

1212
static DIACRITICS: [char; 297] = [
1313
'\u{0305}',
@@ -314,15 +314,6 @@ pub(crate) struct Kgp;
314314
impl Kgp {
315315
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
316316
let img = Image::downscale(path, max).await?;
317-
Self::draw_image(img, max).await
318-
}
319-
320-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
321-
let img = Svg::downscale(path, max).await?;
322-
Self::draw_image(img, max).await
323-
}
324-
325-
async fn draw_image(img: DynamicImage, max: Rect) -> Result<Rect> {
326317
let area = Image::pixel_area((img.width(), img.height()), max);
327318

328319
let b1 = Self::encode(img).await?;

yazi-adapter/src/drivers/kgp_old.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,13 @@ use image::DynamicImage;
77
use ratatui::layout::Rect;
88
use yazi_shared::tty::TTY;
99

10-
use crate::{CLOSE, ESCAPE, Emulator, Image, START, Svg, adapter::Adapter};
10+
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
1111

1212
pub(crate) struct KgpOld;
1313

1414
impl KgpOld {
1515
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
1616
let img = Image::downscale(path, max).await?;
17-
Self::draw_image(img, max).await
18-
}
19-
20-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
21-
let img = Svg::downscale(path, max).await?;
22-
Self::draw_image(img, max).await
23-
}
24-
25-
async fn draw_image(img: DynamicImage, max: Rect) -> Result<Rect> {
2617
let area = Image::pixel_area((img.width(), img.height()), max);
2718
let b = Self::encode(img).await?;
2819

yazi-adapter/src/drivers/sixel.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,13 @@ use image::DynamicImage;
77
use ratatui::layout::Rect;
88
use yazi_config::YAZI;
99

10-
use crate::{CLOSE, ESCAPE, Emulator, Image, START, Svg, adapter::Adapter};
10+
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
1111

1212
pub(crate) struct Sixel;
1313

1414
impl Sixel {
1515
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
1616
let img = Image::downscale(path, max).await?;
17-
Self::draw_image(img, max).await
18-
}
19-
20-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
21-
let img = Svg::downscale(path, max).await?;
22-
Self::draw_image(img, max).await
23-
}
24-
25-
async fn draw_image(img: DynamicImage, max: Rect) -> Result<Rect> {
2617
let area = Image::pixel_area((img.width(), img.height()), max);
2718
let b = Self::encode(img).await?;
2819

yazi-adapter/src/drivers/ueberzug.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ impl Ueberzug {
6767
Ok(area)
6868
}
6969

70-
pub(crate) async fn svg_show(path: &Path, max: Rect) -> Result<Rect> {
71-
Self::image_show(path, max).await
72-
}
73-
7470
pub(crate) fn image_erase(_: Rect) -> Result<()> {
7571
if let Some(tx) = &*DEMON {
7672
Ok(tx.send(None)?)

yazi-adapter/src/image.rs

Lines changed: 125 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
use std::path::{Path, PathBuf};
1+
use std::{path::{Path, PathBuf}, sync::{Arc, LazyLock, RwLock}};
22

33
use anyhow::Result;
4-
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
4+
use image::{DynamicImage, ExtendedColorType, ImageBuffer, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, error::UnsupportedErrorKind, imageops::FilterType, metadata::Orientation};
55
use ratatui::layout::Rect;
6+
use resvg::{tiny_skia::{Pixmap, Transform}, usvg::{Options, Tree, fontdb::Database}};
67
use yazi_config::YAZI;
78

89
use crate::Dimension;
910

11+
pub static GLOBAL_OPTIONS: LazyLock<RwLock<Options<'static>>> =
12+
LazyLock::new(|| RwLock::new(Options::default()));
13+
1014
pub struct Image;
1115

1216
impl Image {
1317
pub async fn precache(path: &Path, cache: PathBuf) -> Result<()> {
14-
let (mut img, orientation, icc) = Self::decode_from(path).await?;
15-
let (w, h) = Self::flip_size(orientation, (YAZI.preview.max_width, YAZI.preview.max_height));
18+
let (img, icc) =
19+
Self::decode_to_fit_from(path, YAZI.preview.max_width, YAZI.preview.max_height).await?;
1620

1721
let buf = tokio::task::spawn_blocking(move || {
18-
if img.width() > w || img.height() > h {
19-
img = img.resize(w, h, Self::filter());
20-
}
21-
if orientation != Orientation::NoTransforms {
22-
img.apply_orientation(orientation);
23-
}
24-
2522
let mut buf = Vec::new();
2623
if img.color().has_alpha() {
2724
let rgba = img.into_rgba8();
@@ -42,25 +39,8 @@ impl Image {
4239
}
4340

4441
pub(super) async fn downscale(path: &Path, rect: Rect) -> Result<DynamicImage> {
45-
let (mut img, orientation, _) = Self::decode_from(path).await?;
46-
let (w, h) = Self::flip_size(orientation, Self::max_pixel(rect));
47-
48-
// Fast path.
49-
if img.width() <= w && img.height() <= h && orientation == Orientation::NoTransforms {
50-
return Ok(img);
51-
}
52-
53-
let img = tokio::task::spawn_blocking(move || {
54-
if img.width() > w || img.height() > h {
55-
img = img.resize(w, h, Self::filter())
56-
}
57-
if orientation != Orientation::NoTransforms {
58-
img.apply_orientation(orientation);
59-
}
60-
img
61-
})
62-
.await?;
63-
42+
let (width, height) = Self::max_pixel(rect);
43+
let (img, _) = Self::decode_to_fit_from(path, width, height).await?;
6444
Ok(img)
6545
}
6646

@@ -96,7 +76,54 @@ impl Image {
9676
}
9777
}
9878

99-
async fn decode_from(path: &Path) -> ImageResult<(DynamicImage, Orientation, Option<Vec<u8>>)> {
79+
async fn decode_to_fit_from(
80+
path: &Path,
81+
width: u32,
82+
height: u32,
83+
) -> Result<(DynamicImage, Option<Vec<u8>>)> {
84+
let path = path.to_owned();
85+
86+
tokio::task::spawn_blocking(move || {
87+
Self::try_decode_raster(&path, width, height).or_else(|err| match err {
88+
ImageError::Unsupported(ref unsupported) => match unsupported.kind() {
89+
UnsupportedErrorKind::Format(_) => Self::try_decode_svg(&path, width, height),
90+
_ => Err(err.into()),
91+
},
92+
_ => Err(err.into()),
93+
})
94+
})
95+
.await
96+
.map_err(|e| ImageError::IoError(e.into()))?
97+
}
98+
99+
#[inline]
100+
fn try_decode_raster(
101+
path: &Path,
102+
width: u32,
103+
height: u32,
104+
) -> ImageResult<(DynamicImage, Option<Vec<u8>>)> {
105+
let limits = Self::build_limits();
106+
let mut reader = ImageReader::open(path)?;
107+
reader.limits(limits);
108+
let mut decoder = reader.with_guessed_format()?.into_decoder()?;
109+
let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms);
110+
let icc = decoder.icc_profile().unwrap_or_default();
111+
112+
let mut img = DynamicImage::from_decoder(decoder)?;
113+
let (w, h) = Self::flip_size(orientation, (width, height));
114+
115+
if img.width() > w || img.height() > h {
116+
img = img.resize(w, h, Self::filter());
117+
}
118+
if orientation != Orientation::NoTransforms {
119+
img.apply_orientation(orientation);
120+
}
121+
122+
Ok((img, icc))
123+
}
124+
125+
#[inline]
126+
fn build_limits() -> Limits {
100127
let mut limits = Limits::no_limits();
101128
if YAZI.tasks.image_alloc > 0 {
102129
limits.max_alloc = Some(YAZI.tasks.image_alloc as u64);
@@ -107,20 +134,76 @@ impl Image {
107134
if YAZI.tasks.image_bound[1] > 0 {
108135
limits.max_image_height = Some(YAZI.tasks.image_bound[1] as u32);
109136
}
137+
limits
138+
}
110139

111-
let path = path.to_owned();
112-
tokio::task::spawn_blocking(move || {
113-
let mut reader = ImageReader::open(path)?;
114-
reader.limits(limits);
140+
#[inline]
141+
fn try_decode_svg(
142+
path: &Path,
143+
width: u32,
144+
height: u32,
145+
) -> Result<(DynamicImage, Option<Vec<u8>>)> {
146+
let pixmap = Self::render_svg_to_fit_from(path, width, height)?;
147+
let (width, height) = (pixmap.width(), pixmap.height());
148+
let mut container = pixmap.take();
149+
150+
for rgba in container.chunks_exact_mut(4) {
151+
let alpha = rgba[3];
152+
if alpha != 0xff {
153+
let pixel: &mut [u8; 4] = unsafe { rgba.try_into().unwrap_unchecked() };
154+
let a = alpha as f64 / 255.0;
155+
pixel[0] = (pixel[0] as f64 / a + 0.5) as u8;
156+
pixel[1] = (pixel[1] as f64 / a + 0.5) as u8;
157+
pixel[2] = (pixel[2] as f64 / a + 0.5) as u8;
158+
}
159+
}
115160

116-
let mut decoder = reader.with_guessed_format()?.into_decoder()?;
117-
let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms);
118-
let icc = decoder.icc_profile().unwrap_or_default();
161+
let img = ImageBuffer::from_raw(width, height, container)
162+
.ok_or_else(|| anyhow::anyhow!("Failed to create image buffer"))?;
119163

120-
Ok((DynamicImage::from_decoder(decoder)?, orientation, icc))
121-
})
122-
.await
123-
.map_err(|e| ImageError::IoError(e.into()))?
164+
Ok((DynamicImage::ImageRgba8(img), None))
165+
}
166+
167+
/// Helper function to rasterize an SVG to a pixmap fitting within max_width
168+
/// and max_height.
169+
fn render_svg_to_fit_from(path: &Path, max_width: u32, max_height: u32) -> Result<Pixmap> {
170+
let svg = std::fs::read(path)?;
171+
let options_guard =
172+
GLOBAL_OPTIONS.read().map_err(|e| anyhow::anyhow!("RwLock poisoned: {}", e))?;
173+
let tree = Tree::from_data(&svg, &options_guard)?;
174+
let (width, height, transform) = Self::svg_size_and_scale(&tree, max_width, max_height);
175+
176+
let mut pixmap =
177+
Pixmap::new(width, height).ok_or_else(|| anyhow::anyhow!("Cannot create pixmap"))?;
178+
179+
resvg::render(&tree, transform, &mut pixmap.as_mut());
180+
181+
Ok(pixmap)
182+
}
183+
184+
fn svg_size_and_scale(tree: &Tree, max_width: u32, max_height: u32) -> (u32, u32, Transform) {
185+
// It is Ok. The max_width and max_height could not be larger then monitor
186+
// dimensions which much less then f32::MAX
187+
let max_width = max_width as f32;
188+
let max_height = max_height as f32;
189+
let mut width = tree.size().width();
190+
let mut height = tree.size().height();
191+
192+
for node in tree.root().children() {
193+
if let Some(bounding_box) = node.abs_layer_bounding_box() {
194+
width = width.max(bounding_box.width());
195+
height = height.max(bounding_box.height());
196+
}
197+
}
198+
if width <= max_width && height <= max_height {
199+
return (width.floor() as u32, height.floor() as u32, Transform::from_scale(1.0, 1.0));
200+
}
201+
let ratio = f32::min(max_width / width, max_height / height);
202+
(
203+
(width * ratio).floor() as u32,
204+
(height * ratio).floor() as u32,
205+
Transform::from_scale(ratio, ratio),
206+
)
124207
}
125208

126209
fn flip_size(orientation: Orientation, (w, h): (u32, u32)) -> (u32, u32) {

yazi-adapter/src/info.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{collections::HashSet, path::Path};
33
use image::{ImageDecoder, ImageError};
44
use resvg::usvg::{Font, Group, ImageKind, Node, Tree};
55

6-
use super::svg::GLOBAL_FONTDB;
6+
use super::image::GLOBAL_OPTIONS;
77

88
pub type ImageFormat = image::ImageFormat;
99
pub type ImageColor = image::ColorType;
@@ -59,8 +59,9 @@ impl SvgInfo {
5959
let path = path.to_owned();
6060
tokio::task::spawn_blocking(move || {
6161
let svg = std::fs::read(path)?;
62-
63-
let tree = Tree::from_data(&svg, &GLOBAL_FONTDB)?;
62+
let options_guard =
63+
GLOBAL_OPTIONS.read().map_err(|e| anyhow::anyhow!("RwLock poisoned: {}", e))?;
64+
let tree = Tree::from_data(&svg, &options_guard)?;
6465

6566
let mut info = SvgInfo {
6667
width: tree.size().width().round(),

yazi-adapter/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
yazi_macro::mod_pub!(drivers);
44

5-
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux svg unknown);
5+
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
66

77
use yazi_shared::{SyncCell, in_wsl};
88

0 commit comments

Comments
 (0)