skrifa/outline/glyf/hint/
graphics.rs

1//! Graphics state for the TrueType interpreter.
2
3use super::{
4    round::RoundState,
5    zone::{Zone, ZonePointer},
6    F26Dot6, Point, Target,
7};
8use core::ops::{Deref, DerefMut};
9
10/// Describes the axis to which a measurement or point movement operation
11/// applies.
12#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
13pub enum CoordAxis {
14    #[default]
15    Both,
16    X,
17    Y,
18}
19
20/// Context in which instructions are executed.
21///
22/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html>
23#[derive(Debug)]
24pub struct GraphicsState<'a> {
25    /// Fields of the graphics state that persist between calls to the interpreter.
26    pub retained: RetainedGraphicsState,
27    /// A unit vector whose direction establishes an axis along which
28    /// distances are measured.
29    ///
30    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#projection%20vector>
31    pub proj_vector: Point<i32>,
32    /// Current axis for the projection vector.
33    pub proj_axis: CoordAxis,
34    /// A second projection vector set to a line defined by the original
35    /// outline location of two points. The dual projection vector is used
36    /// when it is necessary to measure distances from the scaled outline
37    /// before any instructions were executed.
38    ///
39    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#dual%20projection%20vector>
40    pub dual_proj_vector: Point<i32>,
41    /// Current axis for the dual projection vector.
42    pub dual_proj_axis: CoordAxis,
43    /// A unit vector that establishes an axis along which points can move.
44    ///
45    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#freedom%20vector>
46    pub freedom_vector: Point<i32>,
47    /// Current axis for point movement.
48    pub freedom_axis: CoordAxis,
49    /// Dot product of freedom and projection vectors.
50    pub fdotp: i32,
51    /// Determines the manner in which values are rounded.
52    ///
53    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#round%20state>
54    pub round_state: RoundState,
55    /// First reference point.
56    ///
57    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp0>
58    pub rp0: usize,
59    /// Second reference point.
60    ///
61    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp1>    
62    pub rp1: usize,
63    /// Third reference point.
64    ///
65    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp1>     
66    pub rp2: usize,
67    /// Makes it possible to repeat certain instructions a designated number of
68    /// times. The default value of one assures that unless the value of loop
69    /// is altered, these instructions will execute one time.
70    ///
71    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#loop>
72    pub loop_counter: u32,
73    /// First zone pointer.
74    ///
75    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp0>
76    pub zp0: ZonePointer,
77    /// Second zone pointer.
78    ///
79    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp1>
80    pub zp1: ZonePointer,
81    /// Third zone pointer.
82    ///
83    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp2>
84    pub zp2: ZonePointer,
85    /// Outline data for each zone.
86    ///
87    /// This array contains the twilight and glyph zones, in that order.
88    pub zones: [Zone<'a>; 2],
89    /// True if the current glyph is a composite.
90    pub is_composite: bool,
91    /// If true, enables a set of backward compatibility heuristics that
92    /// prevent certain modifications to the outline. The purpose is to
93    /// support "modern" vertical only hinting that attempts to preserve
94    /// outline shape and metrics in the horizontal direction. This is
95    /// enabled by default, but fonts (and specific glyphs) can opt out
96    /// of this behavior using the INSTCTRL instruction. In practice,
97    /// opting out is usually only done by "ClearType native" fonts.
98    ///
99    /// See <https://learn.microsoft.com/en-us/typography/cleartype/truetypecleartype>
100    /// for more background and some gory details.
101    ///
102    /// Defaults to true.
103    ///
104    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.h#L344>
105    pub backward_compatibility: bool,
106    /// If true, enables more strict error checking.
107    ///
108    /// Defaults to false.
109    ///
110    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.h#L195>
111    pub is_pedantic: bool,
112    /// Set to true when IUP has been executed in the horizontal direction.
113    pub did_iup_x: bool,
114    /// Set to true when IUP has been executed in the vertical direction.
115    pub did_iup_y: bool,
116}
117
118impl GraphicsState<'_> {
119    /// Returns the factor for scaling unscaled points to pixels.
120    ///
121    /// For composite glyphs, "unscaled" points are already scaled so we
122    /// return the identity.
123    pub fn unscaled_to_pixels(&self) -> i32 {
124        if self.is_composite {
125            1 << 16
126        } else {
127            self.scale
128        }
129    }
130
131    /// Resets the non-retained portions of the graphics state.
132    pub fn reset(&mut self) {
133        let GraphicsState {
134            retained,
135            zones,
136            is_composite,
137            ..
138        } = core::mem::take(self);
139        *self = GraphicsState {
140            retained,
141            zones,
142            is_composite,
143            ..Default::default()
144        };
145        self.update_projection_state();
146    }
147
148    /// Resets the retained portion of the graphics state to default
149    /// values while saving the user instance settings.
150    pub fn reset_retained(&mut self) {
151        let scale = self.scale;
152        let ppem = self.ppem;
153        let mode = self.target;
154        self.retained = RetainedGraphicsState {
155            scale,
156            ppem,
157            target: mode,
158            ..Default::default()
159        }
160    }
161}
162
163impl Default for GraphicsState<'_> {
164    fn default() -> Self {
165        // For table of default values, see <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_graphics_state>
166        // All vectors are set to the x-axis (normalized in 2.14)
167        let vector = Point::new(0x4000, 0);
168        Self {
169            retained: RetainedGraphicsState::default(),
170            proj_vector: vector,
171            proj_axis: CoordAxis::Both,
172            dual_proj_vector: vector,
173            dual_proj_axis: CoordAxis::Both,
174            freedom_vector: vector,
175            freedom_axis: CoordAxis::Both,
176            fdotp: 0x4000,
177            round_state: RoundState::default(),
178            rp0: 0,
179            rp1: 0,
180            rp2: 0,
181            loop_counter: 1,
182            zp0: ZonePointer::default(),
183            zp1: ZonePointer::default(),
184            zp2: ZonePointer::default(),
185            zones: [Zone::default(), Zone::default()],
186            is_composite: false,
187            backward_compatibility: true,
188            is_pedantic: false,
189            did_iup_x: false,
190            did_iup_y: false,
191        }
192    }
193}
194
195/// The persistent graphics state.
196///
197/// Some of the graphics state is set by the control value program and
198/// persists between runs of the interpreter. This struct captures that
199/// state.
200///
201/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html>
202#[derive(Copy, Clone, Debug)]
203pub struct RetainedGraphicsState {
204    /// Controls whether the sign of control value table entries will be
205    /// changed to match the sign of the actual distance measurement with
206    /// which it is compared.
207    ///
208    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#auto%20flip>
209    pub auto_flip: bool,
210    /// Limits the regularizing effects of control value table entries to
211    /// cases where the difference between the table value and the measurement
212    /// taken from the original outline is sufficiently small.
213    ///
214    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#control_value_cut-in>
215    pub control_value_cutin: F26Dot6,
216    /// Establishes the base value used to calculate the range of point sizes
217    /// to which a given DELTAC[] or DELTAP[] instruction will apply.
218    ///
219    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#delta%20base>
220    pub delta_base: u16,
221    /// Determines the range of movement and smallest magnitude of movement
222    /// (the step) in a DELTAC[] or DELTAP[] instruction.
223    ///
224    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#delta%20shift>
225    pub delta_shift: u16,
226    /// Makes it possible to turn off instructions under some circumstances.
227    /// When set to TRUE, no instructions will be executed
228    ///
229    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#instruct%20control>
230    pub instruct_control: u8,
231    /// Establishes the smallest possible value to which a distance will be
232    /// rounded.
233    ///
234    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#minimum%20distance>
235    pub min_distance: F26Dot6,
236    /// Determines whether the interpreter will activate dropout control for
237    /// the current glyph.
238    ///
239    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#scan%20control>
240    pub scan_control: bool,
241    /// Type associated with `scan_control`.
242    pub scan_type: i32,
243    /// The distance difference below which the interpreter will replace a
244    /// CVT distance or an actual distance in favor of the single width value.
245    ///
246    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#single_width_cut_in>
247    pub single_width_cutin: F26Dot6,
248    /// The value used in place of the control value table distance or the
249    /// actual distance value when the difference between that distance and
250    /// the single width value is less than the single width cut-in.
251    ///
252    /// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#single_width_value>
253    pub single_width: F26Dot6,
254    /// The user requested hinting target.
255    pub target: Target,
256    /// The scale factor for the current instance. Conversion from font units
257    /// to 26.6 for current ppem.
258    pub scale: i32,
259    /// The nominal pixels per em value for the current instance.
260    pub ppem: i32,
261    /// True if a rotation is being applied.
262    pub is_rotated: bool,
263    /// True if a non-uniform scale is being applied.
264    pub is_stretched: bool,
265}
266
267impl RetainedGraphicsState {
268    pub fn new(scale: i32, ppem: i32, target: Target) -> Self {
269        Self {
270            scale,
271            ppem,
272            target,
273            ..Default::default()
274        }
275    }
276}
277
278impl Default for RetainedGraphicsState {
279    fn default() -> Self {
280        // For table of default values, see <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_graphics_state>
281        Self {
282            auto_flip: true,
283            // 17/16 pixels in 26.6
284            // (17 * 64 / 16) = 68
285            control_value_cutin: F26Dot6::from_bits(68),
286            delta_base: 9,
287            delta_shift: 3,
288            instruct_control: 0,
289            // 1 pixel in 26.6
290            min_distance: F26Dot6::from_bits(64),
291            scan_control: false,
292            scan_type: 0,
293            single_width_cutin: F26Dot6::ZERO,
294            single_width: F26Dot6::ZERO,
295            target: Default::default(),
296            scale: 0,
297            ppem: 0,
298            is_rotated: false,
299            is_stretched: false,
300        }
301    }
302}
303
304impl Deref for GraphicsState<'_> {
305    type Target = RetainedGraphicsState;
306
307    fn deref(&self) -> &Self::Target {
308        &self.retained
309    }
310}
311
312impl DerefMut for GraphicsState<'_> {
313    fn deref_mut(&mut self) -> &mut Self::Target {
314        &mut self.retained
315    }
316}