Fix LinearGradient PAD spread mode
This commit is contained in:
parent
42df53898d
commit
211ae1bdf9
|
@ -264,7 +264,7 @@ func ContainerTagFromPathEntry(path shapes.DrawPath, clip *shapes.ClipPath, colo
|
|||
//TODO: this is broken
|
||||
translationTransform := math.TranslateTransform(matrixTransform.GetTranslation().Multiply(-1))
|
||||
path = shapes.DrawPath{
|
||||
Style: path.Style,
|
||||
Style: path.Style, //TODO: apply transform to Style?
|
||||
Shape: clip.ApplyMatrixTransform(translationTransform, true).ClipShape(path.Shape, true),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -490,7 +490,7 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
colorTransform = t
|
||||
}
|
||||
|
||||
transform := math.MatrixTransformFromSWF(node.Matrix)
|
||||
transform := math.MatrixTransformFromSWF(node.Matrix, 1)
|
||||
|
||||
p.placeObject(object, placeObjectData{
|
||||
Action: ActionPlace,
|
||||
|
@ -514,7 +514,7 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
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),
|
||||
Transform: SomeWith(math.MatrixTransformFromSWF(node.Matrix, 1), node.Flag.HasMatrix),
|
||||
ColorTransform: SomeWith(math.ColorTransformFromSWFAlpha(node.ColorTransform), node.Flag.HasColorTransform),
|
||||
Visible: None[bool](),
|
||||
})
|
||||
|
@ -532,7 +532,7 @@ func (p *SWFTreeProcessor) process(actions ActionList) (tag swftag.Tag, newActio
|
|||
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),
|
||||
Transform: SomeWith(math.MatrixTransformFromSWF(node.Matrix, 1), 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),
|
||||
|
|
|
@ -194,10 +194,10 @@ func (m MatrixTransform) String() string {
|
|||
return fmt.Sprintf("%#v", mat.Formatted(m.matrix, mat.FormatPython()))
|
||||
}
|
||||
|
||||
func MatrixTransformFromSWF(m types.MATRIX) MatrixTransform {
|
||||
func MatrixTransformFromSWF(m types.MATRIX, scale float64) MatrixTransform {
|
||||
return NewMatrixTransform(
|
||||
NewVector2(m.ScaleX.Float64(), m.ScaleY.Float64()),
|
||||
NewVector2(m.RotateSkew0.Float64(), m.RotateSkew1.Float64()),
|
||||
NewVector2(m.TranslateX.Float64(), m.TranslateY.Float64()),
|
||||
)
|
||||
).Multiply(ScaleTransform(NewVector2(scale, scale)))
|
||||
}
|
||||
|
|
|
@ -116,6 +116,14 @@ func (v Vector2[T]) Normals() (a, b Vector2[T]) {
|
|||
}
|
||||
}
|
||||
|
||||
func (v Vector2[T]) Max(b Vector2[T]) Vector2[T] {
|
||||
return NewVector2(max(v.X, b.X), max(v.Y, b.Y))
|
||||
}
|
||||
|
||||
func (v Vector2[T]) Min(b Vector2[T]) Vector2[T] {
|
||||
return NewVector2(min(v.X, b.X), min(v.Y, b.Y))
|
||||
}
|
||||
|
||||
func (v Vector2[T]) Abs() Vector2[T] {
|
||||
x := v.X
|
||||
if x < 0 {
|
||||
|
|
|
@ -32,10 +32,10 @@ func (r CubicCurveRecord) Reverse() Record {
|
|||
func (r CubicCurveRecord) ApplyMatrixTransform(transform math2.MatrixTransform, applyTranslation bool) Record {
|
||||
//TODO: see how accurate this is
|
||||
return CubicCurveRecord{
|
||||
Control1: math2.MatrixTransformApplyToVector(transform, r.Control1, applyTranslation),
|
||||
Control2: math2.MatrixTransformApplyToVector(transform, r.Control2, applyTranslation),
|
||||
Anchor: math2.MatrixTransformApplyToVector(transform, r.Anchor, applyTranslation),
|
||||
Start: math2.MatrixTransformApplyToVector(transform, r.Start, applyTranslation),
|
||||
Control1: transform.ApplyToVector(r.Control1, applyTranslation),
|
||||
Control2: transform.ApplyToVector(r.Control2, applyTranslation),
|
||||
Anchor: transform.ApplyToVector(r.Anchor, applyTranslation),
|
||||
Start: transform.ApplyToVector(r.Start, applyTranslation),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,10 @@ func (r CubicCurveRecord) String() string {
|
|||
return fmt.Sprintf("c %s %s %s", r.Control1, r.Control2, r.Anchor)
|
||||
}
|
||||
|
||||
func (r CubicCurveRecord) BoundingBox() (topLeft, bottomRight math2.Vector2[float64]) {
|
||||
return r.Start.Min(r.Control1).Min(r.Control2).Min(r.Anchor), r.Start.Max(r.Control1).Max(r.Control2).Max(r.Anchor)
|
||||
}
|
||||
|
||||
func CubicCurveFromQuadraticRecord(q QuadraticCurveRecord) CubicCurveRecord {
|
||||
return CubicCurveRecord{
|
||||
Control1: q.Start.AddVector(q.Control.Multiply(2)).Divide(3),
|
||||
|
|
|
@ -43,6 +43,16 @@ func (r CubicSplineCurveRecord) ApplyMatrixTransform(transform math.MatrixTransf
|
|||
}
|
||||
}
|
||||
|
||||
func (r CubicSplineCurveRecord) BoundingBox() (topLeft, bottomRight math.Vector2[float64]) {
|
||||
topLeft = r.Start.Min(r.Anchor)
|
||||
bottomRight = r.Start.Max(r.Anchor)
|
||||
for _, c := range r.Control {
|
||||
topLeft = topLeft.Min(c)
|
||||
bottomRight = bottomRight.Max(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r CubicSplineCurveRecord) Equals(other Record) bool {
|
||||
if o, ok := other.(CubicSplineCurveRecord); ok {
|
||||
return reflect.DeepEqual(r.Control, o.Control) && r.Start == o.Start && r.Anchor == o.Anchor
|
||||
|
|
|
@ -29,15 +29,11 @@ func (r LineRecord) Delta() math.Vector2[float64] {
|
|||
return r.To.SubVector(r.Start)
|
||||
}
|
||||
|
||||
func fake2DCross(a, b math.Vector2[float64]) float64 {
|
||||
return a.X*b.Y - a.Y + b.X
|
||||
}
|
||||
|
||||
func (r LineRecord) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) Record {
|
||||
//TODO: see how accurate this is
|
||||
return LineRecord{
|
||||
To: math.MatrixTransformApplyToVector(transform, r.To, applyTranslation),
|
||||
Start: math.MatrixTransformApplyToVector(transform, r.Start, applyTranslation),
|
||||
To: transform.ApplyToVector(r.To, applyTranslation),
|
||||
Start: transform.ApplyToVector(r.Start, applyTranslation),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +49,10 @@ func (r LineRecord) SameType(other Record) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
func (r LineRecord) BoundingBox() (topLeft, bottomRight math.Vector2[float64]) {
|
||||
return r.Start.Min(r.To), r.Start.Max(r.To)
|
||||
}
|
||||
|
||||
func (r LineRecord) IsFlat() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ func (r MoveRecord) Reverse() Record {
|
|||
func (r MoveRecord) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) Record {
|
||||
//TODO: see how accurate this is
|
||||
return MoveRecord{
|
||||
To: math.MatrixTransformApplyToVector(transform, r.To, applyTranslation),
|
||||
Start: math.MatrixTransformApplyToVector(transform, r.Start, applyTranslation),
|
||||
To: transform.ApplyToVector(r.To, applyTranslation),
|
||||
Start: transform.ApplyToVector(r.Start, applyTranslation),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,10 @@ func (r MoveRecord) SameType(other Record) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
func (r MoveRecord) BoundingBox() (topLeft, bottomRight math.Vector2[float64]) {
|
||||
return r.Start.Min(r.To), r.Start.Max(r.To)
|
||||
}
|
||||
|
||||
func (r MoveRecord) IsFlat() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ func (r QuadraticCurveRecord) Reverse() Record {
|
|||
func (r QuadraticCurveRecord) ApplyMatrixTransform(transform math2.MatrixTransform, applyTranslation bool) Record {
|
||||
//TODO: see how accurate this is
|
||||
return QuadraticCurveRecord{
|
||||
Control: math2.MatrixTransformApplyToVector(transform, r.Control, applyTranslation),
|
||||
Anchor: math2.MatrixTransformApplyToVector(transform, r.Anchor, applyTranslation),
|
||||
Start: math2.MatrixTransformApplyToVector(transform, r.Start, applyTranslation),
|
||||
Control: transform.ApplyToVector(r.Control, applyTranslation),
|
||||
Anchor: transform.ApplyToVector(r.Anchor, applyTranslation),
|
||||
Start: transform.ApplyToVector(r.Start, applyTranslation),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,10 @@ func QuadraticCurveFromLineRecord(l LineRecord) QuadraticCurveRecord {
|
|||
}
|
||||
}
|
||||
|
||||
func (r QuadraticCurveRecord) BoundingBox() (topLeft, bottomRight math2.Vector2[float64]) {
|
||||
return r.Start.Min(r.Control).Min(r.Anchor), r.Start.Max(r.Control).Max(r.Anchor)
|
||||
}
|
||||
|
||||
func (r QuadraticCurveRecord) ToLineRecords(scale int64) []Record {
|
||||
distanceToleranceSquare := math.Pow(0.5/float64(scale), 2)
|
||||
points := QuadraticRecursiveBezier(nil, BezierCurveAngleTolerance, distanceToleranceSquare, r.Start, r.Control, r.Anchor, 0)
|
||||
|
|
|
@ -16,6 +16,8 @@ type Record interface {
|
|||
|
||||
ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) Record
|
||||
|
||||
BoundingBox() (topLeft, bottomRight math.Vector2[float64])
|
||||
|
||||
IsFlat() bool
|
||||
|
||||
String() string
|
||||
|
|
|
@ -13,20 +13,27 @@ type Bitmap struct {
|
|||
|
||||
func (b Bitmap) ApplyColorTransform(transform math2.ColorTransform) Fillable {
|
||||
b2 := b
|
||||
b2.List = b.List.ApplyColorTransform(transform).(DrawPathList)
|
||||
b2.Transform = b.Transform
|
||||
return b2
|
||||
}
|
||||
|
||||
func (b Bitmap) ApplyMatrixTransform(transform math2.MatrixTransform, applyTranslation bool) Fillable {
|
||||
b2 := b
|
||||
if !applyTranslation {
|
||||
panic("not supported")
|
||||
}
|
||||
b2.Transform = transform.Combine(b2.Transform)
|
||||
return b2
|
||||
}
|
||||
|
||||
func (b Bitmap) Fill(shape Shape) DrawPathList {
|
||||
return b.List.ApplyMatrixTransform(b.Transform, true).Fill(shape)
|
||||
}
|
||||
|
||||
func BitmapFillFromSWF(l DrawPathList, transform types.MATRIX) Bitmap {
|
||||
// shape is already in pixel world, but matrix comes as twip
|
||||
baseScale := math2.ScaleTransform(math2.NewVector2[float64](1./types.TwipFactor, 1./types.TwipFactor))
|
||||
return Bitmap{
|
||||
List: l,
|
||||
Transform: math2.MatrixTransformFromSWF(transform).Multiply(baseScale),
|
||||
List: l,
|
||||
// shape is already in pixel world, but matrix comes as twip
|
||||
Transform: math2.MatrixTransformFromSWF(transform, 1/types.TwipFactor),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,7 +288,7 @@ func ConvertBitmapToDrawPathList(i image.Image) (r DrawPathList) {
|
|||
|
||||
scale := math.ScaleTransform(math.NewVector2(ratioX, ratioY))
|
||||
r2 := r.ApplyMatrixTransform(scale, true)
|
||||
return r2
|
||||
return r2.(DrawPathList)
|
||||
}
|
||||
|
||||
var bitmapHeaderJPEG = []byte{0xff, 0xd8}
|
||||
|
|
|
@ -9,7 +9,7 @@ type DrawPath struct {
|
|||
|
||||
func (p DrawPath) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) (r DrawPath) {
|
||||
return DrawPath{
|
||||
Style: p.Style,
|
||||
Style: p.Style.ApplyMatrixTransform(transform, applyTranslation),
|
||||
Shape: p.Shape.ApplyMatrixTransform(transform, applyTranslation),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,8 @@ func (l DrawPathList) ApplyColorTransform(transform math.ColorTransform) Fillabl
|
|||
return r
|
||||
}
|
||||
|
||||
func (l DrawPathList) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) (r DrawPathList) {
|
||||
r = make(DrawPathList, 0, len(l))
|
||||
func (l DrawPathList) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) Fillable {
|
||||
r := make(DrawPathList, 0, len(l))
|
||||
for i := range l {
|
||||
r = append(r, l[i].ApplyMatrixTransform(transform, applyTranslation))
|
||||
}
|
||||
|
|
|
@ -16,27 +16,27 @@ type Gradient struct {
|
|||
SpreadMode swfsubtypes.GradientSpreadMode
|
||||
InterpolationMode swfsubtypes.GradientInterpolationMode
|
||||
|
||||
Interpolation func(self Gradient, overlap, blur float64, gradientSlices int) DrawPathList
|
||||
}
|
||||
|
||||
func (g Gradient) GetSpreadMode() swfsubtypes.GradientSpreadMode {
|
||||
return g.SpreadMode
|
||||
}
|
||||
|
||||
func (g Gradient) GetInterpolationMode() swfsubtypes.GradientInterpolationMode {
|
||||
return g.InterpolationMode
|
||||
Interpolation func(self Gradient, overlap, blur float64, gradientSlices int, bb Rectangle[float64]) DrawPathList
|
||||
}
|
||||
|
||||
func (g Gradient) GetItems() []GradientItem {
|
||||
return g.Records
|
||||
}
|
||||
|
||||
func (g Gradient) GetInterpolatedDrawPaths(overlap, blur float64, gradientSlices int) DrawPathList {
|
||||
return g.Interpolation(g, overlap, blur, gradientSlices)
|
||||
func (g Gradient) GetInterpolatedDrawPaths(overlap, blur float64, gradientSlices int, bb Rectangle[float64]) DrawPathList {
|
||||
return g.Interpolation(g, overlap, blur, gradientSlices, bb).ApplyMatrixTransform(g.Transform, true).(DrawPathList)
|
||||
}
|
||||
|
||||
func (g Gradient) GetMatrixTransform() math2.MatrixTransform {
|
||||
return g.Transform
|
||||
func (g Gradient) ApplyMatrixTransform(transform math2.MatrixTransform, applyTranslation bool) Fillable {
|
||||
if transform.IsIdentity() {
|
||||
return g
|
||||
}
|
||||
g2 := g
|
||||
if !applyTranslation {
|
||||
panic("not supported")
|
||||
}
|
||||
g2.Transform = transform.Combine(g2.Transform)
|
||||
return g2
|
||||
}
|
||||
|
||||
func (g Gradient) ApplyColorTransform(transform math2.ColorTransform) Fillable {
|
||||
|
@ -48,11 +48,15 @@ func (g Gradient) ApplyColorTransform(transform math2.ColorTransform) Fillable {
|
|||
Color: transform.ApplyToColor(g.Color),
|
||||
}
|
||||
}
|
||||
return &g2
|
||||
return g2
|
||||
}
|
||||
|
||||
func (g Gradient) Fill(shape Shape) DrawPathList {
|
||||
return g.GetInterpolatedDrawPaths(settings.GlobalSettings.GradientOverlap, settings.GlobalSettings.GradientBlur, settings.GlobalSettings.GradientSlices).Fill(shape)
|
||||
bb := Rectangle[float64]{}
|
||||
if inverse := g.Transform.Inverse(); inverse != nil {
|
||||
bb = shape.ApplyMatrixTransform(*inverse, true).BoundingBox()
|
||||
}
|
||||
return g.GetInterpolatedDrawPaths(settings.GlobalSettings.GradientOverlap, settings.GlobalSettings.GradientBlur, settings.GlobalSettings.GradientSlices, bb).Fill(shape)
|
||||
}
|
||||
|
||||
type GradientItem struct {
|
||||
|
@ -79,7 +83,7 @@ func InterpolateGradient(gradient Gradient, gradientSlices int) (result []Gradie
|
|||
items := gradient.GetItems()
|
||||
//TODO: spread modes
|
||||
|
||||
interpolationMode := gradient.GetInterpolationMode()
|
||||
interpolationMode := gradient.InterpolationMode
|
||||
|
||||
first := items[0]
|
||||
last := items[len(items)-1]
|
||||
|
|
|
@ -6,6 +6,80 @@ import (
|
|||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
|
||||
)
|
||||
|
||||
func interpolateLinearGradient(self Gradient, overlap, blur float64, gradientSlices int, bb Rectangle[float64]) DrawPathList {
|
||||
//items is max size 8 to 15 depending on SWF version
|
||||
|
||||
height0 := GradientBoundsMin.Float64()
|
||||
height1 := GradientBoundsMax.Float64()
|
||||
|
||||
switch self.SpreadMode {
|
||||
case swfsubtypes.GradientSpreadPad:
|
||||
height0 = min(height0, bb.TopLeft.Y)
|
||||
height1 = max(height1, bb.BottomRight.Y)
|
||||
}
|
||||
|
||||
topLeft0 := math.NewVector2(GradientBoundsMin.Float64(), height0)
|
||||
topLeft1 := math.NewVector2(GradientBoundsMax.Float64(), height0)
|
||||
bottomRight0 := math.NewVector2(GradientBoundsMin.Float64(), height1)
|
||||
bottomRight1 := math.NewVector2(GradientBoundsMax.Float64(), height1)
|
||||
|
||||
//TODO: more spreadMode, generalize
|
||||
|
||||
vOverlap := math.NewVector2(overlap, 0).Divide(2)
|
||||
|
||||
items := InterpolateGradient(self, gradientSlices)
|
||||
|
||||
var paths DrawPathList
|
||||
for _, item := range items {
|
||||
if item.Start == 0 {
|
||||
switch self.SpreadMode {
|
||||
case swfsubtypes.GradientSpreadPad:
|
||||
if bb.TopLeft.X < topLeft0.X {
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
Rectangle[float64]{
|
||||
TopLeft: math.NewVector2(bb.TopLeft.X, height0),
|
||||
BottomRight: math.NewVector2(topLeft0.X, height1).AddVector(vOverlap),
|
||||
}.Draw(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
Rectangle[float64]{
|
||||
TopLeft: math.LerpVector2(topLeft0, topLeft1, item.Start).SubVector(vOverlap),
|
||||
BottomRight: math.LerpVector2(bottomRight0, bottomRight1, item.End).AddVector(vOverlap),
|
||||
}.Draw(),
|
||||
))
|
||||
if item.End == 1 {
|
||||
switch self.SpreadMode {
|
||||
case swfsubtypes.GradientSpreadPad:
|
||||
if bb.BottomRight.X > bottomRight1.X {
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
Rectangle[float64]{
|
||||
TopLeft: math.NewVector2(topLeft1.X, height0),
|
||||
BottomRight: math.NewVector2(bb.BottomRight.X, height1).AddVector(vOverlap),
|
||||
}.Draw(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
func LinearGradientFromSWF(records []swfsubtypes.GRADRECORD, transform types.MATRIX, spreadMode swfsubtypes.GradientSpreadMode, interpolationMode swfsubtypes.GradientInterpolationMode) Gradient {
|
||||
items := make([]GradientItem, 0, len(records))
|
||||
for _, r := range records {
|
||||
|
@ -15,31 +89,10 @@ func LinearGradientFromSWF(records []swfsubtypes.GRADRECORD, transform types.MAT
|
|||
//TODO: interpolationMode, spreadMode
|
||||
|
||||
return Gradient{
|
||||
Records: items,
|
||||
//TODO: do we need to scale this to pixel world from twips?
|
||||
Transform: math.MatrixTransformFromSWF(transform),
|
||||
Records: items,
|
||||
Transform: math.MatrixTransformFromSWF(transform, 1),
|
||||
SpreadMode: spreadMode,
|
||||
InterpolationMode: interpolationMode,
|
||||
Interpolation: func(self Gradient, overlap, blur float64, gradientSlices int) DrawPathList {
|
||||
//items is max size 8 to 15 depending on SWF version
|
||||
size := GradientBounds.Width()
|
||||
|
||||
//TODO spreadMode
|
||||
|
||||
var paths DrawPathList
|
||||
for _, item := range InterpolateGradient(self, gradientSlices) {
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
Rectangle[float64]{
|
||||
TopLeft: math.NewVector2(GradientBounds.TopLeft.X+item.Start*size-overlap/2, GradientBounds.TopLeft.Y),
|
||||
BottomRight: math.NewVector2(GradientBounds.TopLeft.X+item.End*size+overlap/2, GradientBounds.BottomRight.Y),
|
||||
}.Draw(),
|
||||
).ApplyMatrixTransform(self.Transform, true))
|
||||
}
|
||||
return paths
|
||||
},
|
||||
Interpolation: interpolateLinearGradient,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,36 @@ import (
|
|||
"git.gammaspectra.live/WeebDataHoarder/swf2ass-go/types/math"
|
||||
)
|
||||
|
||||
func interpolateRadialGradient(self Gradient, overlap, blur float64, gradientSlices int, bb Rectangle[float64]) DrawPathList {
|
||||
//items is max size 8 to 15 depending on SWF version
|
||||
size := GradientBounds.Width()
|
||||
|
||||
//TODO spreadMode
|
||||
|
||||
var paths DrawPathList
|
||||
for _, item := range InterpolateGradient(self, gradientSlices) {
|
||||
//Create concentric circles to cut out a shape
|
||||
var shape Shape
|
||||
radiusStart := (item.Start*size)/2 - overlap/4
|
||||
radiusEnd := (item.End*size)/2 + overlap/4
|
||||
start := NewCircle(math.NewVector2[float64](0, 0), radiusStart).Draw()
|
||||
if radiusStart <= 0 {
|
||||
start = nil
|
||||
}
|
||||
end := NewCircle(math.NewVector2[float64](0, 0), radiusEnd).Draw()
|
||||
shape = append(shape, end...)
|
||||
shape = append(shape, start.Reverse()...)
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
shape,
|
||||
))
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func RadialGradientFromSWF(records []swfsubtypes.GRADRECORD, transform types.MATRIX, spreadMode swfsubtypes.GradientSpreadMode, interpolationMode swfsubtypes.GradientInterpolationMode) Gradient {
|
||||
items := make([]GradientItem, 0, len(records))
|
||||
for _, r := range records {
|
||||
|
@ -17,37 +47,9 @@ func RadialGradientFromSWF(records []swfsubtypes.GRADRECORD, transform types.MAT
|
|||
return Gradient{
|
||||
Records: items,
|
||||
//TODO: do we need to scale this to pixel world from twips?
|
||||
Transform: math.MatrixTransformFromSWF(transform),
|
||||
Transform: math.MatrixTransformFromSWF(transform, 1),
|
||||
SpreadMode: spreadMode,
|
||||
InterpolationMode: interpolationMode,
|
||||
Interpolation: func(self Gradient, overlap, blur float64, gradientSlices int) DrawPathList {
|
||||
//items is max size 8 to 15 depending on SWF version
|
||||
size := GradientBounds.Width()
|
||||
|
||||
//TODO spreadMode
|
||||
|
||||
var paths DrawPathList
|
||||
for _, item := range InterpolateGradient(self, gradientSlices) {
|
||||
//Create concentric circles to cut out a shape
|
||||
var shape Shape
|
||||
radiusStart := (item.Start*size)/2 - overlap/4
|
||||
radiusEnd := (item.End*size)/2 + overlap/4
|
||||
start := NewCircle(math.NewVector2[float64](0, 0), radiusStart).Draw()
|
||||
if radiusStart <= 0 {
|
||||
start = nil
|
||||
}
|
||||
end := NewCircle(math.NewVector2[float64](0, 0), radiusEnd).Draw()
|
||||
shape = append(shape, end...)
|
||||
shape = append(shape, start.Reverse()...)
|
||||
paths = append(paths, DrawPathFill(
|
||||
&FillStyleRecord{
|
||||
Fill: item.Color,
|
||||
Blur: blur,
|
||||
},
|
||||
shape,
|
||||
))
|
||||
}
|
||||
return paths.ApplyMatrixTransform(self.Transform, true)
|
||||
},
|
||||
Interpolation: interpolateRadialGradient,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,20 @@ func (s Shape) Merge(o Shape) (r Shape) {
|
|||
return r
|
||||
}
|
||||
|
||||
func (s Shape) BoundingBox() Rectangle[float64] {
|
||||
rect := Rectangle[float64]{}
|
||||
if len(s) == 0 {
|
||||
return rect
|
||||
}
|
||||
rect.TopLeft, rect.BottomRight = s[0].BoundingBox()
|
||||
for _, r := range s[1:] {
|
||||
tl, br := r.BoundingBox()
|
||||
rect.TopLeft = rect.TopLeft.Min(tl)
|
||||
rect.BottomRight = rect.BottomRight.Max(br)
|
||||
}
|
||||
return rect
|
||||
}
|
||||
|
||||
// Flatten Converts all non-linear records into line segments and returns a new Shape
|
||||
func (s Shape) Flatten() (r Shape) {
|
||||
if s.IsFlat() {
|
||||
|
|
|
@ -16,6 +16,7 @@ type StyleRecord interface {
|
|||
|
||||
type Fillable interface {
|
||||
Fill(shape Shape) DrawPathList
|
||||
ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) Fillable
|
||||
ApplyColorTransform(transform math.ColorTransform) Fillable
|
||||
}
|
||||
|
||||
|
@ -60,14 +61,27 @@ func (r *FillStyleRecord) Flatten(s Shape) DrawPathList {
|
|||
}
|
||||
|
||||
func (r *FillStyleRecord) ApplyMatrixTransform(transform math.MatrixTransform, applyTranslation bool) StyleRecord {
|
||||
fill := r.Fill
|
||||
if color, ok := fill.(math.Color); ok {
|
||||
fill = color
|
||||
} else if fillable, ok := r.Fill.(Fillable); ok {
|
||||
fill = fillable.ApplyMatrixTransform(transform, applyTranslation)
|
||||
} else {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
if r.Border != nil {
|
||||
return &FillStyleRecord{
|
||||
Fill: r.Fill,
|
||||
Fill: fill,
|
||||
Border: r.Border.ApplyMatrixTransform(transform, applyTranslation).(*LineStyleRecord),
|
||||
Blur: r.Blur, //TODO: scale blur?
|
||||
}
|
||||
}
|
||||
return r
|
||||
return &FillStyleRecord{
|
||||
Fill: fill,
|
||||
Border: nil,
|
||||
Blur: r.Blur, //TODO: scale blur?
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FillStyleRecord) ApplyColorTransform(transform math.ColorTransform) StyleRecord {
|
||||
|
|
|
@ -33,7 +33,7 @@ func (d *FontDefinition) ComposeTextSWF(entries []subtypes.GLYPHENTRY, height, y
|
|||
e := d.Entries[g.Index]
|
||||
|
||||
t := math.TranslateTransform(math.NewVector2(xOffset, yOffset)).Multiply(math.ScaleTransform(math.NewVector2(height/d.Scale, height/d.Scale)))
|
||||
for _, dp := range e.List.ApplyMatrixTransform(t, true) {
|
||||
for _, dp := range e.List.ApplyMatrixTransform(t, true).(shapes.DrawPathList) {
|
||||
if _, ok := dp.Style.(*shapes.FillStyleRecord); ok {
|
||||
list = append(list, shapes.DrawPathFill(&shapes.FillStyleRecord{
|
||||
Fill: c,
|
||||
|
@ -142,7 +142,7 @@ func TextDefinitionFromSWF(collection shapes.ObjectCollection, characterId uint1
|
|||
return &TextDefinition{
|
||||
ObjectId: characterId,
|
||||
Bounds: characterBounds,
|
||||
ShapeList: r.ApplyMatrixTransform(math.MatrixTransformFromSWF(matrix), true),
|
||||
ShapeList: r.ApplyMatrixTransform(math.MatrixTransformFromSWF(matrix, 1), true).(shapes.DrawPathList),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,9 +150,13 @@ func FontDefinitionFromSWF[T1 uint16 | uint32, T2 uint8 | uint16](fontId uint16,
|
|||
|
||||
styleList := shapes.StyleList{
|
||||
//TODO: why is this needed????
|
||||
FillStyles: []*shapes.FillStyleRecord{{}},
|
||||
FillStyles: []*shapes.FillStyleRecord{{
|
||||
Fill: math.Color{},
|
||||
}},
|
||||
//TODO: why is this needed????
|
||||
LineStyles: []*shapes.LineStyleRecord{{}},
|
||||
LineStyles: []*shapes.LineStyleRecord{{
|
||||
Color: math.Color{},
|
||||
}},
|
||||
}
|
||||
|
||||
var entries []FontEntry
|
||||
|
|
Loading…
Reference in a new issue