[encoding] Replace linewidth stream with the new style encoding

* The linewidth stream is now the "style" stream. This has been wired up
  all the way up to the `flatten` pipeline which uses the new encoding
  to extract the fill rule.

* Pipelines downstream of `flatten` still use the old linewidth scheme.
  They will be fixed up in a follow-up change.
diff --git a/crates/encoding/src/encoding.rs b/crates/encoding/src/encoding.rs
index 997fada..e569646 100644
--- a/crates/encoding/src/encoding.rs
+++ b/crates/encoding/src/encoding.rs
@@ -1,7 +1,7 @@
 // Copyright 2022 The Vello authors
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-use super::{DrawColor, DrawTag, PathEncoder, PathTag, Transform};
+use super::{DrawColor, DrawTag, PathEncoder, PathTag, Style, Transform};
 
 use peniko::{kurbo::Shape, BlendMode, BrushRef, Color, Fill};
 
@@ -25,8 +25,8 @@
     pub draw_data: Vec<u8>,
     /// The transform stream.
     pub transforms: Vec<Transform>,
-    /// The line width stream.
-    pub linewidths: Vec<f32>,
+    /// The style stream
+    pub styles: Vec<Style>,
     /// Late bound resource data.
     #[cfg(feature = "full")]
     pub resources: Resources,
@@ -56,7 +56,7 @@
         self.transforms.clear();
         self.path_tags.clear();
         self.path_data.clear();
-        self.linewidths.clear();
+        self.styles.clear();
         self.draw_data.clear();
         self.draw_tags.clear();
         self.n_paths = 0;
@@ -67,7 +67,7 @@
         self.resources.reset();
         if !is_fragment {
             self.transforms.push(Transform::IDENTITY);
-            self.linewidths.push(-1.0);
+            self.styles.push(Style::from_fill(&Fill::NonZero));
         }
     }
 
@@ -96,7 +96,7 @@
                     run.stream_offsets.draw_tags += offsets.draw_tags;
                     run.stream_offsets.draw_data += offsets.draw_data;
                     run.stream_offsets.transforms += offsets.transforms;
-                    run.stream_offsets.linewidths += offsets.linewidths;
+                    run.stream_offsets.styles += offsets.styles;
                     run
                 }));
             self.resources
@@ -148,7 +148,7 @@
         } else {
             self.transforms.extend_from_slice(&other.transforms);
         }
-        self.linewidths.extend_from_slice(&other.linewidths);
+        self.styles.extend_from_slice(&other.styles);
     }
 
     /// Returns a snapshot of the current stream offsets.
@@ -159,19 +159,16 @@
             draw_tags: self.draw_tags.len(),
             draw_data: self.draw_data.len(),
             transforms: self.transforms.len(),
-            linewidths: self.linewidths.len(),
+            styles: self.styles.len(),
         }
     }
 
     /// Encodes a fill style.
     pub fn encode_fill_style(&mut self, fill: Fill) {
-        let linewidth = match fill {
-            Fill::NonZero => -1.0,
-            Fill::EvenOdd => -2.0,
-        };
-        if self.linewidths.last() != Some(&linewidth) {
-            self.path_tags.push(PathTag::LINEWIDTH);
-            self.linewidths.push(linewidth);
+        let style = Style::from_fill(&fill);
+        if self.styles.last() != Some(&style) {
+            self.path_tags.push(PathTag::STYLE);
+            self.styles.push(style);
         }
     }
 
@@ -449,8 +446,8 @@
     pub draw_data: usize,
     /// Current length of transform stream.
     pub transforms: usize,
-    /// Current length of linewidth stream.
-    pub linewidths: usize,
+    /// Current length of style stream.
+    pub styles: usize,
 }
 
 impl StreamOffsets {
@@ -461,6 +458,6 @@
         self.draw_tags += other.draw_tags;
         self.draw_data += other.draw_data;
         self.transforms += other.transforms;
-        self.linewidths += other.linewidths;
+        self.styles += other.styles;
     }
 }
