Skip to content

Commit

Permalink
[skrifa] tthint: graphics state
Browse files Browse the repository at this point in the history
First of a series of TrueType hinting patches.

This one models the TT "graphics state" which is essentially all of the data involving outlines, measurement and rounding.

Includes a tiny math module that provides support for working with fixed point values as raw i32s. This is not ideal but exists from the initial port from FreeType and will require some time and attention to correct. This will need to be deferred to a later time.
  • Loading branch information
dfrg committed Jan 19, 2024
1 parent 5143ffa commit 05721f4
Show file tree
Hide file tree
Showing 8 changed files with 1,336 additions and 6 deletions.
73 changes: 73 additions & 0 deletions skrifa/src/outline/glyf/hint/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Hinting error definitions.

/// Errors that may occur when interpreting TrueType bytecode.
#[derive(Clone, Debug)]
pub enum HintErrorKind {
UnexpectedEndOfBytecode,
InvalidOpcode(u8),
DefinitionInGlyphProgram,
NestedDefinition,
InvalidDefintionIndex(usize),
ValueStackOverflow,
ValueStackUnderflow,
CallStackOverflow,
CallStackUnderflow,
InvalidStackValue(i32),
InvalidPointIndex(usize),
InvalidPointRange(usize, usize),
InvalidContourIndex(usize),
InvalidCvtIndex(usize),
InvalidStorageIndex(usize),
DivideByZero,
InvalidZoneIndex(i32),
NegativeLoopCounter,
InvalidJump,
}

impl core::fmt::Display for HintErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnexpectedEndOfBytecode => write!(f, "unexpected end of bytecode"),
Self::InvalidOpcode(opcode) => write!(f, "invalid instruction opcode {opcode}"),
Self::DefinitionInGlyphProgram => {
write!(f, "FDEF or IDEF instruction present in glyph program")
}
Self::NestedDefinition => write!(
f,
"FDEF or IDEF instruction present in another FDEF or IDEF block"
),
Self::InvalidDefintionIndex(index) => write!(
f,
"invalid function or instruction definition index {index}"
),
Self::ValueStackOverflow => write!(f, "value stack overflow"),
Self::ValueStackUnderflow => write!(f, "value stack underflow"),
Self::CallStackOverflow => write!(f, "call stack overflow"),
Self::CallStackUnderflow => write!(f, "call stack underflow"),
Self::InvalidStackValue(value) => write!(
f,
"stack value {value} was invalid for the current operation"
),
Self::InvalidPointIndex(index) => write!(f, "point index {index} was out of bounds"),
Self::InvalidPointRange(start, end) => {
write!(f, "point range {start}..{end} was out of bounds")
}
Self::InvalidContourIndex(index) => {
write!(f, "contour index {index} was out of bounds")
}
Self::InvalidCvtIndex(index) => write!(f, "cvt index {index} was out of bounds"),
Self::InvalidStorageIndex(index) => {
write!(f, "storage area index {index} was out of bounds")
}
Self::DivideByZero => write!(f, "attempt to divide by 0"),
Self::InvalidZoneIndex(index) => write!(
f,
"zone index {index} was invalid (only 0 or 1 are permitted)"
),
Self::NegativeLoopCounter => {
write!(f, "attempt to set the loop counter to a negative value")
}
Self::InvalidJump => write!(f, "the target of a jump instruction was invalid"),
}
}
}
211 changes: 211 additions & 0 deletions skrifa/src/outline/glyf/hint/graphics_state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
//! Graphics state for the TrueType interpreter.

mod projection;
mod round;
mod zone;

use core::ops::{Deref, DerefMut};
use read_fonts::types::Point;

pub use {
round::{RoundMode, RoundState},
zone::{Zone, ZonePointer},
};

/// Describes the axis to which a measurement or point movement operation
/// applies.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
pub enum CoordAxis {
#[default]
Both,
X,
Y,
}

