ebiten: add blend factors

Updates #2382
This commit is contained in:
Hajime Hoshi 2022-10-17 11:25:58 +09:00
parent d2a99afa44
commit da5f5ea327
6 changed files with 328 additions and 27 deletions

View File

@ -77,13 +77,58 @@ const (
// BlendFactorDefault is the default factor value.
// The actual value depends on which source or destination this value is used.
BlendFactorDefault BlendFactor = iota
// BlendFactorZero is a factor:
//
// 0
BlendFactorZero
// BlendFactorOne is a factor:
//
// 1
BlendFactorOne
// BlendFactorSourceColor is a factor:
//
// (source RGBA)
BlendFactorSourceColor
// BlendFactorOneMinusSourceColor is a factor:
//
// 1 - (source color)
BlendFactorOneMinusSourceColor
// BlendFactorSourceAlpha is a factor:
//
// (source alpha)
BlendFactorSourceAlpha
BlendFactorDestinationAlpha
// BlendFactorOneMinusSourceAlpha is a factor:
//
// 1 - (source alpha)
BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
// BlendFactorDestinationColor is a factor:
//
// (destination RGBA)
BlendFactorDestinationColor
// BlendFactorOneMinusDestinationColor is a factor:
//
// 1 - (destination RGBA)
BlendFactorOneMinusDestinationColor
// BlendFactorDestinationAlpha is a factor:
//
// (destination alpha)
BlendFactorDestinationAlpha
// BlendFactorOneMinusDestinationAlpha is a factor:
//
// 1 - (destination alpha)
BlendFactorOneMinusDestinationAlpha
// TODO: Add BlendFactorSourceAlphaSaturated. This might not work well on some platforms like Steam SDK (#2382).
)
func (b BlendFactor) internalBlendFactor(source bool) graphicsdriver.BlendFactor {
@ -98,16 +143,22 @@ func (b BlendFactor) internalBlendFactor(source bool) graphicsdriver.BlendFactor
return graphicsdriver.BlendFactorZero
case BlendFactorOne:
return graphicsdriver.BlendFactorOne
case BlendFactorSourceColor:
return graphicsdriver.BlendFactorSourceColor
case BlendFactorOneMinusSourceColor:
return graphicsdriver.BlendFactorOneMinusSourceColor
case BlendFactorSourceAlpha:
return graphicsdriver.BlendFactorSourceAlpha
case BlendFactorDestinationAlpha:
return graphicsdriver.BlendFactorDestinationAlpha
case BlendFactorOneMinusSourceAlpha:
return graphicsdriver.BlendFactorOneMinusSourceAlpha
case BlendFactorOneMinusDestinationAlpha:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha
case BlendFactorDestinationColor:
return graphicsdriver.BlendFactorDestinationColor
case BlendFactorOneMinusDestinationColor:
return graphicsdriver.BlendFactorOneMinusDestinationColor
case BlendFactorDestinationAlpha:
return graphicsdriver.BlendFactorDestinationAlpha
case BlendFactorOneMinusDestinationAlpha:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha
default:
panic(fmt.Sprintf("ebiten: invalid blend factor: %d", b))
}

View File

