Customize preloader html5 : different background image?

Sorry I’ve just copied/pasted an answer from a similar topic. In fact, I think my answer is partly wrong.
You should extend your own preloader from DefaultPreloader and not from Preloader.
Then you do like @gonzos suggests (but extending with DefaultPreloader).

1 Like

:confused:
Sorry, but then, how does this work ?

bitmapData = Assets.getBitmapData("preloader:assets/preloader/logo.png");

I created a folder ‘preloader’ under ‘assets’ and have an img called ‘logo.png’ in there.
I have this in my project.xml :

<assets path="assets/preloader" rename="preloader"/>
<library name="preloader" />
<app preloader="Preloader_FF"  />

In my custom preloader class, I have an onInit() that contains this :

bitmapData = Assets.getBitmapData("preloader:preloader/logo.png");

I’m still getting the console msg ‘this asset is available, but only asynchronously’ and a black rectangle on screen.

I really thought this would be easier to do …:sob:

You CAN do something like this:

Assets.loadBitmapData ("preloader/logo.png").onComplete (function (bitmapData) {
    var bitmap = new Bitmap (bitmapData);
    addChildAt (bitmap, 0);
});

…however, the above asset library solution is nice, because you can go from a preloader that uses only one asset to 100 assets, and still load all of your required preload assets at once. It also makes sure that you don’t load preloader assets twice (preloading the preload assets with the regular assets). It lets you do something like the above call, but for any preload-related assets

Assets.loadLibrary ("preloader").onComplete (function (_) {
    var bitmap = new Bitmap (Assets.getBitmapData ("preloader:logo.png"));
    addChildAt (bitmap, 0);
});

The “preloader:” prefix in the getBitmapData call from above specifies which asset library to request the asset from. By default (with no prefix) openfl.utils.Assets requests from the “default:” library

I probably would not set a “preloader” library to embed="true", though, because I am not sure that would be compatible with the Flash target (since embedded Flash assets are available, to my knowledge, only after preloading)

You can also do things like:

<assets path="preloader" embed="false" />
Assets.loadBitmapData ("preloader/logo.png").onComplete (...);

embed="false" tells asset system to copy the file, but not to preload it

Ok. It’s starting to become a bit clearer now …

Just one more thing : how do I create the preloader library ? Is it just right-clicking a folder under ‘assets’ >>> ‘add to library’ (I use HaxeDevelop), or do I do that from the command line ?

There is an optional “library” attribute in the <assets /> tag.

A tag without a “library” attribute will default to the “default” library. Thus, the following are the same :slight_smile:

<assets path="Assets" rename="assets" library="default" />
<assets path="Assets" rename="assets" />

Similarly, openfl.utils.Assets presumes “default:” as the asset prefix if you do not specify one, so these are also the same:

var bitmapData = Assets.getBitmapData ("default:logo.png");
var bitmapData = Assets.getBitmapData ("logo.png");

…just be careful that you do not include the same assets twice in multiple asset libraries. This can lead to loading the same assets twice, and generally is confusing :wink:

For example, this wouldn’t be ideal

<assets path="Assets" rename="assets" />
<assets path="Assets/preloader" rename="assets/preloader" library="preloader" />

…the “Assets/preloader” folder will be included in both libraries. For this sort of structure, you might use subdirectories, and include each one individually (or you could try to use filters). You do not have to use the “rename” attribute, but it does help in shortening/customizing the paths that exist in your final application

<assets path="Assets/images" rename="images" />
<assets path="Assets/sounds" rename="sounds" />
<assets path="Assets/preloader" rename="preloader" library="preloader" />

Success !!! :upside_down_face:

Thank you all for helping me out !

I will summarize what I did a bit further down, but first I would like to mention this to @singmajesty :
With the preloader, Firefox gives me an error message about 'texImage2D (fills up the entire console) ; Chrome does not.

Alright. The code.

  1. my asset folder looks like this :

assets_folder

  1. project.xml :
<!--preloader-->
<app preloader="Preloader_FF" />
		
<!-- assets -->
<icon path="assets/icon.png" />
<assets path="assets/preloader" rename="preloader" library="preloader" />
<assets path="assets/img" rename="img" />
<assets path="assets/sfx" rename="sfx" />
  1. Custom preloader class :

Since I had to override almost every function in the Preloader class that comes with the ‘CustomPreloader’ sample, I opted to not extend my own class from it, but just create it from scratch :

class Preloader_FF extends Sprite
{
	private var progress_fg:Sprite;
	private var w:Float = 0;
	private var h:Float = 0;
	private var TL_x:Float = 0;
	private var TL_y:Float = 0;

