-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
49 changed files
with
6,243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[x86_64-unknown-linux-gnu] | ||
rustflags = "-C target-feature=+crt-static-C target-cpu=sandybridge" | ||
|
||
[target.x86_64-pc-windows-msvc] | ||
rustflags = "-C target-feature=+crt-static -C target-cpu=sandybridge" | ||
|
||
[target.x86_64-apple-darwin] | ||
rustflags = "-C target-feature=+crt-static -C target-cpu=sandybridge -C link-arg=-mmacosx-version-min=10.12" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "quickraw" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
attrs = {path="attrs"} | ||
anyhow = "1" | ||
image = "0.23" | ||
thiserror = "1.0" | ||
phf = {version = "0.10", features = ["macros"]} | ||
rayon = "1.5" | ||
|
||
[profile.release] | ||
codegen-units = 1 | ||
lto="fat" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "attrs" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
syn = {version="1", features=["full"]} | ||
quote = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use proc_macro::TokenStream; | ||
use quote::quote; | ||
use syn::{parse_macro_input, ItemFn}; | ||
|
||
#[proc_macro_attribute] | ||
pub fn bench(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let ItemFn { attrs, vis, sig, block } = parse_macro_input!(item as ItemFn); | ||
|
||
let name = match syn::parse::<syn::Ident>(attr) { | ||
Ok(x) => x.to_string(), | ||
Err(_) => sig.ident.to_string(), | ||
}; | ||
|
||
let ts = quote! { | ||
#(#attrs)* #vis #sig { | ||
let __t = std::time::Instant::now(); | ||
let __result = #block; | ||
match std::env::var(crate::BENCH_FLAG) { | ||
Ok(_) => { | ||
println!("{}: {:?}", #name, __t.elapsed()); | ||
}, | ||
Err(_) => {} | ||
}; | ||
__result | ||
} | ||
}; | ||
ts.into() | ||
} | ||
|
||
#[proc_macro_attribute] | ||
pub fn pause(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let ItemFn { attrs, vis, sig, block } = parse_macro_input!(item as ItemFn); | ||
let fn_name = sig.ident.to_string(); | ||
|
||
let token_flush_pause = quote! { | ||
{ | ||
use std::io::Write; | ||
std::io::stdout().flush().unwrap(); | ||
} | ||
std::io::stdin().read_line(&mut String::new()).unwrap(); | ||
}; | ||
let pause_print = quote! { | ||
print!("Press enter to start '{}'... ", #fn_name); | ||
#token_flush_pause | ||
}; | ||
|
||
let pause_end = match syn::parse::<syn::Ident>(attr) { | ||
Ok(x) => { | ||
if &x.to_string() == "both" { | ||
quote! { | ||
print!("Press enter to end '{}'... ", #fn_name); | ||
#token_flush_pause | ||
} | ||
} else { | ||
quote! {} | ||
} | ||
} | ||
Err(_) => quote! {}, | ||
}; | ||
|
||
let ts = quote! { | ||
#(#attrs)* #vis #sig { | ||
#pause_print; | ||
let __result = #block; | ||
#pause_end; | ||
__result | ||
} | ||
}; | ||
ts.into() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@echo off | ||
cargo build -r & target\release\quickraw.exe %* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use super::{*, utility::*}; | ||
use std::cmp; | ||
|
||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html | ||
pub const XYZ2ADOBE_RGB: [f32; 9] = [ | ||
1.8037626, | ||
-0.49918914, | ||
-0.3045735, | ||
-1.0221082, | ||
1.9782866, | ||
0.04382154, | ||
0.014769779, | ||
-0.13003181, | ||
1.115262, | ||
]; | ||
|
||
pub const XYZ2SRGB: [f32; 9] = [ | ||
2.689655, | ||
-1.275862, | ||
-0.4137931, | ||
-1.0221082, | ||
1.9782866, | ||
0.04382154, | ||
0.061224457, | ||
-0.22448978, | ||
1.1632653, | ||
]; | ||
|
||
pub const XYZ2RAW: [f32; 9] = [1.0, 0., 0., 0., 1.0, 0., 0., 0., 1.0]; | ||
|
||
pub const GAMMA_LINEAR: [f32; 2] = [1.0, 0.0]; | ||
pub const GAMMA_SRGB: [f32; 2] = [0.45, 4.5]; | ||
|
||
impl ColorConversion { | ||
const CLIP_LIMIT_I32: i32 = 65535; | ||
const CLIP_RANGE : (i32, i32) = (0, Self::CLIP_LIMIT_I32); | ||
|
||
pub fn new(raw_job : &RawJob, color_space : [f32;9], gamma: [f32;2]) -> Self { | ||
let color_space = matrix3_mul(&color_space, &raw_job.cam_matrix); | ||
let color_space = color_space.mul(1 << BIT_SHIFT); | ||
let white_balance = raw_job.white_balance.mul(1 << (BIT_SHIFT - log2(raw_job.white_balance[1]))); | ||
let gamma_lut = gen_gamma_lut(gamma); | ||
ColorConversion { | ||
white_balance, | ||
gamma_lut, | ||
color_space, | ||
} | ||
} | ||
|
||
#[inline(always)] | ||
fn limit_to_range<T: Ord>(v: T, (left, right): (T, T)) -> T { | ||
cmp::min(cmp::max(v, left), right) | ||
} | ||
|
||
#[inline(always)] | ||
fn rgb_color_shift_i32((r, g, b): (i32, i32, i32), wb: &[i32; 3], c: &[i32; 9]) -> (usize, usize, usize) { | ||
let r = cmp::min((r * wb[0]) >> BIT_SHIFT, Self::CLIP_LIMIT_I32); | ||
let g = cmp::min((g * wb[1]) >> BIT_SHIFT, Self::CLIP_LIMIT_I32); | ||
let b = cmp::min((b * wb[2]) >> BIT_SHIFT, Self::CLIP_LIMIT_I32); | ||
|
||
( | ||
Self::limit_to_range((c[0] * r + c[1] * g + c[2] * b) >> BIT_SHIFT, Self::CLIP_RANGE) as usize, | ||
Self::limit_to_range((c[3] * r + c[4] * g + c[5] * b) >> BIT_SHIFT, Self::CLIP_RANGE) as usize, | ||
Self::limit_to_range((c[6] * r + c[7] * g + c[8] * b) >> BIT_SHIFT, Self::CLIP_RANGE) as usize, | ||
) | ||
} | ||
|
||
#[inline(always)] | ||
pub fn convert(&self, rgb: (i32, i32, i32)) -> [u16; 3] { | ||
let (r, g, b) = Self::rgb_color_shift_i32(rgb, &self.white_balance, &self.color_space); | ||
[self.gamma_lut[r], self.gamma_lut[g], self.gamma_lut[b]] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
use std::{ | ||
fs::File, | ||
io::{BufWriter, Write}, | ||
}; | ||
|
||
use image::{ | ||
codecs::jpeg, imageops, ColorType, EncodableLayout, ImageBuffer, ImageEncoder, ImageFormat, ImageResult, Rgb, | ||
}; | ||
|
||
use super::*; | ||
use crate::{ | ||
raw::{Orientation, RawImage}, | ||
tiff::{RawInfoError, ValueError}, | ||
}; | ||
|
||
trait SaveWithQuality { | ||
fn save_with_quality(&self, path: &str, quality: u8) -> ImageResult<()>; | ||
} | ||
|
||
impl<T: 'static + image::Primitive> SaveWithQuality for ImageBuffer<Rgb<T>, Vec<T>> | ||
where | ||
[T]: image::EncodableLayout, | ||
{ | ||
fn save_with_quality(&self, path: &str, quality: u8) -> ImageResult<()> { | ||
let format = ImageFormat::from_path(path)?; | ||
match format { | ||
ImageFormat::Jpeg => { | ||
let fout = &mut BufWriter::new(File::create(path)?); | ||
jpeg::JpegEncoder::new_with_quality(fout, quality).write_image( | ||
self.as_bytes(), | ||
self.width(), | ||
self.height(), | ||
ColorType::Rgb8, | ||
) | ||
} | ||
_ => self.save(path), | ||
} | ||
} | ||
} | ||
|
||
impl Export { | ||
pub fn cast_identity(x: u16) -> u16 { | ||
x | ||
} | ||
pub fn cast_u16_u8(x: u16) -> u8 { | ||
(x / 256) as u8 | ||
} | ||
pub fn new(input: Input, output: Output) -> Result<Self, RawFileReadingError> { | ||
let raw_job = match input { | ||
Input::ByFile(file) => RawJob::new(file), | ||
Input::ByBuffer(buffer) => RawJob::new_from_buffer(buffer), | ||
}?; | ||
|
||
let color_conversion = ColorConversion::new(&raw_job, output.color_space, output.gamma); | ||
let raw_image = RawImage::new(&raw_job)?; | ||
|
||
Ok(Export { | ||
raw_job, | ||
color_conversion, | ||
raw_image, | ||
output, | ||
}) | ||
} | ||
|
||
pub fn export_thumbnail_data<'a>(buffer: &'a [u8]) -> Result<(&'a [u8], Orientation), ExportError> { | ||
let (thumbnail, orientation) = RawJob::get_thumbnail(buffer)?; | ||
Ok((thumbnail, orientation)) | ||
} | ||
pub fn export_thumbnail_to_file(input_path: &str, output_path: &str) -> Result<(), ExportError> { | ||
let buffer = RawJob::get_buffer_from_file(input_path)?; | ||
let (thumbnail, orientation) = RawJob::get_thumbnail(buffer.as_slice())?; | ||
|
||
match orientation { | ||
Orientation::Horizontal => { | ||
let mut f = File::create(output_path) | ||
.map_err(|_| ExportError::ErrorWhenExportingFile(output_path.to_owned()))?; | ||
f.write_all(thumbnail) | ||
.map_err(|_| ExportError::ErrorWhenExportingFile(output_path.to_owned()))?; | ||
} | ||
_ => { | ||
let img = image::load_from_memory(thumbnail) | ||
.map_err(|_| ExportError::CannotReadThumbnail(thumbnail.len(), input_path.to_owned()))?; | ||
|
||
let img = match orientation { | ||
Orientation::Rotate90 => imageops::rotate90(&img), | ||
Orientation::Rotate180 => imageops::rotate180(&img), | ||
Orientation::Rotate270 => imageops::rotate270(&img), | ||
_ => img.to_rgba8(), | ||
}; | ||
|
||
img.save(output_path) | ||
.map_err(|_| ExportError::ErrorWhenExportingFile(output_path.to_owned()))?; | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[attrs::bench(demosaicing_with_postprocess)] | ||
pub fn export_image_data<T>(&self, cast_fn: fn(u16) -> T) -> (Vec<T>, usize, usize) { | ||
match self.output.demosaicing_method { | ||
DemosaicingMethod::None => self.raw_image.no_demosaic_render(&self.color_conversion, cast_fn), | ||
DemosaicingMethod::SuperPixel => self.raw_image.super_pixel_render(&self.color_conversion, cast_fn), | ||
DemosaicingMethod::Linear => self.raw_image.linear_render(&self.color_conversion, cast_fn), | ||
} | ||
} | ||
|
||
pub fn export_exif_info(&self) -> Result<String, ValueError> { | ||
self.raw_job.decoder.get_info().stringify_all() | ||
} | ||
|
||
pub fn export_exif_info_directly(input: Input) -> Result<String, RawFileReadingError> { | ||
let raw_job = match input { | ||
Input::ByFile(file) => RawJob::new(file), | ||
Input::ByBuffer(buffer) => RawJob::new_from_buffer(buffer), | ||
}?; | ||
raw_job | ||
.decoder | ||
.get_info() | ||
.stringify_all() | ||
.map_err(|err| RawInfoError::from(err).into()) | ||
} | ||
|
||
#[attrs::bench(writing_file)] | ||
fn write_to_file<T: 'static + image::Primitive>( | ||
&self, | ||
path: &String, | ||
(data, width, height): (Vec<T>, usize, usize), | ||
quality: u8, | ||
) -> Result<(), ExportError> | ||
where | ||
[T]: image::EncodableLayout, | ||
{ | ||
let len = data.len(); | ||
let mut image = ImageBuffer::<Rgb<T>, Vec<T>>::from_raw(width as u32, height as u32, data) | ||
.ok_or_else(|| ExportError::ImageBufferError(stringify!(T).to_owned(), len, width, height))?; | ||
|
||
let image = match (&self.raw_image.crop, self.output.auto_crop) { | ||
(Some(c), true) => imageops::crop(&mut image, c.x, c.y, c.width, c.height).to_image(), | ||
_ => image, | ||
}; | ||
let image = match (&self.raw_image.orientation, self.output.auto_rotate) { | ||
(Orientation::Horizontal, _) | (_, false) => image, | ||
(Orientation::Rotate90, true) => imageops::rotate90(&image), | ||
(Orientation::Rotate180, true) => imageops::rotate180(&image), | ||
(Orientation::Rotate270, true) => imageops::rotate270(&image), | ||
}; | ||
|
||
image | ||
.save_with_quality(path, quality) | ||
.map_err(|_| ExportError::ErrorWhenExportingFile(path.to_owned()))?; | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn export_to_file(&self, quality: u8) -> Result<(), ExportError> { | ||
match &self.output.output_type { | ||
OutputType::Image8(path) => { | ||
let data = self.export_image_data(Export::cast_u16_u8); | ||
self.write_to_file(path, data, quality)?; | ||
} | ||
OutputType::Image16(path) => { | ||
let data = self.export_image_data(Export::cast_identity); | ||
self.write_to_file(path, data, quality)?; | ||
} | ||
_ => {} | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.