shiny/iconvg: fix panic for out-of-range colors

Fixes golang/go#39526

Change-Id: I4ab207b24e424e92b2d8af86a64294db94631aa7
Reviewed-on: https://go-review.googlesource.com/c/exp/+/291150
Trust: Nigel Tao <nigeltao@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/shiny/iconvg/decode_test.go b/shiny/iconvg/decode_test.go
index c06d816..82356f0 100644
--- a/shiny/iconvg/decode_test.go
+++ b/shiny/iconvg/decode_test.go
@@ -17,6 +17,8 @@
 	"runtime"
 	"strings"
 	"testing"
+
+	"golang.org/x/image/math/f32"
 )
 
 // disassemble returns a disassembly of an encoded IconVG graphic. Users of
@@ -286,6 +288,44 @@
 	}
 }
 
+func TestInvalidAlphaPremultipliedColor(t *testing.T) {
+	// See http://golang.org/issue/39526 for some discussion.
+
+	dst := image.NewRGBA(image.Rect(0, 0, 1, 1))
+	var z Rasterizer
+	z.SetDstImage(dst, dst.Bounds(), draw.Over)
+	z.Reset(Metadata{
+		ViewBox: Rectangle{
+			Min: f32.Vec2{0.0, 0.0},
+			Max: f32.Vec2{1.0, 1.0},
+		},
+	})
+
+	// Fill the unit square with red.
+	z.SetCReg(0, false, RGBAColor(color.RGBA{0x55, 0x00, 0x00, 0x66}))
+	z.StartPath(0, 0.0, 0.0)
+	z.AbsLineTo(1.0, 0.0)
+	z.AbsLineTo(1.0, 1.0)
+	z.AbsLineTo(0.0, 1.0)
+	z.ClosePathEndPath()
+
+	// Fill the unit square with an invalid (non-gradient) alpha-premultiplied
+	// color (super-saturated green). This should be a no-op (and not crash).
+	z.SetCReg(0, false, RGBAColor(color.RGBA{0x00, 0x99, 0x00, 0x88}))
+	z.StartPath(0, 0.0, 0.0)
+	z.AbsLineTo(1.0, 0.0)
+	z.AbsLineTo(1.0, 1.0)
+	z.AbsLineTo(0.0, 1.0)
+	z.ClosePathEndPath()
+
+	// We should see red.
+	got := dst.Pix
+	want := []byte{0x55, 0x00, 0x00, 0x66}
+	if !bytes.Equal(got, want) {
+		t.Errorf("got [% 02x], want [% 02x]", got, want)
+	}
+}
+
 func TestBlendColor(t *testing.T) {
 	// This example comes from doc.go. Look for "orange" in the "Colors"
 	// section.
diff --git a/shiny/iconvg/doc.go b/shiny/iconvg/doc.go
index 8627814..a16871a 100644
--- a/shiny/iconvg/doc.go
+++ b/shiny/iconvg/doc.go
@@ -133,8 +133,10 @@
 defined by two points.
 
 At the time a gradient is used to fill a path, it is invalid for any of the
-stop colors to itself be a gradient, or for any stop offset to be less than or
-equal to a previous offset, or outside the range [0, 1].
+stop colors to be out of alpha-premultiplied range (where red, green or blue is
+greater than alpha), including referencing another gradient. It is also invalid
+for any stop offset to be less than or equal to a previous offset, or outside
+the range [0, 1].
 
 
 Colors
@@ -177,7 +179,8 @@
 
 It is valid for some encodings to yield a color value where the red, green or
 blue value is greater than the alpha value, as this may be a gradient. If it
-isn't a gradient, the subsequent rendering is undefined.
+isn't a valid gradient, it is equivalent to transparent black and drawing with
+that color is a no-op.
 
 
 Palettes
diff --git a/shiny/iconvg/rasterizer.go b/shiny/iconvg/rasterizer.go
index f3720f8..ec99992 100644
--- a/shiny/iconvg/rasterizer.go
+++ b/shiny/iconvg/rasterizer.go
@@ -234,6 +234,9 @@
 	} else if z.flatColor.A == 0x00 && z.flatColor.B&0x80 != 0 {
 		z.fill = &z.gradient
 		z.disabled = !z.initGradient(z.flatColor)
+	} else {
+		z.fill = nil
+		z.disabled = true
 	}
 
 	width, height := z.r.Dx(), z.r.Dy()