Get text working

This wires up the text rendering. WIP though, as it tends to get stuck.
Not sure if that's text related or just being triggered by it.
diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs
index 12e9db4..0314003 100644
--- a/piet-gpu/src/encoder.rs
+++ b/piet-gpu/src/encoder.rs
@@ -31,6 +31,18 @@
     n_pathseg: u32,
 }
 
+/// A scene fragment encoding a glyph.
+///
+/// This is a reduced version of the full encoder.
+#[derive(Default)]
+pub struct GlyphEncoder {
+    tag_stream: Vec<u8>,
+    pathseg_stream: Vec<u8>,
+    drawobj_stream: Vec<u8>,
+    n_path: u32,
+    n_pathseg: u32,
+}
+
 // Currently same as Element, but may change - should become packed.
 const DRAWOBJ_SIZE: usize = 36;
 const TRANSFORM_SIZE: usize = 24;
@@ -187,6 +199,14 @@
     pub(crate) fn n_transform(&self) -> usize {
         self.transform_stream.len()
     }
+
+    pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
+        self.tag_stream.extend(&glyph.tag_stream);
+        self.pathseg_stream.extend(&glyph.pathseg_stream);
+        self.drawobj_stream.extend(&glyph.drawobj_stream);
+        self.n_path += glyph.n_path;
+        self.n_pathseg += glyph.n_pathseg;
+    }
 }
 
 fn align_up(x: usize, align: usize) -> usize {
@@ -197,3 +217,30 @@
 fn padding(x: usize, align: usize) -> usize {
     x.wrapping_neg() & (align - 1)
 }
+
+impl GlyphEncoder {
+    pub(crate) fn path_encoder(&mut self) -> PathEncoder {
+        PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream)
+    }
+
+    pub(crate) fn finish_path(&mut self, n_pathseg: u32) {
+        self.n_path += 1;
+        self.n_pathseg += n_pathseg;
+    }
+
+    /// Encode a fill color draw object.
+    ///
+    /// This should be encoded after a path.
+    pub(crate) fn fill_color(&mut self, rgba_color: u32) {
+        let element = FillColor {
+            tag: ELEMENT_FILLCOLOR,
+            rgba_color,
+            ..Default::default()
+        };
+        self.drawobj_stream.extend(bytemuck::bytes_of(&element));
+    }
+
+    pub(crate) fn is_color(&self) -> bool {
+        !self.drawobj_stream.is_empty()
+    }
+}
diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs
index 5b10fec..d13a888 100644
--- a/piet-gpu/src/render_ctx.rs
+++ b/piet-gpu/src/render_ctx.rs
@@ -1,6 +1,7 @@
 use std::borrow::Cow;
 
-use crate::stages::Config;
+use crate::encoder::GlyphEncoder;
+use crate::stages::{Config, Transform};
 use crate::MAX_BLEND_STACK;
 use piet::kurbo::{Affine, Insets, PathEl, Point, Rect, Shape};
 use piet::{
@@ -10,11 +11,11 @@
 
 use piet_gpu_hal::BufWrite;
 use piet_gpu_types::encoder::{Encode, Encoder};
-use piet_gpu_types::scene::{Clip, Element, FillColor, FillLinGradient, SetFillMode, Transform};
+use piet_gpu_types::scene::{Clip, Element, FillLinGradient, SetFillMode};
 
 use crate::gradient::{LinearGradient, RampCache};
 use crate::text::Font;
-pub use crate::text::{PathEncoder, PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
+pub use crate::text::{PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
 
 pub struct PietGpuImage;
 
@@ -262,6 +263,7 @@
     }
 
     fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>) {
+        self.encode_linewidth(-1.0);
         layout.draw_text(self, pos.into());
     }
 
@@ -278,7 +280,7 @@
         if let Some(state) = self.state_stack.pop() {
             if state.rel_transform != Affine::default() {
                 let a_inv = state.rel_transform.inverse();
-                self.encode_transform(to_scene_transform(a_inv));
+                self.encode_transform(Transform::from_kurbo(a_inv));
             }
             self.cur_transform = state.transform;
             for _ in 0..state.n_clip {
@@ -298,7 +300,7 @@
     }
 
     fn transform(&mut self, transform: Affine) {
-        self.encode_transform(to_scene_transform(transform));
+        self.encode_transform(Transform::from_kurbo(transform));
         if let Some(tos) = self.state_stack.last_mut() {
             tos.rel_transform *= transform;
         }
@@ -437,26 +439,16 @@
         }
     }
 
-    pub(crate) fn append_path_encoder(&mut self, path: &PathEncoder) {
-        let elements = path.elements();
-        self.elements.extend(elements.iter().cloned());
-        self.pathseg_count += path.n_segs();
+    pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
+        self.new_encoder.encode_glyph(glyph);
     }
 
     pub(crate) fn fill_glyph(&mut self, rgba_color: u32) {
-        let fill = FillColor { rgba_color };
-        self.elements.push(Element::FillColor(fill));
-        self.path_count += 1;
-    }
-
-    /// Bump the path count when rendering a color emoji.
-    pub(crate) fn bump_n_paths(&mut self, n_paths: usize) {
-        self.path_count += n_paths;
+        self.new_encoder.fill_color(rgba_color);
     }
 
     pub(crate) fn encode_transform(&mut self, transform: Transform) {
-        self.elements.push(Element::Transform(transform));
-        self.trans_count += 1;
+        self.new_encoder.transform(transform);
     }
 
     fn encode_linewidth(&mut self, linewidth: f32) {
@@ -507,14 +499,6 @@
     ]
 }
 
