Save GIFs on headless mode

This commit is contained in:
Iván Izaguirre 2022-05-09 14:34:47 +02:00
parent dcc1597cb2
commit 695eaa603b
4 changed files with 92 additions and 13 deletions

5
.gitignore vendored
View File

@ -4,4 +4,7 @@ a2sdl.exe
frontend/a2sdl/a2sdl
frontend/a2sdl/*.woz
frontend/a2sdl/*.dsk
frontend/a2fyne/a2fyne
frontend/a2fyne/a2fyne
frontend/headless/headless
frontend/*/snapshot.gif
frontend/*/snapshot.png

View File

@ -3,8 +3,10 @@ package main
import (
"bufio"
"fmt"
"image/gif"
"os"
"strings"
"time"
"github.com/ivanizag/izapple2"
"github.com/ivanizag/izapple2/screen"
@ -88,6 +90,9 @@ func main() {
case "return":
fe.keyChannel <- 13
case "gif":
SaveGif(a, "snapshot.gif")
case "help":
fmt.Print(`
Available commands:
@ -99,6 +104,7 @@ Available commands:
Key or k: Sends a key to the emulator
Keys or ks: Sends a string to the emulator
Return or r: Sends a return to the emulator
GIF or gif: Captures a GIF animation
Help: Prints this help
`)
default:
@ -107,6 +113,40 @@ Available commands:
}
}
func SaveGif(a *izapple2.Apple2, filename string) error {
animation := gif.GIF{}
delay := 50 * time.Millisecond
delayHundredsS := 5
frames := 20 // 1 second
planned := time.Now()
for i := 0; i < frames; i++ {
lapse := planned.Sub(time.Now())
fmt.Printf("%v\n", lapse)
if lapse > 0 {
time.Sleep(lapse)
}
fmt.Printf("%v\n", time.Now())
img := screen.SnapshotPaletted(a, screen.ScreenModeNTSC)
animation.Image = append(animation.Image, img)
animation.Delay = append(animation.Delay, delayHundredsS)
planned = planned.Add(delay)
}
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
gif.EncodeAll(f, &animation)
return nil
}
/*
Uses the console to send commands and queries to an emulated machine.
*/

View File

@ -30,6 +30,21 @@ var ntscColorMap = [16]color.Color{
color.RGBA{255, 255, 255, 255}, // White
}
var attenuatedColorMap = buildAttenuatedColorMap(ntscColorMap)
func buildAttenuatedColorMap(colorMap [16]color.Color) [16]color.Color {
colors := [16]color.Color{}
for i := 0; i < len(colorMap); i++ {
r, g, b, _ := colorMap[i].RGBA()
colors[i] = color.RGBA64{
uint16(r / 2), uint16(g / 2), uint16(b / 2),
65535,
}
}
return colors
}
/*
var rgbColorMap = [16]color.Color{
color.RGBA{0, 0, 0, 255}, // Black
color.RGBA{221, 0, 51, 255}, // Magenta
@ -48,18 +63,13 @@ var rgbColorMap = [16]color.Color{
color.RGBA{68, 255, 153, 255}, // Aquamarine
color.RGBA{255, 255, 255, 255}, // White
}
*/
func filterNTSCColor(in *image.RGBA, mask *image.Alpha, screenMode int) *image.RGBA {
colorMap := ntscColorMap // or rgbColorMap
attenuatedColorMap := ntscColorMap
colorMapLow := ntscColorMap
if screenMode == ScreenModeNTSC {
for i := 0; i < len(colorMap); i++ {
r, g, b, _ := colorMap[i].RGBA()
attenuatedColorMap[i] = color.RGBA64{
uint16(r / 2), uint16(g / 2), uint16(b / 2),
65535,
}
}
colorMapLow = attenuatedColorMap
}
b := in.Bounds()
@ -90,7 +100,7 @@ func filterNTSCColor(in *image.RGBA, mask *image.Alpha, screenMode int) *image.R
if r != 0 {
cOut = colorMap[v]
} else {
cOut = attenuatedColorMap[v]
cOut = colorMapLow[v]
}
if mask != nil {
// RGB mode7

View File

@ -36,6 +36,16 @@ func Snapshot(vs VideoSource, screenMode int) *image.RGBA {
return snap
}
// SnapshotPaletted, snapshot of the currently visible screen as a paletted image
func SnapshotPaletted(vs VideoSource, screenMode int) *image.Paletted {
img := Snapshot(vs, screenMode)
return palletedFilter(img)
}
// Color for typical Apple ][ period green P1 phosphor monitors
// See: https://superuser.com/questions/361297/what-colour-is-the-dark-green-on-old-fashioned-green-screen-computer-displays
var greenPhosphorColor = color.RGBA{65, 255, 0, 255}
func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGBA {
videoBase := videoMode & VideoBaseMask
mixMode := videoMode & VideoMixTextMask
@ -44,9 +54,7 @@ func snapshotByMode(vs VideoSource, videoMode uint16, screenMode int) *image.RGB
var lightColor color.Color = color.White
if screenMode == ScreenModeGreen {
// Color for typical Apple ][ period green P1 phosphor monitors
// See: https://superuser.com/questions/361297/what-colour-is-the-dark-green-on-old-fashioned-green-screen-computer-displays
lightColor = color.RGBA{65, 255, 0, 255}
lightColor = greenPhosphorColor
}
applyNTSCFilter := screenMode != ScreenModeGreen
@ -170,3 +178,21 @@ func linesSeparatedFilter(in *image.RGBA) *image.RGBA {
}
return out
}
func palletedFilter(in *image.RGBA) *image.Paletted {
bounds := in.Bounds()
outBounds := image.Rect(0, 0, bounds.Dx()*2, bounds.Dy())
palette := []color.Color{color.Black, color.White, greenPhosphorColor}
palette = append(palette, ntscColorMap[:]...)
palette = append(palette, attenuatedColorMap[:]...)
paletted := image.NewPaletted(outBounds, palette)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
c := in.At(x, y)
paletted.Set(x*2, y, c)
paletted.Set(x*2+1, y, c)
}
}
return paletted
}