use std::cmp;
use error::RasterResult;
use Image;
use Color;
#[derive(Debug)]
pub enum InterpolationMode {
Bilinear,
Bicubic,
Nearest
}
pub fn resample(mut src: &mut Image, w: i32, h: i32, interpolation: InterpolationMode) -> RasterResult<()> {
match interpolation {
InterpolationMode::Bilinear => bilinear(src, w, h),
InterpolationMode::Bicubic => bilinear(src, w, h), InterpolationMode::Nearest => nearest(src, w, h)
}
}
pub fn nearest(mut src: &mut Image, w: i32, h: i32) -> RasterResult<()> {
let x_ratio: f64 = src.width as f64 / w as f64;
let y_ratio: f64 = src.height as f64 / h as f64;
let mut dest = Image::blank(w, h);
for y in 0..h {
for x in 0..w {
let px: i32 = ( x as f64 * x_ratio ).floor() as i32;
let py: i32 = ( y as f64 * y_ratio ).floor() as i32;
let pixel = try!(src.get_pixel(px, py));
try!(dest.set_pixel(x, y, pixel));
}
}
src.width = dest.width;
src.height = dest.height;
src.bytes = dest.bytes;
Ok(())
}
pub fn bilinear(mut src: &mut Image, w2: i32, h2: i32) -> RasterResult<()> {
bilinear_width(src, w2)
.and_then(|_| bilinear_height(src, h2))
}
fn bilinear_width(mut src: &mut Image, w2: i32) -> RasterResult<()> {
let w1 = src.width;
let h1 = src.height;
let x_ratio: f64 = w1 as f64 / w2 as f64;
let mut dest = Image::blank(w2, h1);
let offset_x = (w2 / w1 / 2) as i32;
let x_start = 0 - offset_x;
let x_end = w2 - offset_x;
for y in 0..h1 {
for x in x_start..x_end {
let src_x = {
let src_x = x as f64 * x_ratio;
if src_x < 0.0 {
0.0 } else {
src_x
}
};
let src_x_int = (src_x).floor() as i32;
let src_x_int2 = cmp::min(src_x_int + 1, w1-1);
let t_x = src_x - src_x_int as f64;
let src_color1 = try!(src.get_pixel(src_x_int, y));
let src_color2 = try!(src.get_pixel(src_x_int2, y));
let red = _lerp(src_color1.r, src_color2.r, t_x);
let green = _lerp(src_color1.g, src_color2.g, t_x);
let blue = _lerp(src_color1.b, src_color2.b, t_x);
let alpha = _lerp(src_color1.a, src_color2.a, t_x);
try!(dest.set_pixel(x+offset_x, y, Color::rgba(red, green, blue, alpha)));
}
}
src.width = dest.width;
src.height = dest.height;
src.bytes = dest.bytes;
Ok(())
}
fn bilinear_height(mut src: &mut Image, h2: i32) -> RasterResult<()> {
let w1 = src.width;
let h1 = src.height;
let y_ratio: f64 = h1 as f64 / h2 as f64;
let mut dest = Image::blank(w1, h2);
let offset_y = (h2 / h1 / 2) as i32;
let y_start = 0 - offset_y;
let y_end = h2 - offset_y;
for x in 0..w1 {
for y in y_start..y_end {
let src_y = {
let src_y = y as f64 * y_ratio;
if src_y < 0.0 {
0.0 } else {
src_y
}
};
let src_y_int = (src_y).floor() as i32;
let src_y_int2 = cmp::min(src_y_int + 1, h1-1);
let t_y = src_y - src_y_int as f64;
let src_color1 = try!(src.get_pixel(x, src_y_int));
let src_color2 = try!(src.get_pixel(x, src_y_int2));
let red = _lerp(src_color1.r, src_color2.r, t_y);
let green = _lerp(src_color1.g, src_color2.g, t_y);
let blue = _lerp(src_color1.b, src_color2.b, t_y);
let alpha = _lerp(src_color1.a, src_color2.a, t_y);
try!(dest.set_pixel(x, y+offset_y, Color::rgba(red, green, blue, alpha)));
}
}
src.width = dest.width;
src.height = dest.height;
src.bytes = dest.bytes;
Ok(())
}
fn _lerp(a:u8, b:u8, t:f64) -> u8{
let a = a as f64;
let b = b as f64;
(a + (t * (b - a))) as u8
}
fn _bilinear(a: u8, b: u8, c: u8, d: u8, x_diff: f64, y_diff: f64) -> u8 {
(
a as f64 * (1.0 - x_diff) * (1.0 - y_diff) + b as f64 * (x_diff) * (1.0 - y_diff) +
c as f64 * (y_diff) * (1.0 - x_diff) + d as f64 * (x_diff * y_diff)
) as u8
}