diff --git a/crates/encoding/src/glyph_cache.rs b/crates/encoding/src/glyph_cache.rs
index 3469c13..6ed8bb3 100644
--- a/crates/encoding/src/glyph_cache.rs
+++ b/crates/encoding/src/glyph_cache.rs
@@ -105,7 +105,7 @@
             draw_tags: self.end.draw_tags - self.start.draw_tags,
             draw_data: self.end.draw_data - self.start.draw_data,
             transforms: self.end.transforms - self.start.transforms,
-            linewidths: self.end.linewidths - self.start.linewidths,
+            styles: self.end.styles - self.start.styles,
         }
     }
 }
diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs
index 3d24c19..798bf98 100644
--- a/crates/encoding/src/lib.rs
+++ b/crates/encoding/src/lib.rs
@@ -36,7 +36,7 @@
 pub use monoid::Monoid;
 pub use path::{
     Cubic, LineSoup, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType,
-    PathTag, SegmentCount, Tile,
+    PathTag, SegmentCount, Style, Tile,
 };
 pub use resolve::{resolve_solid_paths_only, Layout};
 
diff --git a/crates/encoding/src/path.rs b/crates/encoding/src/path.rs
index 9cd10c8..fb70e59 100644
--- a/crates/encoding/src/path.rs
+++ b/crates/encoding/src/path.rs
@@ -267,8 +267,8 @@
     /// Path marker.
     pub const PATH: Self = Self(0x10);
 
-    /// Line width setting.
-    pub const LINEWIDTH: Self = Self(0x40);
+    /// Style setting.
+    pub const STYLE: Self = Self(0x40);
 
     /// Bit for path segments that are represented as f32 values. If unset
     /// they are represented as i16.
@@ -316,8 +316,8 @@
     pub pathseg_ix: u32,
     /// Offset into path segment stream.
     pub pathseg_offset: u32,
-    /// Index into linewidth stream.
-    pub linewidth_ix: u32,
+    /// Index into style stream.
+    pub style_ix: u32,
     /// Index of containing path.
     pub path_ix: u32,
 }
@@ -337,7 +337,8 @@
         a += a >> 16;
         c.pathseg_offset = a & 0xff;
         c.path_ix = (tag_word & (PathTag::PATH.0 as u32 * 0x1010101)).count_ones();
-        c.linewidth_ix = (tag_word & (PathTag::LINEWIDTH.0 as u32 * 0x1010101)).count_ones();
+        let style_size = (std::mem::size_of::<Style>() / std::mem::size_of::<u32>()) as u32;
+        c.style_ix = (tag_word & (PathTag::STYLE.0 as u32 * 0x1010101)).count_ones() * style_size;
         c
     }
 
@@ -347,7 +348,7 @@
             trans_ix: self.trans_ix + other.trans_ix,
             pathseg_ix: self.pathseg_ix + other.pathseg_ix,
             pathseg_offset: self.pathseg_offset + other.pathseg_offset,
-            linewidth_ix: self.linewidth_ix + other.linewidth_ix,
+            style_ix: self.style_ix + other.style_ix,
             path_ix: self.path_ix + other.path_ix,
         }
     }
diff --git a/crates/encoding/src/resolve.rs b/crates/encoding/src/resolve.rs
index dfd89bc..367f400 100644
--- a/crates/encoding/src/resolve.rs
+++ b/crates/encoding/src/resolve.rs
@@ -3,7 +3,7 @@
 
 use bytemuck::{Pod, Zeroable};
 
-use super::{DrawTag, Encoding, PathTag, StreamOffsets, Transform};
+use super::{DrawTag, Encoding, PathTag, StreamOffsets, Style, Transform};
 
 #[cfg(feature = "full")]
 use {
@@ -38,8 +38,8 @@
     pub draw_data_base: u32,
     /// Start of transform stream.
     pub transform_base: u32,
-    /// Start of linewidth stream.
-    pub linewidth_base: u32,
+    /// Start of style stream.
+    pub style_base: u32,
 }
 
 impl Layout {
@@ -92,13 +92,13 @@
     /// Returns the transform stream.
     pub fn transforms<'a>(&self, data: &'a [u8]) -> &'a [Transform] {
         let start = self.transform_base as usize * 4;
-        let end = self.linewidth_base as usize * 4;
+        let end = self.style_base as usize * 4;
         bytemuck::cast_slice(&data[start..end])
     }
 
