skrifa/outline/glyf/hint/engine/stack.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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
//! Managing the stack and pushing data onto the interpreter stack.
//!
//! Implements 26 instructions.
//!
//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#managing-the-stack>
//! and <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
use read_fonts::tables::glyf::bytecode::InlineOperands;
use super::{Engine, OpResult};
impl<'a> Engine<'a> {
/// Duplicate top stack element.
///
/// DUP[] (0x20)
///
/// Pops: e
/// Pushes: e, e
///
/// Duplicates the element at the top of the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#duplicate-top-stack-element>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2650>
pub(super) fn op_dup(&mut self) -> OpResult {
self.value_stack.dup()
}
/// Pop top stack element.
///
/// POP[] (0x21)
///
/// Pops: e
///
/// Pops the top element of the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pop-top-stack-element>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2663>
pub(super) fn op_pop(&mut self) -> OpResult {
self.value_stack.pop()?;
Ok(())
}
/// Clear the entire stack.
///
/// CLEAR[] (0x22)
///
/// Pops: all the items on the stack
///
/// Clears all elements from the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#clear-the-entire-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2676>
pub(super) fn op_clear(&mut self) -> OpResult {
self.value_stack.clear();
Ok(())
}
/// Swap the top two elements on the stack.
///
/// SWAP[] (0x23)
///
/// Pops: e2, e1
/// Pushes: e1, e2
///
/// Swaps the top two elements of the stack making the old top element the
/// second from the top and the old second element the top element.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#swap-the-top-two-elements-on-the-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2689>
pub(super) fn op_swap(&mut self) -> OpResult {
self.value_stack.swap()
}
/// Returns the depth of the stack.
///
/// DEPTH[] (0x24)
///
/// Pushes: n; number of elements
///
/// Pushes n, the number of elements currently in the stack onto the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#returns-the-depth-of-the-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2707>
pub(super) fn op_depth(&mut self) -> OpResult {
let n = self.value_stack.len();
self.value_stack.push(n as i32)
}
/// Copy the indexed element to the top of the stack.
///
/// CINDEX[] (0x25)
///
/// Pops: k: stack element number
/// Pushes: ek: indexed element
///
/// Puts a copy of the kth stack element on the top of the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#copy-the-indexed-element-to-the-top-of-the-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3232>
pub(super) fn op_cindex(&mut self) -> OpResult {
self.value_stack.copy_index()
}
/// Move the indexed element to the top of the stack.
///
/// MINDEX[] (0x26)
///
/// Pops: k: stack element number
/// Pushes: ek: indexed element
///
/// Moves the indexed element to the top of the stack.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#move-the-indexed-element-to-the-top-of-the-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3199>
pub(super) fn op_mindex(&mut self) -> OpResult {
self.value_stack.move_index()
}
/// Roll the top three stack elements.
///
/// ROLL[] (0x8a)
///
/// Pops: a, b, c (top three stack elements)
/// Pushes: b, a, c (elements reordered)
///
/// Performs a circular shift of the top three objects on the stack with
/// the effect being to move the third element to the top of the stack
/// and to move the first two elements down one position. ROLL is
/// equivalent to MINDEX[] 3.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#roll-the-top-three-stack-elements>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3258>
pub(super) fn op_roll(&mut self) -> OpResult {
self.value_stack.roll()
}
/// Push data onto the interpreter stack.
///
/// NPUSHB[] (0x8a)
///
/// Takes n unsigned bytes from the instruction stream, where n is an
/// unsigned integer in the range (0..255), and pushes them onto the stack.
/// n itself is not pushed onto the stack.
///
/// NPUSHW[] (0x41)
///
/// Takes n 16-bit signed words from the instruction stream, where n is an
/// unsigned integer in the range (0..255), and pushes them onto the stack.
/// n itself is not pushed onto the stack.
///
/// PUSHB\[abc\] (0xB0 - 0xB7)
///
/// Takes the specified number of bytes from the instruction stream and
/// pushes them onto the interpreter stack.
/// The variables a, b, and c are binary digits representing numbers from
/// 000 to 111 (0-7 in binary). Because the actual number of bytes (n) is
/// from 1 to 8, 1 is automatically added to the ABC figure to obtain the
/// actual number of bytes pushed.
///
/// PUSHW\[abc\] (0xB8 - 0xBF)
///
/// Takes the specified number of words from the instruction stream and
/// pushes them onto the interpreter stack.
/// The variables a, b, and c are binary digits representing numbers from
/// 000 to 111 (0-7 binary). Because the actual number of bytes (n) is from
/// 1 to 8, 1 is automatically added to the abc figure to obtain the actual
/// number of bytes pushed.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
/// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3858>
pub(super) fn op_push(&mut self, operands: &InlineOperands) -> OpResult {
self.value_stack.push_inline_operands(operands)
}
}
#[cfg(test)]
mod tests {
use super::super::MockEngine;
use read_fonts::tables::glyf::bytecode::MockInlineOperands;
#[test]
fn stack_ops() {
let mut mock = MockEngine::new();
let mut engine = mock.engine();
let byte_args = MockInlineOperands::from_bytes(&[2, 4, 6, 8]);
let word_args = MockInlineOperands::from_words(&[-2000, 4000, -6000, 8000]);
let initial_stack = byte_args
.operands()
.values()
.chain(word_args.operands().values())
.collect::<Vec<_>>();
// Push instructions
engine.op_push(&byte_args.operands()).unwrap();
engine.op_push(&word_args.operands()).unwrap();
assert_eq!(engine.value_stack.values(), initial_stack);
// DEPTH[]
engine.op_depth().unwrap();
assert_eq!(
engine.value_stack.pop().ok(),
Some(initial_stack.len() as i32)
);
// POP[]
engine.op_pop().unwrap();
engine.op_pop().unwrap();
assert_eq!(
engine.value_stack.values(),
&initial_stack[..initial_stack.len() - 2]
);
// SWAP[]
engine.op_swap().unwrap();
assert_eq!(&engine.value_stack.values()[4..], &[4000, -2000]);
// ROLL[]
engine.op_roll().unwrap();
assert_eq!(&engine.value_stack.values()[3..], &[4000, -2000, 8]);
// CINDEX[]
engine.value_stack.push(4).unwrap();
engine.op_cindex().unwrap();
assert_eq!(engine.value_stack.peek(), Some(6));
// MINDEX[]
engine.value_stack.push(3).unwrap();
engine.op_mindex().unwrap();
assert_eq!(engine.value_stack.peek(), Some(-2000));
}
}