Add image lazy loading
This commit is contained in:
		
							parent
							
								
									27d55a776e
								
							
						
					
					
						commit
						8ff0e868ca
					
				
					 10 changed files with 123 additions and 29 deletions
				
			
		| 
						 | 
				
			
			@ -435,7 +435,7 @@ SET(ASYNCIFY "-s ASYNCIFY=1 -s 'ASYNCIFY_IMPORTS=[\"load_file_async_js\"]'")
 | 
			
		|||
 | 
			
		||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMS_FLAGS} ${ERR_FLAGS} ${ASYNCIFY}")
 | 
			
		||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EMS_FLAGS} ${ERR_FLAGS} ${ASYNCIFY}")
 | 
			
		||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMS_FLAGS} ${ASYNCIFY} -lopenal -s DISABLE_EXCEPTION_CATCHING=1 -s ASSERTIONS=0 -s SAFE_HEAP=0 -s MINIFY_HTML=0  --shell-file extra/shell.html -s EMULATE_FUNCTION_POINTER_CASTS=0 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2")
 | 
			
		||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMS_FLAGS} ${ASYNCIFY} -lopenal -s DISABLE_EXCEPTION_CATCHING=1 -s ASSERTIONS=0 -s SAFE_HEAP=0 -s MINIFY_HTML=0  --shell-file extra/shell.html -s EMULATE_FUNCTION_POINTER_CASTS=0 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS='[\"_main\",\"_reloadBitmap\"]' -s EXPORTED_RUNTIME_METHODS='[\"ccall\",\"cwrap\"]'")
 | 
			
		||||
 | 
			
		||||
