<commit_before><!DOCTYPE html>
<html><head>
	<meta charset="utf-8">
	<title>Context Blender Test</title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
	<script type="text/javascript" src="../context_blender.js"></script>
	<script type="text/javascript">
		var ctx = {};
		var testLoops = 5;

		Object.defineProperty(Array.prototype,'sum',{value:function(){ for (var sum=0,i=this.length;i--;) sum+=this[i]; return sum }});

		$(window).load(function(){
			[].forEach.call(document.querySelectorAll('img'),function(img){
				var id = img.id;
				var canvas = document.createElement('canvas');
				canvas.width = canvas.height = 140;
				ctx[id] = canvas.getContext('2d');
				ctx[id].drawImage(img,0,0);
			});

			var canvas = document.createElement('canvas');
			canvas.width = canvas.height = 140;
			ctx.actual = canvas.getContext('2d');
			var blendOnto = CanvasRenderingContext2D.prototype.blendOnto;

			$.each(blendOnto.supportedBlendModes,function(){
				var mode = this+"";
				if (blendOnto.aliases[mode]) return;
				var section = $("<section>",{id:mode}).appendTo(document.body);
				var actual = $("<canvas>",{class:'actual'}).appendTo(section)[0];
				actual.width = actual.height = 140;
				actual = actual.getContext('2d');

				var ideal = $("<canvas>",{class:'ideal'}).appendTo(section)[0];
				ideal.width = ideal.height = 140;
				ideal = ideal.getContext('2d');
				var img = new Image;
				img.onload = function(){
					ideal.drawImage(img,0,0);
					var result = analyze(actual,ideal,mode);
					showResult(result,section);
				};
				img.src = mode+'-ideal.png';
			});
		});

		function analyze(actual,ideal,mode){
			for (var time=0,i=testLoops;i--;){
				actual.clearRect(0,0,140,140);
				actual.drawImage(ctx.under.canvas,0,0);
				var start = new Date;
				ctx.over.blendOnto(actual,mode);
				time += new Date-start;
			}
			var idata = ideal.getImageData(0,0,140,140).data;
			var adata = actual.getImageData(0,0,140,140).data;

			for (var deltas=[],i=0;i<adata.length;i+=4) deltas[i] = distance(pxl(idata,i),pxl(adata,i));
			var wrongs  = deltas.filter(function(n){ return n>3 });
			var percent = 100 * wrongs.length / Math.pow(140,2);
			return { mode:mode, time:time/testLoops, percentBad:percent, deviation:wrongs.sum() };
		}

		function showResult(result,section){
			var t = $("<table><caption>"+result.mode+"</caption></table>").appendTo(section);
			t.append('<tr><th>Accuracy</th><td>'+Math.round(100-result.percentBad)+'%</td></tr>');
			t.append('<tr><th>Deviation</th><td>'+Math.round(result.deviation)+'</td></tr>');
			t.append('<tr><th>Time</th><td>'+result.time.toFixed(1)+'ms</td></tr>');
		}

		function distance(ideal,actual){
			return Math.sqrt( Math.pow(ideal.r-actual.r,2) + Math.pow(ideal.g-actual.g,2), Math.pow(ideal.b-actual.b,2), Math.pow(ideal.a-actual.a,2) ) * (ideal.a/255);
		}

		function pxl(imgdata,i){
			return {r:imgdata[i],g:imgdata[i+1],b:imgdata[i+2],a:imgdata[i+3]};
		}
	</script>
	<style type="text/css">
	#sources { font-size:150%; text-align:center; }
	#sources img { vertical-align:middle }
	h2 { margin:0 }
	section { clear:left; width:420px; margin:1em auto; border:1px solid #333; box-model:padding-box; }
	section canvas { float:left }
	caption { font-weight:bold; text-align:left; background:#ccc; padding:0 0.5em; height:20px; line-height:20px; }
	table { height:120px; width:140px }
	th { text-align:left; opacity:0.4 }
	td { text-align:right }
	th,td { vertical-align:bottom }
	</style>
</head><body>
<h1 id="sources">
Blending
<img id="over"  src="over.png">
over
<img id="under" src="under.png">
</h1><commit_msg>HTML-based test page now shows result splitting out RGB from alpha, plus red channel solo'd<commit_after><!DOCTYPE html>
<html><head>
	<meta charset="utf-8">
	<title>Context Blender Test</title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
	<script type="text/javascript" src="../context_blender.js"></script>
	<script type="text/javascript">
		var ctx = {};
		var testLoops = 5;

		Object.defineProperty(Array.prototype,'sum',{value:function(){ for (var sum=0,i=this.length;i--;) sum+=this[i]; return sum }});

		$(window).load(function(){
			[].forEach.call(document.querySelectorAll('img'),function(img){
				var id = img.id;
				var canvas = document.createElement('canvas');
				canvas.width = canvas.height = 140;
				ctx[id] = canvas.getContext('2d');
				ctx[id].drawImage(img,0,0);
			});

			var canvas = document.createElement('canvas');
			canvas.width = canvas.height = 140;
			ctx.actual = canvas.getContext('2d');
			var blendOnto = CanvasRenderingContext2D.prototype.blendOnto;

			var tbody = document.querySelector('tbody');

			$.each(blendOnto.supportedBlendModes,function(){
				var mode = this+"";
				if (blendOnto.aliases[mode]) return;
				var row = $("<tr>",{id:mode}).appendTo(tbody);
				var head = $('<th>'+mode+'</th>',{"class":'row'}).appendTo(row);
				var ideal  = addCanvasToRow(row,mode+'-ideal');
				var actual = addCanvasToRow(row,mode+'-actual');
				var img = new Image;
				img.onload = function(){
					ideal.drawImage(img,0,0);
					var result = analyze(actual,ideal,mode);
					splitRGBA(actual,ideal,row);
					showResult(result,row);
				};
				img.src = mode+'-ideal.png';
			});
		});

		function addCanvasToRow(row,id){
			var cell = $('<td>').appendTo(row);
			var canvas = $("<canvas>",{id:id}).appendTo(cell)[0];
			canvas.width = canvas.height = 140;
			return canvas.getContext('2d');
		}

		function analyze(actual,ideal,mode){
			for (var time=0,i=testLoops;i--;){
				actual.clearRect(0,0,140,140);
				actual.drawImage(ctx.under.canvas,0,0);
				var start = new Date;
				ctx.over.blendOnto(actual,mode);
				time += new Date-start;
			}
			var idata = ideal.getImageData(0,0,140,140).data;
			var adata = actual.getImageData(0,0,140,140).data;

			for (var deltas=[],i=0;i<adata.length;i+=4) deltas[i] = distance(pxl(idata,i),pxl(adata,i));
			var wrongs  = deltas.filter(function(n){ return n>3 });
			var percent = 100 * wrongs.length / Math.pow(140,2);
			return { mode:mode, time:time/testLoops, percentBad:percent, deviation:wrongs.sum() };
		}

		function showResult(result,row){
			var cell = $('<td>',{"class":'results'}).appendTo(row);
			cell[0].innerHTML = "<ul>" +
			'<li><strong>Accuracy<\/strong> <span>'+Math.round(100-result.percentBad)+'%<\/span><\/li>' +
			'<li><strong>Deviation<\/strong> <span>'+Math.round(result.deviation)+'<\/span><\/li>' +
			'<li><strong>Calculation<\/strong> <span>'+result.time.toFixed(1)+'ms<\/span><\/li>' +
			"<\/ul>";
		}

		function splitRGBA(actual,ideal,row){
			copy(function(r,g,b,a){ return [r,g,b,255] },'rgb');
			copy(function(r,g,b,a){ return [a,a,a,255] },'alpha');
			copy(function(r,g,b,a){ return [r,0,0,255] },'red');

			function copy(f,idsuffix){
				[ideal,actual].forEach(function(src){
					var dst  = addCanvasToRow(row,src.canvas.id+"-"+idsuffix);
					var srcd = src.getImageData(0,0,140,140).data;
					var dsti = dst.getImageData(0,0,140,140), dstd = dsti.data;
					for (var px=0;px<srcd.length;px+=4){
						var pxs = f(srcd[px],srcd[px+1],srcd[px+2],srcd[px+3]);
						for (var p=4;p--;) dstd[px+p] = pxs[p];
					}
					dst.putImageData(dsti,0,0);
				});
			}
		}

		function distance(ideal,actual){
			return Math.sqrt( Math.pow(ideal.r-actual.r,2) + Math.pow(ideal.g-actual.g,2), Math.pow(ideal.b-actual.b,2), Math.pow(ideal.a-actual.a,2) ) * (ideal.a/255);
		}

		function pxl(imgdata,i){
			return {r:imgdata[i],g:imgdata[i+1],b:imgdata[i+2],a:imgdata[i+3]};
		}
	</script>
	<style type="text/css">
	body { background:#ddd; font-family:sans-serif }
	#sources { font-size:150%; text-align:center; }
	#sources img { vertical-align:middle }
	img, canvas { border:1px solid #333 }
	h2 { margin:0 }
	section { clear:left; width:420px; margin:1em auto; border:1px solid #333; box-model:padding-box }
	table { height:100px; border-collapse:collapse; margin:2em auto; }
	td { text-align:right }
	th.row { vertical-align:top }
	th,td { vertical-align:middle; padding:1px }
	thead th:nth-child(odd), tbody th.row, td:nth-child(odd) { padding-right:1em }
	tbody th, tbody td { padding-bottom:2em; }
	tbody th { text-align:right; font-size:115%; padding-right:0.5em; }
	.results ul { display:table; font-size:80%; margin:0; padding:0; }
	.results li { display:table-row; margin:0; padding:0; }
	.results li > * { display:table-cell }
	.results li strong { text-align:left; padding:0 }
	.results li span { padding-left:1em }
	</style>
</head><body>
<h1 id="sources">
Blending
<img id="over"  src="over.png">
over
<img id="under" src="under.png">
</h1>
<table><thead><tr>
	<th></th>
	<th>Ideal RGBA</th>
	<th>Actual RGBA</th>
	<th>Ideal RGB</th>
	<th>Actual RGB</th>
	<th>Ideal Alpha</th>
	<th>Actual Alpha</th>
	<th>Ideal Red</th>
	<th>Actual Red</th>
	<th>Stats</th>
</tr></thead><tbody></tbody></table>
{'fraction_non_alphanumeric': 0.2075725327657668, 'fraction_numerical': 0.029125126022179905, 'mean_word_length': 4.473942366646229, 'pattern_counts': {'":': 2, '<': 125, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1506498', 'n_tokens_mistral': 3893, 'n_tokens_neox': 3372, 'n_words': 541}