1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
// Copyright 2006 The Android Open Source Project
// Copyright 2020 Yevhenii Reizner
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Skia uses fixed points pretty chaotically, therefore we cannot use
// strongly typed wrappers. Which is unfortunate.
use tiny_skia_path::SaturateCast;
use crate::math::{bound, left_shift, left_shift64};
/// A 26.6 fixed point.
pub type FDot6 = i32;
/// A 24.8 fixed point.
pub type FDot8 = i32;
/// A 16.16 fixed point.
pub type FDot16 = i32;
pub mod fdot6 {
use super::*;
use core::convert::TryFrom;
pub const ONE: FDot6 = 64;
pub fn from_i32(n: i32) -> FDot6 {
debug_assert!(n as i16 as i32 == n);
n << 6
}
pub fn from_f32(n: f32) -> FDot6 {
(n * 64.0) as i32
}
pub fn floor(n: FDot6) -> FDot6 {
n >> 6
}
pub fn ceil(n: FDot6) -> FDot6 {
(n + 63) >> 6
}
pub fn round(n: FDot6) -> FDot6 {
(n + 32) >> 6
}
pub fn to_fdot16(n: FDot6) -> FDot16 {
debug_assert!((left_shift(n, 10) >> 10) == n);
left_shift(n, 10)
}
pub fn div(a: FDot6, b: FDot6) -> FDot16 {
debug_assert_ne!(b, 0);
if i16::try_from(a).is_ok() {
left_shift(a, 16) / b
} else {
fdot16::div(a, b)
}
}
pub fn can_convert_to_fdot16(n: FDot6) -> bool {
let max_dot6 = i32::MAX >> (16 - 6);
n.abs() <= max_dot6
}
pub fn small_scale(value: u8, dot6: FDot6) -> u8 {
debug_assert!(dot6 as u32 <= 64);
((value as i32 * dot6) >> 6) as u8
}
}
pub mod fdot8 {
use super::*;
// Extracted from SkScan_Antihair.cpp
pub fn from_fdot16(x: FDot16) -> FDot8 {
(x + 0x80) >> 8
}
}
pub mod fdot16 {
use super::*;
pub const HALF: FDot16 = (1 << 16) / 2;
pub const ONE: FDot16 = 1 << 16;
// `from_f32` seems to lack a rounding step. For all fixed-point
// values, this version is as accurate as possible for (fixed -> float -> fixed). Rounding reduces
// accuracy if the intermediate floats are in the range that only holds integers (adding 0.5 to an
// odd integer then snaps to nearest even). Using double for the rounding math gives maximum
// accuracy for (float -> fixed -> float), but that's usually overkill.
pub fn from_f32(x: f32) -> FDot16 {
i32::saturate_from(x * ONE as f32)
}
pub fn floor_to_i32(x: FDot16) -> i32 {
x >> 16
}
pub fn ceil_to_i32(x: FDot16) -> i32 {
(x + ONE - 1) >> 16
}
pub fn round_to_i32(x: FDot16) -> i32 {
(x + HALF) >> 16
}
// The divide may exceed 32 bits. Clamp to a signed 32 bit result.
pub fn mul(a: FDot16, b: FDot16) -> FDot16 {
((i64::from(a) * i64::from(b)) >> 16) as FDot16
}
// The divide may exceed 32 bits. Clamp to a signed 32 bit result.
pub fn div(numer: FDot6, denom: FDot6) -> FDot16 {
let v = left_shift64(numer as i64, 16) / denom as i64;
let n = bound(i32::MIN as i64, v, i32::MAX as i64);
n as i32
}
pub fn fast_div(a: FDot6, b: FDot6) -> FDot16 {
debug_assert!((left_shift(a, 16) >> 16) == a);
debug_assert!(b != 0);
left_shift(a, 16) / b
}
}