skrifa/outline/glyf/
outline.rs

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! TrueType outline types.

use std::mem::size_of;

use super::super::{
    path::{to_path, ToPathError},
    pen::PathStyle,
    Hinting, OutlinePen,
};
use raw::tables::glyf::PointCoord;
use read_fonts::{
    tables::glyf::{Glyph, PointFlags},
    types::{F26Dot6, Fixed, GlyphId, Point},
};

/// Represents the information necessary to scale a glyph outline.
///
/// Contains a reference to the glyph data itself as well as metrics that
/// can be used to compute the memory requirements for scaling the glyph.
#[derive(Clone, Default)]
pub struct Outline<'a> {
    pub glyph_id: GlyphId,
    /// The associated top-level glyph for the outline.
    pub glyph: Option<Glyph<'a>>,
    /// Sum of the point counts of all simple glyphs in an outline.
    pub points: usize,
    /// Sum of the contour counts of all simple glyphs in an outline.
    pub contours: usize,
    /// Maximum number of points in a single simple glyph.
    pub max_simple_points: usize,
    /// "Other" points are the unscaled or original scaled points.
    ///
    /// The size of these buffer is the same and this value tracks the size
    /// for one (not both) of the buffers. This is the maximum of
    /// `max_simple_points` and the total number of points for all component
    /// glyphs in a single composite glyph.
    pub max_other_points: usize,
    /// Maximum size of the component delta stack.
    ///
    /// For composite glyphs in variable fonts, delta values are computed
    /// for each component. This tracks the maximum stack depth necessary
    /// to store those values during processing.
    pub max_component_delta_stack: usize,
    /// Number of entries in the hinting value stack.
    pub max_stack: usize,
    /// Number of CVT entries for copy-on-write support.
    pub cvt_count: usize,
    /// Number of storage area entries for copy-on-write support.
    pub storage_count: usize,
    /// Maximum number of points in the twilight zone for hinting.
    pub max_twilight_points: usize,
    /// True if any component of a glyph has bytecode instructions.
    pub has_hinting: bool,
    /// True if the glyph requires variation delta processing.
    pub has_variations: bool,
    /// True if the glyph contains any simple or compound overlap flags.
    pub has_overlaps: bool,
}

impl<'a> Outline<'a> {
    /// Returns the minimum size in bytes required to scale an outline based
    /// on the computed sizes.
    pub fn required_buffer_size(&self, hinting: Hinting) -> usize {
        let mut size = 0;
        let hinting = self.has_hinting && hinting == Hinting::Embedded;
        // Scaled, unscaled and (for hinting) original scaled points
        size += self.points * size_of::<Point<F26Dot6>>();
        // Unscaled and (if hinted) original scaled points
        size += self.max_other_points * size_of::<Point<i32>>() * if hinting { 2 } else { 1 };
        // Contour end points
        size += self.contours * size_of::<u16>();
        // Point flags
        size += self.points * size_of::<PointFlags>();
        if self.has_variations {
            // Interpolation buffer for delta IUP
            size += self.max_simple_points * size_of::<Point<Fixed>>();
            // Delta buffer for points
            size += self.max_simple_points * size_of::<Point<Fixed>>();
            // Delta buffer for composite components
            size += self.max_component_delta_stack * size_of::<Point<Fixed>>();
        }
        if hinting {
            // Hinting value stack
            size += self.max_stack * size_of::<i32>();
            // CVT and storage area copy-on-write buffers
            size += (self.cvt_count + self.storage_count) * size_of::<i32>();
            // Twilight zone storage. Two point buffers plus one point flags buffer
            size += self.max_twilight_points
                * (size_of::<Point<F26Dot6>>() * 2 + size_of::<PointFlags>());
        }
        if size != 0 {
            // If we're given a buffer that is not aligned, we'll need to
            // adjust, so add our maximum alignment requirement in bytes.
            size += std::mem::align_of::<i32>();
        }
        size
    }
}

#[derive(Debug)]
pub struct ScaledOutline<'a, C>
where
    C: PointCoord,
{
    pub points: &'a mut [Point<C>],
    pub flags: &'a mut [PointFlags],
    pub contours: &'a mut [u16],
    pub phantom_points: [Point<C>; 4],
    pub hdmx_width: Option<u8>,
}

impl<'a, C> ScaledOutline<'a, C>
where
    C: PointCoord,
{
    pub(crate) fn new(
        points: &'a mut [Point<C>],
        phantom_points: [Point<C>; 4],
        flags: &'a mut [PointFlags],
        contours: &'a mut [u16],
        hdmx_width: Option<u8>,
    ) -> Self {
        let x_shift = phantom_points[0].x;
        if x_shift != C::zeroed() {
            for point in points.iter_mut() {
                point.x = point.x - x_shift;
            }
        }
        Self {
            points,
            flags,
            contours,
            phantom_points,
            hdmx_width,
        }
    }

    pub fn adjusted_lsb(&self) -> C {
        self.phantom_points[0].x
    }

    pub fn adjusted_advance_width(&self) -> C {
        // Prefer widths from hdmx, otherwise take difference between first
        // two phantom points
        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttgload.c#L1996>
        if let Some(hdmx_width) = self.hdmx_width {
            C::from_i32(hdmx_width as i32)
        } else {
            self.phantom_points[1].x - self.phantom_points[0].x
        }
    }

    pub fn to_path(
        &self,
        path_style: PathStyle,
        pen: &mut impl OutlinePen,
    ) -> Result<(), ToPathError> {
        to_path(self.points, self.flags, self.contours, path_style, pen)
    }
}