Add image lazy loading
This commit is contained in:
parent
27d55a776e
commit
8ff0e868ca
|
@ -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];
|
||||||
|
|
||||||
|
// Main loading function
|
||||||
|
const load = (cb1) => {
|
||||||
|
getLazyAsset(iurl, filename, (data) => {
|
||||||
// Delete original file if existent
|
// Delete original file if existent
|
||||||
try {
|
try { FS.unlink(path + "/" + filename); } catch (err) {}
|
||||||
FS.unlink(path + "/" + filename);
|
|
||||||
} catch (err) {}
|
|
||||||
|
|
||||||
// Get the new file
|
FS.createPreloadedFile(path, filename, new Uint8Array(data), true, true, function() {
|
||||||
getLazyAsset(iurl, filename, () => {
|
|
||||||
FS.createPreloadedFile(path, filename, iurl, true, true, function() {
|
|
||||||
window.fileAsyncCache[fullPath] = 1;
|
window.fileAsyncCache[fullPath] = 1;
|
||||||
if (window.setNotBusy) window.setNotBusy();
|
if (!bitmap && window.setNotBusy) window.setNotBusy();
|
||||||
if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
|
if (window.fileLoadedAsync) window.fileLoadedAsync(fullPath);
|
||||||
callback();
|
callback();
|
||||||
|
if (cb1) cb1();
|
||||||
}, console.error);
|
}, 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
|
#!/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…
Reference in New Issue