Gopher2600/digest/video.go
JetSetIlly 3aa5885ebe removed curated pacakge. replaced with wrapped errors
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
2023-02-13 21:58:39 +00:00

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
}