perf: reduce peak memory footprint during decoding large images (#375)

This commit is contained in:
三咲雅 · Misaki Masa 2023-11-17 12:06:07 +08:00 committed by GitHub
parent ab7acfec5c
commit 6a64b162be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 22 deletions

View file

@ -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;
@ -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?
}
result.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??;
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?
}
}

View file

@ -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())
@ -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?)
}

View file

@ -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) => {