use ops::*;
use opbasics::*;
extern crate multicache;
use self::multicache::MultiCache;
extern crate time;
extern crate serde;
extern crate serde_yaml;
use self::serde::{Serialize,Deserialize};
use std::fmt::Debug;
use std::sync::Arc;
use std::io::Write;
fn do_timing<O, F: FnMut() -> O>(_name: &str, mut closure: F) -> O {
let ret = closure();
ret
}
pub trait ImageOp<'a>: Debug+Serialize+Deserialize<'a> {
fn name(&self) -> &str;
fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash);
fn to_settings(&self) -> String {
serde_yaml::to_string(self).unwrap()
}
fn hash(&self, hasher: &mut BufHasher) {
hasher.write(self.name().as_bytes()).unwrap();
hasher.from_serialize(self);
}
}
#[derive(Debug, Copy, Clone, Serialize)]
pub struct PipelineSettings {
pub maxwidth: usize,
pub maxheight: usize,
pub linear: bool,
}
impl PipelineSettings{
fn hash(&self, hasher: &mut BufHasher) {
hasher.from_serialize(self);
}
}
#[derive(Debug)]
pub struct PipelineGlobals<'a> {
pub cache: MultiCache<BufHash, OpBuffer>,
pub image: &'a RawImage,
pub settings: PipelineSettings,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineOps {
pub gofloat: gofloat::OpGoFloat,
pub demosaic: demosaic::OpDemosaic,
pub level: level::OpLevel,
pub tolab: colorspaces::OpToLab,
pub basecurve: curves::OpBaseCurve,
pub fromlab: colorspaces::OpFromLab,
pub gamma: gamma::OpGamma,
pub transform: transform::OpTransform,
}
macro_rules! for_vals {
([$($val:expr),*] |$x:pat, $i:ident| $body:expr) => {
let mut pos = 0;
$({
let $x = $val;
pos += 1;
let $i = pos-1;
$body
})*
}
}
macro_rules! all_ops {
($ops:expr, |$x:pat, $i:ident| $body:expr) => {
for_vals!([
$ops.gofloat,
$ops.demosaic,
$ops.level,
$ops.tolab,
$ops.basecurve,
$ops.fromlab,
$ops.gamma,
$ops.transform
] |$x, $i| {
$body
});
}
}
#[derive(Debug)]
pub struct Pipeline<'a> {
pub globals: PipelineGlobals<'a>,
pub ops: PipelineOps,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PipelineSerialization {
pub version: u32,
pub filehash: String,
}
impl<'a> Pipeline<'a> {
pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
let (transpose, ..) = img.orientation.to_flips();
let (maxwidth, maxheight) = if transpose {
(maxheight, maxwidth)
} else {
(maxwidth, maxheight)
};
Pipeline {
globals: PipelineGlobals {
cache: MultiCache::new(1),
image: img,
settings: PipelineSettings {maxwidth, maxheight, linear},
},
ops: PipelineOps {
gofloat: gofloat::OpGoFloat::new(img),
demosaic: demosaic::OpDemosaic::new(img),
level: level::OpLevel::new(img),
tolab: colorspaces::OpToLab::new(img),
basecurve: curves::OpBaseCurve::new(img),
fromlab: colorspaces::OpFromLab::new(img),
gamma: gamma::OpGamma::new(img),
transform: transform::OpTransform::new(img),
},
}
}
pub fn to_serial(&self) -> String {
let serial = (PipelineSerialization {
version: 0,
filehash: "0".to_string(),
}, &self.ops);
serde_yaml::to_string(&serial).unwrap()
}
pub fn new_from_serial(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool, serial: String) -> Pipeline {
let serial: (PipelineSerialization, PipelineOps) = serde_yaml::from_str(&serial).unwrap();
Pipeline {
globals: PipelineGlobals {
cache: MultiCache::new(1),
image: img,
settings: PipelineSettings {maxwidth, maxheight, linear},
},
ops: serial.1,
}
}
pub fn run(&mut self) -> Arc<OpBuffer> {
let mut hasher = BufHasher::new();
let mut ophashes = Vec::new();
let mut startpos = 0;
self.globals.settings.hash(&mut hasher);
all_ops!(self.ops, |ref op, i| {
op.hash(&mut hasher);
let result = hasher.result();
ophashes.push(result);
if self.globals.cache.contains_key(&result) {
startpos = i+1;
}
});
let mut bufin = BufHash::default();
all_ops!(self.ops, |ref op, i| {
let hash = ophashes[i];
if i >= startpos { let globals = &mut self.globals;
do_timing(op.name(), ||op.run(globals, bufin, hash));
}
bufin = hash;
});
self.globals.cache.get(&bufin).unwrap()
}
}