-    /// Returns the linewidth stream.
-    pub fn linewidths<'a>(&self, data: &'a [u8]) -> &'a [f32] {
-        let start = self.linewidth_base as usize * 4;
+    /// Returns the style stream.
+    pub fn styles<'a>(&self, data: &'a [u8]) -> &'a [Style] {
+        let start = self.style_base as usize * 4;
         bytemuck::cast_slice(&data[start..])
     }
 }
@@ -150,9 +150,9 @@
     // Transform stream
     layout.transform_base = size_to_words(data.len());
     data.extend_from_slice(bytemuck::cast_slice(&encoding.transforms));
-    // Linewidth stream
-    layout.linewidth_base = size_to_words(data.len());
-    data.extend_from_slice(bytemuck::cast_slice(&encoding.linewidths));
+    // Style stream
+    layout.style_base = size_to_words(data.len());
+    data.extend_from_slice(bytemuck::cast_slice(&encoding.styles));
     layout.n_draw_objects = layout.n_paths;
     assert_eq!(buffer_size, data.len());
     layout
@@ -359,21 +359,21 @@
                 data.extend_from_slice(bytemuck::cast_slice(&stream[pos..]));
             }
         }
-        // Linewidth stream
-        layout.linewidth_base = size_to_words(data.len());
+        // Style stream
+        layout.style_base = size_to_words(data.len());
         {
             let mut pos = 0;
-            let stream = &encoding.linewidths;
+            let stream = &encoding.styles;
             for patch in &self.patches {
                 if let ResolvedPatch::GlyphRun { index, glyphs, .. } = patch {
-                    let stream_offset = resources.glyph_runs[*index].stream_offsets.linewidths;
+                    let stream_offset = resources.glyph_runs[*index].stream_offsets.styles;
                     if pos < stream_offset {
                         data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset]));
                         pos = stream_offset;
                     }
                     for glyph in &self.glyph_ranges[glyphs.clone()] {
-                        let glyph_data = &self.glyph_cache.encoding.linewidths
-                            [glyph.start.linewidths..glyph.end.linewidths];
+                        let glyph_data =
+                            &self.glyph_cache.encoding.styles[glyph.start.styles..glyph.end.styles];
                         data.extend_from_slice(bytemuck::cast_slice(glyph_data));
                     }
                 }
@@ -612,7 +612,7 @@
             )
             + slice_size_in_bytes(&encoding.draw_data, patch_sizes.draw_data)
             + slice_size_in_bytes(&encoding.transforms, patch_sizes.transforms)
