skrifa/outline/glyf/hint/program.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
//! TrueType program management.
use raw::tables::glyf::bytecode::Decoder;
use super::{
call_stack::{CallRecord, CallStack},
definition::Definition,
error::HintErrorKind,
};
/// Describes the source for a piece of bytecode.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[repr(u8)]
pub enum Program {
/// Program that initializes the function and instruction tables. Stored
/// in the `fpgm` table.
#[default]
Font = 0,
/// Program that initializes CVT and storage based on font size and other
/// parameters. Stored in the `prep` table.
ControlValue = 1,
/// Glyph specified program. Stored per-glyph in the `glyf` table.
Glyph = 2,
}
/// State for managing active programs and decoding instructions.
pub struct ProgramState<'a> {
/// Bytecode for each of the three program types, indexed by `Program`.
pub bytecode: [&'a [u8]; 3],
/// The initial program when execution begins.
pub initial: Program,
/// The currently active program.
pub current: Program,
/// Instruction decoder for the currently active program.
pub decoder: Decoder<'a>,
/// Tracks nested function and instruction invocations.
pub call_stack: CallStack,
}
impl<'a> ProgramState<'a> {
pub fn new(
font_code: &'a [u8],
cv_code: &'a [u8],
glyph_code: &'a [u8],
initial_program: Program,
) -> Self {
let bytecode = [font_code, cv_code, glyph_code];
Self {
bytecode,
initial: initial_program,
current: initial_program,
decoder: Decoder::new(bytecode[initial_program as usize], 0),
call_stack: CallStack::default(),
}
}
/// Resets the state for execution of the given program.
pub fn reset(&mut self, program: Program) {
self.initial = program;
self.current = program;
self.decoder = Decoder::new(self.bytecode[program as usize], 0);
self.call_stack.clear();
}
/// Jumps to the code in the given definition and sets it up for
/// execution `count` times.
pub fn enter(&mut self, definition: Definition, count: u32) -> Result<(), HintErrorKind> {
let program = definition.program();
let pc = definition.code_range().start;
let bytecode = self.bytecode[program as usize];
self.call_stack.push(CallRecord {
caller_program: self.current,
return_pc: self.decoder.pc,
current_count: count,
definition,
})?;
self.current = program;
self.decoder = Decoder::new(bytecode, pc);
Ok(())
}
/// Leaves the code from the definition on the top of the stack.
///
/// If the top call record has a loop count greater than 1, restarts
/// execution from the beginning of the definition. Otherwise, resumes
/// execution at the previously active definition.
pub fn leave(&mut self) -> Result<(), HintErrorKind> {
let mut record = self.call_stack.pop()?;
if record.current_count > 1 {
// This is a loop call with some iterations remaining.
record.current_count -= 1;
self.decoder.pc = record.definition.code_range().start;
self.call_stack.push(record)?;
} else {
self.current = record.caller_program;
// Reset the decoder to the calling program and program counter.
self.decoder.bytecode = self.bytecode[record.caller_program as usize];
self.decoder.pc = record.return_pc;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Test accounting of program, bytecode and program counter through
/// enter/leave cycles.
#[test]
fn accounting() {
let font_code = &[0][..];
let cv_code = &[1][..];
let glyph_code = &[2][..];
let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
// We start at glyph code
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
let font_def = Definition::new(Program::Font, 10..20, 0);
let cv_def = Definition::new(Program::ControlValue, 33..111, 1);
// Now move to CV code
state.enter(cv_def, 1).unwrap();
assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 33));
// Bump the program counter to test capture of return_pc
state.decoder.pc += 20;
// And to font code
state.enter(font_def, 1).unwrap();
assert_eq!(state.active_state(), (Program::Font, font_code, 10));
// Back to CV code
state.leave().unwrap();
assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 53));
// And to the original glyph code
state.leave().unwrap();
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
}
/// Ensure calls with a count of `n` require `n` leaves before returning
/// to previous frame. Also ensure program counter is reset to start of
/// definition at each leave.
#[test]
fn loop_call() {
let font_code = &[0][..];
let cv_code = &[1][..];
let glyph_code = &[2][..];
let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
let font_def = Definition::new(Program::Font, 10..20, 0);
// "Execute" font definition 3 times
state.enter(font_def, 3).unwrap();
for _ in 0..3 {
assert_eq!(state.active_state(), (Program::Font, font_code, 10));
// Modify program counter to ensure we reset on leave
state.decoder.pc += 22;
state.leave().unwrap();
}
// Should be back to glyph code
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
}
impl<'a> ProgramState<'a> {
fn active_state(&self) -> (Program, &'a [u8], usize) {
(self.current, self.decoder.bytecode, self.decoder.pc)
}
}
}