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
//! Collection of interpolation types.
//!
//! Currently, 2 types of interpolation are available.
//! - NearestNeighbor
//! - Bilinear

use crate::buffer::Buffer;
use crate::pixel::Pixel;

pub trait Interpolation<P: Pixel, B: Buffer<P>> {
    fn interpolate(&self, buffer: &B, x: f64, y: f64) -> P;
}

/// Nearest neighbor interpolation.
#[derive(Clone)]
pub struct NearestNeighbor;

impl<P: Pixel, B: Buffer<P>> Interpolation<P, B> for NearestNeighbor {
    fn interpolate(&self, buffer: &B, x: f64, y: f64) -> P {
        let (width, height) = buffer.dimensions();
        buffer
            .get_pixel(
                (x.rem_euclid(width as f64).floor() as u32).min(width - 1),
                (y.rem_euclid(height as f64).floor() as u32).min(height - 1),
            )
            .clone()
    }
}

/// Bi-linear interpolation.
#[derive(Clone)]
pub struct Bilinear;

impl<P: Pixel, B: Buffer<P>> Interpolation<P, B> for Bilinear {
    fn interpolate(&self, buffer: &B, x: f64, y: f64) -> P {
        let (width, height) = buffer.dimensions();
        let mut x = x.rem_euclid(width as f64);
        let mut y = y.rem_euclid(height as f64);
        let x2 = (x as u32 + 1) % width;
        let y2 = (y as u32 + 1) % height;
        if x as u32 == width {
            x =  width as f64 - 0.01;
        }
        if y as u32 == height {
            y =  height as f64 - 0.01;
        }
        buffer
            .get_pixel(x as u32, y as u32)
            .lerp(buffer.get_pixel(x2, y as u32), x.fract())
            .lerp(
                &buffer
                    .get_pixel(x as u32, y2)
                    .lerp(buffer.get_pixel(x2, y2), x.fract()),
                y.fract(),
            )
    }
}