/* * 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, canvas) { 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; });