1use super::{
16 glyf::Precision, glyphs::OutlineGlyphs, DrawOptions, DrawnMetrics, PathSink, TransformSink,
17};
18use crate::model::Error;
19use crate::{
20 limits::{MAX_COMPOSITE_EDGES, MAX_RECURSION_DEPTH},
21 mem::take_slice,
22 tables::{
23 varc::{
24 Condition, CoverageTable, DecomposedTransform, MultiItemVariationStore,
25 SparseVariationRegionList, Varc, VarcComponent, VarcFlags,
26 },
27 variations::NO_VARIATION_INDEX,
28 },
29 types::{F2Dot14, GlyphId, Matrix},
30 ReadError,
31};
32use core::{
33 mem::{align_of, size_of},
34 sync::atomic::{AtomicU32, AtomicUsize, Ordering},
35};
36use yoke::Yokeable;
37
38#[cfg(feature = "libm")]
39#[allow(unused_imports)]
40use core_maths::CoreFloat;
41
42pub(crate) trait VarcContext {
47 fn coords(&self) -> &[F2Dot14];
51
52 fn draw_glyph(
58 &self,
59 glyph: GlyphId,
60 coords: &[F2Dot14],
61 ppem: Option<f32>,
62 buf: &mut [u8],
63 sink: &mut dyn PathSink,
64 ) -> Result<(), Error>;
65
66 fn glyph_buffer_size(&self, glyph: GlyphId) -> usize;
69}
70
71#[derive(Debug)]
77pub(crate) struct Outline {
78 glyph: GlyphId,
79 coverage_index: u32,
80 measured: Measured,
81}
82
83impl Outline {
84 pub(crate) fn glyph(&self) -> GlyphId {
86 self.glyph
87 }
88}
89
90impl Clone for Outline {
91 fn clone(&self) -> Self {
92 Self {
93 glyph: self.glyph,
94 coverage_index: self.coverage_index,
95 measured: self.measured.clone(),
96 }
97 }
98}
99
100#[derive(Debug, Default)]
105struct Measured {
106 state: AtomicU32,
109 leaf: AtomicUsize,
111}
112
113impl Measured {
114 const UNMEASURED: u32 = 0;
115 const MEASURED: u32 = 1;
116 const MALFORMED: u32 = 2;
117 const OVER_LIMIT: u32 = 3;
118 const STATE_BITS: u32 = 2;
119}
120
121impl Clone for Measured {
122 fn clone(&self) -> Self {
123 Self {
124 state: AtomicU32::new(self.state.load(Ordering::Acquire)),
125 leaf: AtomicUsize::new(self.leaf.load(Ordering::Relaxed)),
126 }
127 }
128}
129
130#[derive(Clone, Yokeable)]
132pub(crate) struct Outlines<'a> {
133 varc: Varc<'a>,
134 coverage: CoverageTable<'a>,
135 var_store: Option<MultiItemVariationStore<'a>>,
136 regions: Option<SparseVariationRegionList<'a>>,
137 units_per_em: u16,
138 axis_count: usize,
139 index_count: usize,
144}
145
146impl<'a> Outlines<'a> {
147 pub(crate) fn new(
153 varc: Varc<'a>,
154 units_per_em: u16,
155 axis_count: usize,
156 ) -> Result<Self, ReadError> {
157 let coverage = varc.coverage()?;
158 let var_store = varc.multi_var_store().transpose()?;
159 let regions = var_store
160 .as_ref()
161 .map(|store| store.region_list())
162 .transpose()?;
163 let list_count = varc
164 .axis_indices_list()
165 .transpose()?
166 .map_or(0, |lists| lists.count() as usize);
167 let index_count = (0..list_count)
168 .filter_map(|nth| varc.axis_indices(nth).ok())
169 .map(|list| list.count_or_compute())
170 .max()
171 .unwrap_or(0);
172 Ok(Self {
173 varc,
174 coverage,
175 var_store,
176 regions,
177 units_per_em,
178 axis_count,
179 index_count,
180 })
181 }
182
183 pub(crate) fn covers(&self, glyph: GlyphId) -> bool {
185 self.coverage.get(glyph).is_some()
186 }
187
188 pub(crate) fn outline(&self, glyph: GlyphId) -> Option<Outline> {
193 let coverage_index = self.coverage.get(glyph)?;
194 Some(Outline {
195 glyph,
196 coverage_index,
197 measured: Measured::default(),
198 })
199 }
200
201 fn layout_of(&self, outline: &Outline, context: &dyn VarcContext) -> Result<Layout, Error> {
208 let measured = &outline.measured;
209 let mut state = measured.state.load(Ordering::Acquire);
210 if state == Measured::UNMEASURED {
211 let mut walk = Walk::new();
212 let result = self.measure(outline.glyph, outline.coverage_index, context, &mut walk);
213 let (code, leaf) = match result {
214 Err(_) => (Measured::MALFORMED, 0),
215 Ok(_) if walk.truncated => (Measured::OVER_LIMIT, 0),
216 Ok(leaf) => (Measured::MEASURED, leaf),
217 };
218 state = code | (walk.depth as u32) << Measured::STATE_BITS;
219 measured.leaf.store(leaf, Ordering::Relaxed);
220 measured.state.store(state, Ordering::Release);
221 }
222 match state & ((1 << Measured::STATE_BITS) - 1) {
223 Measured::MEASURED => {
224 let depth = (state >> Measured::STATE_BITS) as usize;
225 Ok(self.layout(depth, measured.leaf.load(Ordering::Relaxed)))
226 }
227 Measured::OVER_LIMIT => Err(Error::LimitExceeded),
228 _ => Err(Error::Malformed),
229 }
230 }
231
232 pub(crate) fn required_memory_size(
235 &self,
236 outline: &Outline,
237 context: &dyn VarcContext,
238 ) -> usize {
239 self.layout_of(outline, context)
240 .ok()
241 .and_then(|layout| layout.packed_len())
242 .unwrap_or(0)
243 }
244
245 fn layout(&self, depth: usize, leaf: usize) -> Layout {
250 Layout {
251 depth: depth.max(1),
252 axis_count: self.axis_count,
253 index_count: self.index_count,
254 region_count: self.region_count(),
255 leaf,
256 }
257 }
258
259 fn region_count(&self) -> usize {
262 self.regions
263 .as_ref()
264 .map_or(0, |regions| regions.region_count() as usize)
265 }
266
267 fn measure(
273 &self,
274 glyph: GlyphId,
275 coverage_index: u32,
276 context: &dyn VarcContext,
277 walk: &mut Walk,
278 ) -> Result<usize, ReadError> {
279 if walk.stack.len() >= MAX_RECURSION_DEPTH || walk.edges_left == 0 {
283 walk.truncated = true;
284 return Ok(0);
285 }
286 walk.edges_left -= 1;
287 walk.stack.push(glyph);
288 walk.depth = walk.depth.max(walk.stack.len());
289 let mut leaf = 0usize;
290 let record = self.varc.glyph(coverage_index as usize)?;
291 for component in record.components() {
292 let component_gid = component?.gid();
293 let covered = self
297 .coverage
298 .get(component_gid)
299 .filter(|_| component_gid != glyph);
300 let component_leaf = match covered {
301 Some(coverage_index) if !walk.stack.contains(component_gid) => {
302 self.measure(component_gid, coverage_index, context, walk)?
303 }
304 _ if walk.edges_left == 0 => {
306 walk.truncated = true;
307 break;
308 }
309 Some(_) => {
310 walk.edges_left -= 1;
311 0
312 }
313 None => {
314 walk.edges_left -= 1;
315 context.glyph_buffer_size(component_gid)
316 }
317 };
318 leaf = leaf.max(component_leaf);
319 }
320 walk.stack.pop();
321 Ok(leaf)
322 }
323
324 pub(crate) fn draw(
331 &self,
332 outline: &Outline,
333 context: &dyn VarcContext,
334 ppem: Option<f32>,
335 buf: &mut [u8],
336 sink: &mut dyn PathSink,
337 ) -> Result<(), Error> {
338 let layout = self.layout_of(outline, context)?;
339 let memory = layout.carve(buf).ok_or(Error::InsufficientMemory)?;
340 self.draw_tree(outline, &layout, context, ppem, memory, sink)
341 }
342
343 fn draw_tree(
346 &self,
347 outline: &Outline,
348 layout: &Layout,
349 context: &dyn VarcContext,
350 ppem: Option<f32>,
351 memory: Memory<'_>,
352 sink: &mut dyn PathSink,
353 ) -> Result<(), Error> {
354 let Memory {
355 font_coords,
356 coords,
357 scalars,
358 leaf,
359 mut scratch,
360 } = memory;
361 expand_coords(font_coords, context.coords());
362 let (root_scalars, level_scalars) =
363 split(scalars, layout.region_count).ok_or(Error::InsufficientMemory)?;
364 let mut scalar_cache = ScalarCache::new(root_scalars);
365 let levels = Levels {
366 coords,
367 scalars: level_scalars,
368 axis_count: layout.axis_count,
369 region_count: layout.region_count,
370 };
371 let draw = DrawContext {
372 font_coords,
373 scale: linear_scale(ppem, self.units_per_em),
374 ppem,
375 context,
376 store_regions: self.var_store.as_ref().zip(self.regions.as_ref()),
377 };
378 let mut stack = GlyphStack::new();
379 self.draw_glyph(
380 outline.glyph,
381 outline.coverage_index,
382 draw.font_coords,
383 Matrix::IDENTITY,
384 &draw,
385 &mut scalar_cache,
386 levels,
387 leaf,
388 sink,
389 &mut stack,
390 &mut scratch,
391 )
392 }
393
394 #[allow(clippy::too_many_arguments)]
395 fn draw_glyph(
396 &self,
397 glyph: GlyphId,
398 coverage_index: u32,
399 current_coords: &[F2Dot14],
400 parent_matrix: Matrix<f32>,
401 draw: &DrawContext<'a, '_>,
402 scalar_cache: &mut ScalarCache<'_>,
403 mut levels: Levels<'_>,
404 leaf: &mut [u8],
405 sink: &mut dyn PathSink,
406 stack: &mut GlyphStack,
407 scratch: &mut Scratch<'_>,
408 ) -> Result<(), Error> {
409 if stack.len() >= MAX_RECURSION_DEPTH || scratch.edges_left == 0 {
413 return Err(Error::LimitExceeded);
414 }
415 scratch.edges_left -= 1;
416 let record = self.varc.glyph(coverage_index as usize)?;
417 let mut level = levels.next().ok_or(Error::InsufficientMemory)?;
420 stack.push(glyph);
421 let store_regions = draw.store_regions;
422 for component in record.components() {
423 let component = component?;
424 if !self.component_condition_met(
425 &component,
426 current_coords,
427 scalar_cache,
428 scratch,
429 store_regions,
430 )? {
431 continue;
432 }
433 let component_gid = component.gid();
434 let flags = component.flags();
435
436 let coords_the_same = !flags.contains(VarcFlags::HAVE_AXES)
437 && !flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES);
438
439 let component_coords: &[F2Dot14] = if coords_the_same {
440 current_coords
441 } else {
442 self.component_coords(
443 &component,
444 current_coords,
445 level.coords,
446 scalar_cache,
447 scratch,
448 draw.font_coords,
449 store_regions,
450 )?;
451 level.coords
452 };
453
454 let mut transform = *component.transform();
455 apply_transform_variations(
456 &component,
457 current_coords,
458 &mut transform,
459 scalar_cache,
460 scratch,
461 store_regions,
462 )?;
463 let matrix = parent_matrix * scale_matrix(transform.matrix(), draw.scale);
464 if component_gid != glyph {
465 if let Some(coverage_index) = self.coverage.get(component_gid) {
466 if !stack.contains(component_gid) {
467 if coords_the_same {
468 self.draw_glyph(
471 component_gid,
472 coverage_index,
473 current_coords,
474 matrix,
475 draw,
476 scalar_cache,
477 level.rest.reborrow(),
478 leaf,
479 sink,
480 stack,
481 scratch,
482 )?;
483 } else {
484 let mut child_cache = ScalarCache::new(level.scalars);
485 self.draw_glyph(
486 component_gid,
487 coverage_index,
488 component_coords,
489 matrix,
490 draw,
491 &mut child_cache,
492 level.rest.reborrow(),
493 leaf,
494 sink,
495 stack,
496 scratch,
497 )?;
498 }
499 continue;
500 }
501 if scratch.edges_left == 0 {
504 return Err(Error::LimitExceeded);
505 }
506 scratch.edges_left -= 1;
507 continue;
508 }
509 }
510 if scratch.edges_left == 0 {
512 return Err(Error::LimitExceeded);
513 }
514 scratch.edges_left -= 1;
515 draw.context.draw_glyph(
516 component_gid,
517 component_coords,
518 draw.ppem,
519 leaf,
520 &mut TransformSink::new(&mut *sink, matrix),
521 )?;
522 }
523 stack.pop();
524 Ok(())
525 }
526
527 #[allow(clippy::too_many_arguments)]
531 fn component_coords(
532 &self,
533 component: &VarcComponent<'_>,
534 current_coords: &[F2Dot14],
535 coords: &mut [F2Dot14],
536 scalar_cache: &mut ScalarCache<'_>,
537 scratch: &mut Scratch<'_>,
538 font_coords: &[F2Dot14],
539 store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
540 ) -> Result<(), Error> {
541 let flags = component.flags();
542 if flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES) {
543 expand_coords(coords, font_coords);
544 } else {
545 expand_coords(coords, current_coords);
546 }
547
548 if !flags.contains(VarcFlags::HAVE_AXES) {
549 return Ok(());
550 }
551
552 let axis_indices_index = component.axis_indices_index().ok_or(Error::Malformed)?;
553 let num_axes = self.axis_indices(axis_indices_index as usize, scratch.axis_indices)?;
554 let axis_indices = &scratch.axis_indices[..num_axes];
555 let values = scratch
556 .axis_values
557 .get_mut(..num_axes)
558 .ok_or(Error::Malformed)?;
559 let stated = axis_values(component, values);
560 let values = &mut values[..stated];
561 if let Some(var_idx) = component.axis_values_var_index() {
562 if var_idx != NO_VARIATION_INDEX && !values.is_empty() {
563 let (store, regions) = store_regions.ok_or(Error::Malformed)?;
564 let deltas = scratch
565 .deltas
566 .get_mut(..values.len())
567 .ok_or(Error::Malformed)?;
568 compute_tuple_deltas(
569 store,
570 regions,
571 var_idx,
572 current_coords,
573 scalar_cache,
574 deltas,
575 )?;
576 for (value, delta) in values.iter_mut().zip(deltas.iter()) {
577 *value += *delta;
578 }
579 }
580 }
581
582 for (axis_index, value) in axis_indices.iter().zip(values.iter().copied()) {
583 let Some(slot) = coords.get_mut(*axis_index as usize) else {
584 return Err(Error::Malformed);
585 };
586 let raw = value.round().clamp(i16::MIN as f64, i16::MAX as f64) as i16;
587 *slot = F2Dot14::from_bits(raw);
588 }
589 Ok(())
590 }
591
592 fn axis_indices(&self, nth: usize, out: &mut [u16]) -> Result<usize, ReadError> {
595 let packed = self.varc.axis_indices(nth)?;
596 let mut count = 0;
597 for value in packed.iter() {
598 let slot = out.get_mut(count).ok_or(ReadError::OutOfBounds)?;
600 *slot = u16::try_from(value).map_err(|_| ReadError::OutOfBounds)?;
601 count += 1;
602 }
603 Ok(count)
604 }
605
606 fn component_condition_met(
607 &self,
608 component: &VarcComponent<'_>,
609 coords: &[F2Dot14],
610 scalar_cache: &mut ScalarCache<'_>,
611 scratch: &mut Scratch<'_>,
612 store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
613 ) -> Result<bool, Error> {
614 let Some(condition_index) = component.condition_index() else {
615 return Ok(true);
616 };
617 let condition_list = self.varc.condition_list().ok_or(Error::Malformed)??;
618 let offset = condition_list
619 .condition_offsets()
620 .get(condition_index as usize)
621 .ok_or(Error::Malformed)?;
622 if offset.get().is_null() {
624 return Ok(true);
625 }
626 let condition = condition_list.conditions().get(condition_index as usize)?;
627 eval_condition(&condition, coords, store_regions, scalar_cache, scratch)
628 }
629}
630
631struct DrawContext<'a, 'b> {
633 font_coords: &'b [F2Dot14],
634 scale: f32,
636 ppem: Option<f32>,
637 context: &'b dyn VarcContext,
638 store_regions: Option<(
639 &'b MultiItemVariationStore<'a>,
640 &'b SparseVariationRegionList<'a>,
641 )>,
642}
643
644const TRANSFORM_FIELD_COUNT: usize = 9;
648
649#[derive(Clone, Copy, Debug, PartialEq, Eq)]
655struct Layout {
656 depth: usize,
658 axis_count: usize,
659 index_count: usize,
662 region_count: usize,
663 leaf: usize,
665}
666
667impl Layout {
668 fn tuple_len(&self) -> usize {
670 self.index_count.max(TRANSFORM_FIELD_COUNT)
671 }
672
673 fn packed_len(&self) -> Option<usize> {
678 let doubles = self.tuple_len().checked_add(self.index_count)?;
679 let scalars = (self.depth + 1).checked_mul(self.region_count)?;
680 let halves = (self.depth + 1)
682 .checked_mul(self.axis_count)?
683 .checked_add(self.index_count)?;
684 doubles
685 .checked_mul(size_of::<f64>())?
686 .checked_add(scalars.checked_mul(size_of::<f32>())?)?
687 .checked_add(halves.checked_mul(size_of::<u16>())?)?
688 .checked_add(self.leaf)?
689 .checked_add(align_of::<f64>())
690 }
691
692 fn carve<'m>(&self, buf: &'m mut [u8]) -> Option<Memory<'m>> {
695 let (deltas, rest) = take_slice::<f64>(buf, self.tuple_len())?;
696 let (axis_values, rest) = take_slice::<f64>(rest, self.index_count)?;
697 let (scalars, rest) = take_slice::<f32>(rest, (self.depth + 1) * self.region_count)?;
698 let (font_coords, rest) = take_slice::<F2Dot14>(rest, self.axis_count)?;
699 let (coords, rest) = take_slice::<F2Dot14>(rest, self.depth * self.axis_count)?;
700 let (axis_indices, leaf) = take_slice::<u16>(rest, self.index_count)?;
701 (leaf.len() >= self.leaf).then_some(Memory {
702 font_coords,
703 coords,
704 scalars,
705 leaf,
706 scratch: Scratch {
707 deltas,
708 axis_values,
709 axis_indices,
710 edges_left: MAX_COMPOSITE_EDGES,
711 },
712 })
713 }
714}
715
716struct Memory<'m> {
718 font_coords: &'m mut [F2Dot14],
720 coords: &'m mut [F2Dot14],
722 scalars: &'m mut [f32],
724 leaf: &'m mut [u8],
726 scratch: Scratch<'m>,
727}
728
729struct Scratch<'m> {
732 deltas: &'m mut [f64],
733 axis_values: &'m mut [f64],
734 axis_indices: &'m mut [u16],
735 edges_left: usize,
736}
737
738struct Levels<'m> {
742 coords: &'m mut [F2Dot14],
743 scalars: &'m mut [f32],
744 axis_count: usize,
745 region_count: usize,
746}
747
748struct Level<'m> {
750 coords: &'m mut [F2Dot14],
751 scalars: &'m mut [f32],
752 rest: Levels<'m>,
753}
754
755impl Levels<'_> {
756 fn reborrow(&mut self) -> Levels<'_> {
758 Levels {
759 coords: &mut *self.coords,
760 scalars: &mut *self.scalars,
761 axis_count: self.axis_count,
762 region_count: self.region_count,
763 }
764 }
765
766 fn next(&mut self) -> Option<Level<'_>> {
768 let (coords, rest_coords) = split(&mut *self.coords, self.axis_count)?;
769 let (scalars, rest_scalars) = split(&mut *self.scalars, self.region_count)?;
770 Some(Level {
771 coords,
772 scalars,
773 rest: Levels {
774 coords: rest_coords,
775 scalars: rest_scalars,
776 axis_count: self.axis_count,
777 region_count: self.region_count,
778 },
779 })
780 }
781}
782
783fn split<T>(slice: &mut [T], len: usize) -> Option<(&mut [T], &mut [T])> {
786 (len <= slice.len()).then(|| slice.split_at_mut(len))
787}
788
789struct GlyphStack {
791 ids: [GlyphId; MAX_RECURSION_DEPTH],
792 len: usize,
793}
794
795impl GlyphStack {
796 fn new() -> Self {
797 Self {
798 ids: [GlyphId::new(0); MAX_RECURSION_DEPTH],
799 len: 0,
800 }
801 }
802
803 fn len(&self) -> usize {
804 self.len
805 }
806
807 fn contains(&self, glyph: GlyphId) -> bool {
808 self.ids[..self.len].contains(&glyph)
809 }
810
811 fn push(&mut self, glyph: GlyphId) {
815 if let Some(slot) = self.ids.get_mut(self.len) {
816 *slot = glyph;
817 self.len += 1;
818 }
819 }
820
821 fn pop(&mut self) {
822 self.len = self.len.saturating_sub(1);
823 }
824}
825
826struct Walk {
828 stack: GlyphStack,
829 edges_left: usize,
830 depth: usize,
832 truncated: bool,
834}
835
836impl Walk {
837 fn new() -> Self {
838 Self {
839 stack: GlyphStack::new(),
840 edges_left: MAX_COMPOSITE_EDGES,
841 depth: 0,
842 truncated: false,
843 }
844 }
845}
846
847struct ScalarCache<'m> {
849 values: &'m mut [f32],
850}
851
852impl<'m> ScalarCache<'m> {
853 const INVALID: f32 = 2.0;
857
858 fn new(values: &'m mut [f32]) -> Self {
859 values.fill(Self::INVALID);
860 Self { values }
861 }
862
863 fn get(&self, index: usize) -> f32 {
864 self.values.get(index).copied().unwrap_or(Self::INVALID)
865 }
866
867 fn set(&mut self, index: usize, value: f32) {
868 if let Some(slot) = self.values.get_mut(index) {
869 *slot = value;
870 }
871 }
872}
873
874fn axis_values(component: &VarcComponent<'_>, out: &mut [f64]) -> usize {
880 let Some(packed) = component.axis_values() else {
881 return 0;
882 };
883 out.fill(0.0);
884 for (slot, value) in out.iter_mut().zip(packed.iter()) {
885 *slot = value as f64;
886 }
887 out.len()
888}
889
890fn apply_transform_variations(
891 component: &VarcComponent<'_>,
892 coords: &[F2Dot14],
893 transform: &mut DecomposedTransform,
894 scalar_cache: &mut ScalarCache<'_>,
895 scratch: &mut Scratch<'_>,
896 store_regions: Option<(&MultiItemVariationStore<'_>, &SparseVariationRegionList<'_>)>,
897) -> Result<(), Error> {
898 let Some(var_idx) = component.transform_var_index() else {
899 return Ok(());
900 };
901
902 let flags = component.flags();
903
904 const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
905 VarcFlags::HAVE_TRANSLATE_X.bits()
906 | VarcFlags::HAVE_TRANSLATE_Y.bits()
907 | VarcFlags::HAVE_ROTATION.bits()
908 | VarcFlags::HAVE_SCALE_X.bits()
909 | VarcFlags::HAVE_SCALE_Y.bits()
910 | VarcFlags::HAVE_SKEW_X.bits()
911 | VarcFlags::HAVE_SKEW_Y.bits()
912 | VarcFlags::HAVE_TCENTER_X.bits()
913 | VarcFlags::HAVE_TCENTER_Y.bits(),
914 );
915 let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
916 if field_count == 0 || var_idx == NO_VARIATION_INDEX {
917 return Ok(());
918 }
919
920 let (store, regions) = store_regions.ok_or(Error::Malformed)?;
921 let deltas = scratch
922 .deltas
923 .get_mut(..field_count)
924 .ok_or(Error::Malformed)?;
925 compute_tuple_deltas(store, regions, var_idx, coords, scalar_cache, deltas)?;
926
927 let mut delta_iter = deltas.iter().map(|&delta| delta as f32);
928
929 if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
930 let delta = delta_iter.next().unwrap_or(0.0);
931 transform.set_translate_x(transform.translate_x() + delta);
932 }
933 if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
934 let delta = delta_iter.next().unwrap_or(0.0);
935 transform.set_translate_y(transform.translate_y() + delta);
936 }
937 if flags.contains(VarcFlags::HAVE_ROTATION) {
938 let delta = delta_iter.next().unwrap_or(0.0);
939 transform.set_rotation(transform.rotation() + delta / 4096.0);
940 }
941 if flags.contains(VarcFlags::HAVE_SCALE_X) {
942 let delta = delta_iter.next().unwrap_or(0.0);
943 transform.set_scale_x(transform.scale_x() + delta / 1024.0);
944 }
945 if flags.contains(VarcFlags::HAVE_SCALE_Y) {
946 let delta = delta_iter.next().unwrap_or(0.0);
947 transform.set_scale_y(transform.scale_y() + delta / 1024.0);
948 }
949 const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
950 VarcFlags::HAVE_SKEW_X.bits()
951 | VarcFlags::HAVE_SKEW_Y.bits()
952 | VarcFlags::HAVE_TCENTER_X.bits()
953 | VarcFlags::HAVE_TCENTER_Y.bits(),
954 );
955 if flags.intersects(SKEW_OR_CENTER) {
956 if flags.contains(VarcFlags::HAVE_SKEW_X) {
957 let delta = delta_iter.next().unwrap_or(0.0);
958 transform.set_skew_x(transform.skew_x() + delta / 4096.0);
959 }
960 if flags.contains(VarcFlags::HAVE_SKEW_Y) {
961 let delta = delta_iter.next().unwrap_or(0.0);
962 transform.set_skew_y(transform.skew_y() + delta / 4096.0);
963 }
964 if flags.contains(VarcFlags::HAVE_TCENTER_X) {
965 let delta = delta_iter.next().unwrap_or(0.0);
966 transform.set_center_x(transform.center_x() + delta);
967 }
968 if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
969 let delta = delta_iter.next().unwrap_or(0.0);
970 transform.set_center_y(transform.center_y() + delta);
971 }
972 }
973
974 if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
975 transform.set_scale_y(transform.scale_x());
976 }
977 Ok(())
978}
979
980fn eval_condition(
981 condition: &Condition<'_>,
982 coords: &[F2Dot14],
983 store_regions: Option<(&MultiItemVariationStore<'_>, &SparseVariationRegionList<'_>)>,
984 scalar_cache: &mut ScalarCache<'_>,
985 scratch: &mut Scratch<'_>,
986) -> Result<bool, Error> {
987 condition.evaluate(coords, |var_idx| {
988 let (var_store, regions) = store_regions.ok_or(Error::Malformed)?;
989 let delta = scratch.deltas.get_mut(..1).ok_or(Error::Malformed)?;
990 compute_tuple_deltas(var_store, regions, var_idx, coords, scalar_cache, delta)?;
991 Ok(delta[0])
992 })
993}
994
995fn linear_scale(ppem: Option<f32>, units_per_em: u16) -> f32 {
997 match ppem {
998 Some(ppem) if units_per_em != 0 => ppem / units_per_em as f32,
999 _ => 1.0,
1000 }
1001}
1002
1003fn expand_coords(out: &mut [F2Dot14], coords: &[F2Dot14]) {
1007 out.fill(F2Dot14::ZERO);
1008 for (slot, value) in out.iter_mut().zip(coords.iter().copied()) {
1009 *slot = value;
1010 }
1011}
1012
1013fn compute_tuple_deltas(
1018 store: &MultiItemVariationStore,
1019 regions: &SparseVariationRegionList,
1020 var_idx: u32,
1021 coords: &[F2Dot14],
1022 cache: &mut ScalarCache<'_>,
1023 out: &mut [f64],
1024) -> Result<(), ReadError> {
1025 out.fill(0.0);
1028 if out.is_empty() || var_idx == NO_VARIATION_INDEX {
1029 return Ok(());
1030 }
1031 let outer = (var_idx >> 16) as usize;
1032 let inner = (var_idx & 0xFFFF) as usize;
1033 let data = store.variation_data().get(outer)?;
1034 let region_indices = data.region_indices();
1035 let mut deltas = data.delta_set(inner)?.fetcher();
1036 let regions = regions.regions();
1037
1038 let mut skip = 0;
1039 for region_index in region_indices.iter() {
1040 let region_idx = region_index.get() as usize;
1041 let mut scalar = cache.get(region_idx);
1042 if scalar >= 2.0 {
1043 scalar = regions.get(region_idx)?.compute_scalar_f32(coords);
1044 cache.set(region_idx, scalar);
1045 }
1046 if scalar == 0.0 {
1048 skip += out.len();
1049 } else {
1050 if skip != 0 {
1051 deltas.skip(skip)?;
1052 skip = 0;
1053 }
1054 deltas.add_to_f64_scaled(out, scalar as f64)?;
1055 }
1056 }
1057
1058 Ok(())
1059}
1060
1061#[inline(always)]
1062fn scale_matrix(mut m: Matrix<f32>, s: f32) -> Matrix<f32> {
1063 m.dx *= s;
1064 m.dy *= s;
1065 m
1066}
1067
1068#[derive(Clone)]
1072pub struct VarcGlyph<'a> {
1073 pub(super) outlines: &'a Outlines<'a>,
1074 pub(super) outline: Outline,
1075 pub(super) base: OutlineGlyphs<'a>,
1077}
1078
1079impl<'a> VarcGlyph<'a> {
1080 pub fn glyph(&self) -> GlyphId {
1082 self.outline.glyph()
1083 }
1084
1085 pub fn required_memory_size(&self) -> usize {
1092 let leaves = Leaves::new(self.base, DrawOptions::default(), Precision::Fixed);
1093 self.outlines.required_memory_size(&self.outline, &leaves)
1094 }
1095
1096 pub(super) fn draw(
1097 &self,
1098 options: &DrawOptions,
1099 precision: Precision,
1100 buf: &mut [u8],
1101 sink: &mut dyn PathSink,
1102 ) -> Result<DrawnMetrics, Error> {
1103 let leaves = Leaves::new(self.base, *options, precision);
1104 self.outlines
1105 .draw(&self.outline, &leaves, options.ppem, buf, sink)?;
1106 Ok(DrawnMetrics::default())
1107 }
1108}
1109
1110pub(super) struct Leaves<'a> {
1114 base: OutlineGlyphs<'a>,
1115 options: DrawOptions,
1116 precision: Precision,
1117}
1118
1119impl<'a> Leaves<'a> {
1120 pub(super) fn new(base: OutlineGlyphs<'a>, options: DrawOptions, precision: Precision) -> Self {
1121 Self {
1122 base,
1123 options,
1124 precision,
1125 }
1126 }
1127}
1128
1129impl VarcContext for Leaves<'_> {
1130 fn coords(&self) -> &[F2Dot14] {
1131 self.base.font().normalized_coords()
1132 }
1133
1134 fn draw_glyph(
1135 &self,
1136 glyph: GlyphId,
1137 coords: &[F2Dot14],
1138 ppem: Option<f32>,
1139 buf: &mut [u8],
1140 sink: &mut dyn PathSink,
1141 ) -> Result<(), Error> {
1142 let options = DrawOptions {
1143 ppem,
1144 ..self.options
1145 };
1146 self.base
1147 .draw_base(glyph, coords, &options, self.precision, buf, sink)
1148 }
1149
1150 fn glyph_buffer_size(&self, glyph: GlyphId) -> usize {
1151 self.base.base_buffer_size(glyph)
1152 }
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157 use super::*;
1158 use crate::{model::glyph::outline::PathElement, FontRef, TableProvider};
1159
1160 fn coord(value: f32) -> F2Dot14 {
1161 F2Dot14::from_f32(value)
1162 }
1163
1164 fn test_outlines<'a>(font: &FontRef<'a>) -> Outlines<'a> {
1165 let axis_count = font
1166 .fvar()
1167 .map(|fvar| fvar.axis_count() as usize)
1168 .unwrap_or_default();
1169 let upem = font.head().unwrap().units_per_em();
1170 Outlines::new(font.varc().unwrap(), upem, axis_count).unwrap()
1171 }
1172
1173 fn test_memory(outlines: &Outlines<'_>) -> alloc::vec::Vec<u8> {
1178 alloc::vec![0u8; test_layout(outlines).packed_len().unwrap()]
1179 }
1180
1181 fn test_layout(outlines: &Outlines<'_>) -> Layout {
1182 Layout {
1183 depth: 1,
1184 axis_count: outlines.axis_count.max(8),
1185 index_count: outlines.index_count.max(8),
1186 region_count: outlines.region_count(),
1187 leaf: 0,
1188 }
1189 }
1190
1191 struct NullContext;
1193
1194 impl VarcContext for NullContext {
1195 fn coords(&self) -> &[F2Dot14] {
1196 &[]
1197 }
1198
1199 fn draw_glyph(
1200 &self,
1201 _glyph: GlyphId,
1202 _coords: &[F2Dot14],
1203 _ppem: Option<f32>,
1204 _buf: &mut [u8],
1205 _pen: &mut dyn PathSink,
1206 ) -> Result<(), Error> {
1207 Ok(())
1208 }
1209
1210 fn glyph_buffer_size(&self, _glyph: GlyphId) -> usize {
1211 0
1212 }
1213 }
1214
1215 fn assert_close(actual: f32, expected: f32) {
1216 let diff = (actual - expected).abs();
1217 assert!(
1218 diff <= 1e-6,
1219 "expected {expected}, got {actual}, diff {diff}"
1220 );
1221 }
1222
1223 #[test]
1224 fn expand_coords_pads_and_truncates() {
1225 let mut out = [F2Dot14::ZERO; 4];
1226 expand_coords(&mut out, &[coord(0.25), coord(-0.5)]);
1227 assert_eq!(
1228 out,
1229 [coord(0.25), coord(-0.5), F2Dot14::ZERO, F2Dot14::ZERO]
1230 );
1231
1232 let mut out = [F2Dot14::ZERO; 1];
1233 expand_coords(&mut out, &[coord(0.25), coord(-0.5)]);
1234 assert_eq!(out, [coord(0.25)]);
1235 }
1236
1237 #[test]
1238 fn scale_matrix_only_scales_translation() {
1239 let matrix = Matrix::from_elements([1.0, 2.0, 3.0, 4.0, 5.0, -6.0]);
1240 assert_eq!(
1241 scale_matrix(matrix, 10.0).elements(),
1242 [1.0, 2.0, 3.0, 4.0, 50.0, -60.0]
1243 );
1244 }
1245
1246 #[test]
1247 fn compute_tuple_deltas_no_variation_index_zeroes_the_tuple() {
1248 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1249 let varc = font.varc().unwrap();
1250 let store = varc.multi_var_store().unwrap().unwrap();
1251 let regions = store.region_list().unwrap();
1252 let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1253 let mut cache = ScalarCache::new(&mut values);
1254 let mut out = [42.0f64; 3];
1255
1256 compute_tuple_deltas(
1257 &store,
1258 ®ions,
1259 NO_VARIATION_INDEX,
1260 &[coord(0.0)],
1261 &mut cache,
1262 &mut out,
1263 )
1264 .unwrap();
1265 assert_eq!(out, [0.0, 0.0, 0.0]);
1266
1267 compute_tuple_deltas(
1268 &store,
1269 ®ions,
1270 NO_VARIATION_INDEX,
1271 &[coord(0.0)],
1272 &mut cache,
1273 &mut [],
1274 )
1275 .unwrap();
1276 }
1277
1278 #[test]
1279 fn compute_tuple_deltas_invalid_outer_index_errors() {
1280 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1281 let varc = font.varc().unwrap();
1282 let store = varc.multi_var_store().unwrap().unwrap();
1283 let regions = store.region_list().unwrap();
1284 let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1285 let mut cache = ScalarCache::new(&mut values);
1286 let mut out = [0.0f64; 1];
1287
1288 assert!(
1291 compute_tuple_deltas(&store, ®ions, 0xFFFF_0000, &[], &mut cache, &mut out).is_err()
1292 );
1293 }
1294
1295 #[test]
1296 fn compute_tuple_deltas_matches_manual_decode() {
1297 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1298 let varc = font.varc().unwrap();
1299 let store = varc.multi_var_store().unwrap().unwrap();
1300 let regions = store.region_list().unwrap();
1301 let region_list = regions.regions();
1302 let coords = [coord(0.5); 8];
1303
1304 let mut tried = 0usize;
1305 for (outer, data) in store.variation_data().iter().enumerate() {
1306 let data = data.unwrap();
1307 let region_count = data.region_indices().len();
1308 if region_count == 0 {
1309 continue;
1310 }
1311 let delta_set_count = data.delta_sets().unwrap().count() as usize;
1312 for inner in 0..delta_set_count.min(3) {
1313 let decoded = data
1314 .delta_set(inner)
1315 .unwrap()
1316 .iter()
1317 .collect::<alloc::vec::Vec<_>>();
1318 if decoded.is_empty() || decoded.len() % region_count != 0 {
1319 continue;
1320 }
1321 let tuple_len = decoded.len() / region_count;
1322 let var_idx = ((outer as u32) << 16) | inner as u32;
1323
1324 let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1325 let mut cache = ScalarCache::new(&mut values);
1326 let mut actual = alloc::vec![0.0f64; tuple_len];
1327 compute_tuple_deltas(&store, ®ions, var_idx, &coords, &mut cache, &mut actual)
1328 .unwrap();
1329 let first = actual.clone();
1330
1331 compute_tuple_deltas(&store, ®ions, var_idx, &coords, &mut cache, &mut actual)
1333 .unwrap();
1334 assert_eq!(actual, first);
1335
1336 let mut expected = alloc::vec![0.0f64; tuple_len];
1337 for (region_order, region_idx) in data.region_indices().iter().enumerate() {
1338 let scalar = region_list
1339 .get(region_idx.get() as usize)
1340 .unwrap()
1341 .compute_scalar_f32(&coords);
1342 if scalar == 0.0 {
1343 continue;
1344 }
1345 let base = region_order * tuple_len;
1346 for (i, slot) in expected.iter_mut().enumerate() {
1347 *slot += decoded[base + i] as f64 * scalar as f64;
1348 }
1349 }
1350 assert_eq!(actual.len(), expected.len());
1351 for (a, e) in actual.iter().zip(expected.iter()) {
1352 assert_eq!(*a, *e);
1353 }
1354 tried += 1;
1355 }
1356 }
1357 assert!(tried > 0, "expected at least one tuple to be exercised");
1358 }
1359
1360 fn apply_transform_variations_reference(
1361 component: &VarcComponent<'_>,
1362 coords: &[F2Dot14],
1363 transform: &mut DecomposedTransform,
1364 var_store: Option<&MultiItemVariationStore<'_>>,
1365 regions: Option<&SparseVariationRegionList<'_>>,
1366 scalar_cache: &mut ScalarCache<'_>,
1367 deltas: &mut [f64],
1368 ) -> Result<(), Error> {
1369 let Some(var_idx) = component.transform_var_index() else {
1370 return Ok(());
1371 };
1372 let flags = component.flags();
1373 const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
1374 VarcFlags::HAVE_TRANSLATE_X.bits()
1375 | VarcFlags::HAVE_TRANSLATE_Y.bits()
1376 | VarcFlags::HAVE_ROTATION.bits()
1377 | VarcFlags::HAVE_SCALE_X.bits()
1378 | VarcFlags::HAVE_SCALE_Y.bits()
1379 | VarcFlags::HAVE_SKEW_X.bits()
1380 | VarcFlags::HAVE_SKEW_Y.bits()
1381 | VarcFlags::HAVE_TCENTER_X.bits()
1382 | VarcFlags::HAVE_TCENTER_Y.bits(),
1383 );
1384 let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
1385 if field_count == 0 {
1386 return Ok(());
1387 }
1388
1389 let store = var_store.ok_or(Error::Malformed)?;
1390 let regions = regions.ok_or(Error::Malformed)?;
1391 let deltas = &mut deltas[..field_count];
1392 compute_tuple_deltas(store, regions, var_idx, coords, scalar_cache, deltas)?;
1393
1394 let mut delta_iter = deltas.iter().map(|&delta| delta as f32);
1395 if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
1396 transform.set_translate_x(transform.translate_x() + delta_iter.next().unwrap_or(0.0));
1397 }
1398 if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
1399 transform.set_translate_y(transform.translate_y() + delta_iter.next().unwrap_or(0.0));
1400 }
1401 if flags.contains(VarcFlags::HAVE_ROTATION) {
1402 transform
1403 .set_rotation(transform.rotation() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1404 }
1405 if flags.contains(VarcFlags::HAVE_SCALE_X) {
1406 transform.set_scale_x(transform.scale_x() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1407 }
1408 if flags.contains(VarcFlags::HAVE_SCALE_Y) {
1409 transform.set_scale_y(transform.scale_y() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1410 }
1411 const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
1412 VarcFlags::HAVE_SKEW_X.bits()
1413 | VarcFlags::HAVE_SKEW_Y.bits()
1414 | VarcFlags::HAVE_TCENTER_X.bits()
1415 | VarcFlags::HAVE_TCENTER_Y.bits(),
1416 );
1417 if flags.intersects(SKEW_OR_CENTER) {
1418 if flags.contains(VarcFlags::HAVE_SKEW_X) {
1419 transform
1420 .set_skew_x(transform.skew_x() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1421 }
1422 if flags.contains(VarcFlags::HAVE_SKEW_Y) {
1423 transform
1424 .set_skew_y(transform.skew_y() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1425 }
1426 if flags.contains(VarcFlags::HAVE_TCENTER_X) {
1427 transform.set_center_x(transform.center_x() + delta_iter.next().unwrap_or(0.0));
1428 }
1429 if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
1430 transform.set_center_y(transform.center_y() + delta_iter.next().unwrap_or(0.0));
1431 }
1432 }
1433 if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
1434 transform.set_scale_y(transform.scale_x());
1435 }
1436 Ok(())
1437 }
1438
1439 #[test]
1440 fn apply_transform_variations_matches_reference_path() {
1441 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1442 let outlines = test_outlines(&font);
1443 let coverage = outlines.varc.coverage().unwrap();
1444 let var_store = outlines.varc.multi_var_store().transpose().unwrap();
1445 let regions = var_store
1446 .as_ref()
1447 .map(|s| s.region_list())
1448 .transpose()
1449 .unwrap();
1450 let region_count = regions
1451 .as_ref()
1452 .map(|r| r.region_count() as usize)
1453 .unwrap_or(0);
1454
1455 let mut coords = alloc::vec![F2Dot14::ZERO; outlines.axis_count];
1456 for (i, c) in coords.iter_mut().enumerate() {
1457 *c = match i % 4 {
1458 0 => coord(0.5),
1459 1 => coord(-0.5),
1460 2 => coord(0.25),
1461 _ => coord(-0.25),
1462 };
1463 }
1464
1465 let mut tested = 0usize;
1466 for gid in coverage.iter() {
1467 let coverage_index = coverage.get(gid).unwrap() as usize;
1468 let glyph = outlines.varc.glyph(coverage_index).unwrap();
1469 for component in glyph.components() {
1470 let component = component.unwrap();
1471 if component.transform_var_index().is_none() {
1472 continue;
1473 }
1474
1475 let mut transform_new = *component.transform();
1476 let mut transform_ref = *component.transform();
1477 let mut values_new = alloc::vec![0.0f32; region_count];
1478 let mut values_ref = alloc::vec![0.0f32; region_count];
1479 let mut cache_new = ScalarCache::new(&mut values_new);
1480 let mut cache_ref = ScalarCache::new(&mut values_ref);
1481 let mut deltas_ref = [0.0f64; TRANSFORM_FIELD_COUNT];
1482 let mut buf = test_memory(&outlines);
1483 let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1484 let store_regions = var_store.as_ref().zip(regions.as_ref());
1485
1486 apply_transform_variations(
1487 &component,
1488 &coords,
1489 &mut transform_new,
1490 &mut cache_new,
1491 &mut scratch,
1492 store_regions,
1493 )
1494 .unwrap();
1495 apply_transform_variations_reference(
1496 &component,
1497 &coords,
1498 &mut transform_ref,
1499 var_store.as_ref(),
1500 regions.as_ref(),
1501 &mut cache_ref,
1502 &mut deltas_ref,
1503 )
1504 .unwrap();
1505
1506 assert_close(transform_new.translate_x(), transform_ref.translate_x());
1507 assert_close(transform_new.translate_y(), transform_ref.translate_y());
1508 assert_close(transform_new.rotation(), transform_ref.rotation());
1509 assert_close(transform_new.scale_x(), transform_ref.scale_x());
1510 assert_close(transform_new.scale_y(), transform_ref.scale_y());
1511 assert_close(transform_new.skew_x(), transform_ref.skew_x());
1512 assert_close(transform_new.skew_y(), transform_ref.skew_y());
1513 assert_close(transform_new.center_x(), transform_ref.center_x());
1514 assert_close(transform_new.center_y(), transform_ref.center_y());
1515 tested += 1;
1516 if tested >= 32 {
1517 break;
1518 }
1519 }
1520 if tested >= 32 {
1521 break;
1522 }
1523 }
1524 assert!(tested > 0, "expected at least one transformed component");
1525 }
1526
1527 fn condition_eval_env() -> (
1531 FontRef<'static>,
1532 MultiItemVariationStore<'static>,
1533 SparseVariationRegionList<'static>,
1534 ) {
1535 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1536 let varc = font.varc().unwrap();
1537 let store = varc.multi_var_store().unwrap().unwrap();
1538 let regions = store.region_list().unwrap();
1539 (font, store, regions)
1540 }
1541
1542 #[test]
1543 fn component_conditions_without_variations_do_not_require_a_store() {
1544 use crate::{FontData, FontRead};
1545
1546 let leaf = alloc::vec![0, 1, 0, 0, 0xE0, 0, 0x20, 0]; let mut cases = alloc::vec![(leaf.clone(), false)];
1548 for default in [-1i16, 0, 1] {
1549 let mut bytes = alloc::vec![0, 2];
1550 bytes.extend(default.to_be_bytes());
1551 bytes.extend(NO_VARIATION_INDEX.to_be_bytes());
1552 cases.push((bytes, default > 0));
1553 }
1554 for format in [3, 4] {
1555 let mut bytes = alloc::vec![0, format, 1, 0, 0, 6];
1556 bytes.extend_from_slice(&leaf);
1557 cases.push((bytes, false));
1558 }
1559 let mut negate = alloc::vec![0, 5, 0, 0, 5];
1560 negate.extend_from_slice(&leaf);
1561 cases.push((negate, true));
1562
1563 for (condition, expected) in cases {
1564 let mut bytes = alloc::vec![0u8; 24]; bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1566 bytes[12..16].copy_from_slice(&24u32.to_be_bytes()); bytes.extend(1u32.to_be_bytes()); bytes.extend(8u32.to_be_bytes()); bytes.extend_from_slice(&condition);
1570 let glyphs_offset = bytes.len() as u32;
1571 bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1572 bytes.extend([0, 0, 0, 1, 1, 1, 6, 0x80, 0x80, 0, 1, 0]);
1574 let varc = Varc::read(FontData::new(&bytes)).unwrap();
1575 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1576 let mut outlines = test_outlines(&font);
1577 outlines.varc = varc;
1578 let glyph = outlines.varc.glyph(0).unwrap();
1579 let component = glyph.components().next().unwrap().unwrap();
1580 assert_eq!(component.condition_index(), Some(0));
1581 let mut buf = test_memory(&outlines);
1582 let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1583 let result = outlines.component_condition_met(
1584 &component,
1585 &[coord(0.75)],
1586 &mut ScalarCache::new(&mut []),
1587 &mut scratch,
1588 None,
1589 );
1590 assert_eq!(result.unwrap(), expected, "condition bytes {condition:?}");
1591 }
1592 }
1593
1594 #[test]
1595 fn component_zero_deltas_do_not_require_a_store() {
1596 use crate::{FontData, FontRead};
1597
1598 for (component_bytes, axis_indices, expected_coord, expected_translation) in [
1599 (
1601 alloc::vec![6, 0, 1, 0, 0x40, 0x10, 0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF],
1602 alloc::vec![0, 0], 0.25,
1604 0.0,
1605 ),
1606 (alloc::vec![6, 0, 1, 0, 0], alloc::vec![], 0.5, 0.0),
1608 (
1610 alloc::vec![0x18, 0, 1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0, 7],
1611 alloc::vec![],
1612 0.5,
1613 7.0,
1614 ),
1615 ] {
1616 let mut bytes = alloc::vec![0u8; 24];
1617 bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1618 bytes[16..20].copy_from_slice(&24u32.to_be_bytes());
1619 bytes.extend([0, 0, 0, 1, 1, 1, axis_indices.len() as u8 + 1]);
1620 bytes.extend_from_slice(&axis_indices);
1621 let glyphs_offset = bytes.len() as u32;
1622 bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1623 bytes.extend([0, 0, 0, 1, 1, 1, component_bytes.len() as u8 + 1]);
1624 bytes.extend_from_slice(&component_bytes);
1625 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1626 let mut outlines = test_outlines(&font);
1627 outlines.varc = Varc::read(FontData::new(&bytes)).unwrap();
1628 let glyph = outlines.varc.glyph(0).unwrap();
1629 let component = glyph.components().next().unwrap().unwrap();
1630 let mut coords = [F2Dot14::ZERO; 1];
1631 let mut cache = ScalarCache::new(&mut []);
1632 let mut buf = test_memory(&outlines);
1633 let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1634 scratch.deltas.fill(100.0);
1636 outlines
1637 .component_coords(
1638 &component,
1639 &[coord(0.5)],
1640 &mut coords,
1641 &mut cache,
1642 &mut scratch,
1643 &[coord(0.5)],
1644 None,
1645 )
1646 .unwrap();
1647 assert_eq!(coords, [coord(expected_coord)]);
1648 let mut transform = *component.transform();
1649 apply_transform_variations(
1650 &component,
1651 &[coord(0.5)],
1652 &mut transform,
1653 &mut cache,
1654 &mut scratch,
1655 None,
1656 )
1657 .unwrap();
1658 assert_eq!(transform.translate_x(), expected_translation);
1659 }
1660 }
1661
1662 #[test]
1663 fn null_condition_offsets_are_unconditional() {
1664 use crate::{FontData, FontRead};
1665
1666 let leaf = [0, 1, 0, 0, 0xE0, 0, 0x20, 0]; let mut cases = alloc::vec![
1668 (None, true),
1669 (Some(alloc::vec![0, 3, 1, 0, 0, 0]), true),
1670 (Some(alloc::vec![0, 4, 1, 0, 0, 0]), true),
1671 (Some(alloc::vec![0, 5, 0, 0, 0]), false),
1672 ];
1673 for format in [3, 4] {
1674 for null_first in [false, true] {
1675 let mut bytes = alloc::vec![0, format, 2];
1676 bytes.extend_from_slice(if null_first {
1677 &[0, 0, 0, 0, 0, 9]
1678 } else {
1679 &[0, 0, 9, 0, 0, 0]
1680 });
1681 bytes.extend_from_slice(&leaf);
1682 cases.push((Some(bytes), format == 4));
1683 }
1684 }
1685 for (condition, expected) in cases {
1686 let mut bytes = alloc::vec![0u8; 24];
1687 bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1688 bytes[12..16].copy_from_slice(&24u32.to_be_bytes());
1689 bytes.extend(1u32.to_be_bytes());
1690 bytes.extend(if condition.is_some() { 8u32 } else { 0 }.to_be_bytes());
1691 if let Some(condition) = condition.as_ref() {
1692 bytes.extend_from_slice(condition);
1693 }
1694 let glyphs_offset = bytes.len() as u32;
1695 bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1696 bytes.extend([0, 0, 0, 1, 1, 1, 6, 0x80, 0x80, 0, 1, 0]);
1697 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1698 let mut outlines = test_outlines(&font);
1699 outlines.varc = Varc::read(FontData::new(&bytes)).unwrap();
1700 let glyph = outlines.varc.glyph(0).unwrap();
1701 let component = glyph.components().next().unwrap().unwrap();
1702 let mut buf = test_memory(&outlines);
1703 let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1704 let result = outlines.component_condition_met(
1705 &component,
1706 &[coord(0.75)],
1707 &mut ScalarCache::new(&mut []),
1708 &mut scratch,
1709 None,
1710 );
1711 assert_eq!(result.unwrap(), expected, "condition {condition:?}");
1712 }
1713 }
1714
1715 #[test]
1718 fn eval_condition_bounds_recursion_depth() {
1719 use crate::{FontData, FontRead};
1720 const CHAIN_LEN: usize = 4096;
1726 let mut bytes = alloc::vec::Vec::with_capacity(CHAIN_LEN * 5);
1727 for _ in 0..CHAIN_LEN {
1728 bytes.extend_from_slice(&[0x00, 0x05, 0x00, 0x00, 0x05]);
1729 }
1730 let cond = Condition::read(FontData::new(&bytes)).unwrap();
1731
1732 let (_font, store, regions) = condition_eval_env();
1733 let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1734 let mut cache = ScalarCache::new(&mut values);
1735 let mut deltas = [0.0f64; 1];
1736 let mut scratch = Scratch {
1737 deltas: &mut deltas,
1738 axis_values: &mut [],
1739 axis_indices: &mut [],
1740 edges_left: MAX_COMPOSITE_EDGES,
1741 };
1742
1743 let result = eval_condition(
1744 &cond,
1745 &[],
1746 Some((&store, ®ions)),
1747 &mut cache,
1748 &mut scratch,
1749 );
1750 assert!(
1751 matches!(result, Err(Error::LimitExceeded)),
1752 "expected RecursionLimitExceeded, got {result:?}"
1753 );
1754 }
1755
1756 #[test]
1758 fn eval_condition_shallow_is_correct() {
1759 use crate::{FontData, FontRead};
1760 let (_font, store, regions) = condition_eval_env();
1761 let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1762 let mut cache = ScalarCache::new(&mut values);
1763 let mut deltas = [0.0f64; 1];
1764 let mut scratch = Scratch {
1765 deltas: &mut deltas,
1766 axis_values: &mut [],
1767 axis_indices: &mut [],
1768 edges_left: MAX_COMPOSITE_EDGES,
1769 };
1770
1771 let leaf: [u8; 8] = [0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x40, 0x00];
1773 let cond = Condition::read(FontData::new(&leaf)).unwrap();
1774 assert!(eval_condition(
1775 &cond,
1776 &[coord(0.5)],
1777 Some((&store, ®ions)),
1778 &mut cache,
1779 &mut scratch,
1780 )
1781 .unwrap());
1782 assert!(!eval_condition(
1783 &cond,
1784 &[coord(1.5)],
1785 Some((&store, ®ions)),
1786 &mut cache,
1787 &mut scratch,
1788 )
1789 .unwrap());
1790
1791 let mut negate = alloc::vec![0x00, 0x05, 0x00, 0x00, 0x05];
1794 negate.extend_from_slice(&leaf);
1795 let cond = Condition::read(FontData::new(&negate)).unwrap();
1796 assert!(!eval_condition(
1797 &cond,
1798 &[coord(0.5)],
1799 Some((&store, ®ions)),
1800 &mut cache,
1801 &mut scratch,
1802 )
1803 .unwrap());
1804 }
1805
1806 fn first_nested_varc_edge(outlines: &Outlines<'_>) -> Option<(GlyphId, u32)> {
1807 for gid in outlines.coverage.iter() {
1808 let coverage_index = outlines.coverage.get(gid)?;
1809 let glyph = outlines.varc.glyph(coverage_index as usize).ok()?;
1810 for component in glyph.components() {
1811 let component = component.ok()?;
1812 if component.condition_index().is_none()
1815 && component.gid() != gid
1816 && outlines.coverage.get(component.gid()).is_some()
1817 {
1818 return Some((gid, coverage_index));
1819 }
1820 }
1821 }
1822 None
1823 }
1824
1825 #[test]
1826 fn draw_glyph_respects_total_edge_budget() {
1827 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1828 let outlines = test_outlines(&font);
1829 let (root_gid, _) = first_nested_varc_edge(&outlines)
1830 .expect("expected at least one VARC->VARC component edge in fixture font");
1831 let outline = outlines.outline(root_gid).unwrap();
1832 let layout = outlines.layout_of(&outline, &NullContext).unwrap();
1833 let mut buf = alloc::vec![0u8; layout.packed_len().unwrap()];
1834 let mut memory = layout.carve(&mut buf).unwrap();
1835 memory.scratch.edges_left = 1;
1838 let mut pen = alloc::vec::Vec::<PathElement>::new();
1839 let result = outlines.draw_tree(&outline, &layout, &NullContext, None, memory, &mut pen);
1840 assert!(
1841 matches!(result, Err(Error::LimitExceeded)),
1842 "expected RecursionLimitExceeded when edge budget is exhausted, got {result:?}"
1843 );
1844 }
1845
1846 #[test]
1847 fn measuring_respects_total_edge_budget() {
1848 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1849 let outlines = test_outlines(&font);
1850 let (root_gid, root_cov_idx) = first_nested_varc_edge(&outlines)
1851 .expect("expected at least one VARC->VARC component edge in fixture font");
1852 let mut walk = Walk::new();
1853 walk.edges_left = 0;
1854 let leaf = outlines
1855 .measure(root_gid, root_cov_idx, &NullContext, &mut walk)
1856 .unwrap();
1857 assert_eq!(leaf, 0);
1858 assert_eq!(walk.edges_left, 0);
1859 assert!(walk.truncated);
1860 }
1861
1862 #[test]
1865 fn axis_index_scratch_holds_the_longest_list() {
1866 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1867 let outlines = test_outlines(&font);
1868 let varc = font.varc().unwrap();
1869 let lists = varc.axis_indices_list().unwrap().unwrap().count() as usize;
1870 let longest = (0..lists)
1871 .map(|nth| varc.axis_indices(nth).unwrap().iter().count())
1872 .max()
1873 .unwrap();
1874 assert_eq!(outlines.index_count, longest);
1875 let layout = Layout {
1879 depth: 2,
1880 axis_count: 2,
1881 index_count: 5,
1882 region_count: 3,
1883 leaf: 0,
1884 };
1885 let mut buf = alloc::vec![0u8; layout.packed_len().unwrap()];
1886 let memory = layout.carve(&mut buf).unwrap();
1887 assert_eq!(memory.scratch.axis_indices.len(), 5);
1888 assert_eq!(memory.scratch.axis_values.len(), 5);
1889 assert_eq!(memory.scratch.deltas.len(), TRANSFORM_FIELD_COUNT);
1890 assert_eq!(memory.font_coords.len(), 2);
1891 assert_eq!(memory.coords.len(), 4);
1892 }
1893
1894 #[test]
1897 fn the_tree_is_measured_on_first_use() {
1898 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1899 let outlines = test_outlines(&font);
1900 let (root_gid, _) = first_nested_varc_edge(&outlines).unwrap();
1901 let outline = outlines.outline(root_gid).unwrap();
1902 assert_eq!(
1903 outline.measured.state.load(Ordering::Relaxed),
1904 Measured::UNMEASURED
1905 );
1906 let size = outlines.required_memory_size(&outline, &NullContext);
1907 assert!(size > 0);
1908 let state = outline.measured.state.load(Ordering::Relaxed);
1909 assert_eq!(state & 3, Measured::MEASURED);
1910 let copy = outline.clone();
1911 assert_eq!(copy.measured.state.load(Ordering::Relaxed), state);
1912 assert_eq!(outlines.required_memory_size(©, &NullContext), size);
1913 let uncovered = (0..font.maxp().unwrap().num_glyphs() as u32)
1915 .map(GlyphId::new)
1916 .find(|gid| !outlines.covers(*gid))
1917 .unwrap();
1918 assert!(outlines.outline(uncovered).is_none());
1919 }
1920
1921 #[test]
1922 fn the_layout_counts_the_nesting_and_the_largest_leaf() {
1923 struct LeafSizes;
1926
1927 impl VarcContext for LeafSizes {
1928 fn coords(&self) -> &[F2Dot14] {
1929 &[]
1930 }
1931
1932 fn draw_glyph(
1933 &self,
1934 _glyph: GlyphId,
1935 _coords: &[F2Dot14],
1936 _ppem: Option<f32>,
1937 _buf: &mut [u8],
1938 _pen: &mut dyn PathSink,
1939 ) -> Result<(), Error> {
1940 Ok(())
1941 }
1942
1943 fn glyph_buffer_size(&self, glyph: GlyphId) -> usize {
1944 glyph.to_u32() as usize
1945 }
1946 }
1947
1948 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1949 let outlines = test_outlines(&font);
1950 let (root_gid, _) = first_nested_varc_edge(&outlines).unwrap();
1951 let outline = outlines.outline(root_gid).unwrap();
1952 let layout = outlines.layout_of(&outline, &LeafSizes).unwrap();
1953 assert!(layout.depth >= 2, "a nested composite is two levels deep");
1954 assert_eq!(layout.axis_count, outlines.axis_count);
1955 assert_eq!(layout.index_count, outlines.index_count);
1956 assert_eq!(layout.region_count, outlines.region_count());
1957 assert!(layout.leaf > 0);
1958 let packed_len = layout.packed_len().unwrap();
1961 let mut buf = alloc::vec![0u8; packed_len];
1962 assert!(layout.carve(&mut buf).is_some());
1963 let mut short = alloc::vec![0u8; packed_len - layout.leaf - 1];
1964 assert!(layout.carve(&mut short).is_none());
1965 let huge = Layout {
1967 region_count: usize::MAX / 2,
1968 ..layout
1969 };
1970 assert!(huge.packed_len().is_none());
1971 }
1972
1973 #[test]
1977 fn leaf_components_count_against_the_edge_limit() {
1978 use crate::model::{
1979 glyph::outline::{DrawMemory, DrawOptions, OutlineSource},
1980 Blob, Font,
1981 };
1982 use alloc::{sync::Arc, vec, vec::Vec};
1983 use types::Tag;
1984 let count = MAX_COMPOSITE_EDGES + 1;
1985 let mut varc = vec![0u8; 24];
1986 varc[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1987 varc[4..8].copy_from_slice(&24u32.to_be_bytes());
1988 varc.extend_from_slice(&[0, 1, 0, 1, 0, 0]);
1990 let offset = varc.len() as u32;
1991 varc[20..24].copy_from_slice(&offset.to_be_bytes());
1992 varc.extend_from_slice(&1u32.to_be_bytes());
1994 varc.push(4);
1995 varc.extend_from_slice(&1u32.to_be_bytes());
1996 varc.extend_from_slice(&((count * 3 + 1) as u32).to_be_bytes());
1997 for _ in 0..count {
1998 varc.extend_from_slice(&[0, 0, 1]);
2000 }
2001 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag| {
2002 if tag == Tag::new(b"VARC") {
2003 return Some(Blob::from(varc.clone()));
2004 }
2005 let base = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).ok()?;
2006 base.table_data(tag)
2007 .map(|data| Blob::from(data.as_bytes().to_vec()))
2008 });
2009 let font = Font::new(source, 0).unwrap();
2010 let outline = font.outline_glyphs().get(GlyphId::new(0)).unwrap();
2011 assert_eq!(outline.source(), OutlineSource::Varc);
2012 assert_eq!(outline.required_memory_size(), 0);
2013 let mut path = Vec::<PathElement>::new();
2014 let result = outline.draw(&DrawOptions::default(), DrawMemory::Scratch, &mut path);
2015 assert_eq!(result.err(), Some(Error::LimitExceeded));
2016 assert!(path.is_empty());
2017 }
2018
2019 #[test]
2022 fn a_cycle_draws_nothing() {
2023 use crate::model::{
2024 glyph::outline::{DrawMemory, DrawOptions},
2025 Blob, Font,
2026 };
2027 use alloc::{sync::Arc, vec, vec::Vec};
2028 use types::Tag;
2029 const LEAF: u8 = 2;
2030 let mut varc = vec![0u8; 24];
2031 varc[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
2032 varc[4..8].copy_from_slice(&24u32.to_be_bytes());
2033 varc.extend_from_slice(&[0, 1, 0, 2, 0, 1, 0, 3]);
2035 let offset = varc.len() as u32;
2036 varc[20..24].copy_from_slice(&offset.to_be_bytes());
2037 let records: [&[u8]; 2] = [&[0, 0, 3], &[0, 0, 1, 0, 0, LEAF]];
2040 varc.extend_from_slice(&2u32.to_be_bytes());
2041 varc.push(4);
2042 let mut end = 1u32;
2043 varc.extend_from_slice(&end.to_be_bytes());
2044 for record in records {
2045 end += record.len() as u32;
2046 varc.extend_from_slice(&end.to_be_bytes());
2047 }
2048 records
2049 .iter()
2050 .for_each(|record| varc.extend_from_slice(record));
2051 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag| {
2052 if tag == Tag::new(b"VARC") {
2053 return Some(Blob::from(varc.clone()));
2054 }
2055 let base = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).ok()?;
2056 base.table_data(tag)
2057 .map(|data| Blob::from(data.as_bytes().to_vec()))
2058 });
2059 let draw = |font: &Font, glyph: u8| {
2060 let mut path = Vec::<PathElement>::new();
2061 let outline = font
2062 .outline_glyphs()
2063 .get(GlyphId::new(glyph as u32))
2064 .unwrap();
2065 outline
2066 .draw(&DrawOptions::default(), DrawMemory::Scratch, &mut path)
2067 .unwrap();
2068 path
2069 };
2070 let base = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2071 let leaf = draw(&base, LEAF);
2072 assert!(!leaf.is_empty());
2073 assert!(!draw(&base, 1).is_empty());
2074 assert_eq!(draw(&Font::new(source, 0).unwrap(), 1), leaf);
2075 }
2076}