Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<library />` 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
=====
Expand Down
18 changes: 17 additions & 1 deletion src/swf/exporters/animate/AnimateBitmapSymbol.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
155 changes: 155 additions & 0 deletions src/swf/exporters/animate/AnimateLibrary.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,6 +60,11 @@ import openfl.filters.GlowFilter;
private var bitmapClassNames:Map<String, String>;
private var bitmapSymbols:Array<AnimateBitmapSymbol>;
private var frameRate:Float;
#if (lime && !flash && swf_hardware_bitmap_cache)
private var hardwareBitmapData:Map<String, BitmapData>;
private var readableBitmapDataKeys:Map<String, Bool>;
private var readableShapeBitmapData:Map<String, BitmapData>;
#end
private var id:String;
private var instanceID:String;
private var preloading:Bool;
Expand All @@ -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/";
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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(_)
Expand Down Expand Up @@ -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;

Expand Down
Loading
Loading