#![doc = include_str!("../../docs/font-representations.md")]
use super::{FontRepresentation, FontRepresentationError, FontRepresentationKind};
use crate::image::{SampleLayout, SurfaceDescriptor, SurfacePlanError, SurfaceRequirements};
use crate::wire::{read_u16_le, read_u32_le, write_u16_le, write_u32_le};
pub(in crate::font) const REPRESENTATION_RECORD_LEN: usize = 20;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RepresentationRecord {
metadata: FontRepresentation,
surface_index: u16,
atlas_map_offset: u32,
atlas_map_count: u32,
}
impl RepresentationRecord {
pub(in crate::font) const fn new(metadata: FontRepresentation, surface_index: u16) -> Self {
Self {
metadata,
surface_index,
atlas_map_offset: 0,
atlas_map_count: 0,
}
}
pub(in crate::font) const fn with_atlas_map_range(mut self, offset: u32, count: u32) -> Self {
self.atlas_map_offset = offset;
self.atlas_map_count = count;
self
}
pub const fn representation(self) -> FontRepresentation {
self.metadata
}
pub const fn surface_index(self) -> u16 {
self.surface_index
}
pub const fn atlas_map_offset(self) -> u32 {
self.atlas_map_offset
}
pub const fn atlas_map_count(self) -> u32 {
self.atlas_map_count
}
pub(in crate::font) fn from_record(
bytes: &[u8],
surface: SurfaceDescriptor,
) -> Result<Self, RepresentationRecordError> {
if bytes.len() < REPRESENTATION_RECORD_LEN {
return Err(RepresentationRecordError::Truncated {
needed: REPRESENTATION_RECORD_LEN,
available: bytes.len(),
});
}
if bytes[1] != 0 {
return Err(RepresentationRecordError::ReservedNonZero { offset: 1 });
}
let metadata = Fields {
class: bytes[0],
design_ppem: read_u16_le(bytes, 2).unwrap(),
min_ppem: read_u16_le(bytes, 4).unwrap(),
max_ppem: read_u16_le(bytes, 6).unwrap(),
detail: read_u16_le(bytes, 8).unwrap(),
}
.resolve(surface)?;
let record = Self {
metadata,
surface_index: read_u16_le(bytes, 10).unwrap(),
atlas_map_offset: read_u32_le(bytes, 12).unwrap(),
atlas_map_count: read_u32_le(bytes, 16).unwrap(),
};
record.validate_atlas_map_range()?;
Ok(record)
}
pub(in crate::font) fn validate_for(
self,
surface: SurfaceDescriptor,
) -> Result<(), RepresentationRecordError> {
let resolved = Fields::from_metadata(self.metadata).resolve(surface)?;
match (self.metadata.kind(), resolved.kind()) {
(
FontRepresentationKind::Coverage { bits: actual },
FontRepresentationKind::Coverage { bits: expected },
)
| (
FontRepresentationKind::SignedDistance { bits: actual, .. },
FontRepresentationKind::SignedDistance { bits: expected, .. },
) if actual != expected => {
return Err(RepresentationRecordError::SampleDepthMismatch { expected, actual });
}
_ => {}
}
if self.metadata.decoded_bytes() != resolved.decoded_bytes() {
return Err(RepresentationRecordError::DecodedSizeMismatch {
expected: resolved.decoded_bytes(),
actual: self.metadata.decoded_bytes(),
});
}
Ok(())
}
pub(in crate::font) fn encode_record(
self,
) -> Result<[u8; REPRESENTATION_RECORD_LEN], RepresentationRecordError> {
self.validate_atlas_map_range()?;
let fields = Fields::from_metadata(self.metadata);
let mut record = [0; REPRESENTATION_RECORD_LEN];
record[0] = fields.class;
write_u16_le(&mut record, 2, fields.design_ppem);
write_u16_le(&mut record, 4, fields.min_ppem);
write_u16_le(&mut record, 6, fields.max_ppem);
write_u16_le(&mut record, 8, fields.detail);
write_u16_le(&mut record, 10, self.surface_index);
write_u32_le(&mut record, 12, self.atlas_map_offset);
write_u32_le(&mut record, 16, self.atlas_map_count);
Ok(record)
}
fn validate_atlas_map_range(self) -> Result<(), RepresentationRecordError> {
if self.atlas_map_count == 0 {
return if self.atlas_map_offset == 0 {
Ok(())
} else {
Err(RepresentationRecordError::EmptyAtlasMapRange {
offset: self.atlas_map_offset,
})
};
}
self.atlas_map_offset
.checked_add(self.atlas_map_count)
.ok_or(RepresentationRecordError::AtlasMapRangeOverflow)?;
Ok(())
}
}
#[derive(Clone, Copy)]
struct Fields {
class: u8,
design_ppem: u16,
min_ppem: u16,
max_ppem: u16,
detail: u16,
}
impl Fields {
const fn from_metadata(metadata: FontRepresentation) -> Self {
let (class, min_ppem, max_ppem, detail) = match metadata.kind() {
FontRepresentationKind::Coverage { .. } => (0, 0, 0, 0),
FontRepresentationKind::SignedDistance { spread, .. } => {
(1, metadata.min_ppem(), metadata.max_ppem(), spread)
}
FontRepresentationKind::Application(kind) => {
(2, metadata.min_ppem(), metadata.max_ppem(), kind)
}
};
Self {
class,
design_ppem: metadata.design_ppem(),
min_ppem,
max_ppem,
detail,
}
}
fn resolve(
self,
surface: SurfaceDescriptor,
) -> Result<FontRepresentation, RepresentationRecordError> {
if self.class > 2 {
return Err(RepresentationRecordError::UnknownClass(self.class));
}
if self.class == 0 && (self.min_ppem != 0 || self.max_ppem != 0 || self.detail != 0) {
return Err(RepresentationRecordError::NonCanonicalCoverage);
}
let layout = surface.sample_layout();
let bits = if self.class < 2 {
if !layout.is_alpha() {
return Err(RepresentationRecordError::UnsupportedLayout(layout));
}
surface.plane(0).expect("scalar plane").bits_per_element()
} else {
0
};
let decoded_bytes = surface
.memory_plan(SurfaceRequirements::new())
.map_err(RepresentationRecordError::Memory)?
.byte_len();
match self.class {
0 => FontRepresentation::coverage(bits, self.design_ppem, decoded_bytes),
1 => FontRepresentation::signed_distance(
bits,
self.detail,
self.design_ppem,
self.min_ppem,
self.max_ppem,
decoded_bytes,
),
2 => FontRepresentation::application(
self.detail,
self.design_ppem,
self.min_ppem,
self.max_ppem,
decoded_bytes,
),
_ => unreachable!("validated representation class"),
}
.map_err(RepresentationRecordError::Representation)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum RepresentationRecordError {
Truncated { needed: usize, available: usize },
ReservedNonZero { offset: usize },
EmptyAtlasMapRange { offset: u32 },
AtlasMapRangeOverflow,
UnknownClass(u8),
NonCanonicalCoverage,
UnsupportedLayout(SampleLayout),
Memory(SurfacePlanError),
Representation(FontRepresentationError),
SampleDepthMismatch { expected: u8, actual: u8 },
DecodedSizeMismatch { expected: u32, actual: u32 },
}
#[cfg(test)]
mod tests;