// See http://jsperf.com/fastest-canvas-drawing/2 // See http://jsperf.com/canvas-drawimage-vs-putimagedata/3 // See http://jsperf.com/canvas-drawimage-vs-webgl-drawarrays // ------------------------------------------------------------------------------------------------- function CanvasRender(canvasElement, options) { this.options = options this.context = canvasElement.getContext('2d') } CanvasRender.prototype.draw = function (image) { this.context.drawImage(image, 0, 0) } CanvasRender.prototype.clear = function () { this.context.clearRect(0, 0, this.displayWidth, this.displayHeight) } // ------------------------------------------------------------------------------------------------- /* Based on http://www-cs-students.stanford.edu/~eparker/files/crunch/renderer.js */ /** * Constructs a renderer object. * @param {WebGLRenderingContext} gl The GL context. * @constructor */ var Renderer = function (gl) { /** * The GL context. * @type {WebGLRenderingContext} * @private */ this.gl_ = gl; /** * The WebGLProgram. * @type {WebGLProgram} * @private */ this.program_ = gl.createProgram(); /** * @type {WebGLShader} * @private */ this.vertexShader_ = this.compileShader_( Renderer.vertexShaderSource_, gl.VERTEX_SHADER); /** * @type {WebGLShader} * @private */ this.fragmentShader_ = this.compileShader_( Renderer.fragmentShaderSource_, gl.FRAGMENT_SHADER); /** * Cached uniform locations. * @type {Object.} * @private */ this.uniformLocations_ = {}; /** * Cached attribute locations. * @type {Object.} * @private */ this.attribLocations_ = {}; /** * A vertex buffer containing a single quad with xy coordinates from [-1,-1] * to [1,1] and uv coordinates from [0,0] to [1,1]. * @private */ this.quadVertexBuffer_ = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer_); var vertices = new Float32Array( [-1.0, -1.0, 0.0, 1.0, +1.0, -1.0, 1.0, 1.0, -1.0, +1.0, 0.0, 0.0, 1.0, +1.0, 1.0, 0.0]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); // Init shaders gl.attachShader(this.program_, this.vertexShader_); gl.attachShader(this.program_, this.fragmentShader_); gl.bindAttribLocation(this.program_, 0, 'vert'); gl.linkProgram(this.program_); gl.useProgram(this.program_); gl.enableVertexAttribArray(0); gl.enable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); var count = gl.getProgramParameter(this.program_, gl.ACTIVE_UNIFORMS); for (var i = 0; i < /** @type {number} */(count); i++) { var infoU = gl.getActiveUniform(this.program_, i); this.uniformLocations_[infoU.name] = gl.getUniformLocation(this.program_, infoU.name); } count = gl.getProgramParameter(this.program_, gl.ACTIVE_ATTRIBUTES); for (var j = 0; j < /** @type {number} */(count); j++) { var infoA = gl.getActiveAttrib(this.program_, j); this.attribLocations_[infoA.name] = gl.getAttribLocation(this.program_, infoA.name); } }; Renderer.prototype.finishInit = function () { this.draw(); }; Renderer.prototype.createDxtTexture = function ( dxtData , width , height , format ) { var gl = this.gl_; var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.compressedTexImage2D( gl.TEXTURE_2D, 0, format, width, height, 0, dxtData); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //gl.generateMipmap(gl.TEXTURE_2D) gl.bindTexture(gl.TEXTURE_2D, null); return tex; }; Renderer.prototype.createRgb565Texture = function (rgb565Data, width, height) { var gl = this.gl_; var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); //gl.generateMipmap(gl.TEXTURE_2D) gl.bindTexture(gl.TEXTURE_2D, null); return tex; }; Renderer.prototype.drawTexture = function (texture, width, height) { var gl = this.gl_; // draw scene gl.clearColor(0, 0, 0, 1); gl.clearDepth(1.0); gl.viewport(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.uniform1i(this.uniformLocations_.texSampler, 0); gl.enableVertexAttribArray(this.attribLocations_.vert); gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer_); gl.vertexAttribPointer(this.attribLocations_.vert, 4, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); }; /** * Compiles a GLSL shader and returns a WebGLShader. * @param {string} shaderSource The shader source code string. * @param {number} type Either VERTEX_SHADER or FRAGMENT_SHADER. * @return {WebGLShader} The new WebGLShader. * @private */ Renderer.prototype.compileShader_ = function (shaderSource, type) { var gl = this.gl_; var shader = gl.createShader(type); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); return shader; }; /** * @type {string} * @private */ Renderer.vertexShaderSource_ = [ 'attribute vec4 vert;', 'varying vec2 v_texCoord;', 'void main() {', ' gl_Position = vec4(vert.xy, 0.0, 1.0);', ' v_texCoord = vert.zw;', '}' ].join('\n'); /** * @type {string} * @private */ Renderer.fragmentShaderSource_ = [ 'precision highp float;', 'uniform sampler2D texSampler;', 'varying vec2 v_texCoord;', 'void main() {', ' gl_FragColor = texture2D(texSampler, v_texCoord);', '}' ].join('\n'); // ------------------------------------------------------------------------------------------------- function WebGLRender(canvasElement) { this.options = { // alpha: this.transparent, // antialias: !!antialias, // premultipliedAlpha: !!transparent, // stencil: true } try { this.ctx = canvasElement.getContext('experimental-webgl', this.options) } catch (e) { try { this.ctx = canvasElement.getContext('webgl', this.options) } catch (e2) { // fail, not able to get a context throw new Error('This browser does not support webGL. Try using the' + 'canvas renderer' + this) } } if (!this.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc')) { this.dxtSupported = false } this.renderer = new Renderer(this.ctx) this.contextLost = false // gl.useProgram(this.shaderManager.defaultShader.program) //this.ctx.disable(this.ctx.DEPTH_TEST) //this.ctx.disable(this.ctx.CULL_FACE) //this.setup() } WebGLRender.prototype.setup = function () { // create shaders var vertexShaderSrc = 'attribute vec2 aVertex;' + 'attribute vec2 aUV;' + 'varying vec2 vTex;' + 'void main(void) {' + ' gl_Position = vec4(aVertex, 0.0, 1.0);' + ' vTex = aUV;' + '}'; var fragmentShaderSrc = 'precision highp float;' + 'varying vec2 vTex;' + 'uniform sampler2D sampler0;' + 'void main(void){' + ' gl_FragColor = texture2D(sampler0, vTex);' + '}'; var vertShaderObj = this.ctx.createShader(this.ctx.VERTEX_SHADER) var fragShaderObj = this.ctx.createShader(this.ctx.FRAGMENT_SHADER) this.ctx.shaderSource(vertShaderObj, vertexShaderSrc) this.ctx.shaderSource(fragShaderObj, fragmentShaderSrc) this.ctx.compileShader(vertShaderObj) this.ctx.compileShader(fragShaderObj) var progObj = this.ctx.createProgram() this.ctx.attachShader(progObj, vertShaderObj) this.ctx.attachShader(progObj, fragShaderObj) this.ctx.linkProgram(progObj) this.ctx.useProgram(progObj) var width = this.displayWidth var height = this.displayHeight this.ctx.viewport(0, 0, width, height) this.vertexBuff = this.ctx.createBuffer() this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vertexBuff) this.ctx.bufferData( this.ctx.ARRAY_BUFFER , new Float32Array([ -1 / 8 , 1 / 6 , -1 / 8 , -1 / 6 , 1 / 8 , -1 / 6 , 1 / 8 , 1 / 6 ]) , this.ctx.STATIC_DRAW ) this.texBuff = this.ctx.createBuffer() this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.texBuff) this.ctx.bufferData( this.ctx.ARRAY_BUFFER, new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]), this.ctx.STATIC_DRAW) this.vloc = this.ctx.getAttribLocation(progObj, 'aVertex') this.tloc = this.ctx.getAttribLocation(progObj, 'aUV') } WebGLRender.prototype.draw = function (image) { // this.renderer.drawTexture(image, image.width, image.height) this.renderer.drawTexture(image, 643, 1149) } WebGLRender.prototype.drawOld = function (image) { var tex = this.ctx.createTexture() this.ctx.bindTexture(this.ctx.TEXTURE_2D, tex) this.ctx.texParameteri( this.ctx.TEXTURE_2D , this.ctx.TEXTURE_MIN_FILTER , this.ctx.NEAREST ) this.ctx.texParameteri( this.ctx.TEXTURE_2D , this.ctx.TEXTURE_MAG_FILTER , this.ctx.NEAREST ) /* this.ctx.texParameteri( this.ctx.TEXTURE_2D , this.ctx.TEXTURE_MIN_FILTER , this.ctx.LINEAR ) this.ctx.texParameteri( this.ctx.TEXTURE_2D , this.ctx.TEXTURE_WRAP_S , this.ctx.CLAMP_TO_EDGE ) this.ctx.texParameteri( this.ctx.TEXTURE_2D , this.ctx.TEXTURE_WRAP_T , this.ctx.CLAMP_TO_EDGE ) */ this.ctx.generateMipmap(this.ctx.TEXTURE_2D) this.ctx.texImage2D( this.ctx.TEXTURE_2D , 0 , this.ctx.RGBA , this.ctx.RGBA , this.ctx.UNSIGNED_BYTE , image ) this.ctx.enableVertexAttribArray(this.vloc) this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vertexBuff) this.ctx.vertexAttribPointer(this.vloc, 2, this.ctx.FLOAT, false, 0, 0) this.ctx.enableVertexAttribArray(this.tloc) this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.texBuff) this.ctx.bindTexture(this.ctx.TEXTURE_2D, tex) this.ctx.vertexAttribPointer(this.tloc, 2, this.ctx.FLOAT, false, 0, 0) } WebGLRender.prototype.clear = function () { } // ------------------------------------------------------------------------------------------------- function FastImageRender(canvasElement, options) { var that = this this.options = options || {} this.canvasElement = canvasElement if (that.options.raf) { that.animLoop = function() { that.raf = requireAnimationFrame(that.animLoop) // separate render from drawing // render } } if (true) { this.loader = new Image() this.loader.onload = function () { if (typeof(that.onLoad) === 'function') { that.onLoad(this) } } this.loader.onerror = function () { if (typeof(that.onError) === 'function') { that.onError(this) } } } if (this.options.render === 'webgl') { this.render = new WebGLRender(canvasElement, options) } else { this.render = new CanvasRender(canvasElement, options) } } FastImageRender.prototype.destroy = function () { cancelAnimationFrame(this.raf) // delete onLoad & onError } FastImageRender.prototype.load = function (url, type) { var that = this if (this.options.textureLoader) { if (!this.textureLoader) { this.textureLoader = new TextureUtil.TextureLoader(this.render.ctx) } var texture = null if (type) { texture = this.render.ctx.createTexture(); this.textureLoader.loadEx(url, texture, true, function () { if (typeof(that.onLoad) === 'function') { that.onLoad(texture) } }, type) } else { this.textureLoader.load(url, function (texture) { if (typeof(that.onLoad) === 'function') { that.onLoad(texture) } }) } } else { this.loader.src = url } } FastImageRender.prototype.draw = function (image) { this.render.draw(image) } FastImageRender.prototype.clear = function () { this.render.clear() } Object.defineProperty(FastImageRender.prototype, 'canvasWidth', { get: function () { return this.canvasElement.width }, set: function (width) { if (width) { if (width !== this.canvasElement.width) { this.canvasElement.width = width; } } } }) Object.defineProperty(FastImageRender.prototype, 'canvasHeight', { get: function () { return this.canvasElement.height }, set: function (height) { if (height) { if (height !== this.canvasElement.height) { this.canvasElement.height = height; } } } }) Object.defineProperty(FastImageRender.prototype, 'displayWidth', { get: function () { return this.canvasElement.width }, set: function (width) { if (width) { if (width !== this.canvasElement.width) { this.canvasElement.width = width; } } } }) Object.defineProperty(FastImageRender.prototype, 'displayHeight', { get: function () { return this.canvasElement.height }, set: function (height) { if (height) { if (height !== this.canvasElement.height) { this.canvasElement.height = height; } } } }) Object.defineProperty(FastImageRender.prototype, 'canvasStyleWidth', { get: function () { return parseInt(this.canvasElement.style.width, 10) }, set: function (width) { if (width) { var styleWidth = width + 'px' if (styleWidth !== this.canvasElement.style.width) { this.canvasElement.style.width = styleWidth; } } } }) Object.defineProperty(FastImageRender.prototype, 'canvasStyleHeight', { get: function () { return parseInt(this.canvasElement.style.height, 10) }, set: function (height) { if (height) { var styleHeight = height + 'px' if (styleHeight !== this.canvasElement.style.height) { this.canvasElement.style.height = height; } } } }) // ------------------------------------------------------------------------------------------------- // Check for Non CommonJS world if (typeof module !== 'undefined') { module.exports = { FastImageRender: FastImageRender } }