summaryrefslogtreecommitdiff
path: root/src/graphics/command.rs
blob: 54b67447e157875a5f0748c7fa45b07b5978f9c5 (plain)
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
use super::{
    InstanceData,
    font::{self, CharAtlas},
};
use glam::Vec2;
use wgpu::Queue;

pub enum HorizontalAlign {
    Left,
    Center,
    Right,
}

pub enum VerticalAlign {
    Top,
    Center,
    Bottom,
}

pub struct Text {
    pub text: String,
    pub origin: Vec2,
    pub scale: u32,
    pub h_align: HorizontalAlign,
    pub v_align: VerticalAlign,
}

pub enum RenderCommand {
    Text(Text),
}

impl RenderCommand {
    /// 描画コマンドからインスタンス情報に変換するメソッド
    ///
    /// * screen_width - 画面幅
    /// * screen_height - 画面高
    /// * scale_factor - 画面拡大率
    /// * queue - queue
    /// * char_atlas - 文字テクスチャアトラス
    /// * buf - インスタンスバッファ
    pub fn build(
        &self,
        screen_width: f32,
        screen_height: f32,
        scale_factor: f32,
        queue: &Queue,
        char_atlas: &mut CharAtlas,
        buf: &mut Vec<InstanceData>,
    ) {
        match self {
            RenderCommand::Text(text) => build_text(
                text,
                screen_width,
                screen_height,
                scale_factor,
                queue,
                char_atlas,
                buf,
            ),
        }
    }
}

fn build_text(
    text: &Text,
    screen_width: f32,
    screen_height: f32,
    scale_factor: f32,
    queue: &Queue,
    char_atlas: &mut CharAtlas,
    buf: &mut Vec<InstanceData>,
) {
    let scale = text.scale as f32;
    let line_height = font::line_height(scale);
    let mut baseline = text.origin.y + font::ascent(scale);

    for line in text.text.split('\n') {
        // NOTE: ピクセル境界に合わせるため、整数にする。
        //       最終的にRust側でNDCに変換するが、fragmentation時に整数に直る。
        let y = (baseline * scale_factor).round() / scale_factor;
        let mut ox = text.origin.x;
        let mut prev = None;

        for c in line.chars() {
            let ci = char_atlas.acquire(queue, c, scale);

            if let Some(prev) = prev {
                ox += font::kern(scale, prev, ci.glyph_id);
            }
            prev = Some(ci.glyph_id);

            if ci.alloc_id.is_none() {
                ox += font::h_advance(scale, ci.glyph_id);
                continue;
            }

            // NOTE: ピクセル境界に合わせるため、整数にする。
            //       最終的にRust側でNDCに変換するが、fragmentation時に整数に直る。
            let x = (ox * scale_factor).round() / scale_factor;
            let center_x = x + ci.x_offset + ci.width / 2.0;
            let center_y = y + ci.y_offset + ci.height / 2.0;
            buf.push(InstanceData {
                position: Vec2::new(
                    center_x / screen_width * 2.0 - 1.0,
                    1.0 - center_y / screen_height * 2.0,
                ),
                scale: Vec2::new(ci.width / screen_width, ci.height / screen_height),
                uv: ci.uv,
            });
            ox += font::h_advance(scale, ci.glyph_id);
        }

        baseline += line_height;
    }
}