Files
DarkRoom/third_party/rawler-0.7.2/src/decoders/kdc.rs
T
dtourolle 77a1925bac 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.
2026-09-27 17:33:19 -04:00

220 lines
7.7 KiB
Rust

use log::warn;
use crate::RawImage;
use crate::RawLoader;
use crate::RawSource;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::alloc_image_ok;
use crate::analyze::FormatDump;
use crate::bits::BEu16;
use crate::exif::Exif;
use crate::formats::tiff::Entry;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::Value;
use crate::formats::tiff::ifd::OffsetMode;
use crate::formats::tiff::reader::TiffReader;
use crate::packed::decode_12be;
use crate::pixarray::PixU16;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::CFAConfig;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::RawPhotometricInterpretation;
use super::WhiteLevel;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct KdcDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: Option<IFD>,
camera: Camera,
}
impl<'a> KdcDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<KdcDecoder<'a>> {
let camera = rawloader.check_supported(tiff.root_ifd())?;
let makernote = if let Some(exif) = tiff.find_first_ifd_with_tag(ExifTag::MakerNotes) {
exif.parse_makernote(&mut file.reader(), OffsetMode::Absolute, &[])?
} else {
warn!("KDC makernote not found");
None
};
Ok(KdcDecoder {
tiff,
rawloader,
camera,
makernote,
})
}
}
impl<'a> Decoder for KdcDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
if self.camera.clean_model == "DC120" {
let width = 848;
let height = 976;
let raw = self.tiff.find_ifds_with_tag(TiffCommonTag::CFAPattern)[0];
let off = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let mut white = self.camera.whitelevel.clone().expect("KDC needs a whitelevel in camera config")[0];
let src = file.subview_until_eof(off as u64)?;
let image = match fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_usize(0) {
1 => Self::decode_dc120(src, width, height, dummy),
7 => {
white = 0xFF << 1;
Self::decode_dc120_jpeg(src, width, height, dummy)?
}
c => {
return Err(RawlerError::unsupported(
&self.camera,
format!("KDC: DC120: Don't know how to handle compression type {}", c),
));
}
};
let cpp = 1;
let whitelevel = Some(WhiteLevel::new(vec![white; cpp]));
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let img = RawImage::new(self.camera.clone(), image, cpp, [1.0, 1.0, 1.0, f32::NAN], photometric, None, whitelevel, dummy);
return Ok(img);
}
if self.camera.clean_model == "DC50" {
let raw = self.tiff.find_ifds_with_tag(TiffCommonTag::CFAPattern)[0];
let width = self.camera.raw_width;
let height = self.camera.raw_height;
let off = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let white = self.camera.whitelevel.clone().expect("KDC needs a whitelevel in camera config")[0];
let cbpp = match raw.get_entry(ExifTag::CompressedBitsPerPixel) {
Some(Entry {
value: Value::Rational(data), ..
}) if data[0].n == 243 => 2,
_ => 3,
};
let src = file.subview_until_eof_padded(off as u64)?;
let image = crate::decompressors::radc::decompress(&src, width, height, cbpp, dummy)?;
let cpp = 1;
let whitelevel = Some(WhiteLevel::new(vec![white; cpp]));
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let img = RawImage::new(self.camera.clone(), image, cpp, [1.0, 1.0, 1.0, f32::NAN], photometric, None, whitelevel, dummy);
return Ok(img);
}
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::KdcWidth)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with KdcWidth tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::KdcWidth).force_usize(0) + 80;
let height = fetch_tiff_tag!(raw, TiffCommonTag::KdcLength).force_usize(0) + 70;
let offset = fetch_tiff_tag!(raw, TiffCommonTag::KdcOffset);
if offset.count() < 13 {
panic!("KDC Decoder: Couldn't find the KDC offset");
}
let mut off = offset.force_usize(4) + offset.force_usize(12);
// Offset hardcoding gotten from dcraw
if self.camera.find_hint("easyshare_offset_hack") {
off = if off < 0x15000 { 0x15000 } else { 0x17000 };
}
let src = file.subview_until_eof(off as u64)?;
let image = decode_12be(src, width, height, dummy);
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, self.get_wb()?, image, dummy)
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(self.tiff.root_ifd())?;
let mdata = RawMetadata::new(&self.camera, exif);
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::KDC
}
}
impl<'a> KdcDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
if let Some(makernote) = self.makernote.as_ref() {
match makernote.get_entry(TiffCommonTag::KdcWB) {
Some(levels) => {
if levels.count() != 3 {
Err(format!("KDC: Levels count is off: {}", levels.count()).into())
} else {
Ok([levels.force_f32(0), levels.force_f32(1), levels.force_f32(2), f32::NAN])
}
}
None => {
let levels = fetch_tiff_tag!(makernote, TiffCommonTag::KodakWB);
if ![734, 1502, 1512, 2288].contains(&levels.count()) {
Err(format!("KDC: Levels count is off: {}", levels.count()).into())
} else {
let r = BEu16(levels.get_data(), 148) as f32;
let b = BEu16(levels.get_data(), 150) as f32;
Ok([r / 256.0, 1.0, b / 256.0, f32::NAN])
}
}
}
} else {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
}
pub(crate) fn decode_dc120(src: &[u8], width: usize, height: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
let mul: [usize; 4] = [162, 192, 187, 92];
let add: [usize; 4] = [0, 636, 424, 212];
for row in 0..height {
let shift = row * mul[row & 3] + add[row & 3];
for col in 0..width {
out[row * width + col] = src[row * width + ((col + shift) % 848)] as u16;
}
}
out
}
pub(crate) fn decode_dc120_jpeg(src: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let mut out = alloc_image_ok!(width, height, dummy);
let swapped_src: Vec<u8> = src.chunks_exact(2).flat_map(|x| [x[1], x[0]]).collect();
let img = image::load_from_memory_with_format(&swapped_src, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG image: {:?}", err)))?;
assert_eq!(width, img.width() as usize);
assert_eq!(height, img.height() as usize * 2);
let buf = img.as_flat_samples_u8().unwrap();
let jpeg = buf.as_slice();
for irow in 0..img.height() as usize {
let row = irow * 2;
let iline = &jpeg[irow * width * 3..];
for col in (0..width).step_by(2) {
*out.at_mut(row + 0, col + 0) = (iline[col * 3 + 1] as u16) << 1;
*out.at_mut(row + 1, col + 1) = (iline[(col + 1) * 3 + 1] as u16) << 1;
*out.at_mut(row + 0, col + 1) = (iline[col * 3 + 0]) as u16 + (iline[(col + 1) * 3 + 0]) as u16;
*out.at_mut(row + 1, col + 0) = (iline[col * 3 + 2]) as u16 + (iline[(col + 1) * 3 + 2]) as u16;
}
}
Ok(out)
}
}