Reuse stroke context
diff --git a/Cargo.lock b/Cargo.lock
index fab2adf..8c74171 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1737,8 +1737,6 @@
 [[package]]
 name = "kurbo"
 version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
 dependencies = [
  "arrayvec",
  "euclid",
diff --git a/Cargo.toml b/Cargo.toml
index bcf335f..a773e99 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -149,3 +149,6 @@
 proc-macro2 = "1.0.95"
 syn = { version = "2.0.101", features = ["full", "extra-traits"] }
 quote = "1.0.40"
+
+[patch.crates-io]
+kurbo = { path = "../kurbo/kurbo" }
diff --git a/sparse_strips/vello_bench/src/data.rs b/sparse_strips/vello_bench/src/data.rs
index 1c230eb..8490017 100644
--- a/sparse_strips/vello_bench/src/data.rs
+++ b/sparse_strips/vello_bench/src/data.rs
@@ -12,6 +12,7 @@
 use vello_common::strip::Strip;
 use vello_common::tile::Tiles;
 use vello_common::{flatten, strip};
+use vello_cpu::kurbo::StrokeCtx;
 
 static DATA: OnceLock<Vec<DataItem>> = OnceLock::new();
 
@@ -101,6 +102,7 @@
                 &stroke,
                 path.transform,
                 &mut temp_buf,
+                &mut StrokeCtx::default(),
                 &mut FlattenCtx::default(),
             );
             line_buf.extend(&temp_buf);
