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 anyhow::Result;
use image::{imageops::FilterType, DynamicImage, ImageFormat}; use image::{imageops::FilterType, DynamicImage, ImageFormat};
use tokio::fs;
use yazi_config::PREVIEW; use yazi_config::PREVIEW;
use yazi_shared::Term; use yazi_shared::Term;
@ -17,32 +16,51 @@ impl Image {
}) })
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height)); .unwrap_or((PREVIEW.max_width, PREVIEW.max_height));
let img = fs::read(path).await?; let path = path.to_owned();
let img = tokio::task::spawn_blocking(move || -> Result<DynamicImage> { let img = tokio::task::spawn_blocking(move || {
let img = image::load_from_memory(&img)?; image::io::Reader::open(path)?.with_guessed_format()?.decode()
})
.await??;
tokio::task::spawn_blocking(move || {
Ok(if img.width() > w || img.height() > h { Ok(if img.width() > w || img.height() > h {
img.resize(w, h, FilterType::Triangle) img.resize(w, h, FilterType::Triangle)
} else { } else {
img img
}) })
}); })
.await?
img.await?
} }
pub async fn precache(img: Arc<Vec<u8>>, cache: impl AsRef<Path>) -> Result<()> { pub async fn precache(path: &Path, cache: PathBuf) -> Result<()> {
let cache = cache.as_ref().to_owned(); let path = path.to_owned();
let result = tokio::task::spawn_blocking(move || { let img = tokio::task::spawn_blocking(move || {
let img = image::load_from_memory(&img)?; image::io::Reader::open(path)?.with_guessed_format()?.decode()
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height); })
.await??;
tokio::task::spawn_blocking(move || {
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height);
Ok(match img.resize(w, h, FilterType::Triangle) { Ok(match img.resize(w, h, FilterType::Triangle) {
DynamicImage::ImageRgb8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg), DynamicImage::ImageRgb8(buf) => buf.save_with_format(cache, ImageFormat::Jpeg),
DynamicImage::ImageRgba8(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 regex::Regex;
use tokio::process::Command; use tokio::process::Command;
use yazi_adaptor::Image; use yazi_adaptor::Image;
use yazi_shared::PeekError; 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") let output = Command::new("pdftoppm")
.args(["-singlefile", "-jpeg", "-jpegopt", "quality=75", "-f"]) .args(["-singlefile", "-jpeg", "-jpegopt", "quality=75", "-f"])
.arg((skip + 1).to_string()) .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()) }; 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() { if fs::symlink_metadata(&cache).await.is_ok() {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?); return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
} }
if let Ok(img) = fs::read(&task.target).await { Image::precache(&task.target, cache).await.ok();
Image::precache(Arc::new(img), cache).await.ok();
}
self.sch.send(TaskOp::Adv(task.id, 1, 0))?; self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
} }
PrecacheOp::Video(task) => { PrecacheOp::Video(task) => {