phaser/v3/merge/renderer/webgl/canvas/canvas-gl.js
2016-12-16 23:23:42 -03:00

908 lines
No EOL
30 KiB
JavaScript

function CreateRenderTarget(gl, width, height, data) {
var texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ? data : null);
if (gl.getError() != gl.NO_ERROR) {
console.error("GL ERROR", gl.getError());
}
return texture;
}
function UploadTexture(gl, image) {
var texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
if (gl.getError() != gl.NO_ERROR) {
console.error("GL ERROR", gl.getError());
}
return texture;
}
function CreateFramebuffer(gl, width, height) {
var fbo = gl.createFramebuffer();
var rbo = gl.createRenderbuffer();
var tex = null;
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
tex = CreateRenderTarget(gl, width, height);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE)
console.error('Frame buffer incomplete');
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
fbo.width = width;
fbo.height = height;
fbo.targetTexture = tex;
fbo.renderbuffer = rbo;
return fbo;
}
function CanvasGL(gl) {
var program = gl.createProgram();
var vShader = gl.createShader(gl.VERTEX_SHADER);
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vShader, [
'attribute vec2 inPosition;',
'attribute vec4 inColor;',
'attribute vec2 inTexCoord;',
'uniform mat4 uViewMatrix;',
'varying vec2 outTexCoord;',
'varying vec4 outColor;',
'void main() {',
' gl_Position = uViewMatrix * vec4(inPosition, 1.0, 1.0);',
' outColor = inColor;',
' outTexCoord = inTexCoord;',
'}'
].join('\n'));
gl.shaderSource(fShader, [
'precision mediump float;',
'const vec2 gf0 = vec2(-3.0, 0.015625);',
'const vec2 gf1 = vec2(-2.0, 0.09375);',
'const vec2 gf2 = vec2(-1.0, 0.234375);',
'const vec2 gf3 = vec2(0.0, 0.3125);',
'const vec2 gf4 = vec2(1.0, 0.234375);',
'const vec2 gf5 = vec2(2.0, 0.09375);',
'const vec2 gf6 = vec2(3.0, 0.015625);',
'const float factor = 0.5;',
'uniform bool isStencil;',
'uniform bool isPattern;',
'uniform sampler2D mainSampler;',
'uniform sampler2D maskSampler;',
'uniform vec2 scale;',
'uniform bool enableBlur;',
'varying vec4 outColor;',
'varying vec2 outTexCoord;',
'void main() {',
' if (isStencil == false) {',
' gl_FragColor = vec4(outColor.bgr, 1.0);',
' } else if (isPattern) {',
' gl_FragColor = texture2D(mainSampler, outTexCoord);',
' } else {',
' vec4 mainColor = texture2D(mainSampler, outTexCoord);',
' vec4 maskColor = vec4(0, 0, 0, 0);',
' if (enableBlur) {',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf0.x * (scale.x * factor), outTexCoord.y + gf0.x * (scale.y * factor))) * gf0.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf1.x * (scale.x * factor), outTexCoord.y + gf1.x * (scale.y * factor))) * gf1.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf2.x * (scale.x * factor), outTexCoord.y + gf2.x * (scale.y * factor))) * gf2.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf3.x * (scale.x * factor), outTexCoord.y + gf3.x * (scale.y * factor))) * gf3.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf4.x * (scale.x * factor), outTexCoord.y + gf4.x * (scale.y * factor))) * gf4.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf5.x * (scale.x * factor), outTexCoord.y + gf5.x * (scale.y * factor))) * gf5.y;',
' maskColor += texture2D(maskSampler, vec2(outTexCoord.x + gf6.x * (scale.x * factor), outTexCoord.y + gf6.x * (scale.y * factor))) * gf6.y;',
' } else maskColor = texture2D(maskSampler, outTexCoord);',
' gl_FragColor = mainColor * maskColor.a;',
' }',
'}'
].join('\n'));
gl.compileShader(vShader);
gl.compileShader(fShader);
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uViewMatrix'), 0,
new Float32Array([
2 / gl.canvas.width, 0, 0, 0,
0, -2 / gl.canvas.height, 0, 0,
0, 0, 1, 1, -1, 1, 0, 0
])
);
this._isStencilLocation = gl.getUniformLocation(program, 'isStencil');
this._lineWidth = 1;
this._matrixStack = new Float32Array(6 * 1000);
this._matrixStackPointer = 0;
this._matrix = new Float32Array([1, 0, 0, 1, 0, 0]);
this._program = program;
this._vShader = vShader;
this._fShader = fShader;
this._inPositionLocation = gl.getAttribLocation(program, 'inPosition');
this._inColorLocation = gl.getAttribLocation(program, 'inColor');
this._inTexCoordLocation = gl.getAttribLocation(program, 'inTexCoord');
this._gl = gl;
this._fillColor = new Uint32Array([0xFFFFFFFF]);
this._lineColor = 0xFFFFFFFF;
this._vbo = gl.createBuffer();
this._vertexCount = 0;
this._arrayBuffer = new ArrayBuffer(this.VERTEX_SIZE * this.MAX_VERTEX_COUNT);
this._positionBuffer = new Float32Array(this._arrayBuffer);
this._colorBuffer = new Uint32Array(this._arrayBuffer);
this._pathArray = [];
this._lastPath = null;
this._emptyTexture = CreateRenderTarget(gl, 1, 1);
this.bind();
gl.bufferData(gl.ARRAY_BUFFER, this._arrayBuffer.byteLength, gl.DYNAMIC_DRAW);
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.uniform1i(this._isStencilLocation, 0);
this._fboStencilBuffer = CreateFramebuffer(gl, gl.canvas.width, gl.canvas.height);
this._fboColorBuffer = CreateFramebuffer(gl, gl.canvas.width, gl.canvas.height);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
this._isPatternLocation = gl.getUniformLocation(program, 'isPattern');
gl.uniform1i(gl.getUniformLocation(program, 'mainSampler'), 0);
gl.uniform1i(gl.getUniformLocation(program, 'maskSampler'), 1);
gl.uniform1i(this._isPatternLocation, 0);
gl.uniform2f(gl.getUniformLocation(program, 'scale'), 1 / gl.canvas.width, 1 / gl.canvas.height);
this.enableMaskAntialiasing();
this._currentPattern = null;
}
CanvasGL.prototype.createPatternTexture = function (image) {
var texture = UploadTexture(this._gl, image);
var pattern = {
// Other Properties
texture: texture
};
return pattern;
};
CanvasGL.prototype.usePattern = function (pattern) {
this._currentPattern = pattern;
};
CanvasGL.prototype.stopPattern = function () {
this._currentPattern = null;
};
CanvasGL.prototype.enableMaskAntialiasing = function () {
var gl = this._gl;
gl.uniform1i(gl.getUniformLocation(this._program, 'enableBlur'), 1);
};
CanvasGL.prototype.disableMaskAntialiasing = function () {
var gl = this._gl;
gl.uniform1i(gl.getUniformLocation(this._program, 'enableBlur'), 0);
};
CanvasGL.prototype.recordMaskBegin = function () {
var gl = this._gl;
var stencilbuffer = this._fboStencilBuffer;
this.flush();
gl.viewport(0, 0, stencilbuffer.width, stencilbuffer.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, stencilbuffer);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.uniform1i(this._isStencilLocation, 0);
};
CanvasGL.prototype.recordMaskEnd = function () {
var gl = this._gl;
this.flush();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.uniform1i(this._isStencilLocation, 0);
};
CanvasGL.prototype.applyMaskBegin = function () {
var gl = this._gl;
var colorbuffer = this._fboColorBuffer;
this.flush();
gl.viewport(0, 0, colorbuffer.width, colorbuffer.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, colorbuffer);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.uniform1i(this._isStencilLocation, 0);
};
CanvasGL.prototype.applyMaskEnd = function () {
var gl = this._gl;
var colorbuffer = this._fboColorBuffer;
var stencilbuffer = this._fboStencilBuffer;
this.flush();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._currentPattern ? this._currentPattern.texture : colorbuffer.targetTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, stencilbuffer.targetTexture);
gl.uniform1i(this._isStencilLocation, 1);
this.fillRect(0, 0, colorbuffer.width, colorbuffer.height);
this.flush();
gl.uniform1i(this._isStencilLocation, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._emptyTexture);
};
CanvasGL.prototype.clearScreen = function () {
var gl = this._gl;
gl.clear(gl.COLOR_BUFFER_BIT);
};
CanvasGL.Point = function (x, y) {
this.x = x;
this.y = y;
};
CanvasGL.Path = function (x, y) {
this.points = [];
this.points.push(new CanvasGL.Point(x, y));
};
CanvasGL.PointAdd = function (p0, p1) {
return new CanvasGL.Point(
p0.x + p1.x,
p0.y + p1.y
);
};
CanvasGL.PointSub = function (p0, p1) {
return new CanvasGL.Point(
p0.x - p1.x,
p0.y - p1.y
);
};
CanvasGL.LineIntersection = function (lineA0, lineB0, lineA1, lineB1) {
var bx = lineB0.x - lineA0.x;
var by = lineB0.y - lineA0.y;
var dx = lineB1.x - lineA1.x;
var dy = lineB1.y - lineA1.y;
var cross = bx * dy - by * dx;
if (cross === 0) return null;
var cx = lineA1.x - lineA0.x;
var cy = lineA1.y - lineA0.y;
var t = (cx * dy - cy * dx) / cross;
if (t < 0 || t > 1) return null;
var u = (cx * by - cy * bx) / cross;
if (u < 0 || u > 1) return null;
return new CanvasGL.Point(
lineA0.x + t * bx,
lineA0.y + t * by
);
};
CanvasGL.prototype.destroy = function () {
var gl = this._gl;
gl.deleteBuffer(this._vbo);
gl.deleteShader(this._fShader);
gl.deleteShader(this._vShader);
gl.deleteProgram(this._program);
this._vbo = null;
this._fShader = null;
this._vShader = null;
this._program = null;
};
CanvasGL.prototype.save = function () {
var index = this._matrixStackPointer;
var stack = this._matrixStack;
var matrix = this._matrix;
stack[index++] = matrix[0];
stack[index++] = matrix[1];
stack[index++] = matrix[2];
stack[index++] = matrix[3];
stack[index++] = matrix[4];
stack[index++] = matrix[5];
this._matrixStackPointer += 6;
};
CanvasGL.prototype.restore = function () {
var index = this._matrixStackPointer;
var stack = this._matrixStack;
var matrix = this._matrix;
matrix[5] = stack[--index];
matrix[4] = stack[--index];
matrix[3] = stack[--index];
matrix[2] = stack[--index];
matrix[1] = stack[--index];
matrix[0] = stack[--index];
this._matrixStackPointer -= 6;
};
CanvasGL.prototype.translate = function (x, y) {
var matrix = this._matrix;
matrix[4] = matrix[0] * x + matrix[2] * y + matrix[4];
matrix[5] = matrix[1] * x + matrix[3] * y + matrix[5];
};
CanvasGL.prototype.scale = function (x, y) {
var matrix = this._matrix;
matrix[0] = matrix[0] * x;
matrix[1] = matrix[1] * x;
matrix[2] = matrix[2] * y;
matrix[3] = matrix[3] * y;
};
CanvasGL.prototype.rotate = function (radian) {
var matrix = this._matrix;
var a = matrix[0];
var b = matrix[1];
var c = matrix[2];
var d = matrix[3];
var sr = Math.sin(radian);
var cr = Math.cos(radian);
matrix[0] = a * cr + c * sr;
matrix[1] = b * cr + d * sr;
matrix[2] = a * -sr + c * cr;
matrix[3] = b * -sr + d * cr;
};
CanvasGL.prototype.transform = function (a, b, c, d, e, f) {
var matrix = this._matrix;
var a2 = a * matrix[0] + b * matrix[2];
var b2 = a * matrix[1] + b * matrix[3];
var c2 = c * matrix[0] + d * matrix[2];
var d2 = c * matrix[1] + d * matrix[3];
var e2 = e * matrix[0] + f * matrix[2] + matrix[4];
var f2 = e * matrix[1] + f * matrix[3] + matrix[5];
this.setTransform(a2, b2, c2, d2, e2, f2);
};
CanvasGL.prototype.setTransform = function (a, b, c, d, e, f) {
var matrix = this._matrix;
matrix[0] = a;
matrix[1] = b;
matrix[2] = c;
matrix[3] = d;
matrix[4] = e;
matrix[5] = f;
};
CanvasGL.prototype.resetTransform = function () {
var matrix = this._matrix;
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
matrix[4] = 0;
matrix[5] = 0;
};
CanvasGL.prototype.arc = function (x, y, radius, startAngle, endAngle, anticlockwise) {
var lerp = function (norm, min, max) {
return (max - min) * norm + min;
};
var iteration = 0;
var iterStep = 0.01;
var tx = 0;
var ty = 0;
var ta = 0;
var cos = Math.cos;
var sin = Math.sin;
while (iteration < 1) {
ta = lerp(iteration, startAngle, endAngle);
tx = x + cos(ta) * radius;
ty = y + sin(ta) * radius;
if (iteration === 0) {
this.moveTo(tx, ty);
} else {
this.lineTo(tx, ty);
}
iteration += iterStep;
}
};
CanvasGL.prototype.fillTriangle = function (tx0, ty0, tx1, ty1, tx2, ty2, fillColor) {
if (this._vertexCount + 3 >= this.MAX_VERTEX_COUNT) {
this.flush();
}
var offset = this._vertexCount * 5;
var position = this._positionBuffer;
var color = this._colorBuffer;
var intColor = fillColor;
var mat = this._matrix;
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var x0 = tx0 * a + ty0 * c + e;
var y0 = tx0 * b + ty0 * d + f;
var x1 = tx1 * a + ty1 * c + e;
var y1 = tx1 * b + ty1 * d + f;
var x2 = tx2 * a + ty2 * c + e;
var y2 = tx2 * b + ty2 * d + f;
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = intColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = intColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = intColor;
this._vertexCount += 3;
};
CanvasGL.prototype.strokeLine = function (ax, ay, bx, by) {
if (this._vertexCount + 6 >= this.MAX_VERTEX_COUNT) {
this.flush();
}
var dx = bx - ax;
var dy = by - ay;
var len = Math.sqrt(dx * dx + dy * dy);
var lineWidth = this._lineWidth * 0.5;
var offset = this._vertexCount * 5;
var position = this._positionBuffer;
var color = this._colorBuffer;
var intColor = this._lineColor;
var mat = this._matrix;
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var lx0 = bx - lineWidth * (by - ay) / len;
var ly0 = by - lineWidth * (ax - bx) / len;
var lx1 = ax - lineWidth * (by - ay) / len;
var ly1 = ay - lineWidth * (ax - bx) / len;
var lx2 = bx + lineWidth * (by - ay) / len;
var ly2 = by + lineWidth * (ax - bx) / len;
var lx3 = ax + lineWidth * (by - ay) / len;
var ly3 = ay + lineWidth * (ax - bx) / len;
var x0 = lx0 * a + ly0 * c + e;
var y0 = lx0 * b + ly0 * d + f;
var x1 = lx1 * a + ly1 * c + e;
var y1 = lx1 * b + ly1 * d + f;
var x2 = lx2 * a + ly2 * c + e;
var y2 = lx2 * b + ly2 * d + f;
var x3 = lx3 * a + ly3 * c + e;
var y3 = lx3 * b + ly3 * d + f;
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 1;
color[offset++] = intColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = intColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = intColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = intColor;
position[offset++] = x3;
position[offset++] = y3;
position[offset++] = 1;
position[offset++] = 0;
color[offset++] = intColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = intColor;
this._vertexCount += 6;
return [
x0, y0,
x1, y1,
x2, y2,
x3, y3
];
};
CanvasGL.prototype.fillRect = function (x, y, width, height) {
if (this._vertexCount + 6 >= this.MAX_VERTEX_COUNT) {
this.flush();
}
var offset = this._vertexCount * 5;
var position = this._positionBuffer;
var color = this._colorBuffer;
var intColor = this._fillColor[0];
var mat = this._matrix;
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var xmin = x;
var ymin = y;
var xmax = x + width;
var ymax = y + height;
var x0 = xmin * a + ymax * c + e;
var y0 = xmin * b + ymax * d + f;
var x1 = xmax * a + ymax * c + e;
var y1 = xmax * b + ymax * d + f;
var x2 = xmin * a + ymin * c + e;
var y2 = xmin * b + ymin * d + f;
var x3 = xmax * a + ymin * c + e;
var y3 = xmax * b + ymin * d + f;
var umin = 0;
var vmin = 0;
var umax = 1;
var vmax = 1;
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = umin;
position[offset++] = vmin;
color[offset++] = intColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = umax;
position[offset++] = vmin;
color[offset++] = intColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = umin;
position[offset++] = vmax;
color[offset++] = intColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = umax;
position[offset++] = vmin;
color[offset++] = intColor;
position[offset++] = x3;
position[offset++] = y3;
position[offset++] = umax;
position[offset++] = vmax;
color[offset++] = intColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = umin;
position[offset++] = vmax;
color[offset++] = intColor;
this._vertexCount += 6;
};
CanvasGL.prototype.beginPath = function () {
this._pathArray.length = 0;
};
CanvasGL.prototype.closePath = function () {
var lastPath = this._lastPath;
if (lastPath !== null && lastPath.points.length > 0) {
var firstPoint = lastPath.points[0];
var lastPoint = lastPath.points[lastPath.points.length - 1];
lastPath.points.push(firstPoint);
this.moveTo(lastPoint.x, lastPoint.y);
}
};
CanvasGL.prototype.lineTo = function (x, y) {
if (this._lastPath !== null) {
this._lastPath.points.push(new CanvasGL.Point(x, y));
} else {
this.moveTo(x, y);
}
};
CanvasGL.prototype.moveTo = function (x, y) {
this._lastPath = new CanvasGL.Path(x, y);
this._pathArray.push(this._lastPath);
};
CanvasGL.prototype.stroke = function () {
var pathArray = this._pathArray;
var pathArrayLength = pathArray.length;
var lineWidth = this.lineWidth * 0.5;
var pathArrayIndex, path, pathLength, pathIndex, point0, point1;
var polylines = [];
var lineColor = this._lineColor;
var index, length, last, curr;
var x0, y0, x1, y1, x2, y2, offset, position, color;
for (pathArrayIndex = 0; pathArrayIndex < pathArrayLength; ++pathArrayIndex) {
path = pathArray[pathArrayIndex].points;
pathLength = path.length;
for (pathIndex = 0; pathIndex + 1 < pathLength; pathIndex += 1) {
point0 = path[pathIndex];
point1 = path[pathIndex + 1];
polylines.push(this.strokeLine(point0.x, point0.y, point1.x, point1.y));
}
if (pathArray[pathArrayIndex] === this._lastPath) {
for (index = 1, length = polylines.length; index < length; ++index) {
last = polylines[index - 1];
curr = polylines[index];
if (this._vertexCount + 6 >= this.MAX_VERTEX_COUNT) {
this.flush();
}
x0 = last[2 * 2 + 0];
y0 = last[2 * 2 + 1];
x1 = last[2 * 0 + 0];
y1 = last[2 * 0 + 1];
x2 = curr[2 * 3 + 0];
y2 = curr[2 * 3 + 1];
offset = this._vertexCount * 5;
position = this._positionBuffer;
color = this._colorBuffer;
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = lineColor;
x0 = last[2 * 0 + 0];
y0 = last[2 * 0 + 1];
x1 = last[2 * 2 + 0];
y1 = last[2 * 2 + 1];
x2 = curr[2 * 1 + 0];
y2 = curr[2 * 1 + 1];
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = lineColor;
this._vertexCount += 6;
}
} else {
for (index = 0, length = polylines.length; index < length; ++index) {
last = polylines[index - 1] || polylines[polylines.length - 1];
curr = polylines[index];
if (this._vertexCount + 6 >= this.MAX_VERTEX_COUNT) {
this.flush();
}
x0 = last[2 * 2 + 0];
y0 = last[2 * 2 + 1];
x1 = last[2 * 0 + 0];
y1 = last[2 * 0 + 1];
x2 = curr[2 * 3 + 0];
y2 = curr[2 * 3 + 1];
offset = this._vertexCount * 5;
position = this._positionBuffer;
color = this._colorBuffer;
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = lineColor;
x0 = last[2 * 0 + 0];
y0 = last[2 * 0 + 1];
x1 = last[2 * 2 + 0];
y1 = last[2 * 2 + 1];
x2 = curr[2 * 1 + 0];
y2 = curr[2 * 1 + 1];
position[offset++] = x0;
position[offset++] = y0;
position[offset++] = 0;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x1;
position[offset++] = y1;
position[offset++] = 1;
position[offset++] = 1;
color[offset++] = lineColor;
position[offset++] = x2;
position[offset++] = y2;
position[offset++] = 0;
position[offset++] = 0;
color[offset++] = lineColor;
this._vertexCount += 6;
}
}
polylines.length = 0;
}
};
CanvasGL.prototype.fill = function () {
var pathArray = this._pathArray;
var pathArrayLength = pathArray.length;
var polygon = [];
for (var pathArrayIndex = 0; pathArrayIndex < pathArrayLength; ++pathArrayIndex) {
var path = pathArray[pathArrayIndex].points;
var pathLength = path.length;
for (var pathIndex = 0; pathIndex < pathLength; ++pathIndex) {
var point = path[pathIndex];
polygon.push(point.x, point.y);
}
var polygonIndex = earcut(polygon);
for (var index = 0, length = polygonIndex.length; index < length; index += 3) {
var v0 = polygonIndex[index + 0] * 2;
var v1 = polygonIndex[index + 1] * 2;
var v2 = polygonIndex[index + 2] * 2;
this.fillTriangle(
polygon[v0 + 0],
polygon[v0 + 1],
polygon[v1 + 0],
polygon[v1 + 1],
polygon[v2 + 0],
polygon[v2 + 1],
this._fillColor
);
}
polygon.length = 0;
}
};
CanvasGL.prototype.bind = function () {
var gl = this._gl;
var vertexSize = this.VERTEX_SIZE;
var inPositionLocation = this._inPositionLocation;
var inColorLocation = this._inColorLocation;
var inTexCoordLocation = this._inTexCoordLocation;
gl.useProgram(this._program);
gl.bindBuffer(gl.ARRAY_BUFFER, this._vbo);
gl.enableVertexAttribArray(inPositionLocation);
gl.enableVertexAttribArray(inColorLocation);
gl.enableVertexAttribArray(inTexCoordLocation);
gl.vertexAttribPointer(inPositionLocation, 2, gl.FLOAT, gl.FALSE, vertexSize, 0);
gl.vertexAttribPointer(inTexCoordLocation, 2, gl.FLOAT, gl.FALSE, vertexSize, 8);
gl.vertexAttribPointer(inColorLocation, 4, gl.UNSIGNED_BYTE, gl.TRUE, vertexSize, 16);
};
CanvasGL.prototype.flush = function () {
var gl = this._gl;
var vertexCount = this._vertexCount;
if (vertexCount > 0) {
this.bind();
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._positionBuffer.subarray(0, vertexCount * 5));
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
this._vertexCount = 0;
}
};
Object.defineProperty(CanvasGL.prototype, 'fillStyleUINT', {
get: function () {
return this._fillColor[0];
},
set: function (color) {
if (typeof color === 'number') {
this._fillColor[0] = color;
}
}
});
Object.defineProperty(CanvasGL.prototype, 'strokeStyleUINT', {
get: function () {
return this._lineColor;
},
set: function (color) {
if (typeof color === 'number') {
this._lineColor = color;
}
}
});
Object.defineProperty(CanvasGL.prototype, 'fillStyle', {
get: function () {
return '#' + this._fillColor[0].toString(16);
},
set: function (fillStyle) {
this.fillStyleUINT = (0xFF << 24) + parseInt(fillStyle.replace('#', ''), 16);
}
});
Object.defineProperty(CanvasGL.prototype, 'strokeStyle', {
get: function () {
return '#' + this._lineColor.toString(16);
},
set: function (strokeStyle) {
this.strokeStyleUINT = parseInt(strokeStyle.replace('#', ''), 16);
}
});
Object.defineProperty(CanvasGL.prototype, 'lineWidth', {
get: function () {
return this._lineWidth;
},
set: function (lineWidth) {
if (typeof lineWidth === 'number')
this._lineWidth = lineWidth;
}
});
Object.defineProperty(CanvasGL.prototype, 'VERTEX_SIZE', {
get: function () {
return (4 * 2) + (4 * 2) + 4;
}
});
Object.defineProperty(CanvasGL.prototype, 'MAX_VERTEX_COUNT', {
get: function () {
return 20000 * 12;
}
});
Object.defineProperty(CanvasGL.prototype, 'canvas', {
get: function () {
return this._gl.canvas;
}
});