@@ -112,13 +114,15 @@
     /// Get the expanded strokes.
     pub fn expanded_strokes(&self) -> Vec<BezPath> {
         let mut paths = vec![];
+        let mut stroke_ctx = StrokeCtx::default();
 
         for path in &self.strokes {
             let stroke = Stroke {
                 width: path.stroke_width as f64,
                 ..Default::default()
             };
-            paths.push(flatten::expand_stroke(path.path.iter(), &stroke, 0.25));
+            flatten::expand_stroke(path.path.iter(), &stroke, 0.25, &mut stroke_ctx);
+            paths.push(stroke_ctx.output().clone());
         }
 
         paths
diff --git a/sparse_strips/vello_bench/src/flatten.rs b/sparse_strips/vello_bench/src/flatten.rs
index 6a585d5..cc2ac1f 100644
--- a/sparse_strips/vello_bench/src/flatten.rs
+++ b/sparse_strips/vello_bench/src/flatten.rs
@@ -7,7 +7,7 @@
 use vello_common::flatten::FlattenCtx;
 use vello_common::kurbo::Stroke;
 use vello_cpu::Level;
-use vello_cpu::kurbo::Affine;
+use vello_cpu::kurbo::{Affine, StrokeCtx};
 
 pub fn flatten(c: &mut Criterion) {
     let mut g = c.benchmark_group("flatten");
@@ -62,13 +62,14 @@
             g.bench_function($item.name.clone(), |b| {
                 b.iter(|| {
                     let mut paths = vec![];
+                    let mut ctx = StrokeCtx::default();
 
                     for path in &$item.strokes {
                         let stroke = Stroke {
                             width: path.stroke_width as f64,
                             ..Default::default()
                         };
-                        paths.push(flatten::expand_stroke(path.path.iter(), &stroke, 0.25));
+                        paths.push(flatten::expand_stroke(path.path.iter(), &stroke, 0.25, &mut ctx));
                     }
 
                     std::hint::black_box(&paths);
diff --git a/sparse_strips/vello_common/src/flatten.rs b/sparse_strips/vello_common/src/flatten.rs
index a2d07b3..a932cb1 100644
--- a/sparse_strips/vello_common/src/flatten.rs
+++ b/sparse_strips/vello_common/src/flatten.rs
@@ -8,7 +8,7 @@
 use alloc::vec::Vec;
 use fearless_simd::{Level, Simd, simd_dispatch};
 use log::warn;
-
+use peniko::kurbo::StrokeCtx;
 pub use crate::flatten_simd::FlattenCtx;
 
 /// The flattening tolerance.
@@ -131,13 +131,14 @@
     style: &Stroke,
     affine: Affine,
     line_buf: &mut Vec<Line>,
+    stroke_ctx: &mut StrokeCtx,
     flatten_ctx: &mut FlattenCtx,
 ) {
     // TODO: Temporary hack to ensure that strokes are scaled properly by the transform.
     let tolerance = TOL / affine.as_coeffs()[0].abs().max(affine.as_coeffs()[3].abs());
 
-    let expanded = expand_stroke(path.iter(), style, tolerance);
-    fill(level, &expanded, affine, line_buf, flatten_ctx);
+    expand_stroke(path.iter(), style, tolerance, stroke_ctx);
+    fill(level, &stroke_ctx.output(), affine, line_buf, flatten_ctx);
 }
 
 /// Expand a stroked path to a filled path.
@@ -145,8 +146,9 @@
     path: impl IntoIterator<Item = PathEl>,
     style: &Stroke,
     tolerance: f64,
-) -> BezPath {
-    kurbo::stroke(path, style, &StrokeOpts::default(), tolerance)
+    stroke_ctx: &mut StrokeCtx,
+) {
+    kurbo::stroke_with(path, style, &StrokeOpts::default(), tolerance, stroke_ctx)
 }
 
 struct FlattenerCallback<'a> {
diff --git a/sparse_strips/vello_cpu/src/strip_generator.rs b/sparse_strips/vello_cpu/src/strip_generator.rs
index c98d533..c6d1910 100644
--- a/sparse_strips/vello_cpu/src/strip_generator.rs
+++ b/sparse_strips/vello_cpu/src/strip_generator.rs
@@ -9,6 +9,7 @@
 use vello_common::strip::Strip;
 use vello_common::tile::Tiles;
 use vello_common::{flatten, strip};
+use vello_common::kurbo::StrokeCtx;
 
 #[derive(Debug)]
 pub(crate) struct StripGenerator {
@@ -16,6 +17,7 @@
     alphas: Vec<u8>,
     line_buf: Vec<Line>,
     flatten_ctx: FlattenCtx,
+    stroke_ctx: StrokeCtx,
     tiles: Tiles,
     strip_buf: Vec<Strip>,
     width: u16,
@@ -31,6 +33,7 @@
             tiles: Tiles::new(),
             strip_buf: Vec::new(),
             flatten_ctx: FlattenCtx::default(),
+            stroke_ctx: StrokeCtx::default(),
             width,
             height,
         }
@@ -67,6 +70,7 @@
             stroke,
             transform,
             &mut self.line_buf,
+            &mut self.stroke_ctx,
             &mut self.flatten_ctx,
         );
         self.make_strips(Fill::NonZero);
diff --git a/sparse_strips/vello_hybrid/src/scene.rs b/sparse_strips/vello_hybrid/src/scene.rs
index 7e05999..a18a8f0 100644
--- a/sparse_strips/vello_hybrid/src/scene.rs
+++ b/sparse_strips/vello_hybrid/src/scene.rs
@@ -10,7 +10,7 @@
 use vello_common::fearless_simd::Level;
 use vello_common::flatten::{FlattenCtx, Line};
 use vello_common::glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph};
-use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Shape, Stroke};
+use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Shape, Stroke, StrokeCtx};
 use vello_common::mask::Mask;
 use vello_common::paint::{Paint, PaintType};
 use vello_common::peniko::Font;
@@ -54,6 +54,7 @@
     paint_visible: bool,
     level: Level,
     flatten_ctx: FlattenCtx,
+    stroke_ctx: StrokeCtx,
     pub(crate) stroke: Stroke,
     pub(crate) transform: Affine,
     pub(crate) fill_rule: Fill,
@@ -72,6 +73,7 @@
             level: Level::fallback(),
             line_buf: vec![],
             tiles: Tiles::new(),
+            stroke_ctx: StrokeCtx::default(),
             strip_buf: vec![],
             paint: render_state.paint,
             paint_transform: render_state.paint_transform,
@@ -149,6 +151,7 @@
             &self.stroke,
             self.transform,
             &mut self.line_buf,
+            &mut self.stroke_ctx,
             &mut self.flatten_ctx,
         );
         let paint = self.encode_current_paint();
@@ -350,6 +353,7 @@
                     &self.stroke,
                     prepared_glyph.transform,
                     &mut self.line_buf,
