graphics: Add ColorM.Apply (#432)

This commit is contained in:
Hajime Hoshi 2017-10-14 23:58:09 +09:00
parent 2624ed6824
commit 6ae67fc6dd
3 changed files with 109 additions and 1 deletions

View File

@ -15,6 +15,8 @@
package ebiten package ebiten
import ( import (
"image/color"
"github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/affine"
) )
@ -38,6 +40,13 @@ func (c *ColorM) Reset() {
c.impl.Reset() c.impl.Reset()
} }
// Apply pre-multiplies a vector (r, g, b, a, 1) by the matrix
// where r, g, b, and a are clr's values after un-multiplied alpha.
// In other words, Apply calculates ColorM * (r, g, b, a, 1)^T.
func (c *ColorM) Apply(clr color.Color) color.Color {
return c.impl.Apply(clr)
}
// Concat multiplies a color matrix with the other color matrix. // Concat multiplies a color matrix with the other color matrix.
// This is same as muptiplying the matrix other and the matrix c in this order. // This is same as muptiplying the matrix other and the matrix c in this order.
func (c *ColorM) Concat(other ColorM) { func (c *ColorM) Concat(other ColorM) {

View File

@ -15,8 +15,10 @@
package ebiten_test package ebiten_test
import ( import (
. "github.com/hajimehoshi/ebiten" "image/color"
"testing" "testing"
. "github.com/hajimehoshi/ebiten"
) )
func TestColorMInit(t *testing.T) { func TestColorMInit(t *testing.T) {
@ -165,3 +167,60 @@ func TestColorMConcatSelf(t *testing.T) {
} }
} }
} }
func abs(x uint32) uint32 {
if x < 0 {
return -x
}
return x
}
func TestColorMApply(t *testing.T) {
mono := ColorM{}
mono.ChangeHSV(0, 0, 1)
shiny := ColorM{}
shiny.Translate(1, 1, 1, 0)
cases := []struct {
ColorM ColorM
In color.Color
Out color.Color
Delta uint32
}{
{
ColorM: ColorM{},
In: color.RGBA{1, 2, 3, 4},
Out: color.RGBA{1, 2, 3, 4},
Delta: 0x101,
},
{
ColorM: mono,
In: color.NRGBA{0xff, 0xff, 0xff, 0},
Out: color.Transparent,
Delta: 0x101,
},
{
ColorM: mono,
In: color.RGBA{0xff, 0, 0, 0xff},
Out: color.RGBA{0x4c, 0x4c, 0x4c, 0xff},
Delta: 0x101,
},
{
ColorM: shiny,
In: color.RGBA{0x80, 0x90, 0xa0, 0xb0},
Out: color.RGBA{0xb0, 0xb0, 0xb0, 0xb0},
Delta: 1,
},
}
for _, c := range cases {
out := c.ColorM.Apply(c.In)
r0, g0, b0, a0 := out.RGBA()
r1, g1, b1, a1 := c.Out.RGBA()
if abs(r0-r1) > c.Delta || abs(g0-g1) > c.Delta ||
abs(b0-b1) > c.Delta || abs(a0-a1) > c.Delta {
println(r0, r1)
t.Errorf("%v.Apply(%v) = %v, want %v", c.ColorM, c.In, out, c.Out)
}
}
}

View File

@ -15,6 +15,7 @@
package affine package affine
import ( import (
"image/color"
"math" "math"
) )
@ -48,6 +49,45 @@ func (c *ColorM) Reset() {
c.elements = nil c.elements = nil
} }
func clamp(x float64) float64 {
if x > 1 {
return 1
}
if x < 0 {
return 0
}
return x
}
func (c *ColorM) Apply(clr color.Color) color.Color {
if c.elements == nil {
return clr
}
r, g, b, a := clr.RGBA()
if a == 0 {
return color.Transparent
}
rf := float64(r) / float64(a)
gf := float64(g) / float64(a)
bf := float64(b) / float64(a)
af := float64(a) / 0xffff
e := c.elements
rf2 := e[0]*rf + e[1]*gf + e[2]*bf + e[3]*af + e[4]
gf2 := e[5]*rf + e[6]*gf + e[7]*bf + e[8]*af + e[9]
bf2 := e[10]*rf + e[11]*gf + e[12]*bf + e[13]*af + e[14]
af2 := e[15]*rf + e[16]*gf + e[17]*bf + e[18]*af + e[19]
rf2 = clamp(rf2)
gf2 = clamp(gf2)
bf2 = clamp(bf2)
af2 = clamp(af2)
return color.NRGBA64{
R: uint16(rf2 * 0xffff),
G: uint16(gf2 * 0xffff),
B: uint16(bf2 * 0xffff),
A: uint16(af2 * 0xffff),
}
}
func (c *ColorM) UnsafeElements() []float64 { func (c *ColorM) UnsafeElements() []float64 {
if c.elements == nil { if c.elements == nil {
c.elements = colorMIdentityElements c.elements = colorMIdentityElements