read_fonts/tables/glyf/outline/
buffers.rs1use super::{
4 super::{Point, PointFlags},
5 scale::Scale,
6};
7use crate::mem::take_slice;
8use crate::model::Error;
9use bytemuck::{AnyBitPattern, NoUninit};
10
11pub struct Buffers<'a, S: Scale> {
20 pub points: &'a mut [Point<S::Coord>],
22 pub flags: &'a mut [PointFlags],
24 pub contours: &'a mut [u16],
26 pub unscaled: &'a mut [Point<i32>],
29 pub deltas: &'a mut [Point<S::Delta>],
31 pub iup: &'a mut [Point<S::Delta>],
33 pub composite_deltas: &'a mut [Point<S::Delta>],
35}
36
37#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
45pub struct BufferLengths {
46 pub points: u32,
49 pub contours: u32,
51 pub unscaled: u32,
53 pub deltas: u32,
55 pub iup: u32,
57 pub composite_deltas: u32,
59}
60
61impl BufferLengths {
62 pub fn packed_len<S: Scale>(&self) -> usize {
68 let coord = size_of::<Point<S::Coord>>();
69 let delta = size_of::<Point<S::Delta>>();
70 let size = self.points as usize * (coord + size_of::<PointFlags>())
71 + self.contours as usize * size_of::<u16>()
72 + self.unscaled as usize * size_of::<Point<i32>>()
73 + (self.deltas + self.iup + self.composite_deltas) as usize * delta;
74 if size == 0 {
75 return 0;
76 }
77 size + align_of::<Point<S::Coord>>()
80 .max(align_of::<Point<S::Delta>>())
81 .max(align_of::<Point<i32>>())
82 }
83}
84
85impl<'a, S: Scale> Buffers<'a, S> {
86 pub fn from_bytes(bytes: &'a mut [u8], lengths: &BufferLengths) -> Result<Self, Error> {
117 let (points, bytes) = take(bytes, lengths.points)?;
119 let (unscaled, bytes) = take(bytes, lengths.unscaled)?;
120 let (deltas, bytes) = take(bytes, lengths.deltas)?;
121 let (iup, bytes) = take(bytes, lengths.iup)?;
122 let (composite_deltas, bytes) = take(bytes, lengths.composite_deltas)?;
123 let (contours, bytes) = take(bytes, lengths.contours)?;
124 let (flags, _) = take(bytes, lengths.points)?;
125 Ok(Self {
126 points,
127 flags,
128 contours,
129 unscaled,
130 deltas,
131 iup,
132 composite_deltas,
133 })
134 }
135}
136
137fn take<T>(bytes: &mut [u8], len: u32) -> Result<(&mut [T], &mut [u8]), Error>
140where
141 T: AnyBitPattern + NoUninit,
142{
143 take_slice(bytes, len as usize).ok_or(Error::InsufficientMemory)
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::{
150 tables::glyf::outline::{
151 Outline, OutlinePlan, OutlineTables, Scale26Dot6, ScaleF32, Unscaled,
152 },
153 types::{F26Dot6, F2Dot14, GlyphId},
154 FontRef, TableProvider,
155 };
156 use alloc::vec;
157
158 #[test]
161 fn packed_buffers_load_the_same_outline() {
162 fn check<S: Scale>(scale: &S)
163 where
164 S::Coord: core::fmt::Debug,
165 {
166 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
167 let tables = OutlineTables::new(&font).unwrap();
168 for gid in 0..font.maxp().unwrap().num_glyphs() {
169 let gid = GlyphId::from(gid);
170 let plan = OutlinePlan::new(&tables, gid).unwrap();
171 let lengths = plan.buffer_lengths::<S>();
172
173 let mut owned = Outline::<S>::new();
174 let expected = owned.load_with(&tables, gid, scale, None).unwrap();
175 let expected = (expected.points().to_vec(), expected.contours().to_vec());
176
177 let mut block = vec![0u8; lengths.packed_len::<S>()];
178 let buffers = Buffers::from_bytes(&mut block, &lengths).unwrap();
179 let actual = plan.load(&tables, scale, buffers, None).unwrap();
180 assert_eq!(actual.points(), expected.0, "gid {gid}");
181 assert_eq!(actual.contours(), expected.1, "gid {gid}");
182 }
183 }
184 check(&Scale26Dot6::new(Some(16.0), 1000));
185 check(&ScaleF32::new(Some(16.0), 1000));
186 check(&Unscaled);
187 }
188
189 #[test]
191 fn the_reported_size_is_what_it_takes() {
192 let font = FontRef::new(font_test_data::GLYF_COMPONENTS).unwrap();
193 let tables = OutlineTables::new(&font).unwrap();
194 let plan = OutlinePlan::new(&tables, GlyphId::new(5)).unwrap();
195 let lengths = plan.buffer_lengths::<Scale26Dot6>();
196 let size = lengths.packed_len::<Scale26Dot6>();
197 assert!(size > 0);
198
199 let mut block = vec![0u8; size];
200 assert!(Buffers::<Scale26Dot6>::from_bytes(&mut block, &lengths).is_ok());
201
202 let mut short = vec![0u8; size - core::mem::align_of::<Point<F26Dot6>>() - 1];
205 assert_eq!(
206 Buffers::<Scale26Dot6>::from_bytes(&mut short, &lengths).err(),
207 Some(Error::InsufficientMemory)
208 );
209 }
210
211 #[test]
213 fn an_unaligned_block_is_fine() {
214 let font = FontRef::new(font_test_data::GLYF_COMPONENTS).unwrap();
215 let tables = OutlineTables::new(&font).unwrap();
216 let plan = OutlinePlan::new(&tables, GlyphId::new(5)).unwrap();
217 let lengths = plan.buffer_lengths::<Scale26Dot6>();
218 let scale = Scale26Dot6::new(Some(16.0), 1000);
219
220 let aligned = {
221 let mut block = vec![0u8; lengths.packed_len::<Scale26Dot6>()];
222 let buffers = Buffers::from_bytes(&mut block, &lengths).unwrap();
223 plan.load(&tables, &scale, buffers, None)
224 .unwrap()
225 .points()
226 .to_vec()
227 };
228 for offset in 1..8 {
229 let mut block = vec![0u8; lengths.packed_len::<Scale26Dot6>() + offset];
230 let buffers = Buffers::from_bytes(&mut block[offset..], &lengths).unwrap();
231 let outline = plan.load(&tables, &scale, buffers, None).unwrap();
232 assert_eq!(outline.points(), aligned, "offset {offset}");
233 }
234 }
235
236 #[test]
239 fn every_buffer_is_sized_and_aligned() {
240 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
241 let coords = [F2Dot14::from_f32(1.0)];
243 let tables = OutlineTables::new(&font).unwrap().at(&coords, &[]);
244 let plan = OutlinePlan::new(&tables, GlyphId::new(2)).unwrap();
245 assert!(plan.applies_variations);
246 let lengths = plan.buffer_lengths::<Scale26Dot6>();
247 let mut block = vec![0u8; lengths.packed_len::<Scale26Dot6>()];
248 let buffers = Buffers::<Scale26Dot6>::from_bytes(&mut block, &lengths).unwrap();
249 assert_eq!(buffers.points.len(), lengths.points as usize);
250 assert_eq!(buffers.flags.len(), lengths.points as usize);
251 assert_eq!(buffers.contours.len(), lengths.contours as usize);
252 assert_eq!(buffers.unscaled.len(), lengths.unscaled as usize);
253 assert_eq!(buffers.deltas.len(), lengths.deltas as usize);
254 assert_eq!(buffers.iup.len(), lengths.iup as usize);
255 assert_eq!(
256 buffers.composite_deltas.len(),
257 lengths.composite_deltas as usize
258 );
259 assert!(lengths.deltas > 0, "expected a varying glyph");
260 assert_eq!(buffers.points.as_ptr().align_offset(4), 0);
263 assert_eq!(buffers.contours.as_ptr().align_offset(2), 0);
264 }
265
266 #[test]
268 fn an_empty_outline_needs_no_bytes() {
269 let lengths = BufferLengths::default();
270 assert_eq!(lengths.packed_len::<Scale26Dot6>(), 0);
271 let buffers = Buffers::<Scale26Dot6>::from_bytes(&mut [], &lengths).unwrap();
272 assert!(buffers.points.is_empty());
273 assert!(buffers.flags.is_empty());
274 }
275}