michael@0: // -*- mode: js2; indent-tabs-mode: nil; -*- michael@0: michael@0: // A RectArray is a subclass of Array with 'width', 'height', michael@0: // and 'payload' properties (where payload holds 'k'). michael@0: // properties, and 'width*height*k' elements (each an X) in the array. michael@0: // michael@0: // A RectTypedByteArray is a subclass of ArrayBuffer with 'width' michael@0: // and 'height' properties, and 'width*height*k' bytes in the buffer. michael@0: // michael@0: // The 'payload' property is initialized with the value of 'k'. michael@0: // michael@0: // (Felix would have used Array or ArrayView, but for the following bug: michael@0: // Bug 695438 - TypedArrays don't support new named properties michael@0: // https://bugzilla.mozilla.org/show_bug.cgi?id=695438 michael@0: // and so he is resorting to extending ArrayBuffer instead.) michael@0: // michael@0: // Both classes add a .get(x,y[,k]) method that eases access to the michael@0: // contents, and a set(x,y[,k],value) method that eases modifying michael@0: // their entries. michael@0: // michael@0: // In addition, for those who prefer functional-style, michael@0: // RectArray.build, michael@0: // and RectByteTypedArray.build michael@0: michael@0: var RectArray, RectByteTypedArray; michael@0: michael@0: // This is a variant of RectArray that supports the same interface, michael@0: // but instead of attempting to extend Array (a practice fraught with michael@0: // peril), it instead makes a wrapper around another array. michael@0: michael@0: var WrapArray, WrapByteTypedArray; michael@0: michael@0: (function (){ michael@0: michael@0: function defineReadOnly(x, name, value) { michael@0: Object.defineProperty(x, name, { michael@0: value: value, michael@0: writable: false, michael@0: enumerable: true, michael@0: configurable: true }); michael@0: } michael@0: michael@0: RectArray = function RectArray(w,h,k) { michael@0: if (k === undefined) michael@0: k = 1; michael@0: this.length = w*h*k; michael@0: defineReadOnly(this, "width", w); michael@0: defineReadOnly(this, "height", h); michael@0: defineReadOnly(this, "payload", k); michael@0: }; michael@0: michael@0: RectByteTypedArray = function RectByteTypedArray(w,h,k) { michael@0: if (k === undefined) michael@0: k = 1; michael@0: ArrayBuffer.call(this, w*h*k); michael@0: defineReadOnly(this, "width", w); michael@0: defineReadOnly(this, "height", h); michael@0: defineReadOnly(this, "payload", k); michael@0: }; michael@0: michael@0: WrapArray = function WrapArray(w,h,k) { michael@0: if (k === undefined) michael@0: k = 1; michael@0: this.backingArray = new Array(w*h*k); michael@0: defineReadOnly(this, "width", w); michael@0: defineReadOnly(this, "height", h); michael@0: defineReadOnly(this, "payload", k); michael@0: }; michael@0: michael@0: WrapByteTypedArray = function WrapByteTypedArray(w,h,k) { michael@0: if (k === undefined) michael@0: k = 1; michael@0: this.backingArray = new Uint8Array(new ArrayBuffer(w*h*k)); michael@0: defineReadOnly(this, "width", w); michael@0: defineReadOnly(this, "height", h); michael@0: defineReadOnly(this, "payload", k); michael@0: }; michael@0: michael@0: WrapArray.prototype.slice = function(a,b) this.backingArray.slice(a,b); michael@0: WrapArray.prototype.join = function(a) this.backingArray.join(a); michael@0: michael@0: RectArray.prototype = new Array(); michael@0: RectByteTypedArray.prototype = new ArrayBuffer(); michael@0: michael@0: RectArray.prototype.get = function get(x,y,j) { michael@0: if (j === undefined) j = 0; michael@0: return this[(y*this.width+x)*this.payload+j]; michael@0: }; michael@0: michael@0: RectArray.prototype.set = function set(x,y,j,value) { michael@0: if (value === undefined) { michael@0: value = j; michael@0: j = 0; michael@0: } michael@0: this[(y*this.width+x)*this.payload+j] = value; michael@0: }; michael@0: michael@0: RectByteTypedArray.prototype.get = function get(x,y,j) { michael@0: if (j === undefined) j = 0; michael@0: return (new Uint8Array(this))[(y*this.width+x)*this.payload+j]; michael@0: }; michael@0: michael@0: RectByteTypedArray.prototype.set = function set(x,y,j,value) { michael@0: if (value === undefined) { michael@0: value = j; michael@0: j = 0; michael@0: } michael@0: (new Uint8Array(this))[(y*this.width+x)*this.payload+j] = value; michael@0: }; michael@0: michael@0: WrapArray.prototype.get = function get(x,y,j) { michael@0: if (j === undefined) j = 0; michael@0: return this.backingArray[(y*this.width+x)*this.payload+j]; michael@0: }; michael@0: michael@0: WrapArray.prototype.set = function set(x,y,j,value) { michael@0: if (value === undefined) { michael@0: value = j; michael@0: j = 0; michael@0: } michael@0: this.backingArray[(y*this.width+x)*this.payload+j] = value; michael@0: }; michael@0: michael@0: WrapByteTypedArray.prototype.get = function get(x,y,j) { michael@0: if (j === undefined) j = 0; michael@0: return this.backingArray[(y*this.width+x)*this.payload+j]; michael@0: }; michael@0: michael@0: WrapByteTypedArray.prototype.set = function set(x,y,j,value) { michael@0: if (value === undefined) { michael@0: value = j; michael@0: j = 0; michael@0: } michael@0: this.backingArray[(y*this.width+x)*this.payload+j] = value; michael@0: }; michael@0: michael@0: function viewToSource(view, width, height, payload) { michael@0: var ret = "["; michael@0: var i=0; michael@0: var matrixNeedsNewline = false; michael@0: for (var row=0; row < height; row++) { michael@0: if (matrixNeedsNewline) michael@0: ret += ",\n "; michael@0: ret += "["; michael@0: var rowNeedsComma = false; michael@0: for (var x=0; x < width; x++) { michael@0: if (rowNeedsComma) michael@0: ret += ", "; michael@0: if (payload == 1) { michael@0: if (view[i] !== undefined) michael@0: ret += view[i]; michael@0: i++; michael@0: } else { michael@0: var entryNeedsComma = false; michael@0: ret += "("; michael@0: for (var k=0; k < payload; k++) { michael@0: // Might be inefficient (does JavaScript have michael@0: // StringBuffers?, or use them internally, like Tamarin?) michael@0: if (entryNeedsComma) michael@0: ret += ", "; michael@0: if (view[i] !== undefined) michael@0: ret += view[i]; michael@0: entryNeedsComma = true; michael@0: i++; michael@0: } michael@0: ret += ")"; michael@0: } michael@0: rowNeedsComma = true; michael@0: } michael@0: ret += "]"; michael@0: matrixNeedsNewline = true; michael@0: } michael@0: ret += "]"; michael@0: return ret; michael@0: } michael@0: michael@0: RectArray.prototype.toSource = function toSource() { michael@0: return viewToSource(this, michael@0: this.width, this.height, this.payload); michael@0: }; michael@0: michael@0: RectByteTypedArray.prototype.toSource = function toSource() { michael@0: return viewToSource(new Uint8Array(this), michael@0: this.width, this.height, this.payload); michael@0: }; michael@0: michael@0: WrapArray.prototype.toSource = function toSource() { michael@0: return viewToSource(this.backingArray, michael@0: this.width, this.height, this.payload); michael@0: }; michael@0: michael@0: WrapByteTypedArray.prototype.toSource = function toSource() { michael@0: return viewToSource(this.backingArray, michael@0: this.width, this.height, this.payload); michael@0: }; michael@0: michael@0: RectArray.prototype.map = function map(f) { michael@0: var ret = Array.map(this, f); michael@0: ret.__proto__ = RectArray.prototype; michael@0: defineReadOnly(ret, "width", this.width); michael@0: defineReadOnly(ret, "height", this.height); michael@0: defineReadOnly(ret, "payload", this.payload); michael@0: return ret; michael@0: }; michael@0: michael@0: WrapArray.prototype.map = function map(f) { michael@0: var ret = new WrapArray(this.width, this.height, this.payload); michael@0: ret.backingArray = this.backingArray.map(f); michael@0: return ret; michael@0: }; michael@0: michael@0: // (Array|ArrayView) Nat Nat Nat (Nat Nat Nat -> X) -> void michael@0: function fillArrayView(view, width, height, k, fill) { michael@0: var i = 0; michael@0: for (var y=0; y < height; y++) { michael@0: for (var x=0; x < width; x++) { michael@0: for (var j=0; j < k; j++) { michael@0: view[i++] = fill(x, y, j); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // Nat Nat (Nat Nat [Nat] -> X) -> RectArray michael@0: RectArray.build = michael@0: function buildRectArray1(width, height, fill) { michael@0: var a = new RectArray(width, height, 1); michael@0: fillArrayView(a, width, height, 1, fill); michael@0: return a; michael@0: }; michael@0: michael@0: RectArray.buildA = michael@0: function buildRectArrayA(width, height, fill) { michael@0: var a = new Array(width*height); michael@0: fillArrayView(a, width, height, 1, fill); michael@0: a.width = width; michael@0: a.height = height; michael@0: a.payload = 1; michael@0: function F() { } michael@0: F.prototype = RectArray.prototype; michael@0: a.__proto__ = new F(); michael@0: return a; michael@0: }; michael@0: michael@0: // Nat Nat (Nat Nat Nat -> X) -> RectArray michael@0: RectArray.build4 = michael@0: function buildRectArray4(width, height, fill) { michael@0: var a = new RectArray(width, height, 4); michael@0: fillArrayView(a, width, height, 4, fill); michael@0: return a; michael@0: }; michael@0: michael@0: // Nat Nat (Nat Nat Nat -> X) -> RectArray michael@0: RectArray.buildN = michael@0: function buildRectArrayN(width, height, n, fill) { michael@0: var a = new RectArray(width, height, n); michael@0: fillArrayView(a, width, height, n, fill); michael@0: return a; michael@0: }; michael@0: michael@0: michael@0: // Nat Nat (Nat Nat [Nat] -> Byte) -> RectTypedByteArray<4> michael@0: RectByteTypedArray.build = michael@0: function buildRectByteTypedArray1(width, height, fill) { michael@0: var buf = new RectByteTypedArray(width, height, 1); michael@0: fillArrayView(new Uint8Array(buf), width, height, 1, fill); michael@0: return buf; michael@0: }; michael@0: michael@0: // Nat Nat (Nat Nat Nat -> Byte) -> RectTypedByteArray<4> michael@0: RectByteTypedArray.build4 = michael@0: function buildRectByteTypedArray4(width, height, fill) { michael@0: var buf = new RectByteTypedArray(width, height, 4); michael@0: fillArrayView(new Uint8Array(buf), width, height, 4, fill); michael@0: return buf; michael@0: }; michael@0: michael@0: // Nat Nat (Nat Nat Nat -> Byte) -> RectTypedByteArray<4> michael@0: RectByteTypedArray.buildN = michael@0: function buildRectByteTypedArray4(width, height, n, fill) { michael@0: var buf = new RectByteTypedArray(width, height, n); michael@0: fillArrayView(new Uint8Array(buf), width, height, n, fill); michael@0: return buf; michael@0: }; michael@0: michael@0: WrapArray.build = michael@0: function buildWrapArray1(width, height, fill) { michael@0: var a = new WrapArray(width, height, 1); michael@0: fillArrayView(a, width, height, 1, fill); michael@0: return a; michael@0: }; michael@0: michael@0: WrapArray.build = michael@0: function buildWrapArray1(width, height, fill) { michael@0: var a = new WrapArray(width, height, 1); michael@0: fillArrayView(a.backingArray, width, height, 1, fill); michael@0: return a; michael@0: }; michael@0: michael@0: WrapArray.build4 = michael@0: function buildWrapArray1(width, height, fill) { michael@0: var a = new WrapArray(width, height, 4); michael@0: fillArrayView(a.backingArray, width, height, 4, fill); michael@0: return a; michael@0: }; michael@0: michael@0: WrapArray.buildN = michael@0: function buildWrapArray1(width, height, n, fill) { michael@0: var a = new WrapArray(width, height, n); michael@0: fillArrayView(a.backingArray, width, height, n, fill); michael@0: return a; michael@0: }; michael@0: michael@0: })();