swf2ass-go/ass/Renderer.go

217 lines
6.4 KiB
Go
Raw Normal View History

2023-11-20 06:32:13 +00:00
package ass
import (
"fmt"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/ass/line"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/settings"
2023-11-20 06:32:13 +00:00
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/shapes"
2023-11-20 06:32:13 +00:00
"slices"
"strconv"
2023-11-20 06:32:13 +00:00
)
type Renderer struct {
Header []string
RunningBuffer []*line.EventLine
2023-11-20 06:32:13 +00:00
}
func NewRenderer(frameRate float64, display shapes.Rectangle[float64]) *Renderer {
width := int64(display.Width() * settings.GlobalSettings.VideoScaleMultiplier)
height := int64(display.Height() * settings.GlobalSettings.VideoScaleMultiplier)
2023-11-20 06:32:13 +00:00
ar := float64(width) / float64(height)
frameRate *= settings.GlobalSettings.VideoRateMultiplier
2023-11-20 06:32:13 +00:00
return &Renderer{
Header: []string{
"[Script Info]",
"; Script generated by swf2ass Renderer",
"; https://git.gammaspectra.live/WeebDataHoarder/swf2ass-go",
"Title: swf2ass",
"ScriptType: v4.00+",
"; TODO: maybe set WrapStyle: 2",
"WrapStyle: 0",
"ScaledBorderAndShadow: yes",
"YCbCr Matrix: PC.709",
fmt.Sprintf("PlayResX: %d", width),
fmt.Sprintf("PlayResY: %d", height),
"",
"",
2023-11-20 06:32:13 +00:00
"[Aegisub Project Garbage]",
"Last Style Storage: f",
fmt.Sprintf("Video File: ?dummy:%s:10000:%d:%d:160:160:160:c", strconv.FormatFloat(frameRate, 'f', -1, 64), width, height),
fmt.Sprintf("Video AR Value: %.6F", ar),
"Active Line: 0",
2023-11-20 06:32:13 +00:00
"Video Zoom Percent: 2.000000",
"",
"[V4+ Styles]",
"Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding",
fmt.Sprintf("Style: %s,Arial,20,&H00000000,&H00000000,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,0,0,7,0,0,0,1", line.StyleFill),
fmt.Sprintf("Style: %s,Arial,20,&H00000000,&H00000000,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,0,0,7,0,0,0,1", line.StyleLine),
2023-11-20 06:32:13 +00:00
"",
"[Events]",
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text",
},
}
}
func (r *Renderer) RenderFrame(frameInfo types.FrameInformation, frame types.RenderedFrame) (result []string) {
if len(r.Header) != 0 {
result = append(result, r.Header...)
r.Header = nil
}
slices.SortStableFunc(frame, RenderedObjectDepthSort)
2023-11-20 06:32:13 +00:00
var runningBuffer []*line.EventLine
2023-11-20 06:32:13 +00:00
scale := math.ScaleTransform(math.NewVector2(settings.GlobalSettings.VideoScaleMultiplier, settings.GlobalSettings.VideoScaleMultiplier))
2023-11-20 06:32:13 +00:00
animated := 0
for _, object := range frame {
2023-11-20 06:32:13 +00:00
obEntry := *BakeRenderedObjectGradients(object)
object = &obEntry
object.MatrixTransform = scale.Multiply(object.MatrixTransform) //TODO: order?
depth := object.GetDepth()
var tagsToTransition []*line.EventLine
2023-11-20 06:32:13 +00:00
for i := len(r.RunningBuffer) - 1; i >= 0; i-- {
tag := r.RunningBuffer[i]
if depth.Equals(tag.Layer) && object.ObjectId == tag.ObjectId {
tagsToTransition = append(tagsToTransition, tag)
r.RunningBuffer = slices.Delete(r.RunningBuffer, i, i+1)
}
}
slices.Reverse(tagsToTransition)
canTransition := true
var transitionedTags []*line.EventLine
2023-11-20 06:32:13 +00:00
for _, tag := range tagsToTransition {
tag = tag.Transition(frameInfo, object)
if tag != nil {
transitionedTags = append(transitionedTags, tag)
tag.DropCache()
} else {
canTransition = false
break
}
}
if canTransition && len(transitionedTags) > 0 {
animated += len(transitionedTags)
runningBuffer = append(runningBuffer, transitionedTags...)
} else {
r.RunningBuffer = append(r.RunningBuffer, tagsToTransition...)
for _, l := range line.EventLinesFromRenderObject(frameInfo, object, settings.GlobalSettings.BakeMatrixTransforms) {
l.DropCache()
runningBuffer = append(runningBuffer, l)
2023-11-20 06:32:13 +00:00
}
}
}
fmt.Printf("[ASS] Total %d objects, %d flush, %d buffer, %d animated tags.\n", len(frame), len(r.RunningBuffer), len(runningBuffer), animated)
2023-11-20 06:32:13 +00:00
//Flush non dupes
for _, l := range r.RunningBuffer {
l.Name += fmt.Sprintf(" f:%d>%d~%d", l.Start, l.End, l.End-l.Start+1)
l.DropCache()
result = append(result, l.Encode(frameInfo.GetFrameDuration()))
2023-11-20 06:32:13 +00:00
}
r.RunningBuffer = runningBuffer
return result
}
func (r *Renderer) Flush(frameInfo types.FrameInformation) (result []string) {
result = make([]string, 0, len(r.RunningBuffer))
for _, l := range r.RunningBuffer {
l.Name += fmt.Sprintf(" f:%d>%d~%d", l.Start, l.End, l.End-l.Start+1)
l.DropCache()
result = append(result, l.Encode(frameInfo.GetFrameDuration()))
2023-11-20 06:32:13 +00:00
}
r.RunningBuffer = r.RunningBuffer[:0]
return result
}
func BakeRenderedObjectGradients(o *types.RenderedObject) *types.RenderedObject {
var baked bool
drawPathList := make(shapes.DrawPathList, 0, len(o.DrawPathList))
2023-11-20 06:32:13 +00:00
for _, command := range o.DrawPathList {
if fillStyleRecord, ok := command.Style.(*shapes.FillStyleRecord); ok {
if gradient, ok := fillStyleRecord.Fill.(shapes.Gradient); ok {
2023-11-20 06:32:13 +00:00
baked = true
fillClip := types.NewClipPath(command.Commands)
2023-11-20 06:32:13 +00:00
//Convert gradients to many tags
for _, gradientPath := range gradient.GetInterpolatedDrawPaths(settings.GlobalSettings.GradientOverlap, settings.GlobalSettings.GradientBlur, settings.GlobalSettings.GradientSlices) {
2023-11-23 09:15:43 +00:00
gradientClip := types.NewClipPath(gradientPath.Commands)
newPath := shapes.DrawPath{
2023-11-20 06:32:13 +00:00
Style: gradientPath.Style,
Commands: fillClip.Intersect(gradientClip).GetShape(),
2023-11-20 06:32:13 +00:00
}
if len(newPath.Commands.Edges) == 0 {
continue
}
drawPathList = append(drawPathList, newPath)
}
} else {
drawPathList = append(drawPathList, command)
2023-11-20 06:32:13 +00:00
}
} else {
drawPathList = append(drawPathList, command)
2023-11-20 06:32:13 +00:00
}
}
if baked {
return &types.RenderedObject{
Depth: o.Depth,
ObjectId: o.ObjectId,
DrawPathList: drawPathList,
Clip: o.Clip,
ColorTransform: o.ColorTransform,
MatrixTransform: o.MatrixTransform,
}
} else {
return o
}
}
func RenderedObjectDepthSort(a, b *types.RenderedObject) int {
if len(b.Depth) > len(a.Depth) {
for i, depth := range b.Depth {
var otherDepth uint16
if i < len(a.Depth) {
otherDepth = a.Depth[i]
}
if depth != otherDepth {
return int(otherDepth) - int(depth)
}
}
} else {
for i, depth := range a.Depth {
var otherDepth uint16
if i < len(b.Depth) {
otherDepth = b.Depth[i]
}
if depth != otherDepth {
return int(depth) - int(otherDepth)
}
}
}
return 0
}