immo 0.1.2

Processing of various images (e.g., drawing of square systems)
Documentation
use crate::error::{ErrorKind, ImageError, ImageType};

pub struct Png(Vec<u8>, u32, u32);

impl Png {
    pub fn new(width: u32, height: u32) -> Self {
        let mut data = vec![];
        for _i in 0..width * height {
            data.push(0);
            data.push(0);
            data.push(0);
            data.push(0);
        }

        Self {
            0: data,
            1: width,
            2: height,
        }
    }

    pub fn clear(&mut self, color: (u8, u8, u8, u8)) {
        let mut data = vec![];
        for _ in 0..self.1 * self.2 {
            data.push(color.0);
            data.push(color.1);
            data.push(color.2);
            data.push(color.3);
        }
        self.0 = data;
    }

    pub fn line(
        &mut self,
        from: (u32, u32),
        to: (u32, u32),
        thickness: u32,
        color: (u8, u8, u8, u8),
    ) -> Result<(), ImageError> {
        if from.1 == to.1 {
            // Horizontal
            self.fill_rectangle(from.0, from.1, from.0 + to.0 + 1, thickness, color)?;
        } else if from.0 == to.0 {
            // Vertical
            self.fill_rectangle(from.0, from.1, thickness, from.1 + to.1 + 1, color)?;
        } else if from.0 < to.0 {
            // Right lopsided
            let shift = to.0 - from.0;
            let mut shift_x_count = 0;
            let mut shift_y_count = 0;

            let height = (from.1 + to.1) / shift;

            for _ in 0..shift {
                self.fill_rectangle(
                    from.0 + shift_x_count,
                    from.1 + shift_y_count,
                    thickness,
                    height,
                    color,
                )?;
                shift_x_count += thickness;
                shift_y_count += height;
            }

            let actual_height = height * shift;
            if actual_height < from.1 + to.1 {
                self.fill_rectangle(
                    from.0 + shift_x_count - 1,
                    from.1 + shift_y_count,
                    thickness,
                    from.1 + to.1 - actual_height,
                    color,
                )?;
            }
        } else if to.0 < from.0 {
            // Left lopsided
        }

        Ok(())
    }

    pub fn point(&mut self, x: u32, y: u32, color: (u8, u8, u8, u8)) -> Result<(), ImageError> {
        let (r, g, b, a) = color;

        let index = ((x + y * self.1) * 4) as usize;
        self.0[index] = r;
        self.0[index + 1] = g;
        self.0[index + 2] = b;
        self.0[index + 3] = a;

        Ok(())
    }

    pub fn fill_rectangle(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        color: (u8, u8, u8, u8),
    ) -> Result<(), ImageError> {
        let x_start = x;
        let x_end = x + width;
        let y_start = y;
        let y_end = y + height;

        for x in 0..self.1 {
            for y in 0..self.2 {
                if x >= x_start && x <= x_end {
                    if y >= y_start && y <= y_end {
                        self.point(x, y, color)?;
                    }
                }
            }
        }

        Ok(())
    }

    pub fn draw_rectangle(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        thickness: u32,
        color: (u8, u8, u8, u8),
    ) -> Result<(), ImageError> {
        let thickness = if thickness != 0 {
            thickness - 1
        } else {
            return Err(ImageError::new_simple(ErrorKind::InvalidValue(
                ImageType::Png,
            )));
        };

        self.fill_rectangle(x, y, width, thickness, color)?;
        self.fill_rectangle(x, y, thickness, height, color)?;

        self.fill_rectangle(x, height + y - thickness, width, thickness, color)?;
        self.fill_rectangle(width + x - thickness, y, thickness, height, color)?;

        Ok(())
    }

    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }
}