-fn to_scene_transform(transform: Affine) -> Transform {
-    let c = transform.as_coeffs();
-    Transform {
-        mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32],
-        translate: [c[4] as f32, c[5] as f32],
-    }
-}
-
 fn to_srgb(f: f64) -> f64 {
     if f <= 0.0031308 {
         f * 12.92
diff --git a/piet-gpu/src/text.rs b/piet-gpu/src/text.rs
index 39f2922..d25320b 100644
--- a/piet-gpu/src/text.rs
+++ b/piet-gpu/src/text.rs
@@ -10,10 +10,10 @@
     TextLayoutBuilder, TextStorage,
 };
 
-use piet_gpu_types::scene::{CubicSeg, Element, FillColor, LineSeg, QuadSeg, Transform};
-
+use crate::encoder::GlyphEncoder;
 use crate::render_ctx::{self, FillMode};
 use crate::PietGpuRenderContext;
+use crate::stages::Transform;
 
 // This is very much a hack to get things working.
 // On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
@@ -51,13 +51,6 @@
     y: f32,
 }
 
-#[derive(Default)]
-pub struct PathEncoder {
-    elements: Vec<Element>,
-    n_segs: usize,
-    // If this is zero, then it's a text glyph and should be followed by a fill
-    n_colr_layers: usize,
-}
 
 struct TextRenderCtx<'a> {
     scaler: Scaler<'a>,
@@ -126,8 +119,8 @@
         Font { font_ref }
     }
 
-    fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> PathEncoder {
-        let mut encoder = PathEncoder::default();
+    fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> GlyphEncoder {
+        let mut encoder = GlyphEncoder::default();
         if tc.scaler.has_color_outlines() {
             if let Some(outline) = tc.scaler.scale_color_outline(glyph_id) {
                 // TODO: be more sophisticated choosing a palette
@@ -136,8 +129,8 @@
                 while let Some(layer) = outline.get(i) {
                     if let Some(color_ix) = layer.color_index() {
                         let color = palette.get(color_ix);
-                        encoder.append_outline(layer.verbs(), layer.points());
-                        encoder.append_solid_fill(color);
+                        append_outline(&mut encoder, layer.verbs(), layer.points());
+                        encoder.fill_color(*bytemuck::from_bytes(&color));
                     }
                     i += 1;
                 }
@@ -145,7 +138,7 @@
             }
         }
         if let Some(outline) = tc.scaler.scale_outline(glyph_id) {
-            encoder.append_outline(outline.verbs(), outline.points());
+            append_outline(&mut encoder, outline.verbs(), outline.points());
         }
         encoder
     }
@@ -212,12 +205,10 @@
             last_x = glyph.x;
             //println!("{:?}, {:?}", transform.mat, transform.translate);
             ctx.encode_transform(transform);
-            let path = self.font.make_path(glyph.glyph_id, &mut tc);
-            ctx.append_path_encoder(&path);
-            if path.n_colr_layers == 0 {
+            let glyph = self.font.make_path(glyph.glyph_id, &mut tc);
+            ctx.encode_glyph(&glyph);
+            if !glyph.is_color() {
                 ctx.fill_glyph(0xff_ff_ff_ff);
-            } else {
-                ctx.bump_n_paths(path.n_colr_layers);
             }
         }
         if let Some(transform) = inv_transform {
@@ -271,79 +262,38 @@
     }
 }
 
