mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-23 17:32:02 +01:00
parent
5e459bbe42
commit
058b8d5635
54
image.go
54
image.go
@ -286,6 +286,19 @@ const (
|
|||||||
EvenOdd
|
EvenOdd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ColorScaleFormat is the format of color scales in vertices.
|
||||||
|
type ColorScaleFormat int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ColorScaleFormatStraightAlpha indicates color scales in vertices are
|
||||||
|
// straight-alpha encoded color multiplier.
|
||||||
|
ColorScaleFormatStraightAlpha ColorScaleFormat = iota
|
||||||
|
|
||||||
|
// ColorScaleFormatStraightAlpha indicates color scales in vertices are
|
||||||
|
// premultiplied-alpha encoded color multiplier.
|
||||||
|
ColorScaleFormatPremultipliedAlpha
|
||||||
|
)
|
||||||
|
|
||||||
// DrawTrianglesOptions represents options for DrawTriangles.
|
// DrawTrianglesOptions represents options for DrawTriangles.
|
||||||
type DrawTrianglesOptions struct {
|
type DrawTrianglesOptions struct {
|
||||||
// ColorM is a color matrix to draw.
|
// ColorM is a color matrix to draw.
|
||||||
@ -293,6 +306,10 @@ type DrawTrianglesOptions struct {
|
|||||||
// ColorM is applied before vertex color scale is applied.
|
// ColorM is applied before vertex color scale is applied.
|
||||||
ColorM ColorM
|
ColorM ColorM
|
||||||
|
|
||||||
|
// ColorScaleFormat is the format of color scales in vertices.
|
||||||
|
// The default (zero) value is ColorScaleFormatStraightAlpha.
|
||||||
|
ColorScaleFormat
|
||||||
|
|
||||||
// CompositeMode is a composite mode to draw.
|
// CompositeMode is a composite mode to draw.
|
||||||
// The default (zero) value is regular alpha blending.
|
// The default (zero) value is regular alpha blending.
|
||||||
CompositeMode CompositeMode
|
CompositeMode CompositeMode
|
||||||
@ -377,17 +394,32 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
|
|
||||||
vs := graphics.Vertices(len(vertices))
|
vs := graphics.Vertices(len(vertices))
|
||||||
dst := i
|
dst := i
|
||||||
for i, v := range vertices {
|
if options.ColorScaleFormat == ColorScaleFormatStraightAlpha {
|
||||||
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
for i, v := range vertices {
|
||||||
vs[i*graphics.VertexFloatCount] = dx
|
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
||||||
vs[i*graphics.VertexFloatCount+1] = dy
|
vs[i*graphics.VertexFloatCount] = dx
|
||||||
sx, sy := img.adjustPositionF32(v.SrcX, v.SrcY)
|
vs[i*graphics.VertexFloatCount+1] = dy
|
||||||
vs[i*graphics.VertexFloatCount+2] = sx
|
sx, sy := img.adjustPositionF32(v.SrcX, v.SrcY)
|
||||||
vs[i*graphics.VertexFloatCount+3] = sy
|
vs[i*graphics.VertexFloatCount+2] = sx
|
||||||
vs[i*graphics.VertexFloatCount+4] = v.ColorR * v.ColorA * cr
|
vs[i*graphics.VertexFloatCount+3] = sy
|
||||||
vs[i*graphics.VertexFloatCount+5] = v.ColorG * v.ColorA * cg
|
vs[i*graphics.VertexFloatCount+4] = v.ColorR * v.ColorA * cr
|
||||||
vs[i*graphics.VertexFloatCount+6] = v.ColorB * v.ColorA * cb
|
vs[i*graphics.VertexFloatCount+5] = v.ColorG * v.ColorA * cg
|
||||||
vs[i*graphics.VertexFloatCount+7] = v.ColorA * ca
|
vs[i*graphics.VertexFloatCount+6] = v.ColorB * v.ColorA * cb
|
||||||
|
vs[i*graphics.VertexFloatCount+7] = v.ColorA * ca
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, v := range vertices {
|
||||||
|
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
||||||
|
vs[i*graphics.VertexFloatCount] = dx
|
||||||
|
vs[i*graphics.VertexFloatCount+1] = dy
|
||||||
|
sx, sy := img.adjustPositionF32(v.SrcX, v.SrcY)
|
||||||
|
vs[i*graphics.VertexFloatCount+2] = sx
|
||||||
|
vs[i*graphics.VertexFloatCount+3] = sy
|
||||||
|
vs[i*graphics.VertexFloatCount+4] = v.ColorR * cr
|
||||||
|
vs[i*graphics.VertexFloatCount+5] = v.ColorG * cg
|
||||||
|
vs[i*graphics.VertexFloatCount+6] = v.ColorB * cb
|
||||||
|
vs[i*graphics.VertexFloatCount+7] = v.ColorA * ca
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is := make([]uint16, len(indices))
|
is := make([]uint16, len(indices))
|
||||||
copy(is, indices)
|
copy(is, indices)
|
||||||
|
232
image_test.go
232
image_test.go
@ -1929,7 +1929,6 @@ func TestImageWritePixelsOnSubImage(t *testing.T) {
|
|||||||
func TestImageDrawTrianglesWithColorM(t *testing.T) {
|
func TestImageDrawTrianglesWithColorM(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
dst0 := ebiten.NewImage(w, h)
|
dst0 := ebiten.NewImage(w, h)
|
||||||
dst1 := ebiten.NewImage(w, h)
|
|
||||||
src := ebiten.NewImage(w, h)
|
src := ebiten.NewImage(w, h)
|
||||||
src.Fill(color.White)
|
src.Fill(color.White)
|
||||||
|
|
||||||
@ -1980,65 +1979,90 @@ func TestImageDrawTrianglesWithColorM(t *testing.T) {
|
|||||||
is := []uint16{0, 1, 2, 1, 2, 3}
|
is := []uint16{0, 1, 2, 1, 2, 3}
|
||||||
dst0.DrawTriangles(vs0, is, src, op)
|
dst0.DrawTriangles(vs0, is, src, op)
|
||||||
|
|
||||||
vs1 := []ebiten.Vertex{
|
for _, format := range []ebiten.ColorScaleFormat{
|
||||||
{
|
ebiten.ColorScaleFormatStraightAlpha,
|
||||||
DstX: 0,
|
ebiten.ColorScaleFormatPremultipliedAlpha,
|
||||||
DstY: 0,
|
} {
|
||||||
SrcX: 0,
|
format := format
|
||||||
SrcY: 0,
|
t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
|
||||||
ColorR: 0.2,
|
var cr, cg, cb, ca float32
|
||||||
ColorG: 0.4,
|
switch format {
|
||||||
ColorB: 0.6,
|
case ebiten.ColorScaleFormatStraightAlpha:
|
||||||
ColorA: 0.8,
|
// The values are the same as ColorM.Scale
|
||||||
},
|
cr = 0.2
|
||||||
{
|
cg = 0.4
|
||||||
DstX: w,
|
cb = 0.6
|
||||||
DstY: 0,
|
ca = 0.8
|
||||||
SrcX: w,
|
case ebiten.ColorScaleFormatPremultipliedAlpha:
|
||||||
SrcY: 0,
|
cr = 0.2 * 0.8
|
||||||
ColorR: 0.2,
|
cg = 0.4 * 0.8
|
||||||
ColorG: 0.4,
|
cb = 0.6 * 0.8
|
||||||
ColorB: 0.6,
|
ca = 0.8
|
||||||
ColorA: 0.8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DstX: 0,
|
|
||||||
DstY: h,
|
|
||||||
SrcX: 0,
|
|
||||||
SrcY: h,
|
|
||||||
ColorR: 0.2,
|
|
||||||
ColorG: 0.4,
|
|
||||||
ColorB: 0.6,
|
|
||||||
ColorA: 0.8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DstX: w,
|
|
||||||
DstY: h,
|
|
||||||
SrcX: w,
|
|
||||||
SrcY: h,
|
|
||||||
ColorR: 0.2,
|
|
||||||
ColorG: 0.4,
|
|
||||||
ColorB: 0.6,
|
|
||||||
ColorA: 0.8,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
dst1.DrawTriangles(vs1, is, src, nil)
|
|
||||||
|
|
||||||
for j := 0; j < h; j++ {
|
|
||||||
for i := 0; i < w; i++ {
|
|
||||||
got := dst0.At(i, j)
|
|
||||||
want := dst1.At(i, j)
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
|
|
||||||
}
|
}
|
||||||
}
|
vs1 := []ebiten.Vertex{
|
||||||
|
{
|
||||||
|
DstX: 0,
|
||||||
|
DstY: 0,
|
||||||
|
SrcX: 0,
|
||||||
|
SrcY: 0,
|
||||||
|
ColorR: cr,
|
||||||
|
ColorG: cg,
|
||||||
|
ColorB: cb,
|
||||||
|
ColorA: ca,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: w,
|
||||||
|
DstY: 0,
|
||||||
|
SrcX: w,
|
||||||
|
SrcY: 0,
|
||||||
|
ColorR: cr,
|
||||||
|
ColorG: cg,
|
||||||
|
ColorB: cb,
|
||||||
|
ColorA: ca,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: 0,
|
||||||
|
DstY: h,
|
||||||
|
SrcX: 0,
|
||||||
|
SrcY: h,
|
||||||
|
ColorR: cr,
|
||||||
|
ColorG: cg,
|
||||||
|
ColorB: cb,
|
||||||
|
ColorA: ca,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: w,
|
||||||
|
DstY: h,
|
||||||
|
SrcX: w,
|
||||||
|
SrcY: h,
|
||||||
|
ColorR: cr,
|
||||||
|
ColorG: cg,
|
||||||
|
ColorB: cb,
|
||||||
|
ColorA: ca,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dst1 := ebiten.NewImage(w, h)
|
||||||
|
op := &ebiten.DrawTrianglesOptions{}
|
||||||
|
op.ColorScaleFormat = format
|
||||||
|
dst1.DrawTriangles(vs1, is, src, op)
|
||||||
|
|
||||||
|
for j := 0; j < h; j++ {
|
||||||
|
for i := 0; i < w; i++ {
|
||||||
|
got := dst0.At(i, j)
|
||||||
|
want := dst1.At(i, j)
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImageDrawTrianglesInterpolatesColors(t *testing.T) {
|
func TestImageDrawTrianglesInterpolatesColors(t *testing.T) {
|
||||||
const w, h = 3, 1
|
const w, h = 3, 1
|
||||||
src := ebiten.NewImage(w, h)
|
src := ebiten.NewImage(w, h)
|
||||||
dst := ebiten.NewImage(w, h)
|
|
||||||
src.Fill(color.White)
|
src.Fill(color.White)
|
||||||
|
|
||||||
vs := []ebiten.Vertex{
|
vs := []ebiten.Vertex{
|
||||||
@ -2083,25 +2107,38 @@ func TestImageDrawTrianglesInterpolatesColors(t *testing.T) {
|
|||||||
ColorA: 1,
|
ColorA: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
dst.Fill(color.RGBA{0x00, 0x00, 0xff, 0xff})
|
|
||||||
op := &ebiten.DrawTrianglesOptions{}
|
|
||||||
is := []uint16{0, 1, 2, 1, 2, 3}
|
|
||||||
dst.DrawTriangles(vs, is, src, op)
|
|
||||||
|
|
||||||
got := dst.At(1, 0).(color.RGBA)
|
for _, format := range []ebiten.ColorScaleFormat{
|
||||||
|
ebiten.ColorScaleFormatStraightAlpha,
|
||||||
|
ebiten.ColorScaleFormatPremultipliedAlpha,
|
||||||
|
} {
|
||||||
|
format := format
|
||||||
|
t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
|
||||||
|
dst := ebiten.NewImage(w, h)
|
||||||
|
dst.Fill(color.RGBA{0x00, 0x00, 0xff, 0xff})
|
||||||
|
|
||||||
// Correct color interpolation uses the alpha channel and notices that colors on the left side of the texture are fully transparent.
|
op := &ebiten.DrawTrianglesOptions{}
|
||||||
want := color.RGBA{0x00, 0x80, 0x80, 0xff}
|
op.ColorScaleFormat = format
|
||||||
|
|
||||||
// Interpolation isn't exactly specified, so a range is accepable.
|
is := []uint16{0, 1, 2, 1, 2, 3}
|
||||||
diff := math.Max(math.Max(math.Max(
|
dst.DrawTriangles(vs, is, src, op)
|
||||||
math.Abs(float64(got.R)-float64(want.R)),
|
|
||||||
math.Abs(float64(got.G)-float64(want.G))),
|
|
||||||
math.Abs(float64(got.B)-float64(want.B))),
|
|
||||||
math.Abs(float64(got.A)-float64(want.A)))
|
|
||||||
|
|
||||||
if diff > 5 {
|
got := dst.At(1, 0).(color.RGBA)
|
||||||
t.Errorf("At(1, 0): got: %v, want: %v", got, want)
|
|
||||||
|
// Correct color interpolation uses the alpha channel
|
||||||
|
// and notices that colors on the left side of the texture are fully transparent.
|
||||||
|
var want color.RGBA
|
||||||
|
switch format {
|
||||||
|
case ebiten.ColorScaleFormatStraightAlpha:
|
||||||
|
want = color.RGBA{0x00, 0x80, 0x80, 0xff}
|
||||||
|
case ebiten.ColorScaleFormatPremultipliedAlpha:
|
||||||
|
want = color.RGBA{0x80, 0x80, 0x80, 0xff}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sameColors(got, want, 2) {
|
||||||
|
t.Errorf("At(1, 0): got: %v, want: %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2174,14 +2211,7 @@ func TestImageDrawTrianglesShaderInterpolatesValues(t *testing.T) {
|
|||||||
// Shaders get each color value interpolated independently.
|
// Shaders get each color value interpolated independently.
|
||||||
want := color.RGBA{0x80, 0x80, 0x80, 0xff}
|
want := color.RGBA{0x80, 0x80, 0x80, 0xff}
|
||||||
|
|
||||||
// Interpolation isn't exactly specified, so a range is accepable.
|
if !sameColors(got, want, 2) {
|
||||||
diff := math.Max(math.Max(math.Max(
|
|
||||||
math.Abs(float64(got.R)-float64(want.R)),
|
|
||||||
math.Abs(float64(got.G)-float64(want.G))),
|
|
||||||
math.Abs(float64(got.B)-float64(want.B))),
|
|
||||||
math.Abs(float64(got.A)-float64(want.A)))
|
|
||||||
|
|
||||||
if diff > 5 {
|
|
||||||
t.Errorf("At(1, 0): got: %v, want: %v", got, want)
|
t.Errorf("At(1, 0): got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3436,7 +3466,6 @@ func TestImageTooManyConstantBuffersInDirectX(t *testing.T) {
|
|||||||
|
|
||||||
func TestImageColorMAndScale(t *testing.T) {
|
func TestImageColorMAndScale(t *testing.T) {
|
||||||
const w, h = 16, 16
|
const w, h = 16, 16
|
||||||
dst := ebiten.NewImage(w, h)
|
|
||||||
src := ebiten.NewImage(w, h)
|
src := ebiten.NewImage(w, h)
|
||||||
|
|
||||||
src.Fill(color.RGBA{0x80, 0x80, 0x80, 0x80})
|
src.Fill(color.RGBA{0x80, 0x80, 0x80, 0x80})
|
||||||
@ -3483,19 +3512,42 @@ func TestImageColorMAndScale(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
is := []uint16{0, 1, 2, 1, 2, 3}
|
is := []uint16{0, 1, 2, 1, 2, 3}
|
||||||
op := &ebiten.DrawTrianglesOptions{}
|
|
||||||
op.ColorM.Translate(0.25, 0.25, 0.25, 0)
|
|
||||||
dst.DrawTriangles(vs, is, src, op)
|
|
||||||
|
|
||||||
got := dst.At(0, 0).(color.RGBA)
|
for _, format := range []ebiten.ColorScaleFormat{
|
||||||
alphaBeforeScale := 0.5
|
ebiten.ColorScaleFormatStraightAlpha,
|
||||||
want := color.RGBA{
|
ebiten.ColorScaleFormatPremultipliedAlpha,
|
||||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
} {
|
||||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25 * 0.75)),
|
format := format
|
||||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
|
||||||
byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
|
dst := ebiten.NewImage(w, h)
|
||||||
}
|
|
||||||
if !sameColors(got, want, 2) {
|
op := &ebiten.DrawTrianglesOptions{}
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
op.ColorM.Translate(0.25, 0.25, 0.25, 0)
|
||||||
|
op.ColorScaleFormat = format
|
||||||
|
dst.DrawTriangles(vs, is, src, op)
|
||||||
|
|
||||||
|
got := dst.At(0, 0).(color.RGBA)
|
||||||
|
alphaBeforeScale := 0.5
|
||||||
|
var want color.RGBA
|
||||||
|
switch format {
|
||||||
|
case ebiten.ColorScaleFormatStraightAlpha:
|
||||||
|
want = color.RGBA{
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25 * 0.75)),
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
||||||
|
byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
|
||||||
|
}
|
||||||
|
case ebiten.ColorScaleFormatPremultipliedAlpha:
|
||||||
|
want = color.RGBA{
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25)),
|
||||||
|
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
|
||||||
|
byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sameColors(got, want, 2) {
|
||||||
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user