Implement most of the basic arithmetic and control structures in the hinting VM
This commit is contained in:
parent
6d66cfc30a
commit
a9c4c7aa3e
|
@ -10,6 +10,7 @@ euclid = "0.10"
|
||||||
flate2 = "0.2"
|
flate2 = "0.2"
|
||||||
gl = "0.6"
|
gl = "0.6"
|
||||||
memmap = "0.5"
|
memmap = "0.5"
|
||||||
|
num-traits = "0.1"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
|
||||||
[dependencies.compute-shader]
|
[dependencies.compute-shader]
|
||||||
|
|
|
@ -171,5 +171,9 @@ pub enum HintingExecutionError {
|
||||||
ParseError(HintingParseError),
|
ParseError(HintingParseError),
|
||||||
/// An instruction expected more values than were on the stack.
|
/// An instruction expected more values than were on the stack.
|
||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
|
/// An operation tried to read out of bounds of the control value table.
|
||||||
|
CvtReadOutOfBounds,
|
||||||
|
/// An undefined function ID was called.
|
||||||
|
CallToUndefinedFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
//! TrueType instructions.
|
//! TrueType instructions.
|
||||||
|
|
||||||
use error::HintingParseError;
|
use error::HintingParseError;
|
||||||
|
use euclid::Point2D;
|
||||||
|
use util::{F2DOT14_ONE, F2DOT14_ZERO, F2Dot14};
|
||||||
|
|
||||||
/// All TrueType instructions.
|
/// All TrueType instructions.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -455,6 +457,16 @@ pub enum Axis {
|
||||||
X = 1,
|
X = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Axis {
|
||||||
|
#[inline]
|
||||||
|
pub fn as_point(self) -> Point2D<F2Dot14> {
|
||||||
|
match self {
|
||||||
|
Axis::Y => Point2D::new(F2DOT14_ZERO, F2DOT14_ONE),
|
||||||
|
Axis::X => Point2D::new(F2DOT14_ONE, F2DOT14_ZERO),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum LineOrientation {
|
pub enum LineOrientation {
|
||||||
|
|
|
@ -12,8 +12,12 @@
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use error::{HintingAnalysisError, HintingExecutionError, HintingParseError};
|
use error::{HintingAnalysisError, HintingExecutionError, HintingParseError};
|
||||||
use hinting::Hinter;
|
|
||||||
use hinting::insns::Instruction;
|
use hinting::insns::Instruction;
|
||||||
|
use hinting::{FONT_SMOOTHING_GRAYSCALE, GETINFO_VERSION, Hinter};
|
||||||
|
use hinting::{INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT, InfoSelector, RoundState, VERSION};
|
||||||
|
use num_traits::Zero;
|
||||||
|
use std::cmp;
|
||||||
|
use util::{F26DOT6_ZERO, F26Dot6};
|
||||||
|
|
||||||
impl<'a> Hinter<'a> {
|
impl<'a> Hinter<'a> {
|
||||||
pub fn exec(&mut self) -> Result<(), HintingExecutionError> {
|
pub fn exec(&mut self) -> Result<(), HintingExecutionError> {
|
||||||
|
@ -63,6 +67,175 @@ impl<'a> Hinter<'a> {
|
||||||
}
|
}
|
||||||
self.storage_area[addr] = value
|
self.storage_area[addr] = value
|
||||||
}
|
}
|
||||||
|
Instruction::Rcvt => {
|
||||||
|
let addr = try!(self.pop()) as usize;
|
||||||
|
let value = *try!(self.control_value_table
|
||||||
|
.get(addr)
|
||||||
|
.ok_or(HintingExecutionError::CvtReadOutOfBounds));
|
||||||
|
self.stack.push(value.0)
|
||||||
|
}
|
||||||
|
Instruction::Svtca(axis) => {
|
||||||
|
self.projection_vector = axis.as_point();
|
||||||
|
self.freedom_vector = axis.as_point();
|
||||||
|
}
|
||||||
|
Instruction::Spvtca(axis) => self.projection_vector = axis.as_point(),
|
||||||
|
Instruction::Sfvtca(axis) => self.freedom_vector = axis.as_point(),
|
||||||
|
Instruction::Srp0 => self.reference_points[0] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Srp1 => self.reference_points[1] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Srp2 => self.reference_points[2] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Szp0 => self.zone_points[0] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Szp1 => self.zone_points[1] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Szp2 => self.zone_points[2] = try!(self.pop()) as u32,
|
||||||
|
Instruction::Szps => {
|
||||||
|
let zone = try!(self.pop()) as u32;
|
||||||
|
self.zone_points = [zone; 3]
|
||||||
|
}
|
||||||
|
Instruction::Rthg => self.round_state = RoundState::RoundToHalfGrid,
|
||||||
|
Instruction::Rtg => self.round_state = RoundState::RoundToGrid,
|
||||||
|
Instruction::Rtdg => self.round_state = RoundState::RoundToDoubleGrid,
|
||||||
|
Instruction::Rutg => self.round_state = RoundState::RoundUpToGrid,
|
||||||
|
Instruction::Roff => self.round_state = RoundState::RoundOff,
|
||||||
|
Instruction::Sround => {
|
||||||
|
// TODO(pcwalton): Super rounding.
|
||||||
|
try!(self.pop());
|
||||||
|
}
|
||||||
|
Instruction::Scanctrl | Instruction::Scantype => {
|
||||||
|
// Not applicable to antialiased glyphs.
|
||||||
|
try!(self.pop());
|
||||||
|
}
|
||||||
|
Instruction::Mppem => {
|
||||||
|
// We always scale both axes in the same direction, so we don't have to look
|
||||||
|
// at the projection vector.
|
||||||
|
self.stack.push(self.point_size.round() as i32)
|
||||||
|
}
|
||||||
|
Instruction::Dup => {
|
||||||
|
let value = *try!(self.stack
|
||||||
|
.last()
|
||||||
|
.ok_or(HintingExecutionError::StackUnderflow));
|
||||||
|
self.stack.push(value);
|
||||||
|
}
|
||||||
|
Instruction::Pop => {
|
||||||
|
try!(self.pop());
|
||||||
|
}
|
||||||
|
Instruction::Clear => self.stack.clear(),
|
||||||
|
Instruction::Swap => {
|
||||||
|
let (a, b) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push(a);
|
||||||
|
self.stack.push(b);
|
||||||
|
}
|
||||||
|
Instruction::Mindex => {
|
||||||
|
let index = try!(self.pop()) as usize;
|
||||||
|
if index >= self.stack.len() {
|
||||||
|
return Err(HintingExecutionError::StackUnderflow)
|
||||||
|
}
|
||||||
|
let rindex = self.stack.len() - 1 - index;
|
||||||
|
let value = self.stack.remove(rindex);
|
||||||
|
self.stack.push(value)
|
||||||
|
}
|
||||||
|
Instruction::If => {
|
||||||
|
let cond = try!(self.pop());
|
||||||
|
if cond == 0 {
|
||||||
|
// Move to the instruction following `else` or `eif`.
|
||||||
|
let else_target_index = self.scripts[frame.script]
|
||||||
|
.branch_targets
|
||||||
|
.binary_search_by(|script| {
|
||||||
|
script.branch_location.cmp(&frame.pc)
|
||||||
|
}).unwrap();
|
||||||
|
new_pc = self.scripts[frame.script]
|
||||||
|
.branch_targets[else_target_index]
|
||||||
|
.target_location + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::Else => {
|
||||||
|
// The only way we get here is by falling off the end of a then-branch. So jump
|
||||||
|
// to the instruction following the matching `eif`.
|
||||||
|
let eif_target_index = self.scripts[frame.script]
|
||||||
|
.branch_targets
|
||||||
|
.binary_search_by(|script| {
|
||||||
|
script.branch_location.cmp(&frame.pc)
|
||||||
|
}).unwrap();
|
||||||
|
new_pc = self.scripts[frame.script]
|
||||||
|
.branch_targets[eif_target_index]
|
||||||
|
.target_location + 1
|
||||||
|
}
|
||||||
|
Instruction::Eif => {
|
||||||
|
// Likewise, the only way we get here is by falling off the end of a
|
||||||
|
// then-branch.
|
||||||
|
}
|
||||||
|
Instruction::Lt => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs < rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Lteq => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs <= rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Gt => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs > rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Gteq => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs >= rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Eq => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs == rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Neq => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs != rhs) as i32)
|
||||||
|
}
|
||||||
|
Instruction::And => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs != 0 && rhs != 0) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Or => {
|
||||||
|
let (rhs, lhs) = (try!(self.pop()), try!(self.pop()));
|
||||||
|
self.stack.push((lhs != 0 || rhs != 0) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Not => {
|
||||||
|
let cond = try!(self.pop());
|
||||||
|
self.stack.push((cond == 0) as i32)
|
||||||
|
}
|
||||||
|
Instruction::Add => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
self.stack.push((lhs + rhs).0)
|
||||||
|
}
|
||||||
|
Instruction::Sub => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
self.stack.push((lhs - rhs).0)
|
||||||
|
}
|
||||||
|
Instruction::Div => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
if rhs.is_zero() {
|
||||||
|
// Obey Postel's law…
|
||||||
|
self.stack.push(F26DOT6_ZERO.0)
|
||||||
|
} else {
|
||||||
|
self.stack.push((lhs / rhs).0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::Mul => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
self.stack.push((lhs * rhs).0)
|
||||||
|
}
|
||||||
|
Instruction::Abs => {
|
||||||
|
// Actually in fixed point, but it works out the same way.
|
||||||
|
let n = try!(self.pop());
|
||||||
|
self.stack.push(n.abs())
|
||||||
|
}
|
||||||
|
Instruction::Neg => {
|
||||||
|
let n = F26Dot6(try!(self.pop()));
|
||||||
|
self.stack.push((-n).0)
|
||||||
|
}
|
||||||
|
Instruction::Max => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
self.stack.push(cmp::max(rhs, lhs).0)
|
||||||
|
}
|
||||||
|
Instruction::Min => {
|
||||||
|
let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop())));
|
||||||
|
self.stack.push(cmp::max(rhs, lhs).0)
|
||||||
|
}
|
||||||
Instruction::Fdef => {
|
Instruction::Fdef => {
|
||||||
// We should throw an exception here if the function definition list isn't big
|
// We should throw an exception here if the function definition list isn't big
|
||||||
// enough, but let's follow Postel's law.
|
// enough, but let's follow Postel's law.
|
||||||
|
@ -86,6 +259,37 @@ impl<'a> Hinter<'a> {
|
||||||
self.functions[id] = Some(Frame::new(new_pc, end_pc, frame.script));
|
self.functions[id] = Some(Frame::new(new_pc, end_pc, frame.script));
|
||||||
new_pc = end_pc + 1
|
new_pc = end_pc + 1
|
||||||
}
|
}
|
||||||
|
Instruction::Call => {
|
||||||
|
let id = try!(self.pop()) as usize;
|
||||||
|
let new_frame = match self.functions.get(id) {
|
||||||
|
Some(&Some(new_frame)) => new_frame,
|
||||||
|
Some(&None) | None => {
|
||||||
|
return Err(HintingExecutionError::CallToUndefinedFunction)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save our return address.
|
||||||
|
self.call_stack.last_mut().unwrap().pc = new_pc;
|
||||||
|
|
||||||
|
// Jump to the new function.
|
||||||
|
self.call_stack.push(new_frame);
|
||||||
|
new_pc = new_frame.pc
|
||||||
|
}
|
||||||
|
Instruction::Getinfo => {
|
||||||
|
let selector = InfoSelector::from_bits_truncate(try!(self.pop()));
|
||||||
|
|
||||||
|
// We only handle a subset of the selectors here.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): Handle the ones relating to subpixel AA.
|
||||||
|
let mut result = 0;
|
||||||
|
if selector.contains(VERSION) {
|
||||||
|
result |= GETINFO_VERSION
|
||||||
|
}
|
||||||
|
if selector.contains(FONT_SMOOTHING_GRAYSCALE) {
|
||||||
|
result |= 1 << INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT
|
||||||
|
}
|
||||||
|
self.stack.push(result)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("TODO: {:?}", instruction);
|
println!("TODO: {:?}", instruction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,15 @@ use error::{HinterCreationError, HintingExecutionError};
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use font::Font;
|
use font::Font;
|
||||||
use hinting::interp::{Frame, Script};
|
use hinting::interp::{Frame, Script};
|
||||||
|
use util::{F26Dot6, F2Dot14};
|
||||||
|
|
||||||
mod insns;
|
mod insns;
|
||||||
mod interp;
|
mod interp;
|
||||||
|
|
||||||
|
/// The version we return for the `getinfo` instruction. FreeType uses 35, so we do as well. (This
|
||||||
|
/// supposedly corresponds to Windows 98.)
|
||||||
|
pub const GETINFO_VERSION: i32 = 35;
|
||||||
|
|
||||||
const FONT_PROGRAM: usize = 0;
|
const FONT_PROGRAM: usize = 0;
|
||||||
const CONTROL_VALUE_PROGRAM: usize = 1;
|
const CONTROL_VALUE_PROGRAM: usize = 1;
|
||||||
|
|
||||||
|
@ -37,17 +42,17 @@ pub struct Hinter<'a> {
|
||||||
// The set of defined functions.
|
// The set of defined functions.
|
||||||
functions: Vec<Option<Frame>>,
|
functions: Vec<Option<Frame>>,
|
||||||
// The Control Value Table: the VM's initialized memory.
|
// The Control Value Table: the VM's initialized memory.
|
||||||
control_value_table: Vec<i16>,
|
control_value_table: Vec<F26Dot6>,
|
||||||
// The Storage Area: the VM's uninitialized memory.
|
// The Storage Area: the VM's uninitialized memory.
|
||||||
storage_area: Vec<i32>,
|
storage_area: Vec<i32>,
|
||||||
// The current font size.
|
// The current font size.
|
||||||
point_size: f32,
|
point_size: f32,
|
||||||
// The projection vector, in 2.14 fixed point.
|
// The projection vector, in 2.14 fixed point.
|
||||||
projection_vector: Point2D<i16>,
|
projection_vector: Point2D<F2Dot14>,
|
||||||
// The dual projection vector, in 2.14 fixed point.
|
// The dual projection vector, in 2.14 fixed point.
|
||||||
dual_projection_vector: Point2D<i16>,
|
dual_projection_vector: Point2D<F2Dot14>,
|
||||||
// The freedom vector, in 2.14 fixed point.
|
// The freedom vector, in 2.14 fixed point.
|
||||||
freedom_vector: Point2D<i16>,
|
freedom_vector: Point2D<F2Dot14>,
|
||||||
// The reference point indices.
|
// The reference point indices.
|
||||||
reference_points: [u32; 3],
|
reference_points: [u32; 3],
|
||||||
// The zone numbers.
|
// The zone numbers.
|
||||||
|
@ -92,7 +97,11 @@ impl<'a> Hinter<'a> {
|
||||||
HinterCreationError::ControlValueProgramAnalysisError)),
|
HinterCreationError::ControlValueProgramAnalysisError)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let cvt = font.control_value_table().chunks(2).map(BigEndian::read_i16).collect();
|
let cvt = font.control_value_table().chunks(2).map(|bytes| {
|
||||||
|
// FIXME(pcwalton): This is wrong!
|
||||||
|
let unscaled = BigEndian::read_i16(bytes) as i32;
|
||||||
|
F26Dot6(unscaled)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
// Initialize the call stack to the font program, so that we'll start executing it.
|
// Initialize the call stack to the font program, so that we'll start executing it.
|
||||||
let call_stack = vec![Frame::new(0, scripts[FONT_PROGRAM].len(), FONT_PROGRAM)];
|
let call_stack = vec![Frame::new(0, scripts[FONT_PROGRAM].len(), FONT_PROGRAM)];
|
||||||
|
@ -188,3 +197,25 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
// Info returned by the `getinfo` instruction.
|
||||||
|
pub flags InfoSelector: i32 {
|
||||||
|
const VERSION = 0x1,
|
||||||
|
const GLYPH_ROTATION = 0x2,
|
||||||
|
const GLYPH_STRETCHED = 0x4,
|
||||||
|
const FONT_VARIATIONS = 0x8,
|
||||||
|
const VERTICAL_PHANTOM_POINTS = 0x10,
|
||||||
|
const FONT_SMOOTHING_GRAYSCALE = 0x20,
|
||||||
|
const SUBPIXEL_AA_ENABLED = 0x40,
|
||||||
|
const SUBPIXEL_AA_COMPATIBLE_WIDTHS_ENABLED = 0x80,
|
||||||
|
const SUBPIXEL_AA_HORIZONTAL_LCD_STRIPE_ORIENTATION = 0x100,
|
||||||
|
const SUBPIXEL_AA_BGR_LCD_STRIPE_ORDER = 0x200,
|
||||||
|
const SUBPIXEL_AA_SUBPIXEL_POSITIONED_TEXT_ENABLED = 0x400,
|
||||||
|
const SUBPIXEL_AA_SYMMETRIC_RENDERING_ENABLED = 0x800,
|
||||||
|
const SUBPIXEL_AA_GRAY_RENDERING_ENABLED = 0x1000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const INFO_RESULT_VERSION_SHIFT: i32 = 0;
|
||||||
|
pub const INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT: i32 = 12;
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ extern crate flate2;
|
||||||
extern crate gl;
|
extern crate gl;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate memmap;
|
extern crate memmap;
|
||||||
|
extern crate num_traits;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quickcheck;
|
extern crate quickcheck;
|
||||||
|
|
|
@ -14,19 +14,15 @@ use euclid::Point2D;
|
||||||
use font::{FontTable, Point, PointKind};
|
use font::{FontTable, Point, PointKind};
|
||||||
use outline::GlyphBounds;
|
use outline::GlyphBounds;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Mul;
|
|
||||||
use tables::head::HeadTable;
|
use tables::head::HeadTable;
|
||||||
use tables::loca::LocaTable;
|
use tables::loca::LocaTable;
|
||||||
use util::Jump;
|
use util::{F2DOT14_ONE, F2DOT14_ZERO, F2Dot14, Jump};
|
||||||
|
|
||||||
pub const TAG: u32 = ((b'g' as u32) << 24) |
|
pub const TAG: u32 = ((b'g' as u32) << 24) |
|
||||||
((b'l' as u32) << 16) |
|
((b'l' as u32) << 16) |
|
||||||
((b'y' as u32) << 8) |
|
((b'y' as u32) << 8) |
|
||||||
(b'f' as u32);
|
(b'f' as u32);
|
||||||
|
|
||||||
const F2DOT14_ZERO: F2Dot14 = F2Dot14(0);
|
|
||||||
const F2DOT14_ONE: F2Dot14 = F2Dot14(0b0100_0000_0000_0000);
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
flags SimpleFlags: u8 {
|
flags SimpleFlags: u8 {
|
||||||
const ON_CURVE = 1 << 0,
|
const ON_CURVE = 1 << 0,
|
||||||
|
@ -438,15 +434,3 @@ impl Mat3x2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
struct F2Dot14(i16);
|
|
||||||
|
|
||||||
impl Mul<i16> for F2Dot14 {
|
|
||||||
type Output = i16;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: i16) -> i16 {
|
|
||||||
((self.0 as i32 * other as i32) >> 14) as i16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
112
src/util.rs
112
src/util.rs
|
@ -8,6 +8,118 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use num_traits::identities::Zero;
|
||||||
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
|
pub const F26DOT6_ZERO: F26Dot6 = F26Dot6(0);
|
||||||
|
|
||||||
|
pub const F2DOT14_ZERO: F2Dot14 = F2Dot14(0);
|
||||||
|
pub const F2DOT14_ONE: F2Dot14 = F2Dot14(1 << 14);
|
||||||
|
|
||||||
|
/// 26.6 fixed point.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||||
|
pub struct F26Dot6(pub i32);
|
||||||
|
|
||||||
|
impl Zero for F26Dot6 {
|
||||||
|
#[inline]
|
||||||
|
fn zero() -> F26Dot6 {
|
||||||
|
F26DOT6_ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
*self == F26DOT6_ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<F26Dot6> for F26Dot6 {
|
||||||
|
type Output = F26Dot6;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: F26Dot6) -> F26Dot6 {
|
||||||
|
F26Dot6(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<F26Dot6> for F26Dot6 {
|
||||||
|
type Output = F26Dot6;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, other: F26Dot6) -> F26Dot6 {
|
||||||
|
F26Dot6(self.0 - other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<F26Dot6> for F26Dot6 {
|
||||||
|
type Output = F26Dot6;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mul(self, other: F26Dot6) -> F26Dot6 {
|
||||||
|
F26Dot6(((self.0 as i64 * other.0 as i64 + 1 << 5) >> 6) as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<F26Dot6> for F26Dot6 {
|
||||||
|
type Output = F26Dot6;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn div(self, other: F26Dot6) -> F26Dot6 {
|
||||||
|
F26Dot6((((self.0 as i64) << 6) / other.0 as i64) as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for F26Dot6 {
|
||||||
|
type Output = F26Dot6;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn neg(self) -> F26Dot6 {
|
||||||
|
F26Dot6(-self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 2.14 fixed point.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub struct F2Dot14(pub i16);
|
||||||
|
|
||||||
|
impl Zero for F2Dot14 {
|
||||||
|
#[inline]
|
||||||
|
fn zero() -> F2Dot14 {
|
||||||
|
F2DOT14_ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
*self == F2DOT14_ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<F2Dot14> for F2Dot14 {
|
||||||
|
type Output = F2Dot14;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: F2Dot14) -> F2Dot14 {
|
||||||
|
F2Dot14(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<i16> for F2Dot14 {
|
||||||
|
type Output = i16;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mul(self, other: i16) -> i16 {
|
||||||
|
((self.0 as i32 * other as i32) >> 14) as i16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for F2Dot14 {
|
||||||
|
type Output = F2Dot14;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn neg(self) -> F2Dot14 {
|
||||||
|
F2Dot14(-self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A faster version of `Seek` that supports only forward motion from the current position.
|
/// A faster version of `Seek` that supports only forward motion from the current position.
|
||||||
pub trait Jump {
|
pub trait Jump {
|
||||||
/// Moves the pointer forward `n` bytes from the *current* position.
|
/// Moves the pointer forward `n` bytes from the *current* position.
|
||||||
|
|
Loading…
Reference in New Issue