skrifa/outline/glyf/hint/engine/
dispatch.rs

1//! Instruction decoding and dispatch.
2
3use read_fonts::tables::glyf::bytecode::Opcode;
4
5use super::{super::program::Program, Engine, HintError, HintErrorKind, Instruction};
6
7/// Maximum number of instructions we will execute in `Engine::run()`. This
8/// is used to ensure termination of a hinting program.
9/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/include/freetype/config/ftoption.h#L744>
10const MAX_RUN_INSTRUCTIONS: usize = 1_000_000;
11
12impl<'a> Engine<'a> {
13    /// Resets state for the specified program and executes all instructions.
14    pub fn run_program(&mut self, program: Program, is_pedantic: bool) -> Result<(), HintError> {
15        self.reset(program, is_pedantic);
16        self.run()
17    }
18
19    /// Set internal state for running the specified program.
20    pub fn reset(&mut self, program: Program, is_pedantic: bool) {
21        self.program.reset(program);
22        // Reset overall graphics state, keeping the retained bits.
23        self.graphics.reset();
24        self.graphics.is_pedantic = is_pedantic;
25        self.loop_budget.reset();
26        // Program specific setup.
27        match program {
28            Program::Font => {
29                self.definitions.functions.reset();
30                self.definitions.instructions.reset();
31            }
32            Program::ControlValue => {
33                self.graphics.backward_compatibility = false;
34            }
35            Program::Glyph => {
36                // Instruct control bit 1 says we reset retained graphics state
37                // to default values.
38                if self.graphics.instruct_control & 2 != 0 {
39                    self.graphics.reset_retained();
40                }
41                // Set backward compatibility mode
42                if self.graphics.target.preserve_linear_metrics() {
43                    self.graphics.backward_compatibility = true;
44                } else if self.graphics.target.is_smooth() {
45                    self.graphics.backward_compatibility =
46                        (self.graphics.instruct_control & 0x4) == 0;
47                } else {
48                    self.graphics.backward_compatibility = false;
49                }
50            }
51        }
52    }
53
54    /// Decodes and dispatches all instructions until completion or error.
55    pub fn run(&mut self) -> Result<(), HintError> {
56        let mut count = 0;
57        while let Some(ins) = self.decode() {
58            let ins = ins?;
59            self.dispatch(&ins)?;
60            count += 1;
61            if count > MAX_RUN_INSTRUCTIONS {
62                return Err(HintError {
63                    program: self.program.current,
64                    glyph_id: None,
65                    pc: ins.pc,
66                    opcode: Some(ins.opcode),
67                    kind: HintErrorKind::ExceededExecutionBudget,
68                });
69            }
70        }
71        Ok(())
72    }
73
74    /// Decodes the next instruction from the current program.
75    pub fn decode(&mut self) -> Option<Result<Instruction<'a>, HintError>> {
76        let ins = self.program.decoder.decode()?;
77        Some(ins.map_err(|_| HintError {
78            program: self.program.current,
79            glyph_id: None,
80            pc: self.program.decoder.pc,
81            opcode: None,
82            kind: HintErrorKind::UnexpectedEndOfBytecode,
83        }))
84    }
85
86    /// Executes the appropriate code for the given instruction.
87    pub fn dispatch(&mut self, ins: &Instruction) -> Result<(), HintError> {
88        let current_program = self.program.current;
89        self.dispatch_inner(ins).map_err(|kind| HintError {
90            program: current_program,
91            glyph_id: None,
92            pc: ins.pc,
93            opcode: Some(ins.opcode),
94            kind,
95        })
96    }
97
98    fn dispatch_inner(&mut self, ins: &Instruction) -> Result<(), HintErrorKind> {
99        use Opcode::*;
100        let opcode = ins.opcode;
101        let raw_opcode = opcode as u8;
102        match ins.opcode {
103            SVTCA0 | SVTCA1 | SPVTCA0 | SPVTCA1 | SFVTCA0 | SFVTCA1 => self.op_svtca(raw_opcode)?,
104            SPVTL0 | SPVTL1 | SFVTL0 | SFVTL1 => self.op_svtl(raw_opcode)?,
105            SPVFS => self.op_spvfs()?,
106            SFVFS => self.op_sfvfs()?,
107            GPV => self.op_gpv()?,
108            GFV => self.op_gfv()?,
109            SFVTPV => self.op_sfvtpv()?,
110            ISECT => self.op_isect()?,
111            SRP0 => self.op_srp0()?,
112            SRP1 => self.op_srp1()?,
113            SRP2 => self.op_srp2()?,
114            SZP0 => self.op_szp0()?,
115            SZP1 => self.op_szp1()?,
116            SZP2 => self.op_szp2()?,
117            SZPS => self.op_szps()?,
118            SLOOP => self.op_sloop()?,
119            RTG => self.op_rtg()?,
120            RTHG => self.op_rthg()?,
121            SMD => self.op_smd()?,
122            ELSE => self.op_else()?,
123            JMPR => self.op_jmpr()?,
124            SCVTCI => self.op_scvtci()?,
125            SSWCI => self.op_sswci()?,
126            SSW => self.op_ssw()?,
127            DUP => self.op_dup()?,
128            POP => self.op_pop()?,
129            CLEAR => self.op_clear()?,
130            SWAP => self.op_swap()?,
131            DEPTH => self.op_depth()?,
132            CINDEX => self.op_cindex()?,
133            MINDEX => self.op_mindex()?,
134            ALIGNPTS => self.op_alignpts()?,
135            // UNUSED: 0x28
136            UTP => self.op_utp()?,
137            LOOPCALL => self.op_loopcall()?,
138            CALL => self.op_call()?,
139            FDEF => self.op_fdef()?,
140            ENDF => self.op_endf()?,
141            MDAP0 | MDAP1 => self.op_mdap(raw_opcode)?,
142            IUP0 | IUP1 => self.op_iup(raw_opcode)?,
143            SHP0 | SHP1 => self.op_shp(raw_opcode)?,
144            SHC0 | SHC1 => self.op_shc(raw_opcode)?,
145            SHZ0 | SHZ1 => self.op_shz(raw_opcode)?,
146            SHPIX => self.op_shpix()?,
147            IP => self.op_ip()?,
148            MSIRP0 | MSIRP1 => self.op_msirp(raw_opcode)?,
149            ALIGNRP => self.op_alignrp()?,
150            RTDG => self.op_rtdg()?,
151            MIAP0 | MIAP1 => self.op_miap(raw_opcode)?,
152            NPUSHB | NPUSHW => self.op_push(&ins.inline_operands)?,
153            WS => self.op_ws()?,
154            RS => self.op_rs()?,
155            WCVTP => self.op_wcvtp()?,
156            RCVT => self.op_rcvt()?,
157            GC0 | GC1 => self.op_gc(raw_opcode)?,
158            SCFS => self.op_scfs()?,
159            MD0 | MD1 => self.op_md(raw_opcode)?,
160            MPPEM => self.op_mppem()?,
161            MPS => self.op_mps()?,
162            FLIPON => self.op_flipon()?,
163            FLIPOFF => self.op_flipoff()?,
164            // Should be unused in production fonts, but we may want to
165            // support debugging at some point. Just pops a value from
166            // the stack.
167            DEBUG => {
168                self.value_stack.pop()?;
169            }
170            LT => self.op_lt()?,
171            LTEQ => self.op_lteq()?,
172            GT => self.op_gt()?,
173            GTEQ => self.op_gteq()?,
174            EQ => self.op_eq()?,
175            NEQ => self.op_neq()?,
176            ODD => self.op_odd()?,
177            EVEN => self.op_even()?,
178            IF => self.op_if()?,
179            EIF => self.op_eif()?,
180            AND => self.op_and()?,
181            OR => self.op_or()?,
182            NOT => self.op_not()?,
183            DELTAP1 => self.op_deltap(opcode)?,
184            SDB => self.op_sdb()?,
185            SDS => self.op_sds()?,
186            ADD => self.op_add()?,
187            SUB => self.op_sub()?,
188            DIV => self.op_div()?,
189            MUL => self.op_mul()?,
190            ABS => self.op_abs()?,
191            NEG => self.op_neg()?,
192            FLOOR => self.op_floor()?,
193            CEILING => self.op_ceiling()?,
194            ROUND00 | ROUND01 | ROUND10 | ROUND11 => self.op_round()?,
195            // "No round" means do nothing :)
196            NROUND00 | NROUND01 | NROUND10 | NROUND11 => {}
197            WCVTF => self.op_wcvtf()?,
198            DELTAP2 | DELTAP3 => self.op_deltap(opcode)?,
199            DELTAC1 | DELTAC2 | DELTAC3 => self.op_deltac(opcode)?,
200            SROUND => self.op_sround()?,
201            S45ROUND => self.op_s45round()?,
202            JROT => self.op_jrot()?,
203            JROF => self.op_jrof()?,
204            ROFF => self.op_roff()?,
205            // UNUSED: 0x7B
206            RUTG => self.op_rutg()?,
207            RDTG => self.op_rdtg()?,
208            SANGW => self.op_sangw()?,
209            // Unsupported instruction, do nothing
210            AA => {}
211            FLIPPT => self.op_flippt()?,
212            FLIPRGON => self.op_fliprgon()?,
213            FLIPRGOFF => self.op_fliprgoff()?,
214            // UNUSED: 0x83 | 0x84
215            SCANCTRL => self.op_scanctrl()?,
216            SDPVTL0 | SDPVTL1 => self.op_sdpvtl(raw_opcode)?,
217            GETINFO => self.op_getinfo()?,
218            IDEF => self.op_idef()?,
219            ROLL => self.op_roll()?,
220            MAX => self.op_max()?,
221            MIN => self.op_min()?,
222            SCANTYPE => self.op_scantype()?,
223            INSTCTRL => self.op_instctrl()?,
224            // UNUSED: 0x8F | 0x90 (ADJUST?)
225            GETVARIATION => self.op_getvariation()?,
226            GETDATA => self.op_getdata()?,
227            _ => {
228                // FreeType handles MIRP, MDRP and pushes here.
229                // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L7629>
230                if opcode >= MIRP00000 {
231                    self.op_mirp(raw_opcode)?
232                } else if opcode >= MDRP00000 {
233                    self.op_mdrp(raw_opcode)?
234                } else if opcode >= PUSHB000 {
235                    self.op_push(&ins.inline_operands)?;
236                } else {
237                    return self.op_unknown(opcode as u8);
238                }
239            }
240        }
241        Ok(())
242    }
243}