diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 5e2e928..01d29ea 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -125,6 +125,7 @@ jobs:
working-directory: tests
run: |
haxelib run openfl test neko
+ haxelib run openfl test neko -D swf_hardware_bitmap_cache
unit-test-hashlink:
strategy:
diff --git a/README.md b/README.md
index 03e0e6a..89011fe 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,22 @@ There are three primary code paths within the library:
This library can be called automatically by the OpenFL/Lime command-line tools to process `` tags, or it can be used on the command-line to process SWF files into Animate ZIP files.
+Hardware-only Animate bitmap cache
+==================================
+
+Native hardware-rendered projects may define `swf_hardware_bitmap_cache` to
+share each Animate bitmap asset between bitmap instances. After its texture is
+uploaded, the decoded CPU image is released. Bitmap-filled shapes are probed
+against OpenFL's hardware graphics compatibility rules. Hardware-compatible
+shapes use the same cache, while shapes that fall back to software rendering
+keep one shared readable `BitmapData`. Shapes used directly by scale9 grids are
+also kept readable.
+
+This mode is opt-in because cached bitmap assets used by direct `Bitmap`
+instances become non-readable. Pixel read/write APIs and software-renderer
+fallback are not supported for those bitmaps, and their textures cannot be
+restored after a graphics context is lost.
+
Usage
=====
diff --git a/src/swf/exporters/animate/AnimateBitmapSymbol.hx b/src/swf/exporters/animate/AnimateBitmapSymbol.hx
index 3b5e06e..424eec7 100644
--- a/src/swf/exporters/animate/AnimateBitmapSymbol.hx
+++ b/src/swf/exporters/animate/AnimateBitmapSymbol.hx
@@ -22,9 +22,25 @@ class AnimateBitmapSymbol extends AnimateSymbol
private override function __createObject(library:AnimateLibrary):Bitmap
{
#if lime
- return new Bitmap(BitmapData.fromImage(library.getImage(path)), PixelSnapping.AUTO, smooth != false);
+ return new Bitmap(__createBitmapData(library), PixelSnapping.AUTO, smooth != false);
#else
return null;
#end
}
+
+ private function __createBitmapData(library:AnimateLibrary):BitmapData
+ {
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ return library.__getHardwareBitmapData(this);
+ #else
+ return __createBitmapDataUncached(library);
+ #end
+ }
+
+ @:allow(swf.exporters.animate.AnimateLibrary)
+ private function __createBitmapDataUncached(library:AnimateLibrary):BitmapData
+ {
+ var image = library.getImage(path);
+ return image != null ? BitmapData.fromImage(image) : null;
+ }
}
diff --git a/src/swf/exporters/animate/AnimateLibrary.hx b/src/swf/exporters/animate/AnimateLibrary.hx
index 9aed6ed..2ab7bdd 100644
--- a/src/swf/exporters/animate/AnimateLibrary.hx
+++ b/src/swf/exporters/animate/AnimateLibrary.hx
@@ -5,6 +5,7 @@ import lime.graphics.Image;
import lime.graphics.ImageChannel;
import lime.math.Vector2;
import swf.exporters.core.FilterType;
+import openfl.display.BitmapData;
import openfl.display.MovieClip;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
@@ -59,6 +60,11 @@ import openfl.filters.GlowFilter;
private var bitmapClassNames:Map;
private var bitmapSymbols:Array;
private var frameRate:Float;
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ private var hardwareBitmapData:Map;
+ private var readableBitmapDataKeys:Map;
+ private var readableShapeBitmapData:Map;
+ #end
private var id:String;
private var instanceID:String;
private var preloading:Bool;
@@ -79,6 +85,11 @@ import openfl.filters.GlowFilter;
alphaCheck = new Map();
bitmapClassNames = new Map();
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ hardwareBitmapData = new Map();
+ readableBitmapDataKeys = new Map();
+ readableShapeBitmapData = new Map();
+ #end
#if (ios || tvos)
rootPath = "assets/";
@@ -142,6 +153,140 @@ import openfl.filters.GlowFilter;
return instances.get(uuid);
}
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ @:allow(swf.exporters.animate.AnimateBitmapSymbol)
+ private function __getHardwareBitmapData(symbol:AnimateBitmapSymbol):BitmapData
+ {
+ var key = __getBitmapDataKey(symbol);
+ if (readableBitmapDataKeys.exists(key))
+ {
+ return __getReadableShapeBitmapData(symbol);
+ }
+
+ var bitmapData = hardwareBitmapData.get(key);
+
+ if (bitmapData == null)
+ {
+ bitmapData = symbol.__createBitmapDataUncached(this);
+
+ if (bitmapData != null)
+ {
+ bitmapData.disposeImage();
+ hardwareBitmapData.set(key, bitmapData);
+
+ if (symbol.path != null)
+ {
+ cachedImages.remove(symbol.path);
+ alphaCheck.remove(symbol.path);
+ }
+
+ if (symbol.alpha != null)
+ {
+ cachedImages.remove(symbol.alpha);
+ }
+ }
+ }
+
+ return bitmapData;
+ }
+
+ @:allow(swf.exporters.animate.AnimateShapeSymbol)
+ private function __markBitmapDataReadable(symbol:AnimateBitmapSymbol):Void
+ {
+ if (symbol != null)
+ {
+ readableBitmapDataKeys.set(__getBitmapDataKey(symbol), true);
+ }
+ }
+
+ @:allow(swf.exporters.animate.AnimateShapeSymbol)
+ private function __getReadableShapeBitmapData(symbol:AnimateBitmapSymbol):BitmapData
+ {
+ var key = __getBitmapDataKey(symbol);
+ var bitmapData = readableShapeBitmapData.get(key);
+
+ if (bitmapData == null)
+ {
+ bitmapData = symbol.__createBitmapDataUncached(this);
+
+ if (bitmapData != null)
+ {
+ readableShapeBitmapData.set(key, bitmapData);
+ }
+ }
+
+ return bitmapData;
+ }
+
+ private function __getBitmapDataKey(symbol:AnimateBitmapSymbol):String
+ {
+ return symbol.path != null
+ ? "path:" + symbol.path + "|alpha:" + (symbol.alpha != null ? symbol.alpha : "")
+ : "symbol:" + symbol.id;
+ }
+
+ private function __markScale9GridShapesReadable():Void
+ {
+ for (symbol in symbols)
+ {
+ var spriteSymbol:AnimateSpriteSymbol;
+ #if (haxe_ver >= 4.2)
+ if (!Std.isOfType(symbol, AnimateSpriteSymbol))
+ #else
+ if (!Std.is(symbol, AnimateSpriteSymbol))
+ #end
+ {
+ continue;
+ }
+ else
+ {
+ spriteSymbol = cast symbol;
+ }
+
+ if (spriteSymbol.scale9Grid == null)
+ {
+ continue;
+ }
+
+ for (frame in spriteSymbol.frames)
+ {
+ if (frame.objects == null)
+ {
+ continue;
+ }
+
+ for (frameObject in frame.objects)
+ {
+ var childSymbol = symbols.get(frameObject.symbol);
+ #if (haxe_ver >= 4.2)
+ if (Std.isOfType(childSymbol, AnimateShapeSymbol))
+ #else
+ if (Std.is(childSymbol, AnimateShapeSymbol))
+ #end
+ {
+ cast(childSymbol, AnimateShapeSymbol).requiresReadableBitmapData = true;
+ }
+ }
+ }
+ }
+ }
+
+ private function __prepareBitmapCaches():Void
+ {
+ for (symbol in symbols)
+ {
+ #if (haxe_ver >= 4.2)
+ if (Std.isOfType(symbol, AnimateShapeSymbol))
+ #else
+ if (Std.is(symbol, AnimateShapeSymbol))
+ #end
+ {
+ cast(symbol, AnimateShapeSymbol).__prepareBitmapCache(this);
+ }
+ }
+ }
+ #end
+
#if lime
public override function getImage(id:String):Image
{
@@ -306,6 +451,11 @@ import openfl.filters.GlowFilter;
}
}
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ __markScale9GridShapesReadable();
+ __prepareBitmapCaches();
+ #end
+
// SWFLite.instances.set(instanceID, swf);
__load().onProgress(promise.progress).onError(promise.error).onComplete(function(_)
@@ -448,6 +598,11 @@ import openfl.filters.GlowFilter;
public override function unload():Void
{
instances.remove(uuid);
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ hardwareBitmapData = new Map();
+ readableBitmapDataKeys = new Map();
+ readableShapeBitmapData = new Map();
+ #end
if (symbols == null) return;
// if (swf == null) return;
diff --git a/src/swf/exporters/animate/AnimateShapeSymbol.hx b/src/swf/exporters/animate/AnimateShapeSymbol.hx
index 7f2d1c9..62c7c24 100644
--- a/src/swf/exporters/animate/AnimateShapeSymbol.hx
+++ b/src/swf/exporters/animate/AnimateShapeSymbol.hx
@@ -8,6 +8,9 @@ import openfl.display.JointStyle;
import openfl.display.LineScaleMode;
import openfl.display.Shape;
import openfl.display.SpreadMethod;
+#if (lime && !flash && swf_hardware_bitmap_cache)
+import openfl.display._internal.Context3DGraphics;
+#end
#if !openfl_debug
@:fileXml('tags="haxe,release"')
@@ -20,11 +23,20 @@ import openfl.display.SpreadMethod;
@:access(openfl.display.JointStyle)
@:access(openfl.display.LineScaleMode)
@:access(openfl.display.SpreadMethod)
+#if (lime && !flash && swf_hardware_bitmap_cache)
+@:access(openfl.display._internal.Context3DGraphics)
+#end
class AnimateShapeSymbol extends AnimateSymbol
{
public var commands:Array;
public var rendered:Shape;
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ private static var __probeBitmapData:BitmapData;
+ private var hardwareCompatible:Null;
+ private var requiresReadableBitmapData:Bool;
+ #end
+
public function new()
{
super();
@@ -41,6 +53,58 @@ class AnimateShapeSymbol extends AnimateSymbol
return shape;
}
+ var hardwareBitmapFills = false;
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ __prepareBitmapCache(library);
+ hardwareBitmapFills = !requiresReadableBitmapData && hardwareCompatible == true;
+ #end
+
+ __renderCommands(graphics, library, hardwareBitmapFills, false);
+
+ commands = null;
+ rendered = new Shape();
+ rendered.graphics.copyFrom(shape.graphics);
+
+ return shape;
+ }
+
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ @:allow(swf.exporters.animate.AnimateLibrary)
+ private function __prepareBitmapCache(library:AnimateLibrary):Void
+ {
+ if (hardwareCompatible == null)
+ {
+ var probe = new Shape();
+ __renderCommands(probe.graphics, library, false, true);
+ hardwareCompatible = Context3DGraphics.isCompatible(probe.graphics);
+ }
+
+ if (requiresReadableBitmapData || hardwareCompatible != true)
+ {
+ __markBitmapFillsReadable(library);
+ }
+ }
+
+ private function __markBitmapFillsReadable(library:AnimateLibrary):Void
+ {
+ if (commands != null)
+ {
+ for (command in commands)
+ {
+ switch (command)
+ {
+ case BeginBitmapFill(bitmapID, _, _, _):
+ library.__markBitmapDataReadable(cast library.symbols.get(bitmapID));
+
+ default:
+ }
+ }
+ }
+ }
+ #end
+
+ private function __renderCommands(graphics:openfl.display.Graphics, library:AnimateLibrary, hardwareBitmapFills:Bool, probeBitmapFills:Bool):Void
+ {
for (command in commands)
{
switch (command)
@@ -49,15 +113,7 @@ class AnimateShapeSymbol extends AnimateSymbol
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);
-
- if (bitmap != null)
- {
- graphics.beginBitmapFill(BitmapData.fromImage(bitmap), matrix, repeat, smooth);
- }
- #end
+ __beginBitmapFill(graphics, library, bitmapID, matrix, repeat, smooth, hardwareBitmapFills, probeBitmapFills);
case BeginGradientFill(fillType, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio):
#if flash
@@ -90,11 +146,42 @@ class AnimateShapeSymbol extends AnimateSymbol
graphics.moveTo(x, y);
}
}
+ }
- commands = null;
- rendered = new Shape();
- rendered.graphics.copyFrom(shape.graphics);
+ private static function __beginBitmapFill(graphics:openfl.display.Graphics, library:AnimateLibrary, bitmapID:Int, matrix:openfl.geom.Matrix, repeat:Bool,
+ smooth:Bool, hardwareBitmapFill:Bool, probeBitmapFill:Bool):Void
+ {
+ #if lime
+ var bitmapSymbol:AnimateBitmapSymbol = cast library.symbols.get(bitmapID);
+ #if (!flash && swf_hardware_bitmap_cache)
+ var bitmapData:BitmapData;
+ if (probeBitmapFill)
+ {
+ if (__probeBitmapData == null)
+ {
+ __probeBitmapData = new BitmapData(1, 1, true, 0xFFFFFFFF);
+ }
+ bitmapData = __probeBitmapData;
+ }
+ else if (hardwareBitmapFill)
+ {
+ bitmapData = library.__getHardwareBitmapData(bitmapSymbol);
+ }
+ else
+ {
+ // Shapes that fall back to Cairo, including scale9Grid shapes,
+ // still need CPU pixels.
+ bitmapData = library.__getReadableShapeBitmapData(bitmapSymbol);
+ }
+ #else
+ var bitmap = library.getImage(bitmapSymbol.path);
+ var bitmapData = bitmap != null ? BitmapData.fromImage(bitmap) : null;
+ #end
- return shape;
+ if (bitmapData != null)
+ {
+ graphics.beginBitmapFill(bitmapData, matrix, repeat, smooth);
+ }
+ #end
}
}
diff --git a/tests/src/TestMain.hx b/tests/src/TestMain.hx
index 99caed6..835f63c 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.AnimateBitmapSymbolTest());
runner.addCase(new tests.ShapesTest());
Report.create(runner);
diff --git a/tests/src/tests/AnimateBitmapSymbolTest.hx b/tests/src/tests/AnimateBitmapSymbolTest.hx
new file mode 100644
index 0000000..185489b
--- /dev/null
+++ b/tests/src/tests/AnimateBitmapSymbolTest.hx
@@ -0,0 +1,242 @@
+package tests;
+
+import lime.graphics.Image;
+import openfl.display.BitmapData;
+import openfl.geom.Rectangle;
+import swf.exporters.animate.AnimateBitmapSymbol;
+import swf.exporters.animate.AnimateFrame;
+import swf.exporters.animate.AnimateFrameObject;
+import swf.exporters.animate.AnimateLibrary;
+import swf.exporters.animate.AnimateShapeCommand;
+import swf.exporters.animate.AnimateShapeSymbol;
+import swf.exporters.animate.AnimateSpriteSymbol;
+import utest.Assert;
+import utest.Test;
+
+@:access(lime.utils.AssetLibrary)
+@:access(openfl.display.BitmapData)
+@:access(openfl.display.Graphics)
+@:access(openfl.display._internal.DrawCommandBuffer)
+@:access(swf.exporters.animate.AnimateBitmapSymbol)
+@:access(swf.exporters.animate.AnimateLibrary)
+@:access(swf.exporters.animate.AnimateShapeSymbol)
+class AnimateBitmapSymbolTest extends Test
+{
+ public function testHardwareBitmapCacheIsOptIn():Void
+ {
+ var library = new AnimateLibrary("bitmap-test", "bitmap-test");
+ library.bitmapSymbols = [];
+ var image = new Image(null, 0, 0, 32, 32, 0xFF336699);
+ var symbol = new AnimateBitmapSymbol();
+ symbol.id = 1;
+ symbol.path = "bitmap.png";
+ library.cachedImages.set(symbol.path, image);
+
+ var first = symbol.__createBitmapData(library);
+ var second = symbol.__createBitmapData(library);
+
+ Assert.notNull(first);
+ Assert.notNull(second);
+
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ Assert.isTrue(first == second);
+ Assert.isFalse(first.readable);
+ Assert.isFalse(library.cachedImages.exists(symbol.path));
+ Assert.isTrue(first.image == image);
+ #else
+ Assert.isFalse(first == second);
+ Assert.isTrue(first.readable);
+ Assert.isTrue(second.readable);
+ Assert.isTrue(library.cachedImages.exists(symbol.path));
+ #end
+ }
+
+ public function testHardwareCompatibleShapeBitmapFillsUseHardwareCache():Void
+ {
+ var library = new AnimateLibrary("shape-bitmap-test", "shape-bitmap-test");
+ library.bitmapSymbols = [];
+ library.symbols = new Map();
+ var image = new Image(null, 0, 0, 32, 32, 0xFF336699);
+ var bitmapSymbol = new AnimateBitmapSymbol();
+ bitmapSymbol.id = 1;
+ bitmapSymbol.path = "shape-bitmap.png";
+ library.cachedImages.set(bitmapSymbol.path, image);
+ library.symbols.set(bitmapSymbol.id, bitmapSymbol);
+
+ var shapeSymbol = new AnimateShapeSymbol();
+ shapeSymbol.commands = [
+ BeginBitmapFill(bitmapSymbol.id, null, true, true),
+ MoveTo(0, 0),
+ LineTo(32, 0),
+ LineTo(32, 32),
+ LineTo(0, 32),
+ EndFill
+ ];
+
+ var shape = shapeSymbol.__createObject(library);
+ var bitmapData:BitmapData = cast shape.graphics.__commands.o[0];
+ Assert.notNull(bitmapData);
+
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ Assert.isFalse(bitmapData.readable);
+ var secondShape = shapeSymbol.__createObject(library);
+ var secondShapeBitmapData:BitmapData = cast secondShape.graphics.__commands.o[0];
+ Assert.isTrue(bitmapData == secondShapeBitmapData);
+
+ var directBitmapData = bitmapSymbol.__createBitmapData(library);
+ Assert.isFalse(directBitmapData.readable);
+ Assert.isTrue(bitmapData == directBitmapData);
+ Assert.isFalse(library.cachedImages.exists(bitmapSymbol.path));
+ #else
+ Assert.isTrue(bitmapData.readable);
+ Assert.isFalse(bitmapData == bitmapSymbol.__createBitmapData(library));
+ Assert.isTrue(library.cachedImages.exists(bitmapSymbol.path));
+ #end
+ }
+
+ public function testSoftwareShapeBitmapFillsStayReadableAndShared():Void
+ {
+ var library = new AnimateLibrary("software-shape-bitmap-test", "software-shape-bitmap-test");
+ library.bitmapSymbols = [];
+ library.symbols = new Map();
+ var image = new Image(null, 0, 0, 32, 32, 0xFF336699);
+ var bitmapSymbol = new AnimateBitmapSymbol();
+ bitmapSymbol.id = 1;
+ bitmapSymbol.path = "software-shape-bitmap.png";
+ library.cachedImages.set(bitmapSymbol.path, image);
+ library.symbols.set(bitmapSymbol.id, bitmapSymbol);
+
+ var shapeSymbol = new AnimateShapeSymbol();
+ shapeSymbol.commands = [
+ BeginBitmapFill(bitmapSymbol.id, null, true, true),
+ MoveTo(0, 0),
+ LineTo(32, 0),
+ LineTo(32, 32),
+ LineTo(0, 32),
+ EndFill,
+ BeginGradientFill(0, [0x000000, 0xFFFFFF], [1.0, 1.0], [0, 255], null, 0, 0, 0),
+ MoveTo(0, 0),
+ LineTo(1, 0),
+ LineTo(1, 1),
+ LineTo(0, 1),
+ EndFill
+ ];
+
+ var shape = shapeSymbol.__createObject(library);
+ var bitmapData:BitmapData = cast shape.graphics.__commands.o[0];
+ Assert.notNull(bitmapData);
+
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ Assert.isTrue(bitmapData.readable);
+ var secondShape = shapeSymbol.__createObject(library);
+ var secondShapeBitmapData:BitmapData = cast secondShape.graphics.__commands.o[0];
+ Assert.isTrue(bitmapData == secondShapeBitmapData);
+ Assert.isTrue(bitmapData.image == image);
+
+ var directBitmapData = bitmapSymbol.__createBitmapData(library);
+ Assert.isTrue(directBitmapData.readable);
+ Assert.isTrue(bitmapData == directBitmapData);
+ Assert.isTrue(library.cachedImages.exists(bitmapSymbol.path));
+ #else
+ Assert.isTrue(bitmapData.readable);
+ #end
+ }
+
+ public function testBitmapSharedByHardwareAndSoftwareShapesStaysReadable():Void
+ {
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ var library = new AnimateLibrary("shared-shape-bitmap-test", "shared-shape-bitmap-test");
+ library.bitmapSymbols = [];
+ library.symbols = new Map();
+ var image = new Image(null, 0, 0, 32, 32, 0xFF336699);
+ var bitmapSymbol = new AnimateBitmapSymbol();
+ bitmapSymbol.id = 1;
+ bitmapSymbol.path = "shared-shape-bitmap.png";
+ library.cachedImages.set(bitmapSymbol.path, image);
+ library.symbols.set(bitmapSymbol.id, bitmapSymbol);
+
+ var hardwareShapeSymbol = new AnimateShapeSymbol();
+ hardwareShapeSymbol.id = 2;
+ hardwareShapeSymbol.commands = [
+ BeginBitmapFill(bitmapSymbol.id, null, true, true),
+ MoveTo(0, 0),
+ LineTo(32, 0),
+ LineTo(32, 32),
+ LineTo(0, 32),
+ EndFill
+ ];
+ library.symbols.set(hardwareShapeSymbol.id, hardwareShapeSymbol);
+
+ var softwareShapeSymbol = new AnimateShapeSymbol();
+ softwareShapeSymbol.id = 3;
+ softwareShapeSymbol.commands = [
+ BeginBitmapFill(bitmapSymbol.id, null, true, true),
+ BeginGradientFill(0, [0x000000, 0xFFFFFF], [1.0, 1.0], [0, 255], null, 0, 0, 0),
+ EndFill
+ ];
+ library.symbols.set(softwareShapeSymbol.id, softwareShapeSymbol);
+
+ // Cache preparation must make the result independent of which shape is
+ // instantiated first.
+ library.__prepareBitmapCaches();
+
+ var hardwareShape = hardwareShapeSymbol.__createObject(library);
+ var hardwareBitmapData:BitmapData = cast hardwareShape.graphics.__commands.o[0];
+ var softwareShape = softwareShapeSymbol.__createObject(library);
+ var softwareBitmapData:BitmapData = cast softwareShape.graphics.__commands.o[0];
+
+ Assert.isTrue(hardwareBitmapData.readable);
+ Assert.isTrue(hardwareBitmapData == softwareBitmapData);
+ Assert.isTrue(hardwareBitmapData.image == image);
+ Assert.isTrue(library.cachedImages.exists(bitmapSymbol.path));
+ #else
+ Assert.isTrue(true);
+ #end
+ }
+
+ public function testScale9GridShapeBitmapFillsStayReadable():Void
+ {
+ #if (lime && !flash && swf_hardware_bitmap_cache)
+ var library = new AnimateLibrary("scale9-shape-bitmap-test", "scale9-shape-bitmap-test");
+ library.bitmapSymbols = [];
+ library.symbols = new Map();
+ var image = new Image(null, 0, 0, 32, 32, 0xFF336699);
+ var bitmapSymbol = new AnimateBitmapSymbol();
+ bitmapSymbol.id = 1;
+ bitmapSymbol.path = "scale9-shape-bitmap.png";
+ library.cachedImages.set(bitmapSymbol.path, image);
+ library.symbols.set(bitmapSymbol.id, bitmapSymbol);
+
+ var shapeSymbol = new AnimateShapeSymbol();
+ shapeSymbol.id = 2;
+ shapeSymbol.commands = [
+ BeginBitmapFill(bitmapSymbol.id, null, true, true),
+ MoveTo(0, 0),
+ LineTo(32, 0),
+ LineTo(32, 32),
+ LineTo(0, 32),
+ EndFill
+ ];
+ library.symbols.set(shapeSymbol.id, shapeSymbol);
+
+ var frameObject = new AnimateFrameObject();
+ frameObject.symbol = shapeSymbol.id;
+ var frame = new AnimateFrame();
+ frame.objects = [frameObject];
+ var spriteSymbol = new AnimateSpriteSymbol();
+ spriteSymbol.id = 3;
+ spriteSymbol.scale9Grid = new Rectangle(8, 8, 16, 16);
+ spriteSymbol.frames = [frame];
+ library.symbols.set(spriteSymbol.id, spriteSymbol);
+
+ library.__markScale9GridShapesReadable();
+ var shape = shapeSymbol.__createObject(library);
+ var bitmapData:BitmapData = cast shape.graphics.__commands.o[0];
+
+ Assert.isTrue(bitmapData.readable);
+ Assert.isTrue(bitmapData.image == image);
+ #else
+ Assert.isTrue(true);
+ #end
+ }
+}