Skip to main content

kallo_api/
input.rs

1//! Reading the photograph: an ordinary image, or a camera raw.
2
3use std::path::Path;
4
5use kallo_io::{Image, InputEncoding, load};
6
7use crate::error::Error;
8
9/// Read the input, from a camera raw when it is one and the build has a
10/// decoder.
11///
12/// A raw file is scene-referred linear by construction, so `--input-encoding`
13/// does not apply to it: the value is taken as the file's own, and a user who
14/// asked for `srgb` on a `.nef` is not obeyed, because obeying would apply a
15/// transfer curve to data that never had one. Every command that reads a
16/// photograph reads it through here for that reason.
17pub fn read_input(path: &Path, encoding: InputEncoding) -> Result<Image, Error> {
18    #[cfg(feature = "raw")]
19    if kallo_raw::handles(path) {
20        return kallo_raw::load(path).map_err(|source| Error::Raw {
21            path: path.to_path_buf(),
22            message: source.to_string(),
23        });
24    }
25    #[cfg(not(feature = "raw"))]
26    if looks_like_raw(path) {
27        return Err(Error::Raw {
28            path: path.to_path_buf(),
29            message: "this build has no raw decoder; rebuild with `--features raw`, or \
30                      convert the file and pass --input-encoding linear"
31                .to_string(),
32        });
33    }
34    load(path, encoding).map_err(|source| Error::Read {
35        path: path.to_path_buf(),
36        source,
37    })
38}
39
40/// A short list of raw extensions, and only for the error message: without the
41/// `raw` feature there is no decoder to ask, and "unsupported image format" is
42/// a poor answer to someone who passed a camera file.
43#[cfg(not(feature = "raw"))]
44const RAW_EXTENSIONS: &[&str] = &[
45    "3fr", "arw", "cr2", "cr3", "dng", "iiq", "nef", "nrw", "orf", "pef", "raf", "rw2", "srw",
46];
47
48#[cfg(not(feature = "raw"))]
49fn looks_like_raw(path: &Path) -> bool {
50    path.extension()
51        .and_then(|extension| extension.to_str())
52        .is_some_and(|extension| RAW_EXTENSIONS.contains(&extension.to_ascii_lowercase().as_str()))
53}