//! The [cvar (CVT Variations)](https://learn.microsoft.com/en-us/typography/opentype/spec/cvar)
//! table.
include!("../../generated/generated_cvar.rs");
use super::variations::{
PackedPointNumbers, TupleDelta, TupleVariationCount, TupleVariationData, TupleVariationHeader,
};
/// Variation data specialized for the CVT variation table.
pub type CvtVariationData<'a> = TupleVariationData<'a, CvtDelta>;
impl<'a> Cvar<'a> {
/// Returns the variation data containing the tuples and deltas for the
/// control value table.
///
/// This table doesn't contain an axis count field so this must be provided
/// by the user and can be read from the `fvar` table.
pub fn variation_data(&self, axis_count: u16) -> Result<CvtVariationData<'a>, ReadError> {
let count = self.tuple_variation_count();
let data = self.data()?;
let header_data = self.raw_tuple_header_data();
// if there are shared point numbers, get them now
let (shared_point_numbers, serialized_data) = if count.shared_point_numbers() {
let (packed, data) = PackedPointNumbers::split_off_front(data);
(Some(packed), data)
} else {
(None, data)
};
Ok(CvtVariationData {
tuple_count: count,
axis_count,
shared_tuples: None,
shared_point_numbers,
header_data,
serialized_data,
_marker: std::marker::PhantomData,
})
}
/// Computes the accumulated deltas for the given set of normalized
/// coordinates and stores them in `deltas`.
///
/// The `axis_count` parameter expects the value from the `fvar`
/// table.
///
/// The `deltas` slice should have a length greater than or equal
/// to the number of values in the `cvt` table. The values are
/// computed in 16.16 format.
pub fn deltas(
&self,
axis_count: u16,
coords: &[F2Dot14],
deltas: &mut [i32],
) -> Result<(), ReadError> {
let var_data = self.variation_data(axis_count)?;
for (tuple, scalar) in var_data.active_tuples_at(coords) {
for delta in tuple.deltas() {
let ix = delta.position as usize;
if let Some(value) = deltas.get_mut(ix) {
*value += delta.apply_scalar(scalar).to_bits();
}
}
}
Ok(())
}
fn raw_tuple_header_data(&self) -> FontData<'a> {
let range = self.shape.tuple_variation_headers_byte_range();
self.data.split_off(range.start).unwrap()
}
}
/// Delta for an entry in the control value table.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CvtDelta {
/// The index in the CVT.
pub position: u16,
/// The delta to apply to the value in the CVT.
pub value: i32,
}
impl CvtDelta {
/// Applies a tuple scalar to this delta.
pub fn apply_scalar(self, scalar: Fixed) -> Fixed {
Fixed::from_i32(self.value) * scalar
}
}
impl TupleDelta for CvtDelta {
fn is_point() -> bool {
false
}
fn new(position: u16, x: i32, _y: i32) -> Self {
Self { position, value: x }
}
}
#[cfg(test)]
mod tests {
use font_types::F2Dot14;
use crate::{FontRef, TableProvider};
#[test]
fn scaled_deltas() {
let font = FontRef::new(font_test_data::CVAR).unwrap();
// Elements are ([coords], [deltas]) where deltas are fixed point
// values.
// These were generated by fancy printf debugging in FreeType.
let cases = &[
(
[0.5, 0.5],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 720896, 3276800, 1179648, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622592, 0, 1179648, 0, 0, 0, 0, 0, 622592,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
),
(
[-0.5, 0.5],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1441792, -2162688, -1277952, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -917504, 0, -1277952, 0, 0, 0, 0, 0,
-720896, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
],
),
(
[0.5, -0.5],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1900544, 2621440, 2129920, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360448, 0, 1015808, 0, 0, 0, 0, 0,
524288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
],
),
(
[-0.5, -0.5],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1212416, -2293760, -1130496, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1097728, 0, -1277952, 0, 0, 0, 0, 0,
-737280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
],
),
(
[-1.0, -1.0],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2490368, -4325376, -2490368, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0,
-1441792, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
],
),
(
[1.0, 1.0],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1441792, 6553600, 2359296, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1245184, 0, 2359296, 0, 0, 0, 0, 0,
1245184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
],
),
(
[-1.0, 1.0],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2883584, -4325376, -2555904, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0,
-1441792, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
],
),
(
[1.0, -1.0],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5636096, 4456448, 5636096, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 917504, 0, 1703936, 0, 0, 0, 0, 0,
917504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
],
),
];
let cvar = font.cvar().unwrap();
let axis_count = font.fvar().unwrap().axis_count();
let cvar_data = cvar.variation_data(axis_count).unwrap();
for (coords, expected_deltas) in cases {
let coords = coords.map(F2Dot14::from_f32);
let mut deltas = vec![0; expected_deltas.len()];
for (tuple, weight) in cvar_data.active_tuples_at(&coords) {
for delta in tuple.deltas() {
let scaled_delta = delta.apply_scalar(weight);
deltas[delta.position as usize] += scaled_delta.to_bits();
}
}
assert_eq!(&deltas, expected_deltas);
}
}
#[test]
fn raw_tuple_deltas() {
let font = FontRef::new(font_test_data::CVAR).unwrap();
let cvar = font.cvar().unwrap();
let axis_count = font.fvar().unwrap().axis_count();
let cvar_data = cvar.variation_data(axis_count).unwrap();
// An array of slices of (point number, delta) pairs, one for each
// tuple.
// These were taken directly from the ttx
let expected = [
&[(65, 8), (66, -8), (67, 8), (85, -11), (87, 0), (93, -1)],
&[(65, -2), (66, 8), (67, -7), (85, 11), (87, 0), (93, 1)],
&[(65, 56), (66, -24), (67, 42), (85, 6), (87, -10), (93, -4)],
&[
(65, -44),
(66, -66),
(67, -39),
(85, -28),
(87, -39),
(93, -22),
],
&[(65, 22), (66, 100), (67, 36), (85, 19), (87, 36), (93, 19)],
&[(65, 8), (66, 0), (67, 8), (85, -43), (87, -49), (93, -32)],
&[(65, -8), (66, 0), (67, -8), (85, 11), (87, 9), (93, 1)],
&[(65, -80), (66, 0), (67, -90), (85, -6), (87, -47), (93, 4)],
&[(65, -16), (66, 0), (67, -21), (85, 28), (87, 39), (93, 22)],
&[
(65, -46),
(66, 0),
(67, -22),
(85, -19),
(87, 35),
(93, -19),
],
&[(65, 2), (66, 0), (67, 7), (85, -11), (87, -9), (93, -1)],
];
let mut count = 0;
for (tuple, expected) in cvar_data.tuples().zip(&expected) {
count += 1;
let deltas = tuple
.deltas()
.map(|delta| (delta.position, delta.value))
.collect::<Vec<_>>();
assert_eq!(&deltas, expected);
}
assert_eq!(count, expected.len());
}
}