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
use std::path::Path;
use std::io::Write;
use std::fs::File;

const RGB_WIDTH: usize = 3;

#[derive(Clone, Copy)]
pub struct RGB {
    pub r: u8,
    pub g: u8,
    pub b: u8,
    pub a: u8
}

pub struct Framebuffer {
    pub height: usize,
    pub width: usize,
    pub data: Vec<u8>,
}

impl Framebuffer {
    pub fn new(height: usize, width: usize) -> Framebuffer {
        let size = RGB_WIDTH * width * height;
        let buffer = vec![0; size as usize];
        Framebuffer{
            height: height,
            width: width,
            data: buffer,
        }
    }

    fn buffer_size(&self) -> usize {
        RGB_WIDTH * self.height * self.width
    }

    pub fn clear(&mut self, color: RGB) {
        let c: Vec<u8> = vec![
            vec![color.r, color.g, color.b];
            self.width * self.height
        ].into_iter().flatten().collect::<Vec<u8>>();
        self.data = c;
    }

    fn get_offset(&self, x: usize, y: usize) -> Option<usize> {
        let offset = (y * self.width * RGB_WIDTH) + (x * RGB_WIDTH);
        if offset < self.buffer_size() {
            Some(offset as usize)
        } else {
            None
        }
    }

    pub fn get_pixel(&self, x: usize, y: usize) -> Option<RGB> {
        match self.get_offset(x, y) {
            Some(offset) => {
                let r = self.data[offset];
                let g = self.data[offset + 1];
                let b = self.data[offset + 2];
                Some(RGB {r: r, g: g, b: b, a: 255})
            },
            None => None
        }
    }

    pub fn set_pixel(&mut self, x: usize, y: usize, color: RGB) -> bool {
        match self.get_offset(x, y) {
            Some(offset) => {
                self.data[offset] = color.r;
                self.data[offset + 1] = color.g;
                self.data[offset + 2] = color.b;
                true
            },
            None => false
        }
    }

    pub fn draw_rect(&mut self, x: usize, y: usize, w: usize, h: usize, color: RGB) -> bool {
        let mut success = true;
        while success {
            for i in 0..w {
                for j in 0..h {
                    let cx = x + i;
                    let cy = y + j;
                    if cx >= self.width || cy >= self.height {
                        continue
                    }
                    success = self.set_pixel(cx, cy, color);
                }
            }
            break
        }
        success
    }

    pub fn write_file(&self, filename: &str) -> std::io::Result<()> {
        let path = Path::new(filename);
        let mut file = File::create(&path)?;
        let header = format!("P6 {} {} 255\n", self.width, self.height);
        file.write(header.as_bytes())?;
        file.write(&self.data)?;
        Ok(())
    }
}