VideoTools/vendor/github.com/srwiley/oksvg/utils.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

173 lines
4.2 KiB
Go

// Copyright 2017 The oksvg Authors. All rights reserved.
// created: 2/12/2017 by S.R.Wiley
//
// utils.go implements translation of an SVG2.0 path into a rasterx Path.
package oksvg
import (
"errors"
"image/color"
"strconv"
"strings"
"github.com/srwiley/rasterx"
"golang.org/x/image/colornames"
)
// unitSuffixes are suffixes sometimes applied to the width and height attributes
// of the svg element.
var unitSuffixes = []string{"cm", "mm", "px", "pt"}
func parseColorValue(v string) (uint8, error) {
if v[len(v)-1] == '%' {
n, err := strconv.Atoi(strings.TrimSpace(v[:len(v)-1]))
if err != nil {
return 0, err
}
return uint8(n * 0xFF / 100), nil
}
n, err := strconv.Atoi(strings.TrimSpace(v))
if n > 255 {
n = 255
}
return uint8(n), err
}
// trimSuffixes removes unitSuffixes from any number that is not just numeric
func trimSuffixes(a string) (b string) {
if a == "" || (a[len(a)-1] >= '0' && a[len(a)-1] <= '9') {
return a
}
b = a
for _, v := range unitSuffixes {
b = strings.TrimSuffix(b, v)
}
return
}
// parseFloat is a helper function that strips suffixes before passing to strconv.ParseFloat
func parseFloat(s string, bitSize int) (float64, error) {
val := trimSuffixes(s)
return strconv.ParseFloat(val, bitSize)
}
// splitOnCommaOrSpace returns a list of strings after splitting the input on comma and space delimiters
func splitOnCommaOrSpace(s string) []string {
return strings.FieldsFunc(s,
func(r rune) bool {
return r == ',' || r == ' '
})
}
func parseClasses(data string) (map[string]styleAttribute, error) {
res := map[string]styleAttribute{}
arr := strings.Split(data, "}")
for _, v := range arr {
v = strings.TrimSpace(v)
if v == "" {
continue
}
valueIndex := strings.Index(v, "{")
if valueIndex == -1 || valueIndex == len(v)-1 {
return res, errors.New(v + "}: invalid map format in class definitions")
}
classesStr := v[:valueIndex]
attrStr := v[valueIndex+1:]
attrMap, err := parseAttrs(attrStr)
if err != nil {
return res, err
}
classes := strings.Split(classesStr, ",")
for _, class := range classes {
class = strings.TrimSpace(class)
if len(class) > 0 && class[0] == '.' {
class = class[1:]
}
for attrKey, attrVal := range attrMap {
if res[class] == nil {
res[class] = make(styleAttribute, len(attrMap))
}
res[class][attrKey] = attrVal
}
}
}
return res, nil
}
func parseAttrs(attrStr string) (styleAttribute, error) {
arr := strings.Split(attrStr, ";")
res := make(styleAttribute, len(arr))
for _, kv := range arr {
kv = strings.TrimSpace(kv)
if kv == "" {
continue
}
tmp := strings.SplitN(kv, ":", 2)
if len(tmp) != 2 {
return res, errors.New(kv + ": invalid attribute format")
}
k := strings.TrimSpace(tmp[0])
v := strings.TrimSpace(tmp[1])
res[k] = v
}
return res, nil
}
func readFraction(v string) (f float64, err error) {
v = strings.TrimSpace(v)
d := 1.0
if strings.HasSuffix(v, "%") {
d = 100
v = strings.TrimSuffix(v, "%")
}
f, err = parseFloat(v, 64)
f /= d
// Is this is an unnecessary restriction? For now fractions can be all values not just in the range [0,1]
// if f > 1 {
// f = 1
// } else if f < 0 {
// f = 0
// }
return
}
// getColor is a helper function to get the background color
// if ReadGradUrl needs it.
func getColor(clr interface{}) color.Color {
switch c := clr.(type) {
case rasterx.Gradient: // This is a bit lazy but oh well
for _, s := range c.Stops {
if s.StopColor != nil {
return s.StopColor
}
}
case color.NRGBA:
return c
}
return colornames.Black
}
func localizeGradIfStopClrNil(g *rasterx.Gradient, defaultColor interface{}) (grad rasterx.Gradient) {
grad = *g
for _, s := range grad.Stops {
if s.StopColor == nil { // This means we need copy the gradient's Stop slice
// and fill in the default color
// Copy the stops
stops := make([]rasterx.GradStop, len(grad.Stops))
copy(stops, grad.Stops)
grad.Stops = stops
// Use the background color when a stop color is nil
clr := getColor(defaultColor)
for i, s := range stops {
if s.StopColor == nil {
grad.Stops[i].StopColor = clr
}
}
break // Only need to do this once
}
}
return
}