set_target_properties(
 | 
			
		||||
    ${PROJECT_NAME}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,13 @@ function _base64ToBytes(base64) {
 | 
			
		|||
    return bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.loadFileAsync = function(fullPath, callback) {
 | 
			
		||||
// Canvas used for image generation
 | 
			
		||||
var generationCanvas = document.createElement('canvas')
 | 
			
		||||
 | 
			
		||||
window.loadFileAsync = function(fullPath, bitmap, callback) {
 | 
			
		||||
    // noop
 | 
			
		||||
    callback = callback || (() => {});
 | 
			
		||||
 | 
			
		||||
    // Make cache object
 | 
			
		||||
    if (!window.fileAsyncCache) window.fileAsyncCache = {};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +33,7 @@ window.loadFileAsync = function(fullPath, callback) {
 | 
			
		|||
    if (window.fileAsyncCache.hasOwnProperty(fullPath)) return callback();
 | 
			
		||||
 | 
			
		||||
    // Show spinner
 | 
			
		||||
    if (window.setBusy) window.setBusy();
 | 
			
		||||
    if (!bitmap && window.setBusy) window.setBusy();
 | 
			
		||||
 | 
			
		||||
    // Get mapping key
 | 
			
		||||
    const mappingKey = fullPath.toLowerCase().replace(new RegExp("\\.[^/.]+$"), "");
 | 
			
		||||
| 
						 | 
				
			
			@ -46,18 +52,47 @@ window.loadFileAsync = function(fullPath, callback) {
 | 
			
		|||
    const path = "/game/" + mappingValue.substring(0, mappingValue.lastIndexOf("/"));
 | 
			
		||||
    const filename = mappingValue.substring(mappingValue.lastIndexOf("/") + 1).split("?")[0];
 | 
			
		||||
 | 
			
		||||
    // Delete original file if existent
 | 
			
		||||
    try {
 | 
			
		||||
        FS.unlink(path + "/" + filename);
 | 
			
		||||
    } catch (err) {}
 | 
			
		||||
    // Main loading function
 | 
			
		||||
    const load = (cb1) => {
 | 
			
		||||
        getLazyAsset(iurl, filename, (data) => {
 | 
			
		||||
            // Delete original file if existent
 | 
			
		||||
            try { FS.unlink(path + "/" + filename); } catch (err) {}
 | 
			
		||||
 | 
			
		||||
    // Get the new file
 | 
			
		||||
    getLazyAsset(iurl, filename, () => {
 | 
			
		||||
        FS.createPreloadedFile(path, filename, iurl, true, true, function() {
 | 
			
		||||
            window.fileAsyncCache[fullPath] = 1;
 | 
			
		||||
            if (window.setNotBusy) window.setNotBusy();
 | 
			
		||||
            if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
 | 
			
		||||
            callback();
 | 
			
		||||
            FS.createPreloadedFile(path, filename, new Uint8Array(data), true, true, function() {
 | 
			
		||||
                window.fileAsyncCache[fullPath] = 1;
 | 
			
		||||
                if (!bitmap && window.setNotBusy) window.setNotBusy();
 | 
			
		||||
                if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
 | 
			
		||||
                callback();
 | 
			
		||||
                if (cb1) cb1();
 | 
			
		||||
            }, console.error);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Show progress if doing it synchronously only
 | 
			
		||||
    if (bitmap && bitmapSizeMapping[mappingKey]) {
 | 
			
		||||
        // Remove existing file
 | 
			
		||||
        try { FS.unlink(path + "/" + filename); } catch (err) {}
 | 
			
		||||
 | 
			
		||||
        // Get image
 | 
			
		||||
        const sm = bitmapSizeMapping[mappingKey];
 | 
			
		||||
        generationCanvas.width = sm[0];
 | 
			
		||||
        generationCanvas.height = sm[1];
 | 
			
		||||
 | 
			
		||||
        // Create dummy from data uri
 | 
			
		||||
        FS.createPreloadedFile(path, filename, generationCanvas.toDataURL(), true, true, function() {
 | 
			
		||||
            // Return control to C++
 | 
			
		||||
            callback(); callback = () => {};
 | 
			
		||||
 | 
			
		||||
            // Lazy load and refresh
 | 
			
		||||
            load(() => {
 | 
			
		||||
                const reloadBitmap = Module.cwrap('reloadBitmap', 'number', ['number'])
 | 
			
		||||
                reloadBitmap(bitmap);
 | 
			
		||||
            });
 | 
			
		||||
        }, console.error);
 | 
			
		||||
    });
 | 
			
		||||
    } else {
 | 
			
		||||
        if (bitmap) {
 | 
			
		||||
            console.warn('No sizemap for image', mappingKey);
 | 
			
		||||
        }
 | 
			
		||||
        load();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
echo "var mapping = {" > mapping.js
 | 
			
		||||
echo "var bitmapSizeMapping = {" > bitmap-map.js
 | 
			
		||||
 | 
			
		||||
for file in {*,*/*,*/**/*}
 | 
			
		||||
do
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +18,16 @@ fi
 | 
			
		|||
 | 
			
		||||
echo "\"$fl\": \"${file}?h=${md5}\"," >> mapping.js
 | 
			
		||||
 | 
			
		||||
if [ -f $file ]
 | 
			
		||||
then
 | 
			
		||||
    sz=`identify -format "%w,%h" "${file}" 2>/dev/null`
 | 
			
		||||
    if [ $? -eq 0 ]; then
 | 
			
		||||
        echo "\"$fl\": [${sz}]," >> bitmap-map.js
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
echo "};" >> mapping.js
 | 
			
		||||
echo "};" >> bitmap-map.js
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
    <script src="js/localforage.min.js"></script>
 | 
			
		||||
    <script src="js/drive.js"></script>
 | 
			
		||||
    <script src="gameasync/mapping.js"></script>
 | 
			
		||||
    <script src="gameasync/bitmap-map.js"></script>
 | 
			
		||||
 | 
			
		||||
    <title>MKXP</title>
 | 
			
		||||
    <style>
 | 
			
		||||
| 
						 | 
				
			
			@ -292,6 +293,7 @@
 | 
			
		|||
      var hideTimer = 0;
 | 
			
		||||
      function getLazyAsset(url, filename, callback) {
 | 
			
		||||
          const xhr = new XMLHttpRequest();
 | 
			
		||||
          xhr.responseType = "arraybuffer";
 | 
			
		||||
          const pdiv = document.getElementById("progress");
 | 
			
		||||
          let showTimer = 0;
 | 
			
		||||
          let abortTimer = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -308,10 +310,9 @@
 | 
			
		|||
                      pdiv.style.opacity = '0';
 | 
			
		||||
                      hideTimer = 0;
 | 
			
		||||
                  }, 500);
 | 
			
		||||
                  callback();
 | 
			
		||||
 | 
			
		||||
                  clearTimeout(showTimer);
 | 
			
		||||
                  clearTimeout(abortTimer);
 | 
			
		||||
                  callback(xhr.response);
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
          xhr.onprogress = function (event) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,8 +258,22 @@ struct BitmapOpenHandler : FileSystem::OpenHandler
 | 
			
		|||
 | 
			
		||||
Bitmap::Bitmap(const char *filename)
 | 
			
		||||
{
 | 
			
		||||
	strcpy(this->filename, filename);
 | 
			
		||||
	this->loadFromFilename();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Bitmap::loadFromFilename()
 | 
			
		||||
{
 | 
			
		||||
	char * filename = this->filename;
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	load_file_async_js(filename);
 | 
			
		||||
	bool reloading = false;
 | 
			
		||||
	if (this->p) {
 | 
			
		||||
		this->releaseResources();
 | 
			
		||||
		reloading = true;
 | 
			
		||||
	} else {
 | 
			
		||||
		load_file_async_js(filename, (int)this);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	BitmapOpenHandler handler;
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +281,7 @@ Bitmap::Bitmap(const char *filename)
 | 
			
		|||
	SDL_Surface *imgSurf = handler.surf;
 | 
			
		||||
 | 
			
		||||
	if (!imgSurf) {
 | 
			
		||||
		printf("ERROR OCCURED LOADING IMAGE %s : %s\n", filename, SDL_GetError());
 | 
			
		||||
		printf("Error occured loading image %s : %s\n", filename, SDL_GetError());
 | 
			
		||||
		throw Exception(Exception::SDLError, "Error loading image '%s': %s",
 | 
			
		||||
		                filename, SDL_GetError());
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -306,6 +320,11 @@ Bitmap::Bitmap(const char *filename)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	p->addTaintedArea(rect());
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	if (reloading && this->reloadCallback)
 | 
			
		||||
		this->reloadCallback(this->reloadCallbackData);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Bitmap::Bitmap(int width, int height)
 | 
			
		||||
| 
						 | 
				
			
			@ -1334,3 +1353,15 @@ void Bitmap::releaseResources()
 | 
			
		|||
 | 
			
		||||
	delete p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
extern "C" {
 | 
			
		||||
int reloadBitmap(int intptr) {
 | 
			
		||||
	Bitmap * bitmap = (Bitmap*) intptr;
 | 
			
		||||
	if (bitmap->isDisposed()) return 0;
 | 
			
		||||
 | 
			
		||||
	bitmap->loadFromFilename();
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,11 +119,16 @@ public:
 | 
			
		|||
 | 
			
		||||
	sigc::signal<void> modified;
 | 
			
		||||
 | 
			
		||||
	char filename[512];
 | 
			
		||||
	void loadFromFilename();
 | 
			
		||||
	void (*reloadCallback)(void *) = NULL;
 | 
			
		||||
	void * reloadCallbackData = NULL;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void releaseResources();
 | 
			
		||||
	const char *klassName() const { return "bitmap"; }
 | 
			
		||||
 | 
			
		||||
	BitmapPrivate *p;
 | 
			
		||||
	BitmapPrivate *p = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // BITMAP_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,9 +2,9 @@
 | 
			
		|||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
 | 
			
		||||
EM_JS(void, load_file_async_js, (const char* fullPathC), {
 | 
			
		||||
EM_JS(void, load_file_async_js, (const char* fullPathC, int bitmap), {
 | 
			
		||||
	Asyncify.handleSleep(function(wakeUp) {
 | 
			
		||||
    	window.loadFileAsync(UTF8ToString(fullPathC), wakeUp);
 | 
			
		||||
    	window.loadFileAsync(UTF8ToString(fullPathC), bitmap, wakeUp);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
#include <emscripten.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	void load_file_async_js(const char* fullPathC);
 | 
			
		||||
	void load_file_async_js(const char* fullPathC, int bitmap=0);
 | 
			
		||||
 | 
			
		||||
	void save_file_async_js(const char* fullPathC);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1147,6 +1147,14 @@ DEF_ATTR_RD_SIMPLE(Tilemap, Visible, bool, p->visible)
 | 
			
		|||
DEF_ATTR_RD_SIMPLE(Tilemap, OX, int, p->origin.x)
 | 
			
		||||
DEF_ATTR_RD_SIMPLE(Tilemap, OY, int, p->origin.y)
 | 
			
		||||
 | 
			
		||||
void invalidateBitmap(void * tilemap) {
 | 
			
		||||
	((Tilemap *) tilemap)->invalidate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Tilemap::invalidate() {
 | 
			
		||||
	p->invalidateAtlasContents();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Tilemap::setTileset(Bitmap *value)
 | 
			
		||||
{
 | 
			
		||||
	guardDisposed();
 | 
			
		||||
| 
						 | 
				
			
			@ -1159,6 +1167,9 @@ void Tilemap::setTileset(Bitmap *value)
 | 
			
		|||
	if (!value)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	value->reloadCallback = invalidateBitmap;
 | 
			
		||||
	value->reloadCallbackData = (void *) this;
 | 
			
		||||
 | 
			
		||||
	p->invalidateAtlasSize();
 | 
			
		||||
	p->tilesetCon.disconnect();
 | 
			
		||||
	p->tilesetCon = value->modified.connect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ public:
 | 
			
		|||
	~Tilemap();
 | 
			
		||||
 | 
			
		||||
	void update();
 | 
			
		||||
	void invalidate();
 | 
			
		||||
 | 
			
		||||
	Autotiles &getAutotiles();
 | 
			
		||||
	Viewport *getViewport() const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue