Work on HTML5 BitmapData class
This commit is contained in:
parent
63401df118
commit
3ec41e43ee
|
|
@ -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<h; y++) {
|
||||
for (var x=0; x<w; x++) {
|
||||
pos = (x + y * w) * 4;
|
||||
data[pos] = arr[pos];
|
||||
data[pos+1] = arr[pos+1];
|
||||
data[pos+2] = arr[pos+2];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
/*** Canvas2D functions ***/
|
||||
|
||||
this.setPixel = function(x, y, color) {
|
||||
var rgb = hexToRGB(color);
|
||||
var pos = (x + y * this.width) * 4;
|
||||
var data = this.imagedata.data;
|
||||
|
||||
data[pos+0] = rgb.r;
|
||||
data[pos+1] = rgb.g;
|
||||
data[pos+2] = rgb.b;
|
||||
};
|
||||
|
||||
this.getPixel = function(x, y) {
|
||||
var pos = (x + y * this.width) * 4;
|
||||
var data = this.imagedata.data;
|
||||
var rgb = {
|
||||
r: data[pos+0],
|
||||
g: data[pos+1],
|
||||
b: data[pos+2]
|
||||
};
|
||||
|
||||
return RGBToHex(rgb);
|
||||
};
|
||||
|
||||
this.clear = function(rect) {
|
||||
rect=rect || this.rect;
|
||||
this.context.clearRect(rect.x, rect.y, rect.width, rect.height);
|
||||
this.imagedata = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
};
|
||||
|
||||
this.clone = function() {
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
|
||||
var result = new BitmapData(this.width, this.height, this.transparent);
|
||||
result.data = this.context.getImageData(0, 0, this.width, this.height);
|
||||
return result;
|
||||
};
|
||||
|
||||
this.colorTransform=function(rect, colorTransform)
|
||||
{
|
||||
rect=rect || this.rect;
|
||||
colorTransform=colorTransform || new ColorTransform();
|
||||
|
||||
var data=this.imagedata.data;
|
||||
var xMax=rect.x+rect.height;
|
||||
var yMax=rect.y+rect.height;
|
||||
|
||||
for(var y=rect.y;y<yMax;y++)
|
||||
{
|
||||
for(var x=rect.x;x<xMax;x++)
|
||||
{
|
||||
var r=(y*this.width+x)*4;
|
||||
var g=r+1;
|
||||
var b=r+2
|
||||
var a=r+3;
|
||||
|
||||
data[r]=data[r]*colorTransform.redMultiplier+colorTransform.redOffset;
|
||||
data[g]=data[g]*colorTransform.greenMultiplier+colorTransform.greenOffset;
|
||||
data[b]=data[b]*colorTransform.blueMultiplier+colorTransform.blueOffset;
|
||||
data[a]=data[a]*colorTransform.alphaMultiplier+colorTransform.alphaOffset;
|
||||
}
|
||||
}
|
||||
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
}
|
||||
|
||||
this.applyFilter = function(sourceBitmapData, sourceRect, destPoint, filter) {
|
||||
var copy = this.clone();
|
||||
filter.run(sourceRect, this.imagedata.data, copy.imagedata.data);
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
};
|
||||
|
||||
this.compare = function(otherBitmapData) {
|
||||
if(this.width != otherBitmapData.width) return -3;
|
||||
if(this.height != otherBitmapData.height) return -4;
|
||||
if(this.imagedata === otherBitmapData.data) return 0;
|
||||
|
||||
var otherRGB, thisRGB, dif;
|
||||
var result = new BitmapData(this.width, this.height);
|
||||
for (var y = 0; y < this.height; y++) {
|
||||
for (var x = 0; x < this.width; x++) {
|
||||
otherRGB = hexToRGB( otherBitmapData.getPixel(x, y) );
|
||||
thisRGB = hexToRGB( this.getPixel(x, y) );
|
||||
|
||||
dif = {
|
||||
r: Math.abs(otherRGB.r - thisRGB.r),
|
||||
g: Math.abs(otherRGB.g - thisRGB.g),
|
||||
b: Math.abs(otherRGB.b - thisRGB.b)
|
||||
};
|
||||
|
||||
result.setPixel(x, y, RGBToHex(dif));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
this.copyCanvas = function(sourceCanvas, sourceRect, destPoint, blendMode) {
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
|
||||
var bw = this.canvas.width - sourceRect.width - destPoint.x;
|
||||
var bh = this.canvas.height - sourceRect.height - destPoint.y
|
||||
|
||||
var dw = (bw < 0) ? sourceRect.width + (this.canvas.width - sourceRect.width - destPoint.x) : sourceRect.width;
|
||||
var dh = (bh < 0) ? sourceRect.height + (this.canvas.height - sourceRect.height - destPoint.y) : sourceRect.height;
|
||||
|
||||
if(blendMode && blendMode != BlendMode.NORMAL) {
|
||||
|
||||
var sourceData = sourceCanvas.getContext("2d").getImageData(sourceRect.x, sourceRect.y, dw, dh).data;
|
||||
var sourcePos, destPos;
|
||||
var data = this.imagedata.data;
|
||||
|
||||
for (var y=0; y<dh; y++) {
|
||||
for (var x=0; x<dw; x++) {
|
||||
sourcePos = (x + y * dw) * 4;
|
||||
destPos = ((x+destPoint.x) + (y+destPoint.y) * this.width) * 4;
|
||||
|
||||
switch(blendMode) {
|
||||
case BlendMode.ADD:
|
||||
data[destPos] = Math.min(data[destPos] + sourceData[sourcePos], 255);
|
||||
data[destPos+1] = Math.min(data[destPos+1] + sourceData[sourcePos+1], 255);
|
||||
data[destPos+2] = Math.min(data[destPos+2] + sourceData[sourcePos+2], 255);
|
||||
break;
|
||||
|
||||
case BlendMode.SUBTRACT:
|
||||
data[destPos] = Math.max(sourceData[sourcePos] - data[destPos], 0);
|
||||
data[destPos+1] = Math.max(sourceData[sourcePos+1] - data[destPos+1], 0);
|
||||
data[destPos+2] = Math.max(sourceData[sourcePos+2] - data[destPos+2], 0);
|
||||
break;
|
||||
|
||||
case BlendMode.INVERT:
|
||||
data[destPos] = 255 - sourceData[sourcePos];
|
||||
data[destPos+1] = 255 - sourceData[sourcePos+1];
|
||||
data[destPos+2] = 255 - sourceData[sourcePos+1];
|
||||
break;
|
||||
|
||||
case BlendMode.MULTIPLY:
|
||||
data[destPos] = Math.floor(sourceData[sourcePos] * data[destPos] / 255);
|
||||
data[destPos+1] = Math.floor(sourceData[sourcePos+1] * data[destPos+1] / 255);
|
||||
data[destPos+2] = Math.floor(sourceData[sourcePos+2] * data[destPos+2] / 255);
|
||||
break;
|
||||
|
||||
case BlendMode.LIGHTEN:
|
||||
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.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<sourceRect.height; y++) {
|
||||
for (var x=0; x<sourceRect.width; x++) {
|
||||
sourceColor = sourceBitmapData.getPixel(sourceRect.x+x, sourceRect.y+y);
|
||||
sourceRGB = hexToRGB(sourceColor);
|
||||
switch(sourceChannel) {
|
||||
case redChannel: channelValue = sourceRGB.r; break;
|
||||
case greenChannel: channelValue = sourceRGB.g; break;
|
||||
case blueChannel: channelValue = sourceRGB.b; break;
|
||||
}
|
||||
|
||||
rgb = hexToRGB( this.getPixel(destPoint.x+x, destPoint.y+y) ); // redundancy
|
||||
switch(destChannel){
|
||||
case redChannel: rgb.r = channelValue; break;
|
||||
case greenChannel: rgb.g = channelValue; break;
|
||||
case blueChannel: rgb.b = channelValue; break;
|
||||
}
|
||||
|
||||
this.setPixel(destPoint.x+x, destPoint.y+y, RGBToHex(rgb));
|
||||
}
|
||||
}
|
||||
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
};
|
||||
|
||||
this.copyPixels = function(sourceBitmapData, sourceRect, destPoint, alphaBitmapData, alphaPoint, mergeAlpha) {
|
||||
this.copyCanvas(sourceBitmapData.canvas, sourceRect, destPoint);
|
||||
};
|
||||
|
||||
this.draw = function(source, matrix, colorTransform, blendMode, clipRect, smoothing) {
|
||||
|
||||
/*
|
||||
* currently only supports Image object
|
||||
* TODO: implement instanceof switches
|
||||
*/
|
||||
|
||||
sourceMatrix = matrix || new Matrix();
|
||||
sourceRect = clipRect || new Rectangle(0, 0, source.width, source.height);
|
||||
|
||||
if(blendMode && this.gpuEnabled) {
|
||||
// TO DO
|
||||
}
|
||||
|
||||
this.drawingCanvas.setAttribute('width', source.width);
|
||||
this.drawingCanvas.setAttribute('height', source.height);
|
||||
|
||||
this.drawingContext.transform(
|
||||
sourceMatrix.a,
|
||||
sourceMatrix.b,
|
||||
sourceMatrix.c,
|
||||
sourceMatrix.d,
|
||||
sourceMatrix.tx,
|
||||
sourceMatrix.ty);
|
||||
|
||||
this.drawingContext.drawImage(source,
|
||||
0, 0, source.width, source.height,
|
||||
0, 0, source.width, source.height);
|
||||
|
||||
this.copyCanvas(this.drawingCanvas, sourceRect, new Point(sourceRect.x, sourceRect.y), blendMode);
|
||||
}
|
||||
|
||||
this.fillRect = function(rect, color) {
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
var rgb = hexToRGB(color);
|
||||
|
||||
this.context.fillStyle = "rgb("+rgb.r+","+rgb.g+","+rgb.b+")";
|
||||
this.context.fillRect (rect.x, rect.y, rect.width, rect.height);
|
||||
this.imagedata = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
};
|
||||
|
||||
this.floodFill = function(x, y, color) {
|
||||
var queue = new Array();
|
||||
queue.push(new Point(x, y));
|
||||
|
||||
var old = this.getPixel(x, y);
|
||||
var iterations = 0;
|
||||
|
||||
var searchBmp = new BitmapData(this.width, this.height, true, 0xffffff);
|
||||
var currPoint, newPoint;
|
||||
|
||||
while (queue.length > 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<dh; y++) {
|
||||
for(var x=hRect.x; x<dw; x++) {
|
||||
pos = (x + y * this.width) * 4;
|
||||
color[itr++] = data[pos+0];
|
||||
color[itr++] = data[pos+1];
|
||||
color[itr++] = data[pos+2];
|
||||
}
|
||||
}
|
||||
|
||||
itr = 0;
|
||||
for(var i=0; i<p; i+=Math.floor(p/256)) {
|
||||
px = itr*3;
|
||||
rv[itr] = color[px+0];
|
||||
gv[itr] = color[px+1];
|
||||
bv[itr] = color[px+2];
|
||||
itr++;
|
||||
}
|
||||
|
||||
rgb.r = rv;
|
||||
rgb.g = gv;
|
||||
rgb.b = bv;
|
||||
|
||||
return rgb;
|
||||
};
|
||||
|
||||
this.noise = function(randomSeed, low, high, channelOptions, grayScale) {
|
||||
this.rand = this.rand || new PRNG();
|
||||
this.rand.seed = randomSeed;
|
||||
|
||||
var redChannel = BitmapDataChannel.RED;
|
||||
var greenChannel = BitmapDataChannel.GREEN;
|
||||
var blueChannel = BitmapDataChannel.BLUE;
|
||||
|
||||
var data = this.imagedata.data;
|
||||
|
||||
low = low || 0;
|
||||
high = high || 255;
|
||||
channelOptions = channelOptions || 7;
|
||||
grayScale = grayScale || false;
|
||||
|
||||
var pos, cr, cg, cb, gray;
|
||||
for (var y=0; y<this.height; y++) {
|
||||
for (var x=0; x<this.width; x++) {
|
||||
pos = (x + y * this.width) * 4;
|
||||
|
||||
cr = this.rand.nextRange(low, high);
|
||||
cg = this.rand.nextRange(low, high);
|
||||
cb = this.rand.nextRange(low, high);
|
||||
|
||||
if(grayScale) {
|
||||
gray = (cr + cg + cb) / 3;
|
||||
cr = cg = cb = gray;
|
||||
}
|
||||
|
||||
data[pos+0] = (channelOptions & redChannel) ? (1 * cr) : 0x00;
|
||||
data[pos+1] = (channelOptions & greenChannel) ? (1 * cg) : 0x00;
|
||||
data[pos+2] = (channelOptions & blueChannel) ? (1 * cb) : 0x00;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.paletteMap = function(sourceBitmapData, sourceRect, destPoint, redArray, greenArray, blueArray, alphaArray) {
|
||||
var bw = this.canvas.width - sourceRect.width - destPoint.x;
|
||||
var bh = this.canvas.height - sourceRect.height - destPoint.y
|
||||
|
||||
var dw = (bw < 0) ? sourceRect.width + (this.canvas.width - sourceRect.width - destPoint.x) : sourceRect.width;
|
||||
var dh = (bh < 0) ? sourceRect.height + (this.canvas.height - sourceRect.height - destPoint.y) : sourceRect.height;
|
||||
|
||||
var sourceData = sourceBitmapData.imagedata.data;
|
||||
var sourcePos, destPos, sourceHex;
|
||||
var r, g, b, pos;
|
||||
|
||||
var sx = sourceRect.x;
|
||||
var sy = sourceRect.y;
|
||||
var sw = sourceBitmapData.width;
|
||||
var dx = destPoint.x;
|
||||
var dy = destPoint.y;
|
||||
|
||||
var data = this.imagedata.data;
|
||||
var w = this.width;
|
||||
|
||||
for (var y=0; y<dh; y++) {
|
||||
for (var x=0; x<dw; x++) {
|
||||
sourcePos = ((x+sx) + (y+sy) * sw) * 4;
|
||||
|
||||
r = sourceData[sourcePos+0];
|
||||
g = sourceData[sourcePos+1];
|
||||
b = sourceData[sourcePos+2];
|
||||
|
||||
pos = ((x+dx) + (y+dy) * w) * 4;
|
||||
|
||||
data[pos+0] = redArray[r];
|
||||
data[pos+1] = greenArray[g];
|
||||
data[pos+2] = blueArray[b];
|
||||
}
|
||||
}
|
||||
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
};
|
||||
|
||||
this.perlinNoise = function(baseX, baseY, randomSeed, channelOptions, grayScale) {
|
||||
this.rand = this.rand || new PRNG();
|
||||
this.rand.seed = randomSeed;
|
||||
|
||||
var redChannel = BitmapDataChannel.RED;
|
||||
var greenChannel = BitmapDataChannel.GREEN;
|
||||
var blueChannel = BitmapDataChannel.BLUE;
|
||||
|
||||
channelOptions = channelOptions || 7;
|
||||
grayScale = grayScale || false;
|
||||
|
||||
var data = this.imagedata.data;
|
||||
|
||||
var numChannels = 0;
|
||||
if(channelOptions & redChannel){
|
||||
this.simplexR = this.simplexR || new SimplexNoise(this.rand);
|
||||
this.simplexR.setSeed(randomSeed);
|
||||
numChannels++;
|
||||
}
|
||||
if(channelOptions & greenChannel) {
|
||||
this.simplexG = this.simplexG || new SimplexNoise(this.rand);
|
||||
this.simplexG.setSeed(randomSeed+1);
|
||||
numChannels++;
|
||||
}
|
||||
if(channelOptions & blueChannel) {
|
||||
this.simplexB = this.simplexB || new SimplexNoise(this.rand);
|
||||
this.simplexB.setSeed(randomSeed+2);
|
||||
numChannels++;
|
||||
}
|
||||
|
||||
var pos, cr, cg, cb;
|
||||
for(var y=0; y<this.height; y++) {
|
||||
for(var x=0; x<this.width; x++) {
|
||||
pos = (x + y * this.width) * 4;
|
||||
|
||||
cr = (channelOptions & redChannel) ? Math.floor(((this.simplexR.noise(x/baseX, y/baseY)+1)*0.5)*255) : 0x00;
|
||||
cg = (channelOptions & greenChannel) ? Math.floor(((this.simplexG.noise(x/baseX, y/baseY)+1)*0.5)*255) : 0x00;
|
||||
cb = (channelOptions & blueChannel) ? Math.floor(((this.simplexB.noise(x/baseX, y/baseY)+1)*0.5)*255) : 0x00;
|
||||
|
||||
if(grayScale) {
|
||||
gray = (cr + cg + cb) / numChannels;
|
||||
cr = cg = cb = gray;
|
||||
}
|
||||
|
||||
data[pos+0] = cr;
|
||||
data[pos+1] = cg;
|
||||
data[pos+2] = cb;
|
||||
}
|
||||
}
|
||||
|
||||
this.context.putImageData(this.imagedata, 0, 0);
|
||||
};
|
||||
|
||||
this.threshold = function(sourceBitmapData, sourceRect, destPoint, operation, threshold, color, mask, copySource) {
|
||||
color = color || 0;
|
||||
mask = mask || 0xffffff;
|
||||
copySource = copySource || false;
|
||||
|
||||
var bw = this.canvas.width - sourceRect.width - destPoint.x;
|
||||
var bh = this.canvas.height - sourceRect.height - destPoint.y
|
||||
|
||||
var dw = (bw < 0) ? sourceRect.width + (this.canvas.width - sourceRect.width - destPoint.x) : sourceRect.width;
|
||||
var dh = (bh < 0) ? sourceRect.height + (this.canvas.height - sourceRect.height - destPoint.y) : sourceRect.height;
|
||||
|
||||
var sourceData = sourceBitmapData.imagedata.data;
|
||||
var sourcePos, destPos, sourceHex;
|
||||
|
||||
var sx = sourceRect.x;
|
||||
var sy = sourceRect.y;
|
||||
var sw = sourceBitmapData.width;
|
||||
|
||||
for (var y=0; y<dh; y++) {
|
||||
for (var x=0; x<dw; x++) {
|
||||
sourcePos = ((x+sx) + (y+sy) * sw) * 4;
|
||||
sourceHex = RGBToHex({r:sourceData[sourcePos], g:sourceData[sourcePos+1], b:sourceData[sourcePos+2]});
|
||||
|
||||
switch(operation) {
|
||||
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;
|
||||
|
||||
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;
|
||||
});
|
||||
|
|
@ -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<numPixel;i++) {
|
||||
var r=i*4;
|
||||
var g=r+1;
|
||||
var b=r+2;
|
||||
var a=r+3;
|
||||
|
||||
var oR=image[r];
|
||||
var oG=image[g];
|
||||
var oB=image[b];
|
||||
var oA=image[a];
|
||||
|
||||
image[r] = (m[0] * oR) + (m[1] * oG) + (m[2] * oB) + (m[3] * oA) + m[4];
|
||||
image[g] = (m[5] * oR) + (m[6] * oG) + (m[7] * oB) + (m[8] * oA) + m[9];
|
||||
image[b] = (m[10] * oR) + (m[11] * oG) + (m[12] * oB) + (m[13] * oA) + m[14];
|
||||
image[a] = (m[15] * oR) + (m[16] * oG) + (m[17] * oB) + (m[18] * oA) + m[19];
|
||||
}
|
||||
}
|
||||
|
||||
this.clone=function() {
|
||||
return new ColorMatrixFilter(this.matrix);
|
||||
}
|
||||
}
|
||||
|
||||
ColorMatrixFilter.inherits(BitmapFilter)
|
||||
|
||||
function BlurFilter (blurX, blurY, quality) {
|
||||
this.blurX = blurX
|
||||
this.blurY = blurY
|
||||
this.quality = quality
|
||||
this.run = function(sourceRect, image, copy) {
|
||||
var numPixel=image.length/4;
|
||||
|
||||
for(var i=0;i<numPixel;i++) {
|
||||
var r=i*4;
|
||||
var g=r+1;
|
||||
var b=r+2;
|
||||
var a=r+3;
|
||||
|
||||
var oR=image[r];
|
||||
var oG=image[g];
|
||||
var oB=image[b];
|
||||
var oA=image[a];
|
||||
|
||||
image[r] = (m[0] * oR) + (m[1] * oG) + (m[2] * oB) + (m[3] * oA) + m[4];
|
||||
image[g] = (m[5] * oR) + (m[6] * oG) + (m[7] * oB) + (m[8] * oA) + m[9];
|
||||
image[b] = (m[10] * oR) + (m[11] * oG) + (m[12] * oB) + (m[13] * oA) + m[14];
|
||||
image[a] = (m[15] * oR) + (m[16] * oG) + (m[17] * oB) + (m[18] * oA) + m[19];
|
||||
}
|
||||
radius = (blurX+blurY)/2
|
||||
if ( isNaN(radius) || radius < 1 ) return;
|
||||
radius |= 0;
|
||||
|
||||
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
|
||||
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
|
||||
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
|
||||
pr, pg, pb, pa, rbs;
|
||||
|
||||
var width = sourceRect.width
|
||||
var height = sourceRect.height
|
||||
var div = radius + radius + 1;
|
||||
var w4 = width << 2;
|
||||
var widthMinus1 = width - 1;
|
||||
var heightMinus1 = height - 1;
|
||||
var radiusPlus1 = radius + 1;
|
||||
var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2;
|
||||
|
||||
var stackStart = new BlurStack();
|
||||
var stack = stackStart;
|
||||
for ( i = 1; i < div; i++ ) {
|
||||
stack = stack.next = new BlurStack();
|
||||
if ( i == radiusPlus1 ) var stackEnd = stack;
|
||||
}
|
||||
stack.next = stackStart;
|
||||
var stackIn = null;
|
||||
var stackOut = null;
|
||||
|
||||
yw = yi = 0;
|
||||
|
||||
var mul_sum = mul_table[radius];
|
||||
var shg_sum = shg_table[radius];
|
||||
|
||||
for ( y = 0; y < height; y++ ) {
|
||||
r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
for( i = 1; i < radiusPlus1; i++ ) {
|
||||
p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
|
||||
r_sum += ( stack.r = ( pr = image[p])) * ( rbs = radiusPlus1 - i );
|
||||
g_sum += ( stack.g = ( pg = image[p+1])) * rbs;
|
||||
b_sum += ( stack.b = ( pb = image[p+2])) * rbs;
|
||||
a_sum += ( stack.a = ( pa = image[p+3])) * rbs;
|
||||
r_in_sum += pr;
|
||||
g_in_sum += pg;
|
||||
b_in_sum += pb;
|
||||
a_in_sum += pa;
|
||||
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
for ( x = 0; x < width; x++ ) {
|
||||
image[yi+3] = pa = (a_sum * mul_sum) >> 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)
|
||||
|
|
@ -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.right) && y>(this.y && y<this.bottom))
|
||||
}
|
||||
this.containsPoint = function (pt) {
|
||||
return ((pt.x>this.x && pt.x<this.right) && (pt.y>this.y && pt.y<this.bottom))
|
||||
}
|
||||
this.containsRectangle = function (rect) {
|
||||
return ((rect.x>this.x && rect.right<this.right) && (rect.y>this.y && rect.bottom<this.bottom))
|
||||
}
|
||||
this.equals = function (toCompare) {
|
||||
return ((toCompare.x==this.x && toCompare.y==this.y) && (toCompare.width==this.width && toCompare.height==this.height))
|
||||
}
|
||||
this.inflate = function (dx, dy) {
|
||||
this.x -= dx;
|
||||
this.width += 2 * dx;
|
||||
this.y -= dy;
|
||||
this.height += 2 * dy;
|
||||
}
|
||||
this.inflatePoint = function (pt) {
|
||||
this.x -= pt.x;
|
||||
this.width += 2 * pt.x;
|
||||
this.y -= pt.y;
|
||||
this.height += 2 * pt.y;
|
||||
}
|
||||
this.intersection = 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 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+")"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue