diff --git a/src/swf/exporters/animate/AnimateLibrary.hx b/src/swf/exporters/animate/AnimateLibrary.hx index 9aed6ed..39f6761 100644 --- a/src/swf/exporters/animate/AnimateLibrary.hx +++ b/src/swf/exporters/animate/AnimateLibrary.hx @@ -687,48 +687,7 @@ import openfl.filters.GlowFilter; { var symbol = new AnimateShapeSymbol(); symbol.id = data.id; - symbol.commands = []; - - var data:Array = data.commands; - var commands = symbol.commands; - var i = 0; - - while (i < data.length) - { - switch (data[i]) - { - case BEGIN_BITMAP_FILL: - commands.push(BeginBitmapFill(data[i + 1], __parseMatrix(data[i + 2]), data[i + 3], data[i + 4])); - i += 5; - case BEGIN_FILL: - commands.push(BeginFill(data[i + 1], data[i + 2])); - i += 3; - case BEGIN_GRADIENT_FILL: - commands.push(BeginGradientFill(data[i + 1], data[i + 2], data[i + 3], data[i + 4], __parseMatrix(data[i + 5]), data[i + 6], data[i + 7], - data[i + 8])); - i += 9; - case CLEAR_LINE_STYLE: - commands.push(LineStyle(null, null, null, null, null, null, null, null)); - i++; - case CURVE_TO: - commands.push(CurveTo(__pixel(data[i + 1]), __pixel(data[i + 2]), __pixel(data[i + 3]), __pixel(data[i + 4]))); - i += 5; - case END_FILL: - commands.push(EndFill); - i++; - case LINE_STYLE: - commands.push(LineStyle(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6], data[i + 7], data[i + 8])); - i += 9; - case LINE_TO: - commands.push(LineTo(__pixel(data[i + 1]), __pixel(data[i + 2]))); - i += 3; - case MOVE_TO: - commands.push(MoveTo(__pixel(data[i + 1]), __pixel(data[i + 2]))); - i += 3; - default: - i++; - } - } + symbol.__setCompactCommands(data.commands); return symbol; } diff --git a/src/swf/exporters/animate/AnimateShapeSymbol.hx b/src/swf/exporters/animate/AnimateShapeSymbol.hx index 7f2d1c9..2a4ada6 100644 --- a/src/swf/exporters/animate/AnimateShapeSymbol.hx +++ b/src/swf/exporters/animate/AnimateShapeSymbol.hx @@ -8,6 +8,7 @@ import openfl.display.JointStyle; import openfl.display.LineScaleMode; import openfl.display.Shape; import openfl.display.SpreadMethod; +import swf.exporters.animate.AnimateLibrary.SWFShapeCommandType; #if !openfl_debug @:fileXml('tags="haxe,release"') @@ -25,6 +26,8 @@ class AnimateShapeSymbol extends AnimateSymbol public var commands:Array; public var rendered:Shape; + private var compactCommands:Array; + public function new() { super(); @@ -41,38 +44,260 @@ class AnimateShapeSymbol extends AnimateSymbol return shape; } - for (command in commands) + if (compactCommands != null) + { + __renderCompactCommands(graphics, library); + } + else if (commands != null) { - switch (command) + for (command in commands) { - case BeginFill(color, alpha): - graphics.beginFill(color, alpha); + switch (command) + { + case BeginFill(color, alpha): + graphics.beginFill(color, alpha); - case BeginBitmapFill(bitmapID, matrix, repeat, smooth): - #if lime - var bitmapSymbol:AnimateBitmapSymbol = cast library.symbols.get(bitmapID); - var bitmap = library.getImage(bitmapSymbol.path); + case BeginBitmapFill(bitmapID, matrix, repeat, smooth): + __beginBitmapFill(graphics, library, bitmapID, matrix, repeat, smooth); - if (bitmap != null) - { - graphics.beginBitmapFill(BitmapData.fromImage(bitmap), matrix, repeat, smooth); - } - #end + case BeginGradientFill(fillType, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): + #if flash + var colors:Array = cast colors; + #end + graphics.beginGradientFill(GradientType.fromInt(fillType), colors, alphas, ratios, matrix, SpreadMethod.fromInt(spreadMethod), + InterpolationMethod.fromInt(interpolationMethod), focalPointRatio); + + case CurveTo(controlX, controlY, anchorX, anchorY): + graphics.curveTo(controlX, controlY, anchorX, anchorY); + + case EndFill: + graphics.endFill(); + + case LineStyle(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit): + if (thickness != null) + { + graphics.lineStyle(thickness, color, alpha, pixelHinting, LineScaleMode.fromInt(scaleMode), CapsStyle.fromInt(caps), + JointStyle.fromInt(joints), miterLimit); + } + else + { + graphics.lineStyle(); + } + + case LineTo(x, y): + graphics.lineTo(x, y); + + case MoveTo(x, y): + graphics.moveTo(x, y); + } + } + } + + commands = null; + compactCommands = null; + rendered = new Shape(); + rendered.graphics.copyFrom(shape.graphics); + + return shape; + } + + private static function __beginBitmapFill(graphics:openfl.display.Graphics, library:AnimateLibrary, bitmapID:Int, matrix:openfl.geom.Matrix, repeat:Bool, + smooth:Bool):Void + { + #if lime + var bitmapSymbol:AnimateBitmapSymbol = cast library.symbols.get(bitmapID); + var bitmap = library.getImage(bitmapSymbol.path); + + if (bitmap != null) + { + graphics.beginBitmapFill(BitmapData.fromImage(bitmap), matrix, repeat, smooth); + } + #end + } + + @:allow(swf.exporters.animate.AnimateLibrary) + private function __setCompactCommands(data:Array):Void + { + compactCommands = []; + var i = 0; + + while (i < data.length) + { + var type:SWFShapeCommandType = data[i]; + compactCommands.push(cast type); + + switch (type) + { + case BEGIN_BITMAP_FILL: + compactCommands.push(data[i + 1]); + __encodeMatrix(compactCommands, data[i + 2]); + compactCommands.push(data[i + 3] ? 1 : 0); + compactCommands.push(data[i + 4] ? 1 : 0); + i += 5; + + case BEGIN_FILL: + compactCommands.push(data[i + 1]); + compactCommands.push(data[i + 2]); + i += 3; + + case BEGIN_GRADIENT_FILL: + __encodeNullableNumber(compactCommands, data[i + 1]); + __encodeArray(compactCommands, data[i + 2]); + __encodeArray(compactCommands, data[i + 3]); + __encodeArray(compactCommands, data[i + 4]); + __encodeMatrix(compactCommands, data[i + 5]); + __encodeNullableNumber(compactCommands, data[i + 6]); + __encodeNullableNumber(compactCommands, data[i + 7]); + compactCommands.push(data[i + 8]); + i += 9; + + case CLEAR_LINE_STYLE: + i++; + + case CURVE_TO: + compactCommands.push(data[i + 1]); + compactCommands.push(data[i + 2]); + compactCommands.push(data[i + 3]); + compactCommands.push(data[i + 4]); + i += 5; + + case END_FILL: + i++; + + case LINE_STYLE: + __encodeNullableNumber(compactCommands, data[i + 1]); + __encodeNullableNumber(compactCommands, data[i + 2]); + __encodeNullableNumber(compactCommands, data[i + 3]); + __encodeNullableBool(compactCommands, data[i + 4]); + __encodeNullableNumber(compactCommands, data[i + 5]); + __encodeNullableNumber(compactCommands, data[i + 6]); + __encodeNullableNumber(compactCommands, data[i + 7]); + __encodeNullableNumber(compactCommands, data[i + 8]); + i += 9; + + case LINE_TO, MOVE_TO: + compactCommands.push(data[i + 1]); + compactCommands.push(data[i + 2]); + i += 3; + + default: + i++; + } + } + } + + private static function __encodeArray(output:Array, values:Array):Void + { + if (values == null) + { + output.push(-1); + return; + } + + output.push(values.length); + for (value in values) + { + output.push(value); + } + } + + private static function __encodeMatrix(output:Array, values:Array):Void + { + if (values == null) + { + output.push(0); + return; + } + + output.push(1); + for (i in 0...6) + { + output.push(values[i]); + } + } + + private static function __encodeNullableBool(output:Array, value:Dynamic):Void + { + output.push(value == null ? Math.NaN : (value ? 1 : 0)); + } + + private static function __encodeNullableNumber(output:Array, value:Dynamic):Void + { + output.push(value == null ? Math.NaN : value); + } + + private function __renderCompactCommands(graphics:openfl.display.Graphics, library:AnimateLibrary):Void + { + var data = compactCommands; + var position = 0; + + while (position < data.length) + { + var type:SWFShapeCommandType = cast Std.int(data[position++]); + + switch (type) + { + case BEGIN_BITMAP_FILL: + var bitmapID = Std.int(data[position++]); + var matrix = __decodeMatrix(data, position); + position += data[position] == 0 ? 1 : 7; + var repeat = data[position++] != 0; + var smooth = data[position++] != 0; + __beginBitmapFill(graphics, library, bitmapID, matrix, repeat, smooth); + + case BEGIN_FILL: + graphics.beginFill(Std.int(data[position++]), data[position++]); + + case BEGIN_GRADIENT_FILL: + var value = data[position++]; + var fillType:Null = Math.isNaN(value) ? null : Std.int(value); + var colors = __decodeIntArray(data, position); + position += 1 + (colors == null ? 0 : colors.length); + var alphas = __decodeFloatArray(data, position); + position += 1 + (alphas == null ? 0 : alphas.length); + var ratios = __decodeIntArray(data, position); + position += 1 + (ratios == null ? 0 : ratios.length); + var matrix = __decodeMatrix(data, position); + position += data[position] == 0 ? 1 : 7; + value = data[position++]; + var spreadMethod:Null = Math.isNaN(value) ? null : Std.int(value); + value = data[position++]; + var interpolationMethod:Null = Math.isNaN(value) ? null : Std.int(value); + var focalPointRatio = data[position++]; - case BeginGradientFill(fillType, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): #if flash - var colors:Array = cast colors; + var flashColors:Array = cast colors; #end - graphics.beginGradientFill(GradientType.fromInt(fillType), colors, alphas, ratios, matrix, SpreadMethod.fromInt(spreadMethod), - InterpolationMethod.fromInt(interpolationMethod), focalPointRatio); + graphics.beginGradientFill(GradientType.fromInt(fillType), #if flash flashColors #else colors #end, alphas, ratios, matrix, + SpreadMethod.fromInt(spreadMethod), InterpolationMethod.fromInt(interpolationMethod), focalPointRatio); + + case CLEAR_LINE_STYLE: + graphics.lineStyle(); - case CurveTo(controlX, controlY, anchorX, anchorY): - graphics.curveTo(controlX, controlY, anchorX, anchorY); + case CURVE_TO: + graphics.curveTo(data[position++] / 20, data[position++] / 20, data[position++] / 20, data[position++] / 20); - case EndFill: + case END_FILL: graphics.endFill(); - case LineStyle(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit): + case LINE_STYLE: + var value = data[position++]; + var thickness:Null = Math.isNaN(value) ? null : value; + value = data[position++]; + var color:Null = Math.isNaN(value) ? null : Std.int(value); + value = data[position++]; + var alpha:Null = Math.isNaN(value) ? null : value; + value = data[position++]; + var pixelHinting:Null = Math.isNaN(value) ? null : value != 0; + value = data[position++]; + var scaleMode:Null = Math.isNaN(value) ? null : Std.int(value); + value = data[position++]; + var caps:Null = Math.isNaN(value) ? null : Std.int(value); + value = data[position++]; + var joints:Null = Math.isNaN(value) ? null : Std.int(value); + value = data[position++]; + var miterLimit:Null = Math.isNaN(value) ? null : value; + if (thickness != null) { graphics.lineStyle(thickness, color, alpha, pixelHinting, LineScaleMode.fromInt(scaleMode), CapsStyle.fromInt(caps), @@ -83,18 +308,45 @@ class AnimateShapeSymbol extends AnimateSymbol graphics.lineStyle(); } - case LineTo(x, y): - graphics.lineTo(x, y); + case LINE_TO: + graphics.lineTo(data[position++] / 20, data[position++] / 20); - case MoveTo(x, y): - graphics.moveTo(x, y); + case MOVE_TO: + graphics.moveTo(data[position++] / 20, data[position++] / 20); } } + } - commands = null; - rendered = new Shape(); - rendered.graphics.copyFrom(shape.graphics); + private static function __decodeFloatArray(data:Array, position:Int):Array + { + var length = Std.int(data[position++]); + if (length < 0) return null; - return shape; + var result = []; + for (i in 0...length) + { + result.push(data[position + i]); + } + return result; + } + + private static function __decodeIntArray(data:Array, position:Int):Array + { + var length = Std.int(data[position++]); + if (length < 0) return null; + + var result = []; + for (i in 0...length) + { + result.push(Std.int(data[position + i])); + } + return result; + } + + private static function __decodeMatrix(data:Array, position:Int):openfl.geom.Matrix + { + if (data[position++] == 0) return null; + return new openfl.geom.Matrix(data[position], data[position + 1], data[position + 2], data[position + 3], data[position + 4] / 20, + data[position + 5] / 20); } } diff --git a/src/swf/exporters/animate/AnimateTimeline.hx b/src/swf/exporters/animate/AnimateTimeline.hx index dbe2cca..20e7dea 100644 --- a/src/swf/exporters/animate/AnimateTimeline.hx +++ b/src/swf/exporters/animate/AnimateTimeline.hx @@ -249,8 +249,12 @@ class AnimateTimeline extends Timeline { if (targetDepth > mask.depth && targetDepth <= mask.clipDepth) { - #if (openfl >= "9.5.0" && !flash) + #if (openfl >= "9.5.0") + #if flash + child.mask = mask.displayObject; + #else child.clippingLayer = mask.displayObject; + #end #else child.mask = mask.displayObject; #end diff --git a/src/swf/exporters/swflite/timeline/SymbolTimeline.hx b/src/swf/exporters/swflite/timeline/SymbolTimeline.hx index ed1e8d2..37b3e18 100644 --- a/src/swf/exporters/swflite/timeline/SymbolTimeline.hx +++ b/src/swf/exporters/swflite/timeline/SymbolTimeline.hx @@ -360,8 +360,12 @@ class SymbolTimeline extends Timeline { if (targetDepth > mask.depth && targetDepth <= mask.clipDepth) { - #if (openfl >= "9.5.0" && !flash) + #if (openfl >= "9.5.0") + #if flash + child.mask = mask.displayObject; + #else child.clippingLayer = mask.displayObject; + #end #else child.mask = mask.displayObject; #end diff --git a/tests/src/TestMain.hx b/tests/src/TestMain.hx index 99caed6..7a1f16f 100644 --- a/tests/src/TestMain.hx +++ b/tests/src/TestMain.hx @@ -9,6 +9,7 @@ class TestMain extends Sprite super(); var runner = new Runner(); + runner.addCase(new tests.AnimateShapeSymbolTest()); runner.addCase(new tests.ShapesTest()); Report.create(runner); diff --git a/tests/src/tests/AnimateShapeSymbolTest.hx b/tests/src/tests/AnimateShapeSymbolTest.hx new file mode 100644 index 0000000..4a0a743 --- /dev/null +++ b/tests/src/tests/AnimateShapeSymbolTest.hx @@ -0,0 +1,56 @@ +package tests; + +import openfl.geom.Matrix; +import swf.exporters.animate.AnimateLibrary; +import swf.exporters.animate.AnimateShapeCommand; +import swf.exporters.animate.AnimateShapeSymbol; +import utest.Assert; +import utest.Test; + +@:access(swf.exporters.animate.AnimateShapeSymbol) +class AnimateShapeSymbolTest extends Test +{ + public function testCompactCommandsRenderLikeLegacyCommands():Void + { + var colors = [0x123456, 0xABCDEF]; + var alphas = [1.0, 0.25]; + var ratios = [0, 255]; + var matrix = new Matrix(1, 0.25, -0.5, 2, 10, 20); + + var legacy = new AnimateShapeSymbol(); + legacy.commands = [ + BeginFill(0x102030, 0.75), + MoveTo(1, 2), + LineStyle(2.5, 0x405060, 0.5, true, 2, 1, 2, 3), + LineTo(3, 4), + CurveTo(5, 6, 7, 8), + LineStyle(null, null, null, null, null, null, null, null), + BeginGradientFill(0, colors, alphas, ratios, matrix, 1, 0, 0.25), + EndFill + ]; + + var compact = new AnimateShapeSymbol(); + var data:Array = [ + 1, 0x102030, 0.75, + 8, 20, 40, + 6, 2.5, 0x405060, 0.5, true, 2, 1, 2, 3, + 7, 60, 80, + 4, 100, 120, 140, 160, + 3, + 2, 0, colors, alphas, ratios, [1, 0.25, -0.5, 2, 200, 400], 1, 0, 0.25, + 5 + ]; + compact.__setCompactCommands(data); + + var library = new AnimateLibrary("test", "test"); + var legacyShape = legacy.__createObject(library); + var compactShape = compact.__createObject(library); + var cachedCompactShape = compact.__createObject(library); + + Assert.same(legacyShape.graphics.readGraphicsData(false), compactShape.graphics.readGraphicsData(false)); + Assert.same(legacyShape.graphics.readGraphicsData(false), cachedCompactShape.graphics.readGraphicsData(false)); + Assert.same(legacyShape.getBounds(legacyShape), compactShape.getBounds(compactShape)); + Assert.isNull(compact.commands); + Assert.isNull(compact.compactCommands); + } +}