#![doc = include_str!("../../docs/glyph-storage.md")]
use super::{GlyphMap, GlyphPacking};
use crate::image::{
ColorDescription, PlaneMemoryError, PlaneMemoryLayout, RawImageAsset, Region, RegionMemoryPlan,
SampleLayout, SurfaceCopyError, SurfaceDescriptor, SurfacePlanError, SurfaceRequirements,
SurfaceView,
};
#[derive(Clone, Copy, Debug)]
pub struct RawGlyphs<'map, 'data> {
map: GlyphMap<'map>,
surface: SurfaceDescriptor,
memory: PlaneMemoryLayout,
cell_step: u64,
data: &'data [u8],
}
impl<'map, 'data> RawGlyphs<'map, 'data> {
pub const fn builder(map: GlyphMap<'map>, layout: SampleLayout) -> RawGlyphBuilder<'map> {
RawGlyphBuilder {
map,
layout,
memory: None,
}
}
pub const fn map(self) -> GlyphMap<'map> {
self.map
}
pub const fn memory_layout(self) -> PlaneMemoryLayout {
self.memory
}
pub const fn sample_layout(self) -> SampleLayout {
self.surface.sample_layout()
}
pub const fn byte_len(self) -> u32 {
self.data.len() as u32
}
pub const fn as_bytes(self) -> &'data [u8] {
self.data
}
pub fn len(self) -> usize {
self.map.len()
}
pub fn is_empty(self) -> bool {
self.map.is_empty()
}
pub fn get(self, index: usize) -> Option<GlyphRaster<'data>> {
let mapped = self.map.get(index)?;
let (offset, region) = match self.map.packing() {
GlyphPacking::GlyphMajor => (
(u64::from(self.memory.data_offset()) + index as u64 * self.cell_step) as u32,
self.surface
.region(0, 0, self.surface.width(), self.surface.height())
.expect("validated cell extent"),
),
GlyphPacking::Atlas2D => (self.memory.data_offset(), mapped),
};
let memory = PlaneMemoryLayout::builder(self.surface.plane(0).expect("scalar glyph plane"))
.with_allocation_extent(
self.memory.allocation_width(),
self.memory.allocation_height(),
)
.with_stride(self.memory.stride())
.with_data_offset(offset)
.with_alignment(self.memory.required_alignment())
.with_flags(self.memory.flags())
.build()
.expect("validated repeated allocation");
let planes = [memory.bytes(self.data).expect("validated glyph DATA range")];
let storage = RawImageAsset::new(self.surface, &planes)
.with_memory_layouts(&[memory])
.view()
.expect("validated scalar glyph storage");
Some(GlyphRaster { storage, region })
}
pub fn data_addresses_are_aligned(self) -> bool {
self.data.is_empty() || self.memory.runtime_address_is_aligned(self.data)
}
pub const fn file_address_is_aligned(self, data_offset: u32) -> bool {
data_offset.checked_add(self.byte_len()).is_some()
&& (self.data.is_empty() || self.memory.file_address_is_aligned(data_offset))
}
}
#[derive(Clone, Copy, Debug)]
pub struct RawGlyphBuilder<'map> {
map: GlyphMap<'map>,
layout: SampleLayout,
memory: Option<PlaneMemoryLayout>,
}
impl<'map> RawGlyphBuilder<'map> {
pub const fn with_memory_layout(mut self, memory: PlaneMemoryLayout) -> Self {
self.memory = Some(memory);
self
}
pub fn build<'data>(
self,
data: &'data [u8],
) -> Result<RawGlyphs<'map, 'data>, GlyphStorageError> {
if !self.layout.is_alpha() {
return Err(GlyphStorageError::UnsupportedLayout(self.layout));
}
let (width, height) = self
.map
.cell_extent()
.unwrap_or((self.map.width(), self.map.height()));
let surface = SurfaceDescriptor::new(width, height, self.layout, ColorDescription::NONE)
.expect("known scalar sample layout");
let plane = surface.plane(0).expect("scalar glyph plane");
let memory = match self.memory {
Some(memory) => {
memory
.validate_for(plane)
.map_err(GlyphStorageError::Memory)?;
memory
}
None => PlaneMemoryLayout::tight(plane).map_err(GlyphStorageError::Memory)?,
};
let alignment = u64::from(memory.required_alignment().get());
let cell_step = u64::from(memory.byte_len()).div_ceil(alignment) * alignment;
let span = match self.map.packing() {
GlyphPacking::GlyphMajor if self.map.is_empty() => 0,
GlyphPacking::GlyphMajor => {
u64::from(memory.data_offset())
+ (self.map.len() as u64 - 1) * cell_step
+ u64::from(memory.byte_len())
}
GlyphPacking::Atlas2D => u64::from(memory.data_end()),
};
let expected = u32::try_from(span).map_err(|_| GlyphStorageError::SizeOverflow)?;
if data.len() != expected as usize {
return Err(GlyphStorageError::DataSizeMismatch {
expected,
actual: data.len(),
});
}
Ok(RawGlyphs {
map: self.map,
surface,
memory,
cell_step,
data,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GlyphRaster<'a> {
storage: SurfaceView<'a>,
region: Region,
}
impl<'a> GlyphRaster<'a> {
pub const fn storage(self) -> SurfaceView<'a> {
self.storage
}
pub const fn region(self) -> Region {
self.region
}
pub fn memory_plan(
self,
requirements: SurfaceRequirements,
) -> Result<RegionMemoryPlan, SurfacePlanError> {
self.storage
.surface()
.region_plan(self.region, requirements)
}
pub fn copy_into<'output>(
self,
output: &'output mut [u8],
plan: RegionMemoryPlan,
) -> Result<SurfaceView<'output>, SurfaceCopyError> {
if plan.region() != self.region {
return Err(SurfaceCopyError::SurfaceMismatch);
}
self.storage.copy_region_samples_into(output, plan)?;
Ok(SurfaceView::from_plan(plan.memory_plan(), output, None))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum GlyphStorageError {
UnsupportedLayout(SampleLayout),
Memory(PlaneMemoryError),
SizeOverflow,
DataSizeMismatch { expected: u32, actual: usize },
}
#[cfg(test)]
mod tests;