Implement Option, full nested sprite support

This commit is contained in:
DataHoarder 2023-11-28 03:54:19 +01:00
parent cc33d7aa8e
commit 42df53898d
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
12 changed files with 371 additions and 172 deletions

View file

@ -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 {

View file

@ -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 {

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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,
}
}

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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
View 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)
}

View file

@ -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
}