VideoTools/vendor/github.com/srwiley/rasterx/scan.go
Stu Leak 68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00

182 lines
4.9 KiB
Go

// Package rasterx implements a rasterizer in go.
// By default rasterx uses ScannerGV to render images
// which uses the rasterizer in the golang.org/x/image/vector package.
// The freetype rasterizer under the GNU license can also be used, by
// downloading the scanFT package.
//
// Copyright 2018 All rights reserved.
// Created: 5/12/2018 by S.R.Wiley
package rasterx
import (
"image"
"math"
"image/color"
"image/draw"
"golang.org/x/image/math/fixed"
"golang.org/x/image/vector"
)
// At returns the color at the point x,y
func (c *ColorFuncImage) At(x, y int) color.Color {
return c.colorFunc(x, y)
}
type (
// ColorFuncImage implements and image
// using the provided colorFunc
ColorFuncImage struct {
image.Uniform
colorFunc ColorFunc
}
// ScannerGV uses the google vector rasterizer
ScannerGV struct {
r vector.Rasterizer
//a, first fixed.Point26_6
Dest draw.Image
Targ image.Rectangle
clipImage *ClipImage
Source image.Image
Offset image.Point
minX, minY, maxX, maxY fixed.Int26_6 // keep track of bounds
}
)
// ClipImage is a clipable ColorFuncImage
type ClipImage struct {
ColorFuncImage
clip image.Rectangle
}
var noApha = color.RGBA{0, 0, 0, 0}
// GetPathExtent returns the extent of the path
func (s *ScannerGV) GetPathExtent() fixed.Rectangle26_6 {
return fixed.Rectangle26_6{Min: fixed.Point26_6{X: s.minX, Y: s.minY}, Max: fixed.Point26_6{X: s.maxX, Y: s.maxY}}
}
// At returns the color of the ClipImage at the point x,y
func (c *ClipImage) At(x, y int) color.Color {
p := image.Point{x, y}
if p.In(c.clip) {
return c.ColorFuncImage.At(x, y)
}
return noApha
}
// SetWinding set the winding rule for the scanner
func (s *ScannerGV) SetWinding(useNonZeroWinding bool) {
// no-op as scanner gv does not support even-odd winding
}
// SetColor set the color type for the scanner
func (s *ScannerGV) SetColor(clr interface{}) {
switch c := clr.(type) {
case color.Color:
s.clipImage.ColorFuncImage.Uniform.C = c
if s.clipImage.clip == image.ZR {
s.Source = &s.clipImage.ColorFuncImage.Uniform
} else {
s.clipImage.ColorFuncImage.colorFunc = func(x, y int) color.Color {
return c
}
s.Source = s.clipImage
}
case ColorFunc:
s.clipImage.ColorFuncImage.colorFunc = c
if s.clipImage.clip == image.ZR {
s.Source = &s.clipImage.ColorFuncImage
} else {
s.Source = s.clipImage
}
}
}
// SetClip sets an optional clipping rectangle to restrict rendering only to
// that region -- if size is 0 then ignored (set to image.ZR to clear)
func (s *ScannerGV) SetClip(rect image.Rectangle) {
s.clipImage.clip = rect
if s.Source == &s.clipImage.ColorFuncImage.Uniform {
s.SetColor(s.clipImage.ColorFuncImage.Uniform.C)
} else {
s.SetColor(s.clipImage.ColorFuncImage.colorFunc)
}
}
func (s *ScannerGV) set(a fixed.Point26_6) {
if s.maxX < a.X {
s.maxX = a.X
}
if s.maxY < a.Y {
s.maxY = a.Y
}
if s.minX > a.X {
s.minX = a.X
}
if s.minY > a.Y {
s.minY = a.Y
}
}
// Start starts a new path at the given point.
func (s *ScannerGV) Start(a fixed.Point26_6) {
s.set(a)
s.r.MoveTo(float32(a.X)/64, float32(a.Y)/64)
}
// Line adds a linear segment to the current curve.
func (s *ScannerGV) Line(b fixed.Point26_6) {
s.set(b)
s.r.LineTo(float32(b.X)/64, float32(b.Y)/64)
}
// Draw renders the accumulate scan to the desination
func (s *ScannerGV) Draw() {
// This draws the entire bounds of the image, because
// at this point the alpha mask does not shift with the
// placement of the target rectangle in the vector rasterizer
s.r.Draw(s.Dest, s.Dest.Bounds(), s.Source, s.Offset)
// Remove the line above and uncomment the lines below if you
// are using a version of the vector rasterizer that shifts the alpha
// mask with the placement of the target
// s.Targ.Min.X = int(s.minX >> 6)
// s.Targ.Min.Y = int(s.minY >> 6)
// s.Targ.Max.X = int(s.maxX>>6) + 1
// s.Targ.Max.Y = int(s.maxY>>6) + 1
// s.Targ = s.Targ.Intersect(s.Dest.Bounds()) // This check should be done by the rasterizer?
// s.r.Draw(s.Dest, s.Targ, s.Source, s.Offset)
}
// Clear cancels any previous accumulated scans
func (s *ScannerGV) Clear() {
p := s.r.Size()
s.r.Reset(p.X, p.Y)
const mxfi = fixed.Int26_6(math.MaxInt32)
s.minX, s.minY, s.maxX, s.maxY = mxfi, mxfi, -mxfi, -mxfi
}
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
func (s *ScannerGV) SetBounds(width, height int) {
s.r.Reset(width, height)
}
// NewScannerGV creates a new Scanner with the given bounds.
func NewScannerGV(width, height int, dest draw.Image,
targ image.Rectangle) *ScannerGV {
s := new(ScannerGV)
s.SetBounds(width, height)
s.Dest = dest
s.Targ = targ
s.clipImage = &ClipImage{}
s.clipImage.ColorFuncImage.Uniform.C = &color.RGBA{255, 0, 0, 255}
s.Source = &s.clipImage.ColorFuncImage.Uniform
s.Offset = image.Point{0, 0}
return s
}