@ -3647,3 +3647,211 @@ func TestImageBlendOperation(t *testing.T) {
}
}
}
func TestImageBlendFactor(t *testing.T) {
const w, h = 16, 1
dst := ebiten.NewImage(w, h)
src := ebiten.NewImage(w, h)
dstColor := func(i int) (byte, byte, byte, byte) {
return byte(4 * i * 17), byte(4*i*17 + 1), byte(4*i*17 + 2), byte(4*i*17 + 3)
}
srcColor := func(i int) (byte, byte, byte, byte) {
return byte(4 * i * 13), byte(4*i*13 + 1), byte(4*i*13 + 2), byte(4*i*13 + 3)
}
colorToFloats := func(r, g, b, a byte) (float64, float64, float64, float64) {
return float64(r) / 0xff, float64(g) / 0xff, float64(b) / 0xff, float64(a) / 0xff
}
clamp := func(x int) byte {
if x > 255 {
return 255
}
if x < 0 {
return 0
}
return byte(x)
}
dstPix := make([]byte, 4*w*h)
for i := 0; i < w; i++ {
r, g, b, a := dstColor(i)
dstPix[4*i] = r
dstPix[4*i+1] = g
dstPix[4*i+2] = b
dstPix[4*i+3] = a
}
srcPix := make([]byte, 4*w*h)
for i := 0; i < w; i++ {
r, g, b, a := srcColor(i)
srcPix[4*i] = r
srcPix[4*i+1] = g
srcPix[4*i+2] = b
srcPix[4*i+3] = a
}
src.WritePixels(srcPix)
factors := []ebiten.BlendFactor{
ebiten.BlendFactorZero,
ebiten.BlendFactorOne,
ebiten.BlendFactorSourceColor,
ebiten.BlendFactorOneMinusSourceColor,
ebiten.BlendFactorSourceAlpha,
ebiten.BlendFactorOneMinusSourceAlpha,
ebiten.BlendFactorDestinationColor,
ebiten.BlendFactorOneMinusDestinationColor,
ebiten.BlendFactorDestinationAlpha,
ebiten.BlendFactorOneMinusDestinationAlpha,
}
for _, srcRGBFactor := range factors {
for _, srcAlphaFactor := range factors {
for _, dstRGBFactor := range factors {
for _, dstAlphaFactor := range factors {
// Reset the destination state.
dst.WritePixels(dstPix)
op := &ebiten.DrawImageOptions{}
op.Blend = ebiten.Blend{
BlendFactorSourceRGB: srcRGBFactor,
BlendFactorSourceAlpha: srcAlphaFactor,
BlendFactorDestinationRGB: dstRGBFactor,
BlendFactorDestinationAlpha: dstAlphaFactor,
BlendOperationRGB: ebiten.BlendOperationAdd,
BlendOperationAlpha: ebiten.BlendOperationAdd,
}
dst.DrawImage(src, op)
for i := 0; i < w; i++ {
got := dst.At(i, 0).(color.RGBA)
sr, sg, sb, sa := colorToFloats(srcColor(i))
dr, dg, db, da := colorToFloats(dstColor(i))
var r, g, b, a float64
switch srcRGBFactor {
case ebiten.BlendFactorZero:
r += 0 * sr
g += 0 * sg
b += 0 * sb
case ebiten.BlendFactorOne:
r += 1 * sr
g += 1 * sg
b += 1 * sb
case ebiten.BlendFactorSourceColor:
r += sr * sr
g += sg * sg
b += sb * sb
case ebiten.BlendFactorOneMinusSourceColor:
r += (1 - sr) * sr
g += (1 - sg) * sg
b += (1 - sb) * sb
case ebiten.BlendFactorSourceAlpha:
r += sa * sr
g += sa * sg
b += sa * sb
case ebiten.BlendFactorOneMinusSourceAlpha:
r += (1 - sa) * sr
g += (1 - sa) * sg
b += (1 - sa) * sb
case ebiten.BlendFactorDestinationColor:
r += dr * sr
g += dg * sg
b += db * sb
case ebiten.BlendFactorOneMinusDestinationColor:
r += (1 - dr) * sr
g += (1 - dg) * sg
b += (1 - db) * sb
case ebiten.BlendFactorDestinationAlpha:
r += da * sr
g += da * sg
b += da * sb
case ebiten.BlendFactorOneMinusDestinationAlpha:
r += (1 - da) * sr
g += (1 - da) * sg
b += (1 - da) * sb
}
switch srcAlphaFactor {
case ebiten.BlendFactorZero:
a += 0 * sa
case ebiten.BlendFactorOne:
a += 1 * sa
case ebiten.BlendFactorSourceColor, ebiten.BlendFactorSourceAlpha:
a += sa * sa
case ebiten.BlendFactorOneMinusSourceColor, ebiten.BlendFactorOneMinusSourceAlpha:
a += (1 - sa) * sa
case ebiten.BlendFactorDestinationColor, ebiten.BlendFactorDestinationAlpha:
a += da * sa
case ebiten.BlendFactorOneMinusDestinationColor, ebiten.BlendFactorOneMinusDestinationAlpha:
a += (1 - da) * sa
}
switch dstRGBFactor {
case ebiten.BlendFactorZero:
r += 0 * dr
g += 0 * dg
b += 0 * db
case ebiten.BlendFactorOne:
r += 1 * dr
g += 1 * dg
b += 1 * db
case ebiten.BlendFactorSourceColor:
r += sr * dr
g += sg * dg
b += sb * db
case ebiten.BlendFactorOneMinusSourceColor:
r += (1 - sr) * dr
g += (1 - sg) * dg
b += (1 - sb) * db
case ebiten.BlendFactorSourceAlpha:
r += sa * dr
g += sa * dg
b += sa * db
case ebiten.BlendFactorOneMinusSourceAlpha:
r += (1 - sa) * dr
g += (1 - sa) * dg
b += (1 - sa) * db
case ebiten.BlendFactorDestinationColor:
r += dr * dr
g += dg * dg
b += db * db
case ebiten.BlendFactorOneMinusDestinationColor:
r += (1 - dr) * dr
g += (1 - dg) * dg
b += (1 - db) * db
case ebiten.BlendFactorDestinationAlpha:
r += da * dr
g += da * dg
b += da * db
case ebiten.BlendFactorOneMinusDestinationAlpha:
r += (1 - da) * dr
g += (1 - da) * dg
b += (1 - da) * db
}
switch dstAlphaFactor {
case ebiten.BlendFactorZero:
a += 0 * da
case ebiten.BlendFactorOne:
a += 1 * da
case ebiten.BlendFactorSourceColor, ebiten.BlendFactorSourceAlpha:
a += sa * da
case ebiten.BlendFactorOneMinusSourceColor, ebiten.BlendFactorOneMinusSourceAlpha:
a += (1 - sa) * da
case ebiten.BlendFactorDestinationColor, ebiten.BlendFactorDestinationAlpha:
a += da * da
case ebiten.BlendFactorOneMinusDestinationColor, ebiten.BlendFactorOneMinusDestinationAlpha:
a += (1 - da) * da
}
want := color.RGBA{
R: clamp(int(r * 0xff)),
G: clamp(int(g * 0xff)),
B: clamp(int(b * 0xff)),
A: clamp(int(a * 0xff)),
}
if !sameColors(got, want, 1) {
t.Errorf("dst.At(%d, 0): factors: %d, %d, %d, %d: got: %v, want: %v", i, srcRGBFactor, srcAlphaFactor, dstRGBFactor, dstAlphaFactor, got, want)
}
}
}
}
}
}
}

