.
diff --git a/glifo/src/colr.rs b/glifo/src/colr.rs
index 0c67c6a..40d3c35 100644
--- a/glifo/src/colr.rs
+++ b/glifo/src/colr.rs
@@ -6,18 +6,22 @@
 use crate::atlas::commands::AtlasPaint;
 use crate::color::Srgb;
 use crate::color::{AlphaColor, DynamicColor};
-use crate::glyph::{GlyphColr, OutlinePath};
+use crate::glyph::{
+    CachedOutline, FontEmbolden, FontInfo, GlyphColr, OutlineCacheSession, OutlinePath,
+    VarLookupKey,
+};
 use crate::interface::DrawSink;
 use crate::kurbo::{Affine, Point, Rect, Shape};
 use crate::peniko::{self, BlendMode, ColorStops, Compose, Extend, Gradient, Mix};
 use crate::util::FloatExt;
+use alloc::sync::Arc;
 use alloc::vec;
 use alloc::vec::Vec;
 use core::fmt::Debug;
 use peniko::{LinearGradientPosition, RadialGradientPosition, SweepGradientPosition};
 use skrifa::color::{Brush, ColorPainter, ColorStop, CompositeMode, Transform};
 use skrifa::instance::LocationRef;
-use skrifa::outline::{DrawSettings, OutlineGlyphCollection, pen::ControlBoundsPen};
+use skrifa::outline::OutlineGlyphCollection;
 use skrifa::raw::TableProvider;
 use skrifa::raw::types::BoundingBox;
 use skrifa::{FontRef, GlyphId, MetadataProvider};
@@ -41,10 +45,11 @@
 impl<T: DrawSink + ?Sized> ColrDrawSinkExt for T {}
 
 /// An abstraction for painting COLR glyphs.
-pub(crate) struct ColrPainter<'a> {
+pub(crate) struct ColrPainter<'a, 'b> {
     transforms: Vec<Affine>,
     colr_glyph: &'a GlyphColr<'a>,
     outline_glyphs: OutlineGlyphCollection<'a>,
+    outline_cache: &'a mut OutlineCacheSession<'b>,
     clip_outline: OutlinePath,
     context_color: AlphaColor<Srgb>,
     painter: &'a mut dyn DrawSink,
@@ -58,23 +63,25 @@
     BlendLayer,
 }
 
-impl Debug for ColrPainter<'_> {
+impl Debug for ColrPainter<'_, '_> {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("ColrPainter()").finish()
     }
 }
 
