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. // BlendFactorDefault is the default factor value.
// The actual value depends on which source or destination this value is used. // The actual value depends on which source or destination this value is used.
BlendFactorDefault BlendFactor = iota BlendFactorDefault BlendFactor = iota
// BlendFactorZero is a factor:
//
// 0
BlendFactorZero BlendFactorZero
// BlendFactorOne is a factor:
//
// 1
BlendFactorOne BlendFactorOne
// BlendFactorSourceColor is a factor:
//
// (source RGBA)
BlendFactorSourceColor
// BlendFactorOneMinusSourceColor is a factor:
//
// 1 - (source color)
BlendFactorOneMinusSourceColor
// BlendFactorSourceAlpha is a factor:
//
// (source alpha)
BlendFactorSourceAlpha BlendFactorSourceAlpha
BlendFactorDestinationAlpha
// BlendFactorOneMinusSourceAlpha is a factor:
//
// 1 - (source alpha)
BlendFactorOneMinusSourceAlpha BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
// BlendFactorDestinationColor is a factor:
//
// (destination RGBA)
BlendFactorDestinationColor 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 { func (b BlendFactor) internalBlendFactor(source bool) graphicsdriver.BlendFactor {
@ -98,16 +143,22 @@ func (b BlendFactor) internalBlendFactor(source bool) graphicsdriver.BlendFactor
return graphicsdriver.BlendFactorZero return graphicsdriver.BlendFactorZero
case BlendFactorOne: case BlendFactorOne:
return graphicsdriver.BlendFactorOne return graphicsdriver.BlendFactorOne
case BlendFactorSourceColor:
return graphicsdriver.BlendFactorSourceColor
case BlendFactorOneMinusSourceColor:
return graphicsdriver.BlendFactorOneMinusSourceColor
case BlendFactorSourceAlpha: case BlendFactorSourceAlpha:
return graphicsdriver.BlendFactorSourceAlpha return graphicsdriver.BlendFactorSourceAlpha
case BlendFactorDestinationAlpha:
return graphicsdriver.BlendFactorDestinationAlpha
case BlendFactorOneMinusSourceAlpha: case BlendFactorOneMinusSourceAlpha:
return graphicsdriver.BlendFactorOneMinusSourceAlpha return graphicsdriver.BlendFactorOneMinusSourceAlpha
case BlendFactorOneMinusDestinationAlpha:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha
case BlendFactorDestinationColor: case BlendFactorDestinationColor:
return graphicsdriver.BlendFactorDestinationColor return graphicsdriver.BlendFactorDestinationColor
case BlendFactorOneMinusDestinationColor:
return graphicsdriver.BlendFactorOneMinusDestinationColor
case BlendFactorDestinationAlpha:
return graphicsdriver.BlendFactorDestinationAlpha
case BlendFactorOneMinusDestinationAlpha:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha
default: default:
panic(fmt.Sprintf("ebiten: invalid blend factor: %d", b)) 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 ( const (
BlendFactorZero BlendFactor = iota BlendFactorZero BlendFactor = iota
BlendFactorOne BlendFactorOne
BlendFactorSourceColor
BlendFactorOneMinusSourceColor
BlendFactorSourceAlpha BlendFactorSourceAlpha
BlendFactorDestinationAlpha
BlendFactorOneMinusSourceAlpha BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorDestinationColor BlendFactorDestinationColor
BlendFactorOneMinusDestinationColor
BlendFactorDestinationAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorSourceAlphaSaturated
) )
type BlendOperation byte type BlendOperation byte

View File

@ -26,26 +26,44 @@ import (
const numDescriptorsPerFrame = 32 const numDescriptorsPerFrame = 32
func blendFactorToBlend(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND { 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 { switch f {
case graphicsdriver.BlendFactorZero: case graphicsdriver.BlendFactorZero:
return _D3D12_BLEND_ZERO return _D3D12_BLEND_ZERO
case graphicsdriver.BlendFactorOne: case graphicsdriver.BlendFactorOne:
return _D3D12_BLEND_ONE 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: case graphicsdriver.BlendFactorSourceAlpha:
return _D3D12_BLEND_SRC_ALPHA return _D3D12_BLEND_SRC_ALPHA
case graphicsdriver.BlendFactorDestinationAlpha:
return _D3D12_BLEND_DEST_ALPHA
case graphicsdriver.BlendFactorOneMinusSourceAlpha: case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return _D3D12_BLEND_INV_SRC_ALPHA return _D3D12_BLEND_INV_SRC_ALPHA
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return _D3D12_BLEND_INV_DEST_ALPHA
case graphicsdriver.BlendFactorDestinationColor: 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 { if alpha {
return _D3D12_BLEND_DEST_ALPHA return _D3D12_BLEND_DEST_ALPHA
} }
return _D3D12_BLEND_DEST_COLOR 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: default:
panic(fmt.Sprintf("directx: invalid blend factor: %d", f)) 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 return mtl.BlendFactorZero
case graphicsdriver.BlendFactorOne: case graphicsdriver.BlendFactorOne:
return mtl.BlendFactorOne return mtl.BlendFactorOne
case graphicsdriver.BlendFactorSourceColor:
return mtl.BlendFactorSourceColor
case graphicsdriver.BlendFactorOneMinusSourceColor:
return mtl.BlendFactorOneMinusSourceColor
case graphicsdriver.BlendFactorSourceAlpha: case graphicsdriver.BlendFactorSourceAlpha:
return mtl.BlendFactorSourceAlpha return mtl.BlendFactorSourceAlpha
case graphicsdriver.BlendFactorDestinationAlpha:
return mtl.BlendFactorDestinationAlpha
case graphicsdriver.BlendFactorOneMinusSourceAlpha: case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return mtl.BlendFactorOneMinusSourceAlpha return mtl.BlendFactorOneMinusSourceAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return mtl.BlendFactorOneMinusDestinationAlpha
case graphicsdriver.BlendFactorDestinationColor: case graphicsdriver.BlendFactorDestinationColor:
return mtl.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: default:
panic(fmt.Sprintf("metal: invalid blend factor: %d", c)) panic(fmt.Sprintf("metal: invalid blend factor: %d", c))
} }

View File

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