1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-20 14:15:23 +00:00

Screenshot and GIF recording capability (#310)

* Configuration cleanup

* Cleanup

* Gif animation and screenshot support
This commit is contained in:
Alex Yatskov 2020-02-22 20:59:45 -08:00 committed by GitHub
parent e4c84c4fb9
commit 423cef304d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 137 additions and 4 deletions

View File

@ -2,6 +2,7 @@ package ebiten
import (
"fmt"
"image"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
@ -106,3 +107,17 @@ func (s *ebitenSurface) GetDepth() int {
func (s *ebitenSurface) ReplacePixels(pixels []byte) error {
return s.image.ReplacePixels(pixels)
}
func (s *ebitenSurface) Screenshot() *image.RGBA {
width, height := s.GetSize()
bounds := image.Rectangle{image.Point{0, 0}, image.Point{width, height}}
image := image.NewRGBA(bounds)
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
image.Set(x, y, s.image.At(x, y))
}
}
return image
}

View File

@ -1,6 +1,7 @@
package d2render
import (
"image"
"image/color"
)
@ -19,4 +20,5 @@ type Surface interface {
PushTranslation(x, y int)
Render(surface Surface) error
ReplacePixels(pixels []byte) error
Screenshot() *image.RGBA
}

124
main.go
View File

@ -1,12 +1,17 @@
package main
import (
"bytes"
"errors"
"fmt"
"image"
"image/gif"
"image/png"
"log"
"os"
"runtime"
"strconv"
"sync"
"gopkg.in/alecthomas/kingpin.v2"
@ -36,10 +41,22 @@ var GitBranch string
// GitCommit is set by the CI build process to the commit hash
var GitCommit string
type captureState int
const (
captureStateNone captureState = iota
captureStateFrame
captureStateGif
)
var singleton struct {
lastTime float64
showFPS bool
timeScale float64
captureState captureState
capturePath string
captureFrames []*image.RGBA
}
func main() {
@ -103,6 +120,19 @@ func initialize() error {
d2render.SetFullScreen(fullscreen)
d2term.OutputInfo("fullscreen is now: %v", fullscreen)
})
d2term.BindAction("capframe", "captures a still frame", func(path string) {
singleton.captureState = captureStateFrame
singleton.capturePath = path
singleton.captureFrames = nil
})
d2term.BindAction("capgifstart", "captures an animation (start)", func(path string) {
singleton.captureState = captureStateGif
singleton.capturePath = path
singleton.captureFrames = nil
})
d2term.BindAction("capgifstop", "captures an animation (stop)", func() {
singleton.captureState = captureStateNone
})
d2term.BindAction("vsync", "toggles vsync", func() {
vsync := !d2render.GetVSyncEnabled()
d2render.SetVSyncEnabled(vsync)
@ -212,14 +242,100 @@ func render(target d2render.Surface) error {
return err
}
if err := d2term.Render(target); err != nil {
return err
}
if err := renderDebug(target); err != nil {
return err
}
if err := renderCapture(target); err != nil {
return err
}
if err := d2term.Render(target); err != nil {
return err
}
return nil
}
func renderCapture(target d2render.Surface) error {
cleanupCapture := func() {
singleton.captureState = captureStateNone
singleton.capturePath = ""
singleton.captureFrames = nil
}
switch singleton.captureState {
case captureStateFrame:
defer cleanupCapture()
fp, err := os.Create(singleton.capturePath)
if err != nil {
return err
}
defer fp.Close()
screenshot := target.Screenshot()
if err := png.Encode(fp, screenshot); err != nil {
return err
}
log.Printf("saved frame to %s", singleton.capturePath)
break
case captureStateGif:
screenshot := target.Screenshot()
singleton.captureFrames = append(singleton.captureFrames, screenshot)
break
case captureStateNone:
if len(singleton.captureFrames) > 0 {
defer cleanupCapture()
fp, err := os.Create(singleton.capturePath)
if err != nil {
return err
}
defer fp.Close()
var (
framesTotal = len(singleton.captureFrames)
framesPal = make([]*image.Paletted, framesTotal)
frameDelays = make([]int, framesTotal)
framesPerCpu = framesTotal / runtime.NumCPU()
)
var waitGroup sync.WaitGroup
for i := 0; i < framesTotal; i += framesPerCpu {
waitGroup.Add(1)
go func(start, end int) {
defer waitGroup.Done()
for j := start; j < end; j++ {
var buffer bytes.Buffer
if err := gif.Encode(&buffer, singleton.captureFrames[j], nil); err != nil {
panic(err)
}
framePal, err := gif.Decode(&buffer)
if err != nil {
panic(err)
}
framesPal[j] = framePal.(*image.Paletted)
frameDelays[j] = 5
}
}(i, d2common.MinInt(i+framesPerCpu, framesTotal))
}
waitGroup.Wait()
if err := gif.EncodeAll(fp, &gif.GIF{Image: framesPal, Delay: frameDelays}); err != nil {
return err
}
log.Printf("saved animation to %s", singleton.capturePath)
}
}
return nil
}