diff --git a/README.md b/README.md
index 06538bc..c28cefb 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,6 @@ WebGL-2D is a proof of concept and attempts to ascertain performance improvement
It should allow _most_ Canvas2D applications to be switched to a WebGL context.
-Check out a [LIVE DEMO!](http://weare.buildingsky.net/webgl-2d/example.html)
-
![20110304-qfkhgf9hjin5uk3we9rmgpwtf8.jpg](https://img.skitch.com/20110304-qfkhgf9hjin5uk3we9rmgpwtf8.jpg)
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..cd6c077
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,24 @@
+{
+ "name": "webgl-2d",
+ "main": "webgl-2d.js",
+ "homepage": "https://github.com/lekzd/webgl-2d",
+ "authors": [
+ "gameclosure"
+ ],
+ "description": "Use WebGl features as HTML5 Canvas api",
+ "moduleType": [],
+ "keywords": [
+ "WebGl"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "benchmarks",
+ "examples",
+ "support",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
diff --git a/examples/animation.html b/examples/animation.html
new file mode 100644
index 0000000..306b0bd
--- /dev/null
+++ b/examples/animation.html
@@ -0,0 +1,59 @@
+
Canvas2D
+
+Webgl-2d
+
+
+
diff --git a/examples/animationTransparency.html b/examples/animationTransparency.html
new file mode 100644
index 0000000..1e434c1
--- /dev/null
+++ b/examples/animationTransparency.html
@@ -0,0 +1,61 @@
+Canvas2D
+
+Webgl-2d
+
+
+
diff --git a/examples/createPattern.html b/examples/createPattern.html
new file mode 100644
index 0000000..6683741
--- /dev/null
+++ b/examples/createPattern.html
@@ -0,0 +1,63 @@
+
+
+
+ WebGL-2D Example
+
+
+
+
+
+
+
+
+
+
+ Example: WebGL-2D Comparison
+ drawImage
+
+
WebGL-2D
+
+
+
+
Canvas2D
+
+
+
+
diff --git a/examples/hslPalette.html b/examples/hslPalette.html
new file mode 100644
index 0000000..0db45b9
--- /dev/null
+++ b/examples/hslPalette.html
@@ -0,0 +1,35 @@
+Canvas2D
+
+Webgl-2d
+
+
+
diff --git a/examples/hslaPalette.html b/examples/hslaPalette.html
new file mode 100644
index 0000000..6e15f95
--- /dev/null
+++ b/examples/hslaPalette.html
@@ -0,0 +1,43 @@
+Canvas2D
+
+Webgl-2d
+
+
+
diff --git a/examples/rgbaPalette.html b/examples/rgbaPalette.html
new file mode 100644
index 0000000..0a2eb1d
--- /dev/null
+++ b/examples/rgbaPalette.html
@@ -0,0 +1,35 @@
+Canvas2D
+
+Webgl-2d
+
+
+
diff --git a/webgl-2d.js b/webgl-2d.js
index 3979214..320d3c5 100644
--- a/webgl-2d.js
+++ b/webgl-2d.js
@@ -38,7 +38,9 @@
*
* WebGL2D.enable(cvs); // adds "webgl-2d" to cvs
*
- * cvs.getContext("webgl-2d");
+ * var ctx = cvs.getContext("webgl-2d"); // Attempt to get webgl context
+ *
+ * ctx.isWebGL // Detect whether WebGL-2D is active on this context.
*
*/
@@ -280,28 +282,28 @@
var gl = gl2d.gl = gl2d.canvas.$getContext("experimental-webgl");
+ // If we failed to get a WebGL context, return a normal 2D context instead.
+ if ((typeof (gl) === "undefined") || (gl === null)) {
+ return gl2d.canvas.$getContext("2d");
+ }
+
gl2d.initShaders();
gl2d.initBuffers();
// Append Canvas2D API features to the WebGL context
gl2d.initCanvas2DAPI();
- gl.viewport(0, 0, gl2d.canvas.width, gl2d.canvas.height);
-
- // Default white background
- gl.clearColor(1, 1, 1, 1);
- gl.clear(gl.COLOR_BUFFER_BIT); // | gl.DEPTH_BUFFER_BIT);
-
// Disables writing to dest-alpha
- gl.colorMask(1,1,1,0);
+ // gl.colorMask(1, 1, 1, 0);
// Depth options
- //gl.enable(gl.DEPTH_TEST);
- //gl.depthFunc(gl.LEQUAL);
+ // gl.enable(gl.DEPTH_TEST);
+ // gl.depthFunc(gl.LEQUAL);
// Blending options
+ // See http://stackoverflow.com/questions/11521035/blending-with-html-background-in-webgl
gl.enable(gl.BLEND);
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl2d.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
@@ -336,8 +338,8 @@
"precision highp float;",
"#endif",
- "#define hasTexture " + ((sMask&shaderMask.texture) ? "1" : "0"),
- "#define hasCrop " + ((sMask&shaderMask.crop) ? "1" : "0"),
+ "#define hasTexture " + ((sMask == shaderMask.texture) ? "1" : "0"),
+ "#define hasCrop " + ((sMask == shaderMask.crop) ? "1" : "0"),
"varying vec4 vColor;",
@@ -365,13 +367,11 @@
return fsSource;
};
- WebGL2D.prototype.getVertexShaderSource = function getVertexShaderSource(stackDepth,sMask) {
- var w = 2 / this.canvas.width, h = -2 / this.canvas.height;
-
+ WebGL2D.prototype.getVertexShaderSource = function getVertexShaderSource(stackDepth, sMask) {
stackDepth = stackDepth || 1;
var vsSource = [
- "#define hasTexture " + ((sMask&shaderMask.texture) ? "1" : "0"),
+ "#define hasTexture " + ((sMask == shaderMask.texture) ? "1" : "0"),
"attribute vec4 aVertexPosition;",
"#if hasTexture",
@@ -383,7 +383,7 @@
"varying vec4 vColor;",
- "const mat4 pMatrix = mat4(" + w + ",0,0,0, 0," + h + ",0,0, 0,0,1.0,1.0, -1.0,1.0,0,0);",
+ "uniform mat4 pMatrix;",
"mat3 crunchStack(void) {",
"mat3 result = uTransforms[0];",
@@ -407,7 +407,7 @@
// Initialize fragment and vertex shaders
- WebGL2D.prototype.initShaders = function initShaders(transformStackDepth,sMask) {
+ WebGL2D.prototype.initShaders = function initShaders(transformStackDepth, sMask) {
var gl = this.gl;
transformStackDepth = transformStackDepth || 1;
@@ -420,25 +420,23 @@
if (storedShader) {
gl.useProgram(storedShader);
this.shaderProgram = storedShader;
- return storedShader;
} else {
var fs = this.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(this.fs, this.getFragmentShaderSource(sMask));
gl.compileShader(this.fs);
if (!gl.getShaderParameter(this.fs, gl.COMPILE_STATUS)) {
- throw "fragment shader error: "+gl.getShaderInfoLog(this.fs);
+ throw "fragment shader error: " + gl.getShaderInfoLog(this.fs);
}
var vs = this.vs = gl.createShader(gl.VERTEX_SHADER);
- gl.shaderSource(this.vs, this.getVertexShaderSource(transformStackDepth,sMask));
+ gl.shaderSource(this.vs, this.getVertexShaderSource(transformStackDepth, sMask));
gl.compileShader(this.vs);
if (!gl.getShaderParameter(this.vs, gl.COMPILE_STATUS)) {
- throw "vertex shader error: "+gl.getShaderInfoLog(this.vs);
+ throw "vertex shader error: " + gl.getShaderInfoLog(this.vs);
}
-
var shaderProgram = this.shaderProgram = gl.createProgram();
shaderProgram.stackDepth = transformStackDepth;
gl.attachShader(shaderProgram, fs);
@@ -458,20 +456,24 @@
shaderProgram.uSampler = gl.getUniformLocation(shaderProgram, 'uSampler');
shaderProgram.uCropSource = gl.getUniformLocation(shaderProgram, 'uCropSource');
+ shaderProgram.pMatrix = gl.getUniformLocation(shaderProgram, 'pMatrix');
+
shaderProgram.uTransforms = [];
for (var i=0; i 100 ? 1 : l / 100;
l = l < 0 ? 0 : l;
- m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
- m1 = l * 2 - m2;
+ if(s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ function hue2rgb(p, q, t){
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ }
- function getHue(value) {
- var hue;
+ q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ p = 2 * l - q;
- if (value * 6 < 1) {
- hue = m1 + (m2 - m1) * value * 6;
- } else if (value * 2 < 1) {
- hue = m2;
- } else if (value * 3 < 2) {
- hue = m1 + (m2 - m1) * (2/3 - value) * 6;
- } else {
- hue = m1;
- }
-
- return hue;
+ r = hue2rgb(p, q, h + 1/3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3);
}
- r = getHue(h + 1/3);
- g = getHue(h);
- b = getHue(h - 1/3);
-
return [r, g, b, a];
}
@@ -806,7 +812,11 @@
Object.defineProperty(gl, "fillStyle", {
get: function() { return colorVecToString(drawState.fillStyle); },
set: function(value) {
- drawState.fillStyle = colorStringToVec4(value) || drawState.fillStyle;
+ if (value instanceof GlPattern) {
+ drawState.fillStyle = value;
+ } else {
+ drawState.fillStyle = colorStringToVec4(value) || drawState.fillStyle;
+ }
}
});
@@ -1035,31 +1045,104 @@
};
gl.fillRect = function fillRect(x, y, width, height) {
- var transform = gl2d.transform;
- var shaderProgram = gl2d.initShaders(transform.c_stack+2,0);
+ if (drawState.fillStyle instanceof GlPattern) {
+ fillRectPattern.apply(this, arguments);
+ } else {
+ fillRectColor.apply(this, arguments);
+ }
+ };
- gl.bindBuffer(gl.ARRAY_BUFFER, rectVertexPositionBuffer);
- gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
+ function fillRectColor(x, y, width, height) {
+ var transform = gl2d.transform;
+ var shaderProgram = gl2d.initShaders(transform.c_stack + 2, 0);
transform.pushMatrix();
-
transform.translate(x, y);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.rectVertexPositionBuffer);
+ gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
+
+ var r = drawState.fillStyle[0],
+ g = drawState.fillStyle[1],
+ b = drawState.fillStyle[2],
+ a = drawState.fillStyle[3];
transform.scale(width, height);
+ gl.uniform4f(shaderProgram.uColor, r, g, b, a);
sendTransformStack(shaderProgram);
+ gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
+
+ transform.popMatrix();
+ };
+
+ function getRectVertices(x, y) {
+ x2 = x + 1;
+ y2 = y + 1;
+ return [
+ x,y, x,y,
+ x,y2, x,y2,
+ x2,y2, x2,y2,
+ x2,y, x2,y
+ ];
+ };
- gl.uniform4f(shaderProgram.uColor, drawState.fillStyle[0], drawState.fillStyle[1], drawState.fillStyle[2], drawState.fillStyle[3]);
+ function fillRectPattern(x, y, width, height) {
+ var transform = gl2d.transform;
+ var shaderProgram = gl2d.initShaders(transform.c_stack, shaderMask.texture);
+ var textureWidth = drawState.fillStyle.width;
+ var textureHeight = drawState.fillStyle.height;
- gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
+ transform.pushMatrix();
+
+ transform.translate(0, 0);
+
+ var horisontalRepeats = 1;
+ if (~['repeat', 'repeat-x'].indexOf(drawState.fillStyle.direction)) {
+ horisontalRepeats = Math.ceil((x + width) / textureWidth);
+ }
+ var verticalRepeats = 1;
+ if (~['repeat', 'repeat-y'].indexOf(drawState.fillStyle.direction)) {
+ verticalRepeats = Math.ceil((y + height) / textureHeight);
+ }
+ var verts = [];
+ for (vX = 0; vX < horisontalRepeats; vX++) {
+ for (vY = 0; vY < verticalRepeats; vY++) {
+ verts = verts.concat(getRectVertices(vX, vY));
+ }
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.pathVertexPositionBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
+
+ var texture = drawState.fillStyle.texture;
+
+ transform.scale(textureWidth, textureHeight);
+ gl.bindTexture(gl.TEXTURE_2D, texture.obj);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+ gl.activeTexture(gl.TEXTURE0);
+
+ gl.uniform1i(shaderProgram.uSampler, 0);
+
+ gl.enable(gl.SCISSOR_TEST);
+ var verticalOffset = (height - (verticalRepeats * textureHeight)) / 2;
+ gl.scissor(x, gl2d.canvas.height - height - y, width, height);
+
+ sendTransformStack(shaderProgram);
+ gl.drawArrays(gl.TRIANGLE_FAN, 0, verts.length / 4);
+
+ gl.disable(gl.SCISSOR_TEST);
transform.popMatrix();
};
gl.strokeRect = function strokeRect(x, y, width, height) {
var transform = gl2d.transform;
- var shaderProgram = gl2d.initShaders(transform.c_stack + 2,0);
+ var shaderProgram = gl2d.initShaders(transform.c_stack + 2, 0);
- gl.bindBuffer(gl.ARRAY_BUFFER, rectVertexPositionBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.rectVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
transform.pushMatrix();
@@ -1141,7 +1224,7 @@
var subPath = subPaths[index];
var verts = subPath.verts;
- gl.bindBuffer(gl.ARRAY_BUFFER, pathVertexPositionBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.pathVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
@@ -1150,9 +1233,12 @@
sendTransformStack(shaderProgram);
- gl.uniform4f(shaderProgram.uColor, drawState.fillStyle[0], drawState.fillStyle[1], drawState.fillStyle[2], drawState.fillStyle[3]);
-
- gl.drawArrays(gl.TRIANGLE_FAN, 0, verts.length/4);
+ var r = drawState.fillStyle[0],
+ g = drawState.fillStyle[1],
+ b = drawState.fillStyle[2],
+ a = drawState.fillStyle[3];
+ gl.uniform4f(shaderProgram.uColor, r, g, b, a);
+ gl.drawArrays(gl.TRIANGLE_FAN, 0, verts.length / 4);
transform.popMatrix();
}
@@ -1170,7 +1256,7 @@
var subPath = subPaths[index];
var verts = subPath.verts;
- gl.bindBuffer(gl.ARRAY_BUFFER, pathVertexPositionBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.pathVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
@@ -1245,6 +1331,17 @@
gl.bindTexture(gl.TEXTURE_2D, null);
}
+ var getTextureFromImage = function(image) {
+ var texture, cacheIndex = imageCache.indexOf(image);
+
+ if (cacheIndex !== -1) {
+ texture = textureCache[cacheIndex];
+ } else {
+ texture = new Texture(image);
+ }
+ return texture;
+ };
+
gl.drawImage = function drawImage(image, a, b, c, d, e, f, g, h) {
var transform = gl2d.transform;
@@ -1274,25 +1371,18 @@
}
var shaderProgram = gl2d.initShaders(transform.c_stack, sMask);
-
- var texture, cacheIndex = imageCache.indexOf(image);
-
- if (cacheIndex !== -1) {
- texture = textureCache[cacheIndex];
- } else {
- texture = new Texture(image);
- }
+ var texture = getTextureFromImage(image);
if (doCrop) {
gl.uniform4f(shaderProgram.uCropSource, a/image.width, b/image.height, c/image.width, d/image.height);
}
- gl.bindBuffer(gl.ARRAY_BUFFER, rectVertexPositionBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.rectVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
gl.bindTexture(gl.TEXTURE_2D, texture.obj);
gl.activeTexture(gl.TEXTURE0);
-
+ gl.colorMask(1, 1, 1, 1);
gl.uniform1i(shaderProgram.uSampler, 0);
sendTransformStack(shaderProgram);
@@ -1300,6 +1390,34 @@
transform.popMatrix();
};
+
+ var GlPattern = function(image, direction) {
+ var texture = getTextureFromImage(image);
+ var REPEAT_DIRECTIONS = [
+ 'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
+ ];
+ direction = String(direction).toLowerCase();
+ if (!~REPEAT_DIRECTIONS.indexOf(direction)) {
+ direction = REPEAT_DIRECTIONS[0];
+ }
+
+ this.direction = direction;
+ this.texture = texture;
+ this.width = image.width;
+ this.height = image.height;
+ };
+
+ gl.createPattern = function createPattern(image, direction) {
+ return new GlPattern(image, direction);
+ };
+
+ // This enables the user to detect whether they got a webgl-2d context or a 2d context.
+ Object.defineProperty(gl, "isWebGL", {
+ configurable: false,
+ enumerable: true,
+ writable: false,
+ value: true
+ });
};
}(Math));