#![deny(missing_docs)]
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use tinyvec::{tiny_vec, TinyVec};
use crate::binary::read::{
DebugData, ReadArray, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFixedSizeDep, ReadFrom,
ReadScope, ReadUnchecked,
};
use crate::binary::write::{WriteBinary, WriteContext};
use crate::binary::{I16Be, I32Be, U16Be, U32Be, I8, U8};
use crate::error::{ParseError, WriteError};
use crate::tables::variable_fonts::cvar::CvarTable;
use crate::tables::variable_fonts::gvar::{GvarTable, NumPoints};
use crate::tables::{F2Dot14, Fixed};
use crate::SafeFrom;
pub mod avar;
pub mod cvar;
pub mod fvar;
pub mod gvar;
pub mod hvar;
pub mod mvar;
pub mod stat;
pub use crate::tables::variable_fonts::fvar::{OwnedTuple, Tuple};
#[derive(Debug, Clone)]
pub struct ReadTuple<'a>(ReadArray<'a, F2Dot14>);
#[derive(Debug)]
pub struct UserTuple<'a>(ReadArray<'a, Fixed>);
pub enum Gvar {}
pub enum Cvar {}
pub(crate) trait PeakTuple<'data> {
type Table;
fn peak_tuple<'a>(&'a self, table: &'a Self::Table) -> Result<ReadTuple<'data>, ParseError>;
}
pub struct TupleVariationStore<'a, T> {
num_points: u32,
shared_point_numbers: Option<PointNumbers>,
tuple_variation_headers: Vec<TupleVariationHeader<'a, T>>,
}
pub struct TupleVariationHeader<'a, T> {
variation_data_size: u16,
tuple_flags_and_index: u16,
peak_tuple: Option<ReadTuple<'a>>,
intermediate_region: Option<(ReadTuple<'a>, ReadTuple<'a>)>,
data: &'a [u8],
variant: PhantomData<T>,
}
pub struct GvarVariationData<'a> {
point_numbers: Cow<'a, PointNumbers>,
x_coord_deltas: Vec<i16>,
y_coord_deltas: Vec<i16>,
}
pub struct CvarVariationData<'a> {
point_numbers: Cow<'a, PointNumbers>,
deltas: Vec<i16>,
}
#[derive(Clone)]
enum PointNumbers {
All(u32),
Specific(Vec<u16>),
}
pub struct SharedPointNumbers<'a>(&'a PointNumbers);
#[derive(Clone)]
pub struct ItemVariationStore<'a> {
pub variation_region_list: VariationRegionList<'a>,
pub item_variation_data: Vec<ItemVariationData<'a>>,
}
#[derive(Clone)]
pub struct VariationRegionList<'a> {
pub variation_regions: ReadArray<'a, VariationRegion<'a>>,
}
#[derive(Clone)]
pub struct ItemVariationData<'a> {
item_count: u16,
word_delta_count: u16,
region_index_count: u16,
region_indexes: ReadArray<'a, U16Be>,
delta_sets: &'a [u8],
}
#[derive(Clone, Debug)]
pub struct VariationRegion<'a> {
region_axes: ReadArray<'a, RegionAxisCoordinates>,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct RegionAxisCoordinates {
start_coord: F2Dot14,
peak_coord: F2Dot14,
end_coord: F2Dot14,
}
pub struct DeltaSetIndexMap<'a> {
entry_format: u8,
map_count: u32,
map_data: &'a [u8],
}
#[derive(Debug, Copy, Clone)]
pub struct DeltaSetIndexMapEntry {
pub outer_index: u16,
pub inner_index: u16,
}
pub mod owned {
use super::{DeltaSetIndexMapEntry, DeltaSetT, Tuple};
use crate::error::ParseError;
use crate::tables::F2Dot14;
pub struct ItemVariationStore {
pub(super) variation_region_list: VariationRegionList,
pub(super) item_variation_data: Vec<ItemVariationData>,
}
pub(super) struct VariationRegionList {
pub(super) variation_regions: Vec<VariationRegion>,
}
pub(super) struct ItemVariationData {
pub(super) word_delta_count: u16,
pub(super) region_index_count: u16,
pub(super) region_indexes: Vec<u16>,
pub(super) delta_sets: Box<[u8]>,
}
pub(crate) struct VariationRegion {
pub(super) region_axes: Vec<super::RegionAxisCoordinates>,
}
impl ItemVariationStore {
pub(crate) fn adjustment(
&self,
delta_set_entry: DeltaSetIndexMapEntry,
instance: Tuple<'_>,
) -> Result<f32, ParseError> {
let item_variation_data = self
.item_variation_data
.get(usize::from(delta_set_entry.outer_index))
.ok_or(ParseError::BadIndex)?;
let delta_set = item_variation_data
.delta_set(delta_set_entry.inner_index)
.ok_or(ParseError::BadIndex)?;
let mut adjustment = 0.;
for (delta, region_index) in delta_set
.iter()
.zip(item_variation_data.region_indexes.iter().copied())
{
let region = self
.variation_region(region_index)
.ok_or(ParseError::BadIndex)?;
if let Some(scalar) = region.scalar(instance.iter().copied()) {
adjustment += scalar * delta as f32;
}
}
Ok(adjustment)
}
fn variation_region(&self, region_index: u16) -> Option<&VariationRegion> {
let region_index = usize::from(region_index);
if region_index >= self.variation_region_list.variation_regions.len() {
return None;
}
self.variation_region_list
.variation_regions
.get(region_index)
}
}
impl DeltaSetT for ItemVariationData {
fn delta_sets(&self) -> &[u8] {
self.delta_sets.as_ref()
}
fn raw_word_delta_count(&self) -> u16 {
self.word_delta_count
}
fn region_index_count(&self) -> u16 {
self.region_index_count
}
}
impl ItemVariationData {
pub fn delta_set(&self, index: u16) -> Option<super::DeltaSet<'_>> {
self.delta_set_impl(index)
}
}
impl VariationRegion {
pub(crate) fn scalar(&self, tuple: impl Iterator<Item = F2Dot14>) -> Option<f32> {
super::scalar(self.region_axes.iter().copied(), tuple)
}
}
}
impl<'a> UserTuple<'a> {
pub fn iter(&self) -> impl ExactSizeIterator<Item = Fixed> + 'a {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl<'data, T> TupleVariationStore<'data, T> {
const SHARED_POINT_NUMBERS: u16 = 0x8000;
const COUNT_MASK: u16 = 0x0FFF;
pub fn headers(&self) -> impl Iterator<Item = &TupleVariationHeader<'data, T>> {
self.tuple_variation_headers.iter()
}
pub fn shared_point_numbers(&self) -> Option<SharedPointNumbers<'_>> {
self.shared_point_numbers.as_ref().map(SharedPointNumbers)
}
}
impl<'data, T> TupleVariationStore<'data, T> {
pub(crate) fn determine_applicable<'a>(
&'a self,
table: &'a <TupleVariationHeader<'data, T> as PeakTuple<'data>>::Table,
instance: &'a OwnedTuple,
) -> impl Iterator<Item = (f32, &'a TupleVariationHeader<'data, T>)> + 'a
where
TupleVariationHeader<'data, T>: PeakTuple<'data>,
{
self.headers().filter_map(move |header| {
let peak_coords = header.peak_tuple(table).ok()?;
let (start_coords, end_coords) = match header.intermediate_region() {
Some((start, end)) => (
Coordinates::Tuple(start.clone()),
Coordinates::Tuple(end.clone()),
),
None => {
let mut start_coords = tiny_vec!();
let mut end_coords = tiny_vec!();
for peak in peak_coords.0.iter() {
match peak.raw_value().signum() {
-1 => {
start_coords.push(peak);
end_coords.push(F2Dot14::from(0));
}
0 => {
start_coords.push(peak);
end_coords.push(peak);
}
1 => {
start_coords.push(F2Dot14::from(0));
end_coords.push(peak);
}
_ => unreachable!("unknown value from signum"),
}
}
(
Coordinates::Array(start_coords),
Coordinates::Array(end_coords),
)
}
};
let scalar = start_coords
.iter()
.zip(end_coords.iter())
.zip(instance.iter().copied())
.zip(peak_coords.0.iter())
.map(|(((start, end), instance), peak)| {
calculate_scalar(instance, start, peak, end)
})
.fold(1., |scalar, axis_scalar| scalar * axis_scalar);
(scalar != 0.).then_some((scalar, header))
})
}
}
impl TupleVariationStore<'_, Gvar> {
pub fn variation_data(&self, index: u16) -> Result<GvarVariationData<'_>, ParseError> {
let header = self
.tuple_variation_headers
.get(usize::from(index))
.ok_or(ParseError::BadIndex)?;
header.variation_data(
NumPoints::from_raw(self.num_points),
self.shared_point_numbers(),
)
}
}
impl<T> ReadBinaryDep for TupleVariationStore<'_, T> {
type Args<'a> = (u16, u32, ReadScope<'a>);
type HostType<'a> = TupleVariationStore<'a, T>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
(axis_count, num_points, table_scope): (u16, u32, ReadScope<'a>),
) -> Result<Self::HostType<'a>, ParseError> {
let axis_count = usize::from(axis_count);
let tuple_variation_flags_and_count = ctxt.read_u16be()?;
let tuple_variation_count = usize::from(tuple_variation_flags_and_count & Self::COUNT_MASK);
let data_offset = ctxt.read_u16be()?;
let mut tuple_variation_headers = (0..tuple_variation_count)
.map(|_| ctxt.read_dep::<TupleVariationHeader<'_, T>>(axis_count))
.collect::<Result<Vec<_>, _>>()?;
let mut data_ctxt = table_scope.offset(usize::from(data_offset)).ctxt();
let shared_point_numbers = ((tuple_variation_flags_and_count & Self::SHARED_POINT_NUMBERS)
== Self::SHARED_POINT_NUMBERS)
.then(|| read_packed_point_numbers(&mut data_ctxt, num_points))
.transpose()?;
for header in tuple_variation_headers.iter_mut() {
header.data = data_ctxt.read_slice(header.variation_data_size.into())?;
}
Ok(TupleVariationStore {
num_points,
shared_point_numbers,
tuple_variation_headers,
})
}
}
impl PointNumbers {
const POINTS_ARE_WORDS: u8 = 0x80;
const POINT_RUN_COUNT_MASK: u8 = 0x7F;
pub fn len(&self) -> usize {
match self {
PointNumbers::All(n) => usize::safe_from(*n),
PointNumbers::Specific(vec) => vec.len(),
}
}
fn iter(&self) -> impl Iterator<Item = u32> + '_ {
(0..self.len()).map(move |index| {
match self {
PointNumbers::All(_n) => index as u32,
PointNumbers::Specific(numbers) => {
numbers.get(index).copied().map(u32::from).unwrap()
}
}
})
}
}
fn read_packed_point_numbers(
ctxt: &mut ReadCtxt<'_>,
num_points: u32,
) -> Result<PointNumbers, ParseError> {
let count = read_count(ctxt)?;
if count == 0 {
return Ok(PointNumbers::All(num_points));
}
let mut num_read = 0;
let mut point_numbers = Vec::with_capacity(usize::from(count));
while num_read < count {
let control_byte = ctxt.read_u8()?;
let point_run_count = u16::from(control_byte & PointNumbers::POINT_RUN_COUNT_MASK) + 1;
let last_point_number = point_numbers.last().copied().unwrap_or(0);
if (control_byte & PointNumbers::POINTS_ARE_WORDS) == PointNumbers::POINTS_ARE_WORDS {
let array = ctxt.read_array::<U16Be>(point_run_count.into())?;
point_numbers.extend(array.iter().scan(last_point_number, |prev, diff| {
let number = *prev + diff;
*prev = number;
Some(number)
}));
} else {
let array = ctxt.read_array::<U8>(point_run_count.into())?;
point_numbers.extend(array.iter().scan(last_point_number, |prev, diff| {
let number = *prev + u16::from(diff);
*prev = number;
Some(number)
}));
}
num_read += point_run_count;
}
Ok(PointNumbers::Specific(point_numbers))
}
fn read_count(ctxt: &mut ReadCtxt<'_>) -> Result<u16, ParseError> {
let count1 = u16::from(ctxt.read_u8()?);
let count = match count1 {
0 => 0,
1..=127 => count1,
128.. => {
let count2 = ctxt.read_u8()?;
((count1 & 0x7F) << 8) | u16::from(count2)
}
};
Ok(count)
}
mod packed_deltas {
use crate::binary::read::ReadCtxt;
use crate::binary::{I16Be, I8};
use crate::error::ParseError;
use crate::SafeFrom;
const DELTAS_ARE_ZERO: u8 = 0x80;
const DELTAS_ARE_WORDS: u8 = 0x40;
const DELTA_RUN_COUNT_MASK: u8 = 0x3F;
pub(super) fn read(ctxt: &mut ReadCtxt<'_>, num_deltas: u32) -> Result<Vec<i16>, ParseError> {
let mut deltas_read = 0;
let mut deltas = Vec::with_capacity(usize::safe_from(num_deltas));
while deltas_read < usize::safe_from(num_deltas) {
let control_byte = ctxt.read_u8()?;
let count = usize::from(control_byte & DELTA_RUN_COUNT_MASK) + 1; deltas.reserve(count);
if (control_byte & DELTAS_ARE_ZERO) == DELTAS_ARE_ZERO {
deltas.extend(std::iter::repeat_n(0, count));
} else if (control_byte & DELTAS_ARE_WORDS) == DELTAS_ARE_WORDS {
let array = ctxt.read_array::<I16Be>(count)?;
deltas.extend(array.iter())
} else {
let array = ctxt.read_array::<I8>(count)?;
deltas.extend(array.iter().map(i16::from));
};
deltas_read += count;
}
Ok(deltas)
}
}
impl GvarVariationData<'_> {
pub fn iter(&self) -> impl Iterator<Item = (u32, (i16, i16))> + '_ {
let deltas = self
.x_coord_deltas
.iter()
.copied()
.zip(self.y_coord_deltas.iter().copied());
self.point_numbers.iter().zip(deltas)
}
pub fn len(&self) -> usize {
self.point_numbers.len()
}
}
impl CvarVariationData<'_> {
pub fn iter(&self) -> impl Iterator<Item = (u32, i16)> + '_ {
self.point_numbers.iter().zip(self.deltas.iter().copied())
}
pub fn len(&self) -> usize {
self.point_numbers.len()
}
}
impl<'data> TupleVariationHeader<'data, Gvar> {
pub fn variation_data<'a>(
&'a self,
num_points: NumPoints,
shared_point_numbers: Option<SharedPointNumbers<'a>>,
) -> Result<GvarVariationData<'a>, ParseError> {
let mut ctxt = ReadScope::new(self.data).ctxt();
let point_numbers =
self.read_point_numbers(&mut ctxt, num_points.get(), shared_point_numbers)?;
let num_deltas = u32::try_from(point_numbers.len()).map_err(ParseError::from)?;
let mut x_coord_deltas = packed_deltas::read(&mut ctxt, 2 * num_deltas)?;
let y_coord_deltas = x_coord_deltas.split_off(usize::safe_from(num_deltas));
Ok(GvarVariationData {
point_numbers,
x_coord_deltas,
y_coord_deltas,
})
}
pub fn tuple_index(&self) -> Option<u16> {
self.peak_tuple
.is_none()
.then_some(self.tuple_flags_and_index & Self::TUPLE_INDEX_MASK)
}
pub fn peak_tuple<'a>(
&'a self,
gvar: &'a GvarTable<'data>,
) -> Result<ReadTuple<'data>, ParseError> {
match self.peak_tuple.as_ref() {
Some(tuple) => Ok(tuple.clone()),
None => {
let shared_index = self.tuple_flags_and_index & Self::TUPLE_INDEX_MASK;
gvar.shared_tuple(shared_index)
}
}
}
}
impl<'data> PeakTuple<'data> for TupleVariationHeader<'data, Gvar> {
type Table = GvarTable<'data>;
fn peak_tuple<'a>(&'a self, table: &'a Self::Table) -> Result<ReadTuple<'data>, ParseError> {
self.peak_tuple(table)
}
}
impl<'data> TupleVariationHeader<'data, Cvar> {
fn variation_data<'a>(
&'a self,
num_cvts: u32,
shared_point_numbers: Option<SharedPointNumbers<'a>>,
) -> Result<CvarVariationData<'a>, ParseError> {
let mut ctxt = ReadScope::new(self.data).ctxt();
let point_numbers = self.read_point_numbers(&mut ctxt, num_cvts, shared_point_numbers)?;
let num_deltas = u32::try_from(point_numbers.len()).map_err(ParseError::from)?;
let deltas = packed_deltas::read(&mut ctxt, num_deltas)?;
Ok(CvarVariationData {
point_numbers,
deltas,
})
}
pub fn peak_tuple(&self) -> Option<ReadTuple<'data>> {
self.peak_tuple.clone()
}
}
impl<'data> PeakTuple<'data> for TupleVariationHeader<'data, Cvar> {
type Table = CvarTable<'data>;
fn peak_tuple<'a>(&'a self, _table: &'a Self::Table) -> Result<ReadTuple<'data>, ParseError> {
self.peak_tuple().ok_or(ParseError::MissingValue)
}
}
impl<'data, T> TupleVariationHeader<'data, T> {
const EMBEDDED_PEAK_TUPLE: u16 = 0x8000;
const INTERMEDIATE_REGION: u16 = 0x4000;
const PRIVATE_POINT_NUMBERS: u16 = 0x2000;
const TUPLE_INDEX_MASK: u16 = 0x0FFF;
fn read_point_numbers<'a>(
&'a self,
ctxt: &mut ReadCtxt<'data>,
num_points: u32,
shared_point_numbers: Option<SharedPointNumbers<'a>>,
) -> Result<Cow<'a, PointNumbers>, ParseError> {
let private_point_numbers = if (self.tuple_flags_and_index & Self::PRIVATE_POINT_NUMBERS)
== Self::PRIVATE_POINT_NUMBERS
{
read_packed_point_numbers(ctxt, num_points).map(Some)?
} else {
None
};
private_point_numbers
.map(Cow::Owned)
.or_else(|| shared_point_numbers.map(|shared| Cow::Borrowed(shared.0)))
.ok_or(ParseError::MissingValue)
}
pub fn intermediate_region(&self) -> Option<(ReadTuple<'data>, ReadTuple<'data>)> {
self.intermediate_region.clone()
}
}
impl<T> ReadBinaryDep for TupleVariationHeader<'_, T> {
type Args<'a> = usize;
type HostType<'a> = TupleVariationHeader<'a, T>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
axis_count: usize,
) -> Result<Self::HostType<'a>, ParseError> {
let variation_data_size = ctxt.read_u16be()?;
let tuple_flags_and_index = ctxt.read_u16be()?;
let peak_tuple = ((tuple_flags_and_index & Self::EMBEDDED_PEAK_TUPLE)
== Self::EMBEDDED_PEAK_TUPLE)
.then(|| ctxt.read_array(axis_count).map(ReadTuple))
.transpose()?;
let intermediate_region =
if (tuple_flags_and_index & Self::INTERMEDIATE_REGION) == Self::INTERMEDIATE_REGION {
let start = ctxt.read_array(axis_count).map(ReadTuple)?;
let end = ctxt.read_array(axis_count).map(ReadTuple)?;
Some((start, end))
} else {
None
};
Ok(TupleVariationHeader {
variation_data_size,
tuple_flags_and_index,
peak_tuple,
intermediate_region,
data: &[], variant: PhantomData,
})
}
}
impl fmt::Debug for TupleVariationHeader<'_, Gvar> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("TupleVariationHeader");
match &self.peak_tuple {
Some(peak) => debug_struct.field("peak_tuple", peak),
None => debug_struct.field("shared_tuple_index", &self.tuple_index()),
};
debug_struct
.field("intermediate_region", &self.intermediate_region)
.finish()
}
}
impl<'a> ItemVariationStore<'a> {
pub fn adjustment(
&self,
delta_set_entry: DeltaSetIndexMapEntry,
instance: &OwnedTuple,
) -> Result<f32, ParseError> {
let item_variation_data = self
.item_variation_data
.get(usize::from(delta_set_entry.outer_index))
.ok_or(ParseError::BadIndex)?;
let delta_set = item_variation_data
.delta_set(delta_set_entry.inner_index)
.ok_or(ParseError::BadIndex)?;
let mut adjustment = 0.;
for (delta, region_index) in delta_set
.iter()
.zip(item_variation_data.region_indexes.iter())
{
let region = self
.variation_region(region_index)
.ok_or(ParseError::BadIndex)?;
if let Some(scalar) = region.scalar(instance.iter().copied()) {
adjustment += scalar * delta as f32;
}
}
Ok(adjustment)
}
pub fn regions(
&self,
index: u16,
) -> Result<impl Iterator<Item = Result<VariationRegion<'a>, ParseError>> + '_, ParseError>
{
let item_variation_data = self
.item_variation_data
.get(usize::from(index))
.ok_or(ParseError::BadIndex)?;
Ok(item_variation_data
.region_indexes
.iter()
.map(move |region_index| {
self.variation_region(region_index)
.ok_or(ParseError::BadIndex)
}))
}
fn variation_region(&self, region_index: u16) -> Option<VariationRegion<'a>> {
let region_index = usize::from(region_index);
self.variation_region_list
.variation_regions
.read_item(region_index)
.ok()
}
pub fn try_to_owned(&self) -> Result<owned::ItemVariationStore, ParseError> {
let item_variation_data = self
.item_variation_data
.iter()
.map(|data| data.to_owned())
.collect();
Ok(owned::ItemVariationStore {
variation_region_list: self.variation_region_list.try_to_owned()?,
item_variation_data,
})
}
}
impl ReadBinary for ItemVariationStore<'_> {
type HostType<'a> = ItemVariationStore<'a>;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let scope = ctxt.scope();
let format = ctxt.read_u16be()?;
ctxt.check(format == 1)?;
let variation_region_list_offset = ctxt.read_u32be()?;
let item_variation_data_count = ctxt.read_u16be()?;
let item_variation_data_offsets =
ctxt.read_array::<U32Be>(usize::from(item_variation_data_count))?;
let variation_region_list = scope
.offset(usize::safe_from(variation_region_list_offset))
.read::<VariationRegionList<'_>>()?;
let item_variation_data = item_variation_data_offsets
.iter()
.map(|offset| {
scope
.offset(usize::safe_from(offset))
.read::<ItemVariationData<'_>>()
})
.collect::<Result<Vec<_>, _>>()?;
Ok(ItemVariationStore {
variation_region_list,
item_variation_data,
})
}
}
impl VariationRegionList<'_> {
fn try_to_owned(&self) -> Result<owned::VariationRegionList, ParseError> {
let variation_regions = self
.variation_regions
.iter_res()
.map(|region| region.map(|region| region.to_owned()))
.collect::<Result<_, _>>()?;
Ok(owned::VariationRegionList { variation_regions })
}
}
impl WriteBinary<&Self> for ItemVariationStore<'_> {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, store: &Self) -> Result<Self::Output, WriteError> {
U16Be::write(ctxt, 1u16)?; let variation_region_list_offset_placeholder = ctxt.placeholder::<U16Be, _>()?;
U16Be::write(ctxt, u16::try_from(store.item_variation_data.len())?)?;
let item_variation_data_offsets_placeholders =
ctxt.placeholder_array::<U32Be, _>(store.item_variation_data.len())?;
ctxt.write_placeholder(
variation_region_list_offset_placeholder,
u16::try_from(ctxt.bytes_written())?,
)?;
VariationRegionList::write(ctxt, &store.variation_region_list)?;
for (offset_placeholder, variation_data) in item_variation_data_offsets_placeholders
.into_iter()
.zip(store.item_variation_data.iter())
{
ctxt.write_placeholder(offset_placeholder, u32::try_from(ctxt.bytes_written())?)?;
ItemVariationData::write(ctxt, variation_data)?;
}
Ok(())
}
}
impl ReadBinary for VariationRegionList<'_> {
type HostType<'a> = VariationRegionList<'a>;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let axis_count = ctxt.read_u16be()?;
let region_count = ctxt.read_u16be()?;
ctxt.check(region_count < 32768)?;
let variation_regions = ctxt.read_array_dep(usize::from(region_count), axis_count)?;
Ok(VariationRegionList { variation_regions })
}
}
impl WriteBinary<&Self> for VariationRegionList<'_> {
type Output = ();
fn write<C: WriteContext>(
ctxt: &mut C,
region_list: &Self,
) -> Result<Self::Output, WriteError> {
U16Be::write(ctxt, *region_list.variation_regions.args())?; U16Be::write(ctxt, u16::try_from(region_list.variation_regions.len())?)?; for region in region_list.variation_regions.iter_res() {
let region = region.map_err(|_| WriteError::BadValue)?;
VariationRegion::write(ctxt, ®ion)?;
}
Ok(())
}
}
pub struct DeltaSet<'a> {
long_deltas: bool,
word_data: &'a [u8],
short_data: &'a [u8],
}
impl DeltaSet<'_> {
fn iter(&self) -> impl Iterator<Item = i32> + '_ {
let (short_size, long_size) = if self.long_deltas {
(I16Be::SIZE, I32Be::SIZE)
} else {
(I8::SIZE, I16Be::SIZE)
};
let words = self.word_data.chunks(long_size).map(move |chunk| {
if self.long_deltas {
i32::from_be_bytes(chunk.try_into().unwrap())
} else {
i32::from(i16::from_be_bytes(chunk.try_into().unwrap()))
}
});
let shorts = self.short_data.chunks(short_size).map(move |chunk| {
if self.long_deltas {
i32::from(i16::from_be_bytes(chunk.try_into().unwrap()))
} else {
i32::from(chunk[0] as i8)
}
});
words.chain(shorts)
}
}
trait DeltaSetT {
const LONG_WORDS: u16 = 0x8000;
const WORD_DELTA_COUNT_MASK: u16 = 0x7FFF;
fn delta_sets(&self) -> &[u8];
fn raw_word_delta_count(&self) -> u16;
fn region_index_count(&self) -> u16;
fn delta_set_impl(&self, index: u16) -> Option<DeltaSet<'_>> {
let row_length = self.row_length();
let row_data = self
.delta_sets()
.get(usize::from(index) * row_length..)
.and_then(|offset| offset.get(..row_length))?;
let mid = self.word_delta_count() * self.word_delta_size();
if mid > row_data.len() {
return None;
}
let (word_data, short_data) = row_data.split_at(mid);
if short_data.len() % self.short_delta_size() != 0 {
return None;
}
Some(DeltaSet {
long_deltas: self.long_deltas(),
word_data,
short_data,
})
}
fn word_delta_count(&self) -> usize {
usize::from(self.raw_word_delta_count() & Self::WORD_DELTA_COUNT_MASK)
}
fn long_deltas(&self) -> bool {
self.raw_word_delta_count() & Self::LONG_WORDS != 0
}
fn row_length(&self) -> usize {
calculate_row_length(self.region_index_count(), self.raw_word_delta_count())
}
fn word_delta_size(&self) -> usize {
if self.long_deltas() {
I32Be::SIZE
} else {
I16Be::SIZE
}
}
fn short_delta_size(&self) -> usize {
if self.long_deltas() {
I16Be::SIZE
} else {
U8::SIZE
}
}
}
fn calculate_row_length(region_index_count: u16, raw_word_delta_count: u16) -> usize {
let row_length = usize::from(region_index_count)
+ usize::from(raw_word_delta_count & ItemVariationData::WORD_DELTA_COUNT_MASK);
if raw_word_delta_count & ItemVariationData::LONG_WORDS == 0 {
row_length
} else {
row_length * 2
}
}
impl DeltaSetT for ItemVariationData<'_> {
fn delta_sets(&self) -> &[u8] {
self.delta_sets
}
fn raw_word_delta_count(&self) -> u16 {
self.word_delta_count
}
fn region_index_count(&self) -> u16 {
self.region_index_count
}
}
impl ItemVariationData<'_> {
pub fn delta_set(&self, index: u16) -> Option<DeltaSet<'_>> {
self.delta_set_impl(index)
}
fn to_owned(&self) -> owned::ItemVariationData {
owned::ItemVariationData {
word_delta_count: self.word_delta_count,
region_index_count: self.region_index_count(),
region_indexes: self.region_indexes.to_vec(),
delta_sets: Box::from(self.delta_sets),
}
}
}
impl ReadBinary for ItemVariationData<'_> {
type HostType<'a> = ItemVariationData<'a>;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let item_count = ctxt.read_u16be()?;
let word_delta_count = ctxt.read_u16be()?;
let region_index_count = ctxt.read_u16be()?;
let region_indexes = ctxt.read_array::<U16Be>(usize::from(region_index_count))?;
let row_length = calculate_row_length(region_index_count, word_delta_count);
let delta_sets = ctxt.read_slice(usize::from(item_count) * row_length)?;
Ok(ItemVariationData {
item_count,
word_delta_count,
region_index_count,
region_indexes,
delta_sets,
})
}
}
impl WriteBinary<&Self> for ItemVariationData<'_> {
type Output = ();
fn write<C: WriteContext>(
ctxt: &mut C,
variation_data: &Self,
) -> Result<Self::Output, WriteError> {
U16Be::write(ctxt, variation_data.item_count)?;
U16Be::write(ctxt, variation_data.word_delta_count)?;
U16Be::write(ctxt, u16::try_from(variation_data.region_indexes.len())?)?;
ctxt.write_array(&variation_data.region_indexes)?;
ctxt.write_bytes(variation_data.delta_sets)?;
Ok(())
}
}
impl VariationRegion<'_> {
pub(crate) fn scalar(&self, tuple: impl Iterator<Item = F2Dot14>) -> Option<f32> {
scalar(self.region_axes.iter(), tuple)
}
fn to_owned(&self) -> owned::VariationRegion {
owned::VariationRegion {
region_axes: self.region_axes.to_vec(),
}
}
}
pub(crate) fn scalar(
region_axes: impl Iterator<Item = RegionAxisCoordinates>,
tuple: impl Iterator<Item = F2Dot14>,
) -> Option<f32> {
let scalar = region_axes
.zip(tuple)
.map(|(region, instance)| {
let RegionAxisCoordinates {
start_coord: start,
peak_coord: peak,
end_coord: end,
} = region;
calculate_scalar(instance, start, peak, end)
})
.fold(1., |scalar, axis_scalar| scalar * axis_scalar);
(scalar != 0.).then_some(scalar)
}
fn calculate_scalar(instance: F2Dot14, start: F2Dot14, peak: F2Dot14, end: F2Dot14) -> f32 {
if peak == F2Dot14::from(0) {
1.
} else if (start..=end).contains(&instance) {
if instance == peak {
1.
} else if instance < peak {
(f32::from(instance) - f32::from(start)) / (f32::from(peak) - f32::from(start))
} else {
(f32::from(end) - f32::from(instance)) / (f32::from(end) - f32::from(peak))
}
} else {
0.
}
}
impl ReadBinaryDep for VariationRegion<'_> {
type Args<'a> = u16;
type HostType<'a> = VariationRegion<'a>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
axis_count: u16,
) -> Result<Self::HostType<'a>, ParseError> {
let region_axes = ctxt.read_array(usize::from(axis_count))?;
Ok(VariationRegion { region_axes })
}
}
impl ReadFixedSizeDep for VariationRegion<'_> {
fn size(axis_count: u16) -> usize {
usize::from(axis_count) * RegionAxisCoordinates::SIZE
}
}
impl WriteBinary<&Self> for VariationRegion<'_> {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, region: &Self) -> Result<Self::Output, WriteError> {
ctxt.write_array(®ion.region_axes)
}
}
impl ReadFrom for RegionAxisCoordinates {
type ReadType = (F2Dot14, F2Dot14, F2Dot14);
fn read_from((start_coord, peak_coord, end_coord): (F2Dot14, F2Dot14, F2Dot14)) -> Self {
RegionAxisCoordinates {
start_coord,
peak_coord,
end_coord,
}
}
}
impl WriteBinary for RegionAxisCoordinates {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, coords: Self) -> Result<Self::Output, WriteError> {
F2Dot14::write(ctxt, coords.start_coord)?;
F2Dot14::write(ctxt, coords.peak_coord)?;
F2Dot14::write(ctxt, coords.end_coord)?;
Ok(())
}
}
impl DeltaSetIndexMap<'_> {
const INNER_INDEX_BIT_COUNT_MASK: u8 = 0x0F;
const MAP_ENTRY_SIZE_MASK: u8 = 0x30;
pub fn entry(&self, mut i: u32) -> Result<DeltaSetIndexMapEntry, ParseError> {
if i >= self.map_count {
i = self.map_count.checked_sub(1).ok_or(ParseError::BadIndex)?;
}
let entry_size = usize::from(self.entry_size());
let offset = usize::safe_from(i) * entry_size;
let entry_bytes = self
.map_data
.get(offset..(offset + entry_size))
.ok_or(ParseError::BadIndex)?;
let entry = entry_bytes
.iter()
.copied()
.fold(0u32, |entry, byte| (entry << 8) | u32::from(byte));
let outer_index =
(entry >> (u32::from(self.entry_format & Self::INNER_INDEX_BIT_COUNT_MASK) + 1)) as u16;
let inner_index = (entry
& ((1 << (u32::from(self.entry_format & Self::INNER_INDEX_BIT_COUNT_MASK) + 1)) - 1))
as u16;
Ok(DeltaSetIndexMapEntry {
outer_index,
inner_index,
})
}
pub fn len(&self) -> usize {
usize::safe_from(self.map_count)
}
fn entry_size(&self) -> u8 {
Self::entry_size_impl(self.entry_format)
}
fn entry_size_impl(entry_format: u8) -> u8 {
((entry_format & Self::MAP_ENTRY_SIZE_MASK) >> 4) + 1
}
}
impl ReadBinary for DeltaSetIndexMap<'_> {
type HostType<'a> = DeltaSetIndexMap<'a>;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let format = ctxt.read_u8()?;
let entry_format = ctxt.read_u8()?;
let map_count = match format {
0 => ctxt.read_u16be().map(u32::from)?,
1 => ctxt.read_u32be()?,
_ => return Err(ParseError::BadVersion),
};
let entry_size = DeltaSetIndexMap::entry_size_impl(entry_format);
let map_size = usize::from(entry_size) * usize::safe_from(map_count);
let map_data = ctxt.read_slice(map_size)?;
Ok(DeltaSetIndexMap {
entry_format,
map_count,
map_data,
})
}
}
impl fmt::Debug for DeltaSetIndexMap<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let DeltaSetIndexMap {
entry_format,
map_count,
map_data,
} = self;
f.debug_struct("DeltaSetIndexMap")
.field("entry_format", entry_format)
.field("map_count", map_count)
.field("map_data", &DebugData(map_data))
.finish()
}
}
enum Coordinates<'a> {
Tuple(ReadTuple<'a>),
Array(TinyVec<[F2Dot14; 4]>),
}
struct CoordinatesIter<'a, 'data> {
coords: &'a Coordinates<'data>,
index: usize,
}
impl<'data> Coordinates<'data> {
pub fn iter(&self) -> CoordinatesIter<'_, 'data> {
CoordinatesIter {
coords: self,
index: 0,
}
}
pub fn len(&self) -> usize {
match self {
Coordinates::Tuple(coords) => coords.0.len(),
Coordinates::Array(coords) => coords.len(),
}
}
}
impl Iterator for CoordinatesIter<'_, '_> {
type Item = F2Dot14;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.coords.len() {
return None;
}
let index = self.index;
self.index += 1;
match self.coords {
Coordinates::Tuple(coords) => coords.0.get_item(index),
Coordinates::Array(coords) => Some(coords[index]),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::read::ReadScope;
#[test]
fn test_read_count() {
let mut ctxt = ReadScope::new(&[0]).ctxt();
assert_eq!(read_count(&mut ctxt).unwrap(), 0);
let mut ctxt = ReadScope::new(&[0x32]).ctxt();
assert_eq!(read_count(&mut ctxt).unwrap(), 50);
let mut ctxt = ReadScope::new(&[0x81, 0x22]).ctxt();
assert_eq!(read_count(&mut ctxt).unwrap(), 290);
}
#[test]
fn test_read_packed_point_numbers() {
let data = [0x0d, 0x0c, 1, 4, 4, 2, 1, 2, 3, 3, 2, 1, 1, 3, 4];
let mut ctxt = ReadScope::new(&data).ctxt();
let expected = vec![1, 5, 9, 11, 12, 14, 17, 20, 22, 23, 24, 27, 31];
assert_eq!(
read_packed_point_numbers(&mut ctxt, expected.len() as u32)
.unwrap()
.iter()
.collect::<Vec<_>>(),
expected
);
}
#[test]
fn test_read_packed_deltas() {
let data = [
0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
];
let mut ctxt = ReadScope::new(&data).ctxt();
let expected = vec![10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228];
assert_eq!(
packed_deltas::read(&mut ctxt, expected.len() as u32).unwrap(),
expected
);
}
}