use core::convert::TryFrom;
use crate::parser::{FromData, LazyArray16, Stream};
use crate::NormalizedCoordinate;
#[derive(Clone, Copy, Debug)]
pub struct AxisValueMap {
pub from_coordinate: i16,
pub to_coordinate: i16,
}
impl FromData for AxisValueMap {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(AxisValueMap {
from_coordinate: s.read::<i16>()?,
to_coordinate: s.read::<i16>()?,
})
}
}
#[derive(Clone, Copy)]
pub struct SegmentMaps<'a> {
count: u16,
data: &'a [u8],
}
impl<'a> SegmentMaps<'a> {
pub fn len(&self) -> u16 {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
}
impl core::fmt::Debug for SegmentMaps<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "SegmentMaps {{ ... }}")
}
}
impl<'a> IntoIterator for SegmentMaps<'a> {
type Item = LazyArray16<'a, AxisValueMap>;
type IntoIter = SegmentMapsIter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
SegmentMapsIter {
stream: Stream::new(self.data),
}
}
}
#[allow(missing_debug_implementations)]
pub struct SegmentMapsIter<'a> {
stream: Stream<'a>,
}
impl<'a> Iterator for SegmentMapsIter<'a> {
type Item = LazyArray16<'a, AxisValueMap>;
fn next(&mut self) -> Option<Self::Item> {
let count = self.stream.read::<u16>()?;
self.stream.read_array16::<AxisValueMap>(count)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Table<'a> {
pub segment_maps: SegmentMaps<'a>,
}
impl<'a> Table<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let version = s.read::<u32>()?;
if version != 0x00010000 {
return None;
}
s.skip::<u16>(); Some(Self {
segment_maps: SegmentMaps {
count: s.read::<u16>()?,
data: s.tail()?,
},
})
}
pub fn map_coordinates(&self, coordinates: &mut [NormalizedCoordinate]) -> Option<()> {
if usize::from(self.segment_maps.count) != coordinates.len() {
return None;
}
for (map, coord) in self.segment_maps.into_iter().zip(coordinates) {
*coord = NormalizedCoordinate::from(map_value(&map, coord.0)?);
}
Some(())
}
}
fn map_value(map: &LazyArray16<AxisValueMap>, value: i16) -> Option<i16> {
if map.is_empty() {
return Some(value);
} else if map.len() == 1 {
let record = map.get(0)?;
return Some(value - record.from_coordinate + record.to_coordinate);
}
let record_0 = map.get(0)?;
if value <= record_0.from_coordinate {
return Some(value - record_0.from_coordinate + record_0.to_coordinate);
}
let mut i = 1;
while i < map.len() && value > map.get(i)?.from_coordinate {
i += 1;
}
if i == map.len() {
i -= 1;
}
let record_curr = map.get(i)?;
let curr_from = record_curr.from_coordinate;
let curr_to = record_curr.to_coordinate;
if value >= curr_from {
return Some(value - curr_from + curr_to);
}
let record_prev = map.get(i - 1)?;
let prev_from = record_prev.from_coordinate;
let prev_to = record_prev.to_coordinate;
if prev_from == curr_from {
return Some(prev_to);
}
let curr_from = i32::from(curr_from);
let curr_to = i32::from(curr_to);
let prev_from = i32::from(prev_from);
let prev_to = i32::from(prev_to);
let denom = curr_from - prev_from;
let k = (curr_to - prev_to) * (i32::from(value) - prev_from) + denom / 2;
let value = prev_to + k / denom;
i16::try_from(value).ok()
}