1use read_fonts::{
4 tables::{
5 layout::Condition,
6 varc::{
7 DecomposedTransform, MultiItemVariationStore, SparseVariationRegionList, Varc,
8 VarcComponent, VarcFlags,
9 },
10 variations::NO_VARIATION_INDEX,
11 },
12 types::{F2Dot14, GlyphId, Matrix},
13 FontRef, ReadError, TableProvider,
14};
15
16use crate::{
17 collections::SmallVec,
18 instance::Size,
19 outline::{cff, glyf, metrics::GlyphHMetrics, pen::PathStyle, DrawError, OutlinePen},
20 provider::MetadataProvider,
21 GLYF_COMPOSITE_RECURSION_LIMIT,
22};
23
24#[cfg(feature = "libm")]
25#[allow(unused_imports)]
26use core_maths::CoreFloat;
27
28use super::OutlineKind;
29
30type GlyphStack = SmallVec<GlyphId, 8>;
31type CoordVec = SmallVec<F2Dot14, 64>;
32type AxisIndexVec = SmallVec<u16, 64>;
33type AxisValueVec = SmallVec<f32, 64>;
34type DeltaVec = SmallVec<f32, 64>;
35type ScalarCacheVec = SmallVec<f32, 128>;
36type Affine = Matrix<f32>;
37
38struct Scratchpad {
39 deltas: DeltaVec,
40 axis_indices: AxisIndexVec,
41 axis_values: AxisValueVec,
42}
43
44impl Scratchpad {
45 fn new() -> Self {
46 Self {
47 deltas: DeltaVec::new(),
48 axis_indices: AxisIndexVec::new(),
49 axis_values: AxisValueVec::new(),
50 }
51 }
52}
53
54struct VarcSharedContext<'a, 'b> {
55 font_coords: &'b [F2Dot14],
56 size: Size,
57 path_style: PathStyle,
58 coverage: &'b read_fonts::tables::layout::CoverageTable<'a>,
59 var_store: Option<&'b MultiItemVariationStore<'a>>,
60 store_regions: Option<(
61 &'b MultiItemVariationStore<'a>,
62 &'b SparseVariationRegionList<'a>,
63 )>,
64}
65
66#[derive(Clone)]
67enum BaseOutlines<'a> {
68 Glyf(glyf::Outlines<'a>),
69 Cff(cff::Outlines<'a>),
70}
71
72impl<'a> BaseOutlines<'a> {
73 fn glyph_count(&self) -> u32 {
74 match self {
75 Self::Glyf(glyf) => glyf.glyph_count() as u32,
76 Self::Cff(cff) => cff.glyph_count() as u32,
77 }
78 }
79
80 fn font(&self) -> &FontRef<'a> {
81 match self {
82 Self::Glyf(glyf) => &glyf.font,
83 Self::Cff(cff) => &cff.font,
84 }
85 }
86
87 fn base_outline_kind(&self, glyph_id: GlyphId) -> Option<OutlineKind<'a>> {
88 match self {
89 Self::Glyf(glyf) => Some(OutlineKind::Glyf(
90 glyf.clone(),
91 glyf.outline(glyph_id).ok()?,
92 )),
93 Self::Cff(cff) => Some(OutlineKind::Cff(
94 cff.clone(),
95 glyph_id,
96 cff.subfont_index(glyph_id),
97 )),
98 }
99 }
100
101 fn base_outline_memory(&self, glyph_id: GlyphId) -> usize {
102 match self {
103 Self::Glyf(glyf) => glyf
104 .outline(glyph_id)
105 .ok()
106 .map(|outline| outline.required_buffer_size(super::Hinting::None))
107 .unwrap_or(0),
108 Self::Cff(..) => 0,
109 }
110 }
111}
112
113#[derive(Clone)]
114pub(crate) struct Outlines<'a> {
115 varc: Varc<'a>,
116 coverage: read_fonts::tables::layout::CoverageTable<'a>,
117 var_store: Option<MultiItemVariationStore<'a>>,
118 regions: Option<SparseVariationRegionList<'a>>,
119 base: BaseOutlines<'a>,
120 glyph_metrics: GlyphHMetrics<'a>,
121 units_per_em: u16,
122 axis_count: usize,
123}
124
125#[derive(Clone, Copy)]
126pub(crate) struct Outline {
127 pub(crate) glyph_id: GlyphId,
128 pub(crate) coverage_index: u16,
129 max_component_memory: usize,
130}
131
132impl Outline {
133 pub fn required_buffer_size(&self) -> usize {
134 self.max_component_memory
135 }
136}
137
138impl<'a> Outlines<'a> {
139 pub fn new(font: &FontRef<'a>) -> Option<Self> {
140 let varc = font.varc().ok()?;
141 if let Some(glyf) = glyf::Outlines::new(font) {
142 return Self::from_base(font, varc, BaseOutlines::Glyf(glyf));
143 }
144 if let Some(cff) = cff::Outlines::new(font) {
145 return Self::from_base(font, varc, BaseOutlines::Cff(cff));
146 }
147 None
148 }
149
150 fn from_base(font: &FontRef<'a>, varc: Varc<'a>, base: BaseOutlines<'a>) -> Option<Self> {
151 let glyph_metrics = GlyphHMetrics::new(font)?;
152 let units_per_em = font.head().ok()?.units_per_em();
153 let axis_count = font.axes().len();
154 let coverage = varc.coverage().ok()?;
155 let var_store = varc.multi_var_store().transpose().ok()?;
156 let regions = var_store
157 .as_ref()
158 .map(|s| s.region_list())
159 .transpose()
160 .ok()?;
161 Some(Self {
162 varc,
163 coverage,
164 var_store,
165 regions,
166 base,
167 glyph_metrics,
168 units_per_em,
169 axis_count,
170 })
171 }
172
173 pub fn units_per_em(&self) -> u16 {
174 self.units_per_em
175 }
176
177 pub fn glyph_count(&self) -> u32 {
178 self.base.glyph_count()
179 }
180
181 pub fn prefer_interpreter(&self) -> bool {
182 false
183 }
184
185 pub fn fractional_size_hinting(&self) -> bool {
186 false
187 }
188
189 pub fn font(&self) -> &FontRef<'a> {
190 self.base.font()
191 }
192
193 pub(crate) fn fallback_outline_kind(&self, glyph_id: GlyphId) -> Option<OutlineKind<'a>> {
194 self.base.base_outline_kind(glyph_id)
195 }
196
197 pub fn outline(&self, glyph_id: GlyphId) -> Result<Option<Outline>, ReadError> {
198 let Some(coverage_index) = self.coverage.get(glyph_id) else {
199 return Ok(None);
200 };
201 let max_component_memory = self.compute_max_component_memory(glyph_id, coverage_index)?;
202 Ok(Some(Outline {
203 glyph_id,
204 coverage_index,
205 max_component_memory,
206 }))
207 }
208
209 fn coverage_index(&self, glyph_id: GlyphId) -> Result<Option<u16>, ReadError> {
211 Ok(self.coverage.get(glyph_id))
212 }
213
214 fn compute_max_component_memory(
215 &self,
216 glyph_id: GlyphId,
217 coverage_index: u16,
218 ) -> Result<usize, ReadError> {
219 let mut stack = GlyphStack::new();
220 self.max_component_memory_for_glyph(glyph_id, coverage_index, &mut stack)
221 }
222
223 fn max_component_memory_for_glyph(
224 &self,
225 glyph_id: GlyphId,
226 coverage_index: u16,
227 stack: &mut GlyphStack,
228 ) -> Result<usize, ReadError> {
229 if stack.contains(&glyph_id) {
230 return Ok(0);
231 }
232 if stack.len() >= GLYF_COMPOSITE_RECURSION_LIMIT {
233 return Ok(0);
234 }
235 stack.push(glyph_id);
236 let mut max_memory = 0usize;
237 let glyph = self.varc.glyph(coverage_index as usize)?;
238 for component in glyph.components() {
239 let component = component?;
240 let component_gid = component.gid();
241 let component_memory = if component_gid == glyph_id {
242 self.base.base_outline_memory(component_gid)
243 } else if let Some(coverage_index) = self.coverage_index(component_gid)? {
244 self.max_component_memory_for_glyph(component_gid, coverage_index, stack)?
245 } else {
246 self.base.base_outline_memory(component_gid)
247 };
248 max_memory = max_memory.max(component_memory);
249 }
250 stack.pop();
251 Ok(max_memory)
252 }
253
254 pub fn draw(
255 &self,
256 outline: &Outline,
257 buf: &mut [u8],
258 size: Size,
259 coords: &[F2Dot14],
260 path_style: PathStyle,
261 pen: &mut impl OutlinePen,
262 ) -> Result<(), DrawError> {
263 let mut font_coords = CoordVec::new();
264 expand_coords(&mut font_coords, self.axis_count, coords);
265 let mut stack = GlyphStack::new();
266 let pen: &mut dyn OutlinePen = pen;
267 let mut scalar_cache = self
268 .scalar_cache_from_store(self.var_store.as_ref())?
269 .unwrap();
270 let mut scratch = Scratchpad::new();
271 let ctx = VarcSharedContext {
272 font_coords: &font_coords,
273 size,
274 path_style,
275 coverage: &self.coverage,
276 var_store: self.var_store.as_ref(),
277 store_regions: self.var_store.as_ref().zip(self.regions.as_ref()),
278 };
279 self.draw_glyph(
280 outline.glyph_id,
281 outline.coverage_index,
282 &font_coords,
283 Affine::IDENTITY,
284 &ctx,
285 buf,
286 pen,
287 &mut stack,
288 &mut scalar_cache,
289 &mut scratch,
290 )
291 }
292
293 pub fn draw_unscaled(
294 &self,
295 outline: &Outline,
296 buf: &mut [u8],
297 coords: &[F2Dot14],
298 pen: &mut impl OutlinePen,
299 ) -> Result<i32, DrawError> {
300 let size = Size::unscaled();
301 self.draw(outline, buf, size, coords, PathStyle::default(), pen)?;
302 Ok(self.glyph_metrics.advance_width(outline.glyph_id, coords))
303 }
304
305 #[allow(clippy::too_many_arguments)]
306 fn draw_glyph(
307 &self,
308 glyph_id: GlyphId,
309 coverage_index: u16,
310 current_coords: &[F2Dot14],
311 parent_matrix: Affine,
312 ctx: &VarcSharedContext<'a, '_>,
313 buf: &mut [u8],
314 pen: &mut dyn OutlinePen,
315 stack: &mut GlyphStack,
316 scalar_cache: &mut ScalarCache,
317 scratch: &mut Scratchpad,
318 ) -> Result<(), DrawError> {
319 if stack.len() >= GLYF_COMPOSITE_RECURSION_LIMIT {
320 return Err(DrawError::RecursionLimitExceeded(glyph_id));
321 }
322 let glyph = self.varc.glyph(coverage_index as usize)?;
323 stack.push(glyph_id);
324 let coverage = ctx.coverage;
325 let store_regions = ctx.store_regions;
326 let mut component_coords_buffer = CoordVec::new();
327 let mut child_scalar_cache: Option<ScalarCache> = None;
328 for component in glyph.components() {
329 let component = component?;
330 if !self.component_condition_met(
331 &component,
332 current_coords,
333 scalar_cache,
334 scratch,
335 store_regions,
336 )? {
337 continue;
338 }
339 let component_gid = component.gid();
340 let flags = component.flags();
341
342 let coords_the_same = !flags.contains(VarcFlags::HAVE_AXES)
343 && !flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES);
344
345 let component_coords = if coords_the_same {
346 current_coords
347 } else {
348 self.component_coords(
349 &component,
350 current_coords,
351 &mut component_coords_buffer,
352 scalar_cache,
353 scratch,
354 ctx.font_coords,
355 store_regions,
356 )?;
357 component_coords_buffer.as_slice()
358 };
359
360 let mut transform = *component.transform();
361 self.apply_transform_variations(
362 &component,
363 current_coords,
364 &mut transform,
365 scalar_cache,
366 scratch,
367 store_regions,
368 )?;
369 let scale = ctx.size.linear_scale(self.units_per_em);
370 let matrix = parent_matrix * scale_matrix(transform.matrix(), scale);
371 if component_gid != glyph_id {
372 if let Some(coverage_index) = coverage.get(component_gid) {
373 if !stack.contains(&component_gid) {
374 if coords_the_same {
376 self.draw_glyph(
377 component_gid,
378 coverage_index,
379 current_coords,
380 matrix,
381 ctx,
382 buf,
383 pen,
384 stack,
385 scalar_cache,
386 scratch,
387 )?;
388 } else {
389 if let Some(ref mut cache) = child_scalar_cache {
390 cache.values.fill(ScalarCache::INVALID);
391 } else {
392 child_scalar_cache = self.scalar_cache_from_store(ctx.var_store)?;
393 }
394 self.draw_glyph(
395 component_gid,
396 coverage_index,
397 component_coords,
398 matrix,
399 ctx,
400 buf,
401 pen,
402 stack,
403 child_scalar_cache.as_mut().unwrap(),
404 scratch,
405 )?;
406 }
407 continue;
408 }
409 }
410 }
411 let mut transform_pen = TransformPen::new(pen, matrix);
412 self.draw_base_glyph(
413 component_gid,
414 component_coords,
415 ctx.size,
416 ctx.path_style,
417 buf,
418 &mut transform_pen,
419 )?;
420 }
421 stack.pop();
422 Ok(())
423 }
424
425 fn draw_base_glyph(
426 &self,
427 glyph_id: GlyphId,
428 coords: &[F2Dot14],
429 size: Size,
430 path_style: PathStyle,
431 buf: &mut [u8],
432 pen: &mut impl OutlinePen,
433 ) -> Result<(), DrawError> {
434 let Some(kind) = self.base.base_outline_kind(glyph_id) else {
435 return Err(DrawError::GlyphNotFound(glyph_id));
436 };
437 let settings =
438 crate::outline::DrawSettings::unhinted(size, crate::instance::LocationRef::new(coords))
439 .with_path_style(path_style)
440 .with_memory(Some(buf));
441 crate::outline::OutlineGlyph { kind }.draw(settings, pen)?;
442 Ok(())
443 }
444
445 #[allow(clippy::too_many_arguments)]
446 fn component_coords(
447 &self,
448 component: &VarcComponent<'_>,
449 current_coords: &[F2Dot14],
450 coords: &mut CoordVec,
451 scalar_cache: &mut ScalarCache,
452 scratch: &mut Scratchpad,
453 font_coords: &[F2Dot14],
454 store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
455 ) -> Result<(), DrawError> {
456 let flags = component.flags();
457 if flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES) {
458 expand_coords(coords, font_coords.len(), font_coords);
459 } else {
460 expand_coords(coords, current_coords.len(), current_coords);
461 }
462
463 if !flags.contains(VarcFlags::HAVE_AXES) {
464 return Ok(());
465 }
466
467 let axis_indices_index = component
468 .axis_indices_index()
469 .ok_or(ReadError::MalformedData("Missing axisIndicesIndex"))?;
470 let num_axes = self.axis_indices(axis_indices_index as usize, &mut scratch.axis_indices)?;
471
472 self.axis_values(component, num_axes, &mut scratch.axis_values)?;
473 if let Some(var_idx) = component.axis_values_var_index() {
474 let (store, regions) = store_regions.ok_or(ReadError::NullOffset)?;
475 compute_tuple_deltas(
476 store,
477 regions,
478 var_idx,
479 current_coords,
480 scratch.axis_indices.len(),
481 scalar_cache,
482 &mut scratch.deltas,
483 )?;
484 for (value, delta) in scratch.axis_values.iter_mut().zip(scratch.deltas.iter()) {
485 *value += *delta;
486 }
487 }
488
489 for (axis_index, value) in scratch
490 .axis_indices
491 .iter()
492 .zip(scratch.axis_values.iter().copied())
493 {
494 let Some(slot) = coords.get_mut(*axis_index as usize) else {
495 return Err(DrawError::Read(ReadError::OutOfBounds));
496 };
497 let raw = value.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16;
498 *slot = F2Dot14::from_bits(raw);
499 }
500 Ok(())
501 }
502
503 fn axis_indices(&self, nth: usize, out: &mut AxisIndexVec) -> Result<usize, DrawError> {
504 let packed = self.varc.axis_indices(nth)?;
505 out.clear();
506 for value in packed.iter() {
507 out.push(value as u16);
508 }
509 Ok(out.len())
510 }
511
512 fn axis_values(
513 &self,
514 component: &VarcComponent<'_>,
515 count: usize,
516 out: &mut AxisValueVec,
517 ) -> Result<(), DrawError> {
518 let Some(packed) = component.axis_values() else {
519 out.clear();
520 return Ok(());
521 };
522 out.resize_and_fill(count, 0.0);
523 for (slot, value) in out.iter_mut().zip(packed.iter().by_ref().take(count)) {
524 *slot = value as f32;
525 }
526 Ok(())
527 }
528
529 fn apply_transform_variations(
530 &self,
531 component: &VarcComponent<'_>,
532 coords: &[F2Dot14],
533 transform: &mut DecomposedTransform,
534 scalar_cache: &mut ScalarCache,
535 scratch: &mut Scratchpad,
536 store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
537 ) -> Result<(), DrawError> {
538 let Some(var_idx) = component.transform_var_index() else {
539 return Ok(());
540 };
541
542 let flags = component.flags();
543
544 const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
546 VarcFlags::HAVE_TRANSLATE_X.bits()
547 | VarcFlags::HAVE_TRANSLATE_Y.bits()
548 | VarcFlags::HAVE_ROTATION.bits()
549 | VarcFlags::HAVE_SCALE_X.bits()
550 | VarcFlags::HAVE_SCALE_Y.bits()
551 | VarcFlags::HAVE_SKEW_X.bits()
552 | VarcFlags::HAVE_SKEW_Y.bits()
553 | VarcFlags::HAVE_TCENTER_X.bits()
554 | VarcFlags::HAVE_TCENTER_Y.bits(),
555 );
556 let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
557 if field_count == 0 {
558 return Ok(());
559 }
560
561 let (store, regions) = store_regions.ok_or(ReadError::NullOffset)?;
562 compute_tuple_deltas(
563 store,
564 regions,
565 var_idx,
566 coords,
567 field_count,
568 scalar_cache,
569 &mut scratch.deltas,
570 )?;
571
572 let mut delta_iter = scratch.deltas.iter().copied();
574
575 if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
576 let delta = delta_iter.next().unwrap_or(0.0);
577 transform.set_translate_x(transform.translate_x() + delta);
578 }
579 if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
580 let delta = delta_iter.next().unwrap_or(0.0);
581 transform.set_translate_y(transform.translate_y() + delta);
582 }
583 if flags.contains(VarcFlags::HAVE_ROTATION) {
584 let delta = delta_iter.next().unwrap_or(0.0);
585 transform.set_rotation(transform.rotation() + delta / 4096.0);
586 }
587 if flags.contains(VarcFlags::HAVE_SCALE_X) {
588 let delta = delta_iter.next().unwrap_or(0.0);
589 transform.set_scale_x(transform.scale_x() + delta / 1024.0);
590 }
591 if flags.contains(VarcFlags::HAVE_SCALE_Y) {
592 let delta = delta_iter.next().unwrap_or(0.0);
593 transform.set_scale_y(transform.scale_y() + delta / 1024.0);
594 }
595 const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
596 VarcFlags::HAVE_SKEW_X.bits()
597 | VarcFlags::HAVE_SKEW_Y.bits()
598 | VarcFlags::HAVE_TCENTER_X.bits()
599 | VarcFlags::HAVE_TCENTER_Y.bits(),
600 );
601 if flags.intersects(SKEW_OR_CENTER) {
602 if flags.contains(VarcFlags::HAVE_SKEW_X) {
603 let delta = delta_iter.next().unwrap_or(0.0);
604 transform.set_skew_x(transform.skew_x() + delta / 4096.0);
605 }
606 if flags.contains(VarcFlags::HAVE_SKEW_Y) {
607 let delta = delta_iter.next().unwrap_or(0.0);
608 transform.set_skew_y(transform.skew_y() + delta / 4096.0);
609 }
610 if flags.contains(VarcFlags::HAVE_TCENTER_X) {
611 let delta = delta_iter.next().unwrap_or(0.0);
612 transform.set_center_x(transform.center_x() + delta);
613 }
614 if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
615 let delta = delta_iter.next().unwrap_or(0.0);
616 transform.set_center_y(transform.center_y() + delta);
617 }
618 }
619
620 if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
621 transform.set_scale_y(transform.scale_x());
622 }
623 Ok(())
624 }
625
626 fn component_condition_met(
627 &self,
628 component: &VarcComponent<'_>,
629 coords: &[F2Dot14],
630 scalar_cache: &mut ScalarCache,
631 scratch: &mut Scratchpad,
632 store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
633 ) -> Result<bool, DrawError> {
634 let Some(condition_index) = component.condition_index() else {
635 return Ok(true);
636 };
637 let Some(condition_list) = self.varc.condition_list() else {
638 return Err(DrawError::Read(ReadError::NullOffset));
639 };
640 let condition_list = condition_list?;
641 let condition = condition_list.conditions().get(condition_index as usize)?;
642 let (store, regions) = store_regions.ok_or(ReadError::NullOffset)?;
643 Self::eval_condition(&condition, coords, store, regions, scalar_cache, scratch)
644 }
645
646 fn eval_condition(
647 condition: &Condition<'a>,
648 coords: &[F2Dot14],
649 var_store: &MultiItemVariationStore<'a>,
650 regions: &SparseVariationRegionList<'a>,
651 scalar_cache: &mut ScalarCache,
652 scratch: &mut Scratchpad,
653 ) -> Result<bool, DrawError> {
654 match condition {
655 Condition::Format1AxisRange(condition) => {
656 let axis_index = condition.axis_index() as usize;
657 let coord = coords.get(axis_index).copied().unwrap_or(F2Dot14::ZERO);
658 Ok(coord >= condition.filter_range_min_value()
659 && coord <= condition.filter_range_max_value())
660 }
661 Condition::Format2VariableValue(condition) => {
662 let default_value = condition.default_value() as f32;
663 let var_idx = condition.var_index();
664 compute_tuple_deltas(
665 var_store,
666 regions,
667 var_idx,
668 coords,
669 1,
670 scalar_cache,
671 &mut scratch.deltas,
672 )?;
673 let delta = scratch.deltas.first().copied().unwrap_or(0.0);
674 Ok(default_value + delta > 0.0)
675 }
676 Condition::Format3And(condition) => {
677 for nested in condition.conditions().iter() {
678 let nested = nested?;
679 if !Self::eval_condition(
680 &nested,
681 coords,
682 var_store,
683 regions,
684 scalar_cache,
685 scratch,
686 )? {
687 return Ok(false);
688 }
689 }
690 Ok(true)
691 }
692 Condition::Format4Or(condition) => {
693 for nested in condition.conditions().iter() {
694 let nested = nested?;
695 if Self::eval_condition(
696 &nested,
697 coords,
698 var_store,
699 regions,
700 scalar_cache,
701 scratch,
702 )? {
703 return Ok(true);
704 }
705 }
706 Ok(false)
707 }
708 Condition::Format5Negate(condition) => {
709 let nested = condition.condition()?;
710 Ok(!Self::eval_condition(
711 &nested,
712 coords,
713 var_store,
714 regions,
715 scalar_cache,
716 scratch,
717 )?)
718 }
719 }
720 }
721
722 fn scalar_cache_from_store(
723 &self,
724 store: Option<&MultiItemVariationStore<'a>>,
725 ) -> Result<Option<ScalarCache>, DrawError> {
726 let Some(store) = store else {
727 return Ok(None);
728 };
729 let region_count = store.region_list()?.region_count() as usize;
730 Ok(Some(ScalarCache::new(region_count)))
731 }
732}
733
734struct ScalarCache {
735 values: ScalarCacheVec,
736}
737
738impl ScalarCache {
739 const INVALID: f32 = 2.0; fn new(count: usize) -> Self {
742 Self {
743 values: ScalarCacheVec::with_len(count, Self::INVALID),
744 }
745 }
746
747 fn get(&self, index: usize) -> f32 {
748 self.values.get(index).copied().unwrap_or(Self::INVALID)
749 }
750
751 fn set(&mut self, index: usize, value: f32) {
752 if let Some(slot) = self.values.get_mut(index) {
753 *slot = value;
754 }
755 }
756}
757
758fn expand_coords(out: &mut CoordVec, axis_count: usize, coords: &[F2Dot14]) {
759 out.resize_and_fill(axis_count, F2Dot14::ZERO);
760 for (slot, value) in out.iter_mut().zip(coords.iter().copied()) {
761 *slot = value;
762 }
763}
764
765fn compute_tuple_deltas(
766 store: &MultiItemVariationStore,
767 regions: &SparseVariationRegionList,
768 var_idx: u32,
769 coords: &[F2Dot14],
770 tuple_len: usize,
771 cache: &mut ScalarCache,
772 out: &mut DeltaVec,
773) -> Result<(), ReadError> {
774 out.resize_and_fill(tuple_len, 0.0);
775 if tuple_len == 0 || var_idx == NO_VARIATION_INDEX {
776 return Ok(());
777 }
778 let outer = (var_idx >> 16) as usize;
779 let inner = (var_idx & 0xFFFF) as usize;
780 let data = store
781 .variation_data()
782 .get(outer)
783 .map_err(|_| ReadError::InvalidCollectionIndex(outer as _))?;
784 let region_indices = data.region_indices();
785 let mut deltas = data.delta_set(inner)?.fetcher();
786 let regions = regions.regions();
787 let out_slice = out.as_mut_slice();
788
789 let mut skip = 0;
790 for region_index in region_indices.iter() {
791 let region_idx = region_index.get() as usize;
792 let mut scalar = cache.get(region_idx);
793 if scalar >= 2.0 {
794 scalar = regions.get(region_idx)?.compute_scalar_f32(coords);
795 cache.set(region_idx, scalar);
796 }
797 if scalar == 0.0 {
799 skip += out_slice.len();
800 } else {
801 if skip != 0 {
802 deltas.skip(skip)?;
803 skip = 0;
804 }
805 deltas.add_to_f32_scaled(out_slice, scalar)?;
806 }
807 }
808
809 Ok(())
810}
811
812#[inline(always)]
813fn scale_matrix(mut m: Affine, s: f32) -> Affine {
814 m.dx *= s;
815 m.dy *= s;
816 m
817}
818
819struct TransformPen<'a, P: OutlinePen + ?Sized> {
820 pen: &'a mut P,
821 matrix: Affine,
822}
823
824impl<'a, P: OutlinePen + ?Sized> TransformPen<'a, P> {
825 fn new(pen: &'a mut P, matrix: Affine) -> Self {
826 Self { pen, matrix }
827 }
828
829 #[inline(always)]
830 fn transform(&self, x: f32, y: f32) -> (f32, f32) {
831 self.matrix.transform(x, y)
832 }
833}
834
835impl<P: OutlinePen + ?Sized> OutlinePen for TransformPen<'_, P> {
836 fn move_to(&mut self, x: f32, y: f32) {
837 let (x, y) = self.transform(x, y);
838 self.pen.move_to(x, y);
839 }
840
841 fn line_to(&mut self, x: f32, y: f32) {
842 let (x, y) = self.transform(x, y);
843 self.pen.line_to(x, y);
844 }
845
846 fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
847 let (cx0, cy0) = self.transform(cx0, cy0);
848 let (x, y) = self.transform(x, y);
849 self.pen.quad_to(cx0, cy0, x, y);
850 }
851
852 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
853 let (cx0, cy0) = self.transform(cx0, cy0);
854 let (cx1, cy1) = self.transform(cx1, cy1);
855 let (x, y) = self.transform(x, y);
856 self.pen.curve_to(cx0, cy0, cx1, cy1, x, y);
857 }
858
859 fn close(&mut self) {
860 self.pen.close();
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use crate::outline::pen::PathElement;
868 use read_fonts::{FontRef, TableProvider};
869
870 fn coord(value: f32) -> F2Dot14 {
871 F2Dot14::from_f32(value)
872 }
873
874 fn assert_close(actual: f32, expected: f32) {
875 let diff = (actual - expected).abs();
876 assert!(
877 diff <= 1e-6,
878 "expected {expected}, got {actual}, diff {diff}"
879 );
880 }
881
882 fn path_head_signature(path: &[PathElement], n: usize) -> Vec<String> {
883 path.iter()
884 .take(n)
885 .map(|el| match *el {
886 PathElement::MoveTo { x, y } => format!("M{:.2},{:.2}", x, y),
887 PathElement::LineTo { x, y } => format!("L{:.2},{:.2}", x, y),
888 PathElement::QuadTo { cx0, cy0, x, y } => {
889 format!("Q{:.2},{:.2} {:.2},{:.2}", cx0, cy0, x, y)
890 }
891 PathElement::CurveTo {
892 cx0,
893 cy0,
894 cx1,
895 cy1,
896 x,
897 y,
898 } => format!(
899 "C{:.2},{:.2} {:.2},{:.2} {:.2},{:.2}",
900 cx0, cy0, cx1, cy1, x, y
901 ),
902 PathElement::Close => "Z".to_string(),
903 })
904 .collect()
905 }
906
907 #[test]
908 fn expand_coords_pads_and_truncates() {
909 let mut out = CoordVec::new();
910 expand_coords(&mut out, 4, &[coord(0.25), coord(-0.5)]);
911 assert_eq!(
912 out.as_slice(),
913 &[coord(0.25), coord(-0.5), F2Dot14::ZERO, F2Dot14::ZERO]
914 );
915
916 expand_coords(&mut out, 1, &[coord(0.25), coord(-0.5)]);
917 assert_eq!(out.as_slice(), &[coord(0.25)]);
918 }
919
920 #[test]
921 fn scale_matrix_only_scales_translation() {
922 let matrix = Matrix::from_elements([1.0, 2.0, 3.0, 4.0, 5.0, -6.0]);
923 assert_eq!(
924 scale_matrix(matrix, 10.0).elements(),
925 [1.0, 2.0, 3.0, 4.0, 50.0, -60.0]
926 );
927 }
928
929 #[test]
930 fn compute_tuple_deltas_no_variation_index_is_noop_after_resize() {
931 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
932 let varc = font.varc().unwrap();
933 let store = varc.multi_var_store().unwrap().unwrap();
934 let regions = store.region_list().unwrap();
935 let mut cache = ScalarCache::new(regions.region_count() as usize);
936 let mut out = DeltaVec::new();
937 out.push(42.0);
938
939 compute_tuple_deltas(
940 &store,
941 ®ions,
942 NO_VARIATION_INDEX,
943 &[coord(0.0)],
944 3,
945 &mut cache,
946 &mut out,
947 )
948 .unwrap();
949 assert_eq!(out.as_slice(), &[0.0, 0.0, 0.0]);
950
951 compute_tuple_deltas(
952 &store,
953 ®ions,
954 NO_VARIATION_INDEX,
955 &[coord(0.0)],
956 0,
957 &mut cache,
958 &mut out,
959 )
960 .unwrap();
961 assert!(out.is_empty());
962 }
963
964 #[test]
965 fn compute_tuple_deltas_invalid_outer_index_errors() {
966 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
967 let varc = font.varc().unwrap();
968 let store = varc.multi_var_store().unwrap().unwrap();
969 let regions = store.region_list().unwrap();
970 let mut cache = ScalarCache::new(regions.region_count() as usize);
971 let mut out = DeltaVec::new();
972
973 let err = compute_tuple_deltas(&store, ®ions, 0xFFFF_0000, &[], 1, &mut cache, &mut out)
974 .unwrap_err();
975 assert!(matches!(err, ReadError::InvalidCollectionIndex(_)));
976 }
977
978 #[test]
979 fn compute_tuple_deltas_matches_manual_decode() {
980 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
981 let varc = font.varc().unwrap();
982 let store = varc.multi_var_store().unwrap().unwrap();
983 let regions = store.region_list().unwrap();
984 let region_list = regions.regions();
985 let coords = [coord(0.5); 8];
986
987 let mut tried = 0usize;
988 for (outer, data) in store.variation_data().iter().enumerate() {
989 let data = data.unwrap();
990 let region_count = data.region_indices().len();
991 if region_count == 0 {
992 continue;
993 }
994 let delta_set_count = data.delta_sets().unwrap().count() as usize;
995 for inner in 0..delta_set_count.min(3) {
996 let decoded = data.delta_set(inner).unwrap().iter().collect::<Vec<_>>();
997 if decoded.is_empty() || decoded.len() % region_count != 0 {
998 continue;
999 }
1000 let tuple_len = decoded.len() / region_count;
1001 let var_idx = ((outer as u32) << 16) | inner as u32;
1002
1003 let mut cache = ScalarCache::new(regions.region_count() as usize);
1004 let mut actual = DeltaVec::new();
1005 compute_tuple_deltas(
1006 &store,
1007 ®ions,
1008 var_idx,
1009 &coords,
1010 tuple_len,
1011 &mut cache,
1012 &mut actual,
1013 )
1014 .unwrap();
1015 let first = actual.as_slice().to_vec();
1016
1017 compute_tuple_deltas(
1019 &store,
1020 ®ions,
1021 var_idx,
1022 &coords,
1023 tuple_len,
1024 &mut cache,
1025 &mut actual,
1026 )
1027 .unwrap();
1028 assert_eq!(actual.as_slice(), first.as_slice());
1029
1030 let mut expected = vec![0.0f32; tuple_len];
1031 for (region_order, region_idx) in data.region_indices().iter().enumerate() {
1032 let scalar = region_list
1033 .get(region_idx.get() as usize)
1034 .unwrap()
1035 .compute_scalar_f32(&coords);
1036 if scalar == 0.0 {
1037 continue;
1038 }
1039 let base = region_order * tuple_len;
1040 for (i, slot) in expected.iter_mut().enumerate() {
1041 *slot += decoded[base + i] as f32 * scalar;
1042 }
1043 }
1044 assert_eq!(actual.len(), expected.len());
1045 for (a, e) in actual.iter().zip(expected.iter()) {
1046 assert_close(*a, *e);
1047 }
1048 tried += 1;
1049 }
1050 }
1051 assert!(tried > 0, "expected at least one tuple to be exercised");
1052 }
1053
1054 fn apply_transform_variations_reference(
1055 component: &VarcComponent<'_>,
1056 coords: &[F2Dot14],
1057 transform: &mut DecomposedTransform,
1058 var_store: Option<&MultiItemVariationStore<'_>>,
1059 regions: Option<&SparseVariationRegionList<'_>>,
1060 scalar_cache: &mut ScalarCache,
1061 deltas: &mut DeltaVec,
1062 ) -> Result<(), DrawError> {
1063 let Some(var_idx) = component.transform_var_index() else {
1064 return Ok(());
1065 };
1066 let flags = component.flags();
1067 const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
1068 VarcFlags::HAVE_TRANSLATE_X.bits()
1069 | VarcFlags::HAVE_TRANSLATE_Y.bits()
1070 | VarcFlags::HAVE_ROTATION.bits()
1071 | VarcFlags::HAVE_SCALE_X.bits()
1072 | VarcFlags::HAVE_SCALE_Y.bits()
1073 | VarcFlags::HAVE_SKEW_X.bits()
1074 | VarcFlags::HAVE_SKEW_Y.bits()
1075 | VarcFlags::HAVE_TCENTER_X.bits()
1076 | VarcFlags::HAVE_TCENTER_Y.bits(),
1077 );
1078 let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
1079 if field_count == 0 {
1080 return Ok(());
1081 }
1082
1083 let store = var_store.ok_or(ReadError::NullOffset)?;
1084 let regions = regions.ok_or(ReadError::NullOffset)?;
1085 compute_tuple_deltas(
1086 store,
1087 regions,
1088 var_idx,
1089 coords,
1090 field_count,
1091 scalar_cache,
1092 deltas,
1093 )?;
1094
1095 let mut delta_iter = deltas.iter().copied();
1096 if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
1097 transform.set_translate_x(transform.translate_x() + delta_iter.next().unwrap_or(0.0));
1098 }
1099 if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
1100 transform.set_translate_y(transform.translate_y() + delta_iter.next().unwrap_or(0.0));
1101 }
1102 if flags.contains(VarcFlags::HAVE_ROTATION) {
1103 transform
1104 .set_rotation(transform.rotation() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1105 }
1106 if flags.contains(VarcFlags::HAVE_SCALE_X) {
1107 transform.set_scale_x(transform.scale_x() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1108 }
1109 if flags.contains(VarcFlags::HAVE_SCALE_Y) {
1110 transform.set_scale_y(transform.scale_y() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1111 }
1112 const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
1113 VarcFlags::HAVE_SKEW_X.bits()
1114 | VarcFlags::HAVE_SKEW_Y.bits()
1115 | VarcFlags::HAVE_TCENTER_X.bits()
1116 | VarcFlags::HAVE_TCENTER_Y.bits(),
1117 );
1118 if flags.intersects(SKEW_OR_CENTER) {
1119 if flags.contains(VarcFlags::HAVE_SKEW_X) {
1120 transform
1121 .set_skew_x(transform.skew_x() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1122 }
1123 if flags.contains(VarcFlags::HAVE_SKEW_Y) {
1124 transform
1125 .set_skew_y(transform.skew_y() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1126 }
1127 if flags.contains(VarcFlags::HAVE_TCENTER_X) {
1128 transform.set_center_x(transform.center_x() + delta_iter.next().unwrap_or(0.0));
1129 }
1130 if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
1131 transform.set_center_y(transform.center_y() + delta_iter.next().unwrap_or(0.0));
1132 }
1133 }
1134 if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
1135 transform.set_scale_y(transform.scale_x());
1136 }
1137 Ok(())
1138 }
1139
1140 #[test]
1141 fn apply_transform_variations_matches_reference_path() {
1142 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1143 let outlines = Outlines::new(&font).unwrap();
1144 let coverage = outlines.varc.coverage().unwrap();
1145 let var_store = outlines.varc.multi_var_store().transpose().unwrap();
1146 let regions = var_store
1147 .as_ref()
1148 .map(|s| s.region_list())
1149 .transpose()
1150 .unwrap();
1151 let region_count = regions
1152 .as_ref()
1153 .map(|r| r.region_count() as usize)
1154 .unwrap_or(0);
1155
1156 let mut coords = CoordVec::new();
1157 coords.resize_and_fill(outlines.axis_count, F2Dot14::ZERO);
1158 for (i, c) in coords.iter_mut().enumerate() {
1159 *c = match i % 4 {
1160 0 => coord(0.5),
1161 1 => coord(-0.5),
1162 2 => coord(0.25),
1163 _ => coord(-0.25),
1164 };
1165 }
1166
1167 let mut tested = 0usize;
1168 for gid16 in coverage.iter() {
1169 let gid: GlyphId = gid16.into();
1170 let coverage_index = coverage.get(gid).unwrap() as usize;
1171 let glyph = outlines.varc.glyph(coverage_index).unwrap();
1172 for component in glyph.components() {
1173 let component = component.unwrap();
1174 if component.transform_var_index().is_none() {
1175 continue;
1176 }
1177
1178 let mut transform_new = *component.transform();
1179 let mut transform_ref = *component.transform();
1180 let mut cache_new = ScalarCache::new(region_count);
1181 let mut cache_ref = ScalarCache::new(region_count);
1182 let mut deltas_ref = DeltaVec::new();
1183 let mut scratch = Scratchpad::new();
1184 let store_regions = var_store.as_ref().zip(regions.as_ref());
1185
1186 outlines
1187 .apply_transform_variations(
1188 &component,
1189 &coords,
1190 &mut transform_new,
1191 &mut cache_new,
1192 &mut scratch,
1193 store_regions,
1194 )
1195 .unwrap();
1196 apply_transform_variations_reference(
1197 &component,
1198 &coords,
1199 &mut transform_ref,
1200 var_store.as_ref(),
1201 regions.as_ref(),
1202 &mut cache_ref,
1203 &mut deltas_ref,
1204 )
1205 .unwrap();
1206
1207 assert_close(transform_new.translate_x(), transform_ref.translate_x());
1208 assert_close(transform_new.translate_y(), transform_ref.translate_y());
1209 assert_close(transform_new.rotation(), transform_ref.rotation());
1210 assert_close(transform_new.scale_x(), transform_ref.scale_x());
1211 assert_close(transform_new.scale_y(), transform_ref.scale_y());
1212 assert_close(transform_new.skew_x(), transform_ref.skew_x());
1213 assert_close(transform_new.skew_y(), transform_ref.skew_y());
1214 assert_close(transform_new.center_x(), transform_ref.center_x());
1215 assert_close(transform_new.center_y(), transform_ref.center_y());
1216 tested += 1;
1217 if tested >= 32 {
1218 break;
1219 }
1220 }
1221 if tested >= 32 {
1222 break;
1223 }
1224 }
1225 assert!(tested > 0, "expected at least one transformed component");
1226 }
1227
1228 #[test]
1229 fn draw_varc_6868_freetype_path_head_snapshot() {
1230 let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1231 let outlines = Outlines::new(&font).unwrap();
1232 let gid = font.cmap().unwrap().map_codepoint(0x6868_u32).unwrap();
1233 let outline = outlines.outline(gid).unwrap().unwrap();
1234 let mut memory = vec![0u8; outline.required_buffer_size()];
1235 let mut pen = Vec::<PathElement>::new();
1236 outlines
1237 .draw(
1238 &outline,
1239 &mut memory,
1240 Size::unscaled(),
1241 &[],
1242 PathStyle::FreeType,
1243 &mut pen,
1244 )
1245 .unwrap();
1246
1247 let head = path_head_signature(&pen, 8);
1248 assert_eq!(
1249 head,
1250 vec![
1251 "M454.56,574.77".to_string(),
1252 "Q477.58,585.56 499.80,598.25".to_string(),
1253 "Q522.02,610.95 543.36,625.16".to_string(),
1254 "Q564.71,639.37 584.54,655.19".to_string(),
1255 "Q604.38,671.02 623.03,688.41".to_string(),
1256 "Q641.67,705.80 658.41,724.46".to_string(),
1257 "Q675.14,743.12 689.79,763.21".to_string(),
1258 "Q704.43,783.31 717.03,804.56".to_string(),
1259 ]
1260 );
1261 }
1262}