mplusfonts-macros 0.3.4

Procedural macros re-exported in the mplusfonts crate
Documentation
mod complex;
mod inverse;
mod misc;
mod octant;
mod parts;
mod renders;
mod sextant;
mod shade;
mod tall;
mod wide;

use std::{array, iter, slice};

use swash::zeno::{Command, Fill, Mask, PathData, Point, Vector};

use crate::mplus::bitmap::units::Grid;

use super::{GlyphMetrics, Image, ImageCluster};

pub use complex::*;
pub use inverse::*;
pub use misc::*;
pub use octant::*;
pub use sextant::*;
pub use shade::*;
pub use tall::*;
pub use wide::*;

type Points = [[Point; 25]; 25];
type Map<'a> = fn(&'a Block) -> array::IntoIter<Command, 5>;

pub struct Block(pub Point, pub Point);
pub struct BlockList(pub Vec<Block>);

trait MapIndex {
    fn map_index<const MAX: usize>(index: usize) -> usize;
}

impl<const N: usize> MapIndex for [[Point; N]; N] {
    fn map_index<const MAX: usize>(index: usize) -> usize {
        let 1.. = N else {
            panic!("expected `N` greater than `0`, found `{N}`")
        };
        let Some(step) = (N - 1).checked_div(MAX) else {
            panic!("expected `MAX` greater than `0`, found `{MAX}`");
        };

        index * step
    }
}

impl PathData for &Block {
    type Commands = array::IntoIter<Command, 5>;

    fn commands(&self) -> Self::Commands {
        let Block(Point { x: x0, y: y0 }, Point { x: x1, y: y1 }) = self;
        let commands = [
            Command::MoveTo(Point::new(*x0, *y0)),
            Command::LineTo(Point::new(*x1, *y0)),
            Command::LineTo(Point::new(*x1, *y1)),
            Command::LineTo(Point::new(*x0, *y1)),
            Command::Close,
        ];

        commands.into_iter()
    }
}

impl<'a> PathData for &'a BlockList {
    type Commands =
        iter::FlatMap<slice::Iter<'a, Block>, <&'a Block as PathData>::Commands, Map<'a>>;

    fn commands(&self) -> Self::Commands {
        let BlockList(blocks) = self;
        let map: Map = |block| block.commands();

        blocks.iter().flat_map(map)
    }
}

impl<T: Into<Vec<Block>>> From<T> for BlockList {
    fn from(chains: T) -> Self {
        Self(chains.into())
    }
}

fn render_image(data: impl PathData, fill: Fill) -> Image {
    Mask::new(data).style(fill).render()
}

fn render_image_shaded<const MAX: u8>(data: impl PathData, fill: Fill) -> Image {
    let (mut data, placement) = Mask::new(data).style(fill).render();
    for value in data.iter_mut() {
        *value = scale_back::<MAX>(*value);
    }

    (data, placement)
}

const fn scale_back<const MAX: u8>(value: u8) -> u8 {
    let result = MAX as i32 * value as i32 + 255;

    (result >> 8) as u8
}

pub fn full_block(glyph_metrics: &GlyphMetrics, offset: Vector) -> ImageCluster {
    let Grid(points) = &glyph_metrics.block;

    Vec::from([renders::full_block(points, offset)])
}