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
+103
View File
@@ -0,0 +1,103 @@
use crate::RawImage;
use crate::RawLoader;
use crate::Result;
use crate::bits::*;
use crate::exif::Exif;
use crate::formats::tiff::Rational;
use crate::packed::*;
use crate::rawsource::RawSource;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
pub fn is_ari(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => buf[0..4] == b"ARRI"[..],
Err(_) => false,
}
}
#[derive(Debug, Clone)]
pub struct AriDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
camera: Camera,
}
impl<'a> AriDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<AriDecoder<'a>> {
let buffer = file.subview(668, 30)?;
let model = String::from_utf8_lossy(buffer).split_terminator('\0').next().unwrap_or("").to_string();
let camera = rawloader.check_supported_with_everything("ARRI", &model, "")?;
Ok(AriDecoder { rawloader, camera })
}
}
impl<'a> Decoder for AriDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let buffer = file.subview(0, 100)?;
let offset = LEu32(buffer, ArriRawTag::DataOffset as usize) as usize;
let width = LEu32(buffer, ArriRawTag::Width as usize) as usize;
let height = LEu32(buffer, ArriRawTag::Height as usize) as usize;
let src = file.subview_until_eof_padded(offset as u64)?;
let image = if self.camera.find_hint("little-endian") {
decode_12le(&src, width, height, dummy)
} else {
decode_12be_msb32(&src, width, height, dummy)
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, self.get_wb(file)?, image, dummy)
}
fn format_dump(&self) -> crate::analyze::FormatDump {
todo!()
}
fn raw_metadata(&self, file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let mut exif = Exif::default();
let buffer = file.subview(0, 0x0a98)?; // max header
exif.recommended_exposure_index = Some(LEu32(buffer, ArriRawTag::ExposureIndexASA as usize));
exif.sensitivity_type = Some(2);
let lens_model = char_slice_to_string(&buffer[ArriRawTag::LensModel as usize..ArriRawTag::LensModel as usize + 32]);
exif.lens_model = lens_model.map(|s| s.trim().into());
log::debug!("Lens model: {:?}", exif.lens_model);
let exposure_time = LEu32(buffer, ArriRawTag::ExposureTime as usize);
exif.exposure_time = Some(Rational::new(exposure_time, 1000000));
let focal_len = LEu32(buffer, ArriRawTag::LensFocalLen as usize);
exif.focal_length = Some(Rational::new(focal_len, 1000));
let mdata = RawMetadata::new(&self.camera, exif);
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::ARI
}
}
impl<'a> AriDecoder<'a> {
fn get_wb(&self, file: &RawSource) -> Result<[f32; 4]> {
let buffer = file.subview(0, 100 + 12)?;
Ok([LEf32(buffer, 100), LEf32(buffer, 104), LEf32(buffer, 108), f32::NAN])
}
}
enum ArriRawTag {
DataOffset = 0x0008,
Width = 0x0014,
Height = 0x0018,
ExposureIndexASA = 0x0074,
ExposureTime = 0x018C,
LensFocalLen = 0x037C,
LensModel = 0x0398,
}
fn char_slice_to_string(buf: &[u8]) -> Option<String> {
Some(buf.iter().take_while(|&&c| c != 0).map(|&c| char::from(c)).collect())
}
+797
View File
@@ -0,0 +1,797 @@
use std::cmp;
use std::io::Cursor;
use image::DynamicImage;
use log::debug;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::bits::*;
use crate::decoders::decode_threaded;
use crate::decoders::decode_threaded_multiline;
use crate::decompressors::ljpeg::LjpegDecompressor;
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::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::imgop::yuv::interpolate_yuv;
use crate::imgop::yuv::ycbcr_to_rgb;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::decode_12le;
use crate::packed::decode_14be_unpacked;
use crate::packed::decode_16be;
use crate::packed::decode_16le;
use crate::pixarray::PixU16;
use crate::pumps::BitPump;
use crate::pumps::BitPumpLSB;
use crate::pumps::BitPumpMSB;
use crate::rawimage::BlackLevel;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawimage::WhiteLevel;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
const SONY_E_MOUNT: &str = "e-mount";
const SONY_A_MOUNT: &str = "a-mount";
#[derive(Debug, Clone)]
pub struct ArwDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
camera: Camera,
}
impl<'a> ArwDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<ArwDecoder<'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 {
log::warn!("ARW makernote not found");
None
}
.ok_or("File has not makernotes")?;
//makernote.dump::<ExifTag>(0).iter().for_each(|line| eprintln!("DUMP: {}", line));
Ok(ArwDecoder {
tiff,
rawloader,
makernote,
camera,
})
}
}
impl<'a> Decoder for ArwDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let data = self.tiff.find_ifds_with_tag(TiffCommonTag::StripOffsets);
if data.is_empty() {
if self.camera.model == "DSLR-A100" {
return self.image_a100(file, dummy);
} else {
// try decoding as SRF
return self.image_srf(file, dummy);
}
}
let raw = data[0];
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let mut height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let count = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts).force_usize(0);
let compression = fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_u32(0);
let bps = if let Some(forced_bps) = &self.camera.bps {
*forced_bps
} else {
fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_usize(0)
};
let params = self.get_params(file)?;
debug!("Params: {:?}", params);
//assert!(params.blacklevel.is_some());
//assert!(params.whitelevel.is_some()); // DSC-R1 is SR2 format and has no whitelevel
let mut white = params.whitelevel.map(|x| x[0]);
let mut black = params.blacklevel;
let src = file.subview_until_eof(offset as u64)?;
let mut cpp = 1;
let image = match compression {
1 => {
if self.camera.model == "DSC-R1" {
decode_14be_unpacked(src, width, height, dummy)
} else {
decode_16le(src, width, height, dummy)
}
}
7 => {
cpp = fetch_tiff_tag!(raw, TiffCommonTag::SamplesPerPixel).force_usize(0);
// Starting with A-1, image is compressed in tiles with LJPEG92.
// Data is RGGB for bayer readout and YCbCr for reduced resolution files.
ArwDecoder::decode_ljpeg(&self.camera, file, raw, dummy)?
}
32767 => {
if (width * height * bps) != count * 8 {
height += 8;
ArwDecoder::decode_arw1(src, width, height, dummy)
} else {
match bps {
8 => {
let curve = ArwDecoder::get_curve(raw)?;
ArwDecoder::decode_arw2(src, width, height, &curve, dummy)
}
12 => {
/*
Some cameras like the A700 have an uncompressed mode where the output is 12bit and
does not require any curve. For these all we need to do is set 12bit black and white
points instead of the 14bit ones of the normal compressed 8bit -> 10bit -> 14bit mode.
We set these 12bit points by shifting down the 14bit points. It might make sense to
have a separate camera mode instead but since the values seem good we don't bother.
*/
white = white.map(|x| x >> 2);
black = black.map(|mut x| {
x.iter_mut().for_each(|x| *x >>= 2);
x
});
decode_12le(src, width, height, dummy)
}
_ => return Err(RawlerError::DecoderFailed(format!("ARW2: Don't know how to decode images with {} bps", bps))),
}
}
}
_ => return Err(RawlerError::DecoderFailed(format!("ARW: Don't know how to decode type {}", compression))),
};
let blacklevel = black.map(|black| match cpp {
1 => BlackLevel::new(&black, self.camera.cfa.width, self.camera.cfa.height, cpp),
// For YUV data, the blacklevel needs to be multiplicated by 2
3 => BlackLevel::new(&[black[0] * 2, black[0] * 2, black[0] * 2], 1, 1, cpp),
_ => panic!("Unsupported cpp == {}", cpp),
});
let whitelevel = white.map(|white| WhiteLevel(vec![white as u32; cpp]));
let photometric = match cpp {
1 => RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera)),
3 => RawPhotometricInterpretation::LinearRaw,
_ => todo!(),
};
let mut img = RawImage::new(self.camera.clone(), image, cpp, params.wb, photometric, blacklevel, whitelevel, dummy);
if cpp == 3 {
// For debayer images, we assume WB coeffs already applied
img.wb_coeffs = [1.0, 1.0, 1.0, f32::NAN];
}
if let Some(raw_image_size) = self.get_raw_image_size(raw)? {
log::debug!("Found SONYRAWIMAGESIZE tag, using as active_area");
img.active_area = Some(raw_image_size);
} else {
img.active_area = self.camera.active_area.map(|area| Rect::new_with_borders(Dim2::new(width, height), &area));
}
img.crop_area = Rect::from_tiff(raw).or_else(|| self.camera.crop_area.map(|area| Rect::new_with_borders(Dim2::new(width, height), &area)));
log::debug!("raw dim: {}x{}", width, height);
log::debug!("crop_area: {:?}", img.crop_area);
log::debug!("active_area: {:?}", img.active_area);
Ok(img)
}
/// Return the embedded JPEG preview
/// Exiftool docs says there is a tag 0x2002 including the image, but this tag
/// exists in none of the samples?! Instead, we can use the JPEG thumbnail
/// tags which exists for most samples.
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
let root = self.tiff.root_ifd();
if let Some(preview_off) = root.get_entry(ExifTag::JPEGInterchangeFormat) {
if let Some(preview_len) = root.get_entry(ExifTag::JPEGInterchangeFormatLength) {
let buf = file.subview(preview_off.force_u64(0), preview_len.force_u64(0))?;
let img = image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG image: {:?}", err)))?;
return Ok(Some(img));
}
}
Ok(None)
}
fn format_dump(&self) -> crate::analyze::FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let mut exif = Exif::new(self.tiff.root_ifd())?;
exif.extend_from_ifd(self.get_exif()?)?; // TODO: is this required?
let mdata = RawMetadata::new_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::ARW
}
}
impl<'a> ArwDecoder<'a> {
fn get_exif(&self) -> Result<&IFD> {
self
.tiff
.find_first_ifd_with_tag(ExifTag::MakerNotes)
.ok_or_else(|| "EXIF IFD not found".into())
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
// Try tag 0x9416
if let Some(Entry {
value: Value::Undefined(params),
..
}) = self.makernote.get_entry(ArwMakernoteTag::Tag_9416)
{
let dechiphered_9416 = sony_tag9cxx_decipher(params);
let lens_id = LEu16(&dechiphered_9416, 0x004b);
debug!("Lens Id tag: {}", lens_id);
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_lens_id((lens_id as u32, 0))
.with_mounts(&[SONY_E_MOUNT.into(), SONY_A_MOUNT.into()]);
return Ok(resolver.resolve());
}
// Try tag 0x9050
if let Some(Entry {
value: Value::Undefined(params),
..
}) = self.makernote.get_entry(ArwMakernoteTag::Tag_9050)
{
if params.len() >= 263 + 2 {
let dechiphered_9050 = sony_tag9cxx_decipher(params);
let lens_id = LEu16(&dechiphered_9050, 263);
debug!("Lens Id tag: {}", lens_id);
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_lens_id((lens_id as u32, 0))
.with_mounts(&[SONY_E_MOUNT.into(), SONY_A_MOUNT.into()]);
return Ok(resolver.resolve());
}
}
// Try tag 0x940C
if let Some(Entry {
value: Value::Undefined(params),
..
}) = self.makernote.get_entry(ArwMakernoteTag::Tag_940C)
{
let dechiphered_940c = sony_tag9cxx_decipher(params);
let lens_id = LEu16(&dechiphered_940c, 9);
debug!("Lens Id tag: {}", lens_id);
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_lens_id((lens_id as u32, 0))
.with_mounts(&[SONY_E_MOUNT.into(), SONY_A_MOUNT.into()]);
return Ok(resolver.resolve());
}
Ok(None)
}
fn image_a100(&self, file: &RawSource, dummy: bool) -> Result<RawImage> {
// We've caught the elusive A100 in the wild, a transitional format
// between the simple sanity of the MRW custom format and the wordly
// wonderfullness of the Tiff-based ARW format, let's shoot from the hip
let data = self.tiff.find_ifds_with_tag(TiffCommonTag::SubIFDs);
if data.is_empty() {
return Err(RawlerError::DecoderFailed("ARW: Couldn't find the data IFD!".to_string()));
}
let raw = data[0];
let width = 3880;
let height = 2608;
let offset = fetch_tiff_tag!(raw, TiffCommonTag::SubIFDs).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = ArwDecoder::decode_arw1(src, width, height, dummy);
// Get the WB the MRW way
// DNGPrivateTag contains 4 bytes forming a LE u32 offset value.
let priv_offset = {
let entry = fetch_tiff_tag!(self.tiff, TiffCommonTag::DNGPrivateArea);
assert_eq!(entry.value_type(), 0x1);
LEu32(entry.get_data(), 0)
};
let buf = file.subview_until_eof(priv_offset as u64)?;
if BEu32(buf, 0) != 0x4D5249 {
// MRI
return Err(format!("Invalid DNGPRIVATEDATA tag: 0x{:X}, expected 0x4D5249 ", BEu32(buf, 0)).into());
}
let mut currpos: usize = 8;
let mut wb_coeffs: [f32; 4] = [1.0, 1.0, 1.0, f32::NAN];
// At most we read 20 bytes from currpos so check we don't step outside that
while currpos + 20 < buf.len() {
let tag: u32 = BEu32(buf, currpos);
let len: usize = LEu32(buf, currpos + 4) as usize;
if tag == 0x574247 {
// WBG
wb_coeffs[0] = LEu16(buf, currpos + 12) as f32;
wb_coeffs[1] = LEu16(buf, currpos + 14) as f32;
wb_coeffs[2] = LEu16(buf, currpos + 14) as f32;
wb_coeffs[3] = LEu16(buf, currpos + 18) as f32;
break;
}
currpos += len + 8;
}
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, normalize_wb(wb_coeffs), image, dummy)
}
fn image_srf(&self, file: &RawSource, dummy: bool) -> Result<RawImage> {
let data = self.tiff.find_ifds_with_tag(TiffCommonTag::ImageWidth);
if data.is_empty() {
return Err(RawlerError::DecoderFailed("ARW: Couldn't find the data IFD!".to_string()));
}
let raw = data[0];
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let image = if dummy {
PixU16::new_uninit(width, height)
} else {
let buffer = file.as_vec()?;
let len = width * height * 2;
// Constants taken from dcraw
let off: usize = 862144;
let key_off: usize = 200896;
let head_off: usize = 164600;
// Replicate the dcraw contortions to get the "decryption" key
let offset = (buffer[key_off] as usize) * 4;
let first_key = BEu32(&buffer, key_off + offset);
let head = ArwDecoder::sony_decrypt(&buffer, head_off, 40, first_key)?;
let second_key = LEu32(&head, 22);
// "Decrypt" the whole image buffer
let image_data = ArwDecoder::sony_decrypt(&buffer, off, len, second_key)?;
decode_16be(&image_data, width, height, dummy)
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, [f32::NAN, f32::NAN, f32::NAN, f32::NAN], image, dummy)
}
pub(crate) fn decode_arw1(buf: &[u8], width: usize, height: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
let mut pump = BitPumpMSB::new(buf);
let mut sum: i32 = 0;
for x in 0..width {
let col = width - 1 - x;
let mut row = 0;
while row <= height {
if row == height {
row = 1;
}
let mut len: u32 = 4 - pump.get_bits(2);
if len == 3 && pump.get_bits(1) != 0 {
len = 0;
} else if len == 4 {
let zeros = pump.peek_bits(13).leading_zeros() - 19;
len += zeros;
pump.get_bits(cmp::min(13, zeros + 1));
}
let diff: i32 = pump.get_ibits(len);
sum += diff;
if len > 0 && (diff & (1 << (len - 1))) == 0 {
sum -= (1 << len) - 1;
}
out[row * width + col] = sum as u16;
row += 2
}
}
out
}
pub(crate) fn decode_arw2(buf: &[u8], width: usize, height: usize, curve: &LookupTable, dummy: bool) -> PixU16 {
decode_threaded(
width,
height,
dummy,
&(|out: &mut [u16], row| {
let mut pump = BitPumpLSB::new(&buf[(row * width)..]);
let mut random = pump.peek_bits(16);
for out in out.chunks_exact_mut(32) {
// Process 32 pixels at a time in interleaved fashion
for j in 0..2 {
let max = pump.get_bits(11);
let min = pump.get_bits(11);
let delta = max - min;
// Calculate the size of the data shift needed by how large the delta is
// A delta with 11 bits requires a shift of 4, 10 bits of 3, etc
let delta_shift: u32 = cmp::max(0, (32 - (delta.leading_zeros() as i32)) - 7) as u32;
let imax = pump.get_bits(4) as usize;
let imin = pump.get_bits(4) as usize;
for i in 0..16 {
let val = if i == imax {
max
} else if i == imin {
min
} else {
cmp::min(0x7ff, (pump.get_bits(7) << delta_shift) + min)
};
out[j + (i * 2)] = curve.dither((val << 1) as u16, &mut random);
}
}
}
}),
)
}
/// Some newer cameras like Alpha-1 uses LJPEG compression, but in an awkward way.
/// The image is split into 512x512 tiles with cpp = 1, but the LJPEG stream is
/// compressed as 256x256 with cpp = 4. So the total of bytes matches, but the dimension
/// is wrong. Actually, the LJPEG stream is two lines packed into a single line each
/// decompressed line has the bayer pattern: RGGBRGGBRGGB...
/// So we need to decompress first, then unpack the bayer pattern from one line
/// into two lines.
/// For resolution-reduced files (cpp=3), pixels are encoded in YCbCr color space.
pub(crate) fn decode_ljpeg(camera: &Camera, file: &RawSource, raw: &IFD, dummy: bool) -> Result<PixU16> {
let offsets = raw.get_entry(TiffCommonTag::TileOffsets).ok_or("Unable to find TileOffsets")?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let twidth = fetch_tiff_tag!(raw, TiffCommonTag::TileWidth).force_usize(0);
let tlength = fetch_tiff_tag!(raw, TiffCommonTag::TileLength).force_usize(0);
let cpp = fetch_tiff_tag!(raw, TiffCommonTag::SamplesPerPixel).force_usize(0);
let coltiles = (width - 1) / twidth + 1;
let rowtiles = (height - 1) / tlength + 1;
log::debug!("Sony ARW LJPEG raw: width: {}, height: {}, cpp: {}", width, height, cpp);
log::debug!("LJPEG tile parameters: width: {}, length: {}, cpp: {}", twidth, tlength, cpp);
if coltiles * rowtiles != offsets.count() as usize {
return Err(RawlerError::unsupported(
camera,
format!("ARW LJPEG: trying to decode {} tiles from {} offsets", coltiles * rowtiles, offsets.count()),
));
}
let buffer = file.as_vec()?;
if cpp == 3 {
let mut image = decode_threaded_multiline(
width * cpp,
height,
tlength,
dummy,
&(|strip: &mut [u16], row| {
let row = row / tlength;
for col in 0..coltiles {
log::debug!("Decode tile: row({}), col({})", row, col);
let offset = offsets.force_usize(row * coltiles + col);
let src = &buffer[offset..];
let decompressor =
LjpegDecompressor::new(src).map_err(|err| format!("Creating LJPEG decompressor for ARW LJPEG tile ({row},{col}) failed: {err}"))?;
let cpp = 3;
let w = 512;
let h = 512;
let mut data = vec![0; h * w * cpp];
decompressor.decode_sony(&mut data, 0, w * cpp, w * cpp, h, dummy)?;
interpolate_yuv(decompressor.super_h(), decompressor.super_v(), w * cpp, h, &mut data);
let mut strip = &mut *strip;
for line in data.chunks_exact(w * cpp) {
let base = col * twidth * cpp;
strip[base..base + w * cpp].copy_from_slice(line);
// Now move output strip by one row.
strip = &mut strip[width * cpp..];
}
}
Ok(())
}),
)?;
// Convert YC'bC'r data to RGB.
ycbcr_to_rgb(&mut image.data);
Ok(image)
} else if cpp == 1 {
decode_threaded_multiline(
width,
height,
tlength,
dummy,
&(|strip: &mut [u16], row| {
let row = row / tlength;
for col in 0..coltiles {
let offset = offsets.force_usize(row * coltiles + col);
let src = &buffer[offset..];
let decompressor = LjpegDecompressor::new(src)?;
let cpp = 4;
let w = 256;
let h = 256;
let mut data = vec![0; h * w * cpp];
decompressor.decode(&mut data, 0, w * cpp, w * cpp, h, dummy)?;
let mut strip = &mut *strip;
for line in data.chunks_exact(1024) {
for (i, chunk) in line.chunks_exact(4).enumerate() {
// Unpack chunks of RGGB pixel data into two output lines
// so the first line is RGRGRG and the second one is GBGBGB.
strip[col * twidth + i * 2 + 0] = chunk[0];
strip[col * twidth + i * 2 + 1] = chunk[1];
strip[width + col * twidth + i * 2 + 0] = chunk[2];
strip[width + col * twidth + i * 2 + 1] = chunk[3];
}
// Now move output strip by two rows.
strip = &mut strip[width * 2..];
}
}
Ok(())
}),
)
.map_err(RawlerError::DecoderFailed)
} else {
Err(RawlerError::unsupported(
camera,
format!("NRW files with LJPEG compression and unsupported cpp: {}", cpp),
))
}
}
fn get_params(&self, file: &RawSource) -> Result<ArwImageParams> {
let priv_offset = {
let tag = fetch_tiff_tag!(self.tiff, TiffCommonTag::DNGPrivateArea).get_data();
LEu32(tag, 0)
};
let priv_tiff = IFD::new(&mut file.reader(), priv_offset, 0, 0, Endian::Little, &[])?;
//priv_tiff.dump::<ExifTag>(0).iter().for_each(|line| println!("DUMPXX: {}", line));
let sony_offset = fetch_tiff_tag!(priv_tiff, TiffCommonTag::SonyOffset).force_u32(0);
let sony_length = fetch_tiff_tag!(priv_tiff, TiffCommonTag::SonyLength).force_usize(0);
// This tag is of type UNDEFINED and contains a 32 bit value
let sony_key = {
let tag = fetch_tiff_tag!(priv_tiff, TiffCommonTag::SonyKey).get_data();
LEu32(tag, 0)
};
let buffer = file.as_vec()?;
let decrypted_buf = ArwDecoder::sony_decrypt(&buffer, sony_offset as usize, sony_length, sony_key)?;
let decrypted_tiff = IFD::new(&mut Cursor::new(decrypted_buf), 0, 0, -(sony_offset as i32), Endian::Little, &[])?;
let wb = self.get_wb(&decrypted_tiff)?;
let blacklevel = self.get_blacklevel(&decrypted_tiff);
let whitelevel = self.get_whitelevel(&decrypted_tiff);
Ok(ArwImageParams { wb, blacklevel, whitelevel })
}
fn get_blacklevel(&self, sr2: &IFD) -> Option<[u16; 4]> {
if let Some(entry) = sr2.get_entry(SR2SubIFD::BlackLevel2) {
if entry.count() == 4 {
return Some([entry.force_u16(0), entry.force_u16(1), entry.force_u16(2), entry.force_u16(3)]);
} else {
return Some([entry.force_u16(0), entry.force_u16(0), entry.force_u16(0), entry.force_u16(0)]);
}
}
if let Some(entry) = sr2.get_entry(SR2SubIFD::BlackLevel1) {
if entry.count() == 4 {
return Some([entry.force_u16(0), entry.force_u16(1), entry.force_u16(2), entry.force_u16(3)]);
} else {
return Some([entry.force_u16(0), entry.force_u16(0), entry.force_u16(0), entry.force_u16(0)]);
}
}
None
}
fn get_whitelevel(&self, sr2: &IFD) -> Option<[u16; 4]> {
if let Some(entry) = sr2.get_entry(SR2SubIFD::WhiteLevel) {
if entry.count() == 4 {
return Some([entry.force_u16(0), entry.force_u16(1), entry.force_u16(2), entry.force_u16(3)]);
} else {
return Some([entry.force_u16(0), entry.force_u16(0), entry.force_u16(0), entry.force_u16(0)]);
}
}
None
}
fn get_wb(&self, sr2: &IFD) -> Result<[f32; 4]> {
let grbg_levels = sr2.get_entry(SR2SubIFD::SonyGRBG);
let rggb_levels = sr2.get_entry(SR2SubIFD::SonyRGGB);
if let Some(levels) = grbg_levels {
Ok(normalize_wb([
levels.force_u32(1) as f32,
levels.force_u32(0) as f32,
levels.force_u32(3) as f32,
levels.force_u32(2) as f32,
]))
} else if let Some(levels) = rggb_levels {
Ok(normalize_wb([
levels.force_u32(0) as f32,
levels.force_u32(1) as f32,
levels.force_u32(2) as f32,
levels.force_u32(3) as f32,
]))
} else {
Err(RawlerError::DecoderFailed("ARW: Couldn't find GRGB or RGGB levels".to_string()))
}
}
fn get_curve(raw: &IFD) -> Result<LookupTable> {
let centry = fetch_tiff_tag!(raw, TiffCommonTag::SonyCurve);
let mut curve: [usize; 6] = [0, 0, 0, 0, 0, 4095];
for i in 0..4 {
curve[i + 1] = ((centry.force_u32(i) >> 2) & 0xfff) as usize;
}
Ok(Self::calculate_curve(curve))
}
pub(crate) fn calculate_curve(curve: [usize; 6]) -> LookupTable {
let mut out = vec![0_u16; curve[5] + 1];
for i in 0..5 {
for j in (curve[i] + 1)..(curve[i + 1] + 1) {
out[j] = out[j - 1] + (1 << i);
}
}
LookupTable::new(&out)
}
pub(crate) fn sony_decrypt(buf: &[u8], offset: usize, length: usize, key: u32) -> crate::Result<Vec<u8>> {
if buf.len() < offset + 4 * (length / 4) {
return Err(RawlerError::DecoderFailed("sony_decrypt() failed: buffer to short".into()));
}
let mut pad: [u32; 128] = [0_u32; 128];
let mut mkey = key;
// Initialize the decryption pad from the key
for p in 0..4 {
mkey = mkey.wrapping_mul(48828125).wrapping_add(1);
pad[p] = mkey;
}
pad[3] = (pad[3] << 1) | ((pad[0] ^ pad[2]) >> 31);
for p in 4..127 {
pad[p] = ((pad[p - 4] ^ pad[p - 2]) << 1) | ((pad[p - 3] ^ pad[p - 1]) >> 31);
}
for p in 0..127 {
pad[p] = u32::from_be(pad[p]);
}
let mut out = Vec::with_capacity(length + 4);
//for i in 0..(length / 4 + 1) {
for i in 0..(length / 4) {
let p = i + 127;
pad[p & 127] = pad[(p + 1) & 127] ^ pad[(p + 1 + 64) & 127];
let output = LEu32(buf, offset + i * 4) ^ pad[p & 127];
out.push(((output >> 0) & 0xff) as u8);
out.push(((output >> 8) & 0xff) as u8);
out.push(((output >> 16) & 0xff) as u8);
out.push(((output >> 24) & 0xff) as u8);
}
Ok(out)
}
fn get_raw_image_size(&self, raw_ifd: &IFD) -> Result<Option<Rect>> {
if let Some(entry) = raw_ifd.get_entry(ExifTag::SonyRawImageSize) {
Ok(Some(Rect::new(Point::default(), Dim2::new(entry.force_usize(0), entry.force_usize(1)))))
} else {
Ok(None)
}
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("ARW raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1]; // G1 should be 1024 and we use this as divisor
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(ArwMakernoteTag);
/// Specific Makernotes tags.
/// These are only related to the Makernote IFD.
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum ArwMakernoteTag {
CameraInfo = 0x0010,
Tag_940C = 0x940C,
Tag_9050 = 0x9050,
Tag_9405 = 0x9405,
Tag_9416 = 0x9416, // replaces 0x9405 for the Sony ILCE-7SM3, from July 2020
}
/// Decipher/encipher Sony tag 0x2010, 0x900b, 0x9050 and 0x940x data
/// Extracted from exiftool, comment from PH:
/// This is a simple substitution cipher, so use a hardcoded translation table for speed.
/// The formula is: $c = ($b*$b*$b) % 249, where $c is the enciphered data byte
/// note that bytes with values 249-255 are not translated, and 0-1, 82-84,
/// 165-167 and 248 have the same enciphered value)
const fn sony_tag9cxx_decipher_table() -> [u8; 256] {
let mut tbl = [0; 256];
let mut i = 0;
loop {
if i >= 249 {
tbl[i] = i as u8;
} else {
tbl[i * i * i % 249] = i as u8;
}
i += 1;
if i >= tbl.len() {
break;
}
}
tbl
}
const SONY_TAG_940X_DECIPHER_TABLE: [u8; 256] = sony_tag9cxx_decipher_table();
fn sony_tag9cxx_decipher(data: &[u8]) -> Vec<u8> {
let mut buf = Vec::from(data);
buf.iter_mut().for_each(|v| *v = SONY_TAG_940X_DECIPHER_TABLE[*v as usize]);
buf
}
#[derive(Debug)]
struct ArwImageParams {
wb: [f32; 4],
blacklevel: Option<[u16; 4]>,
whitelevel: Option<[u16; 4]>,
}
crate::tags::tiff_tag_enum!(SR2SubIFD);
/// Specific Canon CR2 Makernotes tags.
/// These are only related to the Makernote IFD.
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum SR2SubIFD {
SonyGRBG = 0x7303,
SonyRGGB = 0x7313,
BlackLevel1 = 0x7300,
BlackLevel2 = 0x7310,
WhiteLevel = 0x787f,
}
+276
View File
@@ -0,0 +1,276 @@
use toml::Value;
use crate::CFA;
use crate::cfa::PlaneColor;
use crate::imgop::xyz::FlatColorMatrix;
use crate::imgop::xyz::Illuminant;
use std::collections::HashMap;
use super::BlackLevel;
use super::WhiteLevel;
/// Contains sanitized information about the raw image's properties
#[derive(Debug, Clone, Default)]
pub struct Camera {
pub make: String,
pub model: String,
pub mode: String,
pub clean_make: String,
pub clean_model: String,
pub remark: Option<String>,
pub filesize: usize,
pub raw_width: usize,
pub raw_height: usize,
//pub orientation: Orientation,
pub whitelevel: Option<Vec<u32>>,
pub blacklevel: Option<Vec<u32>>,
pub blackareah: Option<(usize, usize)>,
pub blackareav: Option<(usize, usize)>,
pub xyz_to_cam: [[f32; 3]; 4],
pub color_matrix: HashMap<Illuminant, FlatColorMatrix>,
pub cfa: CFA,
pub plane_color: PlaneColor,
// Active area relative to sensor size
pub active_area: Option<[usize; 4]>,
// Recommended area relative to sensor size
pub crop_area: Option<[usize; 4]>,
// Hint/Replacement for EXIF BITDEPTH info
pub bps: Option<usize>,
// The BPS of the output after decoding
pub real_bps: usize,
pub highres_width: usize,
pub default_scale: DefaultScale,
pub best_quality_scale: BestQualityScale,
pub hints: Vec<String>,
pub params: HashMap<String, Value>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DefaultScale(pub [[u32; 2]; 2]);
impl Default for DefaultScale {
fn default() -> Self {
Self([[1, 1], [1, 1]])
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BestQualityScale(pub [u32; 2]);
impl Default for BestQualityScale {
fn default() -> Self {
Self([1, 1])
}
}
impl Camera {
pub fn find_hint(&self, hint: &str) -> bool {
self.hints.contains(&hint.to_string())
}
pub fn param_usize(&self, name: &str) -> Option<usize> {
self.params.get(name).and_then(|p| p.as_integer()).map(|i| i as usize)
}
pub fn param_i32(&self, name: &str) -> Option<i32> {
self.params.get(name).and_then(|p| p.as_integer()).map(|i| i as i32)
}
pub fn param_str(&self, name: &str) -> Option<&str> {
self.params.get(name).and_then(|p| p.as_str())
}
pub fn make_blacklevel(&self, cpp: usize) -> Option<BlackLevel> {
self.blacklevel.as_ref().map(|x| {
if x.len() == 1 {
BlackLevel::new(&vec![x[0]; cpp], 1, 1, cpp)
} else if x.len() == self.cfa.width * self.cfa.height * cpp {
BlackLevel::new(x, self.cfa.width, self.cfa.height, cpp)
} else {
panic!("Invalid blacklevel data")
}
})
}
pub fn make_whitelevel(&self, cpp: usize) -> Option<WhiteLevel> {
self.whitelevel.as_ref().map(|x| {
if x.len() == 1 {
WhiteLevel(vec![x[0] as u32; cpp])
} else if x.len() == cpp {
WhiteLevel(x.clone())
} else {
panic!("Invalid whitelevel data")
}
})
}
pub fn update_from_toml(&mut self, ct: &toml::value::Table) {
for (name, val) in ct {
match name.as_ref() {
n @ "make" => {
self.make = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "model" => {
self.model = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "mode" => {
self.mode = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "clean_make" => {
self.clean_make = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "clean_model" => {
self.clean_model = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "remark" => {
self.remark = Some(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string());
}
n @ "whitepoint" => {
let white = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as u32;
self.whitelevel = Some(vec![white]);
}
n @ "blackpoint" => {
let black = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as u32;
self.blacklevel = Some(vec![black]);
}
n @ "blackareah" => {
let vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.blackareah = Some((vals[0].as_integer().unwrap() as usize, vals[1].as_integer().unwrap() as usize));
}
n @ "blackareav" => {
let vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.blackareav = Some((vals[0].as_integer().unwrap() as usize, vals[1].as_integer().unwrap() as usize));
}
"color_matrix" => {
if let Some(color_matrix) = val.as_table() {
for (illu_str, matrix) in color_matrix.into_iter() {
let illu = Illuminant::new_from_str(illu_str).unwrap();
let xyz_to_cam = matrix
.as_array()
.expect("color matrix must be array")
.iter()
.map(|a| a.as_float().expect("color matrix values must be float") as f32)
.collect();
self.color_matrix.insert(illu, xyz_to_cam);
}
} else {
eprintln!("Invalid matrix spec for {}", self.clean_model);
}
assert!(!self.color_matrix.is_empty());
}
n @ "active_area" => {
let crop_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let mut crop = [0, 0, 0, 0];
for (i, val) in crop_vals.iter().enumerate() {
crop[i] = val.as_integer().unwrap() as usize;
}
self.active_area = Some(crop);
}
n @ "crop_area" => {
let crop_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let mut crop = [0, 0, 0, 0];
for (i, val) in crop_vals.iter().enumerate() {
crop[i] = val.as_integer().unwrap() as usize;
}
self.crop_area = Some(crop);
}
n @ "color_pattern" => {
self.cfa = CFA::new(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)));
}
n @ "plane_color" => {
self.plane_color = PlaneColor::new(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)));
}
n @ "bps" => {
self.bps = Some(val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize);
}
n @ "real_bps" => {
self.real_bps = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "filesize" => {
self.filesize = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "raw_width" => {
self.raw_width = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "raw_height" => {
self.raw_height = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "highres_width" => {
self.highres_width = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "default_scale" => {
let scale_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let scale_h = scale_vals[0].as_array().expect("must be array");
let scale_v = scale_vals[1].as_array().expect("must be array");
let scale = [
[
scale_h[0].as_integer().expect("must be integer") as u32,
scale_h[1].as_integer().expect("must be integer") as u32,
],
[
scale_v[0].as_integer().expect("must be integer") as u32,
scale_v[1].as_integer().expect("must be integer") as u32,
],
];
self.default_scale = DefaultScale(scale);
}
n @ "best_quality_scale" => {
let scale_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.best_quality_scale = BestQualityScale([
scale_vals[0].as_integer().expect("must be integer") as u32,
scale_vals[1].as_integer().expect("must be integer") as u32,
]);
}
n @ "hints" => {
self.hints = Vec::new();
for hint in val.as_array().unwrap_or_else(|| panic!("{} must be an array", n)) {
self.hints.push(hint.as_str().expect("hints must be a string").to_string());
}
}
n @ "params" => {
for (name, val) in val.as_table().unwrap_or_else(|| panic!("{} must be a table", n)) {
self.params.insert(name.clone(), val.clone());
}
}
"model_aliases" => {}
"modes" => {} // ignore
key => {
panic!("Unknown key: {}", key);
}
}
}
}
pub fn new() -> Camera {
Camera {
make: "".to_string(),
model: "".to_string(),
mode: "".to_string(),
clean_make: "".to_string(),
clean_model: "".to_string(),
remark: None,
filesize: 0,
raw_width: 0,
raw_height: 0,
whitelevel: None,
blacklevel: None,
blackareah: None,
blackareav: None,
xyz_to_cam: [[0.0; 3]; 4],
color_matrix: HashMap::new(),
cfa: CFA::new(""),
plane_color: PlaneColor::default(),
active_area: None,
crop_area: None,
bps: None,
real_bps: 16,
highres_width: usize::MAX,
default_scale: DefaultScale::default(),
best_quality_scale: BestQualityScale::default(),
hints: Vec::new(),
params: HashMap::new(),
//orientation: Orientation::Unknown,
}
}
}
+903
View File
@@ -0,0 +1,903 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use core::panic;
use image::DynamicImage;
use image::ImageBuffer;
use image::Rgb;
use log::debug;
use log::info;
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use serde::Deserialize;
use serde::Serialize;
use std::convert::TryFrom;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::alloc_image_plain;
use crate::analyze::FormatDump;
use crate::bits::LookupTable;
use crate::bits::clampbits;
use crate::decompressors::ljpeg::*;
use crate::exif::Exif;
use crate::formats::tiff::Entry;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::Rational;
use crate::formats::tiff::Value;
use crate::formats::tiff::reader::TiffReader;
use crate::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::BlackLevel;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::Result;
use super::WhiteLevel;
mod colordata;
pub(crate) use colordata::parse_colordata;
const CANON_EF_MOUNT: &str = "ef-mount";
const CANON_CN_MOUNT: &str = "cn-mount";
/// CR2 Decoder
pub struct Cr2Decoder<'a> {
#[allow(dead_code)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
exif: IFD,
makernote: Option<IFD>,
#[allow(dead_code)]
mode: Cr2Mode,
xpacket: Option<Vec<u8>>,
camera: Camera,
model_id: Option<u32>,
}
/// CR2 format encapsulation for analyzer
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Cr2Format {
tiff: GenericTiffReader,
}
impl<'a> Decoder for Cr2Decoder<'a> {
fn format_dump(&self) -> FormatDump {
FormatDump::Cr2(Cr2Format { tiff: self.tiff.clone() })
}
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
/*
for (i, ifd) in self.tiff.chains().iter().enumerate() {
eprintln!("IFD {}", i);
for line in ifd_dump::<crate::tags::LegacyTiffRootTag>(ifd, 10) {
eprintln!("{}", line);
}
}
*/
let camera = &self.camera;
let (raw, offset) = {
if let Some(raw) = self.tiff.find_first_ifd(TiffCommonTag::Cr2Id) {
(raw, fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0))
} else if let Some(raw) = self.tiff.find_first_ifd(TiffCommonTag::CFAPattern) {
(raw, fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0))
} else if let Some(off) = self.makernote.as_ref().and_then(|md| md.get_entry(TiffCommonTag::Cr2OldOffset)) {
// Old Canon TIF files contains the offset in makernote tags
(self.tiff.root_ifd(), off.value.force_usize(0))
} else {
return Err(RawlerError::DecoderFailed("CR2: Couldn't find raw info".to_string()));
}
};
// We don't have an excact length, so read until end.
let src = file.subview_until_eof(offset as u64)?;
let (cpp, image) = {
let decompressor = LjpegDecompressor::new(src)?;
let ljpegwidth = decompressor.width();
let mut width = ljpegwidth;
let mut height = decompressor.height();
let cpp = if decompressor.super_h() == 2 { 3 } else { 1 };
debug!("CR2 ljpeg components: {}", decompressor.components());
debug!("CR2 final cpp: {}", cpp);
debug!("CR2 dimension: {},{}", width / cpp, height);
let mut ljpegout = alloc_image_plain!(width, height, dummy);
if !dummy {
decompressor.decode(ljpegout.pixels_mut(), 0, width, width, height, dummy)?;
}
//crate::devtools::dump_image_u16(&ljpegout, width, height, "/tmp/cr2_before_striped.pnm");
// Linearize the output (applies only to D2000 as far as I can tell)
if !dummy && camera.find_hint("linearization") {
let table = {
let linearization = fetch_tiff_tag!(raw, TiffCommonTag::GrayResponse);
let mut t = [0_u16; 4096];
for i in 0..t.len() {
t[i] = linearization.force_u16(i);
}
LookupTable::new(&t)
};
let mut random = ljpegout[0] as u32;
for p in ljpegout.pixels_mut().iter_mut() {
*p = table.dither(*p, &mut random);
}
}
if cpp == 3 {
if raw.has_entry(TiffCommonTag::ImageWidth) {
width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0) * cpp;
height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
} else if width / cpp < height {
let temp = width / cpp;
width = height * cpp;
height = temp;
}
} else if camera.find_hint("double_line") {
width /= 2;
height *= 2;
}
debug!("CR2 dimension2: {},{}", width / cpp, height);
// Take each of the vertical fields and put them into the right location
// FIXME: Doing this at the decode would reduce about 5% in runtime but I haven't
// been able to do it without hairy code
if let Some(canoncol) = raw.get_entry(TiffCommonTag::Cr2StripeWidths) {
debug!("Found Cr2StripeWidths tag: {:?}", canoncol.value);
if canoncol.value.force_usize(0) == 0 {
if cpp == 3 {
self.convert_to_rgb(file, camera, &decompressor, width, height, ljpegout.pixels_mut(), dummy)?;
//width /= 3;
}
ljpegout.update_dimension(Dim2::new(width, height));
(cpp, ljpegout)
/*
if camera.find_hint("double_line") {
(width, height, cpp, PixU16::new_with(ljpegout.into_inner(), width, height))
} else {
(width, height, cpp, ljpegout)
}
*/
} else {
let mut out = alloc_image_plain!(width, height, dummy);
if !dummy {
let mut fieldwidths = Vec::new();
debug_assert!(canoncol.value.force_usize(0) > 0);
debug_assert!(canoncol.value.force_usize(1) > 0);
debug_assert!(canoncol.value.force_usize(2) > 0);
for _ in 0..canoncol.value.force_usize(0) {
fieldwidths.push(canoncol.value.force_usize(1));
}
fieldwidths.push(canoncol.value.force_usize(2));
if decompressor.super_v() == 2 {
debug!("CR2 v=2 decoder used, h={}", decompressor.super_h());
// We've decoded 2 lines at a time so we also need to copy two strips at a time
let nfields = fieldwidths.len();
let fieldwidth = fieldwidths[0];
let mut fieldstart = 0;
let mut inpos = 0;
for _ in 0..nfields {
for row in (0..height).step_by(2) {
for col in (0..fieldwidth).step_by(3) {
let outpos = row * width + fieldstart + col;
out[outpos..outpos + 3].copy_from_slice(&ljpegout[inpos..inpos + 3]);
let outpos = (row + 1) * width + fieldstart + col;
let inpos2 = inpos + ljpegwidth;
out[outpos..outpos + 3].copy_from_slice(&ljpegout[inpos2..inpos2 + 3]);
inpos += 3;
if inpos % ljpegwidth == 0 {
// we've used a full input line and we're reading 2 by 2 so skip one
inpos += ljpegwidth;
}
}
}
fieldstart += fieldwidth;
}
} else {
let sh = decompressor.super_h();
debug!("CR2 v=1 decoder used, super_h: {}", sh);
let mut fieldstart = 0;
let mut fieldpos = 0;
for fieldwidth in fieldwidths {
// fix the inconsistent slice width in sRaw mode, ask Canon.
let fieldwidth = fieldwidth / sh * cpp;
// The output for full height of a vertical stripe is
// composed by the lines of all input stripes N:
// outb(line0) = slice[0](line[0])
// outb(line1) = slice[1](line[0])
// outb(line2) = slice[N-1](line[0])
for row in 0..height {
let outpos = row * width + fieldstart;
let inpos = fieldpos + row * fieldwidth;
let outb = &mut out[outpos..outpos + fieldwidth];
let inb = &ljpegout[inpos..inpos + fieldwidth];
outb.copy_from_slice(inb);
}
fieldstart += fieldwidth;
fieldpos += fieldwidth * height;
}
}
}
if cpp == 3 {
self.convert_to_rgb(file, camera, &decompressor, width, height, out.pixels_mut(), dummy)?;
//width /= 3;
}
(cpp, out)
//(width, height, cpp, out)
}
} else {
ljpegout.update_dimension(Dim2::new(width, height));
(cpp, ljpegout)
// (width, height, cpp, PixU16::new_with(ljpegout.into_inner(), width, height))
}
};
let wb = self.get_wb(file, camera)?;
debug!("CR2 WB: {:?}", wb);
//assert_eq!(image.width, width * cpp);
let blacklevel = self.get_blacklevel(camera, cpp)?;
let whitelevel = self.get_whitelevel(cpp)?;
let photometric = match cpp {
1 => RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera)),
3 => RawPhotometricInterpretation::LinearRaw,
_ => todo!(),
};
let mut img = RawImage::new(camera.clone(), image, cpp, wb, photometric, blacklevel, whitelevel, dummy);
if let Some(file_crop) = self.get_sensor_area()? {
assert!(
img.crop_area.is_none(),
"Camera {} has embedded crop params, remove crop from config file!",
self.camera.clean_make
);
img.crop_area = Some(file_crop);
} else {
//panic!("Camera {} has no embedded crops, but all CR2 should contain them?!", self.camera.clean_make);
// 1D and D2000C has no crops!
}
if cpp == 3 {
// We have a sRAW or mRAW: the active_area from camera config is invalid now!
// We just apply the crop_area that comes from metadata, which is correct.
img.active_area = img.crop_area;
}
debug!("Blacklevel: {:?}", img.blacklevel);
debug!("Whitelevel: {:?}", img.whitelevel);
debug!("Black areas: {:?}", img.blackareas);
debug!("Active area: {:?}", img.active_area);
debug!("Crop area: {:?}", img.crop_area);
Ok(img)
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(self.tiff.root_ifd())?;
let mdata = RawMetadata::new_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn xpacket(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<Option<Vec<u8>>> {
Ok(self.xpacket.clone())
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
// For CR2, there is a full resolution image in IFD0.
// This is compressed with old-JPEG compression (Compression = 6)
let root_ifd = &self.tiff.root_ifd();
let buf = root_ifd
.singlestrip_data_rawsource(file)
.map_err(|e| RawlerError::DecoderFailed(format!("Failed to get strip data: {}", e)))?;
let compression = root_ifd.get_entry(TiffCommonTag::Compression).ok_or("Missing tag")?.force_usize(0);
let width = fetch_tiff_tag!(root_ifd, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(root_ifd, TiffCommonTag::ImageLength).force_usize(0);
if compression == 1 {
Ok(Some(DynamicImage::ImageRgb8(
ImageBuffer::<Rgb<u8>, Vec<u8>>::from_raw(width as u32, height as u32, buf.to_vec())
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to read uncompressed image")))?,
)))
} else {
let img = image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG image: {:?}", err)))?;
Ok(Some(img))
}
}
fn format_hint(&self) -> FormatHint {
FormatHint::CR2
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
enum Cr2Mode {
Raw,
Sraw1,
Sraw2,
}
impl<'a> Cr2Decoder<'a> {
fn get_mode(makernote: &Option<IFD>) -> Result<Cr2Mode> {
if let Some(settings) = makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::CameraSettings)) {
match settings.get_u16(46) {
Ok(Some(0)) => Ok(Cr2Mode::Raw),
Ok(Some(1)) => Ok(Cr2Mode::Sraw1),
Ok(Some(2)) => Ok(Cr2Mode::Sraw2),
Ok(None) => Ok(Cr2Mode::Raw),
Ok(Some(v)) => Err(RawlerError::DecoderFailed(format!("Unknown sraw quality value found: {}", v))),
Err(_) => Err(RawlerError::DecoderFailed("Unknown sraw quality value".to_string())),
}
} else {
Ok(Cr2Mode::Raw)
}
}
/// Construct new CR2 decoder
/// This parses the RawFile again to include specific sub IFDs.
pub fn new(file: &RawSource, _tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<Cr2Decoder<'a>> {
debug!("CR2 decoder choosen");
// Parse the TIFF again, with custom settings
let tiff = GenericTiffReader::new(&mut file.reader(), 0, 0, None, &[33424])?;
let exif = Self::new_exif_ifd(file, &tiff, rawloader)?;
let makernote = Self::new_makernote(file, &tiff, &exif, rawloader)?;
let mode = Self::get_mode(&makernote)?;
debug!("sRaw quality: {:?}", mode);
let mode_str = match mode {
Cr2Mode::Raw => "",
Cr2Mode::Sraw1 => "sRaw1",
Cr2Mode::Sraw2 => "sRaw2",
};
let camera = rawloader.check_supported_with_mode(tiff.root_ifd(), mode_str)?;
let xpacket = Self::read_xpacket(file, &tiff, rawloader)?;
let model_id = makernote
.as_ref()
.and_then(|mn| mn.get_entry(Cr2MakernoteTag::ModelId).and_then(|v| v.get_u32(0).transpose()))
.transpose()
.map_err(|_| RawlerError::DecoderFailed("CR2: invalid model id".to_string()))?;
Ok(Cr2Decoder {
tiff,
rawloader,
exif,
makernote,
mode,
xpacket,
camera,
model_id,
})
}
/// Search for EXIF IFD, if not found, fallback to root IFD.
/// This is useful for EOS D2000 where EXIF tags are located in the root.
fn new_exif_ifd(_file: &RawSource, tiff: &GenericTiffReader, _rawloader: &RawLoader) -> Result<IFD> {
if let Some(exif_ifd) = tiff
.root_ifd()
.sub_ifds()
.get(&TiffCommonTag::ExifIFDPointer.into())
.and_then(|subs| subs.get(0))
{
Ok(exif_ifd.clone())
} else {
debug!("No EXIF IFD found, fallback to root IFD");
Ok(tiff.root_ifd().clone())
}
/*
if let Some(exif_ifd) = tiff.root_ifd().get_ifd(LegacyTiffRootTag::ExifIFDPointer, &mut file.reader())? {
return Ok(exif_ifd);
} else {
return Ok(tiff.root_ifd().clone());
}
*/
}
fn get_focal_len(&self) -> Result<Option<Rational>> {
if let Some(Entry {
value: Value::Short(focal), ..
}) = self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::FocalLen))
{
return Ok(focal.get(1).map(|v| Rational::new(*v as u32, 1)));
}
Ok(None)
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
let exif_lens_name = if let Some(Entry {
value: Value::Ascii(lens_id), ..
}) = self.exif.get_entry(ExifTag::LensModel)
{
lens_id.strings().get(0)
} else {
None
};
match self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::CameraSettings)) {
Some(Entry {
value: Value::Short(settings), ..
}) => {
let lens_info = settings[22];
debug!("Lens Info tag: {}", lens_info);
let resolver = LensResolver::new()
.with_lens_keyname(exif_lens_name)
.with_camera(&self.camera) // must follow with_lens_keyname() as it my override key
.with_lens_id((lens_info as u32, 0))
.with_focal_len(self.get_focal_len()?)
.with_mounts(&[CANON_CN_MOUNT.into(), CANON_EF_MOUNT.into()]);
return Ok(resolver.resolve());
}
_ => {
log::warn!("Camera settings in makernote not found, no lens data available");
}
}
Ok(None)
}
/// Parse the Canon makernote IFD
fn new_makernote(file: &RawSource, tiff: &GenericTiffReader, exif_ifd: &IFD, _rawloader: &RawLoader) -> Result<Option<IFD>> {
if let Some(entry) = exif_ifd.get_entry(TiffCommonTag::Makernote) {
let offset = entry.offset().expect("Makernote internal offset is not present but should be");
let makernote = tiff.parse_ifd(&mut file.reader(), offset as u32, 0, 0, exif_ifd.endian, &[])?;
return Ok(Some(makernote));
}
info!("No makernote tag found");
Ok(None)
}
/// Read XMP data from TIFF entry
/// This is useful as it stores the image rating (if present).
fn read_xpacket(_file: &RawSource, tiff: &GenericTiffReader, _rawloader: &RawLoader) -> Result<Option<Vec<u8>>> {
if let Some(entry) = tiff.root_ifd().get_entry(TiffCommonTag::Xmp) {
if let Entry { value: Value::Byte(xmp), .. } = entry {
Ok(Some(xmp.clone()))
} else {
Err("Image has XMP data but invalid tag type!".into())
}
} else {
Ok(None)
}
}
/*
pub fn new_makernote(buf: &'a[u8], offset: usize, base_offset: usize, chain_level: isize, e: Endian) -> Result<LegacyTiffIFD<'a>> {
let mut off = 0;
let data = &buf[offset..];
let mut endian = e;
// Some have MM or II to indicate endianness - read that
if data[off..off+2] == b"II"[..] {
off +=2;
endian = Endian::Little;
} if data[off..off+2] == b"MM"[..] {
off +=2;
endian = Endian::Big;
}
Ok(LegacyTiffIFD::new(buf, offset+off, base_offset, 0, chain_level+1, endian, &vec![])?)
}
*/
/// Build firmware value from string
fn get_firmware(&self) -> Result<Option<u32>> {
Ok(
match self
.makernote
.as_ref()
.and_then(|mn| mn.get_entry(Cr2MakernoteTag::FirmareVer).and_then(|v| v.as_string()))
{
Some(fw) => {
let str: String = fw.chars().filter(|c| c.is_ascii_digit() || c == &'.').collect();
let v: Vec<u8> = str.split('.').map(|v| v.parse().expect("Only digits here")).collect();
Some(v.iter().rev().enumerate().map(|(i, v)| 10_u32.pow(i as u32 * 3) * *v as u32).sum())
}
None => None,
},
)
}
/// Get the SRAW white balance coefficents from COLORDATA tag
/// The offsets are always at offset 78.
/// These coefficents are used for SRAW YUV2RGB conversion.
fn get_sraw_wb(&self, rawfile: &RawSource, _cam: &Camera) -> Result<[f32; 4]> {
if let Some(levels) = self
.makernote
.as_ref()
.and_then(|mn| mn.get_entry_raw(Cr2MakernoteTag::ColorData, &mut rawfile.reader()).transpose())
.transpose()?
{
let offset = 78;
return Ok([
levels.get_force_u16(offset) as f32,
(levels.get_force_u16(offset + 1) as f32 + levels.get_force_u16(offset + 2) as f32) / 2.0,
levels.get_force_u16(offset + 3) as f32,
f32::NAN,
]);
}
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
/// Get the white balance coefficents from COLORDATA tag
/// The offsets are different, so we take the offset from camera params.
fn get_wb(&self, rawfile: &RawSource, _cam: &Camera) -> Result<[f32; 4]> {
if let Some(colordata) = self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::ColorData)) {
let raw_wb = colordata::parse_colordata(colordata)?.wb;
return Ok(normalize_wb(raw_wb));
}
// TODO: check if these tags belongs to RootIFD or makernote
if let Some(levels) = self.tiff.get_entry_raw(TiffCommonTag::Cr2PowerShotWB, &mut rawfile.reader())? {
Ok([
levels.get_force_u32(3) as f32,
levels.get_force_u32(2) as f32,
levels.get_force_u32(4) as f32,
f32::NAN,
])
} else if let Some(levels) = self.tiff.get_entry(TiffCommonTag::Cr2OldWB) {
Ok([levels.force_f32(0), levels.force_f32(1), levels.force_f32(2), f32::NAN])
} else {
// At least the D2000 has no WB
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
}
/// Get the black level from COLORDATA tag
/// The offsets are different, so we take the offset from camera params.
fn get_blacklevel(&self, cam: &Camera, cpp: usize) -> Result<Option<BlackLevel>> {
if let Some(colordata) = self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::ColorData)) {
if let Some(blacklevel) = colordata::parse_colordata(colordata)?.blacklevel {
match cpp {
1 => return Ok(Some(BlackLevel::new(&blacklevel, cam.cfa.width, cam.cfa.height, cpp))),
3 => {
let avg = blacklevel.into_iter().sum::<u16>() / 4;
let levels: [u16; 3] = [avg, avg, avg];
return Ok(Some(BlackLevel::new(&levels, 1, 1, cpp)));
}
_ => unreachable!(),
}
}
}
Ok(None)
}
/// Get the white level from COLORDATA tag
/// The offsets are different, so we take the offset from camera params.
fn get_whitelevel(&self, cpp: usize) -> Result<Option<WhiteLevel>> {
if let Some(colordata) = self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::ColorData)) {
if let Some(whitelevel) = colordata::parse_colordata(colordata)?.specular_whitelevel {
return Ok(Some(WhiteLevel(vec![whitelevel as u32; cpp])));
}
}
Ok(None)
}
/// Get the SENSOR information, if available
/// If not, fall back to sensor dimension reported by width/hight values.
fn get_sensor_area(&self) -> Result<Option<Rect>> {
if let Some(sensorinfo) = self.makernote.as_ref().and_then(|mn| mn.get_entry(Cr2MakernoteTag::SensorInfo)) {
match &sensorinfo.value {
Value::Short(v) => {
debug!("Sensor info: {:?}", v);
let _w = v[1] as usize;
let _h = v[2] as usize;
let left = v[5] as usize;
let top = v[6] as usize;
let right = v[7] as usize;
let bottom = v[8] as usize;
Ok(Some(Rect::new_with_points(Point::new(left, top), Point::new(right + 1, bottom + 1))))
}
_ => Err(RawlerError::DecoderFailed("Makernote contains invalid type for SensorInfo tag".to_string())),
}
} else {
Ok(None)
}
}
/// Interpolate YCbCr (YUV) data
fn interpolate_yuv(&self, ljpeg: &LjpegDecompressor, width: usize, _height: usize, image: &mut [u16]) {
if ljpeg.super_h() == 1 && ljpeg.super_v() == 1 {
return; // No interpolation needed
}
// Iterate over a block of 3 rows, smaller chunks are okay
// but must always a multiple of row width.
image.par_chunks_mut(width * 3).for_each(|slice| {
// Do horizontal interpolation.
// [y1 Cb Cr ] [ y2 . . ] [y1 Cb Cr ] [ y2 . . ] ...
if ljpeg.super_h() == 2 {
debug_assert_eq!(slice.len() % width, 0);
for row in 0..(slice.len() / width) {
for col in (6..width).step_by(6) {
let pix1 = row * width + col - 6;
let pix2 = pix1 + 3;
let pix3 = row * width + col;
slice[pix2 + 1] = ((slice[pix1 + 1] as i32 + slice[pix3 + 1] as i32 + 1) / 2) as u16;
slice[pix2 + 2] = ((slice[pix1 + 2] as i32 + slice[pix3 + 2] as i32 + 1) / 2) as u16;
}
}
}
// Do vertical interpolation
// pixel n pixel n+1 pixel n+2 pixel n+3 ...
// row i : [y1 Cb Cr ] [ y2 Cb* Cr* ] [y1 Cb Cr ] [ y2 Cb* Cr* ] ...
// row i+1: [y3 Cb* Cr*] [ y4 Cb** Cr**] [y3 Cb* Cr*] [ y4 Cb** Cr**] ...
// row i+2: [y1 Cb Cr ] [ y2 Cb* Cr* ] [y1 Cb Cr ] [ y2 Cb* Cr* ] ...
// row i+3: [y3 Cb* Cr*] [ y4 Cb** Cr**] [y3 Cb* Cr*] [ y4 Cb** Cr**] ...
if ljpeg.super_v() == 2 && slice.len() == width * 3 {
for col in (0..width).step_by(3) {
let pix1 = col;
let pix2 = width + col;
let pix3 = 2 * width + col;
slice[pix2 + 1] = ((slice[pix1 + 1] as i32 + slice[pix3 + 1] as i32 + 1) / 2) as u16;
slice[pix2 + 2] = ((slice[pix1 + 2] as i32 + slice[pix3 + 2] as i32 + 1) / 2) as u16;
}
}
});
/* Old non-parallel code
if ljpeg.super_h() == 2 {
for row in 0..height {
for col in (6..width).step_by(6) {
let pix1 = row * width + col - 6;
let pix2 = pix1 + 3;
let pix3 = row * width + col;
image[pix2 + 1] = ((image[pix1 + 1] as i32 + image[pix3 + 1] as i32 + 1) / 2) as u16;
image[pix2 + 2] = ((image[pix1 + 2] as i32 + image[pix3 + 2] as i32 + 1) / 2) as u16;
}
}
}
if ljpeg.super_v() == 2 {
for row in (1..height - 1).step_by(2) {
for col in (0..width).step_by(3) {
let pix1 = (row - 1) * width + col;
let pix2 = row * width + col;
let pix3 = (row + 1) * width + col;
image[pix2 + 1] = ((image[pix1 + 1] as i32 + image[pix3 + 1] as i32 + 1) / 2) as u16;
image[pix2 + 2] = ((image[pix1 + 2] as i32 + image[pix3 + 2] as i32 + 1) / 2) as u16;
}
}
}
*/
}
/// Convert YCbCr (YUV) data to linear RGB
fn convert_to_rgb(
&self,
rawfile: &RawSource,
cam: &Camera,
ljpeg: &LjpegDecompressor,
width: usize,
height: usize,
image: &mut [u16],
dummy: bool,
) -> Result<()> {
debug!("YUV2RGB: Regular WB: {:?}", self.get_wb(rawfile, cam));
debug!("YUV2RGB: SRAW WB: {:?}", self.get_sraw_wb(rawfile, cam));
debug!("Model ID: 0x{:X}", self.model_id.unwrap_or(0));
if dummy {
return Ok(());
}
let do_interpolate = std::env::var("RAWLER_CR2_YUV_INTERPOLATE")
.ok()
.map(|id| id.parse::<bool>().expect("RAWLER_CR2_YUV_INTERPOLATE must by of type bool"))
.unwrap_or(true);
if do_interpolate {
self.interpolate_yuv(ljpeg, width, height, image);
}
let coeffs = self.get_sraw_wb(rawfile, cam)?;
let (c1, c2, c3) = if cam.find_hint("invert_sraw_wb") {
let c1 = (1024.0 * 1024.0 / coeffs[0]) as i32;
let c2 = coeffs[1] as i32;
let c3 = (1024.0 * 1024.0 / coeffs[2]) as i32;
(c1, c2, c3)
} else {
(coeffs[0] as i32, coeffs[1] as i32, coeffs[2] as i32)
};
// Starting with 40D, sRaw format was introduced. This uses
// version 0. With 5D Mark II, version 1 gets used.
// And with 5D Mark III, back to version 0 method
// but without an offset of 512 for y.
let version = if cam.find_hint("sraw_40d") {
0
} else if cam.find_hint("sraw_new") {
2
} else {
1
};
let fw = self.get_firmware()?;
debug!("Firmware: {:?}", fw);
// This magic comes from dcraw.
// Seems to because of rounding during interpolation, we need to
// adjust the hue a little bit (only guessing)
let hue = match self.model_id {
None => 0,
Some(model_id) => {
if model_id >= 0x80000281 || (model_id == 0x80000218 && fw.unwrap_or(0) > 1000006) {
(((ljpeg.super_h() * ljpeg.super_v()) - 1) >> 1) as i32
} else {
(ljpeg.super_h() * ljpeg.super_v()) as i32
}
}
};
debug!("SRAW hue correction: {:?}", hue);
// Now calculate RGB for each YUV tuple.
image.par_chunks_exact_mut(3).for_each(|pix| {
let y = pix[0] as i32;
let cb = pix[1] as i32 - 16383;
let cr = pix[2] as i32 - 16383;
match version {
0 => {
let y = y - 512; // correction for 40D and others
let r = c1 * (y + cr);
let g = c2 * (y + ((-778 * cb - (cr << 11)) >> 12));
let b = c3 * (y + cb);
pix[0] = clampbits(r >> 8, 16);
pix[1] = clampbits(g >> 8, 16);
pix[2] = clampbits(b >> 8, 16);
}
1 => {
// found in EOS 5D Mark II
let cb = (cb << 2) + hue;
let cr = (cr << 2) + hue;
let r = c1 * (y + ((50 * cb + 22929 * cr) >> 14));
let g = c2 * (y + ((-5640 * cb - 11751 * cr) >> 14));
let b = c3 * (y + ((29040 * cb - 101 * cr) >> 14));
pix[0] = clampbits(r >> 8, 16);
pix[1] = clampbits(g >> 8, 16);
pix[2] = clampbits(b >> 8, 16);
}
2 => {
// found in EOS 5D Mark III and others
let r = c1 * (y + cr);
let g = c2 * (y + ((-778 * cb - (cr << 11)) >> 12));
let b = c3 * (y + cb);
pix[0] = clampbits(r >> 8, 16);
pix[1] = clampbits(g >> 8, 16);
pix[2] = clampbits(b >> 8, 16);
}
_ => {
unreachable!()
}
}
});
Ok(())
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("CR2 raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1]; // G1 should be 1024 and we use this as divisor
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(Cr2MakernoteTag);
/// Specific Canon CR2 Makernotes tags.
/// These are only related to the Makernote IFD.
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum Cr2MakernoteTag {
CameraSettings = 0x0001,
FocalLen = 0x0002,
FlashInfo = 0x0003,
ShotInfo = 0x0004,
Panorama = 0x0005,
ImageType = 0x0006,
FirmareVer = 0x0007,
FileNumber = 0x0008,
OwnerName = 0x0009,
UnknownD30 = 0x000a,
SerialNum = 0x000c,
CameraInfo = 0x000d,
FileLen = 0x000e,
CustomFunc = 0x000f,
ModelId = 0x0010,
MovieInfo = 0x0011,
AFInfo = 0x0012,
ThumbArea = 0x0013,
SerialFormat = 0x0014,
SuperMacro = 0x001a,
DateStampMode = 0x001c,
MyColors = 0x001d,
FirmwareRev = 0x001e,
Categories = 0x0023,
FaceDetect1 = 0x0024,
FaceDetect2 = 0x0025,
AFInfo2 = 0x0026,
ContrastInfo = 0x0027,
ImgUniqueID = 0x0028,
WBInfo = 0x0029,
FaceDetect3 = 0x002f,
TimeInfo = 0x0035,
BatteryType = 0x0038,
AFInfo3 = 0x003c,
RawDataOffset = 0x0081,
OrigDecisionDataOffset = 0x0083,
CustomFunc1D = 0x0090,
PersFunc = 0x0091,
PersFuncValues = 0x0092,
FileInfo = 0x0093,
AFPointsInFocus1D = 0x0094,
LensModel = 0x0095,
InternalSerial = 0x0096,
DustRemovalData = 0x0097,
CropInfo = 0x0098,
CustomFunc2 = 0x0099,
AspectInfo = 0x009a,
ProcessingInfo = 0x00a0,
ToneCurveTable = 0x00a1,
SharpnessTable = 0x00a2,
SharpnessFreqTable = 0x00a3,
WhiteBalanceTable = 0x00a4,
ColorBalance = 0x00a9,
MeasuredColor = 0x00aa,
ColorTemp = 0x00ae,
CanonFlags = 0x00b0,
ModifiedInfo = 0x00b1,
TnoeCurveMatching = 0x00b2,
WhiteBalanceMatching = 0x00b3,
ColorSpace = 0x00b4,
PreviewImageInfo = 0x00b6,
VRDOffset = 0x00d0,
SensorInfo = 0x00e0,
ColorData = 0x4001,
CRWParam = 0x4002,
ColorInfo = 0x4003,
Flavor = 0x4005,
PictureStyleUserDef = 0x4008,
PictureStylePC = 0x4009,
CustomPictureStyleFileName = 0x4010,
AFMicroAdj = 0x4013,
VignettingCorr = 0x4015,
VignettingCorr2 = 0x4016,
LightningOpt = 0x4018,
LensInfo = 0x4019,
AmbienceInfo = 0x4020,
MultiExp = 0x4021,
FilterInfo = 0x4024,
HDRInfo = 0x4025,
AFConfig = 0x4028,
RawBurstModeRoll = 0x403f,
}
//const CR2_MODEL_40D: u32 = 0x80000190;
+145
View File
@@ -0,0 +1,145 @@
use crate::{
RawlerError, Result,
formats::tiff::{Entry, Value},
};
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) struct ColorData {
pub(crate) version: i16,
pub(crate) wb: [f32; 4],
pub(crate) blacklevel: Option<[u16; 4]>,
pub(crate) normal_whitelevel: Option<u16>,
pub(crate) specular_whitelevel: Option<u16>,
}
impl ColorData {
fn new(data: &[u16], version: i16, wb_off: usize, black_off: Option<usize>, norm_white_off: Option<usize>, specular_white_off: Option<usize>) -> Self {
log::debug!("Found Canon COLORDATA version: {}, data len: {}", version, data.len());
let wb = [data[wb_off] as f32, data[wb_off + 1] as f32, data[wb_off + 2] as f32, data[wb_off + 3] as f32];
let blacklevel = black_off.map(|off| [data[off], data[off + 1], data[off + 2], data[off + 3]]);
let normal_whitelevel = norm_white_off.map(|off| data[off]);
let specular_whitelevel = specular_white_off.map(|off| data[off]);
Self {
version,
wb,
blacklevel,
normal_whitelevel,
specular_whitelevel,
}
}
}
pub(crate) fn parse_colordata(colordata: &Entry) -> Result<ColorData> {
match &colordata.value {
Value::Undefined(undef) => {
let transmuted: Vec<u16> = undef.chunks(2).map(|v| ((v[1] as u16) << 8) | (v[0] as u16)).collect();
let data = &transmuted;
let version: i16 = data[0] as i16;
Ok(match version {
// -4 (M100/M5/M6)
-4 => ColorData::new(data, version, 0x47, Some(0x14d), Some(0x0569), Some(0x056a)),
// -3 (M10/M3)
-3 => ColorData::new(data, version, 0x47, Some(0x108), None, None),
_ => return Err(format!("Unknown2 COLORDATA version: {}", data[0]).into()),
})
/*
Ok(match data.len() {
// 20D and 350D
582 => ColorData::new(&data, version, 0x19, None, None, None),
// 1DmkII and 1DSmkII
653 => ColorData::new(&data, version, 0x22, None, None, None),
// 1DmkIIN, 5D, 30D, 400D
796 => ColorData::new(&data, version, 0x3f, Some(0xc4), None, None),
_ => panic!("COLORDATA count of {} is unknown", data.len())
})
*/
}
Value::Short(data) => {
match data.len() {
// 20D and 350D
582 => return Ok(ColorData::new(data, 0, 0x19, None, None, None)),
// 1DmkII and 1DSmkII
653 => return Ok(ColorData::new(data, 0, 0x22, None, None, None)),
// 1DmkIIN, 5D, 30D, 400D
796 => return Ok(ColorData::new(data, 0, 0x3f, Some(0xc4), None, None)),
_ => log::debug!("COLORDATA count of {} is unknown, continue with version matching", data.len()),
}
let version: i16 = data[0] as i16;
/*
for (i, item) in data.iter().enumerate() {
if *item > 127 {
eprintln!("0x{:>4x}: {}", i, item);
}
}
*/
Ok(match version {
// 1 = (1DmkIIN/5D/30D/400D)
1 => ColorData::new(data, version, 63, Some(196), None, None),
// 2 (1DmkIII)
// 3 (40D)
2 | 3 => ColorData::new(data, version, 0x3f, Some(0xe7), None, None),
// 4 (1DSmkIII)
// 5 (450D/1000D)
4 | 5 => ColorData::new(data, version, 0x3f, Some(0x2b4), Some(0x2b8), Some(0x2b9)),
// 6 (50D/5DmkII)
// 7 (500D/550D/7D/1DmkIV)
6 | 7 => ColorData::new(data, version, 0x3f, Some(0x2cb), Some(0x2cf), Some(0x2d0)),
// 9 (60D/1100D)
9 => ColorData::new(data, version, 0x3f, Some(0x2cf), Some(0x2d3), Some(0x2d4)),
// -4 (M100/M5/M6)
-4 => ColorData::new(data, version, 0x47, Some(0x14d), Some(0x0569), Some(0x056a)),
// -3 (M10/M3)
-3 => ColorData::new(data, version, 0x47, Some(0x108), None, None),
// 10 (600D/1200D)
// 10 (1DX/5DmkIII/6D/70D/100D/650D/700D/M/M2)
10 => {
if data.len() == 1273 || data.len() == 1275 {
ColorData::new(data, version, 0x3f, Some(0x1df), Some(0x1e3), Some(0x1e4))
} else {
ColorData::new(data, version, 0x3f, Some(0x1f8), Some(0x1fc), Some(0x1fd))
}
}
// 11 (7DmkII/750D/760D/8000D)
11 => ColorData::new(data, version, 0x3f, Some(0x2d8), Some(0x2dc), Some(0x2dd)),
// 12 (1DXmkII/5DS/5DSR)
12 => ColorData::new(data, version, 0x3f, Some(0x30a), Some(0x30e), Some(0x30f)),
// 13 (80D/5DmkIV)
13 => ColorData::new(data, version, 0x3f, Some(0x30a), Some(0x30e), Some(0x30f)),
// 14 (1300D/2000D/4000D)
14 => ColorData::new(data, version, 0x3f, Some(0x22c), Some(0x230), Some(0x231)),
// 15 (6DmkII/77D/200D/800D,9000D)
15 => ColorData::new(data, version, 0x3f, Some(0x30a), Some(0x30e), Some(0x30f)),
// 16 (M50)
// 17 (EOS R)
// 18 (EOS RP/250D)
// 19 (90D/850D/M6mkII/M200)
16 | 17 | 18 | 19 => ColorData::new(data, version, 0x47, Some(0x149), Some(0x31c), Some(0x31d)),
// 32 (1DXmkIII)
// 33 (R5/R6)
32 | 33 => ColorData::new(data, version, 0x55, Some(0x157), Some(0x32a), Some(0x32b)),
// 34 (R3)
34 => ColorData::new(data, version, 0x69, Some(0x16b), Some(0x280), Some(0x281)),
// 48 (R7/R10)
48 => ColorData::new(data, version, 0x69, Some(0x16b), Some(0x281), Some(0x282)),
// 64 (R5MK2), 66 (R6MK3)
64 | 66 => ColorData::new(data, version, 0x69, Some(0x17f), Some(0x294), Some(0x295)),
_ => return Err(format!("Unknown COLORDATA version: {}", data[0]).into()),
})
}
_ => Err(RawlerError::DecoderFailed(format!(
"Invalid COLORDATA tag: type {}",
colordata.value_type_name()
))),
}
}
+796
View File
@@ -0,0 +1,796 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use image::DynamicImage;
use log::{debug, warn};
use std::convert::{TryFrom, TryInto};
use std::fmt::Debug;
use crate::bits::Endian;
use crate::decoders::*;
use crate::decompressors::crx::decompress_crx_image;
use crate::envparams::{rawler_crx_raw_trak, rawler_ignore_previews};
use crate::exif::ExifGPS;
use crate::formats::bmff::FileBox;
use crate::formats::bmff::ext_cr3::cmp1::Cmp1Box;
use crate::formats::bmff::ext_cr3::cr3desc::Cr3DescBox;
use crate::formats::bmff::ext_cr3::iad1::{Iad1Box, Iad1Type};
use crate::formats::bmff::trak::TrakBox;
use crate::formats::tiff::reader::TiffReader;
use crate::formats::tiff::{Entry, GenericTiffReader, Rational, Value};
use crate::imgop::{Point, Rect};
use crate::lens::{LensDescription, LensId, LensResolver};
use crate::{RawImage, pumps::ByteStream};
const CANON_CN_MOUNT: &str = "cn-mount";
const CANON_EF_MOUNT: &str = "ef-mount";
const CANON_RF_MOUNT: &str = "rf-mount";
/// Decoder for CR3 and CRM files
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Cr3Decoder<'a> {
camera: Camera,
rawloader: &'a RawLoader,
bmff: Bmff,
// Basic EXIF information
cmt1: GenericTiffReader,
// EXIF
cmt2: GenericTiffReader,
// Makernotes
cmt3: GenericTiffReader,
// GPS
cmt4: GenericTiffReader,
// Metadata cache
md_cache: DecoderCache<Cr3Metadata>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
struct Cr3Metadata {
ctmd_exposure: Option<CtmdExposureInfo>,
ctmd_focallen: Option<Rational>,
ctmd_rec7_exif: Option<GenericTiffReader>,
ctmd_rec7_makernotes: Option<GenericTiffReader>,
// CTMD Makernotes: COLORDATA
ctmd_rec8: Option<GenericTiffReader>,
ctmd_rec9: Option<GenericTiffReader>,
xpacket: Option<Vec<u8>>,
image_unique_id: Option<[u8; 16]>,
lens_description: Option<&'static LensDescription>,
exif: Option<GenericTiffReader>,
makernotes: Option<GenericTiffReader>,
wb: Option<[f32; 4]>,
blacklevels: Option<[u16; 4]>,
whitelevel: Option<u16>,
}
#[allow(dead_code)]
const CR3_CTMD_BLOCK_EXIFIFD: u16 = 0x8769;
const CR3_CTMD_BLOCK_MAKERNOTES: u16 = 0x927c;
/// Type values fro CCTP records
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
enum Cr3ImageType {
CrxBix = 0,
CrxSmall = 1,
PreviewBig = 2,
Ctmd = 3,
CrxDual = 4,
}
impl<'a> Cr3Decoder<'a> {
/// Construct new CR3 or CRM deocder
pub fn new(_rawfile: &RawSource, bmff: Bmff, rawloader: &'a RawLoader) -> Result<Cr3Decoder<'a>> {
if let Some(Cr3DescBox { cmt1, cmt2, cmt3, cmt4, .. }) = bmff.filebox.moov.cr3desc.as_ref() {
let mode = Self::get_mode(cmt3.tiff.root_ifd())?;
let camera = rawloader.check_supported_with_mode(cmt1.tiff.root_ifd(), mode)?;
let decoder = Cr3Decoder {
camera,
rawloader,
cmt1: cmt1.tiff.clone(),
cmt2: cmt2.tiff.clone(),
cmt3: cmt3.tiff.clone(),
cmt4: cmt4.tiff.clone(),
bmff,
md_cache: DecoderCache::new(),
};
Ok(decoder)
} else {
Err("It's not a CR3 file: CMT boxes not found.".into())
}
}
// Search for quality tag inside makernotes and derive
// our mode string for configuration.
fn get_mode(makernotes: &IFD) -> Result<&str> {
Ok(if let Some(entry) = makernotes.get_entry(0x0001) {
match entry.force_u16(3) {
4 => "raw",
7 => "craw",
130 => "crm",
131 => "crm",
_ => "undefined",
}
} else {
"undefined"
})
}
/// Get trak from moov box
fn moov_trak(&self, trak_id: usize) -> Option<&TrakBox> {
self.bmff.filebox.moov.traks.get(trak_id)
}
/// Get IAD1 box for specific trak
fn iad1_box(&self, trak_idx: usize) -> Option<&Iad1Box> {
let trak = &self.bmff.filebox.moov.traks[trak_idx];
let craw = trak.mdia.minf.stbl.stsd.craw.as_ref();
craw.and_then(|craw| craw.cdi1.as_ref()).map(|cdi1| &cdi1.iad1)
}
/// Get CMP1 box for specific trak
fn cmp1_box(&self, trak_idx: usize) -> Option<&Cmp1Box> {
let trak = &self.bmff.filebox.moov.traks[trak_idx];
let craw = trak.mdia.minf.stbl.stsd.craw.as_ref();
craw.and_then(|craw| craw.cmp1.as_ref())
}
/// Read CTMD records for given sample
/// Each sample (for movie files) have their own CTMD records
fn read_ctmd(&self, rawfile: &RawSource, sample_idx: u32) -> Result<Option<Ctmd>> {
// Search for a trak which has a CTMD box (there should be only one)
if let Some(ctmd_trak_index) = self
.bmff
.filebox
.moov
.traks
.iter()
.enumerate()
.find(|(_, trak)| trak.mdia.minf.stbl.stsd.ctmd.is_some())
.map(|(id, _)| id)
{
log::debug!("CTMD trak_index: {}", ctmd_trak_index);
let ctmd_trak = &self.bmff.filebox.moov.traks[ctmd_trak_index];
let (offset, size) = ctmd_trak
.mdia
.minf
.stbl
.get_sample_offset(sample_idx as u32 + 1)
.ok_or_else(|| RawlerError::DecoderFailed(format!("CTMD sample index {} out of bound", sample_idx)))?;
debug!("CR3 CTMD mdat offset for sample_idx {}: {}, len: {}", sample_idx, offset, size);
let buf = rawfile
.subview(offset as u64, size as u64)
.map_err(|e| RawlerError::with_io_error("CR3: failed to read CTMD", rawfile.path(), e))?;
//dump_buf("/tmp/ctmd.buf", &buf);
let mut substream = ByteStream::new(buf, Endian::Little);
let ctmd = Ctmd::new(&mut substream);
Ok(Some(ctmd))
} else {
log::warn!("No CTMD trak found");
Ok(None)
}
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Cr3Format {
pub filebox: FileBox,
}
impl<'a> Decoder for Cr3Decoder<'a> {
fn format_dump(&self) -> FormatDump {
FormatDump::Cr3(Cr3Format {
filebox: self.bmff.filebox.clone(),
})
}
fn raw_metadata(&self, file: &RawSource, params: &RawDecodeParams) -> Result<RawMetadata> {
let cr3md = self.read_cr3_metadata(file, params)?;
let mut exif = Exif::default();
exif.extend_from_ifd(self.cmt1.root_ifd())?;
exif.extend_from_ifd(self.cmt2.root_ifd())?;
exif.extend_from_gps_ifd(self.cmt4.root_ifd())?;
if exif.gps.is_none() {
exif.gps = Some(ExifGPS::default());
}
if let Some(gps) = &mut exif.gps {
for (tag, entry) in self.cmt4.root_ifd().entries() {
match tag {
// Special handling for Exif.GPSInfo.GPSLatitude and Exif.GPSInfo.GPSLongitude.
// Exif.GPSInfo.GPSTimeStamp is wrong, too and can be fixed with the same logic.
// Canon CR3 contains only two rationals, but these tags are specified as a vector
// of three reationals (degrees, minutes, seconds).
// We fix this by extending with 0/1 as seconds value.
0x0002 | 0x0004 | 0x0007 => match &entry.value {
Value::Rational(v) => {
let fixed_value = if v.len() == 2 { vec![v[0], v[1], Rational::new(0, 1)] } else { v.clone() };
match tag {
0x0002 => gps.gps_latitude = fixed_value.try_into().ok(),
0x0004 => gps.gps_longitude = fixed_value.try_into().ok(),
0x0007 => gps.gps_timestamp = fixed_value.try_into().ok(),
_ => unreachable!(),
}
}
_ => {
warn!("CR3: Exif.GPSInfo.GPSLatitude and Exif.GPSInfo.GPSLongitude expected to be of type RATIONAL, GPS data is ignored");
}
},
_ => {}
}
}
}
let mut mdata = RawMetadata::new_with_lens(&self.camera, exif, cr3md.lens_description.cloned());
if let Some(unique_id) = &cr3md.image_unique_id {
// For CR3, we use the already included Makernote tag with unique image ID
mdata.unique_image_id = Some(u128::from_le_bytes(*unique_id));
}
Ok(mdata)
}
fn xpacket(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<Vec<u8>>> {
let cr3md = self.read_cr3_metadata(file, params)?;
Ok(cr3md.xpacket)
}
/// CR3 can store multiple samples in trak
fn raw_image_count(&self) -> Result<usize> {
let raw_trak_id = rawler_crx_raw_trak()
.or_else(|| self.get_trak_index(Cr3ImageType::CrxBix))
.ok_or("Unable to find trak index")?;
let moov_trak = self.moov_trak(raw_trak_id).ok_or(format!("Unable to get MOOV trak {}", raw_trak_id))?;
Ok(moov_trak.mdia.minf.stbl.stsz.sample_count as usize)
}
/// Decode raw image
fn raw_image(&self, file: &RawSource, params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let sample_idx = params.image_index;
if sample_idx >= self.raw_image_count()? {
return Err(RawlerError::DecoderFailed(format!(
"Raw image index {} out of range ({})",
sample_idx,
self.raw_image_count()?
)));
}
let cr3md = self.read_cr3_metadata(file, params)?;
if let Some(cr3desc) = &self.bmff.filebox.moov.cr3desc {
for item in cr3desc.cctp.ccdts.iter() {
log::debug!("CCDT: trak {} type {} dual {}", item.trak_index, item.image_type, item.dual_pixel);
}
}
let raw_trak_id = rawler_crx_raw_trak()
.or_else(|| self.get_trak_index(Cr3ImageType::CrxBix))
.ok_or("Unable to find trak index")?;
// Load trak with raw MDAT section
let moov_trak = self.moov_trak(raw_trak_id).ok_or(format!("Unable to get MOOV trak {}", raw_trak_id))?;
let (offset, size) = moov_trak
.mdia
.minf
.stbl
.get_sample_offset(sample_idx as u32 + 1)
.ok_or_else(|| RawlerError::DecoderFailed(format!("stbl sample not found")))?;
debug!("RAW mdat offset: {}, len: {}", offset, size);
// Raw data buffer
let buf = file
.subview(offset as u64, size as u64)
.map_err(|e| RawlerError::with_io_error("CR3: failed to read raw data", file.path(), e))?;
let cmp1 = self.cmp1_box(raw_trak_id).ok_or(format!("CMP1 box not found for trak {}", raw_trak_id))?;
debug!("cmp1 mdat hdr size: {}", cmp1.mdat_hdr_size);
let mut wb = cr3md.wb.unwrap_or_else(|| {
// This is known for R5 C CRM Standard-Raw files
log::warn!("No WB info in CR3 metadata found, fallback to 1.0 coefficients");
[1.0, 1.0, 1.0, f32::NAN]
});
let whitelevel = cr3md.whitelevel.unwrap_or(((1_u32 << self.camera.bps.unwrap_or(16)) - 1) as u16);
// Special handling for CRM movie files
if let Some(entry) = self.cmt3.get_entry(0x0001) {
if 130 == entry.force_u16(3) {
// Light Raw
// WB is already applied, use 1.0
wb = [1.0, 1.0, 1.0, f32::NAN];
}
if 131 == entry.force_u16(3) { // Standard Raw
// Nothing special for Standard raw
}
}
let image = if !dummy {
PixU16::new_with(
decompress_crx_image(buf, cmp1).map_err(|e| format!("Failed to decode raw: {}", e))?,
cmp1.f_width as usize,
cmp1.f_height as usize,
)
} else {
PixU16::new_uninit(cmp1.f_width as usize, cmp1.f_height as usize)
};
let cpp = 1;
let blacklevel = cr3md
.blacklevels
.map(|x| BlackLevel::new(&x, self.camera.cfa.width, self.camera.cfa.height, cpp));
let whitelevel = WhiteLevel(vec![whitelevel as u32]);
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let mut img = RawImage::new(self.camera.clone(), image, cpp, wb, photometric, blacklevel, Some(whitelevel), dummy);
// IAD1 box contains sensor information
// We use the sensor crop from IAD1 as recommended image crop.
// The same crop is used as ActiveArea, because black areas in IAD1 are not
// correct (they differs like 4-6 pixels from real values).
match self.iad1_box(raw_trak_id) {
Some(iad1) => {
match &iad1.iad1_type {
// IAD1 (small, used for CRM movie files)
Iad1Type::Small(small) => {
img.crop_area = Some(Rect::new_with_points(
Point::new(small.crop_left_offset as usize, small.crop_top_offset as usize),
Point::new((small.crop_right_offset + 1) as usize, (small.crop_bottom_offset + 1) as usize),
));
img.active_area = img.crop_area;
}
// IAD1 (big, used for full size raws)
Iad1Type::Big(big) => {
img.crop_area = Some(Rect::new_with_points(
Point::new(big.crop_left_offset as usize, big.crop_top_offset as usize),
Point::new((big.crop_right_offset + 1) as usize, (big.crop_bottom_offset + 1) as usize),
));
// For uncropped files this is fine, but for 1.6 crop files, the dimension is wrong.
// For example, R5 crop is total height of 3510, but active_area_bottom_offset is 3512.
let rect = {
// Limit the offsets to image bounds.
// Probably broken firmware, glitches in sensor size calculation or I'm just making
// wrong asumptions...
let right = usize::min(cmp1.f_width as usize, (big.active_area_right_offset - 1) as usize);
let bottom = usize::min(cmp1.f_height as usize, (big.active_area_bottom_offset - 1) as usize);
Rect::new_with_points(
Point::new(big.active_area_left_offset as usize, big.active_area_top_offset as usize),
Point::new(right, bottom),
)
};
log::debug!("IAD1 active area: {:?}", rect);
img.active_area = Some(rect);
//img.active_area = img.crop_area;
let blackarea_h = Rect::new_with_points(
Point::new(big.lob_left_offset as usize, big.lob_top_offset as usize),
Point::new((big.lob_right_offset - 1) as usize, (big.lob_bottom_offset - 1) as usize),
);
if !blackarea_h.is_empty() {
//img.blackareas.push(blackarea_h);
}
let blackarea_v = Rect::new_with_points(
Point::new(big.tob_left_offset as usize, big.tob_top_offset as usize),
Point::new((big.tob_right_offset - 1) as usize, (big.tob_bottom_offset - 1) as usize),
);
if !blackarea_v.is_empty() {
//img.blackareas.push(blackarea_v);
}
}
}
}
None => {
warn!("No IAD1 box found for sensor data");
}
}
debug!("Canon active area: {:?}", img.active_area);
debug!("Canon crop area: {:?}", img.crop_area);
debug!("Black areas: {:?}", img.blackareas);
Ok(img)
}
/// Extract preview image embedded in CR3
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
if rawler_ignore_previews() {
return Err(RawlerError::DecoderFailed("Unable to extract preview image".into()));
}
let offset = self.bmff.filebox.moov.traks[0].mdia.minf.stbl.co64.as_ref().expect("co64 box").entries[0] as usize;
let size = self.bmff.filebox.moov.traks[0].mdia.minf.stbl.stsz.sample_sizes[0] as usize;
debug!("JPEG preview mdat offset: {}, len: {}", offset, size);
let buf = file
.subview(offset as u64, size as u64)
.map_err(|e| RawlerError::with_io_error("CR3: failed to read full image data", file.path(), e))?;
match image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg) {
Ok(img) => Ok(Some(img)),
Err(e) => {
debug!("TRAK 0 contains no JPEG preview, is it PQ/HEIF? Error: {}", e);
Err(RawlerError::DecoderFailed(
"Unable to extract preview image from CR3 HDR-PQ file. Please see 'https://github.com/dnglab/dnglab/issues/7'".into(),
))
}
}
}
fn format_hint(&self) -> FormatHint {
FormatHint::CR3
}
}
impl<'a> Cr3Decoder<'a> {
fn read_lens_id(&self) -> Result<LensId> {
let mut id = (0, 0);
if let Some(Entry { value: Value::Short(v), .. }) = self.cmt3.get_entry(Cr3MakernoteTag::CameraSettings) {
id.0 = v[22] as u32;
}
if let Some(Entry { value: Value::Short(v), .. }) = self.cmt3.get_entry(Cr3MakernoteTag::FileInfo) {
id.1 = v[61] as u32;
}
Ok(id)
}
fn read_lens_name(&self) -> Result<Option<&String>> {
if let Some(Entry {
value: crate::formats::tiff::Value::Ascii(lens_id),
..
}) = self.cmt2.get_entry(ExifTag::LensModel)
{
return Ok(lens_id.strings().get(0));
}
Ok(None)
}
fn read_cr3_metadata(&self, rawfile: &RawSource, params: &RawDecodeParams) -> Result<Cr3Metadata> {
if let Some(md) = self.md_cache.get(params) {
return Ok(md);
}
let mut md = Cr3Metadata::default();
let resolver = LensResolver::new()
.with_lens_keyname(self.read_lens_name()?)
.with_camera(&self.camera) // must follow with_lens_keyname() as it my override key
.with_lens_id(self.read_lens_id()?)
.with_mounts(&[CANON_CN_MOUNT.into(), CANON_EF_MOUNT.into(), CANON_RF_MOUNT.into()]);
md.lens_description = resolver.resolve();
if let Some(Entry {
value: crate::formats::tiff::Value::Byte(v),
..
}) = self.cmt3.get_entry(Cr3MakernoteTag::ImgUniqueID)
{
if v.len() == 16 {
debug!("CR3 makernote ImgUniqueID: {:x?}", v);
md.image_unique_id = Some(v.as_slice().try_into().expect("Invalid slice size"));
}
}
if let Some(entry) = self.cmt3.get_entry(0x0001) {
let quality = match entry.force_u16(3) {
4 => "RAW",
5 => "Superfine",
7 => "CRAW",
130 => "LightRaw",
131 => "StandardRaw",
_ => "unknown",
};
debug!("Canon quality mode: {}", quality);
}
if let Some(entry) = self.cmt3.get_entry(0x4026) {
let clog = match entry.force_u32(11) {
0 => "OFF",
1 => "CLog1",
2 => "CLog2",
3 => "CLog3",
_ => "unknown",
};
debug!("Canon CLog mode: {}", clog);
}
if let Some(xpacket_box) = self.bmff.filebox.cr3xpacket.as_ref() {
let offset = xpacket_box.header.offset + xpacket_box.header.header_len;
let size = xpacket_box.header.size - xpacket_box.header.header_len;
let buf = rawfile
.subview(offset, size)
.map_err(|e| RawlerError::with_io_error("CR3: failed to read XPACKET", rawfile.path(), e))?;
md.xpacket = Some(buf.to_vec());
}
if let Some(ctmd) = self.read_ctmd(rawfile, params.image_index as u32)? {
if let Some(rec5) = ctmd.exposure_info()? {
debug!("CTMD Rec(5): {:?}", rec5);
md.ctmd_exposure = Some(rec5);
}
md.ctmd_focallen = ctmd.focal_len()?;
if let Some(rec7) = ctmd.get_as_tiff(7, CR3_CTMD_BLOCK_EXIFIFD)? {
md.ctmd_rec7_exif = Some(rec7);
}
if let Some(rec7) = ctmd.get_as_tiff(7, CR3_CTMD_BLOCK_MAKERNOTES)? {
md.ctmd_rec7_makernotes = Some(rec7);
}
if let Some(rec8) = ctmd.get_as_tiff(8, CR3_CTMD_BLOCK_MAKERNOTES)? {
if let Some(colordata) = rec8.get_entry(Cr3MakernoteTag::ColorData) {
let colordata = cr2::parse_colordata(colordata)?;
//rec8.root_ifd().dump::<TiffCommonTag>(10).iter().for_each(|line| eprintln!("MKD: {}", line));
md.wb = Some(normalize_wb(colordata.wb));
md.blacklevels = colordata.blacklevel;
md.whitelevel = colordata.specular_whitelevel;
/*
if let crate::formats::tiff::Value::Short(v) = &levels.value {
if let Some(offset) = self.camera.param_usize("colordata_wbcoeffs") {
let raw_wb = [v[offset] as f32, v[offset + 1] as f32, v[offset + 2] as f32, v[offset + 3] as f32];
md.wb = Some(normalize_wb(raw_wb));
}
if let Some(offset) = self.camera.param_usize("colordata_blacklevel") {
debug!("Blacklevel offset: {:x}", offset);
md.blacklevels = Some([v[offset], v[offset + 1], v[offset + 2], v[offset + 3]]);
}
if let Some(offset) = self.camera.param_usize("colordata_whitelevel") {
md.whitelevel = Some(v[offset]);
}
}
*/
}
md.ctmd_rec8 = Some(rec8);
}
if let Some(rec9) = ctmd.get_as_tiff(9, CR3_CTMD_BLOCK_MAKERNOTES)? {
md.ctmd_rec9 = Some(rec9);
}
}
debug!("CR3 blacklevels: {:?}", md.blacklevels);
debug!("CR3 whitelevel: {:?}", md.whitelevel);
self.md_cache.set(params, md.clone());
Ok(md)
}
fn get_trak_index(&self, image_type: Cr3ImageType) -> Option<usize> {
if let Some(cr3desc) = &self.bmff.filebox.moov.cr3desc {
cr3desc.cctp.ccdts.iter().find(|ccdt| ccdt.image_type == image_type as u64).map(|rec| {
debug_assert!(rec.trak_index > 0);
(rec.trak_index - 1) as usize
})
} else {
None
}
}
}
/// CTMD section with multiple records
#[derive(Clone, Debug)]
struct Ctmd {
pub records: HashMap<u16, CtmdRecord>,
}
/// Record inside CTMD section
#[allow(dead_code)]
#[derive(Clone, Debug)]
struct CtmdRecord {
pub rec_size: u32,
pub rec_type: u16,
pub reserved1: u8,
pub reserved2: u8,
pub reserved3: u8,
pub reserved4: u8,
pub reserved5: i8,
pub reserved6: i8,
pub payload: Vec<u8>,
pub blocks: HashMap<u16, Vec<u8>>,
}
impl Ctmd {
pub fn new(data: &mut ByteStream) -> Self {
let mut records = HashMap::new();
while data.remaining_bytes() >= 12 {
let size = data.get_u32();
let mut rec = CtmdRecord {
rec_size: size,
rec_type: data.get_u16(),
reserved1: data.get_u8(),
reserved2: data.get_u8(),
reserved3: data.get_u8(),
reserved4: data.get_u8(),
reserved5: data.get_i8(),
reserved6: data.get_i8(),
payload: data.get_bytes(size as usize - 12),
blocks: HashMap::new(),
};
debug!(
"CTMD Rec {:02}: {}, {}, {}, {}, {}, {}",
rec.rec_type, rec.reserved1, rec.reserved2, rec.reserved3, rec.reserved4, rec.reserved5, rec.reserved6
);
//dump_buf(&format!("/tmp/ctmd_rec{}.bin", rec.rec_type), rec.payload.as_slice());
if [7, 8, 9, 10, 11, 12].contains(&rec.rec_type) {
let mut bs = ByteStream::new(rec.payload.as_slice(), Endian::Little);
let mut _block_id = 0;
while bs.remaining_bytes() >= (4 + 2 + 2) {
let sz = bs.get_u32() as usize;
let tag = bs.get_u16();
let _uk = bs.get_u16();
if sz >= 8 && bs.remaining_bytes() >= (sz - 8) {
log::debug!("CTMD BLOCK: size {}, tag {}, remaining: {}", sz, tag, bs.remaining_bytes());
let data = bs.get_bytes(sz as usize - 8);
//dump_buf(&format!("/tmp/ctmd_rec{}_block{}_tag0x{:X}_uk{:X}.bin", rec.rec_type, block_id, tag, uk), data.as_slice());
if [CR3_CTMD_BLOCK_EXIFIFD, CR3_CTMD_BLOCK_MAKERNOTES].contains(&tag) {
assert_eq!(rec.blocks.contains_key(&tag), false, "Double tag found?!");
rec.blocks.insert(tag, data);
}
_block_id += 1;
} else {
log::debug!(
"CTMD BLOCK: size {}, tag {}, remaining: {} - is invalid, maybe not a block",
sz,
tag,
bs.remaining_bytes()
);
break;
}
}
} else {
log::debug!("CTMD record type {} unknown, ignoring.", rec.rec_type);
//dump_buf(&format!("/tmp/ctmd_rec{}.bin", rec.rec_type), rec.payload.as_slice());
}
records.insert(rec.rec_type, rec);
}
Self { records }
}
pub fn get_as_tiff(&self, record: u16, tag: u16) -> Result<Option<GenericTiffReader>> {
if let Some(block) = self.records.get(&record).and_then(|rec| rec.blocks.get(&tag)) {
Ok(Some(GenericTiffReader::new_with_buffer(block, 0, 0, Some(0))?))
} else {
warn!("Unable to find CTMD record {}, tag 0x{:X}", record, tag);
Ok(None)
}
}
pub fn exposure_info(&self) -> Result<Option<CtmdExposureInfo>> {
if let Some(rec) = self.records.get(&5) {
let mut buf = ByteStream::new(rec.payload.as_slice(), Endian::Little);
let fnumber = Rational::new(buf.get_u16().into(), buf.get_u16().into());
let exposure = Rational::new(buf.get_u16().into(), buf.get_u16().into());
let iso_speed = buf.get_u32();
let unknown = buf.get_bytes(buf.remaining_bytes());
Ok(Some(CtmdExposureInfo {
fnumber,
exposure,
iso_speed,
unknown,
}))
} else {
Ok(None)
}
}
pub fn focal_len(&self) -> Result<Option<Rational>> {
if let Some(rec) = self.records.get(&4) {
let mut buf = ByteStream::new(rec.payload.as_slice(), Endian::Little);
let focal_len = Rational::new(buf.get_u16().into(), buf.get_u16().into());
Ok(Some(focal_len))
} else {
Ok(None)
}
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("CR3 raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1]; // G1 should be 1024 and we use this as divisor
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CtmdExposureInfo {
pub fnumber: Rational,
pub exposure: Rational,
pub iso_speed: u32,
pub unknown: Vec<u8>,
}
crate::tags::tiff_tag_enum!(Cr3MakernoteTag);
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum Cr3MakernoteTag {
CameraSettings = 0x0001,
FocusInfo = 0x0002,
FlashInfo = 0x0003,
ShotInfo = 0x0004,
Panorama = 0x0005,
ImageType = 0x0006,
FirmareVer = 0x0007,
FileNumber = 0x0008,
OwnerName = 0x0009,
UnknownD30 = 0x000a,
SerialNum = 0x000c,
CameraInfo = 0x000d,
FileLen = 0x000e,
CustomFunc = 0x000f,
ModelId = 0x0010,
MovieInfo = 0x0011,
AFInfo = 0x0012,
ThumbArea = 0x0013,
SerialFormat = 0x0014,
SuperMacro = 0x001a,
DateStampMode = 0x001c,
MyColors = 0x001d,
FirmwareRev = 0x001e,
Categories = 0x0023,
FaceDetect1 = 0x0024,
FaceDetect2 = 0x0025,
AFInfo2 = 0x0026,
ContrastInfo = 0x0027,
ImgUniqueID = 0x0028,
WBInfo = 0x0029,
FaceDetect3 = 0x002f,
TimeInfo = 0x0035,
BatteryType = 0x0038,
AFInfo3 = 0x003c,
RawDataOffset = 0x0081,
OrigDecisionDataOffset = 0x0083,
CustomFunc1D = 0x0090,
PersFunc = 0x0091,
PersFuncValues = 0x0092,
FileInfo = 0x0093,
AFPointsInFocus1D = 0x0094,
LensModel = 0x0095,
InternalSerial = 0x0096,
DustRemovalData = 0x0097,
CropInfo = 0x0098,
CustomFunc2 = 0x0099,
AspectInfo = 0x009a,
ProcessingInfo = 0x00a0,
ToneCurveTable = 0x00a1,
SharpnessTable = 0x00a2,
SharpnessFreqTable = 0x00a3,
WhiteBalanceTable = 0x00a4,
ColorBalance = 0x00a9,
MeasuredColor = 0x00aa,
ColorTemp = 0x00ae,
CanonFlags = 0x00b0,
ModifiedInfo = 0x00b1,
TnoeCurveMatching = 0x00b2,
WhiteBalanceMatching = 0x00b3,
ColorSpace = 0x00b4,
PreviewImageInfo = 0x00b6,
VRDOffset = 0x00d0,
SensorInfo = 0x00e0,
ColorData = 0x4001,
CRWParam = 0x4002,
ColorInfo = 0x4003,
Flavor = 0x4005,
PictureStyleUserDef = 0x4008,
PictureStylePC = 0x4009,
CustomPictureStyleFileName = 0x4010,
AFMicroAdj = 0x4013,
VignettingCorr = 0x4015,
VignettingCorr2 = 0x4016,
LightningOpt = 0x4018,
LensInfo = 0x4019,
AmbienceInfo = 0x4020,
MultiExp = 0x4021,
FilterInfo = 0x4024,
HDRInfo = 0x4025,
AFConfig = 0x4028,
RawBurstModeRoll = 0x403f,
}
+285
View File
@@ -0,0 +1,285 @@
use crate::RawImage;
use crate::decoders::*;
use crate::decompressors::ljpeg::huffman::*;
use crate::formats::ciff::*;
use crate::packed::*;
use crate::pumps::BitPump;
use crate::pumps::BitPumpJPEG;
const CRW_FIRST_TREE: [[u8; 29]; 3] = [
[
0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0x03, 0x05, 0x06, 0x02, 0x07, 0x01, 0x08, 0x09, 0x00, 0x0a, 0x0b, 0xff,
],
[
0, 2, 2, 3, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0x03, 0x02, 0x04, 0x01, 0x05, 0x00, 0x06, 0x07, 0x09, 0x08, 0x0a, 0x0b, 0xff,
],
[
0, 0, 6, 3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x05, 0x07, 0x04, 0x08, 0x03, 0x09, 0x02, 0x00, 0x0a, 0x01, 0x0b, 0xff,
],
];
const CRW_SECOND_TREE: [[u8; 180]; 3] = [
[
0, 2, 2, 2, 1, 4, 2, 1, 2, 5, 1, 1, 0, 0, 0, 139, 0x03, 0x04, 0x02, 0x05, 0x01, 0x06, 0x07, 0x08, 0x12, 0x13, 0x11, 0x14, 0x09, 0x15, 0x22, 0x00, 0x21,
0x16, 0x0a, 0xf0, 0x23, 0x17, 0x24, 0x31, 0x32, 0x18, 0x19, 0x33, 0x25, 0x41, 0x34, 0x42, 0x35, 0x51, 0x36, 0x37, 0x38, 0x29, 0x79, 0x26, 0x1a, 0x39, 0x56,
0x57, 0x28, 0x27, 0x52, 0x55, 0x58, 0x43, 0x76, 0x59, 0x77, 0x54, 0x61, 0xf9, 0x71, 0x78, 0x75, 0x96, 0x97, 0x49, 0xb7, 0x53, 0xd7, 0x74, 0xb6, 0x98, 0x47,
0x48, 0x95, 0x69, 0x99, 0x91, 0xfa, 0xb8, 0x68, 0xb5, 0xb9, 0xd6, 0xf7, 0xd8, 0x67, 0x46, 0x45, 0x94, 0x89, 0xf8, 0x81, 0xd5, 0xf6, 0xb4, 0x88, 0xb1, 0x2a,
0x44, 0x72, 0xd9, 0x87, 0x66, 0xd4, 0xf5, 0x3a, 0xa7, 0x73, 0xa9, 0xa8, 0x86, 0x62, 0xc7, 0x65, 0xc8, 0xc9, 0xa1, 0xf4, 0xd1, 0xe9, 0x5a, 0x92, 0x85, 0xa6,
0xe7, 0x93, 0xe8, 0xc1, 0xc6, 0x7a, 0x64, 0xe1, 0x4a, 0x6a, 0xe6, 0xb3, 0xf1, 0xd3, 0xa5, 0x8a, 0xb2, 0x9a, 0xba, 0x84, 0xa4, 0x63, 0xe5, 0xc5, 0xf3, 0xd2,
0xc4, 0x82, 0xaa, 0xda, 0xe4, 0xf2, 0xca, 0x83, 0xa3, 0xa2, 0xc3, 0xea, 0xc2, 0xe2, 0xe3, 0xff, 0xff,
],
[
0, 2, 2, 1, 4, 1, 4, 1, 3, 3, 1, 0, 0, 0, 0, 140, 0x02, 0x03, 0x01, 0x04, 0x05, 0x12, 0x11, 0x06, 0x13, 0x07, 0x08, 0x14, 0x22, 0x09, 0x21, 0x00, 0x23,
0x15, 0x31, 0x32, 0x0a, 0x16, 0xf0, 0x24, 0x33, 0x41, 0x42, 0x19, 0x17, 0x25, 0x18, 0x51, 0x34, 0x43, 0x52, 0x29, 0x35, 0x61, 0x39, 0x71, 0x62, 0x36, 0x53,
0x26, 0x38, 0x1a, 0x37, 0x81, 0x27, 0x91, 0x79, 0x55, 0x45, 0x28, 0x72, 0x59, 0xa1, 0xb1, 0x44, 0x69, 0x54, 0x58, 0xd1, 0xfa, 0x57, 0xe1, 0xf1, 0xb9, 0x49,
0x47, 0x63, 0x6a, 0xf9, 0x56, 0x46, 0xa8, 0x2a, 0x4a, 0x78, 0x99, 0x3a, 0x75, 0x74, 0x86, 0x65, 0xc1, 0x76, 0xb6, 0x96, 0xd6, 0x89, 0x85, 0xc9, 0xf5, 0x95,
0xb4, 0xc7, 0xf7, 0x8a, 0x97, 0xb8, 0x73, 0xb7, 0xd8, 0xd9, 0x87, 0xa7, 0x7a, 0x48, 0x82, 0x84, 0xea, 0xf4, 0xa6, 0xc5, 0x5a, 0x94, 0xa4, 0xc6, 0x92, 0xc3,
0x68, 0xb5, 0xc8, 0xe4, 0xe5, 0xe6, 0xe9, 0xa2, 0xa3, 0xe3, 0xc2, 0x66, 0x67, 0x93, 0xaa, 0xd4, 0xd5, 0xe7, 0xf8, 0x88, 0x9a, 0xd7, 0x77, 0xc4, 0x64, 0xe2,
0x98, 0xa5, 0xca, 0xda, 0xe8, 0xf3, 0xf6, 0xa9, 0xb2, 0xb3, 0xf2, 0xd2, 0x83, 0xba, 0xd3, 0xff, 0xff,
],
[
0, 0, 6, 2, 1, 3, 3, 2, 5, 1, 2, 2, 8, 10, 0, 117, 0x04, 0x05, 0x03, 0x06, 0x02, 0x07, 0x01, 0x08, 0x09, 0x12, 0x13, 0x14, 0x11, 0x15, 0x0a, 0x16, 0x17,
0xf0, 0x00, 0x22, 0x21, 0x18, 0x23, 0x19, 0x24, 0x32, 0x31, 0x25, 0x33, 0x38, 0x37, 0x34, 0x35, 0x36, 0x39, 0x79, 0x57, 0x58, 0x59, 0x28, 0x56, 0x78, 0x27,
0x41, 0x29, 0x77, 0x26, 0x42, 0x76, 0x99, 0x1a, 0x55, 0x98, 0x97, 0xf9, 0x48, 0x54, 0x96, 0x89, 0x47, 0xb7, 0x49, 0xfa, 0x75, 0x68, 0xb6, 0x67, 0x69, 0xb9,
0xb8, 0xd8, 0x52, 0xd7, 0x88, 0xb5, 0x74, 0x51, 0x46, 0xd9, 0xf8, 0x3a, 0xd6, 0x87, 0x45, 0x7a, 0x95, 0xd5, 0xf6, 0x86, 0xb4, 0xa9, 0x94, 0x53, 0x2a, 0xa8,
0x43, 0xf5, 0xf7, 0xd4, 0x66, 0xa7, 0x5a, 0x44, 0x8a, 0xc9, 0xe8, 0xc8, 0xe7, 0x9a, 0x6a, 0x73, 0x4a, 0x61, 0xc7, 0xf4, 0xc6, 0x65, 0xe9, 0x72, 0xe6, 0x71,
0x91, 0x93, 0xa6, 0xda, 0x92, 0x85, 0x62, 0xf3, 0xc5, 0xb2, 0xa4, 0x84, 0xba, 0x64, 0xa5, 0xb3, 0xd2, 0x81, 0xe5, 0xd3, 0xaa, 0xc4, 0xca, 0xf2, 0xb1, 0xe4,
0xd1, 0x83, 0x63, 0xea, 0xc3, 0xe2, 0x82, 0xf1, 0xa3, 0xc2, 0xa1, 0xc1, 0xe3, 0xa2, 0xe1, 0xff, 0xff,
],
];
#[derive(Debug, Clone)]
pub struct CrwDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
ciff: CiffIFD,
camera: Camera,
}
impl<'a> CrwDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<CrwDecoder<'a>> {
let ciff = CiffIFD::new_file(file)?;
let makemodel = fetch_ciff_tag!(ciff, CiffTag::MakeModel).get_strings();
if makemodel.len() < 2 {
return Err(RawlerError::DecoderFailed("CRW: MakeModel tag needs to have 2 strings".to_string()));
}
let camera = rawloader.check_supported_with_everything(&makemodel[0], &makemodel[1], "")?;
Ok(CrwDecoder { ciff, rawloader, camera })
}
}
impl<'a> Decoder for CrwDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let image = if self.camera.model == "Canon PowerShot Pro70" {
let src = file.subview_until_eof(26)?;
decode_10le_lsb16(src, 1552, 1024, dummy)
} else {
let sensorinfo = fetch_ciff_tag!(self.ciff, CiffTag::SensorInfo);
let width = sensorinfo.get_usize(1);
let height = sensorinfo.get_usize(2);
self.decode_compressed(file, width, height, dummy)?
};
let wb = {
let wb_raw = self.get_wb()?;
match self.camera.cfa.unique_colors() {
3 => normalize_wb(wb_raw),
4 => {
let maxval = wb_raw.into_iter().map(|v| v as i32).min().unwrap_or(0) as f32;
[wb_raw[0] / maxval, wb_raw[1] / maxval, wb_raw[2] / maxval, wb_raw[3] / maxval]
}
_ => unreachable!(),
}
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, wb, image, dummy)
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, __params: &RawDecodeParams) -> Result<RawMetadata> {
// TODO: Add EXIF info
let exif = Exif::default();
Ok(RawMetadata::new(&self.camera, exif))
}
fn format_hint(&self) -> FormatHint {
FormatHint::CRW
}
}
impl<'a> CrwDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
if let Some(levels) = self.ciff.find_entry(CiffTag::WhiteBalance) {
let offset = self.camera.param_usize("wb_offset").unwrap_or(0);
return Ok([
levels.get_f32(offset + 0),
levels.get_f32(offset + 1),
levels.get_f32(offset + 2),
levels.get_f32(offset + 3),
]);
}
if !self.camera.find_hint("nocinfo2") {
if let Some(cinfo) = self.ciff.find_entry(CiffTag::ColorInfo2) {
return Ok(if cinfo.get_u32(0) > 512 {
[cinfo.get_f32(62), cinfo.get_f32(63), cinfo.get_f32(60), cinfo.get_f32(61)]
// RGBE???
} else {
[cinfo.get_f32(51), cinfo.get_f32(50), cinfo.get_f32(53), cinfo.get_f32(52)]
});
}
}
if let Some(cinfo) = self.ciff.find_entry(CiffTag::ColorInfo1) {
if cinfo.count == 768 {
// D30
return Ok([
1024.0 / (cinfo.get_force_u16(36) as f32),
1024.0 / (cinfo.get_force_u16(37) as f32),
1024.0 / (cinfo.get_force_u16(38) as f32),
1024.0 / (cinfo.get_force_u16(39) as f32),
]);
}
let off = self.camera.param_usize("wb_offset").unwrap_or(0);
let key: [u16; 2] = if self.camera.find_hint("wb_mangle") { [0x410, 0x45f3] } else { [0, 0] };
return Ok([
(cinfo.get_force_u16(off + 1) ^ key[1]) as f32,
(cinfo.get_force_u16(off + 0) ^ key[0]) as f32,
(cinfo.get_force_u16(off + 0) ^ key[0]) as f32,
(cinfo.get_force_u16(off + 2) ^ key[0]) as f32,
]);
}
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
fn create_hufftables(num: usize) -> [HuffTable; 2] {
[Self::create_hufftable(&CRW_FIRST_TREE[num]), Self::create_hufftable(&CRW_SECOND_TREE[num])]
}
fn create_hufftable(table: &[u8]) -> HuffTable {
let mut htable = HuffTable::empty();
for i in 0..16 {
htable.bits[i + 1] = table[i] as u32;
}
for i in 16..table.len() {
htable.huffval[i - 16] = table[i] as u32;
}
htable.disable_cache = true;
htable.initialize().unwrap();
htable
}
fn decode_compressed(&self, file: &RawSource, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let lowbits = !self.camera.find_hint("nolowbits");
let dectable = fetch_ciff_tag!(self.ciff, CiffTag::DecoderTable).get_usize(0);
if dectable > 2 {
return Err(RawlerError::DecoderFailed(format!("CRW: Unknown decoder table {}", dectable)));
}
Self::do_decode(file, lowbits, dectable, width, height, dummy)
}
pub(crate) fn do_decode(file: &RawSource, lowbits: bool, dectable: usize, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let mut out = alloc_image_ok!(width, height, dummy);
let htables = Self::create_hufftables(dectable);
let offset = 540 + (lowbits as usize) * height * width / 4;
let src = file.subview_until_eof(offset as u64)?;
let mut pump = BitPumpJPEG::new(src);
let mut carry: i32 = 0;
let mut base = [0_i32; 2];
let mut pnum = 0;
for pixout in out.pixels_mut().chunks_exact_mut(64) {
// Decode a block of 64 differences
let mut diffbuf = [0_i32; 64];
let mut i: usize = 0;
while i < 64 {
let tbl = &htables[(i > 0) as usize];
let leaf = tbl.huff_get_bits(&mut pump);
if leaf == 0 && i != 0 {
break;
}
if leaf == 0xff {
i += 1;
continue;
}
i += (leaf >> 4) as usize;
let len = leaf & 0x0f;
if len == 0 {
i += 1;
continue;
}
let mut diff: i32 = pump.get_bits(len) as i32;
if (diff & (1 << (len - 1))) == 0 {
diff -= (1 << len) - 1;
}
if i < 64 {
diffbuf[i] = diff;
}
i += 1;
}
diffbuf[0] += carry;
carry = diffbuf[0];
// Save those differences to 64 pixels adjusting the predictor as we go
for i in 0..64 {
// At the start of lines reset the predictor to 512
if pnum % width == 0 {
base[0] = 512;
base[1] = 512;
}
pnum += 1;
base[i & 1] += diffbuf[i];
pixout[i] = base[i & 1] as u16;
}
}
if lowbits {
let buffer = file.as_vec()?;
// Add the uncompressed 2 low bits to the decoded 8 high bits
for (i, o) in out.pixels_mut().chunks_exact_mut(4).enumerate() {
let c = buffer[26 + i] as u16;
o[0] = (o[0] << 2) | (c) & 0x03;
o[1] = (o[1] << 2) | (c >> 2) & 0x03;
o[2] = (o[2] << 2) | (c >> 4) & 0x03;
o[3] = (o[3] << 2) | (c >> 6) & 0x03;
if width == 2672 {
// No idea why this is needed, probably some broken camera
if o[0] < 512 {
o[0] += 2
}
if o[1] < 512 {
o[1] += 2
}
if o[2] < 512 {
o[2] += 2
}
if o[3] < 512 {
o[3] += 2
}
}
}
}
Ok(out)
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("CRW raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
+167
View File
@@ -0,0 +1,167 @@
use std::cmp;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::analyze::FormatDump;
use crate::bits::BEu16;
use crate::bits::Endian;
use crate::bits::LookupTable;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::reader::TiffReader;
use crate::pixarray::PixU16;
use crate::pumps::ByteStream;
use crate::rawsource::RawSource;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct DcrDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
camera: Camera,
}
impl<'a> DcrDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<DcrDecoder<'a>> {
let camera = rawloader.check_supported(tiff.root_ifd())?;
let kodak_ifd = fetch_tiff_tag!(tiff, TiffCommonTag::KodakIFD);
let makernote = IFD::new(&mut file.reader(), kodak_ifd.force_u32(0), 0, 0, tiff.get_endian(), &[])?;
Ok(DcrDecoder {
tiff,
rawloader,
camera,
makernote,
})
}
}
impl<'a> Decoder for DcrDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::CFAPattern)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with CFAPattern tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof_padded(offset as u64)?; // TODO add size and check all samples
let linearization = fetch_tiff_tag!(self.makernote, TiffCommonTag::DcrLinearization);
let curve = {
let mut points = Vec::new();
for i in 0..linearization.count() {
points.push(linearization.force_u32(i) as u16);
}
LookupTable::new(&points)
};
let image = DcrDecoder::decode_kodak65000(&src, &curve, 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::DCR
}
}
impl<'a> DcrDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
let dcrwb = fetch_tiff_tag!(self.makernote, TiffCommonTag::DcrWB);
if dcrwb.count() >= 46 {
let levels = dcrwb.get_data();
Ok([
2048.0 / BEu16(levels, 40) as f32,
2048.0 / BEu16(levels, 42) as f32,
2048.0 / BEu16(levels, 44) as f32,
f32::NAN,
])
} else {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
}
pub(crate) fn decode_kodak65000(buf: &[u8], curve: &LookupTable, width: usize, height: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
let mut input = ByteStream::new(buf, Endian::Little);
let mut random: u32 = 0;
for row in 0..height {
for col in (0..width).step_by(256) {
let mut pred: [i32; 2] = [0; 2];
let buf = DcrDecoder::decode_segment(&mut input, cmp::min(256, width - col));
for (i, val) in buf.iter().enumerate() {
pred[i & 1] += *val;
if pred[i & 1] < 0 {
panic!("Found a negative pixel!");
}
out[row * width + col + i] = curve.dither(pred[i & 1] as u16, &mut random);
}
}
}
out
}
fn decode_segment(input: &mut ByteStream, size: usize) -> Vec<i32> {
let mut out: Vec<i32> = vec![0; size];
let mut lens: [usize; 256] = [0; 256];
for i in (0..size).step_by(2) {
lens[i] = (input.peek_u8() & 15) as usize;
lens[i + 1] = (input.get_u8() >> 4) as usize;
}
let mut bitbuf: u64 = 0;
let mut bits: usize = 0;
if (size & 7) == 4 {
bitbuf = ((input.get_u8() as u64) << 8) | (input.get_u8() as u64);
bits = 16;
}
for i in 0..size {
let len = lens[i];
if bits < len {
for j in (0..32).step_by(8) {
bitbuf += (input.get_u8() as u64) << (bits + (j ^ 8));
}
bits += 32;
}
out[i] = (bitbuf & (0xffff >> (16 - len))) as i32;
bitbuf >>= len;
bits -= len;
if len != 0 && (out[i] & (1 << (len - 1))) == 0 {
out[i] -= (1 << len) - 1;
}
}
out
}
}
+75
View File
@@ -0,0 +1,75 @@
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::analyze::FormatDump;
use crate::bits::LookupTable;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::reader::TiffReader;
use crate::packed::decode_8bit_wtable;
use crate::rawsource::RawSource;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct DcsDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
camera: Camera,
}
impl<'a> DcsDecoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<DcsDecoder<'a>> {
let camera = rawloader.check_supported(tiff.root_ifd())?;
Ok(DcsDecoder { camera, tiff, rawloader })
}
}
impl<'a> Decoder for DcsDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_ifd_with_new_subfile_type(0)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find IFD with subfile type 0")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof_padded(offset as u64)?; // TODO add size and check all samples
let linearization = fetch_tiff_tag!(self.tiff, TiffCommonTag::GrayResponse);
let table = {
let mut t: [u16; 256] = [0; 256];
for i in 0..256 {
t[i] = linearization.force_u32(i) as u16;
}
LookupTable::new(&t)
};
let image = decode_8bit_wtable(&src, &table, width, height, dummy);
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, [f32::NAN, f32::NAN, f32::NAN, f32::NAN], 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::DCS
}
}
+436
View File
@@ -0,0 +1,436 @@
use crate::RawImage;
use crate::cfa::*;
use crate::decoders::*;
use crate::formats::tiff::Entry;
use crate::formats::tiff::Rational;
use crate::formats::tiff::Value;
use crate::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::imgop::matrix::*;
use crate::imgop::xyz::*;
use crate::tags::DngTag;
use crate::tags::TiffCommonTag;
#[derive(Debug, Clone)]
pub struct DngDecoder<'a> {
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
}
impl<'a> DngDecoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<DngDecoder<'a>> {
Ok(DngDecoder { tiff, rawloader })
}
}
/// DNG format encapsulation for analyzer
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DngFormat {
tiff: GenericTiffReader,
}
impl<'a> Decoder for DngDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self.get_raw_ifd()?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let cpp = fetch_tiff_tag!(raw, TiffCommonTag::SamplesPerPixel).force_usize(0);
let bits = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_u32(0);
let orientation = Orientation::from_tiff(self.tiff.root_ifd());
let mut cam = self.make_camera(raw, width, height)?;
// If we know the camera, re-use the clean names
if let Ok(known_cam) = self
.rawloader
.check_supported_with_mode(self.tiff.root_ifd(), "dng")
.or_else(|_| self.rawloader.check_supported(self.tiff.root_ifd()))
{
cam.clean_make = known_cam.clean_make;
cam.clean_model = known_cam.clean_model;
cam.hints.extend_from_slice(&known_cam.hints);
cam.params.extend(known_cam.params.iter().map(|(k, v)| (k.clone(), v.clone())));
} else {
log::debug!("DNG: camera {} / {} is not in the camera catalog", cam.make, cam.model);
// panic!("Camera {} / {} is not in the camera catalog", cam.make, cam.model);
}
let blacklevel = self.get_blacklevels(raw)?;
let whitelevel = self.get_whitelevels(raw)?.or(Some(WhiteLevel::new_bits(bits, cpp)));
let photometric = match fetch_tiff_tag!(raw, TiffCommonTag::PhotometricInt).force_u32(0) {
1 => RawPhotometricInterpretation::BlackIsZero,
32803 => RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&cam)),
34892 => RawPhotometricInterpretation::LinearRaw,
_ => todo!(),
};
let raw_data = plain_image_from_ifd(raw, file)?;
let wb_coeffs = self.get_wb(&cam)?;
let mut image = RawImage::new_with_data(cam, raw_data, width * cpp, height, cpp, wb_coeffs, photometric, blacklevel, whitelevel, dummy);
image.orientation = orientation;
Ok(image)
}
fn format_dump(&self) -> FormatDump {
FormatDump::Dng(DngFormat { tiff: self.tiff.clone() })
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let raw = self.get_raw_ifd()?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let mut cam = self.make_camera(raw, width, height)?;
// If we know the camera, re-use the clean names
if let Ok(known_cam) = self.rawloader.check_supported(self.tiff.root_ifd()) {
cam.clean_make = known_cam.clean_make;
cam.clean_model = known_cam.clean_model;
}
let exif = Exif::new(self.tiff.root_ifd())?;
let mdata = RawMetadata::new(&cam, exif);
Ok(mdata)
}
fn thumbnail_image(&self, file: &RawSource, _params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if let Some(thumb_ifd) = Some(self.tiff.root_ifd()).filter(|ifd| ifd.get_entry(TiffCommonTag::NewSubFileType).map(|entry| entry.force_u16(0)) == Some(1)) {
Ok(Some(dynamic_image_from_ifd(thumb_ifd, file)?))
} else {
Ok(None)
}
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
if let Some(sub_ifds) = self.tiff.root_ifd().get_sub_ifd_all(TiffCommonTag::SubIFDs) {
let first_ifd = sub_ifds
.iter()
.find(|ifd| ifd.get_entry(TiffCommonTag::NewSubFileType).map(|entry| entry.force_u32(0)) == Some(1));
if let Some(preview_ifd) = first_ifd {
return Ok(Some(dynamic_image_from_ifd(preview_ifd, file)?));
}
}
Ok(None)
}
fn ifd(&self, wk_ifd: WellKnownIFD) -> Result<Option<Rc<IFD>>> {
Ok(match wk_ifd {
WellKnownIFD::Root => Some(Rc::new(self.tiff.root_ifd().clone())),
WellKnownIFD::Raw => Some(Rc::new(self.get_raw_ifd()?.clone())),
WellKnownIFD::Exif => self
.tiff
.root_ifd()
.get_sub_ifd_all(ExifTag::ExifOffset)
.and_then(|list| list.get(0))
.cloned()
.map(Rc::new),
WellKnownIFD::ExifGps => self
.tiff
.root_ifd()
.get_sub_ifd_all(ExifTag::GPSInfo)
.and_then(|list| list.get(0))
.cloned()
.map(Rc::new),
WellKnownIFD::VirtualDngRawTags => {
let mut ifd = IFD::default();
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::OpcodeList1);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::OpcodeList2);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::OpcodeList3);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::NoiseProfile);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::BayerGreenSplit);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::ChromaBlurRadius);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::AntiAliasStrength);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::NoiseReductionApplied);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::ProfileGainTableMap);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::CameraCalibration1);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::CameraCalibration2);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::CameraCalibration3);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::ForwardMatrix1);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::ForwardMatrix2);
IFD::copy_tag(&mut ifd, self.get_raw_ifd()?, DngTag::ForwardMatrix3);
Some(Rc::new(ifd))
}
WellKnownIFD::VirtualDngRootTags => {
let mut ifd = IFD::default();
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileEmbedPolicy);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData1);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData2);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData3);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapDims);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData1);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData2);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapData3);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileHueSatMapEncoding);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileLookTableData);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileLookTableDims);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileLookTableEncoding);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileName);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileCopyright);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::ProfileToneCurve);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DNGPrivateData);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::MakerNoteSafety);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::AnalogBalance);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::BaselineExposure);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::BaselineNoise);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::BaselineSharpness);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::LinearResponseLimit);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::CameraSerialNumber);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::AsShotICCProfile);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::AsShotPreProfileMatrix);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::CurrentICCProfile);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::CurrentPreProfileMatrix);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::AsShotProfileName);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DefaultBlackRender);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::BaselineExposureOffset);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DepthFormat);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DepthNear);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DepthFar);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DepthUnits);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::DepthMeasureType);
IFD::copy_tag(&mut ifd, self.tiff.root_ifd(), DngTag::RGBTables);
Some(Rc::new(ifd))
}
_ => return Ok(None),
})
}
fn format_hint(&self) -> FormatHint {
FormatHint::DNG
}
}
impl<'a> DngDecoder<'a> {
fn get_raw_ifd(&self) -> Result<&IFD> {
let ifds = self
.tiff
.find_ifds_with_tag(TiffCommonTag::Compression)
.into_iter()
.filter(|ifd| {
let compression = (**ifd)
.get_entry(TiffCommonTag::Compression)
.expect("This IFD must contains this tag")
.force_u32(0);
let subsampled = match (**ifd).get_entry(TiffCommonTag::NewSubFileType) {
Some(e) => e.force_u32(0) & 1 != 0,
None => false,
};
!subsampled && (compression == 7 || compression == 8 || compression == 1 || compression == 0x884c || compression == 52546)
})
.collect::<Vec<&IFD>>();
if let Some(first) = ifds.first() {
Ok(first)
} else {
Err(RawlerError::DecoderFailed(format!("TODO: Unsupported DNG compression")))
}
}
fn make_camera(&self, raw: &IFD, width: usize, height: usize) -> Result<Camera> {
let mode = String::from("dng");
let make = self
.tiff
.root_ifd()
.get_entry(TiffCommonTag::Make)
.and_then(|x| x.as_string())
.cloned()
.unwrap_or_default();
let model = self
.tiff
.root_ifd()
.get_entry(TiffCommonTag::Model)
.and_then(|x| x.as_string())
.cloned()
.unwrap_or_default();
let active_area = self.get_active_area(raw, width, height);
let crop_area = if let Some(crops) = self.get_crop(raw) {
if let Some(active_area) = &active_area {
let mut full = crops;
full.p.x += active_area[0]; // left
full.p.y += active_area[1]; // Top
Some(full.as_ltrb_offsets(width, height))
} else {
Some(crops.as_ltrb_offsets(width, height))
}
} else {
None
};
let linear = fetch_tiff_tag!(raw, TiffCommonTag::PhotometricInt).force_usize(0) == 34892;
let cfa = if linear { CFA::default() } else { self.get_cfa(raw)? };
let color_matrix = self.get_color_matrix()?;
let real_bps = if raw.has_entry(TiffCommonTag::Linearization) {
// If DNG contains linearization table, output is always 16 bits
16
} else {
raw.get_entry(TiffCommonTag::BitsPerSample).map(|v| v.force_usize(0)).unwrap_or(16)
};
Ok(Camera {
clean_make: make.clone(),
clean_model: model.clone(),
make,
model,
mode,
whitelevel: None,
blacklevel: None,
blackareah: None,
blackareav: None,
xyz_to_cam: Default::default(),
color_matrix,
cfa,
active_area,
crop_area,
real_bps,
..Default::default()
})
}
fn get_wb(&self, cam: &Camera) -> Result<[f32; 4]> {
if let Some(levels) = self.tiff.get_entry(DngTag::AsShotNeutral) {
Ok([1.0 / levels.force_f32(0), 1.0 / levels.force_f32(1), 1.0 / levels.force_f32(2), f32::NAN])
} else if let Some(levels) = self.tiff.get_entry(DngTag::AsShotWhiteXY) {
// TODO: improve once AnalogBalance and CC is properly implemented
if let Some(flat_colormatrix) = cam.color_matrix.get(&Illuminant::D65)
&& let Some(colormatrix) = transform_1d::<3, 3>(flat_colormatrix)
{
let wb_coeff = xy_whitepoint_to_wb_coeff(levels.force_f32(0), levels.force_f32(1), &colormatrix);
Ok([wb_coeff[0], wb_coeff[1], wb_coeff[2], f32::NAN])
} else {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
} else {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
}
fn get_blacklevels(&self, raw: &IFD) -> Result<Option<BlackLevel>> {
let cpp = raw.get_entry(TiffCommonTag::SamplesPerPixel).map(|entry| entry.force_usize(0)).unwrap_or(1);
if let Some(entry) = raw.get_entry(TiffCommonTag::BlackLevels) {
let levels = match &entry.value {
Value::Short(black) => black.iter().copied().map(Rational::from).collect(),
Value::Long(black) => black.iter().copied().map(Rational::from).collect(),
Value::Rational(black) => black.clone(),
_ => return Err(format!("Unsupported BlackLevel type: {}", entry.value_type_name()).into()),
};
let mut repeat = (1, 1);
if let Some(Entry {
value: Value::Short(value), ..
}) = raw.get_entry(DngTag::BlackLevelRepeatDim)
{
if value.len() == 2 {
repeat = (value[0] as usize, value[1] as usize);
} else {
// Pentax K-3 Mark III Monochrome is known to has invalid tag
log::warn!("File has BlackLevelRepeatDim tag but with invalid length: {}", value.len());
}
}
Ok(Some(BlackLevel::new(&levels, repeat.1, repeat.0, cpp)))
} else {
Ok(None)
}
}
fn get_whitelevels(&self, raw: &IFD) -> Result<Option<WhiteLevel>> {
let cpp = fetch_tiff_tag!(raw, TiffCommonTag::SamplesPerPixel).force_usize(0);
if let Some(levels) = raw.get_entry(TiffCommonTag::WhiteLevel) {
let mut whitelevels = WhiteLevel((0..levels.count()).map(|i| levels.force_u32(i as usize)).collect());
// Fixes a bug where only a single whitelevel value is given.
if whitelevels.0.len() == 1 && cpp > 1 {
whitelevels.0 = vec![whitelevels.0[0]; cpp];
}
return Ok(Some(whitelevels));
}
Ok(None)
}
fn get_cfa(&self, raw: &IFD) -> Result<CFA> {
let pattern = fetch_tiff_tag!(raw, TiffCommonTag::CFAPattern);
let cfa = CFA::new_from_tag(pattern);
// If DNG has active area, we need to calulate back the CFA pattern,
// because for DNG the CFAPattern is relative to ActiveArea and we
// use (0, 0) as starting point.
if let Some(active_area) = self.get_active_area_borders(raw) {
let top = active_area[0];
let left = active_area[1];
Ok(cfa.shift(left % cfa.width, top % cfa.height))
} else {
Ok(cfa)
}
}
fn get_active_area_borders(&self, raw: &IFD) -> Option<[usize; 4]> {
if let Some(crops) = raw.get_entry(DngTag::ActiveArea) {
let rect = [crops.force_usize(0), crops.force_usize(1), crops.force_usize(2), crops.force_usize(3)];
Some(rect)
} else {
// Ignore missing crops, at least some pentax DNGs don't have it
None
}
}
fn get_active_area(&self, raw: &IFD, width: usize, height: usize) -> Option<[usize; 4]> {
if let Some(rect) = self.get_active_area_borders(raw) {
Some(Rect::new_with_dng(&rect).as_ltrb_offsets(width, height))
} else {
None
}
}
fn get_crop(&self, raw: &IFD) -> Option<Rect> {
if let Some(crops) = raw.get_entry(DngTag::DefaultCropOrigin) {
let p = Point::new(crops.force_usize(0), crops.force_usize(1));
if let Some(size) = raw.get_entry(DngTag::DefaultCropSize) {
let d = Dim2::new(size.force_usize(0), size.force_usize(1));
return Some(Rect::new(p, d));
}
}
None
}
fn _get_masked_areas(&self, raw: &IFD) -> Vec<Rect> {
let mut areas = Vec::new();
if let Some(masked_area) = raw.get_entry(TiffCommonTag::MaskedAreas) {
for x in (0..masked_area.count() as usize).step_by(4) {
areas.push(Rect::new_with_points(
Point::new(masked_area.force_usize(x), masked_area.force_usize(x + 1)),
Point::new(masked_area.force_usize(x + 2), masked_area.force_usize(x + 3)),
));
}
}
areas
}
fn get_color_matrix(&self) -> Result<HashMap<Illuminant, FlatColorMatrix>> {
let mut result = HashMap::new();
let mut read_matrix = |cal: DngTag, mat: DngTag| -> Result<()> {
if let Some(c) = self.tiff.get_entry(mat) {
let illuminant_val = self.tiff.get_entry(cal).map(|e| e.force_u16(0)).unwrap_or(21);
let illuminant: Illuminant = illuminant_val.try_into().unwrap_or(Illuminant::D65);
let mut matrix = FlatColorMatrix::new();
for i in 0..c.count() as usize {
matrix.push(c.force_f32(i));
}
if !matrix.is_empty() && matrix.len() <= 12 {
result.insert(illuminant, matrix);
} else {
log::warn!("Invalid ColorMatrix dimensions for illuminant {:?}: length {}", illuminant, matrix.len());
}
}
Ok(())
};
read_matrix(DngTag::CalibrationIlluminant1, DngTag::ColorMatrix1)?;
read_matrix(DngTag::CalibrationIlluminant2, DngTag::ColorMatrix2)?;
// TODO: add 3
Ok(result)
}
}
+113
View File
@@ -0,0 +1,113 @@
use log::warn;
use crate::RawlerError;
use crate::analyze::FormatDump;
use crate::rawsource::RawSource;
use crate::RawImage;
use crate::RawLoader;
use crate::Result;
use crate::bits::BEu16;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::ifd::OffsetMode;
use crate::formats::tiff::reader::TiffReader;
use crate::packed::decode_12be_wcontrol;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::BlackLevel;
use super::CFAConfig;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::RawPhotometricInterpretation;
#[derive(Debug, Clone)]
pub struct ErfDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
camera: Camera,
}
impl<'a> ErfDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<ErfDecoder<'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!("ERF makernote not found");
None
}
.ok_or("File has not makernotes")?;
Ok(ErfDecoder {
tiff,
rawloader,
camera,
makernote,
})
}
}
impl<'a> Decoder for ErfDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::CFAPattern)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with CFAPattern tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = decode_12be_wcontrol(src, width, height, dummy);
let cpp = 1;
let blacklevel = self.get_blacklevel(cpp);
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let img = RawImage::new(self.camera.clone(), image, cpp, self.get_wb()?, photometric, blacklevel, None, dummy);
Ok(img)
}
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::ERF
}
}
impl<'a> ErfDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
let levels = fetch_tiff_tag!(self.makernote, TiffCommonTag::EpsonWB);
if levels.count() != 256 {
Err(RawlerError::DecoderFailed("ERF: Levels count is off".to_string()))
} else {
let r = BEu16(levels.get_data(), 48) as f32;
let b = BEu16(levels.get_data(), 50) as f32;
Ok([r * 508.0 * 1.078 / 65536.0, 1.0, b * 382.0 * 1.173 / 65536.0, f32::NAN])
}
}
fn get_blacklevel(&self, cpp: usize) -> Option<BlackLevel> {
if let Some(levels) = self.makernote.get_entry(0x0401) {
let levels = [levels.force_u16(0), levels.force_u16(1), levels.force_u16(2), levels.force_u16(3)];
return Some(BlackLevel::new(&levels, self.camera.cfa.width, self.camera.cfa.height, cpp));
}
None
}
}
File diff suppressed because it is too large Load Diff
+219
View File
@@ -0,0 +1,219 @@
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)
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::analyze::FormatDump;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::reader::TiffReader;
use crate::packed::decode_12be;
use crate::rawsource::RawSource;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct MefDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
camera: Camera,
}
impl<'a> MefDecoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<MefDecoder<'a>> {
let camera = rawloader.check_supported(tiff.root_ifd())?;
Ok(MefDecoder { tiff, rawloader, camera })
}
}
impl<'a> Decoder for MefDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = &self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::CFAPattern)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with CFAPattern tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = decode_12be(src, width, height, dummy);
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, [f32::NAN, f32::NAN, f32::NAN, f32::NAN], 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::MEF
}
}
File diff suppressed because it is too large Load Diff
+142
View File
@@ -0,0 +1,142 @@
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image_ok;
use crate::analyze::FormatDump;
use crate::decompressors::ljpeg::LjpegDecompressor;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::reader::TiffReader;
use crate::packed::decode_16be;
use crate::packed::decode_16le;
use crate::pixarray::PixU16;
use crate::rawsource::RawSource;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct MosDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
camera: Camera,
}
impl<'a> MosDecoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<MosDecoder<'a>> {
let make = Self::xmp_tag(&tiff, "Make")?;
let model_full = Self::xmp_tag(&tiff, "Model")?;
let model = model_full.split_terminator('(').next().unwrap();
let camera = rawloader.check_supported_with_everything(&make, model, "")?;
Ok(MosDecoder { tiff, rawloader, camera })
}
}
impl<'a> Decoder for MosDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::TileOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with TileOffsets tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::TileOffsets).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = match fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_usize(0) {
1 => {
if self.tiff.little_endian() {
decode_16le(src, width, height, dummy)
} else {
decode_16be(src, width, height, dummy)
}
}
7 | 99 => self.decode_compressed(&self.camera, src, width, height, dummy)?,
x => return Err(RawlerError::unsupported(&self.camera, format!("MOS: unsupported compression {}", x))),
};
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::MOS
}
}
impl<'a> MosDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
let meta = fetch_tiff_tag!(self.tiff, TiffCommonTag::LeafMetadata).get_data();
let mut pos = 0;
// We need at least 16+45+10 bytes for the NeutObj_neutrals section itself
while pos + 70 < meta.len() {
if meta[pos..pos + 16] == b"NeutObj_neutrals"[..] {
let data = &meta[pos + 44..];
if let Some(endpos) = data.iter().position(|&x| x == 0) {
let nums = String::from_utf8_lossy(&data[0..endpos])
.split_terminator('\n')
.map(|x| x.parse::<f32>().unwrap_or(f32::NAN))
.collect::<Vec<f32>>();
if nums.len() == 4 {
return Ok([nums[0] / nums[1], nums[0] / nums[2], nums[0] / nums[3], f32::NAN]);
}
}
break;
}
pos += 1;
}
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
fn xmp_tag(tiff: &GenericTiffReader, tag: &str) -> Result<String> {
let xmp_bytes = fetch_tiff_tag!(tiff, TiffCommonTag::Xmp).get_data();
let xmp = String::from_utf8_lossy(xmp_bytes);
let error = format!("MOS: Couldn't find XMP tag {}", tag);
let start = xmp.find(&format!("<tiff:{}>", tag)).ok_or_else(|| error.clone())?;
let end = xmp.find(&format!("</tiff:{}>", tag)).ok_or(error)?;
Ok(xmp[start + tag.len() + 7..end].to_string())
}
pub fn decode_compressed(&self, cam: &Camera, src: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let interlaced = cam.find_hint("interlaced");
Self::do_decode(src, interlaced, width, height, dummy)
}
pub(crate) fn do_decode(src: &[u8], interlaced: bool, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
if dummy {
return Ok(PixU16::new_uninit(width, height));
}
let decompressor = LjpegDecompressor::new_full(src, true, true)?;
let ljpegout = decompressor.decode_leaf(width, height)?;
if interlaced {
let mut out = alloc_image_ok!(width, height, dummy);
for (row, line) in ljpegout.pixels().chunks_exact(width).enumerate() {
let orow = if row & 1 == 1 { height - 1 - row / 2 } else { row / 2 };
out[orow * width..(orow + 1) * width].copy_from_slice(line);
}
Ok(out)
} else {
Ok(ljpegout)
}
}
}
+271
View File
@@ -0,0 +1,271 @@
use byteorder::ReadBytesExt;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::analyze::FormatDump;
use crate::bits::BEu16;
use crate::bits::BEu32;
use crate::exif::Exif;
use crate::formats::jfif::Jfif;
use crate::formats::jfif::Segment;
use crate::formats::jfif::is_exif;
use crate::formats::tiff::IFD;
use crate::packed::decode_12be;
use crate::packed::decode_12be_unpacked;
use crate::packed::decode_16le;
use crate::rawsource::RawSource;
use crate::tags::TiffCommonTag;
use std::io::Cursor;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
const MRW_MAGIC: u32 = 0x004D524D; // !memcmp (head,"\0MRM",4))?
pub fn is_mrw(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => {
if BEu32(buf, 0) == MRW_MAGIC {
true
} else {
log::debug!("MRW: File MAGIC not found");
false
}
}
Err(err) => {
log::error!("is_mrw() error: {:?}", err);
false
}
}
}
#[derive(Debug, Clone)]
pub struct MrwDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
data_offset: usize,
raw_width: usize,
raw_height: usize,
bits: u8,
packed: bool,
wb_vals: [u16; 4],
tiff: IFD,
camera: Camera,
}
impl<'a> MrwDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<MrwDecoder<'a>> {
if is_mrw(file) {
Self::new_mrw(file, rawloader)
} else if is_exif(file) {
let exif = Jfif::new(file)?;
Self::new_jfif(file, exif, rawloader)
} else {
Err(crate::RawlerError::DecoderFailed(format!(
"MRW decoder can't decode given file: {}",
file.path().display()
)))
}
}
/// Makernotes for MRW starts with "MLY" ASCII string
fn get_mly_wb(ifd: &IFD, rawfile: &RawSource, data_offset: u64) -> Result<[u16; 4]> {
if let Some(makernotes) = ifd.get_entry_recursive(TiffCommonTag::Makernote) {
debug_assert_eq!(makernotes.get_data()[0..3], [b'M', b'L', b'Y']);
if makernotes.get_data().get(0..3) == Some(b"MLY") {
let mut buf = Cursor::new(rawfile.as_vec()?);
let mut wb = [0_u16; 4];
let mut cam_mul = [1_u16; 4];
while buf.position() < data_offset {
wb[0] = wb[2];
wb[2] = wb[1];
wb[1] = wb[3];
wb[3] = match buf.read_u16::<byteorder::BigEndian>() {
Ok(val) => val,
Err(_) => break,
};
if wb[1] == 256 && wb[3] == 256 && wb[0] > 256 && wb[0] < 640 && wb[2] > 256 && wb[2] < 640 {
cam_mul.copy_from_slice(&wb);
cam_mul.swap(2, 3);
log::debug!("Found WB match: {:?}", cam_mul);
}
}
return Ok(cam_mul);
}
} else {
log::warn!("Makernotes not found, fall back to defaults!");
}
Ok([1, 1, 1, 1])
}
pub fn new_jfif(file: &RawSource, jfif: Jfif, rawloader: &'a RawLoader) -> Result<MrwDecoder<'a>> {
let tiff = jfif
.exif_ifd()
.ok_or(RawlerError::DecoderFailed("No EXIF IFD found in JFIF file".to_string()))?
.clone();
let camera = rawloader.check_supported(&tiff)?;
let (app1_offset, app1_len) = jfif
.segments
.iter()
.map(|seg| {
if let Segment::APP1 { offset, app1 } = seg {
Some((*offset, app1.len))
} else {
None
}
})
.find(Option::is_some)
.flatten()
.unwrap_or_default();
let data_offset = (app1_offset + app1_len + camera.param_i32("offset_corr").unwrap_or(0) as u64) as usize;
let raw_width = camera.raw_width;
let raw_height = camera.raw_height;
let packed = false;
let wb_vals = Self::get_mly_wb(&tiff, file, data_offset as u64)?;
let bits = 16;
Ok(MrwDecoder {
data_offset,
raw_width,
raw_height,
bits,
packed,
wb_vals,
tiff,
rawloader,
camera,
})
}
fn new_mrw(file: &RawSource, rawloader: &'a RawLoader) -> Result<MrwDecoder<'a>> {
let full = file.as_vec()?;
let buf = &full;
let data_offset: usize = (BEu32(buf, 4) + 8) as usize;
let mut raw_height: usize = 0;
let mut raw_width: usize = 0;
let bits = 12;
let mut packed = false;
let mut wb_vals: [u16; 4] = [0; 4];
let mut tiffpos: usize = 0;
let mut currpos: usize = 8;
// At most we read 20 bytes from currpos so check we don't step outside that
while currpos + 20 < data_offset {
let tag: u32 = BEu32(buf, currpos);
let len: u32 = BEu32(buf, currpos + 4);
match tag {
0x505244 => {
// PRD
raw_height = BEu16(buf, currpos + 16) as usize;
raw_width = BEu16(buf, currpos + 18) as usize;
packed = buf[currpos + 24] == 12;
}
0x574247 => {
// WBG
for i in 0..4 {
wb_vals[i] = BEu16(buf, currpos + 12 + i * 2);
}
}
0x545457 => {
// TTW
// Base value for offsets needs to be at the beginning of the
// TIFF block, not the file
tiffpos = currpos + 8;
}
_ => {}
}
currpos += (len + 8) as usize;
}
let tiff_data = file.subview_until_eof(tiffpos as u64)?;
let tiff = IFD::new(&mut Cursor::new(tiff_data), 8, 0, 0, crate::bits::Endian::Big, &[])?;
let camera = rawloader.check_supported(&tiff)?;
Ok(MrwDecoder {
data_offset,
raw_width,
raw_height,
bits,
packed,
wb_vals,
tiff,
rawloader,
camera,
})
}
}
impl<'a> Decoder for MrwDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let src = file.subview_until_eof(self.data_offset as u64)?;
let buffer = if self.bits == 16 {
decode_16le(src, self.raw_width, self.raw_height, dummy)
} else if self.packed {
decode_12be(src, self.raw_width, self.raw_height, dummy)
} else {
decode_12be_unpacked(src, self.raw_width, self.raw_height, dummy)
};
let wb_coeffs = if self.camera.find_hint("swapped_wb") {
[self.wb_vals[2] as f32, self.wb_vals[0] as f32, self.wb_vals[0] as f32, self.wb_vals[1] as f32]
} else {
[self.wb_vals[0] as f32, self.wb_vals[1] as f32, self.wb_vals[2] as f32, self.wb_vals[3] as f32]
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, normalize_wb(wb_coeffs), buffer, dummy)
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(&self.tiff)?;
let mdata = RawMetadata::new(&self.camera, exif);
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::MRW
}
/*
/// File is EXIF structure, but contains no valid JPEG image, so this is useless...
fn full_image(&self, file: &RawSource) -> Result<Option<image::DynamicImage>> {
if is_mrw(file) {
Ok(None)
} else if is_exif(file) {
let buf = file.as_vec()?;
dump_buf("/tmp/dmp1", &buf);
let img = image::load_from_memory_with_format(&buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to get full image from RAW file: {:?}", err)))?;
log::debug!("Got full image from RAW");
Ok(Some(img))
} else {
Ok(None)
}
}
*/
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
log::debug!("MRW raw wb: {:?}", raw_wb);
let div = raw_wb[1]; // G1 should be 1024 and we use this as divisor
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
+817
View File
@@ -0,0 +1,817 @@
use image::DynamicImage;
use log::debug;
use log::warn;
use serde::Deserialize;
use serde::Serialize;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image_ok;
use crate::analyze::FormatDump;
use crate::bits::BEu16;
use crate::bits::BEu32;
use crate::bits::Endian;
use crate::bits::LEu32;
use crate::bits::LookupTable;
use crate::bits::clampbits;
use crate::buffer::PaddedBuf;
use crate::decoders::decode_threaded;
use crate::decoders::dynamic_image_from_ifd;
use crate::decoders::dynamic_image_from_jpeg_interchange_format;
use crate::decoders::nef::lensdata::NefLensData;
use crate::decompressors::ljpeg::huffman::HuffTable;
use crate::exif::Exif;
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::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::*;
use crate::pixarray::PixU16;
use crate::pumps::BitPump;
use crate::pumps::BitPumpMSB;
use crate::pumps::ByteStream;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawimage::WhiteLevel;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::BlackLevel;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
mod decrypt;
pub mod lensdata;
const NIKON_F_MOUNT: &str = "F-mount";
const NIKON_Z_MOUNT: &str = "Z-mount";
// NEF Huffman tables in order. First two are the normal huffman definitions.
// Third one are weird shifts that are used in the lossy split encodings only
// Values are extracted from dcraw with the shifts unmangled out.
const NIKON_TREE: [[[u8; 16]; 3]; 6] = [
[
// 12-bit lossy
[0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0],
[5, 4, 3, 6, 2, 7, 1, 0, 8, 9, 11, 10, 12, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[
// 12-bit lossy after split
[0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0],
[6, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0, 11, 12, 12, 0, 0],
[3, 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[
// 12-bit lossless
[0, 0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0],
[5, 4, 6, 3, 7, 2, 8, 1, 9, 0, 10, 11, 12, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[
// 14-bit lossy
[0, 0, 1, 4, 3, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0],
[5, 6, 4, 7, 8, 3, 9, 2, 1, 0, 10, 11, 12, 13, 14, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[
// 14-bit lossy after split
[0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0],
[8, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 13, 14, 0],
[0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[
// 14-bit lossless
[0, 0, 1, 4, 2, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0],
[7, 6, 8, 5, 9, 4, 10, 3, 11, 12, 2, 0, 1, 13, 14, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
];
// We use this for the D50 and D2X whacky WB "encryption"
const WB_SERIALMAP: [u8; 256] = [
0xc1, 0xbf, 0x6d, 0x0d, 0x59, 0xc5, 0x13, 0x9d, 0x83, 0x61, 0x6b, 0x4f, 0xc7, 0x7f, 0x3d, 0x3d, 0x53, 0x59, 0xe3, 0xc7, 0xe9, 0x2f, 0x95, 0xa7, 0x95, 0x1f,
0xdf, 0x7f, 0x2b, 0x29, 0xc7, 0x0d, 0xdf, 0x07, 0xef, 0x71, 0x89, 0x3d, 0x13, 0x3d, 0x3b, 0x13, 0xfb, 0x0d, 0x89, 0xc1, 0x65, 0x1f, 0xb3, 0x0d, 0x6b, 0x29,
0xe3, 0xfb, 0xef, 0xa3, 0x6b, 0x47, 0x7f, 0x95, 0x35, 0xa7, 0x47, 0x4f, 0xc7, 0xf1, 0x59, 0x95, 0x35, 0x11, 0x29, 0x61, 0xf1, 0x3d, 0xb3, 0x2b, 0x0d, 0x43,
0x89, 0xc1, 0x9d, 0x9d, 0x89, 0x65, 0xf1, 0xe9, 0xdf, 0xbf, 0x3d, 0x7f, 0x53, 0x97, 0xe5, 0xe9, 0x95, 0x17, 0x1d, 0x3d, 0x8b, 0xfb, 0xc7, 0xe3, 0x67, 0xa7,
0x07, 0xf1, 0x71, 0xa7, 0x53, 0xb5, 0x29, 0x89, 0xe5, 0x2b, 0xa7, 0x17, 0x29, 0xe9, 0x4f, 0xc5, 0x65, 0x6d, 0x6b, 0xef, 0x0d, 0x89, 0x49, 0x2f, 0xb3, 0x43,
0x53, 0x65, 0x1d, 0x49, 0xa3, 0x13, 0x89, 0x59, 0xef, 0x6b, 0xef, 0x65, 0x1d, 0x0b, 0x59, 0x13, 0xe3, 0x4f, 0x9d, 0xb3, 0x29, 0x43, 0x2b, 0x07, 0x1d, 0x95,
0x59, 0x59, 0x47, 0xfb, 0xe5, 0xe9, 0x61, 0x47, 0x2f, 0x35, 0x7f, 0x17, 0x7f, 0xef, 0x7f, 0x95, 0x95, 0x71, 0xd3, 0xa3, 0x0b, 0x71, 0xa3, 0xad, 0x0b, 0x3b,
0xb5, 0xfb, 0xa3, 0xbf, 0x4f, 0x83, 0x1d, 0xad, 0xe9, 0x2f, 0x71, 0x65, 0xa3, 0xe5, 0x07, 0x35, 0x3d, 0x0d, 0xb5, 0xe9, 0xe5, 0x47, 0x3b, 0x9d, 0xef, 0x35,
0xa3, 0xbf, 0xb3, 0xdf, 0x53, 0xd3, 0x97, 0x53, 0x49, 0x71, 0x07, 0x35, 0x61, 0x71, 0x2f, 0x43, 0x2f, 0x11, 0xdf, 0x17, 0x97, 0xfb, 0x95, 0x3b, 0x7f, 0x6b,
0xd3, 0x25, 0xbf, 0xad, 0xc7, 0xc5, 0xc5, 0xb5, 0x8b, 0xef, 0x2f, 0xd3, 0x07, 0x6b, 0x25, 0x49, 0x95, 0x25, 0x49, 0x6d, 0x71, 0xc7,
];
const WB_KEYMAP: [u8; 256] = [
0xa7, 0xbc, 0xc9, 0xad, 0x91, 0xdf, 0x85, 0xe5, 0xd4, 0x78, 0xd5, 0x17, 0x46, 0x7c, 0x29, 0x4c, 0x4d, 0x03, 0xe9, 0x25, 0x68, 0x11, 0x86, 0xb3, 0xbd, 0xf7,
0x6f, 0x61, 0x22, 0xa2, 0x26, 0x34, 0x2a, 0xbe, 0x1e, 0x46, 0x14, 0x68, 0x9d, 0x44, 0x18, 0xc2, 0x40, 0xf4, 0x7e, 0x5f, 0x1b, 0xad, 0x0b, 0x94, 0xb6, 0x67,
0xb4, 0x0b, 0xe1, 0xea, 0x95, 0x9c, 0x66, 0xdc, 0xe7, 0x5d, 0x6c, 0x05, 0xda, 0xd5, 0xdf, 0x7a, 0xef, 0xf6, 0xdb, 0x1f, 0x82, 0x4c, 0xc0, 0x68, 0x47, 0xa1,
0xbd, 0xee, 0x39, 0x50, 0x56, 0x4a, 0xdd, 0xdf, 0xa5, 0xf8, 0xc6, 0xda, 0xca, 0x90, 0xca, 0x01, 0x42, 0x9d, 0x8b, 0x0c, 0x73, 0x43, 0x75, 0x05, 0x94, 0xde,
0x24, 0xb3, 0x80, 0x34, 0xe5, 0x2c, 0xdc, 0x9b, 0x3f, 0xca, 0x33, 0x45, 0xd0, 0xdb, 0x5f, 0xf5, 0x52, 0xc3, 0x21, 0xda, 0xe2, 0x22, 0x72, 0x6b, 0x3e, 0xd0,
0x5b, 0xa8, 0x87, 0x8c, 0x06, 0x5d, 0x0f, 0xdd, 0x09, 0x19, 0x93, 0xd0, 0xb9, 0xfc, 0x8b, 0x0f, 0x84, 0x60, 0x33, 0x1c, 0x9b, 0x45, 0xf1, 0xf0, 0xa3, 0x94,
0x3a, 0x12, 0x77, 0x33, 0x4d, 0x44, 0x78, 0x28, 0x3c, 0x9e, 0xfd, 0x65, 0x57, 0x16, 0x94, 0x6b, 0xfb, 0x59, 0xd0, 0xc8, 0x22, 0x36, 0xdb, 0xd2, 0x63, 0x98,
0x43, 0xa1, 0x04, 0x87, 0x86, 0xf7, 0xa6, 0x26, 0xbb, 0xd6, 0x59, 0x4d, 0xbf, 0x6a, 0x2e, 0xaa, 0x2b, 0xef, 0xe6, 0x78, 0xb6, 0x4e, 0xe0, 0x2f, 0xdc, 0x7c,
0xbe, 0x57, 0x19, 0x32, 0x7e, 0x2a, 0xd0, 0xb8, 0xba, 0x29, 0x00, 0x3c, 0x52, 0x7d, 0xa8, 0x49, 0x3b, 0x2d, 0xeb, 0x25, 0x49, 0xfa, 0xa3, 0xaa, 0x39, 0xa7,
0xc5, 0xa7, 0x50, 0x11, 0x36, 0xfb, 0xc6, 0x67, 0x4a, 0xf5, 0xa5, 0x12, 0x65, 0x7e, 0xb0, 0xdf, 0xaf, 0x4e, 0xb3, 0x61, 0x7f, 0x2f,
];
/// NEF format encapsulation for analyzer
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NefFormat {
tiff: GenericTiffReader,
}
#[derive(Debug, Clone)]
pub struct NefDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
camera: Camera,
}
impl<'a> NefDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<NefDecoder<'a>> {
let raw = tiff
.find_first_ifd_with_tag(TiffCommonTag::CFAPattern)
.or_else(|| tiff.find_ifd_with_new_subfile_type(0))
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a suitable IFD in NEF decoder")))?;
let bps = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_usize(0);
// Make sure we always use a 12/14 bit mode to get correct white/blackpoints
let mode = format!("{}bit", bps);
let camera = rawloader.check_supported_with_mode(tiff.root_ifd(), &mode)?;
let makernote = if let Some(exif) = tiff.find_first_ifd_with_tag(ExifTag::MakerNotes) {
exif.parse_makernote(&mut file.reader(), OffsetMode::Absolute, &[])?
} else {
warn!("NEF makernote not found");
None
}
.ok_or("File has not makernotes")?;
//makernote.dump::<ExifTag>(0).iter().for_each(|line| eprintln!("DUMP: {}", line));
Ok(NefDecoder {
tiff,
rawloader,
makernote,
camera,
})
}
}
impl<'a> Decoder for NefDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::CFAPattern)
.or_else(|| self.tiff.find_ifd_with_new_subfile_type(0))
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a suitable IFD in NEF decoder")))?;
let mut width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let bps = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_usize(0);
let mut cpp = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).count(); // Linear files don't have SamplesPerPixel
let compression = fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_usize(0);
let nef_compression = if let Some(z_makernote) = self.makernote.get_entry(NikonMakernote::Makernotes0x51) {
// For new Z models, a new tag 0x51 for makernotes appears. This contains
// The new-old NEFCompression tag. The old tag is unavailable in this models.
Some(NefCompression::try_from(crate::bits::LEu16(z_makernote.get_data(), 10)).map_err(RawlerError::from)?)
} else {
self
.makernote
.get_entry(NikonMakernote::NefCompression)
.map(|entry| entry.force_u16(0))
.map(NefCompression::try_from)
.transpose()
.map_err(RawlerError::from)?
};
debug!("TIFF compression flag: {}, NEF compression mode: {:?}", compression, nef_compression);
if matches!(nef_compression, Some(NefCompression::HighEfficency)) || matches!(nef_compression, Some(NefCompression::HighEfficencyStar)) {
return Err(RawlerError::DecoderFailed(format!("NEF compression {:?} is not supported", nef_compression)));
}
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let size = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts).force_usize(0);
let rows_per_strip = fetch_tiff_tag!(raw, TiffCommonTag::RowsPerStrip).get_usize(0).ok().flatten().unwrap_or(height);
// That's little bit hacky here. Some files like D500 using multiple strips.
// Because the strips has no holes between and are perfectly aligned, we can process the whole
// chunk at once, instead of iterating over every strip.
// It would be safer to process each strip offset, but it is not need for any known model so far.
let src = if rows_per_strip == height {
file.subview_padded(offset as u64, size as u64)?
} else {
let full_size: u32 = match fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts) {
Value::Long(data) => data.iter().copied().sum(),
_ => {
return Err("StripByteCounts is not of type LONG".into());
}
};
file.subview_padded(offset as u64, full_size as u64)?
};
let coeffs = normalize_wb(self.get_wb()?);
debug!("WB coeff: {:?}", coeffs);
assert_eq!(self.tiff.little_endian(), self.makernote.endian == Endian::Little);
let image = if self.camera.model == "NIKON D100" {
width = 3040;
decode_12be_wcontrol(&src, width, height, dummy)
} else if self.camera.find_hint("coolpixsplit") {
decode_12be_interlaced_unaligned(&src, width, height, dummy)
} else if self.camera.find_hint("msb32") {
decode_12be_msb32(&src, width, height, dummy)
} else if self.camera.find_hint("unpacked") {
// P7800 and others is LE, but data is BE, so we use hints here
if (self.tiff.little_endian() || self.camera.find_hint("little_endian")) && !self.camera.find_hint("big_endian") {
decode_16le(&src, width, height, dummy)
} else {
decode_16be(&src, width, height, dummy)
}
} else if let Some(padding) = self.is_uncompressed(raw)? {
debug!("NEF uncompressed row padding: {}, little-endian: {}", padding, self.tiff.little_endian());
match bps {
16 => {
// Used by Coolscan scanners
if self.tiff.little_endian() {
decode_16le(&src, width * cpp, height, dummy)
} else {
decode_16be(&src, width * cpp, height, dummy)
}
}
14 => {
if (self.tiff.little_endian() || self.camera.find_hint("little_endian")) && !self.camera.find_hint("big_endian") {
// Models like D6 uses packed instead of unpacked 14le encoding. And D6 uses
// row padding.
if matches!(nef_compression, Some(NefCompression::Packed14Bits)) {
decode_14le_padded(&src, width, height, (width * bps / u8::BITS as usize) + padding, dummy)
} else {
decode_14le_unpacked(&src, width, height, dummy)
}
} else {
decode_14be_unpacked(&src, width, height, dummy)
}
}
12 => {
if (self.tiff.little_endian() || self.camera.find_hint("little_endian")) && !self.camera.find_hint("big_endian") {
decode_12le_padded(&src, width, height, (width * bps / u8::BITS as usize) + padding, dummy)
} else {
decode_12be(&src, width, height, dummy)
}
}
x => return Err(RawlerError::unsupported(&self.camera, format!("Don't know uncompressed bps {}", x))),
}
} else if size == width * height * 3 {
cpp = 3;
Self::decode_snef_compressed(&src, coeffs, width, height, dummy)
} else if compression == 34713 {
self.decode_compressed(&src, width, height, bps, dummy)?
} else {
return Err(RawlerError::unsupported(&self.camera, format!("NEF: Don't know compression {}", compression)));
};
assert_eq!(image.width, width * cpp);
let blacklevel = self.get_blacklevel(bps)?;
let whitelevel = None;
let photometric = match cpp {
1 => RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera)),
3 => RawPhotometricInterpretation::LinearRaw,
_ => todo!(),
};
let mut img = RawImage::new(self.camera.clone(), image, cpp, coeffs, photometric, blacklevel, whitelevel, dummy);
if let Some(crop) = self.get_crop()? {
debug!("RAW Crops: {:?}", crop);
img.crop_area = Some(crop);
}
if cpp == 3 {
// Reset levels to defaults (0)
img.blacklevel = BlackLevel::default();
img.whitelevel = WhiteLevel::new(vec![65535; cpp]);
}
Ok(img)
}
fn format_dump(&self) -> FormatDump {
FormatDump::Nef(NefFormat { tiff: self.tiff.clone() })
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(self.tiff.root_ifd())?;
Ok(match self.get_lens_description() {
Ok(lens_data) => RawMetadata::new_with_lens(&self.camera, exif, lens_data.cloned()),
Err(err) => {
log::warn!("Failed to read lens information: {:?}", err);
RawMetadata::new(&self.camera, exif)
}
})
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
// High resolution preview image is stored in JPEGInterchangeFormat tag.
// Search for all IFDs and use the best match.
let mut ifds = self.tiff.find_ifds_with_filter(|ifd| {
if ifd.get_new_sub_file_type() == Some(1) {
ifd.get_entry(ExifTag::JPEGInterchangeFormatLength).is_some()
} else {
false
}
});
ifds.sort_by(|a, b| {
a.get_entry(ExifTag::JPEGInterchangeFormatLength)
.map(|x| x.force_u32(0))
.cmp(&b.get_entry(ExifTag::JPEGInterchangeFormatLength).map(|x| x.force_u32(0)))
});
// Take the IFD with the largest JPEG stream size
if let Some(jpeg_ifd) = ifds.last() {
return Ok(Some(dynamic_image_from_jpeg_interchange_format(jpeg_ifd, file)?));
} else {
// No matching IFDs found, use root IFD (possibly bad resolution)
Ok(Some(dynamic_image_from_ifd(self.tiff.root_ifd(), file)?))
}
}
fn format_hint(&self) -> FormatHint {
FormatHint::NEF
}
}
impl<'a> NefDecoder<'a> {
/// For older formats, we use the camera definitions and this here
/// is useless. But if we found here the levels in makernotes, we
/// use these instead. For 12 bit images, the blacklevels are still relative to
/// 14 bit image data. So we need to reduce them by 2 bits.
fn get_blacklevel(&self, bps: usize) -> Result<Option<BlackLevel>> {
if let Some(levels) = self.makernote.get_entry(NikonMakernote::BlackLevel) {
let mut black = [levels.force_u16(0), levels.force_u16(1), levels.force_u16(2), levels.force_u16(3)];
if bps == 12 {
black.iter_mut().for_each(|v| *v >>= 14 - 12);
}
Ok(Some(BlackLevel::new(&black, self.camera.cfa.width, self.camera.cfa.height, 1)))
} else {
Ok(None)
}
}
fn get_crop(&self) -> Result<Option<Rect>> {
if let Some(crop) = self.makernote.get_entry(NikonMakernote::CropArea) {
let values = [crop.force_u16(0), crop.force_u16(1), crop.force_u16(2), crop.force_u16(3)];
let rect = Rect::new(
Point::new(values[0] as usize, values[1] as usize),
Dim2::new(values[2] as usize, values[3] as usize),
);
Ok(Some(rect))
} else {
Ok(None)
}
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
if let Some(lensdata) = lensdata::from_makernote(&self.makernote)? {
if let Some(lenstype) = self.makernote.get_entry(NikonMakernote::LensType) {
match lensdata {
NefLensData::FMount(oldv) => {
let composite_id = oldv.composite_id(lenstype.force_u8(0));
log::debug!("NEF lens composite ID: {}", composite_id);
let resolver = LensResolver::new()
.with_nikon_id(Some(composite_id))
.with_camera(&self.camera)
.with_mounts(&[NIKON_F_MOUNT.into()]);
return Ok(resolver.resolve());
}
NefLensData::ZMount(newv) => {
let resolver = LensResolver::new()
.with_lens_id((newv.lens_id as u32, 0))
.with_camera(&self.camera)
.with_mounts(&[NIKON_Z_MOUNT.into()]);
return Ok(resolver.resolve());
}
}
}
}
Ok(None)
}
fn get_wb(&self) -> Result<[f32; 4]> {
if self.camera.find_hint("nowb") {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
} else if let Some(levels) = self.makernote.get_entry(TiffCommonTag::NefWB0) {
Ok([levels.force_f32(0), 1.0, 1.0, levels.force_f32(1)])
} else if let Some(levels) = self.makernote.get_entry(TiffCommonTag::NrwWB) {
let data = levels.get_data();
if data[0..3] == b"NRW"[..] {
let offset = if data[4..8] == b"0100"[..] { 1556 } else { 56 };
Ok([
(LEu32(data, offset) << 2) as f32,
(LEu32(data, offset + 4) + LEu32(data, offset + 8)) as f32,
(LEu32(data, offset + 4) + LEu32(data, offset + 8)) as f32,
(LEu32(data, offset + 12) << 2) as f32,
])
} else {
Ok([BEu16(data, 1248) as f32, 256.0, 256.0, BEu16(data, 1250) as f32])
}
} else if let Some(levels) = self.makernote.get_entry(TiffCommonTag::NefWB1) {
let mut version: u32 = 0;
for i in 0..4 {
version = (version << 4) + (levels.get_data()[i] - b'0') as u32;
}
let buf = levels.get_data();
debug!("NEF Color balance version: 0x{:x}", version);
match version {
0x100 => Ok([
BEu16(buf, 36 * 2) as f32,
BEu16(buf, 38 * 2) as f32,
BEu16(buf, 38 * 2) as f32,
BEu16(buf, 37 * 2) as f32,
]),
// Nikon D2H
0x102 => Ok([
BEu16(buf, 5 * 2) as f32,
BEu16(buf, 6 * 2) as f32,
BEu16(buf, 6 * 2) as f32,
BEu16(buf, 8 * 2) as f32,
]),
// Nikon D70
0x103 => Ok([
BEu16(buf, 10 * 2) as f32,
BEu16(buf, 11 * 2) as f32,
BEu16(buf, 11 * 2) as f32,
BEu16(buf, 12 * 2) as f32,
]),
0x204 | 0x205 => {
let serial = fetch_tiff_tag!(self.makernote, TiffCommonTag::NefSerial);
let data = serial.get_data();
let mut serialno = 0_usize;
for i in 0..serial.count() as usize {
if data[i] == 0 {
break;
}
serialno = serialno * 10
+ if data[i] >= 48 && data[i] <= 57 {
// "0" to "9"
(data[i] - 48) as usize
} else {
(data[i] % 10) as usize
};
}
// Get the "decryption" key
let keydata = fetch_tiff_tag!(self.makernote, TiffCommonTag::NefKey).force_u32(0).to_le_bytes();
let keyno = (keydata[0] ^ keydata[1] ^ keydata[2] ^ keydata[3]) as usize;
let src = if version == 0x204 {
&levels.get_data()[284..]
} else {
&levels.get_data()[4..]
};
let ci = WB_SERIALMAP[serialno & 0xff] as u32;
let mut cj = WB_KEYMAP[keyno & 0xff] as u32;
let mut ck = 0x60_u32;
let mut buf = [0_u8; 280];
for i in 0..280 {
cj += ci * ck;
ck += 1;
buf[i] = src[i] ^ (cj as u8);
}
let off = if version == 0x204 { 6 } else { 14 };
Ok([
BEu16(&buf, off) as f32,
BEu16(&buf, off + 2) as f32,
BEu16(&buf, off + 4) as f32,
BEu16(&buf, off + 6) as f32,
])
}
x => Err(RawlerError::unsupported(&self.camera, format!("NEF: Don't know about WB version 0x{:x}", x))),
}
} else {
log::debug!("NEF: Don't know how to fetch WB, fallback to [1.0, 1.0, 1.0]");
Ok([1.0, 1.0, 1.0, 1.0])
}
}
fn create_hufftable(num: usize) -> Result<HuffTable> {
let mut htable = HuffTable::empty();
for i in 0..15 {
htable.bits[i] = NIKON_TREE[num][0][i] as u32;
htable.huffval[i] = NIKON_TREE[num][1][i] as u32;
htable.shiftval[i] = NIKON_TREE[num][2][i] as u32;
}
htable.initialize()?;
Ok(htable)
}
/// The compression flags in some raws are not reliable because of firmware bugs.
/// We try to figure out the compression by some heuristics.
/// The return value is None if the file is not uncompressed or Some(x)
/// where x is the extra amount of bytes after each row.
fn is_uncompressed(&self, raw: &IFD) -> Result<Option<usize>> {
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let bps = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_usize(0);
let compression = fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_usize(0);
let size = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts).force_usize(0);
fn div_round_up(a: usize, b: usize) -> usize {
a.div_ceil(b) // (a + b - 1) / b
}
let req_pixels = width * height;
let req_input_bits = bps * req_pixels;
let req_input_bytes = div_round_up(req_input_bits, 8);
Ok(if compression == 1 || size == width * height * bps / 8 {
Some(0)
} else if size >= req_input_bytes {
// Some models (D6) using row padding, so the row width is slightly larger.
// This should be no more than 16 extra bytes.
let total_padding = size - req_input_bytes;
let per_row_padding = total_padding / height;
if total_padding % height != 0 {
None
} else if per_row_padding < 16 {
Some(per_row_padding)
} else {
None
}
} else {
None
})
}
fn decode_compressed(&self, src: &PaddedBuf, width: usize, height: usize, bps: usize, dummy: bool) -> Result<PixU16> {
let meta = if let Some(meta) = self.makernote.get_entry(TiffCommonTag::NefMeta2) {
debug!("Found NefMeta2");
meta
} else {
debug!("Fallback NefMeta1");
fetch_tiff_tag!(self.makernote, TiffCommonTag::NefMeta1)
};
Self::do_decode(src, meta.get_data(), self.makernote.endian, width, height, bps, dummy)
}
pub(crate) fn do_decode(src: &[u8], meta: &[u8], endian: Endian, width: usize, height: usize, bps: usize, dummy: bool) -> Result<PixU16> {
debug!("NEF decode with: endian: {:?}, width: {}, height: {}, bps: {}", endian, width, height, bps);
let mut out = alloc_image_ok!(width, height, dummy);
let mut stream = ByteStream::new(meta, endian);
let v0 = stream.get_u8();
let v1 = stream.get_u8();
debug!("Nef version v0:{}, v1:{}", v0, v1);
let mut huff_select = 0;
if v0 == 73 || v1 == 88 {
assert!(stream.remaining_bytes() >= 2110);
stream.consume_bytes(2110);
}
if v0 == 70 {
huff_select = 2;
}
if bps == 14 {
huff_select += 3;
}
// Create the huffman table used to decode
let mut htable = Self::create_hufftable(huff_select)?;
// Setup the predictors
let mut pred_up1: [i32; 2] = [stream.get_u16() as i32, stream.get_u16() as i32];
let mut pred_up2: [i32; 2] = [stream.get_u16() as i32, stream.get_u16() as i32];
// Get the linearization curve
let mut points = [0_u16; 1 << 16];
for i in 0..points.len() {
points[i] = i as u16;
}
// Some models reports 14 bits, but the data is 12 bits.
// So we reduce the bps to calculate the max value which
// is needed in the next steps.
let real_bps = if v0 == 68 && v1 == 64 {
bps as u32 - 2 // Special for D780, Z7 and others
} else {
bps as u32
};
let mut max = 1 << real_bps;
let csize = stream.get_u16() as usize;
let mut split = 0_usize;
let step = if csize > 1 { max / (csize - 1) } else { 0 };
if v0 == 68 && (v1 == 32 || v1 == 64) && step > 0 {
for i in 0..csize {
points[i * step] = stream.get_u16();
}
for i in 0..max {
let b_scale = i % step;
let a_pos = i - b_scale;
let b_pos = a_pos + step;
//assert!(a_pos < max);
//assert!(b_pos > 0);
//assert!(b_pos < max);
//assert!(a_pos < b_pos);
let a_scale = step - b_scale;
points[i] = ((a_scale * points[a_pos] as usize + b_scale * points[b_pos] as usize) / step) as u16;
}
split = endian.read_u16(meta, 562) as usize;
} else if v0 != 70 && csize <= 0x4001 {
for i in 0..csize {
points[i] = stream.get_u16();
}
max = csize;
}
let curve = LookupTable::new(&points[0..max]);
let mut pump = BitPumpMSB::new(src);
let mut random = pump.peek_bits(24);
for row in 0..height {
if split > 0 && row == split {
htable = Self::create_hufftable(huff_select + 1)?;
}
pred_up1[row & 1] += htable.huff_decode(&mut pump)?;
pred_up2[row & 1] += htable.huff_decode(&mut pump)?;
let mut pred_left1 = pred_up1[row & 1];
let mut pred_left2 = pred_up2[row & 1];
for col in (0..width).step_by(2) {
if col > 0 {
pred_left1 += htable.huff_decode(&mut pump)?;
pred_left2 += htable.huff_decode(&mut pump)?;
}
out[row * width + col + 0] = curve.dither(clampbits(pred_left1, real_bps), &mut random);
out[row * width + col + 1] = curve.dither(clampbits(pred_left2, real_bps), &mut random);
}
}
Ok(out)
}
// Decodes 12 bit data in an YUY2-like pattern (2 Luma, 1 Chroma per 2 pixels).
// We un-apply the whitebalance, so output matches lossless.
pub(crate) fn decode_snef_compressed(src: &PaddedBuf, coeffs: [f32; 4], width: usize, height: usize, dummy: bool) -> PixU16 {
let inv_wb_r = (1024.0 / coeffs[0]) as i32;
let inv_wb_b = (1024.0 / coeffs[2]) as i32;
//println!("Got invwb {} {}", inv_wb_r, inv_wb_b);
let snef_curve = {
let g: f32 = 2.4;
let f: f32 = 0.055;
let min: f32 = 0.04045;
let mul: f32 = 12.92;
let curve = (0..4096)
.map(|i| {
let v = (i as f32) / 4095.0;
let res = if v <= min { v / mul } else { ((v + f) / (1.0 + f)).powf(g) };
clampbits((res * 65535.0 * 4.0) as i32, 16)
})
.collect::<Vec<u16>>();
LookupTable::new(&curve)
};
decode_threaded(
width * 3,
height,
dummy,
&(|out: &mut [u16], row| {
let inb = &src[row * width * 3..];
let mut random = BEu32(inb, 0);
for (o, i) in out.chunks_exact_mut(6).zip(inb.chunks_exact(6)) {
let g1: u16 = i[0] as u16;
let g2: u16 = i[1] as u16;
let g3: u16 = i[2] as u16;
let g4: u16 = i[3] as u16;
let g5: u16 = i[4] as u16;
let g6: u16 = i[5] as u16;
let y1 = (g1 | ((g2 & 0x0f) << 8)) as f32;
let y2 = ((g2 >> 4) | (g3 << 4)) as f32;
let cb = (g4 | ((g5 & 0x0f) << 8)) as f32 - 2048.0;
let cr = ((g5 >> 4) | (g6 << 4)) as f32 - 2048.0;
let r = snef_curve.dither(clampbits((y1 + 1.370705 * cr) as i32, 12), &mut random);
let g = snef_curve.dither(clampbits((y1 - 0.337633 * cb - 0.698001 * cr) as i32, 12), &mut random);
let b = snef_curve.dither(clampbits((y1 + 1.732446 * cb) as i32, 12), &mut random);
// invert the white balance
o[0] = clampbits((inv_wb_r * r as i32 + (1 << 9)) >> 10, 15);
o[1] = g;
o[2] = clampbits((inv_wb_b * b as i32 + (1 << 9)) >> 10, 15);
let r = snef_curve.dither(clampbits((y2 + 1.370705 * cr) as i32, 12), &mut random);
let g = snef_curve.dither(clampbits((y2 - 0.337633 * cb - 0.698001 * cr) as i32, 12), &mut random);
let b = snef_curve.dither(clampbits((y2 + 1.732446 * cb) as i32, 12), &mut random);
// invert the white balance
o[3] = clampbits((inv_wb_r * r as i32 + (1 << 9)) >> 10, 15);
o[4] = g;
o[5] = clampbits((inv_wb_b * b as i32 + (1 << 9)) >> 10, 15);
}
}),
)
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("NEF raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(NikonMakernote);
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum NikonMakernote {
MakernoteVersion = 0x0001,
NefWB0 = 0x000C,
PreviewIFD = 0x0011,
NrwWB = 0x0014,
NefSerial = 0x001d,
ImageSizeRaw = 0x003e,
CropArea = 0x0045,
BlackLevel = 0x003d,
Makernotes0x51 = 0x0051,
LensType = 0x0083,
NefMeta1 = 0x008c,
NefMeta2 = 0x0096,
ShotInfo = 0x0091,
NefCompression = 0x0093,
NefWB1 = 0x0097,
LensData = 0x0098,
NefKey = 0x00a7,
}
/// Known NEF compression formats
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
enum NefCompression {
LossyType1 = 1,
Uncompressed = 2,
Lossless = 3,
LossyType2 = 4,
StripedPacked12Bits = 5,
UncompressedReduced12Bits = 6,
Unpacked12Bits = 7,
Small = 8,
Packed12Bits = 9,
Packed14Bits = 10,
HighEfficency = 13,
HighEfficencyStar = 14,
}
impl TryFrom<u16> for NefCompression {
type Error = String;
fn try_from(v: u16) -> std::result::Result<Self, Self::Error> {
Ok(match v {
1 => Self::LossyType1,
2 => Self::Uncompressed,
3 => Self::Lossless,
4 => Self::LossyType2,
5 => Self::StripedPacked12Bits,
6 => Self::UncompressedReduced12Bits,
7 => Self::Unpacked12Bits,
8 => Self::Small,
9 => Self::Packed12Bits,
10 => Self::Packed14Bits,
13 => Self::HighEfficency,
14 => Self::HighEfficencyStar,
_ => return Err(format!("unknown nef compression: {}", v)),
})
}
}
+65
View File
@@ -0,0 +1,65 @@
use crate::formats::tiff::IFD;
use super::NikonMakernote;
use crate::Result;
pub(super) fn nef_decrypt(buf: &mut [u8], start: usize, makernote: &IFD) -> Result<()> {
let serial = fetch_tiff_tag!(makernote, NikonMakernote::NefSerial);
let data = serial.get_data();
let mut serialno = 0_usize;
for i in 0..serial.count() as usize {
if data[i] == 0 {
break;
}
serialno = serialno * 10
+ if data[i] >= 48 && data[i] <= 57 {
// "0" to "9"
(data[i] - 48) as usize
} else {
(data[i] % 10) as usize
};
}
// Get the "decryption" key
let keydata = fetch_tiff_tag!(makernote, NikonMakernote::NefKey).force_u32(0).to_le_bytes();
let keyno = (keydata[0] ^ keydata[1] ^ keydata[2] ^ keydata[3]) as usize;
let src = &mut buf[start..];
let ci = WB_SERIALMAP[serialno & 0xff] as u32;
let mut cj = WB_KEYMAP[keyno & 0xff] as u32;
let mut ck = 0x60_u32;
for i in 0..src.len() {
cj += ci * ck;
ck += 1;
src[i] ^= cj as u8;
}
Ok(())
}
// We use this for the D50 and D2X whacky WB "encryption"
const WB_SERIALMAP: [u8; 256] = [
0xc1, 0xbf, 0x6d, 0x0d, 0x59, 0xc5, 0x13, 0x9d, 0x83, 0x61, 0x6b, 0x4f, 0xc7, 0x7f, 0x3d, 0x3d, 0x53, 0x59, 0xe3, 0xc7, 0xe9, 0x2f, 0x95, 0xa7, 0x95, 0x1f,
0xdf, 0x7f, 0x2b, 0x29, 0xc7, 0x0d, 0xdf, 0x07, 0xef, 0x71, 0x89, 0x3d, 0x13, 0x3d, 0x3b, 0x13, 0xfb, 0x0d, 0x89, 0xc1, 0x65, 0x1f, 0xb3, 0x0d, 0x6b, 0x29,
0xe3, 0xfb, 0xef, 0xa3, 0x6b, 0x47, 0x7f, 0x95, 0x35, 0xa7, 0x47, 0x4f, 0xc7, 0xf1, 0x59, 0x95, 0x35, 0x11, 0x29, 0x61, 0xf1, 0x3d, 0xb3, 0x2b, 0x0d, 0x43,
0x89, 0xc1, 0x9d, 0x9d, 0x89, 0x65, 0xf1, 0xe9, 0xdf, 0xbf, 0x3d, 0x7f, 0x53, 0x97, 0xe5, 0xe9, 0x95, 0x17, 0x1d, 0x3d, 0x8b, 0xfb, 0xc7, 0xe3, 0x67, 0xa7,
0x07, 0xf1, 0x71, 0xa7, 0x53, 0xb5, 0x29, 0x89, 0xe5, 0x2b, 0xa7, 0x17, 0x29, 0xe9, 0x4f, 0xc5, 0x65, 0x6d, 0x6b, 0xef, 0x0d, 0x89, 0x49, 0x2f, 0xb3, 0x43,
0x53, 0x65, 0x1d, 0x49, 0xa3, 0x13, 0x89, 0x59, 0xef, 0x6b, 0xef, 0x65, 0x1d, 0x0b, 0x59, 0x13, 0xe3, 0x4f, 0x9d, 0xb3, 0x29, 0x43, 0x2b, 0x07, 0x1d, 0x95,
0x59, 0x59, 0x47, 0xfb, 0xe5, 0xe9, 0x61, 0x47, 0x2f, 0x35, 0x7f, 0x17, 0x7f, 0xef, 0x7f, 0x95, 0x95, 0x71, 0xd3, 0xa3, 0x0b, 0x71, 0xa3, 0xad, 0x0b, 0x3b,
0xb5, 0xfb, 0xa3, 0xbf, 0x4f, 0x83, 0x1d, 0xad, 0xe9, 0x2f, 0x71, 0x65, 0xa3, 0xe5, 0x07, 0x35, 0x3d, 0x0d, 0xb5, 0xe9, 0xe5, 0x47, 0x3b, 0x9d, 0xef, 0x35,
0xa3, 0xbf, 0xb3, 0xdf, 0x53, 0xd3, 0x97, 0x53, 0x49, 0x71, 0x07, 0x35, 0x61, 0x71, 0x2f, 0x43, 0x2f, 0x11, 0xdf, 0x17, 0x97, 0xfb, 0x95, 0x3b, 0x7f, 0x6b,
0xd3, 0x25, 0xbf, 0xad, 0xc7, 0xc5, 0xc5, 0xb5, 0x8b, 0xef, 0x2f, 0xd3, 0x07, 0x6b, 0x25, 0x49, 0x95, 0x25, 0x49, 0x6d, 0x71, 0xc7,
];
const WB_KEYMAP: [u8; 256] = [
0xa7, 0xbc, 0xc9, 0xad, 0x91, 0xdf, 0x85, 0xe5, 0xd4, 0x78, 0xd5, 0x17, 0x46, 0x7c, 0x29, 0x4c, 0x4d, 0x03, 0xe9, 0x25, 0x68, 0x11, 0x86, 0xb3, 0xbd, 0xf7,
0x6f, 0x61, 0x22, 0xa2, 0x26, 0x34, 0x2a, 0xbe, 0x1e, 0x46, 0x14, 0x68, 0x9d, 0x44, 0x18, 0xc2, 0x40, 0xf4, 0x7e, 0x5f, 0x1b, 0xad, 0x0b, 0x94, 0xb6, 0x67,
0xb4, 0x0b, 0xe1, 0xea, 0x95, 0x9c, 0x66, 0xdc, 0xe7, 0x5d, 0x6c, 0x05, 0xda, 0xd5, 0xdf, 0x7a, 0xef, 0xf6, 0xdb, 0x1f, 0x82, 0x4c, 0xc0, 0x68, 0x47, 0xa1,
0xbd, 0xee, 0x39, 0x50, 0x56, 0x4a, 0xdd, 0xdf, 0xa5, 0xf8, 0xc6, 0xda, 0xca, 0x90, 0xca, 0x01, 0x42, 0x9d, 0x8b, 0x0c, 0x73, 0x43, 0x75, 0x05, 0x94, 0xde,
0x24, 0xb3, 0x80, 0x34, 0xe5, 0x2c, 0xdc, 0x9b, 0x3f, 0xca, 0x33, 0x45, 0xd0, 0xdb, 0x5f, 0xf5, 0x52, 0xc3, 0x21, 0xda, 0xe2, 0x22, 0x72, 0x6b, 0x3e, 0xd0,
0x5b, 0xa8, 0x87, 0x8c, 0x06, 0x5d, 0x0f, 0xdd, 0x09, 0x19, 0x93, 0xd0, 0xb9, 0xfc, 0x8b, 0x0f, 0x84, 0x60, 0x33, 0x1c, 0x9b, 0x45, 0xf1, 0xf0, 0xa3, 0x94,
0x3a, 0x12, 0x77, 0x33, 0x4d, 0x44, 0x78, 0x28, 0x3c, 0x9e, 0xfd, 0x65, 0x57, 0x16, 0x94, 0x6b, 0xfb, 0x59, 0xd0, 0xc8, 0x22, 0x36, 0xdb, 0xd2, 0x63, 0x98,
0x43, 0xa1, 0x04, 0x87, 0x86, 0xf7, 0xa6, 0x26, 0xbb, 0xd6, 0x59, 0x4d, 0xbf, 0x6a, 0x2e, 0xaa, 0x2b, 0xef, 0xe6, 0x78, 0xb6, 0x4e, 0xe0, 0x2f, 0xdc, 0x7c,
0xbe, 0x57, 0x19, 0x32, 0x7e, 0x2a, 0xd0, 0xb8, 0xba, 0x29, 0x00, 0x3c, 0x52, 0x7d, 0xa8, 0x49, 0x3b, 0x2d, 0xeb, 0x25, 0x49, 0xfa, 0xa3, 0xaa, 0x39, 0xa7,
0xc5, 0xa7, 0x50, 0x11, 0x36, 0xfb, 0xc6, 0x67, 0x4a, 0xf5, 0xa5, 0x12, 0x65, 0x7e, 0xb0, 0xdf, 0xaf, 0x4e, 0xb3, 0x61, 0x7f, 0x2f,
];
+283
View File
@@ -0,0 +1,283 @@
use crate::Result;
use crate::bits::LEu16;
use crate::{decoders::nef::NikonMakernote, formats::tiff::IFD};
const ERRMSG: &str = "Lens composite buffer error: EOF";
#[derive(Default, Clone, Debug)]
#[allow(dead_code)]
pub struct NefLensDataF {
version: u32,
exit_pupil_position: u8,
af_aperture: u8,
focus_position: u8,
focus_distance: u8,
lens_id_number: u8,
lens_fstops: u8,
min_focal_len: u8,
max_focal_len: u8,
max_aperture_at_min_focal: u8,
max_aperture_at_max_focal: u8,
mcu_version: u8,
effective_max_aperture: u8,
lens_model: Option<String>,
}
#[derive(Default, Clone, Debug)]
#[allow(dead_code)]
pub struct NefLensDataZ {
version: u32,
pub lens_id: u16,
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub enum NefLensData {
FMount(NefLensDataF),
ZMount(NefLensDataZ),
}
impl NefLensDataF {
pub fn composite_id(&self, lens_type: u8) -> String {
format!(
"{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}",
self.lens_id_number,
self.lens_fstops,
self.min_focal_len,
self.max_focal_len,
self.max_aperture_at_min_focal,
self.max_aperture_at_max_focal,
self.mcu_version,
lens_type
)
}
}
pub(super) fn from_makernote(makernote: &IFD) -> Result<Option<NefLensData>> {
if let Some(levels) = makernote.get_entry(NikonMakernote::LensData) {
let mut buf = levels.get_data().clone();
let mut version: u32 = 0;
for i in 0..4 {
version = (version << 4) + (buf[i] - b'0') as u32;
}
let lensdata = match version {
0x100 => NefLensData::FMount(parse_lensdata_0x100(version, &buf)?),
0x101 => NefLensData::FMount(parse_lensdata_0x101(version, &buf)?),
0x201 | 0x202 | 0x203 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x101(version, &buf)?)
}
0x204 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x204(version, &buf)?)
}
0x400 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x4xx(version, &buf, 0x18a)?)
}
0x401 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x4xx(version, &buf, 0x18a)?)
}
0x402 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x4xx(version, &buf, 0x18b)?)
}
0x403 => {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
NefLensData::FMount(parse_lensdata_0x4xx(version, &buf, 0x2ac)?)
}
0x800 | 0x801 | 0x802 // Z Models
=> {
super::decrypt::nef_decrypt(&mut buf, 4, makernote)?;
parse_lensdata_0x800(version, &buf)?
}
_ => todo!("Lensdata version: 0x{:x} not implemented", version),
};
log::debug!("NEF lens data version: 0x{:x}, lensdata: {:?}", version, lensdata);
Ok(Some(lensdata))
} else {
Ok(None)
}
}
fn parse_lensdata_0x100(version: u32, buf: &[u8]) -> Result<NefLensDataF> {
Ok(NefLensDataF {
version,
exit_pupil_position: 0,
af_aperture: 0,
focus_position: 0,
focus_distance: 0,
lens_id_number: *buf.get(NefLensData00::LensIDNumber as usize).ok_or(ERRMSG)?,
lens_fstops: *buf.get(NefLensData00::LensFStops as usize).ok_or(ERRMSG)?,
min_focal_len: *buf.get(NefLensData00::MinFocalLength as usize).ok_or(ERRMSG)?,
max_focal_len: *buf.get(NefLensData00::MaxFocalLength as usize).ok_or(ERRMSG)?,
max_aperture_at_min_focal: *buf.get(NefLensData00::MaxApertureAtMinFocal as usize).ok_or(ERRMSG)?,
max_aperture_at_max_focal: *buf.get(NefLensData00::MaxApertureAtMaxFocal as usize).ok_or(ERRMSG)?,
mcu_version: *buf.get(NefLensData00::MCUVersion as usize).ok_or(ERRMSG)?,
effective_max_aperture: 0,
lens_model: None,
})
}
fn parse_lensdata_0x101(version: u32, buf: &[u8]) -> Result<NefLensDataF> {
Ok(NefLensDataF {
version,
exit_pupil_position: *buf.get(NefLensData01::ExitPupilPosition as usize).ok_or(ERRMSG)?,
af_aperture: *buf.get(NefLensData01::AFAperture as usize).ok_or(ERRMSG)?,
focus_position: *buf.get(NefLensData01::FocusPosition as usize).ok_or(ERRMSG)?,
focus_distance: *buf.get(NefLensData01::FocusDistance as usize).ok_or(ERRMSG)?,
lens_id_number: *buf.get(NefLensData01::LensIDNumber as usize).ok_or(ERRMSG)?,
lens_fstops: *buf.get(NefLensData01::LensFStops as usize).ok_or(ERRMSG)?,
min_focal_len: *buf.get(NefLensData01::MinFocalLength as usize).ok_or(ERRMSG)?,
max_focal_len: *buf.get(NefLensData01::MaxFocalLength as usize).ok_or(ERRMSG)?,
max_aperture_at_min_focal: *buf.get(NefLensData01::MaxApertureAtMinFocal as usize).ok_or(ERRMSG)?,
max_aperture_at_max_focal: *buf.get(NefLensData01::MaxApertureAtMaxFocal as usize).ok_or(ERRMSG)?,
mcu_version: *buf.get(NefLensData01::MCUVersion as usize).ok_or(ERRMSG)?,
effective_max_aperture: *buf.get(NefLensData01::EffectiveMaxAperture as usize).ok_or(ERRMSG)?,
lens_model: None,
})
}
fn parse_lensdata_0x204(version: u32, buf: &[u8]) -> Result<NefLensDataF> {
Ok(NefLensDataF {
version,
exit_pupil_position: *buf.get(NefLensData204::ExitPupilPosition as usize).ok_or(ERRMSG)?,
af_aperture: *buf.get(NefLensData204::AFAperture as usize).ok_or(ERRMSG)?,
focus_position: *buf.get(NefLensData204::FocusPosition as usize).ok_or(ERRMSG)?,
focus_distance: *buf.get(NefLensData204::FocusDistance as usize).ok_or(ERRMSG)?,
lens_id_number: *buf.get(NefLensData204::LensIDNumber as usize).ok_or(ERRMSG)?,
lens_fstops: *buf.get(NefLensData204::LensFStops as usize).ok_or(ERRMSG)?,
min_focal_len: *buf.get(NefLensData204::MinFocalLength as usize).ok_or(ERRMSG)?,
max_focal_len: *buf.get(NefLensData204::MaxFocalLength as usize).ok_or(ERRMSG)?,
max_aperture_at_min_focal: *buf.get(NefLensData204::MaxApertureAtMinFocal as usize).ok_or(ERRMSG)?,
max_aperture_at_max_focal: *buf.get(NefLensData204::MaxApertureAtMaxFocal as usize).ok_or(ERRMSG)?,
mcu_version: *buf.get(NefLensData204::MCUVersion as usize).ok_or(ERRMSG)?,
effective_max_aperture: *buf.get(NefLensData204::EffectiveMaxAperture as usize).ok_or(ERRMSG)?,
lens_model: None,
})
}
fn parse_lensdata_0x4xx(version: u32, buf: &[u8], model_offset: usize) -> Result<NefLensDataF> {
let mut data = NefLensDataF { version, ..Default::default() };
if buf.len() >= model_offset + 64 {
let str = String::from_utf8_lossy(&buf[model_offset..model_offset + 64]);
data.lens_model = Some(str.trim().into());
}
Ok(data)
}
fn parse_lensdata_0x800(version: u32, buf: &[u8]) -> Result<NefLensData> {
// This check comes from exiftool. If the buffer contains only zeros,
// we consider the block as unused. Hopefully we find another method...
let old_data_avail = !buf[0x04..0x04 + 16].iter().all(|&x| x == 0);
let new_data_avail = !buf[48..].iter().all(|&x| x == 0);
if old_data_avail {
log::debug!("NEF lensdata 0x80X: Found old lensdata block");
Ok(NefLensData::FMount(NefLensDataF {
version,
exit_pupil_position: *buf.get(NefLensData800::ExitPupilPosition as usize).ok_or(ERRMSG)?,
af_aperture: *buf.get(NefLensData800::AFAperture as usize).ok_or(ERRMSG)?,
focus_position: *buf.get(NefLensData800::FocusPosition as usize).ok_or(ERRMSG)?,
focus_distance: *buf.get(NefLensData800::FocusDistance as usize).ok_or(ERRMSG)?,
lens_id_number: *buf.get(NefLensData800::LensIDNumber as usize).ok_or(ERRMSG)?,
lens_fstops: *buf.get(NefLensData800::LensFStops as usize).ok_or(ERRMSG)?,
min_focal_len: *buf.get(NefLensData800::MinFocalLength as usize).ok_or(ERRMSG)?,
max_focal_len: *buf.get(NefLensData800::MaxFocalLength as usize).ok_or(ERRMSG)?,
max_aperture_at_min_focal: *buf.get(NefLensData800::MaxApertureAtMinFocal as usize).ok_or(ERRMSG)?,
max_aperture_at_max_focal: *buf.get(NefLensData800::MaxApertureAtMaxFocal as usize).ok_or(ERRMSG)?,
mcu_version: *buf.get(NefLensData800::MCUVersion as usize).ok_or(ERRMSG)?,
effective_max_aperture: *buf.get(NefLensData800::EffectiveMaxAperture as usize).ok_or(ERRMSG)?,
lens_model: None,
}))
} else if new_data_avail {
log::debug!("NEF lensdata 0x80X: Found new lensdata block");
let mut data = NefLensDataZ { version, ..Default::default() };
data.lens_id = LEu16(buf, 0x30);
log::debug!("NEF lensdata 0x80X: lens_id: {}", data.lens_id);
Ok(NefLensData::ZMount(data))
} else {
Err("NEF lens data 0x80X contains neither old and new data".into())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
enum NefLensData00 {
Version = 0x00,
LensIDNumber = 0x06,
LensFStops = 0x07,
MinFocalLength = 0x08,
MaxFocalLength = 0x09,
MaxApertureAtMinFocal = 0x0a,
MaxApertureAtMaxFocal = 0x0b,
MCUVersion = 0x0c,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
enum NefLensData01 {
Version = 0x00,
ExitPupilPosition = 0x04,
AFAperture = 0x05,
FocusPosition = 0x08,
FocusDistance = 0x09,
FocalLength = 0x0a,
LensIDNumber = 0x0b,
LensFStops = 0x0c,
MinFocalLength = 0x0d,
MaxFocalLength = 0x0e,
MaxApertureAtMinFocal = 0x0f,
MaxApertureAtMaxFocal = 0x10,
MCUVersion = 0x11,
EffectiveMaxAperture = 0x12,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
enum NefLensData204 {
Version = 0x00,
ExitPupilPosition = 0x04,
AFAperture = 0x05,
FocusPosition = 0x08,
FocusDistance = 0x0a,
FocalLength = 0x0b,
LensIDNumber = 0x0c,
LensFStops = 0x0d,
MinFocalLength = 0x0e,
MaxFocalLength = 0x0f,
MaxApertureAtMinFocal = 0x10,
MaxApertureAtMaxFocal = 0x11,
MCUVersion = 0x12,
EffectiveMaxAperture = 0x13,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
enum NefLensData800 {
Version = 0x00,
OldLensDataFlag = 0x03,
ExitPupilPosition = 0x04,
AFAperture = 0x05,
FocusPosition = 0x09,
FocusDistance = 0x0b,
FocalLength = 0x0c,
LensIDNumber = 0x0d,
LensFStops = 0x0e,
MinFocalLength = 0x0f,
MaxFocalLength = 0x10,
MaxApertureAtMinFocal = 0x11,
MaxApertureAtMaxFocal = 0x12,
MCUVersion = 0x13,
EffectiveMaxAperture = 0x14,
}
+57
View File
@@ -0,0 +1,57 @@
use super::{Camera, Decoder, FormatHint, RawDecodeParams, RawMetadata, ok_cfa_image};
use crate::Result;
use crate::analyze::FormatDump;
use crate::exif::Exif;
use crate::packed::{decode_10le_lsb16, decode_12be_msb16, decode_12le_16bitaligned};
use crate::rawsource::RawSource;
use crate::{RawImage, RawLoader, RawlerError};
#[derive(Debug, Clone)]
pub struct NakedDecoder<'a> {
#[allow(dead_code)]
rawloader: &'a RawLoader,
camera: Camera,
}
impl<'a> NakedDecoder<'a> {
pub fn new(camera: Camera, rawloader: &'a RawLoader) -> Result<NakedDecoder<'a>> {
Ok(NakedDecoder { rawloader, camera })
}
}
impl<'a> Decoder for NakedDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let data = file.as_vec()?;
let buffer = &data;
let width = self.camera.raw_width;
let height = self.camera.raw_height;
let size = self.camera.filesize;
let bits = size * 8 / width / height;
let image = if self.camera.find_hint("12le_16bitaligned") {
decode_12le_16bitaligned(buffer, width, height, dummy)
} else {
match bits {
10 => decode_10le_lsb16(buffer, width, height, dummy),
12 => decode_12be_msb16(buffer, width, height, dummy),
_ => return Err(RawlerError::unsupported(&self.camera, format!("Naked: Don't know about {} bps images", bits))),
}
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, [f32::NAN, f32::NAN, f32::NAN, f32::NAN], image, dummy)
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::default();
let mdata = RawMetadata::new(&self.camera, exif);
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::Unknown
}
}
+3
View File
@@ -0,0 +1,3 @@
/// The Nef decoder can be used for NRW files, too,
/// so this is just an type alias.
pub type NrwDecoder<'a> = super::nef::NefDecoder<'a>;
+491
View File
@@ -0,0 +1,491 @@
use std::cmp;
use std::io::Read;
use std::io::Seek;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::analyze::FormatDump;
use crate::buffer::PaddedBuf;
use crate::exif::Exif;
use crate::formats::tiff::Entry;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::Rational;
use crate::formats::tiff::Value;
use crate::formats::tiff::reader::TiffReader;
use crate::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::*;
use crate::pixarray::PixU16;
use crate::pumps::BitPump;
use crate::pumps::BitPumpMSB;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::BlackLevel;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
const MFT_MOUNT: &str = "MFT-mount";
#[derive(Debug, Clone)]
pub struct OrfDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
camera: Camera,
makernote: IFD,
}
pub fn parse_makernote<R: Read + Seek>(reader: &mut R, exif_ifd: &IFD) -> Result<Option<IFD>> {
if let Some(exif) = exif_ifd.get_entry(ExifTag::MakerNotes) {
let offset = exif.offset().unwrap() as u32;
log::debug!("Makernote offset: {}", offset);
match &exif.value {
Value::Undefined(data) => {
let mut off = 0;
// Olympus starts the makernote with their own name, sometimes truncated
if data[0..5] == b"OLYMP"[..] {
off += 8;
if data[0..7] == b"OLYMPUS"[..] {
off += 4;
}
}
// OM Digital Solutions put their name in front of the TIFF structure, too
if data[0..9] == b"OM SYSTEM"[..] {
off += 16;
assert_eq!(data[12..14], b"II"[..]);
}
let endian = exif_ifd.endian;
//assert!(data[off..off + 2] == b"II"[..] || data[off..off + 2] == b"MM"[..], "ORF: must contain endian marker in makernote IFD");
//let endian = if data[off..off + 2] == b"II"[..] { Endian::Little } else { Endian::Big };
//off += 4;
let mut mainifd = IFD::new(reader, offset + off as u32, exif_ifd.base, exif_ifd.corr, endian, &[0x3000])?;
// Parse the Olympus Equipment section if it exists
if let Some(entry) = mainifd.get_entry_raw_with_len(OrfMakernotes::EquipmentIFD, reader, 4)? {
// The entry is of type UNDEFINED and count = 1. This tag contains a single 32 bit
// offset to the IFD.
let ioff = entry.get_force_u32(0);
log::debug!("Found EquipmentIFD at offset: {}", ioff);
// The IFD start at offset+ioff, but all offsets inside the IFD a relative to the main makernote IFD offset.
// So we use the main IFD as base offset, but start parsing IFD at ioff.
let ifd = IFD::new(reader, ioff, offset, 0, endian, &[])?;
mainifd.sub.insert(OrfMakernotes::EquipmentIFD.into(), vec![ifd]);
}
// For Olympus or OM-System models
if off == 12 || off == 16 {
// Parse the Olympus ImgProc section if it exists
let ioff = if let Some(entry) = mainifd.get_entry_raw_with_len(OrfMakernotes::ImageProcessingIFD, reader, 4)? {
// The entry is of type UNDEFINED and count = 1. This tag contains a single 32 bit
// offset to the IFD.
entry.get_force_u32(0)
} else {
0
};
if ioff != 0 {
log::debug!("Found ImageIFD at offset: {}", ioff);
// The IFD start at offset+ioff, but all offsets inside the IFD a relative to the main makernote IFD offset.
// So we use the main IFD as base offset, but start parsing IFD at ioff.
let iprocifd = IFD::new(reader, ioff, offset, 0, endian, &[])?;
mainifd.sub.insert(OrfMakernotes::ImageProcessingIFD.into(), vec![iprocifd]);
} else {
log::debug!("ORF ImageIFD not found");
}
}
Ok(Some(mainifd))
}
_ => Err(RawlerError::DecoderFailed("EXIF makernote has unknown type".to_string())),
}
} else {
Ok(None)
}
}
impl<'a> OrfDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<OrfDecoder<'a>> {
let camera = rawloader.check_supported(tiff.root_ifd())?;
let makernote = if let Some(exif) = tiff.find_first_ifd_with_tag(ExifTag::MakerNotes) {
parse_makernote(&mut file.reader(), exif)?
} else {
log::warn!("ORF makernote not found");
None
}
.ok_or("File has not makernotes")?;
//makernote.dump::<ExifTag>(0).iter().for_each(|line| eprintln!("DUMP: {}", line));
Ok(OrfDecoder {
tiff,
rawloader,
camera,
makernote,
})
}
}
impl<'a> Decoder for OrfDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with StripOffsets tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let counts = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts);
let bps = match self.get_bits_per_pixel()? {
Some(bps) if [12, 14].contains(&bps) => bps,
Some(bps) => {
log::warn!("Unsupported bps: {}", bps);
bps
}
None => {
log::debug!("No bps found, fallback to 12");
12
}
} as usize;
let mut size: usize = 0;
for i in 0..counts.count() {
size += counts.force_u32(i as usize) as usize;
}
let camera = if width >= self.camera.highres_width {
self.rawloader.check_supported_with_mode(self.tiff.root_ifd(), "highres")?
} else {
self.camera.clone()
};
let src = file.subview_padded(offset as u64, size as u64)?; // TODO add size and check all samples
log::debug!(
"ORF raw image size: {}, dim: {}x{}, total mp: {}, strip counts: {}",
size,
width,
height,
width * height,
counts.count()
);
// These conditions are sorted in descending order.
// All ORF files comes with no hints about the used compression.
// But we need to differentiate between 12be-interlaced and
// 12be-msb32 because they are in the same size range.
let image = if size >= width * height * 2 {
if self.tiff.little_endian() {
log::debug!("ORF: decode_12le_unpacked_left_aligned");
decode_12le_unpacked_left_aligned(&src, width, height, dummy)
} else {
log::debug!("ORF: decode_12be_unpacked_left_aligned");
decode_12be_unpacked_left_aligned(&src, width, height, dummy)
}
} else if size >= width * height / 10 * 16 {
log::debug!("ORF: decode_12le_wcontrol");
decode_12le_wcontrol(&src, width, height, dummy)
} else if size >= width * height * 12 / 8 {
if self.camera.find_hint("interlaced") {
log::debug!("ORF: decode_12be_interlaced");
decode_12be_interlaced(&src, width, height, dummy)
} else {
log::debug!("ORF: decode_12be_msb32");
//decode_12be_interlaced(&src, width, height, dummy)
decode_12be_msb32(&src, width, height, dummy)
}
} else {
log::debug!("ORF: fallback to decode_compressed");
OrfDecoder::decode_compressed(&src, width, height, bps, dummy)
};
let cpp = 1;
let blacklevel = self.get_blacklevel(bps)?;
let whitelevel = None;
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let mut img = RawImage::new(camera, image, cpp, normalize_wb(self.get_wb()?), photometric, blacklevel, whitelevel, dummy);
if let Some(crop) = self.get_crop()? {
img.crop_area = Some(crop);
}
if bps == 14 {
// Blacklevel is already corrected, only required for whitelevel.
// Encoded for 12 bps, whitelevel must be multiplied by 4.
img.whitelevel.0.iter_mut().for_each(|level| *level = *level << 2);
}
Ok(img)
}
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_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::ORF
}
}
impl<'a> OrfDecoder<'a> {
/* This is probably the slowest decoder of them all.
* I cannot see any way to effectively speed up the prediction
* phase, which is by far the slowest part of this algorithm.
* Also there is no way to multithread this code, since prediction
* is based on the output of all previous pixel (bar the first four)
*/
pub fn decode_compressed(buf: &PaddedBuf, width: usize, height: usize, bps: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
/* Build a table to quickly look up "high" value */
let mut bittable: [u8; 4096] = [0; 4096];
for i in 0..4096 {
let mut b = 12;
for high in 0..12 {
if ((i >> (11 - high)) & 1) != 0 {
b = high;
break;
}
}
bittable[i] = b;
}
let mut left: [i32; 2] = [0; 2];
let mut nw: [i32; 2] = [0; 2];
let skip = if bps == 14 { 8 } else { 7 };
let mut pump = BitPumpMSB::new(&buf[skip..]);
for row in 0..height {
let mut acarry: [[i32; 3]; 2] = [[0; 3]; 2];
for c in 0..width / 2 {
let col: usize = c * 2;
for s in 0..2 {
// Run twice for odd and even pixels
let i = if acarry[s][2] < 3 { 2 } else { 0 };
let mut nbits = 2 + i;
while ((acarry[s][0] >> (nbits + i)) & 0xffff) > 0 {
nbits += 1
}
nbits = cmp::min(nbits, 16);
let b = pump.peek_ibits(15);
let sign: i32 = -(b >> 14);
let low: i32 = (b >> 12) & 3;
let mut high: i32 = bittable[(b & 4095) as usize] as i32;
// Skip bytes used above or read bits
if high == 12 {
pump.consume_bits(15);
high = pump.get_ibits(16 - nbits) >> 1;
} else {
pump.consume_bits((high + 4) as u32);
}
acarry[s][0] = ((high << nbits) | pump.get_ibits(nbits)) as i32;
let diff = (acarry[s][0] ^ sign) + acarry[s][1];
acarry[s][1] = (diff * 3 + acarry[s][1]) >> 5;
acarry[s][2] = if acarry[s][0] > 16 { 0 } else { acarry[s][2] + 1 };
if row < 2 || col < 2 {
// We're in a border, special care is needed
let pred = if row < 2 && col < 2 {
// We're in the top left corner
0
} else if row < 2 {
// We're going along the top border
left[s]
} else {
// col < 2, we're at the start of a line
nw[s] = out[(row - 2) * width + (col + s)] as i32;
nw[s]
};
left[s] = pred + ((diff << 2) | low);
out[row * width + (col + s)] = left[s] as u16;
} else {
let up: i32 = out[(row - 2) * width + (col + s)] as i32;
let left_minus_nw: i32 = left[s] - nw[s];
let up_minus_nw: i32 = up - nw[s];
// Check if sign is different, and one is not zero
let pred = if left_minus_nw * up_minus_nw < 0 {
if left_minus_nw.abs() > 32 || up_minus_nw.abs() > 32 {
left[s] + up_minus_nw
} else {
(left[s] + up) >> 1
}
} else if left_minus_nw.abs() > up_minus_nw.abs() {
left[s]
} else {
up
};
left[s] = pred + ((diff << 2) | low);
nw[s] = up;
out[row * width + (col + s)] = left[s] as u16;
}
}
}
}
out
}
fn get_blacklevel(&self, bps: usize) -> Result<Option<BlackLevel>> {
let ifd = self.makernote.find_ifds_with_tag(OrfImageProcessing::OrfBlackLevels);
if ifd.is_empty() {
log::info!("ORF: Couldn't find ImgProc IFD, unable to read blacklevel");
return Ok(None);
}
let blacks = fetch_tiff_tag!(ifd[0], OrfImageProcessing::OrfBlackLevels);
let mut levels = [blacks.force_u16(0), blacks.force_u16(1), blacks.force_u16(2), blacks.force_u16(3)];
if bps == 14 {
// Blacklevel is encoded for 12 bits
levels.iter_mut().for_each(|level| *level = *level << 2);
}
Ok(Some(BlackLevel::new(&levels, self.camera.cfa.width, self.camera.cfa.height, 1)))
}
fn get_bits_per_pixel(&self) -> Result<Option<u16>> {
let ifd = self.makernote.find_ifds_with_tag(OrfImageProcessing::ValidBits);
if ifd.is_empty() {
return Ok(None);
}
Ok(Some(fetch_tiff_tag!(ifd[0], OrfImageProcessing::ValidBits).force_u16(0)))
}
fn get_crop(&self) -> Result<Option<Rect>> {
let ifd = self.makernote.find_ifds_with_tag(OrfImageProcessing::CropLeft);
if ifd.is_empty() {
return Ok(None);
}
let crop_left = fetch_tiff_tag!(ifd[0], OrfImageProcessing::CropLeft).force_usize(0);
let crop_top = fetch_tiff_tag!(ifd[0], OrfImageProcessing::CropTop).force_usize(0);
let crop_width = fetch_tiff_tag!(ifd[0], OrfImageProcessing::CropWidth).force_usize(0);
let crop_height = fetch_tiff_tag!(ifd[0], OrfImageProcessing::CropHeight).force_usize(0);
Ok(Some(Rect::new(Point::new(crop_left, crop_top), Dim2::new(crop_width, crop_height))))
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
if let Some(ifd) = self.makernote.get_sub_ifd(OrfMakernotes::EquipmentIFD) {
match ifd.get_entry(OrfEquipmentTags::LensType) {
Some(Entry {
value: Value::Byte(settings), ..
}) => {
log::debug!("Lens type tag: {:?}", settings);
let make_id = settings[0];
let model_id = settings[2];
let submodel_id = settings[3];
let composite_id = format!("{:02X} {:02X} {:02X}", make_id, model_id, submodel_id);
log::debug!("ORF lens composite ID: {}", composite_id);
let resolver = LensResolver::new()
.with_olympus_id(Some(composite_id))
.with_camera(&self.camera)
.with_focal_len(self.get_focal_len()?)
.with_mounts(&[MFT_MOUNT.into()]);
return Ok(resolver.resolve());
}
_ => {
log::warn!("Camera settings in makernote not found, no lens data available");
}
}
}
log::warn!("No lens data found");
Ok(None)
}
fn get_focal_len(&self) -> Result<Option<Rational>> {
if let Some(exif) = self.tiff.find_first_ifd_with_tag(ExifTag::MakerNotes) {
if let Some(Entry {
value: Value::Short(focal), ..
}) = exif.get_entry(ExifTag::FocalLength)
{
return Ok(focal.get(1).map(|v| Rational::new(*v as u32, 1)));
}
}
Ok(None)
}
fn get_wb(&self) -> Result<[f32; 4]> {
let redmul = self.makernote.get_entry(OrfMakernotes::OlympusRedMul);
let bluemul = self.makernote.get_entry(OrfMakernotes::OlympusBlueMul);
match (redmul, bluemul) {
(Some(redmul), Some(bluemul)) => Ok([redmul.force_u32(0) as f32, 256.0, 256.0, bluemul.force_u32(0) as f32]),
_ => {
let ifd = self.makernote.find_ifds_with_tag(OrfImageProcessing::OrfBlackLevels);
if ifd.is_empty() {
return Err(RawlerError::DecoderFailed("ORF: Couldn't find ImgProc IFD".to_string()));
}
let wbs = fetch_tiff_tag!(ifd[0], OrfImageProcessing::WB_RBLevels);
Ok([wbs.force_f32(0), 256.0, 256.0, wbs.force_f32(1)])
}
}
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
log::debug!("ORF raw wb: {:?}", raw_wb);
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(OrfMakernotes);
crate::tags::tiff_tag_enum!(OrfImageProcessing);
crate::tags::tiff_tag_enum!(OrfEquipmentTags);
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum OrfMakernotes {
ImageProcessingIFD = 0x2040,
RawInfo = 0x3000,
OlympusRedMul = 0x1017,
OlympusBlueMul = 0x1018,
EquipmentIFD = 0x2010,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum OrfImageProcessing {
ImageProcessingVersion = 0x0000,
WB_RBLevels = 0x0100,
OrfBlackLevels = 0x0600,
ValidBits = 0x0611,
CropLeft = 0x0612,
CropTop = 0x0613,
CropWidth = 0x0614,
CropHeight = 0x0615,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum OrfEquipmentTags {
LensType = 0x0201,
}
+518
View File
@@ -0,0 +1,518 @@
use image::DynamicImage;
use log::debug;
use log::warn;
use serde::Deserialize;
use serde::Serialize;
use super::BlackLevel;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image_ok;
use crate::analyze::FormatDump;
use crate::bits::Endian;
use crate::decompressors::ljpeg::huffman::*;
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::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::*;
use crate::pixarray::PixU16;
use crate::pumps::BitPumpMSB;
use crate::pumps::ByteStream;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
#[derive(Debug, Clone)]
pub struct PefDecoder<'a> {
camera: Camera,
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
/// Offset of makernote, needed to correct offsets of preview image
makernote_offset: u32,
}
impl<'a> PefDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<PefDecoder<'a>> {
debug!("PEF decoder choosen");
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!("PEF makernote not found");
None
}
.ok_or("File has not makernotes")?;
let makernote_offset = tiff
.find_first_ifd_with_tag(ExifTag::MakerNotes)
.and_then(|exif| exif.get_entry(ExifTag::MakerNotes))
.map(|entry| entry.offset().unwrap() as u32)
.unwrap_or(0);
//eprintln!("IFD makernote:");
//for line in makernote.dump::<PefMakernote>(10) {
// eprintln!("{}", line);
//}
Ok(PefDecoder {
camera,
tiff,
rawloader,
makernote,
makernote_offset,
})
}
}
/// CR2 format encapsulation for analyzer
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PefFormat {
tiff: GenericTiffReader,
}
impl<'a> Decoder for PefDecoder<'a> {
fn format_dump(&self) -> FormatDump {
FormatDump::Pef(PefFormat { tiff: self.tiff.clone() })
}
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
//for (i, ifd) in self.tiff.chains().iter().enumerate() {
// eprintln!("IFD {}", i);
// for line in ifd.dump::<crate::tags::LegacyTiffRootTag>(10) {
// eprintln!("{}", line);
// }
//}
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::unsupported(&self.camera, "Unable to find IFD"))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = match fetch_tiff_tag!(raw, TiffCommonTag::Compression).get_u32(0) {
Ok(Some(1)) => decode_16be(src, width, height, dummy),
Ok(Some(32773)) => decode_12be(src, width, height, dummy),
Ok(Some(65535)) => self.decode_compressed(src, width, height, dummy)?,
Ok(Some(c)) => return Err(RawlerError::unsupported(&self.camera, format!("PEF: Don't know how to read compression {}", c))),
_ => return Err(RawlerError::unsupported(&self.camera, "PEF: No compression tag found")),
};
let cpp = 1;
let wb = self.get_wb()?;
let blacklevel = self.get_blacklevel()?;
let whitelevel = None;
debug!("Found WB: {:?}", wb);
let photometric = if self.camera.cfa.is_valid() {
RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera))
} else {
RawPhotometricInterpretation::LinearRaw
};
Ok(RawImage::new(self.camera.clone(), image, cpp, wb, photometric, blacklevel, whitelevel, dummy))
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
let size = self.makernote.get_entry(PefMakernote::PreviewImageSize);
let length = self.makernote.get_entry(PefMakernote::PreviewImageLength);
let start = self.makernote.get_entry(PefMakernote::PreviewImageStart);
let image = match (size, length, start) {
(Some(size), Some(length), Some(start)) => {
let _width = size.force_u16(0);
let _height = size.force_u16(1);
let len = length.force_u32(0);
let offset = start.force_u32(0);
if len > 0 && offset > 0 {
let buf = file.subview((self.makernote_offset + offset) as u64, len as u64)?;
match image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg) {
Ok(img) => Some(img),
Err(_) => {
// Test offset without correction
let buf = file.subview(offset as u64, len as u64)?;
let img = image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG: {:?}", err)))?;
Some(img)
}
}
} else {
None
}
}
_ => todo!(),
};
if let Some(image) = image {
// This tag contains the border definitions for the preview image.
// We cut away these black borders.
if let Some(Entry {
value: Value::Byte(borders), ..
}) = self.makernote.get_entry(PefMakernote::PreviewImageBorders)
{
let y = borders[0] as u32;
let x = borders[2] as u32;
let width = image.width() - x - borders[3] as u32;
let height = image.height() - y - borders[1] as u32;
return Ok(Some(image.crop_imm(x, y, width, height)));
} else {
return Ok(Some(image));
}
}
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(self.tiff.root_ifd())?;
let mdata = RawMetadata::new_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::PEF
}
}
impl<'a> PefDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
match self.makernote.get_entry(PefMakernote::WhitePoint) {
Some(wb) => {
let raw_wb = [wb.force_u16(0) as f32, wb.force_u16(1) as f32, wb.force_u16(2) as f32, wb.force_u16(3) as f32];
Ok(normalize_wb(raw_wb))
}
None => Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN]),
}
}
fn get_blacklevel(&self) -> Result<Option<BlackLevel>> {
match self.makernote.get_entry(PefMakernote::BlackPoint) {
Some(data) => {
if self.camera.cfa.is_valid() {
let levels = [data.force_u16(0), data.force_u16(1), data.force_u16(2), data.force_u16(3)];
Ok(Some(BlackLevel::new(&levels, self.camera.cfa.width, self.camera.cfa.height, 1)))
} else {
// Monochrome PEF like K-3
let levels = [data.force_u16(0)];
Ok(Some(BlackLevel::new(&levels, 1, 1, 1)))
}
}
None => Ok(None),
}
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
if let Some(Entry {
value: Value::Byte(settings), ..
}) = self.makernote.get_entry(PefMakernote::LensRec)
{
let lens_id = (settings[0] as u32, settings[1] as u32);
debug!("LensRec tag: {:?}", lens_id);
if [0, 1, 2].contains(&lens_id.0) {
// 0 = M-42 or no lens
// 1 = K or M lens
// 2 = A Series lens
return Ok(None);
} else {
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_lens_id(lens_id)
.with_mounts(&["k-mount".into()]);
return Ok(resolver.resolve());
}
}
Ok(None)
}
fn decode_compressed(&self, src: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
if let Some(huff) = self.makernote.get_entry(PefMakernote::HuffmanTable) {
match &huff.value {
Value::Undefined(data) => Self::do_decode(src, Some((data, self.tiff.get_endian())), width, height, dummy),
_ => todo!(), // should not happen!
}
} else {
Self::do_decode(src, None, width, height, dummy)
}
}
pub(crate) fn do_decode(src: &[u8], huff: Option<(&[u8], Endian)>, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let mut out = alloc_image_ok!(width, height, dummy);
let mut htable = HuffTable::empty();
/* Attempt to read huffman table, if found in makernote */
if let Some((huff, endian)) = huff {
debug!("Use in-file Huffman table");
let mut stream = ByteStream::new(huff, endian);
let depth: usize = (stream.get_u16() as usize + 12) & 0xf;
stream.consume_bytes(12);
let mut v0: [u32; 16] = [0; 16];
for i in 0..depth {
v0[i] = stream.get_u16() as u32;
}
let mut v1: [u32; 16] = [0; 16];
for i in 0..depth {
v1[i] = stream.get_u8() as u32;
}
// Calculate codes and store bitcounts
let mut v2: [u32; 16] = [0; 16];
for c in 0..depth {
v2[c] = v0[c] >> (12 - v1[c]);
htable.bits[v1[c] as usize] += 1;
}
// Find smallest
for i in 0..depth {
let mut sm_val: u32 = 0xfffffff;
let mut sm_num: u32 = 0xff;
for j in 0..depth {
if v2[j] <= sm_val {
sm_num = j as u32;
sm_val = v2[j];
}
}
htable.huffval[i] = sm_num;
v2[sm_num as usize] = 0xffffffff;
}
} else {
debug!("Fallback to standard Huffman table");
// Initialize with legacy data
let pentax_tree: [u8; 29] = [0, 2, 3, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4, 2, 5, 1, 6, 0, 7, 8, 9, 10, 11, 12];
let mut acc: usize = 0;
for i in 0..16 {
htable.bits[i + 1] = pentax_tree[i] as u32;
acc += htable.bits[i + 1] as usize;
}
for i in 0..acc {
htable.huffval[i] = pentax_tree[i + 16] as u32;
}
}
htable.initialize()?;
let mut pump = BitPumpMSB::new(src);
let mut pred_up1: [i32; 2] = [0, 0];
let mut pred_up2: [i32; 2] = [0, 0];
let mut pred_left1: i32;
let mut pred_left2: i32;
for row in 0..height {
pred_up1[row & 1] += htable.huff_decode(&mut pump)?;
pred_up2[row & 1] += htable.huff_decode(&mut pump)?;
pred_left1 = pred_up1[row & 1];
pred_left2 = pred_up2[row & 1];
out[row * width + 0] = pred_left1 as u16;
out[row * width + 1] = pred_left2 as u16;
for col in (2..width).step_by(2) {
pred_left1 += htable.huff_decode(&mut pump)?;
pred_left2 += htable.huff_decode(&mut pump)?;
out[row * width + col + 0] = pred_left1 as u16;
out[row * width + col + 1] = pred_left2 as u16;
}
}
Ok(out)
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
debug!("PEF raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(PefMakernote);
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum PefMakernote {
PentaxVersion = 0x0000,
PentaxModelType = 0x0001,
PreviewImageSize = 0x0002,
PreviewImageLength = 0x0003,
PreviewImageStart = 0x0004,
PentaxModelId = 0x0005,
Date = 0x0006,
Time = 0x0007,
Quality = 0x0008,
PentaxImageSize = 0x0009,
PictureMode = 0x000b,
FlashMode = 0x000c,
FocusMode = 0x000d,
AFPointSelected = 0x000e,
AFPointsInFocus = 0x000f,
FocusPosition = 0x0010,
ExposureTime = 0x0012,
FNumber = 0x0013,
ISO = 0x0014,
LightReading = 0x0015,
ExposureCompensation = 0x0016,
MeteringMode = 0x0017,
AutoBracketing = 0x0018,
WhiteBalance = 0x0019,
WhiteBalanceMode = 0x001a,
BlueBalance = 0x001b,
RedBalance = 0x001c,
FocalLength = 0x001d,
DigitalZoom = 0x001e,
Saturation = 0x001f,
Contrast = 0x0020,
Sharpness = 0x0021,
WordTimeLocation = 0x0022,
HometownCity = 0x0023,
DestinationCity = 0x0024,
HometownDST = 0x0025,
DestinationDST = 0x0026,
DSPFirmwareVersion = 0x0027,
CPUFirmwareVersion = 0x0028,
FrameNumber = 0x0029,
EffectiveLV = 0x002d,
ImageEditing = 0x0032,
PictureMode2 = 0x0033,
DriveMode = 0x0034,
SensorSize = 0x0035,
ColorSpace = 0x0037,
ImageAreaOffset = 0x0038,
RawImageSize = 0x0039,
AFPointsInFocus2 = 0x003c,
DataScaling = 0x003d,
PreviewImageBorders = 0x003e,
LensRec = 0x003f,
SensitivityAdjust = 0x0040,
ImageEditCount = 0x0041,
CameraTemerature = 0x0047,
AELock = 0x0048,
NoiseReduction = 0x0049,
FlashExposureComp = 0x004d,
ImageTone = 0x004f,
ColorTemperature = 0x0050,
ColorTempDaylight = 0x0053,
ColorTempShade = 0x0054,
ColorTempCloudy = 0x0055,
ColorTempTungsten = 0x0056,
ColorTempFluorescentD = 0x0057,
ColorTempFluorescentN = 0x0058,
ColorTempFluorescentW = 0x0059,
ColorTempFlash = 0x005a,
ShakeReductionInfo = 0x005c,
ShutterCount = 0x005d,
FaceInfo = 0x0060,
RawDevelopmentProcess = 0x0062,
Hue = 0x0067,
AWBInfo = 0x0068,
DynamicRangeExpansion = 0x0069,
TimeInfo = 0x006b,
HighLowKeyAdj = 0x006c,
ContastHighlight = 0x006d,
ContrastShadow = 0x006e,
ConstrastHightlightShadowAdj = 0x006f,
FineSharpness = 0x0070,
HighISONoiseReduction = 0x0071,
AFAdjustment = 0x0072,
MonochromeFilterEffect = 0x0073,
MonochromeToning = 0x0074,
FaceDetect = 0x0076,
FaceDetectFrameIsze = 0x0077,
ShadowCorrection = 0x0079,
ISOAutoParameters = 0x007a,
CrossProcess = 0x007b,
LensCorr = 0x007d,
WhiteLevel = 0x007e,
BleachBypassToning = 0x007f,
AspectRatio = 0x0080,
BlurControl = 0x0082,
HDR = 0x0085,
ShutterType = 0x0087,
NeutralDensityFilter = 0x0088,
ISO2 = 0x008b,
IntervalShooting = 0x0092,
SkinToneCorrection = 0x0095,
ClarityControl = 0x0096,
BlackPoint = 0x0200,
WhitePoint = 0x0201,
ColorMatrixA = 0x0203,
ColorMatrixB = 0x0204,
CameraSettings = 0x0205,
AEInfo = 0x0206,
LensInfo = 0x0207,
FlashInfo = 0x0208,
AEMeteringSegements = 0x0209,
FlashMeteringSegements = 0x020a,
SlaveFlashMeteringSegements = 0x020b,
WB_RGGBLevelsDaylight = 0x020d,
WB_RGGBLevelsShade = 0x020e,
WB_RGGBLevelsCloudy = 0x020f,
WB_RGGBLevelsTungsten = 0x0210,
WB_RGGBLevelsFluorescentD = 0x0211,
WB_RGGBLevelsFluorescentN = 0x0212,
WB_RGGBLevelsFluorescentW = 0x0213,
WB_RGGBLevelsFlash = 0x0214,
CameraInfo = 0x0215,
BatteryInfo = 0x0216,
SaturationInfo = 0x021b,
ColorMatrixA2 = 0x021c,
ColorMatrixB2 = 0x021d,
AFInfo = 0x021f,
HuffmanTable = 0x0220,
KelvinWB = 0x0221,
ColorInfo = 0x0222,
EVStepInfo = 0x0224,
ShotInfo = 0x0226,
FacePos = 0x0227,
FaceSize = 0x0228,
SerialNumber = 0x0229,
FilterInfo = 0x022a,
LevelInfo = 0x022b,
WBLevels = 0x022d,
Artist = 0x022e,
Copyright = 0x022f,
FirmwareVersion = 0x0230,
ConstrastDetectAFArea = 0x0231,
CrossProcessParams = 0x0235,
LensInfoQ = 0x0239,
Model = 0x023f,
PixelShiftInfo = 0x0243,
AFPointInfo = 0x0245,
DataDump = 0x03fe,
TempInfo = 0x03ff,
ToneCurve = 0x0402,
ToneCurves = 0x0403,
UnknownBlock = 0x0405,
PrintIM = 0x0e00,
}
+240
View File
@@ -0,0 +1,240 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright 2024 Daniel Vogelbacher <daniel@chaospixel.com>
// Originally written in C in dcraw.c by Dave Coffin
use rayon::iter::IndexedParallelIterator;
use rayon::iter::ParallelIterator;
use std::mem::swap;
use std::ops::Not;
use crate::Orientation;
use crate::RawImage;
use crate::RawLoader;
use crate::Result;
use crate::alloc_image_ok;
use crate::analyze::FormatDump;
use crate::bits::BEu16;
use crate::bits::Endian;
use crate::bits::LookupTable;
use crate::buffer::PaddedBuf;
use crate::exif::Exif;
use crate::pixarray::PixU16;
use crate::pumps::BitPump;
use crate::pumps::BitPumpMSB;
use crate::pumps::ByteStream;
use crate::rawsource::RawSource;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image;
#[derive(Debug, Clone)]
pub struct QtkDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
camera: Camera,
}
pub fn is_qtk(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => buf[0..4] == b"qktk"[..] || buf[0..4] == b"qktn"[..],
Err(_) => false,
}
}
impl<'a> QtkDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<QtkDecoder<'a>> {
match file.subview(0, 4)? {
b"qktk" => {
let make = "Apple";
let model = "QuickTake 100";
let camera = rawloader.check_supported_with_everything(make, model, "")?;
Ok(QtkDecoder { rawloader, camera })
}
b"qktn" => {
if file.subview(0, 6)?[5] != 0 {
let make = "Apple";
let model = "QuickTake 200";
let camera = rawloader.check_supported_with_everything(make, model, "")?;
Ok(QtkDecoder { rawloader, camera })
} else {
let make = "Apple";
let model = "QuickTake 150";
let camera = rawloader.check_supported_with_everything(make, model, "")?;
Ok(QtkDecoder { rawloader, camera })
}
}
sig => Err(crate::RawlerError::DecoderFailed(format!(
"Unable to use QTK decoder on file with signature: '{:?}'",
sig
))),
}
}
}
impl<'a> Decoder for QtkDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
const META_OFFSET: u64 = 544;
let meta = file.subview(META_OFFSET, 16)?;
let mut stream = ByteStream::new(meta, Endian::Big);
let mut height = stream.get_u16() as usize;
let mut width = stream.get_u16() as usize;
let _zero = stream.get_u32();
let hint = stream.get_u16();
let offset = if hint == 30 { 738 } else { 736 };
let mut orientation = Orientation::Normal;
if height > width {
swap(&mut width, &mut height);
let info = file.subview(offset - 6, 6)?;
orientation = if BEu16(info, 0).not() & 3 > 0 {
Orientation::Rotate90
} else {
Orientation::Rotate270
};
log::debug!("QTK file has flipped width/height, new orientation: {:?}", orientation);
}
log::debug!("QTK file w: {}, h: {}, hint: {}", width, height, hint);
let src = file.subview_until_eof_padded(offset as u64)?;
let image = match file.subview(0, 4)? {
b"qktk" => Self::decompress_quicktake_100(self, &src, width, height, dummy)?,
b"qktn" => Self::decompress_quicktake_150(self, &src, width, height, dummy)?,
_ => unreachable!(),
};
let cpp = 1;
ok_cfa_image(self.camera.clone(), cpp, self.get_wb()?, image, dummy).map(|mut image| {
image.orientation = orientation;
image
})
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let meta = RawMetadata::new(&self.camera, Exif::default());
Ok(meta)
}
fn format_hint(&self) -> FormatHint {
FormatHint::QTK
}
}
impl<'a> QtkDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
pub fn decompress_quicktake_150(&self, src: &PaddedBuf, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
// Model 150 always compress with cbpp=3
let cbpp = 3;
crate::decompressors::radc::decompress(src, width, height, cbpp, dummy)
}
pub fn decompress_quicktake_100(&self, src: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
assert!(width > height);
let mut out = alloc_image_ok!(width, height, dummy);
let mut pump = BitPumpMSB::new(src);
const GSTEP: [i16; 16] = [-89, -60, -44, -32, -22, -15, -8, -2, 2, 8, 15, 22, 32, 44, 60, 89];
const RSTEP: [[i16; 4]; 6] = [
[-3, -1, 1, 3],
[-5, -1, 1, 5],
[-8, -2, 2, 8],
[-13, -3, 3, 13],
[-19, -4, 4, 19],
[-28, -6, 6, 28],
];
const CURVE: [u16; 256] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 86, 88, 90, 92, 94, 97, 99, 101, 103, 105, 107, 110, 112, 114, 116, 118, 120, 123, 125, 127, 129, 131, 134, 136, 138, 140, 142, 144, 147, 149,
151, 153, 155, 158, 160, 162, 164, 166, 168, 171, 173, 175, 177, 179, 181, 184, 186, 188, 190, 192, 195, 197, 199, 201, 203, 205, 208, 210, 212, 214,
216, 218, 221, 223, 226, 230, 235, 239, 244, 248, 252, 257, 261, 265, 270, 274, 278, 283, 287, 291, 296, 300, 305, 309, 313, 318, 322, 326, 331, 335,
339, 344, 348, 352, 357, 361, 365, 370, 374, 379, 383, 387, 392, 396, 400, 405, 409, 413, 418, 422, 426, 431, 435, 440, 444, 448, 453, 457, 461, 466,
470, 474, 479, 483, 487, 492, 496, 500, 508, 519, 531, 542, 553, 564, 575, 587, 598, 609, 620, 631, 643, 654, 665, 676, 687, 698, 710, 721, 732, 743,
754, 766, 777, 788, 799, 810, 822, 833, 844, 855, 866, 878, 889, 900, 911, 922, 933, 945, 956, 967, 978, 989, 1001, 1012, 1023,
];
let mut pix = [[0x80_i16; 644]; 484];
for row in 2..(height + 2) {
let cstart = 2 + (row & 1);
let mut val = 0;
for col in (cstart..(width + 2)).step_by(2) {
val = (((pix[row - 1][col - 1] + 2 * pix[row - 1][col + 1] + pix[row][col - 2]) >> 2) + GSTEP[pump.get_bits(4) as usize]).clamp(0, 255);
pix[row][col] = val;
if col < 4 {
pix[row][col - 2] = val;
pix[row + 1][(!row) & 1] = val;
}
if row == 2 {
pix[row - 1][col + 1] = val;
pix[row - 1][col + 3] = val;
}
}
pix[row][width + 2 + (row & 1)] = val; // last column
}
for rb in 0..2 {
for row in ((2 + rb)..(height + 2)).step_by(2) {
for col in ((3 - (row & 1))..(width + 2)).step_by(2) {
let sharp = if row < 4 || col < 4 {
2
} else {
let val = (pix[row - 2][col] - pix[row][col - 2]).abs() as i32
+ (pix[row - 2][col] - pix[row - 2][col - 2]).abs() as i32
+ (pix[row][col - 2] - pix[row - 2][col - 2]).abs() as i32;
match val {
0..4 => 0,
4..8 => 1,
8..16 => 2,
16..32 => 3,
32..48 => 4,
_ => 5,
}
};
let val = (((pix[row - 2][col] + pix[row][col - 2]) >> 1) + RSTEP[sharp][pump.get_bits(2) as usize]).clamp(0, 255);
pix[row][col] = val;
if row < 4 {
pix[row - 2][col + 2] = val;
};
if col < 4 {
pix[row + 2][col - 2] = val;
};
}
}
}
for row in 2..(height + 2) {
for col in ((3 - (row & 1))..(width + 2)).step_by(2) {
let val = ((pix[row][col - 1] + (pix[row][col] << 2) + pix[row][col + 1]) >> 1) - 0x100;
pix[row][col] = val.clamp(0, 255);
}
}
let tbl = LookupTable::new_with_bits(&CURVE, 10);
out.par_pixel_rows_mut().enumerate().for_each(|(row, line)| {
let mut random = ((pix[row + 2][2] as u32) << 16) | (pix[row + 2][3]) as u32;
for (x, p) in line.iter_mut().zip(pix[row + 2][2..width + 2].iter()) {
*x = tbl.dither(*p as u16, &mut random);
//*x = CURVE[*p as usize]; // no dither
}
});
Ok(out)
}
}
+749
View File
@@ -0,0 +1,749 @@
use byteorder::BigEndian;
use byteorder::LittleEndian;
use byteorder::ReadBytesExt;
use image::DynamicImage;
use std::collections::BTreeMap;
use std::io::Cursor;
use std::io::Seek;
use std::io::SeekFrom;
use std::mem::size_of;
use crate::CFA;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::alloc_image_plain;
use crate::analyze::FormatDump;
use crate::bits::BEu32;
use crate::bits::Endian;
use crate::decoders::raf::fuji_decompressor::decompress_fuji;
use crate::exif::Exif;
use crate::formats::jfif::Jfif;
use crate::formats::tiff::ifd::OffsetMode;
use crate::formats::tiff::*;
use crate::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::packed::*;
use crate::pixarray::PixU16;
use crate::rawimage::BlackLevel;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawimage::WhiteLevel;
use crate::rawsource::RawSource;
use crate::tags::DngTag;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
mod dbp;
mod fuji_decompressor;
/// RAF decoder
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RafDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
ifd: IFD,
makernotes: IFD,
camera: Camera,
}
/// Check if file has RAF signature
pub fn is_raf(file: &RawSource) -> bool {
match file.subview(0, 8) {
Ok(buf) => buf[0..8] == b"FUJIFILM"[..],
Err(_) => false,
}
}
/// We need to inject a virtual IFD into main IFD.
/// The RAF data block is not a regular TIFF structure but
/// a proprietary structure. This tag id should be unlikely
/// to appear in main IFD.
const RAF_TAG_VIRTUAL_RAF_DATA: u16 = 0xfaaa;
/// Parse a proprietary RAF data structure and return a virtual IFD.
/// Unfortunately, Fujifilm forgot to add a type field to these
/// tags, so we need to match by tag.
pub fn parse_raf_format(file: &RawSource, offset: u32) -> Result<IFD> {
let mut entries = BTreeMap::new();
let stream = &mut file.reader();
stream.seek(SeekFrom::Start(offset as u64))?;
let num = stream.read_u32::<BigEndian>()?; // Directory entries in this IFD
if num > 4000 {
return Err(format_args!("too many entries in IFD ({})", num).into());
}
for _ in 0..num {
let tag = stream.read_u16::<BigEndian>()?;
let len = stream.read_u16::<BigEndian>()? as usize;
//eprintln!("RAF tag: 0x{:X}, len: {}", tag, len);
match RafTags::try_from(tag) {
Ok(RafTags::RawImageFullSize)
| Ok(RafTags::RawImageCropTopLeft)
| Ok(RafTags::RawImageCroppedSize)
| Ok(RafTags::RawImageAspectRatio)
| Ok(RafTags::WB_GRGBLevels) => {
let n = len / size_of::<u16>();
let entry = Entry {
tag,
value: Value::Short((0..n).map(|_| stream.read_u16::<BigEndian>()).collect::<std::io::Result<Vec<_>>>()?),
embedded: None,
};
entries.insert(tag, entry);
}
Ok(RafTags::FujiLayout) | Ok(RafTags::XTransLayout) => {
let n = len / size_of::<u8>();
let entry = Entry {
tag,
value: Value::Byte((0..n).map(|_| stream.read_u8()).collect::<std::io::Result<Vec<_>>>()?),
embedded: None,
};
entries.insert(tag, entry);
}
// This one is in other byte-order...
Ok(RafTags::RAFData) => {
let n = len / size_of::<u32>();
let entry = Entry {
tag,
value: Value::Long((0..n).map(|_| stream.read_u32::<LittleEndian>()).collect::<std::io::Result<Vec<_>>>()?),
embedded: None,
};
entries.insert(tag, entry);
}
// Skip other tags
_ => {
stream.seek(SeekFrom::Current(len as i64))?;
}
}
}
Ok(IFD {
entries,
endian: Endian::Big,
offset: 0,
base: offset as u32,
corr: 0,
next_ifd: 0,
sub: Default::default(),
chain: Default::default(),
})
}
/// RAF format contains multiple TIFF and TIFF-like structures.
/// This creates a IFD with all other IFDs found collected as SubIFDs.
fn parse_raf(file: &RawSource) -> Result<IFD> {
const RAF_TIFF1_PTR_OFFSET: u64 = 84;
const RAF_TIFF2_PTR_OFFSET: u64 = 100;
const RAF_TAGS_PTR_OFFSET: u64 = 92;
//const RAF_BLOCK_PTR_OFFSET2: u64 = 120; TODO: ?!?
log::debug!("parse RAF");
let stream = &mut file.reader();
stream.seek(SeekFrom::Start(RAF_TIFF1_PTR_OFFSET))?;
let offset = stream.read_u32::<BigEndian>()?;
// Main IFD
let mut main = IFD::new_root(stream, offset + 12)?;
//main.dump::<TiffCommonTag>(10).iter().for_each(|line| eprintln!("MAIN: {}", line));
// There is a second TIFF structure, the pointer is stored at offset 100.
// If it is not a valid TIFF structure, the pointer itself is the RAF offset.
stream.seek(SeekFrom::Start(RAF_TIFF2_PTR_OFFSET))?;
let ioffset = stream.read_u32::<BigEndian>()?;
match IFD::new_root_with_correction(stream, 0, ioffset, 0, 10, &[FujiIFD::FujiIFD.into()]) {
Ok(val) => {
log::debug!("Found valid FujiIFD (0xF000)");
//val.dump::<FujiIFD>(10).iter().for_each(|line| eprintln!("FujiIFD: {}", line));
main.sub.insert(FujiIFD::FujiIFD as u16, vec![val]);
}
Err(_) => {
// We fake an FujiIFD to pass the StripOffsets
log::debug!("Unable to find FujiIFD (0xF000), let's fake it");
let mut entries = BTreeMap::<u16, Entry>::new();
entries.insert(
FujiIFD::StripOffsets as u16,
Entry {
tag: FujiIFD::StripOffsets as u16,
value: Value::Long(vec![ioffset]), // The ioffset is absolute to the file start.
embedded: Some(RAF_TIFF2_PTR_OFFSET as u32),
},
);
let fake = IFD {
offset: 0,
base: 0, // For the faked IFD, the offsets are already absolute to the file start.
corr: 0,
next_ifd: 0,
entries,
endian: main.endian,
sub: Default::default(),
chain: Default::default(),
};
main.sub.insert(FujiIFD::FujiIFD as u16, vec![fake]);
}
}
// And we maybe have a RAF data block, try to parse it.
stream.seek(SeekFrom::Start(RAF_TAGS_PTR_OFFSET))?;
let raf_offset = stream.read_u32::<BigEndian>()?;
match parse_raf_format(file, raf_offset) {
Ok(val) => {
//val.dump::<RafTags>(10).iter().for_each(|line| eprintln!("RAFTAGS: {}", line));
main.sub.insert(RAF_TAG_VIRTUAL_RAF_DATA, vec![val]);
}
Err(_) => {
log::debug!("RAF block pointer is not valid, ignoring");
}
}
Ok(main)
}
impl<'a> RafDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<RafDecoder<'a>> {
let ifd = parse_raf(file)?;
let camera = rawloader.check_supported(&ifd)?;
let makernotes = if let Some(exif) = ifd.find_first_ifd_with_tag(ExifTag::MakerNotes) {
exif.parse_makernote(&mut file.reader(), OffsetMode::Absolute, &[])?
} else {
None
}
.ok_or("File has not makernotes")?;
//makernotes.dump::<RafMakernotes>(10).iter().for_each(|line| eprintln!("MKND: {}", line));
Ok(RafDecoder {
ifd,
makernotes,
rawloader,
camera,
})
}
}
impl<'a> Decoder for RafDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self.ifd.find_first_ifd_with_tag(FujiIFD::StripOffsets).ok_or("No StripOffsets found")?;
let (width, height) = if raw.has_entry(FujiIFD::RawImageFullWidth) {
(
fetch_tiff_tag!(raw, FujiIFD::RawImageFullWidth).force_usize(0),
fetch_tiff_tag!(raw, FujiIFD::RawImageFullHeight).force_usize(0),
)
} else {
let raf = &self
.ifd
.sub_ifds()
.get(&RAF_TAG_VIRTUAL_RAF_DATA)
.and_then(|ifds| ifds.get(0))
.ok_or("No RAF data IFD found")?;
let sizes = fetch_tiff_tag!(raf, TiffCommonTag::ImageWidth);
(sizes.force_usize(1), sizes.force_usize(0))
};
let bps = match raw.get_entry(TiffCommonTag::RafBitsPerSample) {
Some(val) => val.force_u32(0) as usize,
None => 16,
};
// Rotation is only used for SuperCCD sensors, so we handle X-Trans CFA only here.
// Some cameras like X-T20 uses different CFA when compression is enabled, so we
// read the correct pattern from metadata.
let corrected_cfa = if let Some(cfa) = self.get_xtrans_cfa()? {
log::debug!(
"Found X-Trans CFA pattern in metadata, use this instead of camera config file. Pattern is: {}",
cfa
);
cfa
} else {
self.camera.cfa.clone()
};
// Strip offset is relative to IFD base
let offset = raw.base as u64 + fetch_tiff_tag!(raw, FujiIFD::StripOffsets).force_u64(0);
let src = if raw.has_entry(FujiIFD::StripByteCounts) {
let strip_count = fetch_tiff_tag!(raw, FujiIFD::StripByteCounts).force_u64(0);
file.subview_padded(offset, strip_count)?
} else {
// Some models like DBP don't have a byte count, so we read until EOF
file.subview_until_eof_padded(offset)?
};
log::debug!("BPS: {}, width: {}, height: {}, offset: {}", bps, width, height, offset);
let image = if self.camera.find_hint("double_width") {
// Some fuji SuperCCD cameras include a second raw image next to the first one
// that is identical but darker to the first. The two combined can produce
// a higher dynamic range image. Right now we're ignoring it.
decode_16le_skiplines(&src, width, height, dummy)
} else if self.camera.find_hint("jpeg32") {
match bps {
12 => decode_12be_msb32(&src, width, height, dummy),
14 => decode_14be_msb32(&src, width, height, dummy),
_ => return Err(RawlerError::unsupported(&self.camera, format!("RAF: Don't know how to decode bps {}", bps))),
}
} else if self.camera.clean_model == "DBP for GX680" {
assert_eq!(bps, 16);
dbp::decode_dbp(&src, width, height, dummy)?
} else if src.len() < bps * width * height / 8 {
if !dummy {
decompress_fuji(&src, width, height, bps, &corrected_cfa)?
} else {
alloc_image_plain!(width, height, dummy)
}
} else {
match bps {
12 => decode_12le(&src, width, height, dummy),
14 => decode_14le_unpacked(&src, width, height, dummy),
16 => {
if self.ifd.endian == Endian::Little {
decode_16le(&src, width, height, dummy)
} else {
decode_16be(&src, width, height, dummy)
}
}
_ => {
return Err(RawlerError::unsupported(&self.camera, format!("RAF: Don't know how to decode bps {}", bps)));
}
}
};
let blacklevel = self.get_blacklevel(&corrected_cfa)?;
log::debug!("RAF Blacklevels: {:?}", blacklevel);
// For now, we put the rotated data into DNG. Much better solution
// would be to support staggered layouts, but this is not used much
// and complicated to implement, because we need rectangular CFA patterns like 2x4.
// The code path for staggered data is already implemented here, but remains unused.
let rotate_for_dng = false;
let cpp = 1;
if self.camera.find_hint("fuji_rotation") || self.camera.find_hint("fuji_rotation_alt") {
log::debug!("Apply Fuji image rotation");
let rotated = if rotate_for_dng {
if self.camera.find_hint("fuji_rotation") {
fuji_raw_rotate(&image, dummy) // Only required for fuji_rotation
} else {
image
}
} else {
self.rotate_image(image.pixels(), &self.camera, width, height, dummy)?
};
let mut camera = self.camera.clone();
camera.cfa = corrected_cfa;
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&camera));
let mut image = RawImage::new(
self.camera.clone(),
rotated,
cpp,
normalize_wb(self.get_wb()?),
photometric,
blacklevel,
None,
dummy,
);
if rotate_for_dng {
image.add_dng_tag(TiffCommonTag::CFARepeatPatternDim, [2, 4]);
image.add_dng_tag(DngTag::CFALayout, 2_u16);
image.add_dng_tag(TiffCommonTag::CFAPattern, &[0_u8, 1, 2, 1, 2, 1, 0, 1][..]);
todo!();
//image.add_dng_tag(DngTag::BlackLevel, image.blacklevel[0]);
//image.add_dng_tag(DngTag::BlackLevelRepeatDim, [1_u16, 1_u16]);
}
// Reset crops because we have rotated the data.
image.active_area = None;
image.crop_area = None;
Ok(image)
} else {
//ok_image(self.camera.clone(), width, height, cpp, self.get_wb()?, image.into_inner())
let mut camera = self.camera.clone();
camera.cfa = corrected_cfa;
let whitelevel = if self.camera.whitelevel.is_none() {
match bps {
12 | 14 | 16 => {
let max_value: u32 = (1_u32 << bps) - 1;
Some(WhiteLevel::new(vec![max_value; cpp]))
}
_ => None,
}
} else {
None
};
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&camera));
let mut image = RawImage::new(camera, image, cpp, normalize_wb(self.get_wb()?), photometric, blacklevel, whitelevel, dummy);
// Overwrite crop if available in metadata
if let Some(crop) = self.get_crop()? {
log::debug!("RAW file metadata contains crop info, overriding toml definitions: {:?}", crop);
image.crop_area = Some(crop);
}
// Ideally, someone would expect that area is at bayer pattern
// boundary. This is not the case, so we don't check this here.
// if let Some(_area) = image.active_area.as_ref() {
// assert_eq!(area.d.w % image.cfa.width , 0);
// assert_eq!(area.d.h % image.cfa.height , 0);
// }
Ok(image)
}
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let mut exif = Exif::new(&self.ifd)?;
// Fuji RAF has all EXIF tags we need and there is no LensID or something
// we can lookup. So this is an exception, we just pass the information.
// TODO: better imeplement LensData::from_exif()?
if let Some(ifd) = self.ifd.get_sub_ifd(TiffCommonTag::ExifIFDPointer) {
exif.lens_make = ifd.get_entry(ExifTag::LensMake).and_then(|entry| entry.as_string().cloned());
exif.lens_model = ifd.get_entry(ExifTag::LensModel).and_then(|entry| entry.as_string().cloned());
exif.lens_spec = ifd.get_entry(ExifTag::LensSpecification).and_then(|entry| match &entry.value {
Value::Rational(data) => Some([data[0], data[1], data[2], data[3]]),
_ => None,
});
}
let mdata = RawMetadata::new(&self.camera, exif);
Ok(mdata)
}
fn xpacket(&self, file: &RawSource, _params: &RawDecodeParams) -> Result<Option<Vec<u8>>> {
let jpeg_buf = self.read_embedded_jpeg(file)?;
let mut cur = Cursor::new(jpeg_buf);
let jfif = Jfif::parse(&mut cur)?;
match jfif.xpacket().cloned() {
Some(xpacket) => {
log::debug!("Found XPacket data in embedded JPEG preview");
Ok(Some(xpacket))
}
None => {
log::debug!("Found no XPacket data");
Ok(None)
}
}
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
let jpeg_buf = self.read_embedded_jpeg(file)?;
let img = image::load_from_memory_with_format(jpeg_buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG: {:?}", err)))?;
Ok(Some(img))
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn format_hint(&self) -> FormatHint {
FormatHint::RAF
}
}
impl<'a> RafDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
let raw = self.ifd.find_first_ifd_with_tag(FujiIFD::StripOffsets).ok_or("No StripOffsets found")?;
match raw.get_entry(FujiIFD::WB_GRBLevels) {
Some(levels) => Ok([levels.force_f32(1), levels.force_f32(0), levels.force_f32(0), levels.force_f32(2)]),
None => {
let raf = &self
.ifd
.sub_ifds()
.get(&RAF_TAG_VIRTUAL_RAF_DATA)
.and_then(|ifds| ifds.get(0))
.ok_or("No RAF data IFD found")?;
let levels = fetch_tiff_tag!(raf, TiffCommonTag::RafOldWB);
Ok([levels.force_f32(1), levels.force_f32(0), levels.force_f32(0), levels.force_f32(3)])
}
}
}
fn get_blacklevel(&self, cfa: &CFA) -> Result<Option<BlackLevel>> {
if let Some(fuji) = self.ifd.get_sub_ifd(FujiIFD::FujiIFD) {
if let Some(Entry { value: Value::Long(black), .. }) = fuji.get_entry_recursive(FujiIFD::BlackLevel) {
let levels: Vec<u16> = black.iter().copied().map(|v| v as u16).collect();
return Ok(Some(BlackLevel::new(&levels, cfa.width, cfa.height, 1)));
} else {
log::debug!("Unable to find black level data");
}
}
Ok(None)
}
/// Get crop from metadata
/// Nearly all models have this parameter, except of FinePix HS10
fn get_crop(&self) -> Result<Option<Rect>> {
if let Some(raf) = &self.ifd.sub_ifds().get(&RAF_TAG_VIRTUAL_RAF_DATA).and_then(|ifds| ifds.get(0)) {
let crops = raf.get_entry(RafTags::RawImageCropTopLeft);
let size = raf.get_entry(RafTags::RawImageCroppedSize);
if let (Some(crops), Some(size)) = (crops, size) {
return Ok(Some(Rect::new(
Point::new(crops.force_usize(1), crops.force_usize(0)),
Dim2::new(size.force_usize(1), size.force_usize(0)),
)));
}
}
Ok(None)
}
/// Get the X-Trans CFA pattern
/// This is encoded in RAF metadata block in XTransLayout.
/// For unknown reason, the values are stored in reverse order and
/// also falsely reported by exiftoool.
fn get_xtrans_cfa(&self) -> Result<Option<CFA>> {
Ok(
if let Some(raf) = &self
.ifd
.sub_ifds()
.get(&RAF_TAG_VIRTUAL_RAF_DATA)
.and_then(|ifds| ifds.get(0).and_then(|ifd| ifd.get_entry(RafTags::XTransLayout)))
{
match &raf.value {
Value::Byte(data) => {
let patname: String = data
.iter()
.rev()
.map(|v| match v {
0 => 'R',
1 => 'G',
2 => 'B',
_ => 'X', // Unknown, let CFA::new() fail...
})
.collect();
Some(CFA::new(&patname))
}
_ => {
return Err("Invalid XTransLayout data type".into());
}
}
} else {
None
},
)
}
fn read_embedded_jpeg<'b>(&self, file: &'b RawSource) -> Result<&'b [u8]> {
// The offset and len of JPEG preview is in the RAF structure
let buf = file.subview(0, 84 + 8)?;
let jpeg_off = BEu32(buf, 84) as u64;
let jpeg_len = BEu32(buf, 84 + 4) as u64;
log::debug!("JPEG off: {}, len: {}", jpeg_off, jpeg_len);
Ok(file.subview(jpeg_off, jpeg_len)?)
}
fn rotate_image(&self, src: &[u16], camera: &Camera, width: usize, height: usize, dummy: bool) -> Result<PixU16> {
if let Some(active_area) = self.camera.active_area {
let x = active_area[0];
let y = active_area[1];
let cropwidth = width - active_area[2] - x;
let cropheight = height - active_area[3] - y; // TODO: bug, invalid order of crop index
if camera.find_hint("fuji_rotation_alt") {
let rotatedwidth = cropheight + cropwidth / 2;
let rotatedheight = rotatedwidth - 1;
let mut out = alloc_image_plain!(rotatedwidth, rotatedheight, dummy);
if !dummy {
for row in 0..cropheight {
let inb = &src[(row + y) * width + x..];
for col in 0..cropwidth {
let out_row = rotatedwidth - (cropheight + 1 - row + (col >> 1));
let out_col = ((col + 1) >> 1) + row;
out[out_row * rotatedwidth + out_col] = inb[col];
}
}
}
Ok(out)
} else {
let rotatedwidth = cropwidth + cropheight / 2;
let rotatedheight = rotatedwidth - 1;
let mut out = alloc_image_plain!(rotatedwidth, rotatedheight, dummy);
if !dummy {
for row in 0..cropheight {
let inb = &src[(row + y) * width + x..];
for col in 0..cropwidth {
let out_row = cropwidth - 1 - col + (row >> 1);
let out_col = ((row + 1) >> 1) + col;
out[out_row * rotatedwidth + out_col] = inb[col];
}
}
}
Ok(out)
}
} else {
Err(RawlerError::DecoderFailed("no active_area for fuji_rotate".to_string()))
}
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
log::debug!("RAF raw wb: {:?}", raw_wb);
// We never have more then RGB colors so far (no RGBE etc.)
// So we combine G1 and G2 to get RGB wb.
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
crate::tags::tiff_tag_enum!(RafMakernotes);
crate::tags::tiff_tag_enum!(FujiIFD);
crate::tags::tiff_tag_enum!(RafTags);
/// Specific RAF Makernotes tags.
/// These are only related to the Makernote IFD.
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum RafMakernotes {
Version = 0x0000,
InternalSerialNumber = 0x0010,
Quality = 0x1000,
Sharpness = 0x1001,
WhiteBalance = 0x1002,
Saturation = 0x1003,
Contrast = 0x1004,
ColorTemperature = 0x1005,
Contrast2 = 0x1006,
WhiteBalanceFineTune = 0x100a,
NoiseReduction = 0x100b,
NoiseReduction2 = 0x100e,
FujiFlashMode = 0x1010,
FlashExposureComp = 0x1011,
Macro = 0x1020,
FocusMode = 0x1021,
AFMode = 0x1022,
FocusPixel = 0x1023,
PrioritySettings = 0x102b,
FocusSettings = 0x102d,
AFCSettings = 0x102e,
SlowSync = 0x1030,
PictureMode = 0x1031,
ExposureCount = 0x1032,
EXRAuto = 0x1033,
EXRMode = 0x1034,
ShadowTone = 0x1040,
HighlightTone = 0x1041,
DigitalZoom = 0x1044,
LensModulationOptimizer = 0x1045,
GrainEffect = 0x1047,
ColorChromeEffect = 0x1048,
BWAdjustment = 0x1049,
CropMode = 0x104d,
ColorChromeFXBlue = 0x104e,
ShutterType = 0x1050,
AutoBracketing = 0x1100,
SequenceNumber = 0x1101,
DriveSettings = 0x1103,
PixelShiftShots = 0x1105,
PixelShiftOffset = 0x1106,
PanoramaAngle = 0x1153,
PanoramaDirection = 0x1154,
AdvancedFilter = 0x1201,
ColorMode = 0x1210,
BlurWarning = 0x1300,
FocusWarning = 0x1301,
ExposureWarning = 0x1302,
GEImageSize = 0x1304,
DynamicRange = 0x1400,
FilmMode = 0x1401,
DynamicRangeSetting = 0x1402,
DevelopmentDynamicRange = 0x1403,
MinFocalLength = 0x1404,
MaxFocalLength = 0x1405,
MaxApertureAtMinFocal = 0x1406,
MaxApertureAtMaxFocal = 0x1407,
AutoDynamicRange = 0x140b,
ImageStabilization = 0x1422,
SceneRecognition = 0x1425,
Rating = 0x1431,
ImageGeneration = 0x1436,
ImageCount = 0x1438,
DRangePriority = 0x1443,
DRangePriorityAuto = 0x1444,
DRangePriorityFixed = 0x1445,
FlickerReduction = 0x1446,
VideoRecordingMode = 0x3803,
PeripheralLighting = 0x3804,
VideoCompression = 0x3806,
FrameRate = 0x3820,
FrameWidth = 0x3821,
FrameHeight = 0x3822,
FullHDHighSpeedRec = 0x3824,
FaceElementSelected = 0x4005,
FacesDetected = 0x4100,
FacePositions = 0x4103,
NumFaceElements = 0x4200,
FaceElementTypes = 0x4201,
FaceElementPositions = 0x4203,
FaceRecInfo = 0x4282,
FileSource = 0x8000,
OrderNumber = 0x8002,
FrameNumber = 0x8003,
Parallax = 0xb211,
}
/// These are only related to the additional FujiIFD in RAF files
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum FujiIFD {
FujiIFD = 0xf000,
RawImageFullWidth = 0xf001,
RawImageFullHeight = 0xf002,
BitsPerSample = 0xf003,
StripOffsets = 0xf007,
StripByteCounts = 0xf008,
BlackLevel = 0xf00a,
GeometricDistortionParams = 0xf00b,
WB_GRBLevelsStandard = 0xf00c,
WB_GRBLevelsAuto = 0xf00d,
WB_GRBLevels = 0xf00e,
ChromaticAberrationParams = 0xf00f,
VignettingParams = 0xf010,
}
/// These are only related to the additional RAF-tags in RAF files
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum RafTags {
RawImageFullSize = 0x0100,
RawImageCropTopLeft = 0x0110,
RawImageCroppedSize = 0x0111,
RawImageAspectRatio = 0x0115,
RawImageSize = 0x0121,
FujiLayout = 0x0130,
XTransLayout = 0x0131,
WB_GRGBLevels = 0x2ff0,
RelativeExposure = 0x9200,
RawExposureBias = 0x9650,
RAFData = 0xc000,
}
pub fn fuji_raw_rotate(img: &PixU16, dummy: bool) -> PixU16 {
let mut out = alloc_image!(img.height, img.width, dummy);
for row in 0..img.height {
for col in 0..img.width {
//*x.at_mut(row, col) = out[flip_index(row, col)];
//*x.at_mut(row, col) = out[(width - 1 - col) * height + (height - 1 - row)];
//*out.at_mut(img.width - 1 - col, img.height - 1 - row) = *img.at(row, col); // out[row * width + col];
*out.at_mut(col, row) = *img.at(row, col); // out[row * width + col];
}
}
out
}
+30
View File
@@ -0,0 +1,30 @@
///
/// Original code by libraw and rawspeed, licensed under LGPL-2
///
/// Copyright (C) 2016 Alexey Danilchenko
/// Copyright (C) 2016 Alex Tutubalin
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Cursor;
use crate::Result;
use crate::{alloc_image_ok, pixarray::PixU16};
pub(super) fn decode_dbp(buf: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let mut out = alloc_image_ok!(width, height, dummy);
let mut cursor = Cursor::new(buf);
let n_tiles = 8;
let tile_width = width / n_tiles;
let _tile_height = 3856;
log::debug!("DBP width: {}, height: {}, tile: {}", width, height, tile_width);
let mut tile_buffer = vec![0_u16; height * tile_width];
for tile_n in 0..n_tiles {
cursor.read_u16_into::<BigEndian>(&mut tile_buffer)?;
for scan_line in 0..height {
let off = scan_line * width + tile_n * tile_width;
out[off..off + tile_width].copy_from_slice(&tile_buffer[scan_line * tile_width..scan_line * tile_width + tile_width]);
}
}
Ok(out)
}
File diff suppressed because it is too large Load Diff
+447
View File
@@ -0,0 +1,447 @@
use image::DynamicImage;
use crate::CFA;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::analyze::FormatDump;
use crate::exif::Exif;
use crate::formats::tiff::Entry;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::Rational;
use crate::formats::tiff::Value;
use crate::formats::tiff::reader::TiffReader;
use crate::imgop::Dim2;
use crate::imgop::Point;
use crate::imgop::Rect;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::decode_12le_unpacked_left_aligned;
use crate::packed::decode_12le_wcontrol;
use crate::pixarray::PixU16;
use crate::rawimage::CFAConfig;
use crate::rawimage::RawPhotometricInterpretation;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use crate::tags::tiff_tag_enum;
use self::v4decompressor::decode_panasonic_v4;
use self::v5decompressor::decode_panasonic_v5;
use self::v6decompressor::decode_panasonic_v6;
use self::v7decompressor::decode_panasonic_v7;
use self::v8decompressor::decode_panasonic_v8;
use super::BlackLevel;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
pub(crate) mod v4decompressor;
pub(crate) mod v5decompressor;
pub(crate) mod v6decompressor;
pub(crate) mod v7decompressor;
pub(crate) mod v8decompressor;
#[derive(Debug, Clone)]
pub struct Rw2Decoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
camera_ifd: Option<IFD>,
camera: Camera,
}
impl<'a> Rw2Decoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<Rw2Decoder<'a>> {
let raw = {
let data = tiff.find_ifds_with_tag(TiffCommonTag::PanaOffsets);
if !data.is_empty() {
data[0]
} else {
tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with StripOffsets tag")))?
}
};
let width = fetch_tiff_tag!(raw, TiffCommonTag::PanaWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::PanaLength).force_usize(0);
let mode = {
let ratio = width * 100 / height;
if ratio < 125 {
"1:1"
} else if ratio < 145 {
"4:3"
} else if ratio < 165 {
"3:2"
} else {
"16:9"
}
};
let camera = rawloader.check_supported_with_mode(tiff.root_ifd(), mode)?;
let camera_ifd = if let Some(ifd) = tiff.get_entry(PanasonicTag::CameraIFD) {
let buf = ifd.get_data();
match IFD::new_root(&mut std::io::Cursor::new(buf), 0) {
Ok(ifd) => Some(ifd),
Err(_) => None,
}
} else {
None
};
Ok(Rw2Decoder {
rawloader,
tiff,
camera_ifd,
camera,
})
}
}
impl<'a> Decoder for Rw2Decoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let width;
let height;
let (raw, split) = {
let data = self.tiff.find_ifds_with_tag(TiffCommonTag::PanaOffsets);
if !data.is_empty() {
(data[0], true)
} else {
(
self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with StripOffsets tag")))?,
false,
)
}
};
let compression = raw.get_entry(PanasonicTag::Compression).map(|entry| entry.force_u16(0)).unwrap_or_default(); // TODO BUG
//let compression = fetch_tiff_tag!(raw, PanasonicTag::Compression).force_u16(0);
let raw_format = raw.get_entry(PanasonicTag::RawFormat).map(|entry| entry.force_u16(0)).unwrap_or_default(); // TODO BUG
let bps = fetch_tiff_tag!(raw, PanasonicTag::BitsPerSample).force_u32(0);
let multishot = raw.get_entry(PanasonicTag::Multishot).map(|entry| entry.force_u32(0) == 65536).unwrap_or(false);
let image = {
let data = self.tiff.find_ifds_with_tag(TiffCommonTag::PanaOffsets);
if !data.is_empty() {
let raw = data[0];
width = fetch_tiff_tag!(raw, TiffCommonTag::PanaWidth).force_usize(0);
height = fetch_tiff_tag!(raw, TiffCommonTag::PanaLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::PanaOffsets).force_usize(0);
//let size = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts).force_usize(0);
log::debug!("PanaOffset: {}", offset);
let src = file.subview_until_eof_padded(offset as u64)?; // TODO add size and check all samples
Rw2Decoder::decode_panasonic(file, &src, width, height, split, raw_format, bps, self.tiff.root_ifd(), dummy)?
} else {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with StripOffsets tag")))?;
width = fetch_tiff_tag!(raw, TiffCommonTag::PanaWidth).force_usize(0);
height = fetch_tiff_tag!(raw, TiffCommonTag::PanaLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
//let size = fetch_tiff_tag!(raw, TiffCommonTag::StripByteCounts).force_usize(0);
log::debug!("StripOffset: {}", offset);
let src = file.subview_until_eof_padded(offset as u64)?; // TODO add size and check all samples
if src.len() >= width * height * 2 {
decode_12le_unpacked_left_aligned(&src, width, height, dummy)
} else if src.len() >= width * height * 3 / 2 {
decode_12le_wcontrol(&src, width, height, dummy)
} else {
Rw2Decoder::decode_panasonic(file, &src, width, height, split, raw_format, bps, self.tiff.root_ifd(), dummy)?
}
}
};
log::debug!(
"RW2 raw: {}, compression: {}, bps: {}, width: {}, height: {}, multishot: {}",
raw_format,
compression,
bps,
width,
height,
multishot
);
let cpp = 1;
let blacklevel = self.get_blacklevel()?;
let mut camera = self.camera.clone();
if let Some(cfa) = self.get_cfa()? {
camera.cfa = cfa;
}
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&camera));
let mut img = RawImage::new(camera, image, cpp, normalize_wb(self.get_wb()?), photometric, blacklevel, None, dummy);
if let Some(area) = self.get_active_area()? {
img.active_area = Some(area);
img.crop_area = Some(area);
} else if let Some(area) = self.get_crop()? {
img.crop_area = Some(area);
}
Ok(img)
}
fn full_image(&self, _file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
if let Some(data) = self.tiff.get_entry(PanasonicTag::JpegData) {
let buf = data.get_data();
let img = image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg)
.map_err(|e| RawlerError::DecoderFailed(format!("Unable to load jpeg preview: {:?}", e)))?;
return Ok(Some(img));
}
Ok(None)
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let mut exif = Exif::new(self.tiff.root_ifd())?;
if exif.iso_speed.unwrap_or(0) == 0 && exif.iso_speed_ratings.unwrap_or(0) == 0 && exif.recommended_exposure_index.unwrap_or(0) == 0 {
// Use ISO from PanasonicRaw IFD
if let Some(iso) = self.tiff.get_entry(PanasonicTag::ISO) {
exif.iso_speed_ratings = Some(iso.force_u16(0));
}
}
let mdata = RawMetadata::new_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::RW2
}
}
impl<'a> Rw2Decoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
if self.tiff.has_entry(PanasonicTag::PanaWBsR) && self.tiff.has_entry(PanasonicTag::PanaWBsB) {
let r = fetch_tiff_tag!(self.tiff, PanasonicTag::PanaWBsR).force_u32(0) as f32;
let b = fetch_tiff_tag!(self.tiff, PanasonicTag::PanaWBsB).force_u32(0) as f32;
Ok([r, 256.0, 256.0, b])
} else if self.tiff.has_entry(PanasonicTag::PanaWBs2R) && self.tiff.has_entry(PanasonicTag::PanaWBs2G) && self.tiff.has_entry(PanasonicTag::PanaWBs2B) {
let r = fetch_tiff_tag!(self.tiff, PanasonicTag::PanaWBs2R).force_u32(0) as f32;
let g = fetch_tiff_tag!(self.tiff, PanasonicTag::PanaWBs2G).force_u32(0) as f32;
let b = fetch_tiff_tag!(self.tiff, PanasonicTag::PanaWBs2B).force_u32(0) as f32;
Ok([r, g, g, b])
} else {
Err(RawlerError::DecoderFailed("RW2: Couldn't find WB".to_string()))
}
}
fn get_cfa(&self) -> Result<Option<CFA>> {
if self.tiff.has_entry(PanasonicTag::CFAPattern) {
let pattern = fetch_tiff_tag!(self.tiff, PanasonicTag::CFAPattern).force_u16(0);
Ok(Some(match pattern {
1 => CFA::new("RGGB"),
2 => CFA::new("GRBG"),
3 => CFA::new("GBRG"),
4 => CFA::new("BGGR"),
_ => return Err(format!("RW2: Unknown CFA pattern: {}", pattern).into()),
}))
} else {
Ok(None)
}
}
fn get_blacklevel(&self) -> Result<Option<BlackLevel>> {
if self.tiff.has_entry(PanasonicTag::BlackLevelRed) {
let r = fetch_tiff_tag!(self.tiff, PanasonicTag::BlackLevelRed).force_u16(0);
let g = fetch_tiff_tag!(self.tiff, PanasonicTag::BlackLevelGreen).force_u16(0);
let b = fetch_tiff_tag!(self.tiff, PanasonicTag::BlackLevelBlue).force_u16(0);
Ok(Some(BlackLevel::new(&[r, g, g, b], self.camera.cfa.width, self.camera.cfa.height, 1)))
} else {
Ok(None)
}
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
const MFT_MOUNT: &str = "MFT-mount";
if let Some(ifd) = &self.camera_ifd {
if ifd.has_entry(CameraIfdTag::LensTypeMake) && ifd.has_entry(CameraIfdTag::LensTypeModel) {
let make_id = fetch_tiff_tag!(ifd, CameraIfdTag::LensTypeMake);
let model_id = fetch_tiff_tag!(ifd, CameraIfdTag::LensTypeModel);
if make_id.value_type() == 3 && model_id.value_type() == 3 {
let composite_id = format!(
"{:02X} {:02X} {:02X}",
make_id.force_u16(0) & 0xFF,
model_id.force_u16(0) & 0xFF,
model_id.force_u16(0) >> 8
);
log::debug!("RW2 lens composite ID: {}", composite_id);
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_olympus_id(Some(composite_id))
.with_focal_len(self.get_focal_len()?)
.with_mounts(&[MFT_MOUNT.into()]);
return Ok(resolver.resolve());
} else {
log::info!("Unknown value types for lens tags: {}, {}", make_id.value_type(), model_id.value_type());
}
}
}
log::warn!("No lens data available");
Ok(None)
}
fn get_focal_len(&self) -> Result<Option<Rational>> {
if let Some(exif) = self.tiff.find_first_ifd_with_tag(ExifTag::MakerNotes) {
if let Some(Entry {
value: Value::Short(focal), ..
}) = exif.get_entry(ExifTag::FocalLength)
{
return Ok(focal.get(1).map(|v| Rational::new(*v as u32, 1)));
}
}
Ok(None)
}
fn get_crop(&self) -> Result<Option<Rect>> {
if self.tiff.has_entry(PanasonicTag::CropLeft) {
let crop_left = fetch_tiff_tag!(self.tiff, PanasonicTag::CropLeft).force_usize(0);
let crop_top = fetch_tiff_tag!(self.tiff, PanasonicTag::CropTop).force_usize(0);
let crop_right = fetch_tiff_tag!(self.tiff, PanasonicTag::CropRight).force_usize(0);
let crop_bottom = fetch_tiff_tag!(self.tiff, PanasonicTag::CropBottom).force_usize(0);
Ok(Some(Rect::new(
Point::new(crop_left, crop_top),
Dim2::new(crop_right - crop_left, crop_bottom - crop_top),
)))
} else {
Ok(None)
}
}
fn get_active_area(&self) -> Result<Option<Rect>> {
if self.tiff.has_entry(PanasonicTag::SensorLeftBorder) {
let sensor_left = fetch_tiff_tag!(self.tiff, PanasonicTag::SensorLeftBorder).force_usize(0);
let sensor_top = fetch_tiff_tag!(self.tiff, PanasonicTag::SensorTopBorder).force_usize(0);
let sensor_right = fetch_tiff_tag!(self.tiff, PanasonicTag::SensorRightBorder).force_usize(0);
let sensor_bottom = fetch_tiff_tag!(self.tiff, PanasonicTag::SensorBottomBorder).force_usize(0);
Ok(Some(Rect::new(
Point::new(sensor_left, sensor_top),
Dim2::new(sensor_right - sensor_left, sensor_bottom - sensor_top),
)))
} else {
Ok(None)
}
}
pub(crate) fn decode_panasonic(
file: &RawSource,
buf: &[u8],
width: usize,
height: usize,
split: bool,
raw_format: u16,
bps: u32,
ifd: &IFD,
dummy: bool,
) -> Result<PixU16> {
log::debug!("width: {}, height: {}, bps: {}", width, height, bps);
Ok(match raw_format {
3 => decode_panasonic_v4(buf, width, height, split, dummy),
4 => decode_panasonic_v4(buf, width, height, split, dummy),
5 => decode_panasonic_v5(buf, width, height, bps, dummy),
6 => decode_panasonic_v6(buf, width, height, bps, dummy),
7 => decode_panasonic_v7(buf, width, height, bps, dummy),
8 => decode_panasonic_v8(file, width, height, bps, ifd, dummy)?,
_ => todo!("Format {} is not implemented", raw_format), // TODO: return error
})
}
}
fn normalize_wb(raw_wb: [f32; 4]) -> [f32; 4] {
log::debug!("RW2 raw wb: {:?}", raw_wb);
let div = raw_wb[1];
let mut norm = raw_wb;
norm.iter_mut().for_each(|v| {
if v.is_normal() {
*v /= div
}
});
[norm[0], (norm[1] + norm[2]) / 2.0, norm[3], f32::NAN]
}
tiff_tag_enum!(PanasonicTag);
tiff_tag_enum!(CameraIfdTag);
/// Common tags, generally used in root IFD or SubIFDs
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum PanasonicTag {
PanaWidth = 0x0002,
PanaLength = 0x0003,
SensorTopBorder = 0x0004,
SensorLeftBorder = 0x0005,
SensorBottomBorder = 0x0006,
SensorRightBorder = 0x0007,
SamplesPerPixel = 0x0008,
CFAPattern = 0x0009,
BitsPerSample = 0x000a,
Compression = 0x000b,
PanaWBsR = 0x0011,
PanaWBsB = 0x0012,
ISO = 0x0017,
BlackLevelRed = 0x001c,
BlackLevelGreen = 0x001d,
BlackLevelBlue = 0x001e,
PanaWBs2R = 0x0024,
PanaWBs2G = 0x0025,
PanaWBs2B = 0x0026,
RawFormat = 0x0002d,
JpegData = 0x002e,
CropTop = 0x002f,
CropLeft = 0x0030,
CropBottom = 0x0031,
CropRight = 0x0032,
CF2StripHeight = 0x0037,
CF2Unknown1 = 0x0039, // Gamma table CF2_GammaSlope?
CF2Unknown2 = 0x003a, // Gamma table CF2_GammaPoint?
CF2ClipVal = 0x003b, // CF2_GammaClipVal
CF2HufInitVal0 = 0x003c,
CF2HufInitVal1 = 0x003d,
CF2HufInitVal2 = 0x003e,
CF2HufInitVal3 = 0x003f,
CF2HufTable = 0x0040,
CF2HufShiftDown = 0x0041,
CF2NumberOfStripsH = 0x0042,
CF2NumberOfStripsV = 0x0043,
CF2StripByteOffsets = 0x0044,
CF2StripLineOffsets = 0x0045,
CF2StripDataSize = 0x0046,
CF2StripWidths = 0x0047,
CF2StripHeights = 0x0048,
CF2StripWidth = 0x0064,
CameraIFD = 0x0120,
Multishot = 0x0121,
}
/// Common tags, generally used in root IFD or SubIFDs
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum CameraIfdTag {
LensTypeMake = 0x1201,
LensTypeModel = 0x1202,
}
@@ -0,0 +1,86 @@
use crate::{bits::LEu16, decoders::*, pumps::BitPump};
pub(crate) fn decode_panasonic_v4(buf: &[u8], width: usize, height: usize, split: bool, dummy: bool) -> PixU16 {
decode_threaded_multiline(
width,
height,
5,
dummy,
&(|out: &mut [u16], row| {
let skip = ((width * row * 9) + (width / 14 * 2 * row)) / 8;
let blocks = skip / 0x4000;
let src = &buf[blocks * 0x4000..];
let mut pump = BitPumpPanasonic::new(src, split);
for _ in 0..(skip % 0x4000) {
pump.get_bits(8);
}
let mut sh: i32 = 0;
for out in out.chunks_exact_mut(14) {
let mut pred: [i32; 2] = [0, 0];
let mut nonz: [i32; 2] = [0, 0];
for i in 0..14 {
if (i % 3) == 2 {
sh = 4 >> (3 - pump.get_bits(2));
}
if nonz[i & 1] != 0 {
let j = pump.get_bits(8) as i32;
if j != 0 {
pred[i & 1] -= 0x80 << sh;
if pred[i & 1] < 0 || sh == 4 {
pred[i & 1] &= !(-1 << sh);
}
pred[i & 1] += j << sh;
}
} else {
nonz[i & 1] = pump.get_bits(8) as i32;
if nonz[i & 1] != 0 || i > 11 {
pred[i & 1] = (nonz[i & 1] << 4) | (pump.get_bits(4) as i32);
}
}
out[i] = pred[i & 1] as u16;
}
}
Ok(())
}),
)
.expect("Failed to decode") // Decoder should never fail
}
pub struct BitPumpPanasonic<'a> {
buffer: &'a [u8],
pos: usize,
nbits: u32,
split: bool,
}
impl<'a> BitPumpPanasonic<'a> {
pub fn new(src: &'a [u8], split: bool) -> BitPumpPanasonic<'a> {
BitPumpPanasonic {
buffer: src,
pos: 0,
nbits: 0,
split,
}
}
}
impl<'a> BitPump for BitPumpPanasonic<'a> {
fn peek_bits(&mut self, num: u32) -> u32 {
if num > self.nbits {
self.nbits += 0x4000 * 8;
self.pos += 0x4000;
}
let mut byte = ((self.nbits - num) >> 3) ^ 0x3ff0;
if self.split {
byte = (byte + 0x4000 - 0x2008) % 0x4000;
}
let bits = LEu16(self.buffer, byte as usize + self.pos - 0x4000) as u32;
(bits >> ((self.nbits - num) & 7)) & (0x0ffffffffu32 >> (32 - num))
}
fn consume_bits(&mut self, num: u32) {
self.nbits -= num;
}
}
@@ -0,0 +1,50 @@
/// Ported from Libraw
use crate::{
decoders::*,
pumps::{BitPump, BitPumpLSB},
};
/// This works for 12 and 14 bit depth images
pub(crate) fn decode_panasonic_v5(buf: &[u8], width: usize, height: usize, bps: u32, dummy: bool) -> PixU16 {
// Raw data is divided into blocks of same size
const V5_BLOCK_SIZE: usize = 0x4000;
// Each block is splitted and swapped, we need to swap back
const V5_SECTION_SPLIT_OFFSET: usize = 0x1FF8;
// Each block contains multiple packets
const V5_BYTES_PER_PACKET: usize = 16;
// Count of packets in a block
const V5_PACKETS_PER_BLOCK: usize = V5_BLOCK_SIZE / V5_BYTES_PER_PACKET;
// Depending on bit depth, a packet forms different amount of pixels
let pixels_per_packet = match bps {
12 => 10,
14 => 9,
_ => unreachable!(),
};
// Pixel count per full block
let pixels_per_block = V5_PACKETS_PER_BLOCK * pixels_per_packet;
log::debug!("RW2 V5 decoder: pixels per block: {}, bps: {}", pixels_per_block, bps);
// We decode chunked at pixels_per_block boundary
// Each block delivers the same amount of pixels.
decode_threaded_chunked(
width,
height,
pixels_per_block,
dummy,
&(|chunk, block_id| {
// Block offset
let src = &buf[block_id * V5_BLOCK_SIZE..block_id * V5_BLOCK_SIZE + V5_BLOCK_SIZE];
// Now swap the two parts of the block
let mut swapped = Vec::with_capacity(V5_BLOCK_SIZE);
swapped.extend_from_slice(&src[V5_SECTION_SPLIT_OFFSET..]);
swapped.extend_from_slice(&src[..V5_SECTION_SPLIT_OFFSET]);
// Transform the packets into final pixels
for (out, bytes) in chunk.chunks_exact_mut(pixels_per_packet).zip(swapped.chunks_exact(V5_BYTES_PER_PACKET)) {
// The packet is a bitstream in LSB order.
let mut pump = BitPumpLSB::new(bytes);
out.iter_mut().for_each(|p| *p = pump.get_bits(bps) as u16);
}
}),
)
}
@@ -0,0 +1,189 @@
/// Ported from Libraw
use crate::{
decoders::*,
pumps::{BitPump, BitPumpLSB},
};
/// This works for 12 and 14 bit depth images
pub(crate) fn decode_panasonic_v6(buf: &[u8], width: usize, height: usize, bps: u32, dummy: bool) -> PixU16 {
log::debug!("width: {}", width);
const V6_BYTES_PER_BLOCK: usize = 16;
let pixels_per_block;
let pixelbase0;
let pixelbase_compare;
let spix_compare;
let pixel_mask;
match bps {
12 => {
pixels_per_block = 14;
pixelbase0 = 0x80;
pixelbase_compare = 0x800;
spix_compare = 0x3fff;
pixel_mask = 0xfff;
}
14 => {
pixels_per_block = 11;
pixelbase0 = 0x200;
pixelbase_compare = 0x2000;
spix_compare = 0xffff;
pixel_mask = 0x3fff;
}
_ => {
unreachable!()
}
}
let blocks_per_row = width / pixels_per_block;
let bytes_per_row = V6_BYTES_PER_BLOCK * blocks_per_row;
assert_eq!(width % pixels_per_block, 0);
//log::debug!("RW2 V5 decoder: pixels per block: {}, bps: {}", pixels_per_block, bps);
// We decode chunked at pixels_per_block boundary
// Each block delivers the same amount of pixels.
decode_threaded(
width,
height,
dummy,
&(|out, row| {
let src = &buf[row * bytes_per_row..row * bytes_per_row + bytes_per_row];
for (block_id, block) in src.chunks_exact(V6_BYTES_PER_BLOCK).enumerate() {
let out = &mut out[block_id * pixels_per_block..];
let mut pixelbuffer = [0_u16; 18];
let mut pump = BitPumpLSB::new(block);
match bps {
14 => {
// We fill from reverse, because bitstream is reversed
pump.get_bits(4); // padding bits, ignore it
pixelbuffer[13] = pump.get_bits(10) as u16;
pixelbuffer[12] = pump.get_bits(10) as u16;
pixelbuffer[11] = pump.get_bits(10) as u16;
pixelbuffer[10] = pump.get_bits(2) as u16;
pixelbuffer[9] = pump.get_bits(10) as u16;
pixelbuffer[8] = pump.get_bits(10) as u16;
pixelbuffer[7] = pump.get_bits(10) as u16;
pixelbuffer[6] = pump.get_bits(2) as u16;
pixelbuffer[5] = pump.get_bits(10) as u16;
pixelbuffer[4] = pump.get_bits(10) as u16;
pixelbuffer[3] = pump.get_bits(10) as u16;
pixelbuffer[2] = pump.get_bits(2) as u16;
pixelbuffer[1] = pump.get_bits(14) as u16;
pixelbuffer[0] = pump.get_bits(14) as u16;
}
12 => {
pixelbuffer[17] = pump.get_bits(8) as u16;
pixelbuffer[16] = pump.get_bits(8) as u16;
pixelbuffer[15] = pump.get_bits(8) as u16;
pixelbuffer[14] = pump.get_bits(2) as u16;
pixelbuffer[13] = pump.get_bits(8) as u16;
pixelbuffer[12] = pump.get_bits(8) as u16;
pixelbuffer[11] = pump.get_bits(8) as u16;
pixelbuffer[10] = pump.get_bits(2) as u16;
pixelbuffer[9] = pump.get_bits(8) as u16;
pixelbuffer[8] = pump.get_bits(8) as u16;
pixelbuffer[7] = pump.get_bits(8) as u16;
pixelbuffer[6] = pump.get_bits(2) as u16;
pixelbuffer[5] = pump.get_bits(8) as u16;
pixelbuffer[4] = pump.get_bits(8) as u16;
pixelbuffer[3] = pump.get_bits(8) as u16;
pixelbuffer[2] = pump.get_bits(2) as u16;
pixelbuffer[1] = pump.get_bits(12) as u16;
pixelbuffer[0] = pump.get_bits(12) as u16;
}
_ => unreachable!(),
}
let mut curr_pixel = 0;
let mut next_pixel = || -> u16 {
curr_pixel += 1;
pixelbuffer[curr_pixel - 1]
};
let mut oddeven = [0, 0];
let mut nonzero = [0, 0];
let mut pmul = 0;
let mut pixel_base = 0;
for pix in 0..pixels_per_block {
if pix % 3 == 2 {
let mut base = next_pixel();
if base == 3 {
base = 4;
}
pixel_base = pixelbase0 << base;
pmul = 1 << base;
}
let mut epixel: u16 = next_pixel();
if oddeven[pix % 2] != 0 {
epixel *= pmul;
if pixel_base < pixelbase_compare && nonzero[pix % 2] > pixel_base {
epixel += nonzero[pix % 2] - pixel_base;
}
nonzero[pix % 2] = epixel;
} else {
oddeven[pix % 2] = epixel;
if epixel != 0 {
nonzero[pix % 2] = epixel;
} else {
epixel = nonzero[pix % 2];
}
}
let spix = (epixel as i32).wrapping_sub(0xf);
if spix <= spix_compare {
out[pix] = (spix & spix_compare) as u16;
} else {
// FIXME: this is a convoluted way to compute zero.
// What was this code trying to do, actually?
epixel = ((epixel as i32).wrapping_add(0x7ffffff1) >> 0x1f) as u16;
//epixel = static_cast<int>(epixel + 0x7ffffff1) >> 0x1f;
//out(row, col) = epixel & 0x3fff;
out[pix] = (epixel & pixel_mask) as u16;
}
}
/*
for (int pix = 0; pix < PanasonicV6Decompressor::PixelsPerBlock;
pix++, col++) {
if (pix % 3 == 2) {
uint16_t base = page.nextpixel();
if (base == 3)
base = 4;
pixel_base = 0x200 << base;
pmul = 1 << base;
}
uint16_t epixel = page.nextpixel();
if (oddeven[pix % 2]) {
epixel *= pmul;
if (pixel_base < 0x2000 && nonzero[pix % 2] > pixel_base)
epixel += nonzero[pix % 2] - pixel_base;
nonzero[pix % 2] = epixel;
} else {
oddeven[pix % 2] = epixel;
if (epixel)
nonzero[pix % 2] = epixel;
else
epixel = nonzero[pix % 2];
}
auto spix = static_cast<unsigned>(static_cast<int>(epixel) - 0xf);
if (spix <= 0xffff)
out(row, col) = spix & 0xffff;
else {
// FIXME: this is a convoluted way to compute zero.
// What was this code trying to do, actually?
epixel = static_cast<int>(epixel + 0x7ffffff1) >> 0x1f;
out(row, col) = epixel & 0x3fff;
}
}
*/
}
}),
)
}
@@ -0,0 +1,36 @@
/// Ported from Libraw
use crate::{
decoders::*,
pumps::{BitPump, BitPumpLSB},
};
/// This works for 12 and 14 bit depth images
pub(crate) fn decode_panasonic_v7(buf: &[u8], width: usize, height: usize, bps: u32, dummy: bool) -> PixU16 {
const V7_BYTES_PER_BLOCK: usize = 16;
let pixels_per_block = match bps {
14 => 9,
12 => 10,
_ => unreachable!(),
};
let blocks_per_row = width / pixels_per_block;
assert_eq!(width % pixels_per_block, 0);
let bytes_per_row = V7_BYTES_PER_BLOCK * blocks_per_row;
decode_threaded(
width,
height,
dummy,
&(|out, row| {
let src = &buf[row * bytes_per_row..row * bytes_per_row + bytes_per_row];
for (block_id, block) in src.chunks_exact(V7_BYTES_PER_BLOCK).enumerate() {
let start = block_id * pixels_per_block;
let out = &mut out[start..start + pixels_per_block];
let mut pump = BitPumpLSB::new(block);
out.iter_mut().for_each(|pixel| *pixel = pump.get_bits(14) as u16);
}
}),
)
}
@@ -0,0 +1,495 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright 2024 Daniel Vogelbacher <daniel@chaospixel.com>
// Originally written by LibRaw LLC
// Copyright (C) 2022-2024 Alex Tutubalin, LibRaw LLC
// Ported from C++ to Rust by Daniel Vogelbacher
use itertools::Itertools;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use crate::{
alloc_image_ok,
bits::{Endian, clamp},
decoders::{Result, rw2::PanasonicTag},
formats::tiff::IFD,
pixarray::{PixU16, SharedPix2D},
pumps::{BitPump, BitPumpReverseBitsMSB, ByteStream},
rawsource::RawSource,
};
/// Defines the offsets for the start of a strip.
#[derive(Clone, Debug)]
struct StripLineOffset {
cols: u16,
rows: u16,
}
#[derive(Clone, Debug)]
struct CF2Params {
/// Unknown value, it's labeled as strip_height but don't match any height
#[allow(dead_code)]
strip_height: u32,
/// Unknown value, it's labeled as strip_width but don't match any width
#[allow(dead_code)]
strip_width: u32,
/// Gamma point and slope value.
gamma_point: Vec<u32>,
gamma_slope: Vec<u32>,
/// Max data value
gamma_clip_val: u16,
/// Initial values (base) for huffman coding
huf_init_val0: u16,
/// Initial values (base) for huffman coding
huf_init_val1: u16,
/// Initial values (base) for huffman coding
huf_init_val2: u16,
/// Initial values (base) for huffman coding
huf_init_val3: u16,
/// Stored huffman table, usually 17 entries
///
/// This is a pair of (bitcnt, symbol).
///
/// Example table:
/// 0000000 10 1022 11 2046 8 254 9 510
/// 0000020 7 126 4 14 4 12 3 4
/// 0000040 3 2 2 0 3 3 3 5
/// 0000060 4 13 5 30 6 62 12 4094
/// 0000100 12 4095
/// 0000104
huf_table: Vec<(u16, u16)>,
/// Shift down (0 in all samples...)
huf_shift_down: Vec<u16>,
/// Number of H strips
num_of_strips_h: u16,
/// Number of V strips
num_of_strips_v: u16,
/// Offset to bitstream
strip_byte_offsets: Vec<u32>,
/// Starting column offset in a output line
strip_line_offsets: Vec<StripLineOffset>,
/// Size in bits of compressed bitstream
strip_data_size: Vec<u32>,
/// Strip widths in pixels
strip_widths: Vec<u16>,
/// Strip heights in pixels
strip_heights: Vec<u16>,
}
impl CF2Params {
fn new(ifd: &IFD) -> Result<Self> {
let strip_height = fetch_tiff_tag!(ifd, PanasonicTag::CF2StripHeight).force_u32(0);
let gamma_point = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2Unknown1).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u32()).collect()
};
let gamma_slope = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2Unknown2).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u32()).collect()
};
let gamma_clip_val = fetch_tiff_tag!(ifd, PanasonicTag::CF2ClipVal).force_u16(0);
let huf_init_val0 = fetch_tiff_tag!(ifd, PanasonicTag::CF2HufInitVal0).force_u16(0);
let huf_init_val1 = fetch_tiff_tag!(ifd, PanasonicTag::CF2HufInitVal1).force_u16(0);
let huf_init_val2 = fetch_tiff_tag!(ifd, PanasonicTag::CF2HufInitVal2).force_u16(0);
let huf_init_val3 = fetch_tiff_tag!(ifd, PanasonicTag::CF2HufInitVal3).force_u16(0);
let huf_table = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2HufTable).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| (bs.get_u16(), bs.get_u16())).collect()
};
let huf_shift_down = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2HufShiftDown).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u16()).collect()
};
let num_of_strips_h = fetch_tiff_tag!(ifd, PanasonicTag::CF2NumberOfStripsH).force_u16(0);
let num_of_strips_v = fetch_tiff_tag!(ifd, PanasonicTag::CF2NumberOfStripsV).force_u16(0);
let strip_byte_offsets = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2StripByteOffsets).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u32()).collect()
};
let strip_line_offsets = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2StripLineOffsets).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count)
.map(|_| StripLineOffset {
cols: bs.get_u16(),
rows: bs.get_u16(),
})
.collect()
};
let strip_data_size = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2StripDataSize).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u32()).collect()
};
let strip_widths = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2StripWidths).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u16()).collect()
};
let strip_heights = {
let mut bs = ByteStream::new(fetch_tiff_tag!(ifd, PanasonicTag::CF2StripHeights).get_data(), Endian::Little);
let count = bs.get_u16();
(0..count).map(|_| bs.get_u16()).collect()
};
let strip_width = fetch_tiff_tag!(ifd, PanasonicTag::CF2StripWidth).force_u32(0);
Ok(Self {
strip_height,
gamma_point,
gamma_slope,
gamma_clip_val,
huf_init_val0,
huf_init_val1,
huf_init_val2,
huf_init_val3,
huf_table,
huf_shift_down,
num_of_strips_h,
num_of_strips_v,
strip_byte_offsets,
strip_line_offsets,
strip_data_size,
strip_widths,
strip_heights,
strip_width,
})
}
}
#[derive(Clone, Debug, Default)]
struct HuffmanSymbol {
/// Length of the symbol in bits
bitcnt: u8,
/// Actual Huffman symbol, right-padded with 0 bits
symbol: u16,
/// Pre-calculated bitmask, actually (-1) << (16-bits)
mask: u16,
}
#[derive(Clone, Debug, Default)]
struct HuffmanDecoder {
#[allow(dead_code)]
huff_symbols: [HuffmanSymbol; 17],
/// Lookup cache for all possible u16 values.
/// If a 16 bit input value is invalid (the symbol is undefined), the
/// value is None, otherwise it's (bitcnt, ssss)
cache: Vec<Option<(u8, u8)>>,
}
impl HuffmanDecoder {
fn new(symlens: impl Iterator<Item = u8>, mut symbols: impl Iterator<Item = u16>) -> Self {
let mut huff_symbols: [HuffmanSymbol; 17] = Default::default();
for (i, symlen) in symlens.enumerate() {
let symbol = symbols.next().expect("symbol iterator is shorter than symlens iterator");
let bitmask = 0xFFFFu16 >> (16 - symlen);
debug_assert_eq!(symbol, symbol & bitmask);
// Left-align symbol and mask
huff_symbols[i] = HuffmanSymbol {
bitcnt: symlen,
symbol: (symbol) << (16 - symlen),
mask: 0xFFFF << (16 - symlen),
};
}
// Generate lookup cache for all possible 16 bit input values.
let cache = (0..=0xFFFFu16)
.map(|x| Self::slow_lookup(&huff_symbols, x).map(|ssss| (huff_symbols[ssss as usize].bitcnt, ssss)))
.collect_vec();
Self { huff_symbols, cache }
}
/// Slow lookup into Huffman symbol table
fn slow_lookup(huff_symbols: &[HuffmanSymbol; 17], bits: u16) -> Option<u8> {
for i in 0..17 {
if (bits & huff_symbols[i].mask) == huff_symbols[i].symbol {
return Some(i as u8);
}
}
None
}
/// Extract Huffman symbol from bitstream pump and
/// return index into symbol table.
fn get_next(&self, pump: &mut dyn BitPump) -> u8 {
let next_bits = pump.peek_bits(16);
debug_assert_eq!(self.cache.len(), u16::MAX as usize + 1);
if let Some((bits, ssss)) = unsafe { *self.cache.get_unchecked(next_bits as usize) } {
pump.consume_bits(bits as u32);
ssss
} else {
panic!("Input value {:016b} starts not with a valid huffman symbol", next_bits);
}
}
}
/// Internal decoder state
struct State {
huffdec: HuffmanDecoder,
gamma_table: Option<Vec<u16>>,
datamax: i32,
line_base: CoeffBase,
current_base: CoeffBase,
}
impl State {
fn new(params: &CF2Params) -> Self {
let initial = [params.huf_init_val0, params.huf_init_val1, params.huf_init_val2, params.huf_init_val3];
let gamma_table = make_gammatable(params);
let line_base = CoeffBase::new([initial[0], initial[1], initial[2], initial[3]]);
let current_base = line_base;
let huffdec = HuffmanDecoder::new(
params.huf_table.iter().map(|(bits, _symbol)| *bits as u8),
params.huf_table.iter().map(|(_bits, symbol)| *symbol),
);
Self {
huffdec,
gamma_table,
datamax: params.gamma_clip_val as i32,
line_base,
current_base,
}
}
}
#[derive(Clone, Copy, Debug)]
struct CoeffBase {
coeff: [u16; 4],
}
impl CoeffBase {
pub fn new(coeff: [u16; 4]) -> Self {
Self { coeff }
}
pub fn update(&mut self, line: &[u16]) {
self.coeff = [line[0], line[1], line[2], line[3]];
}
}
fn calc_gamma(params: &CF2Params, idx: u32) -> u16 {
let gamma_base = 0;
let gamma_points = &params.gamma_point; // [65536, 65536, 65536, 65536, 65536, 65536]
let gamma_slopes = &params.gamma_slope; // 0
let clipping = params.gamma_clip_val;
let mut x = {
let mut tmp: u32 = idx | 0xFFFF0000;
if (idx & 0x10000) == 0 {
tmp = idx & 0x1FFFF;
}
u32::min(gamma_base + tmp, 0xFFFF)
};
let mut idx = 0;
if (x & 0x80000000) != 0 {
x = 0;
}
if x >= (0xFFFF & gamma_slopes[1]) {
idx = 1;
if x >= (0xFFFF & gamma_slopes[2]) {
idx = 2;
if x >= (0xFFFF & gamma_slopes[3]) {
idx = 3;
if x >= (0xFFFF & gamma_slopes[4]) {
idx = (((x as u64 | 0x5_00000000u64) - (0xFFFF & gamma_slopes[5]) as u64) >> 32) as usize;
}
}
}
}
let point = gamma_points[idx];
let slope = gamma_slopes[idx];
let mut tmp: u32 = x - (slope & 0xFFFF);
let result: u16;
if (point & 0x1F) == 31 {
result = if idx == 5 { 0xFFFF } else { (gamma_slopes[idx + 1] >> 16) & 0xFFFF } as u16;
return u16::min(result, clipping);
}
if (point & 0x10) == 0 {
if (point & 0x1F) == 15 {
let result = ((slope >> 16) & 0xFFFF) as u16;
return u16::min(result, clipping);
} else if (point & 0x1F) != 0 {
tmp = (tmp + (1 << ((point & 0x1F) - 1))) >> (point & 0x1F);
}
} else {
tmp <<= point & 0xF;
}
result = (tmp + ((slope >> 16) & 0xFFFF)) as u16;
u16::min(result, clipping)
}
fn make_gammatable(params: &CF2Params) -> Option<Vec<u16>> {
const LINEAR_POINTS: [u32; 6] = [65536; 6];
const LINEAR_SLOPES: [u32; 6] = [0; 6];
if params.gamma_point == LINEAR_POINTS && params.gamma_slope == LINEAR_SLOPES {
None
} else {
let mut table = vec![0; 0x10000];
let mut _linear = true;
for idx in 0..0x10000 {
table[idx] = calc_gamma(params, idx as u32);
_linear = _linear && table[idx] == idx as u16;
}
Some(table)
}
}
/// Decode Panasonic V8 bitstreams
pub(crate) fn decode_panasonic_v8(rawfile: &RawSource, width: usize, height: usize, _bps: u32, ifd: &IFD, dummy: bool) -> Result<PixU16> {
let out = alloc_image_ok!(width, height, dummy);
let params = CF2Params::new(ifd)?;
log::debug!("pana8: params: {:?}", params);
assert_eq!(
params.strip_widths.iter().map(|x| *x as i32).sum::<i32>() as usize / params.num_of_strips_v as usize,
width
);
// Shared output buffer, we need to write from multiple rayon threads to output image.
let shared_pix = SharedPix2D::new(out);
let total_strip_count = (params.num_of_strips_h * params.num_of_strips_v) as usize;
let mut bitstreams = Vec::with_capacity(total_strip_count);
for strip_id in 0..total_strip_count {
bitstreams.push(rawfile.subview(params.strip_byte_offsets[strip_id] as u64, (params.strip_data_size[strip_id] as u64 + 7) / 8)?);
}
// Parallel decode multiple strips
(0..total_strip_count).into_par_iter().for_each(|strip_id| {
let buf = &bitstreams[strip_id];
decode_strip(buf, &params, strip_id, unsafe { shared_pix.inner_mut() });
});
Ok(shared_pix.into_inner())
}
/// Decode a single strip
fn decode_strip(buf: &[u8], params: &CF2Params, strip_id: usize, out: &mut PixU16) {
let mut pump = BitPumpReverseBitsMSB::new(buf);
let width = params.strip_widths[strip_id] as usize;
let height = params.strip_heights[strip_id] as usize;
let halfheight = height >> 1;
let halfwidth = width >> 1;
let doublewidth = halfwidth * 4;
let mut linebuf = vec![0_u16; doublewidth];
// for (i, item) in params.huf_table.iter().enumerate() {
// let fmt = format!("{:#032b}", item.1);
// log::debug!("Pana8 huf_table {i}: {} (bits: {})", fmt.split_at((32 - item.0) as usize).1, item.0);
// //log::debug!("Pana8: huf_table {:02}: {}, {}", i, item.0, item.1);
// }
let mut state = State::new(params);
// Data is encoded in RGRGRG..GBGBGB in a single line (like LJPEG92 4-7 predictors)
for curr_row in 0..halfheight {
state.current_base = state.line_base;
for col in 0..doublewidth {
// Calculate index
let ssss = state.huffdec.get_next(&mut pump);
// Shiftdown seems to be the count of bits shifted to right during encoding.
// It's 0 for all existing samples so far, highly interested in samples that has
let shift_down: u8 = (params.huf_shift_down[ssss as usize] & 0x1F) as u8;
assert_eq!(shift_down, 0, "CF2HufShiftDown samples required");
// Calculate total required bits to read from bitstream.
let req_bits: u32 = ssss.saturating_sub(shift_down as u8) as u32;
let delta1: i32 = if req_bits == 0 {
0
} else {
debug_assert_ne!(req_bits, 0);
let rawbits: u32 = pump.get_bits(req_bits as u32); // Get additional bits
let sign = rawbits >> (req_bits - 1); // Get leading sign bit
let val = (rawbits << (params.huf_shift_down[ssss as usize] & 0xFF)) as i32;
if sign == 1 {
val
} else if ssss > 0 {
if shift_down != 0 { val + (-1 << ssss) } else { val + (-1 << ssss) + 1 }
} else {
0
}
};
let delta2 = if shift_down != 0 { 1 << (shift_down - 1) } else { 0 };
let delta = delta1 + delta2;
// For each col iteration, we write to ONE pixel of of 4-pixel group.
let destpixel = &mut linebuf[col & !0x3..];
if col & 3 == 2 {
let val = state.current_base.coeff[1] as i32 + delta;
destpixel[1] = clamp(val, 0, state.datamax) as u16;
} else if col & 3 == 1 {
let val = state.current_base.coeff[2] as i32 + delta;
destpixel[2] = clamp(val, 0, state.datamax) as u16;
} else if col & 3 != 0 {
let val = state.current_base.coeff[3] as i32 + delta;
destpixel[3] = clamp(val, 0, state.datamax) as u16;
} else {
let val = state.current_base.coeff[0] as i32 + delta;
destpixel[0] = clamp(val, 0, state.datamax) as u16;
}
if col & 3 == 3 {
state.current_base.update(destpixel);
}
if col == 3 {
// base for next line (col == 3 -> first 4 pixels are complete in current row)
state.line_base.update(&linebuf);
}
}
// Copy line buffer into output image.
// Line buffer contains two rows packed into one row with double width.
assert_eq!(linebuf.len(), 2 * width);
for col in (0..width).step_by(2) {
let row_offset = params.strip_line_offsets[strip_id].rows as usize;
let left_margin = params.strip_line_offsets[strip_id].cols as usize;
let dest_row = row_offset + (curr_row * 2);
if let Some(gamma_table) = &state.gamma_table {
debug_assert!(!gamma_table.is_empty());
*out.at_mut(dest_row + 0, left_margin + col + 0) = gamma_table[linebuf[2 * col + 0] as usize];
*out.at_mut(dest_row + 0, left_margin + col + 1) = gamma_table[linebuf[2 * col + 1] as usize];
*out.at_mut(dest_row + 1, left_margin + col + 0) = gamma_table[linebuf[2 * col + 2] as usize];
*out.at_mut(dest_row + 1, left_margin + col + 1) = gamma_table[linebuf[2 * col + 3] as usize];
} else {
*out.at_mut(dest_row + 0, left_margin + col + 0) = linebuf[2 * col + 0];
*out.at_mut(dest_row + 0, left_margin + col + 1) = linebuf[2 * col + 1];
*out.at_mut(dest_row + 1, left_margin + col + 0) = linebuf[2 * col + 2];
*out.at_mut(dest_row + 1, left_margin + col + 1) = linebuf[2 * col + 3];
}
}
}
}
+523
View File
@@ -0,0 +1,523 @@
use log::warn;
use std::cmp;
use crate::RawImage;
use crate::RawLoader;
use crate::RawlerError;
use crate::Result;
use crate::alloc_image;
use crate::analyze::FormatDump;
use crate::bits::LEu32;
use crate::bits::clampbits;
use crate::exif::Exif;
use crate::formats::tiff::GenericTiffReader;
use crate::formats::tiff::IFD;
use crate::formats::tiff::ifd::OffsetMode;
use crate::formats::tiff::reader::TiffReader;
use crate::lens::LensDescription;
use crate::lens::LensResolver;
use crate::packed::decode_12be;
use crate::packed::decode_12le;
use crate::packed::decode_12le_unpacked;
use crate::packed::decode_14le_unpacked;
use crate::pixarray::PixU16;
use crate::pumps::BitPump;
use crate::pumps::BitPumpMSB;
use crate::pumps::BitPumpMSB32;
use crate::rawsource::RawSource;
use crate::tags::ExifTag;
use crate::tags::TiffCommonTag;
use super::Camera;
use super::Decoder;
use super::FormatHint;
use super::RawDecodeParams;
use super::RawMetadata;
use super::ok_cfa_image_with_blacklevels;
const NX_MOUNT: &str = "NX-mount";
#[derive(Debug, Clone)]
pub struct SrwDecoder<'a> {
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
makernote: IFD,
camera: Camera,
}
impl<'a> SrwDecoder<'a> {
pub fn new(file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<SrwDecoder<'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::RelativeToIFD, &[])?
} else {
warn!("SRW makernote not found");
None
}
.ok_or("File has not makernotes")?;
Ok(SrwDecoder {
tiff,
rawloader,
camera,
makernote,
})
}
}
impl<'a> Decoder for SrwDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::StripOffsets)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with StripOffsets tag")))?;
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let compression = fetch_tiff_tag!(raw, TiffCommonTag::Compression).force_u32(0);
let bits = fetch_tiff_tag!(raw, TiffCommonTag::BitsPerSample).force_u32(0);
let src = file.subview_until_eof_padded(offset as u64)?;
let image = match compression {
32769 => match bits {
12 => decode_12le_unpacked(&src, width, height, dummy),
14 => decode_14le_unpacked(&src, width, height, dummy),
x => return Err(RawlerError::unsupported(&self.camera, format!("SRW: Don't know how to handle bps {}", x))),
},
32770 => match raw.get_entry(TiffCommonTag::SrwSensorAreas) {
None => match bits {
12 => {
if self.camera.find_hint("little_endian") {
decode_12le(&src, width, height, dummy)
} else {
decode_12be(&src, width, height, dummy)
}
}
14 => decode_14le_unpacked(&src, width, height, dummy),
x => return Err(RawlerError::unsupported(&self.camera, format!("SRW: Don't know how to handle bps {}", x))),
},
Some(x) => {
let coffset = x.force_usize(0);
assert!(coffset > 0, "Surely this can't be the start of the file");
let loffsets = file.subview_until_eof(coffset as u64)?;
SrwDecoder::decode_srw1(&src, loffsets, width, height, dummy)
}
},
32772 => SrwDecoder::decode_srw2(&src, width, height, dummy),
32773 => SrwDecoder::decode_srw3(&src, width, height, dummy),
x => {
return Err(RawlerError::unsupported(
&self.camera,
format!("SRW: Don't know how to handle compression {}", x),
));
}
};
let cpp = 1;
ok_cfa_image_with_blacklevels(self.camera.clone(), cpp, self.get_wb()?, self.get_blacklevel()?, 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_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
Ok(mdata)
}
fn format_hint(&self) -> FormatHint {
FormatHint::SRW
}
}
impl<'a> SrwDecoder<'a> {
pub fn decode_srw1(buf: &[u8], loffsets: &[u8], width: usize, height: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
for row in 0..height {
let mut len: [u32; 4] = [if row < 2 { 7 } else { 4 }; 4];
let loffset = LEu32(loffsets, row * 4) as usize;
let mut pump = BitPumpMSB32::new(&buf[loffset..]);
let img = width * row;
let img_up = width * (cmp::max(1, row) - 1);
let img_up2 = width * (cmp::max(2, row) - 2);
// Image is arranged in groups of 16 pixels horizontally
for col in (0..width).step_by(16) {
let dir = pump.get_bits(1) == 1;
let ops = [pump.get_bits(2), pump.get_bits(2), pump.get_bits(2), pump.get_bits(2)];
for (i, op) in ops.iter().enumerate() {
match *op {
3 => len[i] = pump.get_bits(4),
2 => len[i] -= 1,
1 => len[i] += 1,
_ => {}
}
}
// First decode even pixels
for c in (0..16).step_by(2) {
let l = len[c >> 3];
let adj = pump.get_ibits_sextended(l);
let predictor = if dir {
// Upward prediction
out[img_up + col + c]
} else {
// Left to right prediction
if col == 0 { 128 } else { out[img + col - 2] }
};
if col + c < width {
// No point in decoding pixels outside the image
out[img + col + c] = ((predictor as i32) + adj) as u16;
}
}
// Now decode odd pixels
for c in (1..16).step_by(2) {
let l = len[2 | (c >> 3)];
let adj = pump.get_ibits_sextended(l);
let predictor = if dir {
// Upward prediction
out[img_up2 + col + c]
} else {
// Left to right prediction
if col == 0 { 128 } else { out[img + col - 1] }
};
if col + c < width {
// No point in decoding pixels outside the image
out[img + col + c] = ((predictor as i32) + adj) as u16;
}
}
}
}
// SRW1 apparently has red and blue swapped, just changing the CFA pattern to
// match causes color fringing in high contrast areas because the actual pixel
// locations would not match the CFA pattern
for row in (0..height).step_by(2) {
for col in (0..width).step_by(2) {
out.pixels_mut().swap(row * width + col + 1, (row + 1) * width + col);
}
}
out
}
pub fn decode_srw2(buf: &[u8], width: usize, height: usize, dummy: bool) -> PixU16 {
let mut out = alloc_image!(width, height, dummy);
// This format has a variable length encoding of how many bits are needed
// to encode the difference between pixels, we use a table to process it
// that has two values, the first the number of bits that were used to
// encode, the second the number of bits that come after with the difference
// The table has 14 entries because the difference can have between 0 (no
// difference) and 13 bits (differences between 12 bits numbers can need 13)
let tab: [[u32; 2]; 14] = [
[3, 4],
[3, 7],
[2, 6],
[2, 5],
[4, 3],
[6, 0],
[7, 9],
[8, 10],
[9, 11],
[10, 12],
[10, 13],
[5, 1],
[4, 8],
[4, 2],
];
// We generate a 1024 entry table (to be addressed by reading 10 bits) by
// consecutively filling in 2^(10-N) positions where N is the variable number of
// bits of the encoding. So for example 4 is encoded with 3 bits so the first
// 2^(10-3)=128 positions are set with 3,4 so that any time we read 000 we
// know the next 4 bits are the difference. We read 10 bits because that is
// the maximum number of bits used in the variable encoding (for the 12 and
// 13 cases)
let mut tbl: [[u32; 2]; 1024] = [[0, 0]; 1024];
let mut n: usize = 0;
for i in 0..14 {
let mut c = 0;
while c < (1024 >> tab[i][0]) {
tbl[n][0] = tab[i][0];
tbl[n][1] = tab[i][1];
n += 1;
c += 1;
}
}
let mut vpred: [[i32; 2]; 2] = [[0, 0], [0, 0]];
let mut hpred: [i32; 2] = [0, 0];
let mut pump = BitPumpMSB::new(buf);
for row in 0..height {
for col in 0..width {
let diff = SrwDecoder::srw2_diff(&mut pump, &tbl);
if col < 2 {
vpred[row & 1][col] += diff;
hpred[col] = vpred[row & 1][col];
} else {
hpred[col & 1] += diff;
}
out[row * width + col] = hpred[col & 1] as u16;
}
}
out
}
pub fn srw2_diff(pump: &mut BitPumpMSB, tbl: &[[u32; 2]; 1024]) -> i32 {
// We read 10 bits to index into our table
let c = pump.peek_bits(10);
// Skip the bits that were used to encode this case
pump.consume_bits(tbl[c as usize][0]);
// Read the number of bits the table tells me
let len = tbl[c as usize][1];
let mut diff = pump.get_bits(len) as i32;
// If the first bit is 0 we need to turn this into a negative number
if len != 0 && (diff & (1 << (len - 1))) == 0 {
diff -= (1 << len) - 1;
}
diff
}
pub fn decode_srw3(buf: &[u8], width: usize, height: usize, dummy: bool) -> PixU16 {
// Decoder for third generation compressed SRW files (NX1)
// Seriously Samsung just use lossless jpeg already, it compresses better too :)
// Thanks to Michael Reichmann (Luminous Landscape) for putting me in contact
// and Loring von Palleske (Samsung) for pointing to the open-source code of
// Samsung's DNG converter at http://opensource.samsung.com/
let mut out = alloc_image!(width, height, dummy);
let mut pump = BitPumpMSB32::new(buf);
// Process the initial metadata bits, we only really use initVal, width and
// height (the last two match the TIFF values anyway)
pump.get_bits(16); // NLCVersion
pump.get_bits(4); // ImgFormat
let bit_depth = pump.get_bits(4) + 1;
pump.get_bits(4); // NumBlkInRCUnit
pump.get_bits(4); // CompressionRatio
pump.get_bits(16); // Width;
pump.get_bits(16); // Height;
pump.get_bits(16); // TileWidth
pump.get_bits(4); // reserved
// The format includes an optimization code that sets 3 flags to change the
// decoding parameters
let optflags = pump.get_bits(4);
static OPT_SKIP: u32 = 1; // Skip checking if we need differences from previous line
static OPT_MV: u32 = 2; // Simplify motion vector definition
static OPT_QP: u32 = 4; // Don't scale the diff values
pump.get_bits(8); // OverlapWidth
pump.get_bits(8); // reserved
pump.get_bits(8); // Inc
pump.get_bits(2); // reserved
let init_val = pump.get_bits(14) as u16;
// The format is relatively straightforward. Each line gets encoded as a set
// of differences from pixels from another line. Pixels are grouped in blocks
// of 16 (8 green, 8 red or blue). Each block is encoded in three sections.
// First 1 or 4 bits to specify which reference pixels to use, then a section
// that specifies for each pixel the number of bits in the difference, then
// the actual difference bits
let mut line_offset = 0;
for row in 0..height {
line_offset += pump.get_pos();
// Align pump to 16byte boundary
if (line_offset & 0x0f) != 0 {
line_offset += 16 - (line_offset & 0xf);
}
pump = BitPumpMSB32::new(&buf[line_offset..]);
let img = width * row;
let img_up = width * (cmp::max(1, row) - 1);
let img_up2 = width * (cmp::max(2, row) - 2);
// Initialize the motion and diff modes at the start of the line
let mut motion: usize = 7;
// By default we are not scaling values at all
let mut scale: i32 = 0;
let mut diff_bits_mode: [[u32; 2]; 3] = [[0; 2]; 3];
for i in 0..3 {
let init: u32 = if row < 2 { 7 } else { 4 };
diff_bits_mode[i][0] = init;
diff_bits_mode[i][1] = init;
}
for col in (0..width).step_by(16) {
// Calculate how much scaling the final values will need
scale = if (optflags & OPT_QP) == 0 && (col & 63) == 0 {
let scalevals: [i32; 3] = [0, -2, 2];
let i = pump.get_bits(2) as usize;
if i < 3 { scale + scalevals[i] } else { pump.get_bits(12) as i32 }
} else {
scale // Keep value from previous iteration
};
// First we figure out which reference pixels mode we're in
if (optflags & OPT_MV) != 0 {
motion = if pump.get_bits(1) != 0 { 3 } else { 7 };
} else if pump.get_bits(1) == 0 {
motion = pump.get_bits(3) as usize;
}
if row < 2 && motion != 7 {
panic!("SRW Decoder: At start of image and motion isn't 7. File corrupted?")
}
if motion == 7 {
// The base case, just set all pixels to the previous ones on the same line
// If we're at the left edge we just start at the initial value
for i in 0..16 {
out[img + col + i] = if col == 0 { init_val } else { out[img + col + i - 2] };
}
} else {
// The complex case, we now need to actually lookup one or two lines above
if row < 2 {
panic!("SRW: Got a previous line lookup on first two lines. File corrupted?");
}
let motion_offset: [isize; 7] = [-4, -2, -2, 0, 0, 2, 4];
let motion_average: [i32; 7] = [0, 0, 1, 0, 1, 0, 0];
let slide_offset = motion_offset[motion];
for i in 0..16 {
let refpixel: usize = if ((row + i) & 0x1) != 0 {
// Red or blue pixels use same color two lines up
((img_up2 + col + i) as isize + slide_offset) as usize
} else {
// Green pixel N uses Green pixel N from row above (top left or top right)
if (i % 2) != 0 {
((img_up + col + i - 1) as isize + slide_offset) as usize
} else {
((img_up + col + i + 1) as isize + slide_offset) as usize
}
};
// In some cases we use as reference interpolation of this pixel and the next
out[img + col + i] = if motion_average[motion] != 0 {
(out[refpixel] + out[refpixel + 2] + 1) >> 1
} else {
out[refpixel]
}
}
}
// Figure out how many difference bits we have to read for each pixel
let mut diff_bits: [u32; 4] = [0; 4];
if (optflags & OPT_SKIP) != 0 || pump.get_bits(1) == 0 {
let flags: [u32; 4] = [pump.get_bits(2), pump.get_bits(2), pump.get_bits(2), pump.get_bits(2)];
for i in 0..4 {
// The color is 0-Green 1-Blue 2-Red
let colornum: usize = if row % 2 != 0 { i >> 1 } else { ((i >> 1) + 2) % 3 };
match flags[i] {
0 => {
diff_bits[i] = diff_bits_mode[colornum][0];
}
1 => {
diff_bits[i] = diff_bits_mode[colornum][0] + 1;
}
2 => {
diff_bits[i] = diff_bits_mode[colornum][0] - 1;
}
3 => {
diff_bits[i] = pump.get_bits(4);
}
_ => {}
}
diff_bits_mode[colornum][0] = diff_bits_mode[colornum][1];
diff_bits_mode[colornum][1] = diff_bits[i];
if diff_bits[i] > bit_depth + 1 {
panic!("SRW Decoder: Too many difference bits. File corrupted?");
}
}
}
// Actually read the differences and write them to the pixels
for i in 0..16 {
let len = diff_bits[i >> 2];
let mut diff = pump.get_ibits_sextended(len);
diff = diff * (scale * 2 + 1) + scale;
// Apply the diff to pixels 0 2 4 6 8 10 12 14 1 3 5 7 9 11 13 15
let pos = if row % 2 != 0 {
((i & 0x7) << 1) + 1 - (i >> 3)
} else {
((i & 0x7) << 1) + (i >> 3)
} + img
+ col;
out[pos] = clampbits((out[pos] as i32) + diff, bit_depth);
}
}
}
out
}
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
if let Some(lens_id) = self.makernote.get_entry(SrwMakernote::LensModel) {
let lens_id = lens_id.force_u16(0);
let resolver = LensResolver::new()
.with_lens_id((lens_id.into(), 0))
.with_camera(&self.camera)
.with_mounts(&[NX_MOUNT.into()]);
return Ok(resolver.resolve());
}
Ok(None)
}
fn get_wb(&self) -> Result<[f32; 4]> {
let rggb_levels = fetch_tiff_tag!(self.makernote, SrwMakernote::SrwRGGBLevels);
let rggb_blacks = fetch_tiff_tag!(self.makernote, SrwMakernote::SrwRGGBBlacks);
if rggb_levels.count() != 4 || rggb_blacks.count() != 4 {
Err(RawlerError::DecoderFailed("SRW: RGGB Levels and Blacks don't have 4 elements".to_string()))
} else {
Ok([
(rggb_levels.force_u32(0) as f32 - rggb_blacks.force_u32(0) as f32) / 4096.0,
(rggb_levels.force_u32(1) as f32 - rggb_blacks.force_u32(1) as f32) / 4096.0,
(rggb_levels.force_u32(3) as f32 - rggb_blacks.force_u32(3) as f32) / 4096.0,
f32::NAN,
])
}
}
/// Extract blacklevel
/// Ironically, the data is already black level subtracted, but the
/// WB coeffs are not. So we can return 0 here. The black level
/// is subtracted in the get_wb() function.
fn get_blacklevel(&self) -> Result<[u32; 4]> {
Ok([0, 0, 0, 0])
/*
let rggb_blacks = fetch_tiff_tag!(self.makernote, SrwMakernote::SrwRGGBBlacks);
if rggb_blacks.count() != 4 {
Err(RawlerError::General("SRW: RGGB Blacks don't have 4 elements".to_string()))
} else {
Ok([
rggb_blacks.force_u16(0),
rggb_blacks.force_u16(1),
rggb_blacks.force_u16(2),
rggb_blacks.force_u16(3),
])
}
*/
}
}
crate::tags::tiff_tag_enum!(SrwMakernote);
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum SrwMakernote {
LensModel = 0xA003,
SrwRGGBLevels = 0xA021,
SrwRGGBBlacks = 0xA028,
}
+177
View File
@@ -0,0 +1,177 @@
use image::{DynamicImage, ImageBuffer, Rgb};
use log::debug;
use serde::{Deserialize, Serialize};
use crate::analyze::FormatDump;
use crate::decompressors::ljpeg::*;
use crate::exif::Exif;
use crate::formats::tiff::reader::TiffReader;
use crate::formats::tiff::{Entry, GenericTiffReader, Rational, Value};
use crate::imgop::{Dim2, Rect};
use crate::lens::{LensDescription, LensResolver};
use crate::packed::decode_16le;
use crate::pixarray::PixU16;
use crate::rawsource::RawSource;
use crate::tags::{DngTag, ExifTag, TiffCommonTag};
use crate::{RawImage, RawLoader, alloc_image_ok};
use crate::{RawlerError, Result};
use super::{BlackLevel, CFAConfig, Camera, Decoder, FormatHint, RawDecodeParams, RawMetadata, RawPhotometricInterpretation, WhiteLevel};
/// 3FR format encapsulation for analyzer
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TfrFormat {
tiff: GenericTiffReader,
}
#[derive(Debug, Clone)]
pub struct TfrDecoder<'a> {
camera: Camera,
#[allow(unused)]
rawloader: &'a RawLoader,
tiff: GenericTiffReader,
}
impl<'a> TfrDecoder<'a> {
pub fn new(_file: &RawSource, tiff: GenericTiffReader, rawloader: &'a RawLoader) -> Result<TfrDecoder<'a>> {
debug!("3FR decoder choosen");
let camera = rawloader.check_supported(tiff.root_ifd())?;
//let makernotes = new_makernote(file, 8).map_err(|ioerr| RawlerError::with_io_error("load 3FR makernotes", file.path(), ioerr))?;
Ok(TfrDecoder {
camera,
tiff,
rawloader,
// makernotes,
})
}
}
impl<'a> Decoder for TfrDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let raw = self
.tiff
.find_first_ifd_with_tag(TiffCommonTag::WhiteLevel)
.ok_or_else(|| RawlerError::DecoderFailed(format!("Failed to find a IFD with WhilteLevel tag")))?;
let whitelevel = raw
.get_entry(TiffCommonTag::WhiteLevel)
.map(|tag| WhiteLevel::new(vec![tag.force_u16(0) as u32]));
let blacklevel = raw
.get_entry(TiffCommonTag::BlackLevels)
.map(|tag| BlackLevel::new(&[tag.force_u16(0)], 1, 1, 1));
let width = fetch_tiff_tag!(raw, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(raw, TiffCommonTag::ImageLength).force_usize(0);
let offset = fetch_tiff_tag!(raw, TiffCommonTag::StripOffsets).force_usize(0);
let src = file.subview_until_eof(offset as u64)?;
let image = if self.camera.find_hint("uncompressed") {
decode_16le(src, width, height, dummy)
} else {
self.decode_compressed(src, width, height, dummy)?
};
let crop = Rect::from_tiff(raw).or_else(|| self.camera.crop_area.map(|area| Rect::new_with_borders(Dim2::new(width, height), &area)));
//crate::devtools::dump_image_u16(&image.data, width, height, "/tmp/tfrdump.pnm");
let cpp = 1;
let photometric = RawPhotometricInterpretation::Cfa(CFAConfig::new_from_camera(&self.camera));
let mut img = RawImage::new(self.camera.clone(), image, cpp, self.get_wb()?, photometric, blacklevel, whitelevel, dummy);
img.crop_area = crop;
Ok(img)
}
fn full_image(&self, file: &RawSource, params: &RawDecodeParams) -> Result<Option<DynamicImage>> {
if params.image_index != 0 {
return Ok(None);
}
let root_ifd = &self.tiff.root_ifd();
let buf = root_ifd
.singlestrip_data_rawsource(file)
.map_err(|e| RawlerError::DecoderFailed(format!("Failed to get strip data: {}", e)))?;
let compression = root_ifd.get_entry(TiffCommonTag::Compression).ok_or("Missing tag")?.force_usize(0);
let width = fetch_tiff_tag!(root_ifd, TiffCommonTag::ImageWidth).force_usize(0);
let height = fetch_tiff_tag!(root_ifd, TiffCommonTag::ImageLength).force_usize(0);
if compression == 1 {
Ok(Some(DynamicImage::ImageRgb8(
ImageBuffer::<Rgb<u8>, Vec<u8>>::from_raw(width as u32, height as u32, buf.to_vec()).unwrap(),
)))
} else {
let img = image::load_from_memory_with_format(buf, image::ImageFormat::Jpeg)
.map_err(|err| RawlerError::DecoderFailed(format!("Failed to read JPEG: {:?}", err)))?;
Ok(Some(img))
}
}
fn format_dump(&self) -> FormatDump {
FormatDump::Tfr(TfrFormat { tiff: self.tiff.clone() })
}
fn format_hint(&self) -> FormatHint {
FormatHint::TFR
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
let exif = Exif::new(self.tiff.root_ifd())?;
let mut mdata = RawMetadata::new_with_lens(&self.camera, exif, self.get_lens_description()?.cloned());
// Read Unique ID
if let Some(Entry {
value: Value::Byte(unique_id), ..
}) = self.tiff.root_ifd().get_entry(DngTag::RawDataUniqueID)
{
if let Ok(id) = unique_id.as_slice().try_into() {
mdata.unique_image_id = Some(u128::from_le_bytes(id));
}
}
Ok(mdata)
}
fn xpacket(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<Option<Vec<u8>>> {
match self.tiff.root_ifd().get_entry(TiffCommonTag::Xmp) {
Some(Entry { value: Value::Byte(buf), .. }) => Ok(Some(buf.clone())),
_ => Ok(None),
}
}
}
impl<'a> TfrDecoder<'a> {
/// Get lens description by analyzing TIFF tags and makernotes
fn get_lens_description(&self) -> Result<Option<&'static LensDescription>> {
if let Some(exif) = self.tiff.root_ifd().get_sub_ifd(TiffCommonTag::ExifIFDPointer) {
let lens_make = exif.get_entry(ExifTag::LensMake).and_then(|entry| entry.as_string());
let lens_model = exif.get_entry(ExifTag::LensModel).and_then(|entry| entry.as_string());
let focal_len = match exif.get_entry(ExifTag::FocalLength) {
Some(Entry { value: Value::Rational(x), .. }) => x.get(0).cloned(),
Some(Entry { value: Value::Short(x), .. }) => x.get(0).copied().map(Rational::from),
_ => None,
};
let resolver = LensResolver::new()
.with_camera(&self.camera)
.with_lens_make(lens_make)
.with_lens_model(lens_model)
.with_focal_len(focal_len)
.with_mounts(&["x-mount".into()]);
return Ok(resolver.resolve());
}
Ok(None)
}
fn get_wb(&self) -> Result<[f32; 4]> {
let levels = fetch_tiff_tag!(self.tiff, TiffCommonTag::AsShotNeutral);
assert_eq!(levels.count(), 3);
Ok([1.0 / levels.force_f32(0), 1.0 / levels.force_f32(1), 1.0 / levels.force_f32(2), f32::NAN])
}
fn decode_compressed(&self, src: &[u8], width: usize, height: usize, dummy: bool) -> Result<PixU16> {
let mut out = alloc_image_ok!(width, height, dummy);
let decompressor = LjpegDecompressor::new_full(src, true, false)?;
decompressor.decode(out.pixels_mut(), 0, width, width, height, dummy)?;
Ok(out)
}
}
+191
View File
@@ -0,0 +1,191 @@
use crate::bits::Endian;
use crate::bits::*;
use crate::buffer::PaddedBuf;
use crate::decoders::*;
use crate::decompressors::ljpeg::LjpegDecompressor;
use crate::packed::*;
pub fn decode_unwrapped(file: &RawSource) -> Result<RawImageData> {
let buffer = file.subview_until_eof_padded(0)?;
let decoder = LEu16(&buffer, 0);
let width = LEu16(&buffer, 2) as usize;
let height = LEu16(&buffer, 4) as usize;
let data = &buffer[6..];
if width > 64 || height > 64 {
panic!("Trying an image larger than 64x64");
}
match decoder {
0 => {
let table = {
let mut t: [u16; 256] = [0; 256];
for i in 0..256 {
t[i] = LEu16(data, i * 2);
}
LookupTable::new(&t)
};
let data = &data[512..];
Ok(RawImageData::Integer(decode_8bit_wtable(data, &table, width, height, false).into_inner()))
}
1 => Ok(RawImageData::Integer(decode_10le_lsb16(data, width, height, false).into_inner())),
2 => Ok(RawImageData::Integer(decode_10be(data, width, height, false).into_inner())),
3 => Ok(RawImageData::Integer(decode_12be(data, width, height, false).into_inner())),
4 => Ok(RawImageData::Integer(decode_12be_msb16(data, width, height, false).into_inner())),
5 => Ok(RawImageData::Integer(decode_12le_16bitaligned(data, width, height, false).into_inner())),
6 => Ok(RawImageData::Integer(decode_12be_msb32(data, width, height, false).into_inner())),
7 => Ok(RawImageData::Integer(decode_12le_wcontrol(data, width, height, false).into_inner())),
8 => Ok(RawImageData::Integer(decode_12be_wcontrol(data, width, height, false).into_inner())),
9 => Ok(RawImageData::Integer(decode_12be_interlaced(data, width, height, false).into_inner())),
10 => Ok(RawImageData::Integer(decode_12be_interlaced_unaligned(data, width, height, false).into_inner())),
11 => Ok(RawImageData::Integer(decode_12le(data, width, height, false).into_inner())),
12 => Ok(RawImageData::Integer(decode_12le_unpacked(data, width, height, false).into_inner())),
13 => Ok(RawImageData::Integer(decode_12be_unpacked(data, width, height, false).into_inner())),
14 => Ok(RawImageData::Integer(
decode_12be_unpacked_left_aligned(data, width, height, false).into_inner(),
)),
15 => Ok(RawImageData::Integer(
decode_12le_unpacked_left_aligned(data, width, height, false).into_inner(),
)),
16 => Ok(RawImageData::Integer(decode_14le_unpacked(data, width, height, false).into_inner())),
17 => Ok(RawImageData::Integer(decode_14be_unpacked(data, width, height, false).into_inner())),
18 => Ok(RawImageData::Integer(decode_16le(data, width, height, false).into_inner())),
19 => Ok(RawImageData::Integer(decode_16le_skiplines(data, width, height, false).into_inner())),
20 => Ok(RawImageData::Integer(decode_16be(data, width, height, false).into_inner())),
21 => Ok(RawImageData::Integer(arw::ArwDecoder::decode_arw1(data, width, height, false).into_inner())),
22 => {
let mut curve: [usize; 6] = [0, 0, 0, 0, 0, 4095];
for i in 0..4 {
curve[i + 1] = (LEu16(data, i * 2) & 0xfff) as usize;
}
let curve = arw::ArwDecoder::calculate_curve(curve);
let data = &data[8..];
Ok(RawImageData::Integer(
arw::ArwDecoder::decode_arw2(data, width, height, &curve, false).into_inner(),
))
}
23 => {
let key = LEu32(data, 0);
let length = LEu16(data, 4) as usize;
let data = &data[10..];
if length > 5000 {
panic!("Trying an SRF style image that's too big");
}
let image_data = arw::ArwDecoder::sony_decrypt(data, 0, length, key)?;
Ok(RawImageData::Integer(decode_16be(&image_data, width, height, false).into_inner()))
}
24 => Ok(RawImageData::Integer(
orf::OrfDecoder::decode_compressed(&buffer, width, height, 12, false).into_inner(),
)),
25 => {
let loffsets = data;
let data = &data[height * 4..];
Ok(RawImageData::Integer(
srw::SrwDecoder::decode_srw1(data, loffsets, width, height, false).into_inner(),
))
}
26 => Ok(RawImageData::Integer(srw::SrwDecoder::decode_srw2(data, width, height, false).into_inner())),
27 => Ok(RawImageData::Integer(srw::SrwDecoder::decode_srw3(data, width, height, false).into_inner())),
28 => Ok(RawImageData::Integer(kdc::KdcDecoder::decode_dc120(data, width, height, false).into_inner())),
29 => Ok(RawImageData::Integer(
rw2::v4decompressor::decode_panasonic_v4(data, width, height, false, false).into_inner(),
)),
30 => Ok(RawImageData::Integer(
rw2::v4decompressor::decode_panasonic_v4(data, width, height, true, false).into_inner(),
)),
31 => {
let table = {
let mut t = [0u16; 1024];
for i in 0..1024 {
t[i] = LEu16(data, i * 2);
}
LookupTable::new(&t)
};
let data = &data[2048..];
Ok(RawImageData::Integer(
dcr::DcrDecoder::decode_kodak65000(data, &table, width, height, false).into_inner(),
))
}
32 => decode_ljpeg(data, width, height, false, false),
33 => decode_ljpeg(data, width, height, false, true),
34 => decode_ljpeg(data, width, height, true, false),
35 => decode_ljpeg(data, width, height, true, true),
36 => Ok(RawImageData::Integer(
pef::PefDecoder::do_decode(data, None, width, height, false).unwrap().into_inner(),
)),
37 => {
let huff = data;
let data = &data[64..];
Ok(RawImageData::Integer(
pef::PefDecoder::do_decode(data, Some((huff, Endian::Little)), width, height, false)
.unwrap()
.into_inner(),
))
}
38 => {
let huff = data;
let data = &data[64..];
Ok(RawImageData::Integer(
pef::PefDecoder::do_decode(data, Some((huff, Endian::Big)), width, height, false)
.unwrap()
.into_inner(),
))
}
39 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, false, 0, width, height, false).unwrap().into_inner(),
)),
40 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, false, 1, width, height, false).unwrap().into_inner(),
)),
41 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, false, 2, width, height, false).unwrap().into_inner(),
)),
42 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, true, 0, width, height, false).unwrap().into_inner(),
)),
43 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, true, 1, width, height, false).unwrap().into_inner(),
)),
44 => Ok(RawImageData::Integer(
crw::CrwDecoder::do_decode(file, true, 2, width, height, false).unwrap().into_inner(),
)),
45 => Ok(RawImageData::Integer(
mos::MosDecoder::do_decode(data, false, width, height, false).unwrap().into_inner(),
)),
46 => Ok(RawImageData::Integer(
mos::MosDecoder::do_decode(data, true, width, height, false).unwrap().into_inner(),
)),
//47 => Ok(RawImageData::Integer(iiq::IiqDecoder::decode_compressed(data, height*4, 0, width, height, false).into_inner())),
48 => decode_nef(data, width, height, Endian::Little, 12),
49 => decode_nef(data, width, height, Endian::Little, 14),
50 => decode_nef(data, width, height, Endian::Big, 12),
51 => decode_nef(data, width, height, Endian::Big, 14),
52 => {
let coeffs = [LEf32(data, 0), LEf32(data, 4), LEf32(data, 8), LEf32(data, 12)];
let data = PaddedBuf::new_owned(data[16..].to_vec(), data.len() - 16);
Ok(RawImageData::Integer(
nef::NefDecoder::decode_snef_compressed(&data, coeffs, width, height, false).into_inner(),
))
}
_ => Err("No such decoder".into()),
}
}
fn decode_ljpeg(src: &[u8], width: usize, height: usize, dng_bug: bool, csfix: bool) -> Result<RawImageData> {
let mut out = vec![0u16; width * height];
let decompressor = LjpegDecompressor::new_full(src, dng_bug, csfix)?;
decompressor.decode(&mut out, 0, width, width, height, false)?;
Ok(RawImageData::Integer(out))
}
fn decode_nef(data: &[u8], width: usize, height: usize, endian: Endian, bps: usize) -> Result<RawImageData> {
let meta = data;
let data = &data[4096..];
Ok(RawImageData::Integer(
nef::NefDecoder::do_decode(data, meta, endian, width, height, bps, false).unwrap().into_inner(),
))
}
+158
View File
@@ -0,0 +1,158 @@
use std::io::Cursor;
use crate::bits::*;
use crate::decoders::*;
pub fn is_x3f(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => buf[0..4] == b"FOVb"[..],
Err(_) => false,
}
}
#[derive(Debug, Clone)]
struct X3fFile {
#[allow(dead_code)]
dirs: Vec<X3fDirectory>,
images: Vec<X3fImage>,
}
#[derive(Debug, Clone)]
struct X3fDirectory {
offset: usize,
#[allow(dead_code)]
len: usize,
id: String,
}
#[derive(Debug, Clone)]
struct X3fImage {
typ: usize,
format: usize,
width: usize,
height: usize,
#[allow(dead_code)]
pitch: usize,
doffset: usize,
}
impl X3fFile {
fn new(file: &RawSource) -> Result<X3fFile> {
let buf = file.as_vec()?;
let offset = LEu32(&buf, buf.len() - 4) as usize;
let data = &buf[offset..];
let version = LEu32(data, 4);
if version < 0x00020000 {
return Err(format_args!("X3F: Directory version too old {}", version).into());
}
let entries = LEu32(data, 8) as usize;
let mut dirs = Vec::new();
let mut images = Vec::new();
for i in 0..entries {
let dir = X3fDirectory::new(data, 12 + i * 12)?;
if dir.id == "IMA2" {
let img = X3fImage::new(&buf, dir.offset)?;
images.push(img);
}
dirs.push(dir);
}
Ok(X3fFile { dirs, images })
}
}
impl X3fDirectory {
fn new(buf: &[u8], offset: usize) -> Result<X3fDirectory> {
let data = &buf[offset..];
let off = LEu32(data, 0) as usize;
let len = LEu32(data, 4) as usize;
let name = String::from_utf8_lossy(&data[8..12]).to_string();
Ok(X3fDirectory { offset: off, len, id: name })
}
}
impl X3fImage {
fn new(buf: &[u8], offset: usize) -> Result<X3fImage> {
let data = &buf[offset..];
Ok(X3fImage {
typ: LEu32(data, 8) as usize,
format: LEu32(data, 12) as usize,
width: LEu32(data, 16) as usize,
height: LEu32(data, 20) as usize,
pitch: LEu32(data, 24) as usize,
doffset: offset + 28,
})
}
}
#[derive(Debug, Clone)]
pub struct X3fDecoder<'a> {
rawloader: &'a RawLoader,
dir: X3fFile,
}
impl<'a> X3fDecoder<'a> {
pub fn new(file: &RawSource, rawloader: &'a RawLoader) -> Result<X3fDecoder<'a>> {
let dir = X3fFile::new(file)?;
Ok(X3fDecoder { rawloader, dir })
}
}
impl<'a> Decoder for X3fDecoder<'a> {
fn raw_image(&self, file: &RawSource, _params: &RawDecodeParams, dummy: bool) -> Result<RawImage> {
let buffer = file.as_vec()?;
let caminfo = self
.dir
.images
.iter()
.find(|i| i.typ == 2 && i.format == 0x12)
.ok_or("X3F: Couldn't find camera info")?;
let data = &buffer[caminfo.doffset + 6..];
if data[0..4] != b"Exif"[..] {
return Err("X3F: Couldn't find EXIF info".into());
}
let tiff = IFD::new(&mut Cursor::new(&buffer), (caminfo.doffset + 12) as u32, 0, 0, Endian::Little, &[]).unwrap();
let camera = self.rawloader.check_supported(&tiff)?;
let imginfo = self.dir.images.iter().find(|i| i.typ == 1 || i.typ == 3).ok_or("X3F: Couldn't find image")?;
let width = imginfo.width;
let height = imginfo.height;
let offset = imginfo.doffset;
let src = &buffer[offset..];
let image = match imginfo.format {
35 => self.decode_compressed(src, width, height, dummy)?,
x => return Err(format_args!("X3F Don't know how to decode format {}", x).into()),
};
let cpp = 3;
let photometric = RawPhotometricInterpretation::LinearRaw;
Ok(RawImage::new(camera, image, cpp, self.get_wb()?, photometric, None, None, dummy))
}
fn format_dump(&self) -> FormatDump {
todo!()
}
fn format_hint(&self) -> FormatHint {
FormatHint::X3F
}
fn raw_metadata(&self, _file: &RawSource, _params: &RawDecodeParams) -> Result<RawMetadata> {
todo!()
}
}
impl<'a> X3fDecoder<'a> {
fn get_wb(&self) -> Result<[f32; 4]> {
Ok([f32::NAN, f32::NAN, f32::NAN, f32::NAN])
}
fn decode_compressed(&self, _buf: &[u8], _width: usize, _height: usize, _dummy: bool) -> Result<PixU16> {
Err("X3F decoding not implemented yet".into())
}
}