Skip to content

Commit

Permalink
Refactor out pixel.RGBA
Browse files Browse the repository at this point in the history
Stuff

Revert "Refactor out pixel.RGBA"

This reverts commit d029ce8.

Reworked to keep pixel.RGBA around, but an alias

working on color

more color work
  • Loading branch information
Allen Ray committed Aug 15, 2024
1 parent aacf9ae commit 7ddf133
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 118 deletions.
22 changes: 9 additions & 13 deletions backends/opengl/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,24 +193,20 @@ func setBlendFunc(cmp pixel.ComposeMethod) {
func (c *Canvas) Clear(color color.Color) {
c.gf.Dirty()

rgba := pixel.ToRGBA(color)

// color masking
rgba = rgba.Mul(pixel.RGBA{
R: float64(c.col[0]),
G: float64(c.col[1]),
B: float64(c.col[2]),
A: float64(c.col[3]),
})
r, g, b, a := pixel.ColorToFloats[float32](
pixel.ToRGBA(color).Mul(
pixel.FloatsToColor(c.col[0], c.col[1], c.col[2], c.col[3]),
),
)

mainthread.CallNonBlock(func() {
c.setGlhfBounds()
c.gf.Frame().Begin()
glhf.Clear(
float32(rgba.R),
float32(rgba.G),
float32(rgba.B),
float32(rgba.A),
r,
g,
b,
a,
)
c.gf.Frame().End()
})
Expand Down
26 changes: 26 additions & 0 deletions backends/opengl/color.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package opengl

import (
"image/color"

"github.com/go-gl/mathgl/mgl32"
)

type GLColor mgl32.Vec4

func (c GLColor) RGBA() (r, g, b, a uint32) {
return uint32(c[0] * 0xffff), uint32(c[1] * 0xffff), uint32(c[2] * 0xffff), uint32(c[3] * 0xffff)
}

func ToGLColor(col color.Color) GLColor {
if c, ok := col.(GLColor); ok {
return c
}
r, g, b, a := col.RGBA()
return GLColor{
float32(r) / 0xffff,
float32(g) / 0xffff,
float32(b) / 0xffff,
float32(a) / 0xffff,
}
}
8 changes: 4 additions & 4 deletions backends/opengl/glframe.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ func (gf *GLFrame) Color(at pixel.Vec) pixel.RGBA {
x, y := int(at.X)-bx, int(at.Y)-by
off := y*bw + x
return pixel.RGBA{
R: float64(gf.pixels[off*4+0]) / 255,
G: float64(gf.pixels[off*4+1]) / 255,
B: float64(gf.pixels[off*4+2]) / 255,
A: float64(gf.pixels[off*4+3]) / 255,
R: gf.pixels[off*4+0],
G: gf.pixels[off*4+1],
B: gf.pixels[off*4+2],
A: gf.pixels[off*4+3],
}
}

Expand Down
8 changes: 4 additions & 4 deletions backends/opengl/glpicture.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ func (gp *glPicture) Color(at pixel.Vec) pixel.RGBA {
x, y := int(at.X)-bx, int(at.Y)-by
off := y*bw + x
return pixel.RGBA{
R: float64(gp.pixels[off*4+0]) / 255,
G: float64(gp.pixels[off*4+1]) / 255,
B: float64(gp.pixels[off*4+2]) / 255,
A: float64(gp.pixels[off*4+3]) / 255,
R: gp.pixels[off*4+0],
G: gp.pixels[off*4+1],
B: gp.pixels[off*4+2],
A: gp.pixels[off*4+3],
}
}
26 changes: 11 additions & 15 deletions backends/opengl/gltriangles.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,20 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) {
if t, ok := t.(*pixel.TrianglesData); ok {
for i := 0; i < length; i++ {
var (
px, py = (*t)[i].Position.XY()
col = (*t)[i].Color
tx, ty = (*t)[i].Picture.XY()
in = (*t)[i].Intensity
rec = (*t)[i].ClipRect
px, py = (*t)[i].Position.XY()
col = (*t)[i].Color
tx, ty = (*t)[i].Picture.XY()
in = (*t)[i].Intensity
rec = (*t)[i].ClipRect
r, g, b, a = pixel.ColorToFloats[float32](col)
)
d := gt.data[i*stride : i*stride+trisAttrLen]
d[triPosX] = float32(px)
d[triPosY] = float32(py)
d[triColorR] = float32(col.R)
d[triColorG] = float32(col.G)
d[triColorB] = float32(col.B)
d[triColorA] = float32(col.A)
d[triColorR] = r
d[triColorG] = g
d[triColorB] = b
d[triColorA] = a
d[triPicX] = float32(tx)
d[triPicY] = float32(ty)
d[triIntensity] = float32(in)
Expand Down Expand Up @@ -251,12 +252,7 @@ func (gt *GLTriangles) Color(i int) pixel.RGBA {
g := gt.data[gt.index(i, triColorG)]
b := gt.data[gt.index(i, triColorB)]
a := gt.data[gt.index(i, triColorA)]
return pixel.RGBA{
R: float64(r),
G: float64(g),
B: float64(b),
A: float64(a),
}
return pixel.FloatsToColor(r, g, b, a)
}

// SetColor sets the color property of the i-th vertex.
Expand Down
96 changes: 51 additions & 45 deletions color.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,97 @@
package pixel

import "image/color"
import (
"image/color"

"golang.org/x/exp/constraints"
)

// RGBA represents an alpha-premultiplied RGBA color with components within range [0, 1].
//
// The difference between color.RGBA is that the value range is [0, 1] and the values are floats.
type RGBA struct {
R, G, B, A float64
}
type RGBA color.RGBA

// RGB returns a fully opaque RGBA color with the given RGB values.
//
// A common way to construct a transparent color is to create one with RGB constructor, then
// multiply it by a color obtained from the Alpha constructor.
func RGB(r, g, b float64) RGBA {
return RGBA{r, g, b, 1}
return RGBA{R: uint8(r * 255), G: uint8(g * 255), B: uint8(b * 255), A: 255}
}

// Alpha returns a white RGBA color with the given alpha component.
func Alpha(a float64) RGBA {
return RGBA{a, a, a, a}
A := uint8(a * 255)
return RGBA{A, A, A, A}
}

// Add adds color d to color c component-wise and returns the result (the components are not
// clamped).
func (c RGBA) Add(d RGBA) RGBA {
func (c RGBA) Add(d color.Color) RGBA {
rgba := ToRGBA(d)
return RGBA{
R: c.R + d.R,
G: c.G + d.G,
B: c.B + d.B,
A: c.A + d.A,
R: c.R + rgba.R,
G: c.G + rgba.G,
B: c.B + rgba.B,
A: c.A + rgba.A,
}
}

// Sub subtracts color d from color c component-wise and returns the result (the components
// are not clamped).
func (c RGBA) Sub(d RGBA) RGBA {
func (c RGBA) Sub(d color.Color) RGBA {
rgba := ToRGBA(d)
return RGBA{
R: c.R - d.R,
G: c.G - d.G,
B: c.B - d.B,
A: c.A - d.A,
R: c.R - rgba.R,
G: c.G - rgba.G,
B: c.B - rgba.B,
A: c.A - rgba.A,
}
}

// Mul multiplies color c by color d component-wise (the components are not clamped).
func (c RGBA) Mul(d RGBA) RGBA {
return RGBA{
R: c.R * d.R,
G: c.G * d.G,
B: c.B * d.B,
A: c.A * d.A,
}
func (c RGBA) Mul(d color.Color) RGBA {
r1, g1, b1, a1 := ColorToFloats[float64](c)
r2, g2, b2, a2 := ColorToFloats[float64](d)
return FloatsToColor(r1*r2, g1*g2, b1*b2, a1*a2)
}

// Scaled multiplies each component of color c by scale and returns the result (the components
// are not clamped).
// Scaled multiplies each component of color c by scale and returns the result.
func (c RGBA) Scaled(scale float64) RGBA {
r, g, b, a := ColorToFloats[float64](c)
return RGBA{
R: c.R * scale,
G: c.G * scale,
B: c.B * scale,
A: c.A * scale,
R: uint8(r * scale),
G: uint8(g * scale),
B: uint8(b * scale),
A: uint8(a * scale),
}
}

// RGBA returns alpha-premultiplied red, green, blue and alpha components of the RGBA color.
// RGBA returns components of the color.
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(0xffff * c.R)
g = uint32(0xffff * c.G)
b = uint32(0xffff * c.B)
a = uint32(0xffff * c.A)
return
return color.RGBA(c).RGBA()
}

// ToRGBA converts a color to RGBA format. Using this function is preferred to using RGBAModel, for
// performance (using RGBAModel introduces additional unnecessary allocations).
// ColorToFloats converts a color to float32 or float64 components.
func ColorToFloats[F constraints.Float](c color.Color) (r, g, b, a F) {
r1, g1, b1, a1 := c.RGBA()
return F(r1) / 0xffff, F(g1) / 0xffff, F(b1) / 0xffff, F(a1) / 0xffff
}

// FloatsToColor converts float32 or float64 components to a color.
func FloatsToColor[F constraints.Float](r, g, b, a F) RGBA {
return RGBA{uint8(r * F(255)), uint8(g * F(255)), uint8(b * F(255)), uint8(a * F(255))}
}

// ToRGBA converts a color to RGBA format.
func ToRGBA(c color.Color) RGBA {
if c, ok := c.(RGBA); ok {
switch c := c.(type) {
case RGBA:
return c
}
r, g, b, a := c.RGBA()
return RGBA{
float64(r) / 0xffff,
float64(g) / 0xffff,
float64(b) / 0xffff,
float64(a) / 0xffff,
case color.RGBA:
return RGBA(c)
default:
return RGBA(color.RGBAModel.Convert(c).(color.RGBA))
}
}

Expand Down
54 changes: 54 additions & 0 deletions color_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package pixel

import (
"image/color"
"reflect"
"testing"

"github.com/stretchr/testify/require"
)

func TestColorConversions(t *testing.T) {
c0 := RGBA{64, 127, 128, 255}
c1 := FloatsToColor(ColorToFloats[float32](c0))
require.Equal(t, c0, c1)
}

func TestToRGBA(t *testing.T) {
type args struct {
c color.Color
}
tests := []struct {
name string
args args
want RGBA
}{
{
name: "pixel.rgba",
args: args{c: RGBA{64, 127, 128, 255}},
want: RGBA{64, 127, 128, 255},
},
{
name: "color.rgba",
args: args{c: color.RGBA{64, 127, 128, 255}},
want: RGBA{64, 127, 128, 255},
},
{
name: "color.nrgba",
args: args{c: color.NRGBA{64, 127, 128, 255}},
want: RGBA{64, 127, 128, 255},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToRGBA(tt.args.c); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToRGBA() = %v, want %v", got, tt.want)
}
})
}
}

func TestRGB(t *testing.T) {
rgba := RGB(0.25, 0.5, 0.75)
require.Equal(t, RGBA{63, 127, 191, 255}, rgba)
}
33 changes: 21 additions & 12 deletions compose.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package pixel

import "errors"
import (
"errors"
"image/color"
)

// ComposeTarget is a BasicTarget capable of Porter-Duff composition.
type ComposeTarget interface {
Expand Down Expand Up @@ -31,28 +34,34 @@ const (

// Compose composes two colors together according to the ComposeMethod. A is the foreground, B is
// the background.
func (cm ComposeMethod) Compose(a, b RGBA) RGBA {
func (cm ComposeMethod) Compose(a, b color.Color) RGBA {
var fa, fb float64

ac := ToRGBA(a)
bc := ToRGBA(b)

aa := float64(ac.A) / 255
ba := float64(bc.A) / 255

switch cm {
case ComposeOver:
fa, fb = 1, 1-a.A
fa, fb = 1, 1-aa
case ComposeIn:
fa, fb = b.A, 0
fa, fb = ba, 0
case ComposeOut:
fa, fb = 1-b.A, 0
fa, fb = 1-ba, 0
case ComposeAtop:
fa, fb = b.A, 1-a.A
fa, fb = ba, 1-aa
case ComposeRover:
fa, fb = 1-b.A, 1
fa, fb = 1-ba, 1
case ComposeRin:
fa, fb = 0, a.A
fa, fb = 0, aa
case ComposeRout:
fa, fb = 0, 1-a.A
fa, fb = 0, 1-aa
case ComposeRatop:
fa, fb = 1-b.A, a.A
fa, fb = 1-ba, aa
case ComposeXor:
fa, fb = 1-b.A, 1-a.A
fa, fb = 1-ba, 1-aa
case ComposePlus:
fa, fb = 1, 1
case ComposeCopy:
Expand All @@ -61,5 +70,5 @@ func (cm ComposeMethod) Compose(a, b RGBA) RGBA {
panic(errors.New("Compose: invalid ComposeMethod"))
}

return a.Mul(Alpha(fa)).Add(b.Mul(Alpha(fb)))
return ac.Mul(Alpha(fa)).Add(bc.Mul(Alpha(fb)))
}
Loading

0 comments on commit 7ddf133

Please sign in to comment.