背景

之前博客中出现的图片逐步从PNG和JPG迁移到了webp格式,处理webp格式图片的代码在webp-image这个项目中。

最近看到各个平台都在拥抱avif格式,比如内存峰值降60%+,动图加载快75%:爱奇艺图片库一次从'能用'到'极致'的跨越下一代图片格式 AVIF 在 vivo 社区的落地实践,所以想尝试一下使用avif格式来处理图片,看看能不能进一步优化图片的体积和加载速度。

这次因为也没多少行代码,不想再创建一个新的github项目了,直接帖在博客里了,后续有时间再创建一个项目。

处理图片为avif的源码

[package]
name = "avif-image"
version = "0.1.0"
edition = "2024"

[dependencies]
arboard = "3.6.1"
image = "0.25.6"
uuid = { version = "1.18.0", features = ["v4"] }
ravif = "0.13.0"
rgb = "0.8"

[profile.dev]
panic = "abort"

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true

use arboard::Clipboard;
use image::DynamicImage;
use ravif::{Encoder, Img};
use rgb::RGBA8;
use std::fs;
use std::path::Path;

fn encode_and_save_avif(dyn_img: &DynamicImage, avif_path: &Path) {
    let rgba = dyn_img.to_rgba8();
    let width = rgba.width() as usize;
    let height = rgba.height() as usize;
    let pixels: Vec<RGBA8> = rgba
        .pixels()
        .map(|p| RGBA8 {
            r: p[0],
            g: p[1],
            b: p[2],
            a: p[3],
        })
        .collect();

    let result = Encoder::new()
        .with_quality(80.0)
        .with_speed(6)
        .encode_rgba(Img::new(&pixels, width, height))
        .expect("avif编码失败");

    fs::write(avif_path, &result.avif_file).expect("写入avif文件失败");
}

fn main() {
    let mut clipboard = Clipboard::new().expect("无法访问粘贴板");

    // 1. 优先尝试读取文件路径
    if let Ok(text) = clipboard.get().file_list() {
        if !text.is_empty() {
            let path = text[0].clone();
            if path.exists() && path.is_file() {
                if let Ok(dyn_img) = image::open(&path) {
                    let avif_path = path.with_extension("avif");
                    encode_and_save_avif(&dyn_img, &avif_path);
                    println!("已保存: {}", avif_path.display());
                    return;
                } else {
                    println!("文件不是有效图片: {}", path.display());
                    return;
                }
            }
        }
    }

    // 2. 尝试读取粘贴板中的图片内容
    if let Ok(img_data) = clipboard.get_image() {
        let width = img_data.width as u32;
        let height = img_data.height as u32;
        let bytes = img_data.bytes.into_owned();
        let dyn_img = DynamicImage::ImageRgba8(
            image::RgbaImage::from_vec(width, height, bytes).expect("图片数据无效"),
        );
        let uuid = uuid::Uuid::new_v4().to_string();
        let avif_filename = format!("{}.avif", &uuid[..6]);
        let avif_path = Path::new(&avif_filename);
        encode_and_save_avif(&dyn_img, avif_path);
        println!("已保存: {}", avif_filename);
        return;
    }

    println!("粘贴板无图片内容,也无有效图片文件路径");
}