-impl PathEncoder {
-    pub(crate) fn elements(&self) -> &[Element] {
-        &self.elements
-    }
-
-    pub(crate) fn n_segs(&self) -> usize {
-        self.n_segs
-    }
-
-    fn append_outline(&mut self, verbs: &[Verb], points: &[Vector]) {
-        let elements = &mut self.elements;
-        let old_len = elements.len();
-        let mut i = 0;
-        let mut start_pt = [0.0f32; 2];
-        let mut last_pt = [0.0f32; 2];
-        for verb in verbs {
-            match verb {
-                Verb::MoveTo => {
-                    start_pt = convert_swash_point(points[i]);
-                    last_pt = start_pt;
-                    i += 1;
-                }
-                Verb::LineTo => {
-                    let p1 = convert_swash_point(points[i]);
-                    elements.push(Element::Line(LineSeg { p0: last_pt, p1 }));
-                    last_pt = p1;
-                    i += 1;
-                }
-                Verb::QuadTo => {
-                    let p1 = convert_swash_point(points[i]);
-                    let p2 = convert_swash_point(points[i + 1]);
-                    elements.push(Element::Quad(QuadSeg {
-                        p0: last_pt,
-                        p1,
-                        p2,
-                    }));
-                    last_pt = p2;
-                    i += 2;
-                }
-                Verb::CurveTo => {
-                    let p1 = convert_swash_point(points[i]);
-                    let p2 = convert_swash_point(points[i + 1]);
-                    let p3 = convert_swash_point(points[i + 2]);
-                    elements.push(Element::Cubic(CubicSeg {
-                        p0: last_pt,
-                        p1,
-                        p2,
-                        p3,
-                    }));
-                    last_pt = p3;
-                    i += 3;
-                }
-                Verb::Close => {
-                    if start_pt != last_pt {
-                        elements.push(Element::Line(LineSeg {
-                            p0: last_pt,
-                            p1: start_pt,
-                        }));
-                    }
-                }
+fn append_outline(encoder: &mut GlyphEncoder, verbs: &[Verb], points: &[Vector]) {
+    let mut path_encoder = encoder.path_encoder();
+    let mut i = 0;
+    for verb in verbs {
+        match verb {
+            Verb::MoveTo => {
+                let p = points[i];
+                path_encoder.move_to(p.x, p.y);
+                i += 1;
             }
+            Verb::LineTo => {
+                let p = points[i];
+                path_encoder.line_to(p.x, p.y);
+                i += 1;
+            }
+            Verb::QuadTo => {
+                let p1 = points[i];
+                let p2 = points[i + 1];
+                path_encoder.quad_to(p1.x, p1.y, p2.x, p2.y);
+                i += 2;
+            }
+            Verb::CurveTo => {
+                let p1 = points[i];
+                let p2 = points[i + 1];
+                let p3 = points[i + 2];
+                path_encoder.cubic_to(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+                i += 3;
+            }
+            Verb::Close => path_encoder.close_path(),
         }
-        self.n_segs += elements.len() - old_len;
     }
-
-    fn append_solid_fill(&mut self, color: [u8; 4]) {
-        let rgba_color = u32::from_be_bytes(color);
-        self.elements
-            .push(Element::FillColor(FillColor { rgba_color }));
-        self.n_colr_layers += 1;
-    }
-}
-
-fn convert_swash_point(v: Vector) -> [f32; 2] {
-    [v.x, v.y]
+    path_encoder.path();
+    let n_pathseg = path_encoder.n_pathseg();
+    encoder.finish_path(n_pathseg);
 }