Skip to content

Commit

Permalink
perf: reduce peak memory footprint during decoding large images (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyazi authored Nov 17, 2023
1 parent ab7acfe commit 6a64b16
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 22 deletions.
50 changes: 34 additions & 16 deletions yazi-adaptor/src/image.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::{path::Path, sync::Arc};
use std::path::{Path, PathBuf};

use anyhow::Result;
use image::{imageops::FilterType, DynamicImage, ImageFormat};
use tokio::fs;
use yazi_config::PREVIEW;
use yazi_shared::Term;

Expand All @@ -17,32 +16,51 @@ impl Image {
})
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height));

let img = fs::read(path).await?;
let img = tokio::task::spawn_blocking(move || -> Result<DynamicImage> {
let img = image::load_from_memory(&img)?;
let path = path.to_owned();
let img = tokio::task::spawn_blocking(move || {
image::io::Reader::open(path)?.with_guessed_format()?.decode()
})
.await??;

tokio::task::spawn_blocking(move || {
Ok(if img.width() > w || img.height() > h {
img.resize(w, h, FilterType::Triangle)
} else {
img
})
});

img.await?
})
.await?
}

pub async fn precache(img: Arc<Vec<u8>>, cache: impl AsRef<Path>) -> Result<()> {
let cache = cache.as_ref().to_owned();
let result = tokio::task::spawn_blocking(move || {
let img = image::load_from_memory(&img)?;
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height);
pub async fn precache(path: &Path, cache: PathBuf) -> Result<()> {
let path = path.to_owned();
let img = tokio::task::spawn_blocking(move || {
image::io::Reader::open(path)?.with_guessed_format()?.decode()
})
.await??;

tokio::task::spawn_blocking(move || {
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height);
Ok(match img.resize(w, h, FilterType::Triangle) {
DynamicImage::ImageRgb8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg),
DynamicImage::ImageRgba8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg),
buf => buf.to_rgb8().save_with_format(cache, ImageFormat::Jpeg),
buf => buf.into_rgb8().save_with_format(cache, ImageFormat::Jpeg),
}?)
});
})
.await?
}

pub async fn precache_bin(bin: Vec<u8>, cache: PathBuf) -> Result<()> {
let img = tokio::task::spawn_blocking(move || image::load_from_memory(&bin)).await??;

result.await?
tokio::task::spawn_blocking(move || {
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height);
Ok(match img.resize(w, h, FilterType::Triangle) {
DynamicImage::ImageRgb8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg),
DynamicImage::ImageRgba8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg),
buf => buf.into_rgb8().save_with_format(cache, ImageFormat::Jpeg),
}?)
})
.await?
}
}
6 changes: 3 additions & 3 deletions yazi-core/src/external/pdftoppm.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{path::Path, sync::Arc};
use std::path::Path;

use regex::Regex;
use tokio::process::Command;
use yazi_adaptor::Image;
use yazi_shared::PeekError;

pub async fn pdftoppm(src: &Path, dest: impl AsRef<Path>, skip: usize) -> Result<(), PeekError> {
pub async fn pdftoppm(src: &Path, dest: &Path, skip: usize) -> Result<(), PeekError> {
let output = Command::new("pdftoppm")
.args(["-singlefile", "-jpeg", "-jpegopt", "quality=75", "-f"])
.arg((skip + 1).to_string())
Expand All @@ -25,5 +25,5 @@ pub async fn pdftoppm(src: &Path, dest: impl AsRef<Path>, skip: usize) -> Result
return if pages > 0 { Err(PeekError::Exceed(pages - 1)) } else { Err(s.to_string().into()) };
}

Ok(Image::precache(Arc::new(output.stdout), dest).await?)
Ok(Image::precache_bin(output.stdout, dest.to_owned()).await?)
}
4 changes: 1 addition & 3 deletions yazi-core/src/tasks/workers/precache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ impl Precache {
if fs::symlink_metadata(&cache).await.is_ok() {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
}
if let Ok(img) = fs::read(&task.target).await {
Image::precache(Arc::new(img), cache).await.ok();
}
Image::precache(&task.target, cache).await.ok();
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
}
PrecacheOp::Video(task) => {
Expand Down

0 comments on commit 6a64b16

Please sign in to comment.