From 3ec41e43eee0e3f3f74ad8d007ad4b02d868fd68 Mon Sep 17 00:00:00 2001 From: Skyler Lehmkuhl Date: Thu, 9 Feb 2012 14:11:01 -0500 Subject: [PATCH] Work on HTML5 BitmapData class --- flash/display.js | 776 +++++++++++++++++++++++++++++++++++++++++++++++ flash/filters.js | 304 +++++++++++++++++++ flash/geom.js | 271 +++++++++++++++++ 3 files changed, 1351 insertions(+) create mode 100644 flash/display.js create mode 100644 flash/filters.js create mode 100644 flash/geom.js diff --git a/flash/display.js b/flash/display.js new file mode 100644 index 0000000..2d24a9a --- /dev/null +++ b/flash/display.js @@ -0,0 +1,776 @@ +/* + * BitmapData.js by Peter Nitsch - https://github.com/pnitsch/BitmapData.js + * HTML5 Canvas API implementation of the AS3 BitmapData class. + */ + +const halfColorMax = 0.00784313725; + +var BlendMode = new function() { + this.ADD = "add"; + this.ALPHA = "alpha"; + this.DARKEN = "darken"; + this.DIFFERENCE = "difference"; + this.ERASE = "erase"; + this.HARDLIGHT = "hardlight"; + this.INVERT = "invert"; + this.LAYER = "layer"; + this.LIGHTEN = "lighten"; + this.HARDLIGHT = "hardlight"; + this.MULTIPLY = "multiply"; + this.NORMAL = "normal"; + this.OVERLAY = "overlay"; + this.SCREEN = "screen"; + this.SHADER = "shader"; + this.SUBTRACT = "subtract"; +}; + +var BitmapDataChannel = new function() { + this.ALPHA = 8; + this.BLUE = 4; + this.GREEN = 2; + this.RED = 1; +}; + +// RGB <-> Hex conversion +function hexToRGB (hex) { return { r: ((hex & 0xff0000) >> 16), g: ((hex & 0x00ff00) >> 8), b: ((hex & 0x0000ff)) }; }; +function RGBToHex(rgb) { return rgb.r<<16 | rgb.g<<8 | rgb.b; }; + +// 256-value binary Vector struct +function histogramVector(n) { + var v=[]; + for (var i=0; i<256; i++) { v[i] = n; } + return v +} + +// Park-Miller-Carta Pseudo-Random Number Generator +function PRNG() { + this.seed = 1; + this.next = function() { return (this.gen() / 2147483647); }; + this.nextRange = function(min, max) { return min + ((max - min) * this.next()) }; + this.gen = function() { return this.seed = (this.seed * 16807) % 2147483647; }; +}; + +function BitmapData(width, height, transparent, fillColor) { + this.width = width; + this.height = height; + this.rect = new Rectangle(0, 0, this.width, this.height); + this.transparent = transparent || false; + + + + /*this.canvas = canvas || document.createElement("canvas"); + this.context = this.canvas.getContext("2d"); + this.canvas.setAttribute('width', this.width); + this.canvas.setAttribute('height', this.height); + */ + this.drawingCanvas = document.createElement("canvas"); + this.drawingContext = this.drawingCanvas.getContext("2d"); + + this.imagedata = this.context.createImageData(this.width, this.height); + this.__defineGetter__("data", function() { return this.imagedata; }); + this.__defineSetter__("data", function(source) { this.imagedata = source; }); + + + /*** WebGL functions ***/ + /* + this.glCanvas = document.createElement("canvas"); + this.gl = null; + this.program = null; + this.gpuEnabled = true; + try { this.gl = this.glCanvas.getContext("experimental-webgl"); } + catch (e) { this.gpuEnabled = false; } + + this.va = null; + this.tex0 = null; + this.tex1 = null; + this.glPixelArray = null; + + this.initProgram = function(effect) { + var gl = this.gl; + var program = gl.createProgram(); + + var vs = gl.createShader(gl.VERTEX_SHADER); + var fs = gl.createShader(gl.FRAGMENT_SHADER); + + gl.shaderSource(vs, effect.vsSrc); + gl.shaderSource(fs, effect.fsSrc); + gl.compileShader(vs); + gl.compileShader(fs); + + if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { gl.deleteProgram( program ); } + if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { gl.deleteProgram( program ); } + + gl.attachShader(program, vs); + gl.attachShader(program, fs); + gl.deleteShader(vs); + gl.deleteShader(fs); + + gl.linkProgram(program); + if( this.program != null ) gl.deleteProgram( this.program ); + this.program = program; + + gl.viewport( 0, 0, this.canvas.width, this.canvas.height ); + gl.useProgram(program); + + var vertices = new Float32Array( + [-1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0]); + + this.va = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, this.va); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + }; + + this.initTexture = function(pos, image) { + var gl = this.gl; + var tex = gl.createTexture(); + + gl.enable(gl.TEXTURE_2D); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + gl.generateMipmap(gl.TEXTURE_2D) + gl.bindTexture(gl.TEXTURE_2D, null); + + if( pos == 0 ) { + if(this.tex0 != null) gl.deleteTexture(this.tex0); + this.tex0 = tex; + + this.glCanvas.setAttribute('width', image.width); + this.glCanvas.setAttribute('height', image.height); + this.glPixelArray = new Uint8Array(image.width * image.height * 4); + } else { + if(this.tex1 != null) gl.deleteTexture(this.tex1); + this.tex1 = tex; + } + }; + + this.drawGL = function(matrix) { + var gl = this.gl; + var program = this.program; + var ra = [matrix.a, matrix.c, 0, matrix.b, matrix.d, 0, 0, 0, 1]; + + var p = gl.getAttribLocation(program, "pos"); + var ur = gl.getUniformLocation(program, "r"); + var ut = gl.getUniformLocation(program, "t"); + var t0 = gl.getUniformLocation(program, "tex0"); + var t1 = gl.getUniformLocation(program, "tex1"); + var rm = gl.getUniformLocation(program, "rMat"); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.va); + + gl.uniform2f(ur, this.glCanvas.width*2, this.glCanvas.height*2); + gl.uniformMatrix3fv(rm, false, new Float32Array(ra)); + gl.uniform2f(ut, matrix.tx, matrix.ty); + + gl.vertexAttribPointer(p, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(p); + + gl.uniform1i(t0, 0 ); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, this.tex0); + + gl.uniform1i(t1, 1 ); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, this.tex1); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + gl.disableVertexAttribArray(p); + + gl.flush(); + + var w = this.glCanvas.width; + var h = this.glCanvas.height; + var arr = this.glPixelArray; + gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, arr); + + var pos; + var data = this.imagedata.data; + for (var y=0; y data[destPos]) data[destPos] = sourceData[sourcePos]; + if(sourceData[sourcePos+1] > data[destPos+1]) data[destPos+1] = sourceData[sourcePos+1]; + if(sourceData[sourcePos+2] > data[destPos+2]) data[destPos+2] = sourceData[sourcePos+2]; + break; + + case BlendMode.DARKEN: + if(sourceData[sourcePos] < data[destPos]) data[destPos] = sourceData[sourcePos]; + if(sourceData[sourcePos+1] < data[destPos+1]) data[destPos+1] = sourceData[sourcePos+1]; + if(sourceData[sourcePos+2] < data[destPos+2]) data[destPos+2] = sourceData[sourcePos+2]; + break; + + case BlendMode.DIFFERENCE: + data[destPos] = Math.abs(sourceData[sourcePos] - data[destPos]); + data[destPos+1] = Math.abs(sourceData[sourcePos+1] - data[destPos+1]); + data[destPos+2] = Math.abs(sourceData[sourcePos+2] - data[destPos+2]); + break; + + case BlendMode.SCREEN: + data[destPos] = (255 - ( ((255-data[destPos])*(255-sourceData[sourcePos])) >> 8)); + data[destPos+1] = (255 - ( ((255-data[destPos+1])*(255-sourceData[sourcePos+1])) >> 8)); + data[destPos+2] = (255 - ( ((255-data[destPos+2])*(255-sourceData[sourcePos+2])) >> 8)); + break; + + case BlendMode.OVERLAY: + if(sourceData[sourcePos] < 128) data[destPos] = data[destPos] * sourceData[sourcePos] * halfColorMax; + else data[destPos] = 255 - (255-data[destPos])*(255-sourceData[sourcePos])*halfColorMax; + + if(sourceData[sourcePos+1] < 128) data[destPos+1] = data[destPos+1] * sourceData[sourcePos+1] * halfColorMax; + else data[destPos+1] = 255 - (255-data[destPos+1])*(255-sourceData[sourcePos+1])*halfColorMax; + + if(sourceData[sourcePos+2] < 128) data[destPos+2] = data[destPos+2] * sourceData[sourcePos+2] * halfColorMax; + else data[destPos+2] = 255 - (255-data[destPos+2])*(255-sourceData[sourcePos+2])*halfColorMax; + break; + + case BlendMode.HARDLIGHT: + if(data[destPos] < 128) data[destPos] = data[destPos] * sourceData[sourcePos] * halfColorMax; + else data[destPos] = 255 - (255-data[destPos])*(255-sourceData[sourcePos])*halfColorMax; + + if(data[destPos+1] < 128) data[destPos+1] = data[destPos+1] * sourceData[sourcePos+1] * halfColorMax; + else data[destPos+1] = 255 - (255-data[destPos+1])*(255-sourceData[sourcePos+1])*halfColorMax; + + if(data[destPos+2] < 128) data[destPos+2] = data[destPos+2] * sourceData[sourcePos+2] * halfColorMax; + else data[destPos+2] = 255 - (255-data[destPos+2])*(255-sourceData[sourcePos+2])*halfColorMax; + break; + + } + } + } + + } else { + this.context.drawImage(sourceCanvas, + sourceRect.x, sourceRect.y, dw, dh, + destPoint.x, destPoint.y, dw, dh); + + this.imagedata = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); + } + + this.context.putImageData(this.imagedata, 0, 0); + }; + + this.copyChannel = function(sourceBitmapData, sourceRect, destPoint, sourceChannel, destChannel) { + var sourceColor, sourceRGB, rgb; + var redChannel = BitmapDataChannel.RED; + var greenChannel = BitmapDataChannel.GREEN; + var blueChannel = BitmapDataChannel.BLUE; + + for (var y=0; y 0) { + currPoint = queue.shift(); + ++iterations; + + if (currPoint.x < 0 || currPoint.x >= this.width) continue; + if (currPoint.y < 0 || currPoint.y >= this.height) continue; + + searchBmp.setPixel(currPoint.x, currPoint.y, 0x00); + + if (this.getPixel(currPoint.x, currPoint.y) == old) { + this.setPixel(currPoint.x, currPoint.y, color); + + if (searchBmp.getPixel(currPoint.x + 1, currPoint.y) == 0xffffff) { + queue.push(new Point(currPoint.x + 1, currPoint.y)); + } + if (searchBmp.getPixel(currPoint.x, currPoint.y + 1) == 0xffffff) { + queue.push(new Point(currPoint.x, currPoint.y + 1)); + } + if (searchBmp.getPixel(currPoint.x - 1, currPoint.y) == 0xffffff) { + queue.push(new Point(currPoint.x - 1, currPoint.y)); + } + if (searchBmp.getPixel(currPoint.x, currPoint.y - 1) == 0xffffff) { + queue.push(new Point(currPoint.x, currPoint.y - 1)); + } + } + } + + }; + + this.histogram = function(hRect) { + hRect = hRect || this.rect; + + var rgb = { r: [], g: [], b: [] }; + var rv = histogramVector(0); + var gv = histogramVector(0); + var bv = histogramVector(0); + + var p = hRect.width*hRect.height; + var itr = -1; + var pos; + var color = []; + + var bw = this.canvas.width - hRect.width - hRect.x; + var bh = this.canvas.height - hRect.height - hRect.y + var dw = (bw < 0) ? hRect.width + (this.canvas.width - hRect.width - hRect.x) : hRect.width; + var dh = (bh < 0) ? hRect.height + (this.canvas.height - hRect.height - hRect.y) : hRect.height; + + var data = this.imagedata.data; + + for(var y=hRect.y; y": + if((sourceHex & mask) > (threshold & mask)) { + if(copySource) this.setPixel(x+destPoint.x, y+destPoint.y, sourceHex); else this.setPixel(x+destPoint.x, y+destPoint.y, color); + } + break; + + case ">=": + if((sourceHex & mask) <= (threshold & mask)) { + if(copySource) this.setPixel(x+destPoint.x, y+destPoint.y, sourceHex); else this.setPixel(x+destPoint.x, y+destPoint.y, color); + } + break; + + case "==": + if((sourceHex & mask) == (threshold & mask)) { + if(copySource) this.setPixel(x+destPoint.x, y+destPoint.y, sourceHex); else this.setPixel(x+destPoint.x, y+destPoint.y, color); + } + break; + + case "!=": + if((sourceHex & mask) != (threshold & mask)) { + if(copySource) this.setPixel(x+destPoint.x, y+destPoint.y, sourceHex); else this.setPixel(x+destPoint.x, y+destPoint.y, color); + } + break; + } + + } + } + + this.context.putImageData(this.imagedata, 0, 0); + }; + + if(fillColor) this.fillRect(this.rect, fillColor); + else this.fillRect(this.rect, 0); + return this; +}; + +HTMLCanvasElement.prototype._bitmapData = null; +HTMLCanvasElement.prototype.__defineGetter__("bitmapData", function() { + if(!this._bitmapData) { + this._bitmapData = new BitmapData(this.width, this.height, false, 0, this); + } + return this._bitmapData; +}); \ No newline at end of file diff --git a/flash/filters.js b/flash/filters.js new file mode 100644 index 0000000..e80c438 --- /dev/null +++ b/flash/filters.js @@ -0,0 +1,304 @@ +function BitmapFilter () { + this.clone=function() { + return new BitmapFilter(); + } +} + +function ColorMatrixFilter(matrix) { + this.matrix=matrix || [ + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0 + ]; + + this.run=function(sourceRect, image, copy) { + var numPixel=image.length/4; + var m=this.matrix; + + for(var i=0;i> shg_sum; + if ( pa != 0 ) { + pa = 255 / pa; + image[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; + image[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa; + image[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa; + } else { + image[yi] = image[yi+1] = image[yi+2] = 0; + } + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; + + r_in_sum += ( stackIn.r = image[p]); + g_in_sum += ( stackIn.g = image[p+1]); + b_in_sum += ( stackIn.b = image[p+2]); + a_in_sum += ( stackIn.a = image[p+3]); + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + + stackIn = stackIn.next; + + r_out_sum += ( pr = stackOut.r ); + g_out_sum += ( pg = stackOut.g ); + b_out_sum += ( pb = stackOut.b ); + a_out_sum += ( pa = stackOut.a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += 4; + } + yw += width; + } + + + for ( x = 0; x < width; x++ ) { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + + yi = x << 2; + r_out_sum = radiusPlus1 * ( pr = image[yi]); + g_out_sum = radiusPlus1 * ( pg = image[yi+1]); + b_out_sum = radiusPlus1 * ( pb = image[yi+2]); + a_out_sum = radiusPlus1 * ( pa = image[yi+3]); + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for( i = 0; i < radiusPlus1; i++ ) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + yp = width; + + for( i = 1; i <= radius; i++ ) { + yi = ( yp + x ) << 2; + + r_sum += ( stack.r = ( pr = image[yi])) * ( rbs = radiusPlus1 - i ); + g_sum += ( stack.g = ( pg = image[yi+1])) * rbs; + b_sum += ( stack.b = ( pb = image[yi+2])) * rbs; + a_sum += ( stack.a = ( pa = image[yi+3])) * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack = stack.next; + + if( i < heightMinus1 ) + { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for ( y = 0; y < height; y++ ) + { + p = yi << 2; + image[p+3] = pa = (a_sum * mul_sum) >> shg_sum; + if ( pa > 0 ) + { + pa = 255 / pa; + image[p] = ((r_sum * mul_sum) >> shg_sum ) * pa; + image[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa; + image[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa; + } else { + image[p] = image[p+1] = image[p+2] = 0; + } + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; + + r_sum += ( r_in_sum += ( stackIn.r = image[p])); + g_sum += ( g_in_sum += ( stackIn.g = image[p+1])); + b_sum += ( b_in_sum += ( stackIn.b = image[p+2])); + a_sum += ( a_in_sum += ( stackIn.a = image[p+3])); + + stackIn = stackIn.next; + + r_out_sum += ( pr = stackOut.r ); + g_out_sum += ( pg = stackOut.g ); + b_out_sum += ( pb = stackOut.b ); + a_out_sum += ( pa = stackOut.a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += width; + } + } + + //context.putImageData( imageData, top_x, top_y ); + + function BlurStack() { + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.next = null; + } + } + this.clone = function () { + return new BlurFilter(this.blurX, this.blurY, this.quality) + } +} + +BlurFilter.inherits(BitmapFilter) \ No newline at end of file diff --git a/flash/geom.js b/flash/geom.js new file mode 100644 index 0000000..65bc0cc --- /dev/null +++ b/flash/geom.js @@ -0,0 +1,271 @@ +Object.prototype.addProperty = function ( name, getter, setter) { + this.__defineGetter__(name,getter) + if (setter) { + this.__defineSetter__(name,setter) + } +} + +Object.prototype.isPropertyEnumerable = function (name) { + return this.propertyIsEnumerable(name); +} + +Object.defineProperty( Object.prototype, "addProperty", {enumerable: false}); +Object.defineProperty( Object.prototype, "isPropertyEnumerable", {enumerable: false}); + +function Point (x, y) { + this.x = x + this.y = y + this.getlength = function () { + return Math.sqrt(this.x*this.x + this.y*this.y) + } + this.addProperty('length',this.getlength) + this.add = function (v) { + return Point (this.x+v.x, this.y+v.y) + } + this.clone = function () { + return Point (this.x, this.y) + } + this.equals = function (toCompare) { + return (this.x==toCompare.x && this.y==toCompare.y) + } + this.normalize = function (length) { + x = this.x/((this.length*1.0))*length + y = this.y/((this.length*1.0))*length + this.x = x + this.y = y + } + this.offset = function (dx, dy) { + this.x += dx + this.y += dy + } + this.subtract = function (v) { + return Point(this.x-v.x, this.y-v.y) + } + this.toString = function () { + return "(x="+this.x+", y="+this.y+")" + } +} +Point.distance = function (pt1, pt2) { + return Math.sqrt((pt2.x-pt1.x)*(pt2.x-pt1.x) + (pt2.y-pt1.y)*(pt2.y-pt1.y)) +} +Point.interpolate = function (pt1, pt2, f) { + return Point(ave (pt1.x, pt2.x, f), ave (pt1.y, pt2.y, f) ) +} +Point.polar = function (len, angle) { + return Point(len*Math.cos(angle), len*Math.sin(angle)) +} + +function Rectangle (x, y, width, height) { + this.x = x + this.y = y + this.width = width + this.height = height + this.getbottom = function () { + return this.y+this.height; + } + this.getbottomRight = function () { + return Point(this.x + this.width, this.y + this.height) + } + this.getsize = function () { + return Point(this.width, this.height) + } + this.getleft = function () { + return this.x + } + this.getright = function () { + return this.x + this.width + } + this.gettop = function () { + return this.y + } + this.gettopLeft = function () { + return Point(this.x, this.y) + } + this.addProperty('bottom',this.getbottom); + this.addProperty('bottomRight',this.getbottomRight); + this.addProperty('size',this.getsize); + this.addProperty('left',this.getleft); + this.addProperty('right',this.getright); + this.addProperty('top',this.gettop); + this.addProperty('topLeft',this.gettopLeft); + this.clone = function () { + return Rectangle(this.x, this.y, this.width, this.height); + } + this.contains = function (x, y) { + return ((x>this.x && x(this.y && ythis.x && pt.xthis.y && pt.ythis.x && rect.rightthis.y && rect.bottomx && bottom>y) { + return Rectangle(x, y, right-x, bottom-y) + } else { + return Rectangle (0, 0, 0, 0) + } + } + this.intersects = function (toIntersect) { + x = Math.max(this.x, toIntersect.x); + y = Math.max(this.y, toIntersect.y); + right = Math.min(this.right, toIntersect.right) + bottom = Math.min(this.bottom, toIntersect.bottom) + if (right>x && bottom>y) { + return true + } else { + return false + } + } + this.isEmpty = function () { + if (this.width<=0) { + return true + } else if (this.height<=0) { + return true + } else { + return false + } + } + this.offset = function (dx, dy) { + this.x += dx; + this.y += dy; + } + this.offsetPoint = function (pt) { + this.x += pt.x; + this.y += pt.y; + } + this.setEmpty = function () { + this.x = 0; + this.y = 0; + this.width = 0; + this.height = 0; + } + this.toString = function () { + return "(x="+this.x+", y="+this.y+", w="+this.width+", h="+this.height+")" + } + this.union = function (toUnion) { + x = Math.min(this.x, toUnion.x); + y = Math.min(this.y, toUnion.y); + right = Math.max(this.right, toUnion.right) + bottom = Math.max(this.bottom, toUnion.bottom) + return Rectangle(x, y, right-x, bottom-y) + } +} + +function radianToDegree(angle) { return angle * (180.0 / Math.PI); } +function degreeToRadian(angle) { return Math.PI * angle / 180.0; } + +function Matrix(a, b, c, d, tx, ty) { + this.elements = [a||1, c||0, tx||0, + b||0, d||1, ty||0]; + + this.__defineGetter__("a", function() { return this.elements[0]; }); + this.__defineSetter__("a", function(n) { this.elements[0]=n; }); + this.__defineGetter__("b", function() { return this.elements[3]; }); + this.__defineSetter__("b", function(n) { this.elements[3]=n; }); + this.__defineGetter__("c", function() { return this.elements[1]; }); + this.__defineSetter__("c", function(n) { this.elements[1]=n; }); + this.__defineGetter__("d", function() { return this.elements[4]; }); + this.__defineSetter__("d", function(n) { this.elements[4]=n; }); + this.__defineGetter__("tx", function() { return this.elements[2]; }); + this.__defineSetter__("tx", function(n) { this.elements[2]=n; }); + this.__defineGetter__("ty", function() { return this.elements[5]; }); + this.__defineSetter__("ty", function(n) { this.elements[5]=n; }); + + this.clone = function() { + }; + + this.concat = function(m) { + }; + + this.identity = function() { + this.elements = [1, 0, 0, 1, 0, 0]; + }; + + this.scale = function(sx, sy) { + if (sx && !sy) { + sy = sx; + } + if (sx && sy) { + this.elements[0] *= sx; + this.elements[1] *= sy; + this.elements[3] *= sx; + this.elements[4] *= sy; + } + }; + + this.translate = function(dx, dy) { + this.elements[2] = dx * this.elements[0] + dy * this.elements[1] + this.elements[2]; + this.elements[5] = dx * this.elements[3] + dy * this.elements[4] + this.elements[5]; + }; + + this.angle = 0; // faster but dumber method + + this.rotate = function(angle) { + this.angle += angle; + + r = radianToDegree(angle); + c = Math.cos(angle); + s = Math.sin(angle); + + temp1 = this.elements[0]; + temp2 = this.elements[1]; + this.elements[0] = c * temp1 + s * temp2; + this.elements[1] = -s * temp1 + c * temp2; + + temp1 = this.elements[3]; + temp2 = this.elements[4]; + this.elements[3] = c * temp1 + s * temp2; + this.elements[4] = -s * temp1 + c * temp2; + + }; + + +} + +function ColorTransform(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) { + this.redMultiplier=redMultiplier==undefined?1:redMultiplier; + this.greenMultiplier=greenMultiplier==undefined?1:greenMultiplier; + this.blueMultiplier=blueMultiplier==undefined?1:blueMultiplier; + this.alphaMultiplier=alphaMultiplier==undefined?1:alphaMultiplier; + this.redOffset=redOffset || 0; + this.greenOffset=greenOffset || 0; + this.blueOffset=blueOffset || 0; + this.alphaOffset=alphaOffset || 0; + this.concat = function (second) { + this.redMultiplier=this.redMultiplier*second.redMultiplier; + this.greenMultiplier=this.greenMultiplier*second.greenMultiplier; + this.blueMultiplier=this.blueMultiplier*second.blueMultiplier; + this.alphaMultiplier=this.alphaMultiplier*second.alphaMultiplier; + this.redOffset=this.redOffset+second.redOffset; + this.greenOffset=this.redOffset+second.greenOffset; + this.blueOffset=this.redOffset+second.blueOffset; + this.alphaOffset=this.redOffset+second.alphaOffset; + } + this.toString = function () { + return "(redMultiplier="+this.redMultiplier+", greenMultiplier="+this.greenMultiplier+ + ", blueMultiplier="+this.blueMultiplier+", alphaMultiplier="+this.alphaMultiplier+ + ", redOffset="+this.redOffset+", greenOffset="+this.greenOffset+ + ", blueOffset="+this.blueOffset+", alphaOffset="+this.alphaOffset+")" + } +} \ No newline at end of file