// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Graphics state instruction implementations for the TrueType interpreter.
///
/// Ported from `fontations/skrifa/src/outline/glyf/hint/engine/graphics.rs`
/// (Apache-2.0 OR MIT).
///|
fn TtEngine::op_svtca(
self : TtEngine,
opcode0 : Int,
) -> Result[Unit, HintError] {
let opcode = opcode0
let x = (opcode & 1) << 14
let y = x ^ 0x4000
if opcode < 4 {
self.graphics.proj_vector = { x, y }
self.graphics.dual_proj_vector = { x, y }
}
if (opcode & 2) == 0 {
self.graphics.freedom_vector = { x, y }
}
self.graphics.update_projection_state()
Ok(())
}
///|
fn tt_line_vector(p1 : TtPoint, p2 : TtPoint, is_parallel : Bool) -> TtVec14 {
let mut a = p1.x - p2.x
let mut b = p1.y - p2.y
if a == 0 && b == 0 {
a = 0x4000
} else if !is_parallel {
// Counter-clockwise 90deg rotation.
let c = b
b = a
a = -c
}
let (nx, ny) = tt_hint_normalize14(a, b)
{ x: nx, y: ny }
}
///|
fn TtEngine::op_svtl(self : TtEngine, opcode : Int) -> Result[Unit, HintError] {
let index1 = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
let index2 = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
let is_parallel = (opcode & 1) == 0
let p1 = match self.graphics.zp1_zone().point(index2) {
Err(e) => return Err(e)
Ok(v) => v
}
let p2 = match self.graphics.zp2_zone().point(index1) {
Err(e) => return Err(e)
Ok(v) => v
}
let v = tt_line_vector(p1, p2, is_parallel)
if opcode < 8 {
self.graphics.proj_vector = v
self.graphics.dual_proj_vector = v
} else {
self.graphics.freedom_vector = v
}
self.graphics.update_projection_state()
Ok(())
}
///|
fn TtEngine::op_spvfs(self : TtEngine) -> Result[Unit, HintError] {
let y0 = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let x0 = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let x = Int16::from_int(x0).to_int()
let y = Int16::from_int(y0).to_int()
let v = if x == 0 && y == 0 {
self.graphics.proj_vector
} else {
let (nx, ny) = tt_hint_normalize14(x, y)
{ x: nx, y: ny }
}
self.graphics.proj_vector = v
self.graphics.dual_proj_vector = v
self.graphics.update_projection_state()
Ok(())
}
///|
fn TtEngine::op_sfvfs(self : TtEngine) -> Result[Unit, HintError] {
let y0 = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let x0 = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let x = Int16::from_int(x0).to_int()
let y = Int16::from_int(y0).to_int()
let v = if x == 0 && y == 0 {
self.graphics.freedom_vector
} else {
let (nx, ny) = tt_hint_normalize14(x, y)
{ x: nx, y: ny }
}
self.graphics.freedom_vector = v
self.graphics.update_projection_state()
Ok(())
}
///|
fn TtEngine::op_gpv(self : TtEngine) -> Result[Unit, HintError] {
let v = self.graphics.proj_vector
match self.value_stack.push(v.x) {
Err(e) => return Err(e)
Ok(_) => ()
}
self.value_stack.push(v.y)
}
///|
fn TtEngine::op_gfv(self : TtEngine) -> Result[Unit, HintError] {
let v = self.graphics.freedom_vector
match self.value_stack.push(v.x) {
Err(e) => return Err(e)
Ok(_) => ()
}
self.value_stack.push(v.y)
}
///|
fn TtEngine::op_sfvtpv(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.freedom_vector = self.graphics.proj_vector
self.graphics.update_projection_state()
Ok(())
}
///|
fn TtEngine::op_srp0(self : TtEngine) -> Result[Unit, HintError] {
let p = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.rp0 = p
Ok(())
}
///|
fn TtEngine::op_srp1(self : TtEngine) -> Result[Unit, HintError] {
let p = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.rp1 = p
Ok(())
}
///|
fn TtEngine::op_srp2(self : TtEngine) -> Result[Unit, HintError] {
let p = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.rp2 = p
Ok(())
}
///|
fn TtEngine::op_szp0(self : TtEngine) -> Result[Unit, HintError] {
let z = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.zp0 = match tt_zone_pointer_from_i32(z) {
Err(e) => return Err(e)
Ok(v) => v
}
Ok(())
}
///|
fn TtEngine::op_szp1(self : TtEngine) -> Result[Unit, HintError] {
let z = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.zp1 = match tt_zone_pointer_from_i32(z) {
Err(e) => return Err(e)
Ok(v) => v
}
Ok(())
}
///|
fn TtEngine::op_szp2(self : TtEngine) -> Result[Unit, HintError] {
let z = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.zp2 = match tt_zone_pointer_from_i32(z) {
Err(e) => return Err(e)
Ok(v) => v
}
Ok(())
}
///|
fn TtEngine::op_szps(self : TtEngine) -> Result[Unit, HintError] {
let z = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let zp = match tt_zone_pointer_from_i32(z) {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.zp0 = zp
self.graphics.zp1 = zp
self.graphics.zp2 = zp
Ok(())
}
///|
fn TtEngine::op_sloop(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
if n < 0 {
return Err(NegativeLoopCounter)
}
// Limit to 16-bit like FreeType.
self.graphics.loop_counter = n.min(0xFFFF)
Ok(())
}
///|
fn TtEngine::op_rtg(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = Grid
Ok(())
}
///|
fn TtEngine::op_rthg(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = HalfGrid
Ok(())
}
///|
fn TtEngine::op_rtdg(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = DoubleGrid
Ok(())
}
///|
fn TtEngine::op_rdtg(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = DownToGrid
Ok(())
}
///|
fn TtEngine::op_rutg(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = UpToGrid
Ok(())
}
///|
fn TtEngine::op_roff(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.round_state.mode = Off
Ok(())
}
///|
fn tt_super_round(
gs : TtGraphicsState,
grid_period : Int,
selector : Int,
) -> Unit {
let state = gs.round_state
let period = match selector & 0xC0 {
0x00 => grid_period / 2
0x40 => grid_period
0x80 => grid_period * 2
0xC0 => grid_period
_ => state.period << 8
}
let phase = match selector & 0x30 {
0x00 => 0
0x10 => period / 4
0x20 => period / 2
0x30 => period * 3 / 4
_ => state.phase << 8
}
let threshold = if (selector & 0x0F) == 0 {
period - 1
} else {
((selector & 0x0F) - 4) * period / 8
}
gs.round_state.period = period >> 8
gs.round_state.phase = phase >> 8
gs.round_state.threshold = threshold >> 8
}
///|
fn TtEngine::op_sround(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
tt_super_round(self.graphics, 0x4000, n)
self.graphics.round_state.mode = Super
Ok(())
}
///|
fn TtEngine::op_s45round(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
tt_super_round(self.graphics, 0x2D41, n)
self.graphics.round_state.mode = Super45
Ok(())
}
///|
fn TtEngine::op_smd(self : TtEngine) -> Result[Unit, HintError] {
let d = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.retained.min_distance = d
Ok(())
}
///|
fn TtEngine::op_scvtci(self : TtEngine) -> Result[Unit, HintError] {
let c = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.retained.control_value_cutin = c
Ok(())
}
///|
fn TtEngine::op_sswci(self : TtEngine) -> Result[Unit, HintError] {
let c = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.retained.single_width_cutin = c
Ok(())
}
///|
fn TtEngine::op_ssw(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.retained.single_width = tt_hint_mul_16_16(
n,
self.graphics.retained.scale,
)
Ok(())
}
///|
fn TtEngine::op_flipon(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.retained.auto_flip = true
Ok(())
}
///|
fn TtEngine::op_flipoff(self : TtEngine) -> Result[Unit, HintError] {
self.graphics.retained.auto_flip = false
Ok(())
}
///|
fn TtEngine::op_sangw(self : TtEngine) -> Result[Unit, HintError] {
match self.value_stack.pop() {
Err(e) => Err(e)
Ok(_) => Ok(())
}
}
///|
fn TtEngine::op_instctrl(self : TtEngine) -> Result[Unit, HintError] {
let selector = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let value = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
// Selector is index (1..=3).
if selector < 1 || selector > 3 {
return Ok(())
}
let selector_flag = 1 << (selector - 1)
if value != 0 && value != selector_flag {
return Ok(())
}
// If preserving linear metrics, prevent modification of the backward compat flag.
if selector == 3 && self.graphics.retained.target.preserve_linear_metrics() {
return Ok(())
}
match self.program.current {
ControlValue => {
self.graphics.retained.instruct_control = self.graphics.retained.instruct_control &
(selector_flag ^ -1)
self.graphics.retained.instruct_control = self.graphics.retained.instruct_control |
value
}
Glyph =>
if selector == 3 {
self.graphics.backward_compatibility = value != 4
}
_ => ()
}
Ok(())
}
///|
fn TtEngine::op_scanctrl(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let threshold = n & 0xFF
if threshold == 0xFF {
self.graphics.retained.scan_control = true
return Ok(())
}
if threshold == 0 {
self.graphics.retained.scan_control = false
return Ok(())
}
let ppem = self.graphics.retained.ppem
let is_rotated = self.graphics.retained.is_rotated
let is_stretched = self.graphics.retained.is_stretched
// See fontations/skrifa + FreeType: bits 8..13 conditionally enable/disable.
let mut scan_control = self.graphics.retained.scan_control
// Bits 8-10 set scan_control to true when the condition is met.
if (n & 0x100) != 0 && ppem <= threshold {
scan_control = true
}
if (n & 0x200) != 0 && is_rotated {
scan_control = true
}
if (n & 0x400) != 0 && is_stretched {
scan_control = true
}
// Bits 11-13 set scan_control to false when the condition is met.
if (n & 0x800) != 0 && ppem > threshold {
scan_control = false
}
if (n & 0x1000) != 0 && is_rotated {
scan_control = false
}
if (n & 0x2000) != 0 && is_stretched {
scan_control = false
}
self.graphics.retained.scan_control = scan_control
Ok(())
}
///|
fn TtEngine::op_scantype(self : TtEngine) -> Result[Unit, HintError] {
let n = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
self.graphics.retained.scan_type = n & 0xFFFF
Ok(())
}
///|
fn TtEngine::op_getinfo(self : TtEngine) -> Result[Unit, HintError] {
// See
// and `fontations/skrifa` GETINFO implementation.
let selector = match self.value_stack.pop() {
Err(e) => return Err(e)
Ok(v) => v
}
let mut result = 0
// Interpreter version: selector bit 0, result bits 0..7.
if (selector & (1 << 0)) != 0 {
result = 40
}
// Glyph rotated: selector bit 1, result bit 8.
if (selector & (1 << 1)) != 0 && self.graphics.retained.is_rotated {
result = result | (1 << 8)
}
// Glyph stretched: selector bit 2, result bit 9.
if (selector & (1 << 2)) != 0 && self.graphics.retained.is_stretched {
result = result | (1 << 9)
}
// Font variations: selector bit 3, result bit 10.
if (selector & (1 << 3)) != 0 && self.axis_count != 0 {
result = result | (1 << 10)
}
if self.graphics.retained.target.is_smooth() {
// Subpixel hinting: selector bit 6, result bit 13 (always enabled).
if (selector & (1 << 6)) != 0 {
result = result | (1 << 13)
}
// Vertical LCD: selector bit 8, result bit 15.
if (selector & (1 << 8)) != 0 &&
self.graphics.retained.target.is_vertical_lcd() {
result = result | (1 << 15)
}
// Subpixel positioned: selector bit 10, result bit 17 (always enabled).
if (selector & (1 << 10)) != 0 {
result = result | (1 << 17)
}
// Symmetrical smoothing: selector bit 11, result bit 18.
if (selector & (1 << 11)) != 0 &&
self.graphics.retained.target.symmetric_rendering() {
result = result | (1 << 18)
}
// ClearType + grayscale: selector bit 12, result bit 19.
if (selector & (1 << 12)) != 0 &&
self.graphics.retained.target.is_grayscale_cleartype() {
result = result | (1 << 19)
}
}
self.value_stack.push(result)
}
///|
fn TtEngine::op_getvariation(
self : TtEngine,
pc : Int,
) -> Result[Unit, HintError] {
if self.axis_count != 0 {
for i in 0.. return Err(e)
Ok(_) => ()
}
}
Ok(())
} else {
self.op_unknown(0x91, pc)
}
}
///|
fn TtEngine::op_getdata(self : TtEngine, pc : Int) -> Result[Unit, HintError] {
if self.axis_count != 0 {
self.value_stack.push(17)
} else {
self.op_unknown(0x92, pc)
}
}
///|
fn TtEngine::op_sdpvtl(
self : TtEngine,
opcode : Int,
) -> Result[Unit, HintError] {
// Sets dual projection vector to line.
let index1 = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
let index2 = match self.value_stack.pop_usize() {
Err(e) => return Err(e)
Ok(v) => v
}
let is_parallel = (opcode & 1) == 0
let p1 = match self.graphics.zp1_zone().original_point(index2) {
Err(e) => return Err(e)
Ok(v) => v
}
let p2 = match self.graphics.zp2_zone().original_point(index1) {
Err(e) => return Err(e)
Ok(v) => v
}
let v = tt_line_vector(p1, p2, is_parallel)
self.graphics.dual_proj_vector = v
self.graphics.update_projection_state()
Ok(())
}