1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
use anyhow::Context;
use jellybase::{cache_file, AssetLocationExt};
use jellycommon::AssetLocation;
use log::{debug, info};
use rgb::FromSlice;
use std::{
fs::File,
io::{BufReader, Write},
path::PathBuf,
};
pub fn transcode(
asset: AssetLocation,
quality: f32,
speed: u8,
width: usize,
) -> anyhow::Result<PathBuf> {
let original_path = asset.path();
let path = cache_file(&[
original_path.as_os_str().to_str().unwrap(),
&format!("{width} {quality} {speed}"),
])
.path();
if !path.exists() {
info!("encoding {path:?} (speed={speed}, quality={quality}, width={width})");
// TODO shouldn't be neccessary with guessed format.
let file = BufReader::new(File::open(&original_path).context("opening source")?);
let mut reader = image::io::Reader::new(file);
reader.set_format(image::ImageFormat::Avif);
let reader = reader.with_guessed_format().context("guessing format")?;
debug!("guessed format (or fallback): {:?}", reader.format());
let original = reader.decode().context("decoding image")?.to_rgba8();
let image = image::imageops::resize(
&original,
width as u32,
width as u32 * original.height() / original.width(),
image::imageops::FilterType::Lanczos3,
);
let pixels = image.to_vec();
let encoded = ravif::Encoder::new()
.with_speed(speed.clamp(1, 10))
.with_quality(quality.clamp(1., 100.))
.encode_rgba(imgref::Img::new(
pixels.as_rgba(),
image.width() as usize,
image.height() as usize,
))?;
info!("transcode finished");
File::create(&path)?.write_all(&encoded.avif_file)?;
}
Ok(path)
}
|