Skip to main content

media_decode/decode/
mod.rs

1//! 通用图像解码:ImageReader → ICO 宽松 → 色调映射。
2//!
3//! 不含平台 Shell/GDI;应用层(cherry-box)负责最终兜底。
4
5mod bmp;
6pub(crate) mod ffmpeg_decode;
7mod ffmpeg_image;
8pub(crate) mod ffmpeg_log;
9pub(crate) mod ffmpeg_probe;
10mod ico;
11mod j2k;
12mod jxl;
13mod mng;
14mod reader;
15pub mod tone_map;
16
17use std::path::Path;
18use std::sync::Once;
19
20use image::DynamicImage;
21use thiserror::Error;
22
23static REGISTER_EXTRAS: Once = Once::new();
24
25fn ensure_extras_registered() {
26    REGISTER_EXTRAS.call_once(|| {
27        image_extras::register();
28    });
29}
30
31#[derive(Error, Debug)]
32pub enum DecodeError {
33    #[error("无法解码图像")]
34    Unsupported,
35}
36
37/// 尽力解码任意支持的图像文件
38pub fn decode_image(path: &Path) -> Result<DynamicImage, DecodeError> {
39    ensure_extras_registered();
40    #[cfg(feature = "video")]
41    ffmpeg_log::init_ffmpeg_logging();
42    reader::try_decode_reader(path)
43        .or_else(|| jxl::try_decode_jxl(path))
44        .or_else(|| j2k::try_decode_j2k(path))
45        .or_else(|| j2k::try_decode_j2k_rgba(path))
46        .or_else(|| j2k::try_decode_j2k_via_ffmpeg(path))
47        .or_else(|| ico::try_decode_ico(path))
48        .or_else(|| mng::try_decode_mng(path))
49        .or_else(|| ffmpeg_image::try_decode_via_ffmpeg(path))
50        .ok_or(DecodeError::Unsupported)
51}
52
53/// 解码并缩放到 max_dim 边长内
54pub fn decode_and_thumbnail(path: &Path, max_dim: u32) -> Result<DynamicImage, DecodeError> {
55    let img = decode_image(path)?;
56    Ok(if img.width() > max_dim || img.height() > max_dim {
57        img.thumbnail(max_dim, max_dim)
58    } else {
59        img
60    })
61}