diff --git a/color.go b/color.go
new file mode 100644
index 0000000000000000000000000000000000000000..72a50bf891a813470c0bd95dabec13ccd85ac0f0
--- /dev/null
+++ b/color.go
@@ -0,0 +1,77 @@
+package pixel
+
+import "image/color"
+
+// NRGBA represents a non-alpha-premultiplied RGBA color with components within range [0, 1].
+//
+// The difference between color.NRGBA is that the value range is [0, 1] and the values are floats.
+type NRGBA struct {
+	R, G, B, A float64
+}
+
+// Add adds color d to color c component-wise and returns the result (the components are not clamped).
+func (c NRGBA) Add(d NRGBA) NRGBA {
+	return NRGBA{
+		R: c.R + d.R,
+		G: c.G + d.G,
+		B: c.B + d.B,
+		A: c.A + d.A,
+	}
+}
+
+// Sub subtracts color d from color c component-wise and returns the result (the components are not clamped).
+func (c NRGBA) Sub(d NRGBA) NRGBA {
+	return NRGBA{
+		R: c.R - d.R,
+		G: c.G - d.G,
+		B: c.B - d.B,
+		A: c.A - d.A,
+	}
+}
+
+// Mul multiplies color c by color d component-wise (the components are not clamped).
+func (c NRGBA) Mul(d NRGBA) NRGBA {
+	return NRGBA{
+		R: c.R * d.R,
+		G: c.G * d.G,
+		B: c.B * d.B,
+		A: c.A * d.A,
+	}
+}
+
+// Scaled multiplies each component of color c by scale and returns the result (the components are not clamped).
+func (c NRGBA) Scaled(scale float64) NRGBA {
+	return NRGBA{
+		R: c.R * scale,
+		G: c.G * scale,
+		B: c.B * scale,
+		A: c.A * scale,
+	}
+}
+
+// RGBA returns alpha-premultiplied red, green, blue and alpha components of a color.
+func (c NRGBA) RGBA() (r, g, b, a uint32) {
+	c.R = clamp(c.R, 0, 1)
+	c.G = clamp(c.G, 0, 1)
+	c.B = clamp(c.B, 0, 1)
+	c.A = clamp(c.A, 0, 1)
+	r = uint32(0xffff * c.R * c.A)
+	g = uint32(0xffff * c.G * c.A)
+	b = uint32(0xffff * c.B * c.A)
+	a = uint32(0xffff * c.A)
+	return
+}
+
+// NRGBAModel converts colors to NRGBA format.
+var NRGBAModel = color.ModelFunc(func(c color.Color) color.Color {
+	r, g, b, a := c.RGBA()
+	if a == 0 {
+		return NRGBA{0, 0, 0, 0}
+	}
+	return NRGBA{
+		float64(r) / float64(a),
+		float64(g) / float64(a),
+		float64(b) / float64(a),
+		float64(a) / 0xffff,
+	}
+})
diff --git a/graphics.go b/graphics.go
index 6af986ff2cfe6e52c1fec7a1bf70223a7d6c43bc..954f56dbf65ecef65c448700aea02072ca4b457e 100644
--- a/graphics.go
+++ b/graphics.go
@@ -141,8 +141,8 @@ func (s *Shape) Draw(t ...Transform) {
 	mat = mat.Mul3(s.transform.Mat())
 
 	s.parent.Do(func(ctx pixelgl.Context) {
-		r, g, b, a := colorToRGBA(s.color)
-		ctx.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{r, g, b, a})
+		c := NRGBAModel.Convert(s.color).(NRGBA)
+		ctx.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)})
 		ctx.Shader().SetUniformAttr(transformMat3, mat)
 
 		if s.picture != nil {
@@ -202,12 +202,12 @@ func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape {
 			}
 			if color, ok := shapeVertices[vertex][colorVec4]; ok {
 				color := color.(mgl32.Vec4)
-				r, g, b, a := colorToRGBA(shape.Color())
+				c := NRGBAModel.Convert(shape.Color()).(NRGBA)
 				color = mgl32.Vec4{
-					color[0] * r,
-					color[1] * g,
-					color[2] * b,
-					color[3] * a,
+					color[0] * float32(c.R),
+					color[1] * float32(c.G),
+					color[2] * float32(c.B),
+					color[3] * float32(c.A),
 				}
 				shapeVertices[vertex][colorVec4] = color
 			}
diff --git a/util.go b/util.go
index 2e6401802154fd6fc9dee1d87df6a1799820cfad..24e61991f8b3811104c419be3fce74582f56b36e 100644
--- a/util.go
+++ b/util.go
@@ -1,13 +1,11 @@
 package pixel
 
-import "image/color"
-
-// colorToRGBA converts a color from image/color to RGBA components in interval [0, 1].
-func colorToRGBA(c color.Color) (r, g, b, a float32) {
-	ri, gi, bi, ai := c.RGBA()
-	r = float32(ri) / 0xffff
-	g = float32(gi) / 0xffff
-	b = float32(bi) / 0xffff
-	a = float32(ai) / 0xffff
-	return
+func clamp(x, low, high float64) float64 {
+	if x < low {
+		return low
+	}
+	if x > high {
+		return high
+	}
+	return x
 }
diff --git a/window.go b/window.go
index c8001938f0dac203ff6664ff1e0b1652d9a5da3a..c3b40d32abec43dc5930d27eb8368c737132ac23 100644
--- a/window.go
+++ b/window.go
@@ -150,7 +150,8 @@ func (w *Window) Destroy() {
 func (w *Window) Clear(c color.Color) {
 	w.Do(func(pixelgl.Context) {
 		pixelgl.DoNoBlock(func() {
-			gl.ClearColor(colorToRGBA(c))
+			c := NRGBAModel.Convert(c).(NRGBA)
+			gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
 			gl.Clear(gl.COLOR_BUFFER_BIT)
 		})
 	})