Preprocess Lines
diff --git a/sparse_strips/vello_common/src/strip.rs b/sparse_strips/vello_common/src/strip.rs
index 235828b..2cd9cfc 100644
--- a/sparse_strips/vello_common/src/strip.rs
+++ b/sparse_strips/vello_common/src/strip.rs
@@ -93,6 +93,61 @@
     }
 }
 
+
+
+/// This struct acts as a cache for expensive per-line calculations (square roots,
+/// divisions) that would otherwise happen per-tile.
+#[derive(Debug, Clone, Copy, Default)]
+pub struct PreprocessedLine {
+    /// 1.0 / (dx^2 + dy^2).sqrt()
+    pub recip_len: f32,
+    /// p1.x - p0.x
+    pub dx: f32,
+    /// p1.y - p0.y
+    pub dy: f32,
+    /// 1.0 / dx (or 0.0 if vertical)
+    pub idx: f32,
+    /// 1.0 / dy (or 0.0 if horizontal)
+    pub idy: f32,
+    /// dx / dy (slope relative to y)
+    pub dxdy: f32,
+}
+
+/// Calculate per line information
+pub fn preprocess_lines(lines: &[Line]) -> Vec<PreprocessedLine> {
+    lines
+        .iter()
+        .map(|line| {
+            let dx = line.p1.x - line.p0.x;
+            let dy = line.p1.y - line.p0.y;
+
+            let delta_sq = dx * dx + dy * dy;
+            let recip_len = if delta_sq < 1e-12 {
+                0.0
+            } else {
+                1.0 / delta_sq.sqrt()
+            };
+
+            let is_vertical = dx.abs() <= f32::EPSILON;
+            let is_horizontal = dy.abs() <= f32::EPSILON;
+
+            let idx = if is_vertical { 0.0 } else { 1.0 / dx };
+            let idy = if is_horizontal { 0.0 } else { 1.0 / dy };
+
+            let dxdy = dx * idy;
+
+            PreprocessedLine {
+                recip_len,
+                dx,
+                dy,
+                idx,
+                idy,
+                dxdy,
+            }
+        })
+        .collect()
+}
+
 /// Render the tiles stored in `tiles` into the strip and alpha buffer.
 pub fn render<T: MsaaMask>(
     level: Level,
@@ -113,13 +168,14 @@
                                                       aliasing_threshold,
                                                       lines));
     } else {
+        let preprocessed = preprocess_lines(lines);
         dispatch!(level, simd => T::render_msaa(simd,
                                                 tiles,
                                                 strip_buf,
                                                 alpha_buf,
                                                 fill_rule,
-                                                aliasing_threshold,
                                                 lines,
+                                                &preprocessed,
                                                 mask_lut));
     }
 }
@@ -643,8 +699,8 @@
         strip_buf: &mut Vec<Strip>,
         alpha_buf: &mut Vec<u8>,
         fill_rule: Fill,
-        aliasing_threshold: Option<u8>,
         lines: &[Line],
+        preprocessed: &[PreprocessedLine],
         mask_lut: &[Self],
     );
 }
@@ -660,8 +716,8 @@
         strip_buf: &mut Vec<Strip>,
         alpha_buf: &mut Vec<u8>,
         fill_rule: Fill,
-        aliasing_threshold: Option<u8>,
         lines: &[Line],
+        preprocessed: &[PreprocessedLine],
         mask_lut: &[Self],
     ) {
         if tiles.is_empty() {
@@ -791,6 +847,7 @@
             prev_tile = tile;
 
             let line = lines[tile.line_idx() as usize];
+            let pre = preprocessed[tile.line_idx() as usize];
             let p0_x = line.p0.x;
             let p0_y = line.p0.y;
             let p1_x = line.p1.x;
@@ -807,13 +864,12 @@
                 continue;
             }
 
-            let dx = p1_x - p0_x;
-            let dy = p1_y - p0_y;
-            let is_vertical = dx.abs() <= f32::EPSILON;
-            let is_horizontal = dy.abs() <= f32::EPSILON;
-            let idx = if is_vertical { 0.0 } else { 1.0 / dx };
-            let idy = if is_horizontal { 0.0 } else { 1.0 / dy };
-            let dxdy = dx * idy;
+            let dx = pre.dx;
+            let dy = pre.dy;
+            let idx = pre.idx;
+            let idy = pre.idy;
+            let dxdy = pre.dxdy;
+            let is_horizontal = idx == 0.0;
 
             if tile_start {
                 let min_x_u32 = (tile.x as u32) * (Tile::WIDTH as u32);
@@ -866,19 +922,8 @@
             }
             top_row[end_y] = clipped_bot;
 
-            let delta_sq = dx * dx + dy * dy;
-            let nx = dy
-                * (if delta_sq < 1e-12 {
-                    0.0
-                } else {
-                    1.0 / delta_sq.sqrt()
-                });
-            let ny = -dx
-                * (if delta_sq < 1e-12 {
-                    0.0
-                } else {
-                    1.0 / delta_sq.sqrt()
-                });
+            let nx = dy * pre.recip_len;
+            let ny = -dx * pre.recip_len;
             let c_anchor = clipped_top[0] * nx + clipped_top[1] * ny - 0.5 * (nx + ny);
             let x_dir = clipped_top[0] <= clipped_bot[0];
 
@@ -1040,8 +1085,8 @@
         _strip_buf: &mut Vec<Strip>,
         _alpha_buf: &mut Vec<u8>,
         _fill_rule: Fill,
-        _aliasing_threshold: Option<u8>,
         _lines: &[Line],
+        preprocessed: &[PreprocessedLine],
         _mask_lut: &[Self],
     ) {
         // MSAA16 stub