Issue with embedded BitmapData

I believe preloader is broken again for native, I see black screen instead of my image on Android.
Lime 4.0.3
OpenFL 4.9.2
Problem with @:bitmap?
Any fix?

Another bug is that Event.COMPLETE fires twice in Preloader, if I do event.preventDefault().
This is happening on all platforms, even Flash.

Can you verify using trace if your preloader is displaying, or could you use a different background color for the preloader, and see if the preloader is not displaying, or if it is an issue with embedding images during the preload step?

OK, I’ve verified with trace and background change, my preloader is displaying, but not my bitmap.
If I do like this in my preloader, I see green color:

graphics.beginFill(0x00FF00);
graphics.drawRect(0, 0, w, h);

Event.COMPLETE fires only once on Android, but twice on Flash, if I do event.preventDefault().

Perhaps it would fire once if you try putting the start call here inside the preloadComplete check?

Otherwise we can handle a double-call of start() on the OpenFL Preloader side

Putting start(); inside preloadComplete fixed the problem.
Any ideas about bitmap?

Embedded assets were moved into the ManifestResources class, which should be initialized before the preloader is created.

Does tyour embedded image display on any platform? (native, HTML5, Flash?)

Does it instantiate properly, or is there an error with it not being available?

Works fine on Flash, doesn’t work on Android and Windows.
No errors, it worked before on every platform and I don’t remember making any changes to my code.

The following test worked for me. Could you give it a try?

1.) Create a copy of the “CustomPreloader” sample

openfl create CustomPreloader

2.) Edit “CustomPreloader/Source/Preloader.hx” and add the following code near the top, after the imports, before the class declaration

@:bitmap("../Assets/openfl.png") class OpenFLGraphic extends openfl.display.BitmapData {}

3.) Add the following code in the class constructor

		var bitmapData = new OpenFLGraphic (0, 0);
		var bitmap = new openfl.display.Bitmap (bitmapData);
		addChild (bitmap);

4.) Run the sample

cd CustomPreloader
openfl test flash -Dsimulate-preloader
openfl test neko -Dsimulate-preloader

Ok, your example works, but this one does not work for native, why?

var bitmapData = new OpenFLGraphic (0, 0);
var bitmap = new openfl.display.Bitmap (bitmapData);
bitmap.width = 300;
bitmap.height = 300;

Okay, I found the root cause. This opens up a broader, more complicated issue that I would be interested to hear your feedback on…

new OpenFLGraphic currently fetches bytes stored in haxe.Resource, then turns this into a Lime Image before setting the bitmapData.width and bitmapData.height

We can perform an image decode synchronously on native platforms, but in the browser load a base64 image is asynchronous.

The current implementation allows you to use new OpenFLGraphic then put it in a Bitmap and have it work as soon as that image is loaded, but accessing the pixels in the BitmapData, or in this case, having the correct width and height for the image wait until decoding is complete.

One perspective would be to use a preload process, where every embedded bitmap is instantiated at runtime (before the preloader), so it will be available instantly later. There are a number of problems with this, since you may want multiple instances, or you may have many different embedded assets, and might not want them all to be created in advance. The alternative is a different API that works better with an asynchronous load.

Off the top of my head, something like:

Assets.loadBitmapData ("embedded:OpenFLGraphic").onComplete (function (bitmapData) {
    
    var bitmap = new Bitmap (bitmapData);
    bitmap.width = 300;
    bitmap.height = 300;
    
});

I’m not sure, but ultimately it’s a broader discussion. I know how I can change it so that on native, your above sample will work, but HTML5 will still be broken. The current behavior is consistent between the browser and native

Thanks

What is the different between loading image and decoding it?
I don’t really know how it all works inside Lime/OpenFL. :slight_smile:

If you do it with Assets.loadBitmapData, what will be the purpose of @:bitmap then? Same for @:sound, etc.
I mean, for me the only purpose of @:bitmap was to have image available before any other resources are loaded.

Also, can I have a fix at least for native now, I’m not currently targeting HTML5, but need to release updates for native platforms.

https://github.com/openfl/openfl/blob/develop/openfl/display/BitmapData.hx#L1462

You can patch your release to use var image = Image.fromBytes (bytes); (and delete the closing brace and parenthesis on line 1480) to work immediately on native

I’m just trying to think about the issue – these images are available already, but in order to support HTML5, must be loaded asynchronously. That’s why it appears to display “instantly” when you don’t set the width and height of the parent Bitmap, but why the width and height values immediately after creation are 0, causing a scale of zero and making it disappear

I want to continue to think about the behavior of embedded assets (such as using @:bitmap), but in the meantime I have made a small patch to Lime that allows another workaround

project.xml

<assets path="Assets" rename="assets" exclude="preloader" />
<assets path="Assets/preloader" rename="" embed="true" library="preloader" />

Preloader.hx

Assets.loadBitmapData ("preloader:graphic.png").onComplete (function (bitmapData) {
    // handle bitmapData
});

Lime 4 included the new ability to split up your assets into separate asset libraries. The reason why this comes in handy (in this case) is that we can make all the assets we want for the preloader embedded, and available without preloading.

The “default” library (or any other library) can preload as normal, but within your preloader you should be able to access your assets.

We prefetch embedded assets on the HTML5 target during the preload process, but if you are in the preloader, you’ll need the async loadBitmapData method for them to work properly. getBitmapData works on native here.

Open to feedback, but hopefully this helps lend more flexibility to preloading.

EDIT:

As an alternative, realized (with the above “preloader” asset library) the following code can also work:

Assets.loadLibrary ("preloader").onComplete (function (_) {
    var bitmapData = Assets.getBitmapData ("preloader:graphic.png");
    // handle bitmapData
});

Perhaps loadBitmapData is still nicer, though – depends on whether you want to wait once for all embedded assets, or wait separately :slight_smile:

2 Likes

So, now we can have special assets just for preloader. :slight_smile:
Can you provide some details about new parameters in assets tag?
embed="true" is self explanatory.
What is library="preloader" for?

<assets /> can have a library="" attribute to specify which library the assets will be included in. By default, the library="default"

The prefix in the Assets.loadBitmapData call is the name of the asset library. For example, Assets.loadBitmapData ("graphic.png") is the same as Assets.loadBitmapData ("default:graphic.png");

By using multiple libraries, you can preload at separate times during your project. For example:

<assets path="level1" library="level1" />
<assets path="level2" library="level2" />

It is also nice because you can get a single progress value for loading the whole library, or unload separately

// change levels
Assets.unloadLibrary ("level1");
Assets.loadLibrary ("level2")
    .onProgress (function (loaded, total) { trace (loaded, total); })
    .onComplete (function (_) {
        trace ("level2 assets are loaded");
    });
3 Likes