2023-11-20 06:32:13 +00:00
|
|
|
package ass
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-11-22 01:42:40 +00:00
|
|
|
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/ass/line"
|
|
|
|
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/settings"
|
2023-11-20 06:32:13 +00:00
|
|
|
swftypes "git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf/types"
|
|
|
|
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types"
|
2023-11-20 18:01:35 +00:00
|
|
|
"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"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Renderer struct {
|
|
|
|
Header []string
|
2023-11-22 01:42:40 +00:00
|
|
|
RunningBuffer []*line.Line
|
2023-11-20 06:32:13 +00:00
|
|
|
}
|
|
|
|
|
2023-11-20 18:01:35 +00:00
|
|
|
func NewRenderer(frameRate float64, viewPort shapes.Rectangle[swftypes.Twip]) *Renderer {
|
2023-11-20 06:32:13 +00:00
|
|
|
display := viewPort.Divide(swftypes.TwipFactor)
|
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
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)
|
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
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),
|
|
|
|
"",
|
|
|
|
"[Aegisub Project Garbage]",
|
|
|
|
"Last Style Storage: f",
|
|
|
|
fmt.Sprintf("Video File: ?dummy:%f:10000:%d:%d:160:160:160:c", frameRate, width, height),
|
|
|
|
fmt.Sprintf("Video AR Value: %.4F", ar),
|
|
|
|
"Active Line: 0",
|
|
|
|
"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",
|
|
|
|
"Style: f,Arial,20,&H00000000,&H00000000,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,0,0,7,0,0,0,1",
|
|
|
|
"",
|
|
|
|
"[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
|
|
|
|
}
|
|
|
|
|
|
|
|
objects := slices.Clone(frame)
|
|
|
|
slices.SortStableFunc(objects, RenderedObjectDepthSort)
|
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
var runningBuffer []*line.Line
|
2023-11-20 06:32:13 +00:00
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
scale := math.ScaleTransform(math.NewVector2(settings.GlobalSettings.VideoScaleMultiplier, settings.GlobalSettings.VideoScaleMultiplier).Float64())
|
2023-11-20 06:32:13 +00:00
|
|
|
|
|
|
|
animated := 0
|
|
|
|
|
|
|
|
for _, object := range objects {
|
|
|
|
obEntry := *BakeRenderedObjectGradients(object)
|
|
|
|
object = &obEntry
|
|
|
|
|
|
|
|
object.MatrixTransform = scale.Multiply(object.MatrixTransform) //TODO: order?
|
|
|
|
|
|
|
|
depth := object.GetDepth()
|
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
var tagsToTransition []*line.Line
|
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
|
2023-11-22 01:42:40 +00:00
|
|
|
var transitionedTags []*line.Line
|
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...)
|
|
|
|
|
2023-11-22 01:42:40 +00:00
|
|
|
for _, l := range line.LinesFromRenderObject(frameInfo, object, settings.GlobalSettings.BakeMatrixTransforms) {
|
|
|
|
l.Style = "f"
|
|
|
|
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(objects), len(r.RunningBuffer), len(runningBuffer), animated)
|
|
|
|
|
|
|
|
//Flush non dupes
|
2023-11-22 01:42:40 +00:00
|
|
|
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))
|
2023-11-22 01:42:40 +00:00
|
|
|
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
|
|
|
|
|
2023-11-21 01:55:09 +00:00
|
|
|
drawPathList := make(shapes.DrawPathList, 0, len(o.DrawPathList))
|
2023-11-20 06:32:13 +00:00
|
|
|
|
|
|
|
for _, command := range o.DrawPathList {
|
2023-11-21 01:55:09 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
gradientClip := types.NewClipPath(command.Commands)
|
|
|
|
//Convert gradients to many tags
|
2023-11-22 01:42:40 +00:00
|
|
|
for _, gradientPath := range gradient.GetInterpolatedDrawPaths(0, settings.GlobalSettings.GradientSlices) {
|
2023-11-21 01:55:09 +00:00
|
|
|
newPath := shapes.DrawPath{
|
2023-11-20 06:32:13 +00:00
|
|
|
Style: gradientPath.Style,
|
|
|
|
Commands: gradientClip.Intersect(types.NewClipPath(gradientPath.Commands)).GetShape(),
|
|
|
|
}
|
|
|
|
if len(newPath.Commands.Edges) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
drawPathList = append(drawPathList, newPath)
|
|
|
|
}
|
2023-11-21 17:38:17 +00:00
|
|
|
} else {
|
|
|
|
drawPathList = append(drawPathList, command)
|
2023-11-20 06:32:13 +00:00
|
|
|
}
|
2023-11-21 17:38:17 +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
|
|
|
|
}
|