mirror of https://github.com/espruino/BangleApps
Allow writing image data to separate file to keep it out of memory
parent
7a62248f6b
commit
fbc62eed03
|
@ -1,3 +1,4 @@
|
||||||
0.01: New App
|
0.01: New App
|
||||||
0.02: Allow drawing polys
|
0.02: Allow drawing polys
|
||||||
0.03: Allow partly importing Amazfit decompiler formatted watchfaces
|
0.03: Allow partly importing Amazfit decompiler formatted watchfaces
|
||||||
|
0.04: Allow writing all image data to separate file to save some RAM
|
||||||
|
|
|
@ -4,29 +4,47 @@ var resources = require("Storage").readJSON("imageclock.resources.json");
|
||||||
function prepareImg(resource){
|
function prepareImg(resource){
|
||||||
//print("prepareImg: ", resource);
|
//print("prepareImg: ", resource);
|
||||||
|
|
||||||
|
var result = cacheBuffers ? resource : {
|
||||||
|
width: resource.width,
|
||||||
|
height: resource.height,
|
||||||
|
bpp: resource.bpp
|
||||||
|
};
|
||||||
|
if (!cacheBuffers && resource.transparent) result.transparent = resource.transparent;
|
||||||
|
|
||||||
if (resource.img){
|
if (resource.img){
|
||||||
resource.buffer = E.toArrayBuffer(atob(resource.img));
|
|
||||||
resource.img = undefined;
|
|
||||||
//print("buffer from img");
|
//print("buffer from img");
|
||||||
|
result.buffer = E.toArrayBuffer(atob(resource.img));
|
||||||
|
result.img = undefined;
|
||||||
} else if (resource.file){
|
} else if (resource.file){
|
||||||
resource.buffer = E.toArrayBuffer(atob(require("Storage").read(resource.file)));
|
|
||||||
resource.file = undefined;
|
|
||||||
//print("buffer from file");
|
//print("buffer from file");
|
||||||
} else if (resource.compressed){
|
result.buffer = E.toArrayBuffer(atob(require("Storage").read(resource.file)));
|
||||||
resource.buffer = require("heatshrink").decompress(atob(resource.compressed));
|
result.file = undefined;
|
||||||
resource.compressed = undefined;
|
} else if (resource.compressed && (resource.dataOffset == undefined)){
|
||||||
//print("buffer from compressed");
|
//print("buffer from compressed");
|
||||||
|
result.buffer = require("heatshrink").decompress(atob(resource.compressed));
|
||||||
|
result.compressed = undefined;
|
||||||
} else if (resource.buffer){
|
} else if (resource.buffer){
|
||||||
//print("buffer cached");
|
//print("buffer cached");
|
||||||
resource.buffer = resource.buffer;
|
} else if (resource.dataOffset !== undefined){
|
||||||
|
//print("buffer from data file");
|
||||||
|
if (resource.compressed){
|
||||||
|
result.buffer = require("heatshrink").decompress(atob(require("Storage").read("imageclock.resources.data", resource.dataOffset, resource.dataLength)));
|
||||||
|
} else {
|
||||||
|
result.buffer = E.toArrayBuffer(atob(require("Storage").read("imageclock.resources.data", resource.dataOffset, resource.dataLength)));
|
||||||
|
}
|
||||||
|
result.compressed = undefined;
|
||||||
|
result.dataOffset = undefined;
|
||||||
|
result.dataLength = undefined;
|
||||||
|
} else {
|
||||||
|
print("Could not get image data for resource", resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.paletteData){
|
if (result.paletteData){
|
||||||
result.palette = new Uint16Array(resource.paletteData);
|
result.palette = new Uint16Array(resource.paletteData);
|
||||||
result.paletteData = undefined;
|
result.paletteData = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getByPath(object, path, lastElem){
|
function getByPath(object, path, lastElem){
|
||||||
|
@ -609,6 +627,7 @@ var lockedRedraw = getByPath(face, ["Properties","Redraw","Locked"]) || 60000;
|
||||||
var unlockedRedraw = getByPath(face, ["Properties","Redraw","Unlocked"]) || 1000;
|
var unlockedRedraw = getByPath(face, ["Properties","Redraw","Unlocked"]) || 1000;
|
||||||
var defaultRedraw = getByPath(face, ["Properties","Redraw","Default"]) || "Always";
|
var defaultRedraw = getByPath(face, ["Properties","Redraw","Default"]) || "Always";
|
||||||
var redrawEvents = getByPath(face, ["Properties","Redraw","Events"]);
|
var redrawEvents = getByPath(face, ["Properties","Redraw","Events"]);
|
||||||
|
var cacheBuffers = getByPath(face, ["Properties","CacheBuffers"]) || false;
|
||||||
var events = getByPath(face, ["Properties","Events"]);
|
var events = getByPath(face, ["Properties","Events"]);
|
||||||
|
|
||||||
var stepsgoal = 2000;
|
var stepsgoal = 2000;
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
<label for="useAmazefit">ALPHA: Decompiled Amazfit</label></br>
|
<label for="useAmazefit">ALPHA: Decompiled Amazfit</label></br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Options:</br>
|
||||||
|
<input type="checkbox" id="useAmazefit" name="mode" checked/>
|
||||||
|
<label for="useDataFile">Use resource data file (image data not in RAM by default)</label></br>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>Select watchface folder:</br><input type="file" id="fileLoader" name="files[]" multiple directory="" webkitdirectory="" moxdirectory="" /></p>
|
<p>Select watchface folder:</br><input type="file" id="fileLoader" name="files[]" multiple directory="" webkitdirectory="" moxdirectory="" /></p>
|
||||||
<p><b>or</b></p>
|
<p><b>or</b></p>
|
||||||
<p>Select watchface zip file: </br><input type="file" id="zipLoader" name="zip"/></p><br/>
|
<p>Select watchface zip file: </br><input type="file" id="zipLoader" name="zip"/></p><br/>
|
||||||
|
@ -38,6 +44,8 @@
|
||||||
var resultJson = {};
|
var resultJson = {};
|
||||||
var infoJson;
|
var infoJson;
|
||||||
var faceJson;
|
var faceJson;
|
||||||
|
var resourceDataString = "";
|
||||||
|
var resourceDataOffset = 0;
|
||||||
var handledFiles = 0;
|
var handledFiles = 0;
|
||||||
var expectedFiles = 0;
|
var expectedFiles = 0;
|
||||||
var rootZip = new JSZip();
|
var rootZip = new JSZip();
|
||||||
|
@ -586,12 +594,22 @@
|
||||||
performFileChanges().then(()=>{
|
performFileChanges().then(()=>{
|
||||||
rootZip.file("face.json", JSON.stringify(faceJson, null, 2));
|
rootZip.file("face.json", JSON.stringify(faceJson, null, 2));
|
||||||
rootZip.file("info.json", JSON.stringify(infoJson, null, 2));
|
rootZip.file("info.json", JSON.stringify(infoJson, null, 2));
|
||||||
|
|
||||||
|
if (document.getElementById('useDataFile').checked){
|
||||||
|
moveData(resultJson);
|
||||||
|
console.log("Created data file", resourceDataString, resourceDataOffset, resultJson);
|
||||||
|
}
|
||||||
document.getElementById('btnSave').disabled = false;
|
document.getElementById('btnSave').disabled = false;
|
||||||
document.getElementById('btnSaveFace').disabled = false;
|
document.getElementById('btnSaveFace').disabled = false;
|
||||||
document.getElementById('btnSaveZip').disabled = false;
|
document.getElementById('btnSaveZip').disabled = false;
|
||||||
document.getElementById('btnUpload').disabled = false;
|
document.getElementById('btnUpload').disabled = false;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if (true){
|
||||||
|
moveData(resultJson);
|
||||||
|
console.log("Created data file", resourceDataString, resourceDataOffset, resultJson);
|
||||||
|
}
|
||||||
document.getElementById('btnSave').disabled = false;
|
document.getElementById('btnSave').disabled = false;
|
||||||
document.getElementById('btnSaveFace').disabled = false;
|
document.getElementById('btnSaveFace').disabled = false;
|
||||||
document.getElementById('btnUpload').disabled = false;
|
document.getElementById('btnUpload').disabled = false;
|
||||||
|
@ -706,6 +724,30 @@
|
||||||
};
|
};
|
||||||
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
||||||
|
|
||||||
|
function moveData(json){
|
||||||
|
console.log("MoveData for", json);
|
||||||
|
for (var k in json){
|
||||||
|
var c = json[k];
|
||||||
|
if (c.img){
|
||||||
|
var currentData = c.img;
|
||||||
|
delete c.img;
|
||||||
|
c.dataOffset = resourceDataOffset;
|
||||||
|
c.dataLength = currentData.length;
|
||||||
|
resourceDataString += currentData;
|
||||||
|
resourceDataOffset += currentData.length;
|
||||||
|
} else if (c.compressed){
|
||||||
|
var currentData = c.compressed;
|
||||||
|
c.compressed = true;
|
||||||
|
c.dataOffset = resourceDataOffset;
|
||||||
|
c.dataLength = currentData.length;
|
||||||
|
resourceDataString += currentData;
|
||||||
|
resourceDataOffset += currentData.length;
|
||||||
|
} else {
|
||||||
|
moveData(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("btnSave").addEventListener("click", function() {
|
document.getElementById("btnSave").addEventListener("click", function() {
|
||||||
var h = document.createElement('a');
|
var h = document.createElement('a');
|
||||||
h.href = 'data:text/json;charset=utf-8,' + encodeURI(JSON.stringify(resultJson));
|
h.href = 'data:text/json;charset=utf-8,' + encodeURI(JSON.stringify(resultJson));
|
||||||
|
@ -714,6 +756,7 @@
|
||||||
h.click();
|
h.click();
|
||||||
});
|
});
|
||||||
document.getElementById("btnUpload").addEventListener("click", function() {
|
document.getElementById("btnUpload").addEventListener("click", function() {
|
||||||
|
|
||||||
var appDef = {
|
var appDef = {
|
||||||
id : "imageclock",
|
id : "imageclock",
|
||||||
storage:[
|
storage:[
|
||||||
|
@ -723,6 +766,10 @@
|
||||||
{name:"imageclock.img", url:"app-icon.js", evaluate:true},
|
{name:"imageclock.img", url:"app-icon.js", evaluate:true},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
if (resourceDataString.length > 0){
|
||||||
|
appDef.storage.push({name:"imageclock.resources.data", content: resourceDataString});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
console.log("Uploading app:", appDef);
|
console.log("Uploading app:", appDef);
|
||||||
sendCustomizedApp(appDef);
|
sendCustomizedApp(appDef);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "imageclock",
|
"id": "imageclock",
|
||||||
"name": "Imageclock",
|
"name": "Imageclock",
|
||||||
"shortName": "imageclock",
|
"shortName": "imageclock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
Loading…
Reference in New Issue