<html>
	<body>
		<script src="Stats.js"></script>
                <p id="timing">&nbsp;</p>
		<script>

			var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame ||  window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;

			var stats = new Stats();
			document.body.appendChild( stats.getDomElement() );

                        var blocksize = 8;

			var canvas = document.createElement( 'canvas' );
			canvas.width = 1024;
			canvas.height = 512;
			document.body.appendChild( canvas );

			var canvasWidth = canvas.width;
                        var canvasHeight = canvas.height;

                        var canvasWBlocks = Math.floor((canvasWidth + blocksize-1) / blocksize);
                        var canvasHBlocks = Math.floor((canvasHeight + blocksize-1) / blocksize);

			var context = canvas.getContext( '2d' );

			var imagedata = context.getImageData( 0, 0, canvasWidth, canvasHeight );
			var data = imagedata.data;
                        var block_full = new Uint8Array(canvasWBlocks * canvasHBlocks);

			// var typedarray = new Uint8ClampedArray( data.length );
			// console.log( data instanceof Uint8ClampedArray );

			render();
			animate();

			function animate() {

				requestAnimationFrame( animate );
				render();
				stats.update();

			}

			function render() {

				// clear

				for ( var i = 3, l = data.length; i < l; i += 4 ) {

					data[ i ] = 0;

				}

                                for (var i = 0, l = block_full.length; i < l; i++) {
                                        block_full[i] = 0;
                                }

				/*
				for ( var i = 0; i < 100000; i ++ ) {

					var color = Math.random() * 0xffffff;

					drawPixel(
						Math.floor( Math.random() * canvasWidth ),
						Math.floor( Math.random() * canvasHeight ),
						color >> 16 & 255,
						color >> 8 & 255,
						color & 255
					);

				}
				*/

				/*
				for ( var i = 0; i < 100; i ++ ) {

					drawRectangle(
						Math.random() * canvasWidth,
						Math.random() * canvasHeight,
						Math.random() * canvasWidth,
						Math.random() * canvasHeight,
						Math.random() * 0xffffff
					);

				}
				*/

                                var startTime = new Date();

				for ( var i = 0; i < 1000; i ++ ) {

					drawTriangle(
						Math.random() * canvasWidth,
						Math.random() * canvasHeight,
						Math.random() * 0xffffff,
						Math.random() * canvasWidth,
						Math.random() * canvasHeight,
						Math.random() * 0xffffff,
						Math.random() * canvasWidth,
						Math.random() * canvasHeight,
						Math.random() * 0xffffff
					);


				}

                                var endTime = new Date();
                                document.getElementById("timing").innerText = (endTime - startTime) + "ms";

				context.putImageData( imagedata, 0, 0 );

			}

			// 

			function drawPixel( x, y, r, g, b ) {

				var offset = ( x + y * canvasWidth ) * 4;

				if ( data[ offset + 3 ] ) return;

				data[ offset ] = r;
				data[ offset + 1 ] = g;
				data[ offset + 2 ] = b;
				data[ offset + 3 ] = 255;

			}

			function drawRectangle( x1, y1, x2, y2, color ) {

				var r = color >> 16 & 255;
				var g = color >> 8 & 255;
				var b = color & 255;

				var xmin = Math.min( x1, x2 ) >> 0;
				var xmax = Math.max( x1, x2 ) >> 0;
				var ymin = Math.min( y1, y2 ) >> 0;
				var ymax = Math.max( y1, y2 ) >> 0;

				for ( var y = ymin; y < ymax; y ++ ) {

					for ( var x = xmin; x < xmax; x ++ ) {

						drawPixel( x, y, r, g, b );

					}

				}

			}

			function drawTriangle( x1, y1, color1, x2, y2, color2, x3, y3, color3 ) {

				// http://devmaster.net/forums/topic/1145-advanced-rasterization/

				// 28.4 fixed-point coordinates

				var x1 = Math.round( 16 * x1 );
				var x2 = Math.round( 16 * x2 );
				var x3 = Math.round( 16 * x3 );

				var y1 = Math.round( 16 * y1 );
				var y2 = Math.round( 16 * y2 );
				var y3 = Math.round( 16 * y3 );

				// Deltas

				var dx12 = x1 - x2, dy12 = y2 - y1;
				var dx23 = x2 - x3, dy23 = y3 - y2;
				var dx31 = x3 - x1, dy31 = y1 - y3;

				// Bounding rectangle

				var minx = Math.max( ( Math.min( x1, x2, x3 ) + 0xf ) >> 4, 0 );
				var maxx = Math.min( ( Math.max( x1, x2, x3 ) + 0xf ) >> 4, canvasWidth );
				var miny = Math.max( ( Math.min( y1, y2, y3 ) + 0xf ) >> 4, 0 );
				var maxy = Math.min( ( Math.max( y1, y2, y3 ) + 0xf ) >> 4, canvasHeight );

				// Block size, standard 8x8 (must be power of two)

				var q = blocksize;

				// Start in corner of 8x8 block

				minx &= ~(q - 1);
				miny &= ~(q - 1);

				// Constant part of half-edge functions

				var c1 = -dy12 * x1 - dx12 * y1;
				var c2 = -dy23 * x2 - dx23 * y2;
				var c3 = -dy31 * x3 - dx31 * y3;

				// Correct for fill convention

                                if ( dy12 > 0 || ( dy12 == 0 && dx12 > 0 ) ) c1 ++;
				if ( dy23 > 0 || ( dy23 == 0 && dx23 > 0 ) ) c2 ++;
				if ( dy31 > 0 || ( dy31 == 0 && dx31 > 0 ) ) c3 ++;

                                // Note this doesn't kill subpixel precision, but only because we test for >=0 (not >0).
                                // It's a bit subtle. :)
                                c1 = (c1 - 1) >> 4;
                                c2 = (c2 - 1) >> 4;
                                c3 = (c3 - 1) >> 4;

                                // Set up min/max corners
                                var qm1 = q - 1; // for convenience
                                var nmin1 = 0, nmax1 = 0;
                                var nmin2 = 0, nmax2 = 0;
                                var nmin3 = 0, nmax3 = 0;
                                if (dx12 >= 0) nmax1 -= qm1*dx12; else nmin1 -= qm1*dx12;
                                if (dy12 >= 0) nmax1 -= qm1*dy12; else nmin1 -= qm1*dy12;
                                if (dx23 >= 0) nmax2 -= qm1*dx23; else nmin2 -= qm1*dx23;
                                if (dy23 >= 0) nmax2 -= qm1*dy23; else nmin2 -= qm1*dy23;
                                if (dx31 >= 0) nmax3 -= qm1*dx31; else nmin3 -= qm1*dx31;
                                if (dy31 >= 0) nmax3 -= qm1*dy31; else nmin3 -= qm1*dy31;

				// Loop through blocks
                                var linestep = (canvasWidth - q) * 4;
                                var scale = 255.0 / (c1 + c2 + c3);

				for ( var y0 = miny; y0 < maxy; y0 += q ) {

					for ( var x0 = minx; x0 < maxx; x0 += q ) {

                                                // Edge functions at top-left corner
                                                var cy1 = c1 + dx12 * y0 + dy12 * x0;
                                                var cy2 = c2 + dx23 * y0 + dy23 * x0;
                                                var cy3 = c3 + dx31 * y0 + dy31 * x0;

                                                // Skip block when at least one edge completely out
                                                if (cy1 < nmax1 || cy2 < nmax2 || cy3 < nmax3) continue;

                                                // Skip writing full block if it's already fully covered
                                                var blockX = (x0 / q) | 0;
                                                var blockY = (y0 / q) | 0;
                                                var blockInd = blockX + blockY * canvasWBlocks;
                                                if (block_full[blockInd]) continue;

                                                // Offset at top-left corner
                                                var offset = (x0 + y0 * canvasWidth) * 4;

                                                // Accept whole block when fully covered
                                                if (cy1 >= nmin1 && cy2 >= nmin2 && cy3 >= nmin3) {

							for ( var iy = 0; iy < q; iy ++ ) {
                                                                var cx1 = cy1;
                                                                var cx2 = cy2;

								for ( var ix = 0; ix < q; ix ++ ) {
                                                                        if (!data[offset + 3]) {
                                                                                var u = cx1 * scale; // 0-255!
                                                                                var v = cx2 * scale; // 0-255!
                                                                                data[offset] = u;
                                                                                data[offset + 1] = v;
                                                                                data[offset + 2] = 0;
                                                                                data[offset + 3] = 255;
                                                                        }

                                                                        cx1 += dy12;
                                                                        cx2 += dy23;
                                                                        offset += 4;
								}

                                                                cy1 += dx12;
                                                                cy2 += dx23;
                                                                offset += linestep;
							}

                                                        block_full[blockInd] = 1;

						} else { // Partially covered block

							for ( var iy = 0; iy < q; iy ++ ) {
                                                                var cx1 = cy1;
                                                                var cx2 = cy2;
                                                                var cx3 = cy3;

								for ( var ix = 0; ix < q; ix ++ ) {
                                                                        if ( (cx1 | cx2 | cx3) >= 0 && !data[offset+3]) {
                                                                                var u = cx1 * scale; // 0-255!
                                                                                var v = cx2 * scale; // 0-255!
                                                                                data[offset] = u;
                                                                                data[offset + 1] = v; 
                                                                                data[offset + 2] = 0;
                                                                                data[offset + 3] = 255;
									}

									cx1 += dy12;
									cx2 += dy23;
									cx3 += dy31;
                                                                        offset += 4;
                                                                }

                                                                cy1 += dx12;
                                                                cy2 += dx23;
                                                                cy3 += dx31;
                                                                offset += linestep;
							}

						}

					}

				}

			}

		</script>
	</body>
</html>