├── tests ├── README.md └── test_export.rs ├── .gitignore ├── src ├── maker │ ├── decode_utility │ │ ├── mod.rs │ │ ├── lookup_table.rs │ │ ├── byte_stream.rs │ │ ├── huffman.rs │ │ ├── bit_pump.rs │ │ └── ljpeg │ │ │ ├── decompressors.rs │ │ │ └── mod.rs │ ├── mod.rs │ ├── fujifilm.rs │ ├── panasonic.rs │ ├── selector.rs │ ├── utility.rs │ ├── sony.rs │ ├── adobe.rs │ ├── nikon.rs │ └── olympus.rs ├── pass │ ├── general.rs │ ├── mod.rs │ ├── demosaicing │ │ ├── mod.rs │ │ ├── enhanced_linear.rs │ │ └── linear.rs │ └── color.rs ├── utility.rs ├── decode.rs ├── export.rs ├── lib.rs ├── lib_c.rs └── lib_wasm.rs ├── Cargo.toml ├── quickraw-header.h ├── README.md └── supported-models.md /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests for quickraw 2 | 3 | Please notice that sample raw files needed are not included in the repository. 4 | 5 | You can download your own tests files in [https://raw.pixls.us/](https://raw.pixls.us/). 6 | 7 | Sample file list: 8 | * sample0.ARW -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .DS_Store 13 | *.ARW 14 | *.dng 15 | 16 | /libquickraw.xcframework/ 17 | 18 | /pkg/ -------------------------------------------------------------------------------- /src/maker/decode_utility/mod.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | pub(in super::super) mod huffman; 4 | pub(in super::super) mod bit_pump; 5 | pub(in super::super) mod byte_stream; 6 | pub(in super::super) mod lookup_table; 7 | pub(in super::super) mod ljpeg; 8 | 9 | #[derive(Error, Debug)] 10 | pub enum DecodingError { 11 | #[error("No marker found inside rest of buffer.")] 12 | ByteStreamNoMarkerFound, 13 | #[error("LJpeg constructor error: {0}")] 14 | LJpegErrorConstructor(String), 15 | #[error("LJpegDecompressing error: {0}")] 16 | LJpegError(String), 17 | } -------------------------------------------------------------------------------- /tests/test_export.rs: -------------------------------------------------------------------------------- 1 | use quickraw::{data, export}; 2 | 3 | #[test] 4 | fn test_export() { 5 | let options = export::Options::new(data::GAMMA_SRGB, &data::XYZ2SRGB, false); 6 | let (image, width, height) = 7 | export::load_image_from_file("tests/sample0.ARW", options).unwrap(); 8 | 9 | assert_eq!( 10 | "73011456 6048 4024", 11 | format!("{} {} {}", image.len(), width, height) 12 | ) 13 | } 14 | 15 | #[test] 16 | fn test_dng0() { 17 | let options = export::Options::new(data::GAMMA_SRGB, &data::XYZ2SRGB, false); 18 | let (image, width, height) = 19 | export::load_image_from_file("tests/sample1.dng", options).unwrap(); 20 | println!("{} {} {}", image.len(), width, height); 21 | } 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quickraw" 3 | version = "0.2.1-alpha.1" 4 | edition = "2021" 5 | description = "A pure rust library to handle camera raw files" 6 | keywords = ["raw", "camera"] 7 | documentation = "https://docs.rs/quickraw" 8 | repository = "https://github.com/qdwang/quickraw" 9 | license = "LGPL-2.1" 10 | exclude = ["tests/"] 11 | 12 | [dependencies] 13 | fn-util = { version = "0.1" } 14 | thiserror = "1" 15 | anyhow = "1" 16 | once_cell = "1" 17 | phf = { version = "0.10", features = ["macros"] } 18 | quickexif = "0.1" 19 | 20 | # only for wasm target 21 | wasm-bindgen = { version = "0.2", optional = true } 22 | image = { version = "0.24", default-features = false, features = ["jpeg"], optional = true } 23 | 24 | [features] 25 | wasm = ["wasm-bindgen", "image"] 26 | 27 | [package.metadata.docs.rs] 28 | all-features = true 29 | rustdoc-args = ["--cfg", "docsrs"] 30 | 31 | [profile.release] 32 | codegen-units = 1 33 | lto = "fat" 34 | 35 | [lib] 36 | crate-type = ["lib", "staticlib", "cdylib"] 37 | -------------------------------------------------------------------------------- /src/pass/general.rs: -------------------------------------------------------------------------------- 1 | 2 | #[inline(always)] 3 | pub fn u16rgb_to_i32rgb(iter: impl Iterator) -> impl Iterator { 4 | iter.map(|[r, g, b]| [r as i32, g as i32, b as i32]) 5 | } 6 | 7 | #[inline(always)] 8 | pub fn u16rgb_to_u8rgb(iter: impl Iterator) -> impl Iterator { 9 | iter.map(|[r, g, b]| [(r >> 8) as u8, (g >> 8) as u8, (b >> 8) as u8]) 10 | } 11 | 12 | #[inline(always)] 13 | pub fn u16rgb_to_u16rgba(iter: impl Iterator) -> impl Iterator { 14 | iter.map(|[r, g, b]| [r, g, b, u16::MAX]) 15 | } 16 | 17 | 18 | // #[inline(always)] 19 | // pub fn sub_black_level( 20 | // iter: impl Iterator, 21 | // black_level: u16, 22 | // ) -> impl Iterator { 23 | // iter.map(move |v| v.saturating_sub(black_level)) 24 | // } 25 | 26 | // #[inline(always)] 27 | // pub fn level_scale_up( 28 | // iter: impl Iterator, 29 | // factor: u16, 30 | // ) -> impl Iterator { 31 | // iter.map(move |v| v.saturating_mul(factor)) 32 | // } 33 | -------------------------------------------------------------------------------- /quickraw-header.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct RustVec { 7 | unsigned char *ptr; 8 | unsigned int len; 9 | unsigned int capacity; 10 | } RustVec; 11 | 12 | typedef struct BasicInfo { 13 | char *exif; 14 | struct RustVec thumbnail; 15 | unsigned char orientation; 16 | } BasicInfo; 17 | 18 | typedef struct QuickrawResponse_BasicInfo { 19 | bool has_error; 20 | char *error_msg; 21 | struct BasicInfo content; 22 | } QuickrawResponse_BasicInfo; 23 | 24 | typedef struct Image { 25 | struct RustVec data; 26 | unsigned int width; 27 | unsigned int height; 28 | } Image; 29 | 30 | typedef struct QuickrawResponse_Image { 31 | bool has_error; 32 | char *error_msg; 33 | struct Image content; 34 | } QuickrawResponse_Image; 35 | 36 | struct QuickrawResponse_BasicInfo quickraw_load_basicinfo(char *cpath); 37 | 38 | void quickraw_free_basicinfo(struct QuickrawResponse_BasicInfo response); 39 | 40 | struct QuickrawResponse_Image quickraw_load_image(char *cpath); 41 | 42 | void quickraw_free_image(struct QuickrawResponse_Image response); 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickraw 2 | [![](https://img.shields.io/crates/v/quickraw?style=flat-square)](https://crates.io/crates/quickraw) 3 | [![](https://img.shields.io/github/v/release/qdwang/quickraw?label=cli-release&style=flat-square)](https://github.com/qdwang/quickraw/releases/latest) 4 | 5 | A pure rust library to handle camera raw files. 6 | 7 | **This library uses integer data in process to boost the speed, it may not get a very precise result like other softwares that uses high precision float point data.** 8 | 9 | Checkout docs here: [https://docs.rs/quickraw](https://docs.rs/quickraw) 10 | 11 | ## Supported models 12 | Please check: [supported-models.md](supported-models.md) 13 | 14 | ## References 15 | * Raw file process logic from [https://www.odelama.com/photo/Developing-a-RAW-Photo-by-hand/](https://www.odelama.com/photo/Developing-a-RAW-Photo-by-hand/) 16 | * Raw file process logic from [https://github.com/LibRaw/LibRaw](https://github.com/LibRaw/LibRaw) 17 | * Some decoding methods from [https://github.com/pedrocr/rawloader](https://github.com/pedrocr/rawloader) 18 | * Sample files for testing from [https://raw.pixls.us/](https://raw.pixls.us/) -------------------------------------------------------------------------------- /src/maker/decode_utility/lookup_table.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub(in super::super) struct LookupTable { 3 | table: Vec<(u16, u16, u16)>, 4 | } 5 | 6 | impl LookupTable { 7 | pub(in super::super) fn new(table: &[u16]) -> LookupTable { 8 | let mut tbl = vec![(0, 0, 0); table.len()]; 9 | for i in 0..table.len() { 10 | let center = table[i]; 11 | let lower = if i > 0 { table[i - 1] } else { center }; 12 | let upper = if i < (table.len() - 1) { table[i + 1] } else { center }; 13 | let base = if center == 0 { 14 | 0 15 | } else { 16 | center - ((upper - lower + 2) / 4) 17 | }; 18 | let delta = upper - lower; 19 | tbl[i] = (center, base, delta); 20 | } 21 | LookupTable { table: tbl } 22 | } 23 | 24 | // pub(in super::super) fn lookup(&self, value: u16) -> u16 { 25 | // let (val, _, _) = self.table[value as usize]; 26 | // val 27 | // } 28 | 29 | #[inline(always)] 30 | pub(in super::super) fn dither(&self, value: u16, rand: &mut u32) -> u16 { 31 | let (_, sbase, sdelta) = self.table[value as usize]; 32 | let base = sbase as u32; 33 | let delta = sdelta as u32; 34 | let pixel = base + ((delta * (*rand & 2047) + 1024) >> 12); 35 | *rand = 15700 * (*rand & 65535) + (*rand >> 16); 36 | pixel as u16 37 | } 38 | } -------------------------------------------------------------------------------- /src/pass/mod.rs: -------------------------------------------------------------------------------- 1 | mod color; 2 | mod demosaicing; 3 | mod general; 4 | 5 | pub use color::*; 6 | pub use demosaicing::*; 7 | pub use general::*; 8 | 9 | #[macro_export] 10 | macro_rules! iters_to_vec { 11 | [$iter:ident $($body:tt)*] => { 12 | iters_to_vec!(@acc($iter) $($body)*) 13 | }; 14 | 15 | // go to match accumulator 16 | [@acc($($x:tt)*) [$target:expr] { $($rules:tt)* } $($body:tt)*] => { 17 | iters_to_vec!(@acc($($x)*) @body($($body)*) @match($target) @match_acc() $($rules)*) 18 | }; 19 | 20 | // if accumulator 21 | [@acc($($x:tt)*) [. $fn:ident ( $($params:tt)* ) $($cond:tt)* ] $($body:tt)*] => { 22 | if $($cond)* { 23 | iters_to_vec!(@acc($fn($($x)*, $($params)*)) $($body)*) 24 | } else { 25 | iters_to_vec!(@acc($($x)*) $($body)*) 26 | } 27 | }; 28 | 29 | [@acc($($x:tt)*) .. $fn:ident ( $($params:tt)* ) $($body:tt)*] => { 30 | iters_to_vec!(@acc($($x)* . $fn($($params)*)) $($body)*) 31 | }; 32 | [@acc($($x:tt)*) . $fn:ident ( $($params:tt)* ) $($body:tt)*] => { 33 | iters_to_vec!(@acc($fn($($x)*, $($params)*)) $($body)*) 34 | }; 35 | [@acc($($x:tt)*)] => { 36 | $($x)* . collect::>() 37 | }; 38 | 39 | // match accumulator 40 | [@acc($($x:tt)*) @body($($body:tt)*) @match($target:expr) @match_acc($($r:tt)*) $(,)? $p:pat => . $fn:ident ( $($params:tt)* ) $($rules:tt)* ] => { 41 | iters_to_vec!(@acc($($x)*) @body($($body)*) @match($target) @match_acc($($r)* $p => iters_to_vec!(@acc($fn($($x)*, $($params)*)) $($body)*),) $($rules)* ) 42 | }; 43 | [@acc($($x:tt)*) @body($($body:tt)*) @match($target:expr) @match_acc($($r:tt)*)] => { 44 | match $target { 45 | $($r)* 46 | } 47 | } 48 | } 49 | 50 | pub use iters_to_vec; 51 | -------------------------------------------------------------------------------- /src/maker/decode_utility/byte_stream.rs: -------------------------------------------------------------------------------- 1 | use super::super::utility::GetNumFromBytes; 2 | 3 | use super::DecodingError; 4 | 5 | #[derive(Debug, Copy, Clone)] 6 | pub(in super::super) struct ByteStream<'a> { 7 | buffer: &'a [u8], 8 | pos: usize, 9 | is_le: bool, 10 | } 11 | 12 | impl<'a> ByteStream<'a> { 13 | pub(in super::super) fn new(src: &'a [u8], is_le: bool) -> ByteStream { 14 | ByteStream { 15 | buffer: src, 16 | pos: 0, 17 | is_le, 18 | } 19 | } 20 | 21 | #[inline(always)] 22 | pub(in super::super) fn get_pos(&self) -> usize { 23 | self.pos 24 | } 25 | 26 | #[inline(always)] 27 | pub(in super::super) fn peek_u8(&self) -> u8 { 28 | self.buffer[self.pos] 29 | } 30 | #[inline(always)] 31 | pub(in super::super) fn get_u8(&mut self) -> u8 { 32 | let val = self.peek_u8(); 33 | self.pos += 1; 34 | val 35 | } 36 | 37 | #[inline(always)] 38 | pub(in super::super) fn peek_u16(&self) -> u16 { 39 | self.buffer.u16(self.is_le, self.pos) 40 | } 41 | #[inline(always)] 42 | pub(in super::super) fn get_u16(&mut self) -> u16 { 43 | let val = self.peek_u16(); 44 | self.pos += 2; 45 | val 46 | } 47 | 48 | #[inline(always)] 49 | pub(in super::super) fn consume_bytes(&mut self, num: usize) { 50 | self.pos += num 51 | } 52 | 53 | #[inline(always)] 54 | pub(in super::super) fn skip_to_marker(&mut self) -> Result { 55 | let mut skip_count = 0; 56 | while !(self.buffer[self.pos] == 0xFF && self.buffer[self.pos + 1] != 0 && self.buffer[self.pos + 1] != 0xFF) { 57 | self.pos += 1; 58 | skip_count += 1; 59 | if self.pos >= self.buffer.len() { 60 | return Err(DecodingError::ByteStreamNoMarkerFound); 61 | } 62 | } 63 | self.pos += 1; // Make the next byte the marker 64 | Ok(skip_count + 1) 65 | } 66 | } -------------------------------------------------------------------------------- /src/pass/demosaicing/mod.rs: -------------------------------------------------------------------------------- 1 | mod enhanced_linear; 2 | mod linear; 3 | 4 | #[inline(always)] 5 | pub fn none<'a>( 6 | iter: impl Iterator + 'a, 7 | ) -> impl Iterator + 'a { 8 | iter.map(|(_, v)| [v; 3]) 9 | } 10 | 11 | macro_rules! gen_linear { 12 | ($name:ident, $fn:expr) => { 13 | #[inline(always)] 14 | pub fn $name<'a>( 15 | iter: impl Iterator + 'a, 16 | image: &'a [u16], 17 | width: usize, 18 | height: usize, 19 | ) -> impl Iterator + 'a { 20 | iter.map(move |(i, v)| $fn(i, v, image, width, height)) 21 | } 22 | }; 23 | } 24 | 25 | gen_linear!(linear_rggb, linear::rggb); 26 | gen_linear!(linear_bggr, linear::bggr); 27 | gen_linear!(linear_grbg, linear::grbg); 28 | gen_linear!(linear_gbrg, linear::gbrg); 29 | gen_linear!(linear_xtrans0, linear::xtrans0); 30 | gen_linear!(linear_xtrans1, linear::xtrans1); 31 | 32 | gen_linear!(elinear_rggb, enhanced_linear::rggb); 33 | gen_linear!(elinear_bggr, enhanced_linear::bggr); 34 | gen_linear!(elinear_grbg, enhanced_linear::grbg); 35 | gen_linear!(elinear_gbrg, enhanced_linear::gbrg); 36 | 37 | #[inline(always)] 38 | pub(self) fn get_pixel(image: &[u16], i: usize) -> u16 { 39 | unsafe { *image.get_unchecked(i) } 40 | } 41 | #[inline(always)] 42 | pub(self) fn avg(image: &[u16], indexes: &[usize; N]) -> u16 { 43 | let mut sum = 0; 44 | for &i in indexes { 45 | sum += get_pixel(image, i) as u32; 46 | } 47 | 48 | (sum / N as u32) as u16 49 | } 50 | #[inline(always)] 51 | pub(self) fn bayer_pixel_info( 52 | i: usize, 53 | w: usize, 54 | h: usize, 55 | ) -> (bool, bool, bool, bool, bool, bool) { 56 | let x = i % w; 57 | let y = i / w; 58 | let is_top = y == 0; 59 | let is_left = x == 0; 60 | let is_bottom = y == h - 1; 61 | let is_right = x == w - 1; 62 | let is_column_even = x % 2 == 0; 63 | let is_row_even = y % 2 == 0; 64 | ( 65 | is_top, 66 | is_bottom, 67 | is_left, 68 | is_right, 69 | is_column_even, 70 | is_row_even, 71 | ) 72 | } 73 | -------------------------------------------------------------------------------- /src/pass/color.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | const BIT_SHIFT: u32 = 13u32; 4 | const CLIP_LIMIT_I32: i32 = 65535; 5 | const CLIP_RANGE: (i32, i32) = (0, CLIP_LIMIT_I32); 6 | 7 | #[inline(always)] 8 | pub fn white_balance_fix<'a>( 9 | iter: impl Iterator + 'a, 10 | white_balance: &'a [i32; 3], 11 | ) -> impl Iterator + 'a { 12 | iter.map(move |[r, g, b]| { 13 | let r = cmp::min((r * white_balance[0]) >> BIT_SHIFT, CLIP_LIMIT_I32); 14 | let g = cmp::min((g * white_balance[1]) >> BIT_SHIFT, CLIP_LIMIT_I32); 15 | let b = cmp::min((b * white_balance[2]) >> BIT_SHIFT, CLIP_LIMIT_I32); 16 | [r, g, b] 17 | }) 18 | } 19 | 20 | #[inline(always)] 21 | pub fn color_convert<'a>( 22 | iter: impl Iterator + 'a, 23 | c: &'a [i32; 9], 24 | ) -> impl Iterator + 'a { 25 | iter.map(move |[r, g, b]| { 26 | [ 27 | limit_to_range((c[0] * r + c[1] * g + c[2] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 28 | limit_to_range((c[3] * r + c[4] * g + c[5] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 29 | limit_to_range((c[6] * r + c[7] * g + c[8] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 30 | ] 31 | }) 32 | } 33 | 34 | #[inline(always)] 35 | pub fn color_convert_rgba<'a>( 36 | iter: impl Iterator + 'a, 37 | c: &'a [i32; 9], 38 | ) -> impl Iterator + 'a { 39 | iter.map(move |[r, g, b]| { 40 | [ 41 | limit_to_range((c[0] * r + c[1] * g + c[2] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 42 | limit_to_range((c[3] * r + c[4] * g + c[5] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 43 | limit_to_range((c[6] * r + c[7] * g + c[8] * b) >> BIT_SHIFT, CLIP_RANGE) as u16, 44 | u16::MAX 45 | ] 46 | }) 47 | } 48 | 49 | #[inline(always)] 50 | pub fn gamma_correct<'a>( 51 | iter: impl Iterator + 'a, 52 | gamma_lut: &'a [u16; 65536], 53 | ) -> impl Iterator + 'a { 54 | iter.map(|[r, g, b]| { 55 | [ 56 | gamma_lut[r as usize], 57 | gamma_lut[g as usize], 58 | gamma_lut[b as usize], 59 | ] 60 | }) 61 | } 62 | 63 | #[inline(always)] 64 | pub fn gen_gamma_lut(gamma: f32) -> [u16; 65536] { 65 | let mut lut = [0u16; 65536]; 66 | for (i, elem) in lut.iter_mut().enumerate() { 67 | let l = i as f32 / 65535.; 68 | *elem = (l.powf(gamma) * 65535.) as u16; 69 | } 70 | lut 71 | } 72 | 73 | #[inline(always)] 74 | fn limit_to_range(v: T, (left, right): (T, T)) -> T { 75 | cmp::min(cmp::max(v, left), right) 76 | } 77 | -------------------------------------------------------------------------------- /src/maker/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::decode::{CFAPattern, Crop, Orientation}; 2 | use thiserror::Error; 3 | 4 | pub(super) mod selector; 5 | mod utility; 6 | 7 | mod adobe; 8 | mod decode_utility; 9 | mod fujifilm; 10 | mod nikon; 11 | mod olympus; 12 | mod panasonic; 13 | mod sony; 14 | 15 | pub(super) trait RawDecoder { 16 | fn new(info: quickexif::ParsedInfo) -> Self 17 | where 18 | Self: Sized; 19 | fn get_info(&self) -> &quickexif::ParsedInfo; 20 | fn into_info(self) -> quickexif::ParsedInfo; 21 | fn get_white_balance(&self) -> Result<[i32; 3], DecodingError> { 22 | let info = self.get_info(); 23 | Ok([ 24 | info.i32("white_balance_r")?, 25 | info.i32("white_balance_g")?, 26 | info.i32("white_balance_b")?, 27 | ]) 28 | } 29 | fn get_crop(&self) -> Option; 30 | fn get_bps_scale(&self) -> Result { 31 | let bps = self.get_info().u16("bps")?; 32 | let result = match bps { 33 | 12 => 16, 34 | 14 => 4, 35 | _ => 1, 36 | }; 37 | Ok(result) 38 | } 39 | fn get_orientation(&self) -> Orientation { 40 | match self.get_info().u16("orientation").ok() { 41 | None => Orientation::Horizontal, 42 | Some(o) => match o { 43 | 1 => Orientation::Horizontal, 44 | 3 => Orientation::Rotate180, 45 | 6 => Orientation::Rotate90, 46 | 8 => Orientation::Rotate270, 47 | _ => Orientation::Horizontal, 48 | }, 49 | } 50 | } 51 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError>; 52 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError>; 53 | fn get_cfa_pattern(&self) -> Result { 54 | let cfa_pattern = self.get_info().u8a4("cfa_pattern")?; 55 | let result = match cfa_pattern { 56 | [0, 1, 1, 2] => CFAPattern::RGGB, 57 | [2, 1, 1, 0] => CFAPattern::BGGR, 58 | [1, 0, 2, 1] => CFAPattern::GRBG, 59 | [1, 2, 0, 1] => CFAPattern::GBRG, 60 | _ => CFAPattern::RGGB, 61 | }; 62 | Ok(result) 63 | } 64 | } 65 | 66 | #[derive(Error, Debug)] 67 | pub enum DecodingError { 68 | #[error("Decoding error.")] 69 | RawInfoError(#[from] quickexif::parsed_info::Error), 70 | #[error("The decoded image size({0}) is invalid due to the width x height = {1}.")] 71 | InvalidDecodedImageSize(usize, usize), 72 | #[error("JPEG error.")] 73 | LJPEGError(#[from] decode_utility::DecodingError), 74 | } 75 | -------------------------------------------------------------------------------- /src/utility.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use once_cell::sync::Lazy; 3 | 4 | pub(super) trait ArrayMulNum { 5 | fn mul(&self, factor: i32) -> [i32; N]; 6 | } 7 | macro_rules! gen_array_mul_num_impls { 8 | ($t:ty) => { 9 | impl ArrayMulNum for [$t; N] { 10 | fn mul(&self, factor: i32) -> [i32; N] { 11 | let factor = factor as $t; 12 | let mut result: [i32; N] = [0i32; N]; 13 | for (i, &v) in self.iter().enumerate() { 14 | result[i] = (factor * v) as i32; 15 | } 16 | result 17 | } 18 | } 19 | }; 20 | } 21 | gen_array_mul_num_impls!(f32); 22 | gen_array_mul_num_impls!(i32); 23 | 24 | pub(super) fn log2(x: i32) -> u32 { 25 | for i in 1..BIT_SHIFT { 26 | if (x >> i) == 1 { 27 | return i; 28 | } 29 | } 30 | BIT_SHIFT 31 | } 32 | 33 | #[inline(always)] 34 | pub(super) fn matrix3_mul(a: &[f32; 9], b: &[f32; 9]) -> [f32; 9] { 35 | [ 36 | a[0] * b[0] + a[1] * b[3] + a[2] * b[6], 37 | a[0] * b[1] + a[1] * b[4] + a[2] * b[7], 38 | a[0] * b[2] + a[1] * b[5] + a[2] * b[8], 39 | a[3] * b[0] + a[4] * b[3] + a[5] * b[6], 40 | a[3] * b[1] + a[4] * b[4] + a[5] * b[7], 41 | a[3] * b[2] + a[4] * b[5] + a[5] * b[8], 42 | a[6] * b[0] + a[7] * b[3] + a[8] * b[6], 43 | a[6] * b[1] + a[7] * b[4] + a[8] * b[7], 44 | a[6] * b[2] + a[7] * b[5] + a[8] * b[8], 45 | ] 46 | } 47 | 48 | pub(super) static BASIC_INFO_RULE : Lazy = Lazy::new(|| { 49 | quickexif::describe_rule!(tiff { 50 | 0x010f { 51 | str + 0 / make 52 | } 53 | 0x0110 { 54 | str + 0 / model 55 | } 56 | 0x828e? / cfa_pattern 57 | 0xc612? / dng_version 58 | if dng_version ? { 59 | 0xc614 { 60 | str + 0 / make_model 61 | } 62 | if cfa_pattern ? { 63 | 0xc622 { // for normal dng 64 | r64 + 0 / c0 65 | r64 + 1 / c1 66 | r64 + 2 / c2 67 | r64 + 3 / c3 68 | r64 + 4 / c4 69 | r64 + 5 / c5 70 | r64 + 6 / c6 71 | r64 + 7 / c7 72 | r64 + 8 / c8 73 | } 74 | } else { 75 | 0xc621 { // for Apple ProRaw 76 | r64 + 0 / c0 77 | r64 + 1 / c1 78 | r64 + 2 / c2 79 | r64 + 3 / c3 80 | r64 + 4 / c4 81 | r64 + 5 / c5 82 | r64 + 6 / c6 83 | r64 + 7 / c7 84 | r64 + 8 / c8 85 | } 86 | } 87 | } 88 | }) 89 | }); -------------------------------------------------------------------------------- /src/decode.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::{fs::File, io::Read}; 3 | 4 | #[allow(clippy::upper_case_acronyms)] 5 | #[derive(Debug)] 6 | pub enum CFAPattern { 7 | RGGB, 8 | GRBG, 9 | GBRG, 10 | BGGR, 11 | XTrans0, // RBGBRG 12 | XTrans1, // GGRGGB 13 | } 14 | 15 | pub struct Crop { 16 | pub x: u32, 17 | pub y: u32, 18 | pub width: u32, 19 | pub height: u32, 20 | } 21 | 22 | pub struct DecodedImage { 23 | pub cfa_pattern: CFAPattern, 24 | pub width: usize, 25 | pub height: usize, 26 | pub crop: Option, 27 | pub orientation: Orientation, 28 | pub image: Vec, 29 | pub white_balance: [i32; 3], 30 | pub cam_matrix: [f32; 9], 31 | pub parsed_info: quickexif::ParsedInfo, 32 | } 33 | 34 | pub enum Orientation { 35 | Horizontal = 0, 36 | Rotate90 = 90, 37 | Rotate180 = 180, 38 | Rotate270 = 270, 39 | } 40 | 41 | pub(super) fn get_buffer_from_file(path: &str) -> Result, RawFileReadingError> { 42 | let mut f = 43 | File::open(path).map_err(|_| RawFileReadingError::FileNotExisted(path.to_owned()))?; 44 | let len = f 45 | .metadata() 46 | .map_err(|_| RawFileReadingError::FileMetadataReadingError(path.to_owned()))? 47 | .len() as usize; 48 | let mut buffer = vec![0u8; len]; 49 | f.read(&mut buffer) 50 | .map_err(|_| RawFileReadingError::FileContentReadingError(path.to_owned()))?; 51 | 52 | Ok(buffer) 53 | } 54 | fn prepare_buffer(mut buffer: Vec) -> Vec { 55 | buffer.extend([0u8; 16]); // + 16 is for BitPumpMSB fix 56 | 57 | fuji_buffer_fix(buffer) 58 | } 59 | fn fuji_buffer_fix(buffer: Vec) -> Vec { 60 | if buffer[..4] == [0x46, 0x55, 0x4a, 0x49] { 61 | buffer[148..].to_vec() 62 | } else { 63 | buffer 64 | } 65 | } 66 | fn fuji_buffer_slice_fix(buffer: &[u8]) -> &[u8] { 67 | if buffer[..4] == [0x46, 0x55, 0x4a, 0x49] { 68 | &buffer[148..] 69 | } else { 70 | buffer 71 | } 72 | } 73 | 74 | /// Gets `RawImage` from a file 75 | #[cfg_attr(not(feature = "wasm-bindgen"), fn_util::bench(decoding))] 76 | pub fn decode_file(path: &str) -> Result { 77 | let buffer = get_buffer_from_file(path)?; 78 | decode_buffer(buffer) 79 | } 80 | 81 | /// Gets `RawImage` from a buffer 82 | #[inline(always)] 83 | pub fn decode_buffer(buffer: Vec) -> Result { 84 | let buffer = prepare_buffer(buffer); 85 | 86 | let rule = &utility::BASIC_INFO_RULE; 87 | let decoder_select_info = quickexif::parse(&buffer, rule)?; 88 | 89 | let decoded_image = maker::selector::select_and_decode(buffer.as_slice(), decoder_select_info)?; 90 | 91 | Ok(decoded_image) 92 | } 93 | 94 | pub(super) fn get_exif_info(buffer: &[u8]) -> Result { 95 | let buffer = fuji_buffer_slice_fix(buffer); 96 | let rule = &utility::BASIC_INFO_RULE; 97 | let decoder_select_info = quickexif::parse(buffer, rule)?; 98 | let result = maker::selector::select_and_decode_exif_info(buffer, decoder_select_info)?; 99 | Ok(result) 100 | } 101 | 102 | pub(super) fn get_thumbnail(buffer: &[u8]) -> Result<(&[u8], Orientation), RawFileReadingError> { 103 | let buffer = fuji_buffer_slice_fix(buffer); 104 | let rule = &utility::BASIC_INFO_RULE; 105 | let decoder_select_info = quickexif::parse(buffer, rule)?; 106 | let result = maker::selector::select_and_decode_thumbnail(buffer, decoder_select_info)?; 107 | Ok(result) 108 | } 109 | -------------------------------------------------------------------------------- /src/export.rs: -------------------------------------------------------------------------------- 1 | use crate::{decode::CFAPattern, utility::ArrayMulNum}; 2 | 3 | use super::*; 4 | use pass::*; 5 | 6 | pub struct Options<'a> { 7 | gamma: f32, 8 | color_space: &'a [f32; 9], 9 | no_demosaicing: bool, 10 | } 11 | impl<'a> Options<'a> { 12 | pub fn new(gamma: f32, color_space: &'a [f32; 9], no_demosaicing: bool) -> Self { 13 | Options { 14 | gamma, 15 | color_space, 16 | no_demosaicing, 17 | } 18 | } 19 | } 20 | 21 | pub fn load_image_from_file( 22 | path: &str, 23 | options: Options, 24 | ) -> Result<(Vec, usize, usize), RawFileReadingError> { 25 | let buffer = decode::get_buffer_from_file(path)?; 26 | load_image_from_buffer(buffer, options) 27 | } 28 | 29 | 30 | pub fn load_origin_image_from_file( 31 | path: &str, 32 | options: Options, 33 | ) -> Result<(Vec, usize, usize), RawFileReadingError> { 34 | let buffer = decode::get_buffer_from_file(path)?; 35 | load_origin_image_from_buffer(buffer, options) 36 | } 37 | 38 | pub fn load_origin_image_from_buffer( 39 | buffer: Vec, 40 | options: Options, 41 | ) -> Result<(Vec, usize, usize), RawFileReadingError> { 42 | let decoded_image = decode::decode_buffer(buffer)?; 43 | 44 | let image = decoded_image.image; 45 | let width = decoded_image.width; 46 | let height = decoded_image.height; 47 | 48 | let iter = image.iter().copied(); 49 | let data = pass::iters_to_vec! ( 50 | iter 51 | ..enumerate() 52 | [(options.no_demosaicing, decoded_image.cfa_pattern)] { 53 | (true, _) => .none(), 54 | (false, CFAPattern::RGGB) => .linear_rggb(&image, width, height), 55 | (false, CFAPattern::GRBG) => .linear_grbg(&image, width, height), 56 | (false, CFAPattern::GBRG) => .linear_gbrg(&image, width, height), 57 | (false, CFAPattern::BGGR) => .linear_bggr(&image, width, height), 58 | (false, CFAPattern::XTrans0) => .linear_xtrans0(&image, width, height), 59 | (false, CFAPattern::XTrans1) => .linear_xtrans1(&image, width, height) 60 | } 61 | .u16rgb_to_u16rgba() 62 | ..flatten() 63 | ); 64 | 65 | Ok((data, width, height)) 66 | } 67 | 68 | pub fn load_image_from_buffer( 69 | buffer: Vec, 70 | options: Options, 71 | ) -> Result<(Vec, usize, usize), RawFileReadingError> { 72 | let decoded_image = decode::decode_buffer(buffer)?; 73 | 74 | let color_matrix = utility::matrix3_mul(options.color_space, &decoded_image.cam_matrix); 75 | let color_matrix = color_matrix.mul(1 << BIT_SHIFT); 76 | 77 | let white_balance = decoded_image 78 | .white_balance 79 | .mul(1 << (BIT_SHIFT - utility::log2(decoded_image.white_balance[1]))); 80 | 81 | let gamma_lut = gen_gamma_lut(options.gamma); 82 | 83 | let image = decoded_image.image; 84 | let width = decoded_image.width; 85 | let height = decoded_image.height; 86 | 87 | if image.len() == width * height * 3 { 88 | return Ok((image, width, height)); 89 | } 90 | 91 | let iter = image.iter().copied(); 92 | let data = pass::iters_to_vec! ( 93 | iter 94 | ..enumerate() 95 | [(options.no_demosaicing, decoded_image.cfa_pattern)] { 96 | (true, _) => .none(), 97 | (false, CFAPattern::RGGB) => .linear_rggb(&image, width, height), 98 | (false, CFAPattern::GRBG) => .linear_grbg(&image, width, height), 99 | (false, CFAPattern::GBRG) => .linear_gbrg(&image, width, height), 100 | (false, CFAPattern::BGGR) => .linear_bggr(&image, width, height), 101 | (false, CFAPattern::XTrans0) => .linear_xtrans0(&image, width, height), 102 | (false, CFAPattern::XTrans1) => .linear_xtrans1(&image, width, height) 103 | } 104 | .gamma_correct(&gamma_lut) 105 | .u16rgb_to_i32rgb() 106 | .white_balance_fix(&white_balance) 107 | .color_convert(&color_matrix) 108 | ..flatten() 109 | ); 110 | 111 | Ok((data, width, height)) 112 | } 113 | -------------------------------------------------------------------------------- /src/maker/fujifilm.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use once_cell::sync::Lazy; 3 | 4 | static FUJI_SENSOR_TABLE: phf::Map<&'static str, u8> = phf::phf_map! { 5 | "X-T1" => 0, // RBGBRG by default 6 | 7 | "X-T3" => 1, // GGRGGB 8 | "X-T4" => 1, 9 | "X-T30" => 1, 10 | "X-S10" => 1, 11 | "X-Pro3" => 1, 12 | "X-Pro4" => 1, 13 | "X-E4" => 1, 14 | "X100V" => 1, 15 | 16 | "GFX50R" => 100, // RGGB 17 | "GFX50S" => 100, 18 | "GFX100" => 100, 19 | "GFX50SII" => 100, 20 | "GFX100S" => 100, 21 | }; 22 | 23 | pub(super) struct General { 24 | info: quickexif::ParsedInfo, 25 | } 26 | 27 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 28 | quickexif::describe_rule!(tiff { 29 | 0x0112 / orientation 30 | next { 31 | 0x0201 / thumbnail 32 | 0x0202 / thumbnail_len 33 | } 34 | }) 35 | }); 36 | 37 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 38 | quickexif::describe_rule!(tiff { 39 | 0x0112 / orientation 40 | offset + 8 { 41 | scan [0x49, 0x49, 0x2a, 0x00] / tiff_offset { 42 | tiff { 43 | 0xf000 { 44 | 0xf001 / width 45 | 0xf002 / height 46 | 0xf003 / bps 47 | 0xf007 / strip 48 | 0xf008 / strip_len 49 | 0xf00a { 50 | u32 + 0 / black_level 51 | } 52 | 0xf00d { 53 | u32 + 0 / white_balance_g 54 | u32 + 1 / white_balance_r 55 | u32 + 2 / white_balance_b 56 | } 57 | } 58 | } 59 | } 60 | } 61 | }) 62 | }); 63 | 64 | 65 | impl RawDecoder for General { 66 | fn new(info: quickexif::ParsedInfo) -> Self { 67 | General { info } 68 | } 69 | fn get_info(&self) -> &quickexif::ParsedInfo { 70 | &self.info 71 | } 72 | fn into_info(self) -> quickexif::ParsedInfo { 73 | self.info 74 | } 75 | fn get_crop(&self) -> Option { 76 | None 77 | } 78 | fn get_cfa_pattern(&self) -> Result { 79 | let model = self 80 | .info 81 | .str("model")? 82 | .split_whitespace() 83 | .collect::(); 84 | let pattern = FUJI_SENSOR_TABLE.get(model.as_str()).unwrap_or(&0); 85 | let result = match pattern { 86 | 1 => CFAPattern::XTrans1, 87 | 100 => CFAPattern::RGGB, 88 | _ => CFAPattern::XTrans0, 89 | }; 90 | Ok(result) 91 | } 92 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 93 | let jpeg_header_offset = 12; 94 | let tiff_offset = self.info.usize("tiff_offset")?; 95 | let strip_offset = self.info.usize("strip")?; 96 | let strip_len = self.info.usize("strip_len")?; 97 | let width = self.info.usize("width")?; 98 | let height = self.info.usize("height")?; 99 | let black_level = self.info.u16("black_level")?; 100 | let bps_scale = self.get_bps_scale()?; 101 | 102 | let data_offset = jpeg_header_offset + tiff_offset + strip_offset; 103 | let buf = &buffer[data_offset..data_offset + strip_len]; 104 | let image: Vec = utility::to_14bit_iter(buf, self.info.is_le) 105 | .map(|x| bps_scale.saturating_mul(x.saturating_sub(black_level))) 106 | .collect(); 107 | 108 | if image.len() != width * height { 109 | Err(DecodingError::InvalidDecodedImageSize(image.len(), width * height)) 110 | } else { 111 | Ok(image) 112 | } 113 | } 114 | 115 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 116 | let offset = self.info.usize("thumbnail")?; 117 | let len = self.info.usize("thumbnail_len")?; 118 | let jpeg_header_offset = 12; 119 | let tiny_thumbnail_offset = jpeg_header_offset + offset + len; 120 | 121 | let jpeg_eoi = &buffer[tiny_thumbnail_offset..] 122 | .windows(2) 123 | .enumerate() 124 | .find(|(_, data)| data == &[0xff, 0xd9]); 125 | 126 | match jpeg_eoi { 127 | None => Ok(&buffer[offset..tiny_thumbnail_offset]), 128 | &Some((index, _)) => Ok(&buffer[..tiny_thumbnail_offset + index + 2]), 129 | } 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /supported-models.md: -------------------------------------------------------------------------------- 1 | # Supported camera models 2 | 3 | ## iPhone 4 | * iPhone Raw 5 | * iPhone ProRaw 6 | 7 | ## Nikon 8 | Some shooting modes are not yet supported 9 | * Z5 10 | * Z6 11 | * Z62 12 | * Z7 13 | * Z72 14 | * Z9 15 | * Z50 16 | * Z fc 17 | * D7500 18 | * D7200 19 | * D7100 20 | * D7000 21 | * D5600 22 | * D5500 23 | * D5300 24 | * D5200 25 | * D5100 26 | * D5000 27 | * D3500 28 | * D3400 29 | * D3300 30 | * D3200 31 | * D3100 32 | * D3000 33 | * D300 34 | * D300S 35 | * D500 36 | * D600 37 | * D610 38 | * D700 39 | * D750 40 | * D780 41 | * D800 42 | * D800E 43 | * D810 44 | * D850 45 | * D2Xs 46 | * D3 47 | * D3s 48 | * D3X 49 | * D4 50 | * D4S 51 | * D5 52 | * D6 53 | * Df 54 | * D40 55 | * D40X 56 | * D60 57 | * D80 58 | * D90 59 | * 1 AW1 60 | * 1 J1 61 | * 1 J2 62 | * 1 J3 63 | * 1 J4 64 | * 1 J5 65 | * 1 S1 66 | * 1 S2 67 | * 1 V1 68 | * 1 V2 69 | * 1 V3 70 | * Coolpix A 71 | 72 | 73 | ## Fujifilm 74 | Only uncompressed shooting modes are supported 75 | * X-T1 76 | * X-T2 77 | * X-T3 78 | * X-T4 79 | * X-T10 80 | * X-T20 81 | * X-T30 82 | * X-S10 83 | * X-Pro2 84 | * X-Pro3 85 | * X-Pro3 86 | * X-E2 87 | * X-E2S 88 | * X-E3 89 | * X-E4 90 | * X100F 91 | * X100S 92 | * X100S 93 | * X100T 94 | * X100V 95 | * GFX 50R 96 | * GFX 50S 97 | * GFX 100 98 | * GFX50S II 99 | * GFX100S 100 | * X-H1 101 | 102 | 103 | ## Sony 104 | Some shooting modes are not yet supported 105 | * DSC-HX95 106 | * DSC-HX99 107 | * DSC-RX0 108 | * DSC-RX0M2 109 | * DSC-RX1 110 | * DSC-RX1R 111 | * DSC-RX1RM2 112 | * DSC-RX10 113 | * DSC-RX10M2 114 | * DSC-RX10M3 115 | * DSC-RX10M4 116 | * DSC-RX100 117 | * DSC-RX100M2 118 | * DSC-RX100M3 119 | * DSC-RX100M4 120 | * DSC-RX100M5 121 | * DSC-RX100M5A 122 | * DSC-RX100M6 123 | * DSC-RX100M7 124 | * DSLR-A450 125 | * DSLR-A500 126 | * DSLR-A550 127 | * DSLR-A560 128 | * DSLR-A580 129 | * DSLR-A700 130 | * ILCA-68 131 | * ILCA-77M2 132 | * ILCA-99M2 133 | * ILCE-1 134 | * ILCE-7 135 | * ILCE-7C 136 | * ILCE-7M2 137 | * ILCE-7M3 138 | * ILCE-7M4 139 | * ILCE-7R 140 | * ILCE-7RM2 141 | * ILCE-7RM3 142 | * ILCE-7RM3A 143 | * ILCE-7RM4 144 | * ILCE-7RM4A 145 | * ILCE-7S 146 | * ILCE-7SM2 147 | * ILCE-7SM3 148 | * ILCE-9 149 | * ILCE-9M2 150 | * ILCE-3000 151 | * ILCE-3500 152 | * ILCE-5000 153 | * ILCE-5100 154 | * ILCE-6000 155 | * ILCE-6100 156 | * ILCE-6300 157 | * ILCE-6400 158 | * ILCE-6500 159 | * ILCE-6600 160 | * ILCE-QX1 161 | * NEX-3 162 | * NEX-3N 163 | * NEX-5 164 | * NEX-5N 165 | * NEX-5R 166 | * NEX-5T 167 | * NEX-6 168 | * NEX-7 169 | * NEX-C3 170 | * NEX-F3 171 | * SLT-A33 172 | * SLT-A35 173 | * SLT-A37 174 | * SLT-A55V 175 | * SLT-A57 176 | * SLT-A58 177 | * SLT-A65V 178 | * SLT-A77V 179 | * SLT-A99V 180 | * ZV-1 181 | * ZV-E10 182 | 183 | * ## Panasonic 184 | * DC-FZ45 185 | * DC-FZ80 186 | * DC-FZ82 187 | * DC-FZ10002 188 | * DC-G9 189 | * DC-G70 190 | * DC-G90 191 | * DC-G81 192 | * DC-G91 193 | * DC-G95 194 | * DC-G100 195 | * DC-G110 196 | * DC-GF1 197 | * DC-GF2 198 | * DC-GF6 199 | * DC-GF7 200 | * DC-GF10 201 | * DC-GH2 202 | * DC-GH4 203 | * DC-GH5 204 | * DC-GX7 205 | * DC-GX7MK3 206 | * DC-GX9 207 | * DC-GX85 208 | * DC-GX850 209 | * DC-GX800 210 | * DC-GX850 211 | * DC-GX880 212 | * DC-LX100M2 213 | * DC-TZ90 214 | * DC-TZ91 215 | * DC-TZ95 216 | * DC-TZ96 217 | * DC-TZ200 218 | * DC-TZ202 219 | * DMC-CM1 220 | * DMC-FZ28 221 | * DMC-FZ35 222 | * DMC-FZ45 223 | * DMC-FZ70 224 | * DMC-FZ72 225 | * DMC-FZ80 226 | * DMC-FZ100 227 | * DMC-FZ150 228 | * DMC-FZ200 229 | * DMC-FZ300 230 | * DMC-FZ330 231 | * DMC-FZ1000 232 | * DMC-FZ2000 233 | * DMC-FZ2500 234 | * DMC-G1 235 | * DMC-GH1 236 | * DMC-GM1 237 | * DMC-GM5 238 | * DMC-G2 239 | * DMC-G3 240 | * DMC-G5 241 | * DMC-G6 242 | * DMC-G7 243 | * DMC-GF5 244 | * DMC-GF3 245 | * DMC-G10 246 | * DMC-G80 247 | * DMC-G81 248 | * DMC-G85 249 | * DMC-LF1 250 | * DMC-LX3 251 | * DMC-LX5 252 | * DMC-LX7 253 | * DMC-LX10 254 | * DMC-LX15 255 | * DMC-LX100 256 | * DMC-TZ60 257 | * DMC-TZ61 258 | * DMC-TZ70 259 | * DMC-TZ71 260 | * DMC-TZ80 261 | * DMC-TZ81 262 | * DMC-TZ90 263 | * DMC-TZ91 264 | * DMC-TZ96 265 | * DMC-TZ100 266 | * DMC-TZ101 267 | * DMC-TZ110 268 | * DMC-TZ202 269 | * DMC-ZS40 270 | * DMC-ZS60 271 | * DMC-ZS100 272 | 273 | 274 | ## Olympus 275 | * E-3 276 | * E-5 277 | * E-30 278 | * E-420 279 | * E-450 280 | * E-520 281 | * E-600 282 | * E-620 283 | * E-M1 284 | * E-M1MarkII 285 | * E-M1MarkIII 286 | * E-M1X 287 | * E-M5 288 | * E-M5 Mark II 289 | * E-M5 Mark III 290 | * E-M10 291 | * E-M10 Mark II 292 | * E-M10 Mark III 293 | * E-M10 Mark IV 294 | * E-M10MarkIIIS 295 | * PenF 296 | * E-P1 297 | * E-P2 298 | * E-P3 299 | * E-P5 300 | * E-P7 301 | * E-PL1 302 | * E-PL2 303 | * E-PL3 304 | * E-PL5 305 | * E-PL6 306 | * E-PL7 307 | * E-PL8 308 | * E-PL9 309 | * E-PL10 310 | * E-PM1 311 | * E-PM2 312 | * TG-4 313 | * TG-5 314 | * TG-6 315 | * XZ-1 316 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A pure rust library to handle camera raw files. 2 | //! 3 | //! **quickraw** is a pure rust library to decode and renderer image from camera raw files. 4 | //! 5 | //! ## Examples 6 | //! #### Export thumbnail 7 | //! ```no_run 8 | //! use quickraw::Export; 9 | //! 10 | //! let raw_data = std::fs::read("sample.ARW").unwrap(); 11 | //! let (thumbnail_data, orientation) = Export::export_thumbnail_data(&raw_data).unwrap(); 12 | //! 13 | //! // notice that this function is available on feature `image` only. 14 | //! quickraw::Export::export_thumbnail_to_file("sample.ARW", "sample.thumbnail.jpg").unwrap(); 15 | //! ``` 16 | //! 17 | //! #### Get EXIF data 18 | //! ```no_run 19 | //! use quickraw::Export; 20 | //! let info = Export::export_exif_info(Input::ByFile("sample.ARW")).unwrap(); 21 | //! 22 | //! // info is a `quickexif::ParsedInfo` type, for more info please check https://docs.rs/quickexif 23 | //! let width = info.usize("width").unwrap(); 24 | //! ``` 25 | //! #### Export image 26 | //! ```no_run 27 | //! use quickraw::{data, DemosaicingMethod, Input, Output, Export, OutputType}; 28 | //! 29 | //! let demosaicing_method = DemosaicingMethod::Linear; 30 | //! let color_space = data::XYZ2SRGB; 31 | //! let gamma = data::GAMMA_SRGB; 32 | //! let output_type = OutputType::Raw16; 33 | //! let auto_crop = false; 34 | //! let auto_rotate = false; 35 | //! 36 | //! let export_job = Export::new( 37 | //! Input::ByFile("sample.ARW"), 38 | //! Output::new( 39 | //! demosaicing_method, 40 | //! color_space, 41 | //! gamma, 42 | //! output_type, 43 | //! auto_crop, 44 | //! auto_rotate, 45 | //! ), 46 | //! ).unwrap(); 47 | //! 48 | //! let (image, width, height) = export_job.export_16bit_image(); 49 | //! 50 | //! // or you can also export an image with quality(only works when the output type is JPEG). 51 | //! // notice that this function is available on feature `image` only. 52 | //! export_job.export_image(92).unwrap(); 53 | //! ``` 54 | 55 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 56 | 57 | /// A flag to enable benchmark for several key processes. 58 | pub const BENCH_FLAG: &str = "QUICKRAW_BENCH"; 59 | 60 | use thiserror::Error; 61 | 62 | pub mod data; 63 | 64 | mod utility; 65 | 66 | mod pass; 67 | mod maker; 68 | mod decode; 69 | pub use decode::decode_file; 70 | pub use decode::decode_buffer; 71 | 72 | #[cfg(feature = "wasm-bindgen")] 73 | mod lib_wasm; 74 | #[cfg(any(debug_assertions, not(feature = "wasm-bindgen")))] 75 | mod lib_c; 76 | #[cfg(any(debug_assertions, not(feature = "wasm-bindgen")))] 77 | pub mod export; 78 | 79 | const BIT_SHIFT: u32 = 13u32; 80 | 81 | /// All the demosaicing method currently supported. 82 | #[derive(Clone)] 83 | pub enum DemosaicingMethod { 84 | None, 85 | SuperPixel, 86 | Linear, 87 | } 88 | 89 | /// Decides if the output should be 8bit or 16bit. 90 | #[derive(Clone)] 91 | pub enum OutputType { 92 | Raw8, 93 | Raw16, 94 | Image8(String), 95 | Image16(String), 96 | } 97 | 98 | /// Chooses the input from a file or a buffer. 99 | pub enum Input<'a> { 100 | ByFile(&'a str), 101 | ByBuffer(Vec), 102 | } 103 | 104 | /// Contains options for image rendering. 105 | #[allow(dead_code)] 106 | #[derive(Clone)] 107 | pub struct Output { 108 | demosaicing_method: DemosaicingMethod, 109 | color_space: [f32; 9], 110 | gamma: [f32; 2], 111 | output_type: OutputType, 112 | auto_crop: bool, 113 | auto_rotate: bool, 114 | } 115 | impl Output { 116 | pub fn new( 117 | demosaicing_method: DemosaicingMethod, 118 | color_space: [f32; 9], 119 | gamma: [f32; 2], 120 | output_type: OutputType, 121 | auto_crop: bool, 122 | auto_rotate: bool, 123 | ) -> Output { 124 | Output { 125 | demosaicing_method, 126 | color_space, 127 | gamma, 128 | output_type, 129 | auto_crop, 130 | auto_rotate, 131 | } 132 | } 133 | } 134 | 135 | /// Errors of raw file reading. 136 | #[derive(Error, Debug)] 137 | pub enum RawFileReadingError { 138 | #[error("Exif parsing error.")] 139 | ExifParseError(#[from] quickexif::parser::Error), 140 | #[error("Exif parsed info error.")] 141 | ExifParseInfoError(#[from] quickexif::parsed_info::Error), 142 | #[error("Cannot read the raw file.")] 143 | DecodingError(#[from] maker::DecodingError), 144 | #[error("The file '{0}' is not existed.")] 145 | FileNotExisted(String), 146 | #[error("The metadata of file '{0}' cannot be read.")] 147 | FileMetadataReadingError(String), 148 | #[error("The content of file '{0}' cannot be read.")] 149 | FileContentReadingError(String), 150 | #[error("Cannot read Make info from this raw file.")] 151 | CannotReadMake, 152 | #[error("Cannot read Model info from this raw file.")] 153 | CannotReadModel, 154 | #[error("This raw file from maker: '{0}' is not supported yet.")] 155 | MakerIsNotSupportedYet(String), 156 | #[error("This raw file model: '{0}' is not supported yet.")] 157 | ModelIsNotSupportedYet(String), 158 | } -------------------------------------------------------------------------------- /src/lib_c.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use anyhow::Result; 3 | use std::ffi::{CStr, CString}; 4 | use std::os::raw::*; 5 | 6 | fn str_from_cchar<'a>(ptr: *mut c_char) -> &'a str { 7 | let s = unsafe { CStr::from_ptr(ptr) }; 8 | s.to_str().unwrap() 9 | } 10 | fn free_cstring(ptr: *mut c_char) { 11 | unsafe { CString::from_raw(ptr) }; 12 | } 13 | fn gen_cstring(s: String) -> *mut c_char { 14 | CString::new(s).unwrap().into_raw() 15 | } 16 | fn gen_empty_cstring() -> *mut c_char { 17 | CString::new("").unwrap().into_raw() 18 | } 19 | 20 | pub trait Free { 21 | fn free(&mut self); 22 | } 23 | 24 | #[repr(C)] 25 | pub struct RustVec { 26 | ptr: *mut c_uchar, 27 | len: c_uint, 28 | capacity: c_uint, 29 | } 30 | 31 | impl RustVec { 32 | fn new_empty() -> Self { 33 | let x: Vec = vec![]; 34 | RustVec { 35 | ptr: x.as_ptr() as *mut c_uchar, 36 | len: x.len() as c_uint, 37 | capacity: x.capacity() as c_uint, 38 | } 39 | } 40 | 41 | fn new(x: Vec) -> Self { 42 | let result = RustVec { 43 | ptr: x.as_ptr() as *mut c_uchar, 44 | len: x.len() as c_uint, 45 | capacity: x.capacity() as c_uint, 46 | }; 47 | std::mem::forget(x); 48 | result 49 | } 50 | 51 | fn free(&mut self) { 52 | unsafe { Vec::from_raw_parts(self.ptr, self.len as usize, self.capacity as usize) }; 53 | } 54 | } 55 | 56 | #[repr(C)] 57 | pub struct QuickrawResponse { 58 | has_error: bool, 59 | error_msg: *mut c_char, 60 | content: T, 61 | } 62 | 63 | impl QuickrawResponse { 64 | fn new(x: Result) -> Self { 65 | let (has_error, error, content) = match x { 66 | Ok(x) => (false, gen_empty_cstring(), x), 67 | Err(e) => (true, gen_cstring(e.to_string()), T::default()), 68 | }; 69 | QuickrawResponse { 70 | has_error, 71 | error_msg: error, 72 | content, 73 | } 74 | } 75 | } 76 | impl Free for QuickrawResponse { 77 | fn free(&mut self) { 78 | free_cstring(self.error_msg); 79 | self.content.free(); 80 | } 81 | } 82 | 83 | #[repr(C)] 84 | pub struct BasicInfo { 85 | exif: *mut c_char, 86 | thumbnail: RustVec, 87 | orientation: c_uchar, 88 | } 89 | impl Default for BasicInfo { 90 | fn default() -> Self { 91 | BasicInfo { 92 | exif: gen_empty_cstring(), 93 | thumbnail: RustVec::new_empty(), 94 | orientation: 0, 95 | } 96 | } 97 | } 98 | impl BasicInfo { 99 | fn new(exif: String, thumbnail: RustVec, orientation: c_uchar) -> Self { 100 | BasicInfo { 101 | exif: gen_cstring(exif), 102 | thumbnail, 103 | orientation, 104 | } 105 | } 106 | } 107 | impl Free for BasicInfo { 108 | fn free(&mut self) { 109 | free_cstring(self.exif); 110 | self.thumbnail.free(); 111 | } 112 | } 113 | 114 | fn load_basicinfo(cpath: *mut c_char) -> Result { 115 | let path = str_from_cchar(cpath); 116 | let buffer = decode::get_buffer_from_file(path)?; 117 | let exif = decode::get_exif_info(&buffer)?; 118 | let s = exif.stringify_all()?; 119 | let thumbnail = RustVec::new_empty(); 120 | Ok(BasicInfo::new(s, thumbnail, 0)) 121 | } 122 | #[no_mangle] 123 | pub extern "C" fn quickraw_load_basicinfo(cpath: *mut c_char) -> QuickrawResponse { 124 | QuickrawResponse::new(load_basicinfo(cpath)) 125 | } 126 | #[no_mangle] 127 | pub extern "C" fn quickraw_free_basicinfo(mut response: QuickrawResponse) { 128 | response.free(); 129 | } 130 | 131 | #[repr(C)] 132 | pub struct Image { 133 | data: RustVec, 134 | width: c_uint, 135 | height: c_uint, 136 | } 137 | impl Image { 138 | fn new(img: Vec, width: usize, height: usize) -> Self { 139 | Image { 140 | data: RustVec::new(img), 141 | width: width as c_uint, 142 | height: height as c_uint, 143 | } 144 | } 145 | } 146 | impl Free for Image { 147 | fn free(&mut self) { 148 | self.data.free(); 149 | } 150 | } 151 | impl Default for Image { 152 | fn default() -> Self { 153 | Image { 154 | data: RustVec::new_empty(), 155 | width: 0, 156 | height: 0, 157 | } 158 | } 159 | } 160 | 161 | fn load_image(cpath: *mut c_char) -> Result { 162 | let path = str_from_cchar(cpath); 163 | let options = export::Options::new(data::GAMMA_SRGB, &data::XYZ2SRGB, false); 164 | 165 | let (img, width, height) = export::load_image_from_file(path, options)?; 166 | let img = img.into_iter().map(|x| (x / 257) as u8).collect::>(); 167 | Ok(Image::new(img, width, height)) 168 | } 169 | #[no_mangle] 170 | pub extern "C" fn quickraw_load_image(cpath: *mut c_char) -> QuickrawResponse { 171 | QuickrawResponse::new(load_image(cpath)) 172 | } 173 | #[no_mangle] 174 | pub extern "C" fn quickraw_free_image(mut response: QuickrawResponse) { 175 | response.free(); 176 | } 177 | -------------------------------------------------------------------------------- /src/maker/panasonic.rs: -------------------------------------------------------------------------------- 1 | use super::decode_utility::bit_pump::*; 2 | use super::*; 3 | use once_cell::sync::Lazy; 4 | 5 | pub(super) struct General { 6 | info: quickexif::ParsedInfo, 7 | } 8 | 9 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 10 | quickexif::describe_rule!(tiff { 11 | 0x0112 / orientation 12 | 0x002e / thumbnail(thumbnail_len) 13 | }) 14 | }); 15 | 16 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 17 | quickexif::describe_rule!(tiff { 18 | 0x0002 / width 19 | 0x0003 / height 20 | 0x0009 / cfa_pattern 21 | 0x000a / bps 22 | 0x001c / black_level_r 23 | 0x001d / black_level_g 24 | 0x001e / black_level_b 25 | 0x0024 / white_balance_r 26 | 0x0025 / white_balance_g 27 | 0x0026 / white_balance_b 28 | 0x0118 / strip 29 | 0x0117 / strip_len 30 | 0x002f? / crop_top 31 | 0x0030? / crop_left 32 | 0x0031? / crop_bottom 33 | 0x0032? / crop_right 34 | 0x0112 / orientation 35 | 0x002e { 36 | offset + 12 { 37 | tiff { 38 | 0x8769 { 39 | 0x927c { 40 | offset + 12 { 41 | 0x004b / cropped_width 42 | 0x004c / cropped_height 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | }) 50 | }); 51 | 52 | impl RawDecoder for General { 53 | fn new(info: quickexif::ParsedInfo) -> Self { 54 | General { info } 55 | } 56 | fn get_info(&self) -> &quickexif::ParsedInfo { 57 | &self.info 58 | } 59 | fn into_info(self) -> quickexif::ParsedInfo { 60 | self.info 61 | } 62 | 63 | fn get_crop(&self) -> Option { 64 | let x = self.info.u32("crop_left").ok()?; 65 | let y = self.info.u32("crop_top").ok()?; 66 | let right = self.info.u32("crop_right").ok()?; 67 | let bottom = self.info.u32("crop_bottom").ok()?; 68 | 69 | Some(Crop { 70 | x, 71 | y, 72 | width: right - x, 73 | height: bottom - y, 74 | }) 75 | } 76 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 77 | let image = load_raw(&self.info, buffer)?; 78 | let black_level = self.info.u16("black_level_r")?; 79 | let bps_scale = self.get_bps_scale()?; 80 | Ok(image 81 | .iter() 82 | .map(|x| bps_scale.saturating_mul(x.saturating_sub(black_level))) 83 | .collect()) 84 | } 85 | fn get_cfa_pattern(&self) -> Result { 86 | let cfa_pattern = self.info.u16("cfa_pattern")?; 87 | let result = match cfa_pattern { 88 | 1 => CFAPattern::RGGB, 89 | 2 => CFAPattern::GRBG, 90 | 3 => CFAPattern::GBRG, 91 | 4 => CFAPattern::BGGR, 92 | _ => CFAPattern::RGGB, 93 | }; 94 | Ok(result) 95 | } 96 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 97 | let offset = self.info.usize("thumbnail")?; 98 | let len = self.info.usize("thumbnail_len")?; 99 | Ok(&buffer[offset..offset + len]) 100 | } 101 | } 102 | 103 | fn load_raw(info: &quickexif::ParsedInfo, buffer: &[u8]) -> Result, DecodingError> { 104 | const SPLIT: bool = true; 105 | const BLOCK_LINES: usize = 5; 106 | 107 | let width = info.usize("width")?; 108 | let height = info.usize("height")?; 109 | let offset = info.usize("strip")?; 110 | 111 | let buf = &buffer[offset..]; 112 | let mut out: Vec = vec![0u16; width * height]; 113 | 114 | out.chunks_exact_mut(width * BLOCK_LINES) 115 | .enumerate() 116 | .for_each(|(index, out)| { 117 | let row = index * BLOCK_LINES; 118 | 119 | let skip = ((width * row * 9) + (width / 14 * 2 * row)) / 8; 120 | let blocks = skip / 0x4000; 121 | let src = &buf[blocks * 0x4000..]; 122 | let mut pump = BitPumpPanasonic::new(src, SPLIT); 123 | for _ in 0..(skip % 0x4000) { 124 | pump.get_bits(8); 125 | } 126 | 127 | let mut sh: i32 = 0; 128 | for out in out.chunks_exact_mut(14) { 129 | let mut pred: [i32; 2] = [0, 0]; 130 | let mut nonz: [i32; 2] = [0, 0]; 131 | 132 | for i in 0..14 { 133 | if (i % 3) == 2 { 134 | sh = 4 >> (3 - pump.get_bits(2)); 135 | } 136 | if nonz[i & 1] != 0 { 137 | let j = pump.get_bits(8) as i32; 138 | if j != 0 { 139 | pred[i & 1] -= 0x80 << sh; 140 | if pred[i & 1] < 0 || sh == 4 { 141 | pred[i & 1] &= !(-1 << sh); 142 | } 143 | pred[i & 1] += j << sh; 144 | } 145 | } else { 146 | nonz[i & 1] = pump.get_bits(8) as i32; 147 | if nonz[i & 1] != 0 || i > 11 { 148 | pred[i & 1] = nonz[i & 1] << 4 | (pump.get_bits(4) as i32); 149 | } 150 | } 151 | out[i] = pred[i & 1] as u16; 152 | } 153 | } 154 | }); 155 | Ok(out) 156 | } 157 | -------------------------------------------------------------------------------- /src/maker/selector.rs: -------------------------------------------------------------------------------- 1 | use super::super::data; 2 | use super::*; 3 | use crate::decode::DecodedImage; 4 | use crate::RawFileReadingError; 5 | 6 | fn prepare( 7 | basic_info: &quickexif::ParsedInfo, 8 | only_thumbnail: bool, 9 | ) -> Result<(&str, Option, [f32; 9]), RawFileReadingError> { 10 | let make = basic_info 11 | .str("make") 12 | .map_err(|_| RawFileReadingError::CannotReadMake)?; 13 | let model = basic_info 14 | .str("model") 15 | .map_err(|_| RawFileReadingError::CannotReadModel)? 16 | .split_whitespace() 17 | .collect::(); 18 | 19 | let dng_version = basic_info.u16("dng_version").ok(); 20 | 21 | let cam_matrix = if only_thumbnail { 22 | [0f32; 9] 23 | } else { 24 | match dng_version { 25 | None => *data::CAM_XYZ_MAP 26 | .get(model.as_str()) 27 | .ok_or_else(|| RawFileReadingError::ModelIsNotSupportedYet(model.clone()))?, 28 | Some(_) => { 29 | let mut matrix = [0f32; 9]; 30 | for (i, item) in matrix.iter_mut().enumerate() { 31 | *item = basic_info.f64(format!("c{}", i).as_str())? as f32; 32 | } 33 | utility::matrix3_inverse(&mut matrix); 34 | utility::matrix3_normalize(&mut matrix); 35 | matrix 36 | } 37 | } 38 | }; 39 | 40 | Ok((make, dng_version, cam_matrix)) 41 | } 42 | 43 | pub(in super::super) fn select_and_decode_exif_info( 44 | file_buffer: &[u8], 45 | basic_info: quickexif::ParsedInfo, 46 | ) -> Result { 47 | let (make, dng_version, _) = prepare(&basic_info, true)?; 48 | 49 | let rule = match dng_version { 50 | None => match make { 51 | "NIKON" | "NIKON CORPORATION" => Ok(&nikon::IMAGE_RULE), 52 | "SONY" => Ok(&sony::IMAGE_RULE), 53 | "Panasonic" => Ok(&panasonic::IMAGE_RULE), 54 | "OLYMPUS CORPORATION" | "OLYMPUS IMAGING CORP." => Ok(&olympus::IMAGE_RULE), 55 | "FUJIFILM" => Ok(&fujifilm::IMAGE_RULE), 56 | _ => Err(RawFileReadingError::MakerIsNotSupportedYet(make.to_owned())), 57 | }, 58 | Some(_version) => Ok(&adobe::IMAGE_RULE), 59 | }?; 60 | 61 | Ok(quickexif::parse_with_prev_info( 62 | file_buffer, 63 | rule, 64 | basic_info, 65 | )?) 66 | } 67 | 68 | pub(in super::super) fn select_and_decode_thumbnail( 69 | file_buffer: &[u8], 70 | basic_info: quickexif::ParsedInfo, 71 | ) -> Result<(&[u8], Orientation), RawFileReadingError> { 72 | let (make, dng_version, _) = prepare(&basic_info, true)?; 73 | 74 | macro_rules! decode { 75 | ($t:ident) => {{ 76 | let raw_info = 77 | quickexif::parse_with_prev_info(file_buffer, &$t::THUMBNAIL_RULE, basic_info)?; 78 | let decoder = $t::General::new(raw_info); 79 | let thumbnail = decoder.get_thumbnail(&file_buffer)?; 80 | let orientation = decoder.get_orientation(); 81 | (thumbnail, orientation) 82 | }}; 83 | } 84 | 85 | match dng_version { 86 | None => match make { 87 | "NIKON" | "NIKON CORPORATION" => Ok(decode!(nikon)), 88 | "SONY" => Ok(decode!(sony)), 89 | "Panasonic" => Ok(decode!(panasonic)), 90 | "OLYMPUS CORPORATION" | "OLYMPUS IMAGING CORP." => Ok(decode!(olympus)), 91 | "FUJIFILM" => Ok(decode!(fujifilm)), 92 | _ => Err(RawFileReadingError::MakerIsNotSupportedYet(make.to_owned())), 93 | }, 94 | Some(_version) => Ok(decode!(adobe)), 95 | } 96 | } 97 | 98 | pub(in super::super) fn select_and_decode( 99 | file_buffer: &[u8], 100 | basic_info: quickexif::ParsedInfo, 101 | ) -> Result { 102 | let (make, dng_version, cam_matrix) = prepare(&basic_info, false)?; 103 | 104 | macro_rules! decode { 105 | ($t:ident) => {{ 106 | let raw_info = 107 | quickexif::parse_with_prev_info(file_buffer, &$t::IMAGE_RULE, basic_info)?; 108 | let width = raw_info.usize("width")?; 109 | let height = raw_info.usize("height")?; 110 | 111 | let decoder = $t::General::new(raw_info); 112 | let cfa_pattern = decoder.get_cfa_pattern().unwrap_or(CFAPattern::RGGB); 113 | let crop = decoder.get_crop(); 114 | let orientation = decoder.get_orientation(); 115 | let white_balance = decoder.get_white_balance()?; 116 | let image = decoder.decode_with_preprocess(file_buffer)?; 117 | 118 | DecodedImage { 119 | image, 120 | width, 121 | height, 122 | cfa_pattern, 123 | crop, 124 | orientation, 125 | white_balance, 126 | cam_matrix, 127 | parsed_info: decoder.into_info() 128 | } 129 | }}; 130 | } 131 | 132 | let decoded_image = match dng_version { 133 | None => match make { 134 | "NIKON" | "NIKON CORPORATION" => Ok(decode!(nikon)), 135 | "SONY" => Ok(decode!(sony)), 136 | "Panasonic" => Ok(decode!(panasonic)), 137 | "OLYMPUS CORPORATION" | "OLYMPUS IMAGING CORP." => Ok(decode!(olympus)), 138 | "FUJIFILM" => Ok(decode!(fujifilm)), 139 | _ => Err(RawFileReadingError::MakerIsNotSupportedYet(make.to_owned())), 140 | }, 141 | Some(_version) => Ok(decode!(adobe)), 142 | }?; 143 | 144 | Ok(decoded_image) 145 | } 146 | -------------------------------------------------------------------------------- /src/maker/utility.rs: -------------------------------------------------------------------------------- 1 | pub(super) trait GetNumFromBytes { 2 | fn u16(&self, is_le: bool, start: usize) -> u16; 3 | fn u16le(&self, start: usize) -> u16; 4 | fn u16be(&self, start: usize) -> u16; 5 | fn u32(&self, is_le: bool, start: usize) -> u32; 6 | fn u32le(&self, start: usize) -> u32; 7 | fn u32be(&self, start: usize) -> u32; 8 | fn i32(&self, is_le: bool, start: usize) -> i32; 9 | fn r64(&self, is_le: bool, start: usize) -> f64; 10 | } 11 | 12 | pub(super) trait GetBytesFromInt { 13 | fn to_bytes(self, is_le: bool) -> T; 14 | } 15 | 16 | macro_rules! gen_impl_get_int { 17 | ($t:tt, $len:expr) => { 18 | fn $t(&self, is_le: bool, start: usize) -> $t { 19 | let bytes: [u8; $len] = *&self[start..start + $len].try_into().unwrap(); 20 | if is_le { 21 | $t::from_le_bytes(bytes) 22 | } else { 23 | $t::from_be_bytes(bytes) 24 | } 25 | } 26 | }; 27 | } 28 | impl GetNumFromBytes for &[u8] { 29 | gen_impl_get_int!(u16, 2); 30 | gen_impl_get_int!(u32, 4); 31 | gen_impl_get_int!(i32, 4); 32 | 33 | fn u16le(&self, start: usize) -> u16 { 34 | let bytes: [u8; 2] = self[start..start + 2].try_into().unwrap(); 35 | u16::from_le_bytes(bytes) 36 | } 37 | fn u16be(&self, start: usize) -> u16 { 38 | let bytes: [u8; 2] = self[start..start + 2].try_into().unwrap(); 39 | u16::from_be_bytes(bytes) 40 | } 41 | fn u32le(&self, start: usize) -> u32 { 42 | let bytes: [u8; 4] = self[start..start + 4].try_into().unwrap(); 43 | u32::from_le_bytes(bytes) 44 | } 45 | fn u32be(&self, start: usize) -> u32 { 46 | let bytes: [u8; 4] = self[start..start + 4].try_into().unwrap(); 47 | u32::from_be_bytes(bytes) 48 | } 49 | 50 | fn r64(&self, is_le: bool, start: usize) -> f64 { 51 | let n = self.i32(is_le, start) as f64; 52 | let d = self.u32(is_le, start + 4) as f64; 53 | n / d 54 | } 55 | } 56 | 57 | macro_rules! gen_get_bytes_impls { 58 | ($t:ty, $n:expr) => { 59 | impl GetBytesFromInt<[u8; $n]> for $t { 60 | fn to_bytes(self, is_le: bool) -> [u8; $n] { 61 | if is_le { 62 | self.to_le_bytes() 63 | } else { 64 | self.to_be_bytes() 65 | } 66 | } 67 | } 68 | }; 69 | } 70 | gen_get_bytes_impls!(u16, 2); 71 | gen_get_bytes_impls!(u32, 4); 72 | 73 | 74 | #[inline(always)] 75 | pub(super) fn to_16bit_iter(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 76 | buffer.chunks_exact(2).map(move |bytes| bytes.u16(is_le, 0)) 77 | } 78 | #[inline(always)] 79 | pub(super) fn to_14bit_iter(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 80 | buffer.chunks_exact(2).map(move |bytes| bytes.u16(is_le, 0) & 0x3fff) 81 | } 82 | #[inline(always)] 83 | pub(super) fn to_14bit_iter_packed(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 84 | buffer.chunks_exact(7).flat_map(move |bytes| { 85 | let g1 = bytes[0] as u16; 86 | let g2 = bytes[1] as u16; 87 | let g3 = bytes[2] as u16; 88 | let g4 = bytes[3] as u16; 89 | let g5 = bytes[4] as u16; 90 | let g6 = bytes[5] as u16; 91 | let g7 = bytes[6] as u16; 92 | 93 | // 11111111 11111111 11111111 11111111 11111111 11111111 11111111 94 | // aaaaaaaa aaaaaabb bbbbbbbb bbbbcccc cccccccc ccdddddd dddddddd 95 | // aaaaaaaa bbaaaaaa bbbbbbbb ccccbbbb cccccccc ddddddcc dddddddd 96 | if is_le { 97 | [ 98 | ((g2 & 0b111111) << 8) | g1, 99 | ((g4 & 0x0f) << 10) | (g3 << 2) | (g2 >> 6), 100 | ((g6 & 0b11) << 12) | (g5 << 4) | (g4 >> 4), 101 | (g7 << 6) | (g6 >> 2), 102 | ] 103 | } else { 104 | [ 105 | (g1 << 6) | (g2 >> 2), 106 | ((g2 & 0b11) << 12) | (g3 << 4) | (g4 >> 4), 107 | ((g4 & 0b1111) << 10) | (g5 << 2) | (g6 >> 6), 108 | ((g6 & 0b111111) << 8) | g7, 109 | ] 110 | } 111 | }) 112 | } 113 | 114 | #[inline(always)] 115 | pub(super) fn to_12bit_iter(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 116 | buffer.chunks_exact(2).map(move |bytes| bytes.u16(is_le, 0) & 0x0fff) 117 | } 118 | #[inline(always)] 119 | pub(super) fn _to_12bit_left_aligned_iter(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 120 | buffer.chunks_exact(2).map(move |bytes| bytes.u16(is_le, 0) >> 4) 121 | } 122 | #[inline(always)] 123 | pub(super) fn to_12bit_iter_packed(buffer: &[u8], is_le: bool) -> impl Iterator + '_ { 124 | buffer.chunks_exact(3).flat_map(move |bytes| { 125 | let g1 = bytes[0] as u16; 126 | let g2 = bytes[1] as u16; 127 | let g3 = bytes[2] as u16; 128 | 129 | if is_le { 130 | [((g2 & 0x0f) << 8) | g1, (g3 << 4) | (g2 >> 4)] 131 | } else { 132 | [(g1 << 4) | (g2 >> 4), ((g2 & 0x0f) << 8) | g3] 133 | } 134 | }) 135 | } 136 | 137 | 138 | pub(super) fn matrix3_normalize(x: &mut [f32]) { 139 | assert!(x.len() == 9); 140 | x.chunks_exact_mut(3).for_each(|x| { 141 | let sum = x.iter().sum::(); 142 | x.iter_mut().for_each(|x| *x /= sum); 143 | }); 144 | } 145 | 146 | pub(super) fn matrix3_inverse(x: &mut [f32]) { 147 | assert!(x.len() == 9); 148 | let m11 = x[0]; 149 | let m12 = x[3]; 150 | let m13 = x[6]; 151 | 152 | let m21 = x[1]; 153 | let m22 = x[4]; 154 | let m23 = x[7]; 155 | 156 | let m31 = x[2]; 157 | let m32 = x[5]; 158 | let m33 = x[8]; 159 | 160 | let minor_m12_m23 = m22 * m33 - m32 * m23; 161 | let minor_m11_m23 = m21 * m33 - m31 * m23; 162 | let minor_m11_m22 = m21 * m32 - m31 * m22; 163 | 164 | let determinant = m11 * minor_m12_m23 - m12 * minor_m11_m23 + m13 * minor_m11_m22; 165 | 166 | x[0] = minor_m12_m23 / determinant; 167 | x[1] = (m13 * m32 - m33 * m12) / determinant; 168 | x[2] = (m12 * m23 - m22 * m13) / determinant; 169 | 170 | x[3] = -minor_m11_m23 / determinant; 171 | x[4] = (m11 * m33 - m31 * m13) / determinant; 172 | x[5] = (m13 * m21 - m23 * m11) / determinant; 173 | 174 | x[6] = minor_m11_m22 / determinant; 175 | x[7] = (m12 * m31 - m32 * m11) / determinant; 176 | x[8] = (m11 * m22 - m21 * m12) / determinant; 177 | } 178 | -------------------------------------------------------------------------------- /src/maker/sony.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use super::{decode_utility::bit_pump::*, decode_utility::lookup_table::*, utility::to_14bit_iter}; 4 | use std::cmp; 5 | 6 | use super::utility::GetNumFromBytes; 7 | 8 | use once_cell::sync::Lazy; 9 | 10 | pub(super) struct General { 11 | info: quickexif::ParsedInfo, 12 | } 13 | 14 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 15 | quickexif::describe_rule!(tiff { 16 | 0x0112 / orientation 17 | 0x0201 / preview_offset 18 | 0x0202 / preview_len 19 | }) 20 | }); 21 | 22 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 23 | quickexif::describe_rule!(tiff { 24 | 0x0112 / orientation 25 | 0x8769 { 26 | 0x9102 { 27 | r64 + 0 / compressed_bps 28 | } 29 | } 30 | 0x014a { 31 | 0x0103 / compression 32 | 0x0100 / width 33 | 0x0101 / height 34 | 0x0102 / bps 35 | 0x828e / cfa_pattern 36 | 0x0111 / strip 37 | 0x0117 / strip_len 38 | 0x7010? / tone_curve_addr 39 | 0xc61f? { 40 | u32 + 0 / crop_x 41 | u32 + 1 / crop_y 42 | } 43 | 0xc620? { 44 | u32 + 0 / crop_w 45 | u32 + 1 / crop_h 46 | } 47 | } 48 | 0xc634 { 49 | sony_decrypt / 0x7200 / 0x7201 / 0x7221 { 50 | 0x7310 { 51 | u16 + 0 / black_level 52 | } 53 | 0x7312 { 54 | u16 + 0 / white_balance_r 55 | u16 + 1 / white_balance_g 56 | u16 + 3 / white_balance_b 57 | } 58 | 0x787f / legacy_white_level { 59 | u16 + 0 / white_level 60 | } 61 | } 62 | } 63 | }) 64 | }); 65 | 66 | impl General { 67 | fn get_white_level_scale(&self) -> Result { 68 | let legacy_white_level = self.info.u16("legacy_white_level")?; 69 | let result = match legacy_white_level { 70 | 15360 => 4, 71 | _ => { 72 | let white_level = self.info.u16("white_level")?; 73 | match white_level { 74 | 15360 => 4, 75 | _ => 1, 76 | } 77 | } 78 | }; 79 | Ok(result) 80 | } 81 | } 82 | 83 | impl RawDecoder for General { 84 | fn new(info: quickexif::ParsedInfo) -> Self { 85 | General { info } 86 | } 87 | fn get_info(&self) -> &quickexif::ParsedInfo { 88 | &self.info 89 | } 90 | fn into_info(self) -> quickexif::ParsedInfo { 91 | self.info 92 | } 93 | 94 | fn get_bps_scale(&self) -> Result { 95 | let result = self.get_white_level_scale()?; 96 | Ok(result) 97 | } 98 | 99 | fn get_crop(&self) -> Option { 100 | let x = self.info.u32("crop_x").ok()?; 101 | let y = self.info.u32("crop_y").ok()?; 102 | let width = self.info.u32("crop_w").ok()?; 103 | let height = self.info.u32("crop_h").ok()?; 104 | 105 | Some(Crop { 106 | x, 107 | y, 108 | width, 109 | height, 110 | }) 111 | } 112 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 113 | let width = self.info.usize("width")?; 114 | let height = self.info.usize("height")?; 115 | let black_level = self.info.u16("black_level")?; 116 | let strip_offset = self.info.usize("strip")?; 117 | let strip_len = self.info.usize("strip_len")?; 118 | let compression = self.info.u32("compression")?; 119 | let level_scale = self.get_white_level_scale()?; 120 | let buf = &buffer[strip_offset..strip_offset + strip_len]; 121 | 122 | let black_level_sub = |v: u16| level_scale.saturating_mul(v.saturating_sub(black_level)); 123 | 124 | let image: Vec = match compression { 125 | 0x7fffu32 => { 126 | let tone_curve_addr = self.info.usize("tone_curve_addr")?; 127 | let tone_curve = buffer[tone_curve_addr..tone_curve_addr + 8] 128 | .chunks_exact(2) 129 | .map(|x| x.u16(self.info.is_le, 0)) 130 | .collect::>(); 131 | 132 | load_raw8(buf, &tone_curve, width, height) 133 | .iter() 134 | .copied() 135 | .map(black_level_sub) 136 | .collect() 137 | } 138 | 7 => unimplemented!(), 139 | _ => to_14bit_iter(buf, self.info.is_le) 140 | .map(black_level_sub) 141 | .collect(), 142 | }; 143 | 144 | if image.len() != width * height { 145 | Err(DecodingError::InvalidDecodedImageSize(image.len(), width * height)) 146 | } else { 147 | Ok(image) 148 | } 149 | } 150 | 151 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 152 | let offset = self.info.usize("preview_offset")?; 153 | let len = self.info.usize("preview_len")?; 154 | Ok(&buffer[offset..offset + len]) 155 | } 156 | } 157 | 158 | fn gen_curve_lut(tone_curve: &[u16]) -> LookupTable { 159 | let mut curve: [usize; 6] = [0, 0, 0, 0, 0, 4095]; 160 | 161 | for i in 0..4 { 162 | curve[i + 1] = ((tone_curve[i] as u32 >> 2) & 0xfff) as usize; 163 | } 164 | 165 | let mut table = vec![0u16; curve[5] + 1]; 166 | for i in 0..5 { 167 | for j in (curve[i] + 1)..(curve[i + 1] + 1) { 168 | table[j] = table[(j - 1)] + (1 << i); 169 | } 170 | } 171 | 172 | LookupTable::new(&table) 173 | } 174 | 175 | fn load_raw8(buf: &[u8], tone_curve: &[u16], width: usize, height: usize) -> Vec { 176 | let mut out: Vec = vec![0u16; width * height]; 177 | let curve = gen_curve_lut(tone_curve); 178 | 179 | out.chunks_exact_mut(width) 180 | .enumerate() 181 | .for_each(|(row_index, out)| { 182 | let mut pump = BitPumpLSB::new(&buf[(row_index * width)..]); 183 | 184 | let mut random = pump.peek_bits(16); 185 | for out in out.chunks_exact_mut(32) { 186 | // Process 32 pixels at a time in interleaved fashion 187 | for j in 0..2 { 188 | let max = pump.get_bits(11); 189 | let min = pump.get_bits(11); 190 | let delta = max - min; 191 | // Calculate the size of the data shift needed by how large the delta is 192 | // A delta with 11 bits requires a shift of 4, 10 bits of 3, etc 193 | let delta_shift: u32 = 194 | cmp::max(0, (32 - (delta.leading_zeros() as i32)) - 7) as u32; 195 | let imax = pump.get_bits(4) as usize; 196 | let imin = pump.get_bits(4) as usize; 197 | 198 | for i in 0..16 { 199 | let val = if i == imax { 200 | max 201 | } else if i == imin { 202 | min 203 | } else { 204 | cmp::min(0x7ff, (pump.get_bits(7) << delta_shift) + min) 205 | }; 206 | out[j + (i * 2)] = curve.dither((val << 1) as u16, &mut random); 207 | } 208 | } 209 | } 210 | }); 211 | out 212 | } 213 | -------------------------------------------------------------------------------- /src/maker/decode_utility/huffman.rs: -------------------------------------------------------------------------------- 1 | use super::DecodingError; 2 | 3 | use super::bit_pump::BitPump; 4 | use std::fmt; 5 | 6 | const DECODE_CACHE_BITS: u32 = 13; 7 | 8 | pub(in super::super) struct HuffTable { 9 | // These two fields directly represent the contents of a JPEG DHT marker 10 | pub(in super::super) bits: [u32; 17], 11 | pub(in super::super) huffval: [u32; 256], 12 | 13 | // Represent the weird shifts that are needed for some NEF files 14 | pub(in super::super) shiftval: [u32; 256], 15 | 16 | // Enable the workaround for 16 bit decodes in DNG that need to consume those 17 | // bits instead of the value being implied 18 | pub(in super::super) dng_bug: bool, 19 | 20 | // In CRW we only use the len code so the cache is not needed 21 | pub(in super::super) disable_cache: bool, 22 | 23 | // The remaining fields are computed from the above to allow more 24 | // efficient coding and decoding and thus private 25 | 26 | // The max number of bits in a huffman code and the table that converts those 27 | // bits into how many bits to consume and the decoded length and shift 28 | nbits: u32, 29 | hufftable: Vec<(u8, u8, u8)>, 30 | 31 | // A pregenerated table that goes straight to decoding a diff without first 32 | // finding a length, fetching bits, and sign extending them. The table is 33 | // sized by DECODE_CACHE_BITS and can have 99%+ hit rate with 13 bits 34 | decodecache: [Option<(u8, i16)>; 1 << DECODE_CACHE_BITS], 35 | 36 | initialized: bool, 37 | } 38 | 39 | struct MockPump { 40 | bits: u64, 41 | nbits: u32, 42 | } 43 | 44 | impl MockPump { 45 | pub(in super::super) fn empty() -> Self { 46 | MockPump { bits: 0, nbits: 0 } 47 | } 48 | 49 | pub(in super::super) fn set(&mut self, bits: u32, nbits: u32) { 50 | self.bits = (bits as u64) << 32; 51 | self.nbits = nbits + 32; 52 | } 53 | 54 | pub(in super::super) fn validbits(&self) -> i32 { 55 | self.nbits as i32 - 32 56 | } 57 | } 58 | 59 | impl BitPump for MockPump { 60 | fn peek_bits(&mut self, num: u32) -> u32 { 61 | (self.bits >> (self.nbits - num)) as u32 62 | } 63 | 64 | fn consume_bits(&mut self, num: u32) { 65 | self.nbits -= num; 66 | self.bits &= (1 << self.nbits) - 1; 67 | } 68 | } 69 | 70 | impl HuffTable { 71 | pub(in super::super) fn empty() -> HuffTable { 72 | HuffTable { 73 | bits: [0; 17], 74 | huffval: [0; 256], 75 | shiftval: [0; 256], 76 | dng_bug: false, 77 | disable_cache: false, 78 | 79 | nbits: 0, 80 | hufftable: Vec::new(), 81 | decodecache: [None; 1 << DECODE_CACHE_BITS], 82 | initialized: false, 83 | } 84 | } 85 | 86 | pub(in super::super) fn new(bits: [u32; 17], huffval: [u32; 256], dng_bug: bool) -> Result { 87 | let mut tbl = HuffTable { 88 | bits, 89 | huffval, 90 | shiftval: [0; 256], 91 | dng_bug, 92 | disable_cache: false, 93 | 94 | nbits: 0, 95 | hufftable: Vec::new(), 96 | decodecache: [None; 1 << DECODE_CACHE_BITS], 97 | initialized: false, 98 | }; 99 | tbl.initialize(); 100 | Ok(tbl) 101 | } 102 | 103 | pub(in super::super) fn initialize(&mut self) { 104 | // Find out the max code length and allocate a table with that size 105 | self.nbits = 16; 106 | for i in 0..16 { 107 | if self.bits[16 - i] != 0 { 108 | break; 109 | } 110 | self.nbits -= 1; 111 | } 112 | self.hufftable = vec![(0, 0, 0); 1 << self.nbits]; 113 | 114 | // Fill in the table itself 115 | let mut h = 0; 116 | let mut pos = 0; 117 | for len in 0..self.nbits { 118 | for _ in 0..self.bits[len as usize + 1] { 119 | for _ in 0..(1 << (self.nbits - len - 1)) { 120 | self.hufftable[h] = ( 121 | len as u8 + 1, 122 | self.huffval[pos] as u8, 123 | self.shiftval[pos] as u8, 124 | ); 125 | h += 1; 126 | } 127 | pos += 1; 128 | } 129 | } 130 | 131 | // Create the decode cache by running the slow code over all the possible 132 | // values DECODE_CACHE_BITS wide 133 | if !self.disable_cache { 134 | let mut pump = MockPump::empty(); 135 | let mut i = 0; 136 | loop { 137 | pump.set(i, DECODE_CACHE_BITS); 138 | let (bits, decode) = self.huff_decode_slow(&mut pump); 139 | if pump.validbits() >= 0 { 140 | self.decodecache[i as usize] = Some((bits, decode as i16)); 141 | } 142 | i += 1; 143 | if i >= 1 << DECODE_CACHE_BITS { 144 | break; 145 | } 146 | } 147 | } 148 | 149 | self.initialized = true; 150 | } 151 | 152 | #[inline(always)] 153 | pub(in super::super) fn huff_decode(&self, pump: &mut dyn BitPump) -> i32 { 154 | let code = pump.peek_bits(DECODE_CACHE_BITS) as usize; 155 | if let Some((bits, decode)) = self.decodecache[code] { 156 | match (decode, self.dng_bug) { 157 | // Special case: for -32768 no SSSS bits are stored 158 | (-32768, false) => { 159 | debug_assert!(bits > 16); 160 | pump.consume_bits(bits as u32 - 16); 161 | } 162 | _ => { 163 | pump.consume_bits(bits as u32); 164 | } 165 | } 166 | decode as i32 167 | } else { 168 | let decode = self.huff_decode_slow(pump); 169 | decode.1 170 | } 171 | } 172 | 173 | #[inline(always)] 174 | pub(in super::super) fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u8, i32) { 175 | let len = self.huff_len(pump); 176 | (len.0 + len.1, self.huff_diff(pump, len)) 177 | } 178 | 179 | #[inline(always)] 180 | pub(in super::super) fn huff_len(&self, pump: &mut dyn BitPump) -> (u8, u8, u8) { 181 | let code = pump.peek_bits(self.nbits) as usize; 182 | let (bits, len, shift) = self.hufftable[code]; 183 | pump.consume_bits(bits as u32); 184 | (bits, len, shift) 185 | } 186 | 187 | #[inline(always)] 188 | pub(in super::super) fn huff_diff(&self, pump: &mut dyn BitPump, input: (u8, u8, u8)) -> i32 { 189 | let (_, len, shift) = input; 190 | 191 | match len { 192 | 0 => 0, 193 | 16 => { 194 | if self.dng_bug { 195 | pump.get_bits(16); // consume can fail because we haven't peeked yet 196 | } 197 | -32768 198 | } 199 | len => { 200 | // decode the difference and extend sign bit 201 | let fulllen: i32 = len as i32 + shift as i32; 202 | let shift: i32 = shift as i32; 203 | let bits = pump.get_bits(len as u32) as i32; 204 | let mut diff: i32 = ((bits << 1) + 1) << shift >> 1; 205 | if (diff & (1 << (fulllen - 1))) == 0 { 206 | diff -= (1 << fulllen) - ((shift == 0) as i32); 207 | } 208 | diff 209 | } 210 | } 211 | } 212 | } 213 | 214 | impl fmt::Debug for HuffTable { 215 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 216 | if self.initialized { 217 | write!( 218 | f, 219 | "HuffTable {{ bits: {:?} huffval: {:?} }}", 220 | self.bits, 221 | &self.huffval[..] 222 | ) 223 | } else { 224 | write!(f, "HuffTable {{ uninitialized }}") 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/maker/decode_utility/bit_pump.rs: -------------------------------------------------------------------------------- 1 | use super::super::utility::GetNumFromBytes; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub(in super::super) struct BitPumpMSB<'a> { 5 | buffer: &'a [u8], 6 | pos: usize, 7 | bits: u64, 8 | nbits: u32, 9 | } 10 | 11 | impl<'a> BitPumpMSB<'a> { 12 | pub(in super::super) fn new(src: &'a [u8]) -> BitPumpMSB { 13 | BitPumpMSB { 14 | buffer: src, 15 | pos: 0, 16 | bits: 0, 17 | nbits: 0, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Debug, Copy, Clone)] 23 | pub(in super::super) struct BitPumpMSB32<'a> { 24 | buffer: &'a [u8], 25 | pos: usize, 26 | bits: u64, 27 | nbits: u32, 28 | } 29 | 30 | impl<'a> BitPumpMSB32<'a> { 31 | pub(in super::super) fn new(src: &'a [u8]) -> BitPumpMSB32 { 32 | BitPumpMSB32 { 33 | buffer: src, 34 | pos: 0, 35 | bits: 0, 36 | nbits: 0, 37 | } 38 | } 39 | 40 | // #[inline(always)] 41 | // pub(in super::super) fn get_pos(&self) -> usize { 42 | // self.pos - ((self.nbits >> 3) as usize) 43 | // } 44 | } 45 | 46 | impl<'a> BitPump for BitPumpMSB32<'a> { 47 | #[inline(always)] 48 | fn peek_bits(&mut self, num: u32) -> u32 { 49 | if num > self.nbits { 50 | let inbits: u64 = self.buffer.u32le(self.pos) as u64; 51 | self.bits = (self.bits << 32) | inbits; 52 | self.pos += 4; 53 | self.nbits += 32; 54 | } 55 | (self.bits >> (self.nbits - num)) as u32 56 | } 57 | 58 | #[inline(always)] 59 | fn consume_bits(&mut self, num: u32) { 60 | self.nbits -= num; 61 | self.bits &= (1 << self.nbits) - 1; 62 | } 63 | } 64 | #[derive(Debug, Copy, Clone)] 65 | pub(in super::super) struct BitPumpJPEG<'a> { 66 | buffer: &'a [u8], 67 | pos: usize, 68 | bits: u64, 69 | nbits: u32, 70 | finished: bool, 71 | } 72 | 73 | impl<'a> BitPumpJPEG<'a> { 74 | pub(in super::super) fn new(src: &'a [u8]) -> BitPumpJPEG { 75 | BitPumpJPEG { 76 | buffer: src, 77 | pos: 0, 78 | bits: 0, 79 | nbits: 0, 80 | finished: false, 81 | } 82 | } 83 | } 84 | 85 | impl<'a> BitPump for BitPumpJPEG<'a> { 86 | #[inline(always)] 87 | fn peek_bits(&mut self, num: u32) -> u32 { 88 | if num > self.nbits && !self.finished { 89 | if self.pos < self.buffer.len() - 4 90 | && self.buffer[self.pos] != 0xff 91 | && self.buffer[self.pos + 1] != 0xff 92 | && self.buffer[self.pos + 2] != 0xff 93 | && self.buffer[self.pos + 3] != 0xff 94 | { 95 | let inbits: u64 = self.buffer.u32be(self.pos) as u64; 96 | self.bits = (self.bits << 32) | inbits; 97 | self.pos += 4; 98 | self.nbits += 32; 99 | } else { 100 | // Read 32 bits the hard way 101 | let mut read_bytes = 0; 102 | while read_bytes < 4 && !self.finished { 103 | let byte = { 104 | if self.pos >= self.buffer.len() { 105 | self.finished = true; 106 | 0 107 | } else { 108 | let nextbyte = self.buffer[self.pos]; 109 | if nextbyte != 0xff { 110 | nextbyte 111 | } else if self.buffer[self.pos + 1] == 0x00 { 112 | self.pos += 1; // Skip the extra byte used to mark 255 113 | nextbyte 114 | } else { 115 | self.finished = true; 116 | 0 117 | } 118 | } 119 | }; 120 | self.bits = (self.bits << 8) | (byte as u64); 121 | self.pos += 1; 122 | self.nbits += 8; 123 | read_bytes += 1; 124 | } 125 | } 126 | } 127 | if num > self.nbits && self.finished { 128 | // Stuff with zeroes to not fail to read 129 | self.bits <<= 32; 130 | self.nbits += 32; 131 | } 132 | 133 | (self.bits >> (self.nbits - num)) as u32 134 | } 135 | 136 | #[inline(always)] 137 | fn consume_bits(&mut self, num: u32) { 138 | self.nbits -= num; 139 | self.bits &= (1 << self.nbits) - 1; 140 | } 141 | } 142 | #[derive(Debug, Copy, Clone)] 143 | pub(in super::super) struct BitPumpLSB<'a> { 144 | buffer: &'a [u8], 145 | pos: usize, 146 | bits: u64, 147 | nbits: u32, 148 | } 149 | 150 | impl<'a> BitPumpLSB<'a> { 151 | pub(in super::super) fn new(src: &'a [u8]) -> BitPumpLSB { 152 | BitPumpLSB { 153 | buffer: src, 154 | pos: 0, 155 | bits: 0, 156 | nbits: 0, 157 | } 158 | } 159 | } 160 | 161 | pub(in super::super) struct BitPumpPanasonic<'a> { 162 | buffer: &'a [u8], 163 | pos: usize, 164 | nbits: u32, 165 | split: bool, 166 | } 167 | 168 | impl<'a> BitPumpPanasonic<'a> { 169 | pub(in super::super) fn new(src: &'a [u8], split: bool) -> BitPumpPanasonic { 170 | BitPumpPanasonic { 171 | buffer: src, 172 | pos: 0, 173 | nbits: 0, 174 | split, 175 | } 176 | } 177 | } 178 | 179 | pub(in super::super) trait BitPump { 180 | fn peek_bits(&mut self, num: u32) -> u32; 181 | fn consume_bits(&mut self, num: u32); 182 | 183 | #[inline(always)] 184 | fn get_bits(&mut self, num: u32) -> u32 { 185 | if num == 0 { 186 | return 0; 187 | } 188 | 189 | let val = self.peek_bits(num); 190 | self.consume_bits(num); 191 | 192 | val 193 | } 194 | 195 | #[inline(always)] 196 | fn peek_ibits(&mut self, num: u32) -> i32 { 197 | self.peek_bits(num) as i32 198 | } 199 | 200 | #[inline(always)] 201 | fn get_ibits(&mut self, num: u32) -> i32 { 202 | self.get_bits(num) as i32 203 | } 204 | 205 | // Sign extend ibits 206 | #[inline(always)] 207 | fn get_ibits_sextended(&mut self, num: u32) -> i32 { 208 | let val = self.get_ibits(num); 209 | val.wrapping_shl(32 - num).wrapping_shr(32 - num) 210 | } 211 | } 212 | 213 | impl<'a> BitPump for BitPumpLSB<'a> { 214 | #[inline(always)] 215 | fn peek_bits(&mut self, num: u32) -> u32 { 216 | if num > self.nbits { 217 | let inbits: u64 = self.buffer.u32le(self.pos) as u64; 218 | self.bits = ((inbits << 32) | (self.bits << (32 - self.nbits))) >> (32 - self.nbits); 219 | self.pos += 4; 220 | self.nbits += 32; 221 | } 222 | (self.bits & (0x0ffffffffu64 >> (32 - num))) as u32 223 | } 224 | 225 | #[inline(always)] 226 | fn consume_bits(&mut self, num: u32) { 227 | self.nbits -= num; 228 | self.bits >>= num; 229 | } 230 | } 231 | 232 | impl<'a> BitPump for BitPumpMSB<'a> { 233 | #[inline(always)] 234 | fn peek_bits(&mut self, num: u32) -> u32 { 235 | if num > self.nbits { 236 | let inbits: u64 = self.buffer.u32be(self.pos) as u64; 237 | self.bits = (self.bits << 32) | inbits; 238 | self.pos += 4; 239 | self.nbits += 32; 240 | } 241 | (self.bits >> (self.nbits - num)) as u32 242 | } 243 | 244 | #[inline(always)] 245 | fn consume_bits(&mut self, num: u32) { 246 | self.nbits -= num; 247 | self.bits &= (1 << self.nbits) - 1; 248 | } 249 | } 250 | 251 | impl<'a> BitPump for BitPumpPanasonic<'a> { 252 | fn peek_bits(&mut self, num: u32) -> u32 { 253 | if num > self.nbits { 254 | self.nbits += 0x4000 * 8; 255 | self.pos += 0x4000; 256 | } 257 | let mut byte = (self.nbits - num) >> 3 ^ 0x3ff0; 258 | if self.split { 259 | byte = (byte + 0x4000 - 0x2008) % 0x4000; 260 | } 261 | let bits = self.buffer.u16le(byte as usize + self.pos - 0x4000) as u32; 262 | (bits >> ((self.nbits - num) & 7)) & (0x0ffffffffu32 >> (32 - num)) 263 | } 264 | 265 | fn consume_bits(&mut self, num: u32) { 266 | self.nbits -= num; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/lib_wasm.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::io::Cursor; 4 | 5 | use decode::CFAPattern; 6 | use pass::*; 7 | 8 | use crate::{ 9 | decode::Orientation, 10 | utility::{log2, ArrayMulNum}, 11 | }; 12 | 13 | use super::*; 14 | use wasm_bindgen::prelude::*; 15 | 16 | #[wasm_bindgen] 17 | extern "C" { 18 | #[wasm_bindgen(js_namespace = console)] 19 | fn log(s: &str); 20 | } 21 | 22 | #[wasm_bindgen] 23 | pub struct Image { 24 | pub width: usize, 25 | pub height: usize, 26 | pub orientation: isize, 27 | data: Vec, 28 | white_balance: [f32; 3], 29 | color_matrix: [f32; 9], 30 | } 31 | 32 | #[wasm_bindgen] 33 | impl Image { 34 | #[wasm_bindgen(getter)] 35 | pub fn white_balance(&self) -> Vec { 36 | self.white_balance.to_vec() 37 | } 38 | #[wasm_bindgen(getter)] 39 | pub fn color_matrix(&self) -> Vec { 40 | self.color_matrix.to_vec() 41 | } 42 | #[wasm_bindgen(getter)] 43 | pub fn data(self) -> Vec { 44 | self.data 45 | } 46 | } 47 | 48 | fn expand_err>(input: Result) -> Result { 49 | input.map_err(|e| JsError::new(&format!("{:?}", anyhow::anyhow!(e)))) 50 | } 51 | 52 | macro_rules! gen_image_loader { 53 | ($name:ident, $rggb:ident, $grbg:ident, $gbrg:ident, $bggr:ident) => { 54 | #[wasm_bindgen] 55 | pub fn $name(input: Vec) -> Result { 56 | let decoded_image = expand_err(decode::decode_buffer(input))?; 57 | 58 | let image = decoded_image.image; 59 | let orientation = decoded_image.orientation as isize; 60 | let width = decoded_image.width; 61 | let height = decoded_image.height; 62 | 63 | let color_matrix = utility::matrix3_mul(&data::XYZ2SRGB, &decoded_image.cam_matrix); 64 | let white_balance = { 65 | let [r, g, b] = decoded_image.white_balance; 66 | [r as f32 / g as f32, 1f32, b as f32 / g as f32] 67 | }; 68 | 69 | if image.len() == width * height * 3 { 70 | return Ok(Image { 71 | data: image, 72 | orientation, 73 | width, 74 | height, 75 | white_balance, 76 | color_matrix, 77 | }) 78 | } 79 | 80 | let iter = image.iter().copied(); 81 | let data = pass::iters_to_vec!( 82 | iter 83 | ..enumerate() 84 | [decoded_image.cfa_pattern] { 85 | CFAPattern::RGGB => .$rggb(&image, width, height), 86 | CFAPattern::GRBG => .$grbg(&image, width, height), 87 | CFAPattern::GBRG => .$gbrg(&image, width, height), 88 | CFAPattern::BGGR => .$bggr(&image, width, height), 89 | CFAPattern::XTrans0 => .linear_xtrans0(&image, width, height), 90 | CFAPattern::XTrans1 => .linear_xtrans1(&image, width, height) 91 | } 92 | ..flatten() 93 | ); 94 | 95 | Ok(Image { 96 | data, 97 | orientation, 98 | width, 99 | height, 100 | white_balance, 101 | color_matrix, 102 | }) 103 | } 104 | }; 105 | } 106 | gen_image_loader!( 107 | load_image, 108 | linear_rggb, 109 | linear_grbg, 110 | linear_gbrg, 111 | linear_bggr 112 | ); 113 | gen_image_loader!( 114 | load_image_enhanced, 115 | elinear_rggb, 116 | elinear_grbg, 117 | elinear_gbrg, 118 | elinear_bggr 119 | ); 120 | 121 | #[wasm_bindgen] 122 | pub fn calc_histogram(pixels: Vec) -> Vec { 123 | let mut histogram = [0u32; 256 * 3 + 1]; 124 | let mut max = 0u32; 125 | 126 | for point in pixels.chunks_exact(4) { 127 | if let &[r, g, b, _] = point { 128 | let r_index = r as usize; 129 | let g_index = 256 + g as usize; 130 | let b_index = 256 * 2 + b as usize; 131 | 132 | max = [r_index, g_index, b_index] 133 | .into_iter() 134 | .fold(max, |acc, index| { 135 | histogram[index] += 1; 136 | acc.max(histogram[index]) 137 | }); 138 | } 139 | } 140 | 141 | if let Some(last) = histogram.last_mut() { 142 | *last = max; 143 | } 144 | 145 | histogram.to_vec() 146 | } 147 | 148 | #[cfg(feature = "image")] 149 | #[wasm_bindgen] 150 | pub fn encode_to_jpeg( 151 | pixels_ptr: *mut u8, 152 | width: u32, 153 | height: u32, 154 | quality: u8, 155 | ) -> Result, JsError> { 156 | use image::codecs::jpeg; 157 | use image::ColorType; 158 | 159 | let len = (width * height * 4) as usize; 160 | let pixels = unsafe { Vec::from_raw_parts(pixels_ptr, len, len) }; 161 | 162 | let mut writer = Cursor::new(vec![]); 163 | let mut encoder = jpeg::JpegEncoder::new_with_quality(&mut writer, quality); 164 | expand_err(encoder.encode(&pixels, width, height, ColorType::Rgba8))?; 165 | 166 | Ok(writer.into_inner()) 167 | } 168 | 169 | #[wasm_bindgen] 170 | pub struct ExifWithThumbnail { 171 | pub orientation: isize, 172 | exif: String, 173 | thumbnail: Vec, 174 | } 175 | 176 | #[wasm_bindgen] 177 | impl ExifWithThumbnail { 178 | #[wasm_bindgen(getter)] 179 | pub fn thumbnail(self) -> Vec { 180 | self.thumbnail 181 | } 182 | #[wasm_bindgen(getter)] 183 | pub fn exif(&self) -> String { 184 | self.exif.clone() 185 | } 186 | } 187 | 188 | fn quick_image_load( 189 | input: Vec, 190 | ) -> Result<(Vec, u32, u32, Orientation), RawFileReadingError> { 191 | let decoded_image = decode::decode_buffer(input)?; 192 | 193 | let width = decoded_image.width; 194 | let image = decoded_image 195 | .image 196 | .chunks(width * 4) 197 | .enumerate() 198 | .map(|(row_index, row)| { 199 | let left_bound = row_index % 4 * width; 200 | row[left_bound..left_bound + width] 201 | .chunks(4) 202 | .enumerate() 203 | .map(|(column_index, values)| values[column_index % 4]) 204 | .collect::>() 205 | }) 206 | .flatten() 207 | .collect::>(); 208 | 209 | let orientation = decoded_image.orientation; 210 | let width = decoded_image.width / 4; 211 | let height = decoded_image.height / 4; 212 | 213 | let gamma_lut = gen_gamma_lut(0.45); 214 | let color_matrix = utility::matrix3_mul(&data::XYZ2SRGB, &decoded_image.cam_matrix); 215 | let color_matrix = color_matrix.mul(1 << BIT_SHIFT); 216 | let white_balance = decoded_image 217 | .white_balance 218 | .mul(1 << (BIT_SHIFT - log2(decoded_image.white_balance[1]))); 219 | 220 | let iter = image.iter().copied(); 221 | let data = pass::iters_to_vec!( 222 | iter 223 | ..enumerate() 224 | [decoded_image.cfa_pattern] { 225 | CFAPattern::RGGB => .linear_rggb(&image, width, height), 226 | CFAPattern::GRBG => .linear_grbg(&image, width, height), 227 | CFAPattern::GBRG => .linear_gbrg(&image, width, height), 228 | CFAPattern::BGGR => .linear_bggr(&image, width, height), 229 | CFAPattern::XTrans0 => .linear_xtrans0(&image, width, height), 230 | CFAPattern::XTrans1 => .linear_xtrans1(&image, width, height) 231 | } 232 | .u16rgb_to_i32rgb() 233 | .white_balance_fix(&white_balance) 234 | .color_convert(&color_matrix) 235 | .gamma_correct(&gamma_lut) 236 | .u16rgb_to_u8rgb() 237 | ..flatten() 238 | ); 239 | 240 | Ok((data, width as u32, height as u32, orientation)) 241 | } 242 | 243 | #[wasm_bindgen] 244 | pub fn load_exif_with_thumbnail(buffer: Vec) -> Result { 245 | let info = expand_err(decode::get_exif_info(&buffer))?; 246 | let exif = info.stringify_all()?; 247 | let (thumbnail, orientation) = match decode::get_thumbnail(&buffer) { 248 | Ok((data, orientation)) => (data.to_vec(), orientation), 249 | Err(_) => { 250 | use image::codecs::jpeg; 251 | use image::ColorType; 252 | 253 | let (data, width, height, orientation) = quick_image_load(buffer)?; 254 | let mut writer = Cursor::new(vec![]); 255 | let mut encoder = jpeg::JpegEncoder::new_with_quality(&mut writer, 80); 256 | expand_err(encoder.encode(&data, width, height, ColorType::Rgb8))?; 257 | 258 | (writer.into_inner(), orientation) 259 | } 260 | }; 261 | 262 | Ok(ExifWithThumbnail { 263 | orientation: orientation as isize, 264 | thumbnail, 265 | exif, 266 | }) 267 | } 268 | -------------------------------------------------------------------------------- /src/maker/adobe.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use once_cell::sync::Lazy; 3 | 4 | use super::utility::GetNumFromBytes; 5 | use super::{decode_utility::ljpeg::LjpegDecompressor, utility::*}; 6 | 7 | pub(super) struct General { 8 | info: quickexif::ParsedInfo, 9 | } 10 | 11 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 12 | quickexif::describe_rule!(tiff { 13 | 0x0112 : u16 / orientation 14 | 0x014a? / sub_ifd(sub_ifd_count) 15 | 0x828e? / cfa_pattern 16 | if sub_ifd_count ? 17 | { 18 | if sub_ifd_count > 2 19 | { 20 | 0x014a { 21 | offset + 8 { 22 | offset address { 23 | 0x0111 / thumbnail 24 | 0x0117 / thumbnail_len 25 | } 26 | } 27 | } 28 | } 29 | else 30 | { 31 | if sub_ifd_count > 1 32 | { 33 | 0x014a { 34 | offset + 4 { 35 | offset address { 36 | 0x0111 / thumbnail 37 | 0x0117 / thumbnail_len 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | if cfa_pattern ? { 45 | 46 | } else { 47 | 0x0100 : u16 / orientation // use width tag to force Horizontal orientation 48 | 0x0111 / thumbnail 49 | 0x0117 / thumbnail_len 50 | } 51 | }) 52 | }); 53 | 54 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 55 | let template_rule = quickexif::describe_rule!(template { 56 | 0x0100 / width 57 | 0x0101 / height 58 | 0x0102 : u16 / bps 59 | 0x0103 : u16 / compression 60 | 0x828e? / cfa_pattern 61 | 0xc61d / wl(white_level_len) 62 | 63 | if white_level_len == 1 64 | { 65 | 0xc61d : u16 / white_level 66 | } 67 | else 68 | { 69 | 0xc61d { 70 | u16 + 0 / white_level 71 | } 72 | } 73 | 0xc61a / bl(black_level_len) 74 | if black_level_len == 1 { 75 | 0xc61a : u16 / black_level 76 | } else { 77 | 0xc61a { 78 | r64 + 0 / black_level 79 | } 80 | } 81 | 0x0111? / strip(strip_offsets_count) 82 | if strip ? 83 | { 84 | 0x0117 / strip_len 85 | } 86 | else 87 | { 88 | 0x0144 / tile_offsets(tile_offsets_count) 89 | 0x0145 / tile_byte_counts 90 | 0x0142 / tile_width 91 | 0x0143 / tile_len 92 | } 93 | if is_adobe_dng_converted ? { 94 | 0xc61f? { 95 | r64 + 0 / crop_x 96 | r64 + 1 / crop_y 97 | } 98 | 0xc620? { 99 | r64 + 0 / crop_width 100 | r64 + 1 / crop_height 101 | } 102 | } else { 103 | 0xc61f? / crop_origin 104 | 0xc620? / crop_size 105 | } 106 | }); 107 | 108 | quickexif::describe_rule!(tiff { 109 | 0x0112: u16 / orientation 110 | 0x00fe / sub_file_type 111 | 0xc628 { 112 | r64 + 0 / white_balance_r 113 | r64 + 1 / white_balance_g 114 | r64 + 2 / white_balance_b 115 | } 116 | 0xc717? / is_adobe_dng_converted 117 | if sub_file_type == 0 118 | { 119 | load(template_rule) 120 | } 121 | else 122 | { 123 | 0x014a / sub_ifd(sub_ifd_count) 124 | if sub_ifd_count == 1 125 | { 126 | 0x014a { 127 | 0x00fe / sub_file_type1 128 | if sub_file_type1 == 0 129 | { 130 | load(template_rule) 131 | } 132 | } 133 | } 134 | else 135 | { 136 | 0x014a { 137 | offset address { 138 | 0x00fe / sub_file_type2 139 | if sub_file_type2 == 0 140 | { 141 | load(template_rule) 142 | } 143 | } 144 | } 145 | } 146 | } 147 | }) 148 | }); 149 | 150 | impl General { 151 | fn get_white_level_scale(&self) -> Result { 152 | let white_level = self.info.u16("white_level")?; 153 | Ok(u16::MAX / white_level) 154 | } 155 | } 156 | 157 | impl RawDecoder for General { 158 | fn new(info: quickexif::ParsedInfo) -> Self { 159 | General { info } 160 | } 161 | fn get_info(&self) -> &quickexif::ParsedInfo { 162 | &self.info 163 | } 164 | fn into_info(self) -> quickexif::ParsedInfo { 165 | self.info 166 | } 167 | fn get_white_balance(&self) -> Result<[i32; 3], DecodingError> { 168 | let r = 512.0 / self.info.f64("white_balance_r")?; 169 | let g = 512.0 / self.info.f64("white_balance_g")?; 170 | let b = 512.0 / self.info.f64("white_balance_b")?; 171 | Ok([r as i32, g as i32, b as i32]) 172 | } 173 | fn get_crop(&self) -> Option { 174 | if let (Ok(crop_origin), Ok(crop_size)) = 175 | (self.info.u8a4("crop_origin"), self.info.u8a4("crop_size")) 176 | { 177 | let x = crop_origin.as_slice().u16(self.info.is_le, 0) as u32; 178 | let y = crop_origin.as_slice().u16(self.info.is_le, 2) as u32; 179 | let width = crop_size.as_slice().u16(self.info.is_le, 0) as u32; 180 | let height = crop_size.as_slice().u16(self.info.is_le, 2) as u32; 181 | Some(Crop { 182 | x, 183 | y, 184 | width, 185 | height, 186 | }) 187 | } else { 188 | let x = self.info.f64("crop_x").ok()? as u32; 189 | let y = self.info.f64("crop_y").ok()? as u32; 190 | let width = self.info.f64("crop_width").ok()? as u32; 191 | let height = self.info.f64("crop_height").ok()? as u32; 192 | Some(Crop { 193 | x, 194 | y, 195 | width, 196 | height, 197 | }) 198 | } 199 | } 200 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 201 | let offset = self.info.usize("thumbnail")?; 202 | let len = self.info.usize("thumbnail_len")?; 203 | 204 | Ok(&buffer[offset..offset + len]) 205 | } 206 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 207 | let width = self.info.usize("width")?; 208 | let height = self.info.usize("height")?; 209 | let compression = self.info.u16("compression")?; 210 | let bps = self.info.u16("bps")?; 211 | let white_level_scale = self.get_white_level_scale()?; 212 | let black_level = self.info.u16("black_level")?; 213 | 214 | macro_rules! to_image { 215 | ($iter:expr) => { 216 | $iter 217 | .map(|x| white_level_scale.saturating_mul(x.saturating_sub(black_level))) 218 | .collect() 219 | }; 220 | } 221 | 222 | let image: Vec = match compression { 223 | 1 => { // uncompressed dng 224 | let offset_addr = self.info.usize("strip")?; 225 | let offset_count = self.info.usize("strip_offsets_count")?; 226 | let len_addr = self.info.usize("strip_len")?; 227 | 228 | let buf = if (offset_count) > 1 { 229 | let strip_addr = buffer.u32(self.info.is_le, offset_addr) as usize; 230 | let tile_count = buffer.u32(self.info.is_le, len_addr) as usize; 231 | &buffer[strip_addr..strip_addr + tile_count * offset_count] 232 | } else { 233 | &buffer[offset_addr..offset_addr + len_addr] 234 | }; 235 | 236 | match bps { 237 | 12 => to_image!(to_12bit_iter_packed(buf, self.info.is_le)), 238 | 14 => to_image!(to_14bit_iter_packed(buf, self.info.is_le)), 239 | _ => to_image!(to_16bit_iter(buf, self.info.is_le)), 240 | } 241 | } 242 | 7 => { 243 | // only support Apple ProRaw now 244 | let byte_counts_addr = self.info.usize("tile_byte_counts")?; 245 | let tile_offsets_count = self.info.usize("tile_offsets_count")?; 246 | let tile_offsets_addr = self.info.usize("tile_offsets")?; 247 | let tile_width = self.info.usize("tile_width")?; 248 | let tile_len = self.info.usize("tile_len")?; 249 | 250 | let offsets_iter = 251 | buffer[tile_offsets_addr..tile_offsets_addr + 4 * tile_offsets_count].chunks(4); 252 | let counts_iter = 253 | buffer[byte_counts_addr..byte_counts_addr + 4 * tile_offsets_count].chunks(4); 254 | 255 | let tiles = offsets_iter 256 | .zip(counts_iter) 257 | .map(|(offset_bytes, count_bytes)| { 258 | let offset_addr = offset_bytes.u32(self.info.is_le, 0) as usize; 259 | let count_addr = count_bytes.u32(self.info.is_le, 0) as usize; 260 | (offset_addr, count_addr) 261 | }) 262 | .collect::>(); 263 | 264 | load_compressed(buffer, width, height, tiles, tile_width, tile_len)? 265 | } 266 | _ => { 267 | unimplemented!() 268 | } 269 | }; 270 | 271 | if image.len() != width * height && image.len() != width * height * 3 { 272 | Err(DecodingError::InvalidDecodedImageSize( 273 | image.len(), 274 | width * height, 275 | )) 276 | } else { 277 | Ok(image) 278 | } 279 | } 280 | } 281 | 282 | fn load_compressed( 283 | buffer: &[u8], 284 | width: usize, 285 | height: usize, 286 | tiles: Vec<(usize, usize)>, 287 | tile_width: usize, 288 | tile_height: usize, 289 | ) -> Result, DecodingError> { 290 | let mut out = vec![0u16; width * height * 3]; 291 | 292 | let tile_count_per_row = width / tile_width; 293 | 294 | for (tile_index, (addr, size)) in tiles.into_iter().enumerate() { 295 | let col = tile_index % tile_count_per_row * tile_width * 3; 296 | let row = tile_index / tile_count_per_row * tile_height; 297 | 298 | let mut tile_out = vec![0u16; tile_width * tile_height * 3]; 299 | 300 | let src = &buffer[addr..addr + size]; 301 | let decompressor = LjpegDecompressor::new(src)?; 302 | 303 | decompressor.decode( 304 | &mut tile_out, 305 | 0, 306 | tile_width * 3, 307 | tile_width * 3, 308 | tile_height, 309 | )?; 310 | 311 | tile_out 312 | .chunks(tile_width * 3) 313 | .enumerate() 314 | .for_each(|(offset_row, data)| { 315 | let start = col + (row + offset_row) * width * 3; 316 | let end = start + tile_width * 3; 317 | (&mut out[start..end]).copy_from_slice(data); 318 | }); 319 | } 320 | 321 | Ok(out) 322 | } 323 | -------------------------------------------------------------------------------- /src/pass/demosaicing/enhanced_linear.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[inline(always)] 4 | fn get_window(image: &[u16], i: usize, w: usize) -> [i32; 9] { 5 | if let (Some(&[v1, v2, v3]), Some(&[v4, v5, v6]), Some(&[v7, v8, v9])) = ( 6 | image.get(i - w - 1..=i - w + 1), 7 | image.get(i - 1..=i + 1), 8 | image.get(i + w - 1..=i + w + 1), 9 | ) { 10 | [ 11 | v1 as i32, v2 as i32, v3 as i32, v4 as i32, v5 as i32, v6 as i32, v7 as i32, v8 as i32, 12 | v9 as i32, 13 | ] 14 | } else { 15 | [0; 9] 16 | } 17 | } 18 | 19 | #[inline(always)] 20 | fn clamp(x: i32) -> u16 { 21 | use std::cmp::{max, min}; 22 | min(max(x, 0), 65535) as u16 23 | } 24 | 25 | const DIFF_ALPHA: i32 = 16; 26 | 27 | #[inline(always)] 28 | fn calc_pixel_at_rb(image: &[u16], i: usize, w: usize) -> [u16; 3] { 29 | let [v1, v2, v3, v4, v5, v6, v7, v8, v9] = get_window(image, i, w); 30 | let mut green_lst = [v2, v4, v6, v8]; 31 | green_lst.sort(); 32 | 33 | let g = if let [Some(&p1), Some(&p2), Some(&p3), Some(&p4)] = [ 34 | i.checked_sub(w * 2).and_then(|i| image.get(i)), 35 | i.checked_sub(2).and_then(|i| image.get(i)), 36 | image.get(i + w * 2), 37 | image.get(i + 2), 38 | ] { 39 | let (n, w, s, e) = (p1 as i32, p2 as i32, p3 as i32, p4 as i32); 40 | let diff_p = v5 * 4 - (n + w + s + e); 41 | (green_lst[1] + green_lst[2]) / 2 + diff_p / DIFF_ALPHA 42 | } else { 43 | (green_lst[1] + green_lst[2]) / 2 44 | }; 45 | 46 | let diff_g = g * 4 - (v2 + v4 + v6 + v8); 47 | let third = (v1 + v3 + v7 + v9) / 4; 48 | let third = third + diff_g / DIFF_ALPHA; 49 | 50 | [v5 as u16, clamp(g), clamp(third)] 51 | } 52 | #[inline(always)] 53 | fn calc_pixel_at_g(image: &[u16], i: usize, w: usize) -> [u16; 3] { 54 | let [v1, v2, v3, v4, v5, v6, v7, v8, v9] = get_window(image, i, w); 55 | let diff_g = v5 * 4 - (v1 + v3 + v7 + v9); 56 | let horiz = (v4 + v6) / 2 + diff_g / DIFF_ALPHA; 57 | let vert = (v2 + v8) / 2 + diff_g / DIFF_ALPHA; 58 | 59 | [clamp(horiz), v5 as u16, clamp(vert)] 60 | } 61 | 62 | #[inline(always)] 63 | pub(super) fn rggb(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 64 | match bayer_pixel_info(i, w, h) { 65 | // top left corner 66 | (true, _, true, _, _, _) => [v, avg(image, &[i + 1, i + w]), get_pixel(image, i + w + 1)], 67 | // top right corner 68 | (true, _, _, true, _, _) => [get_pixel(image, i - 1), v, get_pixel(image, i + w)], 69 | // bottom left corner 70 | (_, true, true, _, _, _) => [get_pixel(image, i - w), v, get_pixel(image, i + 1)], 71 | // bottom right corner 72 | (_, true, _, true, _, _) => [get_pixel(image, i - w - 1), avg(image, &[i - w, i - 1]), v], 73 | // top edge 74 | (true, _, _, _, true, _) => [ 75 | v, 76 | avg(image, &[i - 1, i + w, i + 1]), 77 | avg(image, &[i + w - 1, i + w + 1]), 78 | ], 79 | (true, _, _, _, false, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i + w)], 80 | // bottom edge 81 | (_, true, _, _, true, _) => [get_pixel(image, i - w), v, avg(image, &[i - 1, i + 1])], 82 | (_, true, _, _, false, _) => [get_pixel(image, i - w - 1), avg(image, &[i - w, i - 1]), v], 83 | // left edge 84 | (_, _, true, _, _, true) => [ 85 | v, 86 | avg(image, &[i - w, i + 1, i + w]), 87 | avg(image, &[i - w + 1, i + w + 1]), 88 | ], 89 | (_, _, true, _, _, false) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i + 1)], 90 | // right edge 91 | (_, _, _, true, _, true) => [get_pixel(image, i - 1), v, get_pixel(image, i + w)], 92 | (_, _, _, true, _, false) => [ 93 | avg(image, &[i - w - 1, i + w - 1]), 94 | avg(image, &[i - w, i + w, i - 1]), 95 | v, 96 | ], 97 | // red 98 | (_, _, _, _, true, true) => calc_pixel_at_rb(image, i, w), 99 | // green1 100 | (_, _, _, _, false, true) => calc_pixel_at_g(image, i, w), 101 | // green2 102 | (_, _, _, _, true, false) => { 103 | let [horiz, g, vert] = calc_pixel_at_g(image, i, w); 104 | [vert, g, horiz] 105 | } 106 | // blue 107 | (_, _, _, _, false, false) => { 108 | let [b, g, r] = calc_pixel_at_rb(image, i, w); 109 | [r, g, b] 110 | } 111 | } 112 | } 113 | 114 | #[inline(always)] 115 | pub(super) fn bggr(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 116 | match bayer_pixel_info(i, w, h) { 117 | // top left corner 118 | (true, _, true, _, _, _) => [get_pixel(image, i + w + 1), avg(image, &[i + 1, i + w]), v], 119 | // top right corner 120 | (true, _, _, true, _, _) => [get_pixel(image, i + w), v, get_pixel(image, i - 1)], 121 | // bottom left corner 122 | (_, true, true, _, _, _) => [get_pixel(image, i + 1), v, get_pixel(image, i - w)], 123 | // bottom right corner 124 | (_, true, _, true, _, _) => [v, avg(image, &[i - w, i - 1]), get_pixel(image, i - w - 1)], 125 | // top edge 126 | (true, _, _, _, true, _) => [ 127 | avg(image, &[i + w - 1, i + w + 1]), 128 | avg(image, &[i - 1, i + w, i + 1]), 129 | v, 130 | ], 131 | (true, _, _, _, false, _) => [get_pixel(image, i + w), v, avg(image, &[i - 1, i + 1])], 132 | // bottom edge 133 | (_, true, _, _, true, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i - w)], 134 | (_, true, _, _, false, _) => [v, avg(image, &[i - w, i - 1]), get_pixel(image, i - w - 1)], 135 | // left edge 136 | (_, _, true, _, _, true) => [ 137 | avg(image, &[i - w + 1, i + w + 1]), 138 | avg(image, &[i - w, i + 1, i + w]), 139 | v, 140 | ], 141 | (_, _, true, _, _, false) => [get_pixel(image, i + 1), v, avg(image, &[i - w, i + w])], 142 | // right edge 143 | (_, _, _, true, _, true) => [get_pixel(image, i + w), v, get_pixel(image, i - 1)], 144 | (_, _, _, true, _, false) => [ 145 | v, 146 | avg(image, &[i - w, i + w, i - 1]), 147 | avg(image, &[i - w - 1, i + w - 1]), 148 | ], 149 | // blue 150 | (_, _, _, _, true, true) => { 151 | let [b, g, r] = calc_pixel_at_rb(image, i, w); 152 | [r, g, b] 153 | } 154 | // green2 155 | (_, _, _, _, false, true) => { 156 | let [horiz, g, vert] = calc_pixel_at_g(image, i, w); 157 | [vert, g, horiz] 158 | } 159 | // green1 160 | (_, _, _, _, true, false) => calc_pixel_at_g(image, i, w), 161 | // red 162 | (_, _, _, _, false, false) => calc_pixel_at_rb(image, i, w), 163 | } 164 | } 165 | 166 | #[inline(always)] 167 | pub(super) fn grbg(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 168 | match bayer_pixel_info(i, w, h) { 169 | // top left corner 170 | (true, _, true, _, _, _) => [get_pixel(image, i + 1), v, get_pixel(image, i + w)], 171 | // top right corner 172 | (true, _, _, true, _, _) => [v, avg(image, &[i - 1, i + w]), get_pixel(image, i + w - 1)], 173 | // bottom left corner 174 | (_, true, true, _, _, _) => [get_pixel(image, i - w + 1), avg(image, &[i - w, i + 1]), v], 175 | // bottom right corner 176 | (_, true, _, true, _, _) => [get_pixel(image, i - w), v, get_pixel(image, i - 1)], 177 | // top edge 178 | (true, _, _, _, true, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i + w)], 179 | (true, _, _, _, false, _) => [ 180 | v, 181 | avg(image, &[i - 1, i + 1, i + w]), 182 | avg(image, &[i + w - 1, i + w + 1]), 183 | ], 184 | // bottom edge 185 | (_, true, _, _, true, _) => [ 186 | avg(image, &[i - w - 1, i - w + 1]), 187 | avg(image, &[i - 1, i + 1, i - w]), 188 | v, 189 | ], 190 | (_, true, _, _, false, _) => [get_pixel(image, i - w), v, avg(image, &[i - 1, i + 1])], 191 | // left edge 192 | (_, _, true, _, _, true) => [get_pixel(image, i + 1), v, avg(image, &[i - w, i + w])], 193 | (_, _, true, _, _, false) => [ 194 | avg(image, &[i - w + 1, i + w + 1]), 195 | avg(image, &[i - w, i + w, i + 1]), 196 | v, 197 | ], 198 | // right edge 199 | (_, _, _, true, _, true) => [ 200 | v, 201 | avg(image, &[i - w, i - 1, i + w]), 202 | avg(image, &[i - w - 1, i + w - 1]), 203 | ], 204 | (_, _, _, true, _, false) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i - 1)], 205 | // green1 206 | (_, _, _, _, true, true) => calc_pixel_at_g(image, i, w), 207 | // red 208 | (_, _, _, _, false, true) => calc_pixel_at_rb(image, i, w), 209 | // blue 210 | (_, _, _, _, true, false) => { 211 | let [b, g, r] = calc_pixel_at_rb(image, i, w); 212 | [r, g, b] 213 | } 214 | // green2 215 | (_, _, _, _, false, false) => { 216 | let [horiz, g, vert] = calc_pixel_at_g(image, i, w); 217 | [vert, g, horiz] 218 | } 219 | } 220 | } 221 | 222 | #[inline(always)] 223 | pub(super) fn gbrg(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 224 | match bayer_pixel_info(i, w, h) { 225 | // top left corner 226 | (true, _, true, _, _, _) => [get_pixel(image, i + w), v, get_pixel(image, i + 1)], 227 | // top right corner 228 | (true, _, _, true, _, _) => [get_pixel(image, i + w - 1), avg(image, &[i - 1, i + w]), v], 229 | // bottom left corner 230 | (_, true, true, _, _, _) => [v, avg(image, &[i - w, i + 1]), get_pixel(image, i - w + 1)], 231 | // bottom right corner 232 | (_, true, _, true, _, _) => [get_pixel(image, i - 1), v, get_pixel(image, i - w)], 233 | // top edge 234 | (true, _, _, _, true, _) => [get_pixel(image, i + w), v, avg(image, &[i - 1, i + 1])], 235 | (true, _, _, _, false, _) => [ 236 | avg(image, &[i + w - 1, i + w + 1]), 237 | avg(image, &[i - 1, i + 1, i + w]), 238 | v, 239 | ], 240 | // bottom edge 241 | (_, true, _, _, true, _) => [ 242 | v, 243 | avg(image, &[i - 1, i + 1, i - w]), 244 | avg(image, &[i - w - 1, i - w + 1]), 245 | ], 246 | (_, true, _, _, false, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i - w)], 247 | // left edge 248 | (_, _, true, _, _, true) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i + 1)], 249 | (_, _, true, _, _, false) => [ 250 | v, 251 | avg(image, &[i - w, i + w, i + 1]), 252 | avg(image, &[i - w + 1, i + w + 1]), 253 | ], 254 | // right edge 255 | (_, _, _, true, _, true) => [ 256 | avg(image, &[i - w - 1, i + w - 1]), 257 | avg(image, &[i - w, i - 1, i + w]), 258 | v, 259 | ], 260 | (_, _, _, true, _, false) => [get_pixel(image, i - 1), v, avg(image, &[i - w, i + w])], 261 | // green2 262 | (_, _, _, _, true, true) => { 263 | let [horiz, g, vert] = calc_pixel_at_g(image, i, w); 264 | [vert, g, horiz] 265 | } 266 | // blue 267 | (_, _, _, _, false, true) => { 268 | let [b, g, r] = calc_pixel_at_rb(image, i, w); 269 | [r, g, b] 270 | } 271 | // red 272 | (_, _, _, _, true, false) => calc_pixel_at_rb(image, i, w), 273 | // green1 274 | (_, _, _, _, false, false) => calc_pixel_at_g(image, i, w), 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/maker/decode_utility/ljpeg/decompressors.rs: -------------------------------------------------------------------------------- 1 | use super::super::bit_pump::BitPumpJPEG; 2 | use super::super::bit_pump::BitPumpMSB32; 3 | use super::super::huffman::*; 4 | use super::LjpegDecompressor; 5 | 6 | /// Decode the ljpeg stream to `out`. 7 | /// `x` is the start of output in `out`, usually 0. 8 | /// `stripwidth` is the widh of bytes of a single output strip (may 9 | /// be larger than width if each row has padding bytes). 10 | /// `width` is the output image width in bytes. 11 | #[allow(clippy::let_and_return)] 12 | pub fn decode_ljpeg( 13 | ljpeg: &LjpegDecompressor, 14 | out: &mut [u16], 15 | x: usize, 16 | stripwidth: usize, 17 | width: usize, 18 | height: usize, 19 | ) -> Result<(), String> { 20 | let ncomp: usize = ljpeg.components(); 21 | if ljpeg.sof.width * ncomp < width || ljpeg.sof.height < height { 22 | return Err(format!( 23 | "ljpeg: trying to decode {}x{} into {}x{}", 24 | ljpeg.sof.width, ljpeg.sof.height, width, height 25 | )); 26 | } 27 | 28 | let htable = 29 | |index: usize| -> &HuffTable { &ljpeg.dhts[ljpeg.sof.components[index].dc_tbl_num] }; 30 | let mut pump = BitPumpJPEG::new(ljpeg.buffer); 31 | let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1); 32 | 33 | // initialize first pixel components 34 | for c in 0..ncomp { 35 | out[x + c] = (base_prediction + htable(c).huff_decode(&mut pump)) as u16; 36 | } 37 | 38 | let skip_x = ljpeg.sof.width - width / ncomp; 39 | 40 | for row in 0..height { 41 | let startcol = if row == 0 { x + ncomp } else { x }; // skip first pixel in first row 42 | for col in (startcol..(width + x)).step_by(ncomp) { 43 | for c in 0..ncomp { 44 | let p: i32 = if col == x { 45 | // At start of line predictor starts with start of previous line 46 | out[(row - 1) * stripwidth + x + c] as i32 47 | } else { 48 | // All other cases use the two previous pixels in the same line 49 | match (row, ljpeg.predictor) { 50 | (row @ 0, _) | (row, 1) => { 51 | let a = out[row * stripwidth + (col - ncomp) + c] as i32; 52 | a 53 | } 54 | (row, 2) => { 55 | let b = out[(row - 1) * stripwidth + col + c] as i32; 56 | b 57 | } 58 | (row, 3) => { 59 | let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32; 60 | c 61 | } 62 | (row, 4) => { 63 | let a = out[row * stripwidth + (col - ncomp) + c] as i32; 64 | let b = out[(row - 1) * stripwidth + col + c] as i32; 65 | let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32; 66 | a + b - c 67 | } 68 | (row, 5) => { 69 | let a = out[row * stripwidth + (col - ncomp) + c] as i32; 70 | let b = out[(row - 1) * stripwidth + col + c] as i32; 71 | let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32; 72 | a + ((b - c) >> 1) 73 | } 74 | (row, 6) => { 75 | let a = out[row * stripwidth + (col - ncomp) + c] as i32; 76 | let b = out[(row - 1) * stripwidth + col + c] as i32; 77 | let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32; 78 | b + ((a - c) >> 1) 79 | } 80 | (row, 7) => { 81 | let a = out[row * stripwidth + (col - ncomp) + c] as i32; 82 | let b = out[(row - 1) * stripwidth + col + c] as i32; 83 | (a + b) >> 1 // Adobe DNG SDK uses int32 and shifts, so we will do, too. 84 | } 85 | _ => { 86 | panic!("Unsupported prediction in LJPEG") 87 | } 88 | } 89 | }; 90 | 91 | let diff = htable(c).huff_decode(&mut pump); 92 | out[row * stripwidth + col + c] = (p + diff) as u16; 93 | } 94 | } 95 | for _ in 0..skip_x { 96 | for c in 0..ncomp { 97 | // Skip extra encoded differences if the ljpeg frame is wider than the output 98 | htable(c).huff_decode(&mut pump); 99 | } 100 | } 101 | } 102 | 103 | Ok(()) 104 | } 105 | 106 | fn set_yuv_420( 107 | out: &mut [u16], 108 | row: usize, 109 | col: usize, 110 | width: usize, 111 | y1: i32, 112 | y2: i32, 113 | y3: i32, 114 | y4: i32, 115 | cb: i32, 116 | cr: i32, 117 | ) { 118 | let pix1 = row * width + col; 119 | let pix2 = pix1 + 3; 120 | let pix3 = (row + 1) * width + col; 121 | let pix4 = pix3 + 3; 122 | 123 | debug_assert!(!y1.is_negative()); 124 | debug_assert!(!y2.is_negative()); 125 | debug_assert!(!y3.is_negative()); 126 | debug_assert!(!y4.is_negative()); 127 | debug_assert!(!cb.is_negative()); 128 | debug_assert!(!cr.is_negative()); 129 | 130 | out[pix1 + 0] = y1 as u16; 131 | out[pix1 + 1] = cb as u16; 132 | out[pix1 + 2] = cr as u16; 133 | out[pix2 + 0] = y2 as u16; 134 | out[pix2 + 1] = cb as u16; 135 | out[pix2 + 2] = cr as u16; 136 | out[pix3 + 0] = y3 as u16; 137 | out[pix3 + 1] = cb as u16; 138 | out[pix3 + 2] = cr as u16; 139 | out[pix4 + 0] = y4 as u16; 140 | out[pix4 + 1] = cb as u16; 141 | out[pix4 + 2] = cr as u16; 142 | } 143 | 144 | pub fn decode_ljpeg_420( 145 | ljpeg: &LjpegDecompressor, 146 | out: &mut [u16], 147 | width: usize, 148 | height: usize, 149 | ) -> Result<(), String> { 150 | if ljpeg.sof.width * 3 != width || ljpeg.sof.height != height { 151 | return Err(format!( 152 | "ljpeg: trying to decode {}x{} into {}x{}", 153 | ljpeg.sof.width * 3, 154 | ljpeg.sof.height, 155 | width, 156 | height 157 | )); 158 | } 159 | 160 | debug_assert_eq!(width % 2, 0); 161 | debug_assert_eq!(width % 6, 0); // Ensure we have enough samples for .step_by(6) 162 | debug_assert_eq!(height % 2, 0); 163 | 164 | let htable1 = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num]; 165 | let htable2 = &ljpeg.dhts[ljpeg.sof.components[1].dc_tbl_num]; 166 | let htable3 = &ljpeg.dhts[ljpeg.sof.components[2].dc_tbl_num]; 167 | let mut pump = BitPumpJPEG::new(ljpeg.buffer); 168 | 169 | let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1); 170 | let y1 = base_prediction + htable1.huff_decode(&mut pump); 171 | let y2 = y1 + htable1.huff_decode(&mut pump); 172 | let y3 = y2 + htable1.huff_decode(&mut pump); 173 | let y4 = y3 + htable1.huff_decode(&mut pump); 174 | let cb = base_prediction + htable2.huff_decode(&mut pump); 175 | let cr = base_prediction + htable3.huff_decode(&mut pump); 176 | set_yuv_420(out, 0, 0, width, y1, y2, y3, y4, cb, cr); 177 | 178 | for row in (0..height).step_by(2) { 179 | let startcol = if row == 0 { 6 } else { 0 }; 180 | for col in (startcol..width).step_by(6) { 181 | let pos = if col == 0 { 182 | // At start of line predictor starts with first pixel of start of previous line 183 | (row - 2) * width 184 | } else { 185 | // All other cases use the last pixel in the same two lines 186 | (row + 1) * width + col - 3 187 | }; 188 | let (py, pcb, pcr) = (out[pos], out[pos + 1], out[pos + 2]); 189 | 190 | let y1 = (py as i32) + htable1.huff_decode(&mut pump); 191 | let y2 = (y1 as i32) + htable1.huff_decode(&mut pump); 192 | let y3 = (y2 as i32) + htable1.huff_decode(&mut pump); 193 | let y4 = (y3 as i32) + htable1.huff_decode(&mut pump); 194 | let cb = (pcb as i32) + htable2.huff_decode(&mut pump); 195 | let cr = (pcr as i32) + htable3.huff_decode(&mut pump); 196 | set_yuv_420(out, row, col, width, y1, y2, y3, y4, cb, cr); 197 | } 198 | } 199 | 200 | Ok(()) 201 | } 202 | 203 | fn set_yuv_422( 204 | out: &mut [u16], 205 | row: usize, 206 | col: usize, 207 | width: usize, 208 | y1: i32, 209 | y2: i32, 210 | cb: i32, 211 | cr: i32, 212 | ) { 213 | let pix1 = row * width + col; 214 | let pix2 = pix1 + 3; 215 | 216 | debug_assert!(!y1.is_negative()); 217 | debug_assert!(!y2.is_negative()); 218 | debug_assert!(!cb.is_negative()); 219 | debug_assert!(!cr.is_negative()); 220 | 221 | out[pix1 + 0] = y1 as u16; 222 | out[pix1 + 1] = cb as u16; 223 | out[pix1 + 2] = cr as u16; 224 | out[pix2 + 0] = y2 as u16; 225 | out[pix2 + 1] = cb as u16; 226 | out[pix2 + 2] = cr as u16; 227 | } 228 | 229 | pub fn decode_ljpeg_422( 230 | ljpeg: &LjpegDecompressor, 231 | out: &mut [u16], 232 | width: usize, 233 | height: usize, 234 | ) -> Result<(), String> { 235 | if ljpeg.sof.width * 3 != width || ljpeg.sof.height != height { 236 | return Err(format!( 237 | "ljpeg: trying to decode {}x{} into {}x{}", 238 | ljpeg.sof.width * 3, 239 | ljpeg.sof.height, 240 | width, 241 | height 242 | )); 243 | } 244 | let htable1 = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num]; 245 | let htable2 = &ljpeg.dhts[ljpeg.sof.components[1].dc_tbl_num]; 246 | let htable3 = &ljpeg.dhts[ljpeg.sof.components[2].dc_tbl_num]; 247 | let mut pump = BitPumpJPEG::new(ljpeg.buffer); 248 | 249 | let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1); 250 | let y1 = base_prediction + htable1.huff_decode(&mut pump); 251 | let y2 = y1 + htable1.huff_decode(&mut pump); 252 | let cb = base_prediction + htable2.huff_decode(&mut pump); 253 | let cr = base_prediction + htable3.huff_decode(&mut pump); 254 | set_yuv_422(out, 0, 0, width, y1, y2, cb, cr); 255 | 256 | for row in 0..height { 257 | let startcol = if row == 0 { 6 } else { 0 }; 258 | for col in (startcol..width).step_by(6) { 259 | let pos = if col == 0 { 260 | // At start of line predictor starts with first pixel of start of previous line 261 | (row - 1) * width 262 | } else { 263 | // All other cases use the last pixel in the same two lines 264 | row * width + col - 3 265 | }; 266 | let (py, pcb, pcr) = (out[pos], out[pos + 1], out[pos + 2]); 267 | 268 | let y1 = (py as i32) + htable1.huff_decode(&mut pump); 269 | let y2 = (y1 as i32) + htable1.huff_decode(&mut pump); 270 | let cb = (pcb as i32) + htable2.huff_decode(&mut pump); 271 | let cr = (pcr as i32) + htable3.huff_decode(&mut pump); 272 | 273 | set_yuv_422(out, row, col, width, y1, y2, cb, cr); 274 | } 275 | } 276 | 277 | Ok(()) 278 | } 279 | 280 | pub fn decode_hasselblad( 281 | ljpeg: &LjpegDecompressor, 282 | out: &mut [u16], 283 | width: usize, 284 | ) -> Result<(), String> { 285 | // Pixels are packed two at a time, not like LJPEG: 286 | // [p1_length_as_huffman][p2_length_as_huffman][p0_diff_with_length][p1_diff_with_length]|NEXT PIXELS 287 | let mut pump = BitPumpMSB32::new(ljpeg.buffer); 288 | let htable = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num]; 289 | 290 | for line in out.chunks_exact_mut(width) { 291 | let mut p1: i32 = 0x8000; 292 | let mut p2: i32 = 0x8000; 293 | for o in line.chunks_exact_mut(2) { 294 | let len1 = htable.huff_len(&mut pump); 295 | let len2 = htable.huff_len(&mut pump); 296 | p1 += htable.huff_diff(&mut pump, len1); 297 | p2 += htable.huff_diff(&mut pump, len2); 298 | o[0] = p1 as u16; 299 | o[1] = p2 as u16; 300 | } 301 | } 302 | 303 | Ok(()) 304 | } 305 | -------------------------------------------------------------------------------- /src/maker/nikon.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use once_cell::sync::Lazy; 3 | 4 | use super::utility::GetNumFromBytes; 5 | 6 | use super::{ 7 | decode_utility::bit_pump::*, decode_utility::byte_stream::*, decode_utility::huffman::*, 8 | decode_utility::lookup_table::*, utility::*, 9 | }; 10 | 11 | pub(super) struct General { 12 | info: quickexif::ParsedInfo, 13 | } 14 | 15 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 16 | quickexif::describe_rule!(tiff { 17 | 0x0112 : u16 / orientation 18 | 0x014a { 19 | offset address { 20 | 0x0201 / thumbnail 21 | 0x0202 / thumbnail_len 22 | } 23 | } 24 | }) 25 | }); 26 | 27 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 28 | quickexif::describe_rule!(tiff { 29 | 0x0112 : u16 / orientation 30 | 0x8769 { 31 | 0xa302 { 32 | u32 + 1 / cfa_pattern 33 | } 34 | 0x927c / maker_notes { 35 | offset + 18 { 36 | 0x000c { 37 | offset + maker_notes { 38 | offset + 10 { 39 | r64 + 0 / white_balance_r 40 | r64 + 1 / white_balance_b 41 | r64 + 2 / white_balance_g 42 | } 43 | } 44 | } 45 | 0x003d? { 46 | offset + maker_notes { 47 | offset + 10 { 48 | u16 + 0 / black_level 49 | } 50 | } 51 | } 52 | 0x0045? { 53 | offset + maker_notes { 54 | offset + 10 { 55 | u16 + 0 / crop_left 56 | u16 + 1 / crop_top 57 | u16 + 2 / crop_width 58 | u16 + 3 / crop_height 59 | } 60 | } 61 | } 62 | 0x008c / contrast_curve_offset(contrast_curve_len) 63 | 0x0096? / linear_table_offset(linear_table_len) 64 | } 65 | } 66 | } 67 | 0x014a { 68 | offset + 4 { 69 | offset address { 70 | 0x0100 / width 71 | 0x0101 / height 72 | 0x0102 : u16 / bps 73 | 0x0103 : u16 / compression 74 | 0x0111 / strip 75 | 0x0117 / strip_len 76 | } 77 | } 78 | } 79 | }) 80 | }); 81 | 82 | impl RawDecoder for General { 83 | fn new(info: quickexif::ParsedInfo) -> Self { 84 | General { info } 85 | } 86 | fn get_info(&self) -> &quickexif::ParsedInfo { 87 | &self.info 88 | } 89 | fn into_info(self) -> quickexif::ParsedInfo { 90 | self.info 91 | } 92 | fn get_crop(&self) -> Option { 93 | let x = self.info.u32("crop_left").ok()?; 94 | let y = self.info.u32("crop_top").ok()?; 95 | let width = self.info.u32("crop_width").ok()?; 96 | let height = self.info.u32("crop_height").ok()?; 97 | 98 | Some(Crop { 99 | x, 100 | y, 101 | width, 102 | height, 103 | }) 104 | } 105 | fn get_white_balance(&self) -> Result<[i32; 3], DecodingError> { 106 | let r = 512.0 * self.info.f64("white_balance_r")?; 107 | let g = 512.0 * self.info.f64("white_balance_g")?; 108 | let b = 512.0 * self.info.f64("white_balance_b")?; 109 | Ok([r as i32, g as i32, b as i32]) 110 | } 111 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 112 | let offset = self.info.usize("thumbnail")?; 113 | let len = self.info.usize("thumbnail_len")?; 114 | Ok(&buffer[offset..offset + len]) 115 | } 116 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 117 | let strip_offset = self.info.usize("strip")?; 118 | let strip_len = self.info.usize("strip_len")?; 119 | let width = self.info.usize("width")?; 120 | let height = self.info.usize("height")?; 121 | let bps = self.info.u16("bps")?; 122 | let bps_scale = self.get_bps_scale()?; 123 | let compression = self.info.u16("compression")?; 124 | let black_level = self.info.u16("black_level").unwrap_or(0); 125 | let black_level = match bps { 126 | 12 => black_level / 4, 127 | _ => black_level, 128 | }; 129 | let maker_notes_addr = self.info.usize("maker_notes")? + 10; 130 | let color_data = match self.info.usize("linear_table_offset") { 131 | Ok(offset) => { 132 | let offset = offset + maker_notes_addr; 133 | let len = self.info.usize("linear_table_len")?; 134 | &buffer[offset..offset + len] 135 | } 136 | Err(_) => { 137 | let offset = self.info.usize("contrast_curve_offset")? + maker_notes_addr; 138 | let len = self.info.usize("contrast_curve_len")?; 139 | &buffer[offset..offset + len] 140 | } 141 | }; 142 | 143 | let buf = &buffer[strip_offset..]; 144 | 145 | macro_rules! to_image { 146 | ($iter:expr) => { 147 | $iter 148 | .map(|x| bps_scale.saturating_mul(x.saturating_sub(black_level))) 149 | .collect() 150 | }; 151 | } 152 | let image: Vec = if width * height * 3 == strip_len { 153 | let wb_r = self.info.f64("white_balance_r")?; 154 | let wb_b = self.info.f64("white_balance_b")?; 155 | to_image!(load_raw_yuv2(buf, wb_r, wb_b, width, height).iter()) 156 | } else { 157 | match compression { 158 | 1 => match bps { 159 | 12 => to_image!(to_12bit_iter(buf, self.info.is_le)), 160 | 14 => to_image!(to_14bit_iter(buf, self.info.is_le)), 161 | _ => to_image!(to_16bit_iter(buf, self.info.is_le)), 162 | }, 163 | 0x8799 => { 164 | to_image!( 165 | load_raw(buf, color_data, self.info.is_le, bps, width, height)?.iter() 166 | ) 167 | } 168 | _ => unimplemented!(), 169 | } 170 | }; 171 | 172 | if image.len() != width * height { 173 | Err(DecodingError::InvalidDecodedImageSize( 174 | image.len(), 175 | width * height, 176 | )) 177 | } else { 178 | Ok(image) 179 | } 180 | } 181 | } 182 | 183 | fn load_raw_yuv2(src: &[u8], wb_r: f64, wb_b: f64, width: usize, height: usize) -> Vec { 184 | let inv_wb_r = (1024.0 / wb_r) as i32; 185 | let inv_wb_b = (1024.0 / wb_b) as i32; 186 | 187 | let snef_curve = { 188 | let g: f32 = 2.4; 189 | let f: f32 = 0.055; 190 | let min: f32 = 0.04045; 191 | let mul: f32 = 12.92; 192 | let curve = (0..4096) 193 | .map(|i| { 194 | let v = (i as f32) / 4095.0; 195 | let res = if v <= min { 196 | v / mul 197 | } else { 198 | ((v + f) / (1.0 + f)).powf(g) 199 | }; 200 | clampbits((res * 65535.0 * 4.0) as i32, 16) 201 | }) 202 | .collect::>(); 203 | LookupTable::new(&curve) 204 | }; 205 | 206 | let mut out = vec![0u16; width * height]; 207 | out.chunks_mut(width * 3) 208 | .enumerate() 209 | .for_each(|(row, out)| { 210 | let inb = &src[row * width * 3..]; 211 | let mut random = inb.u32be(0); 212 | for (o, i) in out.chunks_exact_mut(6).zip(inb.chunks_exact(6)) { 213 | let g1: u16 = i[0] as u16; 214 | let g2: u16 = i[1] as u16; 215 | let g3: u16 = i[2] as u16; 216 | let g4: u16 = i[3] as u16; 217 | let g5: u16 = i[4] as u16; 218 | let g6: u16 = i[5] as u16; 219 | 220 | let y1 = (g1 | ((g2 & 0x0f) << 8)) as f32; 221 | let y2 = ((g2 >> 4) | (g3 << 4)) as f32; 222 | let cb = (g4 | ((g5 & 0x0f) << 8)) as f32 - 2048.0; 223 | let cr = ((g5 >> 4) | (g6 << 4)) as f32 - 2048.0; 224 | 225 | let r = snef_curve.dither(clampbits((y1 + 1.370705 * cr) as i32, 12), &mut random); 226 | let g = snef_curve.dither( 227 | clampbits((y1 - 0.337633 * cb - 0.698001 * cr) as i32, 12), 228 | &mut random, 229 | ); 230 | let b = snef_curve.dither(clampbits((y1 + 1.732446 * cb) as i32, 12), &mut random); 231 | // invert the white balance 232 | o[0] = clampbits((inv_wb_r * r as i32 + (1 << 9)) >> 10, 15); 233 | o[1] = g; 234 | o[2] = clampbits((inv_wb_b * b as i32 + (1 << 9)) >> 10, 15); 235 | 236 | let r = snef_curve.dither(clampbits((y2 + 1.370705 * cr) as i32, 12), &mut random); 237 | let g = snef_curve.dither( 238 | clampbits((y2 - 0.337633 * cb - 0.698001 * cr) as i32, 12), 239 | &mut random, 240 | ); 241 | let b = snef_curve.dither(clampbits((y2 + 1.732446 * cb) as i32, 12), &mut random); 242 | // invert the white balance 243 | o[3] = clampbits((inv_wb_r * r as i32 + (1 << 9)) >> 10, 15); 244 | o[4] = g; 245 | o[5] = clampbits((inv_wb_b * b as i32 + (1 << 9)) >> 10, 15); 246 | } 247 | }); 248 | 249 | out 250 | } 251 | 252 | fn load_raw( 253 | src: &[u8], 254 | meta: &[u8], 255 | is_le: bool, 256 | bps: u16, 257 | width: usize, 258 | height: usize, 259 | ) -> Result, DecodingError> { 260 | let mut out = vec![0u16; width * height]; 261 | let mut stream = ByteStream::new(meta, is_le); 262 | let v0 = stream.get_u8(); 263 | let v1 = stream.get_u8(); 264 | 265 | let mut huff_select = 0; 266 | if v0 == 73 || v1 == 88 { 267 | stream.consume_bytes(2110); 268 | } 269 | if v0 == 70 { 270 | huff_select = 2; 271 | } 272 | if bps == 14 { 273 | huff_select += 3; 274 | } 275 | 276 | // Create the huffman table used to decode 277 | let mut htable = create_hufftable(huff_select); 278 | 279 | // Setup the predictors 280 | let mut pred_up1: [i32; 2] = [stream.get_u16() as i32, stream.get_u16() as i32]; 281 | let mut pred_up2: [i32; 2] = [stream.get_u16() as i32, stream.get_u16() as i32]; 282 | 283 | // Get the linearization curve 284 | let mut points = [0u16; 1 << 16]; 285 | for (i, point) in points.iter_mut().enumerate() { 286 | *point = i as u16; 287 | } 288 | let mut max = 1 << bps; 289 | let csize = stream.get_u16() as usize; 290 | let mut split = 0usize; 291 | let step = if csize > 1 { max / (csize - 1) } else { 0 }; 292 | if v0 == 68 && v1 == 32 && step > 0 { 293 | for i in 0..csize { 294 | points[i * step] = stream.get_u16(); 295 | } 296 | for i in 0..max { 297 | points[i] = ((points[i - i % step] as usize * (step - i % step) 298 | + points[i - i % step + step] as usize * (i % step)) 299 | / step) as u16; 300 | } 301 | split = meta.u16(is_le, 562) as usize; 302 | } else if v0 != 70 && csize <= 0x4001 { 303 | for point in points.iter_mut().take(csize) { 304 | *point = stream.get_u16(); 305 | } 306 | max = csize; 307 | } 308 | let curve = LookupTable::new(&points[0..max]); 309 | 310 | let mut pump = BitPumpMSB::new(src); 311 | let mut random = pump.peek_bits(24); 312 | 313 | let bps: u32 = bps as u32; 314 | for row in 0..height { 315 | if split > 0 && row == split { 316 | htable = create_hufftable(huff_select + 1); 317 | } 318 | pred_up1[row & 1] += htable.huff_decode(&mut pump); 319 | pred_up2[row & 1] += htable.huff_decode(&mut pump); 320 | let mut pred_left1 = pred_up1[row & 1]; 321 | let mut pred_left2 = pred_up2[row & 1]; 322 | for col in (0..width).step_by(2) { 323 | if col > 0 { 324 | pred_left1 += htable.huff_decode(&mut pump); 325 | pred_left2 += htable.huff_decode(&mut pump); 326 | } 327 | out[row * width + col] = curve.dither(clampbits(pred_left1, bps), &mut random); 328 | out[row * width + col + 1] = curve.dither(clampbits(pred_left2, bps), &mut random); 329 | } 330 | } 331 | Ok(out) 332 | } 333 | 334 | #[inline(always)] 335 | fn clampbits(val: i32, bits: u32) -> u16 { 336 | let max = (1 << bits) - 1; 337 | if val < 0 { 338 | 0 339 | } else if val > max { 340 | max as u16 341 | } else { 342 | val as u16 343 | } 344 | } 345 | 346 | fn create_hufftable(num: usize) -> HuffTable { 347 | let mut htable = HuffTable::empty(); 348 | 349 | for i in 0..15 { 350 | htable.bits[i] = NIKON_TREE[num][0][i] as u32; 351 | htable.huffval[i] = NIKON_TREE[num][1][i] as u32; 352 | htable.shiftval[i] = NIKON_TREE[num][2][i] as u32; 353 | } 354 | 355 | htable.initialize(); 356 | htable 357 | } 358 | 359 | const NIKON_TREE: [[[u8; 16]; 3]; 6] = [ 360 | [ 361 | // 12-bit lossy 362 | [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0], 363 | [5, 4, 3, 6, 2, 7, 1, 0, 8, 9, 11, 10, 12, 0, 0, 0], 364 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 365 | ], 366 | [ 367 | // 12-bit lossy after split 368 | [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0], 369 | [6, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0, 11, 12, 12, 0, 0], 370 | [3, 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 371 | ], 372 | [ 373 | // 12-bit lossless 374 | [0, 0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0], 375 | [5, 4, 6, 3, 7, 2, 8, 1, 9, 0, 10, 11, 12, 0, 0, 0], 376 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 377 | ], 378 | [ 379 | // 14-bit lossy 380 | [0, 0, 1, 4, 3, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0], 381 | [5, 6, 4, 7, 8, 3, 9, 2, 1, 0, 10, 11, 12, 13, 14, 0], 382 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 383 | ], 384 | [ 385 | // 14-bit lossy after split 386 | [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0], 387 | [8, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 13, 14, 0], 388 | [0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 389 | ], 390 | [ 391 | // 14-bit lossless 392 | [0, 0, 1, 4, 2, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0], 393 | [7, 6, 8, 5, 9, 4, 10, 3, 11, 12, 2, 0, 1, 13, 14, 0], 394 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 395 | ], 396 | ]; 397 | -------------------------------------------------------------------------------- /src/maker/decode_utility/ljpeg/mod.rs: -------------------------------------------------------------------------------- 1 | use super::byte_stream::ByteStream; 2 | use super::huffman::*; 3 | use super::DecodingError; 4 | use decompressors::*; 5 | 6 | mod decompressors; 7 | 8 | enum Marker { 9 | Stuff = 0x00, 10 | SOF3 = 0xc3, // lossless 11 | DHT = 0xc4, // huffman tables 12 | SOI = 0xd8, // start of image 13 | EOI = 0xd9, // end of image 14 | SOS = 0xda, // start of scan 15 | DQT = 0xdb, // quantization tables 16 | Fill = 0xff, 17 | } 18 | 19 | fn m(marker: Marker) -> u8 { 20 | marker as u8 21 | } 22 | 23 | #[derive(Debug, Copy, Clone)] 24 | struct JpegComponentInfo { 25 | // These values are fixed over the whole image, read from the SOF marker. 26 | id: usize, // identifier for this component (0..255) 27 | #[allow(dead_code)] 28 | index: usize, // its index in SOF or cPtr->compInfo[] 29 | 30 | // Huffman table selector (0..3). The value may vary between scans. 31 | // It is read from the SOS marker. 32 | dc_tbl_num: usize, 33 | super_h: usize, // Horizontal Supersampling 34 | super_v: usize, // Vertical Supersampling 35 | } 36 | 37 | #[derive(Debug, Clone)] 38 | struct SOFInfo { 39 | width: usize, 40 | height: usize, 41 | cps: usize, 42 | precision: usize, 43 | components: Vec, 44 | csfix: bool, 45 | } 46 | 47 | impl SOFInfo { 48 | fn empty(csfix: bool) -> SOFInfo { 49 | SOFInfo { 50 | width: 0, 51 | height: 0, 52 | cps: 0, 53 | precision: 0, 54 | components: Vec::new(), 55 | csfix, 56 | } 57 | } 58 | 59 | fn parse_sof(&mut self, input: &mut ByteStream) -> Result<(), String> { 60 | let header_length = input.get_u16() as usize; 61 | self.precision = input.get_u8() as usize; 62 | self.height = input.get_u16() as usize; 63 | self.width = input.get_u16() as usize; 64 | self.cps = input.get_u8() as usize; 65 | 66 | if self.precision > 16 { 67 | return Err("ljpeg: More than 16 bits per channel is not supported.".to_string()); 68 | } 69 | if self.cps > 4 || self.cps < 1 { 70 | return Err("ljpeg: Only from 1 to 4 components are supported.".to_string()); 71 | } 72 | if header_length != 8 + self.cps * 3 { 73 | return Err("ljpeg: Header size mismatch.".to_string()); 74 | } 75 | 76 | for i in 0..self.cps { 77 | let id = input.get_u8() as usize; 78 | let subs = input.get_u8() as usize; 79 | input.get_u8(); // Skip info about quantized 80 | 81 | self.components.push(JpegComponentInfo { 82 | id, 83 | index: i, 84 | dc_tbl_num: 0, 85 | super_v: subs & 0xf, 86 | super_h: subs >> 4, 87 | }); 88 | } 89 | Ok(()) 90 | } 91 | 92 | fn parse_sos(&mut self, input: &mut ByteStream) -> Result<(usize, usize), String> { 93 | if self.width == 0 { 94 | return Err("ljpeg: Trying to parse SOS before SOF".to_string()); 95 | } 96 | input.get_u16(); //skip header length 97 | let soscps = input.get_u8() as usize; 98 | if self.cps != soscps { 99 | return Err("ljpeg: component number mismatch in SOS".to_string()); 100 | } 101 | for cs in 0..self.cps { 102 | // At least some MOS cameras have this broken 103 | let readcs = input.get_u8() as usize; 104 | let cs = if self.csfix { cs } else { readcs }; 105 | let component = match self.components.iter_mut().find(|&&mut c| c.id == cs) { 106 | Some(val) => val, 107 | None => return Err(format!("ljpeg: invalid component selector {}", cs)), 108 | }; 109 | let td = (input.get_u8() as usize) >> 4; 110 | if td > 3 { 111 | return Err("ljpeg: Invalid Huffman table selection".to_string()); 112 | } 113 | component.dc_tbl_num = td; 114 | } 115 | let pred = input.get_u8() as usize; 116 | input.get_u8(); // Se + Ah Not used in LJPEG 117 | let pt = (input.get_u8() as usize) & 0xf; // Point Transform 118 | Ok((pred, pt)) 119 | } 120 | } 121 | 122 | #[derive(Debug)] 123 | pub struct LjpegDecompressor<'a> { 124 | buffer: &'a [u8], 125 | sof: SOFInfo, 126 | predictor: usize, 127 | point_transform: usize, 128 | dhts: Vec, 129 | } 130 | 131 | impl<'a> LjpegDecompressor<'a> { 132 | pub fn new(src: &'a [u8]) -> Result { 133 | LjpegDecompressor::new_full(src, false, false) 134 | .map_err(|err| DecodingError::LJpegErrorConstructor(err)) 135 | } 136 | 137 | pub fn new_full( 138 | src: &'a [u8], 139 | dng_bug: bool, 140 | csfix: bool, 141 | ) -> Result { 142 | let mut input = ByteStream::new(src, false); 143 | if LjpegDecompressor::get_next_marker(&mut input, false)? != m(Marker::SOI) { 144 | return Err("ljpeg: Image did not start with SOI. Probably not LJPEG".to_string()); 145 | } 146 | 147 | let mut sof = SOFInfo::empty(csfix); 148 | let mut dht_init = [false; 4]; 149 | let mut dht_bits = [[0_u32; 17]; 4]; 150 | let mut dht_huffval = [[0_u32; 256]; 4]; 151 | let pred; 152 | let pt; 153 | loop { 154 | let marker = LjpegDecompressor::get_next_marker(&mut input, true)?; 155 | if marker == m(Marker::SOF3) { 156 | // Start of the frame, giving us the basic info 157 | sof.parse_sof(&mut input)?; 158 | if sof.precision > 16 || sof.precision < 10 { 159 | return Err(format!("ljpeg: sof.precision {}", sof.precision)); 160 | } 161 | } else if marker == m(Marker::DHT) { 162 | // Huffman table settings 163 | LjpegDecompressor::parse_dht( 164 | &mut input, 165 | &mut dht_init, 166 | &mut dht_bits, 167 | &mut dht_huffval, 168 | )?; 169 | } else if marker == m(Marker::SOS) { 170 | // Start of the actual stream, we can decode after this 171 | let (a, b) = sof.parse_sos(&mut input)?; 172 | pred = a; 173 | pt = b; 174 | break; 175 | } else if marker == m(Marker::EOI) { 176 | // Should never be reached as we stop at SOS 177 | return Err("ljpeg: reached EOI before SOS".to_string()); 178 | } else if marker == m(Marker::DQT) { 179 | return Err("ljpeg: not a valid raw file, found DQT".to_string()); 180 | } 181 | } 182 | 183 | let mut dhts = Vec::new(); 184 | for i in 0..4 { 185 | dhts.push(if dht_init[i] { 186 | HuffTable::new(dht_bits[i], dht_huffval[i], dng_bug).unwrap() 187 | } else { 188 | HuffTable::empty() 189 | }); 190 | } 191 | 192 | // log::debug!( 193 | // "LJPEGDecompressor: super_h: {}, super_v: {}, pred: {}, pt: {}, prec: {}", 194 | // sof.components[0].super_h, 195 | // sof.components[0].super_v, 196 | // pred, 197 | // pt, 198 | // sof.precision 199 | // ); 200 | 201 | if sof.components[0].super_h == 2 && sof.components[0].super_v == 2 { 202 | // log::debug!("LJPEG with YUV 4:2:0 encoding"); 203 | } else if sof.components[0].super_h == 2 && sof.components[0].super_v == 1 { 204 | // log::debug!("LJPEG with YUV 4:2:2 encoding"); 205 | } 206 | 207 | let offset = input.get_pos(); 208 | Ok(LjpegDecompressor { 209 | buffer: &src[offset..], 210 | sof, 211 | predictor: pred, 212 | point_transform: pt, 213 | dhts, 214 | }) 215 | } 216 | 217 | fn get_next_marker(input: &mut ByteStream, allowskip: bool) -> Result { 218 | if !allowskip { 219 | if input.get_u8() != 0xff { 220 | return Err("ljpeg: (noskip) expected marker not found".to_string()); 221 | } 222 | let mark = input.get_u8(); 223 | if mark == m(Marker::Stuff) || mark == m(Marker::Fill) { 224 | return Err("ljpeg: (noskip) expected marker but found stuff or fill".to_string()); 225 | } 226 | return Ok(mark); 227 | } 228 | input.skip_to_marker().unwrap(); 229 | 230 | Ok(input.get_u8()) 231 | } 232 | 233 | fn parse_dht( 234 | input: &mut ByteStream, 235 | init: &mut [bool; 4], 236 | bits: &mut [[u32; 17]; 4], 237 | huffval: &mut [[u32; 256]; 4], 238 | ) -> Result<(), String> { 239 | let mut length = (input.get_u16() as usize) - 2; 240 | 241 | while length > 0 { 242 | let b = input.get_u8() as usize; 243 | let tc = b >> 4; 244 | let th = b & 0xf; 245 | 246 | if tc != 0 { 247 | return Err("ljpeg: unsuported table class in DHT".to_string()); 248 | } 249 | if th > 3 { 250 | return Err(format!("ljpeg: unsuported table id {}", th)); 251 | } 252 | 253 | let mut acc: usize = 0; 254 | for i in 0..16 { 255 | bits[th][i + 1] = input.get_u8() as u32; 256 | acc += bits[th][i + 1] as usize; 257 | } 258 | bits[th][0] = 0; 259 | 260 | if acc > 256 { 261 | return Err("ljpeg: invalid DHT table".to_string()); 262 | } 263 | 264 | if length < 1 + 16 + acc { 265 | return Err("ljpeg: invalid DHT table length".to_string()); 266 | } 267 | 268 | for i in 0..acc { 269 | huffval[th][i] = input.get_u8() as u32; 270 | } 271 | 272 | init[th] = true; 273 | length -= 1 + 16 + acc; 274 | } 275 | 276 | Ok(()) 277 | } 278 | 279 | /// Handle special SONY YUV 4:2:0 encoding in ILCE-7RM5 280 | // pub fn decode_sony( 281 | // &self, 282 | // out: &mut [u16], 283 | // x: usize, 284 | // stripwidth: usize, 285 | // width: usize, 286 | // height: usize, 287 | // dummy: bool, 288 | // ) -> Result<(), String> { 289 | // if dummy { 290 | // return Ok(()); 291 | // } 292 | // // log::debug!("LJPEG decode with special Sony mode"); 293 | // if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 2 { 294 | // decode_sony_ljpeg_420(self, out, width, height) 295 | // } else if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 1 { 296 | // decode_ljpeg_422(self, out, width, height) 297 | // } else if self.sof.components[0].super_h == 1 && self.sof.components[0].super_v == 1 { 298 | // match self.predictor { 299 | // 1 | 2 | 3 | 4 | 5 | 6 | 7 => decode_ljpeg(self, out, x, stripwidth, width, height), 300 | // 8 => decode_hasselblad(self, out, width), 301 | // p => Err(format!("ljpeg: predictor {} not supported", p)), 302 | // } 303 | // } else { 304 | // Err(format!( 305 | // "ljpeg: unsupported interleave configuration, super_h: {}, super_v: {}", 306 | // self.sof.components[0].super_h, self.sof.components[0].super_v 307 | // )) 308 | // } 309 | // } 310 | 311 | pub fn decode( 312 | &self, 313 | out: &mut [u16], 314 | x: usize, 315 | stripwidth: usize, 316 | width: usize, 317 | height: usize, 318 | ) -> Result<(), DecodingError> { 319 | let result = if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 2 { 320 | decode_ljpeg_420(self, out, width, height) 321 | } else if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 1 { 322 | decode_ljpeg_422(self, out, width, height) 323 | } else if self.sof.components[0].super_h == 1 && self.sof.components[0].super_v == 1 { 324 | match self.predictor { 325 | 1 | 2 | 3 | 4 | 5 | 6 | 7 => decode_ljpeg(self, out, x, stripwidth, width, height), 326 | 8 => decode_hasselblad(self, out, width), 327 | p => Err(format!("ljpeg: predictor {} not supported", p)), 328 | } 329 | } else { 330 | Err(format!( 331 | "ljpeg: unsupported interleave configuration, super_h: {}, super_v: {}", 332 | self.sof.components[0].super_h, self.sof.components[0].super_v 333 | )) 334 | }; 335 | result.map_err(|err| DecodingError::LJpegError(err)) 336 | } 337 | 338 | // pub fn decode_leaf(&self, width: usize, height: usize) -> Result { 339 | // let mut offsets = vec![0_usize; 1]; 340 | // let mut input = ByteStream::new(self.buffer, Endian::Big); 341 | 342 | // while let Ok(marker) = LjpegDecompressor::get_next_marker(&mut input, true) { 343 | // if marker == m(Marker::EOI) { 344 | // break; 345 | // } 346 | // offsets.push(input.get_pos()); 347 | // } 348 | // let nstrips = (height - 1) / 8 + 1; 349 | // if offsets.len() != nstrips { 350 | // return Err(format!( 351 | // "MOS: expecting {} strips found {}", 352 | // nstrips, 353 | // offsets.len() 354 | // )); 355 | // } 356 | 357 | // let htable1 = &self.dhts[self.sof.components[0].dc_tbl_num]; 358 | // let htable2 = &self.dhts[self.sof.components[1].dc_tbl_num]; 359 | // let bpred = 1 << (self.sof.precision - self.point_transform - 1); 360 | // Ok(decode_threaded_multiline( 361 | // width, 362 | // height, 363 | // 8, 364 | // false, 365 | // &(|strip: &mut [u16], block| { 366 | // let block = block / 8; 367 | // let offset = offsets[block]; 368 | // let nlines = strip.len() / width; 369 | // decode_leaf_strip( 370 | // &self.buffer[offset..], 371 | // strip, 372 | // width, 373 | // nlines, 374 | // htable1, 375 | // htable2, 376 | // bpred, 377 | // ) 378 | // .unwrap(); 379 | // }), 380 | // )) 381 | // } 382 | 383 | // pub fn width(&self) -> usize { 384 | // self.sof.width * self.sof.cps 385 | // } 386 | // pub fn height(&self) -> usize { 387 | // self.sof.height 388 | // } 389 | // pub fn super_v(&self) -> usize { 390 | // self.sof.components[0].super_v 391 | // } 392 | // pub fn super_h(&self) -> usize { 393 | // self.sof.components[0].super_h 394 | // } 395 | pub fn components(&self) -> usize { 396 | self.sof.components.len() 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /src/maker/olympus.rs: -------------------------------------------------------------------------------- 1 | use super::decode_utility::bit_pump::*; 2 | use super::*; 3 | use once_cell::sync::Lazy; 4 | use std::cmp; 5 | 6 | pub(super) struct General { 7 | info: quickexif::ParsedInfo, 8 | } 9 | 10 | pub(super) static THUMBNAIL_RULE: Lazy = Lazy::new(|| { 11 | quickexif::describe_rule!(tiff { 12 | 0x0112 / orientation 13 | 0x8769 { 14 | 0x927c / maker_notes { 15 | offset + 12 { 16 | 0x2020 { 17 | offset + maker_notes { 18 | 0x0101 / preview_image_start 19 | 0x0102 / preview_image_len 20 | } 21 | } 22 | } 23 | } 24 | } 25 | }) 26 | }); 27 | 28 | pub(super) static IMAGE_RULE: Lazy = Lazy::new(|| { 29 | quickexif::describe_rule!(tiff { 30 | 0x0112 / orientation 31 | 0x0100 / width 32 | 0x0101 / height 33 | 0x0111 / strip 34 | 0x0117 / strip_len 35 | 0x8769 { 36 | 0xa302 { 37 | u32 + 1 / cfa_pattern 38 | } 39 | 0x927c / maker_notes { 40 | offset + 12 { 41 | 0x2040 { 42 | offset + maker_notes { 43 | 0x0611 / bps 44 | 0x0612 / crop_left 45 | 0x0613 / crop_top 46 | 0x0614 / crop_width 47 | 0x0615 / crop_height 48 | 0x0100 { 49 | offset + maker_notes { 50 | u16 + 0 / white_balance_r 51 | u16 + 1 / white_balance_b 52 | u16 + 2 / white_balance_g 53 | } 54 | } 55 | 0x0600 { 56 | offset + maker_notes { 57 | u16 + 0 / black_level 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }) 66 | }); 67 | 68 | impl RawDecoder for General { 69 | fn new(info: quickexif::ParsedInfo) -> Self { 70 | General { info } 71 | } 72 | fn get_info(&self) -> &quickexif::ParsedInfo { 73 | &self.info 74 | } 75 | fn into_info(self) -> quickexif::ParsedInfo { 76 | self.info 77 | } 78 | 79 | fn get_crop(&self) -> Option { 80 | let x = self.info.u32("crop_left").ok()?; 81 | let y = self.info.u32("crop_top").ok()?; 82 | let width = self.info.u32("crop_width").ok()?; 83 | let height = self.info.u32("crop_height").ok()?; 84 | 85 | Some(Crop { 86 | x, 87 | y, 88 | width, 89 | height, 90 | }) 91 | } 92 | fn decode_with_preprocess(&self, buffer: &[u8]) -> Result, DecodingError> { 93 | let width = self.info.usize("width")?; 94 | let height = self.info.usize("height")?; 95 | let strip_offset = self.info.usize("strip")?; 96 | let strip_len = self.info.usize("strip_len")?; 97 | let buffer = &buffer[strip_offset..]; 98 | 99 | let image = if strip_len >= width * height / 10 * 16 { 100 | load_12bit_raw(buffer, width, height)? 101 | } else { 102 | load_compressed_raw(buffer, width, height)? 103 | }; 104 | 105 | let black_level = self.info.u16("black_level")?; 106 | let bps_scale = self.get_bps_scale()?; 107 | Ok(image 108 | .iter() 109 | .map(|x| bps_scale.saturating_mul(x.saturating_sub(black_level))) 110 | .collect()) 111 | } 112 | fn get_thumbnail<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8], DecodingError> { 113 | let base = self.info.usize("maker_notes")?; 114 | let offset = self.info.usize("preview_image_start")? + base; 115 | let len = self.info.usize("preview_image_len")?; 116 | Ok(&buffer[offset..offset + len]) 117 | } 118 | } 119 | 120 | #[inline(always)] 121 | fn fast_inc_get(vec: &[T], index: &mut usize) -> T { 122 | unsafe { 123 | let ret = *vec.get_unchecked(*index); 124 | *index += 1; 125 | ret 126 | } 127 | } 128 | #[inline(always)] 129 | fn fast_inc_set(vec: &mut [T], index: &mut usize, value: T) { 130 | unsafe { 131 | *vec.get_unchecked_mut(*index) = value; 132 | *index += 1; 133 | } 134 | } 135 | 136 | fn load_compressed_raw(buf: &[u8], width: usize, height: usize) -> Result, DecodingError> { 137 | let mut out = vec![0u16; width * height]; 138 | 139 | let mut left: [i32; 2] = [0; 2]; 140 | let mut nw: [i32; 2] = [0; 2]; 141 | let mut pump = BitPumpMSB::new(&buf[7..]); 142 | let mut set_index = 0; 143 | let mut get_index = 0; 144 | 145 | for row in 0..height { 146 | let mut acarry: [[i32; 3]; 2] = [[0; 3]; 2]; 147 | 148 | for c in 0..width / 2 { 149 | let col: usize = c * 2; 150 | for s in 0..2 { 151 | // Run twice for odd and even pixels 152 | let i = if acarry[s][2] < 3 { 2 } else { 0 }; 153 | let mut nbits = 2 + i; 154 | while ((acarry[s][0] >> (nbits + i)) & 0xffff) > 0 { 155 | nbits += 1 156 | } 157 | nbits = cmp::min(nbits, 16); 158 | let b = pump.peek_ibits(15); 159 | 160 | let sign: i32 = -(b >> 14); 161 | let low: i32 = (b >> 12) & 3; 162 | let mut high: i32 = BITTABLE[(b & 4095) as usize]; 163 | 164 | // Skip bytes used above or read bits 165 | if high == 12 { 166 | pump.consume_bits(15); 167 | high = pump.get_ibits(16 - nbits) >> 1; 168 | } else { 169 | pump.consume_bits((high + 4) as u32); 170 | } 171 | 172 | acarry[s][0] = ((high << nbits) | pump.get_ibits(nbits)) as i32; 173 | let diff = (acarry[s][0] ^ sign) + acarry[s][1]; 174 | acarry[s][1] = (diff * 3 + acarry[s][1]) >> 5; 175 | acarry[s][2] = if acarry[s][0] > 16 { 176 | 0 177 | } else { 178 | acarry[s][2] + 1 179 | }; 180 | 181 | let v = if row < 2 || col < 2 { 182 | // We're in a border, special care is needed 183 | let pred = if row < 2 && col < 2 { 184 | // We're in the top left corner 185 | 0 186 | } else if row < 2 { 187 | // We're going along the top border 188 | left[s] 189 | } else { 190 | // col < 2, we're at the start of a line 191 | nw[s] = fast_inc_get(&out, &mut get_index) as i32; 192 | nw[s] 193 | }; 194 | left[s] = pred + ((diff << 2) | low); 195 | left[s] as u16 196 | } else { 197 | let up: i32 = fast_inc_get(&out, &mut get_index) as i32; 198 | let left_minus_nw: i32 = left[s] - nw[s]; 199 | let up_minus_nw: i32 = up - nw[s]; 200 | // Check if sign is different, and one is not zero 201 | let pred = if left_minus_nw * up_minus_nw < 0 { 202 | if left_minus_nw.abs() > 32 || up_minus_nw.abs() > 32 { 203 | left[s] + up_minus_nw 204 | } else { 205 | (left[s] + up) >> 1 206 | } 207 | } else if left_minus_nw.abs() > up_minus_nw.abs() { 208 | left[s] 209 | } else { 210 | up 211 | }; 212 | 213 | left[s] = pred + ((diff << 2) | low); 214 | nw[s] = up; 215 | left[s] as u16 216 | }; 217 | fast_inc_set(&mut out, &mut set_index, v); 218 | } 219 | } 220 | } 221 | 222 | Ok(out) 223 | } 224 | 225 | fn load_12bit_raw(buf: &[u8], width: usize, height: usize) -> Result, DecodingError> { 226 | let perline = width * 12 / 8 + ((width + 2) / 10); 227 | let mut out = vec![0u16; width * height]; 228 | 229 | out.chunks_exact_mut(width) 230 | .enumerate() 231 | .for_each(|(index, out)| { 232 | let inb = &buf[(index * perline)..]; 233 | 234 | for (oc, ic) in out.chunks_exact_mut(10).zip(inb.chunks_exact(16)) { 235 | for (o, i) in oc.chunks_exact_mut(2).zip(ic.chunks_exact(3)) { 236 | let g1: u16 = i[0] as u16; 237 | let g2: u16 = i[1] as u16; 238 | let g3: u16 = i[2] as u16; 239 | 240 | o[0] = ((g2 & 0x0f) << 8) | g1; 241 | o[1] = (g3 << 4) | (g2 >> 4); 242 | } 243 | } 244 | }); 245 | 246 | Ok(out) 247 | } 248 | 249 | static BITTABLE: [i32; 4096] = [ 250 | 12, 11, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 251 | 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 252 | 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 253 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 254 | 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 256 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 257 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 258 | 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 259 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 260 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 261 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 262 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 263 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 264 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 265 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 266 | 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 267 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 268 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 269 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 270 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 271 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 272 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 273 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 274 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 275 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 276 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 277 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 278 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 279 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 280 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 281 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 282 | 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 283 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 284 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 285 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 286 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 287 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 288 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 289 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 290 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 291 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 292 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 293 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 294 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 295 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 296 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 297 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 298 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 299 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 300 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 301 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 302 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 303 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 304 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 305 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 306 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 307 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 308 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 309 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 310 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 311 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 312 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 313 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 314 | 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 315 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 316 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 317 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 321 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 322 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 323 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 324 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 326 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 327 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 328 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 329 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 330 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 331 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 332 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 334 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 335 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 336 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 337 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 338 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 339 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 340 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 341 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 342 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 343 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 344 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 345 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 346 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 347 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 348 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 349 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 350 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 353 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 354 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 355 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 356 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 357 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 358 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 359 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 361 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 362 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 363 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 364 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 365 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 366 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 367 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 369 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 370 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 371 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 372 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 373 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 374 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 375 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 376 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 377 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 378 | 0, 0, 379 | ]; 380 | -------------------------------------------------------------------------------- /src/pass/demosaicing/linear.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[inline(always)] 4 | pub(super) fn rggb(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 5 | match bayer_pixel_info(i, w, h) { 6 | // top left corner 7 | (true, _, true, _, _, _) => [v, avg(image, &[i + 1, i + w]), get_pixel(image, i + w + 1)], 8 | // top right corner 9 | (true, _, _, true, _, _) => [get_pixel(image, i - 1), v, get_pixel(image, i + w)], 10 | // bottom left corner 11 | (_, true, true, _, _, _) => [get_pixel(image, i - w), v, get_pixel(image, i + 1)], 12 | // bottom right corner 13 | (_, true, _, true, _, _) => [get_pixel(image, i - w - 1), avg(image, &[i - w, i - 1]), v], 14 | // top edge 15 | (true, _, _, _, true, _) => [ 16 | v, 17 | avg(image, &[i - 1, i + w, i + 1]), 18 | avg(image, &[i + w - 1, i + w + 1]), 19 | ], 20 | (true, _, _, _, false, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i + w)], 21 | // bottom edge 22 | (_, true, _, _, true, _) => [get_pixel(image, i - w), v, avg(image, &[i - 1, i + 1])], 23 | (_, true, _, _, false, _) => [get_pixel(image, i - w - 1), avg(image, &[i - w, i - 1]), v], 24 | // left edge 25 | (_, _, true, _, _, true) => [ 26 | v, 27 | avg(image, &[i - w, i + 1, i + w]), 28 | avg(image, &[i - w + 1, i + w + 1]), 29 | ], 30 | (_, _, true, _, _, false) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i + 1)], 31 | // right edge 32 | (_, _, _, true, _, true) => [get_pixel(image, i - 1), v, get_pixel(image, i + w)], 33 | (_, _, _, true, _, false) => [ 34 | avg(image, &[i - w - 1, i + w - 1]), 35 | avg(image, &[i - w, i + w, i - 1]), 36 | v, 37 | ], 38 | // red 39 | (_, _, _, _, true, true) => [ 40 | v, 41 | avg(image, &[i - w, i + w, i - 1, i + 1]), 42 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 43 | ], 44 | // green1 45 | (_, _, _, _, false, true) => [avg(image, &[i - 1, i + 1]), v, avg(image, &[i - w, i + w])], 46 | // green2 47 | (_, _, _, _, true, false) => [avg(image, &[i - w, i + w]), v, avg(image, &[i - 1, i + 1])], 48 | // blue 49 | (_, _, _, _, false, false) => [ 50 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 51 | avg(image, &[i - w, i + w, i - 1, i + 1]), 52 | v, 53 | ], 54 | } 55 | } 56 | 57 | #[inline(always)] 58 | pub(super) fn bggr(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 59 | match bayer_pixel_info(i, w, h) { 60 | // top left corner 61 | (true, _, true, _, _, _) => [get_pixel(image, i + w + 1), avg(image, &[i + 1, i + w]), v], 62 | // top right corner 63 | (true, _, _, true, _, _) => [get_pixel(image, i + w), v, get_pixel(image, i - 1)], 64 | // bottom left corner 65 | (_, true, true, _, _, _) => [get_pixel(image, i + 1), v, get_pixel(image, i - w)], 66 | // bottom right corner 67 | (_, true, _, true, _, _) => [v, avg(image, &[i - w, i - 1]), get_pixel(image, i - w - 1)], 68 | // top edge 69 | (true, _, _, _, true, _) => [ 70 | avg(image, &[i + w - 1, i + w + 1]), 71 | avg(image, &[i - 1, i + w, i + 1]), 72 | v, 73 | ], 74 | (true, _, _, _, false, _) => [get_pixel(image, i + w), v, avg(image, &[i - 1, i + 1])], 75 | // bottom edge 76 | (_, true, _, _, true, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i - w)], 77 | (_, true, _, _, false, _) => [v, avg(image, &[i - w, i - 1]), get_pixel(image, i - w - 1)], 78 | // left edge 79 | (_, _, true, _, _, true) => [ 80 | avg(image, &[i - w + 1, i + w + 1]), 81 | avg(image, &[i - w, i + 1, i + w]), 82 | v, 83 | ], 84 | (_, _, true, _, _, false) => [get_pixel(image, i + 1), v, avg(image, &[i - w, i + w])], 85 | // right edge 86 | (_, _, _, true, _, true) => [get_pixel(image, i + w), v, get_pixel(image, i - 1)], 87 | (_, _, _, true, _, false) => [ 88 | v, 89 | avg(image, &[i - w, i + w, i - 1]), 90 | avg(image, &[i - w - 1, i + w - 1]), 91 | ], 92 | // blue 93 | (_, _, _, _, true, true) => [ 94 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 95 | avg(image, &[i - w, i + w, i - 1, i + 1]), 96 | v, 97 | ], 98 | // green2 99 | (_, _, _, _, false, true) => [avg(image, &[i - w, i + w]), v, avg(image, &[i - 1, i + 1])], 100 | // green1 101 | (_, _, _, _, true, false) => [avg(image, &[i - 1, i + 1]), v, avg(image, &[i - w, i + w])], 102 | // red 103 | (_, _, _, _, false, false) => [ 104 | v, 105 | avg(image, &[i - w, i + w, i - 1, i + 1]), 106 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 107 | ], 108 | } 109 | } 110 | 111 | #[inline(always)] 112 | pub(super) fn grbg(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 113 | match bayer_pixel_info(i, w, h) { 114 | // top left corner 115 | (true, _, true, _, _, _) => [get_pixel(image, i + 1), v, get_pixel(image, i + w)], 116 | // top right corner 117 | (true, _, _, true, _, _) => [v, avg(image, &[i - 1, i + w]), get_pixel(image, i + w - 1)], 118 | // bottom left corner 119 | (_, true, true, _, _, _) => [get_pixel(image, i - w + 1), avg(image, &[i - w, i + 1]), v], 120 | // bottom right corner 121 | (_, true, _, true, _, _) => [get_pixel(image, i - w), v, get_pixel(image, i - 1)], 122 | // top edge 123 | (true, _, _, _, true, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i + w)], 124 | (true, _, _, _, false, _) => [ 125 | v, 126 | avg(image, &[i - 1, i + 1, i + w]), 127 | avg(image, &[i + w - 1, i + w + 1]), 128 | ], 129 | // bottom edge 130 | (_, true, _, _, true, _) => [ 131 | avg(image, &[i - w - 1, i - w + 1]), 132 | avg(image, &[i - 1, i + 1, i - w]), 133 | v, 134 | ], 135 | (_, true, _, _, false, _) => [get_pixel(image, i - w), v, avg(image, &[i - 1, i + 1])], 136 | // left edge 137 | (_, _, true, _, _, true) => [get_pixel(image, i + 1), v, avg(image, &[i - w, i + w])], 138 | (_, _, true, _, _, false) => [ 139 | avg(image, &[i - w + 1, i + w + 1]), 140 | avg(image, &[i - w, i + w, i + 1]), 141 | v, 142 | ], 143 | // right edge 144 | (_, _, _, true, _, true) => [ 145 | v, 146 | avg(image, &[i - w, i - 1, i + w]), 147 | avg(image, &[i - w - 1, i + w - 1]), 148 | ], 149 | (_, _, _, true, _, false) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i - 1)], 150 | // green1 151 | (_, _, _, _, true, true) => [avg(image, &[i - 1, i + 1]), v, avg(image, &[i - w, i + w])], 152 | // red 153 | (_, _, _, _, false, true) => [ 154 | v, 155 | avg(image, &[i - w, i + w, i - 1, i + 1]), 156 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 157 | ], 158 | // blue 159 | (_, _, _, _, true, false) => [ 160 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 161 | avg(image, &[i - w, i + w, i - 1, i + 1]), 162 | v, 163 | ], 164 | // green2 165 | (_, _, _, _, false, false) => [avg(image, &[i - w, i + w]), v, avg(image, &[i - 1, i + 1])], 166 | } 167 | } 168 | 169 | #[inline(always)] 170 | pub(super) fn gbrg(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 171 | match bayer_pixel_info(i, w, h) { 172 | // top left corner 173 | (true, _, true, _, _, _) => [get_pixel(image, i + w), v, get_pixel(image, i + 1)], 174 | // top right corner 175 | (true, _, _, true, _, _) => [get_pixel(image, i + w - 1), avg(image, &[i - 1, i + w]), v], 176 | // bottom left corner 177 | (_, true, true, _, _, _) => [v, avg(image, &[i - w, i + 1]), get_pixel(image, i - w + 1)], 178 | // bottom right corner 179 | (_, true, _, true, _, _) => [get_pixel(image, i - 1), v, get_pixel(image, i - w)], 180 | // top edge 181 | (true, _, _, _, true, _) => [get_pixel(image, i + w), v, avg(image, &[i - 1, i + 1])], 182 | (true, _, _, _, false, _) => [ 183 | avg(image, &[i + w - 1, i + w + 1]), 184 | avg(image, &[i - 1, i + 1, i + w]), 185 | v, 186 | ], 187 | // bottom edge 188 | (_, true, _, _, true, _) => [ 189 | v, 190 | avg(image, &[i - 1, i + 1, i - w]), 191 | avg(image, &[i - w - 1, i - w + 1]), 192 | ], 193 | (_, true, _, _, false, _) => [avg(image, &[i - 1, i + 1]), v, get_pixel(image, i - w)], 194 | // left edge 195 | (_, _, true, _, _, true) => [avg(image, &[i - w, i + w]), v, get_pixel(image, i + 1)], 196 | (_, _, true, _, _, false) => [ 197 | v, 198 | avg(image, &[i - w, i + w, i + 1]), 199 | avg(image, &[i - w + 1, i + w + 1]), 200 | ], 201 | // right edge 202 | (_, _, _, true, _, true) => [ 203 | avg(image, &[i - w - 1, i + w - 1]), 204 | avg(image, &[i - w, i - 1, i + w]), 205 | v, 206 | ], 207 | (_, _, _, true, _, false) => [get_pixel(image, i - 1), v, avg(image, &[i - w, i + w])], 208 | // green2 209 | (_, _, _, _, true, true) => [avg(image, &[i - w, i + w]), v, avg(image, &[i - 1, i + 1])], 210 | // blue 211 | (_, _, _, _, false, true) => [ 212 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 213 | avg(image, &[i - w, i + w, i - 1, i + 1]), 214 | v, 215 | ], 216 | // red 217 | (_, _, _, _, true, false) => [ 218 | v, 219 | avg(image, &[i - w, i + w, i - 1, i + 1]), 220 | avg(image, &[i - w - 1, i - w + 1, i + w - 1, i + w + 1]), 221 | ], 222 | // green1 223 | (_, _, _, _, false, false) => [avg(image, &[i - 1, i + 1]), v, avg(image, &[i - w, i + w])], 224 | } 225 | } 226 | 227 | #[inline(always)] 228 | pub(super) fn xtrans0(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 229 | let x = i % w; 230 | let y = i / w; 231 | let is_top = y == 0; 232 | let is_left = x == 0; 233 | let is_bottom = y == h - 1; 234 | let is_right = x == w - 1; 235 | 236 | let index = (x % 6, y % 6); 237 | macro_rules! avg { 238 | (137) => { 239 | avg(image, &[i - w, i - 1, i + w]) 240 | }; 241 | (056) => { 242 | avg(image, &[i - w - 1, i + w - 1, i + 1]) 243 | }; 244 | (238) => { 245 | avg(image, &[i + w + 1, i - 1, i + w + 1]) 246 | }; 247 | (157) => { 248 | avg(image, &[i - w, i + 1, i + w]) 249 | }; 250 | (17) => { 251 | avg(image, &[i - w, i + w]) 252 | }; 253 | (35) => { 254 | avg(image, &[i - 1, i + 1]) 255 | }; 256 | (16) => { 257 | avg(image, &[i - w, i + w - 1]) 258 | }; 259 | (56) => { 260 | avg(image, &[i + 1, i + w - 1]) 261 | }; 262 | (23) => { 263 | avg(image, &[i - w + 1, i - 1]) 264 | }; 265 | (05) => { 266 | avg(image, &[i - w - 1, i + 1]) 267 | }; 268 | (07) => { 269 | avg(image, &[i - w - 1, i + w]) 270 | }; 271 | (27) => { 272 | avg(image, &[i - w + 1, i + w]) 273 | }; 274 | (18) => { 275 | avg(image, &[i - w, i + w + 1]) 276 | }; 277 | (38) => { 278 | avg(image, &[i - 1, i + w + 1]) 279 | }; 280 | (135) => { 281 | avg(image, &[i - w, i - 1, i + 1]) 282 | }; 283 | (027) => { 284 | avg(image, &[i - w - 1, i - w + 1, i + w]) 285 | }; 286 | (168) => { 287 | avg(image, &[i - w, i + w - 1, i + w + 1]) 288 | }; 289 | (357) => { 290 | avg(image, &[i - 1, i + 1, i + w]) 291 | }; 292 | (35) => { 293 | avg(image, &[i - 1, i + 1]) 294 | }; 295 | (57) => { 296 | avg(image, &[i + 1, i + w]) 297 | }; 298 | (37) => { 299 | avg(image, &[i - 1, i + w]) 300 | }; 301 | (17) => { 302 | avg(image, [i - w, i + w]) 303 | }; 304 | } 305 | macro_rules! p { 306 | ($i:expr) => { 307 | get_pixel(image, $i) 308 | }; 309 | } 310 | 311 | match (is_top, is_left, is_bottom, is_right, index) { 312 | (false, false, false, false, (0, 0)) => [v, avg!(137), avg!(056)], 313 | (false, false, false, false, (1, 0)) => [avg!(238), avg!(157), v], 314 | (false, false, false, false, (2, 0)) => [avg!(17), v, avg!(35)], 315 | (false, false, false, false, (3, 0)) => [avg!(056), avg!(137), v], 316 | (false, false, false, false, (4, 0)) => [v, avg!(157), avg!(238)], 317 | (false, false, false, false, (5, 0)) => [avg!(35), v, avg!(17)], 318 | 319 | (false, false, false, false, (0, 1)) => [avg!(16), v, avg!(23)], 320 | (false, false, false, false, (1, 1)) => [avg!(05), v, avg!(18)], 321 | (false, false, false, false, (2, 1)) => [v, avg!(135), avg!(027)], 322 | (false, false, false, false, (3, 1)) => [avg!(23), v, avg!(16)], 323 | (false, false, false, false, (4, 1)) => [avg!(18), v, avg!(05)], 324 | (false, false, false, false, (5, 1)) => [avg!(027), avg!(135), v], 325 | 326 | (false, false, false, false, (0, 2)) => [avg!(38), v, avg!(07)], 327 | (false, false, false, false, (1, 2)) => [avg!(27), v, avg!(56)], 328 | (false, false, false, false, (2, 2)) => [avg!(168), avg!(357), v], 329 | (false, false, false, false, (3, 2)) => [avg!(07), v, avg!(38)], 330 | (false, false, false, false, (4, 2)) => [avg!(56), v, avg!(27)], 331 | (false, false, false, false, (5, 2)) => [v, avg!(357), avg!(168)], 332 | 333 | (false, false, false, false, (0, 3)) => [avg!(056), avg!(137), v], 334 | (false, false, false, false, (1, 3)) => [v, avg!(157), avg!(238)], 335 | (false, false, false, false, (2, 3)) => [avg!(35), v, avg!(17)], 336 | (false, false, false, false, (3, 3)) => [v, avg!(137), avg!(056)], 337 | (false, false, false, false, (4, 3)) => [avg!(238), avg!(157), v], 338 | (false, false, false, false, (5, 3)) => [avg!(17), v, avg!(35)], 339 | 340 | (false, false, false, false, (0, 4)) => [avg!(23), v, avg!(16)], 341 | (false, false, false, false, (1, 4)) => [avg!(18), v, avg!(05)], 342 | (false, false, false, false, (2, 4)) => [avg!(027), avg!(135), v], 343 | (false, false, false, false, (3, 4)) => [avg!(16), v, avg!(23)], 344 | (false, false, false, false, (4, 4)) => [avg!(05), v, avg!(18)], 345 | (false, false, false, false, (5, 4)) => [v, avg!(135), avg!(027)], 346 | 347 | (false, false, false, false, (0, 5)) => [avg!(07), v, avg!(38)], 348 | (false, false, false, false, (1, 5)) => [avg!(56), v, avg!(27)], 349 | (false, false, false, false, (2, 5)) => [v, avg!(357), avg!(168)], 350 | (false, false, false, false, (3, 5)) => [avg!(38), v, avg!(07)], 351 | (false, false, false, false, (4, 5)) => [avg!(27), v, avg!(56)], 352 | (false, false, false, false, (5, 5)) => [avg!(168), avg!(357), v], 353 | 354 | (true, _, _, _, (0, _)) => [v, p!(i + w), p!(i + 1)], 355 | (true, _, _, _, (1, _)) => [p!(i - 1), avg!(57), v], 356 | (true, _, _, _, (2, _)) => [p!(i + w), v, avg!(35)], 357 | (true, _, _, _, (3, _)) => [p!(i + 1), avg!(37), v], 358 | (true, _, _, _, (4, _)) => [v, p!(i + w), p!(i - 1)], 359 | (true, _, _, _, (5, _)) => [p!(i - 1), v, p!(i + w)], 360 | 361 | (_, _, true, _, (0, _)) => [p!(i - w + 2), v, p!(i + 2)], 362 | (_, _, true, _, (1, _)) => [p!(i + 1), v, p!(i - w + 1)], 363 | (_, _, true, _, (2, _)) => [v, avg!(35), p!(i - w)], 364 | (_, _, true, _, (3, _)) => [p!(i - 1), v, p!(i - w - 1)], 365 | (_, _, true, _, (4, _)) => [p!(i - w + 1), v, p!(i + 1)], 366 | (_, _, true, _, (5, _)) => [p!(i - w), p!(i - 1), v], 367 | 368 | (_, true, _, _, (_, 0)) => [v, p!(i + w), p!(i + 1)], 369 | (_, true, _, _, (_, 1)) => [p!(i - w), v, p!(i - w + 1)], 370 | (_, true, _, _, (_, 2)) => [p!(i + w + 1), v, p!(i + w)], 371 | (_, true, _, _, (_, 3)) => [p!(i + 1), avg!(17), v], 372 | (_, true, _, _, (_, 4)) => [p!(i - w + 1), v, p!(i - w)], 373 | (_, true, _, _, (_, 5)) => [p!(i + 2), v, p!(i - w * 2)], 374 | 375 | (_, _, _, true, (_, 1)) => [p!(i + w), p!(i - 1), v], 376 | (_, _, _, true, (_, 2)) => [v, p!(i - 1), p!(i - w)], 377 | (_, _, _, true, (_, 3)) => [avg!(17), v, p!(i - 1)], 378 | (_, _, _, true, (_, 4)) => [v, p!(i - 1), p!(i + w)], 379 | (_, _, _, true, (_, 5)) => [p!(i - w), p!(i - 1), v], 380 | 381 | _ => [0; 3], 382 | } 383 | } 384 | 385 | #[inline(always)] 386 | pub(super) fn xtrans1(i: usize, v: u16, image: &[u16], w: usize, h: usize) -> [u16; 3] { 387 | let x = i % w; 388 | let y = i / w; 389 | let is_top = y == 0; 390 | let is_left = x == 0; 391 | let is_bottom = y == h - 1; 392 | let is_right = x == w - 1; 393 | 394 | let index = (x % 6, y % 6); 395 | macro_rules! avg { 396 | (137) => { 397 | avg(image, &[i - w, i - 1, i + w]) 398 | }; 399 | (056) => { 400 | avg(image, &[i - w - 1, i + w - 1, i + 1]) 401 | }; 402 | (238) => { 403 | avg(image, &[i + w + 1, i - 1, i + w + 1]) 404 | }; 405 | (157) => { 406 | avg(image, &[i - w, i + 1, i + w]) 407 | }; 408 | (17) => { 409 | avg(image, &[i - w, i + w]) 410 | }; 411 | (35) => { 412 | avg(image, &[i - 1, i + 1]) 413 | }; 414 | (16) => { 415 | avg(image, &[i - w, i + w - 1]) 416 | }; 417 | (56) => { 418 | avg(image, &[i + 1, i + w - 1]) 419 | }; 420 | (23) => { 421 | avg(image, &[i - w + 1, i - 1]) 422 | }; 423 | (05) => { 424 | avg(image, &[i - w - 1, i + 1]) 425 | }; 426 | (07) => { 427 | avg(image, &[i - w - 1, i + w]) 428 | }; 429 | (27) => { 430 | avg(image, &[i - w + 1, i + w]) 431 | }; 432 | (18) => { 433 | avg(image, &[i - w, i + w + 1]) 434 | }; 435 | (38) => { 436 | avg(image, &[i - 1, i + w + 1]) 437 | }; 438 | (135) => { 439 | avg(image, &[i - w, i - 1, i + 1]) 440 | }; 441 | (027) => { 442 | avg(image, &[i - w - 1, i - w + 1, i + w]) 443 | }; 444 | (168) => { 445 | avg(image, &[i - w, i + w - 1, i + w + 1]) 446 | }; 447 | (357) => { 448 | avg(image, &[i - 1, i + 1, i + w]) 449 | }; 450 | (35) => { 451 | avg(image, &[i - 1, i + 1]) 452 | }; 453 | (57) => { 454 | avg(image, &[i + 1, i + w]) 455 | }; 456 | (37) => { 457 | avg(image, &[i - 1, i + w]) 458 | }; 459 | (17) => { 460 | avg(image, &[i - w, i + w]) 461 | }; 462 | } 463 | macro_rules! p { 464 | ($i:expr) => { 465 | get_pixel(image, $i) 466 | }; 467 | } 468 | 469 | match (is_top, is_left, is_bottom, is_right, index) { 470 | (false, false, false, false, (0, 5)) => [v, avg!(137), avg!(056)], 471 | (false, false, false, false, (1, 5)) => [avg!(238), avg!(157), v], 472 | (false, false, false, false, (2, 5)) => [avg!(17), v, avg!(35)], 473 | (false, false, false, false, (3, 5)) => [avg!(056), avg!(137), v], 474 | (false, false, false, false, (4, 5)) => [v, avg!(157), avg!(238)], 475 | (false, false, false, false, (5, 5)) => [avg!(35), v, avg!(17)], 476 | 477 | (false, false, false, false, (0, 0)) => [avg!(16), v, avg!(23)], 478 | (false, false, false, false, (1, 0)) => [avg!(05), v, avg!(18)], 479 | (false, false, false, false, (2, 0)) => [v, avg!(135), avg!(027)], 480 | (false, false, false, false, (3, 0)) => [avg!(23), v, avg!(16)], 481 | (false, false, false, false, (4, 0)) => [avg!(18), v, avg!(05)], 482 | (false, false, false, false, (5, 0)) => [avg!(027), avg!(135), v], 483 | 484 | (false, false, false, false, (0, 1)) => [avg!(38), v, avg!(07)], 485 | (false, false, false, false, (1, 1)) => [avg!(27), v, avg!(56)], 486 | (false, false, false, false, (2, 1)) => [avg!(168), avg!(357), v], 487 | (false, false, false, false, (3, 1)) => [avg!(07), v, avg!(38)], 488 | (false, false, false, false, (4, 1)) => [avg!(56), v, avg!(27)], 489 | (false, false, false, false, (5, 1)) => [v, avg!(357), avg!(168)], 490 | 491 | (false, false, false, false, (0, 2)) => [avg!(056), avg!(137), v], 492 | (false, false, false, false, (1, 2)) => [v, avg!(157), avg!(238)], 493 | (false, false, false, false, (2, 2)) => [avg!(35), v, avg!(17)], 494 | (false, false, false, false, (3, 2)) => [v, avg!(137), avg!(056)], 495 | (false, false, false, false, (4, 2)) => [avg!(238), avg!(157), v], 496 | (false, false, false, false, (5, 2)) => [avg!(17), v, avg!(35)], 497 | 498 | (false, false, false, false, (0, 3)) => [avg!(23), v, avg!(16)], 499 | (false, false, false, false, (1, 3)) => [avg!(18), v, avg!(05)], 500 | (false, false, false, false, (2, 3)) => [avg!(027), avg!(135), v], 501 | (false, false, false, false, (3, 3)) => [avg!(16), v, avg!(23)], 502 | (false, false, false, false, (4, 3)) => [avg!(05), v, avg!(18)], 503 | (false, false, false, false, (5, 3)) => [v, avg!(135), avg!(027)], 504 | 505 | (false, false, false, false, (0, 4)) => [avg!(07), v, avg!(38)], 506 | (false, false, false, false, (1, 4)) => [avg!(56), v, avg!(27)], 507 | (false, false, false, false, (2, 4)) => [v, avg!(357), avg!(168)], 508 | (false, false, false, false, (3, 4)) => [avg!(38), v, avg!(07)], 509 | (false, false, false, false, (4, 4)) => [avg!(27), v, avg!(56)], 510 | (false, false, false, false, (5, 4)) => [avg!(168), avg!(357), v], 511 | 512 | (true, _, _, _, (0, _)) => [p!(i + 2), v, p!(i + w + 2)], 513 | (true, _, _, _, (1, _)) => [p!(i + 1), v, p!(i + w + 1)], 514 | (true, _, _, _, (2, _)) => [v, avg!(35), p!(i + w)], 515 | (true, _, _, _, (3, _)) => [p!(i - 1), v, p!(i + w - 1)], 516 | (true, _, _, _, (4, _)) => [p!(i + w + 1), v, p!(i + 1)], 517 | (true, _, _, _, (5, _)) => [p!(i + w), p!(i - 1), v], 518 | 519 | (_, _, true, _, (0, _)) => [v, p!(i - w), p!(i + 1)], 520 | (_, _, true, _, (1, _)) => [p!(i - 1), p!(i - w), v], 521 | (_, _, true, _, (2, _)) => [p!(i - w), v, avg!(35)], 522 | (_, _, true, _, (3, _)) => [p!(i + 1), p!(i - w), v], 523 | (_, _, true, _, (4, _)) => [v, p!(i - w), p!(i - 1)], 524 | (_, _, true, _, (5, _)) => [p!(i - 1), v, p!(i - w)], 525 | 526 | (_, true, _, _, (_, 0)) => [p!(i + 2), v, p!(i + w + 2)], 527 | (_, true, _, _, (_, 1)) => [p!(i + w + 1), v, p!(i + w)], 528 | (_, true, _, _, (_, 2)) => [p!(i + 1), avg!(17), v], 529 | (_, true, _, _, (_, 3)) => [p!(i - w + 1), v, p!(i - w)], 530 | (_, true, _, _, (_, 4)) => [p!(i + 2), v, p!(i - w + 2)], 531 | (_, true, _, _, (_, 5)) => [v, p!(i - w), p!(i + 1)], 532 | 533 | (_, _, _, true, (_, 1)) => [v, p!(i - 1), p!(i - w)], 534 | (_, _, _, true, (_, 2)) => [avg!(17), v, p!(i - 1)], 535 | (_, _, _, true, (_, 3)) => [v, p!(i - 1), p!(i + w)], 536 | (_, _, _, true, (_, 4)) => [p!(i - w), p!(i - 1), v], 537 | (_, _, _, true, (_, 5)) => [p!(i - 1), v, p!(i - w)], 538 | 539 | _ => [0; 3], 540 | } 541 | } 542 | --------------------------------------------------------------------------------