+                    &mut self.stroke_ctx,
                     &mut self.flatten_ctx,
                 );
                 let paint = self.encode_current_paint();
diff --git a/sparse_strips/vello_sparse_tests/snapshots/clip_deeply_nested_circles.png b/sparse_strips/vello_sparse_tests/snapshots/clip_deeply_nested_circles.png
index b761e6a..8cea797 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/clip_deeply_nested_circles.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/clip_deeply_nested_circles.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f474ae57ffdbafeb4ca1b0b28419005eebe476cf9b560a922c8f87dda585dbfe
-size 5705
+oid sha256:2d62873acad1c121caa7b881ddc669cd4a2f9e272c76f3da24316774e9ebb097
+size 5720
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked.png
index ae4e2ba..b4c2c8c 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:8cabec676809814826e2e780aeadc2a58c7254bafc0e2e8b6f91b364c7d753e3
-size 3331
+oid sha256:488ec752e4248c18edcdf44e5707108f99e7a211efb503fe647f7bd713ecb2a0
+size 3326
diff --git a/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked_unhinted.png b/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked_unhinted.png
index fd2173d..c3ea77d 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked_unhinted.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/glyphs_stroked_unhinted.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2bff3d144d195b9e8f1d7c7f76f36e8d3a5e453bd6b8e1f0e7c4638517b178e8
-size 3410
+oid sha256:1d8debf5601b03b5ab18ccb94909ea7c811a3336f7605ca1b3d7a4eab041bf21
+size 3405
diff --git a/sparse_strips/vello_sparse_tests/snapshots/stroked_circle.png b/sparse_strips/vello_sparse_tests/snapshots/stroked_circle.png
index d46d0b5..cc9851f 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/stroked_circle.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/stroked_circle.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:ff530f473616100441d34c327ea86bb5b3d73bdb0704dc1c22020a1a81d4b1cd
-size 1005
+oid sha256:7ed2d0d6a3285f8778ac4a8e383e4e9db8155f5c21fe1ff416a43c76958fd5a8
+size 1000
diff --git a/sparse_strips/vello_sparse_tests/snapshots/tricky_strokes.png b/sparse_strips/vello_sparse_tests/snapshots/tricky_strokes.png
index 51886da..591f0bb 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/tricky_strokes.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/tricky_strokes.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:5f32dd647597c33ab04274e3478f8b6056524d4184de6e7976832f2404ee6b7b
-size 34967
+oid sha256:2d1d24f927ae36e639309f766872716281b7b70c5e690ebd7ff3d3aaf531c848
+size 35007
diff --git a/sparse_strips/vello_sparse_tests/tests/scenes.rs b/sparse_strips/vello_sparse_tests/tests/scenes.rs
index a34699f..5485f91 100644
--- a/sparse_strips/vello_sparse_tests/tests/scenes.rs
+++ b/sparse_strips/vello_sparse_tests/tests/scenes.rs
@@ -2,8 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
 use crate::renderer::Renderer;
-use vello_common::kurbo::{Affine, BezPath, Cap, CubicBez, Join, Point, Rect, Shape, Stroke};
-use vello_common::peniko::Fill;
+use vello_common::kurbo::{Affine, BezPath, Cap, CubicBez, Join, Rect, Shape, Stroke};
 use vello_cpu::color::AlphaColor;
 use vello_dev_macros::vello_test;
 
diff --git a/sparse_strips/vello_toy/src/debug.rs b/sparse_strips/vello_toy/src/debug.rs
index 8a3e9da..61e4a72 100644
--- a/sparse_strips/vello_toy/src/debug.rs
+++ b/sparse_strips/vello_toy/src/debug.rs
@@ -23,6 +23,7 @@
 use vello_common::strip::Strip;
 use vello_common::tile::{Tile, Tiles};
 use vello_common::{flatten, strip};
+use vello_cpu::kurbo::StrokeCtx;
 
 fn main() {
     let args = Args::parse();
@@ -63,6 +64,7 @@
                 &stroke,
                 Affine::IDENTITY,
                 &mut line_buf,
+                &mut StrokeCtx::default(),
                 &mut FlattenCtx::default(),
             );
         }