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
116
117
118
119
120
121
122
123
124
use crate::opbasics::*;

use std::cmp;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OpBaseCurve {
  pub points: Vec<(f32, f32)>,
}

impl OpBaseCurve {
  pub fn new(_img: &RawImage) -> OpBaseCurve {
    OpBaseCurve{
      points: vec![
        (0.00, 0.00),
        (0.50, 0.60), // Slopes the curve to go from the linear raw to a more natural look
        (1.00, 1.00),
      ],
    }
  }
}

impl<'a> ImageOp<'a> for OpBaseCurve {
  fn name(&self) -> &str {"basecurve"}
  fn run(&self, _pipeline: &PipelineGlobals, buf: Arc<OpBuffer>) -> Arc<OpBuffer> {
    let func = SplineFunc::new(self.points.clone());

    Arc::new(buf.mutate_lines_copying(&(|line: &mut [f32], _| {
      for pix in line.chunks_exact_mut(3) {
        pix[0] = func.interpolate(pix[0]);
        pix[1] = pix[1];
        pix[2] = pix[2];
      }
    })))
  }
}


struct SplineFunc {
  points: Vec<(f32,f32)>,
  c1s: Vec<f32>,
  c2s: Vec<f32>,
  c3s: Vec<f32>,
}

impl SplineFunc {
  // Monotone cubic interpolation code adapted from the Javascript example in Wikipedia
  fn new(points: Vec<(f32,f32)>) -> SplineFunc {
    if points.len() < 2 { panic!("Need at least 2 points for Spline"); }

    // Get consecutive differences and slopes
    let mut dxs = Vec::new();
    let mut dys = Vec::new();
    let mut slopes = Vec::new();
    for i in 0..(points.len()-1) {
      let dx = points[i+1].0 - points[i].0;
      let dy = points[i+1].1 - points[i].1;
      dxs.push(dx);
      dys.push(dy);
      slopes.push(dy/dx);
    }
    
    // Get degree-1 coefficients
    let mut c1s = vec![slopes[0]];
    for i in 0..(dxs.len()-1) {
      let m = slopes[i];
      let next = slopes[i+1];
      if m*next <= 0.0 {
        c1s.push(0.0);
      } else {
        let dx = dxs[i];
        let dxnext = dxs[i+1];
        let common = dx + dxnext;
        c1s.push(3.0*common/((common+dxnext)/m + (common + dx)/next));
      }
    }
    c1s.push(slopes[slopes.len()-1]);
  
    // Get degree-2 and degree-3 coefficients
    let mut c2s = Vec::new();
    let mut c3s = Vec::new();
    for i in 0..(c1s.len()-1) {
      let c1 = c1s[i];
      let slope = slopes[i];
      let invdx = 1.0 / dxs[i];
      let common = c1+c1s[i+1] - slope - slope;
      c2s.push((slope-c1-common)*invdx);
      c3s.push(common*invdx*invdx);
    }
  
    SplineFunc {
      points: points,
      c1s: c1s,
      c2s: c2s,
      c3s: c3s,
    }
  }

  fn interpolate(&self, val: f32) -> f32 {
    // Anything at or beyond the last value returns the last value
    let end = self.points[self.points.len()-1].0;
    if val >= end {
      return self.points[self.points.len()-1].1;
    }
    
    // Search for the interval x is in, returning the corresponding y if x is one of the original xs
    let mut low: isize = 0;
    let mut mid: isize;
    let mut high: isize = (self.c3s.len() - 1) as isize;

    while low <= high {
      mid = (low+high)/2;
      let xhere = self.points[mid as usize].0;
      if xhere < val { low = mid + 1; }
      else if xhere > val { high = mid - 1; }
      else { return self.points[mid as usize].1 }
    }
    let i = cmp::max(0, high) as usize;
    
    // Interpolate
    let diff = val - self.points[i].0;

    self.points[i].1 + self.c1s[i]*diff + self.c2s[i]*diff*diff + self.c3s[i]*diff*diff*diff
  }
}