aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
blob: fa44f602465cbe632a2bd999dd1f1107bfc5c759 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::{fs::File, io::Read};

use ab_glyph::{Font, FontVec, PxScale, ScaleFont, point};
use anyhow::{Context, Result};
use font_kit::{
    family_name::FamilyName, handle::Handle, properties::Properties, source::SystemSource,
};

use crate::wayland::ansi::{Color, StyledString};

mod layout;

fn get_font(weight: f32) -> Result<impl Font> {
    let handle = SystemSource::new()
        .select_best_match(
            &[FamilyName::Monospace],
            Properties::new().weight(font_kit::properties::Weight(weight)),
        )
        .context("Failed to find a monospace font")?;

    match handle {
        Handle::Path { path, font_index } => {
            let data = {
                let mut buffer = vec![];

                let mut file = File::open(&path)?;
                file.read_to_end(&mut buffer)?;
                buffer
            };

            FontVec::try_from_vec_and_index(data, font_index).with_context(|| {
                format!(
                    "Failed to load font at '{}' with index {}",
                    path.display(),
                    font_index
                )
            })
        }
        Handle::Memory { .. } => unimplemented!(),
    }
}

pub(super) type ColorVec = (Vec<f32>, Vec<Option<Color>>);
pub(super) fn text(input: &StyledString) -> Result<(ColorVec, (u32, u32))> {
    let normal_font = get_font(400.0)?;
    let bold_font = get_font(600.0)?;

    let height: f32 = 15.0;
    let px_height = height.ceil() as usize;

    let scale = PxScale {
        x: height,
        y: height,
    };

    let scaled_font = normal_font.into_scaled(scale);
    let bold_scaled_font = bold_font.into_scaled(scale);

    let mut glyphs = Vec::new();
    layout::layout_paragraph(
        &scaled_font,
        &bold_scaled_font,
        point(0.0, 0.0),
        9999.0,
        input,
        &mut glyphs,
    );

    let px_width = glyphs
        .iter()
        .fold(0.0, |acc, (g, c)| {
            let next = g.position.x
                + if c.is_bold() {
                    bold_scaled_font.h_advance(g.id)
                } else {
                    scaled_font.h_advance(g.id)
                };

            if next > acc { next } else { acc }
        })
        .ceil() as usize;

    // Rasterise to a f32 alpha vec
    let mut pixel_data = vec![0.0; px_width * px_height];
    let mut color_data = vec![None; px_width * px_height];
    for (g, c) in glyphs {
        let maybe_glyph = if c.is_bold() {
            bold_scaled_font.outline_glyph(g)
        } else {
            scaled_font.outline_glyph(g)
        };

        if let Some(og) = maybe_glyph {
            let bounds = og.px_bounds();
            og.draw(|x, y, v| {
                let x = x as f32 + bounds.min.x;
                let y = y as f32 + bounds.min.y;
                let next_idx = x as usize + y as usize * px_width;

                assure_idx(&mut pixel_data, next_idx, 0.0);
                assure_idx(&mut color_data, next_idx, None);

                // save the coverage alpha
                pixel_data[next_idx] += v;
                color_data[next_idx] = c.color();
            });
        }
    }

    let len = pixel_data.len();
    Ok((
        (pixel_data, color_data),
        (px_width as u32, (len / px_width) as u32),
    ))
}

fn assure_idx<T: Copy + Clone>(pixel_data: &mut Vec<T>, next_idx: usize, fill: T) {
    let last = pixel_data.len() - 1;

    if next_idx > last {
        let needed = next_idx - last;

        pixel_data.extend(vec![fill; needed]);
    }
}