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
/*
 * Copyright (C) Simon Werner, 2019
 *
 * A Rust port of the original C++ code by Christian Briones, 2013.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

/// Colours required for a PNG file, includes the alpha channel.
#[derive(Clone)]
pub struct RGBAColour {
  r: u8,
  g: u8,
  b: u8,
  a: u8,
}

impl RGBAColour {
  pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
    Self { r, g, b, a }
  }

  pub fn to_vec(&self) -> Vec<u8> {
    vec![self.r, self.g, self.b, self.a]
  }
}

/// ColourGradient allows you to create custom colour gradients for each
/// PNG created.
pub struct ColourGradient {
  colours: Vec<RGBAColour>,
  min: f32,
  max: f32,
}

impl ColourGradient {
  pub fn new() -> Self {
    Self {
      colours: vec![],
      min: 0.0,
      max: 1.0,
    }
  }

  pub fn get_colour(&self, value: f32) -> RGBAColour {
    assert!(self.colours.len() > 1);

    if value >= self.max {
      return self.colours.last().unwrap().clone();
    }
    let mut ratio = value / self.max;
    let width = 1.0 / (self.colours.len() as f32 - 1.0);
    let mut i = 0;

    // Find the "bin"
    while ratio > width {
      ratio -= width;
      i += 1;
    }

    ratio *= (self.colours.len() - 1) as f32;

    assert!(0.0 <= ratio);
    assert!(ratio <= 1.0);
    assert!(i < self.colours.len());

    let first = self.colours[i].clone();
    let second = self.colours[i + 1].clone();

    RGBAColour {
      r: self.interpolate(first.r, second.r, ratio),
      g: self.interpolate(first.g, second.g, ratio),
      b: self.interpolate(first.b, second.b, ratio),
      a: 255,
    }
  }

  pub fn add_colour(&mut self, colour: RGBAColour) {
    self.colours.push(colour);
  }

  fn interpolate(&self, start: u8, finish: u8, ratio: f32) -> u8 {
    ((f32::from(finish) - f32::from(start)) * ratio + f32::from(start)).round() as u8
  }

  pub fn set_max(&mut self, max: f32) {
    self.max = max
  }

  pub fn set_min(&mut self, min: f32) {
    self.min = min
  }
}