776 lines
24 KiB
JavaScript
776 lines
24 KiB
JavaScript
/*
|
|
* 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;
|
|
});
|