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_C_FLAGS "${CMAKE_C_FLAGS} ${EMS_FLAGS} ${ERR_FLAGS} ${ASYNCIFY}")
 | 
				
			||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_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(
 | 
					set_target_properties(
 | 
				
			||||||
    ${PROJECT_NAME}
 | 
					    ${PROJECT_NAME}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,13 @@ function _base64ToBytes(base64) {
 | 
				
			||||||
    return bytes;
 | 
					    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
 | 
					    // Make cache object
 | 
				
			||||||
    if (!window.fileAsyncCache) window.fileAsyncCache = {};
 | 
					    if (!window.fileAsyncCache) window.fileAsyncCache = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +33,7 @@ window.loadFileAsync = function(fullPath, callback) {
 | 
				
			||||||
    if (window.fileAsyncCache.hasOwnProperty(fullPath)) return callback();
 | 
					    if (window.fileAsyncCache.hasOwnProperty(fullPath)) return callback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Show spinner
 | 
					    // Show spinner
 | 
				
			||||||
    if (window.setBusy) window.setBusy();
 | 
					    if (!bitmap && window.setBusy) window.setBusy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get mapping key
 | 
					    // Get mapping key
 | 
				
			||||||
    const mappingKey = fullPath.toLowerCase().replace(new RegExp("\\.[^/.]+$"), "");
 | 
					    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 path = "/game/" + mappingValue.substring(0, mappingValue.lastIndexOf("/"));
 | 
				
			||||||
    const filename = mappingValue.substring(mappingValue.lastIndexOf("/") + 1).split("?")[0];
 | 
					    const filename = mappingValue.substring(mappingValue.lastIndexOf("/") + 1).split("?")[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Delete original file if existent
 | 
					    // Main loading function
 | 
				
			||||||
    try {
 | 
					    const load = (cb1) => {
 | 
				
			||||||
        FS.unlink(path + "/" + filename);
 | 
					        getLazyAsset(iurl, filename, (data) => {
 | 
				
			||||||
    } catch (err) {}
 | 
					            // Delete original file if existent
 | 
				
			||||||
 | 
					            try { FS.unlink(path + "/" + filename); } catch (err) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the new file
 | 
					            FS.createPreloadedFile(path, filename, new Uint8Array(data), true, true, function() {
 | 
				
			||||||
    getLazyAsset(iurl, filename, () => {
 | 
					                window.fileAsyncCache[fullPath] = 1;
 | 
				
			||||||
        FS.createPreloadedFile(path, filename, iurl, true, true, function() {
 | 
					                if (!bitmap && window.setNotBusy) window.setNotBusy();
 | 
				
			||||||
            window.fileAsyncCache[fullPath] = 1;
 | 
					                if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
 | 
				
			||||||
            if (window.setNotBusy) window.setNotBusy();
 | 
					                callback();
 | 
				
			||||||
            if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
 | 
					                if (cb1) cb1();
 | 
				
			||||||
            callback();
 | 
					            }, 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);
 | 
					        }, console.error);
 | 
				
			||||||
    });
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (bitmap) {
 | 
				
			||||||
 | 
					            console.warn('No sizemap for image', mappingKey);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        load();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
#!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "var mapping = {" > mapping.js
 | 
					echo "var mapping = {" > mapping.js
 | 
				
			||||||
 | 
					echo "var bitmapSizeMapping = {" > bitmap-map.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for file in {*,*/*,*/**/*}
 | 
					for file in {*,*/*,*/**/*}
 | 
				
			||||||
do
 | 
					do
 | 
				
			||||||
| 
						 | 
					@ -17,7 +18,16 @@ fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "\"$fl\": \"${file}?h=${md5}\"," >> mapping.js
 | 
					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
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "};" >> mapping.js
 | 
					echo "};" >> mapping.js
 | 
				
			||||||
 | 
					echo "};" >> bitmap-map.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@
 | 
				
			||||||
    <script src="js/localforage.min.js"></script>
 | 
					    <script src="js/localforage.min.js"></script>
 | 
				
			||||||
    <script src="js/drive.js"></script>
 | 
					    <script src="js/drive.js"></script>
 | 
				
			||||||
    <script src="gameasync/mapping.js"></script>
 | 
					    <script src="gameasync/mapping.js"></script>
 | 
				
			||||||
 | 
					    <script src="gameasync/bitmap-map.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <title>MKXP</title>
 | 
					    <title>MKXP</title>
 | 
				
			||||||
    <style>
 | 
					    <style>
 | 
				
			||||||
| 
						 | 
					@ -292,6 +293,7 @@
 | 
				
			||||||
      var hideTimer = 0;
 | 
					      var hideTimer = 0;
 | 
				
			||||||
      function getLazyAsset(url, filename, callback) {
 | 
					      function getLazyAsset(url, filename, callback) {
 | 
				
			||||||
          const xhr = new XMLHttpRequest();
 | 
					          const xhr = new XMLHttpRequest();
 | 
				
			||||||
 | 
					          xhr.responseType = "arraybuffer";
 | 
				
			||||||
          const pdiv = document.getElementById("progress");
 | 
					          const pdiv = document.getElementById("progress");
 | 
				
			||||||
          let showTimer = 0;
 | 
					          let showTimer = 0;
 | 
				
			||||||
          let abortTimer = 0;
 | 
					          let abortTimer = 0;
 | 
				
			||||||
| 
						 | 
					@ -308,10 +310,9 @@
 | 
				
			||||||
                      pdiv.style.opacity = '0';
 | 
					                      pdiv.style.opacity = '0';
 | 
				
			||||||
                      hideTimer = 0;
 | 
					                      hideTimer = 0;
 | 
				
			||||||
                  }, 500);
 | 
					                  }, 500);
 | 
				
			||||||
                  callback();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  clearTimeout(showTimer);
 | 
					                  clearTimeout(showTimer);
 | 
				
			||||||
                  clearTimeout(abortTimer);
 | 
					                  clearTimeout(abortTimer);
 | 
				
			||||||
 | 
					                  callback(xhr.response);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          xhr.onprogress = function (event) {
 | 
					          xhr.onprogress = function (event) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,8 +258,22 @@ struct BitmapOpenHandler : FileSystem::OpenHandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Bitmap::Bitmap(const char *filename)
 | 
					Bitmap::Bitmap(const char *filename)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						strcpy(this->filename, filename);
 | 
				
			||||||
 | 
						this->loadFromFilename();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Bitmap::loadFromFilename()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char * filename = this->filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __EMSCRIPTEN__
 | 
					#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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BitmapOpenHandler handler;
 | 
						BitmapOpenHandler handler;
 | 
				
			||||||
| 
						 | 
					@ -267,7 +281,7 @@ Bitmap::Bitmap(const char *filename)
 | 
				
			||||||
	SDL_Surface *imgSurf = handler.surf;
 | 
						SDL_Surface *imgSurf = handler.surf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!imgSurf) {
 | 
						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",
 | 
							throw Exception(Exception::SDLError, "Error loading image '%s': %s",
 | 
				
			||||||
		                filename, SDL_GetError());
 | 
							                filename, SDL_GetError());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -306,6 +320,11 @@ Bitmap::Bitmap(const char *filename)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p->addTaintedArea(rect());
 | 
						p->addTaintedArea(rect());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __EMSCRIPTEN__
 | 
				
			||||||
 | 
						if (reloading && this->reloadCallback)
 | 
				
			||||||
 | 
							this->reloadCallback(this->reloadCallbackData);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Bitmap::Bitmap(int width, int height)
 | 
					Bitmap::Bitmap(int width, int height)
 | 
				
			||||||
| 
						 | 
					@ -1334,3 +1353,15 @@ void Bitmap::releaseResources()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	delete p;
 | 
						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;
 | 
						sigc::signal<void> modified;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char filename[512];
 | 
				
			||||||
 | 
						void loadFromFilename();
 | 
				
			||||||
 | 
						void (*reloadCallback)(void *) = NULL;
 | 
				
			||||||
 | 
						void * reloadCallbackData = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void releaseResources();
 | 
						void releaseResources();
 | 
				
			||||||
	const char *klassName() const { return "bitmap"; }
 | 
						const char *klassName() const { return "bitmap"; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BitmapPrivate *p;
 | 
						BitmapPrivate *p = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // BITMAP_H
 | 
					#endif // BITMAP_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __EMSCRIPTEN__
 | 
					#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) {
 | 
						Asyncify.handleSleep(function(wakeUp) {
 | 
				
			||||||
    	window.loadFileAsync(UTF8ToString(fullPathC), wakeUp);
 | 
					    	window.loadFileAsync(UTF8ToString(fullPathC), bitmap, wakeUp);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
#include <emscripten.h>
 | 
					#include <emscripten.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern "C" {
 | 
					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);
 | 
						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, OX, int, p->origin.x)
 | 
				
			||||||
DEF_ATTR_RD_SIMPLE(Tilemap, OY, int, p->origin.y)
 | 
					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)
 | 
					void Tilemap::setTileset(Bitmap *value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	guardDisposed();
 | 
						guardDisposed();
 | 
				
			||||||
| 
						 | 
					@ -1159,6 +1167,9 @@ void Tilemap::setTileset(Bitmap *value)
 | 
				
			||||||
	if (!value)
 | 
						if (!value)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						value->reloadCallback = invalidateBitmap;
 | 
				
			||||||
 | 
						value->reloadCallbackData = (void *) this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p->invalidateAtlasSize();
 | 
						p->invalidateAtlasSize();
 | 
				
			||||||
	p->tilesetCon.disconnect();
 | 
						p->tilesetCon.disconnect();
 | 
				
			||||||
	p->tilesetCon = value->modified.connect
 | 
						p->tilesetCon = value->modified.connect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,7 @@ public:
 | 
				
			||||||
	~Tilemap();
 | 
						~Tilemap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void update();
 | 
						void update();
 | 
				
			||||||
 | 
						void invalidate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Autotiles &getAutotiles();
 | 
						Autotiles &getAutotiles();
 | 
				
			||||||
	Viewport *getViewport() const;
 | 
						Viewport *getViewport() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue