izapple2/screen.go

152 lines
3.8 KiB
Go
Raw Permalink Normal View History

2019-04-21 19:04:02 +00:00
package apple2
import (
"fmt"
"image"
"image/color"
"image/png"
"os"
)
/*
References:
- "Understanding the Apple II", http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf
- "Apple II Reference Manual"
- "More Colors for your Apple", https://archive.org/details/byte-magazine-1979-06/page/n61
*/
2019-04-21 22:18:14 +00:00
// Snapshot the currently visible screen
func Snapshot(a *Apple2) *image.RGBA {
2019-05-05 11:25:45 +00:00
isColor := a.isColor
2019-04-26 16:08:30 +00:00
isTextMode := a.io.isSoftSwitchActive(ioFlagText)
isHiResMode := a.io.isSoftSwitchActive(ioFlagHiRes)
2019-05-03 19:45:29 +00:00
isMixMode := a.io.isSoftSwitchActive(ioFlagMixed)
2019-11-08 22:56:54 +00:00
is80Columns := a.io.isSoftSwitchActive(ioFlag80Col)
2019-11-09 16:44:04 +00:00
isDoubleResMode := !isTextMode && is80Columns && !a.io.isSoftSwitchActive(ioFlagAnnunciator3)
2019-11-08 22:56:54 +00:00
isSecondPage := a.io.isSoftSwitchActive(ioFlagSecondPage) && !a.mmu.store80Active
2019-11-11 21:58:42 +00:00
isSuperHighResMode := a.io.isSoftSwitchActive(ioDataNewVideo)
2019-05-05 11:25:45 +00:00
var lightColor color.Color
if isColor {
lightColor = color.White
} else {
// Color for typical Apple ][ period green P1 phosphor monitors
2019-05-03 19:45:29 +00:00
// See: https://superuser.com/questions/361297/what-colour-is-the-dark-green-on-old-fashioned-green-screen-computer-displays
2019-05-05 11:25:45 +00:00
lightColor = color.RGBA{65, 255, 0, 255}
}
2019-05-03 19:45:29 +00:00
2019-05-05 11:25:45 +00:00
var snap *image.RGBA
2019-11-11 21:58:42 +00:00
if isSuperHighResMode { // Has to be first and disables the rest
snap = snapshotSuperHiResMode(a)
} else if isTextMode {
2019-11-08 22:56:54 +00:00
snap = snapshotTextMode(a, is80Columns, isSecondPage, false /*isMixMode*/, lightColor)
2019-04-26 16:08:30 +00:00
} else {
if isHiResMode {
2019-11-09 16:44:04 +00:00
if isDoubleResMode {
snap = snapshotDoubleHiResModeMono(a, isSecondPage, isMixMode, lightColor)
} else {
snap = snapshotHiResModeMono(a, isSecondPage, isMixMode, lightColor)
}
2019-04-26 16:08:30 +00:00
} else {
2019-11-08 23:44:13 +00:00
snap = snapshotLoResModeMono(a, isDoubleResMode, isSecondPage, isMixMode, lightColor)
2019-05-03 19:45:29 +00:00
}
2019-05-03 19:45:29 +00:00
if isMixMode {
2019-11-08 22:56:54 +00:00
snapText := snapshotTextMode(a, is80Columns, false /*isSecondPage*/, true /*isMixMode*/, lightColor)
2019-05-03 19:45:29 +00:00
snap = mixSnapshots(snap, snapText)
}
2019-05-05 11:25:45 +00:00
if isColor {
snap = filterNTSCColor(false /*blacker*/, snap)
}
2019-05-03 19:45:29 +00:00
}
2019-05-05 11:25:45 +00:00
2019-11-11 21:58:42 +00:00
if !isSuperHighResMode {
snap = linesSeparatedFilter(snap)
}
2019-05-03 19:45:29 +00:00
return snap
}
func mixSnapshots(top, bottom *image.RGBA) *image.RGBA {
topBounds := top.Bounds()
topWidth := topBounds.Dx()
topHeight := topBounds.Dy()
bottomBounds := bottom.Bounds()
bottomWidth := bottomBounds.Dx()
bottomHeight := bottomBounds.Dy()
factor := topWidth / bottomWidth
size := image.Rect(0, 0, topWidth, topHeight+bottomHeight)
out := image.NewRGBA(size)
// Copy top
for y := topBounds.Min.Y; y < topBounds.Max.Y; y++ {
for x := topBounds.Min.X; x < topBounds.Max.X; x++ {
c := top.At(x, y)
out.Set(x, y, c)
2019-04-26 16:08:30 +00:00
}
2019-04-21 19:04:02 +00:00
}
2019-04-26 16:08:30 +00:00
2019-05-03 19:45:29 +00:00
// Copy bottom, applyng the factor
for y := bottomBounds.Min.Y; y < bottomBounds.Max.Y; y++ {
for x := bottomBounds.Min.X; x < bottomBounds.Max.X; x++ {
c := bottom.At(x, y)
for f := 0; f < factor; f++ {
out.Set(x*factor+f, topHeight+y, c)
}
}
}
return out
2019-04-21 19:04:02 +00:00
}
2019-06-02 20:59:51 +00:00
// SaveSnapshot saves a snapshot of the screen to a png file
2019-10-05 23:26:00 +00:00
func SaveSnapshot(a *Apple2, filename string) error {
2019-04-21 22:18:14 +00:00
img := Snapshot(a)
2019-06-02 20:59:51 +00:00
img = squarishPixelsFilter(img)
2019-04-21 19:04:02 +00:00
2019-06-02 20:59:51 +00:00
f, err := os.Create(filename)
2019-04-21 19:04:02 +00:00
if err != nil {
2019-10-05 23:26:00 +00:00
return err
2019-04-21 19:04:02 +00:00
}
defer f.Close()
png.Encode(f, img)
2019-10-05 23:26:00 +00:00
return nil
2019-04-21 19:04:02 +00:00
}
2019-06-02 20:59:51 +00:00
func squarishPixelsFilter(in *image.RGBA) *image.RGBA {
b := in.Bounds()
factor := 1200 / b.Dx()
fmt.Println(factor)
size := image.Rect(0, 0, factor*b.Dx(), b.Dy())
out := image.NewRGBA(size)
for x := b.Min.X; x < b.Max.X; x++ {
for y := b.Min.Y; y < b.Max.Y; y++ {
c := in.At(x, y)
for i := 0; i < factor; i++ {
out.Set(factor*x+i, y, c)
}
}
}
return out
}
func linesSeparatedFilter(in *image.RGBA) *image.RGBA {
b := in.Bounds()
size := image.Rect(0, 0, b.Dx(), 4*b.Dy())
out := image.NewRGBA(size)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
c := in.At(x, y)
out.Set(x, 4*y, c)
out.Set(x, 4*y+1, c)
out.Set(x, 4*y+2, c)
out.Set(x, 4*y+3, color.Black)
}
}
return out
}