emulator/gba3/js/util.js
2019-06-14 13:47:15 +08:00

284 lines
8.6 KiB
JavaScript

Object.prototype.inherit = function() {
for (var v in this) {
this[v] = this[v];
}
};
function hex(number, leading, usePrefix) {
if (typeof(usePrefix) === 'undefined') {
usePrefix = true;
}
if (typeof(leading) === 'undefined') {
leading = 8;
}
var string = (number >>> 0).toString(16).toUpperCase();
leading -= string.length;
if (leading < 0)
return string;
return (usePrefix ? '0x' : '') + new Array(leading + 1).join('0') + string;
}
Serializer = {
TAG_INT: 1,
TAG_STRING: 2,
TAG_STRUCT: 3,
TAG_BLOB: 4,
TAG_BOOLEAN: 5,
TYPE: 'application/octet-stream',
pointer: function() {
this.index = 0;
this.top = 0;
this.stack = [];
},
pack: function(value) {
var object = new DataView(new ArrayBuffer(4));
object.setUint32(0, value, true);
return object.buffer;
},
pack8: function(value) {
var object = new DataView(new ArrayBuffer(1));
object.setUint8(0, value, true);
return object.buffer;
},
prefix: function(value) {
return new Blob([Serializer.pack(value.size || value.length || value.byteLength), value], { type: Serializer.TYPE });
},
serialize: function(stream) {
var parts = [];
var size = 4;
for (i in stream) {
if (stream.hasOwnProperty(i)) {
var tag;
var head = Serializer.prefix(i);
var body;
switch (typeof(stream[i])) {
case 'number':
tag = Serializer.TAG_INT;
body = Serializer.pack(stream[i]);
break;
case 'string':
tag = Serializer.TAG_STRING;
body = Serializer.prefix(stream[i]);
break;
case 'object':
if (stream[i].type == Serializer.TYPE) {
tag = Serializer.TAG_BLOB;
body = stream[i];
} else {
tag = Serializer.TAG_STRUCT;
body = Serializer.serialize(stream[i]);
}
break;
case 'boolean':
tag = Serializer.TAG_BOOLEAN;
body = Serializer.pack8(stream[i]);
break;
default:
console.log(stream[i]);
break;
}
size += 1 + head.size + (body.size || body.byteLength || body.length);
parts.push(Serializer.pack8(tag));
parts.push(head);
parts.push(body);
}
}
parts.unshift(Serializer.pack(size));
return new Blob(parts);
},
deserialize: function(blob, callback) {
var reader = new FileReader();
reader.onload = function(data) {
callback(Serializer.deserealizeStream(new DataView(data.target.result), new Serializer.pointer));
}
reader.readAsArrayBuffer(blob);
},
deserealizeStream: function(view, pointer) {
pointer.push();
var object = {};
var remaining = view.getUint32(pointer.advance(4), true);
while (pointer.mark() < remaining) {
var tag = view.getUint8(pointer.advance(1));
var head = pointer.readString(view);
var body;
switch (tag) {
case Serializer.TAG_INT:
body = view.getUint32(pointer.advance(4), true);
break;
case Serializer.TAG_STRING:
body = pointer.readString(view);
break;
case Serializer.TAG_STRUCT:
body = Serializer.deserealizeStream(view, pointer);
break;
case Serializer.TAG_BLOB:
var size = view.getUint32(pointer.advance(4), true);
body = view.buffer.slice(pointer.advance(size), pointer.advance(0));
break;
case Serializer.TAG_BOOLEAN:
body = !!view.getUint8(pointer.advance(1));
break;
}
object[head] = body;
}
if (pointer.mark() > remaining) {
throw "Size of serialized data exceeded";
}
pointer.pop();
return object;
},
serializePNG: function(blob, base, callback) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var pixels = base.getContext('2d').getImageData(0, 0, base.width, base.height);
var transparent = 0;
for (var y = 0; y < base.height; ++y) {
for (var x = 0; x < base.width; ++x) {
if (!pixels.data[(x + y * base.width) * 4 + 3]) {
++transparent;
}
}
}
var bytesInCanvas = transparent * 3 + (base.width * base.height - transparent);
for (var multiplier = 1; (bytesInCanvas * multiplier * multiplier) < blob.size; ++multiplier);
var edges = bytesInCanvas * multiplier * multiplier - blob.size;
var padding = Math.ceil(edges / (base.width * multiplier));
canvas.setAttribute('width', base.width * multiplier);
canvas.setAttribute('height', base.height * multiplier + padding);
var reader = new FileReader();
reader.onload = function(data) {
var view = new Uint8Array(data.target.result);
var pointer = 0;
var pixelPointer = 0;
var newPixels = context.createImageData(canvas.width, canvas.height + padding);
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
var oldY = (y / multiplier) | 0;
var oldX = (x / multiplier) | 0;
if (oldY > base.height || !pixels.data[(oldX + oldY * base.width) * 4 + 3]) {
newPixels.data[pixelPointer++] = view[pointer++];
newPixels.data[pixelPointer++] = view[pointer++];
newPixels.data[pixelPointer++] = view[pointer++];
newPixels.data[pixelPointer++] = 0;
} else {
var byte = view[pointer++];
newPixels.data[pixelPointer++] = pixels.data[(oldX + oldY * base.width) * 4 + 0] | (byte & 7);
newPixels.data[pixelPointer++] = pixels.data[(oldX + oldY * base.width) * 4 + 1] | ((byte >> 3) & 7);
newPixels.data[pixelPointer++] = pixels.data[(oldX + oldY * base.width) * 4 + 2] | ((byte >> 6) & 7);
newPixels.data[pixelPointer++] = pixels.data[(oldX + oldY * base.width) * 4 + 3];
}
}
}
context.putImageData(newPixels, 0, 0);
callback(canvas.toDataURL('image/png'));
}
reader.readAsArrayBuffer(blob);
return canvas;
},
deserializeCurrentPNG: function(image, callback) {
var canvas = document.createElement('canvas');
canvas.setAttribute('height', image.height);
canvas.setAttribute('width', image.width);
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
var pixels = context.getImageData(0, 0, canvas.width, canvas.height);
var data = [];
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
if (!pixels.data[(x + y * canvas.width) * 4 + 3]) {
data.push(pixels.data[(x + y * canvas.width) * 4 + 0]);
data.push(pixels.data[(x + y * canvas.width) * 4 + 1]);
data.push(pixels.data[(x + y * canvas.width) * 4 + 2]);
} else {
var byte = 0;
byte |= pixels.data[(x + y * canvas.width) * 4 + 0] & 7;
byte |= (pixels.data[(x + y * canvas.width) * 4 + 1] & 7) << 3;
byte |= (pixels.data[(x + y * canvas.width) * 4 + 2] & 7) << 6;
data.push(byte);
}
}
}
var newBlob = new Blob(data.map(function (byte) {
var array = new Uint8Array(1);
array[0] = byte;
return array;
}), { type: Serializer.TYPE});
Serializer.deserialize(newBlob, callback);
},
deserializePNG: function(blob, callback) {
var reader = new FileReader();
reader.onload = function(dataImg) {
var image = document.createElement('img');
image.setAttribute('src', dataImg.target.result);
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.setAttribute('height', image.height);
canvas.setAttribute('width', image.width);
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
var pixels = context.getImageData(0, 0, canvas.width, canvas.height);
var data = [];
for (var y = 0; y < canvas.height; ++y) {
for (var x = 0; x < canvas.width; ++x) {
if (!pixels.data[(x + y * canvas.width) * 4 + 3]) {
data.push(pixels.data[(x + y * canvas.width) * 4 + 0]);
data.push(pixels.data[(x + y * canvas.width) * 4 + 1]);
data.push(pixels.data[(x + y * canvas.width) * 4 + 2]);
} else {
var byte = 0;
byte |= pixels.data[(x + y * canvas.width) * 4 + 0] & 7;
byte |= (pixels.data[(x + y * canvas.width) * 4 + 1] & 7) << 3;
byte |= (pixels.data[(x + y * canvas.width) * 4 + 2] & 7) << 6;
data.push(byte);
}
}
}
var newBlob = new Blob(data.map(function (byte) {
var array = new Uint8Array(1);
array[0] = byte;
return array;
}), { type: Serializer.TYPE});
Serializer.deserialize(newBlob, callback);
}
}
reader.readAsDataURL(blob);
}
};
Serializer.pointer.prototype.advance = function(amount) {
var index = this.index;
this.index += amount;
return index;
};
Serializer.pointer.prototype.mark = function() {
return this.index - this.top;
};
Serializer.pointer.prototype.push = function() {
this.stack.push(this.top);
this.top = this.index;
};
Serializer.pointer.prototype.pop = function() {
this.top = this.stack.pop();
};
Serializer.pointer.prototype.readString = function(view) {
var length = view.getUint32(this.advance(4), true);
var bytes = [];
for (var i = 0; i < length; ++i) {
bytes.push(String.fromCharCode(view.getUint8(this.advance(1))));
}
return bytes.join('');
};