1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::{Color, Filter, Image, Type};
use euclid;

pub type Point<T> = euclid::Point2D<T>;
pub struct Transform(pub euclid::Transform2D<f64>);

impl Filter for Transform {
    fn compute_at<T: Type, C: Color, I: Image<T, C>>(
        &self,
        x: usize,
        y: usize,
        c: usize,
        input: &[&I],
    ) -> f64 {
        let pt = Point::new(x as f64, y as f64);
        let dest = self.0.transform_point(&pt);
        (input[0].get_f(dest.x.floor() as usize, dest.y.floor() as usize, c)
            + input[0].get_f(dest.x.ceil() as usize, dest.y.ceil() as usize, c))
            / 2.
    }
}

#[inline]
pub fn rotate<T: Type, C: Color, I: Image<T, C>>(
    dest: &mut I,
    src: &I,
    deg: f64,
    center: Point<f64>,
) {
    let filter = Transform(
        euclid::Transform2D::create_rotation(euclid::Angle::degrees(deg))
            .pre_translate(euclid::TypedVector2D::new(-center.x, -center.y))
            .post_translate(euclid::TypedVector2D::new(center.x, center.y)),
    );

    filter.eval(dest, &[src])
}

#[inline]
pub fn scale<T: Type, C: Color, I: Image<T, C>>(dest: &mut I, src: &I, x: f64, y: f64) {
    let filter = Transform(euclid::Transform2D::create_scale(1.0 / x, 1.0 / y));

    filter.eval(dest, &[src])
}

#[inline]
pub fn resize<T: Type, C: Color, I: Image<T, C>>(dest: &mut I, src: &I, x: usize, y: usize) {
    let filter = Transform(euclid::Transform2D::create_scale(
        src.width() as f64 / x as f64,
        src.height() as f64 / y as f64,
    ));

    filter.eval(dest, &[src])
}

pub fn rotate90<T: Type, C: Color, I: Image<T, C>>(dest: &mut I, src: &I) {
    let dwidth = dest.width() as f64;
    let height = src.height() as f64;
    rotate(dest, src, 90., Point::new(dwidth / 2., height / 2.));
}

pub fn rotate180<T: Type, C: Color, I: Image<T, C>>(dest: &mut I, src: &I) {
    let dwidth = src.width() as f64;
    let height = src.height() as f64;
    rotate(dest, src, 180., Point::new(dwidth / 2., height / 2.));
}

pub fn rotate270<T: Type, C: Color, I: Image<T, C>>(dest: &mut I, src: &I) {
    let width = src.height() as f64;
    let dheight = dest.width() as f64;
    rotate(dest, src, 270., Point::new(width / 2., dheight / 2.));
}

#[cfg(test)]
mod test {
    use crate::{
        io::magick,
        transform::{resize, rotate180, rotate90, scale},
        Image, ImageBuf, Rgb,
    };

    #[test]
    fn test_rotate90() {
        let a: ImageBuf<u8, Rgb> = magick::read("test/test.jpg").unwrap();
        let mut dest = ImageBuf::new(a.height(), a.width());
        rotate90(&mut dest, &a);
        magick::write("test/test-rotate90.jpg", &dest).unwrap();
    }

    #[test]
    fn test_rotate180() {
        let a: ImageBuf<u8, Rgb> = magick::read("test/test.jpg").unwrap();
        let mut dest = ImageBuf::new(a.width(), a.height());
        rotate180(&mut dest, &a);
        magick::write("test/test-rotate180.jpg", &dest).unwrap();
    }

    #[test]
    fn test_scale() {
        let a: ImageBuf<u8, Rgb> = magick::read("test/test.jpg").unwrap();
        let mut dest = ImageBuf::new(a.width() * 2, a.height() * 2);
        scale(&mut dest, &a, 2., 2.);
        magick::write("test/test-scale.jpg", &dest).unwrap();
    }

    #[test]
    fn test_scale_resize() {
        let a: ImageBuf<u8, Rgb> = magick::read("test/test.jpg").unwrap();
        let mut dest0 = ImageBuf::new(a.width() * 2, a.height() * 2);
        let mut dest1 = ImageBuf::new(a.width() * 2, a.height() * 2);
        scale(&mut dest0, &a, 2., 2.);
        resize(&mut dest1, &a, a.width() * 2, a.height() * 2);
        assert_eq!(dest0, dest1);
    }
}