| 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; |
| }, |
|
|
| deserializePNG: function(blob, callback) { |
| var reader = new FileReader(); |
| reader.onload = function(data) { |
| var image = document.createElement('img'); |
| image.setAttribute('src', data.target.result); |
| 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); |
| } |
| } |
| } |
| 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(''); |
| }; |
|
|