-impl<'a> ColrPainter<'a> {
+impl<'a, 'b> ColrPainter<'a, 'b> {
     /// Create a new COLR painter.
     pub(crate) fn new(
         colr_glyph: &'a GlyphColr<'a>,
         context_color: AlphaColor<Srgb>,
         painter: &'a mut dyn DrawSink,
+        outline_cache: &'a mut OutlineCacheSession<'b>,
     ) -> Self {
         Self {
             transforms: vec![colr_glyph.draw_transform],
             colr_glyph,
             outline_glyphs: colr_glyph.font_ref.outline_glyphs(),
+            outline_cache,
             clip_outline: OutlinePath::new(),
             context_color,
             painter,
@@ -106,6 +113,20 @@
         self.transforms.last().copied().unwrap_or_default()
     }
 
+    fn get_outline(&mut self, glyph_id: GlyphId) -> Option<CachedOutline<'_>> {
+        let outline_glyph = self.outline_glyphs.get(glyph_id)?;
+
+        Some(self.outline_cache.get_or_insert(
+            glyph_id.to_u32(),
+            self.colr_glyph.font_info,
+            self.colr_glyph.font_info.upem,
+            FontEmbolden::default(),
+            VarLookupKey::new(self.colr_glyph.location.coords()),
+            &outline_glyph,
+            None,
+        ))
+    }
+
     fn palette_index_to_color(&self, palette_index: u16, alpha: f32) -> Option<AlphaColor<Srgb>> {
         if palette_index != u16::MAX {
             let color = self
@@ -205,34 +226,45 @@
     pub(crate) has_non_default_blend: bool,
 }
 
-pub(crate) fn get_colr_info<'a>(
+pub(crate) fn get_colr_info<'a, 'b>(
     font_ref: &'a FontRef<'a>,
     color_glyph: &skrifa::color::ColorGlyph<'a>,
     location: LocationRef<'a>,
+    outline_cache: &'a mut OutlineCacheSession<'b>,
+    font_info: FontInfo,
 ) -> ColrGlyphInfo {
-    let mut extractor = GlyphInfoExtractor::new(font_ref, location);
+    let mut extractor = GlyphInfoExtractor::new(font_ref, location, outline_cache, font_info);
     let _ = color_glyph.paint(location, &mut extractor);
     extractor.finish()
 }
 
-struct GlyphInfoExtractor<'a> {
+struct GlyphInfoExtractor<'a, 'b> {
     transforms: Vec<Affine>,
     clip_stack: Vec<Rect>,
     coarse_bbox: Option<Rect>,
     has_non_default_blend: bool,
     outline_glyphs: OutlineGlyphCollection<'a>,
+    outline_cache: &'a mut OutlineCacheSession<'b>,
     location: LocationRef<'a>,
+    font_info: FontInfo,
 }
 
-impl<'a> GlyphInfoExtractor<'a> {
-    fn new(font_ref: &'a FontRef<'a>, location: LocationRef<'a>) -> Self {
+impl<'a, 'b> GlyphInfoExtractor<'a, 'b> {
+    fn new(
+        font_ref: &'a FontRef<'a>,
+        location: LocationRef<'a>,
+        outline_cache: &'a mut OutlineCacheSession<'b>,
+        font_info: FontInfo,
+    ) -> Self {
         Self {
             transforms: vec![Affine::IDENTITY],
             clip_stack: Vec::new(),
             coarse_bbox: None,
             has_non_default_blend: false,
             outline_glyphs: font_ref.outline_glyphs(),
+            outline_cache,
             location,
+            font_info,
         }
     }
 
@@ -257,6 +289,20 @@
         self.cur_transform().transform_rect_bbox(rect)
     }
 
+    fn get_outline(&mut self, glyph_id: GlyphId) -> Option<CachedOutline<'_>> {
+        let outline_glyph = self.outline_glyphs.get(glyph_id)?;
+
+        Some(self.outline_cache.get_or_insert(
+            glyph_id.to_u32(),
+            self.font_info,
+            self.font_info.upem,
+            FontEmbolden::default(),
+            VarLookupKey::new(self.location.coords()),
+            &outline_glyph,
+            None,
+        ))
+    }
+
     fn finish(self) -> ColrGlyphInfo {
         ColrGlyphInfo {
             bbox: self.coarse_bbox,
@@ -265,7 +311,7 @@
     }
 }
 
-impl ColorPainter for ColrPainter<'_> {
+impl ColorPainter for ColrPainter<'_, '_> {
     fn push_transform(&mut self, t: Transform) {
         self.transforms
             .push(self.cur_transform() * convert_affine(t));
@@ -276,18 +322,17 @@
     }
 
     fn push_clip_glyph(&mut self, glyph_id: GlyphId) {
-        // TODO: Make it possible to use the outline cache for this.
-        let Some(outline_glyph) = self.outline_glyphs.get(glyph_id) else {
-            return;
+        let outline = {
+            let Some(outline) = self.get_outline(glyph_id) else {
+                return;
+            };
+            Arc::clone(outline.path)
         };
 
         self.clip_outline.reuse();
-        let _ = outline_glyph.draw(
-            DrawSettings::unhinted(skrifa::instance::Size::unscaled(), self.colr_glyph.location),
-            &mut self.clip_outline,
-        );
-
-        // Note that the bbox will become stale, but we don't need it anyway here.
+        // TODO: We can make use of `DrawSink::set_transform` to let the client
+        // take care of applying it, so we can avoid copying the path.
+        self.clip_outline.path.extend(outline.iter());
         self.clip_outline.path.apply_affine(self.cur_transform());
         self.push_clip();
     }
@@ -464,7 +509,7 @@
     }
 }
 
-impl ColorPainter for GlyphInfoExtractor<'_> {
+impl ColorPainter for GlyphInfoExtractor<'_, '_> {
     fn push_transform(&mut self, t: Transform) {
         self.transforms
             .push(self.cur_transform() * convert_affine(t));
@@ -475,21 +520,14 @@
     }
 
     fn push_clip_glyph(&mut self, glyph_id: GlyphId) {
-        let mut outline_bbox = ControlBoundsPen::default();
-
-        // TODO: Make it possible to use the outline cache for this.
-        let Some(outline_glyph) = self.outline_glyphs.get(glyph_id) else {
-            return;
+        let outline_bbox = {
+            let Some(outline) = self.get_outline(glyph_id) else {
+                return;
+            };
+            outline.bbox
         };
 
-        let _ = outline_glyph.draw(
-            DrawSettings::unhinted(skrifa::instance::Size::unscaled(), self.location),
-            &mut outline_bbox,
-        );
-
-        if let Some(outline_bbox) = outline_bbox.bounding_box().map(convert_bounding_box) {
-            self.push_clip_bbox(self.transform_rect(outline_bbox));
-        }
+        self.push_clip_bbox(self.transform_rect(outline_bbox));
     }
 
     fn push_clip_box(&mut self, clip_box: BoundingBox<f32>) {
diff --git a/glifo/src/glyph.rs b/glifo/src/glyph.rs
index 0d6d96a..98ed222 100644
--- a/glifo/src/glyph.rs
+++ b/glifo/src/glyph.rs
@@ -178,6 +178,17 @@
     pub(crate) area: Rect,
 }
 
+/// Basic metadata about a font.
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct FontInfo {
+    /// Unique identifier for the font data.
+    pub(crate) id: u64,
+    /// Index of the font within the font data.
+    pub(crate) index: u32,
+    /// Font units per em.
+    pub(crate) upem: f32,
+}
+
 /// A glyph defined by a COLR glyph description.
 ///
 /// Clients are supposed to first draw the glyph into an intermediate image texture/pixmap
@@ -190,6 +201,8 @@
     pub location: LocationRef<'a>,
     /// The font reference.
     pub font_ref: &'a FontRef<'a>,
+    /// Basic metadata about the font.
+    pub(crate) font_info: FontInfo,
     /// The transform to apply to the glyph.
     pub draw_transform: Affine,
     /// The rectangular area that should be filled with the rendered representation of the
@@ -346,19 +359,18 @@
 
         let mut outline_cache_session = OutlineCacheSession::new(
             self.outline_cache,
-            VarLookupKey(self.prepared_run.normalized_coords),
+            VarLookupKey::new(self.prepared_run.normalized_coords),
         );
         let PreparedGlyphRun {
             draw_props,
             run_size: _,
+            font_info,
             font_embolden,
             normalized_coords,
             hinting_instance,
             ..
         } = self.prepared_run;
 
-        let font_id = self.prepared_run.font.data.id();
-        let font_index = self.prepared_run.font.index;
         let hinted = hinting_instance.is_some();
 
         let colr_bitmap_cache_enabled = self
@@ -374,7 +386,7 @@
         let context_color = renderer.get_context_color();
         let context_color_packed = pack_color(context_color);
         let scale_props =
-            GlyphScaleProperties::new(draw_props.font_size, self.prepared_run.upem, hinted, style);
+            GlyphScaleProperties::new(draw_props.font_size, font_info.upem, hinted, style);
 
         for glyph in self.glyph_iterator.clone() {
             // TODO: Add a mechanism such that glyphs that are completely outside of the viewport
@@ -392,8 +404,8 @@
             let outline_cache_key = outline_cache_enabled.then(|| {
                 let fractional_x = outline_transform.translation().x.fract() as f32;
                 GlyphCacheKey::new(
-                    font_id,
-                    font_index,
+                    font_info.id,
+                    font_info.index,
                     glyph.id,
                     draw_props.font_size,
                     hinted,
@@ -421,20 +433,21 @@
                 let location = LocationRef::new(normalized_coords);
                 let metrics = calculate_colr_metrics(
                     draw_props.font_size,
-                    self.prepared_run.upem,
                     draw_props,
                     glyph,
                     &font_ref,
                     &color_glyph,
                     location,
+                    &mut outline_cache_session,
+                    font_info,
                 );
                 let transform = calculate_colr_transform(&metrics);
 
                 // COLR glyphs are never hinted and have no sub-pixel offset;
                 // context_color is part of the key because it affects painted layers.
                 let cache_key = colr_bitmap_cache_enabled.then(|| GlyphCacheKey {
-                    font_id,
-                    font_index,
+                    font_id: font_info.id,
+                    font_index: font_info.index,
                     glyph_id: glyph.id,
                     size_bits: draw_props.font_size.to_bits(),
                     hinted: false,
@@ -469,8 +482,13 @@
                 }
 
                 // Cache miss — rasterize the COLR glyph from scratch.
-                let glyph_type =
-                    create_colr_glyph(&font_ref, &metrics, color_glyph, normalized_coords);
+                let glyph_type = create_colr_glyph(
+                    &font_ref,
+                    &metrics,
+                    color_glyph,
+                    normalized_coords,
+                    font_info,
+                );
 
                 let prepared_glyph = PreparedGlyph {
                     glyph_type,
@@ -478,8 +496,18 @@
                     cache_key,
                 };
                 match style {
-                    Style::Fill => fill_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
-                    Style::Stroke => stroke_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
+                    Style::Fill => fill_glyph(
+                        renderer,
+                        prepared_glyph,
+                        &mut self.atlas_cacher,
+                        &mut outline_cache_session,
+                    ),
+                    Style::Stroke => stroke_glyph(
+                        renderer,
+                        prepared_glyph,
+                        &mut self.atlas_cacher,
+                        &mut outline_cache_session,
+                    ),
                 }
                 continue;
             }
@@ -509,7 +537,7 @@
                     &pixmap,
                     draw_props,
                     draw_props.font_size,
-                    self.prepared_run.upem,
+                    font_info.upem,
                     &bitmap_glyph,
                     &bitmaps,
                 );
@@ -517,8 +545,8 @@
                 // Bitmaps are not hinted and have no sub-pixel offset or
                 // context color; variation coords are irrelevant for fixed strikes.
                 let cache_key = colr_bitmap_cache_enabled.then(|| GlyphCacheKey {
-                    font_id,
-                    font_index,
+                    font_id: font_info.id,
+                    font_index: font_info.index,
                     glyph_id: glyph.id,
                     size_bits: bitmap_ppem.to_bits(),
                     hinted: false,
@@ -549,8 +577,18 @@
                     cache_key,
                 };
                 match style {
-                    Style::Fill => fill_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
-                    Style::Stroke => stroke_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
+                    Style::Fill => fill_glyph(
+                        renderer,
+                        prepared_glyph,
+                        &mut self.atlas_cacher,
+                        &mut outline_cache_session,
+                    ),
+                    Style::Stroke => stroke_glyph(
+                        renderer,
+                        prepared_glyph,
+                        &mut self.atlas_cacher,
+                        &mut outline_cache_session,
+                    ),
                 }
                 continue;
             }
@@ -567,8 +605,7 @@
 
             let glyph_type = create_outline_glyph(
                 glyph.id,
-                font_id,
-                font_index,
+                font_info,
                 &mut outline_cache_session,
                 scale_props.cache_size,
                 scale_props.draw_scale,
@@ -584,8 +621,18 @@
                 cache_key: outline_cache_key,
             };
             match style {
-                Style::Fill => fill_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
-                Style::Stroke => stroke_glyph(renderer, prepared_glyph, &mut self.atlas_cacher),
+                Style::Fill => fill_glyph(
+                    renderer,
+                    prepared_glyph,
+                    &mut self.atlas_cacher,
+                    &mut outline_cache_session,
+                ),
+                Style::Stroke => stroke_glyph(
+                    renderer,
+                    prepared_glyph,
+                    &mut self.atlas_cacher,
+                    &mut outline_cache_session,
+                ),
             }
         }
     }
@@ -638,6 +685,7 @@
 
         let PreparedGlyphRun {
             draw_props,
+            font_info,
             font_embolden,
             hinting_instance,
             ..
@@ -652,7 +700,7 @@
         // simply drawing in global space, but we need to invert it for drawing decorations.
         let scale_props = GlyphScaleProperties::new(
             draw_props.font_size,
-            self.prepared_run.upem,
+            font_info.upem,
             hinting_instance.is_some(),
             Style::Fill,
         );
@@ -678,7 +726,7 @@
         let layout_y1 = f64::from(-offset + size);
 
         // Get a cache session for this font's variation coordinates
-        let var_key = VarLookupKey(self.prepared_run.normalized_coords);
+        let var_key = VarLookupKey::new(self.prepared_run.normalized_coords);
         let mut outline_cache_session = OutlineCacheSession::new(self.outline_cache, var_key);
 
         // Collect and merge exclusion zones from all glyphs.
@@ -694,8 +742,7 @@
 
             let cached = outline_cache_session.get_or_insert(
                 glyph.id,
-                self.prepared_run.font.data.id(),
-                self.prepared_run.font.index,
+                font_info,
                 scale_props.cache_size,
                 font_embolden,
                 var_key,
@@ -1018,9 +1065,8 @@
 /// without any positioning information.
 fn create_outline_glyph<'a>(
     glyph_id: u32,
-    font_id: u64,
-    font_index: u32,
-    outline_cache: &'a mut OutlineCacheSession<'_>,
+    font_info: FontInfo,
+    outline_cache: &mut OutlineCacheSession<'_>,
     size: f32,
     scale: f64,
     embolden: FontEmbolden,
@@ -1030,11 +1076,10 @@
 ) -> GlyphType<'a> {
     let cached = outline_cache.get_or_insert(
         glyph_id,
-        font_id,
-        font_index,
+        font_info,
         size,
         embolden,
-        VarLookupKey(normalized_coords),
+        VarLookupKey::new(normalized_coords),
         outline_glyph,
         hinting_instance,
     );
@@ -1197,17 +1242,18 @@
 ///
 /// This computes the intermediate values needed for both creating the `GlyphColr`
 /// and calculating its positioning transform.
-fn calculate_colr_metrics(
+fn calculate_colr_metrics<'a>(
     font_size: f32,
-    upem: f32,
     draw_props: DrawProps,
     glyph: Glyph,
-    font_ref: &FontRef<'_>,
-    color_glyph: &skrifa::color::ColorGlyph<'_>,
-    location: LocationRef<'_>,
+    font_ref: &'a FontRef<'a>,
+    color_glyph: &skrifa::color::ColorGlyph<'a>,
+    location: LocationRef<'a>,
+    outline_cache: &mut OutlineCacheSession<'_>,
+    font_info: FontInfo,
 ) -> ColrMetrics {
     // The scale factor we need to apply to scale from font units to our font size.
-    let font_size_scale = (font_size / upem) as f64;
+    let font_size_scale = (font_size / font_info.upem) as f64;
     let transform = draw_props.positioned_transform(glyph);
 
     // Estimate the size of the intermediate pixmap. Ideally, the intermediate bitmap should have
@@ -1219,7 +1265,7 @@
     };
 
     // TODO: Cache this across frames.
-    let colr_info = get_colr_info(font_ref, color_glyph, location);
+    let colr_info = get_colr_info(font_ref, color_glyph, location, outline_cache, font_info);
     let bbox = color_glyph
         // First try to get the clip bbox from the COLR table,
         // as this one has the highest priority.
@@ -1292,6 +1338,7 @@
     metrics: &ColrMetrics,
     color_glyph: skrifa::color::ColorGlyph<'a>,
     normalized_coords: &'a [skrifa::instance::NormalizedCoord],
+    font_info: FontInfo,
 ) -> GlyphType<'a> {
     let (pix_width, pix_height) = (
         metrics.scaled_bbox.width().ceil() as u16,
@@ -1325,6 +1372,7 @@
         pix_height,
         draw_transform,
         has_non_default_blend: metrics.has_non_default_blend,
+        font_info,
     }))
 }
 
@@ -1370,8 +1418,8 @@
 struct PreparedGlyphRun<'a> {
     /// The underlying font data.
     font: FontData,
-    /// Font units per em for the underlying font.
-    upem: f32,
+    /// Basic metadata about the underlying font.
+    font_info: FontInfo,
     // The fact that we store `run_size` and `glyph_transform` here, as well
     // as having more transforms and an effective font size inside of the `draw_props` field is pretty
     // confusing, so here is a brief explanation:
@@ -1447,7 +1495,7 @@
         // HintingInstance doesn't implement Debug so we have to do this manually :(
         f.debug_struct("PreparedGlyphRun")
             .field("font", &self.font)
-            .field("upem", &self.upem)
+            .field("font_info", &self.font_info)
             .field("run_size", &self.run_size)
             .field("font_embolden", &self.font_embolden)
             .field("glyph_transform", &self.glyph_transform)
@@ -1542,10 +1590,15 @@
         .map(|h| h.units_per_em())
         .unwrap()
         .into();
+    let font_info = FontInfo {
+        id: run.font.data.id(),
+        index: run.font.index,
+        upem,
+    };
 
     PreparedGlyphRun {
         font: run.font,
-        upem,
+        font_info,
         run_size: run.font_size,
         font_embolden: run.font_embolden,
         glyph_transform: run.glyph_transform,
@@ -1818,7 +1871,7 @@
     }
 }
 
-struct OutlineCacheSession<'a> {
+pub(crate) struct OutlineCacheSession<'a> {
     map: &'a mut HashMap<OutlineKey, OutlineEntry>,
     free_list: &'a mut Vec<OutlinePath>,
     serial: u32,
@@ -1827,7 +1880,7 @@
 
 impl<'a> OutlineCacheSession<'a> {
     fn new(outline_cache: &'a mut OutlineCache, var_key: VarLookupKey<'_>) -> Self {
-        let map = if var_key.0.is_empty() {
+        let map = if var_key.coords().is_empty() {
             &mut outline_cache.static_map
         } else {
             match outline_cache
@@ -1847,11 +1900,10 @@
         }
     }
 
-    fn get_or_insert(
+    pub(crate) fn get_or_insert(
         &mut self,
         glyph_id: u32,
-        font_id: u64,
-        font_index: u32,
+        font_info: FontInfo,
         size: f32,
         embolden: FontEmbolden,
         var_key: VarLookupKey<'_>,
@@ -1860,8 +1912,8 @@
     ) -> CachedOutline<'_> {
         let key = OutlineKey {
             glyph_id,
-            font_id,
-            font_index,
+            font_id: font_info.id,
+            font_index: font_info.index,
             size_bits: size.to_bits(),
             embolden_x_bits: f32_bits(embolden.amount.xx),
             embolden_y_bits: f32_bits(embolden.amount.yy),
@@ -1887,7 +1939,7 @@
                 let draw_settings = if let Some(hinting_instance) = hinting_instance {
                     DrawSettings::hinted(hinting_instance, false)
                 } else {
-                    DrawSettings::unhinted(Size::new(size), var_key.0)
+                    DrawSettings::unhinted(Size::new(size), var_key.coords())
                 };
 
                 drawing_buf.reuse();
@@ -1923,7 +1975,17 @@
 
 /// Lookup key for variable font caches.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-struct VarLookupKey<'a>(&'a [skrifa::instance::NormalizedCoord]);
+pub(crate) struct VarLookupKey<'a>(&'a [skrifa::instance::NormalizedCoord]);
+
+impl<'a> VarLookupKey<'a> {
+    pub(crate) fn new(coords: &'a [skrifa::instance::NormalizedCoord]) -> Self {
+        Self(coords)
+    }
+
+    fn coords(self) -> &'a [skrifa::instance::NormalizedCoord] {
+        self.0
+    }
+}
 
 impl Equivalent<VarKey> for VarLookupKey<'_> {
     fn equivalent(&self, other: &VarKey) -> bool {
diff --git a/glifo/src/renderer.rs b/glifo/src/renderer.rs
index 6c53580..51a63d2 100644
--- a/glifo/src/renderer.rs
+++ b/glifo/src/renderer.rs
@@ -8,7 +8,8 @@
 use crate::atlas::{AtlasSlot, GlyphAtlas, GlyphCacheKey, ImageCache, RasterMetrics};
 use crate::colr::ColrPainter;
 use crate::glyph::{
-    AtlasCacher, CachedGlyphType, GlyphBitmap, GlyphColr, GlyphOutline, GlyphType, PreparedGlyph,
+    AtlasCacher, CachedGlyphType, GlyphBitmap, GlyphColr, GlyphOutline, GlyphType,
+    OutlineCacheSession, PreparedGlyph,
 };
 use crate::interface::{DrawSink, GlyphRenderer};
 use crate::util::AffineExt;
@@ -43,6 +44,7 @@
     renderer: &mut impl GlyphRenderer,
     prepared_glyph: PreparedGlyph<'_>,
     atlas_cacher: &mut AtlasCacher<'_>,
+    outline_cache: &mut OutlineCacheSession<'_>,
 ) {
     let AtlasCacher::Enabled(glyph_atlas, image_cache) = atlas_cacher else {
         let transform = prepared_glyph.transform;
@@ -54,7 +56,13 @@
             GlyphType::Bitmap(glyph) => render_uncached_bitmap_glyph(renderer, glyph, transform),
             GlyphType::Colr(glyph) => {
                 let context_color = renderer.get_context_color();
-                render_uncached_colr_glyph(renderer, &glyph, transform, context_color);
+                render_uncached_colr_glyph(
+                    renderer,
+                    &glyph,
+                    transform,
+                    context_color,
+                    outline_cache,
+                );
             }
         };
     };
@@ -106,13 +114,14 @@
                     key,
                     glyph_atlas,
                     image_cache,
+                    outline_cache,
                 )
             {
                 return;
             }
 
             let context_color = renderer.get_context_color();
-            render_uncached_colr_glyph(renderer, &glyph, transform, context_color);
+            render_uncached_colr_glyph(renderer, &glyph, transform, context_color, outline_cache);
         }
     }
 }
@@ -123,6 +132,7 @@
     renderer: &mut impl GlyphRenderer,
     prepared_glyph: PreparedGlyph<'_>,
     atlas_cacher: &mut AtlasCacher<'_>,
+    outline_cache: &mut OutlineCacheSession<'_>,
 ) {
     let AtlasCacher::Enabled(glyph_atlas, image_cache) = atlas_cacher else {
         let transform = prepared_glyph.transform;
@@ -131,7 +141,7 @@
                 stroke_uncached_outline_glyph(renderer, &glyph.path, glyph.scale, transform);
             }
             GlyphType::Bitmap(_) | GlyphType::Colr(_) => {
-                fill_glyph(renderer, prepared_glyph, atlas_cacher);
+                fill_glyph(renderer, prepared_glyph, atlas_cacher, outline_cache);
             }
         };
     };
@@ -159,7 +169,7 @@
             stroke_uncached_outline_glyph(renderer, &glyph.path, glyph.scale, transform);
         }
         GlyphType::Bitmap(_) | GlyphType::Colr(_) => {
-            fill_glyph(renderer, prepared_glyph, atlas_cacher);
+            fill_glyph(renderer, prepared_glyph, atlas_cacher, outline_cache);
         }
     }
 }
@@ -215,6 +225,7 @@
     glyph: &GlyphColr<'_>,
     transform: Affine,
     context_color: AlphaColor<Srgb>,
+    outline_cache: &mut OutlineCacheSession<'_>,
 ) {
     let state = renderer.save_state();
     renderer.set_transform(transform);
@@ -232,7 +243,7 @@
     }
 
     // TODO: Maybe ColrPainter can be reused across glyphs?
-    let mut colr_painter = ColrPainter::new(glyph, context_color, renderer);
+    let mut colr_painter = ColrPainter::new(glyph, context_color, renderer, outline_cache);
     colr_painter.paint();
     if glyph.has_non_default_blend {
         renderer.pop_layer();
@@ -320,6 +331,7 @@
     context_color: AlphaColor<Srgb>,
     recorder: &mut AtlasCommandRecorder,
     atlas_slot: AtlasSlot,
+    outline_cache: &mut OutlineCacheSession<'_>,
 ) {
     recorder.set_transform(Affine::translate((
         atlas_slot.x as f64,
@@ -334,7 +346,7 @@
     }
 
     // TODO: Maybe ColrPainter can be reused across glyphs?
-    let mut colr_painter = ColrPainter::new(glyph, context_color, recorder);
+    let mut colr_painter = ColrPainter::new(glyph, context_color, recorder, outline_cache);
     colr_painter.paint();
 
     if glyph.has_non_default_blend {
@@ -435,6 +447,7 @@
     cache_key: GlyphCacheKey,
     glyph_atlas: &mut GlyphAtlas,
     image_cache: &mut ImageCache,
+    outline_cache: &mut OutlineCacheSession<'_>,
 ) -> CacheResult {
     if !supports_atlas_caching(&transform, CachedGlyphType::Colr(Rect::ZERO)) {
         return CacheResult::UnsupportedTransform;
@@ -458,7 +471,7 @@
         return CacheResult::AtlasFull;
     };
 
-    render_colr_to_atlas(glyph, context_color, recorder, atlas_slot);
+    render_colr_to_atlas(glyph, context_color, recorder, atlas_slot, outline_cache);
 
     render_from_atlas(
         renderer,