	public function new ()
	{
		super ();

		Assets.loadLibrary ("preloader").onComplete (function (_)
		{
			/*background img*/
			var bmd:BitmapData = null; 
                        #if html5   //without this check you get an error in Flash !
                        bmd = Assets.getBitmapData("preloader:preloader/bg.png");   //PATH !!!!!
                        #end
			if (bmd == null) return;

			var bm = new Bitmap(bmd, PixelSnapping.AUTO, true);
			addChild (bm);
			bm.x = (stage.stageWidth - bm.width) / 2;
			bm.y = (stage.stageHeight - bm.height) / 2;

			/*progress loader : background rect : brown*/
			var progress_bg:Sprite = new Sprite();     //CREATE NEW SPRITE (on top of bg img) !!!
			addChild(progress_bg);
			w = (256 / 800) * stage.stageWidth;
			h = (11 / 600) * stage.stageHeight;
			TL_x = (stage.stageWidth - w) / 2;
			TL_y = (279 / 600) * stage.stageHeight;

			progress_bg.graphics.lineStyle(1, 0xd6d1c3, 1);
			progress_bg.graphics.beginFill (0x2a1d1b);
			progress_bg.graphics.drawRoundRect (TL_x, TL_y, w, h, 8, 4);
			progress_bg.graphics.endFill();

			/*progress loader : foreground rect : white*/
			progress_fg = new Sprite();     //CREATE NEW SPRITE (op top of bg rect) !!!
			addChild(progress_fg);

			addEventListener (ProgressEvent.PROGRESS, this_onProgress);
			addEventListener (Event.COMPLETE, this_onComplete);
		});
	}
	
	private function update (percent:Float):Void
	{
		progress_fg.graphics.clear ();
		progress_fg.graphics.beginFill (0xd6d1c3);
		progress_fg.graphics.drawRoundRect (TL_x, TL_y, w*percent, h, 8, 4);
	}
	
	
/* *** EHs ************************************************************************************************************************* */
	

	private function this_onProgress (event:ProgressEvent):Void
	{
		if (event.bytesTotal <= 0) update (0);
		else update (event.bytesLoaded / event.bytesTotal);
	}

	private function this_onComplete (event:Event):Void
	{
		update (1);

		/*optional : unload at your own time ; if you comment this out : unload immediately*/
		event.preventDefault ();
		Timer.delay (function ()
		{
			dispatchEvent (new Event (Event.UNLOAD));
		}, 1000 /*2000*/);
	}
}

Two things that tripped me up :

  • The correct path for my preloader asset : Assets.getBitmapData(“preloader:preloader/bg.png”);
  • The fact that I needed to add sprites on top of the background image, so that the progress bar would be visible.
1 Like

I’ve tried a lot of different WebGL call combinations, but Firefox basically always gives a warning of some kind. I don’t think we need to worry about it :slight_smile:

Yep, I would recommend using “CustomPreloader” as a guide, but not using that Preloader class directly :slight_smile:

Thanks for sharing your final code!

1 Like

Just trying to get preloader image to work in my game and wonder if this works for HTML5?
Cause I see black screen, while it works fine elsewhere.

@:bitmap("assets/preload.jpg") class Back extends BitmapData {}

...

var bmpd:BitmapData = new Back(0, 0);
var back:Bitmap = new Bitmap(bmpd, PixelSnapping.AUTO, true);
addChild(back);

“embedded” images don’t preload on HTML5, there’s a delay before the bitmapData becomes available. I think there’s an onload callback parameter added on HTML5 right now:

#if html5
new Back (0, 0, function (bitmapData) {
    trace ("loaded");
});
#end

If this is available, you could use that to know just when the file is ready. Otherwise, I suggest using embed="false" on an asset, and BitmapData.loadFromFile ("assets/preload.jpg").onComplete (...) or using a separate preloader asset library, and loading that

1 Like

Actually, I was able to make embedded image work as expected in preloader by using “-Ddom” build option.
Is it ok, or this is some “undocumented” feature, that will go away eventually? :slight_smile:

1 Like

No, that should work. It sets the URL, and the browser loads it in when it is ready.

There is potentially an opportunity here to improve other targets, which used to wait until the BitmapData was valid, then started rendering it, but improvements to skipping rendering when nothing has changed may have affected this

Custom image is not working in latest version of OpenFL/Lime, I see black screen.
I have checked js code and couldn’t find any difference and image seems to load fine.
Any ideas? :slight_smile:

Do you have sample code you could share?

I’ve made a sample project, you can download it here: https://drive.google.com/open?id=1KCjQ8gY-a2ttuWX-SUlbaJFYCWpTXYBY

It has both sound test and preloader.
Thank you!

Any chance these two problems will be fixed in nearest future? :slight_smile:

