| 1 | //! Camera RAW through `rawler`: the embedded preview first (fast), a full develop on demand |
| 2 | //! once the view zooms past the preview's resolution. |
| 3 | |
| 4 | use std::path::Path; |
| 5 | |
| 6 | use anyhow::anyhow; |
| 7 | use image::metadata::Orientation; |
| 8 | use rawler::decoders::RawDecodeParams; |
| 9 | use rawler::imgop::develop::RawDevelop; |
| 10 | use rawler::rawsource::RawSource; |
| 11 | |
| 12 | use super::color; |
| 13 | use super::{DecodedImage, Options, Refine}; |
| 14 | |
| 15 | fn orientation(o: rawler::decoders::Orientation, opts: &Options) -> Orientation { |
| 16 | if !opts.auto_orient { |
| 17 | return Orientation::NoTransforms; |
| 18 | } |
| 19 | Orientation::from_exif(o.to_u16() as u8).unwrap_or(Orientation::NoTransforms) |
| 20 | } |
| 21 | |
| 22 | pub fn decode(path: &Path, opts: &Options) -> anyhow::Result<DecodedImage> { |
| 23 | let src = RawSource::new(path)?; |
| 24 | let decoder = rawler::get_decoder(&src)?; |
| 25 | let params = RawDecodeParams::default(); |
| 26 | let preview = decoder |
| 27 | .preview_image(&src, ¶ms) |
| 28 | .ok() |
| 29 | .flatten() |
| 30 | .or_else(|| decoder.full_image(&src, ¶ms).ok().flatten()); |
| 31 | let Some(preview) = preview else { |
| 32 | return develop(path, opts); |
| 33 | }; |
| 34 | // The raw data's size (cheap: no pixels decoded) is the size the view works in. |
| 35 | let raw = decoder.raw_image(&src, ¶ms, true)?; |
| 36 | let o = orientation(raw.orientation, opts); |
| 37 | let (size, pixels) = color::to_rgba8(preview); |
| 38 | let (size, pixels) = color::orient(size, pixels, o); |
| 39 | let (rw, rh) = match raw.crop_area { |
| 40 | Some(r) => (r.d.w, r.d.h), |
| 41 | None => (raw.width, raw.height), |
| 42 | }; |
| 43 | let raw_long = rw.max(rh) as u32; |
| 44 | let long = size[0].max(size[1]); |
| 45 | let mut img = DecodedImage::still(size, pixels, "RAW"); |
| 46 | if raw_long > long { |
| 47 | // Same shape as the preview, scaled up to the sensor's resolution. |
| 48 | let k = raw_long as f64 / long as f64; |
| 49 | img.source_size = [ |
| 50 | (size[0] as f64 * k).round() as u32, |
| 51 | (size[1] as f64 * k).round() as u32, |
| 52 | ]; |
| 53 | img.refine = Some(Refine::Raw); |
| 54 | } |
| 55 | Ok(img) |
| 56 | } |
| 57 | |
| 58 | /// Demosaic and develop the raw data in full. |
| 59 | pub fn develop(path: &Path, opts: &Options) -> anyhow::Result<DecodedImage> { |
| 60 | let raw = rawler::decode_file(path)?; |
| 61 | let o = orientation(raw.orientation, opts); |
| 62 | let dynamic = RawDevelop::default() |
| 63 | .develop_intermediate(&raw)? |
| 64 | .to_dynamic_image() |
| 65 | .ok_or_else(|| anyhow!("develop produced no image"))?; |
| 66 | let (size, pixels) = color::to_rgba8(dynamic); |
| 67 | let (size, pixels) = color::orient(size, pixels, o); |
| 68 | Ok(DecodedImage::still(size, pixels, "RAW")) |
| 69 | } |