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}