1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-04 10:19:30 +02:00
OpenSTF/res/app/components/stf/screen/fast-image-render/index.js
2014-03-07 19:11:04 +09:00

559 lines
15 KiB
JavaScript

// 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) {
var checkForCanvasElement = function checkForCanvasElement() {
if (!canvasElement) {
throw new Error('Needs a canvas element')
}
this.displayWidth = canvasElement.offsetWidth
this.displayHeight = canvasElement.offsetHeight
if (!this.displayWidth || !this.displayHeight) {
throw new Error('Unable to get display size canvas must have dimensions')
}
}
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.<string, WebGLUniformLocation>}
* @private
*/
this.uniformLocations_ = {};
/**
* Cached attribute locations.
* @type {Object.<string, WebGLActiveInfo>}
* @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, options) {
var checkForCanvasElement = function checkForCanvasElement() {
if (!canvasElement) {
throw new Error('Needs a canvas element')
}
this.displayWidth = canvasElement.offsetWidth
this.displayHeight = canvasElement.offsetHeight
if (!this.displayWidth || !this.displayHeight) {
throw new Error('Unable to get display size canvas must have dimensions')
}
}
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 = 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 (tex) {
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
}
}