makepad_draw/text/
glyph_outline.rs1use {
2 super::{
3 geom::{Point, Rect, Size, Transform},
4 image::{SubimageMut, R},
5 num::Zero,
6 },
7 makepad_rustybuzz as rustybuzz,
8 rustybuzz::ttf_parser,
9};
10
11#[derive(Clone, Debug)]
12pub struct GlyphOutline {
13 bounds: Rect<f32>,
14 units_per_em: f32,
15 commands: Vec<Command>,
16}
17
18impl GlyphOutline {
19 pub fn origin_in_ems(&self) -> Point<f32> {
20 self.bounds.origin / self.units_per_em
21 }
22
23 pub fn size_in_ems(&self) -> Size<f32> {
24 self.bounds.size / self.units_per_em
25 }
26
27 pub fn bounds_in_ems(&self) -> Rect<f32> {
28 Rect::new(self.origin_in_ems(), self.size_in_ems())
29 }
30
31 pub fn rasterize(&self, dpxs_per_em: f32, output: &mut SubimageMut<R>) {
32 use ab_glyph_rasterizer::Rasterizer;
33
34 fn to_ab_glyph(p: Point<f32>) -> ab_glyph_rasterizer::Point {
35 ab_glyph_rasterizer::point(p.x, p.y)
36 }
37
38 let output_size = output.bounds().size;
39 let mut rasterizer = Rasterizer::new(output_size.width, output_size.height);
40 let origin = self.bounds.origin;
41 let transform = Transform::from_translate(-origin.x, -origin.y)
42 .scale_uniform(dpxs_per_em / self.units_per_em);
43 let mut last = Point::ZERO;
44 let mut last_move = None;
45 for command in self.commands.iter().copied() {
46 match command {
47 Command::MoveTo(p) => {
48 last = p;
49 last_move = Some(p);
50 }
51 Command::LineTo(p) => {
52 rasterizer.draw_line(
53 to_ab_glyph(last.apply_transform(transform)),
54 to_ab_glyph(p.apply_transform(transform)),
55 );
56 last = p;
57 }
58 Command::QuadTo(p1, p) => {
59 rasterizer.draw_quad(
60 to_ab_glyph(last.apply_transform(transform)),
61 to_ab_glyph(p1.apply_transform(transform)),
62 to_ab_glyph(p.apply_transform(transform)),
63 );
64 last = p;
65 }
66 Command::CurveTo(p1, p2, p) => {
67 rasterizer.draw_cubic(
68 to_ab_glyph(last.apply_transform(transform)),
69 to_ab_glyph(p1.apply_transform(transform)),
70 to_ab_glyph(p2.apply_transform(transform)),
71 to_ab_glyph(p.apply_transform(transform)),
72 );
73 last = p;
74 }
75 Command::Close => {
76 if let Some(last_move) = last_move.take() {
77 rasterizer.draw_line(
78 to_ab_glyph(last.apply_transform(transform)),
79 to_ab_glyph(last_move.apply_transform(transform)),
80 );
81 last = last_move;
82 }
83 }
84 }
85 }
86 rasterizer.for_each_pixel_2d(|x, y, a| {
87 let point = Point::new(x as usize, output_size.height - 1 - y as usize);
88 let pixel = R::new((a * 255.0) as u8);
89 output[point] = pixel;
90 });
91 }
92}
93
94#[derive(Clone, Copy, Debug)]
95pub enum Command {
96 MoveTo(Point<f32>),
97 LineTo(Point<f32>),
98 QuadTo(Point<f32>, Point<f32>),
99 CurveTo(Point<f32>, Point<f32>, Point<f32>),
100 Close,
101}
102
103#[derive(Debug)]
104pub struct Builder {
105 commands: Vec<Command>,
106}
107
108impl Builder {
109 pub fn new() -> Self {
110 Self {
111 commands: Vec::new(),
112 }
113 }
114
115 pub fn finish(self, bounds: Rect<f32>, units_per_em: f32) -> GlyphOutline {
116 GlyphOutline {
117 bounds,
118 units_per_em,
119 commands: self.commands,
120 }
121 }
122}
123
124impl ttf_parser::OutlineBuilder for Builder {
125 fn move_to(&mut self, x: f32, y: f32) {
126 self.commands.push(Command::MoveTo(Point::new(x, y)));
127 }
128
129 fn line_to(&mut self, x: f32, y: f32) {
130 self.commands.push(Command::LineTo(Point::new(x, y)));
131 }
132
133 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
134 self.commands
135 .push(Command::QuadTo(Point::new(x1, y1), Point::new(x, y)));
136 }
137
138 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
139 self.commands.push(Command::CurveTo(
140 Point::new(x1, y1),
141 Point::new(x2, y2),
142 Point::new(x, y),
143 ));
144 }
145
146 fn close(&mut self) {
147 self.commands.push(Command::Close);
148 }
149}