Sorry for the delay, on the latest development build, I was able to fix the preloader by removing back.width = w; and back.height = h from your preloader code. Initially on HTML5, BitmapData is not fully loaded, so the bitmapData.width and height are each zero. Setting bitmap.width and height applies a scale, which ends up creating trouble. Open to ideas if this can be handled better somehow. Otherwise, you should be able to Assets.loadBitmapData (with a non-embedded asset, perhaps of a different asset library of some kind?) or the simpler BitmapData.loadFromFile, which provides a callback so you can manage your image after it’s fully loaded, when this code is guaranteed to work. I didn’t see an issue with the sound, at least on this machine

Your majesty :wink: , am I right saying that we can’t load preloader graphics(progress bar etc) prior to loading other libraries marked for preload (html5)?
As far as I understand and samples posted here… we can start loading “preloader” lib, but it will be loaded in parallel with the openfl.display.Preloader work (or whatever is using it) .

Also I was hoping that embed=“true” would make it loaded prior to preload=“true” marked libs
but no luck with that.
It would be nice to have a callback or event, to handle that, like hey all embeds loaded))
or some hook to suspend (keep from starting) the main load process,
load our “preload” lib and then let it continue with our marvelous progress bar on screen already

thanx!)

Hmm, yes, it is possible that it would get stuck in the preload queue behind other assets. Another alternative would be to use BitmapData.loadFromFile directly, I’m not sure if that would avoid the queue and load immediately?

EDIT: Perhaps you could try modifying this value:

Try a super-high value, and see if it behaves differently

In my project, I use:

@:bitmap("Assets/some.png") class Background extends BitmapData {}

class SomePreloader extends Sprite {
    public function new() {
        super();

        this.addChild(new Bitmap(new Background(0, 0)));
    }
}

This class is then set as preloader in project.xml file. Works perfectly fine.

Displaying Base64 Encoded Images in OpenFL Using BitmapData.loadFromBytes

If you need to display an image stored as a base64 string directly in your OpenFL project, this guide walks you through how to decode and display it using the asynchronous BitmapData.loadFromBytes method. This approach is especially useful when you want to avoid using external assets and load images dynamically.

Here’s the complete code:

package;

import haxe.Timer;
import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.ProgressEvent;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.utils.ByteArray;
import haxe.crypto.Base64;

class Preloader extends Sprite {
    public function new() {
        super();

        // Insert your base64-encoded PNG image string here
        var base64Image:String = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4...";
        var base64Data = base64Image.split(",")[1];

        // Decode the base64 string to bytes
        var imageBytes:ByteArray = Base64.decode(base64Data);
        trace("Decoded byte size: " + imageBytes.length);

        // Load the BitmapData asynchronously from bytes
        BitmapData.loadFromBytes(imageBytes).onComplete(function(bitmapData:BitmapData) {
            if (bitmapData != null) {
                trace("BitmapData created successfully.");
                var bitmap:Bitmap = new Bitmap(bitmapData);
                addChild(bitmap);
                bitmap.x = 0;
                bitmap.y = 0;
            } else {
                trace("Failed to create BitmapData.");
            }
        }).onError(function(error) {
            trace("Error loading BitmapData: " + error);
        });

        addEventListener(Event.COMPLETE, this_onComplete);
        addEventListener(ProgressEvent.PROGRESS, this_onProgress);
    }

    private function update(percent:Float):Void {
        graphics.clear();
        graphics.beginFill(0x1F9DB2);
        graphics.drawRect(0, 120, stage.stageWidth * percent, stage.stageHeight);
    }

    private function this_onComplete(event:Event):Void {
        update(1);
        event.preventDefault();
        Timer.delay(function() {
            dispatchEvent(new Event(Event.UNLOAD));
        }, 2000);
    }

    private function this_onProgress(event:ProgressEvent):Void {
        if (event.bytesTotal <= 0) {
            update(0);
        } else {
            update(event.bytesLoaded / event.bytesTotal);
        }
    }
}

Explanation of the Code

  1. Decoding Base64: We use Base64.decode to convert the base64 image string into a ByteArray.

  2. Loading the Image Asynchronously: By calling BitmapData.loadFromBytes(imageBytes), the image is processed asynchronously. If successful, we receive a BitmapData object in the onComplete callback.

  3. Adding the Image to the Stage: Once BitmapData is successfully created, we add it as a Bitmap to the display list.

  4. Error Handling: If anything goes wrong during loading, an onError callback provides details about the error.

Why Use BitmapData.loadFromBytes?

This approach is effective for dynamically loading base64 images, avoiding direct assets. Using loadFromBytes asynchronously ensures smooth handling of data in memory, which might not always work with BitmapData.fromBytes.

Where This Could Be Useful

This solution is ideal for OpenFL projects where images are dynamically generated, stored in base64, or need to be embedded directly within the code. Feel free to use and modify this code in your own projects!

1 Like