Vendor rawler 0.7.2 unmodified

The crate as crates.io publishes it, minus .cargo-ok, its Cargo.lock and
data/testdata (13 MB of sample files only its own tests read). Not yet
routed through [patch.crates-io]; the next commit is the patch.
This commit is contained in:
2026-09-27 17:33:19 -04:00
parent 0d9feb0556
commit 77a1925bac
940 changed files with 66954 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
use std::{fs::metadata, io::Write, path::Path};
use byteorder::{BigEndian, WriteBytesExt};
use hex::FromHex;
use image::DynamicImage;
use itertools::Itertools;
use md5::Digest;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zerocopy::IntoBytes;
use crate::{
RawImage, RawImageData, RawlerError, Result,
decoders::{RawDecodeParams, RawMetadata, cr2::Cr2Format, cr3::Cr3Format, dng::DngFormat, iiq::IiqFormat, nef::NefFormat, pef::PefFormat, tfr::TfrFormat},
formats::tiff::Rational,
formats::tiff::SRational,
imgop::{Rect, develop::RawDevelop},
rawimage::{BlackLevel, WhiteLevel},
rawsource::RawSource,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Md5Digest {
digest: md5::Digest,
}
impl From<md5::Digest> for Md5Digest {
fn from(digest: md5::Digest) -> Self {
Self { digest }
}
}
impl Serialize for Md5Digest {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = format!("{:x}", self.digest);
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for Md5Digest {
fn deserialize<D>(deserializer: D) -> std::result::Result<Md5Digest, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
if s.len() != 32 {
Err(D::Error::custom(format!("Invalid digest value: {}", s)))
} else {
Ok(Md5Digest {
digest: Digest(<[u8; 16]>::from_hex(s).map_err(D::Error::custom)?),
})
}
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileMetadata {
file_size: u64,
file_name: String,
digest: Option<Md5Digest>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnalyzerMetadata {
pub raw_params: RawParams,
pub raw_metadata: RawMetadata,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(clippy::large_enum_variant)]
pub enum AnalyzerData {
FileStructure(FormatDump),
Metadata(AnalyzerMetadata),
RawParams(RawParams),
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnalyzerResult {
pub file: FileMetadata,
pub data: Option<AnalyzerData>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CaptureInfo {
pub make: String,
pub model: String,
pub exposure_time: Option<Rational>,
pub shutter_speed: Option<SRational>,
pub exposure_bias: Option<SRational>,
pub lens_make: Option<String>,
pub lens_model: Option<String>,
pub lens_spec: Option<[Rational; 4]>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawParams {
pub raw_width: usize,
pub raw_height: usize,
pub bit_depth: usize,
pub crop_area: Option<Rect>,
pub active_area: Option<Rect>,
pub blacklevels: BlackLevel,
pub whitelevels: WhiteLevel,
pub wb_coeffs: (Option<f32>, Option<f32>, Option<f32>, Option<f32>),
}
impl From<&RawImage> for RawParams {
fn from(rawimage: &RawImage) -> Self {
Self {
raw_width: rawimage.width,
raw_height: rawimage.height,
bit_depth: rawimage.bps,
crop_area: rawimage.crop_area,
active_area: rawimage.active_area,
blacklevels: rawimage.blacklevel.clone(),
whitelevels: rawimage.whitelevel.clone(),
wb_coeffs: rawimage
.wb_coeffs
.iter()
.map(|c| if c.is_nan() { None } else { Some(*c) })
.collect_tuple()
.unwrap(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(clippy::large_enum_variant)]
pub enum FormatDump {
Cr3(Cr3Format),
Cr2(Cr2Format),
Pef(PefFormat),
Iiq(IiqFormat),
Tfr(TfrFormat),
Nef(NefFormat),
Dng(DngFormat),
}
fn file_metadata<P: AsRef<Path>>(path: P, rawfile: &RawSource) -> Result<FileMetadata> {
let fs_meta = metadata(&path).map_err(|e| RawlerError::with_io_error("read metadata", &path, e))?;
let digest = rawfile.digest();
Ok(FileMetadata {
file_name: path.as_ref().file_name().unwrap().to_string_lossy().to_string(),
file_size: fs_meta.len(),
digest: Some(digest.into()),
})
}
pub fn analyze_metadata<P: AsRef<Path>>(path: P) -> Result<AnalyzerResult> {
//let input = BufReader::new(File::open(&path).map_err(|e| RawlerError::with_io_error("load into buffer", &path, e))?);
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
let rawimage = decoder.raw_image(&rawfile, &RawDecodeParams::default(), true)?;
let mut result = AnalyzerResult {
file: file_metadata(path, &rawfile)?,
..Default::default()
};
let md = decoder.raw_metadata(&rawfile, &RawDecodeParams::default())?;
result.data = Some(AnalyzerData::Metadata(AnalyzerMetadata {
raw_params: RawParams::from(&rawimage),
raw_metadata: md,
}));
Ok(result)
}
pub fn analyze_file_structure<P: AsRef<Path>>(path: P) -> Result<AnalyzerResult> {
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
let result = AnalyzerResult {
file: file_metadata(path, &rawfile)?,
data: Some(AnalyzerData::FileStructure(decoder.format_dump())),
};
Ok(result)
}
pub fn extract_raw_pixels<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<RawImage> {
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
let rawimage = decoder.raw_image(&rawfile, params, false)?;
Ok(rawimage)
}
pub fn raw_pixels_digest<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<[u8; 16]> {
let rawimage = extract_raw_pixels(path, params)?;
let byte_buf = match &rawimage.data {
RawImageData::Integer(items) => items.as_slice().as_bytes(),
RawImageData::Float(items) => items.as_slice().as_bytes(),
};
Ok(md5::compute(byte_buf).into())
}
pub fn extract_full_pixels<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<DynamicImage> {
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
match decoder.full_image(&rawfile, params)? {
Some(preview) => Ok(preview),
None => Err("Unable to extract full image from RAW".into()),
}
}
pub fn extract_preview_pixels<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<DynamicImage> {
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
match decoder.preview_image(&rawfile, params)? {
Some(preview) => Ok(preview),
None => match decoder.full_image(&rawfile, params)? {
Some(preview) => Ok(preview),
None => Err("Unable to extract preview image from RAW".into()),
},
}
}
pub fn extract_thumbnail_pixels<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<DynamicImage> {
let rawfile = RawSource::new(path.as_ref())?;
let decoder = crate::get_decoder(&rawfile)?;
match decoder.thumbnail_image(&rawfile, params)? {
Some(thumbnail) => Ok(thumbnail),
None => match decoder.preview_image(&rawfile, params)? {
Some(thumbnail) => Ok(thumbnail),
None => match decoder.full_image(&rawfile, params)? {
Some(thumbnail) => Ok(thumbnail),
None => Err("Unable to extract thumbnail image from RAW".into()),
},
},
}
}
pub fn raw_to_srgb<P: AsRef<Path>>(path: P, params: &RawDecodeParams) -> Result<DynamicImage> {
let rawfile = RawSource::new(path.as_ref())?;
// Get decoder or return
let decoder = crate::get_decoder(&rawfile)?;
//decoder.decode_metadata(&mut rawfile)?;
let rawimage = decoder.raw_image(&rawfile, params, false)?;
let dev = RawDevelop::default();
assert_eq!(rawimage.cpp, 1);
Ok(dev.develop_intermediate(&rawimage)?.to_dynamic_image().unwrap())
}
/// Dump raw pixel data as PGM
pub fn raw_as_pgm(width: usize, height: usize, buf: &[u16], writer: &mut dyn Write) -> std::io::Result<()> {
let header = format!("P5 {} {} {}\n", width, height, 65535);
writer.write_all(header.as_bytes())?;
for px in buf {
writer.write_u16::<BigEndian>(*px)?;
}
Ok(())
}
/// Dump pixel data as PGM
pub fn rgb8_as_ppm8(width: usize, height: usize, buf: &[u8], writer: &mut dyn Write) -> std::io::Result<()> {
let header = format!("P6 {} {} {}\n", width, height, u8::MAX);
writer.write_all(header.as_bytes())?;
writer.write_all(buf)?;
Ok(())
}
/// Dump raw pixel data as PPM
pub fn raw_as_ppm16(width: usize, height: usize, buf: &[u16], writer: &mut dyn Write) -> std::io::Result<()> {
let header = format!("P6 {} {} {}\n", width, height, 65535);
writer.write_all(header.as_bytes())?;
for px in buf {
writer.write_u16::<BigEndian>(*px)?;
}
Ok(())
}