kitkat 1.1.1

The classic kit kat clock utility.
/*
 * kitkat
 *
 * Copyright 2021 - Manos Pitsidianakis
 *
 * This file is part of kitkat.
 *
 * kitkat 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 3 of the License, or
 * (at your option) any later version.
 *
 * kitkat 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 kitkat. If not, see <http://www.gnu.org/licenses/>.
 */

use super::*;
use std::collections::VecDeque;
pub struct Image {
    pub bytes: Vec<u32>,
    pub width: usize,
    pub height: usize,
    pub x_offset: usize,
    pub y_offset: usize,
}

impl From<Bitmap<'_>> for Image {
    fn from(val: Bitmap<'_>) -> Image {
        Image {
            bytes: bits_to_bytes(val.bits, val.width),
            width: val.width,
            height: val.height,
            x_offset: val.x_offset,
            y_offset: val.y_offset,
        }
    }
}

impl Image {
    pub fn new(width: usize, height: usize, x_offset: usize, y_offset: usize) -> Self {
        Image {
            bytes: vec![WHITE; width * height],
            width,
            height,
            x_offset,
            y_offset,
        }
    }
    pub fn draw(&self, buffer: &mut Vec<u32>, fg: u32, bg: Option<u32>) {
        for y in 0..self.height {
            for x in 0..self.width {
                if self.bytes[y * self.width + x] == BLACK {
                    buffer[(self.y_offset + y) * CAT_WIDTH + self.x_offset + x] = fg;
                } else if let Some(bg) = bg {
                    buffer[(self.y_offset + y) * CAT_WIDTH + self.x_offset + x] = bg;
                }
            }
        }
    }

    pub fn draw_outline(&mut self) {
        for i in 0..(self.height as _) {
            self.plot(0, i);
            self.plot(self.width as i64 - 1, i);
        }
        for i in 0..(self.width as _) {
            self.plot(i, self.height as i64 - 1);
            self.plot(i, 0);
        }
    }

    pub fn clear(&mut self) {
        for i in self.bytes.iter_mut() {
            *i = WHITE;
        }
    }

    pub fn copy(&mut self, other: &Image, x_offset: usize, y_offset: usize) {
        for y in y_offset..self.height {
            for x in x_offset..self.width {
                if let Some(p) = other.get((x - x_offset) as i64, (y - y_offset) as i64) {
                    if p == BLACK {
                        self.plot(x as i64, y as i64);
                    }
                }
            }
        }
    }
    pub fn plot(&mut self, x: i64, y: i64) {
        //std::dbg!((x, y));
        //std::dbg!(self.bytes.len());
        //std::dbg!(self.width);
        //std::dbg!(self.height);
        //std::dbg!(self.width * self.height);
        if x < 0 || y < 0 || y >= (self.height as i64) || x >= (self.width as i64) {
            if cfg!(debug_assertions) {
                eprintln!("invalid plot() coors: ({}, {})", x, y);
            }
            return;
        }
        let (x, y): (usize, usize) = (x as _, y as _);
        self.bytes[y * self.width + x] = BLACK;
    }

    pub fn get(&self, x: i64, y: i64) -> Option<u32> {
        //std::dbg!((x, y));
        //std::dbg!(self.bytes.len());
        //std::dbg!(self.width);
        //std::dbg!(self.height);
        //std::dbg!(self.width * self.height);
        if x < 0 || y < 0 || y >= (self.height as i64) || x >= (self.width as i64) {
            return None;
        }
        let (x, y): (usize, usize) = (x as _, y as _);
        Some(self.bytes[y * self.width + x])
    }

    pub fn plot_ellipse(
        &mut self,
        (xm, ym): (i64, i64),
        (a, b): (i64, i64),
        quadrants: [bool; 4],
        _wd: f64,
    ) {
        let mut x = -a;
        let mut y = 0;
        let mut e2 = b;
        let mut dx = (1 + 2 * x) * e2 * e2;
        let mut dy = x * x;
        let mut err = dx + dy;
        loop {
            if quadrants[0] {
                self.plot(xm - x, ym + y); /*   I. Quadrant */
            }
            if quadrants[1] {
                self.plot(xm + x, ym + y); /*  II. Quadrant */
            }
            if quadrants[2] {
                self.plot(xm + x, ym - y); /* III. Quadrant */
            }
            if quadrants[3] {
                self.plot(xm - x, ym - y); /*  IV. Quadrant */
            }
            e2 = 2 * err;
            if e2 >= dx {
                x += 1;
                dx += 2 * b * b;
                err += dx;
                //err += dx += 2*(long)b*b; }    /* x step */
            }
            if e2 <= dy {
                y += 1;
                dy += 2 * a * a;
                err += dy;
                //err += dy += 2*(long)a*a; }    /* y step */
            }
            if x > 0 {
                break;
            }
        }
        while y < b {
            /* to early stop for flat ellipses with a=1, */
            y += 1;
            self.plot(xm, ym + y); /* -> finish tip of ellipse */
            self.plot(xm, ym - y);
        }
    }

