Firefox も version 92.0 で AVIF をサポートするということで Windows 環境の Rust で AVIF を生成してみたいと思います。
image crate
image crate が AVIF をサポートしているので使ってみます。↓こんな感じで書いてみました。JPEG -> AVIF 変換しています。
[package]
name = "avif_test"
version = "0.1.0"
authors = ["benki"]
edition = "2018"
[dependencies]
anyhow = "1.0"
[dependencies.image]
version = "0.23"
default-features = false
features = [
"jpeg",
"avif-encoder"
]
use anyhow::Result;
fn main() -> Result<()> {
let img = image::open(r"C:\Users\benki\Desktop\test.jpg")?;
img.save_with_format(
r"C:\Users\benki\Desktop\test.avif",
image::ImageFormat::Avif,
)?;
Ok(())
}
cargo run --release
すると…
error: failed to run custom build command for `rav1e v0.4.1`
Caused by:
process didn't exit successfully: `C:\Users\benki\Documents\rust_projects\avif_test\target\release\build\rav1e-595ba7b94a49d3bb\build-script-build` (exit code: 101)
--- stdout
cargo:rustc-cfg=nasm_x86_64
--- stderr
thread 'main' panicked at 'This version of NASM is too old: 指定されたファイルが見つかりません。 (os error 2)', C:\Users\benki\.cargo\registry\src\github.com-1ecc6299db9ec823\rav1e-0.4.1\build.rs:251:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed
エラーが出ました。NASM が必要らしいのでインストールします。
NASM インストール(不要)
インストール手順は zip をダウンロードして解凍してパスを通して再起動です。これでビルドできるでしょう。cargo run --release
すると…
error: failed to run custom build command for `rav1e v0.4.1`
Caused by:
process didn't exit successfully: `C:\Users\benki\Documents\rust_projects\avif_test\target\release\build\rav1e-595ba7b94a49d3bb\build-script-build` (exit code: 101)
--- stdout
cargo:rustc-cfg=nasm_x86_64
running: "nasm" "-fwin64" "-IC:\\Users\\benki\\Documents\\rust_projects\\avif_test\\target\\release\\build\\rav1e-24085274cf597553\\out/" "-Isrc/" "C:\\Users\\benki\\.cargo\\registry\\src\\github.com-1ecc6299db9ec823\\rav1e-0.4.1\\src/x86/mc_sse.asm" "-o" "C:\\Users\\benki\\Documents\\rust_projects\\avif_test\\target\\release\\build\\rav1e-24085274cf597553\\out\\src/x86/mc_sse.o"
(中略)
--- stderr
Some((2, 14, 0)) "NASM version 2.15.05 compiled on Aug 28 2020\r\n" (2, 15, 5)
C:\Users\benki\.cargo\registry\src\github.com-1ecc6299db9ec823\rav1e-0.4.1\src/x86/sse.asm:446: warning: dropping trailing empty parameter in call to multi-line macro `LOAD_SCALES_8X4' [-w+macro-params-legacy]
(中略)
C:\Users\benki\.cargo\registry\src\github.com-1ecc6299db9ec823\rav1e-0.4.1\src/x86/tables.asm:534: warning: dropping trailing empty parameter in call to multi-line macro `const' [-w+macro-params-legacy]
thread 'main' panicked at 'NASM build failed. Make sure you have nasm installed. https://nasm.us: "failed to spawn process: 指定されたフ
ァイルが見つかりません。 (os error 2)"', C:\Users\benki\.cargo\registry\src\github.com-1ecc6299db9ec823\rav1e-0.4.1\build.rs:116:6
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
だめだー!
パス文字列がダメなのかと思って、rav1e の build.rs を弄ってみましたがやっぱりビルドできないので、asm feature を使わないことにします。
[patch]
image crate は AVIF エンコーダとして、ravif を使用しています。ravif は rav1e に依存しています。ravif が rav1e の asm feature を使用しているので、これを無効にします。
sub dependencies のfeatures
をどうやって設定するのか悩んだのですが、Cargo.toml の[patch]
を使って ravif の Cargo.toml を書き換えることにしました。
まず ravif-0.6.4 のソースコードを丸ごと src フォルダにコピー。src/ravif-0.6.4/Cargo.toml の 56~57 行目をコメントアウトします。
[features]
# asm = ["rav1e/asm"]
# default = ["asm"]
そして、avif_test の Cargo.toml に以下を追加。
[patch.crates-io]
ravif = { path = "src/ravif-0.6.4" }
これで crates.io リポジトリの ravif ではなく、ローカルのものが使用されます。便利。cargo run --release
すると…
Compiling avif_test v0.1.0 (C:\Users\benki\Documents\rust_projects\avif_test)
Finished release [optimized] target(s) in 2.56s
Running `target\release\avif_test.exe`
NASM なしでビルドできました。無事 test.avif も生成されました。やったぜ。
でも何やら様子がおかしい…
test.jpg | test.avif |
---|---|
204 KB | 566 KB |
元の JPEG よりファイルサイズ大きくなってるんですが?
どうやら image crate のsave_with_format(path, image::ImageFormat::Avif)
は最高品質で AVIF を生成するようです。
もっと圧縮
ということで、もっとファイルサイズを圧縮するコードを書きました。全部まとめてどうぞ。
[package]
name = "avif_test"
version = "0.1.0"
authors = ["benki"]
edition = "2018"
[dependencies]
anyhow = "1.0"
[dependencies.image]
version = "0.23"
default-features = false
features = [
"jpeg",
"avif-encoder"
]
[patch.crates-io]
ravif = { path = "src/ravif-0.6.4" }
[features]
# asm = ["rav1e/asm"]
# default = ["asm"]
use anyhow::{Context, Result};
use image::{avif::AvifEncoder, ColorType::Rgb8, GenericImageView};
use std::fs::File;
use std::io::BufWriter;
fn main() -> Result<()> {
let img = image::open(r"C:\Users\benki\Desktop\test.JPG")?;
let data = img.as_rgb8().context("no rgb8")?.to_vec();
let fout = &mut BufWriter::new(File::create(r"C:\Users\benki\Desktop\test.avif")?);
// Create a new encoder with specified speed and quality, that writes its output to `w`.
// `speed` accepts a value in the range 0-10, where 0 is the slowest and 10 is the fastest.
// `quality` accepts a value in the range 0-100, where 0 is the worst and 100 is the best.
// pub fn new_with_speed_quality(w: W, speed: u8, quality: u8) -> Self
AvifEncoder::new_with_speed_quality(fout, 5, 60).write_image(
&data,
img.width(),
img.height(),
Rgb8,
)?;
Ok(())
}
cargo run --release
すると…
test.jpg | test.avif |
---|---|
204 KB | 126 KB |
けっこう小さくなりました。new_with_speed_quality
のspeed
とquality
の値を変えて画質とファイルサイズのバランスを調整すると良いでしょう。speed
を小さくすると画質はよくなりますが、画像の生成に現実的でないくらいの時間が掛かります。何事もバランスが大事です。
さて肝心の Firefox でのサポート状況なんですが、生成した test.avif を Firefox で開いても「データが壊れている」とエラーが出て表示できませんでした…。VSCode だとちゃんと表示されるんですけどねぇ。
Netflix の AVIF サンプルも Firefox ではエラーで表示できないので、Firefox のデコーダがおかしいのかもしれません…。おわり。