makepad_draw/text/
glyph_outline.rs

1use {
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}