mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
curated package predated the standard errors package introduced in go1.13 the standard package does a better job of what curated attempted to do the change of package also gave me a opportunity to clean up the error messages a little bit
135 lines
4 KiB
Go
135 lines
4 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package digest
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"fmt"
|
|
|
|
"github.com/jetsetilly/gopher2600/hardware/television"
|
|
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
|
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
|
)
|
|
|
|
// Video is an implementation of the television.PixelRenderer interface with an
|
|
// embedded television for convenience. It generates a SHA-1 value of the image
|
|
// every frame. it does not display the image anywhere.
|
|
//
|
|
// Note that the use of SHA-1 is fine for this application because this is not
|
|
// a cryptographic task.
|
|
type Video struct {
|
|
*television.Television
|
|
spec specification.Spec
|
|
digest [sha1.Size]byte
|
|
pixels []byte
|
|
frameNum int
|
|
}
|
|
|
|
const pixelDepth = 3
|
|
|
|
// NewVideo initialises a new instance of DigestTV. For convenience, the
|
|
// television argument can be nil, in which case an instance of
|
|
// StellaTelevision will be created.
|
|
func NewVideo(tv *television.Television) (*Video, error) {
|
|
// set up digest tv
|
|
dig := &Video{
|
|
Television: tv,
|
|
spec: specification.SpecNTSC,
|
|
}
|
|
|
|
// register ourselves as a television.Renderer
|
|
dig.AddPixelRenderer(dig)
|
|
|
|
// length of pixels array contains enough room for the previous frames digest value
|
|
l := len(dig.digest)
|
|
l += specification.AbsoluteMaxClks * pixelDepth
|
|
|
|
// allocate enough pixels for entire frame
|
|
dig.pixels = make([]byte, l)
|
|
|
|
return dig, nil
|
|
}
|
|
|
|
// Hash implements digest.Digest interface.
|
|
func (dig Video) Hash() string {
|
|
return fmt.Sprintf("%x", dig.digest)
|
|
}
|
|
|
|
// ResetDigest implements digest.Digest interface.
|
|
func (dig *Video) ResetDigest() {
|
|
for i := range dig.digest {
|
|
dig.digest[i] = 0
|
|
}
|
|
}
|
|
|
|
// Resize implements television.PixelRenderer interface
|
|
//
|
|
// In this implementation we only handle specification changes. This means the
|
|
// digest is immune from changes to the frame resizing method used by the
|
|
// television implementation. Changes to how the specification is flipped might
|
|
// cause comparison failures however.
|
|
func (dig *Video) Resize(frameInfo television.FrameInfo) error {
|
|
dig.spec = frameInfo.Spec
|
|
return nil
|
|
}
|
|
|
|
// NewFrame implements television.PixelRenderer interface.
|
|
func (dig *Video) NewFrame(_ television.FrameInfo) error {
|
|
// chain fingerprints by copying the value of the last fingerprint
|
|
// to the head of the video data
|
|
n := copy(dig.pixels, dig.digest[:])
|
|
if n != len(dig.digest) {
|
|
return fmt.Errorf("digest: video: digest error during new frame")
|
|
}
|
|
dig.digest = sha1.Sum(dig.pixels)
|
|
dig.frameNum++
|
|
return nil
|
|
}
|
|
|
|
// NewScanline implements television.PixelRenderer interface.
|
|
func (dig *Video) NewScanline(scanline int) error {
|
|
return nil
|
|
}
|
|
|
|
// SetPixels implements television.PixelRenderer interface.
|
|
func (dig *Video) SetPixels(sig []signal.SignalAttributes, _ int) error {
|
|
// offset always starts at after the digest leader
|
|
offset := len(dig.digest)
|
|
|
|
for i := range sig {
|
|
// ignore VBLANK and extract the color signal in all situations
|
|
px := signal.ColorSignal((sig[i] & signal.Color) >> signal.ColorShift)
|
|
col := dig.spec.GetColor(px)
|
|
|
|
// setting every pixel regardless of vblank value
|
|
s := dig.pixels[offset : offset+3 : offset+3]
|
|
s[0] = col.R
|
|
s[1] = col.G
|
|
s[2] = col.B
|
|
|
|
offset += pixelDepth
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reset implements television.PixelRenderer interface.
|
|
func (dig *Video) Reset() {
|
|
}
|
|
|
|
// EndRendering implements television.PixelRenderer interface.
|
|
func (dig *Video) EndRendering() error {
|
|
return nil
|
|
}
|