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
use super::SwRenderer;
use crate::render::font::{Font, GlyphKind};
use crate::types::{Color, Fixed, Point, Rect};
impl SwRenderer<'_> {
pub(super) fn draw_label_inner(
&mut self,
pos: &Point,
text: &[u8],
font: &Font,
clip: &Rect,
color: &Color,
opa: u8,
) {
let phys_pos = self.viewport.point_to_physical(*pos);
let phys_clip = self.viewport.rect_to_physical(*clip);
let scale = self.viewport.scale().to_int().max(1);
let phys_bounds = phys_clip.pixel_bounds();
let (mut cx, cy) = phys_pos.floor();
let metrics = font.metrics();
let char_h = metrics.line_height as i32;
// Target glyph height in physical pixels — lets a multi-table
// provider pick the closest fixed-size representation.
let requested_size = (font.size as i32 * scale).clamp(1, u16::MAX as i32) as u16;
// Decode the byte stream as UTF-8 so multi-byte CJK glyphs
// map to one codepoint each. Invalid bytes fall back to U+FFFD,
// which the font is free to skip.
let chars = core::str::from_utf8(text)
.map(|s| s.chars().collect::<alloc::vec::Vec<_>>())
.unwrap_or_else(|_| text.iter().map(|&b| b as char).collect());
for ch in chars {
let Some(g) = font.glyph(ch, requested_size) else {
continue;
};
let advance;
match &g.kind {
GlyphKind::Mono(bitmap) => {
advance = g.advance as i32 * scale;
self.blit_mono_glyph(bitmap, cx, cy, scale, char_h, phys_bounds, color, opa);
}
GlyphKind::Sdf {
atlas,
source_size,
bit_depth,
spread,
..
} => {
// Render at the Font's requested size, not the atlas
// size: one SDF atlas resamples to whatever font.size
// asks for, so a larger Font grows the glyph smoothly.
// Advance scales by the same resample ratio so the pen
// keeps pace; with font.size == source_size this is
// g.advance × scale, leaving the common path
// byte-identical.
advance =
g.advance as i32 * requested_size as i32 / (*source_size as i32).max(1);
self.blit_sdf_glyph(
atlas,
*source_size,
*bit_depth,
*spread,
cx,
cy,
requested_size,
phys_bounds,
color,
opa,
);
}
}
cx += advance;
}
}
#[allow(clippy::too_many_arguments)]
fn blit_mono_glyph(
&mut self,
bitmap: &[u8],
cx: i32,
cy: i32,
scale: i32,
char_h: i32,
phys_bounds: (i32, i32, i32, i32),
color: &Color,
opa: u8,
) {
let (clip_x, clip_y, clip_x2, clip_y2) = phys_bounds;
for row in 0..char_h.min(bitmap.len() as i32) {
let byte = bitmap[row as usize];
for col in 0..8 {
if byte & (0x80 >> col) == 0 {
continue;
}
for sy in 0..scale {
for sx in 0..scale {
let px = cx + col * scale + sx;
let py = cy + row * scale + sy;
if px >= clip_x && px < clip_x2 && py >= clip_y && py < clip_y2 {
self.target.blend_pixel(
Fixed::from_int(px),
Fixed::from_int(py),
color,
opa,
);
}
}
}
}
}
}
}