use opbasics::*;
use std::mem;
use std::usize;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct OpTransform {
pub orientation: Orientation,
}
impl OpTransform {
pub fn new(img: &RawImage) -> OpTransform {
OpTransform{
orientation: img.orientation,
}
}
}
impl<'a> ImageOp<'a> for OpTransform {
fn name(&self) -> &str {"transform"}
fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash) {
if self.orientation == Orientation::Normal || self.orientation == Orientation::Unknown {
pipeline.cache.alias(inid, outid);
} else {
let buf = pipeline.cache.get(&inid).unwrap();
let buf = rotate_buffer(&buf, &self.orientation);
pipeline.cache.put(outid, buf, 1);
}
}
}
fn rotate_buffer(buf: &OpBuffer, orientation: &Orientation) -> OpBuffer {
assert_eq!(buf.colors, 3);
if *orientation == Orientation::Normal || *orientation == Orientation::Unknown {
return buf.clone();
}
if buf.data.len() >= usize::MAX / 2 {
panic!("Buffer is too wide or high to rotate");
}
let mut width = buf.width as isize;
let mut height = buf.height as isize;
let (transpose, flip_x, flip_y) = orientation.to_flips();
let mut base_offset: isize = 0;
let mut x_step: isize = 3;
let mut y_step: isize = width * 3;
if flip_x {
x_step = -x_step;
base_offset += (width - 1) * 3;
}
if flip_y {
y_step = -y_step;
base_offset += width * (height - 1) * 3;
}
let mut out = if transpose {
mem::swap(&mut width, &mut height);
mem::swap(&mut x_step, &mut y_step);
OpBuffer::new(buf.height, buf.width, 3 as usize)
} else {
OpBuffer::new(buf.width, buf.height, 3 as usize)
};
out.mutate_lines(&(|line: &mut [f32], row| {
let line_offset = base_offset + y_step * row as isize;
for col in 0..width {
let offset = line_offset + x_step * col;
for c in 0..3 {
line[(col * 3 + c) as usize] = buf.data[(offset + c) as usize];
}
}
}));
out
}
#[cfg(test)]
mod tests {
use rawloader::decoders::Orientation;
use buffer::OpBuffer;
use super::rotate_buffer;
lazy_static! {
static ref F: OpBuffer = {
OpBuffer::from_rgb_str_vec(vec![
" ",
" RRRRRR ",
" GG ",
" BBBB ",
" GG ",
" GG ",
" ",
])
};
}
#[test]
fn rotate_unknown() {
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Unknown), F.clone());
}
#[test]
fn rotate_normal() {
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Normal), F.clone());
}
#[test]
fn rotate_flip_x() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" RRRRRR ",
" GG ",
" BBBB ",
" GG ",
" GG ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::HorizontalFlip), output);
}
#[test]
fn rotate_flip_y() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" GG ",
" GG ",
" BBBB ",
" GG ",
" RRRRRR ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::VerticalFlip), output);
}
#[test]
fn rotate_rotate90_cw() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" GGBGR ",
" GGBGR ",
" B R ",
" B R ",
" R ",
" R ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate90), output);
}
#[test]
fn rotate_rotate270_cw() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" R ",
" R ",
" R B ",
" R B ",
" RGBGG ",
" RGBGG ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate270), output);
}
#[test]
fn rotate_rotate180() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" GG ",
" GG ",
" BBBB ",
" GG ",
" RRRRRR ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate180), output);
}
#[test]
fn rotate_transpose() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" RGBGG ",
" RGBGG ",
" R B ",
" R B ",
" R ",
" R ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transpose), output);
}
#[test]
fn rotate_transverse() {
let output = OpBuffer::from_rgb_str_vec(vec![
" ",
" R ",
" R ",
" B R ",
" B R ",
" GGBGR ",
" GGBGR ",
" ",
]);
assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transverse), output);
}
}