Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
qdwang committed May 20, 2022
1 parent 012682d commit 4b8a7c3
Show file tree
Hide file tree
Showing 49 changed files with 6,243 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .cargo/config.toml
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"
16 changes: 16 additions & 0 deletions Cargo.toml
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"
11 changes: 11 additions & 0 deletions attrs/Cargo.toml
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"
70 changes: 70 additions & 0 deletions attrs/src/lib.rs
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()
}
2 changes: 2 additions & 0 deletions dev.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
cargo build -r & target\release\quickraw.exe %*
73 changes: 73 additions & 0 deletions src/color.rs
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]]
}
}
171 changes: 171 additions & 0 deletions src/export.rs
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(())
}
}
Loading

0 comments on commit 4b8a7c3

Please sign in to comment.