skrifa/outline/glyf/hint/engine/stack.rs
1//! Managing the stack and pushing data onto the interpreter stack.
2//!
3//! Implements 26 instructions.
4//!
5//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#managing-the-stack>
6//! and <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
7
8use read_fonts::tables::glyf::bytecode::InlineOperands;
9
10use super::{Engine, OpResult};
11
12impl Engine<'_> {
13 /// Duplicate top stack element.
14 ///
15 /// DUP[] (0x20)
16 ///
17 /// Pops: e
18 /// Pushes: e, e
19 ///
20 /// Duplicates the element at the top of the stack.
21 ///
22 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#duplicate-top-stack-element>
23 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2650>
24 pub(super) fn op_dup(&mut self) -> OpResult {
25 self.value_stack.dup()
26 }
27
28 /// Pop top stack element.
29 ///
30 /// POP[] (0x21)
31 ///
32 /// Pops: e
33 ///
34 /// Pops the top element of the stack.
35 ///
36 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pop-top-stack-element>
37 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2663>
38 pub(super) fn op_pop(&mut self) -> OpResult {
39 self.value_stack.pop()?;
40 Ok(())
41 }
42
43 /// Clear the entire stack.
44 ///
45 /// CLEAR[] (0x22)
46 ///
47 /// Pops: all the items on the stack
48 ///
49 /// Clears all elements from the stack.
50 ///
51 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#clear-the-entire-stack>
52 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2676>
53 pub(super) fn op_clear(&mut self) -> OpResult {
54 self.value_stack.clear();
55 Ok(())
56 }
57
58 /// Swap the top two elements on the stack.
59 ///
60 /// SWAP[] (0x23)
61 ///
62 /// Pops: e2, e1
63 /// Pushes: e1, e2
64 ///
65 /// Swaps the top two elements of the stack making the old top element the
66 /// second from the top and the old second element the top element.
67 ///
68 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#swap-the-top-two-elements-on-the-stack>
69 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2689>
70 pub(super) fn op_swap(&mut self) -> OpResult {
71 self.value_stack.swap()
72 }
73
74 /// Returns the depth of the stack.
75 ///
76 /// DEPTH[] (0x24)
77 ///
78 /// Pushes: n; number of elements
79 ///
80 /// Pushes n, the number of elements currently in the stack onto the stack.
81 ///
82 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#returns-the-depth-of-the-stack>
83 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2707>
84 pub(super) fn op_depth(&mut self) -> OpResult {
85 let n = self.value_stack.len();
86 self.value_stack.push(n as i32)
87 }
88
89 /// Copy the indexed element to the top of the stack.
90 ///
91 /// CINDEX[] (0x25)
92 ///
93 /// Pops: k: stack element number
94 /// Pushes: ek: indexed element
95 ///
96 /// Puts a copy of the kth stack element on the top of the stack.
97 ///
98 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#copy-the-indexed-element-to-the-top-of-the-stack>
99 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3232>
100 pub(super) fn op_cindex(&mut self) -> OpResult {
101 self.value_stack.copy_index()
102 }
103
104 /// Move the indexed element to the top of the stack.
105 ///
106 /// MINDEX[] (0x26)
107 ///
108 /// Pops: k: stack element number
109 /// Pushes: ek: indexed element
110 ///
111 /// Moves the indexed element to the top of the stack.
112 ///
113 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#move-the-indexed-element-to-the-top-of-the-stack>
114 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3199>
115 pub(super) fn op_mindex(&mut self) -> OpResult {
116 self.value_stack.move_index()
117 }
118
119 /// Roll the top three stack elements.
120 ///
121 /// ROLL[] (0x8a)
122 ///
123 /// Pops: a, b, c (top three stack elements)
124 /// Pushes: b, a, c (elements reordered)
125 ///
126 /// Performs a circular shift of the top three objects on the stack with
127 /// the effect being to move the third element to the top of the stack
128 /// and to move the first two elements down one position. ROLL is
129 /// equivalent to MINDEX[] 3.
130 ///
131 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#roll-the-top-three-stack-elements>
132 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3258>
133 pub(super) fn op_roll(&mut self) -> OpResult {
134 self.value_stack.roll()
135 }
136
137 /// Push data onto the interpreter stack.
138 ///
139 /// NPUSHB[] (0x8a)
140 ///
141 /// Takes n unsigned bytes from the instruction stream, where n is an
142 /// unsigned integer in the range (0..255), and pushes them onto the stack.
143 /// n itself is not pushed onto the stack.
144 ///
145 /// NPUSHW[] (0x41)
146 ///
147 /// Takes n 16-bit signed words from the instruction stream, where n is an
148 /// unsigned integer in the range (0..255), and pushes them onto the stack.
149 /// n itself is not pushed onto the stack.
150 ///
151 /// PUSHB\[abc\] (0xB0 - 0xB7)
152 ///
153 /// Takes the specified number of bytes from the instruction stream and
154 /// pushes them onto the interpreter stack.
155 /// The variables a, b, and c are binary digits representing numbers from
156 /// 000 to 111 (0-7 in binary). Because the actual number of bytes (n) is
157 /// from 1 to 8, 1 is automatically added to the ABC figure to obtain the
158 /// actual number of bytes pushed.
159 ///
160 /// PUSHW\[abc\] (0xB8 - 0xBF)
161 ///
162 /// Takes the specified number of words from the instruction stream and
163 /// pushes them onto the interpreter stack.
164 /// The variables a, b, and c are binary digits representing numbers from
165 /// 000 to 111 (0-7 binary). Because the actual number of bytes (n) is from
166 /// 1 to 8, 1 is automatically added to the abc figure to obtain the actual
167 /// number of bytes pushed.
168 ///
169 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
170 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3858>
171 pub(super) fn op_push(&mut self, operands: &InlineOperands) -> OpResult {
172 self.value_stack.push_inline_operands(operands)
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::super::MockEngine;
179 use read_fonts::tables::glyf::bytecode::MockInlineOperands;
180
181 #[test]
182 fn stack_ops() {
183 let mut mock = MockEngine::new();
184 let mut engine = mock.engine();
185 let byte_args = MockInlineOperands::from_bytes(&[2, 4, 6, 8]);
186 let word_args = MockInlineOperands::from_words(&[-2000, 4000, -6000, 8000]);
187 let initial_stack = byte_args
188 .operands()
189 .values()
190 .chain(word_args.operands().values())
191 .collect::<Vec<_>>();
192 // Push instructions
193 engine.op_push(&byte_args.operands()).unwrap();
194 engine.op_push(&word_args.operands()).unwrap();
195 assert_eq!(engine.value_stack.values(), initial_stack);
196 // DEPTH[]
197 engine.op_depth().unwrap();
198 assert_eq!(
199 engine.value_stack.pop().ok(),
200 Some(initial_stack.len() as i32)
201 );
202 // POP[]
203 engine.op_pop().unwrap();
204 engine.op_pop().unwrap();
205 assert_eq!(
206 engine.value_stack.values(),
207 &initial_stack[..initial_stack.len() - 2]
208 );
209 // SWAP[]
210 engine.op_swap().unwrap();
211 assert_eq!(&engine.value_stack.values()[4..], &[4000, -2000]);
212 // ROLL[]
213 engine.op_roll().unwrap();
214 assert_eq!(&engine.value_stack.values()[3..], &[4000, -2000, 8]);
215 // CINDEX[]
216 engine.value_stack.push(4).unwrap();
217 engine.op_cindex().unwrap();
218 assert_eq!(engine.value_stack.peek(), Some(6));
219 // MINDEX[]
220 engine.value_stack.push(3).unwrap();
221 engine.op_mindex().unwrap();
222 assert_eq!(engine.value_stack.peek(), Some(-2000));
223 }
224}