├── 3rdparty ├── m3.js └── twgl-full.min.js ├── LICENSE.md ├── README.md ├── test ├── f-texture.png ├── test-2d.html ├── test-rect-alpha-2d.html ├── test-rect-alpha-2d.js ├── test-rect-alpha-webgl.html ├── test-webgl.html └── test.js ├── webgl-canvas-2d-shim.js └── webgl-canvas-2d.js /3rdparty/m3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Gregg Tavares. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Gregg Tavares. nor the names of his 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * Various 2d math functions. 34 | * 35 | * @module webgl-2d-math 36 | */ 37 | (function(root, factory) { // eslint-disable-line 38 | if (typeof define === 'function' && define.amd) { 39 | // AMD. Register as an anonymous module. 40 | define([], factory); 41 | } else { 42 | // Browser globals 43 | root.m3 = factory(); 44 | } 45 | }(this, function() { 46 | "use strict"; 47 | 48 | /** 49 | * An array or typed array with 9 values. 50 | * @typedef {number[]|TypedArray} Matrix3 51 | * @memberOf module:webgl-2d-math 52 | */ 53 | 54 | /** 55 | * Takes two Matrix3s, a and b, and computes the product in the order 56 | * that pre-composes b with a. In other words, the matrix returned will 57 | * @param {module:webgl-2d-math.Matrix3} a A matrix. 58 | * @param {module:webgl-2d-math.Matrix3} b A matrix. 59 | * @return {module:webgl-2d-math.Matrix3} the result. 60 | * @memberOf module:webgl-2d-math 61 | */ 62 | function multiply(a, b) { 63 | var a00 = a[0 * 3 + 0]; 64 | var a01 = a[0 * 3 + 1]; 65 | var a02 = a[0 * 3 + 2]; 66 | var a10 = a[1 * 3 + 0]; 67 | var a11 = a[1 * 3 + 1]; 68 | var a12 = a[1 * 3 + 2]; 69 | var a20 = a[2 * 3 + 0]; 70 | var a21 = a[2 * 3 + 1]; 71 | var a22 = a[2 * 3 + 2]; 72 | var b00 = b[0 * 3 + 0]; 73 | var b01 = b[0 * 3 + 1]; 74 | var b02 = b[0 * 3 + 2]; 75 | var b10 = b[1 * 3 + 0]; 76 | var b11 = b[1 * 3 + 1]; 77 | var b12 = b[1 * 3 + 2]; 78 | var b20 = b[2 * 3 + 0]; 79 | var b21 = b[2 * 3 + 1]; 80 | var b22 = b[2 * 3 + 2]; 81 | 82 | return [ 83 | b00 * a00 + b01 * a10 + b02 * a20, 84 | b00 * a01 + b01 * a11 + b02 * a21, 85 | b00 * a02 + b01 * a12 + b02 * a22, 86 | b10 * a00 + b11 * a10 + b12 * a20, 87 | b10 * a01 + b11 * a11 + b12 * a21, 88 | b10 * a02 + b11 * a12 + b12 * a22, 89 | b20 * a00 + b21 * a10 + b22 * a20, 90 | b20 * a01 + b21 * a11 + b22 * a21, 91 | b20 * a02 + b21 * a12 + b22 * a22, 92 | ]; 93 | } 94 | 95 | 96 | /** 97 | * Creates a 3x3 identity matrix 98 | * @return {module:webgl2-2d-math.Matrix3} an identity matrix 99 | */ 100 | function identity() { 101 | return [ 102 | 1, 0, 0, 103 | 0, 1, 0, 104 | 0, 0, 1, 105 | ]; 106 | } 107 | 108 | /** 109 | * Creates a 2D projection matrix 110 | * @param {number} width width in pixels 111 | * @param {number} height height in pixels 112 | * @return {module:webgl-2d-math.Matrix3} a projection matrix that converts from pixels to clipspace with Y = 0 at the top. 113 | * @memberOf module:webgl-2d-math 114 | */ 115 | function projection(width, height) { 116 | // Note: This matrix flips the Y axis so 0 is at the top. 117 | return [ 118 | 2 / width, 0, 0, 119 | 0, -2 / height, 0, 120 | -1, 1, 1, 121 | ]; 122 | } 123 | 124 | /** 125 | * Multiplies by a 2D projection matrix 126 | * @param {module:webgl-2d-math.Matrix3} the matrix to be multiplied 127 | * @param {number} width width in pixels 128 | * @param {number} height height in pixels 129 | * @return {module:webgl-2d-math.Matrix3} the result 130 | * @memberOf module:webgl-2d-math 131 | */ 132 | function project(m, width, height) { 133 | return multiply(m, projection(width, height)); 134 | } 135 | 136 | /** 137 | * Creates a 2D translation matrix 138 | * @param {number} tx amount to translate in x 139 | * @param {number} ty amount to translate in y 140 | * @return {module:webgl-2d-math.Matrix3} a translation matrix that translates by tx and ty. 141 | * @memberOf module:webgl-2d-math 142 | */ 143 | function translation(tx, ty) { 144 | return [ 145 | 1, 0, 0, 146 | 0, 1, 0, 147 | tx, ty, 1, 148 | ]; 149 | } 150 | 151 | /** 152 | * Multiplies by a 2D translation matrix 153 | * @param {module:webgl-2d-math.Matrix3} the matrix to be multiplied 154 | * @param {number} tx amount to translate in x 155 | * @param {number} ty amount to translate in y 156 | * @return {module:webgl-2d-math.Matrix3} the result 157 | * @memberOf module:webgl-2d-math 158 | */ 159 | function translate(m, tx, ty) { 160 | return multiply(m, translation(tx, ty)); 161 | } 162 | 163 | /** 164 | * Creates a 2D rotation matrix 165 | * @param {number} angleInRadians amount to rotate in radians 166 | * @return {module:webgl-2d-math.Matrix3} a rotation matrix that rotates by angleInRadians 167 | * @memberOf module:webgl-2d-math 168 | */ 169 | function rotation(angleInRadians) { 170 | var c = Math.cos(angleInRadians); 171 | var s = Math.sin(angleInRadians); 172 | return [ 173 | c, -s, 0, 174 | s, c, 0, 175 | 0, 0, 1, 176 | ]; 177 | } 178 | 179 | /** 180 | * Multiplies by a 2D rotation matrix 181 | * @param {module:webgl-2d-math.Matrix3} the matrix to be multiplied 182 | * @param {number} angleInRadians amount to rotate in radians 183 | * @return {module:webgl-2d-math.Matrix3} the result 184 | * @memberOf module:webgl-2d-math 185 | */ 186 | function rotate(m, angleInRadians) { 187 | return multiply(m, rotation(angleInRadians)); 188 | } 189 | 190 | /** 191 | * Creates a 2D scaling matrix 192 | * @param {number} sx amount to scale in x 193 | * @param {number} sy amount to scale in y 194 | * @return {module:webgl-2d-math.Matrix3} a scale matrix that scales by sx and sy. 195 | * @memberOf module:webgl-2d-math 196 | */ 197 | function scaling(sx, sy) { 198 | return [ 199 | sx, 0, 0, 200 | 0, sy, 0, 201 | 0, 0, 1, 202 | ]; 203 | } 204 | 205 | /** 206 | * Multiplies by a 2D scaling matrix 207 | * @param {module:webgl-2d-math.Matrix3} the matrix to be multiplied 208 | * @param {number} sx amount to scale in x 209 | * @param {number} sy amount to scale in y 210 | * @return {module:webgl-2d-math.Matrix3} the result 211 | * @memberOf module:webgl-2d-math 212 | */ 213 | function scale(m, sx, sy) { 214 | return multiply(m, scaling(sx, sy)); 215 | } 216 | 217 | function dot(x1, y1, x2, y2) { 218 | return x1 * x2 + y1 * y2; 219 | } 220 | 221 | function distance(x1, y1, x2, y2) { 222 | var dx = x1 - x2; 223 | var dy = y1 - y2; 224 | return Math.sqrt(dx * dx + dy * dy); 225 | } 226 | 227 | function normalize(x, y) { 228 | var l = distance(0, 0, x, y); 229 | if (l > 0.00001) { 230 | return [x / l, y / l]; 231 | } else { 232 | return [0, 0]; 233 | } 234 | } 235 | 236 | // i = incident 237 | // n = normal 238 | function reflect(ix, iy, nx, ny) { 239 | // I - 2.0 * dot(N, I) * N. 240 | var d = dot(nx, ny, ix, iy); 241 | return [ 242 | ix - 2 * d * nx, 243 | iy - 2 * d * ny, 244 | ]; 245 | } 246 | 247 | function radToDeg(r) { 248 | return r * 180 / Math.PI; 249 | } 250 | 251 | function degToRad(d) { 252 | return d * Math.PI / 180; 253 | } 254 | 255 | function transformPoint(m, v) { 256 | var v0 = v[0]; 257 | var v1 = v[1]; 258 | var d = v0 * m[0 * 3 + 2] + v1 * m[1 * 3 + 2] + m[2 * 3 + 2]; 259 | return [ 260 | (v0 * m[0 * 3 + 0] + v1 * m[1 * 3 + 0] + m[2 * 3 + 0]) / d, 261 | (v0 * m[0 * 3 + 1] + v1 * m[1 * 3 + 1] + m[2 * 3 + 1]) / d, 262 | ]; 263 | } 264 | 265 | function inverse(m) { 266 | var t00 = m[1 * 3 + 1] * m[2 * 3 + 2] - m[1 * 3 + 2] * m[2 * 3 + 1]; 267 | var t10 = m[0 * 3 + 1] * m[2 * 3 + 2] - m[0 * 3 + 2] * m[2 * 3 + 1]; 268 | var t20 = m[0 * 3 + 1] * m[1 * 3 + 2] - m[0 * 3 + 2] * m[1 * 3 + 1]; 269 | var d = 1.0 / (m[0 * 3 + 0] * t00 - m[1 * 3 + 0] * t10 + m[2 * 3 + 0] * t20); 270 | return [ 271 | d * t00, -d * t10, d * t20, 272 | -d * (m[1 * 3 + 0] * m[2 * 3 + 2] - m[1 * 3 + 2] * m[2 * 3 + 0]), 273 | d * (m[0 * 3 + 0] * m[2 * 3 + 2] - m[0 * 3 + 2] * m[2 * 3 + 0]), 274 | -d * (m[0 * 3 + 0] * m[1 * 3 + 2] - m[0 * 3 + 2] * m[1 * 3 + 0]), 275 | d * (m[1 * 3 + 0] * m[2 * 3 + 1] - m[1 * 3 + 1] * m[2 * 3 + 0]), 276 | -d * (m[0 * 3 + 0] * m[2 * 3 + 1] - m[0 * 3 + 1] * m[2 * 3 + 0]), 277 | d * (m[0 * 3 + 0] * m[1 * 3 + 1] - m[0 * 3 + 1] * m[1 * 3 + 0]), 278 | ]; 279 | } 280 | 281 | return { 282 | degToRad: degToRad, 283 | distance: distance, 284 | dot: dot, 285 | identity: identity, 286 | inverse: inverse, 287 | multiply: multiply, 288 | normalize: normalize, 289 | projection: projection, 290 | radToDeg: radToDeg, 291 | reflect: reflect, 292 | rotation: rotation, 293 | rotate: rotate, 294 | scaling: scaling, 295 | scale: scale, 296 | transformPoint: transformPoint, 297 | translation: translation, 298 | translate: translate, 299 | project: project, 300 | }; 301 | 302 | })); 303 | 304 | -------------------------------------------------------------------------------- /3rdparty/twgl-full.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @license twgl.js 4.9.0 Copyright (c) 2015, Gregg Tavares All Rights Reserved. 3 | * Available via the MIT license. 4 | * see: http://github.com/greggman/twgl.js for details 5 | */ 6 | !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.twgl=r():e.twgl=r()}("undefined"!=typeof self?self:this,function(){return function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=8)}([function(e,r,t){"use strict";r.__esModule=!0,r.copyExistingProperties=function(e,r){Object.keys(r).forEach(function(t){r.hasOwnProperty(t)&&e.hasOwnProperty(t)&&(r[t]=e[t])})},r.copyNamedProperties=function(e,r,t){e.forEach(function(e){var n=r[e];void 0!==n&&(t[e]=n)})},r.isBuffer=function(e,r){o||(o=e.createBuffer());return r instanceof o.constructor},r.isRenderbuffer=function(e,r){i||(i=e.createRenderbuffer());return r instanceof i.constructor},r.isShader=function(e,r){u||(u=e.createShader(e.VERTEX_SHADER));return r instanceof u.constructor},r.isTexture=function(e,r){a||(a=e.createTexture());return r instanceof a.constructor},r.isSampler=function(e,r){if(!f){if(!e.createSampler)return!1;f=e.createSampler()}return r instanceof f.constructor},r.warn=r.error=void 0;var n="undefined"!=typeof console&&console.error&&"function"==typeof console.error?console.error.bind(console):function(){};r.error=n;var o,i,u,a,f,c="undefined"!=typeof console&&console.warn&&"function"==typeof console.warn?console.warn.bind(console):function(){};r.warn=c},function(e,r,t){"use strict";r.__esModule=!0,r.getGLTypeForTypedArray=function(e){if(e instanceof Int8Array)return n;if(e instanceof Uint8Array)return o;if(e instanceof Uint8ClampedArray)return o;if(e instanceof Int16Array)return i;if(e instanceof Uint16Array)return u;if(e instanceof Int32Array)return a;if(e instanceof Uint32Array)return f;if(e instanceof Float32Array)return c;throw"unsupported typed array type"},r.getGLTypeForTypedArrayType=function(e){if(e===Int8Array)return n;if(e===Uint8Array)return o;if(e===Uint8ClampedArray)return o;if(e===Int16Array)return i;if(e===Uint16Array)return u;if(e===Int32Array)return a;if(e===Uint32Array)return f;if(e===Float32Array)return c;throw"unsupported typed array type"},r.getTypedArrayTypeForGLType=function(e){var r=l[e];if(!r)throw"unknown gl type";return r},r.isArrayBuffer=void 0;var n=5120,o=5121,i=5122,u=5123,a=5124,f=5125,c=5126,l={},s=l;s[n]=Int8Array,s[o]=Uint8Array,s[i]=Int16Array,s[u]=Uint16Array,s[a]=Int32Array,s[f]=Uint32Array,s[c]=Float32Array,s[32819]=Uint16Array,s[32820]=Uint16Array,s[33635]=Uint16Array,s[5131]=Uint16Array,s[33640]=Uint32Array,s[35899]=Uint32Array,s[35902]=Uint32Array,s[36269]=Uint32Array,s[34042]=Uint32Array;var v="undefined"!=typeof SharedArrayBuffer?function(e){return e&&e.buffer&&(e.buffer instanceof ArrayBuffer||e.buffer instanceof SharedArrayBuffer)}:function(e){return e&&e.buffer&&e.buffer instanceof ArrayBuffer};r.isArrayBuffer=v},function(e,r,t){"use strict";r.__esModule=!0,r.add=function(e,r,t){return(t=t||new n(3))[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t},r.copy=function(e,r){return(r=r||new n(3))[0]=e[0],r[1]=e[1],r[2]=e[2],r},r.create=function(e,r,t){var o=new n(3);e&&(o[0]=e);r&&(o[1]=r);t&&(o[2]=t);return o},r.cross=function(e,r,t){t=t||new n(3);var o=e[2]*r[0]-e[0]*r[2],i=e[0]*r[1]-e[1]*r[0];return t[0]=e[1]*r[2]-e[2]*r[1],t[1]=o,t[2]=i,t},r.distance=function(e,r){var t=e[0]-r[0],n=e[1]-r[1],o=e[2]-r[2];return Math.sqrt(t*t+n*n+o*o)},r.distanceSq=function(e,r){var t=e[0]-r[0],n=e[1]-r[1],o=e[2]-r[2];return t*t+n*n+o*o},r.divide=function(e,r,t){return(t=t||new n(3))[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t},r.divScalar=function(e,r,t){return(t=t||new n(3))[0]=e[0]/r,t[1]=e[1]/r,t[2]=e[2]/r,t},r.dot=function(e,r){return e[0]*r[0]+e[1]*r[1]+e[2]*r[2]},r.lerp=function(e,r,t,o){return(o=o||new n(3))[0]=e[0]+t*(r[0]-e[0]),o[1]=e[1]+t*(r[1]-e[1]),o[2]=e[2]+t*(r[2]-e[2]),o},r.lerpV=function(e,r,t,o){return(o=o||new n(3))[0]=e[0]+t[0]*(r[0]-e[0]),o[1]=e[1]+t[1]*(r[1]-e[1]),o[2]=e[2]+t[2]*(r[2]-e[2]),o},r.length=function(e){return Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2])},r.lengthSq=function(e){return e[0]*e[0]+e[1]*e[1]+e[2]*e[2]},r.max=function(e,r,t){return(t=t||new n(3))[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t},r.min=function(e,r,t){return(t=t||new n(3))[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t},r.mulScalar=function(e,r,t){return(t=t||new n(3))[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t},r.multiply=function(e,r,t){return(t=t||new n(3))[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t},r.negate=function(e,r){return(r=r||new n(3))[0]=-e[0],r[1]=-e[1],r[2]=-e[2],r},r.normalize=function(e,r){r=r||new n(3);var t=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],o=Math.sqrt(t);o>1e-5?(r[0]=e[0]/o,r[1]=e[1]/o,r[2]=e[2]/o):(r[0]=0,r[1]=0,r[2]=0);return r},r.setDefaultType=function(e){var r=n;return n=e,r},r.subtract=function(e,r,t){return(t=t||new n(3))[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t};var n=Float32Array},function(e,r,t){"use strict";r.__esModule=!0,r.isWebGL1=function(e){return!e.texStorage2D},r.isWebGL2=function(e){return!!e.texStorage2D},r.glEnumToString=void 0;var n=function(){var e={},r={};return function(t,n){return function(t){var n=t.constructor.name;if(!e[n]){for(var o in t)if("number"==typeof t[o]){var i=r[t[o]];r[t[o]]=i?"".concat(i," | ").concat(o):o}e[n]=!0}}(t),r[n]||"0x"+n.toString(16)}}();r.glEnumToString=n},function(e,r,t){"use strict";r.__esModule=!0,r.createAttributeSetters=X,r.createProgram=R,r.createProgramFromScripts=function(e,r,t,n,o){for(var i=_(t,n,o),u=[],a=0;a=0?e.FRAGMENT_SHADER:r.indexOf("vert")>=0?e.VERTEX_SHADER:void 0}function P(e,r){r.forEach(function(r){e.deleteShader(r)})}function R(e,r,t,n,i){for(var u=_(t,n,i),a=[],c=[],l=0;l1&&"[0]"===n.name.substr(-3),a=n.type,f=c[a];if(!f)throw"unknown type: 0x"+a.toString(16);if(f.bindPoint){var l=t;t+=n.size,o=u?f.arraySetter(e,a,l,i,n.size):f.setter(e,a,l,i,n.size)}else o=f.arraySetter&&u?f.arraySetter(e,i):f.setter(e,i);return o.location=i,o}for(var o={},i=e.getProgramParameter(r,e.ACTIVE_UNIFORMS),u=0;u0)throw"numComponents "+i+" not correct for length "+o;return u}(r);return t},r.createBufferFromArray=x,r.createBufferFromTypedArray=f,r.createBufferInfoFromArrays=function(e,r,t){var o=d(e,r),i=Object.assign({},t||{});i.attribs=Object.assign({},t?t.attribs:{},o);var a=r.indices;if(a){var c=m(a,"indices");i.indices=f(e,c,e.ELEMENT_ARRAY_BUFFER),i.numElements=c.length,i.elementType=n.getGLTypeForTypedArray(c)}else i.numElements||(i.numElements=function(e,r){var t,n;for(n=0;n0)throw"Can not guess numComponents for attribute '"+e+"'. Tried "+t+" but "+r+" values is not evenly divisible by "+t+". You should specify it.";return t}function b(e,r){return e.numComponents||e.size||y(r,l(e).length)}function m(e,r){if(n.isArrayBuffer(e))return e;if(n.isArrayBuffer(e.data))return e.data;Array.isArray(e)&&(e={data:e});var t=e.type;return t||(t=c(r)?Uint16Array:Float32Array),new t(e.data)}function d(e,r){var t={};return Object.keys(r).forEach(function(o){if(!c(o)){var i=r[o],a=i.attrib||i.name||i.attribName||u.attribPrefix+o;if(i.value){if(!Array.isArray(i.value)&&!n.isArrayBuffer(i.value))throw new Error("array.value is not array or typedarray");t[a]={value:i.value}}else{var l,s,v,d;if(i.buffer&&i.buffer instanceof WebGLBuffer)l=i.buffer,d=i.numComponents||i.size,s=i.type,v=i.normalize;else if("number"==typeof i||"number"==typeof i.data){var p=i.data||i,x=i.type||Float32Array,F=p*x.BYTES_PER_ELEMENT;s=n.getGLTypeForTypedArrayType(x),v=void 0!==i.normalize?i.normalize:(w=x)===Int8Array||w===Uint8Array,d=i.numComponents||i.size||y(o,p),l=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,l),e.bufferData(e.ARRAY_BUFFER,F,i.drawType||e.STATIC_DRAW)}else{var h=m(i,o);l=f(e,h,void 0,i.drawType),s=n.getGLTypeForTypedArray(h),v=void 0!==i.normalize?i.normalize:function(e){return e instanceof Int8Array||e instanceof Uint8Array}(h),d=b(i,o)}t[a]={buffer:l,numComponents:d,type:s,normalize:v,stride:i.stride||0,offset:i.offset||0,divisor:void 0===i.divisor?void 0:i.divisor,drawType:i.drawType}}}var w}),e.bindBuffer(e.ARRAY_BUFFER,null),t}var p=["position","positions","a_position"];function x(e,r,t){var n="indices"===t?e.ELEMENT_ARRAY_BUFFER:e.ARRAY_BUFFER;return f(e,m(r,t),n)}},function(e,r,t){"use strict";r.__esModule=!0,r.setTextureDefaults_=function(e){i.copyExistingProperties(e,a),e.textureColor&&A(e.textureColor)},r.createSampler=g,r.createSamplers=function(e,r){var t={};return Object.keys(r).forEach(function(n){t[n]=g(e,r[n])}),t},r.setSamplerParameters=z,r.createTexture=Y,r.setEmptyTexture=V,r.setTextureFromArray=q,r.loadTextureFromUrl=W,r.setTextureFromElement=B,r.setTextureFilteringForSize=U,r.setTextureParameters=T,r.setDefaultTextureColor=A,r.createTextures=function(e,r,t){t=t||k;var n=0,o=[],i={},u={};function a(){0===n&&setTimeout(function(){t(o.length?o:void 0,i,u)},0)}return Object.keys(r).forEach(function(t){var f,c,l=r[t];("string"==typeof(c=l.src)||Array.isArray(c)&&"string"==typeof c[0])&&(f=function(e,r,i){u[t]=i,--n,e&&o.push(e),a()},++n),i[t]=Y(e,l,f)}),a(),i},r.resizeTexture=function(e,r,t,n,o){n=n||t.width,o=o||t.height;var i=t.target||e.TEXTURE_2D;e.bindTexture(i,r);var u,a=t.level||0,c=t.internalFormat||t.format||e.RGBA,l=p(c),s=t.format||l.format,v=t.src;u=v&&(f(v)||Array.isArray(v)&&"number"==typeof v[0])?t.type||w(e,v,l.type):t.type||l.type;if(i===e.TEXTURE_CUBE_MAP)for(var y=0;y<6;++y)e.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+y,a,c,n,o,0,s,u,null);else e.texImage2D(i,a,c,n,o,0,s,u,null)},r.canGenerateMipmap=F,r.canFilter=h,r.getNumComponentsForFormat=function(e){var r=v[e];if(!r)throw"unknown format: "+e;return r.u},r.getBytesPerElementForInternalFormat=d,r.getFormatAndTypeForInternalFormat=p;var n=u(t(3)),o=u(t(1)),i=u(t(0));function u(e){if(e&&e.__esModule)return e;var r={};if(null!=e)for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t)){var n=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,t):{};n.get||n.set?Object.defineProperty(r,t,n):r[t]=e[t]}return r.default=e,r}var a={textureColor:new Uint8Array([128,192,255,255]),textureOptions:{},crossOrigin:void 0},f=o.isArrayBuffer,c="undefined"!=typeof document&&document.createElement?document.createElement("canvas").getContext("2d"):null,l=6407,s=33319,v={},y=v;y[6406]={u:1},y[6409]={u:1},y[6410]={u:2},y[l]={u:3},y[6408]={u:4},y[6403]={u:1},y[36244]={u:1},y[s]={u:2},y[33320]={u:2},y[l]={u:3},y[36248]={u:3},y[6408]={u:4},y[36249]={u:4},y[6402]={u:1},y[34041]={u:2};var b={},m=b;function d(e,r){var t=b[e];if(!t)throw"unknown internal format";var n=t.bytesPerElementMap[r];if(void 0===n)throw"unknown internal format";return n}function p(e){var r=b[e];if(!r)throw"unknown internal format";return{format:r.v,type:r.type[0]}}function x(e){return 0==(e&e-1)}function F(e,r,t,o){if(!n.isWebGL2(e))return x(r)&&x(t);var i=b[o];if(!i)throw"unknown internal format";return i.F&&i.h}function h(e){var r=b[e];if(!r)throw"unknown internal format";return r.h}function w(e,r,t){return f(r)?o.getGLTypeForTypedArray(r):t||e.UNSIGNED_BYTE}function E(e,r,t,n,o){if(o%1!=0)throw"can't guess dimensions";if(t||n){if(n){if(!t&&(t=o/n)%1)throw"can't guess dimensions"}else if((n=o/t)%1)throw"can't guess dimensions"}else{var i=Math.sqrt(o/(r===e.TEXTURE_CUBE_MAP?6:1));i%1==0?(t=i,n=i):(t=o,n=1)}return{width:t,height:n}}function A(e){a.textureColor=new Uint8Array([255*e[0],255*e[1],255*e[2],255*e[3]])}m[6406]={v:6406,F:!0,h:!0,A:[1,2,2,4],type:[5121,5131,36193,5126]},m[6409]={v:6409,F:!0,h:!0,A:[1,2,2,4],type:[5121,5131,36193,5126]},m[6410]={v:6410,F:!0,h:!0,A:[2,4,4,8],type:[5121,5131,36193,5126]},m[l]={v:l,F:!0,h:!0,A:[3,6,6,12,2],type:[5121,5131,36193,5126,33635]},m[6408]={v:6408,F:!0,h:!0,A:[4,8,8,16,2,2],type:[5121,5131,36193,5126,32819,32820]},m[33321]={v:6403,F:!0,h:!0,A:[1],type:[5121]},m[36756]={v:6403,F:!1,h:!0,A:[1],type:[5120]},m[33325]={v:6403,F:!1,h:!0,A:[4,2],type:[5126,5131]},m[33326]={v:6403,F:!1,h:!1,A:[4],type:[5126]},m[33330]={v:36244,F:!0,h:!1,A:[1],type:[5121]},m[33329]={v:36244,F:!0,h:!1,A:[1],type:[5120]},m[33332]={v:36244,F:!0,h:!1,A:[2],type:[5123]},m[33331]={v:36244,F:!0,h:!1,A:[2],type:[5122]},m[33334]={v:36244,F:!0,h:!1,A:[4],type:[5125]},m[33333]={v:36244,F:!0,h:!1,A:[4],type:[5124]},m[33323]={v:s,F:!0,h:!0,A:[2],type:[5121]},m[36757]={v:s,F:!1,h:!0,A:[2],type:[5120]},m[33327]={v:s,F:!1,h:!0,A:[8,4],type:[5126,5131]},m[33328]={v:s,F:!1,h:!1,A:[8],type:[5126]},m[33336]={v:33320,F:!0,h:!1,A:[2],type:[5121]},m[33335]={v:33320,F:!0,h:!1,A:[2],type:[5120]},m[33338]={v:33320,F:!0,h:!1,A:[4],type:[5123]},m[33337]={v:33320,F:!0,h:!1,A:[4],type:[5122]},m[33340]={v:33320,F:!0,h:!1,A:[8],type:[5125]},m[33339]={v:33320,F:!0,h:!1,A:[8],type:[5124]},m[32849]={v:l,F:!0,h:!0,A:[3],type:[5121]},m[35905]={v:l,F:!1,h:!0,A:[3],type:[5121]},m[36194]={v:l,F:!0,h:!0,A:[3,2],type:[5121,33635]},m[36758]={v:l,F:!1,h:!0,A:[3],type:[5120]},m[35898]={v:l,F:!1,h:!0,A:[12,6,4],type:[5126,5131,35899]},m[35901]={v:l,F:!1,h:!0,A:[12,6,4],type:[5126,5131,35902]},m[34843]={v:l,F:!1,h:!0,A:[12,6],type:[5126,5131]},m[34837]={v:l,F:!1,h:!1,A:[12],type:[5126]},m[36221]={v:36248,F:!1,h:!1,A:[3],type:[5121]},m[36239]={v:36248,F:!1,h:!1,A:[3],type:[5120]},m[36215]={v:36248,F:!1,h:!1,A:[6],type:[5123]},m[36233]={v:36248,F:!1,h:!1,A:[6],type:[5122]},m[36209]={v:36248,F:!1,h:!1,A:[12],type:[5125]},m[36227]={v:36248,F:!1,h:!1,A:[12],type:[5124]},m[32856]={v:6408,F:!0,h:!0,A:[4],type:[5121]},m[35907]={v:6408,F:!0,h:!0,A:[4],type:[5121]},m[36759]={v:6408,F:!1,h:!0,A:[4],type:[5120]},m[32855]={v:6408,F:!0,h:!0,A:[4,2,4],type:[5121,32820,33640]},m[32854]={v:6408,F:!0,h:!0,A:[4,2],type:[5121,32819]},m[32857]={v:6408,F:!0,h:!0,A:[4],type:[33640]},m[34842]={v:6408,F:!1,h:!0,A:[16,8],type:[5126,5131]},m[34836]={v:6408,F:!1,h:!1,A:[16],type:[5126]},m[36220]={v:36249,F:!0,h:!1,A:[4],type:[5121]},m[36238]={v:36249,F:!0,h:!1,A:[4],type:[5120]},m[36975]={v:36249,F:!0,h:!1,A:[4],type:[33640]},m[36214]={v:36249,F:!0,h:!1,A:[8],type:[5123]},m[36232]={v:36249,F:!0,h:!1,A:[8],type:[5122]},m[36226]={v:36249,F:!0,h:!1,A:[16],type:[5124]},m[36208]={v:36249,F:!0,h:!1,A:[16],type:[5125]},m[33189]={v:6402,F:!0,h:!1,A:[2,4],type:[5123,5125]},m[33190]={v:6402,F:!0,h:!1,A:[4],type:[5125]},m[36012]={v:6402,F:!0,h:!1,A:[4],type:[5126]},m[35056]={v:34041,F:!0,h:!1,A:[4],type:[34042]},m[36013]={v:34041,F:!0,h:!1,A:[4],type:[36269]},Object.keys(m).forEach(function(e){var r=m[e];r.bytesPerElementMap={},r.A.forEach(function(e,t){var n=r.type[t];r.bytesPerElementMap[n]=e})});var O={};function _(e,r){void 0!==r.colorspaceConversion&&(O.colorspaceConversion=e.getParameter(e.UNPACK_COLORSPACE_CONVERSION_WEBGL),e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL,r.colorspaceConversion)),void 0!==r.premultiplyAlpha&&(O.premultiplyAlpha=e.getParameter(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,r.premultiplyAlpha)),void 0!==r.flipY&&(O.flipY=e.getParameter(e.UNPACK_FLIP_Y_WEBGL),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,r.flipY))}function j(e,r){void 0!==r.colorspaceConversion&&e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL,O.colorspaceConversion),void 0!==r.premultiplyAlpha&&e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,O.premultiplyAlpha),void 0!==r.flipY&&e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,O.flipY)}function M(e){O.unpackAlignment=e.getParameter(e.UNPACK_ALIGNMENT),n.isWebGL2(e)&&(O.unpackRowLength=e.getParameter(e.UNPACK_ROW_LENGTH),O.unpackImageHeight=e.getParameter(e.UNPACK_IMAGE_HEIGHT),O.unpackSkipPixels=e.getParameter(e.UNPACK_SKIP_PIXELS),O.unpackSkipRows=e.getParameter(e.UNPACK_SKIP_ROWS),O.unpackSkipImages=e.getParameter(e.UNPACK_SKIP_IMAGES))}function P(e){e.pixelStorei(e.UNPACK_ALIGNMENT,O.unpackAlignment),n.isWebGL2(e)&&(e.pixelStorei(e.UNPACK_ROW_LENGTH,O.unpackRowLength),e.pixelStorei(e.UNPACK_IMAGE_HEIGHT,O.unpackImageHeight),e.pixelStorei(e.UNPACK_SKIP_PIXELS,O.unpackSkipPixels),e.pixelStorei(e.UNPACK_SKIP_ROWS,O.unpackSkipRows),e.pixelStorei(e.UNPACK_SKIP_IMAGES,O.unpackSkipImages))}function R(e,r,t,n){n.minMag&&(t.call(e,r,e.TEXTURE_MIN_FILTER,n.minMag),t.call(e,r,e.TEXTURE_MAG_FILTER,n.minMag)),n.min&&t.call(e,r,e.TEXTURE_MIN_FILTER,n.min),n.mag&&t.call(e,r,e.TEXTURE_MAG_FILTER,n.mag),n.wrap&&(t.call(e,r,e.TEXTURE_WRAP_S,n.wrap),t.call(e,r,e.TEXTURE_WRAP_T,n.wrap),(r===e.TEXTURE_3D||i.isSampler(e,r))&&t.call(e,r,e.TEXTURE_WRAP_R,n.wrap)),n.wrapR&&t.call(e,r,e.TEXTURE_WRAP_R,n.wrapR),n.wrapS&&t.call(e,r,e.TEXTURE_WRAP_S,n.wrapS),n.wrapT&&t.call(e,r,e.TEXTURE_WRAP_T,n.wrapT),n.minLod&&t.call(e,r,e.TEXTURE_MIN_LOD,n.minLod),n.maxLod&&t.call(e,r,e.TEXTURE_MAX_LOD,n.maxLod),n.baseLevel&&t.call(e,r,e.TEXTURE_BASE_LEVEL,n.baseLevel),n.maxLevel&&t.call(e,r,e.TEXTURE_MAX_LEVEL,n.maxLevel)}function T(e,r,t){var n=t.target||e.TEXTURE_2D;e.bindTexture(n,r),R(e,n,e.texParameteri,t)}function z(e,r,t){R(e,r,e.samplerParameteri,t)}function g(e,r){var t=e.createSampler();return z(e,t,r),t}function U(e,r,t,n,o,i,u){t=t||a.textureOptions,i=i||e.RGBA,u=u||e.UNSIGNED_BYTE;var f=t.target||e.TEXTURE_2D;if(n=n||t.width,o=o||t.height,e.bindTexture(f,r),F(e,n,o,i))e.generateMipmap(f);else{var c=h(i)?e.LINEAR:e.NEAREST;e.texParameteri(f,e.TEXTURE_MIN_FILTER,c),e.texParameteri(f,e.TEXTURE_MAG_FILTER,c),e.texParameteri(f,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(f,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE)}}function C(e){return!0===e.auto||void 0===e.auto&&void 0===e.level}function S(e,r){return(r=r||{}).cubeFaceOrder||[e.TEXTURE_CUBE_MAP_POSITIVE_X,e.TEXTURE_CUBE_MAP_NEGATIVE_X,e.TEXTURE_CUBE_MAP_POSITIVE_Y,e.TEXTURE_CUBE_MAP_NEGATIVE_Y,e.TEXTURE_CUBE_MAP_POSITIVE_Z,e.TEXTURE_CUBE_MAP_NEGATIVE_Z]}function I(e,r){var t=S(e,r).map(function(e,r){return{face:e,ndx:r}});return t.sort(function(e,r){return e.face-r.face}),t}function B(e,r,t,n){var o=(n=n||a.textureOptions).target||e.TEXTURE_2D,i=n.level||0,u=t.width,f=t.height,l=n.internalFormat||n.format||e.RGBA,s=p(l),v=n.format||s.format,y=n.type||s.type;if(_(e,n),e.bindTexture(o,r),o===e.TEXTURE_CUBE_MAP){var b,m,d=t.width,x=t.height;if(d/6===x)b=x,m=[0,0,1,0,2,0,3,0,4,0,5,0];else if(x/6===d)b=d,m=[0,0,0,1,0,2,0,3,0,4,0,5];else if(d/3==x/2)b=d/3,m=[0,0,1,0,2,0,0,1,1,1,2,1];else{if(d/2!=x/3)throw"can't figure out cube map from element: "+(t.src?t.src:t.nodeName);b=d/2,m=[0,0,1,0,0,1,1,1,0,2,1,2]}c?(c.canvas.width=b,c.canvas.height=b,u=b,f=b,I(e,n).forEach(function(r){var n=m[2*r.ndx+0]*b,o=m[2*r.ndx+1]*b;c.drawImage(t,n,o,b,b,0,0,b,b),e.texImage2D(r.face,i,l,v,y,c.canvas)}),c.canvas.width=1,c.canvas.height=1):"undefined"!=typeof createImageBitmap&&(u=b,f=b,I(e,n).forEach(function(a){var c=m[2*a.ndx+0]*b,s=m[2*a.ndx+1]*b;e.texImage2D(a.face,i,l,b,b,0,v,y,null),createImageBitmap(t,c,s,b,b,{premultiplyAlpha:"none",colorSpaceConversion:"none"}).then(function(t){_(e,n),e.bindTexture(o,r),e.texImage2D(a.face,i,l,v,y,t),j(e,n),C(n)&&U(e,r,n,u,f,l,y)})}))}else if(o===e.TEXTURE_3D||o===e.TEXTURE_2D_ARRAY){var F=Math.min(t.width,t.height),h=Math.max(t.width,t.height),w=h/F;if(w%1!=0)throw"can not compute 3D dimensions of element";var E=t.width===h?1:0,A=t.height===h?1:0;M(e),e.pixelStorei(e.UNPACK_ALIGNMENT,1),e.pixelStorei(e.UNPACK_ROW_LENGTH,t.width),e.pixelStorei(e.UNPACK_IMAGE_HEIGHT,0),e.pixelStorei(e.UNPACK_SKIP_IMAGES,0),e.texImage3D(o,i,l,F,F,F,0,v,y,null);for(var O=0;O=0?x(n,r):t.indexOf("tan")>=0||t.indexOf("binorm")>=0?d(n,r):t.indexOf("norm")>=0&&p(n,r)}),e}function h(e,r,t){return e=e||2,{position:{numComponents:2,data:[(r=r||0)+-1*(e*=.5),(t=t||0)+-1*e,r+1*e,t+-1*e,r+-1*e,t+1*e,r+1*e,t+1*e]},normal:[0,0,1,0,0,1,0,0,1,0,0,1],texcoord:[0,0,1,0,0,1,1,1],indices:[0,1,2,2,1,3]}}function w(e,r,t,n,o){e=e||1,r=r||1,t=t||1,n=n||1,o=o||u.identity();for(var i=(t+1)*(n+1),a=v(3,i),f=v(3,i),c=v(2,i),l=0;l<=n;l++)for(var s=0;s<=t;s++){var y=s/t,b=l/n;a.push(e*y-.5*e,0,r*b-.5*r),f.push(0,1,0),c.push(y,b)}for(var m=t+1,d=v(3,t*n*2,Uint16Array),p=0;p 0");n=n||0,i=i||0;for(var a=(o=o||Math.PI)-n,f=(u=u||2*Math.PI)-i,c=(r+1)*(t+1),l=v(3,c),s=v(3,c),y=v(2,c),b=0;b<=t;b++)for(var m=0;m<=r;m++){var d=m/r,p=b/t,x=f*d+i,F=a*p+n,h=Math.sin(x),w=Math.cos(x),E=Math.sin(F),A=w*E,O=Math.cos(F),_=h*E;l.push(e*A,e*O,e*_),s.push(A,O,_),y.push(1-d,p)}for(var j=r+1,M=v(3,r*t*2,Uint16Array),P=0;Po?(A=t,E=1,O=r):O=e+w/o*(r-e),-2!==w&&w!==o+2||(O=0,E=0),A-=t/2;for(var _=0;_o?0:j*x,w<0?-1:w>o?1:F,w<0||w>o?0:M*x),b.push(_/n,1-E)}}for(var P=0;P 0");var f=2,c=(u=u||1)-(i=i||0),l=2*(o+1)*(2+f),s=v(3,l),y=v(3,l),b=v(2,l);function m(e,r,t){return e+(r-e)*t}function d(r,t,u,l,v,d){for(var p=0;p<=o;p++){var x=t/(f-1),F=p/o,h=2*(x-.5),w=(i+F*c)*Math.PI,E=Math.sin(w),A=Math.cos(w),O=m(e,r,E),_=h*n,j=A*e,M=E*O;s.push(_,j,M);var P=a.add(a.multiply([0,E,A],u),l);y.push(P),b.push(x*v+d,F)}}for(var p=0;p0&&d!==r){var h=l+(d+1),w=l+d,E=l+d-y,A=l+(d+1)-y;c.push(h,w,E),c.push(h,E,A)}}l+=r+1}return{position:u,normal:a,texcoord:f,indices:c}}function g(e){return function(r){var t=e.apply(this,Array.prototype.slice.call(arguments,1));return n.createBuffersFromArrays(r,t)}}function U(e){return function(r){var t=e.apply(null,Array.prototype.slice.call(arguments,1));return n.createBufferInfoFromArrays(r,t)}}var C=["numComponents","size","type","normalize","stride","offset","attrib","name","attribName"];function S(e,r,t,n){n=n||0;for(var o=e.length,i=0;i 30 | 31 | 32 | ``` 33 | 34 | Then you can create a `WebGLCanvas2DRenderingContext` with 35 | 36 | ``` 37 | const ctx = new WebGLCanvas2DRenderingContext(someCanvas); 38 | ``` 39 | 40 | Note that it does not handle canvas resize automatically. To resize you have 2 options. 41 | 42 | (a) call `canvas.resize` 43 | 44 | Example 45 | 46 | ``` 47 | if (canvas.resize) { 48 | canvas.resize(newWidth, newHeight); 49 | } else { 50 | canvas.width = newWidth; 51 | canvas.height = newHeight; 52 | ``` 53 | 54 | (b) call `ctx.updateSize` 55 | 56 | ``` 57 | canvas.width = newWidth; 58 | canvas.height = newHeight; 59 | ctx.updateSize(); 60 | ``` 61 | 62 | You can also use the shim which makes it create `WebGLCanvas2DRenderingContext`s automatically. 63 | Of course that means you can't create a normal 2D context, only this limited context. 64 | 65 | ``` 66 | 67 | const ctx = someCanvas.getContext('2d'); // this is a WebGLCanvas2DRenderingContext 68 | ``` 69 | 70 | 71 | -------------------------------------------------------------------------------- /test/f-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-canvas-2d/d368beea79aa22e78c117839a362f85f126ab518/test/f-texture.png -------------------------------------------------------------------------------- /test/test-2d.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/test-rect-alpha-2d.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/test-rect-alpha-2d.js: -------------------------------------------------------------------------------- 1 | const canvas = document.querySelector('canvas'); 2 | const ctx = canvas.getContext('2d'); 3 | 4 | ctx.globalAlpha = 0.5; 5 | 6 | ctx.fillStyle = 'blue'; 7 | ctx.fillRect(10, 10, 100, 100); 8 | 9 | ctx.fillStyle = 'red'; 10 | ctx.fillRect(50, 50, 100, 100); 11 | -------------------------------------------------------------------------------- /test/test-rect-alpha-webgl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/test-webgl.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | function loadImage(url) { 2 | return new Promise((resolve, reject) => { 3 | const img = new Image(); 4 | img.onload = () => { 5 | resolve(img); 6 | }; 7 | img.onerror = reject; 8 | img.src = url; 9 | }); 10 | } 11 | 12 | function resizeCanvasToDisplaySize(canvas) { 13 | const width = canvas.clientWidth; 14 | const height = canvas.clientHeight; 15 | if (width === canvas.width && height === canvas.height) { 16 | return false; 17 | } 18 | 19 | if (canvas.resize) { 20 | canvas.resize(width, height); 21 | } else { 22 | canvas.width = width; 23 | canvas.height = height; 24 | } 25 | return true; 26 | } 27 | 28 | 29 | async function main() { 30 | const img = await loadImage('f-texture.png'); 31 | const ctx = document.querySelector('canvas').getContext('2d'); 32 | 33 | function render(time) { 34 | time *= 0.001; 35 | 36 | resizeCanvasToDisplaySize(ctx.canvas); 37 | 38 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 39 | 40 | ctx.save(); 41 | ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2); 42 | ctx.rotate(time); 43 | 44 | ctx.save(); 45 | { 46 | ctx.translate(img.width / -2, img.height / -2); 47 | 48 | ctx.drawImage(img, 0, 0); 49 | } 50 | ctx.restore(); 51 | 52 | ctx.save(); 53 | { 54 | // We're at the center of the center image so go to the top/left corner 55 | ctx.translate(img.width / -2, img.height / -2); 56 | ctx.rotate(Math.sin(time * 2.2)); 57 | ctx.scale(0.2, 0.2); 58 | // Now we want the bottom/right corner of the image we're about to draw 59 | ctx.translate(-img.width, -img.height); 60 | 61 | ctx.drawImage(img, 0, 0); 62 | } 63 | ctx.restore(); 64 | 65 | ctx.save(); 66 | { 67 | // We're at the center of the center image so go to the top/right corner 68 | ctx.translate(img.width / 2, img.height / -2); 69 | ctx.rotate(Math.sin(time * 2.3)); 70 | ctx.scale(0.2, 0.2); 71 | // Now we want the bottom/right corner of the image we're about to draw 72 | ctx.translate(0, -img.height); 73 | 74 | ctx.drawImage(img, 0, 0); 75 | } 76 | ctx.restore(); 77 | 78 | ctx.save(); 79 | { 80 | // We're at the center of the center image so go to the bottom/left corner 81 | ctx.translate(img.width / -2, img.height / 2); 82 | ctx.rotate(Math.sin(time * 2.4)); 83 | ctx.scale(0.2, 0.2); 84 | // Now we want the top/right corner of the image we're about to draw 85 | ctx.translate(-img.width, 0); 86 | 87 | ctx.drawImage(img, 0, 0); 88 | } 89 | ctx.restore(); 90 | 91 | ctx.save(); 92 | { 93 | // We're at the center of the center image so go to the bottom/right corner 94 | ctx.translate(img.width / 2, img.height / 2); 95 | ctx.rotate(Math.sin(time * 2.5)); 96 | ctx.scale(0.2, 0.2); 97 | // Now we want the top/left corner of the image we're about to draw 98 | ctx.translate(0, 0); // 0,0 means this line is not really doing anything 99 | 100 | ctx.drawImage(img, 0, 0); 101 | } 102 | ctx.restore(); 103 | 104 | 105 | ctx.restore(); 106 | 107 | requestAnimationFrame(render); 108 | } 109 | requestAnimationFrame(render); 110 | } 111 | main(); 112 | 113 | -------------------------------------------------------------------------------- /webgl-canvas-2d-shim.js: -------------------------------------------------------------------------------- 1 | HTMLCanvasElement.prototype.getContext = function(origFn) { 2 | 3 | return function(type, attribs) { 4 | if (type === '2d') { 5 | return new WebGLCanvas2DRenderingContext(this); 6 | } 7 | return origFn.call(this, type, attribs); 8 | }; 9 | 10 | }(HTMLCanvasElement.prototype.getContext); 11 | -------------------------------------------------------------------------------- /webgl-canvas-2d.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2019 Gregg Tavares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | 23 | */ 24 | (function() { 25 | class MatrixStack { 26 | constructor() { 27 | this.reset(); 28 | } 29 | reset() { 30 | this.stack = []; 31 | 32 | // since the stack is empty this will put an initial matrix in it 33 | this.restore(); 34 | } 35 | // Pops the top of the stack restoring the previously saved matrix 36 | restore() { 37 | this.stack.pop(); 38 | // Never let the stack be totally empty 39 | if (this.stack.length < 1) { 40 | this.stack[0] = m3.identity(); 41 | } 42 | } 43 | 44 | // Pushes a copy of the current matrix on the stack 45 | save() { 46 | this.stack.push(this.getCurrentMatrix()); 47 | } 48 | 49 | // Gets a copy of the current matrix (top of the stack) 50 | getCurrentMatrix() { 51 | return this.stack[this.stack.length - 1].slice(); 52 | } 53 | 54 | // Lets us set the current matrix 55 | setCurrentMatrix(m) { 56 | this.stack[this.stack.length - 1] = m; 57 | return m; 58 | } 59 | 60 | // Translates the current matrix 61 | translate(x, y) { 62 | const m = this.getCurrentMatrix(); 63 | this.setCurrentMatrix(m3.translate(m, x, y)); 64 | } 65 | 66 | // Rotates the current matrix around Z 67 | rotate(angleInRadians) { 68 | const m = this.getCurrentMatrix(); 69 | this.setCurrentMatrix(m3.rotate(m, -angleInRadians)); 70 | } 71 | 72 | // Scales the current matrix 73 | scale(x, y) { 74 | const m = this.getCurrentMatrix(); 75 | this.setCurrentMatrix(m3.scale(m, x, y)); 76 | } 77 | } 78 | 79 | const colorNames = { 80 | 'aliceblue': 0xF0F8FF, 81 | 'antiquewhite': 0xFAEBD7, 82 | 'aqua': 0x00FFFF, 83 | 'aquamarine': 0x7FFFD4, 84 | 'azure': 0xF0FFFF, 85 | 'beige': 0xF5F5DC, 86 | 'bisque': 0xFFE4C4, 87 | 'black': 0x000000, 88 | 'blanchedalmond': 0xFFEBCD, 89 | 'blue': 0x0000FF, 90 | 'blueviolet': 0x8A2BE2, 91 | 'brown': 0xA52A2A, 92 | 'burlywood': 0xDEB887, 93 | 'cadetblue': 0x5F9EA0, 94 | 'chartreuse': 0x7FFF00, 95 | 'chocolate': 0xD2691E, 96 | 'coral': 0xFF7F50, 97 | 'cornflowerblue': 0x6495ED, 98 | 'cornsilk': 0xFFF8DC, 99 | 'crimson': 0xDC143C, 100 | 'cyan': 0x00FFFF, 101 | 'darkblue': 0x00008B, 102 | 'darkcyan': 0x008B8B, 103 | 'darkgoldenrod': 0xB8860B, 104 | 'darkgray': 0xA9A9A9, 105 | 'darkgreen': 0x006400, 106 | 'darkgrey': 0xA9A9A9, 107 | 'darkkhaki': 0xBDB76B, 108 | 'darkmagenta': 0x8B008B, 109 | 'darkolivegreen': 0x556B2F, 110 | 'darkorange': 0xFF8C00, 111 | 'darkorchid': 0x9932CC, 112 | 'darkred': 0x8B0000, 113 | 'darksalmon': 0xE9967A, 114 | 'darkseagreen': 0x8FBC8F, 115 | 'darkslateblue': 0x483D8B, 116 | 'darkslategray': 0x2F4F4F, 117 | 'darkslategrey': 0x2F4F4F, 118 | 'darkturquoise': 0x00CED1, 119 | 'darkviolet': 0x9400D3, 120 | 'deeppink': 0xFF1493, 121 | 'deepskyblue': 0x00BFFF, 122 | 'dimgray': 0x696969, 123 | 'dimgrey': 0x696969, 124 | 'dodgerblue': 0x1E90FF, 125 | 'firebrick': 0xB22222, 126 | 'floralwhite': 0xFFFAF0, 127 | 'forestgreen': 0x228B22, 128 | 'fuchsia': 0xFF00FF, 129 | 'gainsboro': 0xDCDCDC, 130 | 'ghostwhite': 0xF8F8FF, 131 | 'gold': 0xFFD700, 132 | 'goldenrod': 0xDAA520, 133 | 'gray': 0x808080, 134 | 'green': 0x008000, 135 | 'greenyellow': 0xADFF2F, 136 | 'grey': 0x808080, 137 | 'honeydew': 0xF0FFF0, 138 | 'hotpink': 0xFF69B4, 139 | 'indianred': 0xCD5C5C, 140 | 'indigo': 0x4B0082, 141 | 'ivory': 0xFFFFF0, 142 | 'khaki': 0xF0E68C, 143 | 'lavender': 0xE6E6FA, 144 | 'lavenderblush': 0xFFF0F5, 145 | 'lawngreen': 0x7CFC00, 146 | 'lemonchiffon': 0xFFFACD, 147 | 'lightblue': 0xADD8E6, 148 | 'lightcoral': 0xF08080, 149 | 'lightcyan': 0xE0FFFF, 150 | 'lightgoldenrodyellow': 0xFAFAD2, 151 | 'lightgray': 0xD3D3D3, 152 | 'lightgreen': 0x90EE90, 153 | 'lightgrey': 0xD3D3D3, 154 | 'lightpink': 0xFFB6C1, 155 | 'lightsalmon': 0xFFA07A, 156 | 'lightseagreen': 0x20B2AA, 157 | 'lightskyblue': 0x87CEFA, 158 | 'lightslategray': 0x778899, 159 | 'lightslategrey': 0x778899, 160 | 'lightsteelblue': 0xB0C4DE, 161 | 'lightyellow': 0xFFFFE0, 162 | 'lime': 0x00FF00, 163 | 'limegreen': 0x32CD32, 164 | 'linen': 0xFAF0E6, 165 | 'magenta': 0xFF00FF, 166 | 'maroon': 0x800000, 167 | 'mediumaquamarine': 0x66CDAA, 168 | 'mediumblue': 0x0000CD, 169 | 'mediumorchid': 0xBA55D3, 170 | 'mediumpurple': 0x9370DB, 171 | 'mediumseagreen': 0x3CB371, 172 | 'mediumslateblue': 0x7B68EE, 173 | 'mediumspringgreen': 0x00FA9A, 174 | 'mediumturquoise': 0x48D1CC, 175 | 'mediumvioletred': 0xC71585, 176 | 'midnightblue': 0x191970, 177 | 'mintcream': 0xF5FFFA, 178 | 'mistyrose': 0xFFE4E1, 179 | 'moccasin': 0xFFE4B5, 180 | 'navajowhite': 0xFFDEAD, 181 | 'navy': 0x000080, 182 | 'oldlace': 0xFDF5E6, 183 | 'olive': 0x808000, 184 | 'olivedrab': 0x6B8E23, 185 | 'orange': 0xFFA500, 186 | 'orangered': 0xFF4500, 187 | 'orchid': 0xDA70D6, 188 | 'palegoldenrod': 0xEEE8AA, 189 | 'palegreen': 0x98FB98, 190 | 'paleturquoise': 0xAFEEEE, 191 | 'palevioletred': 0xDB7093, 192 | 'papayawhip': 0xFFEFD5, 193 | 'peachpuff': 0xFFDAB9, 194 | 'peru': 0xCD853F, 195 | 'pink': 0xFFC0CB, 196 | 'plum': 0xDDA0DD, 197 | 'powderblue': 0xB0E0E6, 198 | 'purple': 0x800080, 199 | 'rebeccapurple': 0x663399, 200 | 'red': 0xFF0000, 201 | 'rosybrown': 0xBC8F8F, 202 | 'royalblue': 0x4169E1, 203 | 'saddlebrown': 0x8B4513, 204 | 'salmon': 0xFA8072, 205 | 'sandybrown': 0xF4A460, 206 | 'seagreen': 0x2E8B57, 207 | 'seashell': 0xFFF5EE, 208 | 'sienna': 0xA0522D, 209 | 'silver': 0xC0C0C0, 210 | 'skyblue': 0x87CEEB, 211 | 'slateblue': 0x6A5ACD, 212 | 'slategray': 0x708090, 213 | 'slategrey': 0x708090, 214 | 'snow': 0xFFFAFA, 215 | 'springgreen': 0x00FF7F, 216 | 'steelblue': 0x4682B4, 217 | 'tan': 0xD2B48C, 218 | 'teal': 0x008080, 219 | 'thistle': 0xD8BFD8, 220 | 'tomato': 0xFF6347, 221 | 'turquoise': 0x40E0D0, 222 | 'violet': 0xEE82EE, 223 | 'wheat': 0xF5DEB3, 224 | 'white': 0xFFFFFF, 225 | 'whitesmoke': 0xF5F5F5, 226 | 'yellow': 0xFFFF00, 227 | 'yellowgreen': 0x9ACD32, 228 | }; 229 | 230 | function fromInt(hex) { 231 | return [ 232 | ((hex >> 16) & 255) / 255, 233 | ((hex >> 8 ) & 255) / 255, 234 | ((hex ) & 255) / 255, 235 | ]; 236 | } 237 | 238 | function hue2rgb(p, q, t) { 239 | if (t < 1 / 6) return p + (q - p) * 6 * t; 240 | if (t < 1 / 2) return q; 241 | if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); 242 | return p; 243 | } 244 | 245 | function euclideanModulo(n, m) { 246 | return ((n % m) + m) % m; 247 | } 248 | 249 | function fromHSL(h, s, l) { 250 | h = euclideanModulo(h, 1); 251 | s = clamp(s, 0, 1); 252 | l = clamp(l, 0, 1); 253 | 254 | if (s === 0) { 255 | return [l, l, l]; 256 | } else { 257 | const p = l <= 0.5 ? l * (1 + s) : l + s - (l * s); 258 | const q = (2 * l) - p; 259 | 260 | return [ 261 | hue2rgb(q, p, h + 1 / 3), 262 | hue2rgb(q, p, h), 263 | hue2rgb(q, p, h - 1 / 3), 264 | ]; 265 | } 266 | } 267 | 268 | function from255To1(s) { 269 | return Math.min(255, parseInt(s, 10)) / 255; 270 | } 271 | 272 | function from100To1(s) { 273 | return Math.min(100, parseInt(s, 10)) / 100; 274 | } 275 | 276 | function singleHexTo1(hex, n) { 277 | return parseInt(hex.charAt(n) + hex.charAt(n), 16) / 255; 278 | } 279 | 280 | function doubleHexTo1(hex, n) { 281 | return parseInt(hex.charAt(n) + hex.charAt(n + 1), 16) / 255; 282 | } 283 | 284 | function parseAlpha(string) { 285 | if (string === undefined) return 1; 286 | return parseFloat(string); 287 | } 288 | 289 | const rgbHslRE = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/; 290 | const rgbRE = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/; 291 | const rgbPercentRE = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/; 292 | const hslRE = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/; 293 | 294 | function cssToColor(style) { 295 | 296 | const rgbHsl = rgbHslRE.exec(style); 297 | 298 | if (rgbHsl) { 299 | // rgb / hsl 300 | const name = rgbHsl[1]; 301 | const components = rgbHsl[2]; 302 | 303 | switch (name) { 304 | case 'rgb': 305 | case 'rgba': 306 | { 307 | const color = rgbRE.exec(components); 308 | if (color) { 309 | // rgb(255,0,0) rgba(255,0,0,0.5) 310 | return [ 311 | from255To1(color[1]), 312 | from255To1(color[2]), 313 | from255To1(color[3]), 314 | parseAlpha(color[5]), 315 | ]; 316 | } 317 | } 318 | { 319 | const color = rgbPercentRE.exec(components); 320 | if (color) { 321 | // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) 322 | return [ 323 | from100To1(color[1]), 324 | from100To1(color[2]), 325 | from100To1(color[3]), 326 | parseAlpha(color[5]), 327 | ]; 328 | } 329 | } 330 | break; 331 | case 'hsl': 332 | case 'hsla': 333 | { 334 | const color = hslRE.exec(components); 335 | if (color) { 336 | // hsl(120,50%,50%) hsla(120,50%,50%,0.5) 337 | const h = parseFloat(color[1]) / 360; 338 | const s = parseInt(color[2], 10) / 100; 339 | const l = parseInt(color[3], 10) / 100; 340 | 341 | return [...fromHSL(h, s, l), parseAlpha(color[5])]; 342 | } 343 | } 344 | break; 345 | } 346 | } else { 347 | const hexMatch = /^\#([A-Fa-f0-9]+)$/.exec(style); 348 | if (hexMatch) { 349 | // hex color 350 | const hex = hexMatch[1]; 351 | const size = hex.length; 352 | 353 | if (size === 3) { 354 | // #ff0 355 | return [ 356 | singleHexTo1(hex, 0), 357 | singleHexTo1(hex, 1), 358 | singleHexTo1(hex, 2), 359 | 1, 360 | ]; 361 | } else if (size === 6) { 362 | // #ff0000 363 | return [ 364 | doubleHexTo1(hex, 0), 365 | doubleHexTo1(hex, 2), 366 | doubleHexTo1(hex, 4), 367 | 1, 368 | ]; 369 | } 370 | } 371 | } 372 | 373 | if (style && style.length > 0) { 374 | // color keywords 375 | const hex = colorNames[style]; 376 | if (hex !== undefined) { 377 | return [...fromInt(hex), 1]; 378 | } 379 | } 380 | 381 | // unknown color 382 | console.warn('unknown color:', style); 383 | return [1, 0, 1, 1]; 384 | } 385 | 386 | class WebGLCanvas2DRenderingContext { 387 | constructor(canvas) { 388 | this.canvas = canvas; 389 | canvas.resize = this.resize.bind(this); 390 | 391 | const gl = canvas.getContext('webgl', {preserveDrawingBuffer: true}); 392 | this.gl = gl; 393 | 394 | gl.enable(gl.BLEND); 395 | gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); 396 | 397 | const vs = ` 398 | attribute vec2 position; 399 | 400 | uniform mat3 u_matrix; 401 | uniform mat3 u_textureMatrix; 402 | 403 | varying vec2 v_texcoord; 404 | 405 | void main() { 406 | gl_Position = vec4((u_matrix * vec3(position, 1)).xy, 0, 1); 407 | 408 | // using position for texcoord since we know position is a unit square 409 | v_texcoord = (u_textureMatrix * vec3(position, 1)).xy; 410 | } 411 | `; 412 | const fs = ` 413 | precision mediump float; 414 | 415 | varying vec2 v_texcoord; 416 | 417 | uniform sampler2D texture; 418 | uniform vec4 color; 419 | 420 | void main() { 421 | gl_FragColor = texture2D(texture, v_texcoord) * color; 422 | gl_FragColor.rgb *= gl_FragColor.a; 423 | } 424 | `; 425 | this.programInfo = twgl.createProgramInfo(gl, [vs, fs]); 426 | this.bufferInfo = twgl.createBufferInfoFromArrays(gl, { 427 | position: { 428 | numComponents: 2, 429 | data: [ 430 | 0, 0, 431 | 0, 1, 432 | 1, 0, 433 | 1, 0, 434 | 0, 1, 435 | 1, 1, 436 | ], 437 | }, 438 | }); 439 | 440 | twgl.setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo); 441 | 442 | this.matrixStack = new MatrixStack(); 443 | this.translate = this.matrixStack.translate.bind(this.matrixStack); 444 | this.rotate = this.matrixStack.rotate.bind(this.matrixStack); 445 | this.scale = this.matrixStack.scale.bind(this.matrixStack); 446 | this.texturesByImage = new Map(); 447 | this.whiteTexture = { 448 | width: 1, 449 | height: 1, 450 | } 451 | 452 | this.texturesByImage.set(this.whiteTexture, this.createColorTexture255([255, 255, 255, 255])); 453 | this.reset(); 454 | } 455 | reset() { 456 | const {gl} = this; 457 | this.matrixStack.reset(); 458 | this.state = { 459 | fillStyle: 'black', 460 | fillColor: [0, 0, 0, 1], 461 | globalAlpha: 1, 462 | }; 463 | this.stack = []; 464 | } 465 | save() { 466 | this.matrixStack.save(); 467 | const newState = Object.assign({}, this.state); 468 | this.stack.push(this.state) 469 | this.state = newState; 470 | } 471 | restore() { 472 | this.matrixStack.restore(); 473 | this.state = this.stack.pop(); 474 | } 475 | resize(width, height) { 476 | const {canvas, gl} = this; 477 | canvas.width = width; 478 | canvas.height = height; 479 | this.updateSize(); 480 | } 481 | updateSize() { 482 | const {gl} = this; 483 | gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 484 | } 485 | setTransform(m1, m2, m3, m4, m5, m6) { 486 | this.matrixStack.setCurrentMatrix([ 487 | m1, m2, 0, 488 | m3, m4, 0, 489 | m5, m6, 1, 490 | ]); 491 | } 492 | get fillStyle() { 493 | return this.state.fillStyle; 494 | } 495 | set fillStyle(v) { 496 | this.state.fillStyle = v; 497 | this.state.fillColor = cssToColor(v); 498 | } 499 | get globalAlpha() { 500 | return this.state.globalAlpha; 501 | } 502 | set globalAlpha(v) { 503 | this.state.globalAlpha = v; 504 | } 505 | clearRect(x, y, width, height) { 506 | const {gl} = this; 507 | gl.disable(gl.BLEND); 508 | this.fillRectImpl(x, y, width, height, [0, 0, 0, 0]); 509 | gl.enable(gl.BLEND); 510 | } 511 | fillRect(x, y, width, height) { 512 | this.fillRectImpl(x, y, width, height, this.state.fillColor); 513 | } 514 | fillRectImpl(x, y, width, height, color) { 515 | this.drawImageImpl(color, this.whiteTexture, x, y, width, height) 516 | } 517 | createTexture() { 518 | const {gl} = this; 519 | const tex = gl.createTexture(); 520 | gl.bindTexture(gl.TEXTURE_2D, tex); 521 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 522 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 523 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 524 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 525 | return tex; 526 | } 527 | createColorTexture255(color) { 528 | const {gl} = this; 529 | const tex = this.createTexture(); 530 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(color)); 531 | return tex; 532 | } 533 | drawImage(img, srcX, srcY, srcW, srcH, dstX, dstY, dstWidth, dstHeight) { 534 | this.drawImageImpl([1, 1, 1, 1], img, srcX, srcY, srcW, srcH, dstX, dstY, dstWidth, dstHeight); 535 | } 536 | drawImageImpl(color, img, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight) { 537 | const {gl, programInfo, bufferInfo, matrixStack} = this; 538 | 539 | let tex = this.texturesByImage.get(img); 540 | if (!tex) { 541 | tex = this.createTexture(); 542 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); 543 | this.texturesByImage.set(img, tex); 544 | } 545 | 546 | if (dstX === undefined) { 547 | dstX = srcX; 548 | } 549 | if (dstY === undefined) { 550 | dstY = srcY; 551 | } 552 | if (srcWidth === undefined) { 553 | srcWidth = img.width; 554 | } 555 | if (srcHeight === undefined) { 556 | srcHeight = img.height; 557 | } 558 | if (dstWidth === undefined) { 559 | dstWidth = srcWidth; 560 | } 561 | if (dstHeight === undefined) { 562 | dstHeight = srcHeight; 563 | } 564 | 565 | // Tell WebGL to use our shader program pair 566 | gl.useProgram(programInfo.program); 567 | 568 | // this matirx will convert from pixels to clip space 569 | let matrix = m3.projection(gl.canvas.width, gl.canvas.height); 570 | 571 | // this matrix moves the origin to the one represented by 572 | // the current matrix stack. 573 | matrix = m3.multiply(matrix, matrixStack.getCurrentMatrix()); 574 | 575 | // this matrix will translate our quad to dstX, dstY 576 | matrix = m3.translate(matrix, dstX, dstY); 577 | 578 | // this matrix will scale our 1 unit quad 579 | // from 1 unit to texWidth, texHeight units 580 | matrix = m3.scale(matrix, dstWidth, dstHeight); 581 | 582 | // Because texture coordinates go from 0 to 1 583 | // and because our texture coordinates are already a unit quad 584 | // we can select an area of the texture by scaling the unit quad 585 | // down 586 | let texMatrix = m3.translation(srcX / img.width, srcY / img.height); 587 | texMatrix = m3.scale(texMatrix, srcWidth / img.width, srcHeight / img.height); 588 | 589 | twgl.setUniforms(programInfo, { 590 | u_matrix: matrix, 591 | u_textureMatrix: texMatrix, 592 | color: [color[0], color[1], color[2], color[3] * this.state.globalAlpha], 593 | texture: tex, 594 | }); 595 | twgl.drawBufferInfo(gl, bufferInfo); 596 | } 597 | } 598 | 599 | window.WebGLCanvas2DRenderingContext = WebGLCanvas2DRenderingContext; 600 | 601 | }()); --------------------------------------------------------------------------------