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) }) })