mirror of
https://github.com/openstf/stf
synced 2025-10-04 10:19:30 +02:00
581 lines
14 KiB
JavaScript
581 lines
14 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) {
|
|
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) {
|
|
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
|
|
}
|
|
}
|