rocketsplash-rt 0.2.2

Runtime library for loading and rendering Rocketsplash assets (.rst, .rsf)
Documentation
// <FILE>crates/rocketsplash-rt/src/font/cls_font.rs</FILE>
// <DESC>Font runtime representation and API</DESC>
// <VERS>VERSION: 1.0.1</VERS>
// <WCTX>Runtime library implementation</WCTX>
// <CLOG>Refine has_style to match available variant semantics</CLOG>

use std::collections::BTreeMap;
use std::path::Path;

use rocketsplash_formats::{FontMeta, RenderMode, StyleFlags};

use crate::font::load_font_from_bytes;
use crate::{Error, TextBuilder, TextStyle};

#[derive(Clone, Debug)]
pub struct Font {
    pub meta: FontMeta,
    pub line_height: u32,
    pub mode: RenderMode,
    pub available_styles: StyleFlags,
    pub(crate) glyphs: BTreeMap<char, RuntimeGlyphVariants>,
}

impl Font {
    pub fn load(path: impl AsRef<Path>) -> Result<Self, Error> {
        let bytes = std::fs::read(path)?;
        load_font_from_bytes(&bytes)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
        load_font_from_bytes(bytes)
    }

    pub fn render<'a>(&'a self, text: &str) -> TextBuilder<'a> {
        TextBuilder::new(self, text)
    }

    pub fn available_chars(&self) -> impl Iterator<Item = char> + '_ {
        self.glyphs.keys().copied()
    }

    pub fn has_glyph(&self, ch: char) -> bool {
        self.glyphs.contains_key(&ch)
    }

    pub fn has_style(&self, style: TextStyle) -> bool {
        let bold = style.contains(TextStyle::BOLD);
        let italic = style.contains(TextStyle::ITALIC);
        if bold && italic && !self.available_styles.contains(StyleFlags::BOLD_ITALIC) {
            return false;
        }
        if bold && !italic && !self.available_styles.contains(StyleFlags::BOLD) {
            return false;
        }
        if italic && !bold && !self.available_styles.contains(StyleFlags::ITALIC) {
            return false;
        }
        if style.contains(TextStyle::REVERSE)
            && !self.available_styles.contains(StyleFlags::REVERSE)
        {
            return false;
        }
        true
    }

    pub fn mode(&self) -> RenderMode {
        self.mode
    }

    pub fn line_height(&self) -> usize {
        self.line_height as usize
    }
}

#[derive(Clone, Debug)]
pub(crate) struct RuntimeGlyph {
    pub width: usize,
    pub height: usize,
    pub chars: Vec<char>,
    pub opacity: Option<Vec<u8>>,
}

impl RuntimeGlyph {
    pub fn char_at(&self, x: usize, y: usize) -> Option<char> {
        if x >= self.width || y >= self.height {
            return None;
        }
        let idx = y * self.width + x;
        self.chars.get(idx).copied()
    }
}

#[derive(Clone, Debug)]
pub(crate) struct RuntimeGlyphVariants {
    pub base: RuntimeGlyph,
    pub bold: Option<RuntimeGlyph>,
    pub italic: Option<RuntimeGlyph>,
    pub bold_italic: Option<RuntimeGlyph>,
    pub reverse: Option<RuntimeGlyph>,
}

impl RuntimeGlyphVariants {
    pub fn select(&self, style: TextStyle) -> &RuntimeGlyph {
        if style.contains(TextStyle::REVERSE) {
            if let Some(glyph) = &self.reverse {
                return glyph;
            }
        }
        let bold = style.contains(TextStyle::BOLD);
        let italic = style.contains(TextStyle::ITALIC);
        if bold && italic {
            if let Some(glyph) = &self.bold_italic {
                return glyph;
            }
        }
        if bold {
            if let Some(glyph) = &self.bold {
                return glyph;
            }
        }
        if italic {
            if let Some(glyph) = &self.italic {
                return glyph;
            }
        }
        &self.base
    }
}

// <FILE>crates/rocketsplash-rt/src/font/cls_font.rs</FILE>
// <VERS>END OF VERSION: 1.0.1</VERS>