Implement Option, full nested sprite support
This commit is contained in:
parent
cc33d7aa8e
commit
42df53898d
|
@ -6,6 +6,7 @@ import (
|
|||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf"
|
||||
swftag "git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf/tag"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/shapes"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -55,7 +56,7 @@ func TestParser(t *testing.T) {
|
|||
|
||||
var frameOffset int64
|
||||
|
||||
processor := types.NewSWFProcessor(tags, shapes.RectangleFromSWF(swfReader.Header().FrameSize), swfReader.Header().FrameRate.Float64(), int64(swfReader.Header().FrameCount))
|
||||
processor := types.NewSWFProcessor(tags, shapes.RectangleFromSWF(swfReader.Header().FrameSize), swfReader.Header().FrameRate.Float64(), int64(swfReader.Header().FrameCount), swfReader.Header().Version)
|
||||
|
||||
assRenderer := ass.NewRenderer(processor.FrameRate, processor.ViewPort)
|
||||
|
||||
|
@ -83,7 +84,7 @@ func TestParser(t *testing.T) {
|
|||
|
||||
frame.FrameOffset = frameOffset
|
||||
|
||||
rendered := frame.Frame.Render(0, nil, nil, nil)
|
||||
rendered := frame.Frame.Render(0, nil, types.None[math.ColorTransform](), types.None[math.MatrixTransform]())
|
||||
|
||||
if frame.GetFrameNumber() == 0 {
|
||||
for _, object := range rendered {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf"
|
||||
swftag "git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf/tag"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/shapes"
|
||||
"io"
|
||||
math2 "math"
|
||||
|
@ -100,7 +101,7 @@ func main() {
|
|||
|
||||
var frameOffset int64
|
||||
|
||||
processor := types.NewSWFProcessor(tags, shapes.RectangleFromSWF(swfReader.Header().FrameSize), swfReader.Header().FrameRate.Float64(), int64(swfReader.Header().FrameCount))
|
||||
processor := types.NewSWFProcessor(tags, shapes.RectangleFromSWF(swfReader.Header().FrameSize), swfReader.Header().FrameRate.Float64(), int64(swfReader.Header().FrameCount), swfReader.Header().Version)
|
||||
|
||||
assRenderer := ass.NewRenderer(processor.FrameRate, processor.ViewPort)
|
||||
|
||||
|
@ -156,7 +157,7 @@ func main() {
|
|||
|
||||
frame.FrameOffset = frameOffset
|
||||
|
||||
rendered := frame.Frame.Render(0, nil, nil, nil)
|
||||
rendered := frame.Frame.Render(0, nil, types.None[math.ColorTransform](), types.None[math.MatrixTransform]())
|
||||
|
||||
if frame.GetFrameNumber() == 0 {
|
||||
for _, object := range rendered {
|
||||
|
|
|
@ -4,5 +4,5 @@ import "git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/shapes"
|
|||
|
||||
type MultiFrameObjectDefinition interface {
|
||||
shapes.ObjectDefinition
|
||||
NextFrame() *ViewFrame
|
||||
NextFrame(frameNumber int64, p shapes.ObjectProperties) *ViewFrame
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ type SWFProcessor struct {
|
|||
Audio *AudioStream
|
||||
}
|
||||
|
||||
func NewSWFProcessor(tags []swftag.Tag, viewPort shapes.Rectangle[float64], frameRate float64, frameCount int64) *SWFProcessor {
|
||||
func NewSWFProcessor(tags []swftag.Tag, viewPort shapes.Rectangle[float64], frameRate float64, frameCount int64, version uint8) *SWFProcessor {
|
||||
p := &SWFProcessor{
|
||||
SWFTreeProcessor: *NewSWFTreeProcessor(0, tags, make(shapes.ObjectCollection)),
|
||||
SWFTreeProcessor: *NewSWFTreeProcessor(0, tags, make(shapes.ObjectCollection), version),
|
||||
Background: &shapes.FillStyleRecord{
|
||||
Fill: math.Color{
|
||||
R: 255,
|
||||
|
@ -87,6 +87,10 @@ func (p *SWFProcessor) NextFrameOutput() *FrameInformation {
|
|||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
// Stop looping main video
|
||||
if p.Loops > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !p.Playing && (p.Audio == nil || p.Audio.Start == nil) || p.Frame == 1 { //Force play till finding audio, or first frame is 0
|
||||
p.Playing = true
|
||||
|
|
|
@ -26,18 +26,21 @@ type SWFTreeProcessor struct {
|
|||
Playing bool
|
||||
Loops int
|
||||
|
||||
Version uint8
|
||||
|
||||
JPEGTables []byte
|
||||
|
||||
processFunc func(actions ActionList) (tag swftag.Tag, newActions ActionList)
|
||||
}
|
||||
|
||||
func NewSWFTreeProcessor(objectId uint16, tags []swftag.Tag, objects shapes.ObjectCollection) *SWFTreeProcessor {
|
||||
func NewSWFTreeProcessor(objectId uint16, tags []swftag.Tag, objects shapes.ObjectCollection, version uint8) *SWFTreeProcessor {
|
||||
return &SWFTreeProcessor{
|
||||
Objects: objects,
|
||||
Frame: 0,
|
||||
Tags: tags,
|
||||
Layout: NewViewLayout(objectId, nil, nil),
|
||||
Playing: true,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,42 +62,108 @@ func (p *SWFTreeProcessor) Process(actions ActionList) (tag swftag.Tag, newActio
|
|||
return p.process(actions)
|
||||
}
|
||||
|
||||
func (p *SWFTreeProcessor) placeObject(object shapes.ObjectDefinition, depth, clipDepth uint16, isMove, hasRatio, hasClipDepth bool, ratio float64, transform *math.MatrixTransform, colorTransform *math.ColorTransform) {
|
||||
type PlaceAction uint8
|
||||
|
||||
func PlaceActionFromFlags(hasCharacter, isMove bool) PlaceAction {
|
||||
var action PlaceAction
|
||||
if hasCharacter && !isMove {
|
||||
action = ActionPlace
|
||||
} else if hasCharacter && isMove {
|
||||
action = ActionReplace
|
||||
} else if !hasCharacter && isMove {
|
||||
action = ActionModify
|
||||
} else {
|
||||
panic("invalid action")
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
const (
|
||||
ActionPlace = PlaceAction(iota)
|
||||
ActionReplace
|
||||
ActionModify
|
||||
)
|
||||
|
||||
type placeObjectData struct {
|
||||
Action PlaceAction
|
||||
Depth uint16
|
||||
ClipDepth Option[uint16]
|
||||
Ratio Option[float64]
|
||||
Transform Option[math.MatrixTransform]
|
||||
ColorTransform Option[math.ColorTransform]
|
||||
Visible Option[bool]
|
||||
BlendMode Option[swftag.BlendMode]
|
||||
}
|
||||
|
||||
func (p *SWFTreeProcessor) applyPlaceObject(layout *ViewLayout, data placeObjectData) {
|
||||
data.Transform.With(func(transform math.MatrixTransform) {
|
||||
layout.MatrixTransform = Some(transform)
|
||||
})
|
||||
data.ColorTransform.With(func(colorTransform math.ColorTransform) {
|
||||
layout.ColorTransform = Some(colorTransform)
|
||||
})
|
||||
data.Ratio.With(func(ratio float64) {
|
||||
layout.Properties.Ratio = ratio
|
||||
})
|
||||
|
||||
if p.Version >= 11 {
|
||||
data.Visible.With(func(b bool) {
|
||||
layout.Properties.Visible = b
|
||||
})
|
||||
//todo: background color
|
||||
}
|
||||
//todo: filters
|
||||
}
|
||||
|
||||
func (p *SWFTreeProcessor) placeObject(object shapes.ObjectDefinition, data placeObjectData) {
|
||||
if object == nil {
|
||||
//TODO: place bogus element
|
||||
fmt.Printf("Object at depth:%d not found\n", depth)
|
||||
p.Layout.Remove(depth)
|
||||
fmt.Printf("Object at depth:%d not found\n", data.Depth)
|
||||
p.Layout.Remove(data.Depth)
|
||||
return
|
||||
}
|
||||
|
||||
currentLayout := p.Layout.Get(depth)
|
||||
data.BlendMode.With(func(mode swftag.BlendMode) {
|
||||
fmt.Printf("Unsupported blends!!!\n")
|
||||
switch mode {
|
||||
case swftag.BlendOverlay:
|
||||
//fake it somewhat with half transparency for now, TODO: split underlying image in intersections and hardcode-apply this
|
||||
i := math.IdentityColorTransform()
|
||||
i.Multiply.Alpha = 128
|
||||
|
||||
if isMove && currentLayout != nil && currentLayout.GetObjectId() == object.GetObjectId() {
|
||||
if transform != nil {
|
||||
currentLayout.MatrixTransform = transform
|
||||
}
|
||||
if colorTransform != nil {
|
||||
currentLayout.ColorTransform = colorTransform
|
||||
}
|
||||
if hasRatio {
|
||||
currentLayout.Properties.Ratio = ratio
|
||||
}
|
||||
return
|
||||
}
|
||||
data.ColorTransform.With(func(transform math.ColorTransform) {
|
||||
i = i.Combine(transform)
|
||||
})
|
||||
|
||||
var view *ViewLayout
|
||||
if hasClipDepth {
|
||||
view = NewClippingViewLayout(object.GetObjectId(), clipDepth, object.GetSafeObject(), p.Layout)
|
||||
} else {
|
||||
view = NewViewLayout(object.GetObjectId(), object.GetSafeObject(), p.Layout)
|
||||
}
|
||||
view.MatrixTransform = transform
|
||||
view.ColorTransform = colorTransform
|
||||
view.Properties.Ratio = ratio
|
||||
if isMove {
|
||||
p.Layout.Replace(depth, view)
|
||||
} else {
|
||||
p.Layout.Place(depth, view)
|
||||
data.ColorTransform = Some(i)
|
||||
}
|
||||
})
|
||||
|
||||
switch data.Action {
|
||||
case ActionPlace:
|
||||
var view *ViewLayout
|
||||
if clipDepth, ok := data.ClipDepth.Some(); ok {
|
||||
view = NewClippingViewLayout(object.GetObjectId(), clipDepth, object.GetSafeObject(), p.Layout)
|
||||
} else {
|
||||
view = NewViewLayout(object.GetObjectId(), object.GetSafeObject(), p.Layout)
|
||||
}
|
||||
view.Properties.PlaceFrame = p.Frame
|
||||
p.applyPlaceObject(view, data)
|
||||
p.Layout.Place(data.Depth, view)
|
||||
case ActionReplace:
|
||||
var view *ViewLayout
|
||||
if clipDepth, ok := data.ClipDepth.Some(); ok {
|
||||
view = NewClippingViewLayout(object.GetObjectId(), clipDepth, object.GetSafeObject(), p.Layout)
|
||||
} else {
|
||||
view = NewViewLayout(object.GetObjectId(), object.GetSafeObject(), p.Layout)
|
||||
}
|
||||
view.Properties.PlaceFrame = p.Frame
|
||||
p.applyPlaceObject(view, data)
|
||||
p.Layout.Replace(data.Depth, view)
|
||||
case ActionModify:
|
||||
if currentLayout := p.Layout.Get(data.Depth); currentLayout != nil && currentLayout.GetObjectId() == object.GetObjectId() {
|
||||
p.applyPlaceObject(currentLayout, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,10 +209,7 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
if p.Loops > 0 {
|
||||
break
|
||||
}
|
||||
p.Objects.Add(&SpriteDefinition{
|
||||
ObjectId: node.SpriteId,
|
||||
Processor: NewSWFTreeProcessor(node.SpriteId, node.ControlTags, p.Objects),
|
||||
})
|
||||
p.Objects.Add(SpriteDefinitionFromSWF(node.SpriteId, int(node.FrameCount), NewSWFTreeProcessor(node.SpriteId, node.ControlTags, p.Objects, p.Version)))
|
||||
case *swftag.DefineText:
|
||||
ob := TextDefinitionFromSWF(p.Objects, node.CharacterId, node.Bounds, node.TextRecords, node.Matrix)
|
||||
if ob == nil {
|
||||
|
@ -416,21 +482,25 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
p.Layout.Remove(node.Depth)
|
||||
|
||||
case *swftag.PlaceObject:
|
||||
var object shapes.ObjectDefinition
|
||||
p.Objects.Get(node.CharacterId)
|
||||
object := p.Objects.Get(node.CharacterId)
|
||||
|
||||
var transform *math.MatrixTransform
|
||||
if t := math.MatrixTransformFromSWF(node.Matrix); !t.IsIdentity() {
|
||||
transform = &t
|
||||
}
|
||||
|
||||
var colorTransform *math.ColorTransform
|
||||
var colorTransform math.ColorTransform
|
||||
if node.Flag.HasColorTransform && node.ColorTransform != nil {
|
||||
t := math.ColorTransformFromSWF(*node.ColorTransform)
|
||||
colorTransform = &t
|
||||
colorTransform = t
|
||||
}
|
||||
|
||||
p.placeObject(object, node.Depth, 0, false, false, false, 0, transform, colorTransform)
|
||||
transform := math.MatrixTransformFromSWF(node.Matrix)
|
||||
|
||||
p.placeObject(object, placeObjectData{
|
||||
Action: ActionPlace,
|
||||
Depth: node.Depth,
|
||||
ClipDepth: None[uint16](),
|
||||
Ratio: None[float64](),
|
||||
Transform: SomeWith(transform, !transform.IsIdentity()),
|
||||
ColorTransform: SomeWith(colorTransform, node.Flag.HasColorTransform),
|
||||
Visible: None[bool](),
|
||||
})
|
||||
case *swftag.PlaceObject2:
|
||||
var object shapes.ObjectDefinition
|
||||
if node.Flag.HasCharacter {
|
||||
|
@ -439,19 +509,15 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
object = vl.Object
|
||||
}
|
||||
|
||||
var transform *math.MatrixTransform
|
||||
if node.Flag.HasMatrix {
|
||||
t := math.MatrixTransformFromSWF(node.Matrix)
|
||||
transform = &t
|
||||
}
|
||||
|
||||
var colorTransform *math.ColorTransform
|
||||
if node.Flag.HasColorTransform {
|
||||
t := math.ColorTransformFromSWFAlpha(node.ColorTransform)
|
||||
colorTransform = &t
|
||||
}
|
||||
|
||||
p.placeObject(object, node.Depth, node.ClipDepth, node.Flag.Move, node.Flag.HasRatio, node.Flag.HasClipDepth, float64(node.Ratio)/math2.MaxUint16, transform, colorTransform)
|
||||
p.placeObject(object, placeObjectData{
|
||||
Action: PlaceActionFromFlags(node.Flag.HasCharacter, node.Flag.Move),
|
||||
Depth: node.Depth,
|
||||
ClipDepth: SomeWith(node.ClipDepth, node.Flag.HasClipDepth),
|
||||
Ratio: SomeWith(float64(node.Ratio)/math2.MaxUint16, node.Flag.HasRatio),
|
||||
Transform: SomeWith(math.MatrixTransformFromSWF(node.Matrix), node.Flag.HasMatrix),
|
||||
ColorTransform: SomeWith(math.ColorTransformFromSWFAlpha(node.ColorTransform), node.Flag.HasColorTransform),
|
||||
Visible: None[bool](),
|
||||
})
|
||||
case *swftag.PlaceObject3:
|
||||
//TODO: handle extra properties
|
||||
var object shapes.ObjectDefinition
|
||||
|
@ -461,34 +527,16 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
object = p.Layout.Get(node.Depth).Object
|
||||
}
|
||||
|
||||
var transform *math.MatrixTransform
|
||||
if node.Flag.HasMatrix {
|
||||
t := math.MatrixTransformFromSWF(node.Matrix)
|
||||
transform = &t
|
||||
}
|
||||
|
||||
var colorTransform *math.ColorTransform
|
||||
if node.Flag.HasColorTransform {
|
||||
t := math.ColorTransformFromSWFAlpha(node.ColorTransform)
|
||||
colorTransform = &t
|
||||
}
|
||||
|
||||
if node.Flag.HasBlendMode {
|
||||
fmt.Printf("Unsupported blends!!!\n")
|
||||
switch node.BlendMode {
|
||||
case swftag.BlendOverlay:
|
||||
//fake it somewhat with half transparency for now, TODO: split underlying image in intersections and hardcode-apply this
|
||||
i := math.IdentityColorTransform()
|
||||
i.Multiply.Alpha = 128
|
||||
|
||||
if colorTransform != nil {
|
||||
i = i.Combine(*colorTransform)
|
||||
}
|
||||
colorTransform = &i
|
||||
}
|
||||
}
|
||||
|
||||
p.placeObject(object, node.Depth, node.ClipDepth, node.Flag.Move, node.Flag.HasRatio, node.Flag.HasClipDepth, float64(node.Ratio)/math2.MaxUint16, transform, colorTransform)
|
||||
p.placeObject(object, placeObjectData{
|
||||
Action: PlaceActionFromFlags(node.Flag.HasCharacter, node.Flag.Move),
|
||||
Depth: node.Depth,
|
||||
ClipDepth: SomeWith(node.ClipDepth, node.Flag.HasClipDepth),
|
||||
Ratio: SomeWith(float64(node.Ratio)/math2.MaxUint16, node.Flag.HasRatio),
|
||||
Transform: SomeWith(math.MatrixTransformFromSWF(node.Matrix), node.Flag.HasMatrix),
|
||||
ColorTransform: SomeWith(math.ColorTransformFromSWFAlpha(node.ColorTransform), node.Flag.HasColorTransform),
|
||||
Visible: SomeWith(node.Visible > 0, node.Flag.HasVisible),
|
||||
BlendMode: SomeWith(node.BlendMode, node.Flag.HasBlendMode),
|
||||
})
|
||||
case *swftag.ShowFrame:
|
||||
case *swftag.End:
|
||||
case *swftag.DoAction:
|
||||
|
@ -534,20 +582,21 @@ func (p *SWFTreeProcessor) NextFrame() *ViewFrame {
|
|||
}
|
||||
}
|
||||
|
||||
if node == nil { //Loop again
|
||||
if node == nil {
|
||||
// We are done looping, check if we still need to keep playback
|
||||
p.Loops++
|
||||
p.Frame = 0
|
||||
//p.Frame = 0
|
||||
p.Index = 0
|
||||
p.Layout = NewViewLayout(p.Layout.GetObjectId(), nil, nil)
|
||||
//p.Layout = NewViewLayout(p.Layout.GetObjectId(), nil, nil)
|
||||
if p.LastFrame != nil {
|
||||
return p.NextFrame()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
p.Frame++
|
||||
frame := p.Layout.NextFrame(p.Frame, actions)
|
||||
|
||||
frame := p.Layout.NextFrame(actions)
|
||||
p.Frame++
|
||||
|
||||
p.LastFrame = frame
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
math2 "git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/shapes"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type SpriteDefinition struct {
|
||||
ObjectId uint16
|
||||
Processor *SWFTreeProcessor
|
||||
CurrentFrame *ViewFrame
|
||||
ObjectId uint16
|
||||
Frames [][]SpriteFrameEntry
|
||||
}
|
||||
|
||||
func (d *SpriteDefinition) GetObjectId() uint16 {
|
||||
|
@ -15,27 +16,93 @@ func (d *SpriteDefinition) GetObjectId() uint16 {
|
|||
}
|
||||
|
||||
func (d *SpriteDefinition) GetShapeList(p shapes.ObjectProperties) (list shapes.DrawPathList) {
|
||||
if d.CurrentFrame != nil {
|
||||
for _, object := range d.CurrentFrame.Render(0, nil, nil, nil) {
|
||||
list = append(list, object.DrawPathList...)
|
||||
}
|
||||
}
|
||||
panic("should not be called")
|
||||
return list
|
||||
}
|
||||
|
||||
func (d *SpriteDefinition) NextFrame() *ViewFrame {
|
||||
//TODO: figure out why this can return null. missing shapes?
|
||||
d.CurrentFrame = d.Processor.NextFrame()
|
||||
if d.CurrentFrame == nil {
|
||||
return NewViewFrame(d.GetObjectId(), &shapes.DrawPathList{})
|
||||
func (d *SpriteDefinition) NextFrame(frameNumber int64, p shapes.ObjectProperties) *ViewFrame {
|
||||
frameN := frameNumber - p.PlaceFrame
|
||||
|
||||
n := frameN % int64(len(d.Frames))
|
||||
|
||||
spriteFrame := NewViewFrame(d.ObjectId, nil)
|
||||
|
||||
for _, e := range d.Frames[n] {
|
||||
var frame *ViewFrame
|
||||
if mfod, ok := e.Object.(MultiFrameObjectDefinition); ok {
|
||||
frame = mfod.NextFrame(frameN, e.Properties)
|
||||
} else {
|
||||
list := e.Object.GetShapeList(e.Properties)
|
||||
frame = NewViewFrame(e.Object.GetObjectId(), &list)
|
||||
}
|
||||
|
||||
frame.ColorTransform = e.ColorTransform
|
||||
frame.MatrixTransform = e.MatrixTransform
|
||||
|
||||
frame.ClipDepth = e.ClipDepth
|
||||
|
||||
spriteFrame.AddChild(e.Depth, frame)
|
||||
}
|
||||
return d.CurrentFrame
|
||||
|
||||
return spriteFrame
|
||||
}
|
||||
|
||||
func (d *SpriteDefinition) GetSafeObject() shapes.ObjectDefinition {
|
||||
return d
|
||||
}
|
||||
|
||||
type SpriteFrameEntry struct {
|
||||
Depth uint16
|
||||
Object shapes.ObjectDefinition
|
||||
ColorTransform Option[math2.ColorTransform]
|
||||
MatrixTransform Option[math2.MatrixTransform]
|
||||
ClipDepth Option[uint16]
|
||||
Properties shapes.ObjectProperties
|
||||
}
|
||||
|
||||
func SpriteDefinitionFromSWF(spriteId uint16, frameCount int, p *SWFTreeProcessor) *SpriteDefinition {
|
||||
var frames [][]SpriteFrameEntry
|
||||
|
||||
var lastFrame *ViewFrame
|
||||
for p.Loops == 0 && (len(frames) < frameCount || (frameCount == 0 && len(frames) == 0)) {
|
||||
f := p.NextFrame()
|
||||
if f == nil {
|
||||
break
|
||||
}
|
||||
if lastFrame == f {
|
||||
break
|
||||
}
|
||||
|
||||
lastFrame = f
|
||||
|
||||
var entries []SpriteFrameEntry
|
||||
|
||||
for depth, layout := range p.Layout.DepthMap {
|
||||
if layout.Object == nil {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
entries = append(entries, SpriteFrameEntry{
|
||||
Depth: depth,
|
||||
Object: layout.Object,
|
||||
ColorTransform: layout.ColorTransform,
|
||||
MatrixTransform: layout.MatrixTransform,
|
||||
ClipDepth: layout.ClipDepth,
|
||||
Properties: layout.Properties,
|
||||
})
|
||||
}
|
||||
slices.SortFunc(entries, func(a, b SpriteFrameEntry) int {
|
||||
return int(a.Depth) - int(b.Depth)
|
||||
})
|
||||
frames = append(frames, entries)
|
||||
}
|
||||
|
||||
if len(frames) == 0 {
|
||||
panic("unsupported")
|
||||
}
|
||||
|
||||
return &SpriteDefinition{
|
||||
ObjectId: d.ObjectId,
|
||||
Processor: NewSWFTreeProcessor(d.ObjectId, d.Processor.Tags, d.Processor.Objects),
|
||||
ObjectId: spriteId,
|
||||
Frames: frames,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,20 +14,18 @@ type ViewFrame struct {
|
|||
|
||||
DrawPathList *shapes.DrawPathList
|
||||
|
||||
ColorTransform *math.ColorTransform
|
||||
MatrixTransform *math.MatrixTransform
|
||||
ColorTransform Option[math.ColorTransform]
|
||||
MatrixTransform Option[math.MatrixTransform]
|
||||
|
||||
IsClipping bool
|
||||
ClipDepth uint16
|
||||
ClipDepth Option[uint16]
|
||||
}
|
||||
|
||||
func NewClippingFrame(objectId, clipDepth uint16, list *shapes.DrawPathList) *ViewFrame {
|
||||
return &ViewFrame{
|
||||
ObjectId: objectId,
|
||||
ClipDepth: clipDepth,
|
||||
ClipDepth: Some(clipDepth),
|
||||
DrawPathList: list,
|
||||
DepthMap: make(map[uint16]*ViewFrame),
|
||||
IsClipping: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,31 +44,12 @@ func (f *ViewFrame) AddChild(depth uint16, frame *ViewFrame) {
|
|||
f.DepthMap[depth] = frame
|
||||
}
|
||||
|
||||
func (f *ViewFrame) Render(baseDepth uint16, depthChain Depth, parentColor *math.ColorTransform, parentMatrix *math.MatrixTransform) RenderedFrame {
|
||||
depthChain = slices.Clone(depthChain)
|
||||
depthChain = append(depthChain, baseDepth)
|
||||
func (f *ViewFrame) Render(baseDepth uint16, depthChain Depth, parentColor Option[math.ColorTransform], parentMatrix Option[math.MatrixTransform]) RenderedFrame {
|
||||
depthChain = append(slices.Clone(depthChain), baseDepth)
|
||||
|
||||
matrixTransform := math.IdentityTransform()
|
||||
if f.MatrixTransform != nil {
|
||||
if parentMatrix != nil {
|
||||
matrixTransform = parentMatrix.Multiply(*f.MatrixTransform)
|
||||
} else {
|
||||
matrixTransform = *f.MatrixTransform
|
||||
}
|
||||
} else if parentMatrix != nil {
|
||||
matrixTransform = *parentMatrix
|
||||
}
|
||||
matrixTransform := parentMatrix.Combine(f.MatrixTransform, nil)
|
||||
|
||||
colorTransform := math.IdentityColorTransform()
|
||||
if f.ColorTransform != nil {
|
||||
if parentColor != nil {
|
||||
colorTransform = parentColor.Combine(*f.ColorTransform)
|
||||
} else {
|
||||
colorTransform = *f.ColorTransform
|
||||
}
|
||||
} else if parentColor != nil {
|
||||
colorTransform = *parentColor
|
||||
}
|
||||
colorTransform := parentColor.Combine(f.ColorTransform, nil)
|
||||
|
||||
var renderedFrame RenderedFrame
|
||||
|
||||
|
@ -80,27 +59,18 @@ func (f *ViewFrame) Render(baseDepth uint16, depthChain Depth, parentColor *math
|
|||
ObjectId: f.ObjectId,
|
||||
DrawPathList: *f.DrawPathList,
|
||||
Clip: nil,
|
||||
ColorTransform: colorTransform,
|
||||
MatrixTransform: matrixTransform,
|
||||
ColorTransform: SomeDefault(colorTransform, math.IdentityColorTransform()).Unwrap(),
|
||||
MatrixTransform: SomeDefault(matrixTransform, math.IdentityTransform()).Unwrap(),
|
||||
})
|
||||
} else {
|
||||
clipMap := make(map[uint16]*ViewFrame)
|
||||
clipPaths := make(map[uint16]*shapes.ClipPath)
|
||||
|
||||
matrixTransform := &matrixTransform
|
||||
if matrixTransform.IsIdentity() {
|
||||
matrixTransform = nil
|
||||
}
|
||||
colorTransform := &colorTransform
|
||||
if colorTransform.IsIdentity() {
|
||||
colorTransform = nil
|
||||
}
|
||||
|
||||
keys := maps.Keys(f.DepthMap)
|
||||
slices.Sort(keys)
|
||||
for _, depth := range keys {
|
||||
frame := f.DepthMap[depth]
|
||||
if frame.IsClipping { //Process clips as they come
|
||||
if _, isClipping := frame.ClipDepth.Some(); isClipping { //Process clips as they come
|
||||
clipMap[depth] = frame
|
||||
var clipPath *shapes.ClipPath
|
||||
for _, clipObject := range frame.Render(depth, depthChain, colorTransform, matrixTransform) {
|
||||
|
@ -135,14 +105,14 @@ func (f *ViewFrame) Render(baseDepth uint16, depthChain Depth, parentColor *math
|
|||
|
||||
for _, depth := range keys {
|
||||
frame := f.DepthMap[depth]
|
||||
if frame.IsClipping { //Already processed
|
||||
if _, isClipping := frame.ClipDepth.Some(); isClipping { //Already processed
|
||||
continue
|
||||
}
|
||||
var clipPath *shapes.ClipPath
|
||||
|
||||
for _, clipDepth := range clipMapKeys {
|
||||
clip := clipMap[clipDepth]
|
||||
if clip.ClipDepth > depth && clipDepth < depth {
|
||||
if clip.ClipDepth.Unwrap() > depth && clipDepth < depth {
|
||||
if clipPath == nil {
|
||||
clipPath = clipPaths[clipDepth]
|
||||
} else {
|
||||
|
|
|
@ -16,19 +16,17 @@ type ViewLayout struct {
|
|||
|
||||
Object shapes.ObjectDefinition
|
||||
|
||||
ColorTransform *math2.ColorTransform
|
||||
MatrixTransform *math2.MatrixTransform
|
||||
ColorTransform Option[math2.ColorTransform]
|
||||
MatrixTransform Option[math2.MatrixTransform]
|
||||
|
||||
Properties shapes.ObjectProperties
|
||||
|
||||
IsClipping bool
|
||||
ClipDepth uint16
|
||||
ClipDepth Option[uint16]
|
||||
}
|
||||
|
||||
func NewClippingViewLayout(objectId, clipDepth uint16, object shapes.ObjectDefinition, parent *ViewLayout) *ViewLayout {
|
||||
l := NewViewLayout(objectId, object, parent)
|
||||
l.IsClipping = true
|
||||
l.ClipDepth = clipDepth
|
||||
l.ClipDepth = Some(clipDepth)
|
||||
return l
|
||||
}
|
||||
|
||||
|
@ -40,6 +38,9 @@ func NewViewLayout(objectId uint16, object shapes.ObjectDefinition, parent *View
|
|||
Parent: parent,
|
||||
Object: object,
|
||||
DepthMap: make(map[uint16]*ViewLayout),
|
||||
Properties: shapes.ObjectProperties{
|
||||
Visible: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,10 +59,10 @@ func (v *ViewLayout) Replace(depth uint16, ob *ViewLayout) {
|
|||
if v.Object != nil {
|
||||
panic("Cannot have ObjectDefinition and children at the same time")
|
||||
} else if oldObject, ok := v.DepthMap[depth]; ok && oldObject != nil {
|
||||
if ob.MatrixTransform == nil {
|
||||
if _, ok := ob.MatrixTransform.Some(); !ok {
|
||||
ob.MatrixTransform = oldObject.MatrixTransform
|
||||
}
|
||||
if ob.ColorTransform == nil {
|
||||
if _, ok := ob.ColorTransform.Some(); !ok {
|
||||
ob.ColorTransform = oldObject.ColorTransform
|
||||
}
|
||||
}
|
||||
|
@ -81,11 +82,11 @@ func (v *ViewLayout) Remove(depth uint16) {
|
|||
delete(v.DepthMap, depth)
|
||||
}
|
||||
|
||||
func (v *ViewLayout) NextFrame(actions ActionList) (frame *ViewFrame) {
|
||||
frame = v.nextFrame(actions)
|
||||
func (v *ViewLayout) NextFrame(frameNumber int64, actions ActionList) (frame *ViewFrame) {
|
||||
frame = v.nextFrame(frameNumber, actions)
|
||||
|
||||
if v.IsClipping {
|
||||
clip := NewClippingFrame(frame.ObjectId, v.ClipDepth, frame.DrawPathList)
|
||||
if clipDepth, isClipping := v.ClipDepth.Some(); isClipping {
|
||||
clip := NewClippingFrame(frame.ObjectId, clipDepth, frame.DrawPathList)
|
||||
for depth, f := range frame.DepthMap {
|
||||
clip.AddChild(depth, f)
|
||||
}
|
||||
|
@ -96,10 +97,10 @@ func (v *ViewLayout) NextFrame(actions ActionList) (frame *ViewFrame) {
|
|||
return frame
|
||||
}
|
||||
|
||||
func (v *ViewLayout) nextFrame(actions ActionList) (frame *ViewFrame) {
|
||||
func (v *ViewLayout) nextFrame(frameNumber int64, actions ActionList) (frame *ViewFrame) {
|
||||
if v.Object != nil {
|
||||
if mfod, ok := v.Object.(MultiFrameObjectDefinition); ok {
|
||||
frame = mfod.NextFrame()
|
||||
frame = mfod.NextFrame(frameNumber, v.Properties)
|
||||
} else {
|
||||
list := v.Object.GetShapeList(v.Properties)
|
||||
frame = NewViewFrame(v.GetObjectId(), &list)
|
||||
|
@ -112,7 +113,7 @@ func (v *ViewLayout) nextFrame(actions ActionList) (frame *ViewFrame) {
|
|||
|
||||
for _, depth := range keys {
|
||||
child := v.DepthMap[depth]
|
||||
f := child.NextFrame(actions)
|
||||
f := child.NextFrame(frameNumber, actions)
|
||||
frame.AddChild(depth, f)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/swf/types"
|
||||
"math"
|
||||
)
|
||||
|
@ -44,6 +45,19 @@ func IdentityColorTransform() ColorTransform {
|
|||
}
|
||||
}
|
||||
|
||||
func (t ColorTransform) String() string {
|
||||
return fmt.Sprintf("ColorTransform{MULT(%d,%d,%d,%d),ADD(%d,%d,%d,%d)}",
|
||||
t.Multiply.Red,
|
||||
t.Multiply.Green,
|
||||
t.Multiply.Blue,
|
||||
t.Multiply.Alpha,
|
||||
t.Add.Red,
|
||||
t.Add.Green,
|
||||
t.Add.Blue,
|
||||
t.Add.Alpha,
|
||||
)
|
||||
}
|
||||
|
||||
func (t ColorTransform) IsIdentity() bool {
|
||||
return t.Add.Red == 0 && t.Add.Green == 0 && t.Add.Blue == 0 && t.Add.Alpha == 0 && t.Multiply.Red == 256 && t.Multiply.Green == 256 && t.Multiply.Blue == 256 && t.Multiply.Alpha == 256
|
||||
}
|
||||
|
|
|
@ -74,6 +74,10 @@ func SkewYTransform(angle float64) MatrixTransform {
|
|||
return NewMatrixTransform(DefaultScale, NewVector2(0, math.Tan(angle)), DefaultTranslation)
|
||||
}
|
||||
|
||||
func (m MatrixTransform) Combine(o MatrixTransform) MatrixTransform {
|
||||
return m.Multiply(o)
|
||||
}
|
||||
|
||||
func (m MatrixTransform) Multiply(o MatrixTransform) MatrixTransform {
|
||||
var r mat.Dense
|
||||
r.Mul(m.matrix, o.matrix)
|
||||
|
|
86
types/option.go
Normal file
86
types/option.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package types
|
||||
|
||||
type Option[T any] struct {
|
||||
value *T
|
||||
}
|
||||
|
||||
func (o Option[T]) Some() (T, bool) {
|
||||
if o.value != nil {
|
||||
return *o.value, true
|
||||
} else {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
}
|
||||
|
||||
func (o Option[T]) Unwrap() T {
|
||||
if o.value == nil {
|
||||
panic("Option must have Some")
|
||||
}
|
||||
return *o.value
|
||||
}
|
||||
|
||||
type Combinable[T any] interface {
|
||||
Combine(o T) T
|
||||
}
|
||||
|
||||
// Combine Combines two Option or returns either if any is Option.Some but not the other
|
||||
// f is not necessary if T implements Combinable
|
||||
func (o Option[T]) Combine(other Option[T], f func(a, b T) Option[T]) (result Option[T]) {
|
||||
if a, ok := o.Some(); ok {
|
||||
if b, ok := other.Some(); ok {
|
||||
if c, ok := any(a).(Combinable[T]); ok {
|
||||
return Some(c.Combine(b))
|
||||
}
|
||||
return f(a, b)
|
||||
}
|
||||
return o
|
||||
} else if _, ok := other.Some(); ok {
|
||||
return other
|
||||
} else {
|
||||
return Option[T]{}
|
||||
}
|
||||
}
|
||||
|
||||
func (o Option[T]) Pointer() *T {
|
||||
return o.value
|
||||
}
|
||||
|
||||
func (o Option[T]) With(f func(T)) {
|
||||
v, ok := o.Some()
|
||||
if ok {
|
||||
f(v)
|
||||
}
|
||||
}
|
||||
|
||||
func None[T any]() Option[T] {
|
||||
return Option[T]{}
|
||||
}
|
||||
|
||||
func Some[T any](value T) Option[T] {
|
||||
return Option[T]{
|
||||
value: &value,
|
||||
}
|
||||
}
|
||||
|
||||
func SomePointer[T any](value *T) Option[T] {
|
||||
return Option[T]{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func SomeWith[T any](value T, ok bool) Option[T] {
|
||||
if !ok {
|
||||
return Option[T]{}
|
||||
}
|
||||
return Option[T]{
|
||||
value: &value,
|
||||
}
|
||||
}
|
||||
|
||||
func SomeDefault[T any](o Option[T], def T) Option[T] {
|
||||
if _, ok := o.Some(); ok {
|
||||
return o
|
||||
}
|
||||
return Some(def)
|
||||
}
|
|
@ -7,7 +7,9 @@ type ObjectDefinition interface {
|
|||
}
|
||||
|
||||
type ObjectProperties struct {
|
||||
Ratio float64
|
||||
Ratio float64
|
||||
Visible bool
|
||||
PlaceFrame int64
|
||||
// Data can be any value internal to the object itself
|
||||
Data any
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue