├── .gitignore ├── COPYING.txt ├── Gruntfile.js ├── README.md ├── build ├── tc.js ├── tiny-canvas.js ├── tiny-canvas.zip ├── tiny-sprite.js ├── tiny-sprite.zip └── ts.js ├── example ├── app_canvas.js ├── app_sprite.js ├── fpsmeter.js ├── index_canvas.html └── index_sprite.html ├── package.json └── src ├── canvas.js ├── header-tc.js ├── header-ts.js ├── sprite.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | // Load Grunt tasks declared in the package.json file 3 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('package.json'), 8 | concat: { 9 | dist: { 10 | files: { 11 | 'build/tiny-canvas.js': [ 12 | ['src/header-tc.js', 'src/utils.js', 'src/canvas.js'] 13 | ], 14 | 'build/tiny-sprite.js': [ 15 | ['src/header-ts.js', 'src/utils.js', 'src/sprite.js'] 16 | ] 17 | } 18 | } 19 | }, 20 | clean: { 21 | beforeRelease: [ 22 | 'build/**/', 23 | ], 24 | afterRelease: [ 25 | 'build/tc.js.report.txt', 26 | 'build/ts.js.report.txt' 27 | ] 28 | }, 29 | compress: { 30 | canvas: { 31 | options: { 32 | archive: 'build/tiny-canvas.zip' 33 | }, 34 | files: [{ 35 | src: ['build/tc.js'] 36 | }] 37 | }, 38 | sprite: { 39 | options: { 40 | archive: 'build/tiny-sprite.zip' 41 | }, 42 | files: [{ 43 | src: ['build/ts.js'] 44 | }] 45 | } 46 | }, 47 | 'closure-compiler': { 48 | canvas: { 49 | closurePath: 'node_modules/grunt-closure-compiler', 50 | js: 'build/tiny-canvas.js', 51 | jsOutputFile: 'build/tc.js', 52 | options: { 53 | compilation_level: 'ADVANCED_OPTIMIZATIONS' 54 | } 55 | }, 56 | sprite: { 57 | closurePath: 'node_modules/grunt-closure-compiler', 58 | js: 'build/tiny-sprite.js', 59 | jsOutputFile: 'build/ts.js', 60 | options: { 61 | compilation_level: 'ADVANCED_OPTIMIZATIONS' 62 | } 63 | } 64 | } 65 | }); 66 | 67 | // Default task(s). 68 | grunt.registerTask('default', [ 69 | 'clean:beforeRelease', 70 | 'concat', 71 | 'closure-compiler', 72 | 'clean:afterRelease', 73 | 'compress' 74 | ]); 75 | grunt.registerTask('dev', [ 76 | 'clean:beforeRelease', 77 | 'concat' 78 | ]); 79 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![TinyCanvas](http://voidptr.io/dev/extra/shin.png "Shin Logo") 2 | 3 | Tiny-Canvas 4 | ==== 5 | 6 | Tiny-Canvas is composed by two minimal and lightweight rendering modules. One called TinyCanvas and the other one called TinySprite. Both of this modules use WebGL as a rendering backend for high performance. None of them falls back to canvas to keep the size of the library as small as possible. Tiny-Canvas modules are great if you have limited space (ex: Limited size contests) and want to have WebGL features like custom shaders. 7 | 8 | The main difference between TinyCanvas and TinySprite is that the first one implements a simple matrix stack to enable multiple layers of transformation, this would allow an easy implementation of a scene graph type of structure. TinySprite is mostly useful if you are working on a single layer of transformation. This means that TinySprite is a bit faster than TinyCanvas but less flexible. 9 | 10 | Modules size 11 | ----- 12 | 13 | TinyCanvas zipped size is: 1571 bytes 14 | 15 | TinyCanvas compiled size is: 3195 bytes 16 | 17 | TinyCanvas full size is: 10335 bytes 18 | 19 | --- 20 | 21 | TinySprite zipped size is: 1416 bytes 22 | 23 | TinySprite compiled size is: 3137 bytes 24 | 25 | TinySprite full size is: 9844 bytes 26 | 27 | Examples 28 | ----- 29 | 30 | [Small KittenMark Example (TinyCanvas)](https://raw.githack.com/bitnenfer/tiny-canvas/master/example/index_canvas.html) 31 | 32 | [Small KittenMark Example (TinySprite)](https://raw.githack.com/bitnenfer/tiny-canvas/master/example/index_sprite.html) 33 | 34 | TinyCanvas Module 35 | ----- 36 | 37 | TinyCanvas module is instanced using the construction function TC or TinyCanvas if working with unminified version. 38 | The function requires as a parameter a HTML Canvas element. 39 | 40 | ``` 41 | var canvas = TC(document.getElementsByClassName('canvas')[0]); 42 | ``` 43 | 44 | TinyCanvas Properties: 45 | 46 | - `g : WebGLRenderingContext` : Reference to the WebGL Context used by the renderer. 47 | - `c : HTMLCanvasElement` : Reference to the HTML Canvas Element used by the renderer. 48 | - `col: Number` : Integer number representing the current tint color on the canvas. It's represented like ARGB (ex: 0xFFFFFFFF). 49 | 50 | TinyCanvas Methods: 51 | 52 | - `bkg(red : Number, green : Number, blue : Number) : void`: Sets the background color. Maps to glClearColor. It requires normilized to 1.0 values. 53 | - `cls() : void`: Clear the current frame buffer. 54 | - `trans(x : Number, y : Number) : void`: Applies translate transformation to current matrix. 55 | - `scale(x : Number, y : Number) : void`: Applies scale transformation to current matrix. 56 | - `rot(radians : Number) : void`: Applies rotation transformation to current matrix. 57 | - `push() : void`: Pushes the current matrix into the matrix stack. 58 | - `pop() : void`: Pops the matrix stack into the current matrix. 59 | - `img( 60 | texture : WebGLTexture, 61 | x : Number, 62 | y : Number, 63 | width : Number, 64 | height : Number, 65 | u0 : Number, 66 | v0 : Number, 67 | u1 : Number, 68 | v1 : Number) : void`: 69 | Batches texture rendering properties. NOTE: If you are not drawing a tile of a texture then you can set u0 = 0, v0 = 0, u1 = 1 and v1 = 1. 70 | - `flush() : void`: Pushes the current batch information to the GPU for rendering. 71 | 72 | TinySprite Module 73 | ----- 74 | 75 | TinySprite module is instanced using the construction function TS or TinySprite if working with unminified version. 76 | The function requires as a parameter a HTML Canvas element. 77 | 78 | ``` 79 | var canvas = TS(document.getElementsByClassName('canvas')[0]); 80 | ``` 81 | 82 | TinySprite Properties: 83 | 84 | - `g : WebGLRenderingContext` : Reference to the WebGL Context used by the renderer. 85 | - `c : HTMLCanvasElement` : Reference to the HTML Canvas Element used by the renderer. 86 | - `col: Number` : Integer number representing the current tint color on the canvas. It's represented like ARGB (ex: 0xFFFFFFFF). 87 | 88 | TinySprite Methods: 89 | 90 | - `bkg(red : Number, green : Number, blue : Number) : void`: Sets the background color. Maps to glClearColor. It requires normilized to 1.0 values. 91 | - `cls() : void`: Clear the current frame buffer. 92 | - `img( 93 | texture : WebGLTexture, 94 | x : Number, 95 | y : Number, 96 | width : Number, 97 | height : Number, 98 | rotation: Number, 99 | translateX: Number, 100 | translateY: Number, 101 | scaleX: Number, 102 | scaleY: Number, 103 | u0 : Number, 104 | v0 : Number, 105 | u1 : Number, 106 | v1 : Number) : void`: 107 | Batches texture rendering properties. It's very similar to TinyCanvas `img` method but here you must pass the transformation parameters drawing function. This is because TinySprite doesn't have a matrix stack. NOTE: If you are not drawing a tile of a texture then you can set u0 = 0, v0 = 0, u1 = 1 and v1 = 1. 108 | - `flush() : void`: Pushes the current batch information to the GPU for rendering. 109 | 110 | Utility Functions 111 | ----- 112 | 113 | Tiny-Canvas comes with a couple of utility functions for easy creation of some WebGL primitives. 114 | 115 | - `TCPrg(gl : WebGLRenderingContext, vsSrouce : String, fsSource : String) : WebGLProgram` : Compiles a vertex and fragment shader and then links them to program. 116 | 117 | - `TCBuf(gl : WebGLRenderingContext, bufferType : Number, size : Number, usage : Number) : WebGlBuffer` : Creates and allocates a buffer object. 118 | 119 | - `TCTex(gl : WebGLRenderingContext, image : (Image | ArrayBuffer), width : Number, height: Number) : WebGLTexture` 120 | 121 | --- 122 | 123 | Developed by [Felipe Alfonso](https://twitter.com/bitnenfer/). 124 | 125 | The license for this software is [WTFPL](http://www.wtfpl.net/). -------------------------------------------------------------------------------- /build/tc.js: -------------------------------------------------------------------------------- 1 | function C(b,a,d){d=b.createShader(d);b.shaderSource(d,a);b.compileShader(d);return d}function D(b,a,d){var m=b.createProgram();a=C(b,a,35633);d=C(b,d,35632);b.attachShader(m,a);b.attachShader(m,d);b.linkProgram(m);return m}function F(b,a,d,m){var e=b.createBuffer();b.bindBuffer(a,e);b.bufferData(a,d,m);return e}window.TCShd=C;window.TCPrg=D;window.TCBuf=F; 2 | window.TCTex=function(b,a,d,m){var e=b.createTexture();b.bindTexture(3553,e);b.texParameteri(3553,10242,33071);b.texParameteri(3553,10243,33071);b.texParameteri(3553,10240,9728);b.texParameteri(3553,10241,9728);b.texImage2D(3553,0,6408,6408,5121,a);b.bindTexture(3553,null);e.width=d;e.height=m;return e}; 3 | window.TC=function(b){var a=b.getContext("webgl"),d=b.width,m=b.height,e=D(a,"precision lowp float;\nattribute vec2 a, b;\nattribute vec4 c;\nvarying vec2 d;\nvarying vec4 e;\nuniform mat4 m;\nuniform vec2 r;\nvoid main(){\ngl_Position=m*vec4(a,1.0,1.0);\nd=b;\ne=c;\n}","precision lowp float;\nvarying vec2 d;\nvarying vec4 e;\nuniform sampler2D f;\nvoid main(){\ngl_FragColor=texture2D(f,d)*e;\n}"),r=a.bufferSubData.bind(a),E=a.drawElements.bind(a),I=a.bindTexture.bind(a),J=a.clear.bind(a),K=a.clearColor.bind(a), 4 | q=new ArrayBuffer(873760),g=new Float32Array(q),t=new Uint32Array(q),l=new Uint16Array(131064),h=F(a,34963,l.byteLength,35044),p=F(a,34962,q.byteLength,35048),n=0,c=new Float32Array([1,0,0,1,0,0]),k=new Float32Array(100),f=0,L=Math.cos,M=Math.sin,u=null,G=null;a.blendFunc(770,771);a.enable(3042);a.useProgram(e);a.bindBuffer(34963,h);for(h=indexB=0;65532>h;h+=6,indexB+=4)l[h+0]=indexB,l[h+1]=indexB+1,l[h+2]=indexB+2,l[h+3]=indexB+0,l[h+4]=indexB+3,l[h+5]=indexB+1;r(34963,0,l);a.bindBuffer(34962,p); 5 | l=a.getAttribLocation(e,"a");p=a.getAttribLocation(e,"b");h=a.getAttribLocation(e,"c");a.enableVertexAttribArray(l);a.vertexAttribPointer(l,2,5126,0,20,0);a.enableVertexAttribArray(p);a.vertexAttribPointer(p,2,5126,0,20,8);a.enableVertexAttribArray(h);a.vertexAttribPointer(h,4,5121,1,20,16);a.uniformMatrix4fv(a.getUniformLocation(e,"m"),0,new Float32Array([2/d,0,0,0,0,-2/m,0,0,0,0,1,1,-1,1,0,0]));a.activeTexture(33984);return G={g:a,c:b,col:4294967295,bkg:function(a,c,b){K(a,c,b,1)},cls:function(){J(16384)}, 6 | trans:function(a,b){c[4]=c[0]*a+c[2]*b+c[4];c[5]=c[1]*a+c[3]*b+c[5]},scale:function(a,b){c[0]*=a;c[1]*=a;c[2]*=b;c[3]*=b},rot:function(a){var b=c[0],g=c[1],d=c[2],f=c[3],e=M(a);a=L(a);c[0]=b*a+d*e;c[1]=g*a+f*e;c[2]=b*-e+d*a;c[3]=g*-e+f*a},push:function(){k[f+0]=c[0];k[f+1]=c[1];k[f+2]=c[2];k[f+3]=c[3];k[f+4]=c[4];k[f+5]=c[5];f+=6},pop:function(){f-=6;c[0]=k[f+0];c[1]=k[f+1];c[2]=k[f+2];c[3]=k[f+3];c[4]=k[f+4];c[5]=k[f+5]},img:function(a,b,d,e,f,h,k,l,m){var p=b+e,H=d+f;f=d+f;e=b+e;var v=c[0],w=c[1], 7 | x=c[2],y=c[3],z=c[4],A=c[5],B=G.col;if(a!=u||10922<=n+1)r(34962,0,q),E(4,6*n,5123,0),n=0,u!=a&&(u=a,I(3553,u));a=20*n;g[a++]=b*v+d*x+z;g[a++]=b*w+d*y+A;g[a++]=h;g[a++]=k;t[a++]=B;g[a++]=p*v+H*x+z;g[a++]=p*w+H*y+A;g[a++]=l;g[a++]=m;t[a++]=B;g[a++]=b*v+f*x+z;g[a++]=b*w+f*y+A;g[a++]=h;g[a++]=m;t[a++]=B;g[a++]=e*v+d*x+z;g[a++]=e*w+d*y+A;g[a++]=l;g[a++]=k;t[a++]=B;10922<=++n&&(r(34962,0,q),E(4,6*n,5123,0),n=0)},flush:function(){0!=n&&(r(34962,0,g.subarray(0,20*n)),E(4,6*n,5123,0),n=0)}}}; 8 | -------------------------------------------------------------------------------- /build/tiny-canvas.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TinyCanvas module (https://github.com/bitnenfer/tiny-canvas) 3 | * Developed by Felipe Alfonso -> https://twitter.com/bitnenfer/ 4 | * 5 | * ---------------------------------------------------------------------- 6 | * 7 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | * Version 2, December 2004 9 | * 10 | * Copyright (C) 2004 Sam Hocevar 11 | * 12 | * Everyone is permitted to copy and distribute verbatim or modified 13 | * copies of this license document, and changing it is allowed as long 14 | * as the name is changed. 15 | * 16 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 17 | * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 18 | * 19 | * 0. You just DO WHAT THE FUCK YOU WANT TO. 20 | * 21 | * ---------------------------------------------------------------------- 22 | * 23 | */ 24 | 25 | function CompileShader(gl, source, type) { 26 | var shader = gl.createShader(type); 27 | gl.shaderSource(shader, source); 28 | gl.compileShader(shader); 29 | return shader; 30 | } 31 | 32 | function CreateShaderProgram(gl, vsSource, fsSource) { 33 | var program = gl.createProgram(), 34 | vShader = CompileShader(gl, vsSource, 35633), 35 | fShader = CompileShader(gl, fsSource, 35632); 36 | gl.attachShader(program, vShader); 37 | gl.attachShader(program, fShader); 38 | gl.linkProgram(program); 39 | return program; 40 | } 41 | 42 | function CreateBuffer(gl, bufferType, size, usage) { 43 | var buffer = gl.createBuffer(); 44 | gl.bindBuffer(bufferType, buffer); 45 | gl.bufferData(bufferType, size, usage); 46 | return buffer; 47 | } 48 | 49 | function CreateTexture(gl, image, width, height) { 50 | var texture = gl.createTexture(); 51 | gl.bindTexture(3553, texture); 52 | gl.texParameteri(3553, 10242, 33071); 53 | gl.texParameteri(3553, 10243, 33071); 54 | gl.texParameteri(3553, 10240, 9728); 55 | gl.texParameteri(3553, 10241, 9728); 56 | gl.texImage2D(3553, 0, 6408, 6408, 5121, image); 57 | gl.bindTexture(3553, null); 58 | texture.width = width; 59 | texture.height = height; 60 | return texture; 61 | } 62 | window['TCShd'] = CompileShader; 63 | window['TCPrg'] = CreateShaderProgram; 64 | window['TCBuf'] = CreateBuffer; 65 | window['TCTex'] = CreateTexture; 66 | 67 | function TinyCanvas(canvas) { 68 | var gl = canvas.getContext('webgl'), 69 | VERTEX_SIZE = (4 * 2) + (4 * 2) + (4), 70 | MAX_BATCH = 10922, // floor((2 ^ 16) / 6) 71 | MAX_STACK = 100, 72 | MAT_SIZE = 6, 73 | VERTICES_PER_QUAD = 6, 74 | MAT_STACK_SIZE = MAX_STACK * MAT_SIZE, 75 | VERTEX_DATA_SIZE = VERTEX_SIZE * MAX_BATCH * 4, 76 | INDEX_DATA_SIZE = MAX_BATCH * (2 * VERTICES_PER_QUAD), 77 | width = canvas.width, 78 | height = canvas.height, 79 | shader = CreateShaderProgram( 80 | gl, [ 81 | 'precision lowp float;', 82 | // IN Vertex Position and 83 | // IN Texture Coordinates 84 | 'attribute vec2 a, b;', 85 | // IN Vertex Color 86 | 'attribute vec4 c;', 87 | // OUT Texture Coordinates 88 | 'varying vec2 d;', 89 | // OUT Vertex Color 90 | 'varying vec4 e;', 91 | // CONST View Matrix 92 | 'uniform mat4 m;', 93 | 'uniform vec2 r;', 94 | 'void main(){', 95 | 'gl_Position=m*vec4(a,1.0,1.0);', 96 | 'd=b;', 97 | 'e=c;', 98 | '}' 99 | ].join('\n'), [ 100 | 'precision lowp float;', 101 | // OUT Texture Coordinates 102 | 'varying vec2 d;', 103 | // OUT Vertex Color 104 | 'varying vec4 e;', 105 | // CONST Single Sampler2D 106 | 'uniform sampler2D f;', 107 | 'void main(){', 108 | 'gl_FragColor=texture2D(f,d)*e;', 109 | '}' 110 | ].join('\n') 111 | ), 112 | glBufferSubData = gl.bufferSubData.bind(gl), 113 | glDrawElements = gl.drawElements.bind(gl), 114 | glBindTexture = gl.bindTexture.bind(gl), 115 | glClear = gl.clear.bind(gl), 116 | glClearColor = gl.clearColor.bind(gl), 117 | vertexData = new ArrayBuffer(VERTEX_DATA_SIZE), 118 | vPositionData = new Float32Array(vertexData), 119 | vColorData = new Uint32Array(vertexData), 120 | vIndexData = new Uint16Array(INDEX_DATA_SIZE), 121 | IBO = CreateBuffer(gl, 34963, vIndexData.byteLength, 35044), 122 | VBO = CreateBuffer(gl, 34962, vertexData.byteLength, 35048), 123 | count = 0, 124 | mat = new Float32Array([1, 0, 0, 1, 0, 0]), 125 | stack = new Float32Array(100), 126 | stackp = 0, 127 | cos = Math.cos, 128 | sin = Math.sin, 129 | currentTexture = null, 130 | renderer = null, 131 | locA, locB, locC; 132 | 133 | gl.blendFunc(770, 771); 134 | gl.enable(3042); 135 | gl.useProgram(shader); 136 | gl.bindBuffer(34963, IBO); 137 | for (var indexA = indexB = 0; indexA < MAX_BATCH * VERTICES_PER_QUAD; indexA += VERTICES_PER_QUAD, indexB += 4) 138 | vIndexData[indexA + 0] = indexB, 139 | vIndexData[indexA + 1] = indexB + 1, 140 | vIndexData[indexA + 2] = indexB + 2, 141 | vIndexData[indexA + 3] = indexB + 0, 142 | vIndexData[indexA + 4] = indexB + 3, 143 | vIndexData[indexA + 5] = indexB + 1; 144 | 145 | glBufferSubData(34963, 0, vIndexData); 146 | gl.bindBuffer(34962, VBO); 147 | locA = gl.getAttribLocation(shader, 'a'); 148 | locB = gl.getAttribLocation(shader, 'b'); 149 | locC = gl.getAttribLocation(shader, 'c'); 150 | gl.enableVertexAttribArray(locA); 151 | gl.vertexAttribPointer(locA, 2, 5126, 0, VERTEX_SIZE, 0); 152 | gl.enableVertexAttribArray(locB); 153 | gl.vertexAttribPointer(locB, 2, 5126, 0, VERTEX_SIZE, 8); 154 | gl.enableVertexAttribArray(locC); 155 | gl.vertexAttribPointer(locC, 4, 5121, 1, VERTEX_SIZE, 16); 156 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, 'm'), 0, 157 | new Float32Array([ 158 | 2 / width, 0, 0, 0, 159 | 0, -2 / height, 0, 0, 160 | 0, 0, 1, 1, -1, 1, 0, 0 161 | ]) 162 | ); 163 | gl.activeTexture(33984); 164 | renderer = { 165 | 'g': gl, 166 | 'c': canvas, 167 | 'col': 0xFFFFFFFF, 168 | 'bkg': function (r, g, b) { 169 | glClearColor(r, g, b, 1); 170 | }, 171 | 'cls': function () { 172 | glClear(16384); 173 | }, 174 | 'trans': function (x, y) { 175 | mat[4] = mat[0] * x + mat[2] * y + mat[4]; 176 | mat[5] = mat[1] * x + mat[3] * y + mat[5]; 177 | }, 178 | 'scale': function (x, y) { 179 | mat[0] = mat[0] * x; 180 | mat[1] = mat[1] * x; 181 | mat[2] = mat[2] * y; 182 | mat[3] = mat[3] * y; 183 | }, 184 | 'rot': function (r) { 185 | var a = mat[0], 186 | b = mat[1], 187 | c = mat[2], 188 | d = mat[3], 189 | sr = sin(r), 190 | cr = cos(r); 191 | 192 | mat[0] = a * cr + c * sr; 193 | mat[1] = b * cr + d * sr; 194 | mat[2] = a * -sr + c * cr; 195 | mat[3] = b * -sr + d * cr; 196 | }, 197 | 'push': function () { 198 | stack[stackp + 0] = mat[0]; 199 | stack[stackp + 1] = mat[1]; 200 | stack[stackp + 2] = mat[2]; 201 | stack[stackp + 3] = mat[3]; 202 | stack[stackp + 4] = mat[4]; 203 | stack[stackp + 5] = mat[5]; 204 | stackp += 6; 205 | }, 206 | 'pop': function () { 207 | stackp -= 6; 208 | mat[0] = stack[stackp + 0]; 209 | mat[1] = stack[stackp + 1]; 210 | mat[2] = stack[stackp + 2]; 211 | mat[3] = stack[stackp + 3]; 212 | mat[4] = stack[stackp + 4]; 213 | mat[5] = stack[stackp + 5]; 214 | }, 215 | 'img': function (texture, x, y, w, h, u0, v0, u1, v1) { 216 | var x0 = x, 217 | y0 = y, 218 | x1 = x + w, 219 | y1 = y + h, 220 | x2 = x, 221 | y2 = y + h, 222 | x3 = x + w, 223 | y3 = y, 224 | a = mat[0], 225 | b = mat[1], 226 | c = mat[2], 227 | d = mat[3], 228 | e = mat[4], 229 | f = mat[5], 230 | offset = 0, 231 | argb = renderer['col']; 232 | 233 | if (texture != currentTexture || 234 | count + 1 >= MAX_BATCH) { 235 | glBufferSubData(34962, 0, vertexData); 236 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 237 | count = 0; 238 | if (currentTexture != texture) { 239 | currentTexture = texture; 240 | glBindTexture(3553, currentTexture); 241 | } 242 | } 243 | 244 | offset = count * VERTEX_SIZE; 245 | // Vertex Order 246 | // Vertex Position | UV | ARGB 247 | // Vertex 1 248 | vPositionData[offset++] = x0 * a + y0 * c + e; 249 | vPositionData[offset++] = x0 * b + y0 * d + f; 250 | vPositionData[offset++] = u0; 251 | vPositionData[offset++] = v0; 252 | vColorData[offset++] = argb; 253 | 254 | // Vertex 2 255 | vPositionData[offset++] = x1 * a + y1 * c + e; 256 | vPositionData[offset++] = x1 * b + y1 * d + f; 257 | vPositionData[offset++] = u1; 258 | vPositionData[offset++] = v1; 259 | vColorData[offset++] = argb; 260 | 261 | // Vertex 3 262 | vPositionData[offset++] = x2 * a + y2 * c + e; 263 | vPositionData[offset++] = x2 * b + y2 * d + f; 264 | vPositionData[offset++] = u0; 265 | vPositionData[offset++] = v1; 266 | vColorData[offset++] = argb; 267 | 268 | // Vertex 4 269 | vPositionData[offset++] = x3 * a + y3 * c + e; 270 | vPositionData[offset++] = x3 * b + y3 * d + f; 271 | vPositionData[offset++] = u1; 272 | vPositionData[offset++] = v0; 273 | vColorData[offset++] = argb; 274 | 275 | if (++count >= MAX_BATCH) { 276 | glBufferSubData(34962, 0, vertexData); 277 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 278 | count = 0; 279 | } 280 | }, 281 | 'flush': function () { 282 | if (count == 0) return; 283 | glBufferSubData(34962, 0, vPositionData.subarray(0, count * VERTEX_SIZE)); 284 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 285 | count = 0; 286 | } 287 | }; 288 | return renderer; 289 | } 290 | window['TC'] = TinyCanvas; -------------------------------------------------------------------------------- /build/tiny-canvas.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitnenfer/tiny-canvas/97bd9ceec968fbcc6ded85a923eb6e5f7ca47c76/build/tiny-canvas.zip -------------------------------------------------------------------------------- /build/tiny-sprite.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TinySprite module (https://github.com/bitnenfer/tiny-canvas) 3 | * Developed by Felipe Alfonso -> https://twitter.com/bitnenfer/ 4 | * 5 | * ---------------------------------------------------------------------- 6 | * 7 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | * Version 2, December 2004 9 | * 10 | * Copyright (C) 2004 Sam Hocevar 11 | * 12 | * Everyone is permitted to copy and distribute verbatim or modified 13 | * copies of this license document, and changing it is allowed as long 14 | * as the name is changed. 15 | * 16 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 17 | * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 18 | * 19 | * 0. You just DO WHAT THE FUCK YOU WANT TO. 20 | * 21 | * ---------------------------------------------------------------------- 22 | * 23 | */ 24 | function CompileShader(gl, source, type) { 25 | var shader = gl.createShader(type); 26 | gl.shaderSource(shader, source); 27 | gl.compileShader(shader); 28 | return shader; 29 | } 30 | 31 | function CreateShaderProgram(gl, vsSource, fsSource) { 32 | var program = gl.createProgram(), 33 | vShader = CompileShader(gl, vsSource, 35633), 34 | fShader = CompileShader(gl, fsSource, 35632); 35 | gl.attachShader(program, vShader); 36 | gl.attachShader(program, fShader); 37 | gl.linkProgram(program); 38 | return program; 39 | } 40 | 41 | function CreateBuffer(gl, bufferType, size, usage) { 42 | var buffer = gl.createBuffer(); 43 | gl.bindBuffer(bufferType, buffer); 44 | gl.bufferData(bufferType, size, usage); 45 | return buffer; 46 | } 47 | 48 | function CreateTexture(gl, image, width, height) { 49 | var texture = gl.createTexture(); 50 | gl.bindTexture(3553, texture); 51 | gl.texParameteri(3553, 10242, 33071); 52 | gl.texParameteri(3553, 10243, 33071); 53 | gl.texParameteri(3553, 10240, 9728); 54 | gl.texParameteri(3553, 10241, 9728); 55 | gl.texImage2D(3553, 0, 6408, 6408, 5121, image); 56 | gl.bindTexture(3553, null); 57 | texture.width = width; 58 | texture.height = height; 59 | return texture; 60 | } 61 | window['TCShd'] = CompileShader; 62 | window['TCPrg'] = CreateShaderProgram; 63 | window['TCBuf'] = CreateBuffer; 64 | window['TCTex'] = CreateTexture; 65 | 66 | function TinySprite(canvas) { 67 | var gl = canvas.getContext('webgl'), 68 | // float + (vec2 * 4) + (char * 4) 69 | VERTEX_SIZE = 4 + ((4 * 2) * 4) + (4), 70 | MAX_BATCH = 10922, // floor((2 ^ 16) / 6) 71 | VERTEX_DATA_SIZE = VERTEX_SIZE * MAX_BATCH * 4, 72 | VERTICES_PER_QUAD = 6, 73 | INDEX_DATA_SIZE = MAX_BATCH * (2 * VERTICES_PER_QUAD), 74 | width = canvas.width, 75 | height = canvas.height, 76 | shader = CreateShaderProgram( 77 | gl, [ 78 | 'precision lowp float;', 79 | 'attribute float a;', 80 | 'attribute vec2 b,c,d,e;', 81 | 'attribute vec4 f;', 82 | 'varying vec2 g;', 83 | 'varying vec4 h;', 84 | 'uniform mat4 i;', 85 | 'void main() {', 86 | 'float q=cos(a);', 87 | 'float w=sin(a);', 88 | 'gl_Position=i*vec4(((vec2(d.x*q-d.y*w,d.x*w+d.y*q)*c)+b),1.0,1.0);', 89 | 'g=e;', 90 | 'h=f;', 91 | '}' 92 | ].join('\n'), [ 93 | 'precision lowp float;', 94 | 'varying vec2 g;', 95 | 'varying vec4 h;', 96 | 'uniform sampler2D j;', 97 | 'void main(){', 98 | 'gl_FragColor=texture2D(j,g)*h;', 99 | '}' 100 | ].join('\n') 101 | ), 102 | glBufferSubData = gl.bufferSubData.bind(gl), 103 | glDrawElements = gl.drawElements.bind(gl), 104 | glBindTexture = gl.bindTexture.bind(gl), 105 | glClear = gl.clear.bind(gl), 106 | glClearColor = gl.clearColor.bind(gl), 107 | vertexData = new ArrayBuffer(VERTEX_DATA_SIZE), 108 | vPositionData = new Float32Array(vertexData), 109 | vColorData = new Uint32Array(vertexData), 110 | vIndexData = new Uint16Array(INDEX_DATA_SIZE), 111 | IBO = CreateBuffer(gl, 34963, vIndexData.byteLength, 35044), 112 | VBO = CreateBuffer(gl, 34962, vertexData.byteLength, 35048), 113 | count = 0, 114 | currentTexture = null, 115 | renderer = null, 116 | locRotation, locTranslation, locScale, 117 | locPosition, locUV, locColor; 118 | 119 | gl.blendFunc(770, 771); 120 | gl.enable(3042); 121 | gl.useProgram(shader); 122 | gl.bindBuffer(34963, IBO); 123 | for (var indexA = indexB = 0; 124 | indexA < MAX_BATCH * VERTICES_PER_QUAD; 125 | indexA += VERTICES_PER_QUAD, indexB += 4) 126 | vIndexData[indexA + 0] = indexB, 127 | vIndexData[indexA + 1] = indexB + 1, 128 | vIndexData[indexA + 2] = indexB + 2, 129 | vIndexData[indexA + 3] = indexB + 0, 130 | vIndexData[indexA + 4] = indexB + 3, 131 | vIndexData[indexA + 5] = indexB + 1; 132 | 133 | gl.bufferSubData(34963, 0, vIndexData); 134 | gl.bindBuffer(34962, VBO); 135 | 136 | locRotation = gl.getAttribLocation(shader, 'a'); 137 | locTranslation = gl.getAttribLocation(shader, 'b'); 138 | locScale = gl.getAttribLocation(shader, 'c'); 139 | locPosition = gl.getAttribLocation(shader, 'd'); 140 | locUV = gl.getAttribLocation(shader, 'e'); 141 | locColor = gl.getAttribLocation(shader, 'f'); 142 | 143 | // Rotation 144 | gl.enableVertexAttribArray(locRotation); 145 | gl.vertexAttribPointer(locRotation, 1, 5126, 0, VERTEX_SIZE, 0); 146 | 147 | // Translation 148 | gl.enableVertexAttribArray(locTranslation); 149 | gl.vertexAttribPointer(locTranslation, 2, 5126, 0, VERTEX_SIZE, 4); 150 | 151 | // Scale 152 | gl.enableVertexAttribArray(locScale); 153 | gl.vertexAttribPointer(locScale, 2, 5126, 0, VERTEX_SIZE, 12); 154 | 155 | // Position 156 | gl.enableVertexAttribArray(locPosition); 157 | gl.vertexAttribPointer(locPosition, 2, 5126, 0, VERTEX_SIZE, 20); 158 | 159 | // UV 160 | gl.enableVertexAttribArray(locUV); 161 | gl.vertexAttribPointer(locUV, 2, 5126, 0, VERTEX_SIZE, 28); 162 | 163 | // Color 164 | gl.enableVertexAttribArray(locColor); 165 | gl.vertexAttribPointer(locColor, 4, 5121, 1, VERTEX_SIZE, 36); 166 | 167 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, 'i'), 0, 168 | new Float32Array([ 169 | 2 / width, 0, 0, 0, 170 | 0, -2 / height, 0, 0, 171 | 0, 0, 1, 1, 172 | -1, 1, 0, 0 173 | ]) 174 | ); 175 | gl.activeTexture(33984); 176 | renderer = { 177 | 'g': gl, 178 | 'c': canvas, 179 | 'col': 0xFFFFFFFF, 180 | 'bkg': function (r, g, b) { 181 | gl.clearColor(r, g, b, 1); 182 | }, 183 | 'cls': function () { 184 | gl.clear(16384); 185 | }, 186 | 'img': function (texture, x, y, w, h, r, tx, ty, sx, sy, u0, v0, u1, v1) { 187 | var x0 = x, 188 | y0 = y, 189 | x1 = x + w, 190 | y1 = y + h, 191 | x2 = x, 192 | y2 = y + h, 193 | x3 = x + w, 194 | y3 = y, 195 | offset = 0, 196 | argb = renderer['col']; 197 | 198 | if (texture != currentTexture || 199 | count + 1 >= MAX_BATCH) { 200 | glBufferSubData(34962, 0, vertexData); 201 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 202 | count = 0; 203 | if (texture != currentTexture) { 204 | currentTexture = texture; 205 | glBindTexture(3553, currentTexture); 206 | } 207 | } 208 | 209 | offset = count * VERTEX_SIZE; 210 | // Vertex Order: 211 | // rotation | translation | scale | position | uv | color 212 | // Vertex 1 213 | vPositionData[offset++] = r; 214 | vPositionData[offset++] = tx; 215 | vPositionData[offset++] = ty; 216 | vPositionData[offset++] = sx; 217 | vPositionData[offset++] = sy; 218 | vPositionData[offset++] = x0; 219 | vPositionData[offset++] = y0; 220 | vPositionData[offset++] = u0; 221 | vPositionData[offset++] = v0; 222 | vColorData[offset++] = argb; 223 | 224 | // Vertex 2 225 | vPositionData[offset++] = r; 226 | vPositionData[offset++] = tx; 227 | vPositionData[offset++] = ty; 228 | vPositionData[offset++] = sx; 229 | vPositionData[offset++] = sy; 230 | vPositionData[offset++] = x1; 231 | vPositionData[offset++] = y1; 232 | vPositionData[offset++] = u1; 233 | vPositionData[offset++] = v1; 234 | vColorData[offset++] = argb; 235 | 236 | // Vertex 3 237 | vPositionData[offset++] = r; 238 | vPositionData[offset++] = tx; 239 | vPositionData[offset++] = ty; 240 | vPositionData[offset++] = sx; 241 | vPositionData[offset++] = sy; 242 | vPositionData[offset++] = x2; 243 | vPositionData[offset++] = y2; 244 | vPositionData[offset++] = u0; 245 | vPositionData[offset++] = v1; 246 | vColorData[offset++] = argb; 247 | 248 | // Vertex 4 249 | vPositionData[offset++] = r; 250 | vPositionData[offset++] = tx; 251 | vPositionData[offset++] = ty; 252 | vPositionData[offset++] = sx; 253 | vPositionData[offset++] = sy; 254 | vPositionData[offset++] = x3; 255 | vPositionData[offset++] = y3; 256 | vPositionData[offset++] = u1; 257 | vPositionData[offset++] = v0; 258 | vColorData[offset++] = argb; 259 | 260 | if (++count >= MAX_BATCH) { 261 | glBufferSubData(34962, 0, vertexData); 262 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 263 | count = 0; 264 | } 265 | }, 266 | 'flush': function () { 267 | if (count == 0) return; 268 | glBufferSubData(34962, 0, vPositionData.subarray(0, count * VERTEX_SIZE)); 269 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 270 | count = 0; 271 | } 272 | }; 273 | return renderer; 274 | } 275 | 276 | window['TS'] = TinySprite; -------------------------------------------------------------------------------- /build/tiny-sprite.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitnenfer/tiny-canvas/97bd9ceec968fbcc6ded85a923eb6e5f7ca47c76/build/tiny-sprite.zip -------------------------------------------------------------------------------- /build/ts.js: -------------------------------------------------------------------------------- 1 | function x(c,a,e){e=c.createShader(e);c.shaderSource(e,a);c.compileShader(e);return e}function y(c,a,e){var h=c.createProgram();a=x(c,a,35633);e=x(c,e,35632);c.attachShader(h,a);c.attachShader(h,e);c.linkProgram(h);return h}function B(c,a,e,h){var d=c.createBuffer();c.bindBuffer(a,d);c.bufferData(a,e,h);return d}window.TCShd=x;window.TCPrg=y;window.TCBuf=B; 2 | window.TCTex=function(c,a,e,h){var d=c.createTexture();c.bindTexture(3553,d);c.texParameteri(3553,10242,33071);c.texParameteri(3553,10243,33071);c.texParameteri(3553,10240,9728);c.texParameteri(3553,10241,9728);c.texImage2D(3553,0,6408,6408,5121,a);c.bindTexture(3553,null);d.width=e;d.height=h;return d}; 3 | window.TS=function(c){var a=c.getContext("webgl"),e=c.width,h=c.height,d=y(a,"precision lowp float;\nattribute float a;\nattribute vec2 b,c,d,e;\nattribute vec4 f;\nvarying vec2 g;\nvarying vec4 h;\nuniform mat4 i;\nvoid main() {\nfloat q=cos(a);\nfloat w=sin(a);\ngl_Position=i*vec4(((vec2(d.x*q-d.y*w,d.x*w+d.y*q)*c)+b),1.0,1.0);\ng=e;\nh=f;\n}","precision lowp float;\nvarying vec2 g;\nvarying vec4 h;\nuniform sampler2D j;\nvoid main(){\ngl_FragColor=texture2D(j,g)*h;\n}"),z=a.bufferSubData.bind(a), 4 | A=a.drawElements.bind(a),E=a.bindTexture.bind(a),q=new ArrayBuffer(1747520),b=new Float32Array(q),r=new Uint32Array(q),g=new Uint16Array(131064),f=B(a,34963,g.byteLength,35044),l=B(a,34962,q.byteLength,35048),k=0,t=null,C=null,m,n,p;a.blendFunc(770,771);a.enable(3042);a.useProgram(d);a.bindBuffer(34963,f);for(f=indexB=0;65532>f;f+=6,indexB+=4)g[f+0]=indexB,g[f+1]=indexB+1,g[f+2]=indexB+2,g[f+3]=indexB+0,g[f+4]=indexB+3,g[f+5]=indexB+1;a.bufferSubData(34963,0,g);a.bindBuffer(34962,l);g=a.getAttribLocation(d, 5 | "a");l=a.getAttribLocation(d,"b");f=a.getAttribLocation(d,"c");m=a.getAttribLocation(d,"d");n=a.getAttribLocation(d,"e");p=a.getAttribLocation(d,"f");a.enableVertexAttribArray(g);a.vertexAttribPointer(g,1,5126,0,40,0);a.enableVertexAttribArray(l);a.vertexAttribPointer(l,2,5126,0,40,4);a.enableVertexAttribArray(f);a.vertexAttribPointer(f,2,5126,0,40,12);a.enableVertexAttribArray(m);a.vertexAttribPointer(m,2,5126,0,40,20);a.enableVertexAttribArray(n);a.vertexAttribPointer(n,2,5126,0,40,28);a.enableVertexAttribArray(p); 6 | a.vertexAttribPointer(p,4,5121,1,40,36);a.uniformMatrix4fv(a.getUniformLocation(d,"i"),0,new Float32Array([2/e,0,0,0,0,-2/h,0,0,0,0,1,1,-1,1,0,0]));a.activeTexture(33984);return C={g:a,c:c,col:4294967295,bkg:function(F,b,c){a.clearColor(F,b,c,1)},cls:function(){a.clear(16384)},img:function(a,c,d,e,f,g,h,l,u,v,m,n,p,D){var G=c+e,H=d+f;f=d+f;e=c+e;var w=C.col;if(a!=t||10922<=k+1)z(34962,0,q),A(4,6*k,5123,0),k=0,a!=t&&(t=a,E(3553,t));a=40*k;b[a++]=g;b[a++]=h;b[a++]=l;b[a++]=u;b[a++]=v;b[a++]=c;b[a++]= 7 | d;b[a++]=m;b[a++]=n;r[a++]=w;b[a++]=g;b[a++]=h;b[a++]=l;b[a++]=u;b[a++]=v;b[a++]=G;b[a++]=H;b[a++]=p;b[a++]=D;r[a++]=w;b[a++]=g;b[a++]=h;b[a++]=l;b[a++]=u;b[a++]=v;b[a++]=c;b[a++]=f;b[a++]=m;b[a++]=D;r[a++]=w;b[a++]=g;b[a++]=h;b[a++]=l;b[a++]=u;b[a++]=v;b[a++]=e;b[a++]=d;b[a++]=p;b[a++]=n;r[a++]=w;10922<=++k&&(z(34962,0,q),A(4,6*k,5123,0),k=0)},flush:function(){0!=k&&(z(34962,0,b.subarray(0,40*k)),A(4,6*k,5123,0),k=0)}}}; 8 | -------------------------------------------------------------------------------- /example/app_canvas.js: -------------------------------------------------------------------------------- 1 | // This example is based of PIXI's bunnymark 2 | var canvas = TC(document.getElementById('canvas')), 3 | kittenTexture = null, 4 | fpsMeter = new FPSMeter({ 5 | graph: 1, 6 | heat: 1, 7 | theme: 'dark', 8 | interval: 50 9 | }), 10 | gl = canvas.g, 11 | kittenImage = new Image(), 12 | gravity = 0.5, 13 | random = Math.random, 14 | maxX = canvas.c.width, 15 | minX = 0, 16 | maxY = canvas.c.height, 17 | minY = 0, 18 | add = false, 19 | startBunnyCount = 2, 20 | count = 0, 21 | amount = 100, 22 | kittens = [], 23 | counter = document.getElementById('kitten-count'), 24 | frames = [ 25 | [0, 0, 32, 32], 26 | [0, 32, 32, 32], 27 | [0, 64, 32, 32], 28 | [0, 96, 32, 32] 29 | ], 30 | currentFrame = 0; 31 | 32 | function Sprite(x, y, texture, frameX, frameY, frameW, frameH) { 33 | this.positionX = x; 34 | this.positionY = y; 35 | this.width = frameW; 36 | this.height = frameH; 37 | this.texture = texture; 38 | this.speedX = 0; 39 | this.speedY = 0; 40 | this.rotation = 0; 41 | this.u0 = frameX / texture.width; 42 | this.v0 = frameY / texture.height; 43 | this.u1 = this.u0 + (frameW / texture.width); 44 | this.v1 = this.v0 + (frameH / texture.height); 45 | this.halfWidth = frameW / 2; 46 | } 47 | 48 | function create() { 49 | var frame = frames[currentFrame]; 50 | for (var i = 0; i < startBunnyCount; i++) { 51 | var kitten = new Sprite(0, 0, kittenTexture, frame[0], frame[1], frame[2], frame[3]); 52 | kitten.speedX = Math.random() * 10; 53 | kitten.speedY = (Math.random() * 10) - 5; 54 | kittens[count++] = kitten; 55 | } 56 | counter.innerHTML = count + " KITTENS"; 57 | 58 | canvas.bkg(0.227, 0.227, 0.227); 59 | mainLoop(); 60 | } 61 | 62 | function update() { 63 | if (add) { 64 | if (count < 200000) { 65 | var frame = frames[currentFrame]; 66 | for (var i = 0; i < amount; i++) { 67 | var kitten = new Sprite(0, 0, kittenTexture, frame[0], frame[1], frame[2], frame[3]); 68 | kitten.speedX = Math.random() * 10; 69 | kitten.speedY = (Math.random() * 10) - 5; 70 | kitten.rotation = (Math.random() - 0.5); 71 | kittens[count++] = kitten; 72 | } 73 | counter.innerHTML = count + " KITTENS"; 74 | } 75 | } 76 | for (var i = 0; i < count; i++) { 77 | var kitten = kittens[i]; 78 | kitten.positionX += kitten.speedX; 79 | kitten.positionY += kitten.speedY; 80 | kitten.speedY += gravity; 81 | 82 | if (kitten.positionX > maxX) { 83 | kitten.speedX *= -1; 84 | kitten.positionX = maxX; 85 | } else if (kitten.positionX < minX) { 86 | kitten.speedX *= -1; 87 | kitten.positionX = minX; 88 | } 89 | if (kitten.positionY > maxY) { 90 | kitten.speedY *= -0.85; 91 | kitten.positionY = maxY; 92 | kitten.spin = (random() - 0.5) * 0.2 93 | if (random() > 0.5) { 94 | kitten.speedY -= random() * 6; 95 | } 96 | } else if (kitten.positionY < minY) { 97 | kitten.speedY = 0; 98 | kitten.positionY = minY; 99 | } 100 | } 101 | } 102 | 103 | function draw() { 104 | canvas.cls(); 105 | for (var i = 0; i < count; i++) { 106 | var kitten = kittens[i]; 107 | canvas.push(); 108 | canvas.trans(kitten.positionX, kitten.positionY); 109 | canvas.rot(kitten.rotation); 110 | canvas.img( 111 | kitten.texture, -kitten.halfWidth, 112 | 0, 113 | kitten.width, 114 | kitten.height, 115 | kitten.u0, 116 | kitten.v0, 117 | kitten.u1, 118 | kitten.v1 119 | ); 120 | canvas.pop(); 121 | } 122 | canvas.flush(); 123 | } 124 | 125 | function mainLoop() { 126 | requestAnimationFrame(mainLoop); 127 | fpsMeter.tick(); 128 | update(); 129 | draw(); 130 | } 131 | 132 | canvas.c.onmousedown = function (evt) { 133 | add = true; 134 | currentFrame = (currentFrame + 1) % frames.length; 135 | }; 136 | canvas.c.onmouseup = function (evt) { 137 | add = false; 138 | }; 139 | canvas.c.ontouchstart = function (evt) { 140 | add = true; 141 | currentFrame = (currentFrame + 1) % frames.length; 142 | }; 143 | canvas.c.ontouchend = function (evt) { 144 | add = false; 145 | }; 146 | kittenImage.onload = function () { 147 | kittenTexture = TCTex(gl, kittenImage, kittenImage.width, kittenImage.height); 148 | create(); 149 | }; 150 | kittenImage.src = 'data:image/png;base64,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'; -------------------------------------------------------------------------------- /example/app_sprite.js: -------------------------------------------------------------------------------- 1 | // This example is based of PIXI's bunnymark 2 | var canvas = TS(document.getElementById('canvas')), 3 | kittenTexture = null, 4 | fpsMeter = new FPSMeter({ 5 | graph: 1, 6 | heat: 1, 7 | theme: 'dark', 8 | interval: 50 9 | }), 10 | gl = canvas.g, 11 | kittenImage = new Image(), 12 | gravity = 0.5, 13 | random = Math.random, 14 | maxX = canvas.c.width, 15 | minX = 0, 16 | maxY = canvas.c.height, 17 | minY = 0, 18 | add = false, 19 | startBunnyCount = 2, 20 | count = 0, 21 | amount = 100, 22 | kittens = [], 23 | counter = document.getElementById('kitten-count'), 24 | frames = [ 25 | [0, 0, 32, 32], 26 | [0, 32, 32, 32], 27 | [0, 64, 32, 32], 28 | [0, 96, 32, 32] 29 | ], 30 | currentFrame = 0; 31 | 32 | function Sprite(x, y, texture, frameX, frameY, frameW, frameH) { 33 | this.positionX = x; 34 | this.positionY = y; 35 | this.width = frameW; 36 | this.height = frameH; 37 | this.texture = texture; 38 | this.speedX = 0; 39 | this.speedY = 0; 40 | this.rotation = 0; 41 | this.u0 = frameX / texture.width; 42 | this.v0 = frameY / texture.height; 43 | this.u1 = this.u0 + (frameW / texture.width); 44 | this.v1 = this.v0 + (frameH / texture.height); 45 | this.halfWidth = frameW / 2; 46 | } 47 | 48 | function create() { 49 | var frame = frames[currentFrame]; 50 | for (var i = 0; i < startBunnyCount; i++) { 51 | var kitten = new Sprite(0, 0, kittenTexture, frame[0], frame[1], frame[2], frame[3]); 52 | kitten.speedX = Math.random() * 10; 53 | kitten.speedY = (Math.random() * 10) - 5; 54 | kittens[count++] = kitten; 55 | } 56 | counter.innerHTML = count + " KITTENS"; 57 | 58 | canvas.bkg(0.227, 0.227, 0.227); 59 | mainLoop(); 60 | } 61 | 62 | function update() { 63 | if (add) { 64 | if (count < 200000) { 65 | var frame = frames[currentFrame]; 66 | for (var i = 0; i < amount; i++) { 67 | var kitten = new Sprite(0, 0, kittenTexture, frame[0], frame[1], frame[2], frame[3]); 68 | kitten.speedX = Math.random() * 10; 69 | kitten.speedY = (Math.random() * 10) - 5; 70 | kitten.rotation = (Math.random() - 0.5); 71 | kittens[count++] = kitten; 72 | } 73 | counter.innerHTML = count + " KITTENS"; 74 | } 75 | } 76 | for (var i = 0; i < count; i++) { 77 | var kitten = kittens[i]; 78 | kitten.positionX += kitten.speedX; 79 | kitten.positionY += kitten.speedY; 80 | kitten.speedY += gravity; 81 | 82 | if (kitten.positionX > maxX) { 83 | kitten.speedX *= -1; 84 | kitten.positionX = maxX; 85 | } else if (kitten.positionX < minX) { 86 | kitten.speedX *= -1; 87 | kitten.positionX = minX; 88 | } 89 | if (kitten.positionY > maxY) { 90 | kitten.speedY *= -0.85; 91 | kitten.positionY = maxY; 92 | kitten.spin = (random() - 0.5) * 0.2 93 | if (random() > 0.5) { 94 | kitten.speedY -= random() * 6; 95 | } 96 | } else if (kitten.positionY < minY) { 97 | kitten.speedY = 0; 98 | kitten.positionY = minY; 99 | } 100 | } 101 | } 102 | 103 | function draw() { 104 | canvas.cls(); 105 | for (var i = 0; i < count; i++) { 106 | var kitten = kittens[i]; 107 | //exture, x, y, w, h, r, tx, ty, sx, sy, u0, v0, u1, v1 108 | canvas.img( 109 | // Texture 110 | kitten.texture, 111 | // Position X 112 | -kitten.halfWidth, 113 | // Position Y 114 | 0, 115 | // Width 116 | kitten.width, 117 | // Height 118 | kitten.height, 119 | // Rotation 120 | kitten.rotation, 121 | // Translation X 122 | kitten.positionX, 123 | // Translation Y 124 | kitten.positionY, 125 | // Scale X 126 | 1, 127 | // Scale Y 128 | 1, 129 | // UV0 130 | kitten.u0, 131 | kitten.v0, 132 | // UV1 133 | kitten.u1, 134 | kitten.v1 135 | ); 136 | } 137 | canvas.flush(); 138 | } 139 | 140 | function mainLoop() { 141 | requestAnimationFrame(mainLoop); 142 | fpsMeter.tick(); 143 | update(); 144 | draw(); 145 | } 146 | 147 | canvas.c.onmousedown = function (evt) { 148 | add = true; 149 | currentFrame = (currentFrame + 1) % frames.length; 150 | }; 151 | canvas.c.onmouseup = function (evt) { 152 | add = false; 153 | }; 154 | canvas.c.ontouchstart = function (evt) { 155 | add = true; 156 | currentFrame = (currentFrame + 1) % frames.length; 157 | }; 158 | canvas.c.ontouchend = function (evt) { 159 | add = false; 160 | }; 161 | kittenImage.onload = function () { 162 | kittenTexture = TCTex(gl, kittenImage, kittenImage.width, kittenImage.height); 163 | create(); 164 | }; 165 | kittenImage.src = 'data:image/png;base64,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'; -------------------------------------------------------------------------------- /example/fpsmeter.js: -------------------------------------------------------------------------------- 1 | /*! FPSMeter 0.3.1 - 9th May 2013 | https://github.com/Darsain/fpsmeter */ 2 | (function (m, j) { 3 | function s(a, e) { 4 | for (var g in e) try { 5 | a.style[g] = e[g] 6 | } catch (j) {} 7 | return a 8 | } 9 | 10 | function H(a) { 11 | return null == a ? String(a) : "object" === typeof a || "function" === typeof a ? Object.prototype.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase() || "object" : typeof a 12 | } 13 | 14 | function R(a, e) { 15 | if ("array" !== H(e)) return -1; 16 | if (e.indexOf) return e.indexOf(a); 17 | for (var g = 0, j = e.length; g < j; g++) 18 | if (e[g] === a) return g; 19 | return -1 20 | } 21 | 22 | function I() { 23 | var a = arguments, 24 | e; 25 | for (e in a[1]) 26 | if (a[1].hasOwnProperty(e)) switch (H(a[1][e])) { 27 | case "object": 28 | a[0][e] = 29 | I({}, a[0][e], a[1][e]); 30 | break; 31 | case "array": 32 | a[0][e] = a[1][e].slice(0); 33 | break; 34 | default: 35 | a[0][e] = a[1][e] 36 | } 37 | return 2 < a.length ? I.apply(null, [a[0]].concat(Array.prototype.slice.call(a, 2))) : a[0] 38 | } 39 | 40 | function N(a) { 41 | a = Math.round(255 * a).toString(16); 42 | return 1 === a.length ? "0" + a : a 43 | } 44 | 45 | function S(a, e, g, j) { 46 | if (a.addEventListener) a[j ? "removeEventListener" : "addEventListener"](e, g, !1); 47 | else if (a.attachEvent) a[j ? "detachEvent" : "attachEvent"]("on" + e, g) 48 | } 49 | 50 | function D(a, e) { 51 | function g(a, b, d, c) { 52 | return y[0 | a][Math.round(Math.min((b - d) / (c - d) * J, J))] 53 | } 54 | 55 | function r() { 56 | f.legend.fps !== q && (f.legend.fps = q, f.legend[T] = q ? "FPS" : "ms"); 57 | K = q ? b.fps : b.duration; 58 | f.count[T] = 999 < K ? "999+" : K.toFixed(99 < K ? 0 : d.decimals) 59 | } 60 | 61 | function m() { 62 | z = A(); 63 | L < z - d.threshold && (b.fps -= b.fps / Math.max(1, 60 * d.smoothing / d.interval), b.duration = 1E3 / b.fps); 64 | for (c = d.history; c--;) E[c] = 0 === c ? b.fps : E[c - 1], F[c] = 0 === c ? b.duration : F[c - 1]; 65 | r(); 66 | if (d.heat) { 67 | if (w.length) 68 | for (c = w.length; c--;) w[c].el.style[h[w[c].name].heatOn] = q ? g(h[w[c].name].heatmap, b.fps, 0, d.maxFps) : g(h[w[c].name].heatmap, b.duration, d.threshold, 69 | 0); 70 | if (f.graph && h.column.heatOn) 71 | for (c = u.length; c--;) u[c].style[h.column.heatOn] = q ? g(h.column.heatmap, E[c], 0, d.maxFps) : g(h.column.heatmap, F[c], d.threshold, 0) 72 | } 73 | if (f.graph) 74 | for (p = 0; p < d.history; p++) u[p].style.height = (q ? E[p] ? Math.round(O / d.maxFps * Math.min(E[p], d.maxFps)) : 0 : F[p] ? Math.round(O / d.threshold * Math.min(F[p], d.threshold)) : 0) + "px" 75 | } 76 | 77 | function k() { 78 | 20 > d.interval ? (x = M(k), m()) : (x = setTimeout(k, d.interval), P = M(m)) 79 | } 80 | 81 | function G(a) { 82 | a = a || window.event; 83 | a.preventDefault ? (a.preventDefault(), a.stopPropagation()) : (a.returnValue = !1, a.cancelBubble = !0); 84 | b.toggle() 85 | } 86 | 87 | function U() { 88 | d.toggleOn && S(f.container, d.toggleOn, G, 1); 89 | a.removeChild(f.container) 90 | } 91 | 92 | function V() { 93 | f.container && U(); 94 | h = D.theme[d.theme]; 95 | y = h.compiledHeatmaps || []; 96 | if (!y.length && h.heatmaps.length) { 97 | for (p = 0; p < h.heatmaps.length; p++) { 98 | y[p] = []; 99 | for (c = 0; c <= J; c++) { 100 | var b = y[p], 101 | e = c, 102 | g; 103 | g = 0.33 / J * c; 104 | var j = h.heatmaps[p].saturation, 105 | m = h.heatmaps[p].lightness, 106 | n = void 0, 107 | k = void 0, 108 | l = void 0, 109 | t = l = void 0, 110 | v = n = k = void 0, 111 | v = void 0, 112 | l = 0.5 >= m ? m * (1 + j) : m + j - m * j; 113 | 0 === l ? g = "#000" : (t = 2 * m - l, k = (l - t) / l, g *= 6, n = Math.floor(g), 114 | v = g - n, v *= l * k, 0 === n || 6 === n ? (n = l, k = t + v, l = t) : 1 === n ? (n = l - v, k = l, l = t) : 2 === n ? (n = t, k = l, l = t + v) : 3 === n ? (n = t, k = l - v) : 4 === n ? (n = t + v, k = t) : (n = l, k = t, l -= v), g = "#" + N(n) + N(k) + N(l)); 115 | b[e] = g 116 | } 117 | } 118 | h.compiledHeatmaps = y 119 | } 120 | f.container = s(document.createElement("div"), h.container); 121 | f.count = f.container.appendChild(s(document.createElement("div"), h.count)); 122 | f.legend = f.container.appendChild(s(document.createElement("div"), h.legend)); 123 | f.graph = d.graph ? f.container.appendChild(s(document.createElement("div"), h.graph)) : 0; 124 | w.length = 0; 125 | for (var q in f) f[q] && 126 | h[q].heatOn && w.push({ 127 | name: q, 128 | el: f[q] 129 | }); 130 | u.length = 0; 131 | if (f.graph) { 132 | f.graph.style.width = d.history * h.column.width + (d.history - 1) * h.column.spacing + "px"; 133 | for (c = 0; c < d.history; c++) u[c] = f.graph.appendChild(s(document.createElement("div"), h.column)), u[c].style.position = "absolute", u[c].style.bottom = 0, u[c].style.right = c * h.column.width + c * h.column.spacing + "px", u[c].style.width = h.column.width + "px", u[c].style.height = "0px" 134 | } 135 | s(f.container, d); 136 | r(); 137 | a.appendChild(f.container); 138 | f.graph && (O = f.graph.clientHeight); 139 | d.toggleOn && ("click" === 140 | d.toggleOn && (f.container.style.cursor = "pointer"), S(f.container, d.toggleOn, G)) 141 | } 142 | "object" === H(a) && a.nodeType === j && (e = a, a = document.body); 143 | a || (a = document.body); 144 | var b = this, 145 | d = I({}, D.defaults, e || {}), 146 | f = {}, 147 | u = [], 148 | h, y, J = 100, 149 | w = [], 150 | W = 0, 151 | B = d.threshold, 152 | Q = 0, 153 | L = A() - B, 154 | z, E = [], 155 | F = [], 156 | x, P, q = "fps" === d.show, 157 | O, K, c, p; 158 | b.options = d; 159 | b.fps = 0; 160 | b.duration = 0; 161 | b.isPaused = 0; 162 | b.tickStart = function () { 163 | Q = A() 164 | }; 165 | b.tick = function () { 166 | z = A(); 167 | W = z - L; 168 | B += (W - B) / d.smoothing; 169 | b.fps = 1E3 / B; 170 | b.duration = Q < L ? B : z - Q; 171 | L = z 172 | }; 173 | b.pause = function () { 174 | x && (b.isPaused = 1, clearTimeout(x), 175 | C(x), C(P), x = P = 0); 176 | return b 177 | }; 178 | b.resume = function () { 179 | x || (b.isPaused = 0, k()); 180 | return b 181 | }; 182 | b.set = function (a, c) { 183 | d[a] = c; 184 | q = "fps" === d.show; - 1 !== R(a, X) && V(); - 1 !== R(a, Y) && s(f.container, d); 185 | return b 186 | }; 187 | b.showDuration = function () { 188 | b.set("show", "ms"); 189 | return b 190 | }; 191 | b.showFps = function () { 192 | b.set("show", "fps"); 193 | return b 194 | }; 195 | b.toggle = function () { 196 | b.set("show", q ? "ms" : "fps"); 197 | return b 198 | }; 199 | b.hide = function () { 200 | b.pause(); 201 | f.container.style.display = "none"; 202 | return b 203 | }; 204 | b.show = function () { 205 | b.resume(); 206 | f.container.style.display = "block"; 207 | return b 208 | }; 209 | b.destroy = function () { 210 | b.pause(); 211 | U(); 212 | b.tick = b.tickStart = function () {} 213 | }; 214 | V(); 215 | k() 216 | } 217 | var A, r = m.performance; 218 | A = r && (r.now || r.webkitNow) ? r[r.now ? "now" : "webkitNow"].bind(r) : function () { 219 | return +new Date 220 | }; 221 | for (var C = m.cancelAnimationFrame || m.cancelRequestAnimationFrame, M = m.requestAnimationFrame, r = ["moz", "webkit", "o"], G = 0, k = 0, Z = r.length; k < Z && !C; ++k) M = (C = m[r[k] + "CancelAnimationFrame"] || m[r[k] + "CancelRequestAnimationFrame"]) && m[r[k] + "RequestAnimationFrame"]; 222 | C || (M = function (a) { 223 | var e = A(), 224 | g = Math.max(0, 16 - (e - G)); 225 | G = e + g; 226 | return m.setTimeout(function () { 227 | a(e + 228 | g) 229 | }, g) 230 | }, C = function (a) { 231 | clearTimeout(a) 232 | }); 233 | var T = "string" === H(document.createElement("div").textContent) ? "textContent" : "innerText"; 234 | D.extend = I; 235 | window.FPSMeter = D; 236 | D.defaults = { 237 | interval: 100, 238 | smoothing: 10, 239 | show: "fps", 240 | toggleOn: "click", 241 | decimals: 1, 242 | maxFps: 60, 243 | threshold: 100, 244 | position: "absolute", 245 | zIndex: 10, 246 | left: "5px", 247 | top: "5px", 248 | right: "auto", 249 | bottom: "auto", 250 | margin: "0 0 0 0", 251 | theme: "dark", 252 | heat: 0, 253 | graph: 0, 254 | history: 20 255 | }; 256 | var X = ["toggleOn", "theme", "heat", "graph", "history"], 257 | Y = "position zIndex left top right bottom margin".split(" ") 258 | })(window); 259 | (function (m, j) { 260 | j.theme = {}; 261 | var s = j.theme.base = { 262 | heatmaps: [], 263 | container: { 264 | heatOn: null, 265 | heatmap: null, 266 | padding: "5px", 267 | minWidth: "95px", 268 | height: "30px", 269 | lineHeight: "30px", 270 | textAlign: "right", 271 | textShadow: "none" 272 | }, 273 | count: { 274 | heatOn: null, 275 | heatmap: null, 276 | position: "absolute", 277 | top: 0, 278 | right: 0, 279 | padding: "5px 10px", 280 | height: "30px", 281 | fontSize: "24px", 282 | fontFamily: "Consolas, Andale Mono, monospace", 283 | zIndex: 2 284 | }, 285 | legend: { 286 | heatOn: null, 287 | heatmap: null, 288 | position: "absolute", 289 | top: 0, 290 | left: 0, 291 | padding: "5px 10px", 292 | height: "30px", 293 | fontSize: "12px", 294 | lineHeight: "32px", 295 | fontFamily: "sans-serif", 296 | textAlign: "left", 297 | zIndex: 2 298 | }, 299 | graph: { 300 | heatOn: null, 301 | heatmap: null, 302 | position: "relative", 303 | boxSizing: "padding-box", 304 | MozBoxSizing: "padding-box", 305 | height: "100%", 306 | zIndex: 1 307 | }, 308 | column: { 309 | width: 4, 310 | spacing: 1, 311 | heatOn: null, 312 | heatmap: null 313 | } 314 | }; 315 | j.theme.dark = j.extend({}, s, { 316 | heatmaps: [{ 317 | saturation: 0.8, 318 | lightness: 0.8 319 | }], 320 | container: { 321 | background: "#222", 322 | color: "#fff", 323 | border: "1px solid #1a1a1a", 324 | textShadow: "1px 1px 0 #222" 325 | }, 326 | count: { 327 | heatOn: "color" 328 | }, 329 | column: { 330 | background: "#3f3f3f" 331 | } 332 | }); 333 | j.theme.light = j.extend({}, s, { 334 | heatmaps: [{ 335 | saturation: 0.5, 336 | lightness: 0.5 337 | }], 338 | container: { 339 | color: "#666", 340 | background: "#fff", 341 | textShadow: "1px 1px 0 rgba(255,255,255,.5), -1px -1px 0 rgba(255,255,255,.5)", 342 | boxShadow: "0 0 0 1px rgba(0,0,0,.1)" 343 | }, 344 | count: { 345 | heatOn: "color" 346 | }, 347 | column: { 348 | background: "#eaeaea" 349 | } 350 | }); 351 | j.theme.colorful = j.extend({}, s, { 352 | heatmaps: [{ 353 | saturation: 0.5, 354 | lightness: 0.6 355 | }], 356 | container: { 357 | heatOn: "backgroundColor", 358 | background: "#888", 359 | color: "#fff", 360 | textShadow: "1px 1px 0 rgba(0,0,0,.2)", 361 | boxShadow: "0 0 0 1px rgba(0,0,0,.1)" 362 | }, 363 | column: { 364 | background: "#777", 365 | backgroundColor: "rgba(0,0,0,.2)" 366 | } 367 | }); 368 | j.theme.transparent = 369 | j.extend({}, s, { 370 | heatmaps: [{ 371 | saturation: 0.8, 372 | lightness: 0.5 373 | }], 374 | container: { 375 | padding: 0, 376 | color: "#fff", 377 | textShadow: "1px 1px 0 rgba(0,0,0,.5)" 378 | }, 379 | count: { 380 | padding: "0 5px", 381 | height: "40px", 382 | lineHeight: "40px" 383 | }, 384 | legend: { 385 | padding: "0 5px", 386 | height: "40px", 387 | lineHeight: "42px" 388 | }, 389 | graph: { 390 | height: "40px" 391 | }, 392 | column: { 393 | width: 5, 394 | background: "#999", 395 | heatOn: "backgroundColor", 396 | opacity: 0.5 397 | } 398 | }) 399 | })(window, FPSMeter); 400 | 401 | -------------------------------------------------------------------------------- /example/index_canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tiny-canvas kittenmark 5 | 6 | 28 | 29 | 30 | 31 | 0 KITTENS 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/index_sprite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tiny-sprite kittenmark 5 | 6 | 28 | 29 | 30 | 31 | 0 KITTENS 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tiny-canvas", 3 | "version": "0.0.1", 4 | "author": { 5 | "name": "Felipe Alfonso", 6 | "github-handle": "bitnenfer" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/bitnenfer/tiny-canvas" 11 | }, 12 | "devDependencies": { 13 | "grunt": "~0.4.1", 14 | "grunt-contrib-concat": "~0.3.0", 15 | "grunt-contrib-clean": "~0.4.1", 16 | "grunt-contrib-compress": "~0.10.0", 17 | "grunt-closure-compiler": "~0.0.21", 18 | "matchdep": "~0.1.2" 19 | }, 20 | "engine": "node 0.8.12", 21 | "scripts": { 22 | "install": "bower install;grunt", 23 | "release": "grunt release" 24 | } 25 | } -------------------------------------------------------------------------------- /src/canvas.js: -------------------------------------------------------------------------------- 1 | function TinyCanvas(canvas) { 2 | var gl = canvas.getContext('webgl'), 3 | VERTEX_SIZE = (4 * 2) + (4 * 2) + (4), 4 | MAX_BATCH = 10922, // floor((2 ^ 16) / 6) 5 | MAX_STACK = 100, 6 | MAT_SIZE = 6, 7 | VERTICES_PER_QUAD = 6, 8 | MAT_STACK_SIZE = MAX_STACK * MAT_SIZE, 9 | VERTEX_DATA_SIZE = VERTEX_SIZE * MAX_BATCH * 4, 10 | INDEX_DATA_SIZE = MAX_BATCH * (2 * VERTICES_PER_QUAD), 11 | width = canvas.width, 12 | height = canvas.height, 13 | shader = CreateShaderProgram( 14 | gl, [ 15 | 'precision lowp float;', 16 | // IN Vertex Position and 17 | // IN Texture Coordinates 18 | 'attribute vec2 a, b;', 19 | // IN Vertex Color 20 | 'attribute vec4 c;', 21 | // OUT Texture Coordinates 22 | 'varying vec2 d;', 23 | // OUT Vertex Color 24 | 'varying vec4 e;', 25 | // CONST View Matrix 26 | 'uniform mat4 m;', 27 | 'uniform vec2 r;', 28 | 'void main(){', 29 | 'gl_Position=m*vec4(a,1.0,1.0);', 30 | 'd=b;', 31 | 'e=c;', 32 | '}' 33 | ].join('\n'), [ 34 | 'precision lowp float;', 35 | // OUT Texture Coordinates 36 | 'varying vec2 d;', 37 | // OUT Vertex Color 38 | 'varying vec4 e;', 39 | // CONST Single Sampler2D 40 | 'uniform sampler2D f;', 41 | 'void main(){', 42 | 'gl_FragColor=texture2D(f,d)*e;', 43 | '}' 44 | ].join('\n') 45 | ), 46 | glBufferSubData = gl.bufferSubData.bind(gl), 47 | glDrawElements = gl.drawElements.bind(gl), 48 | glBindTexture = gl.bindTexture.bind(gl), 49 | glClear = gl.clear.bind(gl), 50 | glClearColor = gl.clearColor.bind(gl), 51 | vertexData = new ArrayBuffer(VERTEX_DATA_SIZE), 52 | vPositionData = new Float32Array(vertexData), 53 | vColorData = new Uint32Array(vertexData), 54 | vIndexData = new Uint16Array(INDEX_DATA_SIZE), 55 | IBO = CreateBuffer(gl, 34963, vIndexData.byteLength, 35044), 56 | VBO = CreateBuffer(gl, 34962, vertexData.byteLength, 35048), 57 | count = 0, 58 | mat = new Float32Array([1, 0, 0, 1, 0, 0]), 59 | stack = new Float32Array(100), 60 | stackp = 0, 61 | cos = Math.cos, 62 | sin = Math.sin, 63 | currentTexture = null, 64 | renderer = null, 65 | locA, locB, locC; 66 | 67 | gl.blendFunc(770, 771); 68 | gl.enable(3042); 69 | gl.useProgram(shader); 70 | gl.bindBuffer(34963, IBO); 71 | for (var indexA = indexB = 0; indexA < MAX_BATCH * VERTICES_PER_QUAD; indexA += VERTICES_PER_QUAD, indexB += 4) 72 | vIndexData[indexA + 0] = indexB, 73 | vIndexData[indexA + 1] = indexB + 1, 74 | vIndexData[indexA + 2] = indexB + 2, 75 | vIndexData[indexA + 3] = indexB + 0, 76 | vIndexData[indexA + 4] = indexB + 3, 77 | vIndexData[indexA + 5] = indexB + 1; 78 | 79 | glBufferSubData(34963, 0, vIndexData); 80 | gl.bindBuffer(34962, VBO); 81 | locA = gl.getAttribLocation(shader, 'a'); 82 | locB = gl.getAttribLocation(shader, 'b'); 83 | locC = gl.getAttribLocation(shader, 'c'); 84 | gl.enableVertexAttribArray(locA); 85 | gl.vertexAttribPointer(locA, 2, 5126, 0, VERTEX_SIZE, 0); 86 | gl.enableVertexAttribArray(locB); 87 | gl.vertexAttribPointer(locB, 2, 5126, 0, VERTEX_SIZE, 8); 88 | gl.enableVertexAttribArray(locC); 89 | gl.vertexAttribPointer(locC, 4, 5121, 1, VERTEX_SIZE, 16); 90 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, 'm'), 0, 91 | new Float32Array([ 92 | 2 / width, 0, 0, 0, 93 | 0, -2 / height, 0, 0, 94 | 0, 0, 1, 1, -1, 1, 0, 0 95 | ]) 96 | ); 97 | gl.activeTexture(33984); 98 | renderer = { 99 | 'g': gl, 100 | 'c': canvas, 101 | 'col': 0xFFFFFFFF, 102 | 'bkg': function (r, g, b) { 103 | glClearColor(r, g, b, 1); 104 | }, 105 | 'cls': function () { 106 | glClear(16384); 107 | }, 108 | 'trans': function (x, y) { 109 | mat[4] = mat[0] * x + mat[2] * y + mat[4]; 110 | mat[5] = mat[1] * x + mat[3] * y + mat[5]; 111 | }, 112 | 'scale': function (x, y) { 113 | mat[0] = mat[0] * x; 114 | mat[1] = mat[1] * x; 115 | mat[2] = mat[2] * y; 116 | mat[3] = mat[3] * y; 117 | }, 118 | 'rot': function (r) { 119 | var a = mat[0], 120 | b = mat[1], 121 | c = mat[2], 122 | d = mat[3], 123 | sr = sin(r), 124 | cr = cos(r); 125 | 126 | mat[0] = a * cr + c * sr; 127 | mat[1] = b * cr + d * sr; 128 | mat[2] = a * -sr + c * cr; 129 | mat[3] = b * -sr + d * cr; 130 | }, 131 | 'push': function () { 132 | stack[stackp + 0] = mat[0]; 133 | stack[stackp + 1] = mat[1]; 134 | stack[stackp + 2] = mat[2]; 135 | stack[stackp + 3] = mat[3]; 136 | stack[stackp + 4] = mat[4]; 137 | stack[stackp + 5] = mat[5]; 138 | stackp += 6; 139 | }, 140 | 'pop': function () { 141 | stackp -= 6; 142 | mat[0] = stack[stackp + 0]; 143 | mat[1] = stack[stackp + 1]; 144 | mat[2] = stack[stackp + 2]; 145 | mat[3] = stack[stackp + 3]; 146 | mat[4] = stack[stackp + 4]; 147 | mat[5] = stack[stackp + 5]; 148 | }, 149 | 'img': function (texture, x, y, w, h, u0, v0, u1, v1) { 150 | var x0 = x, 151 | y0 = y, 152 | x1 = x + w, 153 | y1 = y + h, 154 | x2 = x, 155 | y2 = y + h, 156 | x3 = x + w, 157 | y3 = y, 158 | a = mat[0], 159 | b = mat[1], 160 | c = mat[2], 161 | d = mat[3], 162 | e = mat[4], 163 | f = mat[5], 164 | offset = 0, 165 | argb = renderer['col']; 166 | 167 | if (texture != currentTexture || 168 | count + 1 >= MAX_BATCH) { 169 | glBufferSubData(34962, 0, vertexData); 170 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 171 | count = 0; 172 | if (currentTexture != texture) { 173 | currentTexture = texture; 174 | glBindTexture(3553, currentTexture); 175 | } 176 | } 177 | 178 | offset = count * VERTEX_SIZE; 179 | // Vertex Order 180 | // Vertex Position | UV | ARGB 181 | // Vertex 1 182 | vPositionData[offset++] = x0 * a + y0 * c + e; 183 | vPositionData[offset++] = x0 * b + y0 * d + f; 184 | vPositionData[offset++] = u0; 185 | vPositionData[offset++] = v0; 186 | vColorData[offset++] = argb; 187 | 188 | // Vertex 2 189 | vPositionData[offset++] = x1 * a + y1 * c + e; 190 | vPositionData[offset++] = x1 * b + y1 * d + f; 191 | vPositionData[offset++] = u1; 192 | vPositionData[offset++] = v1; 193 | vColorData[offset++] = argb; 194 | 195 | // Vertex 3 196 | vPositionData[offset++] = x2 * a + y2 * c + e; 197 | vPositionData[offset++] = x2 * b + y2 * d + f; 198 | vPositionData[offset++] = u0; 199 | vPositionData[offset++] = v1; 200 | vColorData[offset++] = argb; 201 | 202 | // Vertex 4 203 | vPositionData[offset++] = x3 * a + y3 * c + e; 204 | vPositionData[offset++] = x3 * b + y3 * d + f; 205 | vPositionData[offset++] = u1; 206 | vPositionData[offset++] = v0; 207 | vColorData[offset++] = argb; 208 | 209 | if (++count >= MAX_BATCH) { 210 | glBufferSubData(34962, 0, vertexData); 211 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 212 | count = 0; 213 | } 214 | }, 215 | 'flush': function () { 216 | if (count == 0) return; 217 | glBufferSubData(34962, 0, vPositionData.subarray(0, count * VERTEX_SIZE)); 218 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 219 | count = 0; 220 | } 221 | }; 222 | return renderer; 223 | } 224 | window['TC'] = TinyCanvas; -------------------------------------------------------------------------------- /src/header-tc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TinyCanvas module (https://github.com/bitnenfer/tiny-canvas) 3 | * Developed by Felipe Alfonso -> https://twitter.com/bitnenfer/ 4 | * 5 | * ---------------------------------------------------------------------- 6 | * 7 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | * Version 2, December 2004 9 | * 10 | * Copyright (C) 2004 Sam Hocevar 11 | * 12 | * Everyone is permitted to copy and distribute verbatim or modified 13 | * copies of this license document, and changing it is allowed as long 14 | * as the name is changed. 15 | * 16 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 17 | * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 18 | * 19 | * 0. You just DO WHAT THE FUCK YOU WANT TO. 20 | * 21 | * ---------------------------------------------------------------------- 22 | * 23 | */ 24 | -------------------------------------------------------------------------------- /src/header-ts.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TinySprite module (https://github.com/bitnenfer/tiny-canvas) 3 | * Developed by Felipe Alfonso -> https://twitter.com/bitnenfer/ 4 | * 5 | * ---------------------------------------------------------------------- 6 | * 7 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | * Version 2, December 2004 9 | * 10 | * Copyright (C) 2004 Sam Hocevar 11 | * 12 | * Everyone is permitted to copy and distribute verbatim or modified 13 | * copies of this license document, and changing it is allowed as long 14 | * as the name is changed. 15 | * 16 | * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 17 | * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 18 | * 19 | * 0. You just DO WHAT THE FUCK YOU WANT TO. 20 | * 21 | * ---------------------------------------------------------------------- 22 | * 23 | */ -------------------------------------------------------------------------------- /src/sprite.js: -------------------------------------------------------------------------------- 1 | function TinySprite(canvas) { 2 | var gl = canvas.getContext('webgl'), 3 | // float + (vec2 * 4) + (char * 4) 4 | VERTEX_SIZE = 4 + ((4 * 2) * 4) + (4), 5 | MAX_BATCH = 10922, // floor((2 ^ 16) / 6) 6 | VERTEX_DATA_SIZE = VERTEX_SIZE * MAX_BATCH * 4, 7 | VERTICES_PER_QUAD = 6, 8 | INDEX_DATA_SIZE = MAX_BATCH * (2 * VERTICES_PER_QUAD), 9 | width = canvas.width, 10 | height = canvas.height, 11 | shader = CreateShaderProgram( 12 | gl, [ 13 | 'precision lowp float;', 14 | 'attribute float a;', 15 | 'attribute vec2 b,c,d,e;', 16 | 'attribute vec4 f;', 17 | 'varying vec2 g;', 18 | 'varying vec4 h;', 19 | 'uniform mat4 i;', 20 | 'void main() {', 21 | 'float q=cos(a);', 22 | 'float w=sin(a);', 23 | 'gl_Position=i*vec4(((vec2(d.x*q-d.y*w,d.x*w+d.y*q)*c)+b),1.0,1.0);', 24 | 'g=e;', 25 | 'h=f;', 26 | '}' 27 | ].join('\n'), [ 28 | 'precision lowp float;', 29 | 'varying vec2 g;', 30 | 'varying vec4 h;', 31 | 'uniform sampler2D j;', 32 | 'void main(){', 33 | 'gl_FragColor=texture2D(j,g)*h;', 34 | '}' 35 | ].join('\n') 36 | ), 37 | glBufferSubData = gl.bufferSubData.bind(gl), 38 | glDrawElements = gl.drawElements.bind(gl), 39 | glBindTexture = gl.bindTexture.bind(gl), 40 | glClear = gl.clear.bind(gl), 41 | glClearColor = gl.clearColor.bind(gl), 42 | vertexData = new ArrayBuffer(VERTEX_DATA_SIZE), 43 | vPositionData = new Float32Array(vertexData), 44 | vColorData = new Uint32Array(vertexData), 45 | vIndexData = new Uint16Array(INDEX_DATA_SIZE), 46 | IBO = CreateBuffer(gl, 34963, vIndexData.byteLength, 35044), 47 | VBO = CreateBuffer(gl, 34962, vertexData.byteLength, 35048), 48 | count = 0, 49 | currentTexture = null, 50 | renderer = null, 51 | locRotation, locTranslation, locScale, 52 | locPosition, locUV, locColor; 53 | 54 | gl.blendFunc(770, 771); 55 | gl.enable(3042); 56 | gl.useProgram(shader); 57 | gl.bindBuffer(34963, IBO); 58 | for (var indexA = indexB = 0; 59 | indexA < MAX_BATCH * VERTICES_PER_QUAD; 60 | indexA += VERTICES_PER_QUAD, indexB += 4) 61 | vIndexData[indexA + 0] = indexB, 62 | vIndexData[indexA + 1] = indexB + 1, 63 | vIndexData[indexA + 2] = indexB + 2, 64 | vIndexData[indexA + 3] = indexB + 0, 65 | vIndexData[indexA + 4] = indexB + 3, 66 | vIndexData[indexA + 5] = indexB + 1; 67 | 68 | gl.bufferSubData(34963, 0, vIndexData); 69 | gl.bindBuffer(34962, VBO); 70 | 71 | locRotation = gl.getAttribLocation(shader, 'a'); 72 | locTranslation = gl.getAttribLocation(shader, 'b'); 73 | locScale = gl.getAttribLocation(shader, 'c'); 74 | locPosition = gl.getAttribLocation(shader, 'd'); 75 | locUV = gl.getAttribLocation(shader, 'e'); 76 | locColor = gl.getAttribLocation(shader, 'f'); 77 | 78 | // Rotation 79 | gl.enableVertexAttribArray(locRotation); 80 | gl.vertexAttribPointer(locRotation, 1, 5126, 0, VERTEX_SIZE, 0); 81 | 82 | // Translation 83 | gl.enableVertexAttribArray(locTranslation); 84 | gl.vertexAttribPointer(locTranslation, 2, 5126, 0, VERTEX_SIZE, 4); 85 | 86 | // Scale 87 | gl.enableVertexAttribArray(locScale); 88 | gl.vertexAttribPointer(locScale, 2, 5126, 0, VERTEX_SIZE, 12); 89 | 90 | // Position 91 | gl.enableVertexAttribArray(locPosition); 92 | gl.vertexAttribPointer(locPosition, 2, 5126, 0, VERTEX_SIZE, 20); 93 | 94 | // UV 95 | gl.enableVertexAttribArray(locUV); 96 | gl.vertexAttribPointer(locUV, 2, 5126, 0, VERTEX_SIZE, 28); 97 | 98 | // Color 99 | gl.enableVertexAttribArray(locColor); 100 | gl.vertexAttribPointer(locColor, 4, 5121, 1, VERTEX_SIZE, 36); 101 | 102 | gl.uniformMatrix4fv(gl.getUniformLocation(shader, 'i'), 0, 103 | new Float32Array([ 104 | 2 / width, 0, 0, 0, 105 | 0, -2 / height, 0, 0, 106 | 0, 0, 1, 1, 107 | -1, 1, 0, 0 108 | ]) 109 | ); 110 | gl.activeTexture(33984); 111 | renderer = { 112 | 'g': gl, 113 | 'c': canvas, 114 | 'col': 0xFFFFFFFF, 115 | 'bkg': function (r, g, b) { 116 | gl.clearColor(r, g, b, 1); 117 | }, 118 | 'cls': function () { 119 | gl.clear(16384); 120 | }, 121 | 'img': function (texture, x, y, w, h, r, tx, ty, sx, sy, u0, v0, u1, v1) { 122 | var x0 = x, 123 | y0 = y, 124 | x1 = x + w, 125 | y1 = y + h, 126 | x2 = x, 127 | y2 = y + h, 128 | x3 = x + w, 129 | y3 = y, 130 | offset = 0, 131 | argb = renderer['col']; 132 | 133 | if (texture != currentTexture || 134 | count + 1 >= MAX_BATCH) { 135 | glBufferSubData(34962, 0, vertexData); 136 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 137 | count = 0; 138 | if (texture != currentTexture) { 139 | currentTexture = texture; 140 | glBindTexture(3553, currentTexture); 141 | } 142 | } 143 | 144 | offset = count * VERTEX_SIZE; 145 | // Vertex Order: 146 | // rotation | translation | scale | position | uv | color 147 | // Vertex 1 148 | vPositionData[offset++] = r; 149 | vPositionData[offset++] = tx; 150 | vPositionData[offset++] = ty; 151 | vPositionData[offset++] = sx; 152 | vPositionData[offset++] = sy; 153 | vPositionData[offset++] = x0; 154 | vPositionData[offset++] = y0; 155 | vPositionData[offset++] = u0; 156 | vPositionData[offset++] = v0; 157 | vColorData[offset++] = argb; 158 | 159 | // Vertex 2 160 | vPositionData[offset++] = r; 161 | vPositionData[offset++] = tx; 162 | vPositionData[offset++] = ty; 163 | vPositionData[offset++] = sx; 164 | vPositionData[offset++] = sy; 165 | vPositionData[offset++] = x1; 166 | vPositionData[offset++] = y1; 167 | vPositionData[offset++] = u1; 168 | vPositionData[offset++] = v1; 169 | vColorData[offset++] = argb; 170 | 171 | // Vertex 3 172 | vPositionData[offset++] = r; 173 | vPositionData[offset++] = tx; 174 | vPositionData[offset++] = ty; 175 | vPositionData[offset++] = sx; 176 | vPositionData[offset++] = sy; 177 | vPositionData[offset++] = x2; 178 | vPositionData[offset++] = y2; 179 | vPositionData[offset++] = u0; 180 | vPositionData[offset++] = v1; 181 | vColorData[offset++] = argb; 182 | 183 | // Vertex 4 184 | vPositionData[offset++] = r; 185 | vPositionData[offset++] = tx; 186 | vPositionData[offset++] = ty; 187 | vPositionData[offset++] = sx; 188 | vPositionData[offset++] = sy; 189 | vPositionData[offset++] = x3; 190 | vPositionData[offset++] = y3; 191 | vPositionData[offset++] = u1; 192 | vPositionData[offset++] = v0; 193 | vColorData[offset++] = argb; 194 | 195 | if (++count >= MAX_BATCH) { 196 | glBufferSubData(34962, 0, vertexData); 197 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 198 | count = 0; 199 | } 200 | }, 201 | 'flush': function () { 202 | if (count == 0) return; 203 | glBufferSubData(34962, 0, vPositionData.subarray(0, count * VERTEX_SIZE)); 204 | glDrawElements(4, count * VERTICES_PER_QUAD, 5123, 0); 205 | count = 0; 206 | } 207 | }; 208 | return renderer; 209 | } 210 | 211 | window['TS'] = TinySprite; -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | function CompileShader(gl, source, type) { 2 | var shader = gl.createShader(type); 3 | gl.shaderSource(shader, source); 4 | gl.compileShader(shader); 5 | return shader; 6 | } 7 | 8 | function CreateShaderProgram(gl, vsSource, fsSource) { 9 | var program = gl.createProgram(), 10 | vShader = CompileShader(gl, vsSource, 35633), 11 | fShader = CompileShader(gl, fsSource, 35632); 12 | gl.attachShader(program, vShader); 13 | gl.attachShader(program, fShader); 14 | gl.linkProgram(program); 15 | return program; 16 | } 17 | 18 | function CreateBuffer(gl, bufferType, size, usage) { 19 | var buffer = gl.createBuffer(); 20 | gl.bindBuffer(bufferType, buffer); 21 | gl.bufferData(bufferType, size, usage); 22 | return buffer; 23 | } 24 | 25 | function CreateTexture(gl, image, width, height) { 26 | var texture = gl.createTexture(); 27 | gl.bindTexture(3553, texture); 28 | gl.texParameteri(3553, 10242, 33071); 29 | gl.texParameteri(3553, 10243, 33071); 30 | gl.texParameteri(3553, 10240, 9728); 31 | gl.texParameteri(3553, 10241, 9728); 32 | gl.texImage2D(3553, 0, 6408, 6408, 5121, image); 33 | gl.bindTexture(3553, null); 34 | texture.width = width; 35 | texture.height = height; 36 | return texture; 37 | } 38 | window['TCShd'] = CompileShader; 39 | window['TCPrg'] = CreateShaderProgram; 40 | window['TCBuf'] = CreateBuffer; 41 | window['TCTex'] = CreateTexture; 42 | --------------------------------------------------------------------------------