-            + slice_size_in_bytes(&encoding.linewidths, patch_sizes.linewidths);
+            + slice_size_in_bytes(&encoding.styles, patch_sizes.styles);
         Self {
             buffer_size,
             path_tag_padded,
diff --git a/shader/flatten.wgsl b/shader/flatten.wgsl
index e27bb6e..a5d328f 100644
--- a/shader/flatten.wgsl
+++ b/shader/flatten.wgsl
@@ -218,7 +218,13 @@
     var tag_byte = (tag_word >> shift) & 0xffu;
 
     let out = &path_bboxes[tm.path_ix];
-    let linewidth = bitcast<f32>(scene[config.linewidth_base + tm.linewidth_ix]);
+    let style_flags = scene[config.style_base + tm.style_ix];
+    // TODO: We assume all paths are fills at the moment. This is where we will extract the stroke
+    // vs fill state using STYLE_FLAGS_STYLE_BIT.
+    // TODO: The downstream pipelines still use the old floating-point linewidth/fill encoding scheme
+    // This will change to represent the fill rule as a single bit inside the bounding box and draw
+    // info data structures.
+    let linewidth = select(-2.0, -1.0, (style_flags & STYLE_FLAGS_FILL_BIT) == 0u);
     if (tag_byte & PATH_TAG_PATH) != 0u {
         (*out).linewidth = linewidth;
         (*out).trans_ix = tm.trans_ix;
diff --git a/shader/shared/config.wgsl b/shader/shared/config.wgsl
index 1e553f9..fcae33c 100644
--- a/shader/shared/config.wgsl
+++ b/shader/shared/config.wgsl
@@ -29,7 +29,7 @@
     drawdata_base: u32,
 
     transform_base: u32,
-    linewidth_base: u32,
+    style_base: u32,
 
     // Sizes of bump allocated buffers (in element size units)
     binning_size: u32,
diff --git a/shader/shared/pathtag.wgsl b/shader/shared/pathtag.wgsl
index 9777f47..0aa942d 100644
--- a/shader/shared/pathtag.wgsl
+++ b/shader/shared/pathtag.wgsl
@@ -6,7 +6,7 @@
     pathseg_ix: u32,
     pathseg_offset: u32,
 #ifdef full
-    linewidth_ix: u32,
+    style_ix: u32,
     path_ix: u32,
 #endif
 }
@@ -19,9 +19,16 @@
 let PATH_TAG_TRANSFORM = 0x20u;
 #ifdef full
 let PATH_TAG_PATH = 0x10u;
-let PATH_TAG_LINEWIDTH = 0x40u;
+let PATH_TAG_STYLE = 0x40u;
 #endif
 
+// Size of the `Style` data structure in words
+let STYLE_SIZE_IN_WORDS: u32 = 2u;
+let STYLE_FLAGS_STYLE_BIT: u32 = 0x80000000u;
+let STYLE_FLAGS_FILL_BIT: u32 = 0x40000000u;
+
+// TODO: Declare the remaining STYLE flags here.
+
 fn tag_monoid_identity() -> TagMonoid {
     return TagMonoid();
 }
@@ -32,7 +39,7 @@
     c.pathseg_ix = a.pathseg_ix + b.pathseg_ix;
     c.pathseg_offset = a.pathseg_offset + b.pathseg_offset;
 #ifdef full
-    c.linewidth_ix = a.linewidth_ix + b.linewidth_ix;
+    c.style_ix = a.style_ix + b.style_ix;
     c.path_ix = a.path_ix + b.path_ix;
 #endif
     return c;
@@ -50,7 +57,7 @@
     c.pathseg_offset = a & 0xffu;
 #ifdef full
     c.path_ix = countOneBits(tag_word & (PATH_TAG_PATH * 0x1010101u));
-    c.linewidth_ix = countOneBits(tag_word & (PATH_TAG_LINEWIDTH * 0x1010101u));
+    c.style_ix = countOneBits(tag_word & (PATH_TAG_STYLE * 0x1010101u)) * STYLE_SIZE_IN_WORDS;
 #endif
     return c;
 }
diff --git a/src/cpu_shader/flatten.rs b/src/cpu_shader/flatten.rs
index 2cdf725..150aec1 100644
--- a/src/cpu_shader/flatten.rs
+++ b/src/cpu_shader/flatten.rs
@@ -4,7 +4,9 @@
 use crate::cpu_dispatch::CpuBinding;
 
 use super::util::{Transform, Vec2};
-use vello_encoding::{BumpAllocators, ConfigUniform, LineSoup, Monoid, PathBbox, PathMonoid};
+use vello_encoding::{
+    BumpAllocators, ConfigUniform, LineSoup, Monoid, PathBbox, PathMonoid, Style,
+};
 
 fn to_minus_one_quarter(x: f32) -> f32 {
     // could also be written x.powf(-0.25)
@@ -211,11 +213,19 @@
         if tag_byte != 0 {
             tm = tag_monoids[ix >> 2].combine(&tm);
         }
-        let linewidth =
-            f32::from_bits(scene[(config.layout.linewidth_base + tm.linewidth_ix) as usize]);
+        let style_flags = scene[(config.layout.style_base + tm.style_ix) as usize];
         if (tag_byte & PATH_TAG_PATH) != 0 {
             let out = &mut path_bboxes[tm.path_ix as usize];
-            out.linewidth = linewidth;
+            // TODO: We assume all paths are fills at the moment. This is where we will extract the
+            // stroke vs fill state using STYLE_FLAGS_STYLE_BIT.
+            // TODO: The downstream pipelines still use the old floating-point linewidth/fill
+            // encoding scheme. This will change to represent the fill rule as a single bit inside
+            // the bounding box and draw info data structures.
+            out.linewidth = if (style_flags & Style::FLAGS_FILL_BIT) == 0 {
+                -1.0
+            } else {
+                -2.0
+            };
             out.trans_ix = tm.trans_ix;
         }
         let seg_type = tag_byte & PATH_TAG_SEG_TYPE;