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
125
126
127
128
129
130
131
132
133
134
135
136
137
// mask.rs    A 2D image mask.
//
// Copyright (c) 2017-2018  Douglas P Lau
//
use std::fs::File;
use std::io;
use std::io::Write;
use std::ptr;
use png;
use png::HasParameters;
use path::FillRule;
use imgbuf::{accumulate_non_zero, accumulate_odd};

/// A Mask is an image with only an 8-bit alpha channel.
///
/// It can be obtained from a [Plotter](struct.Plotter.html) after plotting.
/// A [Raster](struct.Raster.html) can be composited with a Mask.
///
/// # Example
/// ```
/// use footile::{PathBuilder,Plotter};
/// let path = PathBuilder::new()
///                        .move_to(10.0, 10.0)
///                        .line_to(90.0, 90.0)
///                        .build();
/// let mut p = Plotter::new(100, 100);
/// let mask = p.stroke(&path);
/// ```
pub struct Mask {
    width  : u32,
    height : u32,
    pixels : Vec<u8>,
}

impl Mask {
    /// Create a new mask
    ///
    /// * `width` Width in pixels.
    /// * `height` Height in pixels.
    pub(crate) fn new(width: u32, height: u32) -> Mask {
        let len = (width * height) as usize;
        // Capacity must be 8-element multiple (for SIMD)
        let cap = ((len + 7) >> 3) << 3;
        let mut pixels = vec![0; cap];
        // Remove excess pixels
        for _ in 0..cap-len { pixels.pop(); };
        Mask { width, height, pixels }
    }
    /// Get mask width.
    pub(crate) fn width(&self) -> u32 {
        self.width
    }
    /// Get mask height.
    pub(crate) fn height(&self) -> u32 {
        self.height
    }
    /// Get pixel slice
    pub fn pixels(&self) -> &[u8] {
        &self.pixels
    }
    /// Clear the mask.
    pub(crate) fn clear(&mut self) {
        let len = self.pixels.len();
        self.fill(0, len, 0);
    }
    /// Fill a range of pixels with a single value
    fn fill(&mut self, x: usize, len: usize, v: u8) {
        assert!(x + len <= self.pixels.len());
        unsafe {
            let pix = self.pixels.as_mut_ptr().offset(x as isize);
            ptr::write_bytes(pix, v, len);
        }
    }
    /// Accumulate signed area to mask.
    pub(crate) fn scan_accumulate(&mut self, sgn_area: &mut [i16], row: u32,
        rule: FillRule)
    {
        assert!(self.width <= sgn_area.len() as u32);
        assert!(row < self.height);
        let dst = self.scan_line(row);
        match rule {
            FillRule::NonZero => accumulate_non_zero(dst, sgn_area),
            FillRule::EvenOdd => accumulate_odd(dst, sgn_area),
        }
    }
    /// Get one scan line (row)
    fn scan_line(&mut self, row: u32) -> &mut [u8] {
        let s = (row * self.width) as usize;
        let t = s + self.width as usize;
        &mut self.pixels[s..t]
    }
    /// Write the mask to a PGM (portable gray map) file.
    ///
    /// * `filename` Name of file to write.
    pub fn write_pgm(&self, filename: &str) -> io::Result<()> {
        let fl = File::create(filename)?;
        let mut bw = io::BufWriter::new(fl);
        let w = bw.get_mut();
        w.write_all(format!("P5\n{} {}\n255\n", self.width, self.height)
         .as_bytes())?;
        w.write_all(&self.pixels[..])?;
        w.flush()?;
        Ok(())
    }
    /// Write the mask to a PNG (portable network graphics) file.
    ///
    /// * `filename` Name of file to write.
    pub fn write_png(&self, filename: &str) -> io::Result<()> {
        let fl = File::create(filename)?;
        let ref mut bw = io::BufWriter::new(fl);
        let mut enc = png::Encoder::new(bw, self.width, self.height);
        enc.set(png::ColorType::Grayscale).set(png::BitDepth::Eight);
        let mut writer = enc.write_header()?;
        writer.write_image_data(&self.pixels[..])?;
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::Mask;
    #[test]
    fn test_mask() {
        let mut m = Mask::new(10, 10);
        m.clear();
        assert!(m.width == 10);
        assert!(m.height == 10);
        assert!(m.pixels.len() == 100);
        m.fill(40, 20, 255);
        assert!(m.pixels[0] == 0);
        assert!(m.pixels[39] == 0);
        assert!(m.pixels[40] == 255);
        assert!(m.pixels[59] == 255);
        assert!(m.pixels[60] == 0);
        assert!(m.pixels[99] == 0);
    }
}