    pub fn plot_line_width(&mut self, (mut x0, mut y0): (i64, i64), (x1, y1): (i64, i64), wd: f64) {
        //eprintln!(
        //    "plot_line_width: ({}, {}), ({}, {}) width = {}",
        //    x0, y0, x1, y1, wd
        //);
        /* Bresenham's line algorithm */
        let dx = (x1 - x0).abs();
        let sx = if x0 < x1 { 1 } else { -1 };
        let dy = (y1 - y0).abs();
        let sy = if y0 < y1 { 1 } else { -1 };
        let mut err = dx - dy;
        /* error value e_xy */
        let mut e2: i64;
        let mut x2: i64;
        let mut y2: i64;
        let ed: f64 = if (dx + dy) == 0 {
            1.0
        } else {
            f64::sqrt((dx * dx) as f64 + (dy * dy) as f64)
        };
        let mut points = vec![];
        let wd = (wd + 1.0) / 2.0;
        //eprintln!("wd = {}, ed = {}", wd, ed);
        loop {
            points.push((x0, y0));
            self.plot(x0, y0);
            e2 = err;
            x2 = x0;
            if 2 * e2 >= -dx {
                /* x step */
                //eprintln!(" x step ");
                e2 += dy;
                y2 = y0;
                while e2 < ((ed as f64 * wd) as i64) && (y1 != y2 || dx > dy) {
                    y2 += sy;
                    self.plot(x0, y2);
                    points.push((x0, y2));
                    e2 += dx;
                }
                if x0 == x1 {
                    break;
                };
                e2 = err;
                err -= dy;
                x0 += sx;
            }
            if 2 * e2 <= dy {
                /* y step */
                //eprintln!(" y step ");
                e2 = dx - e2;
                while e2 < ((ed as f64 * wd) as i64) && (x1 != x2 || dx < dy) {
                    x2 += sx;
                    self.plot(x2, y0);
                    points.push((x2, y0));
                    e2 += dy;
                }
                if y0 == y1 {
                    break;
                };
                err += dx;
                y0 += sy;
            }
        }
    }

    pub fn flood_fill(&mut self, x: i64, y: i64) {
        if self.get(x, y) != Some(WHITE) {
            return;
        }

        let w = self.width as i64;
        let h = self.height as i64;
        let mut span_above: bool;
        let mut span_below: bool;

        let mut s = VecDeque::new();
        s.push_back((x, y));

        while let Some((x, y)) = s.pop_back() {
            let mut x1 = x;
            while x1 >= 0 && self.get(x1, y).map(|some| some == WHITE).unwrap_or(false) {
                x1 -= 1;
            }
            x1 += 1;
            span_above = false;
            span_below = false;
            while x1 < w && self.get(x1, y).map(|some| some == WHITE).unwrap_or(false) {
                self.plot(x1, y);
                if !span_above
                    && y > 0
                    && self
                        .get(x1, y - 1)
                        .map(|some| some == WHITE)
                        .unwrap_or(false)
                {
                    s.push_back((x1, y - 1));
                    span_above = true;
                } else if span_above
                    && y > 0
                    && self
                        .get(x1, y - 1)
                        .map(|some| some != WHITE)
                        .unwrap_or(false)
                {
                    span_above = false;
                }
                if !span_below
                    && y < h - 1
                    && self
                        .get(x1, y + 1)
                        .map(|some| some == WHITE)
                        .unwrap_or(false)
                {
                    s.push_back((x1, y + 1));
                    span_below = true;
                } else if span_below
                    && y < h - 1
                    && self
                        .get(x1, y + 1)
                        .map(|some| some != WHITE)
                        .unwrap_or(false)
                {
                    span_below = false;
                }
                x1 += 1;
            }
        }
    }
}