quickraw 0.1.6

A pure rust library to handle camera raw files
Documentation
use super::decode_utility::bit_pump::*;
use super::*;
use once_cell::sync::Lazy;

pub(super) struct General {
    info: quickexif::ParsedInfo,
}

pub(super) static THUMBNAIL_RULE: Lazy<quickexif::ParsingRule> = Lazy::new(|| {
    quickexif::describe_rule!(tiff {
        0x0112 / orientation
        0x002e / thumbnail(thumbnail_len)
    })
});

pub(super) static IMAGE_RULE: Lazy<quickexif::ParsingRule> = Lazy::new(|| {
    quickexif::describe_rule!(tiff {
        0x0002 / width
        0x0003 / height
        0x0009 / cfa_pattern
        0x000a / bps
        0x001c / black_level_r
        0x001d / black_level_g
        0x001e / black_level_b
        0x0024 / white_balance_r
        0x0025 / white_balance_g
        0x0026 / white_balance_b
        0x0118 / strip
        0x0117 / strip_len
        0x002f? / crop_top
        0x0030? / crop_left
        0x0031? / crop_bottom
        0x0032? / crop_right
        0x0112 / orientation
        0x002e {
            offset + 12 {
                tiff {
                    0x8769 {
                        0x927c {
                            offset + 12 {
                                0x004b / cropped_width
                                0x004c / cropped_height
                            }
                        }
                    }
                }
            }
        }
    })
});

impl RawDecoder for General {
    fn new(info: quickexif::ParsedInfo) -> Self {
        General { info }
    }
    fn get_info(&self) -> &quickexif::ParsedInfo {
        &self.info
    }

    fn get_crop(&self) -> Option<Crop> {
        let x = self.info.u32("crop_left").ok()?;
        let y = self.info.u32("crop_top").ok()?;
        let right = self.info.u32("crop_right").ok()?;
        let bottom = self.info.u32("crop_bottom").ok()?;

        Some(Crop {
            x,
            y,
            width: right - x,
            height: bottom - y,
        })
    }
    fn decode_with_preprocess(&self, buffer: &[u8]) -> Result<Vec<u16>, DecodingError> {
        let image = load_raw(&self.info, buffer)?;
        let black_level = self.info.u16("black_level_r")?;
        let bps_scale = self.get_bps_scale()?;
        Ok(image
            .iter()
            .map(|x| bps_scale.saturating_mul(x.saturating_sub(black_level)))
            .collect())
    }
    fn get_cfa_pattern(&self) -> Result<CFAPattern, DecodingError> {
        let cfa_pattern = self.info.u16("cfa_pattern")?;
        let result = match cfa_pattern {
            1 => CFAPattern::RGGB,
            2 => CFAPattern::GRBG,
            3 => CFAPattern::GBRG,
            4 => CFAPattern::BGGR,
            _ => CFAPattern::RGGB,
        };
        Ok(result)
    }
    fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> {
        let offset = self.info.usize("thumbnail")?;
        let len = self.info.usize("thumbnail_len")?;
        Ok(&buffer[offset..offset + len])
    }
}

fn load_raw(info: &quickexif::ParsedInfo, buffer: &[u8]) -> Result<Vec<u16>, DecodingError> {
    const SPLIT: bool = true;
    const BLOCK_LINES: usize = 5;

    let width = info.usize("width")?;
    let height = info.usize("height")?;
    let offset = info.usize("strip")?;

    let buf = &buffer[offset..];
    let mut out: Vec<u16> = vec![0u16; width * height];

    out.chunks_exact_mut(width * BLOCK_LINES)
        .enumerate()
        .for_each(|(index, out)| {
            let row = index * BLOCK_LINES;

            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(out)
}