// 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.
///|
/// Fixed point math helpers for the TrueType interpreter.
///
/// Ported from `fontations/skrifa/src/outline/glyf/hint/math.rs` (Apache-2.0 OR MIT).
///|
fn tt_hint_floor_26(x : Int) -> Int {
x & -64
}
///|
fn tt_hint_round_26(x : Int) -> Int {
tt_hint_floor_26(x + 32)
}
///|
fn tt_hint_ceil_26(x : Int) -> Int {
tt_hint_floor_26(x + 63)
}
///|
fn tt_hint_floor_pad(x : Int, n : Int) -> Int {
// `n` is always a power of two in our uses (matches fontations).
x & -n
}
///|
fn tt_hint_round_pad(x : Int, n : Int) -> Int {
tt_hint_floor_pad(x + n / 2, n)
}
///|
/// 16.16 fixed multiply with FreeType-style rounding.
fn tt_hint_mul_16_16(a : Int, b : Int) -> Int {
let ab = a.to_int64() * b.to_int64()
let adj = 0x8000 - (if ab < 0 { 1 } else { 0 })
((ab + adj.to_int64()) >> 16).to_int()
}
///|
/// 16.16 fixed division with FreeType-style rounding.
fn tt_hint_div_16_16(a0 : Int, b0 : Int) -> Int {
let mut sign = 1
let mut a = a0
let mut b = b0
if a < 0 {
a = -a
sign = -sign
}
if b < 0 {
b = -b
sign = -sign
}
let q : Int = if b == 0 {
0x7FFFFFFF
} else {
let num = (a.to_int64() << 16) + (b.to_int64() >> 1)
(num / b.to_int64()).to_int()
}
if sign < 0 {
-q
} else {
q
}
}
///|
/// 16.16 fixed multiply and divide: a * b / c.
fn tt_hint_mul_div_16_16(a : Int, b : Int, c : Int) -> Int {
// Matches font-types Fixed::mul_div() behavior.
let mut sign = 1
let mut au : UInt = a.reinterpret_as_uint()
let mut bu : UInt = b.reinterpret_as_uint()
let mut cu : UInt = c.reinterpret_as_uint()
if a < 0 {
au = (0 : UInt) - au
sign = -sign
}
if b < 0 {
bu = (0 : UInt) - bu
sign = -sign
}
if c < 0 {
cu = (0 : UInt) - cu
sign = -sign
}
let res_u : UInt64 = if cu.to_uint64() > 0 {
(au.to_uint64() * bu.to_uint64() + (cu.to_uint64() >> 1)) / cu.to_uint64()
} else {
0x7FFFFFFF
}
let res_i : Int = res_u.to_int()
if sign < 0 {
-res_i
} else {
res_i
}
}
///|
/// Fixed point multiply and divide without rounding: a * b / c.
///
/// Port of `FT_MulDiv_NoRound`.
fn tt_hint_mul_div_no_round(a0 : Int, b0 : Int, c0 : Int) -> Int {
let mut a = a0
let mut b = b0
let mut c = c0
let mut s = 1
if a < 0 {
a = -a
s = -s
}
if b < 0 {
b = -b
s = -s
}
if c < 0 {
c = -c
s = -s
}
let d : Int64 = if c > 0 {
a.to_int64() * b.to_int64() / c.to_int64()
} else {
0x7FFFFFFF
}
let out = d.to_int()
if s < 0 {
-out
} else {
out
}
}
///|
/// Multiplication for 2.14 fixed point.
fn tt_hint_mul14(a : Int, b : Int) -> Int {
let mut v = a.to_int64() * b.to_int64()
v = v + 0x2000 + (v >> 63)
(v >> 14).to_int()
}
///|
fn tt_hint_u32_leading_zeros(x0 : UInt) -> Int {
let x = x0
if x == 0 {
return 32
}
let mut n = 0
let mut bit : UInt = 0x80000000
while bit != 0 && (x & bit) == 0 {
n = n + 1
bit = bit >> 1
}
n
}
///|
/// Normalize a vector in 2.14 fixed point.
///
/// Ported from FreeType's fixed point normalize routine (via fontations).
fn tt_hint_normalize14(x0 : Int, y0 : Int) -> (Int, Int) {
// Port of fontations `math::normalize14` (FreeType fixed-point implementation).
// Returns a normalized vector in 2.14 fixed point.
let mut sx = 1
let mut sy = 1
let mut ux : UInt = x0.reinterpret_as_uint()
let mut uy : UInt = y0.reinterpret_as_uint()
if x0 < 0 {
ux = (0 : UInt) - ux
sx = -sx
}
if y0 < 0 {
uy = (0 : UInt) - uy
sy = -sy
}
if ux == 0 {
return (0, if uy > 0 { sy * 0x10000 / 4 } else { 0 })
}
if uy == 0 {
return (if ux > 0 { sx * 0x10000 / 4 } else { 0 }, 0)
}
let mut len : UInt = if ux > uy { ux + (uy >> 1) } else { uy + (ux >> 1) }
let mut shift = tt_hint_u32_leading_zeros(len)
// len is non-zero here, so shift is in 0..31.
let extra = if len >= (0xAAAAAAAA : UInt) >> shift { 1 } else { 0 }
shift = shift - 15 - extra
if shift > 0 {
ux = ux << shift
uy = uy << shift
len = if ux > uy { ux + (uy >> 1) } else { uy + (ux >> 1) }
} else {
let s = -shift
ux = ux >> s
uy = uy >> s
len = len >> s
}
let x = ux.reinterpret_as_int()
let y = uy.reinterpret_as_int()
let mut b : Int = 0x10000 - len.reinterpret_as_int()
let mut out_x = 0
let mut out_y = 0
while true {
let u : UInt = (x + ((x * b) >> 16)).reinterpret_as_uint()
let v : UInt = (y + ((y * b) >> 16)).reinterpret_as_uint()
let sum : UInt = u * u + v * v
let mut z : Int = -sum.reinterpret_as_int() / 0x200
z = z * ((0x10000 + b) >> 8) / 0x10000
b = b + z
if z <= 0 {
out_x = u.reinterpret_as_int() * sx / 4
out_y = v.reinterpret_as_int() * sy / 4
break
}
}
(out_x, out_y)
}