/// Context in which instructions are executed.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html>
#[derive(Debug)]
pub struct GraphicsState<'a> {
/// Fields of the graphics state that persist between calls to the intepreter.
pub retained: RetainedGraphicsState,
/// A unit vector whose direction establishes an axis along which
/// distances are measured.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#projection%20vector>
pub proj_vector: Point<i32>,
/// Current axis for the projection vector.
pub proj_axis: CoordAxis,
/// A second projection vector set to a line defined by the original
/// outline location of two points. The dual projection vector is used
/// when it is necessary to measure distances from the scaled outline
/// before any instructions were executed.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#dual%20projection%20vector>
pub dual_proj_vector: Point<i32>,
/// Current axis for the dual projection vector.
pub dual_proj_axis: CoordAxis,
/// A unit vector that establishes an axis along which points can move.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#freedom%20vector>
pub freedom_vector: Point<i32>,
/// Current axis for point movement.
pub freedom_axis: CoordAxis,
/// Dot product of freedom and projection vectors.
pub fdotp: i32,
/// Determines the manner in which values are rounded.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#round%20state>
pub round_state: RoundState,
/// First reference point.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp0>
pub rp0: usize,
/// Second reference point.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp1>
pub rp1: usize,
/// Third reference point.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#rp1>
pub rp2: usize,
/// Makes it possible to repeat certain instructions a designated number of
/// times. The default value of one assures that unless the value of loop
/// is altered, these instructions will execute one time.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#loop>
pub loop_counter: u32,
/// First zone pointer.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp0>
pub zp0: ZonePointer,
/// Second zone pointer.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp1>
pub zp1: ZonePointer,
/// Third zone pointer.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#zp2>
pub zp2: ZonePointer,
/// Outline data for each zone.
///
/// This array contains the twilight and glyph zones, in that order.
pub zones: [Zone<'a>; 2],
}

impl Default for GraphicsState<'_> {
fn default() -> Self {
let vector = Point::new(0x4000, 0);
Self {
retained: RetainedGraphicsState::default(),
proj_vector: vector,
proj_axis: CoordAxis::Both,
dual_proj_vector: vector,
dual_proj_axis: CoordAxis::Both,
freedom_vector: vector,
freedom_axis: CoordAxis::Both,
fdotp: 0x4000,
round_state: RoundState::default(),
rp0: 0,
rp1: 0,
rp2: 0,
loop_counter: 1,
zones: [Zone::default(), Zone::default()],
zp0: ZonePointer::default(),
zp1: ZonePointer::default(),
zp2: ZonePointer::default(),
}
}
}

/// The persistent graphics state.
///
/// Some of the graphics state is set by the control value program and
/// persists between runs of the interpreter. This struct captures that
/// state.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html>
#[derive(Copy, Clone, Debug)]
pub struct RetainedGraphicsState {
/// Controls whether the sign of control value table entries will be
/// changed to match the sign of the actual distance measurement with
/// which it is compared.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#auto%20flip>
pub auto_flip: bool,
/// Limits the regularizing effects of control value table entries to
/// cases where the difference between the table value and the measurement
/// taken from the original outline is sufficiently small.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#control_value_cut-in>
pub control_value_cutin: i32,
/// Establishes the base value used to calculate the range of point sizes
/// to which a given DELTAC[] or DELTAP[] instruction will apply.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#delta%20base>
pub delta_base: u16,
/// Determines the range of movement and smallest magnitude of movement
/// (the step) in a DELTAC[] or DELTAP[] instruction.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#delta%20shift>
pub delta_shift: u16,
/// Makes it possible to turn off instructions under some circumstances.
/// When set to TRUE, no instructions will be executed
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#instruct%20control>
pub instruct_control: u8,
/// Establishes the smallest possible value to which a distance will be
/// rounded.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#minimum%20distance>
pub min_distance: i32,
/// Determines whether the interpreter will activate dropout control for
/// the current glyph.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#scan%20control>
pub scan_control: bool,
/// Type associated with `scan_control`.
pub scan_type: i32,
/// The distance difference below which the interpreter will replace a
/// CVT distance or an actual distance in favor of the single width value.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#single_width_cut_in>
pub single_width_cutin: i32,
/// The value used in place of the control value table distance or the
/// actual distance value when the difference between that distance and
/// the single width value is less than the single width cut-in.
///
/// See <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM04/Chap4.html#single_width_value>
pub single_width: i32,
}

impl Default for RetainedGraphicsState {
fn default() -> Self {
Self {
auto_flip: true,
control_value_cutin: 68,
delta_base: 9,
delta_shift: 3,
instruct_control: 0,
min_distance: 64,
scan_control: false,
scan_type: 0,
single_width_cutin: 0,
single_width: 0,
}
}
}

impl Deref for GraphicsState<'_> {
type Target = RetainedGraphicsState;

fn deref(&self) -> &Self::Target {
&self.retained
}
}

impl DerefMut for GraphicsState<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.retained
}
}
Loading

0 comments on commit 05721f4

Please sign in to comment.