135 lines
3.4 KiB
Go
135 lines
3.4 KiB
Go
package glossary
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/faiface/pixel"
|
|
"github.com/faiface/pixel/imdraw"
|
|
"github.com/faiface/pixel/text"
|
|
"golang.org/x/image/colornames"
|
|
)
|
|
|
|
// FPSWatch measures the real-time frame rates and displays it on a target canvas.
|
|
type FPSWatch struct {
|
|
txt *text.Text // shared variable
|
|
atlas *text.Atlas // borrowed atlas for txt
|
|
imd *imdraw.IMDraw // shared variable
|
|
mutex sync.Mutex // synchronize
|
|
//
|
|
fps int // The FPS evaluated every second.
|
|
frames int // Frames count before the FPS update.
|
|
seccer <-chan time.Time // Ticks time every second.
|
|
//
|
|
desc string
|
|
pos pixel.Vec
|
|
anchorX AnchorX
|
|
anchorY AnchorY
|
|
colorBg color.Color
|
|
colorTxt color.Color
|
|
}
|
|
|
|
// NewFPSWatch is a constructor.
|
|
func NewFPSWatch(
|
|
additionalCaption string, _pos pixel.Vec,
|
|
_anchorY AnchorY, _anchorX AnchorX, // This is because the order is usually Y then X in spoken language.
|
|
_colorBg, _colorTxt color.Color,
|
|
) (watch *FPSWatch) {
|
|
return &FPSWatch{
|
|
atlas: AtlasASCII(),
|
|
fps: 0,
|
|
frames: 0,
|
|
seccer: nil,
|
|
desc: additionalCaption,
|
|
pos: _pos,
|
|
anchorX: _anchorX,
|
|
anchorY: _anchorY,
|
|
colorBg: _colorBg,
|
|
colorTxt: _colorTxt,
|
|
}
|
|
}
|
|
|
|
// NewFPSWatchSimple is a constructor.
|
|
func NewFPSWatchSimple(_pos pixel.Vec, _anchorY AnchorY, _anchorX AnchorX) *FPSWatch {
|
|
return NewFPSWatch("", _pos, _anchorY, _anchorX, colornames.Black, colornames.White)
|
|
}
|
|
|
|
// Start ticking every second.
|
|
func (watch *FPSWatch) Start() {
|
|
watch.seccer = time.Tick(time.Second)
|
|
}
|
|
|
|
// Poll () should be called only once and in every single frame. (Obligatory)
|
|
// This is an extended behavior of Update() like funcs.
|
|
func (watch *FPSWatch) Poll() {
|
|
watch.frames++
|
|
select {
|
|
case <-watch.seccer:
|
|
watch.fps = watch.frames
|
|
watch.frames = 0
|
|
go watch._Update()
|
|
default:
|
|
}
|
|
}
|
|
|
|
// SetPos to a position in screen coords.
|
|
func (watch *FPSWatch) SetPos(pos pixel.Vec, anchorY AnchorY, anchorX AnchorX) {
|
|
watch.pos = pos
|
|
watch.anchorX = anchorX
|
|
watch.anchorY = anchorY
|
|
}
|
|
|
|
// GetFPS returns the most recent FPS recorded.
|
|
// A non-ptr FPSWatch as a read only argument passes lock by value within itself but that seems totally fine.
|
|
func (watch FPSWatch) GetFPS() int {
|
|
return watch.fps
|
|
}
|
|
|
|
// Draw FPSWatch.
|
|
func (watch *FPSWatch) Draw(t pixel.Target) {
|
|
// lock before accessing txt & imdraw
|
|
watch.mutex.Lock()
|
|
defer watch.mutex.Unlock()
|
|
|
|
if watch.imd == nil && watch.txt == nil { // isInvisible set to true.
|
|
return // An empty image is drawn.
|
|
}
|
|
|
|
watch.imd.Draw(t)
|
|
watch.txt.Draw(t, pixel.IM)
|
|
}
|
|
|
|
// unexported
|
|
func (watch *FPSWatch) _Update() {
|
|
// lock before txt & imdraw update
|
|
watch.mutex.Lock()
|
|
defer watch.mutex.Unlock()
|
|
|
|
// text label (a state machine)
|
|
if watch.txt == nil { // lazy creation
|
|
watch.txt = text.New(pixel.ZV, watch.atlas)
|
|
}
|
|
txt := watch.txt
|
|
txt.Clear()
|
|
|
|
str := fmt.Sprint("FPS: ", watch.fps, " ", watch.desc)
|
|
AnchorTxt(txt, watch.pos, watch.anchorX, watch.anchorY, str)
|
|
txt.Color = watch.colorTxt
|
|
txt.Dot.X -= 1.0
|
|
txt.Dot.Y += 5.0
|
|
txt.WriteString(str)
|
|
|
|
// imdraw (a state machine)
|
|
if watch.imd == nil { // lazy creation
|
|
watch.imd = imdraw.New(nil)
|
|
}
|
|
imd := watch.imd
|
|
imd.Clear()
|
|
|
|
imd.Color = watch.colorBg
|
|
imd.Push(VerticesOfRect(txt.Bounds())...)
|
|
imd.Polygon(0)
|
|
}
|