ltrender 0.1.3

a diff based terminal rendering engine
Documentation
use ascii_assets::Color;
use common_stdx::Point;
use dyn_clone::DynClone;
use std::fmt::Debug;

use crate::terminal_buffer::drawable::BasicDraw;

pub trait Shader: Send + Sync + Debug + DynClone {
    fn apply(&self, draw: &mut BasicDraw, frame_size: (usize, usize), top_left: Point<i32>);
}

dyn_clone::clone_trait_object!(Shader);

#[derive(Debug, Clone, Copy)]
pub struct FlipHorizontal;

#[derive(Debug, Clone, Copy)]
pub struct FlipVertical;

impl Shader for FlipHorizontal {
    fn apply(&self, draw: &mut BasicDraw, frame_size: (usize, usize), top_left: Point<i32>) {
        let local_x = draw.pos.x - top_left.x;
        let flipped_x = (frame_size.0 - 1) as i32 - local_x;
        draw.pos.x = top_left.x + flipped_x;
    }
}

impl Shader for FlipVertical {
    fn apply(&self, draw: &mut BasicDraw, frame_size: (usize, usize), top_left: Point<i32>) {
        let local_y = draw.pos.y - top_left.y;
        let flipped_y = (frame_size.1 - 1) as i32 - local_y;
        draw.pos.y = top_left.y + flipped_y;
    }
}

#[derive(Debug, Clone, Copy)]
pub struct FlipDiagonal;

impl Shader for FlipDiagonal {
    fn apply(&self, draw: &mut BasicDraw, frame_size: (usize, usize), top_left: Point<i32>) {
        let local_x = draw.pos.x - top_left.x;
        let local_y = draw.pos.y - top_left.y;
        if local_x < frame_size.0 as i32 && local_y < frame_size.1 as i32 {
            draw.pos.x = top_left.x + local_y;
            draw.pos.y = top_left.y + local_x;
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Checkerboard;

impl Shader for Checkerboard {
    fn apply(&self, draw: &mut BasicDraw, _frame_size: (usize, usize), _top_left: Point<i32>) {
        if (draw.pos.x + draw.pos.y) % 2 == 0 {
            draw.chr.bg_color = Some(Color::rgb(0, 0, 0));
        } else {
            draw.chr.bg_color = Some(Color::rgb(255, 255, 255));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct StripesHorizontal;

impl Shader for StripesHorizontal {
    fn apply(&self, draw: &mut BasicDraw, _frame_size: (usize, usize), _top_left: Point<i32>) {
        if draw.pos.y % 2 == 0 {
            draw.chr.bg_color = Some(Color::rgb(100, 100, 100));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct StripesVertical;

impl Shader for StripesVertical {
    fn apply(&self, draw: &mut BasicDraw, _frame_size: (usize, usize), _top_left: Point<i32>) {
        if draw.pos.x % 2 == 0 {
            draw.chr.bg_color = Some(Color::rgb(100, 100, 100));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ToUpperCase;

impl Shader for ToUpperCase {
    fn apply(&self, draw: &mut BasicDraw, _frame_size: (usize, usize), _top_left: Point<i32>) {
        draw.chr.chr = draw.chr.chr.to_ascii_uppercase();
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ToLowerCase;

impl Shader for ToLowerCase {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        draw.chr.chr = draw.chr.chr.to_ascii_lowercase();
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Brighten(pub u8);

impl Shader for Brighten {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        fn brighten_color(c: Color, amount: u8) -> Color {
            let (r, g, b) = c.rgb;
            Color::rgb(
                r.saturating_add(amount),
                g.saturating_add(amount),
                b.saturating_add(amount),
            )
        }
        if let Some(fg) = draw.chr.fg_color {
            draw.chr.fg_color = Some(brighten_color(fg, self.0));
        }
        if let Some(bg) = draw.chr.bg_color {
            draw.chr.bg_color = Some(brighten_color(bg, self.0));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Darken(pub u8);

impl Shader for Darken {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        fn darken_color(c: Color, amount: u8) -> Color {
            let (r, g, b) = c.rgb;
            Color::rgb(
                r.saturating_sub(amount),
                g.saturating_sub(amount),
                b.saturating_sub(amount),
            )
        }
        if let Some(fg) = draw.chr.fg_color {
            draw.chr.fg_color = Some(darken_color(fg, self.0));
        }
        if let Some(bg) = draw.chr.bg_color {
            draw.chr.bg_color = Some(darken_color(bg, self.0));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct InvertColors;

impl Shader for InvertColors {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        fn invert(c: Color) -> Color {
            let (r, g, b) = c.rgb;
            Color::rgb(255 - r, 255 - g, 255 - b)
        }
        if let Some(fg) = draw.chr.fg_color {
            draw.chr.fg_color = Some(invert(fg));
        }
        if let Some(bg) = draw.chr.bg_color {
            draw.chr.bg_color = Some(invert(bg));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Grayscale;

impl Shader for Grayscale {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        fn gray(c: Color) -> Color {
            let (r, g, b) = c.rgb;
            let avg = ((r as u16 + g as u16 + b as u16) / 3) as u8;
            Color::rgb(avg, avg, avg)
        }
        if let Some(fg) = draw.chr.fg_color {
            draw.chr.fg_color = Some(gray(fg));
        }
        if let Some(bg) = draw.chr.bg_color {
            draw.chr.bg_color = Some(gray(bg));
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct SwapColors;

impl Shader for SwapColors {
    fn apply(
        &self,
        draw: &mut BasicDraw,
        _frame_size: (usize, usize),
        _top_left: common_stdx::Point<i32>,
    ) {
        std::mem::swap(&mut draw.chr.fg_color, &mut draw.chr.bg_color);
    }
}