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
182 lines
4.9 KiB
Go
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
|
|
}
|