about summary refs log tree commit diff stats
path: root/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
blob: e92def3c0a2b65a2e6fdf356cc7ba1ee3383ea9b (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
126
127
128
129
use std::{fs::File, io::Read};

use ab_glyph::{point, Font, FontVec, PxScale, ScaleFont};
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]);
    }
}