View File

@ -28,11 +28,15 @@ type BlendFactor byte
const (
BlendFactorZero BlendFactor = iota
BlendFactorOne
BlendFactorSourceColor
BlendFactorOneMinusSourceColor
BlendFactorSourceAlpha
BlendFactorDestinationAlpha
BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorDestinationColor
BlendFactorOneMinusDestinationColor
BlendFactorDestinationAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorSourceAlphaSaturated
)
type BlendOperation byte

View File

@ -26,26 +26,44 @@ import (
const numDescriptorsPerFrame = 32
func blendFactorToBlend(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND {
// D3D12_RENDER_TARGET_BLEND_DESC's *BlendAlpha members don't allow *_COLOR values.
// See https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc.
switch f {
case graphicsdriver.BlendFactorZero:
return _D3D12_BLEND_ZERO
case graphicsdriver.BlendFactorOne:
return _D3D12_BLEND_ONE
case graphicsdriver.BlendFactorSourceColor:
if alpha {
return _D3D12_BLEND_SRC_ALPHA
}
return _D3D12_BLEND_SRC_COLOR
case graphicsdriver.BlendFactorOneMinusSourceColor:
if alpha {
return _D3D12_BLEND_INV_SRC_ALPHA
}
return _D3D12_BLEND_INV_SRC_COLOR
case graphicsdriver.BlendFactorSourceAlpha:
return _D3D12_BLEND_SRC_ALPHA
case graphicsdriver.BlendFactorDestinationAlpha:
return _D3D12_BLEND_DEST_ALPHA
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return _D3D12_BLEND_INV_SRC_ALPHA
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return _D3D12_BLEND_INV_DEST_ALPHA
case graphicsdriver.BlendFactorDestinationColor:
// D3D12_RENDER_TARGET_BLEND_DESC's *BlendAlpha members don't allow *_COLOR values.
// See https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc.
if alpha {
return _D3D12_BLEND_DEST_ALPHA
}
return _D3D12_BLEND_DEST_COLOR
case graphicsdriver.BlendFactorOneMinusDestinationColor:
if alpha {
return _D3D12_BLEND_INV_DEST_ALPHA
}
return _D3D12_BLEND_INV_DEST_COLOR
case graphicsdriver.BlendFactorDestinationAlpha:
return _D3D12_BLEND_DEST_ALPHA
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return _D3D12_BLEND_INV_DEST_ALPHA
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return _D3D12_BLEND_SRC_ALPHA_SAT
default:
panic(fmt.Sprintf("directx: invalid blend factor: %d", f))
}

View File

@ -306,16 +306,24 @@ func blendFactorToMetalBlendFactor(c graphicsdriver.BlendFactor) mtl.BlendFactor
return mtl.BlendFactorZero
case graphicsdriver.BlendFactorOne:
return mtl.BlendFactorOne
case graphicsdriver.BlendFactorSourceColor:
return mtl.BlendFactorSourceColor
case graphicsdriver.BlendFactorOneMinusSourceColor:
return mtl.BlendFactorOneMinusSourceColor
case graphicsdriver.BlendFactorSourceAlpha:
return mtl.BlendFactorSourceAlpha
case graphicsdriver.BlendFactorDestinationAlpha:
return mtl.BlendFactorDestinationAlpha
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return mtl.BlendFactorOneMinusSourceAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return mtl.BlendFactorOneMinusDestinationAlpha
case graphicsdriver.BlendFactorDestinationColor:
return mtl.BlendFactorDestinationColor
case graphicsdriver.BlendFactorOneMinusDestinationColor:
return mtl.BlendFactorOneMinusDestinationColor
case graphicsdriver.BlendFactorDestinationAlpha:
return mtl.BlendFactorDestinationAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return mtl.BlendFactorOneMinusDestinationAlpha
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return mtl.BlendFactorSourceAlphaSaturated
default:
panic(fmt.Sprintf("metal: invalid blend factor: %d", c))
}

View File

@ -24,12 +24,16 @@ import (
type blendFactor int
const (
glDstAlpha blendFactor = 0x0304
glDstColor blendFactor = 0x0306
glDstAlpha blendFactor = 0x304
glDstColor blendFactor = 0x306
glOne blendFactor = 1
glOneMinusDstAlpha blendFactor = 0x0305
glOneMinusSrcAlpha blendFactor = 0x0303
glSrcAlpha blendFactor = 0x0302
glOneMinusDstAlpha blendFactor = 0x305
glOneMinusDstColor blendFactor = 0x307
glOneMinusSrcAlpha blendFactor = 0x303
glOneMinusSrcColor blendFactor = 0x301
glSrcAlpha blendFactor = 0x302
glSrcAlphaSaturate blendFactor = 0x308
glSrcColor blendFactor = 0x300
glZero blendFactor = 0
)
@ -47,16 +51,24 @@ func convertBlendFactor(f graphicsdriver.BlendFactor) blendFactor {
return glZero
case graphicsdriver.BlendFactorOne:
return glOne
case graphicsdriver.BlendFactorSourceColor:
return glSrcColor
case graphicsdriver.BlendFactorOneMinusSourceColor:
return glOneMinusSrcColor
case graphicsdriver.BlendFactorSourceAlpha:
return glSrcAlpha
case graphicsdriver.BlendFactorDestinationAlpha:
return glDstAlpha
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return glOneMinusSrcAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return glOneMinusDstAlpha
case graphicsdriver.BlendFactorDestinationColor:
return glDstColor
case graphicsdriver.BlendFactorOneMinusDestinationColor:
return glOneMinusDstColor
case graphicsdriver.BlendFactorDestinationAlpha:
return glDstAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return glOneMinusDstAlpha
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return glSrcAlphaSaturate
default:
panic(fmt.Sprintf("opengl: invalid blend factor %d", f))
}