image_extras/
lib.rs

1//! This crate provides additional formats for the image crate.
2//!
3//! Call the [`register`] function at program startup:
4//!
5//!  ```rust,no_run
6//! image_extras::register();
7//!
8//! // Now you can use the image crate as normal
9//! let img = image::open("path/to/image.pcx").unwrap();
10//! ```
11//!
12//! By default, all supported formats are enabled. For finer control, enable
13//! individual formats via Cargo features.
14
15#![forbid(unsafe_code)]
16
17#[cfg(feature = "ora")]
18pub mod ora;
19
20#[cfg(feature = "otb")]
21pub mod otb;
22
23#[cfg(feature = "pcx")]
24pub mod pcx;
25
26#[cfg(feature = "sgi")]
27pub mod sgi;
28
29#[cfg(feature = "wbmp")]
30pub mod wbmp;
31
32#[cfg(feature = "xbm")]
33pub mod xbm;
34
35#[cfg(feature = "xpm")]
36pub mod xpm;
37
38#[allow(unused_imports)]
39use image::hooks::{register_decoding_hook, register_format_detection_hook};
40
41static REGISTER: std::sync::Once = std::sync::Once::new();
42
43/// Register all enabled extra formats with the image crate.
44///
45/// Calling this function more than once is allowed but has no additional
46/// effect.
47pub fn register() {
48    REGISTER.call_once(|| {
49        // OpenRaster images are ZIP files and have no simple signature to distinguish them
50        // from ZIP files containing other content
51        #[cfg(feature = "ora")]
52        register_decoding_hook(
53            "ora".into(),
54            Box::new(|r| {
55                Ok(Box::new(ora::OpenRasterDecoder::with_limits(
56                    r,
57                    image::Limits::no_limits(),
58                )?))
59            }),
60        );
61
62        #[cfg(feature = "otb")]
63        image::hooks::register_decoding_hook(
64            "otb".into(),
65            Box::new(|r| Ok(Box::new(otb::OtbDecoder::new(r)?))),
66        );
67
68        #[cfg(feature = "pcx")]
69        if register_decoding_hook(
70            "pcx".into(),
71            Box::new(|r| Ok(Box::new(pcx::PCXDecoder::new(r)?))),
72        ) {
73            register_format_detection_hook("pcx".into(), &[0x0a, 0x0], Some(b"\xFF\xF8"));
74        }
75
76        // SGI RGB images generally show up with a .rgb ending (whether or not they
77        // have 3 channels), and sometimes .bw (when grayscale) and .rgba. The
78        // extensions .sgi and .iris, while unambiguous, do not seem to have been
79        // used much. The extension .rgb is also used for a variety of other files,
80        // including bare image data, so to be sure it would be best to check both
81        // extension and leading bytes
82        #[cfg(feature = "sgi")]
83        {
84            let hook: for<'a> fn(
85                image::hooks::GenericReader<'a>,
86            )
87                -> image::ImageResult<Box<dyn image::ImageDecoder + 'a>> =
88                |r| Ok(Box::new(sgi::SgiDecoder::new(r)?));
89            image::hooks::register_decoding_hook("bw".into(), Box::new(hook));
90            image::hooks::register_decoding_hook("rgb".into(), Box::new(hook));
91            image::hooks::register_decoding_hook("rgba".into(), Box::new(hook));
92            image::hooks::register_decoding_hook("iris".into(), Box::new(hook));
93            if image::hooks::register_decoding_hook("sgi".into(), Box::new(hook)) {
94                // The main signature bytes are technically just 01 da, but this is short
95                // and the following storage and bpc fields are constrained well enough to
96                // efficiently match them as well
97                image::hooks::register_format_detection_hook(
98                    "sgi".into(),
99                    b"\x01\xda\x00\x01",
100                    None,
101                );
102                image::hooks::register_format_detection_hook(
103                    "sgi".into(),
104                    b"\x01\xda\x01\x01",
105                    None,
106                );
107                image::hooks::register_format_detection_hook(
108                    "sgi".into(),
109                    b"\x01\xda\x00\x02",
110                    None,
111                );
112                image::hooks::register_format_detection_hook(
113                    "sgi".into(),
114                    b"\x01\xda\x01\x02",
115                    None,
116                );
117            }
118        }
119
120        #[cfg(feature = "wbmp")]
121        image::hooks::register_decoding_hook(
122            "wbmp".into(),
123            Box::new(|r| Ok(Box::new(wbmp::WbmpDecoder::new(r)?))),
124        );
125
126        #[cfg(feature = "xbm")]
127        {
128            register_decoding_hook(
129                "xbm".into(),
130                Box::new(|r| Ok(Box::new(xbm::XbmDecoder::new(r)?))),
131            );
132            register_decoding_hook(
133                "bm".into(),
134                Box::new(|r| Ok(Box::new(xbm::XbmDecoder::new(r)?))),
135            );
136        }
137
138        #[cfg(feature = "xpm")]
139        if register_decoding_hook(
140            "xpm".into(),
141            Box::new(|r| Ok(Box::new(xpm::XpmDecoder::new(r)?))),
142        ) {
143            register_format_detection_hook("xpm".into(), b"/* XPM */", None);
144        }
145    });
146}