shiny/iconvg: make the zero Encoder value usable.

Change-Id: Iee4b4be38ef43dab1475bf5cebdb678b08f8c29a
Reviewed-on: https://go-review.googlesource.com/29890
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/shiny/iconvg/encode.go b/shiny/iconvg/encode.go
index 4c0d950..9d5fe45 100644
--- a/shiny/iconvg/encode.go
+++ b/shiny/iconvg/encode.go
@@ -16,18 +16,19 @@
 	errStylingOpsUsedInDrawingMode = errors.New("iconvg: styling ops used in drawing mode")
 )
 
-// TODO: delete the NewEncoder function, and just make the zero value usable.
+type mode uint8
 
-// NewEncoder returns a new Encoder for the given Metadata.
-func NewEncoder(m Metadata) *Encoder {
-	e := &Encoder{
-		buf: make(buffer, 0, 1024),
-	}
-	e.Reset(m)
-	return e
-}
+const (
+	modeInitial mode = iota
+	modeStyling
+	modeDrawing
+)
 
 // Encoder is an IconVG encoder.
+//
+// The zero value is usable. Calling Reset, which is optional, sets the
+// Metadata for the subsequent encoded form. If Reset is not called before
+// other Encoder methods, the default metadata is implied.
 type Encoder struct {
 	buf      buffer
 	altBuf   buffer
@@ -49,6 +50,9 @@
 	if e.err != nil {
 		return nil, e.err
 	}
+	if e.mode == modeInitial {
+		e.appendDefaultMetadata()
+	}
 	return []byte(e.buf), nil
 }
 
@@ -57,6 +61,7 @@
 	*e = Encoder{
 		buf:      append(e.buf[:0], magic...),
 		metadata: m,
+		mode:     modeStyling,
 		lod1:     positiveInfinity,
 	}
 
@@ -88,17 +93,44 @@
 	}
 }
 
-func (e *Encoder) CSel() uint32              { return e.cSel }
-func (e *Encoder) NSel() uint32              { return e.nSel }
-func (e *Encoder) LOD() (lod0, lod1 float32) { return e.lod0, e.lod1 }
+func (e *Encoder) appendDefaultMetadata() {
+	e.buf = append(e.buf[:0], magic...)
+	e.buf = append(e.buf, 0x00) // There are zero metadata chunks.
+	e.mode = modeStyling
+}
+
+func (e *Encoder) CSel() uint32 {
+	if e.mode == modeInitial {
+		e.appendDefaultMetadata()
+	}
+	return e.cSel
+}
+
+func (e *Encoder) NSel() uint32 {
+	if e.mode == modeInitial {
+		e.appendDefaultMetadata()
+	}
+	return e.nSel
+}
+
+func (e *Encoder) LOD() (lod0, lod1 float32) {
+	if e.mode == modeInitial {
+		e.appendDefaultMetadata()
+	}
+	return e.lod0, e.lod1
+}
 
 func (e *Encoder) SetCSel(cSel uint32) {
 	if e.err != nil {
 		return
 	}
 	if e.mode != modeStyling {
-		e.err = errStylingOpsUsedInDrawingMode
-		return
+		if e.mode == modeInitial {
+			e.appendDefaultMetadata()
+		} else {
+			e.err = errStylingOpsUsedInDrawingMode
+			return
+		}
 	}
 	e.cSel = cSel
 	e.buf = append(e.buf, uint8(cSel&0x3f))
@@ -109,8 +141,12 @@
 		return
 	}
 	if e.mode != modeStyling {
-		e.err = errStylingOpsUsedInDrawingMode
-		return
+		if e.mode == modeInitial {
+			e.appendDefaultMetadata()
+		} else {
+			e.err = errStylingOpsUsedInDrawingMode
+			return
+		}
 	}
 	e.nSel = nSel
 	e.buf = append(e.buf, uint8((nSel&0x3f)|0x40))
@@ -121,8 +157,12 @@
 		return
 	}
 	if e.mode != modeStyling {
-		e.err = errStylingOpsUsedInDrawingMode
-		return
+		if e.mode == modeInitial {
+			e.appendDefaultMetadata()
+		} else {
+			e.err = errStylingOpsUsedInDrawingMode
+			return
+		}
 	}
 	e.lod0 = lod0
 	e.lod1 = lod1
@@ -136,8 +176,12 @@
 		return
 	}
 	if e.mode != modeStyling {
-		e.err = errStylingOpsUsedInDrawingMode
-		return
+		if e.mode == modeInitial {
+			e.appendDefaultMetadata()
+		} else {
+			e.err = errStylingOpsUsedInDrawingMode
+			return
+		}
 	}
 	if adj < 0 || 6 < adj {
 		e.err = errInvalidSelectorAdjustment
diff --git a/shiny/iconvg/encode_test.go b/shiny/iconvg/encode_test.go
index 82a096e..6c42c5e 100644
--- a/shiny/iconvg/encode_test.go
+++ b/shiny/iconvg/encode_test.go
@@ -11,6 +11,21 @@
 	"golang.org/x/image/math/f32"
 )
 
+func TestEncoderZeroValue(t *testing.T) {
+	var e Encoder
+	got, err := e.Bytes()
+	if err != nil {
+		t.Fatalf("Bytes: %v", err)
+	}
+	want := []byte{
+		0x89, 0x49, 0x56, 0x47, // Magic identifier.
+		0x00, // Zero metadata chunks.
+	}
+	if !bytes.Equal(got, want) {
+		t.Errorf("\ngot  %d bytes:\n% x\nwant %d bytes:\n% x", len(got), got, len(want), want)
+	}
+}
+
 // actionInfoIconVG is the IconVG encoding of the "action/info" icon from the
 // Material Design icon set.
 //
@@ -24,7 +39,8 @@
 }
 
 func TestEncodeActionInfo(t *testing.T) {
-	e := NewEncoder(Metadata{
+	var e Encoder
+	e.Reset(Metadata{
 		ViewBox: Rectangle{
 			Min: f32.Vec2{-24, -24},
 			Max: f32.Vec2{+24, +24},
diff --git a/shiny/iconvg/iconvg.go b/shiny/iconvg/iconvg.go
index ec5a881..75e78cc 100644
--- a/shiny/iconvg/iconvg.go
+++ b/shiny/iconvg/iconvg.go
@@ -26,13 +26,6 @@
 	midSuggestedPalette = 1
 )
 
-type mode bool
-
-const (
-	modeStyling mode = false
-	modeDrawing mode = true
-)
-
 // Rectangle is defined by its minimum and maximum coordinates.
 type Rectangle struct {
 	Min, Max f32.Vec2