Use our own DrawEffects enum for draw effects (#564)

* Add PushEffect, handle effects in renderer

* Set effects instead of blend

* Stop using PushCompositeMode, use PushEffect

* Remove remaining composite mode things
This commit is contained in:
Ziemas 2020-07-09 03:57:35 +02:00 committed by GitHub
parent a10a53be26
commit e2e8a303c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 87 additions and 172 deletions

View File

@ -1,47 +0,0 @@
package d2enum
// CompositeMode defines the composite mode
type CompositeMode int
const (
// CompositeModeSourceOver applies a composite based on:
// c_out = c_src + c_dst × (1 - α_src) (Regular alpha blending)
CompositeModeSourceOver CompositeMode = iota + 1
// CompositeModeClear applies a composite based on: c_out = 0
CompositeModeClear
// CompositeModeCopy applies a composite based on: c_out = c_src
CompositeModeCopy
// CompositeModeDestination applies a composite based on: c_out = c_dst
CompositeModeDestination
// CompositeModeDestinationOver applies a composite based on: c_out = c_src × (1 - α_dst) + c_dst
CompositeModeDestinationOver
// CompositeModeSourceIn applies a composite based on: c_out = c_src × α_dst
CompositeModeSourceIn
// CompositeModeDestinationIn applies a composite based on: c_out = c_dst × α_src
CompositeModeDestinationIn
// CompositeModeSourceOut applies a composite based on: c_out = c_src × (1 - α_dst)
CompositeModeSourceOut
// CompositeModeDestinationOut applies a composite based on: c_out = c_dst × (1 - α_src)
CompositeModeDestinationOut
// CompositeModeSourceAtop applies a composite based on: c_out = c_src × α_dst + c_dst × (1 - α_src)
CompositeModeSourceAtop
// CompositeModeDestinationAtop applies a composite based on: c_out = c_src × (1 - α_dst) + c_dst × α_src
CompositeModeDestinationAtop
// CompositeModeXor applies a composite based on: c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src)
CompositeModeXor
// CompositeModeLighter applies a composite based on:
// c_out = c_src + c_dst Sum of source and destination (a.k.a. 'plus' or 'additive')
CompositeModeLighter
)

View File

@ -3,6 +3,8 @@ package d2interface
import (
"image"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// Animation is an animation
@ -35,5 +37,5 @@ type Animation interface {
SetColorMod(colorMod color.Color)
GetPlayedCount() int
ResetPlayedCount()
SetBlend(blend bool)
SetEffect(effect d2enum.DrawEffect)
}

View File

@ -18,7 +18,7 @@ type Surface interface {
Pop()
PopN(n int)
PushColor(color color.Color)
PushCompositeMode(mode d2enum.CompositeMode)
PushEffect(effect d2enum.DrawEffect)
PushFilter(filter d2enum.Filter)
PushTranslation(x, y int)
PushBrightness(brightness float64)

View File

@ -40,12 +40,12 @@ type animationDirection struct {
// animation has directionality, play modes, and frame counting
type animation struct {
directions []animationDirection
effect d2enum.DrawEffect
colorMod color.Color
frameIndex int
directionIndex int
lastFrameTime float64
playedCount int
compositeMode d2enum.CompositeMode
playMode playMode
playLength float64
subStartingFrame int
@ -120,7 +120,7 @@ func (a *animation) Render(target d2iface.Surface) error {
target.PushTranslation(frame.offsetX, frame.offsetY)
defer target.Pop()
target.PushCompositeMode(a.compositeMode)
target.PushEffect(a.effect)
defer target.Pop()
target.PushColor(a.colorMod)
@ -148,7 +148,7 @@ func (a *animation) RenderSection(sfc d2iface.Surface, bound image.Rectangle) er
frame := direction.frames[a.frameIndex]
sfc.PushTranslation(frame.offsetX, frame.offsetY)
sfc.PushCompositeMode(a.compositeMode)
sfc.PushEffect(a.effect)
sfc.PushColor(a.colorMod)
defer sfc.PopN(3)
@ -305,11 +305,6 @@ func (a *animation) ResetPlayedCount() {
a.playedCount = 0
}
// SetBlend sets the Animation alpha blending status
func (a *animation) SetBlend(blend bool) {
if blend {
a.compositeMode = d2enum.CompositeModeLighter
} else {
a.compositeMode = d2enum.CompositeModeSourceOver
}
func (a *animation) SetEffect(e d2enum.DrawEffect) {
a.effect = e
}

View File

@ -53,7 +53,7 @@ func (am *animationManager) LoadAnimation(
return nil, err
}
animation, err = CreateDC6Animation(am.renderer, animationPath, palette)
animation, err = CreateDC6Animation(am.renderer, animationPath, palette, d2enum.DrawEffectNone)
if err != nil {
return nil, err
}

View File

@ -18,7 +18,7 @@ type DC6Animation struct {
// CreateDC6Animation creates an Animation from d2dc6.DC6 and d2dat.DATPalette
func CreateDC6Animation(renderer d2iface.Renderer, dc6Path string,
palette d2iface.Palette) (d2iface.Animation, error) {
palette d2iface.Palette, effect d2enum.DrawEffect) (d2iface.Animation, error) {
dc6, err := loadDC6(dc6Path)
if err != nil {
return nil, err
@ -29,6 +29,7 @@ func CreateDC6Animation(renderer d2iface.Renderer, dc6Path string,
playLength: defaultPlayLength,
playLoop: true,
originAtBottom: true,
effect: effect,
}
anim := DC6Animation{

View File

@ -15,7 +15,6 @@ import (
type DCCAnimation struct {
animation
dccPath string
effect d2enum.DrawEffect
palette d2iface.Palette
renderer d2iface.Renderer
}
@ -32,6 +31,7 @@ func CreateDCCAnimation(renderer d2iface.Renderer, dccPath string, palette d2ifa
playLength: defaultPlayLength,
playLoop: true,
directions: make([]animationDirection, dcc.NumberOfDirections),
effect: effect,
}
DCC := DCCAnimation{
@ -39,17 +39,8 @@ func CreateDCCAnimation(renderer d2iface.Renderer, dccPath string, palette d2ifa
dccPath: dccPath,
palette: palette,
renderer: renderer,
effect: effect,
}
// Really the renderer should take DrawEffects and do the right thing
if effect == d2enum.DrawEffectModulate {
DCC.SetBlend(true)
}
// Transparency is now no longer handled, it should be done by using PL2 palette and
// picking the appropriate transform for the transparency level
err = DCC.SetDirection(0)
if err != nil {
return nil, err

View File

@ -98,7 +98,6 @@ func (f *Font) GetTextMetrics(text string) (width, height int) {
// RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise)
func (f *Font) RenderText(text string, target d2interface.Surface) error {
f.sheet.SetColorMod(f.color)
f.sheet.SetBlend(false)
lines := strings.Split(text, "\n")

View File

@ -1,6 +1,7 @@
package d2gui
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
)
@ -53,7 +54,6 @@ func createAnimatedSprite(imagePath, palettePath string, direction AnimationDire
} else {
sprite.animation.PlayBackward()
}
sprite.animation.SetBlend(false)
sprite.SetVisible(true)
return sprite, nil
@ -84,3 +84,7 @@ func (s *Sprite) advance(elapsed float64) error {
func (s *Sprite) getSize() (int, int) {
return s.animation.GetCurrentFrameSize()
}
func (s *Sprite) SetEffect(e d2enum.DrawEffect) {
s.animation.SetEffect(e)
}

View File

@ -5,6 +5,7 @@ import (
"math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
)
@ -27,7 +28,7 @@ func CreateMissile(x, y int, record *d2datadict.MissileRecord) (*Missile, error)
animation.SetSubLoop(record.Animation.SubStartingFrame, record.Animation.SubEndingFrame)
}
animation.SetBlend(true)
animation.SetEffect(d2enum.DrawEffectModulate)
//animation.SetPlaySpeed(float64(record.Animation.AnimationSpeed))
animation.SetPlayLoop(record.Animation.LoopAnimation)
animation.PlayForward()

View File

@ -1,72 +0,0 @@
package ebiten
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/hajimehoshi/ebiten"
)
func d2ToEbitenCompositeMode(comp d2enum.CompositeMode) ebiten.CompositeMode {
switch comp {
case d2enum.CompositeModeSourceOver:
return ebiten.CompositeModeSourceOver
case d2enum.CompositeModeClear:
return ebiten.CompositeModeClear
case d2enum.CompositeModeCopy:
return ebiten.CompositeModeCopy
case d2enum.CompositeModeDestination:
return ebiten.CompositeModeDestination
case d2enum.CompositeModeDestinationOver:
return ebiten.CompositeModeDestinationOver
case d2enum.CompositeModeSourceIn:
return ebiten.CompositeModeSourceIn
case d2enum.CompositeModeDestinationIn:
return ebiten.CompositeModeDestinationIn
case d2enum.CompositeModeSourceOut:
return ebiten.CompositeModeSourceOut
case d2enum.CompositeModeDestinationOut:
return ebiten.CompositeModeDestinationOut
case d2enum.CompositeModeSourceAtop:
return ebiten.CompositeModeSourceAtop
case d2enum.CompositeModeDestinationAtop:
return ebiten.CompositeModeDestinationAtop
case d2enum.CompositeModeXor:
return ebiten.CompositeModeXor
case d2enum.CompositeModeLighter:
return ebiten.CompositeModeLighter
}
return ebiten.CompositeModeSourceOver
}
func ebitenToD2CompositeMode(comp ebiten.CompositeMode) d2enum.CompositeMode {
switch comp {
case ebiten.CompositeModeSourceOver:
return d2enum.CompositeModeSourceOver
case ebiten.CompositeModeClear:
return d2enum.CompositeModeClear
case ebiten.CompositeModeCopy:
return d2enum.CompositeModeCopy
case ebiten.CompositeModeDestination:
return d2enum.CompositeModeDestination
case ebiten.CompositeModeDestinationOver:
return d2enum.CompositeModeDestinationOver
case ebiten.CompositeModeSourceIn:
return d2enum.CompositeModeSourceIn
case ebiten.CompositeModeDestinationIn:
return d2enum.CompositeModeDestinationIn
case ebiten.CompositeModeSourceOut:
return d2enum.CompositeModeSourceOut
case ebiten.CompositeModeDestinationOut:
return d2enum.CompositeModeDestinationOut
case ebiten.CompositeModeSourceAtop:
return d2enum.CompositeModeSourceAtop
case ebiten.CompositeModeDestinationAtop:
return d2enum.CompositeModeDestinationAtop
case ebiten.CompositeModeXor:
return d2enum.CompositeModeXor
case ebiten.CompositeModeLighter:
return d2enum.CompositeModeLighter
}
return d2enum.CompositeModeSourceOver
}

View File

@ -67,7 +67,7 @@ func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surfa
surface.(*ebitenSurface).image,
surfaceState{
filter: ebiten.FilterNearest,
mode: ebiten.CompositeModeSourceOver,
effect: d2enum.DrawEffectNone,
},
)

View File

@ -32,7 +32,7 @@ type ebitenSurface struct {
}
func createEbitenSurface(img *ebiten.Image, currentState ...surfaceState) *ebitenSurface {
state := surfaceState{}
state := surfaceState{effect: d2enum.DrawEffectNone}
if len(currentState) > 0 {
state = currentState[0]
}
@ -50,9 +50,9 @@ func (s *ebitenSurface) PushTranslation(x, y int) {
s.stateCurrent.y += y
}
func (s *ebitenSurface) PushCompositeMode(mode d2enum.CompositeMode) {
func (s *ebitenSurface) PushEffect(effect d2enum.DrawEffect) {
s.stateStack = append(s.stateStack, s.stateCurrent)
s.stateCurrent.mode = d2ToEbitenCompositeMode(mode)
s.stateCurrent.effect = effect
}
func (s *ebitenSurface) PushFilter(filter d2enum.Filter) {
@ -87,7 +87,7 @@ func (s *ebitenSurface) PopN(n int) {
}
func (s *ebitenSurface) Render(sfc d2interface.Surface) error {
opts := &ebiten.DrawImageOptions{CompositeMode: s.stateCurrent.mode}
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y))
opts.Filter = s.stateCurrent.filter
@ -99,6 +99,25 @@ func (s *ebitenSurface) Render(sfc d2interface.Surface) error {
opts.ColorM.ChangeHSV(0, 1, s.stateCurrent.brightness)
}
// Are these correct? who even knows
switch s.stateCurrent.effect {
case d2enum.DrawEffectPctTransparency25:
opts.ColorM.Translate(0, 0, 0, -0.25)
case d2enum.DrawEffectPctTransparency50:
opts.ColorM.Translate(0, 0, 0, -0.50)
case d2enum.DrawEffectPctTransparency75:
opts.ColorM.Translate(0, 0, 0, -0.75)
case d2enum.DrawEffectModulate:
opts.CompositeMode = ebiten.CompositeModeLighter
// TODO: idk what to do when ebiten doesn't exactly match, pick closest?
case d2enum.DrawEffectBurn:
case d2enum.DrawEffectNormal:
case d2enum.DrawEffectMod2XTrans:
case d2enum.DrawEffectMod2X:
case d2enum.DrawEffectNone:
opts.CompositeMode = ebiten.CompositeModeSourceOver
}
var img = sfc.(*ebitenSurface).image
return s.image.DrawImage(img, opts)
@ -106,7 +125,7 @@ func (s *ebitenSurface) Render(sfc d2interface.Surface) error {
// Renders the section of the animation frame enclosed by bounds
func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Rectangle) error {
opts := &ebiten.DrawImageOptions{CompositeMode: s.stateCurrent.mode}
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y))
opts.Filter = s.stateCurrent.filter
@ -118,6 +137,25 @@ func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Recta
opts.ColorM.ChangeHSV(0, 1, s.stateCurrent.brightness)
}
// Are these correct? who even knows
switch s.stateCurrent.effect {
case d2enum.DrawEffectPctTransparency25:
opts.ColorM.Translate(0, 0, 0, -0.25)
case d2enum.DrawEffectPctTransparency50:
opts.ColorM.Translate(0, 0, 0, -0.50)
case d2enum.DrawEffectPctTransparency75:
opts.ColorM.Translate(0, 0, 0, -0.75)
case d2enum.DrawEffectModulate:
opts.CompositeMode = ebiten.CompositeModeLighter
// TODO: idk what to do when ebiten doesn't exactly match, pick closest?
case d2enum.DrawEffectBurn:
case d2enum.DrawEffectNormal:
case d2enum.DrawEffectMod2XTrans:
case d2enum.DrawEffectMod2X:
case d2enum.DrawEffectNone:
opts.CompositeMode = ebiten.CompositeModeSourceOver
}
var img = sfc.(*ebitenSurface).image
return s.image.DrawImage(img.SubImage(bound).(*ebiten.Image), opts)

View File

@ -3,14 +3,15 @@ package ebiten
import (
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/hajimehoshi/ebiten"
)
type surfaceState struct {
x int
y int
mode ebiten.CompositeMode
filter ebiten.Filter
color color.Color
x int
y int
filter ebiten.Filter
color color.Color
brightness float64
effect d2enum.DrawEffect
}

View File

@ -137,7 +137,7 @@ func CreateButton(renderer d2interface.Renderer, buttonType ButtonType, text str
xOffset := result.width / 2
buttonSprite.SetPosition(0, 0)
buttonSprite.SetBlend(true)
buttonSprite.SetEffect(d2enum.DrawEffectModulate)
buttonSprite.RenderSegmented(result.normalSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame)
lbl.SetPosition(xOffset, textY)
@ -190,10 +190,9 @@ func (v *Button) Activate() {
// Render renders the button
func (v *Button) Render(target d2interface.Surface) {
target.PushCompositeMode(d2enum.CompositeModeSourceAtop)
target.PushFilter(d2enum.FilterNearest)
target.PushTranslation(v.x, v.y)
defer target.PopN(3)
defer target.PopN(2)
if !v.enabled {
target.PushColor(color.RGBA{R: 128, G: 128, B: 128, A: 195})

View File

@ -43,10 +43,9 @@ func CreateCheckbox(renderer d2interface.Renderer, checkState bool) Checkbox {
}
func (v *Checkbox) Render(target d2interface.Surface) {
target.PushCompositeMode(d2enum.CompositeModeSourceAtop)
target.PushTranslation(v.x, v.y)
target.PushFilter(d2enum.FilterNearest)
defer target.PopN(3)
defer target.PopN(2)
if v.checkState {
_ = target.Render(v.checkedImage)

View File

@ -5,6 +5,7 @@ import (
"image"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
@ -179,11 +180,10 @@ func (s *Sprite) SetColorMod(color color.Color) {
s.animation.SetColorMod(color)
}
// SetBlend sets the animation alpha blending status
func (s *Sprite) SetBlend(blend bool) {
s.animation.SetBlend(blend)
}
func (s *Sprite) Advance(elapsed float64) error {
return s.animation.Advance(elapsed)
}
func (s *Sprite) SetEffect(e d2enum.DrawEffect) {
s.animation.SetEffect(e)
}

View File

@ -182,14 +182,14 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) {
func (v *MainMenu) createLogos(loading d2screen.LoadingState) {
animation, _ := d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits)
v.diabloLogoLeft, _ = d2ui.LoadSprite(animation)
v.diabloLogoLeft.SetBlend(true)
v.diabloLogoLeft.SetEffect(d2enum.DrawEffectModulate)
v.diabloLogoLeft.PlayForward()
v.diabloLogoLeft.SetPosition(400, 120)
loading.Progress(0.6)
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireRight, d2resource.PaletteUnits)
v.diabloLogoRight, _ = d2ui.LoadSprite(animation)
v.diabloLogoRight.SetBlend(true)
v.diabloLogoRight.SetEffect(d2enum.DrawEffectModulate)
v.diabloLogoRight.PlayForward()
v.diabloLogoRight.SetPosition(400, 120)

View File

@ -602,7 +602,8 @@ func advanceSprite(sprite *d2ui.Sprite, elapsed float64) {
}
}
func loadSprite(animationPath string, position image.Point, playLength int, playLoop, blend bool) *d2ui.Sprite {
func loadSprite(animationPath string, position image.Point, playLength int, playLoop,
blend bool) *d2ui.Sprite {
if animationPath == "" {
return nil
}
@ -615,7 +616,10 @@ func loadSprite(animationPath string, position image.Point, playLength int, play
animation.PlayForward()
animation.SetPlayLoop(playLoop)
animation.SetBlend(blend)
if blend {
animation.SetEffect(d2enum.DrawEffectModulate)
}
if playLength != 0 {
animation.SetPlayLengthMs(playLength)

View File

@ -412,7 +412,7 @@ func (g *GameControls) Render(target d2interface.Surface) {
// Stamina status bar
target.PushTranslation(273, 572)
target.PushCompositeMode(d2enum.CompositeModeLighter)
target.PushEffect(d2enum.DrawEffectModulate)
staminaPercent := float64(g.hero.Stats.Stamina) / float64(g.hero.Stats.MaxStamina)
target.DrawRect(int(staminaPercent*staminaBarWidth), 19, color.RGBA{R: 175, G: 136, B: 72, A: 200})
target.PopN(2)