284 lines
8.6 KiB
JavaScript
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('');
|
|
};
|